1 use crate::*;
2 use nix::sys::fanotify::{
3     EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
4     Response,
5 };
6 use std::fs::{read_link, File, OpenOptions};
7 use std::io::ErrorKind;
8 use std::io::{Read, Write};
9 use std::os::fd::AsRawFd;
10 use std::thread;
11 
12 #[test]
13 /// Run fanotify tests sequentially to avoid tmp files races
test_fanotify()14 pub fn test_fanotify() {
15     require_capability!("test_fanotify", CAP_SYS_ADMIN);
16 
17     test_fanotify_notifications();
18     test_fanotify_responses();
19 }
20 
test_fanotify_notifications()21 fn test_fanotify_notifications() {
22     let group =
23         Fanotify::init(InitFlags::FAN_CLASS_NOTIF, EventFFlags::O_RDONLY)
24             .unwrap();
25     let tempdir = tempfile::tempdir().unwrap();
26     let tempfile = tempdir.path().join("test");
27     OpenOptions::new()
28         .write(true)
29         .create_new(true)
30         .open(&tempfile)
31         .unwrap();
32 
33     group
34         .mark(
35             MarkFlags::FAN_MARK_ADD,
36             MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
37             None,
38             Some(&tempfile),
39         )
40         .unwrap();
41 
42     // modify test file
43     {
44         let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
45         f.write_all(b"hello").unwrap();
46     }
47 
48     let mut events = group.read_events().unwrap();
49     assert_eq!(events.len(), 1, "should have read exactly one event");
50     let event = events.pop().unwrap();
51     assert!(event.check_version());
52     assert_eq!(
53         event.mask(),
54         MaskFlags::FAN_OPEN
55             | MaskFlags::FAN_MODIFY
56             | MaskFlags::FAN_CLOSE_WRITE
57     );
58     let fd_opt = event.fd();
59     let fd = fd_opt.as_ref().unwrap();
60     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
61     assert_eq!(path, tempfile);
62 
63     // read test file
64     {
65         let mut f = File::open(&tempfile).unwrap();
66         let mut s = String::new();
67         f.read_to_string(&mut s).unwrap();
68     }
69 
70     let mut events = group.read_events().unwrap();
71     assert_eq!(events.len(), 1, "should have read exactly one event");
72     let event = events.pop().unwrap();
73     assert!(event.check_version());
74     assert_eq!(
75         event.mask(),
76         MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE
77     );
78     let fd_opt = event.fd();
79     let fd = fd_opt.as_ref().unwrap();
80     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
81     assert_eq!(path, tempfile);
82 }
83 
test_fanotify_responses()84 fn test_fanotify_responses() {
85     let group =
86         Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY)
87             .unwrap();
88     let tempdir = tempfile::tempdir().unwrap();
89     let tempfile = tempdir.path().join("test");
90     OpenOptions::new()
91         .write(true)
92         .create_new(true)
93         .open(&tempfile)
94         .unwrap();
95 
96     group
97         .mark(
98             MarkFlags::FAN_MARK_ADD,
99             MaskFlags::FAN_OPEN_PERM,
100             None,
101             Some(&tempfile),
102         )
103         .unwrap();
104 
105     let file_thread = thread::spawn({
106         let tempfile = tempfile.clone();
107 
108         move || {
109             // first open, should fail
110             let Err(e) = File::open(&tempfile) else {
111                 panic!("The first open should fail");
112             };
113             assert_eq!(e.kind(), ErrorKind::PermissionDenied);
114 
115             // second open, should succeed
116             File::open(&tempfile).unwrap();
117         }
118     });
119 
120     // Deny the first open try
121     let mut events = group.read_events().unwrap();
122     assert_eq!(events.len(), 1, "should have read exactly one event");
123     let event = events.pop().unwrap();
124     assert!(event.check_version());
125     assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
126     let fd_opt = event.fd();
127     let fd = fd_opt.as_ref().unwrap();
128     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
129     assert_eq!(path, tempfile);
130     group
131         .write_response(FanotifyResponse::new(*fd, Response::FAN_DENY))
132         .unwrap();
133 
134     // Allow the second open try
135     let mut events = group.read_events().unwrap();
136     assert_eq!(events.len(), 1, "should have read exactly one event");
137     let event = events.pop().unwrap();
138     assert!(event.check_version());
139     assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
140     let fd_opt = event.fd();
141     let fd = fd_opt.as_ref().unwrap();
142     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
143     assert_eq!(path, tempfile);
144     group
145         .write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW))
146         .unwrap();
147 
148     file_thread.join().unwrap();
149 }
150