1010use clap:: { CommandFactory , Parser } ;
1111use gettextrs:: { bind_textdomain_codeset, setlocale, textdomain, LocaleCategory } ;
1212use plib:: PROJECT_NAME ;
13+ use std:: fs:: { metadata, Metadata } ;
1314use std:: io:: { self , Write } ;
15+ use std:: sync:: mpsc;
16+ use std:: thread;
17+ use std:: time:: Duration ;
1418use 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
2325const 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