1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2019 CTERA Networks. All Rights Reserved. 4f08c3bdfSopenharmony_ci * 5f08c3bdfSopenharmony_ci * Started by Amir Goldstein <amir73il@gmail.com> 6f08c3bdfSopenharmony_ci * Modified by Matthew Bobrowski <mbobrowski@mbobrowski.org> 7f08c3bdfSopenharmony_ci */ 8f08c3bdfSopenharmony_ci 9f08c3bdfSopenharmony_ci/*\ 10f08c3bdfSopenharmony_ci * [Description] 11f08c3bdfSopenharmony_ci * Test file that has been purposely designed to verify FAN_REPORT_FID 12f08c3bdfSopenharmony_ci * functionality while using newly defined dirent events. 13f08c3bdfSopenharmony_ci */ 14f08c3bdfSopenharmony_ci 15f08c3bdfSopenharmony_ci/* 16f08c3bdfSopenharmony_ci * Test case #1 is a regression test for commit f367a62a7cad: 17f08c3bdfSopenharmony_ci * fanotify: merge duplicate events on parent and child 18f08c3bdfSopenharmony_ci */ 19f08c3bdfSopenharmony_ci 20f08c3bdfSopenharmony_ci#define _GNU_SOURCE 21f08c3bdfSopenharmony_ci#include "config.h" 22f08c3bdfSopenharmony_ci 23f08c3bdfSopenharmony_ci#include <string.h> 24f08c3bdfSopenharmony_ci#include <errno.h> 25f08c3bdfSopenharmony_ci#include <sys/statfs.h> 26f08c3bdfSopenharmony_ci#include <sys/types.h> 27f08c3bdfSopenharmony_ci#include "tst_test.h" 28f08c3bdfSopenharmony_ci 29f08c3bdfSopenharmony_ci#ifdef HAVE_SYS_FANOTIFY_H 30f08c3bdfSopenharmony_ci#include "fanotify.h" 31f08c3bdfSopenharmony_ci 32f08c3bdfSopenharmony_ci#define EVENT_MAX 10 33f08c3bdfSopenharmony_ci 34f08c3bdfSopenharmony_ci/* Size of the event structure, not including file handle */ 35f08c3bdfSopenharmony_ci#define EVENT_SIZE (sizeof(struct fanotify_event_metadata) + \ 36f08c3bdfSopenharmony_ci sizeof(struct fanotify_event_info_fid)) 37f08c3bdfSopenharmony_ci 38f08c3bdfSopenharmony_ci/* Double events buffer size to account for file handles */ 39f08c3bdfSopenharmony_ci#define EVENT_BUF_LEN (EVENT_MAX * EVENT_SIZE * 2) 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_ci#define MOUNT_POINT "mntpoint" 42f08c3bdfSopenharmony_ci#define TEST_DIR MOUNT_POINT"/test_dir" 43f08c3bdfSopenharmony_ci#define DIR1 TEST_DIR"/dir1" 44f08c3bdfSopenharmony_ci#define DIR2 TEST_DIR"/dir2" 45f08c3bdfSopenharmony_ci#define FILE1 TEST_DIR"/file1" 46f08c3bdfSopenharmony_ci#define FILE2 TEST_DIR"/file2" 47f08c3bdfSopenharmony_ci 48f08c3bdfSopenharmony_ci#if defined(HAVE_NAME_TO_HANDLE_AT) 49f08c3bdfSopenharmony_cistruct event_t { 50f08c3bdfSopenharmony_ci unsigned long long mask; 51f08c3bdfSopenharmony_ci struct fanotify_fid_t *fid; 52f08c3bdfSopenharmony_ci}; 53f08c3bdfSopenharmony_ci 54f08c3bdfSopenharmony_cistatic int fanotify_fd; 55f08c3bdfSopenharmony_cistatic char events_buf[EVENT_BUF_LEN]; 56f08c3bdfSopenharmony_cistatic struct event_t event_set[EVENT_MAX]; 57f08c3bdfSopenharmony_ci 58f08c3bdfSopenharmony_cistatic struct test_case_t { 59f08c3bdfSopenharmony_ci const char *tname; 60f08c3bdfSopenharmony_ci struct fanotify_mark_type mark; 61f08c3bdfSopenharmony_ci unsigned long mask; 62f08c3bdfSopenharmony_ci} test_cases[] = { 63f08c3bdfSopenharmony_ci { 64f08c3bdfSopenharmony_ci "FAN_REPORT_FID on filesystem including FAN_DELETE_SELF", 65f08c3bdfSopenharmony_ci INIT_FANOTIFY_MARK_TYPE(FILESYSTEM), 66f08c3bdfSopenharmony_ci FAN_DELETE_SELF, 67f08c3bdfSopenharmony_ci }, 68f08c3bdfSopenharmony_ci { 69f08c3bdfSopenharmony_ci "FAN_REPORT_FID on directory with FAN_EVENT_ON_CHILD", 70f08c3bdfSopenharmony_ci INIT_FANOTIFY_MARK_TYPE(INODE), 71f08c3bdfSopenharmony_ci FAN_EVENT_ON_CHILD, 72f08c3bdfSopenharmony_ci }, 73f08c3bdfSopenharmony_ci}; 74f08c3bdfSopenharmony_ci 75f08c3bdfSopenharmony_cistatic void do_test(unsigned int number) 76f08c3bdfSopenharmony_ci{ 77f08c3bdfSopenharmony_ci int i, fd, len, count = 0; 78f08c3bdfSopenharmony_ci 79f08c3bdfSopenharmony_ci struct file_handle *event_file_handle; 80f08c3bdfSopenharmony_ci struct fanotify_event_metadata *metadata; 81f08c3bdfSopenharmony_ci struct fanotify_event_info_fid *event_fid; 82f08c3bdfSopenharmony_ci struct test_case_t *tc = &test_cases[number]; 83f08c3bdfSopenharmony_ci struct fanotify_mark_type *mark = &tc->mark; 84f08c3bdfSopenharmony_ci struct fanotify_fid_t root_fid, dir_fid, file_fid; 85f08c3bdfSopenharmony_ci 86f08c3bdfSopenharmony_ci tst_res(TINFO, "Test #%d: %s", number, tc->tname); 87f08c3bdfSopenharmony_ci 88f08c3bdfSopenharmony_ci SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_ADD | mark->flag, tc->mask | 89f08c3bdfSopenharmony_ci FAN_CREATE | FAN_DELETE | FAN_MOVE | 90f08c3bdfSopenharmony_ci FAN_MODIFY | FAN_ONDIR, 91f08c3bdfSopenharmony_ci AT_FDCWD, TEST_DIR); 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_ci /* Save the test root dir fid */ 94f08c3bdfSopenharmony_ci fanotify_save_fid(TEST_DIR, &root_fid); 95f08c3bdfSopenharmony_ci 96f08c3bdfSopenharmony_ci /* All dirent events on testdir are merged */ 97f08c3bdfSopenharmony_ci event_set[count].mask = FAN_CREATE | FAN_MOVE | FAN_DELETE; 98f08c3bdfSopenharmony_ci event_set[count].fid = &root_fid; 99f08c3bdfSopenharmony_ci count++; 100f08c3bdfSopenharmony_ci 101f08c3bdfSopenharmony_ci fd = SAFE_CREAT(FILE1, 0644); 102f08c3bdfSopenharmony_ci SAFE_CLOSE(fd); 103f08c3bdfSopenharmony_ci 104f08c3bdfSopenharmony_ci /* Save the file fid */ 105f08c3bdfSopenharmony_ci fanotify_save_fid(FILE1, &file_fid); 106f08c3bdfSopenharmony_ci 107f08c3bdfSopenharmony_ci /* Recursive watch file for events "on self" */ 108f08c3bdfSopenharmony_ci if (mark->flag == FAN_MARK_INODE && 109f08c3bdfSopenharmony_ci fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag, 110f08c3bdfSopenharmony_ci FAN_MODIFY | FAN_DELETE_SELF, 111f08c3bdfSopenharmony_ci AT_FDCWD, FILE1) == -1) { 112f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 113f08c3bdfSopenharmony_ci "fanotify_mark(%d, FAN_MARK_ADD | %s, " 114f08c3bdfSopenharmony_ci "FAN_DELETE_SELF, AT_FDCWD, %s) failed", 115f08c3bdfSopenharmony_ci fanotify_fd, mark->name, FILE1); 116f08c3bdfSopenharmony_ci } 117f08c3bdfSopenharmony_ci 118f08c3bdfSopenharmony_ci /* 119f08c3bdfSopenharmony_ci * Event on child file is not merged with dirent events. 120f08c3bdfSopenharmony_ci * FAN_MODIFY event reported on file mark should be merged with the 121f08c3bdfSopenharmony_ci * FAN_MODIFY event reported on parent directory watch. 122f08c3bdfSopenharmony_ci */ 123f08c3bdfSopenharmony_ci event_set[count].mask = FAN_MODIFY; 124f08c3bdfSopenharmony_ci event_set[count].fid = &file_fid; 125f08c3bdfSopenharmony_ci count++; 126f08c3bdfSopenharmony_ci 127f08c3bdfSopenharmony_ci SAFE_TRUNCATE(FILE1, 1); 128f08c3bdfSopenharmony_ci SAFE_RENAME(FILE1, FILE2); 129f08c3bdfSopenharmony_ci 130f08c3bdfSopenharmony_ci /* 131f08c3bdfSopenharmony_ci * FAN_DELETE_SELF may be merged with FAN_MODIFY event above. 132f08c3bdfSopenharmony_ci */ 133f08c3bdfSopenharmony_ci event_set[count].mask = FAN_DELETE_SELF; 134f08c3bdfSopenharmony_ci event_set[count].fid = &file_fid; 135f08c3bdfSopenharmony_ci count++; 136f08c3bdfSopenharmony_ci 137f08c3bdfSopenharmony_ci SAFE_UNLINK(FILE2); 138f08c3bdfSopenharmony_ci 139f08c3bdfSopenharmony_ci /* Read file events from the event queue */ 140f08c3bdfSopenharmony_ci len = SAFE_READ(0, fanotify_fd, events_buf, EVENT_BUF_LEN); 141f08c3bdfSopenharmony_ci 142f08c3bdfSopenharmony_ci /* 143f08c3bdfSopenharmony_ci * Generate a sequence of events on a directory. Subsequent events 144f08c3bdfSopenharmony_ci * are merged, so it's required that we set FAN_ONDIR once in 145f08c3bdfSopenharmony_ci * order to acknowledge that changes related to a subdirectory 146f08c3bdfSopenharmony_ci * took place. Events on subdirectories are not merged with events 147f08c3bdfSopenharmony_ci * on non-subdirectories. 148f08c3bdfSopenharmony_ci */ 149f08c3bdfSopenharmony_ci event_set[count].mask = FAN_ONDIR | FAN_CREATE | FAN_MOVE | FAN_DELETE; 150f08c3bdfSopenharmony_ci event_set[count].fid = &root_fid; 151f08c3bdfSopenharmony_ci count++; 152f08c3bdfSopenharmony_ci 153f08c3bdfSopenharmony_ci SAFE_MKDIR(DIR1, 0755); 154f08c3bdfSopenharmony_ci 155f08c3bdfSopenharmony_ci /* Save the subdir fid */ 156f08c3bdfSopenharmony_ci fanotify_save_fid(DIR1, &dir_fid); 157f08c3bdfSopenharmony_ci 158f08c3bdfSopenharmony_ci /* Recursive watch subdir for events "on self" */ 159f08c3bdfSopenharmony_ci if (mark->flag == FAN_MARK_INODE && 160f08c3bdfSopenharmony_ci fanotify_mark(fanotify_fd, FAN_MARK_ADD | mark->flag, 161f08c3bdfSopenharmony_ci FAN_DELETE_SELF | FAN_ONDIR, 162f08c3bdfSopenharmony_ci AT_FDCWD, DIR1) == -1) { 163f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, 164f08c3bdfSopenharmony_ci "fanotify_mark(%d, FAN_MARK_ADD | %s, " 165f08c3bdfSopenharmony_ci "FAN_DELETE_SELF | FAN_ONDIR, AT_FDCWD, %s) failed", 166f08c3bdfSopenharmony_ci fanotify_fd, mark->name, DIR1); 167f08c3bdfSopenharmony_ci } 168f08c3bdfSopenharmony_ci 169f08c3bdfSopenharmony_ci SAFE_RENAME(DIR1, DIR2); 170f08c3bdfSopenharmony_ci 171f08c3bdfSopenharmony_ci event_set[count].mask = FAN_ONDIR | FAN_DELETE_SELF; 172f08c3bdfSopenharmony_ci event_set[count].fid = &dir_fid; 173f08c3bdfSopenharmony_ci count++; 174f08c3bdfSopenharmony_ci 175f08c3bdfSopenharmony_ci SAFE_RMDIR(DIR2); 176f08c3bdfSopenharmony_ci 177f08c3bdfSopenharmony_ci /* Read dir events from the event queue */ 178f08c3bdfSopenharmony_ci len += SAFE_READ(0, fanotify_fd, events_buf + len, EVENT_BUF_LEN - len); 179f08c3bdfSopenharmony_ci 180f08c3bdfSopenharmony_ci /* 181f08c3bdfSopenharmony_ci * Cleanup the mark 182f08c3bdfSopenharmony_ci */ 183f08c3bdfSopenharmony_ci SAFE_FANOTIFY_MARK(fanotify_fd, FAN_MARK_FLUSH | mark->flag, 0, 184f08c3bdfSopenharmony_ci AT_FDCWD, TEST_DIR); 185f08c3bdfSopenharmony_ci 186f08c3bdfSopenharmony_ci /* Process each event in buffer */ 187f08c3bdfSopenharmony_ci for (i = 0, metadata = (struct fanotify_event_metadata *) events_buf; 188f08c3bdfSopenharmony_ci FAN_EVENT_OK(metadata, len); i++) { 189f08c3bdfSopenharmony_ci struct event_t *expected = &event_set[i]; 190f08c3bdfSopenharmony_ci 191f08c3bdfSopenharmony_ci event_fid = (struct fanotify_event_info_fid *) (metadata + 1); 192f08c3bdfSopenharmony_ci event_file_handle = (struct file_handle *) event_fid->handle; 193f08c3bdfSopenharmony_ci 194f08c3bdfSopenharmony_ci if (i >= count) { 195f08c3bdfSopenharmony_ci tst_res(TFAIL, 196f08c3bdfSopenharmony_ci "got unnecessary event: mask=%llx " 197f08c3bdfSopenharmony_ci "pid=%u fd=%d", 198f08c3bdfSopenharmony_ci (unsigned long long) metadata->mask, 199f08c3bdfSopenharmony_ci metadata->pid, 200f08c3bdfSopenharmony_ci metadata->fd); 201f08c3bdfSopenharmony_ci metadata->mask = 0; 202f08c3bdfSopenharmony_ci } else if (metadata->fd != FAN_NOFD) { 203f08c3bdfSopenharmony_ci tst_res(TFAIL, 204f08c3bdfSopenharmony_ci "Received unexpected file descriptor %d in " 205f08c3bdfSopenharmony_ci "event. Expected to get FAN_NOFD(%d)", 206f08c3bdfSopenharmony_ci metadata->fd, FAN_NOFD); 207f08c3bdfSopenharmony_ci } else if (!(metadata->mask & expected->mask)) { 208f08c3bdfSopenharmony_ci tst_res(TFAIL, 209f08c3bdfSopenharmony_ci "Got event: mask=%llx (expected %llx) " 210f08c3bdfSopenharmony_ci "pid=%u fd=%d", 211f08c3bdfSopenharmony_ci (unsigned long long) metadata->mask, 212f08c3bdfSopenharmony_ci expected->mask, (unsigned int) metadata->pid, 213f08c3bdfSopenharmony_ci metadata->fd); 214f08c3bdfSopenharmony_ci } else if (metadata->pid != getpid()) { 215f08c3bdfSopenharmony_ci tst_res(TFAIL, 216f08c3bdfSopenharmony_ci "Got event: mask=%llx pid=%u " 217f08c3bdfSopenharmony_ci "(expected %u) fd=%d", 218f08c3bdfSopenharmony_ci (unsigned long long) metadata->mask, 219f08c3bdfSopenharmony_ci (unsigned int) metadata->pid, 220f08c3bdfSopenharmony_ci (unsigned int) getpid(), 221f08c3bdfSopenharmony_ci metadata->fd); 222f08c3bdfSopenharmony_ci } else if (event_file_handle->handle_bytes != 223f08c3bdfSopenharmony_ci expected->fid->handle.handle_bytes) { 224f08c3bdfSopenharmony_ci tst_res(TFAIL, 225f08c3bdfSopenharmony_ci "Got event: handle_bytes (%x) returned in " 226f08c3bdfSopenharmony_ci "event does not equal handle_bytes (%x) " 227f08c3bdfSopenharmony_ci "retunred in name_to_handle_at(2)", 228f08c3bdfSopenharmony_ci event_file_handle->handle_bytes, 229f08c3bdfSopenharmony_ci expected->fid->handle.handle_bytes); 230f08c3bdfSopenharmony_ci } else if (event_file_handle->handle_type != 231f08c3bdfSopenharmony_ci expected->fid->handle.handle_type) { 232f08c3bdfSopenharmony_ci tst_res(TFAIL, 233f08c3bdfSopenharmony_ci "handle_type (%x) returned in event does not " 234f08c3bdfSopenharmony_ci "equal to handle_type (%x) returned in " 235f08c3bdfSopenharmony_ci "name_to_handle_at(2)", 236f08c3bdfSopenharmony_ci event_file_handle->handle_type, 237f08c3bdfSopenharmony_ci expected->fid->handle.handle_type); 238f08c3bdfSopenharmony_ci } else if (memcmp(event_file_handle->f_handle, 239f08c3bdfSopenharmony_ci expected->fid->handle.f_handle, 240f08c3bdfSopenharmony_ci expected->fid->handle.handle_bytes) != 0) { 241f08c3bdfSopenharmony_ci tst_res(TFAIL, 242f08c3bdfSopenharmony_ci "file_handle returned in event does not match " 243f08c3bdfSopenharmony_ci "the file_handle returned in " 244f08c3bdfSopenharmony_ci "name_to_handle_at(2)"); 245f08c3bdfSopenharmony_ci } else if (memcmp(&event_fid->fsid, &expected->fid->fsid, 246f08c3bdfSopenharmony_ci sizeof(event_fid->fsid)) != 0) { 247f08c3bdfSopenharmony_ci tst_res(TFAIL, 248f08c3bdfSopenharmony_ci "event_fid->fsid != stats.f_fsid that was " 249f08c3bdfSopenharmony_ci "obtained via statfs(2)"); 250f08c3bdfSopenharmony_ci } else { 251f08c3bdfSopenharmony_ci tst_res(TPASS, 252f08c3bdfSopenharmony_ci "Got event: mask=%llx, pid=%u, " 253f08c3bdfSopenharmony_ci "fid=%x.%x.%lx values", 254f08c3bdfSopenharmony_ci metadata->mask, 255f08c3bdfSopenharmony_ci getpid(), 256f08c3bdfSopenharmony_ci FSID_VAL_MEMBER(event_fid->fsid, 0), 257f08c3bdfSopenharmony_ci FSID_VAL_MEMBER(event_fid->fsid, 1), 258f08c3bdfSopenharmony_ci *(unsigned long *) 259f08c3bdfSopenharmony_ci event_file_handle->f_handle); 260f08c3bdfSopenharmony_ci } 261f08c3bdfSopenharmony_ci metadata->mask &= ~expected->mask; 262f08c3bdfSopenharmony_ci /* No events left in current mask? Go for next event */ 263f08c3bdfSopenharmony_ci if (metadata->mask == 0) 264f08c3bdfSopenharmony_ci metadata = FAN_EVENT_NEXT(metadata, len); 265f08c3bdfSopenharmony_ci } 266f08c3bdfSopenharmony_ci 267f08c3bdfSopenharmony_ci for (; i < count; i++) 268f08c3bdfSopenharmony_ci tst_res(TFAIL, 269f08c3bdfSopenharmony_ci "Didn't receive event: mask=%llx", 270f08c3bdfSopenharmony_ci event_set[i].mask); 271f08c3bdfSopenharmony_ci} 272f08c3bdfSopenharmony_ci 273f08c3bdfSopenharmony_cistatic void do_setup(void) 274f08c3bdfSopenharmony_ci{ 275f08c3bdfSopenharmony_ci SAFE_MKDIR(TEST_DIR, 0755); 276f08c3bdfSopenharmony_ci REQUIRE_FANOTIFY_INIT_FLAGS_SUPPORTED_ON_FS(FAN_REPORT_FID, TEST_DIR); 277f08c3bdfSopenharmony_ci fanotify_fd = SAFE_FANOTIFY_INIT(FAN_REPORT_FID, O_RDONLY); 278f08c3bdfSopenharmony_ci} 279f08c3bdfSopenharmony_ci 280f08c3bdfSopenharmony_cistatic void do_cleanup(void) 281f08c3bdfSopenharmony_ci{ 282f08c3bdfSopenharmony_ci if (fanotify_fd > 0) 283f08c3bdfSopenharmony_ci SAFE_CLOSE(fanotify_fd); 284f08c3bdfSopenharmony_ci} 285f08c3bdfSopenharmony_ci 286f08c3bdfSopenharmony_cistatic struct tst_test test = { 287f08c3bdfSopenharmony_ci .needs_root = 1, 288f08c3bdfSopenharmony_ci .mount_device = 1, 289f08c3bdfSopenharmony_ci .mntpoint = MOUNT_POINT, 290f08c3bdfSopenharmony_ci .all_filesystems = 1, 291f08c3bdfSopenharmony_ci .test = do_test, 292f08c3bdfSopenharmony_ci .tcnt = ARRAY_SIZE(test_cases), 293f08c3bdfSopenharmony_ci .setup = do_setup, 294f08c3bdfSopenharmony_ci .cleanup = do_cleanup, 295f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]) { 296f08c3bdfSopenharmony_ci {"linux-git", "f367a62a7cad"}, 297f08c3bdfSopenharmony_ci {} 298f08c3bdfSopenharmony_ci } 299f08c3bdfSopenharmony_ci}; 300f08c3bdfSopenharmony_ci 301f08c3bdfSopenharmony_ci#else 302f08c3bdfSopenharmony_ci TST_TEST_TCONF("System does not have required name_to_handle_at() support"); 303f08c3bdfSopenharmony_ci#endif 304f08c3bdfSopenharmony_ci#else 305f08c3bdfSopenharmony_ci TST_TEST_TCONF("System does not have required fanotify support"); 306f08c3bdfSopenharmony_ci#endif 307