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
9 /*\
10 * [Description]
11 * Test file that has been purposely designed to verify FAN_REPORT_FID
12 * functionality while using newly defined dirent events.
13 */
14
15 /*
16 * Test case #1 is a regression test for commit f367a62a7cad:
17 * fanotify: merge duplicate events on parent and child
18 */
19
20 #define _GNU_SOURCE
21 #include "config.h"
22
23 #include <string.h>
24 #include <errno.h>
25 #include <sys/statfs.h>
26 #include <sys/types.h>
27 #include "tst_test.h"
28
29 #ifdef HAVE_SYS_FANOTIFY_H
30 #include "fanotify.h"
31
32 #define EVENT_MAX 10
33
34 /* Size of the event structure, not including file handle */
35 #define EVENT_SIZE (sizeof(struct fanotify_event_metadata) + \
36 sizeof(struct fanotify_event_info_fid))
37 /* Double events buffer size to account for file handles */
38 #define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE * 2)
39
40
41 #define MOUNT_POINT "mntpoint"
42 #define TEST_DIR MOUNT_POINT"/test_dir"
43 #define DIR1 TEST_DIR"/dir1"
44 #define DIR2 TEST_DIR"/dir2"
45 #define FILE1 TEST_DIR"/file1"
46 #define FILE2 TEST_DIR"/file2"
47
48 #if defined(HAVE_NAME_TO_HANDLE_AT)
49 struct event_t {
50 unsigned long long mask;
51 struct fanotify_fid_t *fid;
52 };
53
54 static int fanotify_fd;
55 static char events_buf[EVENT_BUF_LEN];
56 static struct event_t event_set[EVENT_MAX];
57
58 static struct test_case_t {
59 const char *tname;
60 struct fanotify_mark_type mark;
61 unsigned long mask;
62 } test_cases[] = {
63 {
64 "FAN_REPORT_FID on filesystem including FAN_DELETE_SELF",
65 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
66 FAN_DELETE_SELF,
67 },
68 {
69 "FAN_REPORT_FID on directory with FAN_EVENT_ON_CHILD",
70 INIT_FANOTIFY_MARK_TYPE(INODE),
71 FAN_EVENT_ON_CHILD,
72 },
73 };
74
do_test(unsigned int number)75 static void do_test(unsigned int number)
76 {
77 int i, fd, len, count = 0;
78
79 struct file_handle *event_file_handle;
80 struct fanotify_event_metadata *metadata;
81 struct fanotify_event_info_fid *event_fid;
82 struct test_case_t *tc = &test_cases[number];
83 struct fanotify_mark_type *mark = &tc->mark;
84 struct fanotify_fid_t root_fid, dir_fid, file_fid;
85
86 tst_res(TINFO, "Test #%d: %s", number, tc->tname);
87
88 SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | mark->flag, tc->mask |
89 FAN_CREATE | FAN_DELETE | FAN_MOVE |
90 FAN_MODIFY | FAN_ONDIR,
91 AT_FDCWD, TEST_DIR);
92
93 /* Save the test root dir fid */
94 fanotify_save_fid(TEST_DIR, &root_fid);
95
96 /* All dirent events on testdir are merged */
97 event_set[count].mask = FAN_CREATE | FAN_MOVE | FAN_DELETE;
98 event_set[count].fid = &root_fid;
99 count++;
100
101 fd = SAFE_CREAT(FILE1, 0644);
102 SAFE_CLOSE(fd);
103
104 /* Save the file fid */
105 fanotify_save_fid(FILE1, &file_fid);
106
107 /* Recursive watch file for events "on self" */
108 if (mark->flag == FAN_MARK_INODE &&
109 fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
110 FAN_MODIFY | FAN_DELETE_SELF,
111 AT_FDCWD, FILE1) == -1) {
112 tst_brk(TBROK | TERRNO,
113 "fanotify_mark(%d, FAN_MARK_ADD | %s, "
114 "FAN_DELETE_SELF, AT_FDCWD, %s) failed",
115 fanotify_fd, mark->name, FILE1);
116 }
117
118 /*
119 * Event on child file is not merged with dirent events.
120 * FAN_MODIFY event reported on file mark should be merged with the
121 * FAN_MODIFY event reported on parent directory watch.
122 */
123 event_set[count].mask = FAN_MODIFY;
124 event_set[count].fid = &file_fid;
125 count++;
126
127 SAFE_TRUNCATE(FILE1, 1);
128 SAFE_RENAME(FILE1, FILE2);
129
130 /*
131 * FAN_DELETE_SELF may be merged with FAN_MODIFY event above.
132 */
133 event_set[count].mask = FAN_DELETE_SELF;
134 event_set[count].fid = &file_fid;
135 count++;
136
137 SAFE_UNLINK(FILE2);
138
139 /* Read file events from the event queue */
140 len = SAFE_READ(0, fanotify_fd, events_buf, EVENT_BUF_LEN);
141
142 /*
143 * Generate a sequence of events on a directory. Subsequent events
144 * are merged, so it's required that we set FAN_ONDIR once in
145 * order to acknowledge that changes related to a subdirectory
146 * took place. Events on subdirectories are not merged with events
147 * on non-subdirectories.
148 */
149 event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVE | FAN_DELETE;
150 event_set[count].fid = &root_fid;
151 count++;
152
153 SAFE_MKDIR(DIR1, 0755);
154
155 /* Save the subdir fid */
156 fanotify_save_fid(DIR1, &dir_fid);
157
158 /* Recursive watch subdir for events "on self" */
159 if (mark->flag == FAN_MARK_INODE &&
160 fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag,
161 FAN_DELETE_SELF | FAN_ONDIR,
162 AT_FDCWD, DIR1) == -1) {
163 tst_brk(TBROK | TERRNO,
164 "fanotify_mark(%d, FAN_MARK_ADD | %s, "
165 "FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed",
166 fanotify_fd, mark->name, DIR1);
167 }
168
169 SAFE_RENAME(DIR1, DIR2);
170
171 event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF;
172 event_set[count].fid = &dir_fid;
173 count++;
174
175 SAFE_RMDIR(DIR2);
176
177 /* Read dir events from the event queue */
178 len += SAFE_READ(0, fanotify_fd, events_buf + len, EVENT_BUF_LEN - len);
179
180 /*
181 * Cleanup the mark
182 */
183 SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_FLUSH | mark->flag, 0,
184 AT_FDCWD, TEST_DIR);
185
186 /* Process each event in buffer */
187 for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf;
188 FAN_EVENT_OK(metadata, len); i++) {
189 struct event_t *expected = &event_set[i];
190 event_fid = (struct fanotify_event_info_fid *) (metadata + 1);
191 event_file_handle = (struct file_handle *) event_fid->handle;
192
193 if (i >= count) {
194 tst_res(TFAIL,
195 "got unnecessary event: mask=%llx "
196 "pid=%u fd=%d",
197 (unsigned long long) metadata->mask,
198 metadata->pid,
199 metadata->fd);
200 metadata->mask = 0;
201 } else if (metadata->fd != FAN_NOFD) {
202 tst_res(TFAIL,
203 "Received unexpected file descriptor %d in "
204 "event. Expected to get FAN_NOFD(%d)",
205 metadata->fd, FAN_NOFD);
206 } else if (!(metadata->mask & expected->mask)) {
207 tst_res(TFAIL,
208 "Got event: mask=%llx (expected %llx) "
209 "pid=%u fd=%d",
210 (unsigned long long) metadata->mask,
211 expected->mask, (unsigned) metadata->pid,
212 metadata->fd);
213 } else if (metadata->pid != getpid()) {
214 tst_res(TFAIL,
215 "Got event: mask=%llx pid=%u "
216 "(expected %u) fd=%d",
217 (unsigned long long) metadata->mask,
218 (unsigned) metadata->pid,
219 (unsigned) getpid(),
220 metadata->fd);
221 } else if (event_file_handle->handle_bytes !=
222 expected->fid->handle.handle_bytes) {
223 tst_res(TFAIL,
224 "Got event: handle_bytes (%x) returned in "
225 "event does not equal handle_bytes (%x) "
226 "retunred in name_to_handle_at(2)",
227 event_file_handle->handle_bytes,
228 expected->fid->handle.handle_bytes);
229 } else if (event_file_handle->handle_type !=
230 expected->fid->handle.handle_type) {
231 tst_res(TFAIL,
232 "handle_type (%x) returned in event does not "
233 "equal to handle_type (%x) returned in "
234 "name_to_handle_at(2)",
235 event_file_handle->handle_type,
236 expected->fid->handle.handle_type);
237 } else if (memcmp(event_file_handle->f_handle,
238 expected->fid->handle.f_handle,
239 expected->fid->handle.handle_bytes) != 0) {
240 tst_res(TFAIL,
241 "file_handle returned in event does not match "
242 "the file_handle returned in "
243 "name_to_handle_at(2)");
244 } else if (memcmp(&event_fid->fsid, &expected->fid->fsid,
245 sizeof(event_fid->fsid)) != 0) {
246 tst_res(TFAIL,
247 "event_fid->fsid != stats.f_fsid that was "
248 "obtained via statfs(2)");
249 } else {
250 tst_res(TPASS,
251 "Got event: mask=%llx, pid=%u, "
252 "fid=%x.%x.%lx values",
253 metadata->mask,
254 getpid(),
255 FSID_VAL_MEMBER(event_fid->fsid, 0),
256 FSID_VAL_MEMBER(event_fid->fsid, 1),
257 *(unsigned long *)
258 event_file_handle->f_handle);
259 }
260 metadata->mask &= ~expected->mask;
261 /* No events left in current mask? Go for next event */
262 if (metadata->mask == 0)
263 metadata = FAN_EVENT_NEXT(metadata, len);
264 }
265
266 for (; i < count; i++)
267 tst_res(TFAIL,
268 "Didn't receive event: mask=%llx",
269 event_set[i].mask);
270 }
271
do_setup(void)272 static void do_setup(void)
273 {
274 SAFE_MKDIR(TEST_DIR, 0755);
275 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_DIR);
276 fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_FID, O_RDONLY);
277 }
278
do_cleanup(void)279 static void do_cleanup(void)
280 {
281 if (fanotify_fd > 0)
282 SAFE_CLOSE(fanotify_fd);
283 }
284
285 static struct tst_test test = {
286 .needs_root = 1,
287 .mount_device = 1,
288 .mntpoint = MOUNT_POINT,
289 .all_filesystems = 1,
290 .test = do_test,
291 .tcnt = ARRAY_SIZE(test_cases),
292 .setup = do_setup,
293 .cleanup = do_cleanup,
294 .tags = (const struct tst_tag[]) {
295 {"linux-git", "f367a62a7cad"},
296 {}
297 }
298 };
299
300 #else
301 TST_TEST_TCONF("System does not have required name_to_handle_at() support");
302 #endif
303 #else
304 TST_TEST_TCONF("System does not have required fanotify support");
305 #endif
306