t_vnode.c revision 313498
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
16static const char *dir_target = "foo";
17static const char *dir_inside1 = "foo/bar1";
18static const char *dir_inside2 = "foo/bar2";
19static const char *dir_outside = "bar";
20static const char *file_inside1 = "foo/baz1";
21static const char *file_inside2 = "foo/baz2";
22static const char *file_outside = "qux";
23static const struct timespec ts = {0, 0};
24static int kq = -1;
25static int target = -1;
26
27int init_target(void);
28int init_kqueue(void);
29int create_file(const char *);
30void cleanup(void);
31
32int
33init_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
42int
43init_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
58int
59create_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
70void
71cleanup(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
84ATF_TC_WITH_CLEANUP(dir_no_note_link_create_file_in);
85ATF_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}
91ATF_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}
102ATF_TC_CLEANUP(dir_no_note_link_create_file_in, tc)
103{
104	cleanup();
105}
106
107ATF_TC_WITH_CLEANUP(dir_no_note_link_delete_file_in);
108ATF_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}
114ATF_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}
126ATF_TC_CLEANUP(dir_no_note_link_delete_file_in, tc)
127{
128	cleanup();
129}
130
131ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_dir_within);
132ATF_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}
138ATF_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}
150ATF_TC_CLEANUP(dir_no_note_link_mv_dir_within, tc)
151{
152	cleanup();
153}
154
155ATF_TC_WITH_CLEANUP(dir_no_note_link_mv_file_within);
156ATF_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}
162ATF_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}
174ATF_TC_CLEANUP(dir_no_note_link_mv_file_within, tc)
175{
176	cleanup();
177}
178
179ATF_TC_WITH_CLEANUP(dir_note_link_create_dir_in);
180ATF_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}
186ATF_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}
197ATF_TC_CLEANUP(dir_note_link_create_dir_in, tc)
198{
199	cleanup();
200}
201
202ATF_TC_WITH_CLEANUP(dir_note_link_delete_dir_in);
203ATF_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}
209ATF_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}
221ATF_TC_CLEANUP(dir_note_link_delete_dir_in, tc)
222{
223	cleanup();
224}
225
226ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_in);
227ATF_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}
233ATF_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}
245ATF_TC_CLEANUP(dir_note_link_mv_dir_in, tc)
246{
247	cleanup();
248}
249
250ATF_TC_WITH_CLEANUP(dir_note_link_mv_dir_out);
251ATF_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}
257ATF_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}
269ATF_TC_CLEANUP(dir_note_link_mv_dir_out, tc)
270{
271	cleanup();
272}
273
274ATF_TC_WITH_CLEANUP(dir_note_write_create_dir_in);
275ATF_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}
281ATF_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}
292ATF_TC_CLEANUP(dir_note_write_create_dir_in, tc)
293{
294	cleanup();
295}
296
297ATF_TC_WITH_CLEANUP(dir_note_write_create_file_in);
298ATF_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}
304ATF_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}
315ATF_TC_CLEANUP(dir_note_write_create_file_in, tc)
316{
317	cleanup();
318}
319
320ATF_TC_WITH_CLEANUP(dir_note_write_delete_dir_in);
321ATF_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}
327ATF_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}
339ATF_TC_CLEANUP(dir_note_write_delete_dir_in, tc)
340{
341	cleanup();
342}
343
344ATF_TC_WITH_CLEANUP(dir_note_write_delete_file_in);
345ATF_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}
351ATF_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}
363ATF_TC_CLEANUP(dir_note_write_delete_file_in, tc)
364{
365	cleanup();
366}
367
368ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_in);
369ATF_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}
375ATF_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}
387ATF_TC_CLEANUP(dir_note_write_mv_dir_in, tc)
388{
389	cleanup();
390}
391
392ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_out);
393ATF_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}
399ATF_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}
411ATF_TC_CLEANUP(dir_note_write_mv_dir_out, tc)
412{
413	cleanup();
414}
415
416ATF_TC_WITH_CLEANUP(dir_note_write_mv_dir_within);
417ATF_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}
423ATF_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}
435ATF_TC_CLEANUP(dir_note_write_mv_dir_within, tc)
436{
437	cleanup();
438}
439
440ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_in);
441ATF_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}
447ATF_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}
459ATF_TC_CLEANUP(dir_note_write_mv_file_in, tc)
460{
461	cleanup();
462}
463
464ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_out);
465ATF_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}
471ATF_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}
483ATF_TC_CLEANUP(dir_note_write_mv_file_out, tc)
484{
485	cleanup();
486}
487
488ATF_TC_WITH_CLEANUP(dir_note_write_mv_file_within);
489ATF_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}
495ATF_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}
507ATF_TC_CLEANUP(dir_note_write_mv_file_within, tc)
508{
509	cleanup();
510}
511
512ATF_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