1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2013 SUSE. All Rights Reserved.
4 *
5 * Started by Jan Kara <jack@suse.cz>
6 *
7 * DESCRIPTION
8 * Check that fanotify work for a file
9 */
10 #define _GNU_SOURCE
11 #include "config.h"
12
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <sys/syscall.h>
20 #include "tst_test.h"
21 #include "fanotify.h"
22
23 #if defined(HAVE_SYS_FANOTIFY_H)
24 #include <sys/fanotify.h>
25
26 #define EVENT_MAX 1024
27 /* size of the event structure, not counting name */
28 #define EVENT_SIZE (sizeof (struct fanotify_event_metadata))
29 /* reasonable guess as to size of 1024 events */
30 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE)
31
32 #define BUF_SIZE 256
33 #define TST_TOTAL 12
34
35 #define MOUNT_PATH "fs_mnt"
36
37 static struct tcase {
38 const char *tname;
39 struct fanotify_mark_type mark;
40 unsigned int init_flags;
41 } tcases[] = {
42 {
43 "inode mark events",
44 INIT_FANOTIFY_MARK_TYPE(INODE),
45 FAN_CLASS_NOTIF
46 },
47 {
48 "mount mark events",
49 INIT_FANOTIFY_MARK_TYPE(MOUNT),
50 FAN_CLASS_NOTIF
51 },
52 {
53 "filesystem mark events",
54 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
55 FAN_CLASS_NOTIF
56 },
57 {
58 "inode mark events (FAN_REPORT_FID)",
59 INIT_FANOTIFY_MARK_TYPE(INODE),
60 FAN_CLASS_NOTIF|FAN_REPORT_FID
61 },
62 {
63 "mount mark events (FAN_REPORT_FID)",
64 INIT_FANOTIFY_MARK_TYPE(MOUNT),
65 FAN_CLASS_NOTIF|FAN_REPORT_FID
66 },
67 {
68 "filesystem mark events (FAN_REPORT_FID)",
69 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
70 FAN_CLASS_NOTIF|FAN_REPORT_FID
71 },
72 };
73
74 static char fname[BUF_SIZE];
75 static char buf[BUF_SIZE];
76 static int fd_notify;
77
78 static unsigned long long event_set[EVENT_MAX];
79
80 static char event_buf[EVENT_BUF_LEN];
81
test_fanotify(unsigned int n)82 static void test_fanotify(unsigned int n)
83 {
84 struct tcase *tc = &tcases[n];
85 struct fanotify_mark_type *mark = &tc->mark;
86 int fd, ret, len, i = 0, test_num = 0;
87 int tst_count = 0;
88
89 tst_res(TINFO, "Test #%d: %s", n, tc->tname);
90
91 fd_notify = fanotify_init(tc->init_flags, O_RDONLY);
92 if (fd_notify < 0) {
93 if (errno == EINVAL &&
94 (tc->init_flags & FAN_REPORT_FID)) {
95 tst_res(TCONF,
96 "FAN_REPORT_FID not supported in kernel?");
97 return;
98 }
99 tst_brk(TBROK | TERRNO,
100 "fanotify_init (0x%x, O_RDONLY) "
101 "failed", tc->init_flags);
102 }
103
104 if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag,
105 FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN,
106 AT_FDCWD, fname) < 0) {
107 if (errno == EINVAL && mark->flag == FAN_MARK_FILESYSTEM) {
108 tst_res(TCONF,
109 "FAN_MARK_FILESYSTEM not supported in kernel?");
110 return;
111 }
112 tst_brk(TBROK | TERRNO,
113 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS | %s | "
114 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) "
115 "failed", fd_notify, mark->name, fname);
116 }
117
118 /*
119 * generate sequence of events
120 */
121 fd = SAFE_OPEN(fname, O_RDONLY);
122 event_set[tst_count] = FAN_OPEN;
123 tst_count++;
124
125 SAFE_READ(0, fd, buf, BUF_SIZE);
126 event_set[tst_count] = FAN_ACCESS;
127 tst_count++;
128
129 SAFE_CLOSE(fd);
130 event_set[tst_count] = FAN_CLOSE_NOWRITE;
131 tst_count++;
132
133 /*
134 * Get list of events so far. We get events here to avoid
135 * merging of following events with the previous ones.
136 */
137 ret = SAFE_READ(0, fd_notify, event_buf, EVENT_BUF_LEN);
138 len = ret;
139
140 fd = SAFE_OPEN(fname, O_RDWR | O_CREAT, 0700);
141 event_set[tst_count] = FAN_OPEN;
142 tst_count++;
143
144 SAFE_WRITE(1, fd, fname, strlen(fname));
145 event_set[tst_count] = FAN_MODIFY;
146 tst_count++;
147
148 SAFE_CLOSE(fd);
149 event_set[tst_count] = FAN_CLOSE_WRITE;
150 tst_count++;
151
152 /*
153 * get another list of events
154 */
155 ret = SAFE_READ(0, fd_notify, event_buf + len,
156 EVENT_BUF_LEN - len);
157 len += ret;
158
159 /*
160 * Ignore mask testing
161 */
162
163 /* Ignore access events */
164 if (fanotify_mark(fd_notify,
165 FAN_MARK_ADD | mark->flag | FAN_MARK_IGNORED_MASK,
166 FAN_ACCESS, AT_FDCWD, fname) < 0) {
167 tst_brk(TBROK | TERRNO,
168 "fanotify_mark (%d, FAN_MARK_ADD | %s | "
169 "FAN_MARK_IGNORED_MASK, FAN_ACCESS, AT_FDCWD, %s) "
170 "failed", fd_notify, mark->name, fname);
171 }
172
173 fd = SAFE_OPEN(fname, O_RDWR);
174 event_set[tst_count] = FAN_OPEN;
175 tst_count++;
176
177 /* This event should be ignored */
178 SAFE_READ(0, fd, buf, BUF_SIZE);
179
180 /*
181 * get another list of events to verify the last one got ignored
182 */
183 ret = SAFE_READ(0, fd_notify, event_buf + len,
184 EVENT_BUF_LEN - len);
185 len += ret;
186
187 lseek(fd, 0, SEEK_SET);
188 /* Generate modify event to clear ignore mask */
189 SAFE_WRITE(1, fd, fname, 1);
190 event_set[tst_count] = FAN_MODIFY;
191 tst_count++;
192
193 /*
194 * This event shouldn't be ignored because previous modification
195 * should have removed the ignore mask
196 */
197 SAFE_READ(0, fd, buf, BUF_SIZE);
198 event_set[tst_count] = FAN_ACCESS;
199 tst_count++;
200
201 SAFE_CLOSE(fd);
202 event_set[tst_count] = FAN_CLOSE_WRITE;
203 tst_count++;
204
205 /* Read events to verify previous access was properly generated */
206 ret = SAFE_READ(0, fd_notify, event_buf + len,
207 EVENT_BUF_LEN - len);
208 len += ret;
209
210 /*
211 * Now ignore open & close events regardless of file
212 * modifications
213 */
214 if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag |
215 FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY,
216 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) {
217 tst_brk(TBROK | TERRNO,
218 "fanotify_mark (%d, FAN_MARK_ADD | %s | "
219 "FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY, "
220 "FAN_OPEN | FAN_CLOSE, AT_FDCWD, %s) failed",
221 fd_notify, mark->name, fname);
222 }
223
224 /* This event should be ignored */
225 fd = SAFE_OPEN(fname, O_RDWR);
226
227 SAFE_WRITE(1, fd, fname, 1);
228 event_set[tst_count] = FAN_MODIFY;
229 tst_count++;
230
231 /* This event should be still ignored */
232 SAFE_CLOSE(fd);
233
234 /* This event should still be ignored */
235 fd = SAFE_OPEN(fname, O_RDWR);
236
237 /* Read events to verify open & close were ignored */
238 ret = SAFE_READ(0, fd_notify, event_buf + len,
239 EVENT_BUF_LEN - len);
240 len += ret;
241
242 /* Now remove open and close from ignored mask */
243 if (fanotify_mark(fd_notify,
244 FAN_MARK_REMOVE | mark->flag | FAN_MARK_IGNORED_MASK,
245 FAN_OPEN | FAN_CLOSE, AT_FDCWD, fname) < 0) {
246 tst_brk(TBROK | TERRNO,
247 "fanotify_mark (%d, FAN_MARK_REMOVE | %s | "
248 "FAN_MARK_IGNORED_MASK, FAN_OPEN | FAN_CLOSE, "
249 "AT_FDCWD, %s) failed", fd_notify,
250 mark->name, fname);
251 }
252
253 SAFE_CLOSE(fd);
254 event_set[tst_count] = FAN_CLOSE_WRITE;
255 tst_count++;
256
257 /* Read events to verify close was generated */
258 ret = SAFE_READ(0, fd_notify, event_buf + len,
259 EVENT_BUF_LEN - len);
260 len += ret;
261
262 if (TST_TOTAL != tst_count) {
263 tst_brk(TBROK,
264 "TST_TOTAL (%d) and tst_count (%d) are not "
265 "equal", TST_TOTAL, tst_count);
266 }
267 tst_count = 0;
268
269 /*
270 * check events
271 */
272 while (i < len) {
273 struct fanotify_event_metadata *event;
274
275 event = (struct fanotify_event_metadata *)&event_buf[i];
276 if (test_num >= TST_TOTAL) {
277 tst_res(TFAIL,
278 "got unnecessary event: mask=%llx "
279 "pid=%u fd=%d",
280 (unsigned long long)event->mask,
281 (unsigned)event->pid, event->fd);
282 } else if (!(event->mask & event_set[test_num])) {
283 tst_res(TFAIL,
284 "got event: mask=%llx (expected %llx) "
285 "pid=%u fd=%d",
286 (unsigned long long)event->mask,
287 event_set[test_num],
288 (unsigned)event->pid, event->fd);
289 } else if (event->pid != getpid()) {
290 tst_res(TFAIL,
291 "got event: mask=%llx pid=%u "
292 "(expected %u) fd=%d",
293 (unsigned long long)event->mask,
294 (unsigned)event->pid,
295 (unsigned)getpid(),
296 event->fd);
297 } else {
298 if (event->fd == -2 || (event->fd == FAN_NOFD &&
299 (tc->init_flags & FAN_REPORT_FID)))
300 goto pass;
301 ret = read(event->fd, buf, BUF_SIZE);
302 if (ret != (int)strlen(fname)) {
303 tst_res(TFAIL,
304 "cannot read from returned fd "
305 "of event: mask=%llx pid=%u "
306 "fd=%d ret=%d (errno=%d)",
307 (unsigned long long)event->mask,
308 (unsigned)event->pid,
309 event->fd, ret, errno);
310 } else if (memcmp(buf, fname, strlen(fname))) {
311 tst_res(TFAIL,
312 "wrong data read from returned fd "
313 "of event: mask=%llx pid=%u "
314 "fd=%d",
315 (unsigned long long)event->mask,
316 (unsigned)event->pid,
317 event->fd);
318 } else {
319 pass:
320 tst_res(TPASS,
321 "got event: mask=%llx pid=%u fd=%d",
322 (unsigned long long)event->mask,
323 (unsigned)event->pid, event->fd);
324 }
325 }
326 /*
327 * We have verified the data now so close fd and
328 * invalidate it so that we don't check it again
329 * unnecessarily
330 */
331 if (event->fd >= 0)
332 SAFE_CLOSE(event->fd);
333 event->fd = -2;
334 event->mask &= ~event_set[test_num];
335 /* No events left in current mask? Go for next event */
336 if (event->mask == 0) {
337 i += event->event_len;
338 }
339 test_num++;
340 }
341 for (; test_num < TST_TOTAL; test_num++) {
342 tst_res(TFAIL, "didn't get event: mask=%llx",
343 event_set[test_num]);
344
345 }
346 /* Remove mark to clear FAN_MARK_IGNORED_SURV_MODIFY */
347 if (fanotify_mark(fd_notify, FAN_MARK_REMOVE | mark->flag,
348 FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN,
349 AT_FDCWD, fname) < 0) {
350 tst_brk(TBROK | TERRNO,
351 "fanotify_mark (%d, FAN_MARK_REMOVE | %s, FAN_ACCESS | "
352 "FAN_MODIFY | FAN_CLOSE | FAN_OPEN, AT_FDCWD, %s) "
353 "failed", fd_notify, mark->name, fname);
354 }
355
356 SAFE_CLOSE(fd_notify);
357 }
358
setup(void)359 static void setup(void)
360 {
361 int fd;
362
363 /* Check for kernel fanotify support */
364 fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
365 SAFE_CLOSE(fd);
366
367 sprintf(fname, MOUNT_PATH"/tfile_%d", getpid());
368 SAFE_FILE_PRINTF(fname, "1");
369 }
370
cleanup(void)371 static void cleanup(void)
372 {
373 if (fd_notify > 0)
374 SAFE_CLOSE(fd_notify);
375 }
376
377 static struct tst_test test = {
378 .test = test_fanotify,
379 .tcnt = ARRAY_SIZE(tcases),
380 .setup = setup,
381 .cleanup = cleanup,
382 .needs_root = 1,
383 .mount_device = 1,
384 .mntpoint = MOUNT_PATH,
385 };
386
387 #else
388 TST_TEST_TCONF("system doesn't have required fanotify support");
389 #endif
390