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