• 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  * DESCRIPTION
8  *	Validate that the values returned within an event when
9  *	FAN_REPORT_FID is specified matches those that are obtained via
10  *	explicit invocation to system calls statfs(2) and
11  *	name_to_handle_at(2).
12  *
13  * This is also regression test for:
14  *     c285a2f01d69 ("fanotify: update connector fsid cache on add mark")
15  */
16 #define _GNU_SOURCE
17 #include "config.h"
18 
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/statfs.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include "tst_test.h"
28 #include "fanotify.h"
29 
30 #if defined(HAVE_SYS_FANOTIFY_H)
31 #include <sys/fanotify.h>
32 
33 #define PATH_LEN 128
34 #define BUF_SIZE 256
35 #define DIR_ONE "dir_one"
36 #define FILE_ONE "file_one"
37 #define FILE_TWO "file_two"
38 #define MOUNT_PATH "mntpoint"
39 #define EVENT_MAX ARRAY_SIZE(objects)
40 #define DIR_PATH_ONE MOUNT_PATH"/"DIR_ONE
41 #define FILE_PATH_ONE MOUNT_PATH"/"FILE_ONE
42 #define FILE_PATH_TWO MOUNT_PATH"/"FILE_TWO
43 
44 #if defined(HAVE_NAME_TO_HANDLE_AT)
45 struct event_t {
46 	unsigned long long expected_mask;
47 };
48 
49 static struct object_t {
50 	const char *path;
51 	int is_dir;
52 	struct fanotify_fid_t fid;
53 } objects[] = {
54 	{FILE_PATH_ONE, 0, {}},
55 	{FILE_PATH_TWO, 0, {}},
56 	{DIR_PATH_ONE, 1, {}}
57 };
58 
59 static struct test_case_t {
60 	struct fanotify_mark_type mark;
61 	unsigned long long mask;
62 } test_cases[] = {
63 	{
64 		INIT_FANOTIFY_MARK_TYPE(INODE),
65 		FAN_OPEN | FAN_CLOSE_NOWRITE
66 	},
67 	{
68 		INIT_FANOTIFY_MARK_TYPE(INODE),
69 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
70 	},
71 	{
72 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
73 		FAN_OPEN | FAN_CLOSE_NOWRITE
74 	},
75 	{
76 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
77 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
78 	},
79 	{
80 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
81 		FAN_OPEN | FAN_CLOSE_NOWRITE
82 	},
83 	{
84 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
85 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
86 	}
87 };
88 
89 static int nofid_fd;
90 static int fanotify_fd;
91 static char events_buf[BUF_SIZE];
92 static struct event_t event_set[EVENT_MAX];
93 
create_objects(void)94 static void create_objects(void)
95 {
96 	unsigned int i;
97 
98 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
99 		if (objects[i].is_dir)
100 			SAFE_MKDIR(objects[i].path, 0755);
101 		else
102 			SAFE_FILE_PRINTF(objects[i].path, "0");
103 	}
104 }
105 
get_object_stats(void)106 static void get_object_stats(void)
107 {
108 	unsigned int i;
109 	for (i = 0; i < ARRAY_SIZE(objects); i++)
110 		fanotify_save_fid(objects[i].path, &objects[i].fid);
111 }
112 
setup_marks(unsigned int fd,struct test_case_t * tc)113 static int setup_marks(unsigned int fd, struct test_case_t *tc)
114 {
115 	unsigned int i;
116 	struct fanotify_mark_type *mark = &tc->mark;
117 
118 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
119 		if (fanotify_mark(fd, FAN_MARK_ADD | mark->flag, tc->mask,
120 					AT_FDCWD, objects[i].path) == -1) {
121 			if (errno == EINVAL &&
122 				mark->flag & FAN_MARK_FILESYSTEM) {
123 				tst_res(TCONF,
124 					"FAN_MARK_FILESYSTEM not supported by "
125 					"kernel");
126 				return 1;
127 			} else if (errno == ENODEV &&
128 				   !objects[i].fid.fsid.val[0] &&
129 				   !objects[i].fid.fsid.val[1]) {
130 				tst_res(TCONF,
131 					"FAN_REPORT_FID not supported on "
132 					"filesystem type %s",
133 					tst_device->fs_type);
134 				return 1;
135 			}
136 			tst_brk(TBROK | TERRNO,
137 				"fanotify_mark(%d, FAN_MARK_ADD, FAN_OPEN, "
138 				"AT_FDCWD, %s) failed",
139 				fanotify_fd, objects[i].path);
140 		}
141 
142 		/* Setup the expected mask for each generated event */
143 		event_set[i].expected_mask = tc->mask;
144 		if (!objects[i].is_dir)
145 			event_set[i].expected_mask &= ~FAN_ONDIR;
146 	}
147 	return 0;
148 }
149 
do_test(unsigned int number)150 static void do_test(unsigned int number)
151 {
152 	unsigned int i;
153 	int len, fds[ARRAY_SIZE(objects)];
154 
155 	struct file_handle *event_file_handle;
156 	struct fanotify_event_metadata *metadata;
157 	struct fanotify_event_info_fid *event_fid;
158 	struct test_case_t *tc = &test_cases[number];
159 	struct fanotify_mark_type *mark = &tc->mark;
160 
161 	tst_res(TINFO,
162 		"Test #%d: FAN_REPORT_FID with mark flag: %s",
163 		number, mark->name);
164 
165 	fanotify_fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
166 	if (fanotify_fd == -1) {
167 		if (errno == EINVAL) {
168 			tst_res(TCONF,
169 				"FAN_REPORT_FID not supported by kernel");
170 			return;
171 		}
172 		tst_brk(TBROK | TERRNO,
173 			"fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, "
174 			"O_RDONLY) failed");
175 	}
176 
177 	/*
178 	 * Place marks on a set of objects and setup the expected masks
179 	 * for each event that is expected to be generated.
180 	 */
181 	if (setup_marks(fanotify_fd, tc) != 0)
182 		goto out;
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 	/* Read events from event queue */
199 	len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
200 
201 	/* Iterate over event queue */
202 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
203 		FAN_EVENT_OK(metadata, len);
204 		metadata = FAN_EVENT_NEXT(metadata, len), i++) {
205 		struct fanotify_fid_t *expected_fid = &objects[i].fid;
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 	/* Check for kernel fanotify support */
285 	nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
286 
287 	/* Create file and directory objects for testing */
288 	create_objects();
289 
290 	/*
291 	 * Create a mark on first inode without FAN_REPORT_FID, to test
292 	 * uninitialized connector->fsid cache. This mark remains for all test
293 	 * cases and is not expected to get any events (no writes in this test).
294 	 */
295 	if (fanotify_mark(nofid_fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, AT_FDCWD,
296 			  FILE_PATH_ONE) == -1) {
297 		tst_brk(TBROK | TERRNO,
298 			"fanotify_mark(%d, FAN_MARK_ADD, FAN_CLOSE_WRITE, "
299 			"AT_FDCWD, "FILE_PATH_ONE") failed",
300 			nofid_fd);
301 	}
302 
303 	/* Get the filesystem fsid and file handle for each created object */
304 	get_object_stats();
305 }
306 
do_cleanup(void)307 static void do_cleanup(void)
308 {
309 	SAFE_CLOSE(nofid_fd);
310 	if (fanotify_fd > 0)
311 		SAFE_CLOSE(fanotify_fd);
312 }
313 
314 static struct tst_test test = {
315 	.test = do_test,
316 	.tcnt = ARRAY_SIZE(test_cases),
317 	.setup = do_setup,
318 	.cleanup = do_cleanup,
319 	.needs_root = 1,
320 	.needs_tmpdir = 1,
321 	.mount_device = 1,
322 	.mntpoint = MOUNT_PATH,
323 	.all_filesystems = 1,
324 	.tags = (const struct tst_tag[]) {
325 		{"linux-git", "c285a2f01d69"},
326 		{}
327 	}
328 };
329 
330 #else
331 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
332 #endif
333 #else
334 	TST_TEST_TCONF("System does not have required fanotify support");
335 #endif
336