• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Monitoring API for filesystem events.
2 //!
3 //! Fanotify is a Linux-only API to monitor filesystems events.
4 //!
5 //! Additional capabilities compared to the `inotify` API include the ability to
6 //! monitor all of the objects in a mounted filesystem, the ability to make
7 //! access permission decisions, and the possibility to read or modify files
8 //! before access by other applications.
9 //!
10 //! For more documentation, please read
11 //! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
12 
13 use crate::errno::Errno;
14 use crate::fcntl::{at_rawfd, OFlag};
15 use crate::unistd::{close, read, write};
16 use crate::{NixPath, Result};
17 use std::marker::PhantomData;
18 use std::mem::{size_of, MaybeUninit};
19 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
20 use std::ptr;
21 
22 libc_bitflags! {
23     /// Mask for defining which events shall be listened with
24     /// [`fanotify_mark`](fn.fanotify_mark.html) and for querying notifications.
25     pub struct MaskFlags: u64 {
26         /// File was accessed.
27         FAN_ACCESS;
28         /// File was modified.
29         FAN_MODIFY;
30         /// Metadata has changed. Since Linux 5.1.
31         FAN_ATTRIB;
32         /// Writtable file was closed.
33         FAN_CLOSE_WRITE;
34         /// Unwrittable file was closed.
35         FAN_CLOSE_NOWRITE;
36         /// File was opened.
37         FAN_OPEN;
38         /// File was moved from X. Since Linux 5.1.
39         FAN_MOVED_FROM;
40         /// File was moved to Y. Since Linux 5.1.
41         FAN_MOVED_TO;
42         /// Subfile was created. Since Linux 5.1.
43         FAN_CREATE;
44         /// Subfile was deleted. Since Linux 5.1.
45         FAN_DELETE;
46         /// Self was deleted. Since Linux 5.1.
47         FAN_DELETE_SELF;
48         /// Self was moved. Since Linux 5.1.
49         FAN_MOVE_SELF;
50         /// File was opened for execution. Since Linux 5.0.
51         FAN_OPEN_EXEC;
52 
53         /// Event queue overflowed.
54         FAN_Q_OVERFLOW;
55         /// Filesystem error. Since Linux 5.16.
56         FAN_FS_ERROR;
57 
58         /// Permission to open file was requested.
59         FAN_OPEN_PERM;
60         /// Permission to access file was requested.
61         FAN_ACCESS_PERM;
62         /// Permission to open file for execution was requested. Since Linux
63         /// 5.0.
64         FAN_OPEN_EXEC_PERM;
65 
66         /// Interested in child events.
67         FAN_EVENT_ON_CHILD;
68 
69         /// File was renamed. Since Linux 5.17.
70         FAN_RENAME;
71 
72         /// Event occurred against dir.
73         FAN_ONDIR;
74 
75         /// Combination of `FAN_CLOSE_WRITE` and `FAN_CLOSE_NOWRITE`.
76         FAN_CLOSE;
77         /// Combination of `FAN_MOVED_FROM` and `FAN_MOVED_TO`.
78         FAN_MOVE;
79     }
80 }
81 
82 libc_bitflags! {
83     /// Configuration options for [`fanotify_init`](fn.fanotify_init.html).
84     pub struct InitFlags: libc::c_uint {
85         /// Close-on-exec flag set on the file descriptor.
86         FAN_CLOEXEC;
87         /// Nonblocking flag set on the file descriptor.
88         FAN_NONBLOCK;
89 
90         /// Receipt of events notifications.
91         FAN_CLASS_NOTIF;
92         /// Receipt of events for permission decisions, after they contain final
93         /// data.
94         FAN_CLASS_CONTENT;
95         /// Receipt of events for permission decisions, before they contain
96         /// final data.
97         FAN_CLASS_PRE_CONTENT;
98 
99         /// Remove the limit on the number of events in the event queue.
100         ///
101         /// Prior to Linux kernel 5.13, this limit was hardcoded to 16384. After
102         /// 5.13, one can change it via file `/proc/sys/fs/fanotify/max_queued_events`.
103         ///
104         /// See `fanotify(7)` for details about this limit. Use of this flag
105         /// requires the `CAP_SYS_ADMIN` capability.
106         FAN_UNLIMITED_QUEUE;
107         /// Remove the limit on the number of fanotify marks per user.
108         ///
109         /// Prior to Linux kernel 5.13, this limit was hardcoded to 8192 (per
110         /// group, not per user). After 5.13, one can change it via file
111         /// `/proc/sys/fs/fanotify/max_user_marks`.
112         ///
113         /// See `fanotify(7)` for details about this limit. Use of this flag
114         /// requires the `CAP_SYS_ADMIN` capability.
115         FAN_UNLIMITED_MARKS;
116 
117         /// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15.
118         FAN_REPORT_PIDFD;
119         /// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
120         FAN_REPORT_TID;
121     }
122 }
123 
124 libc_bitflags! {
125     /// File status flags for fanotify events file descriptors.
126     pub struct EventFFlags: libc::c_uint {
127         /// Read only access.
128         O_RDONLY as libc::c_uint;
129         /// Write only access.
130         O_WRONLY as libc::c_uint;
131         /// Read and write access.
132         O_RDWR as libc::c_uint;
133         /// Support for files exceeded 2 GB.
134         O_LARGEFILE as libc::c_uint;
135         /// Close-on-exec flag for the file descriptor. Since Linux 3.18.
136         O_CLOEXEC as libc::c_uint;
137         /// Append mode for the file descriptor.
138         O_APPEND as libc::c_uint;
139         /// Synchronized I/O data integrity completion.
140         O_DSYNC as libc::c_uint;
141         /// No file last access time update.
142         O_NOATIME as libc::c_uint;
143         /// Nonblocking mode for the file descriptor.
144         O_NONBLOCK as libc::c_uint;
145         /// Synchronized I/O file integrity completion.
146         O_SYNC as libc::c_uint;
147     }
148 }
149 
150 impl TryFrom<OFlag> for EventFFlags {
151     type Error = Errno;
152 
try_from(o_flag: OFlag) -> Result<Self>153     fn try_from(o_flag: OFlag) -> Result<Self> {
154         EventFFlags::from_bits(o_flag.bits() as u32).ok_or(Errno::EINVAL)
155     }
156 }
157 
158 impl From<EventFFlags> for OFlag {
from(event_f_flags: EventFFlags) -> Self159     fn from(event_f_flags: EventFFlags) -> Self {
160         OFlag::from_bits_retain(event_f_flags.bits() as i32)
161     }
162 }
163 
164 libc_bitflags! {
165     /// Configuration options for [`fanotify_mark`](fn.fanotify_mark.html).
166     pub struct MarkFlags: libc::c_uint {
167         /// Add the events to the marks.
168         FAN_MARK_ADD;
169         /// Remove the events to the marks.
170         FAN_MARK_REMOVE;
171         /// Don't follow symlinks, mark them.
172         FAN_MARK_DONT_FOLLOW;
173         /// Raise an error if filesystem to be marked is not a directory.
174         FAN_MARK_ONLYDIR;
175         /// Events added to or removed from the marks.
176         FAN_MARK_IGNORED_MASK;
177         /// Ignore mask shall survive modify events.
178         FAN_MARK_IGNORED_SURV_MODIFY;
179         /// Remove all marks.
180         FAN_MARK_FLUSH;
181         /// Do not pin inode object in the inode cache. Since Linux 5.19.
182         FAN_MARK_EVICTABLE;
183         /// Events added to or removed from the marks. Since Linux 6.0.
184         FAN_MARK_IGNORE;
185 
186         /// Default flag.
187         FAN_MARK_INODE;
188         /// Mark the mount specified by pathname.
189         FAN_MARK_MOUNT;
190         /// Mark the filesystem specified by pathname. Since Linux 4.20.
191         FAN_MARK_FILESYSTEM;
192 
193         /// Combination of `FAN_MARK_IGNORE` and `FAN_MARK_IGNORED_SURV_MODIFY`.
194         FAN_MARK_IGNORE_SURV;
195     }
196 }
197 
198 /// Compile version number of fanotify API.
199 pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
200 
201 /// Abstract over `libc::fanotify_event_metadata`, which represents an event
202 /// received via `Fanotify::read_events`.
203 // Is not Clone due to fd field, to avoid use-after-close scenarios.
204 #[derive(Debug, Eq, Hash, PartialEq)]
205 #[repr(transparent)]
206 #[allow(missing_copy_implementations)]
207 pub struct FanotifyEvent(libc::fanotify_event_metadata);
208 
209 impl FanotifyEvent {
210     /// Version number for the structure. It must be compared to
211     /// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
212     /// version does match. It can be done with the
213     /// `FanotifyEvent::check_version` method.
version(&self) -> u8214     pub fn version(&self) -> u8 {
215         self.0.vers
216     }
217 
218     /// Checks that compile fanotify API version is equal to the version of the
219     /// event.
check_version(&self) -> bool220     pub fn check_version(&self) -> bool {
221         self.version() == FANOTIFY_METADATA_VERSION
222     }
223 
224     /// Mask flags of the events.
mask(&self) -> MaskFlags225     pub fn mask(&self) -> MaskFlags {
226         MaskFlags::from_bits_truncate(self.0.mask)
227     }
228 
229     /// The file descriptor of the event. If the value is `None` when reading
230     /// from the fanotify group, this event is to notify that a group queue
231     /// overflow occured.
fd(&self) -> Option<BorrowedFd>232     pub fn fd(&self) -> Option<BorrowedFd> {
233         if self.0.fd == libc::FAN_NOFD {
234             None
235         } else {
236             // SAFETY: self.0.fd will be opened for the lifetime of `Self`,
237             // which is longer than the lifetime of the returned BorrowedFd, so
238             // it is safe.
239             Some(unsafe { BorrowedFd::borrow_raw(self.0.fd) })
240         }
241     }
242 
243     /// PID of the process that caused the event. TID in case flag
244     /// `FAN_REPORT_TID` was set at group initialization.
pid(&self) -> i32245     pub fn pid(&self) -> i32 {
246         self.0.pid
247     }
248 }
249 
250 impl Drop for FanotifyEvent {
drop(&mut self)251     fn drop(&mut self) {
252         if self.0.fd == libc::FAN_NOFD {
253             return;
254         }
255         let e = close(self.0.fd);
256         if !std::thread::panicking() && e == Err(Errno::EBADF) {
257             panic!("Closing an invalid file descriptor!");
258         };
259     }
260 }
261 
262 /// Abstraction over the structure to be sent to allow or deny a given event.
263 #[derive(Debug)]
264 #[repr(transparent)]
265 pub struct FanotifyResponse<'a> {
266     inner: libc::fanotify_response,
267     _borrowed_fd: PhantomData<BorrowedFd<'a>>,
268 }
269 
270 impl<'a> FanotifyResponse<'a> {
271     /// Create a new response.
new(fd: BorrowedFd<'a>, response: Response) -> Self272     pub fn new(fd: BorrowedFd<'a>, response: Response) -> Self {
273         Self {
274             inner: libc::fanotify_response {
275                 fd: fd.as_raw_fd(),
276                 response: response.bits(),
277             },
278             _borrowed_fd: PhantomData,
279         }
280     }
281 }
282 
283 libc_bitflags! {
284     /// Response to be wrapped in `FanotifyResponse` and sent to the `Fanotify`
285     /// group to allow or deny an event.
286     pub struct Response: u32 {
287         /// Allow the event.
288         FAN_ALLOW;
289         /// Deny the event.
290         FAN_DENY;
291     }
292 }
293 
294 /// A fanotify group. This is also a file descriptor that can feed to other
295 /// interfaces consuming file descriptors.
296 #[derive(Debug)]
297 pub struct Fanotify {
298     fd: OwnedFd,
299 }
300 
301 impl Fanotify {
302     /// Initialize a new fanotify group.
303     ///
304     /// Returns a Result containing a Fanotify instance.
305     ///
306     /// For more information, see [fanotify_init(2)](https://man7.org/linux/man-pages/man7/fanotify_init.2.html).
init( flags: InitFlags, event_f_flags: EventFFlags, ) -> Result<Fanotify>307     pub fn init(
308         flags: InitFlags,
309         event_f_flags: EventFFlags,
310     ) -> Result<Fanotify> {
311         let res = Errno::result(unsafe {
312             libc::fanotify_init(flags.bits(), event_f_flags.bits())
313         });
314         res.map(|fd| Fanotify {
315             fd: unsafe { OwnedFd::from_raw_fd(fd) },
316         })
317     }
318 
319     /// Add, remove, or modify an fanotify mark on a filesystem object.
320     /// If `dirfd` is `None`, `AT_FDCWD` is used.
321     ///
322     /// Returns a Result containing either `()` on success or errno otherwise.
323     ///
324     /// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
mark<P: ?Sized + NixPath>( &self, flags: MarkFlags, mask: MaskFlags, dirfd: Option<RawFd>, path: Option<&P>, ) -> Result<()>325     pub fn mark<P: ?Sized + NixPath>(
326         &self,
327         flags: MarkFlags,
328         mask: MaskFlags,
329         dirfd: Option<RawFd>,
330         path: Option<&P>,
331     ) -> Result<()> {
332         let res = crate::with_opt_nix_path(path, |p| unsafe {
333             libc::fanotify_mark(
334                 self.fd.as_raw_fd(),
335                 flags.bits(),
336                 mask.bits(),
337                 at_rawfd(dirfd),
338                 p,
339             )
340         })?;
341 
342         Errno::result(res).map(|_| ())
343     }
344 
345     /// Read incoming events from the fanotify group.
346     ///
347     /// Returns a Result containing either a `Vec` of events on success or errno
348     /// otherwise.
349     ///
350     /// # Errors
351     ///
352     /// Possible errors can be those that are explicitly listed in
353     /// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
354     /// addition to the possible errors caused by `read` call.
355     /// In particular, `EAGAIN` is returned when no event is available on a
356     /// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`,
357     /// thus making this method nonblocking.
read_events(&self) -> Result<Vec<FanotifyEvent>>358     pub fn read_events(&self) -> Result<Vec<FanotifyEvent>> {
359         let metadata_size = size_of::<libc::fanotify_event_metadata>();
360         const BUFSIZ: usize = 4096;
361         let mut buffer = [0u8; BUFSIZ];
362         let mut events = Vec::new();
363         let mut offset = 0;
364 
365         let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
366 
367         while (nread - offset) >= metadata_size {
368             let metadata = unsafe {
369                 let mut metadata =
370                     MaybeUninit::<libc::fanotify_event_metadata>::uninit();
371                 ptr::copy_nonoverlapping(
372                     buffer.as_ptr().add(offset),
373                     metadata.as_mut_ptr().cast(),
374                     (BUFSIZ - offset).min(metadata_size),
375                 );
376                 metadata.assume_init()
377             };
378 
379             events.push(FanotifyEvent(metadata));
380             offset += metadata.event_len as usize;
381         }
382 
383         Ok(events)
384     }
385 
386     /// Write an event response on the fanotify group.
387     ///
388     /// Returns a Result containing either `()` on success or errno otherwise.
389     ///
390     /// # Errors
391     ///
392     /// Possible errors can be those that are explicitly listed in
393     /// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
394     /// addition to the possible errors caused by `write` call.
395     /// In particular, `EAGAIN` or `EWOULDBLOCK` is returned when no event is
396     /// available on a group that has been initialized with the flag
397     /// `InitFlags::FAN_NONBLOCK`, thus making this method nonblocking.
write_response(&self, response: FanotifyResponse) -> Result<()>398     pub fn write_response(&self, response: FanotifyResponse) -> Result<()> {
399         write(self.fd.as_fd(), unsafe {
400             std::slice::from_raw_parts(
401                 (&response.inner as *const libc::fanotify_response).cast(),
402                 size_of::<libc::fanotify_response>(),
403             )
404         })?;
405         Ok(())
406     }
407 }
408 
409 impl FromRawFd for Fanotify {
from_raw_fd(fd: RawFd) -> Self410     unsafe fn from_raw_fd(fd: RawFd) -> Self {
411         Fanotify {
412             fd: unsafe { OwnedFd::from_raw_fd(fd) },
413         }
414     }
415 }
416 
417 impl AsFd for Fanotify {
as_fd(&'_ self) -> BorrowedFd<'_>418     fn as_fd(&'_ self) -> BorrowedFd<'_> {
419         self.fd.as_fd()
420     }
421 }
422