xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fanotify/fanotify13.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
4  *
5  * Started by Matthew Bobrowski <[email protected]>
6  */
7 
8 /*\
9  * [Description]
10  * Validate that the values returned within an event when FAN_REPORT_FID is
11  * specified matches those that are obtained via explicit invocation to system
12  * calls statfs(2) and name_to_handle_at(2).
13  */
14 
15 /*
16  * This is also regression test for:
17  *     c285a2f01d69 ("fanotify: update connector fsid cache on add mark")
18  */
19 
20 #define _GNU_SOURCE
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/statfs.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/mount.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include "tst_test.h"
32 
33 #ifdef HAVE_SYS_FANOTIFY_H
34 #include "fanotify.h"
35 
36 #define PATH_LEN 128
37 #define BUF_SIZE 256
38 #define DIR_ONE "dir_one"
39 #define FILE_ONE "file_one"
40 #define FILE_TWO "file_two"
41 #define MOUNT_PATH "tstmnt"
42 #define EVENT_MAX ARRAY_SIZE(objects)
43 #define DIR_PATH_ONE MOUNT_PATH"/"DIR_ONE
44 #define FILE_PATH_ONE MOUNT_PATH"/"FILE_ONE
45 #define FILE_PATH_TWO MOUNT_PATH"/"FILE_TWO
46 
47 #if defined(HAVE_NAME_TO_HANDLE_AT)
48 struct event_t {
49 	unsigned long long expected_mask;
50 };
51 
52 static struct object_t {
53 	const char *path;
54 	int is_dir;
55 	struct fanotify_fid_t fid;
56 } objects[] = {
57 	{FILE_PATH_ONE, 0, {}},
58 	{FILE_PATH_TWO, 0, {}},
59 	{DIR_PATH_ONE, 1, {}}
60 };
61 
62 static struct test_case_t {
63 	struct fanotify_mark_type mark;
64 	unsigned long long mask;
65 } test_cases[] = {
66 	{
67 		INIT_FANOTIFY_MARK_TYPE(INODE),
68 		FAN_OPEN | FAN_CLOSE_NOWRITE
69 	},
70 	{
71 		INIT_FANOTIFY_MARK_TYPE(INODE),
72 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
73 	},
74 	{
75 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
76 		FAN_OPEN | FAN_CLOSE_NOWRITE
77 	},
78 	{
79 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
80 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
81 	},
82 	{
83 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
84 		FAN_OPEN | FAN_CLOSE_NOWRITE
85 	},
86 	{
87 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
88 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
89 	}
90 };
91 
92 static int ovl_mounted;
93 static int bind_mounted;
94 static int ovl_bind_mounted;
95 static int nofid_fd;
96 static int fanotify_fd;
97 static int at_handle_fid;
98 static int filesystem_mark_unsupported;
99 static char events_buf[BUF_SIZE];
100 static struct event_t event_set[EVENT_MAX];
101 
create_objects(void)102 static void create_objects(void)
103 {
104 	unsigned int i;
105 
106 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
107 		if (objects[i].is_dir)
108 			SAFE_MKDIR(objects[i].path, 0755);
109 		else
110 			SAFE_FILE_PRINTF(objects[i].path, "0");
111 	}
112 }
113 
get_object_stats(void)114 static void get_object_stats(void)
115 {
116 	unsigned int i;
117 
118 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
119 		at_handle_fid |=
120 			fanotify_save_fid(objects[i].path, &objects[i].fid);
121 	}
122 }
123 
setup_marks(unsigned int fd,struct test_case_t * tc)124 static int setup_marks(unsigned int fd, struct test_case_t *tc)
125 {
126 	unsigned int i;
127 	struct fanotify_mark_type *mark = &tc->mark;
128 
129 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
130 		SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | mark->flag, tc->mask,
131 				   AT_FDCWD, objects[i].path);
132 
133 		/* Setup the expected mask for each generated event */
134 		event_set[i].expected_mask = tc->mask;
135 		if (!objects[i].is_dir)
136 			event_set[i].expected_mask &= ~FAN_ONDIR;
137 	}
138 	return 0;
139 }
140 
do_test(unsigned int number)141 static void do_test(unsigned int number)
142 {
143 	unsigned int i;
144 	int len, fds[ARRAY_SIZE(objects)];
145 
146 	struct file_handle *event_file_handle;
147 	struct fanotify_event_metadata *metadata;
148 	struct fanotify_event_info_fid *event_fid;
149 	struct test_case_t *tc = &test_cases[number];
150 	struct fanotify_mark_type *mark = &tc->mark;
151 
152 	tst_res(TINFO,
153 		"Test #%d.%d: FAN_REPORT_FID with mark flag: %s",
154 		number, tst_variant, mark->name);
155 
156 	if (tst_variant && !ovl_mounted) {
157 		tst_res(TCONF, "overlayfs not supported on %s", tst_device->fs_type);
158 		return;
159 	}
160 
161 	if (filesystem_mark_unsupported && mark->flag != FAN_MARK_INODE) {
162 		FANOTIFY_MARK_FLAGS_ERR_MSG(mark, filesystem_mark_unsupported);
163 		return;
164 	}
165 
166 	fanotify_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
167 
168 	/*
169 	 * Place marks on a set of objects and setup the expected masks
170 	 * for each event that is expected to be generated.
171 	 */
172 	if (setup_marks(fanotify_fd, tc) != 0)
173 		goto out;
174 
175 	/* Watching base fs - open files on overlayfs */
176 	if (tst_variant && !ovl_bind_mounted) {
177 		if (mark->flag & FAN_MARK_MOUNT) {
178 			tst_res(TCONF, "overlayfs base fs cannot be watched with mount mark");
179 			goto out;
180 		}
181 		SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL);
182 	}
183 
184 	/* Generate sequence of FAN_OPEN events on objects */
185 	for (i = 0; i < ARRAY_SIZE(objects); i++)
186 		fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY);
187 
188 	/*
189 	 * Generate sequence of FAN_CLOSE_NOWRITE events on objects. Each
190 	 * FAN_CLOSE_NOWRITE event is expected to be merged with its
191 	 * respective FAN_OPEN event that was performed on the same object.
192 	 */
193 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
194 		if (fds[i] > 0)
195 			SAFE_CLOSE(fds[i]);
196 	}
197 
198 	if (tst_variant && !ovl_bind_mounted)
199 		SAFE_UMOUNT(MOUNT_PATH);
200 
201 	/* Read events from event queue */
202 	len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
203 
204 	/* Iterate over event queue */
205 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
206 		FAN_EVENT_OK(metadata, len);
207 		metadata = FAN_EVENT_NEXT(metadata, len), i++) {
208 		struct fanotify_fid_t *expected_fid = &objects[i].fid;
209 
210 		event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
211 		event_file_handle = (struct file_handle *) event_fid->handle;
212 
213 		/* File descriptor is redundant with FAN_REPORT_FID */
214 		if (metadata->fd != FAN_NOFD)
215 			tst_res(TFAIL,
216 				"Unexpectedly received file descriptor %d in "
217 				"event. Expected to get FAN_NOFD(%d)",
218 				metadata->fd, FAN_NOFD);
219 
220 		/* Ensure that the correct mask has been reported in event */
221 		if (metadata->mask != event_set[i].expected_mask)
222 			tst_res(TFAIL,
223 				"Unexpected mask received: %llx (expected: "
224 				"%llx) in event",
225 				metadata->mask,
226 				event_set[i].expected_mask);
227 
228 		/* Verify handle_bytes returned in event */
229 		if (event_file_handle->handle_bytes !=
230 		    expected_fid->handle.handle_bytes) {
231 			tst_res(TFAIL,
232 				"handle_bytes (%x) returned in event does not "
233 				"equal to handle_bytes (%x) returned in "
234 				"name_to_handle_at(2)",
235 				event_file_handle->handle_bytes,
236 				expected_fid->handle.handle_bytes);
237 			continue;
238 		}
239 
240 		/* Verify handle_type returned in event */
241 		if (event_file_handle->handle_type !=
242 		    expected_fid->handle.handle_type) {
243 			tst_res(TFAIL,
244 				"handle_type (%x) returned in event does not "
245 				"equal to handle_type (%x) returned in "
246 				"name_to_handle_at(2)",
247 				event_file_handle->handle_type,
248 				expected_fid->handle.handle_type);
249 			continue;
250 		}
251 
252 		/* Verify file identifier f_handle returned in event */
253 		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 			continue;
261 		}
262 
263 		/* Verify filesystem ID fsid  returned in event */
264 		if (memcmp(&event_fid->fsid, &expected_fid->fsid,
265 			   sizeof(expected_fid->fsid)) != 0) {
266 			tst_res(TFAIL,
267 				"event_fid.fsid != stat.f_fsid that was "
268 				"obtained via statfs(2)");
269 			continue;
270 		}
271 
272 		tst_res(TPASS,
273 			"got event: mask=%llx, pid=%d, fid=%x.%x.%lx values "
274 			"returned in event match those returned in statfs(2) "
275 			"and name_to_handle_at(2)",
276 			metadata->mask,
277 			getpid(),
278 			FSID_VAL_MEMBER(event_fid->fsid, 0),
279 			FSID_VAL_MEMBER(event_fid->fsid, 1),
280 			*(unsigned long *) event_file_handle->f_handle);
281 	}
282 out:
283 	SAFE_CLOSE(fanotify_fd);
284 }
285 
do_setup(void)286 static void do_setup(void)
287 {
288 	const char *mnt;
289 
290 	/*
291 	 * Bind mount to either base fs or to overlayfs over base fs:
292 	 * Variant #0: watch base fs - open files on base fs
293 	 * Variant #1: watch lower fs - open lower files on overlayfs
294 	 * Variant #2: watch upper fs - open upper files on overlayfs
295 	 * Variant #3: watch overlayfs - open lower files on overlayfs
296 	 * Variant #4: watch overlayfs - open upper files on overlayfs
297 	 *
298 	 * Variants 1,2 test a bug whose fix bc2473c90fca ("ovl: enable fsnotify
299 	 * events on underlying real files") in kernel 6.5 is not likely to be
300 	 * backported to older kernels.
301 	 * To avoid waiting for events that won't arrive when testing old kernels,
302 	 * require that kernel supports encoding fid with new flag AT_HANDLE_FID,
303 	 * also merged to 6.5 and not likely to be backported to older kernels.
304 	 * Variants 3,4 test overlayfs watch with FAN_REPORT_FID, which also
305 	 * requires kernel with support for AT_HANDLE_FID.
306 	 */
307 	if (tst_variant) {
308 		REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(AT_HANDLE_FID);
309 		ovl_mounted = TST_MOUNT_OVERLAY();
310 		if (!ovl_mounted)
311 			return;
312 
313 		mnt = tst_variant & 1 ? OVL_LOWER : OVL_UPPER;
314 	} else {
315 		mnt = OVL_BASE_MNTPOINT;
316 
317 	}
318 	REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, mnt);
319 	SAFE_MKDIR(MOUNT_PATH, 0755);
320 	SAFE_MOUNT(mnt, MOUNT_PATH, "none", MS_BIND, NULL);
321 	bind_mounted = 1;
322 
323 	nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
324 
325 	/* Create file and directory objects for testing on base fs */
326 	create_objects();
327 
328 	if (tst_variant > 2) {
329 		/* Setup watches on overlayfs */
330 		SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL);
331 		ovl_bind_mounted = 1;
332 	}
333 
334 	filesystem_mark_unsupported =
335 		fanotify_flags_supported_on_fs(FAN_REPORT_FID, FAN_MARK_FILESYSTEM, FAN_OPEN,
336 					       ovl_bind_mounted ? OVL_MNT : MOUNT_PATH);
337 
338 	/*
339 	 * Create a mark on first inode without FAN_REPORT_FID, to test
340 	 * uninitialized connector->fsid cache. This mark remains for all test
341 	 * cases and is not expected to get any events (no writes in this test).
342 	 */
343 	SAFE_FANOTIFY_MARK(nofid_fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, AT_FDCWD,
344 			  FILE_PATH_ONE);
345 
346 	/* Get the filesystem fsid and file handle for each created object */
347 	get_object_stats();
348 }
349 
do_cleanup(void)350 static void do_cleanup(void)
351 {
352 	if (nofid_fd > 0)
353 		SAFE_CLOSE(nofid_fd);
354 	if (fanotify_fd > 0)
355 		SAFE_CLOSE(fanotify_fd);
356 	if (ovl_bind_mounted)
357 		SAFE_UMOUNT(MOUNT_PATH);
358 	if (bind_mounted) {
359 		SAFE_UMOUNT(MOUNT_PATH);
360 		SAFE_RMDIR(MOUNT_PATH);
361 	}
362 	if (ovl_mounted)
363 		SAFE_UMOUNT(OVL_MNT);
364 }
365 
366 static struct tst_test test = {
367 	.test = do_test,
368 	.tcnt = ARRAY_SIZE(test_cases),
369 	.test_variants = 5,
370 	.setup = do_setup,
371 	.cleanup = do_cleanup,
372 	.needs_root = 1,
373 	.mount_device = 1,
374 	.mntpoint = OVL_BASE_MNTPOINT,
375 	.all_filesystems = 1,
376 	.tags = (const struct tst_tag[]) {
377 		{"linux-git", "c285a2f01d69"},
378 		{"linux-git", "bc2473c90fca"},
379 		{}
380 	}
381 };
382 
383 #else
384 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
385 #endif
386 #else
387 	TST_TEST_TCONF("System does not have required fanotify support");
388 #endif
389