10
10
use clap:: { CommandFactory , Parser } ;
11
11
use gettextrs:: { bind_textdomain_codeset, setlocale, textdomain, LocaleCategory } ;
12
12
use plib:: PROJECT_NAME ;
13
+ use std:: fs:: { metadata, Metadata } ;
13
14
use std:: io:: { self , Write } ;
15
+ use std:: sync:: mpsc;
16
+ use std:: thread;
17
+ use std:: time:: Duration ;
14
18
use std:: {
15
19
collections:: BTreeMap ,
16
- ffi:: { CStr , CString } ,
20
+ ffi:: CStr ,
21
+ os:: unix:: fs:: MetadataExt ,
17
22
path:: { Path , PathBuf } ,
18
- sync:: mpsc,
19
- thread,
20
- time:: Duration ,
21
23
} ;
22
24
23
25
const NAME_FIELD : usize = 20 ;
@@ -436,8 +438,8 @@ mod linux {
436
438
Err ( _) => continue ,
437
439
} ;
438
440
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 ( ) ;
441
443
442
444
check_root_access ( names, pid, uid, & root_stat, device_list, inode_list) ?;
443
445
check_cwd_access ( names, pid, uid, & cwd_stat, device_list, inode_list) ?;
@@ -486,28 +488,31 @@ mod linux {
486
488
names : & mut Names ,
487
489
pid : i32 ,
488
490
uid : u32 ,
489
- root_stat : & libc :: stat ,
491
+ root_stat : & Metadata ,
490
492
device_list : & DeviceList ,
491
493
inode_list : & InodeList ,
492
494
) -> Result < ( ) , io:: Error > {
495
+ let root_device_id = root_stat. dev ( ) ;
496
+ let root_inode_number = root_stat. ino ( ) ;
497
+
493
498
if device_list
494
499
. iter ( )
495
- . any ( |device| device. device_id == root_stat . st_dev )
500
+ . any ( |device| device. device_id == root_device_id )
496
501
{
497
502
add_process ( names, pid, uid, Access :: Root , ProcType :: Normal ) ;
498
503
return Ok ( ( ) ) ;
499
504
}
505
+
500
506
if inode_list
501
507
. 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 )
503
509
{
504
510
add_process ( names, pid, uid, Access :: Root , ProcType :: Normal ) ;
505
511
return Ok ( ( ) ) ;
506
512
}
507
513
508
514
Ok ( ( ) )
509
515
}
510
-
511
516
/// Checks if a process has access to the current working directory and updates the `Names` object if it does.
512
517
///
513
518
/// # Arguments
@@ -530,28 +535,31 @@ mod linux {
530
535
names : & mut Names ,
531
536
pid : i32 ,
532
537
uid : u32 ,
533
- cwd_stat : & libc :: stat ,
538
+ cwd_stat : & Metadata ,
534
539
device_list : & DeviceList ,
535
540
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
+
537
545
if device_list
538
546
. iter ( )
539
- . any ( |device| device. device_id == cwd_stat . st_dev )
547
+ . any ( |device| device. device_id == cwd_device_id )
540
548
{
541
549
add_process ( names, pid, uid, Access :: Cwd , ProcType :: Normal ) ;
542
550
return Ok ( ( ) ) ;
543
551
}
552
+
544
553
if inode_list
545
554
. 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 )
547
556
{
548
557
add_process ( names, pid, uid, Access :: Cwd , ProcType :: Normal ) ;
549
558
return Ok ( ( ) ) ;
550
559
}
551
560
552
561
Ok ( ( ) )
553
562
}
554
-
555
563
/// Checks if a process has access to the executable file and updates the `Names` object if it does.
556
564
///
557
565
/// # Arguments
@@ -574,28 +582,31 @@ mod linux {
574
582
names : & mut Names ,
575
583
pid : i32 ,
576
584
uid : u32 ,
577
- exe_stat : & libc :: stat ,
585
+ exe_stat : & Metadata ,
578
586
device_list : & DeviceList ,
579
587
inode_list : & InodeList ,
580
588
) -> Result < ( ) , io:: Error > {
589
+ let exe_device_id = exe_stat. dev ( ) ;
590
+ let exe_inode_number = exe_stat. ino ( ) ;
591
+
581
592
if device_list
582
593
. iter ( )
583
- . any ( |device| device. device_id == exe_stat . st_dev )
594
+ . any ( |device| device. device_id == exe_device_id )
584
595
{
585
596
add_process ( names, pid, uid, Access :: Exe , ProcType :: Normal ) ;
586
597
return Ok ( ( ) ) ;
587
598
}
599
+
588
600
if inode_list
589
601
. 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 )
591
603
{
592
604
add_process ( names, pid, uid, Access :: Exe , ProcType :: Normal ) ;
593
605
return Ok ( ( ) ) ;
594
606
}
595
607
596
608
Ok ( ( ) )
597
609
}
598
-
599
610
/// Checks a directory within a process's `/proc` entry for matching devices and inodes,
600
611
/// and updates the `Names` object with relevant process information.
601
612
///
@@ -630,43 +641,60 @@ mod linux {
630
641
net_dev : u64 ,
631
642
) -> Result < ( ) , io:: Error > {
632
643
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
+ } ;
634
655
for entry in dir_entries {
635
656
let entry = entry?;
636
657
let path = entry. path ( ) ;
637
658
let path_str = path. to_string_lossy ( ) ;
638
659
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
+ }
643
677
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
+ } ;
653
682
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
+ }
664
693
}
665
694
}
666
695
667
696
Ok ( ( ) )
668
697
}
669
-
670
698
/// Checks the memory map of a process for matching devices and updates the `Names` object.
671
699
///
672
700
/// # Arguments
@@ -735,9 +763,9 @@ mod linux {
735
763
}
736
764
737
765
/// 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 > {
739
767
let path = format ! ( "{}/{}{}" , PROC_PATH , pid, filename) ;
740
- timeout ( & path, 5 )
768
+ fs :: metadata ( & path)
741
769
}
742
770
743
771
/// Fills the `unix_socket_list` with information from the `/proc/net/unix` file.
@@ -766,19 +794,20 @@ mod linux {
766
794
let path = normalize_path ( scanned_path) ;
767
795
768
796
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
+ }
775
805
Err ( _) => continue ,
776
806
}
777
807
}
778
808
}
779
809
Ok ( ( ) )
780
810
}
781
-
782
811
/// Reads the `/proc/mounts` file and updates the `mount_list` with mount points.
783
812
///
784
813
/// # Arguments
@@ -1082,16 +1111,17 @@ mod linux {
1082
1111
need_check_map : & mut bool ,
1083
1112
) -> Result < ( ) , std:: io:: Error > {
1084
1113
names. filename = expand_path ( & names. filename ) ?;
1114
+
1085
1115
let st = timeout ( & names. filename . to_string_lossy ( ) , 5 ) ?;
1086
1116
read_proc_mounts ( mount_list) ?;
1087
1117
1088
1118
if mount {
1089
- * device_list = DeviceList :: new ( names. clone ( ) , st. st_dev ) ;
1119
+ * device_list = DeviceList :: new ( names. clone ( ) , st. dev ( ) ) ;
1090
1120
* need_check_map = true ;
1091
1121
} 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 ( ) ) ;
1094
1123
}
1124
+
1095
1125
Ok ( ( ) )
1096
1126
}
1097
1127
@@ -1194,7 +1224,7 @@ mod macos {
1194
1224
include ! ( concat!( env!( "OUT_DIR" ) , "/osx_libproc_bindings.rs" ) ) ;
1195
1225
}
1196
1226
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} ;
1198
1228
1199
1229
// similar to list_pids_ret() below, there are two cases when 0 is returned, one when there are
1200
1230
// no pids, and the other when there is an error
@@ -1283,7 +1313,7 @@ mod macos {
1283
1313
1284
1314
for name in names. iter_mut ( ) {
1285
1315
let st = timeout ( & name. filename . to_string_lossy ( ) , 5 ) ?;
1286
- let uid = st. st_uid ;
1316
+ let uid = st. uid ( ) ;
1287
1317
1288
1318
let pids = listpidspath (
1289
1319
osx_libproc_bindings:: PROC_ALL_PIDS ,
@@ -1442,21 +1472,37 @@ fn print_matches(name: &mut Names, user: bool) -> Result<(), io::Error> {
1442
1472
Ok ( ( ) )
1443
1473
}
1444
1474
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 > {
1448
1491
let ( tx, rx) = mpsc:: channel ( ) ;
1492
+ let path = path. to_string ( ) ; // Clone path into a `String` with `'static` lifetime
1449
1493
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
+ }
1456
1500
} ) ;
1457
1501
1502
+ // Wait for the result or timeout
1458
1503
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
1460
1506
Err ( mpsc:: RecvTimeoutError :: Timeout ) => Err ( io:: Error :: new (
1461
1507
io:: ErrorKind :: TimedOut ,
1462
1508
"Operation timed out" ,
@@ -1466,24 +1512,3 @@ fn timeout(path: &str, seconds: u32) -> Result<libc::stat, io::Error> {
1466
1512
}
1467
1513
}
1468
1514
}
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