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