1 1.1 christos #include <sys/event.h> 2 1.1 christos #include <sys/stat.h> 3 1.1 christos #include <sys/time.h> 4 1.1 christos #include <fcntl.h> 5 1.1 christos #include <stdio.h> 6 1.1 christos #include <unistd.h> 7 1.1 christos 8 1.1 christos #include <atf-c.h> 9 1.1 christos 10 1.1 christos /* 11 1.1 christos * Test cases for events triggered by manipulating a target directory 12 1.1 christos * content. Using EVFILT_VNODE filter on the target directory descriptor. 13 1.1 christos * 14 1.1 christos */ 15 1.1 christos 16 1.1 christos static const char *dir_target = "foo"; 17 1.1 christos static const char *dir_inside1 = "foo/bar1"; 18 1.1 christos static const char *dir_inside2 = "foo/bar2"; 19 1.1 christos static const char *dir_outside = "bar"; 20 1.1 christos static const char *file_inside1 = "foo/baz1"; 21 1.1 christos static const char *file_inside2 = "foo/baz2"; 22 1.1 christos static const char *file_outside = "qux"; 23 1.1 christos static const struct timespec ts = {0, 0}; 24 1.1 christos static int kq = -1; 25 1.1 christos static int target = -1; 26 1.1 christos 27 1.1 christos int init_target(void); 28 1.1 christos int init_kqueue(void); 29 1.1 christos int create_file(const char *); 30 1.1 christos void cleanup(void); 31 1.1 christos 32 1.1 christos int 33 1.1 christos init_target(void) 34 1.1 christos { 35 1.1 christos if (mkdir(dir_target, S_IRWXU) < 0) { 36 1.1 christos return -1; 37 1.1 christos } 38 1.1 christos target = open(dir_target, O_RDONLY, 0); 39 1.1 christos return target; 40 1.1 christos } 41 1.1 christos 42 1.1 christos int 43 1.1 christos init_kqueue(void) 44 1.1 christos { 45 1.1 christos struct kevent eventlist[1]; 46 1.1 christos 47 1.1 christos kq = kqueue(); 48 1.1 christos if (kq < 0) { 49 1.1 christos return -1; 50 1.1 christos } 51 1.1 christos EV_SET(&eventlist[0], (uintptr_t)target, EVFILT_VNODE, 52 1.1 christos EV_ADD | EV_ONESHOT, NOTE_DELETE | 53 1.1 christos NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | 54 1.1 christos NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, 0, 0); 55 1.1 christos return kevent(kq, eventlist, 1, NULL, 0, NULL); 56 1.1 christos } 57 1.1 christos 58 1.1 christos int 59 1.1 christos create_file(const char *file) 60 1.1 christos { 61 1.1 christos int fd; 62 1.1 christos 63 1.1 christos fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 64 1.1 christos if (fd < 0) { 65 1.1 christos return -1; 66 1.1 christos } 67 1.1 christos return close(fd); 68 1.1 christos } 69 1.1 christos 70 1.1 christos void 71 1.1 christos cleanup(void) 72 1.1 christos { 73 1.1 christos (void)unlink(file_inside1); 74 1.1 christos (void)unlink(file_inside2); 75 1.1 christos (void)unlink(file_outside); 76 1.1 christos (void)rmdir(dir_inside1); 77 1.1 christos (void)rmdir(dir_inside2); 78 1.1 christos (void)rmdir(dir_outside); 79 1.1 christos (void)rmdir(dir_target); 80 1.1 christos (void)close(kq); 81 1.1 christos (void)close(target); 82 1.1 christos } 83 1.1 christos 84 1.1 christos ATF_TC_WITH_CLEANUP(dir_no_note_link_create_file_in); 85 1.1 christos ATF_TC_HEAD(dir_no_note_link_create_file_in, tc) 86 1.1 christos { 87 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 88 1.1 christos "that kevent(2) does not return NOTE_LINK for the directory " 89 1.1 christos "'foo' if a file 'foo/baz' is created."); 90 1.1 christos } 91 1.1 christos ATF_TC_BODY(dir_no_note_link_create_file_in, tc) 92 1.1 christos { 93 1.1 christos struct kevent changelist[1]; 94 1.1 christos 95 1.1 christos ATF_REQUIRE(init_target() != -1); 96 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 97 1.1 christos 98 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 99 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 100 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 101 1.1 christos } 102 1.1 christos ATF_TC_CLEANUP(dir_no_note_link_create_file_in, tc) 103 1.1 christos { 104 1.1 christos cleanup(); 105 1.1 christos } 106 1.1 christos 107 1.1 christos ATF_TC_WITH_CLEANUP(dir_no_note_link_delete_file_in); 108 1.1 christos ATF_TC_HEAD(dir_no_note_link_delete_file_in, tc) 109 1.1 christos { 110 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 111 1.1 christos "that kevent(2) does not return NOTE_LINK for the directory " 112 1.1 christos "'foo' if a file 'foo/baz' is deleted."); 113 1.1 christos } 114 1.1 christos ATF_TC_BODY(dir_no_note_link_delete_file_in, tc) 115 1.1 christos { 116 1.1 christos struct kevent changelist[1]; 117 1.1 christos 118 1.1 christos ATF_REQUIRE(init_target() != -1); 119 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 120 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 121 1.1 christos 122 1.1 christos ATF_REQUIRE(unlink(file_inside1) != -1); 123 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 124 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 125 1.1 christos } 126 1.1 christos ATF_TC_CLEANUP(dir_no_note_link_delete_file_in, tc) 127 1.1 christos { 128 1.1 christos cleanup(); 129 1.1 christos } 130 1.1 christos 131 1.1 christos ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_dir_within); 132 1.1 christos ATF_TC_HEAD(dir_no_note_link_mv_dir_within, tc) 133 1.1 christos { 134 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 135 1.1 christos "that kevent(2) does not return NOTE_LINK for the directory " 136 1.1 christos "'foo' if a directory 'foo/bar' is renamed to 'foo/baz'."); 137 1.1 christos } 138 1.1 christos ATF_TC_BODY(dir_no_note_link_mv_dir_within, tc) 139 1.1 christos { 140 1.1 christos struct kevent changelist[1]; 141 1.1 christos 142 1.1 christos ATF_REQUIRE(init_target() != -1); 143 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 144 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 145 1.1 christos 146 1.1 christos ATF_REQUIRE(rename(dir_inside1, dir_inside2) != -1); 147 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 148 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 149 1.1 christos } 150 1.1 christos ATF_TC_CLEANUP(dir_no_note_link_mv_dir_within, tc) 151 1.1 christos { 152 1.1 christos cleanup(); 153 1.1 christos } 154 1.1 christos 155 1.1 christos ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_file_within); 156 1.1 christos ATF_TC_HEAD(dir_no_note_link_mv_file_within, tc) 157 1.1 christos { 158 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 159 1.1 christos "that kevent(2) does not return NOTE_LINK for the directory " 160 1.1 christos "'foo' if a file 'foo/baz' is renamed to 'foo/qux'."); 161 1.1 christos } 162 1.1 christos ATF_TC_BODY(dir_no_note_link_mv_file_within, tc) 163 1.1 christos { 164 1.1 christos struct kevent changelist[1]; 165 1.1 christos 166 1.1 christos ATF_REQUIRE(init_target() != -1); 167 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 168 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 169 1.1 christos 170 1.1 christos ATF_REQUIRE(rename(file_inside1, file_inside2) != -1); 171 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 172 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 173 1.1 christos } 174 1.1 christos ATF_TC_CLEANUP(dir_no_note_link_mv_file_within, tc) 175 1.1 christos { 176 1.1 christos cleanup(); 177 1.1 christos } 178 1.1 christos 179 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_link_create_dir_in); 180 1.1 christos ATF_TC_HEAD(dir_note_link_create_dir_in, tc) 181 1.1 christos { 182 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 183 1.1 christos "that kevent(2) returns NOTE_LINK for the directory " 184 1.1 christos "'foo' if a directory 'foo/bar' is created."); 185 1.1 christos } 186 1.1 christos ATF_TC_BODY(dir_note_link_create_dir_in, tc) 187 1.1 christos { 188 1.1 christos struct kevent changelist[1]; 189 1.1 christos 190 1.1 christos ATF_REQUIRE(init_target() != -1); 191 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 192 1.1 christos 193 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 194 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 195 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 196 1.1 christos } 197 1.1 christos ATF_TC_CLEANUP(dir_note_link_create_dir_in, tc) 198 1.1 christos { 199 1.1 christos cleanup(); 200 1.1 christos } 201 1.1 christos 202 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_link_delete_dir_in); 203 1.1 christos ATF_TC_HEAD(dir_note_link_delete_dir_in, tc) 204 1.1 christos { 205 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 206 1.1 christos "that kevent(2) returns NOTE_LINK for the directory " 207 1.1 christos "'foo' if a directory 'foo/bar' is deleted."); 208 1.1 christos } 209 1.1 christos ATF_TC_BODY(dir_note_link_delete_dir_in, tc) 210 1.1 christos { 211 1.1 christos struct kevent changelist[1]; 212 1.1 christos 213 1.1 christos ATF_REQUIRE(init_target() != -1); 214 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 215 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 216 1.1 christos 217 1.1 christos ATF_REQUIRE(rmdir(dir_inside1) != -1); 218 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 219 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 220 1.1 christos } 221 1.1 christos ATF_TC_CLEANUP(dir_note_link_delete_dir_in, tc) 222 1.1 christos { 223 1.1 christos cleanup(); 224 1.1 christos } 225 1.1 christos 226 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_in); 227 1.1 christos ATF_TC_HEAD(dir_note_link_mv_dir_in, tc) 228 1.1 christos { 229 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 230 1.1 christos "that kevent(2) returns NOTE_LINK for the directory " 231 1.1 christos "'foo' if a directory 'bar' is renamed to 'foo/bar'."); 232 1.1 christos } 233 1.1 christos ATF_TC_BODY(dir_note_link_mv_dir_in, tc) 234 1.1 christos { 235 1.1 christos struct kevent changelist[1]; 236 1.1 christos 237 1.1 christos ATF_REQUIRE(init_target() != -1); 238 1.1 christos ATF_REQUIRE(mkdir(dir_outside, S_IRWXU) != -1); 239 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 240 1.1 christos 241 1.1 christos ATF_REQUIRE(rename(dir_outside, dir_inside1) != -1); 242 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 243 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 244 1.1 christos } 245 1.1 christos ATF_TC_CLEANUP(dir_note_link_mv_dir_in, tc) 246 1.1 christos { 247 1.1 christos cleanup(); 248 1.1 christos } 249 1.1 christos 250 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_out); 251 1.1 christos ATF_TC_HEAD(dir_note_link_mv_dir_out, tc) 252 1.1 christos { 253 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 254 1.1 christos "that kevent(2) returns NOTE_LINK for the directory " 255 1.1 christos "'foo' if a directory 'foo/bar' is renamed to 'bar'."); 256 1.1 christos } 257 1.1 christos ATF_TC_BODY(dir_note_link_mv_dir_out, tc) 258 1.1 christos { 259 1.1 christos struct kevent changelist[1]; 260 1.1 christos 261 1.1 christos ATF_REQUIRE(init_target() != -1); 262 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 263 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 264 1.1 christos 265 1.1 christos ATF_REQUIRE(rename(dir_inside1, dir_outside) != -1); 266 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 267 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 268 1.1 christos } 269 1.1 christos ATF_TC_CLEANUP(dir_note_link_mv_dir_out, tc) 270 1.1 christos { 271 1.1 christos cleanup(); 272 1.1 christos } 273 1.1 christos 274 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_create_dir_in); 275 1.1 christos ATF_TC_HEAD(dir_note_write_create_dir_in, tc) 276 1.1 christos { 277 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 278 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 279 1.1 christos "'foo' if a directory 'foo/bar' is created."); 280 1.1 christos } 281 1.1 christos ATF_TC_BODY(dir_note_write_create_dir_in, tc) 282 1.1 christos { 283 1.1 christos struct kevent changelist[1]; 284 1.1 christos 285 1.1 christos ATF_REQUIRE(init_target() != -1); 286 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 287 1.1 christos 288 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 289 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 290 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 291 1.1 christos } 292 1.1 christos ATF_TC_CLEANUP(dir_note_write_create_dir_in, tc) 293 1.1 christos { 294 1.1 christos cleanup(); 295 1.1 christos } 296 1.1 christos 297 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_create_file_in); 298 1.1 christos ATF_TC_HEAD(dir_note_write_create_file_in, tc) 299 1.1 christos { 300 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 301 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 302 1.1 christos "'foo' if a file 'foo/baz' is created."); 303 1.1 christos } 304 1.1 christos ATF_TC_BODY(dir_note_write_create_file_in, tc) 305 1.1 christos { 306 1.1 christos struct kevent changelist[1]; 307 1.1 christos 308 1.1 christos ATF_REQUIRE(init_target() != -1); 309 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 310 1.1 christos 311 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 312 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 313 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 314 1.1 christos } 315 1.1 christos ATF_TC_CLEANUP(dir_note_write_create_file_in, tc) 316 1.1 christos { 317 1.1 christos cleanup(); 318 1.1 christos } 319 1.1 christos 320 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_delete_dir_in); 321 1.1 christos ATF_TC_HEAD(dir_note_write_delete_dir_in, tc) 322 1.1 christos { 323 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 324 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 325 1.1 christos "'foo' if a directory 'foo/bar' is deleted."); 326 1.1 christos } 327 1.1 christos ATF_TC_BODY(dir_note_write_delete_dir_in, tc) 328 1.1 christos { 329 1.1 christos struct kevent changelist[1]; 330 1.1 christos 331 1.1 christos ATF_REQUIRE(init_target() != -1); 332 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 333 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 334 1.1 christos 335 1.1 christos ATF_REQUIRE(rmdir(dir_inside1) != -1); 336 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 337 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 338 1.1 christos } 339 1.1 christos ATF_TC_CLEANUP(dir_note_write_delete_dir_in, tc) 340 1.1 christos { 341 1.1 christos cleanup(); 342 1.1 christos } 343 1.1 christos 344 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_delete_file_in); 345 1.1 christos ATF_TC_HEAD(dir_note_write_delete_file_in, tc) 346 1.1 christos { 347 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 348 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 349 1.1 christos "'foo' if a file 'foo/baz' is deleted."); 350 1.1 christos } 351 1.1 christos ATF_TC_BODY(dir_note_write_delete_file_in, tc) 352 1.1 christos { 353 1.1 christos struct kevent changelist[1]; 354 1.1 christos 355 1.1 christos ATF_REQUIRE(init_target() != -1); 356 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 357 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 358 1.1 christos 359 1.1 christos ATF_REQUIRE(unlink(file_inside1) != -1); 360 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 361 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 362 1.1 christos } 363 1.1 christos ATF_TC_CLEANUP(dir_note_write_delete_file_in, tc) 364 1.1 christos { 365 1.1 christos cleanup(); 366 1.1 christos } 367 1.1 christos 368 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_in); 369 1.1 christos ATF_TC_HEAD(dir_note_write_mv_dir_in, tc) 370 1.1 christos { 371 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 372 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 373 1.1 christos "'foo' if a directory 'bar' is renamed to 'foo/bar'."); 374 1.1 christos } 375 1.1 christos ATF_TC_BODY(dir_note_write_mv_dir_in, tc) 376 1.1 christos { 377 1.1 christos struct kevent changelist[1]; 378 1.1 christos 379 1.1 christos ATF_REQUIRE(init_target() != -1); 380 1.1 christos ATF_REQUIRE(mkdir(dir_outside, S_IRWXU) != -1); 381 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 382 1.1 christos 383 1.1 christos ATF_REQUIRE(rename(dir_outside, dir_inside1) != -1); 384 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 385 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 386 1.1 christos } 387 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_dir_in, tc) 388 1.1 christos { 389 1.1 christos cleanup(); 390 1.1 christos } 391 1.1 christos 392 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_out); 393 1.1 christos ATF_TC_HEAD(dir_note_write_mv_dir_out, tc) 394 1.1 christos { 395 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 396 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 397 1.1 christos "'foo' if a directory 'foo/bar' is renamed to 'bar'."); 398 1.1 christos } 399 1.1 christos ATF_TC_BODY(dir_note_write_mv_dir_out, tc) 400 1.1 christos { 401 1.1 christos struct kevent changelist[1]; 402 1.1 christos 403 1.1 christos ATF_REQUIRE(init_target() != -1); 404 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 405 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 406 1.1 christos 407 1.1 christos ATF_REQUIRE(rename(dir_inside1, dir_outside) != -1); 408 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 409 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 410 1.1 christos } 411 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_dir_out, tc) 412 1.1 christos { 413 1.1 christos cleanup(); 414 1.1 christos } 415 1.1 christos 416 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_within); 417 1.1 christos ATF_TC_HEAD(dir_note_write_mv_dir_within, tc) 418 1.1 christos { 419 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 420 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 421 1.1 christos "'foo' if a directory 'foo/bar' is renamed to 'foo/baz'."); 422 1.1 christos } 423 1.1 christos ATF_TC_BODY(dir_note_write_mv_dir_within, tc) 424 1.1 christos { 425 1.1 christos struct kevent changelist[1]; 426 1.1 christos 427 1.1 christos ATF_REQUIRE(init_target() != -1); 428 1.1 christos ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 429 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 430 1.1 christos 431 1.1 christos ATF_REQUIRE(rename(dir_inside1, dir_inside2) != -1); 432 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 433 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 434 1.1 christos } 435 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_dir_within, tc) 436 1.1 christos { 437 1.1 christos cleanup(); 438 1.1 christos } 439 1.1 christos 440 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_in); 441 1.1 christos ATF_TC_HEAD(dir_note_write_mv_file_in, tc) 442 1.1 christos { 443 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 444 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 445 1.1 christos "'foo' if a file 'qux' is renamed to 'foo/baz'."); 446 1.1 christos } 447 1.1 christos ATF_TC_BODY(dir_note_write_mv_file_in, tc) 448 1.1 christos { 449 1.1 christos struct kevent changelist[1]; 450 1.1 christos 451 1.1 christos ATF_REQUIRE(init_target() != -1); 452 1.1 christos ATF_REQUIRE(create_file(file_outside) != -1); 453 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 454 1.1 christos 455 1.1 christos ATF_REQUIRE(rename(file_outside, file_inside1) != -1); 456 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 457 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 458 1.1 christos } 459 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_file_in, tc) 460 1.1 christos { 461 1.1 christos cleanup(); 462 1.1 christos } 463 1.1 christos 464 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_out); 465 1.1 christos ATF_TC_HEAD(dir_note_write_mv_file_out, tc) 466 1.1 christos { 467 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 468 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 469 1.1 christos "'foo' if a file 'foo/baz' is renamed to 'qux'."); 470 1.1 christos } 471 1.1 christos ATF_TC_BODY(dir_note_write_mv_file_out, tc) 472 1.1 christos { 473 1.1 christos struct kevent changelist[1]; 474 1.1 christos 475 1.1 christos ATF_REQUIRE(init_target() != -1); 476 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 477 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 478 1.1 christos 479 1.1 christos ATF_REQUIRE(rename(file_inside1, file_outside) != -1); 480 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 481 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 482 1.1 christos } 483 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_file_out, tc) 484 1.1 christos { 485 1.1 christos cleanup(); 486 1.1 christos } 487 1.1 christos 488 1.1 christos ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_within); 489 1.1 christos ATF_TC_HEAD(dir_note_write_mv_file_within, tc) 490 1.1 christos { 491 1.1 christos atf_tc_set_md_var(tc, "descr", "This test case ensures " 492 1.1 christos "that kevent(2) returns NOTE_WRITE for the directory " 493 1.1 christos "'foo' if a file 'foo/baz' is renamed to 'foo/qux'."); 494 1.1 christos } 495 1.1 christos ATF_TC_BODY(dir_note_write_mv_file_within, tc) 496 1.1 christos { 497 1.1 christos struct kevent changelist[1]; 498 1.1 christos 499 1.1 christos ATF_REQUIRE(init_target() != -1); 500 1.1 christos ATF_REQUIRE(create_file(file_inside1) != -1); 501 1.1 christos ATF_REQUIRE(init_kqueue() != -1); 502 1.1 christos 503 1.1 christos ATF_REQUIRE(rename(file_inside1, file_inside2) != -1); 504 1.1 christos ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 505 1.1 christos ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 506 1.1 christos } 507 1.1 christos ATF_TC_CLEANUP(dir_note_write_mv_file_within, tc) 508 1.1 christos { 509 1.1 christos cleanup(); 510 1.1 christos } 511 1.1 christos 512 1.2 thorpej static const char testfile[] = "testfile"; 513 1.2 thorpej 514 1.2 thorpej ATF_TC_WITH_CLEANUP(open_write_read_close); 515 1.2 thorpej ATF_TC_HEAD(open_write_read_close, tc) 516 1.2 thorpej { 517 1.2 thorpej atf_tc_set_md_var(tc, "descr", "This test case exercises " 518 1.2 thorpej "that kevent(2) returns NOTE_OPEN, NOTE_READ, NOTE_WRITE, " 519 1.2 thorpej "NOTE_CLOSE, and NOTE_CLOSE_WRITE."); 520 1.2 thorpej } 521 1.2 thorpej ATF_TC_BODY(open_write_read_close, tc) 522 1.2 thorpej { 523 1.2 thorpej struct kevent event[1]; 524 1.2 thorpej char buf[sizeof(testfile)]; 525 1.2 thorpej int fd; 526 1.2 thorpej 527 1.2 thorpej ATF_REQUIRE((kq = kqueue()) != -1); 528 1.2 thorpej 529 1.2 thorpej /* 530 1.2 thorpej * Create the test file and register an event on it. We need 531 1.2 thorpej * to keep the fd open to keep receiving events, so we'll just 532 1.2 thorpej * leak it and re-use the fd variable. 533 1.2 thorpej */ 534 1.2 thorpej ATF_REQUIRE((fd = open(testfile, 535 1.2 thorpej O_RDWR | O_CREAT | O_TRUNC, 0600)) != -1); 536 1.2 thorpej EV_SET(&event[0], fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, 537 1.2 thorpej NOTE_OPEN | NOTE_READ | NOTE_WRITE | 538 1.2 thorpej NOTE_CLOSE | NOTE_CLOSE_WRITE, 0, NULL); 539 1.2 thorpej ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0); 540 1.2 thorpej 541 1.2 thorpej /* 542 1.2 thorpej * Open the file for writing, check for NOTE_OPEN. 543 1.2 thorpej * Write to the file, check for NOTE_WRITE | NOTE_EXTEND. 544 1.2 thorpej * Re-write the file, check for NOTE_WRITE and !NOTE_EXTEND. 545 1.2 thorpej * Write one additional byte, check for NOTE_WRITE | NOTE_EXTEND. 546 1.2 thorpej * Close the file, check for NOTE_CLOSE_WRITE. 547 1.2 thorpej */ 548 1.2 thorpej ATF_REQUIRE((fd = open(testfile, O_RDWR)) != -1); 549 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 550 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_OPEN); 551 1.2 thorpej 552 1.2 thorpej ATF_REQUIRE((pwrite(fd, testfile, 553 1.2 thorpej sizeof(testfile), 0)) == sizeof(testfile)); 554 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 555 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_WRITE); 556 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_EXTEND); 557 1.2 thorpej 558 1.2 thorpej ATF_REQUIRE((pwrite(fd, testfile, 559 1.2 thorpej sizeof(testfile), 0)) == sizeof(testfile)); 560 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 561 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_WRITE); 562 1.2 thorpej ATF_REQUIRE((event[0].fflags & NOTE_EXTEND) == 0); 563 1.2 thorpej 564 1.2 thorpej ATF_REQUIRE((pwrite(fd, testfile, 565 1.2 thorpej 1, sizeof(testfile))) == 1); 566 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 567 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_WRITE); 568 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_EXTEND); 569 1.2 thorpej 570 1.2 thorpej (void)close(fd); 571 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 572 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_CLOSE_WRITE); 573 1.2 thorpej ATF_REQUIRE((event[0].fflags & NOTE_CLOSE) == 0); 574 1.2 thorpej 575 1.2 thorpej /* 576 1.2 thorpej * Open the file for reading, check for NOTE_OPEN. 577 1.2 thorpej * Read from the file, check for NOTE_READ. 578 1.2 thorpej * Close the file., check for NOTE_CLOSE. 579 1.2 thorpej */ 580 1.2 thorpej ATF_REQUIRE((fd = open(testfile, O_RDONLY)) != -1); 581 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 582 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_OPEN); 583 1.2 thorpej 584 1.2 thorpej ATF_REQUIRE((read(fd, buf, sizeof(buf))) == sizeof(buf)); 585 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 586 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_READ); 587 1.2 thorpej 588 1.2 thorpej (void)close(fd); 589 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 590 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_CLOSE); 591 1.2 thorpej ATF_REQUIRE((event[0].fflags & NOTE_CLOSE_WRITE) == 0); 592 1.2 thorpej } 593 1.2 thorpej ATF_TC_CLEANUP(open_write_read_close, tc) 594 1.2 thorpej { 595 1.2 thorpej (void)unlink(testfile); 596 1.2 thorpej } 597 1.2 thorpej 598 1.2 thorpej ATF_TC_WITH_CLEANUP(interest); 599 1.2 thorpej ATF_TC_HEAD(interest, tc) 600 1.2 thorpej { 601 1.2 thorpej atf_tc_set_md_var(tc, "descr", "This test case exercises " 602 1.2 thorpej "the kernel code that computes vnode kevent interest"); 603 1.2 thorpej } 604 1.2 thorpej ATF_TC_BODY(interest, tc) 605 1.2 thorpej { 606 1.2 thorpej struct kevent event[3]; 607 1.2 thorpej int open_ev_fd, write_ev_fd, close_ev_fd; 608 1.2 thorpej int fd; 609 1.2 thorpej 610 1.2 thorpej /* 611 1.2 thorpej * This test cases exercises some implementation details 612 1.2 thorpej * regarding how "kevent interest" is computed for a vnode. 613 1.2 thorpej * 614 1.2 thorpej * We are going to add events, one at a time, in a specific 615 1.2 thorpej * order, and then remove one of them, with the knowledge that 616 1.2 thorpej * a specific code path in vfs_vnops.c:vn_knote_detach() will 617 1.2 thorpej * be taken. There are several KASSERT()s in this code path 618 1.2 thorpej * that will be validated. 619 1.2 thorpej * 620 1.2 thorpej * In order to ensure distinct knotes are attached to the vnodes, 621 1.2 thorpej * we must use a different file descriptor to register interest 622 1.2 thorpej * in each kind of event. 623 1.2 thorpej */ 624 1.2 thorpej 625 1.2 thorpej ATF_REQUIRE((kq = kqueue()) != -1); 626 1.2 thorpej 627 1.2 thorpej /* 628 1.2 thorpej * Create the test file and register an event on it. We need 629 1.2 thorpej * to keep the fd open to keep receiving events, so we'll just 630 1.2 thorpej * leak it and re-use the fd variable. 631 1.2 thorpej */ 632 1.2 thorpej ATF_REQUIRE((open_ev_fd = open(testfile, 633 1.2 thorpej O_RDWR | O_CREAT | O_TRUNC, 0600)) != -1); 634 1.2 thorpej ATF_REQUIRE((write_ev_fd = dup(open_ev_fd)) != -1); 635 1.2 thorpej ATF_REQUIRE((close_ev_fd = dup(open_ev_fd)) != -1); 636 1.2 thorpej 637 1.2 thorpej EV_SET(&event[0], open_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, 638 1.2 thorpej NOTE_OPEN, 0, NULL); 639 1.2 thorpej EV_SET(&event[1], write_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, 640 1.2 thorpej NOTE_WRITE, 0, NULL); 641 1.2 thorpej EV_SET(&event[2], close_ev_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, 642 1.2 thorpej NOTE_CLOSE | NOTE_CLOSE_WRITE, 0, NULL); 643 1.2 thorpej ATF_REQUIRE(kevent(kq, event, 3, NULL, 0, NULL) == 0); 644 1.2 thorpej 645 1.2 thorpej /* 646 1.2 thorpej * The testfile vnode now has 3 knotes attached, in "LIFO" 647 1.2 thorpej * order: 648 1.2 thorpej * 649 1.2 thorpej * NOTE_CLOSE -> NOTE_WRITE -> NOTE_OPEN 650 1.2 thorpej * 651 1.2 thorpej * We will now remove the NOTE_WRITE knote. 652 1.2 thorpej */ 653 1.2 thorpej (void)close(write_ev_fd); 654 1.2 thorpej 655 1.2 thorpej ATF_REQUIRE((fd = open(testfile, O_RDWR)) != -1); 656 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 657 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_OPEN); 658 1.2 thorpej 659 1.2 thorpej ATF_REQUIRE((pwrite(fd, testfile, 660 1.2 thorpej sizeof(testfile), 0)) == sizeof(testfile)); 661 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 0); 662 1.2 thorpej 663 1.2 thorpej (void)close(fd); 664 1.2 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1); 665 1.2 thorpej ATF_REQUIRE(event[0].fflags & NOTE_CLOSE_WRITE); 666 1.2 thorpej ATF_REQUIRE((event[0].fflags & NOTE_CLOSE) == 0); 667 1.2 thorpej 668 1.2 thorpej } 669 1.2 thorpej ATF_TC_CLEANUP(interest, tc) 670 1.2 thorpej { 671 1.2 thorpej (void)unlink(testfile); 672 1.2 thorpej } 673 1.2 thorpej 674 1.3 thorpej ATF_TC_WITH_CLEANUP(rename_over_self_hardlink); 675 1.3 thorpej ATF_TC_HEAD(rename_over_self_hardlink, tc) 676 1.3 thorpej { 677 1.3 thorpej atf_tc_set_md_var(tc, "descr", "This test case tests " 678 1.3 thorpej "renaming a file over a hard-link to itself"); 679 1.3 thorpej } 680 1.3 thorpej ATF_TC_BODY(rename_over_self_hardlink, tc) 681 1.3 thorpej { 682 1.3 thorpej struct kevent event[2], *dir_ev, *file_ev; 683 1.3 thorpej int dir_fd, file_fd; 684 1.3 thorpej 685 1.3 thorpej ATF_REQUIRE((kq = kqueue()) != -1); 686 1.3 thorpej 687 1.3 thorpej ATF_REQUIRE((mkdir(dir_target, 0700)) == 0); 688 1.3 thorpej ATF_REQUIRE((dir_fd = open(dir_target, O_RDONLY)) != -1); 689 1.3 thorpej 690 1.3 thorpej ATF_REQUIRE((file_fd = open(file_inside1, O_RDONLY | O_CREAT, 691 1.3 thorpej 0600)) != -1); 692 1.3 thorpej ATF_REQUIRE(link(file_inside1, file_inside2) == 0); 693 1.3 thorpej 694 1.3 thorpej EV_SET(&event[0], dir_fd, EVFILT_VNODE, EV_ADD, 695 1.3 thorpej NOTE_WRITE | NOTE_EXTEND | NOTE_LINK, 0, NULL); 696 1.3 thorpej EV_SET(&event[1], file_fd, EVFILT_VNODE, EV_ADD, 697 1.3 thorpej NOTE_LINK | NOTE_DELETE, 0, NULL); 698 1.3 thorpej ATF_REQUIRE(kevent(kq, event, 2, NULL, 0, NULL) == 0); 699 1.3 thorpej 700 1.3 thorpej ATF_REQUIRE(rename(file_inside1, file_inside2) == 0); 701 1.3 thorpej 702 1.3 thorpej ATF_REQUIRE(kevent(kq, NULL, 0, event, 2, &ts) == 2); 703 1.3 thorpej ATF_REQUIRE(event[0].ident == (uintptr_t)dir_fd || 704 1.3 thorpej event[0].ident == (uintptr_t)file_fd); 705 1.3 thorpej ATF_REQUIRE(event[1].ident == (uintptr_t)dir_fd || 706 1.3 thorpej event[1].ident == (uintptr_t)file_fd); 707 1.3 thorpej if (event[0].ident == (uintptr_t)dir_fd) { 708 1.3 thorpej dir_ev = &event[0]; 709 1.3 thorpej file_ev = &event[1]; 710 1.3 thorpej } else { 711 1.3 thorpej dir_ev = &event[1]; 712 1.3 thorpej file_ev = &event[0]; 713 1.3 thorpej } 714 1.3 thorpej ATF_REQUIRE(dir_ev->fflags == NOTE_WRITE); 715 1.3 thorpej ATF_REQUIRE(file_ev->fflags == NOTE_LINK); 716 1.3 thorpej } 717 1.3 thorpej ATF_TC_CLEANUP(rename_over_self_hardlink, tc) 718 1.3 thorpej { 719 1.3 thorpej cleanup(); 720 1.3 thorpej } 721 1.2 thorpej 722 1.1 christos ATF_TP_ADD_TCS(tp) 723 1.1 christos { 724 1.1 christos ATF_TP_ADD_TC(tp, dir_no_note_link_create_file_in); 725 1.1 christos ATF_TP_ADD_TC(tp, dir_no_note_link_delete_file_in); 726 1.2 thorpej 727 1.1 christos ATF_TP_ADD_TC(tp, dir_no_note_link_mv_dir_within); 728 1.1 christos ATF_TP_ADD_TC(tp, dir_no_note_link_mv_file_within); 729 1.2 thorpej 730 1.1 christos ATF_TP_ADD_TC(tp, dir_note_link_create_dir_in); 731 1.1 christos ATF_TP_ADD_TC(tp, dir_note_link_delete_dir_in); 732 1.2 thorpej 733 1.1 christos ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_in); 734 1.1 christos ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_out); 735 1.2 thorpej 736 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_create_dir_in); 737 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_create_file_in); 738 1.2 thorpej 739 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_delete_dir_in); 740 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_delete_file_in); 741 1.2 thorpej 742 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_in); 743 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_out); 744 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_within); 745 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_file_in); 746 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_file_out); 747 1.1 christos ATF_TP_ADD_TC(tp, dir_note_write_mv_file_within); 748 1.2 thorpej 749 1.3 thorpej ATF_TP_ADD_TC(tp, rename_over_self_hardlink); 750 1.3 thorpej 751 1.2 thorpej ATF_TP_ADD_TC(tp, open_write_read_close); 752 1.2 thorpej ATF_TP_ADD_TC(tp, interest); 753 1.2 thorpej 754 1.1 christos return atf_no_error(); 755 1.1 christos } 756