• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2019 CTERA Networks. All Rights Reserved.
4  *
5  * Started by Amir Goldstein <amir73il@gmail.com>
6  * Modified by Matthew Bobrowski <mbobrowski@mbobrowski.org>
7  *
8  * DESCRIPTION
9  *	Test file that has been purposely designed to verify
10  *	FAN_REPORT_FID functionality while using newly defined dirent
11  *	events.
12  */
13 #define _GNU_SOURCE
14 #include "config.h"
15 
16 #include <string.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <sys/statfs.h>
20 #include <sys/types.h>
21 
22 #include "tst_test.h"
23 #include "fanotify.h"
24 
25 #if defined(HAVE_SYS_FANOTIFY_H)
26 #include <sys/fanotify.h>
27 
28 #define BUF_SIZE 256
29 #define EVENT_MAX 256
30 
31 #define MOUNT_POINT "mntpoint"
32 #define TEST_DIR MOUNT_POINT"/test_dir"
33 #define DIR1 TEST_DIR"/dir1"
34 #define DIR2 TEST_DIR"/dir2"
35 #define FILE1 TEST_DIR"/file1"
36 #define FILE2 TEST_DIR"/file2"
37 
38 #if defined(HAVE_NAME_TO_HANDLE_AT)
39 struct event_t {
40 	unsigned long long mask;
41 	__kernel_fsid_t fsid;
42 	struct file_handle handle;
43 	char buf[MAX_HANDLE_SZ];
44 };
45 
46 static int fanotify_fd;
47 static char events_buf[BUF_SIZE];
48 static struct event_t event_set[EVENT_MAX];
49 
do_test(void)50 static void do_test(void)
51 {
52 	int i, fd, len, count = 0;
53 
54 	struct file_handle *event_file_handle;
55 	struct fanotify_event_metadata *metadata;
56 	struct fanotify_event_info_fid *event_fid;
57 
58 	if (fanotify_mark(fanotify_fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
59 				FAN_CREATE | FAN_DELETE | FAN_ATTRIB |
60 				FAN_MOVED_FROM | FAN_MOVED_TO |
61 				FAN_DELETE_SELF | FAN_ONDIR,
62 				AT_FDCWD, TEST_DIR) == -1) {
63 		if (errno == ENODEV)
64 			tst_brk(TCONF,
65 				"FAN_REPORT_FID not supported on %s "
66 				"filesystem", tst_device->fs_type);
67 		tst_brk(TBROK | TERRNO,
68 			"fanotify_mark(%d, FAN_MARK_ADD, FAN_CREATE | "
69 			"FAN_DELETE | FAN_MOVED_FROM | FAN_MOVED_TO | "
70 			"FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed",
71 			fanotify_fd, TEST_DIR);
72 	}
73 
74 	/* Generate a sequence of events */
75 	event_set[count].mask = FAN_CREATE | FAN_MOVED_FROM | FAN_MOVED_TO | \
76 				FAN_DELETE;
77 	event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
78 	fanotify_get_fid(TEST_DIR, &event_set[count].fsid,
79 			 &event_set[count].handle);
80 	count++;
81 
82 	fd = SAFE_CREAT(FILE1, 0644);
83 	SAFE_CLOSE(fd);
84 
85 	SAFE_RENAME(FILE1, FILE2);
86 
87 	event_set[count].mask = FAN_ATTRIB | FAN_DELETE_SELF;
88 	event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
89 	fanotify_get_fid(FILE2, &event_set[count].fsid,
90 			 &event_set[count].handle);
91 	count++;
92 
93 	SAFE_UNLINK(FILE2);
94 
95 	/*
96 	 * Generate a sequence of events on a directory. Subsequent events
97 	 * are merged, so it's required that we set FAN_ONDIR once in
98 	 * order to acknowledge that changes related to a subdirectory
99 	 * took place. Events on subdirectories are not merged with events
100 	 * on non-subdirectories.
101 	 */
102 	event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVED_FROM | \
103 				FAN_MOVED_TO | FAN_DELETE;
104 	event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
105 	fanotify_get_fid(TEST_DIR, &event_set[count].fsid,
106 			 &event_set[count].handle);
107 	count++;
108 
109 	SAFE_MKDIR(DIR1, 0755);
110 
111 	SAFE_RENAME(DIR1, DIR2);
112 
113 	event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF;
114 	event_set[count].handle.handle_bytes = MAX_HANDLE_SZ;
115 	fanotify_get_fid(DIR2, &event_set[count].fsid,
116 			 &event_set[count].handle);
117 	count++;
118 
119 	SAFE_RMDIR(DIR2);
120 
121 	/* Read events from the event queue */
122 	len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE);
123 
124 	/* Process each event in buffer */
125 	for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
126 		FAN_EVENT_OK(metadata, len);
127 		metadata = FAN_EVENT_NEXT(metadata,len), i++) {
128 		event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
129 		event_file_handle = (struct file_handle *) event_fid->handle;
130 
131 		if (i >= count) {
132 			tst_res(TFAIL,
133 				"got unnecessary event: mask=%llx "
134 				"pid=%u fd=%d",
135 				(unsigned long long) metadata->mask,
136 				metadata->pid,
137 				metadata->fd);
138 			metadata->mask = 0;
139 		} else if (metadata->fd != FAN_NOFD) {
140 			tst_res(TFAIL,
141 				"Received unexpected file descriptor %d in "
142 				"event. Expected to get FAN_NOFD(%d)",
143 				metadata->fd, FAN_NOFD);
144 		} else if (metadata->mask != event_set[i].mask) {
145 			tst_res(TFAIL,
146 				"Got event: mask=%llx (expected %llx) "
147 				"pid=%u fd=%d",
148 				(unsigned long long) metadata->mask,
149 				event_set[i].mask,
150 				(unsigned) metadata->pid,
151 				metadata->fd);
152 		} else if (metadata->pid != getpid()) {
153 			tst_res(TFAIL,
154 				"Got event: mask=%llx pid=%u "
155 				"(expected %u) fd=%d",
156 				(unsigned long long) metadata->mask,
157 				(unsigned) metadata->pid,
158 				(unsigned) getpid(),
159 				metadata->fd);
160 		} else if (event_file_handle->handle_bytes !=
161 				event_set[i].handle.handle_bytes) {
162 			tst_res(TFAIL,
163 				"Got event: handle_bytes (%x) returned in "
164 				"event does not equal handle_bytes (%x) "
165 				"retunred in name_to_handle_at(2)",
166 				event_file_handle->handle_bytes,
167 				event_set[i].handle.handle_bytes);
168 		} else if (event_file_handle->handle_type !=
169 				event_set[i].handle.handle_type) {
170 			tst_res(TFAIL,
171 				"handle_type (%x) returned in event does not "
172 				"equal to handle_type (%x) returned in "
173 				"name_to_handle_at(2)",
174 				event_file_handle->handle_type,
175 				event_set[i].handle.handle_type);
176 		} else if (memcmp(event_file_handle->f_handle,
177 					event_set[i].handle.f_handle,
178 					event_set[i].handle.handle_bytes)
179 					!= 0) {
180 			tst_res(TFAIL,
181 				"event_file_handle->f_handle does not match "
182 				"handle.f_handle returned in "
183 				"name_to_handle_at(2)");
184 		} else if (memcmp(&event_fid->fsid, &event_set[i].fsid,
185 					sizeof(event_set[i].fsid)) != 0) {
186 			tst_res(TFAIL,
187 				"event_fid->fsid != stats.f_fsid that was "
188 				"obtained via statfs(2)");
189 		} else {
190 			tst_res(TPASS,
191 				"Got event: mask=%llx, pid=%u, "
192 				"fid=%x.%x.%lx values",
193 				metadata->mask,
194 				getpid(),
195 				FSID_VAL_MEMBER(event_fid->fsid, 0),
196 				FSID_VAL_MEMBER(event_fid->fsid, 1),
197 				*(unsigned long *)
198 				event_file_handle->f_handle);
199 		}
200 	}
201 
202 	for (; i < count; i++)
203 		tst_res(TFAIL,
204 			"Didn't receive event: mask=%llx",
205 			event_set[i].mask);
206 }
207 
do_setup(void)208 static void do_setup(void)
209 {
210 	int fd;
211 
212 	/* Check kernel for fanotify support */
213 	fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY);
214 	SAFE_CLOSE(fd);
215 
216 	fanotify_fd = fanotify_init(FAN_REPORT_FID, O_RDONLY);
217 	if (fanotify_fd == -1) {
218 		if (errno == EINVAL)
219 			tst_brk(TCONF,
220 				"FAN_REPORT_FID not supported in kernel");
221 		tst_brk(TBROK | TERRNO,
222 			"fanotify_init(FAN_REPORT_FID, O_RDONLY) failed");
223 	}
224 
225 	SAFE_MKDIR(TEST_DIR, 0755);
226 }
227 
do_cleanup(void)228 static void do_cleanup(void)
229 {
230 	if (fanotify_fd > 0)
231 		SAFE_CLOSE(fanotify_fd);
232 }
233 
234 static struct tst_test test = {
235 	.needs_root = 1,
236 	.mount_device = 1,
237 	.mntpoint = MOUNT_POINT,
238 	.all_filesystems = 1,
239 	.test_all = do_test,
240 	.setup = do_setup,
241 	.cleanup = do_cleanup
242 };
243 
244 #else
245 	TST_TEST_TCONF("System does not have required name_to_handle_at() support");
246 #endif
247 #else
248 	TST_TEST_TCONF("System does not have required fanotify support");
249 #endif
250