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