• 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 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