fanotify/
high_level.rs

1use crate::low_level::{
2    close_fd, fanotify_init, fanotify_mark, fanotify_read, FanotifyEventMetadata, AT_FDCWD,
3    FAN_ALLOW, FAN_CLASS_CONTENT, FAN_CLASS_NOTIF, FAN_CLASS_PRE_CONTENT, FAN_CLOEXEC, FAN_DENY,
4    FAN_MARK_ADD, FAN_MARK_FLUSH, FAN_MARK_MOUNT, FAN_MARK_REMOVE, FAN_NONBLOCK, O_CLOEXEC,
5    O_RDONLY,
6};
7use crate::FanotifyPath;
8use enum_iterator::{all, Sequence};
9use std::fs::read_link;
10use std::io::Error;
11use std::os::fd::{AsFd, BorrowedFd};
12
13pub use crate::low_level::{
14    FAN_ACCESS, FAN_ACCESS_PERM, FAN_ATTRIB, FAN_CLOSE, FAN_CLOSE_NOWRITE, FAN_CLOSE_WRITE,
15    FAN_CREATE, FAN_DELETE, FAN_DELETE_SELF, FAN_EVENT_ON_CHILD, FAN_MODIFY, FAN_MOVE,
16    FAN_MOVED_FROM, FAN_MOVED_TO, FAN_MOVE_SELF, FAN_ONDIR, FAN_OPEN, FAN_OPEN_EXEC,
17    FAN_OPEN_EXEC_PERM, FAN_OPEN_PERM,
18};
19
20pub struct Fanotify {
21    fd: i32,
22}
23
24// SAFETY: the `fanotify_*` functions are thread safe, and file descriptors are safe for
25// sharing betweent threads if they are used in threadsafe functions
26unsafe impl Send for Fanotify {}
27unsafe impl Sync for Fanotify {}
28
29impl AsFd for Fanotify {
30    fn as_fd(&self) -> BorrowedFd<'_> {
31        unsafe { BorrowedFd::borrow_raw(self.fd) }
32    }
33}
34
35impl<T> From<T> for Fanotify
36where
37    T: Into<i32>,
38{
39    fn from(raw: T) -> Fanotify {
40        Fanotify { fd: raw.into() }
41    }
42}
43
44#[derive(Debug, Clone, Copy, Sequence, PartialEq)]
45pub enum FanEvent {
46    Access = FAN_ACCESS as isize,
47    AccessPerm = FAN_ACCESS_PERM as isize,
48    Attrib = FAN_ATTRIB as isize,
49    Close = FAN_CLOSE as isize,
50    CloseNowrite = FAN_CLOSE_NOWRITE as isize,
51    CloseWrite = FAN_CLOSE_WRITE as isize,
52    Create = FAN_CREATE as isize,
53    Delete = FAN_DELETE as isize,
54    DeleteSelf = FAN_DELETE_SELF as isize,
55    EventOnChild = FAN_EVENT_ON_CHILD as isize,
56    Modify = FAN_MODIFY as isize,
57    Move = FAN_MOVE as isize,
58    MovedFrom = FAN_MOVED_FROM as isize,
59    MovedTo = FAN_MOVED_TO as isize,
60    MoveSelf = FAN_MOVE_SELF as isize,
61    Ondir = FAN_ONDIR as isize,
62    Open = FAN_OPEN as isize,
63    OpenExec = FAN_OPEN_EXEC as isize,
64    OpenExecPerm = FAN_OPEN_EXEC_PERM as isize,
65    OpenPerm = FAN_OPEN_PERM as isize,
66}
67
68impl From<FanEvent> for u64 {
69    fn from(event: FanEvent) -> u64 {
70        match event {
71            FanEvent::Access => FAN_ACCESS,
72            FanEvent::AccessPerm => FAN_ACCESS_PERM,
73            FanEvent::Attrib => FAN_ATTRIB,
74            FanEvent::Close => FAN_CLOSE,
75            FanEvent::CloseNowrite => FAN_CLOSE_NOWRITE,
76            FanEvent::CloseWrite => FAN_CLOSE_WRITE,
77            FanEvent::Create => FAN_CREATE,
78            FanEvent::Delete => FAN_DELETE,
79            FanEvent::DeleteSelf => FAN_DELETE_SELF,
80            FanEvent::EventOnChild => FAN_EVENT_ON_CHILD,
81            FanEvent::Modify => FAN_MODIFY,
82            FanEvent::Move => FAN_MOVE,
83            FanEvent::MovedFrom => FAN_MOVED_FROM,
84            FanEvent::MovedTo => FAN_MOVED_TO,
85            FanEvent::MoveSelf => FAN_MOVE_SELF,
86            FanEvent::Ondir => FAN_ONDIR,
87            FanEvent::Open => FAN_OPEN,
88            FanEvent::OpenExec => FAN_OPEN_EXEC,
89            FanEvent::OpenExecPerm => FAN_OPEN_EXEC_PERM,
90            FanEvent::OpenPerm => FAN_OPEN_PERM,
91        }
92    }
93}
94
95pub fn events_from_mask(mask: u64) -> Vec<FanEvent> {
96    all::<FanEvent>()
97        .filter(|flag| (mask & (*flag as u64)) != 0)
98        .collect::<Vec<FanEvent>>()
99}
100
101#[derive(Debug)]
102pub enum FanotifyResponse {
103    Allow,
104    Deny,
105}
106
107impl From<FanotifyResponse> for u32 {
108    fn from(resp: FanotifyResponse) -> u32 {
109        match resp {
110            FanotifyResponse::Allow => FAN_ALLOW,
111            FanotifyResponse::Deny => FAN_DENY,
112        }
113    }
114}
115
116#[derive(Debug)]
117pub struct Event {
118    pub fd: i32,
119    pub path: String,
120    pub events: Vec<FanEvent>,
121    pub pid: i32,
122}
123
124impl From<FanotifyEventMetadata> for Event {
125    fn from(metadata: FanotifyEventMetadata) -> Self {
126        let path = read_link(format!("/proc/self/fd/{}", metadata.fd)).unwrap_or_default();
127        Event {
128            fd: metadata.fd,
129            path: path.to_str().unwrap().to_string(),
130            events: events_from_mask(metadata.mask),
131            pid: metadata.pid,
132        }
133    }
134}
135
136#[derive(Debug, Copy, Clone)]
137#[repr(C)]
138pub enum FanotifyMode {
139    PRECONTENT,
140    CONTENT,
141    NOTIF,
142}
143
144impl FanotifyMode {
145    fn to_fan_class(self) -> u32 {
146        match self {
147            FanotifyMode::PRECONTENT => FAN_CLASS_PRE_CONTENT,
148            FanotifyMode::CONTENT => FAN_CLASS_CONTENT,
149            FanotifyMode::NOTIF => FAN_CLASS_NOTIF,
150        }
151    }
152}
153
154impl Fanotify {
155    pub fn new_blocking(mode: FanotifyMode) -> Result<Self, Error> {
156        Ok(Fanotify {
157            fd: fanotify_init(
158                FAN_CLOEXEC | mode.to_fan_class(),
159                (O_CLOEXEC | O_RDONLY) as u32,
160            )?,
161        })
162    }
163
164    pub fn new_nonblocking(mode: FanotifyMode) -> Result<Self, Error> {
165        Ok(Fanotify {
166            fd: fanotify_init(
167                FAN_CLOEXEC | FAN_NONBLOCK | mode.to_fan_class(),
168                (O_CLOEXEC | O_RDONLY) as u32,
169            )?,
170        })
171    }
172
173    pub fn add_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
174        fanotify_mark(self.fd, FAN_MARK_ADD, mode, AT_FDCWD, path)?;
175        Ok(())
176    }
177
178    pub fn add_mountpoint<P: ?Sized + FanotifyPath>(
179        &self,
180        mode: u64,
181        path: &P,
182    ) -> Result<(), Error> {
183        fanotify_mark(self.fd, FAN_MARK_ADD | FAN_MARK_MOUNT, mode, AT_FDCWD, path)?;
184        Ok(())
185    }
186
187    pub fn remove_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
188        fanotify_mark(self.fd, FAN_MARK_REMOVE, mode, AT_FDCWD, path)?;
189        Ok(())
190    }
191
192    pub fn flush_path<P: ?Sized + FanotifyPath>(&self, mode: u64, path: &P) -> Result<(), Error> {
193        fanotify_mark(self.fd, FAN_MARK_FLUSH, mode, AT_FDCWD, path)?;
194        Ok(())
195    }
196
197    pub fn read_event(&self) -> Vec<Event> {
198        let mut result = Vec::new();
199        let events = fanotify_read(self.fd);
200        for metadata in events {
201            let path = read_link(format!("/proc/self/fd/{}", metadata.fd)).unwrap_or_default();
202            let path = path.to_str().unwrap();
203            result.push(Event {
204                fd: metadata.fd,
205                path: String::from(path),
206                events: events_from_mask(metadata.mask),
207                pid: metadata.pid,
208            });
209            close_fd(metadata.fd);
210        }
211        result
212    }
213
214    pub fn send_response<T: Into<i32>>(&self, fd: T, resp: FanotifyResponse) {
215        use crate::low_level::FanotifyResponse as LowLeveResponse;
216        use libc::c_void;
217        let response = LowLeveResponse {
218            fd: fd.into(),
219            response: resp.into(),
220        };
221        unsafe {
222            libc::write(
223                self.fd,
224                core::ptr::addr_of!(response) as *const c_void,
225                std::mem::size_of::<LowLeveResponse>(),
226            );
227        }
228    }
229
230    pub fn as_raw_fd(&self) -> i32 {
231        self.fd
232    }
233
234    pub fn close(self) {
235        close_fd(self.fd)
236    }
237}
238
239impl Drop for Fanotify {
240    fn drop(&mut self) {
241        close_fd(self.fd);
242    }
243}
244
245impl Clone for Fanotify {
246    fn clone(&self) -> Self {
247        Self {
248            fd: unsafe { libc::dup(self.fd) },
249        }
250    }
251}
252
253#[derive(Debug, Copy, Clone)]
254pub struct FanotifyBuilder {
255    class: FanotifyMode,
256    flags: u32,
257    event_flags: u32,
258}
259
260impl FanotifyBuilder {
261    pub fn new() -> Self {
262        Self {
263            class: FanotifyMode::NOTIF,
264            flags: FAN_CLOEXEC,
265            event_flags: O_CLOEXEC as u32,
266        }
267    }
268
269    pub fn with_class(self, class: FanotifyMode) -> Self {
270        Self { class, ..self }
271    }
272
273    pub fn with_flags(self, flags: u32) -> Self {
274        Self {
275            flags: FAN_CLOEXEC | flags,
276            ..self
277        }
278    }
279
280    pub fn with_event_flags(self, event_flags: u32) -> Self {
281        Self {
282            event_flags,
283            ..self
284        }
285    }
286
287    pub fn register(&self) -> Result<Fanotify, Error> {
288        Ok(Fanotify {
289            fd: fanotify_init(self.flags | self.class.to_fan_class(), self.event_flags)?,
290        })
291    }
292}