Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "fanotify-rs"
version = "0.3.2"
version = "0.4.0"
authors = ["zhanglei <mrzhang.lei@outlook.com>", "n01e0 <noleo@vanillabeans.mobi>", "Philip Woolford <woolford.philip@gmail.com>"]
edition = "2021"
edition = "2024"
license = "MIT OR Apache-2.0"
description = "The high-level/low-level implementation of Linux Fanotify."
repository = "https://github.com/ZhangLei-cn/fanotify-rs"
Expand All @@ -13,6 +13,11 @@ name = "fanotify"
path = "src/lib.rs"
doc = true

[features]
default = ["high-level"]
high-level = ["bitflags"]

[dependencies]
libc = "0.2"
enum-iterator = "1.5"
enum-iterator = "2.3"
bitflags = {version = "2.10", optional = true}
2 changes: 1 addition & 1 deletion demo/with_poll/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ description = "fanotify-rs with poll"
[dependencies]
clap = "4.4.18"
fanotify-rs = { path = "../../" }
nix = {version = "0.27.1", features = ["poll"] }
nix = {version = "0.30.1", features = ["poll"] }
17 changes: 9 additions & 8 deletions demo/with_poll/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@ use fanotify::high_level::*;
use nix::poll::{poll, PollFd, PollFlags};
use std::os::fd::AsFd;

fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = clap::Command::new("with_poll")
.arg(clap::Arg::new("path").index(1).required(true))
.get_matches();

let fd = Fanotify::new_with_nonblocking(FanotifyMode::CONTENT);
let fd = Fanotify::new_nonblocking(FanotifyMode::CONTENT)?;
fd.add_mountpoint(
FAN_OPEN_EXEC | FAN_CLOSE_WRITE,
MarkMode::OpenExec | MarkMode::CloseWrite,
app.get_one::<String>("path")
.expect("We can unwrap here as clap enforces the existence of `path`"),
)
.unwrap();
)?;

let fd_handle = fd.as_fd();
let mut fds = [PollFd::new(&fd_handle, PollFlags::POLLIN)];
let mut fds = [PollFd::new(fd_handle, PollFlags::POLLIN)];
loop {
let poll_num = poll(&mut fds, -1).unwrap();
let poll_num = poll(&mut fds, None::<u8>).unwrap();
if poll_num > 0 {
for event in fd.read_event() {
for event in fd.read_event()? {
println!("{:#?}", event);
fd.send_response(event.fd, FanotifyResponse::Allow);
}
Expand All @@ -29,4 +28,6 @@ fn main() {
break;
}
}

Ok(())
}
146 changes: 117 additions & 29 deletions src/high_level.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
use crate::low_level::{
close_fd, fanotify_init, fanotify_mark, fanotify_read, FanotifyEventMetadata, AT_FDCWD,
FAN_ALLOW, FAN_CLASS_CONTENT, FAN_CLASS_NOTIF, FAN_CLASS_PRE_CONTENT, FAN_CLOEXEC, FAN_DENY,
FAN_MARK_ADD, FAN_MARK_FLUSH, FAN_MARK_MOUNT, FAN_MARK_REMOVE, FAN_NONBLOCK, O_CLOEXEC,
O_RDONLY,
};
use crate::FanotifyPath;
use enum_iterator::{all, Sequence};
use bitflags::bitflags;
use enum_iterator::{Sequence, all};
use std::fs::read_link;
use std::io::Error;
use std::os::fd::{AsFd, BorrowedFd};

pub use crate::low_level::{
FAN_ACCESS, FAN_ACCESS_PERM, FAN_ATTRIB, FAN_CLOSE, FAN_CLOSE_NOWRITE, FAN_CLOSE_WRITE,
FAN_CREATE, FAN_DELETE, FAN_DELETE_SELF, FAN_EVENT_ON_CHILD, FAN_MODIFY, FAN_MOVE,
FAN_MOVED_FROM, FAN_MOVED_TO, FAN_MOVE_SELF, FAN_ONDIR, FAN_OPEN, FAN_OPEN_EXEC,
FAN_OPEN_EXEC_PERM, FAN_OPEN_PERM,
};
use crate::low_level::*;

pub struct Fanotify {
fd: i32,
Expand All @@ -41,6 +31,61 @@ where
}
}

bitflags! {
pub struct MarkMode: u64 {
const Access = FAN_ACCESS;
const Modify = FAN_MODIFY;
const Attributes = FAN_ATTRIB;
const CloseWrite = FAN_CLOSE_WRITE;
const CloseNoWrite = FAN_CLOSE_NOWRITE;
const Open = FAN_OPEN;
const MovedFrom = FAN_MOVED_FROM;
const MovedTo = FAN_MOVED_TO;
const Moved = FAN_MOVE;
const Create = FAN_CREATE;
const Delete = FAN_DELETE;
const DeleteSelf = FAN_DELETE_SELF;
const MoveSelf = FAN_MOVE_SELF;
const OpenExec = FAN_OPEN_EXEC;
const OpenPerm = FAN_OPEN_PERM;
const OpenExecPerm = FAN_OPEN_EXEC_PERM;
const OnDirectory = FAN_ONDIR;
const EventOnChild = FAN_EVENT_ON_CHILD;
const Close = FAN_CLOSE;
}
}

bitflags! {
pub struct Flags: u32 {
const CloseOnExec = FAN_CLOEXEC;
const NonBlocking = FAN_NONBLOCK;
const NotificationClass = FAN_CLASS_NOTIF;
const ContentClass = FAN_CLASS_CONTENT;
const PreContentClass = FAN_CLASS_PRE_CONTENT;
const UnlimitedEventQueue = FAN_UNLIMITED_QUEUE;
const UnlimitedMarks = FAN_UNLIMITED_MARKS;
const Audit = FAN_ENABLE_AUDIT;
const ReportThreadID = FAN_REPORT_TID;
const ReportDirectoryID = FAN_REPORT_DIR_FID;
const ReportName = FAN_REPORT_NAME;
}
}

bitflags! {
pub struct EventFlags: u32 {
const CloseOnExec = FAN_CLOEXEC;
const ReadOnly = O_RDONLY.cast_unsigned();
const ReadWrite = O_RDWR.cast_unsigned();
const WriteOnly = O_WRONLY.cast_unsigned();
const LargeFile = O_LARGEFILE.cast_unsigned();
const Append = O_APPEND.cast_unsigned();
const MetadataSync = O_DSYNC.cast_unsigned();
const NoAccessTime = O_NOATIME.cast_unsigned();
const NonBlock = O_NONBLOCK.cast_unsigned();
const Sync = O_SYNC.cast_unsigned();
}
}

#[derive(Debug, Clone, Copy, Sequence, PartialEq)]
pub enum FanEvent {
Access = FAN_ACCESS as isize,
Expand Down Expand Up @@ -119,6 +164,7 @@ pub struct Event {
pub path: String,
pub events: Vec<FanEvent>,
pub pid: i32,
pub additional_records: Vec<FanAdditionalRecords>,
}

impl Event {
Expand All @@ -134,6 +180,7 @@ impl Event {
path: self.path.clone(),
events: self.events.clone(),
pid: self.pid,
additional_records: self.additional_records.clone(),
})
}
}
Expand All @@ -152,6 +199,7 @@ impl From<FanotifyEventMetadata> for Event {
path: path.to_str().unwrap().to_string(),
events: events_from_mask(metadata.mask),
pid: metadata.pid,
additional_records: vec![],
}
}
}
Expand Down Expand Up @@ -193,44 +241,78 @@ impl Fanotify {
})
}

pub fn add_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_ADD, mode, AT_FDCWD, path)?;
pub fn add_path<P: ?Sized + FanotifyPath>(
&self,
mode: MarkMode,
path: &P,
) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_ADD, mode.bits(), AT_FDCWD, path)?;
Ok(())
}

pub fn add_mountpoint<P: ?Sized + FanotifyPath>(
&self,
mode: u64,
mode: MarkMode,
path: &P,
) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_ADD | FAN_MARK_MOUNT, mode, AT_FDCWD, path)?;
fanotify_mark(
self.fd,
FAN_MARK_ADD | FAN_MARK_MOUNT,
mode.bits(),
AT_FDCWD,
path,
)?;
Ok(())
}

pub fn remove_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_REMOVE, mode, AT_FDCWD, path)?;
pub fn add_filesystem<P: ?Sized + FanotifyPath>(
&self,
mode: MarkMode,
path: &P,
) -> Result<(), Error> {
fanotify_mark(
self.fd,
FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
mode.bits(),
AT_FDCWD,
path,
)?;
Ok(())
}

pub fn flush_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_FLUSH, mode, AT_FDCWD, path)?;
pub fn remove_path<P: ?Sized + FanotifyPath>(
&self,
mode: MarkMode,
path: &P,
) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_REMOVE, mode.bits(), AT_FDCWD, path)?;
Ok(())
}

pub fn read_event(&self) -> Vec<Event> {
pub fn flush_path<P: ?Sized + FanotifyPath>(
&self,
mode: MarkMode,
path: &P,
) -> Result<(), Error> {
fanotify_mark(self.fd, FAN_MARK_FLUSH, mode.bits(), AT_FDCWD, path)?;
Ok(())
}

pub fn read_event(&self) -> std::io::Result<Vec<Event>> {
let mut result = Vec::new();
let events = fanotify_read(self.fd);
for metadata in events {
let events = fanotify_read(self.fd)?;
for (metadata, additional_records) in events {
let path = read_link(format!("/proc/self/fd/{}", metadata.fd)).unwrap_or_default();
let path = path.to_str().unwrap();
result.push(Event {
fd: metadata.fd,
path: String::from(path),
events: events_from_mask(metadata.mask),
pid: metadata.pid,
additional_records,
});
}
result
Ok(result)
}

pub fn send_response<T: Into<i32>>(&self, fd: T, resp: FanotifyResponse) {
Expand Down Expand Up @@ -292,16 +374,16 @@ impl FanotifyBuilder {
Self { class, ..self }
}

pub fn with_flags(self, flags: u32) -> Self {
pub fn with_flags(self, flags: Flags) -> Self {
Self {
flags: FAN_CLOEXEC | flags,
flags: FAN_CLOEXEC | flags.bits(),
..self
}
}

pub fn with_event_flags(self, event_flags: u32) -> Self {
pub fn with_event_flags(self, event_flags: EventFlags) -> Self {
Self {
event_flags,
event_flags: event_flags.bits(),
..self
}
}
Expand All @@ -312,3 +394,9 @@ impl FanotifyBuilder {
})
}
}

impl Default for FanotifyBuilder {
fn default() -> Self {
Self::new()
}
}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[cfg(feature = "high-level")]
pub mod high_level;
pub mod low_level;

Expand All @@ -21,4 +22,4 @@ impl<T: AsRef<std::ffi::OsStr>> FanotifyPath for T {
fn as_os_str(&self) -> &std::ffi::OsStr {
self.as_ref()
}
}
}
Loading