summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test/sys/test_fanotify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/test/sys/test_fanotify.rs')
-rw-r--r--third_party/rust/nix/test/sys/test_fanotify.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/third_party/rust/nix/test/sys/test_fanotify.rs b/third_party/rust/nix/test/sys/test_fanotify.rs
new file mode 100644
index 0000000000..20226c272a
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_fanotify.rs
@@ -0,0 +1,149 @@
+use crate::*;
+use nix::sys::fanotify::{
+ EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
+ Response,
+};
+use std::fs::{read_link, 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
+pub fn test_fanotify() {
+ require_capability!("test_fanotify", CAP_SYS_ADMIN);
+
+ test_fanotify_notifications();
+ test_fanotify_responses();
+}
+
+fn test_fanotify_notifications() {
+ let group =
+ Fanotify::init(InitFlags::FAN_CLASS_NOTIF, 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 | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
+ None,
+ Some(&tempfile),
+ )
+ .unwrap();
+
+ // modify test file
+ {
+ let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
+ f.write_all(b"hello").unwrap();
+ }
+
+ let mut 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
+ {
+ let mut f = File::open(&tempfile).unwrap();
+ let mut s = String::new();
+ f.read_to_string(&mut s).unwrap();
+ }
+
+ let mut 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
+ let mut 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
+ let mut 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();
+
+ file_thread.join().unwrap();
+}