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
17static 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
41static 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 */
83static 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
109struct fanotify_fid_t {
110	__kernel_fsid_t fsid;
111	struct file_handle handle;
112	char buf[MAX_HANDLE_SZ];
113};
114
115static 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
136static 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
155static 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 */
183static 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
215static inline int fanotify_init_flags_supported_by_kernel(unsigned int flags)
216{
217	return fanotify_init_flags_supported_on_fs(flags, NULL);
218}
219
220typedef void (*tst_res_func_t)(const char *file, const int lineno,
221			       int ttype, const char *fmt, ...);
222
223static 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
246static 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
267static 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
297static 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