summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys/fanotify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/sys/fanotify.rs')
-rw-r--r--third_party/rust/nix/src/sys/fanotify.rs416
1 files changed, 416 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/fanotify.rs b/third_party/rust/nix/src/sys/fanotify.rs
new file mode 100644
index 0000000000..e217406e02
--- /dev/null
+++ b/third_party/rust/nix/src/sys/fanotify.rs
@@ -0,0 +1,416 @@
+//! Monitoring API for filesystem events.
+//!
+//! Fanotify is a Linux-only API to monitor filesystems events.
+//!
+//! Additional capabilities compared to the `inotify` API include the ability to
+//! monitor all of the objects in a mounted filesystem, the ability to make
+//! access permission decisions, and the possibility to read or modify files
+//! before access by other applications.
+//!
+//! For more documentation, please read
+//! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
+
+use crate::errno::Errno;
+use crate::fcntl::{at_rawfd, OFlag};
+use crate::unistd::{close, read, write};
+use crate::{NixPath, Result};
+use std::marker::PhantomData;
+use std::mem::{size_of, MaybeUninit};
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
+use std::ptr;
+
+libc_bitflags! {
+ /// Mask for defining which events shall be listened with
+ /// [`fanotify_mark`](fn.fanotify_mark.html) and for querying notifications.
+ pub struct MaskFlags: u64 {
+ /// File was accessed.
+ FAN_ACCESS;
+ /// File was modified.
+ FAN_MODIFY;
+ /// Metadata has changed. Since Linux 5.1.
+ FAN_ATTRIB;
+ /// Writtable file was closed.
+ FAN_CLOSE_WRITE;
+ /// Unwrittable file was closed.
+ FAN_CLOSE_NOWRITE;
+ /// File was opened.
+ FAN_OPEN;
+ /// File was moved from X. Since Linux 5.1.
+ FAN_MOVED_FROM;
+ /// File was moved to Y. Since Linux 5.1.
+ FAN_MOVED_TO;
+ /// Subfile was created. Since Linux 5.1.
+ FAN_CREATE;
+ /// Subfile was deleted. Since Linux 5.1.
+ FAN_DELETE;
+ /// Self was deleted. Since Linux 5.1.
+ FAN_DELETE_SELF;
+ /// Self was moved. Since Linux 5.1.
+ FAN_MOVE_SELF;
+ /// File was opened for execution. Since Linux 5.0.
+ FAN_OPEN_EXEC;
+
+ /// Event queue overflowed.
+ FAN_Q_OVERFLOW;
+ /// Filesystem error. Since Linux 5.16.
+ FAN_FS_ERROR;
+
+ /// Permission to open file was requested.
+ FAN_OPEN_PERM;
+ /// Permission to access file was requested.
+ FAN_ACCESS_PERM;
+ /// Permission to open file for execution was requested. Since Linux
+ /// 5.0.
+ FAN_OPEN_EXEC_PERM;
+
+ /// Interested in child events.
+ FAN_EVENT_ON_CHILD;
+
+ /// File was renamed. Since Linux 5.17.
+ FAN_RENAME;
+
+ /// Event occurred against dir.
+ FAN_ONDIR;
+
+ /// Combination of `FAN_CLOSE_WRITE` and `FAN_CLOSE_NOWRITE`.
+ FAN_CLOSE;
+ /// Combination of `FAN_MOVED_FROM` and `FAN_MOVED_TO`.
+ FAN_MOVE;
+ }
+}
+
+libc_bitflags! {
+ /// Configuration options for [`fanotify_init`](fn.fanotify_init.html).
+ pub struct InitFlags: libc::c_uint {
+ /// Close-on-exec flag set on the file descriptor.
+ FAN_CLOEXEC;
+ /// Nonblocking flag set on the file descriptor.
+ FAN_NONBLOCK;
+
+ /// Receipt of events notifications.
+ FAN_CLASS_NOTIF;
+ /// Receipt of events for permission decisions, after they contain final
+ /// data.
+ FAN_CLASS_CONTENT;
+ /// Receipt of events for permission decisions, before they contain
+ /// final data.
+ FAN_CLASS_PRE_CONTENT;
+
+ /// Remove the limit of 16384 events for the event queue.
+ FAN_UNLIMITED_QUEUE;
+ /// Remove the limit of 8192 marks.
+ FAN_UNLIMITED_MARKS;
+
+ /// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15.
+ FAN_REPORT_PIDFD;
+ /// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
+ FAN_REPORT_TID;
+ }
+}
+
+libc_bitflags! {
+ /// File status flags for fanotify events file descriptors.
+ pub struct EventFFlags: libc::c_uint {
+ /// Read only access.
+ O_RDONLY as libc::c_uint;
+ /// Write only access.
+ O_WRONLY as libc::c_uint;
+ /// Read and write access.
+ O_RDWR as libc::c_uint;
+ /// Support for files exceeded 2 GB.
+ O_LARGEFILE as libc::c_uint;
+ /// Close-on-exec flag for the file descriptor. Since Linux 3.18.
+ O_CLOEXEC as libc::c_uint;
+ /// Append mode for the file descriptor.
+ O_APPEND as libc::c_uint;
+ /// Synchronized I/O data integrity completion.
+ O_DSYNC as libc::c_uint;
+ /// No file last access time update.
+ O_NOATIME as libc::c_uint;
+ /// Nonblocking mode for the file descriptor.
+ O_NONBLOCK as libc::c_uint;
+ /// Synchronized I/O file integrity completion.
+ O_SYNC as libc::c_uint;
+ }
+}
+
+impl TryFrom<OFlag> for EventFFlags {
+ type Error = Errno;
+
+ fn try_from(o_flag: OFlag) -> Result<Self> {
+ EventFFlags::from_bits(o_flag.bits() as u32).ok_or(Errno::EINVAL)
+ }
+}
+
+impl From<EventFFlags> for OFlag {
+ fn from(event_f_flags: EventFFlags) -> Self {
+ OFlag::from_bits_retain(event_f_flags.bits() as i32)
+ }
+}
+
+libc_bitflags! {
+ /// Configuration options for [`fanotify_mark`](fn.fanotify_mark.html).
+ pub struct MarkFlags: libc::c_uint {
+ /// Add the events to the marks.
+ FAN_MARK_ADD;
+ /// Remove the events to the marks.
+ FAN_MARK_REMOVE;
+ /// Don't follow symlinks, mark them.
+ FAN_MARK_DONT_FOLLOW;
+ /// Raise an error if filesystem to be marked is not a directory.
+ FAN_MARK_ONLYDIR;
+ /// Events added to or removed from the marks.
+ FAN_MARK_IGNORED_MASK;
+ /// Ignore mask shall survive modify events.
+ FAN_MARK_IGNORED_SURV_MODIFY;
+ /// Remove all marks.
+ FAN_MARK_FLUSH;
+ /// Do not pin inode object in the inode cache. Since Linux 5.19.
+ FAN_MARK_EVICTABLE;
+ /// Events added to or removed from the marks. Since Linux 6.0.
+ FAN_MARK_IGNORE;
+
+ /// Default flag.
+ FAN_MARK_INODE;
+ /// Mark the mount specified by pathname.
+ FAN_MARK_MOUNT;
+ /// Mark the filesystem specified by pathname. Since Linux 4.20.
+ FAN_MARK_FILESYSTEM;
+
+ /// Combination of `FAN_MARK_IGNORE` and `FAN_MARK_IGNORED_SURV_MODIFY`.
+ FAN_MARK_IGNORE_SURV;
+ }
+}
+
+/// 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`.
+// 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
+ /// version does match. It can be done with the
+ /// `FanotifyEvent::check_version` method.
+ pub fn version(&self) -> u8 {
+ self.0.vers
+ }
+
+ /// Checks that compile fanotify API version is equal to the version of the
+ /// event.
+ pub fn check_version(&self) -> bool {
+ self.version() == FANOTIFY_METADATA_VERSION
+ }
+
+ /// Mask flags of the events.
+ pub fn mask(&self) -> MaskFlags {
+ MaskFlags::from_bits_truncate(self.0.mask)
+ }
+
+ /// The file descriptor of the event. If the value is `None` when reading
+ /// from the fanotify group, this event is to notify that a group queue
+ /// overflow occured.
+ pub fn fd(&self) -> Option<BorrowedFd> {
+ if self.0.fd == libc::FAN_NOFD {
+ None
+ } else {
+ // SAFETY: self.0.fd 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.fd) })
+ }
+ }
+
+ /// PID of the process that caused the event. TID in case flag
+ /// `FAN_REPORT_TID` was set at group initialization.
+ pub fn pid(&self) -> i32 {
+ self.0.pid
+ }
+}
+
+impl Drop for FanotifyEvent {
+ fn drop(&mut self) {
+ let e = close(self.0.fd);
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+/// Abstraction over the structure to be sent to allow or deny a given event.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct FanotifyResponse<'a> {
+ inner: libc::fanotify_response,
+ _borrowed_fd: PhantomData<BorrowedFd<'a>>,
+}
+
+impl<'a> FanotifyResponse<'a> {
+ /// Create a new response.
+ pub fn new(fd: BorrowedFd<'a>, response: Response) -> Self {
+ Self {
+ inner: libc::fanotify_response {
+ fd: fd.as_raw_fd(),
+ response: response.bits(),
+ },
+ _borrowed_fd: PhantomData,
+ }
+ }
+}
+
+libc_bitflags! {
+ /// Response to be wrapped in `FanotifyResponse` and sent to the `Fanotify`
+ /// group to allow or deny an event.
+ pub struct Response: u32 {
+ /// Allow the event.
+ FAN_ALLOW;
+ /// Deny the event.
+ FAN_DENY;
+ }
+}
+
+/// A fanotify group. This is also a file descriptor that can feed to other
+/// interfaces consuming file descriptors.
+#[derive(Debug)]
+pub struct Fanotify {
+ fd: OwnedFd,
+}
+
+impl Fanotify {
+ /// Initialize a new fanotify group.
+ ///
+ /// Returns a Result containing a Fanotify instance.
+ ///
+ /// For more information, see [fanotify_init(2)](https://man7.org/linux/man-pages/man7/fanotify_init.2.html).
+ pub fn init(
+ flags: InitFlags,
+ event_f_flags: EventFFlags,
+ ) -> Result<Fanotify> {
+ let res = Errno::result(unsafe {
+ libc::fanotify_init(flags.bits(), event_f_flags.bits())
+ });
+ res.map(|fd| Fanotify {
+ fd: unsafe { OwnedFd::from_raw_fd(fd) },
+ })
+ }
+
+ /// Add, remove, or modify an fanotify mark on a filesystem object.
+ /// If `dirfd` is `None`, `AT_FDCWD` is used.
+ ///
+ /// Returns a Result containing either `()` on success or errno otherwise.
+ ///
+ /// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
+ pub fn mark<P: ?Sized + NixPath>(
+ &self,
+ flags: MarkFlags,
+ mask: MaskFlags,
+ dirfd: Option<RawFd>,
+ path: Option<&P>,
+ ) -> Result<()> {
+ fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
+ where
+ P: ?Sized + NixPath,
+ F: FnOnce(*const libc::c_char) -> T,
+ {
+ match p {
+ Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
+ None => Ok(f(std::ptr::null())),
+ }
+ }
+
+ let res = with_opt_nix_path(path, |p| unsafe {
+ libc::fanotify_mark(
+ self.fd.as_raw_fd(),
+ flags.bits(),
+ mask.bits(),
+ at_rawfd(dirfd),
+ p,
+ )
+ })?;
+
+ Errno::result(res).map(|_| ())
+ }
+
+ /// Read incoming events from the fanotify group.
+ ///
+ /// Returns a Result containing either a `Vec` of events 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(&self) -> Result<Vec<FanotifyEvent>> {
+ let metadata_size = size_of::<libc::fanotify_event_metadata>();
+ const BUFSIZ: usize = 4096;
+ let mut buffer = [0u8; BUFSIZ];
+ let mut events = Vec::new();
+ let mut offset = 0;
+
+ let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
+
+ while (nread - offset) >= metadata_size {
+ let metadata = unsafe {
+ let mut metadata =
+ MaybeUninit::<libc::fanotify_event_metadata>::uninit();
+ ptr::copy_nonoverlapping(
+ buffer.as_ptr().add(offset),
+ metadata.as_mut_ptr().cast(),
+ (BUFSIZ - offset).min(metadata_size),
+ );
+ metadata.assume_init()
+ };
+
+ events.push(FanotifyEvent(metadata));
+ 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.
+ ///
+ /// # 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 `write` call.
+ /// In particular, `EAGAIN` or `EWOULDBLOCK` 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 write_response(&self, response: FanotifyResponse) -> Result<()> {
+ write(self.fd.as_fd(), unsafe {
+ std::slice::from_raw_parts(
+ (&response.inner as *const libc::fanotify_response).cast(),
+ size_of::<libc::fanotify_response>(),
+ )
+ })?;
+ Ok(())
+ }
+}
+
+impl FromRawFd for Fanotify {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Fanotify {
+ fd: unsafe { OwnedFd::from_raw_fd(fd) },
+ }
+ }
+}
+
+impl AsFd for Fanotify {
+ fn as_fd(&'_ self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}