Home | History | Annotate | Line # | Download | only in kqueue
      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