From c908c068fb957bd4f17154bfc1e7e3922393116a Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sat, 30 Nov 2024 10:05:36 +0000 Subject: [PATCH 01/13] Added information record support to fanotify --- src/sys/fanotify.rs | 177 +++++++++++++++++++++++++++++++++++++- test/sys/test_fanotify.rs | 62 ++++++++++++- 2 files changed, 237 insertions(+), 2 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index fd3089f702..fee742be1f 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -118,6 +118,46 @@ libc_bitflags! { FAN_REPORT_PIDFD; /// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20. FAN_REPORT_TID; + + /// Allows the receipt of events which contain additional information + /// about the underlying filesystem object correlated to an event. + /// + /// This will make `FanotifyEvent::fd` return `FAN_NOFD`. + /// This should be used with `Fanotify::read_events_with_info_records` to + /// recieve `FanotifyInfoRecord::Fid` info records. + /// Since Linux 5.1 + FAN_REPORT_FID; + + /// Allows the receipt of events which contain additional information + /// about the underlying filesystem object correlated to an event. + /// + /// This will make `FanotifyEvent::fd` return `FAN_NOFD`. + /// This should be used with `Fanotify::read_events_with_info_records` to + /// recieve `FanotifyInfoRecord::Fid` info records. + /// + /// An additional event of `FAN_EVENT_INFO_TYPE_DFID` will also be received, + /// encapsulating information about the target directory (or parent directory of a file) + /// Since Linux 5.9 + FAN_REPORT_DIR_FID; + + /// Events for fanotify groups initialized with this flag will contain additional + /// information about the child correlated with directory entry modification events. + /// This flag must be provided in conjunction with the flags `FAN_REPORT_FID`, + /// `FAN_REPORT_DIR_FID` and `FAN_REPORT_NAME`. + /// Since Linux 5.17 + FAN_REPORT_TARGET_FID; + + /// Events for fanotify groups initialized with this flag will contain additional + /// information about the name of the directory entry correlated to an event. This + /// flag must be provided in conjunction with the flag `FAN_REPORT_DIR_FID`. + /// Since Linux 5.9 + FAN_REPORT_NAME; + + /// This is a synonym for `FAN_REPORT_DIR_FD | FAN_REPORT_NAME`. + FAN_REPORT_DFID_NAME; + + /// This is a synonym for `FAN_REPORT_DIR_FD | FAN_REPORT_NAME | FAN_REPORT_TARGET_FID`. + FAN_REPORT_DFID_NAME_TARGET; } } @@ -206,6 +246,33 @@ pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION; #[allow(missing_copy_implementations)] pub struct FanotifyEvent(libc::fanotify_event_metadata); +/// After a [`libc::fanotify_event_metadata`], there can be 0 or more event_info +/// structs depending on which InitFlags were used in [`Fanotify::init`]. +// Is not Clone due to pidfd in `libc::fanotify_event_info_pidfd` +// Other fanotify_event_info records are not implemented as they don't exist in +// the libc crate yet. +#[derive(Debug, Eq, Hash, PartialEq)] +#[allow(missing_copy_implementations)] +pub enum FanotifyInfoRecord { + /// A [`libc::fanotify_event_info_fid`] event was recieved, usually as + /// a result of passing [`InitFlags::FAN_REPORT_FID`] or [`InitFlags::FAN_REPORT_DIR_FID`] + /// into [`Fanotify::init`]. The containing struct includes a `file_handle` for + /// use with `open_by_handle_at(2)`. + Fid(libc::fanotify_event_info_fid), + + /// A [`libc::FAN_FS_ERROR`] event was received. This event occurs when + /// a filesystem event is detected. Only a single [`libc::FAN_FS_ERROR`] is + /// stored per filesystem at once, extra error messages are suppressed and + /// accounted for in the error_count field. + Error(libc::fanotify_event_info_error), + + /// A [`libc::fanotify_event_info_pidfd`] event was recieved, usually as + /// a result of passing [`InitFlags::FAN_REPORT_PIDFD`] into [`Fanotify::init`]. + /// The containing struct includes a `pidfd` for reliably determining + /// whether the process responsible for generating an event has been recycled or terminated + Pidfd(libc::fanotify_event_info_pidfd), +} + impl FanotifyEvent { /// Version number for the structure. It must be compared to /// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime @@ -341,6 +408,21 @@ impl Fanotify { Errno::result(res).map(|_| ()) } + fn get_struct(&self, buffer: &[u8; 4096], offset: usize) -> T { + let struct_size = size_of::(); + let struct_obj = unsafe { + let mut struct_obj = MaybeUninit::::uninit(); + std::ptr::copy_nonoverlapping( + buffer.as_ptr().add(offset), + struct_obj.as_mut_ptr().cast(), + (4096 - offset).min(struct_size), + ); + struct_obj.assume_init() + }; + + struct_obj + } + /// Read incoming events from the fanotify group. /// /// Returns a Result containing either a `Vec` of events on success or errno @@ -382,6 +464,99 @@ impl Fanotify { Ok(events) } + /// Read incoming events and information records from the fanotify group. + /// + /// Returns a Result containing either a `Vec` of events and information records on success or errno + /// otherwise. + /// + /// # Errors + /// + /// Possible errors can be those that are explicitly listed in + /// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in + /// addition to the possible errors caused by `read` call. + /// In particular, `EAGAIN` is returned when no event is available on a + /// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`, + /// thus making this method nonblocking. + pub fn read_events_with_info_records( + &self, + ) -> Result)>> { + let metadata_size = size_of::(); + const BUFSIZ: usize = 4096; + let mut buffer = [0u8; BUFSIZ]; + let mut events = Vec::new(); + let mut offset = 0; + + let nread = read(&self.fd, &mut buffer)?; + + while (nread - offset) >= metadata_size { + let metadata = unsafe { + let mut metadata = + MaybeUninit::::uninit(); + std::ptr::copy_nonoverlapping( + buffer.as_ptr().add(offset), + metadata.as_mut_ptr().cast(), + (BUFSIZ - offset).min(metadata_size), + ); + metadata.assume_init() + }; + + let mut remaining_len = metadata.event_len; + let mut info_records = Vec::new(); + let mut current_event_offset = offset + metadata_size; + + while remaining_len > 0 { + let header = self + .get_struct::( + &buffer, + current_event_offset, + ); + + let info_record = match header.info_type { + libc::FAN_EVENT_INFO_TYPE_FID => { + let event_fid = self + .get_struct::( + &buffer, + current_event_offset, + ); + Some(FanotifyInfoRecord::Fid(event_fid)) + } + libc::FAN_EVENT_INFO_TYPE_ERROR => { + let error_fid = self + .get_struct::( + &buffer, + current_event_offset, + ); + Some(FanotifyInfoRecord::Error(error_fid)) + } + libc::FAN_EVENT_INFO_TYPE_PIDFD => { + let error_fid = self + .get_struct::( + &buffer, + current_event_offset, + ); + Some(FanotifyInfoRecord::Pidfd(error_fid)) + } + // Ignore unsupported events + _ => None, + }; + + if let Some(record) = info_record { + info_records.push(record); + } + + remaining_len -= header.len as u32; + current_event_offset += header.len as usize; + } + + // libc::fanotify_event_info_header + + events.push((FanotifyEvent(metadata), info_records)); + offset += metadata.event_len as usize; + } + + Ok(events) + } + /// Write an event response on the fanotify group. /// /// Returns a Result containing either `()` on success or errno otherwise. @@ -443,4 +618,4 @@ impl Fanotify { fd } } -} \ No newline at end of file +} diff --git a/test/sys/test_fanotify.rs b/test/sys/test_fanotify.rs index 04b39c4db4..91a092f85e 100644 --- a/test/sys/test_fanotify.rs +++ b/test/sys/test_fanotify.rs @@ -3,7 +3,7 @@ use nix::errno::Errno; use nix::fcntl::AT_FDCWD; use nix::sys::fanotify::{ EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags, - Response, + Response, FanotifyInfoRecord }; use std::fs::{read_link, read_to_string, File, OpenOptions}; use std::io::ErrorKind; @@ -18,6 +18,7 @@ pub fn test_fanotify() { test_fanotify_notifications(); test_fanotify_responses(); + test_fanotify_notifications_with_info_records(); test_fanotify_overflow(); } @@ -84,6 +85,65 @@ fn test_fanotify_notifications() { assert_eq!(path, tempfile); } +fn test_fanotify_notifications_with_info_records() { + let group = + Fanotify::init(InitFlags::FAN_CLASS_NOTIF | InitFlags::FAN_REPORT_FID, EventFFlags::O_RDONLY) + .unwrap(); + let tempdir = tempfile::tempdir().unwrap(); + let tempfile = tempdir.path().join("test"); + OpenOptions::new() + .write(true) + .create_new(true) + .open(&tempfile) + .unwrap(); + + group + .mark( + MarkFlags::FAN_MARK_ADD, + MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE, + AT_FDCWD, + Some(&tempfile), + ) + .unwrap(); + + // modify test file + { + let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap(); + f.write_all(b"hello").unwrap(); + } + + let mut events = group.read_events_with_info_records().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let (event, info_records) = events.pop().unwrap(); + assert_eq!(info_records.len(), 1, "should have read exactly one info record"); + assert!(event.check_version()); + assert_eq!( + event.mask(), + MaskFlags::FAN_OPEN + | MaskFlags::FAN_MODIFY + | MaskFlags::FAN_CLOSE_WRITE + ); + + assert!(matches!(info_records[0], FanotifyInfoRecord::Fid { .. }), "info record should be an fid record"); + + // read test file + { + let mut f = File::open(&tempfile).unwrap(); + let mut s = String::new(); + f.read_to_string(&mut s).unwrap(); + } + + let mut events = group.read_events_with_info_records().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let (event, info_records) = events.pop().unwrap(); + assert_eq!(info_records.len(), 1, "should have read exactly one info record"); + assert!(event.check_version()); + assert_eq!( + event.mask(), + MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE + ); +} + fn test_fanotify_responses() { let group = Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY) From ece88b23b87e1303e719c82bc6276886af5eaf99 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sat, 30 Nov 2024 17:10:05 +0000 Subject: [PATCH 02/13] Added FanotifyFidRecord, FanotifyErrorRecord, FanotifyPidfdRecord --- src/sys/fanotify.rs | 120 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index fee742be1f..5c988cc1d0 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -238,13 +238,89 @@ libc_bitflags! { /// Compile version number of fanotify API. pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION; -/// Abstract over [`libc::fanotify_event_metadata`], which represents an event -/// received via [`Fanotify::read_events`]. +/// Abstract over [`libc::fanotify_event_info_fid`], which represents an +/// information record received via [`Fanotify::read_events_with_info_records`]. // Is not Clone due to fd field, to avoid use-after-close scenarios. #[derive(Debug, Eq, Hash, PartialEq)] #[repr(transparent)] #[allow(missing_copy_implementations)] -pub struct FanotifyEvent(libc::fanotify_event_metadata); +pub struct FanotifyFidRecord(libc::fanotify_event_info_fid); + +impl FanotifyFidRecord { + /// The filesystem id where this event occurred. The value this method returns + /// differs depending on the host system. Please read the statfs(2) documentation + /// for more information: + /// https://man7.org/linux/man-pages/man2/statfs.2.html#VERSIONS + pub fn fsid(&self) -> libc::__kernel_fsid_t { + self.0.fsid + } + + /// The file handle for the filesystem object where the event occurred. The handle is + /// represented as a 0-length u8 array, but it actually points to variable-length + /// file_handle struct.For more information: + /// https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html + pub fn handle(&self) -> [u8; 0] { + self.0.handle + } +} + +/// Abstract over [`libc::fanotify_event_info_error`], which represents an +/// information record received via [`Fanotify::read_events_with_info_records`]. +// Is not Clone due to fd field, to avoid use-after-close scenarios. +#[derive(Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +#[allow(missing_copy_implementations)] +pub struct FanotifyErrorRecord(libc::fanotify_event_info_error); + +impl FanotifyErrorRecord { + /// Errno of the FAN_FS_ERROR that occurred. + pub fn err(&self) -> Errno { + Errno::from_raw(self.0.error) + } + + /// Number of errors that occurred in the filesystem Fanotify in watching. + /// Only a single FAN_FS_ERROR is stored per filesystem at once. As such, Fanotify + /// suppresses subsequent error messages and only increments the `err_count` value. + pub fn err_count(&self) -> u32 { + self.0.error_count + } +} + +/// Abstract over [`libc::fanotify_event_info_pidfd`], which represents an +/// information record received via [`Fanotify::read_events_with_info_records`]. +// Is not Clone due to fd field, to avoid use-after-close scenarios. +#[derive(Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +#[allow(missing_copy_implementations)] +pub struct FanotifyPidfdRecord(libc::fanotify_event_info_pidfd); + +impl FanotifyPidfdRecord { + /// The process file descriptor that refers to the process responsible for + /// generating this event. If the underlying pidfd_create fails, `None` is returned. + pub fn pidfd(&self) -> Option { + if self.0.pidfd == libc::FAN_NOPIDFD || self.0.pidfd == libc::FAN_EPIDFD + { + None + } else { + // SAFETY: self.0.pidfd will be opened for the lifetime of `Self`, + // which is longer than the lifetime of the returned BorrowedFd, so + // it is safe. + Some(unsafe { BorrowedFd::borrow_raw(self.0.pidfd) }) + } + } +} + +impl Drop for FanotifyPidfdRecord { + fn drop(&mut self) { + if self.0.pidfd == libc::FAN_NOFD { + return; + } + let e = close(self.0.pidfd); + if !std::thread::panicking() && e == Err(Errno::EBADF) { + panic!("Closing an invalid file descriptor!"); + }; + } +} /// After a [`libc::fanotify_event_metadata`], there can be 0 or more event_info /// structs depending on which InitFlags were used in [`Fanotify::init`]. @@ -258,21 +334,28 @@ pub enum FanotifyInfoRecord { /// a result of passing [`InitFlags::FAN_REPORT_FID`] or [`InitFlags::FAN_REPORT_DIR_FID`] /// into [`Fanotify::init`]. The containing struct includes a `file_handle` for /// use with `open_by_handle_at(2)`. - Fid(libc::fanotify_event_info_fid), + Fid(FanotifyFidRecord), - /// A [`libc::FAN_FS_ERROR`] event was received. This event occurs when - /// a filesystem event is detected. Only a single [`libc::FAN_FS_ERROR`] is - /// stored per filesystem at once, extra error messages are suppressed and - /// accounted for in the error_count field. - Error(libc::fanotify_event_info_error), + /// A [`libc::fanotify_event_info_error`] event was recieved. + /// This occurs when a FAN_FS_ERROR occurs, indicating an error with + /// the watch filesystem object. (such as a bad file or bad link lookup) + Error(FanotifyErrorRecord), /// A [`libc::fanotify_event_info_pidfd`] event was recieved, usually as /// a result of passing [`InitFlags::FAN_REPORT_PIDFD`] into [`Fanotify::init`]. /// The containing struct includes a `pidfd` for reliably determining /// whether the process responsible for generating an event has been recycled or terminated - Pidfd(libc::fanotify_event_info_pidfd), + Pidfd(FanotifyPidfdRecord), } +/// Abstract over [`libc::fanotify_event_metadata`], which represents an event +/// received via [`Fanotify::read_events`]. +// Is not Clone due to fd field, to avoid use-after-close scenarios. +#[derive(Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +#[allow(missing_copy_implementations)] +pub struct FanotifyEvent(libc::fanotify_event_metadata); + impl FanotifyEvent { /// Version number for the structure. It must be compared to /// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime @@ -513,28 +596,33 @@ impl Fanotify { let info_record = match header.info_type { libc::FAN_EVENT_INFO_TYPE_FID => { - let event_fid = self + let record = self .get_struct::( &buffer, current_event_offset, ); - Some(FanotifyInfoRecord::Fid(event_fid)) + Some(FanotifyInfoRecord::Fid(FanotifyFidRecord(record))) } libc::FAN_EVENT_INFO_TYPE_ERROR => { - let error_fid = self + let record = self .get_struct::( &buffer, current_event_offset, ); - Some(FanotifyInfoRecord::Error(error_fid)) + + Some(FanotifyInfoRecord::Error(FanotifyErrorRecord( + record, + ))) } libc::FAN_EVENT_INFO_TYPE_PIDFD => { - let error_fid = self + let record = self .get_struct::( &buffer, current_event_offset, ); - Some(FanotifyInfoRecord::Pidfd(error_fid)) + Some(FanotifyInfoRecord::Pidfd(FanotifyPidfdRecord( + record, + ))) } // Ignore unsupported events _ => None, From ea62c960431267eb0eb062ca368f196e7da50fab Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 1 Dec 2024 07:44:49 +0000 Subject: [PATCH 03/13] Added additional match cases for all FAN_EVENT_INFO_TYPE_NAME info_types --- src/sys/fanotify.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index 5c988cc1d0..b1d6570088 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -251,7 +251,7 @@ impl FanotifyFidRecord { /// differs depending on the host system. Please read the statfs(2) documentation /// for more information: /// https://man7.org/linux/man-pages/man2/statfs.2.html#VERSIONS - pub fn fsid(&self) -> libc::__kernel_fsid_t { + pub fn filesystem_id(&self) -> libc::__kernel_fsid_t { self.0.fsid } @@ -595,7 +595,14 @@ impl Fanotify { ); let info_record = match header.info_type { - libc::FAN_EVENT_INFO_TYPE_FID => { + // FanotifyFidRecord can be returned for any of the following info_type. + // This isn't found in the fanotify(7) documentation, but the fanotify_init(2) documentation + // https://man7.org/linux/man-pages/man2/fanotify_init.2.html + libc::FAN_EVENT_INFO_TYPE_FID + | libc::FAN_EVENT_INFO_TYPE_DFID + | libc::FAN_EVENT_INFO_TYPE_DFID_NAME + | libc::FAN_EVENT_INFO_TYPE_NEW_DFID_NAME + | libc::FAN_EVENT_INFO_TYPE_OLD_DFID_NAME => { let record = self .get_struct::( &buffer, From 3af1c4c489dcb5a966dd21bd6be36313aa246433 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 1 Dec 2024 07:53:19 +0000 Subject: [PATCH 04/13] Added changelog file --- changelog/2552.added.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/2552.added.md diff --git a/changelog/2552.added.md b/changelog/2552.added.md new file mode 100644 index 0000000000..8d70b81d42 --- /dev/null +++ b/changelog/2552.added.md @@ -0,0 +1,3 @@ +Adds support for nix to receive additional Fanotify information records (such as libc::fanotify_event_info_fid, libc::fanotify_event_info_error and libc::fanotify_event_info_pidfd) +Adds abstractions over the new fanotify structs. +Adds new InitFlags to allow receiving these new information records. \ No newline at end of file From d10b82eccc579fd35e3e1e68452447722e5ef2eb Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 1 Dec 2024 08:48:16 +0000 Subject: [PATCH 05/13] Fixed formatting issues, added conditional compilation to fix musl and armv7 issues --- src/sys/fanotify.rs | 13 +++++++++++-- test/sys/test_fanotify.rs | 29 +++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index b1d6570088..fa5bf22497 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -250,7 +250,7 @@ impl FanotifyFidRecord { /// The filesystem id where this event occurred. The value this method returns /// differs depending on the host system. Please read the statfs(2) documentation /// for more information: - /// https://man7.org/linux/man-pages/man2/statfs.2.html#VERSIONS + /// pub fn filesystem_id(&self) -> libc::__kernel_fsid_t { self.0.fsid } @@ -258,7 +258,7 @@ impl FanotifyFidRecord { /// The file handle for the filesystem object where the event occurred. The handle is /// represented as a 0-length u8 array, but it actually points to variable-length /// file_handle struct.For more information: - /// https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html + /// pub fn handle(&self) -> [u8; 0] { self.0.handle } @@ -270,8 +270,10 @@ impl FanotifyFidRecord { #[derive(Debug, Eq, Hash, PartialEq)] #[repr(transparent)] #[allow(missing_copy_implementations)] +#[cfg(target_env = "gnu")] pub struct FanotifyErrorRecord(libc::fanotify_event_info_error); +#[cfg(target_env = "gnu")] impl FanotifyErrorRecord { /// Errno of the FAN_FS_ERROR that occurred. pub fn err(&self) -> Errno { @@ -292,8 +294,10 @@ impl FanotifyErrorRecord { #[derive(Debug, Eq, Hash, PartialEq)] #[repr(transparent)] #[allow(missing_copy_implementations)] +#[cfg(target_env = "gnu")] pub struct FanotifyPidfdRecord(libc::fanotify_event_info_pidfd); +#[cfg(target_env = "gnu")] impl FanotifyPidfdRecord { /// The process file descriptor that refers to the process responsible for /// generating this event. If the underlying pidfd_create fails, `None` is returned. @@ -310,6 +314,7 @@ impl FanotifyPidfdRecord { } } +#[cfg(target_env = "gnu")] impl Drop for FanotifyPidfdRecord { fn drop(&mut self) { if self.0.pidfd == libc::FAN_NOFD { @@ -339,12 +344,14 @@ pub enum FanotifyInfoRecord { /// A [`libc::fanotify_event_info_error`] event was recieved. /// This occurs when a FAN_FS_ERROR occurs, indicating an error with /// the watch filesystem object. (such as a bad file or bad link lookup) + #[cfg(target_env = "gnu")] Error(FanotifyErrorRecord), /// A [`libc::fanotify_event_info_pidfd`] event was recieved, usually as /// a result of passing [`InitFlags::FAN_REPORT_PIDFD`] into [`Fanotify::init`]. /// The containing struct includes a `pidfd` for reliably determining /// whether the process responsible for generating an event has been recycled or terminated + #[cfg(target_env = "gnu")] Pidfd(FanotifyPidfdRecord), } @@ -610,6 +617,7 @@ impl Fanotify { ); Some(FanotifyInfoRecord::Fid(FanotifyFidRecord(record))) } + #[cfg(target_env = "gnu")] libc::FAN_EVENT_INFO_TYPE_ERROR => { let record = self .get_struct::( @@ -621,6 +629,7 @@ impl Fanotify { record, ))) } + #[cfg(target_env = "gnu")] libc::FAN_EVENT_INFO_TYPE_PIDFD => { let record = self .get_struct::( diff --git a/test/sys/test_fanotify.rs b/test/sys/test_fanotify.rs index 91a092f85e..62175eba73 100644 --- a/test/sys/test_fanotify.rs +++ b/test/sys/test_fanotify.rs @@ -2,8 +2,8 @@ use crate::*; use nix::errno::Errno; use nix::fcntl::AT_FDCWD; use nix::sys::fanotify::{ - EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags, - Response, FanotifyInfoRecord + EventFFlags, Fanotify, FanotifyInfoRecord, FanotifyResponse, InitFlags, + MarkFlags, MaskFlags, Response, }; use std::fs::{read_link, read_to_string, File, OpenOptions}; use std::io::ErrorKind; @@ -86,9 +86,11 @@ fn test_fanotify_notifications() { } fn test_fanotify_notifications_with_info_records() { - let group = - Fanotify::init(InitFlags::FAN_CLASS_NOTIF | InitFlags::FAN_REPORT_FID, EventFFlags::O_RDONLY) - .unwrap(); + let group = Fanotify::init( + InitFlags::FAN_CLASS_NOTIF | InitFlags::FAN_REPORT_FID, + EventFFlags::O_RDONLY, + ) + .unwrap(); let tempdir = tempfile::tempdir().unwrap(); let tempfile = tempdir.path().join("test"); OpenOptions::new() @@ -115,7 +117,11 @@ fn test_fanotify_notifications_with_info_records() { let mut events = group.read_events_with_info_records().unwrap(); assert_eq!(events.len(), 1, "should have read exactly one event"); let (event, info_records) = events.pop().unwrap(); - assert_eq!(info_records.len(), 1, "should have read exactly one info record"); + assert_eq!( + info_records.len(), + 1, + "should have read exactly one info record" + ); assert!(event.check_version()); assert_eq!( event.mask(), @@ -124,7 +130,10 @@ fn test_fanotify_notifications_with_info_records() { | MaskFlags::FAN_CLOSE_WRITE ); - assert!(matches!(info_records[0], FanotifyInfoRecord::Fid { .. }), "info record should be an fid record"); + assert!( + matches!(info_records[0], FanotifyInfoRecord::Fid { .. }), + "info record should be an fid record" + ); // read test file { @@ -136,7 +145,11 @@ fn test_fanotify_notifications_with_info_records() { let mut events = group.read_events_with_info_records().unwrap(); assert_eq!(events.len(), 1, "should have read exactly one event"); let (event, info_records) = events.pop().unwrap(); - assert_eq!(info_records.len(), 1, "should have read exactly one info record"); + assert_eq!( + info_records.len(), + 1, + "should have read exactly one info record" + ); assert!(event.check_version()); assert_eq!( event.mask(), From 0afe35eab24f3821bf9d19b173b0790824c5654d Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 1 Dec 2024 08:51:06 +0000 Subject: [PATCH 06/13] Fixed clippy warning --- src/sys/fanotify.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index fa5bf22497..3ad662de8f 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -500,7 +500,7 @@ impl Fanotify { fn get_struct(&self, buffer: &[u8; 4096], offset: usize) -> T { let struct_size = size_of::(); - let struct_obj = unsafe { + unsafe { let mut struct_obj = MaybeUninit::::uninit(); std::ptr::copy_nonoverlapping( buffer.as_ptr().add(offset), @@ -508,9 +508,7 @@ impl Fanotify { (4096 - offset).min(struct_size), ); struct_obj.assume_init() - }; - - struct_obj + } } /// Read incoming events from the fanotify group. From e99ab6b3a35b6552431092702178d7e17aabbe82 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:45:35 +0800 Subject: [PATCH 07/13] Fixed event length miscalculation causing infinite loop --- src/sys/fanotify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index 3ad662de8f..afa054aa77 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -588,7 +588,7 @@ impl Fanotify { metadata.assume_init() }; - let mut remaining_len = metadata.event_len; + let mut remaining_len = metadata.event_len - metadata_size as u32; let mut info_records = Vec::new(); let mut current_event_offset = offset + metadata_size; From 31414ce2c5c0af8e72874654690b717d1306e1bc Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:36:04 +0800 Subject: [PATCH 08/13] Updated fanotify.rs to store file_handle separately --- src/sys/fanotify.rs | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index afa054aa77..d802d592b5 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -240,11 +240,22 @@ pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION; /// Abstract over [`libc::fanotify_event_info_fid`], which represents an /// information record received via [`Fanotify::read_events_with_info_records`]. -// Is not Clone due to fd field, to avoid use-after-close scenarios. #[derive(Debug, Eq, Hash, PartialEq)] #[repr(transparent)] #[allow(missing_copy_implementations)] -pub struct FanotifyFidRecord(libc::fanotify_event_info_fid); +pub struct LibcFanotifyFidRecord(libc::fanotify_event_info_fid); + +/// Extends LibcFanotifyFidRecord to include file_handle bytes. +/// This allows Rust to move the record around in memory and not lose the file_handle +/// as the libc::fanotify_event_info_fid does not include any of the file_handle bytes. +// Is not Clone due to fd field, to avoid use-after-close scenarios. +#[derive(Debug, Eq, Hash, PartialEq)] +#[repr(C)] +#[allow(missing_copy_implementations)] +pub struct FanotifyFidRecord { + record: LibcFanotifyFidRecord, + handle_bytes: *const u8, +} impl FanotifyFidRecord { /// The filesystem id where this event occurred. The value this method returns @@ -252,15 +263,22 @@ impl FanotifyFidRecord { /// for more information: /// pub fn filesystem_id(&self) -> libc::__kernel_fsid_t { - self.0.fsid + self.record.0.fsid } /// The file handle for the filesystem object where the event occurred. The handle is /// represented as a 0-length u8 array, but it actually points to variable-length /// file_handle struct.For more information: /// - pub fn handle(&self) -> [u8; 0] { - self.0.handle + pub fn handle(&self) -> *const u8 { + self.handle_bytes + } + + /// The specific info_type for this Fid Record. Fanotify can return an Fid Record + /// with many different possible info_types. The info_type is not always necessary + /// but can be useful for connecting similar events together (like a FAN_RENAME) + pub fn info_type(&self) -> u8 { + self.record.0.hdr.info_type } } @@ -613,7 +631,18 @@ impl Fanotify { &buffer, current_event_offset, ); - Some(FanotifyInfoRecord::Fid(FanotifyFidRecord(record))) + + let record_ptr: *const libc::fanotify_event_info_fid = unsafe { + buffer.as_ptr().add(current_event_offset) + as *const libc::fanotify_event_info_fid + }; + + let file_handle_ptr = unsafe { record_ptr.add(1) as *const u8 }; + + Some(FanotifyInfoRecord::Fid(FanotifyFidRecord { + record: LibcFanotifyFidRecord(record), + handle_bytes: file_handle_ptr, + })) } #[cfg(target_env = "gnu")] libc::FAN_EVENT_INFO_TYPE_ERROR => { From 57134c2fa501c9a3196b3b4412022e81acb547ef Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:59:32 +0800 Subject: [PATCH 09/13] Removed false clippy cast_ptr_alignment issue --- src/sys/fanotify.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index d802d592b5..a152d7902a 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -583,6 +583,7 @@ impl Fanotify { /// In particular, `EAGAIN` is returned when no event is available on a /// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`, /// thus making this method nonblocking. + #[allow(clippy::cast_ptr_alignment)] // False positive pub fn read_events_with_info_records( &self, ) -> Result)>> { From b283943b29f1122e5f9f34b9d496ceb5bc7e7014 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:03:24 +0800 Subject: [PATCH 10/13] Added enum for FanotifyFidRecord event types, fixed comments, header now reads instead of copys, file_handle no longer returns pointer but returns [u8; 128] --- src/sys/fanotify.rs | 71 ++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index a152d7902a..60b66d8cc3 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -114,15 +114,15 @@ libc_bitflags! { /// requires the `CAP_SYS_ADMIN` capability. FAN_UNLIMITED_MARKS; - /// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15. + /// Make [`FanotifyEvent::pid()`] return pidfd. Since Linux 5.15. FAN_REPORT_PIDFD; - /// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20. + /// Make [`FanotifyEvent::pid()`] return thread id. Since Linux 4.20. FAN_REPORT_TID; /// Allows the receipt of events which contain additional information /// about the underlying filesystem object correlated to an event. /// - /// This will make `FanotifyEvent::fd` return `FAN_NOFD`. + /// This will make [`FanotifyEvent::fd()`] return `None`. /// This should be used with `Fanotify::read_events_with_info_records` to /// recieve `FanotifyInfoRecord::Fid` info records. /// Since Linux 5.1 @@ -131,7 +131,7 @@ libc_bitflags! { /// Allows the receipt of events which contain additional information /// about the underlying filesystem object correlated to an event. /// - /// This will make `FanotifyEvent::fd` return `FAN_NOFD`. + /// This will make [`FanotifyEvent::fd()`] return `None`. /// This should be used with `Fanotify::read_events_with_info_records` to /// recieve `FanotifyInfoRecord::Fid` info records. /// @@ -235,9 +235,26 @@ libc_bitflags! { } } +libc_enum! { + /// All possible Fanotify event types that result in a FanotifyFidRecord + #[repr(u8)] + #[non_exhaustive] + pub enum FanotifyFidEventInfoType { + FAN_EVENT_INFO_TYPE_FID, + FAN_EVENT_INFO_TYPE_DFID, + FAN_EVENT_INFO_TYPE_DFID_NAME, + FAN_EVENT_INFO_TYPE_OLD_DFID_NAME, + FAN_EVENT_INFO_TYPE_NEW_DFID_NAME, + } + impl TryFrom +} + /// Compile version number of fanotify API. pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION; +/// Maximum file_handle size +pub const MAX_HANDLE_SZ: usize = 128; + /// Abstract over [`libc::fanotify_event_info_fid`], which represents an /// information record received via [`Fanotify::read_events_with_info_records`]. #[derive(Debug, Eq, Hash, PartialEq)] @@ -254,7 +271,7 @@ pub struct LibcFanotifyFidRecord(libc::fanotify_event_info_fid); #[allow(missing_copy_implementations)] pub struct FanotifyFidRecord { record: LibcFanotifyFidRecord, - handle_bytes: *const u8, + file_handle_bytes: [u8; MAX_HANDLE_SZ], } impl FanotifyFidRecord { @@ -270,15 +287,15 @@ impl FanotifyFidRecord { /// represented as a 0-length u8 array, but it actually points to variable-length /// file_handle struct.For more information: /// - pub fn handle(&self) -> *const u8 { - self.handle_bytes + pub fn handle(&self) -> [u8; MAX_HANDLE_SZ] { + self.file_handle_bytes } /// The specific info_type for this Fid Record. Fanotify can return an Fid Record /// with many different possible info_types. The info_type is not always necessary /// but can be useful for connecting similar events together (like a FAN_RENAME) - pub fn info_type(&self) -> u8 { - self.record.0.hdr.info_type + pub fn info_type(&self) -> FanotifyFidEventInfoType { + FanotifyFidEventInfoType::try_from(self.record.0.hdr.info_type).unwrap() } } @@ -348,10 +365,9 @@ impl Drop for FanotifyPidfdRecord { /// After a [`libc::fanotify_event_metadata`], there can be 0 or more event_info /// structs depending on which InitFlags were used in [`Fanotify::init`]. // Is not Clone due to pidfd in `libc::fanotify_event_info_pidfd` -// Other fanotify_event_info records are not implemented as they don't exist in -// the libc crate yet. #[derive(Debug, Eq, Hash, PartialEq)] #[allow(missing_copy_implementations)] +#[non_exhaustive] pub enum FanotifyInfoRecord { /// A [`libc::fanotify_event_info_fid`] event was recieved, usually as /// a result of passing [`InitFlags::FAN_REPORT_FID`] or [`InitFlags::FAN_REPORT_DIR_FID`] @@ -612,13 +628,11 @@ impl Fanotify { let mut current_event_offset = offset + metadata_size; while remaining_len > 0 { - let header = self - .get_struct::( - &buffer, - current_event_offset, - ); + let header_info_type = unsafe { buffer.as_ptr().add(current_event_offset).read() }; + // The +2 here represents the offset between the info_type and the length (which is 2 u8s apart) + let info_type_length = unsafe { buffer.as_ptr().add(current_event_offset + 2).read() }; - let info_record = match header.info_type { + let info_record = match header_info_type { // FanotifyFidRecord can be returned for any of the following info_type. // This isn't found in the fanotify(7) documentation, but the fanotify_init(2) documentation // https://man7.org/linux/man-pages/man2/fanotify_init.2.html @@ -638,11 +652,24 @@ impl Fanotify { as *const libc::fanotify_event_info_fid }; - let file_handle_ptr = unsafe { record_ptr.add(1) as *const u8 }; + let file_handle = unsafe { + let file_handle_ptr = record_ptr.add(1) as *const u8; + let mut file_handle = MaybeUninit::<[u8; MAX_HANDLE_SZ]>::uninit(); + + // Read the entire file_handle. The struct can be found here: + // https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html + let file_handle_length = size_of::() + size_of::() + file_handle_ptr.cast::().read() as usize; + std::ptr::copy_nonoverlapping( + file_handle_ptr, + file_handle.as_mut_ptr().cast(), + (file_handle_length).min(MAX_HANDLE_SZ), + ); + file_handle.assume_init() + }; Some(FanotifyInfoRecord::Fid(FanotifyFidRecord { record: LibcFanotifyFidRecord(record), - handle_bytes: file_handle_ptr, + file_handle_bytes: file_handle, })) } #[cfg(target_env = "gnu")] @@ -676,12 +703,10 @@ impl Fanotify { info_records.push(record); } - remaining_len -= header.len as u32; - current_event_offset += header.len as usize; + remaining_len -= info_type_length as u32; + current_event_offset += info_type_length as usize; } - // libc::fanotify_event_info_header - events.push((FanotifyEvent(metadata), info_records)); offset += metadata.event_len as usize; } From aa3aac38ab986a9baa082d33eb11610ba7877f1f Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:26:16 +0800 Subject: [PATCH 11/13] Added support for retrieving the appended name on an Fid Record --- src/sys/fanotify.rs | 75 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index 60b66d8cc3..a6ea696084 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -14,6 +14,7 @@ use crate::errno::Errno; use crate::fcntl::OFlag; use crate::unistd::{close, read, write}; use crate::{NixPath, Result}; +use std::ffi::CStr; use std::marker::PhantomData; use std::mem::{size_of, MaybeUninit}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; @@ -272,6 +273,7 @@ pub struct LibcFanotifyFidRecord(libc::fanotify_event_info_fid); pub struct FanotifyFidRecord { record: LibcFanotifyFidRecord, file_handle_bytes: [u8; MAX_HANDLE_SZ], + name: Option, } impl FanotifyFidRecord { @@ -293,10 +295,20 @@ impl FanotifyFidRecord { /// The specific info_type for this Fid Record. Fanotify can return an Fid Record /// with many different possible info_types. The info_type is not always necessary - /// but can be useful for connecting similar events together (like a FAN_RENAME) + /// but can be useful for connecting similar events together (like a FAN_RENAME) pub fn info_type(&self) -> FanotifyFidEventInfoType { FanotifyFidEventInfoType::try_from(self.record.0.hdr.info_type).unwrap() } + + /// The name attached to the end of this Fid Record. This will only contain a value + /// if the info_type is expected to return a name (like `FanotifyFidEventInfoType::FAN_EVENT_INFO_TYPE_DFID_NAME`) + pub fn name(&self) -> Option<&str> { + if let Some(name) = self.name.as_ref() { + Some(name) + } else { + None + } + } } /// Abstract over [`libc::fanotify_event_info_error`], which represents an @@ -599,7 +611,7 @@ impl Fanotify { /// In particular, `EAGAIN` is returned when no event is available on a /// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`, /// thus making this method nonblocking. - #[allow(clippy::cast_ptr_alignment)] // False positive + #[allow(clippy::cast_ptr_alignment)] // False positive pub fn read_events_with_info_records( &self, ) -> Result)>> { @@ -628,9 +640,12 @@ impl Fanotify { let mut current_event_offset = offset + metadata_size; while remaining_len > 0 { - let header_info_type = unsafe { buffer.as_ptr().add(current_event_offset).read() }; + let header_info_type = + unsafe { buffer.as_ptr().add(current_event_offset).read() }; // The +2 here represents the offset between the info_type and the length (which is 2 u8s apart) - let info_type_length = unsafe { buffer.as_ptr().add(current_event_offset + 2).read() }; + let info_type_length = unsafe { + buffer.as_ptr().add(current_event_offset + 2).read() + }; let info_record = match header_info_type { // FanotifyFidRecord can be returned for any of the following info_type. @@ -647,18 +662,24 @@ impl Fanotify { current_event_offset, ); - let record_ptr: *const libc::fanotify_event_info_fid = unsafe { - buffer.as_ptr().add(current_event_offset) - as *const libc::fanotify_event_info_fid + let file_handle_ptr = unsafe { + (buffer.as_ptr().add(current_event_offset) + as *const libc::fanotify_event_info_fid) + .add(1) as *const u8 + }; + + // Read the entire file_handle. The struct can be found here: + // https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html + let file_handle_length = unsafe { + size_of::() + + size_of::() + + file_handle_ptr.cast::().read() as usize }; let file_handle = unsafe { - let file_handle_ptr = record_ptr.add(1) as *const u8; - let mut file_handle = MaybeUninit::<[u8; MAX_HANDLE_SZ]>::uninit(); + let mut file_handle = + MaybeUninit::<[u8; MAX_HANDLE_SZ]>::uninit(); - // Read the entire file_handle. The struct can be found here: - // https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html - let file_handle_length = size_of::() + size_of::() + file_handle_ptr.cast::().read() as usize; std::ptr::copy_nonoverlapping( file_handle_ptr, file_handle.as_mut_ptr().cast(), @@ -667,9 +688,32 @@ impl Fanotify { file_handle.assume_init() }; + let name: Option = match header_info_type { + libc::FAN_EVENT_INFO_TYPE_DFID_NAME + | libc::FAN_EVENT_INFO_TYPE_NEW_DFID_NAME + | libc::FAN_EVENT_INFO_TYPE_OLD_DFID_NAME => unsafe { + let name_ptr = + file_handle_ptr.add(file_handle_length); + if !name_ptr.is_null() { + let name_as_c_str = + CStr::from_ptr(name_ptr.cast()) + .to_str(); + if let Ok(name) = name_as_c_str { + Some(name.to_owned()) + } else { + None + } + } else { + None + } + }, + _ => None, + }; + Some(FanotifyInfoRecord::Fid(FanotifyFidRecord { record: LibcFanotifyFidRecord(record), file_handle_bytes: file_handle, + name, })) } #[cfg(target_env = "gnu")] @@ -752,8 +796,7 @@ impl AsFd for Fanotify { } impl AsRawFd for Fanotify { - fn as_raw_fd(&self) -> RawFd - { + fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } } @@ -771,8 +814,6 @@ impl Fanotify { /// /// `OwnedFd` is a valid `Fanotify`. pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self { - Self { - fd - } + Self { fd } } } From dd9b49c7a353bc17b99fcfbff4c3d38cd1aa5967 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Thu, 16 Jan 2025 02:11:19 +0800 Subject: [PATCH 12/13] Fixed clippy errors --- src/sys/fanotify.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index a6ea696084..ac4c403185 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -241,10 +241,23 @@ libc_enum! { #[repr(u8)] #[non_exhaustive] pub enum FanotifyFidEventInfoType { + /// This event occurs if FAN_REPORT_FID was passed into [`Fanotify::init()`] FAN_EVENT_INFO_TYPE_FID, + /// This event occurs if FAN_REPORT_DIR_FID was passed into [`Fanotify::init()`]. + /// For events that occur on a non-directory object, this record includes a file handle + /// that identifies the parent directory filesystem object. FAN_EVENT_INFO_TYPE_DFID, + /// This event occurs if FAN_REPORT_DIR_FID and FAN_REPORT_NAME was passed into [`Fanotify::init()`]. + /// This record is identical to FAN_EVENT_INFO_TYPE_DFID, except that [`FanotifyFidRecord::name()`] + /// will return the name of the target filesystem object. FAN_EVENT_INFO_TYPE_DFID_NAME, + /// This event occurs if a FAN_RENAME event occurs and FAN_REPORT_DIR_FID and FAN_REPORT_NAME was passed into [`Fanotify::init()`]. + /// This record identifies the old parent directory of the renamed directory object. + /// [`FanotifyFidRecord::name()`] will return the name of the old parent directory. FAN_EVENT_INFO_TYPE_OLD_DFID_NAME, + /// This event occurs if a FAN_RENAME event occurs and FAN_REPORT_DIR_FID and FAN_REPORT_NAME was passed into [`Fanotify::init()`]. + /// This record identifies the new parent directory of the renamed directory object. + /// [`FanotifyFidRecord::name()`] will return the name of the new parent directory. FAN_EVENT_INFO_TYPE_NEW_DFID_NAME, } impl TryFrom From 0e3124a9f370912ee4e2c6eaf9d804d6d7cf8a34 Mon Sep 17 00:00:00 2001 From: Carl Voller <27472988+carlvoller@users.noreply.github.com> Date: Sun, 19 Jan 2025 14:01:27 +0800 Subject: [PATCH 13/13] Added Copy and Clone to structs that can have it --- src/sys/fanotify.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index ac4c403185..ceb1ba87a5 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -271,18 +271,15 @@ pub const MAX_HANDLE_SZ: usize = 128; /// Abstract over [`libc::fanotify_event_info_fid`], which represents an /// information record received via [`Fanotify::read_events_with_info_records`]. -#[derive(Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] -#[allow(missing_copy_implementations)] pub struct LibcFanotifyFidRecord(libc::fanotify_event_info_fid); /// Extends LibcFanotifyFidRecord to include file_handle bytes. /// This allows Rust to move the record around in memory and not lose the file_handle /// as the libc::fanotify_event_info_fid does not include any of the file_handle bytes. -// Is not Clone due to fd field, to avoid use-after-close scenarios. -#[derive(Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[repr(C)] -#[allow(missing_copy_implementations)] pub struct FanotifyFidRecord { record: LibcFanotifyFidRecord, file_handle_bytes: [u8; MAX_HANDLE_SZ], @@ -326,10 +323,8 @@ impl FanotifyFidRecord { /// Abstract over [`libc::fanotify_event_info_error`], which represents an /// information record received via [`Fanotify::read_events_with_info_records`]. -// Is not Clone due to fd field, to avoid use-after-close scenarios. -#[derive(Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] -#[allow(missing_copy_implementations)] #[cfg(target_env = "gnu")] pub struct FanotifyErrorRecord(libc::fanotify_event_info_error); @@ -350,7 +345,7 @@ impl FanotifyErrorRecord { /// Abstract over [`libc::fanotify_event_info_pidfd`], which represents an /// information record received via [`Fanotify::read_events_with_info_records`]. -// Is not Clone due to fd field, to avoid use-after-close scenarios. +// Is not Clone due to pidfd field, to avoid use-after-close scenarios. #[derive(Debug, Eq, Hash, PartialEq)] #[repr(transparent)] #[allow(missing_copy_implementations)]