• 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 /*
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