• 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 	__kernel_fsid_t fsid;
48 	struct file_handle handle;
49 	char buf[MAX_HANDLE_SZ];
50 };
51 
52 static struct object_t {
53 	const char *path;
54 	int is_dir;
55 } objects[] = {
56 	{FILE_PATH_ONE, 0},
57 	{FILE_PATH_TWO, 0},
58 	{DIR_PATH_ONE, 1}
59 };
60 
61 static struct test_case_t {
62 	struct fanotify_mark_type mark;
63 	unsigned long long mask;
64 } test_cases[] = {
65 	{
66 		INIT_FANOTIFY_MARK_TYPE(INODE),
67 		FAN_OPEN | FAN_CLOSE_NOWRITE
68 	},
69 	{
70 		INIT_FANOTIFY_MARK_TYPE(INODE),
71 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
72 	},
73 	{
74 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
75 		FAN_OPEN | FAN_CLOSE_NOWRITE
76 	},
77 	{
78 		INIT_FANOTIFY_MARK_TYPE(MOUNT),
79 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
80 	},
81 	{
82 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
83 		FAN_OPEN | FAN_CLOSE_NOWRITE
84 	},
85 	{
86 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
87 		FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR
88 	}
89 };
90 
91 static int nofid_fd;
92 static int fanotify_fd;
93 static char events_buf[BUF_SIZE];
94 static struct event_t event_set[EVENT_MAX];
95 
create_objects(void)96 static void create_objects(void)
97 {
98 	unsigned int i;
99 
100 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
101 		if (objects[i].is_dir)
102 			SAFE_MKDIR(objects[i].path, 0755);
103 		else
104 			SAFE_FILE_PRINTF(objects[i].path, "0");
105 	}
106 }
107 
get_object_stats(void)108 static void get_object_stats(void)
109 {
110 	unsigned int i;
111 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
112 		event_set[i].handle.handle_bytes = MAX_HANDLE_SZ;
113 		fanotify_get_fid(objects[i].path, &event_set[i].fsid,
114 				&event_set[i].handle);
115 	}
116 }
117 
setup_marks(unsigned int fd,struct test_case_t * tc)118 static int setup_marks(unsigned int fd, struct test_case_t *tc)
119 {
120 	unsigned int i;
121 	struct fanotify_mark_type *mark = &tc->mark;
122 
123 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
124 		if (fanotify_mark(fd, FAN_MARK_ADD | mark->flag, tc->mask,
125 					AT_FDCWD, objects[i].path) == -1) {
126 			if (errno == EINVAL &&
127 				mark->flag & FAN_MARK_FILESYSTEM) {
128 				tst_res(TCONF,
129 					"FAN_MARK_FILESYSTEM not supported by "
130 					"kernel");
131 				return 1;
132 			} else if (errno == ENODEV &&
133 					!event_set[i].fsid.val[0] &&
134 					!event_set[i].fsid.val[1]) {
135 				tst_res(TCONF,
136 					"FAN_REPORT_FID not supported on "
137 					"filesystem type %s",
138 					tst_device->fs_type);
139 				return 1;
140 			}
141 			tst_brk(TBROK | TERRNO,
142 				"fanotify_mark(%d, FAN_MARK_ADD, FAN_OPEN, "
143 				"AT_FDCWD, %s) failed",
144 				fanotify_fd, objects[i].path);
145 		}
146 
147 		/* Setup the expected mask for each generated event */
148 		event_set[i].expected_mask = tc->mask;
149 		if (!objects[i].is_dir)
150 			event_set[i].expected_mask &= ~FAN_ONDIR;
151 	}
152 	return 0;
153 }
154 
do_test(unsigned int number)155 static void do_test(unsigned int number)
156 {
157 	unsigned int i;
158 	int len, fds[ARRAY_SIZE(objects)];
159 
160 	struct file_handle *event_file_handle;
161 	struct fanotify_event_metadata *metadata;
162 	struct fanotify_event_info_fid *event_fid;
163 	struct test_case_t *tc = &test_cases[number];
164 	struct fanotify_mark_type *mark = &tc->mark;
165 
166 	tst_res(TINFO,
167 		"Test #%d: FAN_REPORT_FID with mark flag: %s",
168 		number, mark->name);
169 
170 	fanotify_fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
171 	if (fanotify_fd == -1) {
172 		if (errno == EINVAL) {
173 			tst_res(TCONF,
174 				"FAN_REPORT_FID not supported by kernel");
175 			return;
176 		}
177 		tst_brk(TBROK | TERRNO,
178 			"fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, "
179 			"O_RDONLY) failed");
180 	}
181 
182 	/*
183 	 * Place marks on a set of objects and setup the expected masks
184 	 * for each event that is expected to be generated.
185 	 */
186 	if (setup_marks(fanotify_fd, tc) != 0)
187 		goto out;
188 
189 	/* Generate sequence of FAN_OPEN events on objects */
190 	for (i = 0; i < ARRAY_SIZE(objects); i++)
191 		fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY);
192 
193 	/*
194 	 * Generate sequence of FAN_CLOSE_NOWRITE events on objects. Each
195 	 * FAN_CLOSE_NOWRITE event is expected to be merged with its
196 	 * respective FAN_OPEN event that was performed on the same object.
197 	 */
198 	for (i = 0; i < ARRAY_SIZE(objects); i++) {
199 		if (fds[i] > 0)
200 			SAFE_CLOSE(fds[i]);
201 	}
202 
203 	/* Read events from event queue */
204 	len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
205 
206 	/* Iterate over event queue */
207 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
208 		FAN_EVENT_OK(metadata, len);
209 		metadata = FAN_EVENT_NEXT(metadata, len), i++) {
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 				!= event_set[i].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 				event_set[i].handle.handle_bytes);
237 			continue;
238 		}
239 
240 		/* Verify handle_type returned in event */
241 		if (event_file_handle->handle_type !=
242 				event_set[i].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 				event_set[i].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 				event_set[i].handle.f_handle,
255 				event_set[i].handle.handle_bytes) != 0) {
256 			tst_res(TFAIL,
257 				"event_file_handle->f_handle does not match "
258 				"event_set[i].handle.f_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, &event_set[i].fsid,
265 				sizeof(event_set[i].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 	/* Check for kernel fanotify support */
289 	nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
290 
291 	/* Create file and directory objects for testing */
292 	create_objects();
293 
294 	/*
295 	 * Create a mark on first inode without FAN_REPORT_FID, to test
296 	 * uninitialized connector->fsid cache. This mark remains for all test
297 	 * cases and is not expected to get any events (no writes in this test).
298 	 */
299 	if (fanotify_mark(nofid_fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, AT_FDCWD,
300 			  FILE_PATH_ONE) == -1) {
301 		tst_brk(TBROK | TERRNO,
302 			"fanotify_mark(%d, FAN_MARK_ADD, FAN_CLOSE_WRITE, "
303 			"AT_FDCWD, "FILE_PATH_ONE") failed",
304 			nofid_fd);
305 	}
306 
307 	/* Get the filesystem fsid and file handle for each created object */
308 	get_object_stats();
309 }
310 
do_cleanup(void)311 static void do_cleanup(void)
312 {
313 	SAFE_CLOSE(nofid_fd);
314 	if (fanotify_fd > 0)
315 		SAFE_CLOSE(fanotify_fd);
316 }
317 
318 static struct tst_test test = {
319 	.test = do_test,
320 	.tcnt = ARRAY_SIZE(test_cases),
321 	.setup = do_setup,
322 	.cleanup = do_cleanup,
323 	.needs_root = 1,
324 	.needs_tmpdir = 1,
325 	.mount_device = 1,
326 	.mntpoint = MOUNT_PATH,
327 	.all_filesystems = 1,
328 	.tags = (const struct tst_tag[]) {
329 		{"linux-git", "c285a2f01d69"},
330 		{}
331 	}
332 };
333 
334 #else
335 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
336 #endif
337 #else
338 	TST_TEST_TCONF("System does not have required fanotify support");
339 #endif
340