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