t_vnode.c revision 313508
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
19static const char *dir_target = "foo";
20static const char *dir_inside1 = "foo/bar1";
21static const char *dir_inside2 = "foo/bar2";
22static const char *dir_outside = "bar";
23static const char *file_inside1 = "foo/baz1";
24static const char *file_inside2 = "foo/baz2";
25static const char *file_outside = "qux";
26static const struct timespec ts = {0, 0};
27static int kq = -1;
28static int target = -1;
29
30int init_target(void);
31int init_kqueue(void);
32int create_file(const char *);
33void cleanup(void);
34
35int
36init_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
45int
46init_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
61int
62create_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
73void
74cleanup(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
87ATF_TC_WITH_CLEANUP(dir_no_note_link_create_file_in);
88ATF_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}
94ATF_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}
105ATF_TC_CLEANUP(dir_no_note_link_create_file_in, tc)
106{
107	cleanup();
108}
109
110ATF_TC_WITH_CLEANUP(dir_no_note_link_delete_file_in);
111ATF_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}
117ATF_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}
129ATF_TC_CLEANUP(dir_no_note_link_delete_file_in, tc)
130{
131	cleanup();
132}
133
134ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_dir_within);
135ATF_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}
141ATF_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}
153ATF_TC_CLEANUP(dir_no_note_link_mv_dir_within, tc)
154{
155	cleanup();
156}
157
158ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_file_within);
159ATF_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}
165ATF_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}
177ATF_TC_CLEANUP(dir_no_note_link_mv_file_within, tc)
178{
179	cleanup();
180}
181
182ATF_TC_WITH_CLEANUP(dir_note_link_create_dir_in);
183ATF_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}
189ATF_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}
200ATF_TC_CLEANUP(dir_note_link_create_dir_in, tc)
201{
202	cleanup();
203}
204
205ATF_TC_WITH_CLEANUP(dir_note_link_delete_dir_in);
206ATF_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}
212ATF_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}
224ATF_TC_CLEANUP(dir_note_link_delete_dir_in, tc)
225{
226	cleanup();
227}
228
229ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_in);
230ATF_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}
236ATF_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}
248ATF_TC_CLEANUP(dir_note_link_mv_dir_in, tc)
249{
250	cleanup();
251}
252
253ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_out);
254ATF_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}
260ATF_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}
272ATF_TC_CLEANUP(dir_note_link_mv_dir_out, tc)
273{
274	cleanup();
275}
276
277ATF_TC_WITH_CLEANUP(dir_note_write_create_dir_in);
278ATF_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}
284ATF_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}
295ATF_TC_CLEANUP(dir_note_write_create_dir_in, tc)
296{
297	cleanup();
298}
299
300ATF_TC_WITH_CLEANUP(dir_note_write_create_file_in);
301ATF_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}
307ATF_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}
318ATF_TC_CLEANUP(dir_note_write_create_file_in, tc)
319{
320	cleanup();
321}
322
323ATF_TC_WITH_CLEANUP(dir_note_write_delete_dir_in);
324ATF_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}
330ATF_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}
342ATF_TC_CLEANUP(dir_note_write_delete_dir_in, tc)
343{
344	cleanup();
345}
346
347ATF_TC_WITH_CLEANUP(dir_note_write_delete_file_in);
348ATF_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}
354ATF_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}
366ATF_TC_CLEANUP(dir_note_write_delete_file_in, tc)
367{
368	cleanup();
369}
370
371ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_in);
372ATF_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}
378ATF_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}
390ATF_TC_CLEANUP(dir_note_write_mv_dir_in, tc)
391{
392	cleanup();
393}
394
395ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_out);
396ATF_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}
402ATF_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}
414ATF_TC_CLEANUP(dir_note_write_mv_dir_out, tc)
415{
416	cleanup();
417}
418
419ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_within);
420ATF_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}
426ATF_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}
438ATF_TC_CLEANUP(dir_note_write_mv_dir_within, tc)
439{
440	cleanup();
441}
442
443ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_in);
444ATF_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}
450ATF_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}
462ATF_TC_CLEANUP(dir_note_write_mv_file_in, tc)
463{
464	cleanup();
465}
466
467ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_out);
468ATF_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}
474ATF_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}
486ATF_TC_CLEANUP(dir_note_write_mv_file_out, tc)
487{
488	cleanup();
489}
490
491ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_within);
492ATF_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}
498ATF_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}
510ATF_TC_CLEANUP(dir_note_write_mv_file_within, tc)
511{
512	cleanup();
513}
514
515ATF_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