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