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 #ifndef MAX_HANDLE_SZ
72 #define MAX_HANDLE_SZ 128
73 #endif
74
75 #ifndef AT_HANDLE_FID
76 #define AT_HANDLE_FID 0x200
77 #endif
78
79 /*
80 * Helper function used to obtain fsid and file_handle for a given path.
81 * Used by test files correlated to FAN_REPORT_FID functionality.
82 */
fanotify_get_fid(const char * path,__kernel_fsid_t * fsid,struct file_handle * handle)83 static inline void fanotify_get_fid(const char *path, __kernel_fsid_t *fsid,
84 struct file_handle *handle)
85 {
86 int mount_id;
87 struct statfs stats;
88
89 if (statfs(path, &stats) == -1)
90 tst_brk(TBROK | TERRNO,
91 "statfs(%s, ...) failed", path);
92 memcpy(fsid, &stats.f_fsid, sizeof(stats.f_fsid));
93
94 if (name_to_handle_at(AT_FDCWD, path, handle, &mount_id, 0) == -1) {
95 if (errno == EOPNOTSUPP) {
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 }
104
105 #ifndef FILEID_INVALID
106 #define FILEID_INVALID 0xff
107 #endif
108
109 struct fanotify_fid_t {
110 __kernel_fsid_t fsid;
111 struct file_handle handle;
112 char buf[MAX_HANDLE_SZ];
113 };
114
fanotify_save_fid(const char * path,struct fanotify_fid_t * fid)115 static inline void fanotify_save_fid(const char *path,
116 struct fanotify_fid_t *fid)
117 {
118 int *fh = (int *)(fid->handle.f_handle);
119
120 fh[0] = fh[1] = fh[2] = 0;
121 fid->handle.handle_bytes = MAX_HANDLE_SZ;
122 fanotify_get_fid(path, &fid->fsid, &fid->handle);
123
124 tst_res(TINFO,
125 "fid(%s) = %x.%x.%x.%x.%x...", path, fid->fsid.val[0],
126 fid->fsid.val[1], fh[0], fh[1], fh[2]);
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_by_kernel(void)136 static inline void require_fanotify_access_permissions_supported_by_kernel(void)
137 {
138 int fd;
139
140 fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
141
142 if (fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, ".") < 0) {
143 if (errno == EINVAL) {
144 tst_brk(TCONF | TERRNO,
145 "CONFIG_FANOTIFY_ACCESS_PERMISSIONS not configured in kernel?");
146 } else {
147 tst_brk(TBROK | TERRNO,
148 "fanotify_mark (%d, FAN_MARK_ADD, FAN_ACCESS_PERM, AT_FDCWD, \".\") failed", fd);
149 }
150 }
151
152 SAFE_CLOSE(fd);
153 }
154
fanotify_events_supported_by_kernel(uint64_t mask,unsigned int init_flags,unsigned int mark_flags)155 static inline int fanotify_events_supported_by_kernel(uint64_t mask,
156 unsigned int init_flags,
157 unsigned int mark_flags)
158 {
159 int fd;
160 int rval = 0;
161
162 fd = SAFE_FANOTIFY_INIT(init_flags, O_RDONLY);
163
164 if (fanotify_mark(fd, FAN_MARK_ADD | mark_flags, mask, AT_FDCWD, ".") < 0) {
165 if (errno == EINVAL) {
166 rval = -1;
167 } else {
168 tst_brk(TBROK | TERRNO,
169 "fanotify_mark (%d, FAN_MARK_ADD, ..., AT_FDCWD, \".\") failed", fd);
170 }
171 }
172
173 SAFE_CLOSE(fd);
174
175 return rval;
176 }
177
178 /*
179 * @return 0: fanotify supported both in kernel and on tested filesystem
180 * @return -1: @flags not supported in kernel
181 * @return -2: @flags not supported on tested filesystem (tested if @fname is not NULL)
182 */
fanotify_init_flags_supported_on_fs(unsigned int flags,const char * fname)183 static inline int fanotify_init_flags_supported_on_fs(unsigned int flags, const char *fname)
184 {
185 int fd;
186 int rval = 0;
187
188 fd = fanotify_init(flags, O_RDONLY);
189
190 if (fd < 0) {
191 if (errno == ENOSYS)
192 tst_brk(TCONF, "fanotify not configured in kernel");
193
194 if (errno == EINVAL)
195 return -1;
196
197 tst_brk(TBROK | TERRNO, "fanotify_init() failed");
198 }
199
200 if (fname && fanotify_mark(fd, FAN_MARK_ADD, FAN_ACCESS, AT_FDCWD, fname) < 0) {
201 if (errno == ENODEV || errno == EOPNOTSUPP || errno == EXDEV) {
202 rval = -2;
203 } else {
204 tst_brk(TBROK | TERRNO,
205 "fanotify_mark (%d, FAN_MARK_ADD, ..., AT_FDCWD, %s) failed",
206 fd, fname);
207 }
208 }
209
210 SAFE_CLOSE(fd);
211
212 return rval;
213 }
214
fanotify_init_flags_supported_by_kernel(unsigned int flags)215 static inline int fanotify_init_flags_supported_by_kernel(unsigned int flags)
216 {
217 return fanotify_init_flags_supported_on_fs(flags, NULL);
218 }
219
220 typedef void (*tst_res_func_t)(const char *file, const int lineno,
221 int ttype, const char *fmt, ...);
222
fanotify_init_flags_err_msg(const char * flags_str,const char * file,const int lineno,tst_res_func_t res_func,int fail)223 static inline void fanotify_init_flags_err_msg(const char *flags_str,
224 const char *file, const int lineno, tst_res_func_t res_func, int fail)
225 {
226 if (fail == -1)
227 res_func(file, lineno, TCONF,
228 "%s not supported in kernel?", flags_str);
229 if (fail == -2)
230 res_func(file, lineno, TCONF,
231 "%s not supported on %s filesystem",
232 flags_str, tst_device->fs_type);
233 }
234
235 #define FANOTIFY_INIT_FLAGS_ERR_MSG(flags, fail) \
236 fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_res_, (fail))
237
238 #define REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(flags, fname) \
239 fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \
240 fanotify_init_flags_supported_on_fs(flags, fname))
241
242 #define REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_BY_KERNEL(flags) \
243 fanotify_init_flags_err_msg(#flags, __FILE__, __LINE__, tst_brk_, \
244 fanotify_init_flags_supported_by_kernel(flags))
245
fanotify_mark_supported_by_kernel(uint64_t flag)246 static inline int fanotify_mark_supported_by_kernel(uint64_t flag)
247 {
248 int fd;
249 int rval = 0;
250
251 fd = SAFE_FANOTIFY_INIT(FAN_CLASS_CONTENT, O_RDONLY);
252
253 if (fanotify_mark(fd, FAN_MARK_ADD | flag, FAN_ACCESS, AT_FDCWD, ".") < 0) {
254 if (errno == EINVAL) {
255 rval = -1;
256 } else {
257 tst_brk(TBROK | TERRNO,
258 "fanotify_mark (%d, FAN_MARK_ADD, ..., FAN_ACCESS, AT_FDCWD, \".\") failed", fd);
259 }
260 }
261
262 SAFE_CLOSE(fd);
263
264 return rval;
265 }
266
fanotify_handle_supported_by_kernel(int flag)267 static inline int fanotify_handle_supported_by_kernel(int flag)
268 {
269 /*
270 * On Kernel that does not support AT_HANDLE_FID this will result
271 * with EINVAL. On older kernels, this will result in EBADF.
272 */
273 if (name_to_handle_at(-1, "", NULL, NULL, AT_EMPTY_PATH | flag)) {
274 if (errno == EINVAL)
275 return -1;
276 }
277 return 0;
278 }
279
280 #define REQUIRE_MARK_TYPE_SUPPORTED_BY_KERNEL(mark_type) \
281 fanotify_init_flags_err_msg(#mark_type, __FILE__, __LINE__, tst_brk_, \
282 fanotify_mark_supported_by_kernel(mark_type))
283
284 #define REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(handle_type) \
285 fanotify_init_flags_err_msg(#handle_type, __FILE__, __LINE__, tst_brk_, \
286 fanotify_handle_supported_by_kernel(handle_type))
287
288 #define REQUIRE_FANOTIFY_EVENTS_SUPPORTED_ON_FS(init_flags, mark_type, mask, fname) do { \
289 if (mark_type) \
290 REQUIRE_MARK_TYPE_SUPPORTED_BY_KERNEL(mark_type); \
291 if (init_flags) \
292 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(init_flags, fname); \
293 fanotify_init_flags_err_msg(#mask, __FILE__, __LINE__, tst_brk_, \
294 fanotify_events_supported_by_kernel(mask, init_flags, mark_type)); \
295 } while (0)
296
get_event_info(struct fanotify_event_metadata * event,int info_type)297 static inline struct fanotify_event_info_header *get_event_info(
298 struct fanotify_event_metadata *event,
299 int info_type)
300 {
301 struct fanotify_event_info_header *hdr = NULL;
302 char *start = (char *) event;
303 int off;
304
305 for (off = event->metadata_len; (off+sizeof(*hdr)) < event->event_len;
306 off += hdr->len) {
307 hdr = (struct fanotify_event_info_header *) &(start[off]);
308 if (hdr->info_type == info_type)
309 return hdr;
310 }
311 return NULL;
312 }
313
314 #define get_event_info_error(event) \
315 ((struct fanotify_event_info_error *) \
316 get_event_info((event), FAN_EVENT_INFO_TYPE_ERROR))
317
318 #define get_event_info_fid(event) \
319 ((struct fanotify_event_info_fid *) \
320 get_event_info((event), FAN_EVENT_INFO_TYPE_FID))
321
322 #endif /* __FANOTIFY_H__ */
323