1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Copyright (c) 2012-2020 Linux Test Project. All Rights Reserved.
4 * Author: Jan Kara, November 2013
5 */
6
7 #ifndef __FANOTIFY_H__
8 #define __FANOTIFY_H__
9
10 #include <sys/statfs.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <errno.h>
14 #include "lapi/fanotify.h"
15 #include "lapi/fcntl.h"
16
safe_fanotify_init(const char * file,const int lineno,unsigned int flags,unsigned int event_f_flags)17 static inline int safe_fanotify_init(const char *file, const int lineno,
18 unsigned int flags, unsigned int event_f_flags)
19 {
20 int rval;
21
22 rval = fanotify_init(flags, event_f_flags);
23
24 if (rval == -1) {
25 if (errno == ENOSYS) {
26 tst_brk_(file, lineno, TCONF,
27 "fanotify is not configured in this kernel");
28 }
29 tst_brk_(file, lineno, TBROK | TERRNO,
30 "%s:%d: fanotify_init() failed", file, lineno);
31 }
32
33 if (rval < -1) {
34 tst_brk_(file, lineno, TBROK | TERRNO,
35 "invalid fanotify_init() return %d", rval);
36 }
37
38 return rval;
39 }
40
safe_fanotify_mark(const char * file,const int lineno,int fd,unsigned int flags,uint64_t mask,int dfd,const char * pathname)41 static inline int safe_fanotify_mark(const char *file, const int lineno,
42 int fd, unsigned int flags, uint64_t mask,
43 int dfd, const char *pathname)
44 {
45 int rval;
46
47 rval = fanotify_mark(fd, flags, mask, dfd, pathname);
48
49 if (rval == -1) {
50 tst_brk_(file, lineno, TBROK | TERRNO,
51 "fanotify_mark(%d, 0x%x, 0x%lx, ..., %s) failed",
52 fd, flags, mask, pathname);
53 }
54
55 if (rval < -1) {
56 tst_brk_(file, lineno, TBROK | TERRNO,
57 "invalid fanotify_mark() return %d", rval);
58 }
59
60 return rval;
61 }
62
63 #define SAFE_FANOTIFY_MARK(fd, flags, mask, dfd, pathname) \
64 safe_fanotify_mark(__FILE__, __LINE__, (fd), (flags), (mask), (dfd), (pathname))
65
66 #define SAFE_FANOTIFY_INIT(fan, mode) \
67 safe_fanotify_init(__FILE__, __LINE__, (fan), (mode))
68
69 #ifdef HAVE_NAME_TO_HANDLE_AT
70
71 /*
72 * Helper function used to obtain fsid and file_handle for a given path.
73 * Used by test files correlated to FAN_REPORT_FID functionality.
74 *
75 * Returns 0 if normal NFS file handles are supported.
76 * Returns AT_HANDLE_FID, if only non-decodeable file handles are supported.
77 */
fanotify_get_fid(const char * path,__kernel_fsid_t * fsid,struct file_handle * handle)78 static inline int fanotify_get_fid(const char *path, __kernel_fsid_t *fsid,
79 struct file_handle *handle)
80 {
81 int mount_id;
82 struct statfs stats;
83
84 if (statfs(path, &stats) == -1)
85 tst_brk(TBROK | TERRNO,
86 "statfs(%s, ...) failed", path);
87 memcpy(fsid, &stats.f_fsid, sizeof(stats.f_fsid));
88
89 if (name_to_handle_at(AT_FDCWD, path, handle, &mount_id, 0) == -1) {
90 if (errno == EOPNOTSUPP) {
91 /* Try to request non-decodeable fid instead */
92 if (name_to_handle_at(AT_FDCWD, path, handle, &mount_id,
93 AT_HANDLE_FID) == 0)
94 return AT_HANDLE_FID;
95
96 tst_brk(TCONF,
97 "filesystem %s does not support file handles",
98 tst_device->fs_type);
99 }
100 tst_brk(TBROK | TERRNO,
101 "name_to_handle_at(AT_FDCWD, %s, ...) failed", path);
102 }
103 return 0;
104 }
105
106 struct fanotify_fid_t {
107 __kernel_fsid_t fsid;
108 struct file_handle handle;
109 char buf[MAX_HANDLE_SZ];
110 };
111
fanotify_save_fid(const char * path,struct fanotify_fid_t * fid)112 static inline int fanotify_save_fid(const char *path,
113 struct fanotify_fid_t *fid)
114 {
115 int *fh = (int *)(fid->handle.f_handle);
116 int ret;
117
118 fh[0] = fh[1] = fh[2] = 0;
119 fid->handle.handle_bytes = MAX_HANDLE_SZ;
120 ret = fanotify_get_fid(path, &fid->fsid, &fid->handle);
121
122 tst_res(TINFO,
123 "fid(%s) = %x.%x.%x.%x.%x...", path, fid->fsid.val[0],
124 fid->fsid.val[1], fh[0], fh[1], fh[2]);
125
126 return ret;
127 }
128 #endif /* HAVE_NAME_TO_HANDLE_AT */
129
130 #define INIT_FANOTIFY_GROUP_TYPE(t) \
131 { FAN_ ## t, "FAN_" #t }
132
133 #define INIT_FANOTIFY_MARK_TYPE(t) \
134 { FAN_MARK_ ## t, "FAN_MARK_" #t }
135
require_fanotify_access_permissions_supported_on_fs(const char * fname)136 static inline void require_fanotify_access_permissions_supported_on_fs(
137 const char *fname)
138 {
139 int fd;
140
141 fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
142
143 if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, fname) < 0) {
144 if (errno == EINVAL) {
145 tst_brk(TCONF | TERRNO,
146 "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not configured in kernel?");
147 } else {
148 tst_brk(TBROK | TERRNO,
149 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, \".\") failed", fd);
150 }
151 }
152
153 SAFE_CLOSE(fd);
154 }
155
156 /*
157 * @return 0: fanotify flags supported both in kernel and on tested filesystem
158 * @return -1: @init_flags not supported in kernel
159 * @return -2: @mark_flags not supported on tested filesystem (tested if @fname is not NULL)
160 * @return -3: @mark_flags not supported on overlayfs (tested if @fname == OVL_MNT)
161 */
fanotify_flags_supported_on_fs(unsigned int init_flags,unsigned int mark_flags,uint64_t event_flags,const char * fname)162 static inline int fanotify_flags_supported_on_fs(unsigned int init_flags,
163 unsigned int mark_flags,
164 uint64_t event_flags,
165 const char *fname)
166 {
167 int fd;
168 int rval = 0;
169 int err = 0;
170
171 fd = fanotify_init(init_flags, O_RDONLY);
172 if (fd < 0) {
173 err = errno;
174 if (errno == ENOSYS)
175 tst_brk(TCONF, "fanotify not configured in kernel");
176 if (errno != EINVAL)
177 tst_brk(TBROK | TERRNO,
178 "fanotify_init(%x, O_RDONLY) failed",
179 init_flags);
180 errno = err;
181 return -1;
182 }
183
184 if (fname && fanotify_mark(fd, FAN_MARK_ADD | mark_flags, event_flags, AT_FDCWD, fname) < 0) {
185 err = errno;
186 if (errno == ENODEV || errno == EOPNOTSUPP || errno == EXDEV) {
187 rval = strcmp(fname, OVL_MNT) ? -2 : -3;
188 } else if (errno != EINVAL) {
189 tst_brk(TBROK | TERRNO,
190 "fanotify_mark (%d, FAN_MARK_ADD | %x, %llx, AT_FDCWD, %s) failed",
191 fd, mark_flags, (unsigned long long)event_flags,
192 fname);
193 } else {
194 rval = -1;
195 }
196 }
197
198 SAFE_CLOSE(fd);
199
200 errno = err;
201 return rval;
202 }
203
fanotify_init_flags_supported_on_fs(unsigned int flags,const char * fname)204 static inline int fanotify_init_flags_supported_on_fs(unsigned int flags, const char *fname)
205 {
206 return fanotify_flags_supported_on_fs(flags, FAN_MARK_INODE, FAN_ACCESS, fname);
207 }
208
fanotify_mark_supported_on_fs(uint64_t flag,const char * fname)209 static inline int fanotify_mark_supported_on_fs(uint64_t flag, const char *fname)
210 {
211 return fanotify_flags_supported_on_fs(FAN_CLASS_NOTIF, flag, FAN_ACCESS, fname);
212 }
213
214 #define TST_FANOTIFY_INIT_KNOWN_FLAGS \
215 (FAN_REPORT_DFID_NAME_TARGET | FAN_REPORT_TID | FAN_REPORT_PIDFD | \
216 FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT)
217
218 /*
219 * Check support of given init flags one by one and return those which are
220 * supported.
221 */
fanotify_get_supported_init_flags(unsigned int flags,const char * fname)222 static inline unsigned int fanotify_get_supported_init_flags(unsigned int flags,
223 const char *fname)
224 {
225 unsigned int i, flg, arg, ret = 0;
226 static const struct { unsigned int flag, deps; } deplist[] = {
227 {FAN_REPORT_NAME, FAN_REPORT_DIR_FID},
228 {FAN_REPORT_TARGET_FID, FAN_REPORT_DFID_NAME_FID},
229 {0, 0}
230 };
231
232 if (flags & ~TST_FANOTIFY_INIT_KNOWN_FLAGS) {
233 tst_brk(TBROK, "fanotify_init() feature check called with unknown flags %x, please update flag dependency table if needed",
234 flags & ~TST_FANOTIFY_INIT_KNOWN_FLAGS);
235 }
236
237 for (flg = 1; flg; flg <<= 1) {
238 if (!(flags & flg))
239 continue;
240
241 arg = flg;
242
243 for (i = 0; deplist[i].flag; i++) {
244 if (deplist[i].flag == flg) {
245 arg |= deplist[i].deps;
246 break;
247 }
248 }
249
250 if (!fanotify_init_flags_supported_on_fs(arg, fname))
251 ret |= flg;
252 }
253
254 return ret;
255 }
256
257 typedef void (*tst_res_func_t)(const char *file, const int lineno,
258 int ttype, const char *fmt, ...);
259
fanotify_flags_err_msg(const char * flags_str,const char * file,const int lineno,tst_res_func_t res_func,int fail)260 static inline void fanotify_flags_err_msg(const char *flags_str,
261 const char *file, const int lineno, tst_res_func_t res_func, int fail)
262 {
263 if (fail == -1)
264 res_func(file, lineno, TCONF,
265 "%s not supported in kernel?", flags_str);
266 if (fail == -2 || fail == -3)
267 res_func(file, lineno, TCONF,
268 "%s not supported on %s%s filesystem",
269 flags_str, fail == -3 ? "overlayfs over " : "",
270 tst_device->fs_type);
271 }
272
273 #define FANOTIFY_INIT_FLAGS_ERR_MSG(flags, fail) \
274 fanotify_flags_err_msg(#flags, __FILE__, __LINE__, tst_res_, (fail))
275
276 #define FANOTIFY_MARK_FLAGS_ERR_MSG(mark, fail) \
277 fanotify_flags_err_msg((mark)->name, __FILE__, __LINE__, tst_res_, (fail))
278
279 #define REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(flags, fname) \
280 fanotify_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \
281 fanotify_init_flags_supported_on_fs(flags, fname))
282
fanotify_handle_supported_by_kernel(int flag)283 static inline int fanotify_handle_supported_by_kernel(int flag)
284 {
285 /*
286 * On Kernel that does not support AT_HANDLE_FID this will result
287 * with EINVAL. On older kernels, this will result in EBADF.
288 */
289 if (name_to_handle_at(-1, "", NULL, NULL, AT_EMPTY_PATH | flag)) {
290 if (errno == EINVAL)
291 return -1;
292 }
293 return 0;
294 }
295
296 #define REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(mark_type, fname) \
297 fanotify_flags_err_msg(#mark_type, __FILE__, __LINE__, tst_brk_, \
298 fanotify_mark_supported_on_fs(mark_type, fname))
299
300 #define REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(handle_type) \
301 fanotify_flags_err_msg(#handle_type, __FILE__, __LINE__, tst_brk_, \
302 fanotify_handle_supported_by_kernel(handle_type))
303
304 #define REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(init_flags, mark_type, mask, fname) do { \
305 if (mark_type) \
306 REQUIRE_MARK_TYPE_SUPPORTED_ON_FS(mark_type, fname); \
307 if (init_flags) \
308 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(init_flags, fname); \
309 fanotify_flags_err_msg(#mask, __FILE__, __LINE__, tst_brk_, \
310 fanotify_flags_supported_on_fs(init_flags, mark_type, mask, fname)); \
311 } while (0)
312
get_event_info(struct fanotify_event_metadata * event,int info_type)313 static inline struct fanotify_event_info_header *get_event_info(
314 struct fanotify_event_metadata *event,
315 int info_type)
316 {
317 struct fanotify_event_info_header *hdr = NULL;
318 char *start = (char *) event;
319 int off;
320
321 for (off = event->metadata_len; (off+sizeof(*hdr)) < event->event_len;
322 off += hdr->len) {
323 hdr = (struct fanotify_event_info_header *) &(start[off]);
324 if (hdr->info_type == info_type)
325 return hdr;
326 }
327 return NULL;
328 }
329
330 #define get_event_info_error(event) \
331 ((struct fanotify_event_info_error *) \
332 get_event_info((event), FAN_EVENT_INFO_TYPE_ERROR))
333
334 #define get_event_info_fid(event) \
335 ((struct fanotify_event_info_fid *) \
336 get_event_info((event), FAN_EVENT_INFO_TYPE_FID))
337
338 #endif /* __FANOTIFY_H__ */
339