• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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