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