@@ -118,6 +118,46 @@ libc_bitflags! {
118
118
FAN_REPORT_PIDFD ;
119
119
/// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
120
120
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 ;
121
161
}
122
162
}
123
163
@@ -206,6 +246,33 @@ pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
206
246
#[ allow( missing_copy_implementations) ]
207
247
pub struct FanotifyEvent ( libc:: fanotify_event_metadata ) ;
208
248
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
+
209
276
impl FanotifyEvent {
210
277
/// Version number for the structure. It must be compared to
211
278
/// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
@@ -341,6 +408,21 @@ impl Fanotify {
341
408
Errno :: result ( res) . map ( |_| ( ) )
342
409
}
343
410
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
+
344
426
/// Read incoming events from the fanotify group.
345
427
///
346
428
/// Returns a Result containing either a `Vec` of events on success or errno
@@ -382,6 +464,99 @@ impl Fanotify {
382
464
Ok ( events)
383
465
}
384
466
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
+
385
560
/// Write an event response on the fanotify group.
386
561
///
387
562
/// Returns a Result containing either `()` on success or errno otherwise.
@@ -443,4 +618,4 @@ impl Fanotify {
443
618
fd
444
619
}
445
620
}
446
- }
621
+ }
0 commit comments