Skip to content

Commit bb785dc

Browse files
committed
merge
2 parents 3b75aa1 + 1cd3470 commit bb785dc

File tree

9 files changed

+503
-401
lines changed

9 files changed

+503
-401
lines changed

process/Cargo.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@ gettext-rs.workspace = true
1313
libc.workspace = true
1414
dirs = "5.0"
1515

16-
[dev-dependencies]
17-
sysinfo = "0.31"
18-
tokio = { version = "1.39", features = ["net", "macros", "rt"]}
19-
2016
[build-dependencies]
2117
bindgen = { version = "0.70.0", features = ["runtime"] }
2218

2319
[lints]
2420
workspace = true
2521

22+
[dev-dependencies]
23+
sysinfo = "0.31"
24+
2625
[[bin]]
2726
name = "fuser"
2827
path = "./fuser.rs"

process/fuser.rs

Lines changed: 114 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
use clap::{CommandFactory, Parser};
1111
use gettextrs::{bind_textdomain_codeset, setlocale, textdomain, LocaleCategory};
1212
use plib::PROJECT_NAME;
13+
use std::fs::{metadata, Metadata};
1314
use std::io::{self, Write};
15+
use std::sync::mpsc;
16+
use std::thread;
17+
use std::time::Duration;
1418
use std::{
1519
collections::BTreeMap,
16-
ffi::{CStr, CString},
20+
ffi::CStr,
21+
os::unix::fs::MetadataExt,
1722
path::{Path, PathBuf},
18-
sync::mpsc,
19-
thread,
20-
time::Duration,
2123
};
2224

2325
const NAME_FIELD: usize = 20;
@@ -436,8 +438,8 @@ mod linux {
436438
Err(_) => continue,
437439
};
438440

439-
let st = timeout(&entry.path().to_string_lossy(), 5)?;
440-
let uid = st.st_uid;
441+
let st = fs::metadata(&entry.path())?;
442+
let uid = st.uid();
441443

442444
check_root_access(names, pid, uid, &root_stat, device_list, inode_list)?;
443445
check_cwd_access(names, pid, uid, &cwd_stat, device_list, inode_list)?;
@@ -486,28 +488,31 @@ mod linux {
486488
names: &mut Names,
487489
pid: i32,
488490
uid: u32,
489-
root_stat: &libc::stat,
491+
root_stat: &Metadata,
490492
device_list: &DeviceList,
491493
inode_list: &InodeList,
492494
) -> Result<(), io::Error> {
495+
let root_device_id = root_stat.dev();
496+
let root_inode_number = root_stat.ino();
497+
493498
if device_list
494499
.iter()
495-
.any(|device| device.device_id == root_stat.st_dev)
500+
.any(|device| device.device_id == root_device_id)
496501
{
497502
add_process(names, pid, uid, Access::Root, ProcType::Normal);
498503
return Ok(());
499504
}
505+
500506
if inode_list
501507
.iter()
502-
.any(|inode| inode.device_id == root_stat.st_dev && inode.inode == root_stat.st_ino)
508+
.any(|inode| inode.device_id == root_device_id && inode.inode == root_inode_number)
503509
{
504510
add_process(names, pid, uid, Access::Root, ProcType::Normal);
505511
return Ok(());
506512
}
507513

508514
Ok(())
509515
}
510-
511516
/// Checks if a process has access to the current working directory and updates the `Names` object if it does.
512517
///
513518
/// # Arguments
@@ -530,28 +535,31 @@ mod linux {
530535
names: &mut Names,
531536
pid: i32,
532537
uid: u32,
533-
cwd_stat: &libc::stat,
538+
cwd_stat: &Metadata,
534539
device_list: &DeviceList,
535540
inode_list: &InodeList,
536-
) -> Result<(), std::io::Error> {
541+
) -> Result<(), io::Error> {
542+
let cwd_device_id = cwd_stat.dev();
543+
let cwd_inode_number = cwd_stat.ino();
544+
537545
if device_list
538546
.iter()
539-
.any(|device| device.device_id == cwd_stat.st_dev)
547+
.any(|device| device.device_id == cwd_device_id)
540548
{
541549
add_process(names, pid, uid, Access::Cwd, ProcType::Normal);
542550
return Ok(());
543551
}
552+
544553
if inode_list
545554
.iter()
546-
.any(|inode| inode.device_id == cwd_stat.st_dev && inode.inode == cwd_stat.st_ino)
555+
.any(|inode| inode.device_id == cwd_device_id && inode.inode == cwd_inode_number)
547556
{
548557
add_process(names, pid, uid, Access::Cwd, ProcType::Normal);
549558
return Ok(());
550559
}
551560

552561
Ok(())
553562
}
554-
555563
/// Checks if a process has access to the executable file and updates the `Names` object if it does.
556564
///
557565
/// # Arguments
@@ -574,28 +582,31 @@ mod linux {
574582
names: &mut Names,
575583
pid: i32,
576584
uid: u32,
577-
exe_stat: &libc::stat,
585+
exe_stat: &Metadata,
578586
device_list: &DeviceList,
579587
inode_list: &InodeList,
580588
) -> Result<(), io::Error> {
589+
let exe_device_id = exe_stat.dev();
590+
let exe_inode_number = exe_stat.ino();
591+
581592
if device_list
582593
.iter()
583-
.any(|device| device.device_id == exe_stat.st_dev)
594+
.any(|device| device.device_id == exe_device_id)
584595
{
585596
add_process(names, pid, uid, Access::Exe, ProcType::Normal);
586597
return Ok(());
587598
}
599+
588600
if inode_list
589601
.iter()
590-
.any(|inode| inode.device_id == exe_stat.st_dev && inode.inode == exe_stat.st_ino)
602+
.any(|inode| inode.device_id == exe_device_id && inode.inode == exe_inode_number)
591603
{
592604
add_process(names, pid, uid, Access::Exe, ProcType::Normal);
593605
return Ok(());
594606
}
595607

596608
Ok(())
597609
}
598-
599610
/// Checks a directory within a process's `/proc` entry for matching devices and inodes,
600611
/// and updates the `Names` object with relevant process information.
601612
///
@@ -630,43 +641,60 @@ mod linux {
630641
net_dev: u64,
631642
) -> Result<(), io::Error> {
632643
let dir_path = format!("/proc/{}/{}", pid, dirname);
633-
let dir_entries = fs::read_dir(&dir_path)?;
644+
let dir_entries = match fs::read_dir(&dir_path) {
645+
Ok(entries) => entries,
646+
Err(err) if err.kind() == ErrorKind::PermissionDenied => {
647+
eprintln!("Permission denied for directory: {:?}", dir_path);
648+
return Ok(());
649+
}
650+
Err(err) => {
651+
eprintln!("Failed to read directory {:?}: {:?}", dir_path, err);
652+
return Err(err);
653+
}
654+
};
634655
for entry in dir_entries {
635656
let entry = entry?;
636657
let path = entry.path();
637658
let path_str = path.to_string_lossy();
638659

639-
let mut stat = match timeout(&path_str, 5) {
640-
Ok(stat) => stat,
641-
Err(_) => continue,
642-
};
660+
match timeout(&path_str, 5) {
661+
Ok(metadata) => {
662+
let st_dev = metadata.dev();
663+
let st_ino = metadata.ino();
664+
665+
let mut stat_dev = st_dev;
666+
let mut stat_ino = st_ino;
667+
668+
if stat_dev == net_dev {
669+
if let Some(unix_socket) = unix_socket_list
670+
.iter()
671+
.find(|sock| sock.net_inode == stat_ino)
672+
{
673+
stat_dev = unix_socket.device_id;
674+
stat_ino = unix_socket.inode;
675+
}
676+
}
643677

644-
if stat.st_dev == net_dev {
645-
if let Some(unix_socket) = unix_socket_list
646-
.iter()
647-
.find(|sock| sock.net_inode == stat.st_ino)
648-
{
649-
stat.st_dev = unix_socket.device_id;
650-
stat.st_ino = unix_socket.inode;
651-
}
652-
}
678+
let new_access = match access {
679+
Access::File => Access::Filewr,
680+
_ => access.clone(),
681+
};
653682

654-
let new_access = match access {
655-
Access::File => Access::Filewr,
656-
_ => access.clone(),
657-
};
658-
if device_list
659-
.iter()
660-
.any(|dev| dev.name.filename != PathBuf::from("") && stat.st_dev == dev.device_id)
661-
|| inode_list.iter().any(|inode| inode.inode == stat.st_ino)
662-
{
663-
add_process(names, pid, uid, new_access, ProcType::Normal);
683+
if device_list.iter().any(|dev| {
684+
dev.name.filename != PathBuf::from("") && stat_dev == dev.device_id
685+
}) || inode_list.iter().any(|inode| inode.inode == stat_ino)
686+
{
687+
add_process(names, pid, uid, new_access, ProcType::Normal);
688+
}
689+
}
690+
Err(_) => {
691+
continue;
692+
}
664693
}
665694
}
666695

667696
Ok(())
668697
}
669-
670698
/// Checks the memory map of a process for matching devices and updates the `Names` object.
671699
///
672700
/// # Arguments
@@ -735,9 +763,9 @@ mod linux {
735763
}
736764

737765
/// get stat of current /proc/{pid}/{filename}
738-
fn get_pid_stat(pid: i32, filename: &str) -> Result<libc::stat, io::Error> {
766+
fn get_pid_stat(pid: i32, filename: &str) -> Result<fs::Metadata, io::Error> {
739767
let path = format!("{}/{}{}", PROC_PATH, pid, filename);
740-
timeout(&path, 5)
768+
fs::metadata(&path)
741769
}
742770

743771
/// Fills the `unix_socket_list` with information from the `/proc/net/unix` file.
@@ -766,19 +794,20 @@ mod linux {
766794
let path = normalize_path(scanned_path);
767795

768796
match timeout(&path, 5) {
769-
Ok(stat) => UnixSocketList::add_socket(
770-
unix_socket_list,
771-
stat.st_dev,
772-
stat.st_ino,
773-
net_inode,
774-
),
797+
Ok(stat) => {
798+
UnixSocketList::add_socket(
799+
unix_socket_list,
800+
stat.dev(),
801+
stat.ino(),
802+
net_inode,
803+
);
804+
}
775805
Err(_) => continue,
776806
}
777807
}
778808
}
779809
Ok(())
780810
}
781-
782811
/// Reads the `/proc/mounts` file and updates the `mount_list` with mount points.
783812
///
784813
/// # Arguments
@@ -1082,16 +1111,17 @@ mod linux {
10821111
need_check_map: &mut bool,
10831112
) -> Result<(), std::io::Error> {
10841113
names.filename = expand_path(&names.filename)?;
1114+
10851115
let st = timeout(&names.filename.to_string_lossy(), 5)?;
10861116
read_proc_mounts(mount_list)?;
10871117

10881118
if mount {
1089-
*device_list = DeviceList::new(names.clone(), st.st_dev);
1119+
*device_list = DeviceList::new(names.clone(), st.dev());
10901120
*need_check_map = true;
10911121
} else {
1092-
let st = stat(&names.filename.to_string_lossy())?;
1093-
*inode_list = InodeList::new(st.st_dev, st.st_ino);
1122+
*inode_list = InodeList::new(st.dev(), st.ino());
10941123
}
1124+
10951125
Ok(())
10961126
}
10971127

@@ -1194,7 +1224,7 @@ mod macos {
11941224
include!(concat!(env!("OUT_DIR"), "/osx_libproc_bindings.rs"));
11951225
}
11961226
use libc::{c_char, c_int, c_void};
1197-
use std::{os::unix::ffi::OsStrExt, ptr};
1227+
use std::{ffi::CString, os::unix::ffi::OsStrExt, ptr};
11981228

11991229
// similar to list_pids_ret() below, there are two cases when 0 is returned, one when there are
12001230
// no pids, and the other when there is an error
@@ -1283,7 +1313,7 @@ mod macos {
12831313

12841314
for name in names.iter_mut() {
12851315
let st = timeout(&name.filename.to_string_lossy(), 5)?;
1286-
let uid = st.st_uid;
1316+
let uid = st.uid();
12871317

12881318
let pids = listpidspath(
12891319
osx_libproc_bindings::PROC_ALL_PIDS,
@@ -1442,21 +1472,37 @@ fn print_matches(name: &mut Names, user: bool) -> Result<(), io::Error> {
14421472
Ok(())
14431473
}
14441474

1445-
/// Execute stat() system call with timeout to avoid deadlock
1446-
/// on network based file systems.
1447-
fn timeout(path: &str, seconds: u32) -> Result<libc::stat, io::Error> {
1475+
/// Adds a new process to the `Names` object with specified access and process type.
1476+
fn add_process(names: &mut Names, pid: i32, uid: u32, access: Access, proc_type: ProcType) {
1477+
let proc = Procs::new(pid, uid, access, proc_type);
1478+
names.add_procs(proc);
1479+
}
1480+
1481+
/// Executes `metadata()` system call with timeout to avoid deadlock on network-based file systems.
1482+
///
1483+
/// **Arguments:**
1484+
/// - `path`: The file path to retrieve metadata for.
1485+
/// - `seconds`: The number of seconds to wait before timing out.
1486+
///
1487+
/// **Returns:**
1488+
/// - `Ok(fs::Metadata)` if the metadata is successfully retrieved within the timeout.
1489+
/// - `Err(io::Error)` if the operation fails or times out.
1490+
fn timeout(path: &str, seconds: u32) -> Result<Metadata, io::Error> {
14481491
let (tx, rx) = mpsc::channel();
1492+
let path = path.to_string(); // Clone path into a `String` with `'static` lifetime
14491493

1450-
thread::scope(|s| {
1451-
s.spawn(|| {
1452-
if let Err(e) = tx.send(stat(path)) {
1453-
eprintln!("Failed to send result through channel: {}", e);
1454-
}
1455-
});
1494+
// Spawn a thread to retrieve the metadata
1495+
thread::spawn(move || {
1496+
let metadata = metadata(&path); // Use the cloned `String` here
1497+
if let Err(e) = tx.send(metadata) {
1498+
eprintln!("Failed to send result through channel: {}", e);
1499+
}
14561500
});
14571501

1502+
// Wait for the result or timeout
14581503
match rx.recv_timeout(Duration::from_secs(seconds.into())) {
1459-
Ok(stat) => stat,
1504+
Ok(Ok(metadata)) => Ok(metadata), // Successfully retrieved metadata
1505+
Ok(Err(e)) => Err(e), // Metadata retrieval failed with an error
14601506
Err(mpsc::RecvTimeoutError::Timeout) => Err(io::Error::new(
14611507
io::ErrorKind::TimedOut,
14621508
"Operation timed out",
@@ -1466,24 +1512,3 @@ fn timeout(path: &str, seconds: u32) -> Result<libc::stat, io::Error> {
14661512
}
14671513
}
14681514
}
1469-
1470-
/// Retrieves the status of a file given its filename.
1471-
fn stat(filename_str: &str) -> io::Result<libc::stat> {
1472-
let filename = CString::new(filename_str)?;
1473-
1474-
unsafe {
1475-
let mut st: libc::stat = std::mem::zeroed();
1476-
let rc = libc::stat(filename.as_ptr(), &mut st);
1477-
if rc == 0 {
1478-
Ok(st)
1479-
} else {
1480-
Err(io::Error::last_os_error())
1481-
}
1482-
}
1483-
}
1484-
1485-
/// Adds a new process to the `Names` object with specified access and process type.
1486-
fn add_process(names: &mut Names, pid: i32, uid: u32, access: Access, proc_type: ProcType) {
1487-
let proc = Procs::new(pid, uid, access, proc_type);
1488-
names.add_procs(proc);
1489-
}

0 commit comments

Comments
 (0)