usecrate::*; use nix::errno::Errno; use nix::sys::fanotify::{
EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
Response,
}; use std::fs::{read_link, read_to_string, File, OpenOptions}; use std::io::ErrorKind; use std::io::{Read, Write}; use std::os::fd::AsRawFd; use std::thread;
#[test] /// Run fanotify tests sequentially to avoid tmp files races pubfn test_fanotify() {
require_capability!("test_fanotify", CAP_SYS_ADMIN);
// modify test file
{ letmut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
f.write_all(b"hello").unwrap();
}
letmut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event"); let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN
| MaskFlags::FAN_MODIFY
| MaskFlags::FAN_CLOSE_WRITE
); let fd_opt = event.fd(); let fd = fd_opt.as_ref().unwrap(); let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
// read test file
{ letmut f = File::open(&tempfile).unwrap(); letmut s = String::new();
f.read_to_string(&mut s).unwrap();
}
letmut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event"); let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE
); let fd_opt = event.fd(); let fd = fd_opt.as_ref().unwrap(); let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
}
fn test_fanotify_responses() { let group =
Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY)
.unwrap(); let tempdir = tempfile::tempdir().unwrap(); let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN_PERM,
None,
Some(&tempfile),
)
.unwrap();
let file_thread = thread::spawn({ let tempfile = tempfile.clone();
move || { // first open, should fail let Err(e) = File::open(&tempfile) else {
panic!("The first open should fail");
};
assert_eq!(e.kind(), ErrorKind::PermissionDenied);
// second open, should succeed
File::open(&tempfile).unwrap();
}
});
// Deny the first open try letmut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event"); let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM); let fd_opt = event.fd(); let fd = fd_opt.as_ref().unwrap(); let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_DENY))
.unwrap();
// Allow the second open try letmut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event"); let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM); let fd_opt = event.fd(); let fd = fd_opt.as_ref().unwrap(); let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW))
.unwrap();
// make sure the kernel is configured with the default value, // just so this test doesn't run forever
assert_eq!(max_events, 16384);
let group = Fanotify::init(
InitFlags::FAN_CLASS_NOTIF
| InitFlags::FAN_REPORT_TID
| InitFlags::FAN_NONBLOCK,
EventFFlags::O_RDONLY,
)
.unwrap(); let tempdir = tempfile::tempdir().unwrap(); let tempfile = tempdir.path().join("test");
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN,
None,
Some(&tempfile),
)
.unwrap();
thread::scope(|s| { // perform 10 more events to demonstrate some will be dropped for _ in0..(max_events + 10) {
s.spawn(|| {
File::open(&tempfile).unwrap();
});
}
});
// flush the queue until it's empty letmut n = 0; letmut last_event = None; loop { match group.read_events() {
Ok(events) => {
n += events.len(); iflet Some(event) = events.last() {
last_event = Some(event.mask());
}
}
Err(e) if e == Errno::EWOULDBLOCK => break,
Err(e) => panic!("{e:?}"),
}
}
// make sure we read all we expected. // the +1 is for the overflow event.
assert_eq!(n, max_events + 1);
assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW));
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.