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