• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::*;
2 use nix::errno::Errno;
3 use nix::sys::fanotify::{
4     EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
5     Response,
6 };
7 use std::fs::{read_link, read_to_string, File, OpenOptions};
8 use std::io::ErrorKind;
9 use std::io::{Read, Write};
10 use std::os::fd::AsRawFd;
11 use std::thread;
12 
13 #[test]
14 /// Run fanotify tests sequentially to avoid tmp files races
test_fanotify()15 pub fn test_fanotify() {
16     require_capability!("test_fanotify", CAP_SYS_ADMIN);
17 
18     test_fanotify_notifications();
19     test_fanotify_responses();
20     test_fanotify_overflow();
21 }
22 
test_fanotify_notifications()23 fn test_fanotify_notifications() {
24     let group =
25         Fanotify::init(InitFlags::FAN_CLASS_NOTIF, EventFFlags::O_RDONLY)
26             .unwrap();
27     let tempdir = tempfile::tempdir().unwrap();
28     let tempfile = tempdir.path().join("test");
29     OpenOptions::new()
30         .write(true)
31         .create_new(true)
32         .open(&tempfile)
33         .unwrap();
34 
35     group
36         .mark(
37             MarkFlags::FAN_MARK_ADD,
38             MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
39             None,
40             Some(&tempfile),
41         )
42         .unwrap();
43 
44     // modify test file
45     {
46         let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
47         f.write_all(b"hello").unwrap();
48     }
49 
50     let mut events = group.read_events().unwrap();
51     assert_eq!(events.len(), 1, "should have read exactly one event");
52     let event = events.pop().unwrap();
53     assert!(event.check_version());
54     assert_eq!(
55         event.mask(),
56         MaskFlags::FAN_OPEN
57             | MaskFlags::FAN_MODIFY
58             | MaskFlags::FAN_CLOSE_WRITE
59     );
60     let fd_opt = event.fd();
61     let fd = fd_opt.as_ref().unwrap();
62     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
63     assert_eq!(path, tempfile);
64 
65     // read test file
66     {
67         let mut f = File::open(&tempfile).unwrap();
68         let mut s = String::new();
69         f.read_to_string(&mut s).unwrap();
70     }
71 
72     let mut events = group.read_events().unwrap();
73     assert_eq!(events.len(), 1, "should have read exactly one event");
74     let event = events.pop().unwrap();
75     assert!(event.check_version());
76     assert_eq!(
77         event.mask(),
78         MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE
79     );
80     let fd_opt = event.fd();
81     let fd = fd_opt.as_ref().unwrap();
82     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
83     assert_eq!(path, tempfile);
84 }
85 
test_fanotify_responses()86 fn test_fanotify_responses() {
87     let group =
88         Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY)
89             .unwrap();
90     let tempdir = tempfile::tempdir().unwrap();
91     let tempfile = tempdir.path().join("test");
92     OpenOptions::new()
93         .write(true)
94         .create_new(true)
95         .open(&tempfile)
96         .unwrap();
97 
98     group
99         .mark(
100             MarkFlags::FAN_MARK_ADD,
101             MaskFlags::FAN_OPEN_PERM,
102             None,
103             Some(&tempfile),
104         )
105         .unwrap();
106 
107     let file_thread = thread::spawn({
108         let tempfile = tempfile.clone();
109 
110         move || {
111             // first open, should fail
112             let Err(e) = File::open(&tempfile) else {
113                 panic!("The first open should fail");
114             };
115             assert_eq!(e.kind(), ErrorKind::PermissionDenied);
116 
117             // second open, should succeed
118             File::open(&tempfile).unwrap();
119         }
120     });
121 
122     // Deny the first open try
123     let mut events = group.read_events().unwrap();
124     assert_eq!(events.len(), 1, "should have read exactly one event");
125     let event = events.pop().unwrap();
126     assert!(event.check_version());
127     assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
128     let fd_opt = event.fd();
129     let fd = fd_opt.as_ref().unwrap();
130     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
131     assert_eq!(path, tempfile);
132     group
133         .write_response(FanotifyResponse::new(*fd, Response::FAN_DENY))
134         .unwrap();
135 
136     // Allow the second open try
137     let mut events = group.read_events().unwrap();
138     assert_eq!(events.len(), 1, "should have read exactly one event");
139     let event = events.pop().unwrap();
140     assert!(event.check_version());
141     assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
142     let fd_opt = event.fd();
143     let fd = fd_opt.as_ref().unwrap();
144     let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
145     assert_eq!(path, tempfile);
146     group
147         .write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW))
148         .unwrap();
149 
150     file_thread.join().unwrap();
151 }
152 
test_fanotify_overflow()153 fn test_fanotify_overflow() {
154     let max_events: usize =
155         read_to_string("/proc/sys/fs/fanotify/max_queued_events")
156             .unwrap()
157             .trim()
158             .parse()
159             .unwrap();
160 
161     // make sure the kernel is configured with the default value,
162     // just so this test doesn't run forever
163     assert_eq!(max_events, 16384);
164 
165     let group = Fanotify::init(
166         InitFlags::FAN_CLASS_NOTIF
167             | InitFlags::FAN_REPORT_TID
168             | InitFlags::FAN_NONBLOCK,
169         EventFFlags::O_RDONLY,
170     )
171     .unwrap();
172     let tempdir = tempfile::tempdir().unwrap();
173     let tempfile = tempdir.path().join("test");
174 
175     OpenOptions::new()
176         .write(true)
177         .create_new(true)
178         .open(&tempfile)
179         .unwrap();
180 
181     group
182         .mark(
183             MarkFlags::FAN_MARK_ADD,
184             MaskFlags::FAN_OPEN,
185             None,
186             Some(&tempfile),
187         )
188         .unwrap();
189 
190     thread::scope(|s| {
191         // perform 10 more events to demonstrate some will be dropped
192         for _ in 0..(max_events + 10) {
193             s.spawn(|| {
194                 File::open(&tempfile).unwrap();
195             });
196         }
197     });
198 
199     // flush the queue until it's empty
200     let mut n = 0;
201     let mut last_event = None;
202     loop {
203         match group.read_events() {
204             Ok(events) => {
205                 n += events.len();
206                 if let Some(event) = events.last() {
207                     last_event = Some(event.mask());
208                 }
209             }
210             Err(e) if e == Errno::EWOULDBLOCK => break,
211             Err(e) => panic!("{e:?}"),
212         }
213     }
214 
215     // make sure we read all we expected.
216     // the +1 is for the overflow event.
217     assert_eq!(n, max_events + 1);
218     assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW));
219 }
220