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
init_target(void)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
init_kqueue(void)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
create_file(const char * file)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
cleanup(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);
ATF_TC_HEAD(dir_no_note_link_create_file_in,tc)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 }
ATF_TC_BODY(dir_no_note_link_create_file_in,tc)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 }
ATF_TC_CLEANUP(dir_no_note_link_create_file_in,tc)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);
ATF_TC_HEAD(dir_no_note_link_delete_file_in,tc)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 }
ATF_TC_BODY(dir_no_note_link_delete_file_in,tc)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 }
ATF_TC_CLEANUP(dir_no_note_link_delete_file_in,tc)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);
ATF_TC_HEAD(dir_no_note_link_mv_dir_within,tc)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 }
ATF_TC_BODY(dir_no_note_link_mv_dir_within,tc)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 }
ATF_TC_CLEANUP(dir_no_note_link_mv_dir_within,tc)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);
ATF_TC_HEAD(dir_no_note_link_mv_file_within,tc)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 }
ATF_TC_BODY(dir_no_note_link_mv_file_within,tc)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 }
ATF_TC_CLEANUP(dir_no_note_link_mv_file_within,tc)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);
ATF_TC_HEAD(dir_note_link_create_dir_in,tc)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 }
ATF_TC_BODY(dir_note_link_create_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_link_create_dir_in,tc)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);
ATF_TC_HEAD(dir_note_link_delete_dir_in,tc)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 }
ATF_TC_BODY(dir_note_link_delete_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_link_delete_dir_in,tc)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);
ATF_TC_HEAD(dir_note_link_mv_dir_in,tc)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 }
ATF_TC_BODY(dir_note_link_mv_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_link_mv_dir_in,tc)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);
ATF_TC_HEAD(dir_note_link_mv_dir_out,tc)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 }
ATF_TC_BODY(dir_note_link_mv_dir_out,tc)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 }
ATF_TC_CLEANUP(dir_note_link_mv_dir_out,tc)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);
ATF_TC_HEAD(dir_note_write_create_dir_in,tc)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 }
ATF_TC_BODY(dir_note_write_create_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_create_dir_in,tc)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);
ATF_TC_HEAD(dir_note_write_create_file_in,tc)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 }
ATF_TC_BODY(dir_note_write_create_file_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_create_file_in,tc)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);
ATF_TC_HEAD(dir_note_write_delete_dir_in,tc)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 }
ATF_TC_BODY(dir_note_write_delete_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_delete_dir_in,tc)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);
ATF_TC_HEAD(dir_note_write_delete_file_in,tc)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 }
ATF_TC_BODY(dir_note_write_delete_file_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_delete_file_in,tc)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);
ATF_TC_HEAD(dir_note_write_mv_dir_in,tc)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 }
ATF_TC_BODY(dir_note_write_mv_dir_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_dir_in,tc)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);
ATF_TC_HEAD(dir_note_write_mv_dir_out,tc)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 }
ATF_TC_BODY(dir_note_write_mv_dir_out,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_dir_out,tc)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);
ATF_TC_HEAD(dir_note_write_mv_dir_within,tc)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 }
ATF_TC_BODY(dir_note_write_mv_dir_within,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_dir_within,tc)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);
ATF_TC_HEAD(dir_note_write_mv_file_in,tc)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 }
ATF_TC_BODY(dir_note_write_mv_file_in,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_file_in,tc)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);
ATF_TC_HEAD(dir_note_write_mv_file_out,tc)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 }
ATF_TC_BODY(dir_note_write_mv_file_out,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_file_out,tc)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);
ATF_TC_HEAD(dir_note_write_mv_file_within,tc)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 }
ATF_TC_BODY(dir_note_write_mv_file_within,tc)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 }
ATF_TC_CLEANUP(dir_note_write_mv_file_within,tc)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);
ATF_TC_HEAD(open_write_read_close,tc)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 }
ATF_TC_BODY(open_write_read_close,tc)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 }
ATF_TC_CLEANUP(open_write_read_close,tc)593 ATF_TC_CLEANUP(open_write_read_close, tc)
594 {
595           (void)unlink(testfile);
596 }
597 
598 ATF_TC_WITH_CLEANUP(interest);
ATF_TC_HEAD(interest,tc)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 }
ATF_TC_BODY(interest,tc)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 }
ATF_TC_CLEANUP(interest,tc)669 ATF_TC_CLEANUP(interest, tc)
670 {
671           (void)unlink(testfile);
672 }
673 
674 ATF_TC_WITH_CLEANUP(rename_over_self_hardlink);
ATF_TC_HEAD(rename_over_self_hardlink,tc)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 }
ATF_TC_BODY(rename_over_self_hardlink,tc)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 }
ATF_TC_CLEANUP(rename_over_self_hardlink,tc)717 ATF_TC_CLEANUP(rename_over_self_hardlink, tc)
718 {
719           cleanup();
720 }
721 
ATF_TP_ADD_TCS(tp)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