1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2018 Matthew Bobrowski. All Rights Reserved. 4 * 5 * Started by Matthew Bobrowski <mbobrowski@mbobrowski.org> 6 */ 7 8/*\ 9 * [Description] 10 * Validate that the values returned within an event when FAN_REPORT_FID is 11 * specified matches those that are obtained via explicit invocation to system 12 * calls statfs(2) and name_to_handle_at(2). 13 */ 14 15/* 16 * This is also regression test for: 17 * c285a2f01d69 ("fanotify: update connector fsid cache on add mark") 18 */ 19 20#define _GNU_SOURCE 21#include "config.h" 22 23#include <stdio.h> 24#include <string.h> 25#include <sys/statfs.h> 26#include <sys/types.h> 27#include <sys/stat.h> 28#include <sys/mount.h> 29#include <errno.h> 30#include <unistd.h> 31#include "tst_test.h" 32 33#ifdef HAVE_SYS_FANOTIFY_H 34#include "fanotify.h" 35 36#define PATH_LEN 128 37#define BUF_SIZE 256 38#define DIR_ONE "dir_one" 39#define FILE_ONE "file_one" 40#define FILE_TWO "file_two" 41#define MOUNT_PATH "tstmnt" 42#define EVENT_MAX ARRAY_SIZE(objects) 43#define DIR_PATH_ONE MOUNT_PATH"/"DIR_ONE 44#define FILE_PATH_ONE MOUNT_PATH"/"FILE_ONE 45#define FILE_PATH_TWO MOUNT_PATH"/"FILE_TWO 46 47#if defined(HAVE_NAME_TO_HANDLE_AT) 48struct event_t { 49 unsigned long long expected_mask; 50}; 51 52static struct object_t { 53 const char *path; 54 int is_dir; 55 struct fanotify_fid_t fid; 56} objects[] = { 57 {FILE_PATH_ONE, 0, {}}, 58 {FILE_PATH_TWO, 0, {}}, 59 {DIR_PATH_ONE, 1, {}} 60}; 61 62static struct test_case_t { 63 struct fanotify_mark_type mark; 64 unsigned long long mask; 65} test_cases[] = { 66 { 67 INIT_FANOTIFY_MARK_TYPE(INODE), 68 FAN_OPEN | FAN_CLOSE_NOWRITE 69 }, 70 { 71 INIT_FANOTIFY_MARK_TYPE(INODE), 72 FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR 73 }, 74 { 75 INIT_FANOTIFY_MARK_TYPE(MOUNT), 76 FAN_OPEN | FAN_CLOSE_NOWRITE 77 }, 78 { 79 INIT_FANOTIFY_MARK_TYPE(MOUNT), 80 FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR 81 }, 82 { 83 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), 84 FAN_OPEN | FAN_CLOSE_NOWRITE 85 }, 86 { 87 INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), 88 FAN_OPEN | FAN_CLOSE_NOWRITE | FAN_ONDIR 89 } 90}; 91 92static int ovl_mounted; 93static int bind_mounted; 94static int nofid_fd; 95static int fanotify_fd; 96static int filesystem_mark_unsupported; 97static char events_buf[BUF_SIZE]; 98static struct event_t event_set[EVENT_MAX]; 99 100static void create_objects(void) 101{ 102 unsigned int i; 103 104 for (i = 0; i < ARRAY_SIZE(objects); i++) { 105 if (objects[i].is_dir) 106 SAFE_MKDIR(objects[i].path, 0755); 107 else 108 SAFE_FILE_PRINTF(objects[i].path, "0"); 109 } 110} 111 112static void get_object_stats(void) 113{ 114 unsigned int i; 115 116 for (i = 0; i < ARRAY_SIZE(objects); i++) 117 fanotify_save_fid(objects[i].path, &objects[i].fid); 118} 119 120static int setup_marks(unsigned int fd, struct test_case_t *tc) 121{ 122 unsigned int i; 123 struct fanotify_mark_type *mark = &tc->mark; 124 125 for (i = 0; i < ARRAY_SIZE(objects); i++) { 126 SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | mark->flag, tc->mask, 127 AT_FDCWD, objects[i].path); 128 129 /* Setup the expected mask for each generated event */ 130 event_set[i].expected_mask = tc->mask; 131 if (!objects[i].is_dir) 132 event_set[i].expected_mask &= ~FAN_ONDIR; 133 } 134 return 0; 135} 136 137static void do_test(unsigned int number) 138{ 139 unsigned int i; 140 int len, fds[ARRAY_SIZE(objects)]; 141 142 struct file_handle *event_file_handle; 143 struct fanotify_event_metadata *metadata; 144 struct fanotify_event_info_fid *event_fid; 145 struct test_case_t *tc = &test_cases[number]; 146 struct fanotify_mark_type *mark = &tc->mark; 147 148 tst_res(TINFO, 149 "Test #%d.%d: FAN_REPORT_FID with mark flag: %s", 150 number, tst_variant, mark->name); 151 152 if (tst_variant && !ovl_mounted) { 153 tst_res(TCONF, "overlayfs not supported on %s", tst_device->fs_type); 154 return; 155 } 156 157 if (filesystem_mark_unsupported && mark->flag & FAN_MARK_FILESYSTEM) { 158 tst_res(TCONF, "FAN_MARK_FILESYSTEM not supported in kernel?"); 159 return; 160 } 161 162 fanotify_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY); 163 164 /* 165 * Place marks on a set of objects and setup the expected masks 166 * for each event that is expected to be generated. 167 */ 168 if (setup_marks(fanotify_fd, tc) != 0) 169 goto out; 170 171 /* Variant #1: watching upper fs - open files on overlayfs */ 172 if (tst_variant == 1) { 173 if (mark->flag & FAN_MARK_MOUNT) { 174 tst_res(TCONF, "overlayfs upper fs cannot be watched with mount mark"); 175 goto out; 176 } 177 SAFE_MOUNT(OVL_MNT, MOUNT_PATH, "none", MS_BIND, NULL); 178 } 179 180 /* Generate sequence of FAN_OPEN events on objects */ 181 for (i = 0; i < ARRAY_SIZE(objects); i++) 182 fds[i] = SAFE_OPEN(objects[i].path, O_RDONLY); 183 184 /* 185 * Generate sequence of FAN_CLOSE_NOWRITE events on objects. Each 186 * FAN_CLOSE_NOWRITE event is expected to be merged with its 187 * respective FAN_OPEN event that was performed on the same object. 188 */ 189 for (i = 0; i < ARRAY_SIZE(objects); i++) { 190 if (fds[i] > 0) 191 SAFE_CLOSE(fds[i]); 192 } 193 194 if (tst_variant == 1) 195 SAFE_UMOUNT(MOUNT_PATH); 196 197 /* Read events from event queue */ 198 len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE); 199 200 /* Iterate over event queue */ 201 for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf; 202 FAN_EVENT_OK(metadata, len); 203 metadata = FAN_EVENT_NEXT(metadata, len), i++) { 204 struct fanotify_fid_t *expected_fid = &objects[i].fid; 205 206 event_fid = (struct fanotify_event_info_fid *) (metadata + 1); 207 event_file_handle = (struct file_handle *) event_fid->handle; 208 209 /* File descriptor is redundant with FAN_REPORT_FID */ 210 if (metadata->fd != FAN_NOFD) 211 tst_res(TFAIL, 212 "Unexpectedly received file descriptor %d in " 213 "event. Expected to get FAN_NOFD(%d)", 214 metadata->fd, FAN_NOFD); 215 216 /* Ensure that the correct mask has been reported in event */ 217 if (metadata->mask != event_set[i].expected_mask) 218 tst_res(TFAIL, 219 "Unexpected mask received: %llx (expected: " 220 "%llx) in event", 221 metadata->mask, 222 event_set[i].expected_mask); 223 224 /* Verify handle_bytes returned in event */ 225 if (event_file_handle->handle_bytes != 226 expected_fid->handle.handle_bytes) { 227 tst_res(TFAIL, 228 "handle_bytes (%x) returned in event does not " 229 "equal to handle_bytes (%x) returned in " 230 "name_to_handle_at(2)", 231 event_file_handle->handle_bytes, 232 expected_fid->handle.handle_bytes); 233 continue; 234 } 235 236 /* Verify handle_type returned in event */ 237 if (event_file_handle->handle_type != 238 expected_fid->handle.handle_type) { 239 tst_res(TFAIL, 240 "handle_type (%x) returned in event does not " 241 "equal to handle_type (%x) returned in " 242 "name_to_handle_at(2)", 243 event_file_handle->handle_type, 244 expected_fid->handle.handle_type); 245 continue; 246 } 247 248 /* Verify file identifier f_handle returned in event */ 249 if (memcmp(event_file_handle->f_handle, 250 expected_fid->handle.f_handle, 251 expected_fid->handle.handle_bytes) != 0) { 252 tst_res(TFAIL, 253 "file_handle returned in event does not match " 254 "the file_handle returned in " 255 "name_to_handle_at(2)"); 256 continue; 257 } 258 259 /* Verify filesystem ID fsid returned in event */ 260 if (memcmp(&event_fid->fsid, &expected_fid->fsid, 261 sizeof(expected_fid->fsid)) != 0) { 262 tst_res(TFAIL, 263 "event_fid.fsid != stat.f_fsid that was " 264 "obtained via statfs(2)"); 265 continue; 266 } 267 268 tst_res(TPASS, 269 "got event: mask=%llx, pid=%d, fid=%x.%x.%lx values " 270 "returned in event match those returned in statfs(2) " 271 "and name_to_handle_at(2)", 272 metadata->mask, 273 getpid(), 274 FSID_VAL_MEMBER(event_fid->fsid, 0), 275 FSID_VAL_MEMBER(event_fid->fsid, 1), 276 *(unsigned long *) event_file_handle->f_handle); 277 } 278out: 279 SAFE_CLOSE(fanotify_fd); 280} 281 282static void do_setup(void) 283{ 284 const char *mnt; 285 286 /* 287 * Bind mount to either base fs or to overlayfs over base fs: 288 * Variant #0: watch base fs - open files on base fs 289 * Variant #1: watch upper fs - open files on overlayfs 290 * 291 * Variant #1 tests a bug whose fix bc2473c90fca ("ovl: enable fsnotify 292 * events on underlying real files") in kernel 6.5 is not likely to be 293 * backported to older kernels. 294 * To avoid waiting for events that won't arrive when testing old kernels, 295 * require that kernel supports encoding fid with new flag AT_HADNLE_FID, 296 * also merged to 6.5 and not likely to be backported to older kernels. 297 */ 298 if (tst_variant) { 299 REQUIRE_HANDLE_TYPE_SUPPORTED_BY_KERNEL(AT_HANDLE_FID); 300 ovl_mounted = TST_MOUNT_OVERLAY(); 301 mnt = OVL_UPPER; 302 } else { 303 mnt = OVL_BASE_MNTPOINT; 304 305 } 306 REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, mnt); 307 SAFE_MKDIR(MOUNT_PATH, 0755); 308 SAFE_MOUNT(mnt, MOUNT_PATH, "none", MS_BIND, NULL); 309 bind_mounted = 1; 310 311 filesystem_mark_unsupported = fanotify_mark_supported_by_kernel(FAN_MARK_FILESYSTEM); 312 313 nofid_fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY); 314 315 /* Create file and directory objects for testing */ 316 create_objects(); 317 318 /* 319 * Create a mark on first inode without FAN_REPORT_FID, to test 320 * uninitialized connector->fsid cache. This mark remains for all test 321 * cases and is not expected to get any events (no writes in this test). 322 */ 323 SAFE_FANOTIFY_MARK(nofid_fd, FAN_MARK_ADD, FAN_CLOSE_WRITE, AT_FDCWD, 324 FILE_PATH_ONE); 325 326 /* Get the filesystem fsid and file handle for each created object */ 327 get_object_stats(); 328} 329 330static void do_cleanup(void) 331{ 332 SAFE_CLOSE(nofid_fd); 333 if (fanotify_fd > 0) 334 SAFE_CLOSE(fanotify_fd); 335 if (bind_mounted) { 336 SAFE_UMOUNT(MOUNT_PATH); 337 SAFE_RMDIR(MOUNT_PATH); 338 } 339 if (ovl_mounted) 340 SAFE_UMOUNT(OVL_MNT); 341} 342 343static struct tst_test test = { 344 .test = do_test, 345 .tcnt = ARRAY_SIZE(test_cases), 346 .test_variants = 2, 347 .setup = do_setup, 348 .cleanup = do_cleanup, 349 .needs_root = 1, 350 .mount_device = 1, 351 .mntpoint = OVL_BASE_MNTPOINT, 352 .all_filesystems = 1, 353 .tags = (const struct tst_tag[]) { 354 {"linux-git", "c285a2f01d69"}, 355 {"linux-git", "bc2473c90fca"}, 356 {} 357 } 358}; 359 360#else 361 TST_TEST_TCONF("System does not have required name_to_handle_at() support"); 362#endif 363#else 364 TST_TEST_TCONF("System does not have required fanotify support"); 365#endif 366