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