1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2020 CTERA Networks. All Rights Reserved. 4 * 5 * Started by Amir Goldstein <amir73il@gmail.com> 6 * 7 * DESCRIPTION 8 * Check that event is reported to watching parent and watching child 9 * based on their interest 10 * 11 * Test case #3 is a regression test for commit fecc4559780d that fixes 12 * a bug introduced in kernel v5.9: 13 * 14 * fsnotify: fix events reported to watching parent and child 15 */ 16 17#include "config.h" 18 19#if defined(HAVE_SYS_INOTIFY_H) 20# include <sys/inotify.h> 21#endif 22#include <errno.h> 23#include <string.h> 24#include "tst_test.h" 25#include "inotify.h" 26 27#if defined(HAVE_SYS_INOTIFY_H) 28 29#define EVENT_MAX 10 30/* Size of the event structure, not including the name */ 31#define EVENT_SIZE (sizeof(struct inotify_event)) 32#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16)) 33 34 35#define BUF_SIZE 256 36 37struct event_t { 38 char name[BUF_SIZE]; 39 unsigned int mask; 40 int wd; 41}; 42 43#define TEST_DIR "test_dir" 44#define TEST_FILE "test_file" 45 46static struct tcase { 47 const char *tname; 48 unsigned int parent_mask; 49 unsigned int subdir_mask; 50 unsigned int child_mask; 51 unsigned int parent_mask_other; 52 unsigned int subdir_mask_other; 53 unsigned int child_mask_other; 54} tcases[] = { 55 { 56 "Group with parent and child watches", 57 IN_ATTRIB, IN_ATTRIB, IN_ATTRIB, 58 0, 0, 0, 59 }, 60 { 61 "Group with child watches and other group with parent watch", 62 0, IN_ATTRIB, IN_ATTRIB, 63 IN_ATTRIB, 0, 0, 64 }, 65 { 66 "Group with parent watch and other group with child watches", 67 IN_ATTRIB, 0, 0, 68 0, IN_ATTRIB, IN_ATTRIB, 69 }, 70 { 71 "Two Groups with parent and child watches for different events", 72 IN_ATTRIB, IN_OPEN, IN_OPEN, 73 IN_OPEN, IN_ATTRIB, IN_ATTRIB, 74 }, 75}; 76 77struct event_t event_set[EVENT_MAX]; 78 79char event_buf[EVENT_BUF_LEN]; 80 81int fd_notify, fd_notify_other; 82 83static void verify_inotify(unsigned int n) 84{ 85 struct tcase *tc = &tcases[n]; 86 int i = 0, test_num = 0, len; 87 int wd_parent = 0, wd_subdir = 0, wd_child = 0; 88 int test_cnt = 0; 89 90 tst_res(TINFO, "Test #%d: %s", n, tc->tname); 91 92 fd_notify = SAFE_MYINOTIFY_INIT(); 93 fd_notify_other = SAFE_MYINOTIFY_INIT(); 94 95 /* Setup watches on parent dir and children */ 96 if (tc->parent_mask) 97 wd_parent = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, ".", tc->parent_mask); 98 if (tc->subdir_mask) 99 wd_subdir = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, TEST_DIR, tc->subdir_mask); 100 if (tc->child_mask) 101 wd_child = SAFE_MYINOTIFY_ADD_WATCH(fd_notify, TEST_FILE, tc->child_mask); 102 /* 103 * Setup watches on "other" group to verify no intereferecne with our group. 104 * We do not check events reported to the "other" group. 105 */ 106 if (tc->parent_mask_other) 107 SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, ".", tc->parent_mask_other); 108 if (tc->subdir_mask_other) 109 SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, TEST_DIR, tc->subdir_mask_other); 110 if (tc->child_mask_other) 111 SAFE_MYINOTIFY_ADD_WATCH(fd_notify_other, TEST_FILE, tc->child_mask_other); 112 113 /* 114 * Generate IN_ATTRIB events on file and subdir that should be reported to parent 115 * dir with name and to children without name if they have IN_ATTRIB in their mask. 116 */ 117 SAFE_CHMOD(TEST_DIR, 0755); 118 SAFE_CHMOD(TEST_FILE, 0644); 119 120 if (wd_parent && (tc->parent_mask & IN_ATTRIB)) { 121 event_set[test_cnt].wd = wd_parent; 122 event_set[test_cnt].mask = tc->parent_mask | IN_ISDIR; 123 strcpy(event_set[test_cnt].name, TEST_DIR); 124 test_cnt++; 125 } 126 if (wd_subdir && (tc->subdir_mask & IN_ATTRIB)) { 127 event_set[test_cnt].wd = wd_subdir; 128 event_set[test_cnt].mask = tc->subdir_mask | IN_ISDIR; 129 strcpy(event_set[test_cnt].name, ""); 130 test_cnt++; 131 } 132 if (wd_parent && (tc->parent_mask & IN_ATTRIB)) { 133 event_set[test_cnt].wd = wd_parent; 134 event_set[test_cnt].mask = tc->parent_mask; 135 strcpy(event_set[test_cnt].name, TEST_FILE); 136 test_cnt++; 137 } 138 if (wd_child && (tc->child_mask & IN_ATTRIB)) { 139 event_set[test_cnt].wd = wd_child; 140 event_set[test_cnt].mask = tc->child_mask; 141 strcpy(event_set[test_cnt].name, ""); 142 test_cnt++; 143 } 144 145 len = read(fd_notify, event_buf, EVENT_BUF_LEN); 146 if (len == -1) 147 tst_brk(TBROK | TERRNO, "read failed"); 148 149 while (i < len) { 150 struct event_t *expected = &event_set[test_num]; 151 struct inotify_event *event; 152 event = (struct inotify_event *)&event_buf[i]; 153 if (test_num >= test_cnt) { 154 tst_res(TFAIL, 155 "got unnecessary event: " 156 "wd=%d mask=%04x len=%u " 157 "name=\"%.*s\"", event->wd, event->mask, 158 event->len, event->len, event->name); 159 160 } else if (expected->wd == event->wd && 161 expected->mask == event->mask && 162 !strncmp(expected->name, event->name, event->len)) { 163 tst_res(TPASS, 164 "got event: wd=%d mask=%04x " 165 "cookie=%u len=%u name=\"%.*s\"", 166 event->wd, event->mask, event->cookie, 167 event->len, event->len, event->name); 168 169 } else { 170 tst_res(TFAIL, "got event: wd=%d (expected %d) " 171 "mask=%04x (expected %x) len=%u " 172 "name=\"%.*s\" (expected \"%s\")", 173 event->wd, expected->wd, 174 event->mask, expected->mask, 175 event->len, event->len, 176 event->name, expected->name); 177 } 178 test_num++; 179 i += EVENT_SIZE + event->len; 180 } 181 182 for (; test_num < test_cnt; test_num++) { 183 tst_res(TFAIL, "didn't get event: mask=%04x ", 184 event_set[test_num].mask); 185 } 186 187 SAFE_CLOSE(fd_notify); 188 SAFE_CLOSE(fd_notify_other); 189} 190 191static void setup(void) 192{ 193 SAFE_MKDIR(TEST_DIR, 00700); 194 SAFE_FILE_PRINTF(TEST_FILE, "1"); 195} 196 197static void cleanup(void) 198{ 199 if (fd_notify > 0) 200 SAFE_CLOSE(fd_notify); 201 if (fd_notify_other > 0) 202 SAFE_CLOSE(fd_notify_other); 203} 204 205static struct tst_test test = { 206 .needs_tmpdir = 1, 207 .setup = setup, 208 .cleanup = cleanup, 209 .test = verify_inotify, 210 .tcnt = ARRAY_SIZE(tcases), 211 .tags = (const struct tst_tag[]) { 212 {"linux-git", "fecc4559780d"}, 213 {} 214 } 215}; 216 217#else 218 TST_TEST_TCONF("system doesn't have required inotify support"); 219#endif /* defined(HAVE_SYS_INOTIFY_H) */ 220