• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 CTERA Networks. All Rights Reserved.
4  *
5  * Started by Amir Goldstein <amir73il@gmail.com>
6  * Modified by Matthew Bobrowski <mbobrowski@mbobrowski.org>
7  *
8  * DESCRIPTION
9  *	Test file that has been purposely designed to verify
10  *	FAN_REPORT_FID functionality while using newly defined dirent
11  *	events.
12  *
13  * Test case #1 is a regression test for commit f367a62a7cad:
14  *
15  *      fanotify: merge duplicate events on parent and child
16  */
17 #define _GNU_SOURCE
18 #include "config.h"
19 
20 #include <string.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/statfs.h>
24 #include <sys/types.h>
25 
26 #include "tst_test.h"
27 #include "fanotify.h"
28 
29 #if defined(HAVE_SYS_FANOTIFY_H)
30 #include <sys/fanotify.h>
31 
32 #define EVENT_MAX 10
33 
34 /* Size of the event structure, not including file handle */
35 #define EVENT_SIZE (sizeof(struct fanotify_event_metadata) + \
36 		    sizeof(struct fanotify_event_info_fid))
37 /* Double events buffer size to account for file handles */
38 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE * 2)
39 
40 
41 #define MOUNT_POINT "mntpoint"
42 #define TEST_DIR MOUNT_POINT"/test_dir"
43 #define DIR1 TEST_DIR"/dir1"
44 #define DIR2 TEST_DIR"/dir2"
45 #define FILE1 TEST_DIR"/file1"
46 #define FILE2 TEST_DIR"/file2"
47 
48 #if defined(HAVE_NAME_TO_HANDLE_AT)
49 struct event_t {
50 	unsigned long long mask;
51 	struct fanotify_fid_t *fid;
52 };
53 
54 static int fanotify_fd;
55 static char events_buf[EVENT_BUF_LEN];
56 static struct event_t event_set[EVENT_MAX];
57 
58 static struct test_case_t {
59 	const char *tname;
60 	struct fanotify_mark_type mark;
61 	unsigned long mask;
62 } test_cases[] = {
63 	{
64 		"FAN_REPORT_FID on filesystem including FAN_DELETE_SELF",
65 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
66 		FAN_DELETE_SELF,
67 	},
68 	{
69 		"FAN_REPORT_FID on directory with FAN_EVENT_ON_CHILD",
70 		INIT_FANOTIFY_MARK_TYPE(INODE),
71 		FAN_EVENT_ON_CHILD,
72 	},
73 };
74 
do_test(unsigned int number)75 static void do_test(unsigned int number)
76 {
77 	int i, fd, len, count = 0;
78 
79 	struct file_handle *event_file_handle;
80 	struct fanotify_event_metadata *metadata;
81 	struct fanotify_event_info_fid *event_fid;
82 	struct test_case_t *tc = &test_cases[number];
83 	struct fanotify_mark_type *mark = &tc->mark;
84 	struct fanotify_fid_t root_fid, dir_fid, file_fid;
85 
86 	tst_res(TINFO, "Test #%d: %s", number, tc->tname);
87 
88 	if (fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag, tc->mask |
89 				FAN_CREATE | FAN_DELETE | FAN_MOVE |
90 				FAN_MODIFY | FAN_ONDIR,
91 				AT_FDCWD, TEST_DIR) == -1) {
92 		if (errno == ENODEV)
93 			tst_brk(TCONF,
94 				"FAN_REPORT_FID not supported on %s "
95 				"filesystem", tst_device->fs_type);
96 		tst_brk(TBROK | TERRNO,
97 			"fanotify_mark(%d, FAN_MARK_ADD | %s, "
98 			"FAN_CREATE | FAN_DELETE | FAN_MOVE | "
99 			"FAN_MODIFY | FAN_ONDIR | 0x%lx, "
100 			"AT_FDCWD, %s) failed",
101 			fanotify_fd, mark->name, tc->mask, TEST_DIR);
102 	}
103 
104 	/* Save the test root dir fid */
105 	fanotify_save_fid(TEST_DIR, &root_fid);
106 
107 	/* All dirent events on testdir are merged */
108 	event_set[count].mask = FAN_CREATE | FAN_MOVE | FAN_DELETE;
109 	event_set[count].fid = &root_fid;
110 	count++;
111 
112 	fd = SAFE_CREAT(FILE1, 0644);
113 	SAFE_CLOSE(fd);
114 
115 	/* Save the file fid */
116 	fanotify_save_fid(FILE1, &file_fid);
117 
118 	/* Recursive watch file for events "on self" */
119 	if (mark->flag == FAN_MARK_INODE &&
120 	    fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
121 			  FAN_MODIFY | FAN_DELETE_SELF,
122 			  AT_FDCWD, FILE1) == -1) {
123 		tst_brk(TBROK | TERRNO,
124 			"fanotify_mark(%d, FAN_MARK_ADD | %s, "
125 			"FAN_DELETE_SELF, AT_FDCWD, %s) failed",
126 			fanotify_fd, mark->name, FILE1);
127 	}
128 
129 	/*
130 	 * Event on child file is not merged with dirent events.
131 	 * FAN_MODIFY event reported on file mark should be merged with the
132 	 * FAN_MODIFY event reported on parent directory watch.
133 	 */
134 	event_set[count].mask = FAN_MODIFY;
135 	event_set[count].fid = &file_fid;
136 	count++;
137 
138 	SAFE_TRUNCATE(FILE1, 1);
139 	SAFE_RENAME(FILE1, FILE2);
140 
141 	/*
142 	 * FAN_DELETE_SELF may be merged with FAN_MODIFY event above.
143 	 */
144 	event_set[count].mask = FAN_DELETE_SELF;
145 	event_set[count].fid = &file_fid;
146 	count++;
147 
148 	SAFE_UNLINK(FILE2);
149 
150 	/* Read file events from the event queue */
151 	len = SAFE_READ(0, fanotify_fd, events_buf, EVENT_BUF_LEN);
152 
153 	/*
154 	 * Generate a sequence of events on a directory. Subsequent events
155 	 * are merged, so it's required that we set FAN_ONDIR once in
156 	 * order to acknowledge that changes related to a subdirectory
157 	 * took place. Events on subdirectories are not merged with events
158 	 * on non-subdirectories.
159 	 */
160 	event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVE | FAN_DELETE;
161 	event_set[count].fid = &root_fid;
162 	count++;
163 
164 	SAFE_MKDIR(DIR1, 0755);
165 
166 	/* Save the subdir fid */
167 	fanotify_save_fid(DIR1, &dir_fid);
168 
169 	/* Recursive watch subdir for events "on self" */
170 	if (mark->flag == FAN_MARK_INODE &&
171 	    fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
172 			  FAN_DELETE_SELF | FAN_ONDIR,
173 			  AT_FDCWD, DIR1) == -1) {
174 		tst_brk(TBROK | TERRNO,
175 			"fanotify_mark(%d, FAN_MARK_ADD | %s,"
176 			"FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed",
177 			fanotify_fd, mark->name, DIR1);
178 	}
179 
180 	SAFE_RENAME(DIR1, DIR2);
181 
182 	event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF;
183 	event_set[count].fid = &dir_fid;
184 	count++;
185 
186 	SAFE_RMDIR(DIR2);
187 
188 	/* Read dir events from the event queue */
189 	len += SAFE_READ(0, fanotify_fd, events_buf + len, EVENT_BUF_LEN - len);
190 
191 	/*
192 	 * Cleanup the mark
193 	 */
194 	if (fanotify_mark(fanotify_fd, FAN_MARK_FLUSH | mark->flag, 0,
195 			  AT_FDCWD, TEST_DIR) < 0) {
196 		tst_brk(TBROK | TERRNO,
197 			"fanotify_mark (%d, FAN_MARK_FLUSH | %s, 0,"
198 			"AT_FDCWD, '"TEST_DIR"') failed",
199 			fanotify_fd, mark->name);
200 	}
201 
202 	/* Process each event in buffer */
203 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
204 		FAN_EVENT_OK(metadata, len); i++) {
205 		struct event_t *expected = &event_set[i];
206 		event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
207 		event_file_handle = (struct file_handle *) event_fid->handle;
208 
209 		if (i >= count) {
210 			tst_res(TFAIL,
211 				"got unnecessary event: mask=%llx "
212 				"pid=%u fd=%d",
213 				(unsigned long long) metadata->mask,
214 				metadata->pid,
215 				metadata->fd);
216 			metadata->mask = 0;
217 		} else if (metadata->fd != FAN_NOFD) {
218 			tst_res(TFAIL,
219 				"Received unexpected file descriptor %d in "
220 				"event. Expected to get FAN_NOFD(%d)",
221 				metadata->fd, FAN_NOFD);
222 		} else if (!(metadata->mask & expected->mask)) {
223 			tst_res(TFAIL,
224 				"Got event: mask=%llx (expected %llx) "
225 				"pid=%u fd=%d",
226 				(unsigned long long) metadata->mask,
227 				expected->mask, (unsigned) metadata->pid,
228 				metadata->fd);
229 		} else if (metadata->pid != getpid()) {
230 			tst_res(TFAIL,
231 				"Got event: mask=%llx pid=%u "
232 				"(expected %u) fd=%d",
233 				(unsigned long long) metadata->mask,
234 				(unsigned) metadata->pid,
235 				(unsigned) getpid(),
236 				metadata->fd);
237 		} else if (event_file_handle->handle_bytes !=
238 			   expected->fid->handle.handle_bytes) {
239 			tst_res(TFAIL,
240 				"Got event: handle_bytes (%x) returned in "
241 				"event does not equal handle_bytes (%x) "
242 				"retunred in name_to_handle_at(2)",
243 				event_file_handle->handle_bytes,
244 				expected->fid->handle.handle_bytes);
245 		} else if (event_file_handle->handle_type !=
246 			   expected->fid->handle.handle_type) {
247 			tst_res(TFAIL,
248 				"handle_type (%x) returned in event does not "
249 				"equal to handle_type (%x) returned in "
250 				"name_to_handle_at(2)",
251 				event_file_handle->handle_type,
252 				expected->fid->handle.handle_type);
253 		} else if (memcmp(event_file_handle->f_handle,
254 				  expected->fid->handle.f_handle,
255 				  expected->fid->handle.handle_bytes) != 0) {
256 			tst_res(TFAIL,
257 				"file_handle returned in event does not match "
258 				"the file_handle returned in "
259 				"name_to_handle_at(2)");
260 		} else if (memcmp(&event_fid->fsid, &expected->fid->fsid,
261 				  sizeof(event_fid->fsid)) != 0) {
262 			tst_res(TFAIL,
263 				"event_fid->fsid != stats.f_fsid that was "
264 				"obtained via statfs(2)");
265 		} else {
266 			tst_res(TPASS,
267 				"Got event: mask=%llx, pid=%u, "
268 				"fid=%x.%x.%lx values",
269 				metadata->mask,
270 				getpid(),
271 				FSID_VAL_MEMBER(event_fid->fsid, 0),
272 				FSID_VAL_MEMBER(event_fid->fsid, 1),
273 				*(unsigned long *)
274 				event_file_handle->f_handle);
275 		}
276 		metadata->mask  &= ~expected->mask;
277 		/* No events left in current mask? Go for next event */
278 		if (metadata->mask == 0)
279 			metadata = FAN_EVENT_NEXT(metadata, len);
280 	}
281 
282 	for (; i < count; i++)
283 		tst_res(TFAIL,
284 			"Didn't receive event: mask=%llx",
285 			event_set[i].mask);
286 }
287 
do_setup(void)288 static void do_setup(void)
289 {
290 	int fd;
291 
292 	/* Check kernel for fanotify support */
293 	fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
294 	SAFE_CLOSE(fd);
295 
296 	fanotify_fd = fanotify_init(FAN_REPORT_FID, O_RDONLY);
297 	if (fanotify_fd == -1) {
298 		if (errno == EINVAL)
299 			tst_brk(TCONF,
300 				"FAN_REPORT_FID not supported in kernel");
301 		tst_brk(TBROK | TERRNO,
302 			"fanotify_init(FAN_REPORT_FID, O_RDONLY) failed");
303 	}
304 
305 	SAFE_MKDIR(TEST_DIR, 0755);
306 }
307 
do_cleanup(void)308 static void do_cleanup(void)
309 {
310 	if (fanotify_fd > 0)
311 		SAFE_CLOSE(fanotify_fd);
312 }
313 
314 static struct tst_test test = {
315 	.needs_root = 1,
316 	.mount_device = 1,
317 	.mntpoint = MOUNT_POINT,
318 	.all_filesystems = 1,
319 	.test = do_test,
320 	.tcnt = ARRAY_SIZE(test_cases),
321 	.setup = do_setup,
322 	.cleanup = do_cleanup,
323 	.tags = (const struct tst_tag[]) {
324 		{"linux-git", "f367a62a7cad"},
325 		{}
326 	}
327 };
328 
329 #else
330 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
331 #endif
332 #else
333 	TST_TEST_TCONF("System does not have required fanotify support");
334 #endif
335