@@ -118,6 +118,46 @@ libc_bitflags! {
118118 FAN_REPORT_PIDFD ;
119119 /// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
120120 FAN_REPORT_TID ;
121+
122+ /// Allows the receipt of events which contain additional information
123+ /// about the underlying filesystem object correlated to an event.
124+ ///
125+ /// This will make `FanotifyEvent::fd` return `FAN_NOFD`.
126+ /// This should be used with `Fanotify::read_events_with_info_records` to
127+ /// recieve `FanotifyInfoRecord::Fid` info records.
128+ /// Since Linux 5.1
129+ FAN_REPORT_FID ;
130+
131+ /// Allows the receipt of events which contain additional information
132+ /// about the underlying filesystem object correlated to an event.
133+ ///
134+ /// This will make `FanotifyEvent::fd` return `FAN_NOFD`.
135+ /// This should be used with `Fanotify::read_events_with_info_records` to
136+ /// recieve `FanotifyInfoRecord::Fid` info records.
137+ ///
138+ /// An additional event of `FAN_EVENT_INFO_TYPE_DFID` will also be received,
139+ /// encapsulating information about the target directory (or parent directory of a file)
140+ /// Since Linux 5.9
141+ FAN_REPORT_DIR_FID ;
142+
143+ /// Events for fanotify groups initialized with this flag will contain additional
144+ /// information about the child correlated with directory entry modification events.
145+ /// This flag must be provided in conjunction with the flags `FAN_REPORT_FID`,
146+ /// `FAN_REPORT_DIR_FID` and `FAN_REPORT_NAME`.
147+ /// Since Linux 5.17
148+ FAN_REPORT_TARGET_FID ;
149+
150+ /// Events for fanotify groups initialized with this flag will contain additional
151+ /// information about the name of the directory entry correlated to an event. This
152+ /// flag must be provided in conjunction with the flag `FAN_REPORT_DIR_FID`.
153+ /// Since Linux 5.9
154+ FAN_REPORT_NAME ;
155+
156+ /// This is a synonym for `FAN_REPORT_DIR_FD | FAN_REPORT_NAME`.
157+ FAN_REPORT_DFID_NAME ;
158+
159+ /// This is a synonym for `FAN_REPORT_DIR_FD | FAN_REPORT_NAME | FAN_REPORT_TARGET_FID`.
160+ FAN_REPORT_DFID_NAME_TARGET ;
121161 }
122162}
123163
@@ -206,6 +246,33 @@ pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
206246#[ allow( missing_copy_implementations) ]
207247pub struct FanotifyEvent ( libc:: fanotify_event_metadata ) ;
208248
249+ /// After a [`libc::fanotify_event_metadata`], there can be 0 or more event_info
250+ /// structs depending on which InitFlags were used in [`Fanotify::init`].
251+ // Is not Clone due to pidfd in `libc::fanotify_event_info_pidfd`
252+ // Other fanotify_event_info records are not implemented as they don't exist in
253+ // the libc crate yet.
254+ #[ derive( Debug , Eq , Hash , PartialEq ) ]
255+ #[ allow( missing_copy_implementations) ]
256+ pub enum FanotifyInfoRecord {
257+ /// A [`libc::fanotify_event_info_fid`] event was recieved, usually as
258+ /// a result of passing [`InitFlags::FAN_REPORT_FID`] or [`InitFlags::FAN_REPORT_DIR_FID`]
259+ /// into [`Fanotify::init`]. The containing struct includes a `file_handle` for
260+ /// use with `open_by_handle_at(2)`.
261+ Fid ( libc:: fanotify_event_info_fid ) ,
262+
263+ /// A [`libc::FAN_FS_ERROR`] event was received. This event occurs when
264+ /// a filesystem event is detected. Only a single [`libc::FAN_FS_ERROR`] is
265+ /// stored per filesystem at once, extra error messages are suppressed and
266+ /// accounted for in the error_count field.
267+ Error ( libc:: fanotify_event_info_error ) ,
268+
269+ /// A [`libc::fanotify_event_info_pidfd`] event was recieved, usually as
270+ /// a result of passing [`InitFlags::FAN_REPORT_PIDFD`] into [`Fanotify::init`].
271+ /// The containing struct includes a `pidfd` for reliably determining
272+ /// whether the process responsible for generating an event has been recycled or terminated
273+ Pidfd ( libc:: fanotify_event_info_pidfd ) ,
274+ }
275+
209276impl FanotifyEvent {
210277 /// Version number for the structure. It must be compared to
211278 /// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
@@ -341,6 +408,21 @@ impl Fanotify {
341408 Errno :: result ( res) . map ( |_| ( ) )
342409 }
343410
411+ fn get_struct < T > ( & self , buffer : & [ u8 ; 4096 ] , offset : usize ) -> T {
412+ let struct_size = size_of :: < T > ( ) ;
413+ let struct_obj = unsafe {
414+ let mut struct_obj = MaybeUninit :: < T > :: uninit ( ) ;
415+ std:: ptr:: copy_nonoverlapping (
416+ buffer. as_ptr ( ) . add ( offset) ,
417+ struct_obj. as_mut_ptr ( ) . cast ( ) ,
418+ ( 4096 - offset) . min ( struct_size) ,
419+ ) ;
420+ struct_obj. assume_init ( )
421+ } ;
422+
423+ struct_obj
424+ }
425+
344426 /// Read incoming events from the fanotify group.
345427 ///
346428 /// Returns a Result containing either a `Vec` of events on success or errno
@@ -382,6 +464,99 @@ impl Fanotify {
382464 Ok ( events)
383465 }
384466
467+ /// Read incoming events and information records from the fanotify group.
468+ ///
469+ /// Returns a Result containing either a `Vec` of events and information records on success or errno
470+ /// otherwise.
471+ ///
472+ /// # Errors
473+ ///
474+ /// Possible errors can be those that are explicitly listed in
475+ /// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
476+ /// addition to the possible errors caused by `read` call.
477+ /// In particular, `EAGAIN` is returned when no event is available on a
478+ /// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`,
479+ /// thus making this method nonblocking.
480+ pub fn read_events_with_info_records (
481+ & self ,
482+ ) -> Result < Vec < ( FanotifyEvent , Vec < FanotifyInfoRecord > ) > > {
483+ let metadata_size = size_of :: < libc:: fanotify_event_metadata > ( ) ;
484+ const BUFSIZ : usize = 4096 ;
485+ let mut buffer = [ 0u8 ; BUFSIZ ] ;
486+ let mut events = Vec :: new ( ) ;
487+ let mut offset = 0 ;
488+
489+ let nread = read ( & self . fd , & mut buffer) ?;
490+
491+ while ( nread - offset) >= metadata_size {
492+ let metadata = unsafe {
493+ let mut metadata =
494+ MaybeUninit :: < libc:: fanotify_event_metadata > :: uninit ( ) ;
495+ std:: ptr:: copy_nonoverlapping (
496+ buffer. as_ptr ( ) . add ( offset) ,
497+ metadata. as_mut_ptr ( ) . cast ( ) ,
498+ ( BUFSIZ - offset) . min ( metadata_size) ,
499+ ) ;
500+ metadata. assume_init ( )
501+ } ;
502+
503+ let mut remaining_len = metadata. event_len ;
504+ let mut info_records = Vec :: new ( ) ;
505+ let mut current_event_offset = offset + metadata_size;
506+
507+ while remaining_len > 0 {
508+ let header = self
509+ . get_struct :: < libc:: fanotify_event_info_header > (
510+ & buffer,
511+ current_event_offset,
512+ ) ;
513+
514+ let info_record = match header. info_type {
515+ libc:: FAN_EVENT_INFO_TYPE_FID => {
516+ let event_fid = self
517+ . get_struct :: < libc:: fanotify_event_info_fid > (
518+ & buffer,
519+ current_event_offset,
520+ ) ;
521+ Some ( FanotifyInfoRecord :: Fid ( event_fid) )
522+ }
523+ libc:: FAN_EVENT_INFO_TYPE_ERROR => {
524+ let error_fid = self
525+ . get_struct :: < libc:: fanotify_event_info_error > (
526+ & buffer,
527+ current_event_offset,
528+ ) ;
529+ Some ( FanotifyInfoRecord :: Error ( error_fid) )
530+ }
531+ libc:: FAN_EVENT_INFO_TYPE_PIDFD => {
532+ let error_fid = self
533+ . get_struct :: < libc:: fanotify_event_info_pidfd > (
534+ & buffer,
535+ current_event_offset,
536+ ) ;
537+ Some ( FanotifyInfoRecord :: Pidfd ( error_fid) )
538+ }
539+ // Ignore unsupported events
540+ _ => None ,
541+ } ;
542+
543+ if let Some ( record) = info_record {
544+ info_records. push ( record) ;
545+ }
546+
547+ remaining_len -= header. len as u32 ;
548+ current_event_offset += header. len as usize ;
549+ }
550+
551+ // libc::fanotify_event_info_header
552+
553+ events. push ( ( FanotifyEvent ( metadata) , info_records) ) ;
554+ offset += metadata. event_len as usize ;
555+ }
556+
557+ Ok ( events)
558+ }
559+
385560 /// Write an event response on the fanotify group.
386561 ///
387562 /// Returns a Result containing either `()` on success or errno otherwise.
@@ -423,4 +598,4 @@ impl From<Fanotify> for OwnedFd {
423598 fn from ( value : Fanotify ) -> Self {
424599 value. fd
425600 }
426- }
601+ }
0 commit comments