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 ATF_TP_ADD_TCS(tp) 513 { 514 ATF_TP_ADD_TC(tp, dir_no_note_link_create_file_in); 515 ATF_TP_ADD_TC(tp, dir_no_note_link_delete_file_in); 516 ATF_TP_ADD_TC(tp, dir_no_note_link_mv_dir_within); 517 ATF_TP_ADD_TC(tp, dir_no_note_link_mv_file_within); 518 ATF_TP_ADD_TC(tp, dir_note_link_create_dir_in); 519 ATF_TP_ADD_TC(tp, dir_note_link_delete_dir_in); 520 ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_in); 521 ATF_TP_ADD_TC(tp, dir_note_link_mv_dir_out); 522 ATF_TP_ADD_TC(tp, dir_note_write_create_dir_in); 523 ATF_TP_ADD_TC(tp, dir_note_write_create_file_in); 524 ATF_TP_ADD_TC(tp, dir_note_write_delete_dir_in); 525 ATF_TP_ADD_TC(tp, dir_note_write_delete_file_in); 526 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_in); 527 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_out); 528 ATF_TP_ADD_TC(tp, dir_note_write_mv_dir_within); 529 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_in); 530 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_out); 531 ATF_TP_ADD_TC(tp, dir_note_write_mv_file_within); 532 return atf_no_error(); 533 } 534