• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2020 CTERA Networks. All Rights Reserved.
4  *
5  * Started by Amir Goldstein <amir73il@gmail.com>
6  *
7  * DESCRIPTION
8  *     Check FAN_DIR_MODIFY events with name info
9  */
10 #define _GNU_SOURCE
11 #include "config.h"
12 
13 #include <stdio.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <sys/mount.h>
20 #include <sys/syscall.h>
21 #include "tst_test.h"
22 #include "fanotify.h"
23 
24 #if defined(HAVE_SYS_FANOTIFY_H)
25 #include <sys/fanotify.h>
26 #include <sys/inotify.h>
27 
28 #define EVENT_MAX 10
29 
30 /* Size of the event structure, not including file handle */
31 #define EVENT_SIZE (sizeof(struct fanotify_event_metadata) + \
32 		    sizeof(struct fanotify_event_info_fid))
33 /* Tripple events buffer size to account for file handles and names */
34 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE * 3)
35 
36 
37 #define BUF_SIZE 256
38 
39 #ifdef HAVE_NAME_TO_HANDLE_AT
40 struct event_t {
41 	unsigned long long mask;
42 	struct fanotify_fid_t *fid;
43 	char name[BUF_SIZE];
44 };
45 
46 static char fname1[BUF_SIZE + 11], fname2[BUF_SIZE + 11];
47 static char dname1[BUF_SIZE], dname2[BUF_SIZE];
48 static int fd_notify;
49 
50 static struct event_t event_set[EVENT_MAX];
51 
52 static char event_buf[EVENT_BUF_LEN];
53 
54 #define DIR_NAME1 "test_dir1"
55 #define DIR_NAME2 "test_dir2"
56 #define FILE_NAME1 "test_file1"
57 #define FILE_NAME2 "test_file2"
58 #define MOUNT_PATH "fs_mnt"
59 
60 static struct test_case_t {
61 	const char *tname;
62 	struct fanotify_mark_type mark;
63 	unsigned long mask;
64 	struct fanotify_mark_type sub_mark;
65 	unsigned long sub_mask;
66 } test_cases[] = {
67 	{
68 		/* Filesystem watch for dir modify and delete self events */
69 		"FAN_REPORT_FID on filesystem with FAN_DIR_MODIFY",
70 		INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
71 		FAN_DIR_MODIFY | FAN_DELETE_SELF | FAN_ONDIR,
72 		{},
73 		0,
74 	},
75 	{
76 		/* Recursive watches for dir modify events */
77 		"FAN_REPORT_FID on directories with FAN_DIR_MODIFY",
78 		INIT_FANOTIFY_MARK_TYPE(INODE),
79 		FAN_DIR_MODIFY,
80 		/* Watches for delete self event on subdir */
81 		INIT_FANOTIFY_MARK_TYPE(INODE),
82 		FAN_DIR_MODIFY | FAN_DELETE_SELF | FAN_ONDIR,
83 	},
84 };
85 
do_test(unsigned int number)86 static void do_test(unsigned int number)
87 {
88 	int fd, len = 0, i = 0, test_num = 0, tst_count = 0;
89 	struct test_case_t *tc = &test_cases[number];
90 	struct fanotify_mark_type *mark = &tc->mark;
91 	struct fanotify_mark_type *sub_mark = &tc->sub_mark;
92 	struct fanotify_fid_t root_fid, dir_fid, file_fid;
93 
94 	tst_res(TINFO, "Test #%d: %s", number, tc->tname);
95 
96 	fd_notify = fanotify_init(FAN_REPORT_FID, 0);
97 	if (fd_notify == -1) {
98 		if (errno == EINVAL)
99 			tst_brk(TCONF,
100 				"FAN_REPORT_FID not supported by kernel");
101 
102 		tst_brk(TBROK | TERRNO,
103 			"fanotify_init(FAN_REPORT_FID, 0) failed");
104 	}
105 
106 	/*
107 	 * Watch dir modify events with name in filesystem/dir
108 	 */
109 	if (fanotify_mark(fd_notify, FAN_MARK_ADD | mark->flag, tc->mask,
110 			  AT_FDCWD, MOUNT_PATH) < 0) {
111 		if (errno == EINVAL)
112 			tst_brk(TCONF,
113 				"FAN_DIR_MODIFY not supported by kernel");
114 
115 		tst_brk(TBROK | TERRNO,
116 		    "fanotify_mark (%d, FAN_MARK_ADD | %s, 0x%lx, "
117 		    "AT_FDCWD, '"MOUNT_PATH"') failed",
118 		    fd_notify, mark->name, tc->mask);
119 	}
120 
121 	/* Save the mount root fid */
122 	fanotify_save_fid(MOUNT_PATH, &root_fid);
123 
124 	/*
125 	 * Create subdir and watch open events "on children" with name.
126 	 */
127 	SAFE_MKDIR(dname1, 0755);
128 
129 	/* Save the subdir fid */
130 	fanotify_save_fid(dname1, &dir_fid);
131 
132 	if (tc->sub_mask &&
133 	    fanotify_mark(fd_notify, FAN_MARK_ADD | sub_mark->flag, tc->sub_mask,
134 			  AT_FDCWD, dname1) < 0) {
135 		tst_brk(TBROK | TERRNO,
136 		    "fanotify_mark (%d, FAN_MARK_ADD | %s, 0x%lx, "
137 		    "AT_FDCWD, '%s') failed",
138 		    fd_notify, sub_mark->name, tc->sub_mask, dname1);
139 	}
140 
141 	event_set[tst_count].mask = FAN_DIR_MODIFY;
142 	event_set[tst_count].fid = &root_fid;
143 	strcpy(event_set[tst_count].name, DIR_NAME1);
144 	tst_count++;
145 
146 	/* Generate modify events "on child" */
147 	fd = SAFE_CREAT(fname1, 0755);
148 
149 	/* Save the file fid */
150 	fanotify_save_fid(fname1, &file_fid);
151 
152 	SAFE_WRITE(1, fd, "1", 1);
153 	SAFE_RENAME(fname1, fname2);
154 	SAFE_CLOSE(fd);
155 
156 	/* Generate delete events with fname2 */
157 	SAFE_UNLINK(fname2);
158 
159 	/* Read events on files in subdir */
160 	len += SAFE_READ(0, fd_notify, event_buf + len, EVENT_BUF_LEN - len);
161 
162 	/*
163 	 * FAN_DIR_MODIFY events with the same name are merged.
164 	 */
165 	event_set[tst_count].mask = FAN_DIR_MODIFY;
166 	event_set[tst_count].fid = &dir_fid;
167 	strcpy(event_set[tst_count].name, FILE_NAME1);
168 	tst_count++;
169 	event_set[tst_count].mask = FAN_DIR_MODIFY;
170 	event_set[tst_count].fid = &dir_fid;
171 	strcpy(event_set[tst_count].name, FILE_NAME2);
172 	tst_count++;
173 
174 	/*
175 	 * Directory watch does not get self events on children.
176 	 * Filesystem watch gets self event w/o name info.
177 	 */
178 	if (mark->flag == FAN_MARK_FILESYSTEM) {
179 		event_set[tst_count].mask = FAN_DELETE_SELF;
180 		event_set[tst_count].fid = &file_fid;
181 		strcpy(event_set[tst_count].name, "");
182 		tst_count++;
183 	}
184 
185 	SAFE_RENAME(dname1, dname2);
186 	SAFE_RMDIR(dname2);
187 
188 	/* Read more events on dirs */
189 	len += SAFE_READ(0, fd_notify, event_buf + len, EVENT_BUF_LEN - len);
190 
191 	event_set[tst_count].mask = FAN_DIR_MODIFY;
192 	event_set[tst_count].fid = &root_fid;
193 	strcpy(event_set[tst_count].name, DIR_NAME1);
194 	tst_count++;
195 	event_set[tst_count].mask = FAN_DIR_MODIFY;
196 	event_set[tst_count].fid = &root_fid;
197 	strcpy(event_set[tst_count].name, DIR_NAME2);
198 	tst_count++;
199 	/*
200 	 * Directory watch gets self event on itself w/o name info.
201 	 */
202 	event_set[tst_count].mask = FAN_DELETE_SELF | FAN_ONDIR;
203 	strcpy(event_set[tst_count].name, "");
204 	event_set[tst_count].fid = &dir_fid;
205 	tst_count++;
206 
207 	/*
208 	 * Cleanup the marks
209 	 */
210 	SAFE_CLOSE(fd_notify);
211 	fd_notify = -1;
212 
213 	while (i < len) {
214 		struct event_t *expected = &event_set[test_num];
215 		struct fanotify_event_metadata *event;
216 		struct fanotify_event_info_fid *event_fid;
217 		struct file_handle *file_handle;
218 		unsigned int fhlen;
219 		const char *filename;
220 		int namelen, info_type;
221 
222 		event = (struct fanotify_event_metadata *)&event_buf[i];
223 		event_fid = (struct fanotify_event_info_fid *)(event + 1);
224 		file_handle = (struct file_handle *)event_fid->handle;
225 		fhlen = file_handle->handle_bytes;
226 		filename = (char *)file_handle->f_handle + fhlen;
227 		namelen = ((char *)event + event->event_len) - filename;
228 		/* End of event could have name, zero padding, both or none */
229 		if (namelen > 0) {
230 			namelen = strlen(filename);
231 		} else {
232 			filename = "";
233 			namelen = 0;
234 		}
235 
236 		if (expected->name[0]) {
237 			info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
238 		} else {
239 			info_type = FAN_EVENT_INFO_TYPE_FID;
240 		}
241 
242 		if (test_num >= tst_count) {
243 			tst_res(TFAIL,
244 				"got unnecessary event: mask=%llx "
245 				"pid=%u fd=%d name='%s' "
246 				"len=%d info_type=%d info_len=%d fh_len=%d",
247 				(unsigned long long)event->mask,
248 				(unsigned)event->pid, event->fd, filename,
249 				event->event_len, event_fid->hdr.info_type,
250 				event_fid->hdr.len, fhlen);
251 		} else if (!fhlen || namelen < 0) {
252 			tst_res(TFAIL,
253 				"got event without fid: mask=%llx pid=%u fd=%d, "
254 				"len=%d info_type=%d info_len=%d fh_len=%d",
255 				(unsigned long long)event->mask,
256 				(unsigned)event->pid, event->fd,
257 				event->event_len, event_fid->hdr.info_type,
258 				event_fid->hdr.len, fhlen);
259 		} else if (event->mask != expected->mask) {
260 			tst_res(TFAIL,
261 				"got event: mask=%llx (expected %llx) "
262 				"pid=%u fd=%d name='%s' "
263 				"len=%d info_type=%d info_len=%d fh_len=%d",
264 				(unsigned long long)event->mask, expected->mask,
265 				(unsigned)event->pid, event->fd, filename,
266 				event->event_len, event_fid->hdr.info_type,
267 				event_fid->hdr.len, fhlen);
268 		} else if (info_type != event_fid->hdr.info_type) {
269 			tst_res(TFAIL,
270 				"got event: mask=%llx pid=%u fd=%d, "
271 				"len=%d info_type=%d expected(%d) info_len=%d fh_len=%d",
272 				(unsigned long long)event->mask,
273 				(unsigned)event->pid, event->fd,
274 				event->event_len, event_fid->hdr.info_type,
275 				info_type, event_fid->hdr.len, fhlen);
276 		} else if (fhlen != expected->fid->handle.handle_bytes) {
277 			tst_res(TFAIL,
278 				"got event: mask=%llx pid=%u fd=%d name='%s' "
279 				"len=%d info_type=%d info_len=%d fh_len=%d expected(%d)"
280 				"fh_type=%d",
281 				(unsigned long long)event->mask,
282 				(unsigned)event->pid, event->fd, filename,
283 				event->event_len, info_type,
284 				event_fid->hdr.len, fhlen,
285 				expected->fid->handle.handle_bytes,
286 				file_handle->handle_type);
287 		} else if (file_handle->handle_type !=
288 			   expected->fid->handle.handle_type) {
289 			tst_res(TFAIL,
290 				"got event: mask=%llx pid=%u fd=%d name='%s' "
291 				"len=%d info_type=%d info_len=%d fh_len=%d "
292 				"fh_type=%d expected(%x)",
293 				(unsigned long long)event->mask,
294 				(unsigned)event->pid, event->fd, filename,
295 				event->event_len, info_type,
296 				event_fid->hdr.len, fhlen,
297 				file_handle->handle_type,
298 				expected->fid->handle.handle_type);
299 		} else if (memcmp(file_handle->f_handle,
300 				  expected->fid->handle.f_handle, fhlen)) {
301 			tst_res(TFAIL,
302 				"got event: mask=%llx pid=%u fd=%d name='%s' "
303 				"len=%d info_type=%d info_len=%d fh_len=%d "
304 				"fh_type=%d unexpected file handle (%x...)",
305 				(unsigned long long)event->mask,
306 				(unsigned)event->pid, event->fd, filename,
307 				event->event_len, info_type,
308 				event_fid->hdr.len, fhlen,
309 				file_handle->handle_type,
310 				*(int *)(file_handle->f_handle));
311 		} else if (memcmp(&event_fid->fsid, &expected->fid->fsid,
312 				  sizeof(event_fid->fsid)) != 0) {
313 			tst_res(TFAIL,
314 				"got event: mask=%llx pid=%u fd=%d name='%s' "
315 				"len=%d info_type=%d info_len=%d fh_len=%d "
316 				"fsid=%x.%x (expected %x.%x)",
317 				(unsigned long long)event->mask,
318 				(unsigned)event->pid, event->fd, filename,
319 				event->event_len, info_type,
320 				event_fid->hdr.len, fhlen,
321 				FSID_VAL_MEMBER(event_fid->fsid, 0),
322 				FSID_VAL_MEMBER(event_fid->fsid, 1),
323 				expected->fid->fsid.val[0],
324 				expected->fid->fsid.val[1]);
325 		} else if (strcmp(expected->name, filename)) {
326 			tst_res(TFAIL,
327 				"got event: mask=%llx "
328 				"pid=%u fd=%d name='%s' expected('%s') "
329 				"len=%d info_type=%d info_len=%d fh_len=%d",
330 				(unsigned long long)event->mask,
331 				(unsigned)event->pid, event->fd,
332 				filename, expected->name,
333 				event->event_len, event_fid->hdr.info_type,
334 				event_fid->hdr.len, fhlen);
335 		} else if (event->pid != getpid()) {
336 			tst_res(TFAIL,
337 				"got event: mask=%llx pid=%u "
338 				"(expected %u) fd=%d name='%s' "
339 				"len=%d info_type=%d info_len=%d fh_len=%d",
340 				(unsigned long long)event->mask,
341 				(unsigned)event->pid,
342 				(unsigned)getpid(),
343 				event->fd, filename,
344 				event->event_len, event_fid->hdr.info_type,
345 				event_fid->hdr.len, fhlen);
346 		} else {
347 			tst_res(TPASS,
348 				"got event #%d: mask=%llx pid=%u fd=%d name='%s' "
349 				"len=%d info_type=%d info_len=%d fh_len=%d",
350 				test_num, (unsigned long long)event->mask,
351 				(unsigned)event->pid, event->fd, filename,
352 				event->event_len, event_fid->hdr.info_type,
353 				event_fid->hdr.len, fhlen);
354 		}
355 
356 		i += event->event_len;
357 		if (event->fd > 0)
358 			SAFE_CLOSE(event->fd);
359 		test_num++;
360 	}
361 
362 	for (; test_num < tst_count; test_num++) {
363 		tst_res(TFAIL, "didn't get event: mask=%llx, name='%s'",
364 			 event_set[test_num].mask, event_set[test_num].name);
365 
366 	}
367 }
368 
setup(void)369 static void setup(void)
370 {
371 	int fd;
372 
373 	/* Check kernel for fanotify support */
374 	fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
375 	SAFE_CLOSE(fd);
376 
377 	sprintf(dname1, "%s/%s", MOUNT_PATH, DIR_NAME1);
378 	sprintf(dname2, "%s/%s", MOUNT_PATH, DIR_NAME2);
379 	sprintf(fname1, "%s/%s", dname1, FILE_NAME1);
380 	sprintf(fname2, "%s/%s", dname1, FILE_NAME2);
381 }
382 
cleanup(void)383 static void cleanup(void)
384 {
385 	if (fd_notify > 0)
386 		SAFE_CLOSE(fd_notify);
387 }
388 
389 static struct tst_test test = {
390 	.test = do_test,
391 	.tcnt = ARRAY_SIZE(test_cases),
392 	.dev_fs_flags = TST_FS_SKIP_FUSE,
393 	.setup = setup,
394 	.cleanup = cleanup,
395 	.mount_device = 1,
396 	.mntpoint = MOUNT_PATH,
397 	.all_filesystems = 1,
398 	.needs_tmpdir = 1,
399 	.needs_root = 1
400 };
401 
402 #else
403 	TST_TEST_TCONF("system does not have required name_to_handle_at() support");
404 #endif
405 #else
406 	TST_TEST_TCONF("system doesn't have required fanotify support");
407 #endif
408