• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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