// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 CTERA Networks. All Rights Reserved. * * Started by Amir Goldstein * Modified by Matthew Bobrowski * * DESCRIPTION * Test file that has been purposely designed to verify * FAN_REPORT_FID functionality while using newly defined dirent * events. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include "tst_test.h" #include "fanotify.h" #if defined(HAVE_SYS_FANOTIFY_H) #include #define BUF_SIZE 256 #define EVENT_MAX 256 #define MOUNT_POINT "mntpoint" #define TEST_DIR MOUNT_POINT"/test_dir" #define DIR1 TEST_DIR"/dir1" #define DIR2 TEST_DIR"/dir2" #define FILE1 TEST_DIR"/file1" #define FILE2 TEST_DIR"/file2" #if defined(HAVE_NAME_TO_HANDLE_AT) struct event_t { unsigned long long mask; __kernel_fsid_t fsid; struct file_handle handle; char buf[MAX_HANDLE_SZ]; }; static int fanotify_fd; static char events_buf[BUF_SIZE]; static struct event_t event_set[EVENT_MAX]; static void do_test(void) { int i, fd, len, count = 0; struct file_handle *event_file_handle; struct fanotify_event_metadata *metadata; struct fanotify_event_info_fid *event_fid; if (fanotify_mark(fanotify_fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM, FAN_CREATE | FAN_DELETE | FAN_ATTRIB | FAN_MOVED_FROM | FAN_MOVED_TO | FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, TEST_DIR) == -1) { if (errno == ENODEV) tst_brk(TCONF, "FAN_REPORT_FID not supported on %s " "filesystem", tst_device->fs_type); tst_brk(TBROK | TERRNO, "fanotify_mark(%d, FAN_MARK_ADD, FAN_CREATE | " "FAN_DELETE | FAN_MOVED_FROM | FAN_MOVED_TO | " "FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed", fanotify_fd, TEST_DIR); } /* Generate a sequence of events */ event_set[count].mask = FAN_CREATE | FAN_MOVED_FROM | FAN_MOVED_TO | \ FAN_DELETE; event_set[count].handle.handle_bytes = MAX_HANDLE_SZ; fanotify_get_fid(TEST_DIR, &event_set[count].fsid, &event_set[count].handle); count++; fd = SAFE_CREAT(FILE1, 0644); SAFE_CLOSE(fd); SAFE_RENAME(FILE1, FILE2); event_set[count].mask = FAN_ATTRIB | FAN_DELETE_SELF; event_set[count].handle.handle_bytes = MAX_HANDLE_SZ; fanotify_get_fid(FILE2, &event_set[count].fsid, &event_set[count].handle); count++; SAFE_UNLINK(FILE2); /* * Generate a sequence of events on a directory. Subsequent events * are merged, so it's required that we set FAN_ONDIR once in * order to acknowledge that changes related to a subdirectory * took place. Events on subdirectories are not merged with events * on non-subdirectories. */ event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVED_FROM | \ FAN_MOVED_TO | FAN_DELETE; event_set[count].handle.handle_bytes = MAX_HANDLE_SZ; fanotify_get_fid(TEST_DIR, &event_set[count].fsid, &event_set[count].handle); count++; SAFE_MKDIR(DIR1, 0755); SAFE_RENAME(DIR1, DIR2); event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF; event_set[count].handle.handle_bytes = MAX_HANDLE_SZ; fanotify_get_fid(DIR2, &event_set[count].fsid, &event_set[count].handle); count++; SAFE_RMDIR(DIR2); /* Read events from the event queue */ len = SAFE_READ(0, fanotify_fd, events_buf, BUF_SIZE); /* Process each event in buffer */ for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf; FAN_EVENT_OK(metadata, len); metadata = FAN_EVENT_NEXT(metadata,len), i++) { event_fid = (struct fanotify_event_info_fid *) (metadata + 1); event_file_handle = (struct file_handle *) event_fid->handle; if (i >= count) { tst_res(TFAIL, "got unnecessary event: mask=%llx " "pid=%u fd=%d", (unsigned long long) metadata->mask, metadata->pid, metadata->fd); metadata->mask = 0; } else if (metadata->fd != FAN_NOFD) { tst_res(TFAIL, "Received unexpected file descriptor %d in " "event. Expected to get FAN_NOFD(%d)", metadata->fd, FAN_NOFD); } else if (metadata->mask != event_set[i].mask) { tst_res(TFAIL, "Got event: mask=%llx (expected %llx) " "pid=%u fd=%d", (unsigned long long) metadata->mask, event_set[i].mask, (unsigned) metadata->pid, metadata->fd); } else if (metadata->pid != getpid()) { tst_res(TFAIL, "Got event: mask=%llx pid=%u " "(expected %u) fd=%d", (unsigned long long) metadata->mask, (unsigned) metadata->pid, (unsigned) getpid(), metadata->fd); } else if (event_file_handle->handle_bytes != event_set[i].handle.handle_bytes) { tst_res(TFAIL, "Got event: handle_bytes (%x) returned in " "event does not equal handle_bytes (%x) " "retunred in name_to_handle_at(2)", event_file_handle->handle_bytes, event_set[i].handle.handle_bytes); } else if (event_file_handle->handle_type != event_set[i].handle.handle_type) { tst_res(TFAIL, "handle_type (%x) returned in event does not " "equal to handle_type (%x) returned in " "name_to_handle_at(2)", event_file_handle->handle_type, event_set[i].handle.handle_type); } else if (memcmp(event_file_handle->f_handle, event_set[i].handle.f_handle, event_set[i].handle.handle_bytes) != 0) { tst_res(TFAIL, "event_file_handle->f_handle does not match " "handle.f_handle returned in " "name_to_handle_at(2)"); } else if (memcmp(&event_fid->fsid, &event_set[i].fsid, sizeof(event_set[i].fsid)) != 0) { tst_res(TFAIL, "event_fid->fsid != stats.f_fsid that was " "obtained via statfs(2)"); } else { tst_res(TPASS, "Got event: mask=%llx, pid=%u, " "fid=%x.%x.%lx values", metadata->mask, getpid(), FSID_VAL_MEMBER(event_fid->fsid, 0), FSID_VAL_MEMBER(event_fid->fsid, 1), *(unsigned long *) event_file_handle->f_handle); } } for (; i < count; i++) tst_res(TFAIL, "Didn't receive event: mask=%llx", event_set[i].mask); } static void do_setup(void) { int fd; /* Check kernel for fanotify support */ fd = SAFE_FANOTIFY_INIT(FAN_CLASS_NOTIF, O_RDONLY); SAFE_CLOSE(fd); fanotify_fd = fanotify_init(FAN_REPORT_FID, O_RDONLY); if (fanotify_fd == -1) { if (errno == EINVAL) tst_brk(TCONF, "FAN_REPORT_FID not supported in kernel"); tst_brk(TBROK | TERRNO, "fanotify_init(FAN_REPORT_FID, O_RDONLY) failed"); } SAFE_MKDIR(TEST_DIR, 0755); } static void do_cleanup(void) { if (fanotify_fd > 0) SAFE_CLOSE(fanotify_fd); } static struct tst_test test = { .needs_root = 1, .mount_device = 1, .mntpoint = MOUNT_POINT, .all_filesystems = 1, .test_all = do_test, .setup = do_setup, .cleanup = do_cleanup }; #else TST_TEST_TCONF("System does not have required name_to_handle_at() support"); #endif #else TST_TEST_TCONF("System does not have required fanotify support"); #endif