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
24unsafe 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}