1 #ifdef __FreeBSD__ 2 #include <sys/types.h> 3 #endif 4 #include <sys/event.h> 5 #include <sys/stat.h> 6 #include <sys/time.h> 7 #include <fcntl.h> 8 #include <stdio.h> 9 #include <unistd.h> 10 11 #include <atf-c.h> 12 13 /* 14 * Test cases for events triggered by manipulating a target directory 15 * content. Using EVFILT_VNODE filter on the target directory descriptor. 16 * 17 */ 18 19 static const char *dir_target = "foo"; 20 static const char *dir_inside1 = "foo/bar1"; 21 static const char *dir_inside2 = "foo/bar2"; 22 static const char *dir_outside = "bar"; 23 static const char *file_inside1 = "foo/baz1"; 24 static const char *file_inside2 = "foo/baz2"; 25 static const char *file_outside = "qux"; 26 static const struct timespec ts = {0, 0}; 27 static int kq = -1; 28 static int target = -1; 29 30 int init_target(void); 31 int init_kqueue(void); 32 int create_file(const char *); 33 void cleanup(void); 34 35 int 36 init_target(void) 37 { 38 if (mkdir(dir_target, S_IRWXU) < 0) { 39 return -1; 40 } 41 target = open(dir_target, O_RDONLY, 0); 42 return target; 43 } 44 45 int 46 init_kqueue(void) 47 { 48 struct kevent eventlist[1]; 49 50 kq = kqueue(); 51 if (kq < 0) { 52 return -1; 53 } 54 EV_SET(&eventlist[0], (uintptr_t)target, EVFILT_VNODE, 55 EV_ADD | EV_ONESHOT, NOTE_DELETE | 56 NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | 57 NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, 0, 0); 58 return kevent(kq, eventlist, 1, NULL, 0, NULL); 59 } 60 61 int 62 create_file(const char *file) 63 { 64 int fd; 65 66 fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 67 if (fd < 0) { 68 return -1; 69 } 70 return close(fd); 71 } 72 73 void 74 cleanup(void) 75 { 76 (void)unlink(file_inside1); 77 (void)unlink(file_inside2); 78 (void)unlink(file_outside); 79 (void)rmdir(dir_inside1); 80 (void)rmdir(dir_inside2); 81 (void)rmdir(dir_outside); 82 (void)rmdir(dir_target); 83 (void)close(kq); 84 (void)close(target); 85 } 86 87 ATF_TC_WITH_CLEANUP(dir_no_note_link_create_file_in); 88 ATF_TC_HEAD(dir_no_note_link_create_file_in, tc) 89 { 90 atf_tc_set_md_var(tc, "descr", "This test case ensures " 91 "that kevent(2) does not return NOTE_LINK for the directory " 92 "'foo' if a file 'foo/baz' is created."); 93 } 94 ATF_TC_BODY(dir_no_note_link_create_file_in, tc) 95 { 96 struct kevent changelist[1]; 97 98 ATF_REQUIRE(init_target() != -1); 99 ATF_REQUIRE(init_kqueue() != -1); 100 101 ATF_REQUIRE(create_file(file_inside1) != -1); 102 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 103 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 104 } 105 ATF_TC_CLEANUP(dir_no_note_link_create_file_in, tc) 106 { 107 cleanup(); 108 } 109 110 ATF_TC_WITH_CLEANUP(dir_no_note_link_delete_file_in); 111 ATF_TC_HEAD(dir_no_note_link_delete_file_in, tc) 112 { 113 atf_tc_set_md_var(tc, "descr", "This test case ensures " 114 "that kevent(2) does not return NOTE_LINK for the directory " 115 "'foo' if a file 'foo/baz' is deleted."); 116 } 117 ATF_TC_BODY(dir_no_note_link_delete_file_in, tc) 118 { 119 struct kevent changelist[1]; 120 121 ATF_REQUIRE(init_target() != -1); 122 ATF_REQUIRE(create_file(file_inside1) != -1); 123 ATF_REQUIRE(init_kqueue() != -1); 124 125 ATF_REQUIRE(unlink(file_inside1) != -1); 126 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 127 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 128 } 129 ATF_TC_CLEANUP(dir_no_note_link_delete_file_in, tc) 130 { 131 cleanup(); 132 } 133 134 ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_dir_within); 135 ATF_TC_HEAD(dir_no_note_link_mv_dir_within, tc) 136 { 137 atf_tc_set_md_var(tc, "descr", "This test case ensures " 138 "that kevent(2) does not return NOTE_LINK for the directory " 139 "'foo' if a directory 'foo/bar' is renamed to 'foo/baz'."); 140 } 141 ATF_TC_BODY(dir_no_note_link_mv_dir_within, tc) 142 { 143 struct kevent changelist[1]; 144 145 ATF_REQUIRE(init_target() != -1); 146 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 147 ATF_REQUIRE(init_kqueue() != -1); 148 149 ATF_REQUIRE(rename(dir_inside1, dir_inside2) != -1); 150 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 151 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 152 } 153 ATF_TC_CLEANUP(dir_no_note_link_mv_dir_within, tc) 154 { 155 cleanup(); 156 } 157 158 ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_file_within); 159 ATF_TC_HEAD(dir_no_note_link_mv_file_within, tc) 160 { 161 atf_tc_set_md_var(tc, "descr", "This test case ensures " 162 "that kevent(2) does not return NOTE_LINK for the directory " 163 "'foo' if a file 'foo/baz' is renamed to 'foo/qux'."); 164 } 165 ATF_TC_BODY(dir_no_note_link_mv_file_within, tc) 166 { 167 struct kevent changelist[1]; 168 169 ATF_REQUIRE(init_target() != -1); 170 ATF_REQUIRE(create_file(file_inside1) != -1); 171 ATF_REQUIRE(init_kqueue() != -1); 172 173 ATF_REQUIRE(rename(file_inside1, file_inside2) != -1); 174 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 175 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, 0); 176 } 177 ATF_TC_CLEANUP(dir_no_note_link_mv_file_within, tc) 178 { 179 cleanup(); 180 } 181 182 ATF_TC_WITH_CLEANUP(dir_note_link_create_dir_in); 183 ATF_TC_HEAD(dir_note_link_create_dir_in, tc) 184 { 185 atf_tc_set_md_var(tc, "descr", "This test case ensures " 186 "that kevent(2) returns NOTE_LINK for the directory " 187 "'foo' if a directory 'foo/bar' is created."); 188 } 189 ATF_TC_BODY(dir_note_link_create_dir_in, tc) 190 { 191 struct kevent changelist[1]; 192 193 ATF_REQUIRE(init_target() != -1); 194 ATF_REQUIRE(init_kqueue() != -1); 195 196 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 197 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 198 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 199 } 200 ATF_TC_CLEANUP(dir_note_link_create_dir_in, tc) 201 { 202 cleanup(); 203 } 204 205 ATF_TC_WITH_CLEANUP(dir_note_link_delete_dir_in); 206 ATF_TC_HEAD(dir_note_link_delete_dir_in, tc) 207 { 208 atf_tc_set_md_var(tc, "descr", "This test case ensures " 209 "that kevent(2) returns NOTE_LINK for the directory " 210 "'foo' if a directory 'foo/bar' is deleted."); 211 } 212 ATF_TC_BODY(dir_note_link_delete_dir_in, tc) 213 { 214 struct kevent changelist[1]; 215 216 ATF_REQUIRE(init_target() != -1); 217 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 218 ATF_REQUIRE(init_kqueue() != -1); 219 220 ATF_REQUIRE(rmdir(dir_inside1) != -1); 221 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 222 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 223 } 224 ATF_TC_CLEANUP(dir_note_link_delete_dir_in, tc) 225 { 226 cleanup(); 227 } 228 229 ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_in); 230 ATF_TC_HEAD(dir_note_link_mv_dir_in, tc) 231 { 232 atf_tc_set_md_var(tc, "descr", "This test case ensures " 233 "that kevent(2) returns NOTE_LINK for the directory " 234 "'foo' if a directory 'bar' is renamed to 'foo/bar'."); 235 } 236 ATF_TC_BODY(dir_note_link_mv_dir_in, tc) 237 { 238 struct kevent changelist[1]; 239 240 ATF_REQUIRE(init_target() != -1); 241 ATF_REQUIRE(mkdir(dir_outside, S_IRWXU) != -1); 242 ATF_REQUIRE(init_kqueue() != -1); 243 244 ATF_REQUIRE(rename(dir_outside, dir_inside1) != -1); 245 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 246 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 247 } 248 ATF_TC_CLEANUP(dir_note_link_mv_dir_in, tc) 249 { 250 cleanup(); 251 } 252 253 ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_out); 254 ATF_TC_HEAD(dir_note_link_mv_dir_out, tc) 255 { 256 atf_tc_set_md_var(tc, "descr", "This test case ensures " 257 "that kevent(2) returns NOTE_LINK for the directory " 258 "'foo' if a directory 'foo/bar' is renamed to 'bar'."); 259 } 260 ATF_TC_BODY(dir_note_link_mv_dir_out, tc) 261 { 262 struct kevent changelist[1]; 263 264 ATF_REQUIRE(init_target() != -1); 265 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 266 ATF_REQUIRE(init_kqueue() != -1); 267 268 ATF_REQUIRE(rename(dir_inside1, dir_outside) != -1); 269 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 270 ATF_CHECK_EQ(changelist[0].fflags & NOTE_LINK, NOTE_LINK); 271 } 272 ATF_TC_CLEANUP(dir_note_link_mv_dir_out, tc) 273 { 274 cleanup(); 275 } 276 277 ATF_TC_WITH_CLEANUP(dir_note_write_create_dir_in); 278 ATF_TC_HEAD(dir_note_write_create_dir_in, tc) 279 { 280 atf_tc_set_md_var(tc, "descr", "This test case ensures " 281 "that kevent(2) returns NOTE_WRITE for the directory " 282 "'foo' if a directory 'foo/bar' is created."); 283 } 284 ATF_TC_BODY(dir_note_write_create_dir_in, tc) 285 { 286 struct kevent changelist[1]; 287 288 ATF_REQUIRE(init_target() != -1); 289 ATF_REQUIRE(init_kqueue() != -1); 290 291 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 292 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 293 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 294 } 295 ATF_TC_CLEANUP(dir_note_write_create_dir_in, tc) 296 { 297 cleanup(); 298 } 299 300 ATF_TC_WITH_CLEANUP(dir_note_write_create_file_in); 301 ATF_TC_HEAD(dir_note_write_create_file_in, tc) 302 { 303 atf_tc_set_md_var(tc, "descr", "This test case ensures " 304 "that kevent(2) returns NOTE_WRITE for the directory " 305 "'foo' if a file 'foo/baz' is created."); 306 } 307 ATF_TC_BODY(dir_note_write_create_file_in, tc) 308 { 309 struct kevent changelist[1]; 310 311 ATF_REQUIRE(init_target() != -1); 312 ATF_REQUIRE(init_kqueue() != -1); 313 314 ATF_REQUIRE(create_file(file_inside1) != -1); 315 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 316 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 317 } 318 ATF_TC_CLEANUP(dir_note_write_create_file_in, tc) 319 { 320 cleanup(); 321 } 322 323 ATF_TC_WITH_CLEANUP(dir_note_write_delete_dir_in); 324 ATF_TC_HEAD(dir_note_write_delete_dir_in, tc) 325 { 326 atf_tc_set_md_var(tc, "descr", "This test case ensures " 327 "that kevent(2) returns NOTE_WRITE for the directory " 328 "'foo' if a directory 'foo/bar' is deleted."); 329 } 330 ATF_TC_BODY(dir_note_write_delete_dir_in, tc) 331 { 332 struct kevent changelist[1]; 333 334 ATF_REQUIRE(init_target() != -1); 335 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 336 ATF_REQUIRE(init_kqueue() != -1); 337 338 ATF_REQUIRE(rmdir(dir_inside1) != -1); 339 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 340 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 341 } 342 ATF_TC_CLEANUP(dir_note_write_delete_dir_in, tc) 343 { 344 cleanup(); 345 } 346 347 ATF_TC_WITH_CLEANUP(dir_note_write_delete_file_in); 348 ATF_TC_HEAD(dir_note_write_delete_file_in, tc) 349 { 350 atf_tc_set_md_var(tc, "descr", "This test case ensures " 351 "that kevent(2) returns NOTE_WRITE for the directory " 352 "'foo' if a file 'foo/baz' is deleted."); 353 } 354 ATF_TC_BODY(dir_note_write_delete_file_in, tc) 355 { 356 struct kevent changelist[1]; 357 358 ATF_REQUIRE(init_target() != -1); 359 ATF_REQUIRE(create_file(file_inside1) != -1); 360 ATF_REQUIRE(init_kqueue() != -1); 361 362 ATF_REQUIRE(unlink(file_inside1) != -1); 363 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 364 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 365 } 366 ATF_TC_CLEANUP(dir_note_write_delete_file_in, tc) 367 { 368 cleanup(); 369 } 370 371 ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_in); 372 ATF_TC_HEAD(dir_note_write_mv_dir_in, tc) 373 { 374 atf_tc_set_md_var(tc, "descr", "This test case ensures " 375 "that kevent(2) returns NOTE_WRITE for the directory " 376 "'foo' if a directory 'bar' is renamed to 'foo/bar'."); 377 } 378 ATF_TC_BODY(dir_note_write_mv_dir_in, tc) 379 { 380 struct kevent changelist[1]; 381 382 ATF_REQUIRE(init_target() != -1); 383 ATF_REQUIRE(mkdir(dir_outside, S_IRWXU) != -1); 384 ATF_REQUIRE(init_kqueue() != -1); 385 386 ATF_REQUIRE(rename(dir_outside, dir_inside1) != -1); 387 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 388 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 389 } 390 ATF_TC_CLEANUP(dir_note_write_mv_dir_in, tc) 391 { 392 cleanup(); 393 } 394 395 ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_out); 396 ATF_TC_HEAD(dir_note_write_mv_dir_out, tc) 397 { 398 atf_tc_set_md_var(tc, "descr", "This test case ensures " 399 "that kevent(2) returns NOTE_WRITE for the directory " 400 "'foo' if a directory 'foo/bar' is renamed to 'bar'."); 401 } 402 ATF_TC_BODY(dir_note_write_mv_dir_out, tc) 403 { 404 struct kevent changelist[1]; 405 406 ATF_REQUIRE(init_target() != -1); 407 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 408 ATF_REQUIRE(init_kqueue() != -1); 409 410 ATF_REQUIRE(rename(dir_inside1, dir_outside) != -1); 411 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 412 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 413 } 414 ATF_TC_CLEANUP(dir_note_write_mv_dir_out, tc) 415 { 416 cleanup(); 417 } 418 419 ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_within); 420 ATF_TC_HEAD(dir_note_write_mv_dir_within, tc) 421 { 422 atf_tc_set_md_var(tc, "descr", "This test case ensures " 423 "that kevent(2) returns NOTE_WRITE for the directory " 424 "'foo' if a directory 'foo/bar' is renamed to 'foo/baz'."); 425 } 426 ATF_TC_BODY(dir_note_write_mv_dir_within, tc) 427 { 428 struct kevent changelist[1]; 429 430 ATF_REQUIRE(init_target() != -1); 431 ATF_REQUIRE(mkdir(dir_inside1, S_IRWXU) != -1); 432 ATF_REQUIRE(init_kqueue() != -1); 433 434 ATF_REQUIRE(rename(dir_inside1, dir_inside2) != -1); 435 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 436 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 437 } 438 ATF_TC_CLEANUP(dir_note_write_mv_dir_within, tc) 439 { 440 cleanup(); 441 } 442 443 ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_in); 444 ATF_TC_HEAD(dir_note_write_mv_file_in, tc) 445 { 446 atf_tc_set_md_var(tc, "descr", "This test case ensures " 447 "that kevent(2) returns NOTE_WRITE for the directory " 448 "'foo' if a file 'qux' is renamed to 'foo/baz'."); 449 } 450 ATF_TC_BODY(dir_note_write_mv_file_in, tc) 451 { 452 struct kevent changelist[1]; 453 454 ATF_REQUIRE(init_target() != -1); 455 ATF_REQUIRE(create_file(file_outside) != -1); 456 ATF_REQUIRE(init_kqueue() != -1); 457 458 ATF_REQUIRE(rename(file_outside, file_inside1) != -1); 459 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 460 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 461 } 462 ATF_TC_CLEANUP(dir_note_write_mv_file_in, tc) 463 { 464 cleanup(); 465 } 466 467 ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_out); 468 ATF_TC_HEAD(dir_note_write_mv_file_out, tc) 469 { 470 atf_tc_set_md_var(tc, "descr", "This test case ensures " 471 "that kevent(2) returns NOTE_WRITE for the directory " 472 "'foo' if a file 'foo/baz' is renamed to 'qux'."); 473 } 474 ATF_TC_BODY(dir_note_write_mv_file_out, tc) 475 { 476 struct kevent changelist[1]; 477 478 ATF_REQUIRE(init_target() != -1); 479 ATF_REQUIRE(create_file(file_inside1) != -1); 480 ATF_REQUIRE(init_kqueue() != -1); 481 482 ATF_REQUIRE(rename(file_inside1, file_outside) != -1); 483 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 484 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 485 } 486 ATF_TC_CLEANUP(dir_note_write_mv_file_out, tc) 487 { 488 cleanup(); 489 } 490 491 ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_within); 492 ATF_TC_HEAD(dir_note_write_mv_file_within, tc) 493 { 494 atf_tc_set_md_var(tc, "descr", "This test case ensures " 495 "that kevent(2) returns NOTE_WRITE for the directory " 496 "'foo' if a file 'foo/baz' is renamed to 'foo/qux'."); 497 } 498 ATF_TC_BODY(dir_note_write_mv_file_within, tc) 499 { 500 struct kevent changelist[1]; 501 502 ATF_REQUIRE(init_target() != -1); 503 ATF_REQUIRE(create_file(file_inside1) != -1); 504 ATF_REQUIRE(init_kqueue() != -1); 505 506 ATF_REQUIRE(rename(file_inside1, file_inside2) != -1); 507 ATF_REQUIRE(kevent(kq, NULL, 0, changelist, 1, &ts) != -1); 508 ATF_CHECK_EQ(changelist[0].fflags & NOTE_WRITE, NOTE_WRITE); 509 } 510 ATF_TC_CLEANUP(dir_note_write_mv_file_within, tc) 511 { 512 cleanup(); 513 } 514 515 ATF_TP_ADD_TCS(tp) 516 { 517 ATF_TP_ADD_TC(tp, dir_no_note_link_create_file_in); 518 ATF_TP_ADD_TC(tp, dir_no_note_link_delete_file_in); 519 ATF_TP_ADD_TC(tp, dir_no_note_link_mv_dir_within); 520 ATF_TP_ADD_TC(tp, dir_no_note_link_mv_file_within); 521 ATF_TP_ADD_TC(tp, dir_note_link_create_dir_in); 522 ATF_TP_ADD_TC(tp, dir_note_link_delete_dir_in); 523 ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_in); 524 ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_out); 525 ATF_TP_ADD_TC(tp, dir_note_write_create_dir_in); 526 ATF_TP_ADD_TC(tp, dir_note_write_create_file_in); 527 ATF_TP_ADD_TC(tp, dir_note_write_delete_dir_in); 528 ATF_TP_ADD_TC(tp, dir_note_write_delete_file_in); 529 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_in); 530 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_out); 531 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_within); 532 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_in); 533 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_out); 534 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_within); 535 return atf_no_error(); 536 } 537