• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved.
4  *
5  * Started by Matthew Bobrowski <mbobrowski@mbobrowski.org>
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 nofid_fd;
95 static int fanotify_fd;
96 static int filesystem_mark_unsupported;
97 static char events_buf[BUF_SIZE];
98 static struct event_t event_set[EVENT_MAX];
99 
create_objects(void)100 static void create_objects(void)
101 {
102 	unsigned int i;
103 
104 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
105 		if (objects[i].is_dir)
106 			SAFE_MKDIR(objects[i].path, 0755);
107 		else
108 			SAFE_FILE_PRINTF(objects[i].path, "0");
109 	}
110 }
111 
get_object_stats(void)112 static void get_object_stats(void)
113 {
114 	unsigned int i;
115 
116 	for (i = 0; i < ARRAY_SIZE(objects); i++)
117 		fanotify_save_fid(objects[i].path, &objects[i].fid);
118 }
119 
setup_marks(unsigned int fd,struct test_case_t * tc)120 static int setup_marks(unsigned int fd, struct test_case_t *tc)
121 {
122 	unsigned int i;
123 	struct fanotify_mark_type *mark = &tc->mark;
124 
125 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
126 		SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | mark->flag, tc->mask,
127 				   AT_FDCWD, objects[i].path);
128 
129 		/* Setup the expected mask for each generated event */
130 		event_set[i].expected_mask = tc->mask;
131 		if (!objects[i].is_dir)
132 			event_set[i].expected_mask &= ~FAN_ONDIR;
133 	}
134 	return 0;
135 }
136 
do_test(unsigned int number)137 static void do_test(unsigned int number)
138 {
139 	unsigned int i;
140 	int len, fds[ARRAY_SIZE(objects)];
141 
142 	struct file_handle *event_file_handle;
143 	struct fanotify_event_metadata *metadata;
144 	struct fanotify_event_info_fid *event_fid;
145 	struct test_case_t *tc = &test_cases[number];
146 	struct fanotify_mark_type *mark = &tc->mark;
147 
148 	tst_res(TINFO,
149 		"Test #%d.%d: FAN_REPORT_FID with mark flag: %s",
150 		number, tst_variant, mark->name);
151 
152 	if (tst_variant && !ovl_mounted) {
153 		tst_res(TCONF, "overlayfs not supported on %s", tst_device->fs_type);
154 		return;
155 	}
156 
157 	if (filesystem_mark_unsupported && mark->flag & FAN_MARK_FILESYSTEM) {
158 		tst_res(TCONF, "FAN_MARK_FILESYSTEM not supported in kernel?");
159 		return;
160 	}
161 
162 	fanotify_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
163 
164 	/*
165 	 * Place marks on a set of objects and setup the expected masks
166 	 * for each event that is expected to be generated.
167 	 */
168 	if (setup_marks(fanotify_fd, tc) != 0)
169 		goto out;
170 
171 	/* Variant #1: watching upper fs - open files on overlayfs */
172 	if (tst_variant == 1) {
173 		if (mark->flag & FAN_MARK_MOUNT) {
174 			tst_res(TCONF, "overlayfs upper fs cannot be watched with mount mark");
175 			goto out;
176 		}
177 		SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL);
178 	}
179 
180 	/* Generate sequence of FAN_OPEN events on objects */
181 	for (i = 0; i < ARRAY_SIZE(objects); i++)
182 		fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY);
183 
184 	/*
185 	 * Generate sequence of FAN_CLOSE_NOWRITE events on objects. Each
186 	 * FAN_CLOSE_NOWRITE event is expected to be merged with its
187 	 * respective FAN_OPEN event that was performed on the same object.
188 	 */
189 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
190 		if (fds[i] > 0)
191 			SAFE_CLOSE(fds[i]);
192 	}
193 
194 	if (tst_variant == 1)
195 		SAFE_UMOUNT(MOUNT_PATH);
196 
197 	/* Read events from event queue */
198 	len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
199 
200 	/* Iterate over event queue */
201 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
202 		FAN_EVENT_OK(metadata, len);
203 		metadata = FAN_EVENT_NEXT(metadata, len), i++) {
204 		struct fanotify_fid_t *expected_fid = &objects[i].fid;
205 
206 		event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
207 		event_file_handle = (struct file_handle *) event_fid->handle;
208 
209 		/* File descriptor is redundant with FAN_REPORT_FID */
210 		if (metadata->fd != FAN_NOFD)
211 			tst_res(TFAIL,
212 				"Unexpectedly received file descriptor %d in "
213 				"event. Expected to get FAN_NOFD(%d)",
214 				metadata->fd, FAN_NOFD);
215 
216 		/* Ensure that the correct mask has been reported in event */
217 		if (metadata->mask != event_set[i].expected_mask)
218 			tst_res(TFAIL,
219 				"Unexpected mask received: %llx (expected: "
220 				"%llx) in event",
221 				metadata->mask,
222 				event_set[i].expected_mask);
223 
224 		/* Verify handle_bytes returned in event */
225 		if (event_file_handle->handle_bytes !=
226 		    expected_fid->handle.handle_bytes) {
227 			tst_res(TFAIL,
228 				"handle_bytes (%x) returned in event does not "
229 				"equal to handle_bytes (%x) returned in "
230 				"name_to_handle_at(2)",
231 				event_file_handle->handle_bytes,
232 				expected_fid->handle.handle_bytes);
233 			continue;
234 		}
235 
236 		/* Verify handle_type returned in event */
237 		if (event_file_handle->handle_type !=
238 		    expected_fid->handle.handle_type) {
239 			tst_res(TFAIL,
240 				"handle_type (%x) returned in event does not "
241 				"equal to handle_type (%x) returned in "
242 				"name_to_handle_at(2)",
243 				event_file_handle->handle_type,
244 				expected_fid->handle.handle_type);
245 			continue;
246 		}
247 
248 		/* Verify file identifier f_handle returned in event */
249 		if (memcmp(event_file_handle->f_handle,
250 			   expected_fid->handle.f_handle,
251 			   expected_fid->handle.handle_bytes) != 0) {
252 			tst_res(TFAIL,
253 				"file_handle returned in event does not match "
254 				"the file_handle returned in "
255 				"name_to_handle_at(2)");
256 			continue;
257 		}
258 
259 		/* Verify filesystem ID fsid  returned in event */
260 		if (memcmp(&event_fid->fsid, &expected_fid->fsid,
261 			   sizeof(expected_fid->fsid)) != 0) {
262 			tst_res(TFAIL,
263 				"event_fid.fsid != stat.f_fsid that was "
264 				"obtained via statfs(2)");
265 			continue;
266 		}
267 
268 		tst_res(TPASS,
269 			"got event: mask=%llx, pid=%d, fid=%x.%x.%lx values "
270 			"returned in event match those returned in statfs(2) "
271 			"and name_to_handle_at(2)",
272 			metadata->mask,
273 			getpid(),
274 			FSID_VAL_MEMBER(event_fid->fsid, 0),
275 			FSID_VAL_MEMBER(event_fid->fsid, 1),
276 			*(unsigned long *) event_file_handle->f_handle);
277 	}
278 out:
279 	SAFE_CLOSE(fanotify_fd);
280 }
281 
do_setup(void)282 static void do_setup(void)
283 {
284 	const char *mnt;
285 
286 	/*
287 	 * Bind mount to either base fs or to overlayfs over base fs:
288 	 * Variant #0: watch base fs - open files on base fs
289 	 * Variant #1: watch upper fs - open files on overlayfs
290 	 *
291 	 * Variant #1 tests a bug whose fix bc2473c90fca ("ovl: enable fsnotify
292 	 * events on underlying real files") in kernel 6.5 is not likely to be
293 	 * backported to older kernels.
294 	 * To avoid waiting for events that won't arrive when testing old kernels,
295 	 * require that kernel supports encoding fid with new flag AT_HADNLE_FID,
296 	 * also merged to 6.5 and not likely to be backported to older kernels.
297 	 */
298 	if (tst_variant) {
299 		REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(AT_HANDLE_FID);
300 		ovl_mounted = TST_MOUNT_OVERLAY();
301 		mnt = OVL_UPPER;
302 	} else {
303 		mnt = OVL_BASE_MNTPOINT;
304 
305 	}
306 	REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, mnt);
307 	SAFE_MKDIR(MOUNT_PATH, 0755);
308 	SAFE_MOUNT(mnt, MOUNT_PATH, "none", MS_BIND, NULL);
309 	bind_mounted = 1;
310 
311 	filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM);
312 
313 	nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
314 
315 	/* Create file and directory objects for testing */
316 	create_objects();
317 
318 	/*
319 	 * Create a mark on first inode without FAN_REPORT_FID, to test
320 	 * uninitialized connector->fsid cache. This mark remains for all test
321 	 * cases and is not expected to get any events (no writes in this test).
322 	 */
323 	SAFE_FANOTIFY_MARK(nofid_fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, AT_FDCWD,
324 			  FILE_PATH_ONE);
325 
326 	/* Get the filesystem fsid and file handle for each created object */
327 	get_object_stats();
328 }
329 
do_cleanup(void)330 static void do_cleanup(void)
331 {
332 	SAFE_CLOSE(nofid_fd);
333 	if (fanotify_fd > 0)
334 		SAFE_CLOSE(fanotify_fd);
335 	if (bind_mounted) {
336 		SAFE_UMOUNT(MOUNT_PATH);
337 		SAFE_RMDIR(MOUNT_PATH);
338 	}
339 	if (ovl_mounted)
340 		SAFE_UMOUNT(OVL_MNT);
341 }
342 
343 static struct tst_test test = {
344 	.test = do_test,
345 	.tcnt = ARRAY_SIZE(test_cases),
346 	.test_variants = 2,
347 	.setup = do_setup,
348 	.cleanup = do_cleanup,
349 	.needs_root = 1,
350 	.mount_device = 1,
351 	.mntpoint = OVL_BASE_MNTPOINT,
352 	.all_filesystems = 1,
353 	.tags = (const struct tst_tag[]) {
354 		{"linux-git", "c285a2f01d69"},
355 		{"linux-git", "bc2473c90fca"},
356 		{}
357 	}
358 };
359 
360 #else
361 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
362 #endif
363 #else
364 	TST_TEST_TCONF("System does not have required fanotify support");
365 #endif
366