xref: /freebsd/tests/sys/kqueue/libkqueue/vnode.c (revision 5d3e7166)
1 /*
2  * Copyright (c) 2009 Mark Heily <mark@heily.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  * $FreeBSD$
17  */
18 
19 #include "common.h"
20 
21 int vnode_fd;
22 
23 static void
24 test_kevent_vnode_add(void)
25 {
26     const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)";
27     const char *testfile = "./kqueue-test.tmp";
28     struct kevent kev;
29 
30     test_begin(test_id);
31 
32     system("touch ./kqueue-test.tmp");
33     vnode_fd = open(testfile, O_RDONLY);
34     if (vnode_fd < 0)
35         err(1, "open of %s", testfile);
36     else
37         printf("vnode_fd = %d\n", vnode_fd);
38 
39     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD,
40             NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL);
41     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
42         err(1, "%s", test_id);
43 
44     success();
45 }
46 
47 static void
48 test_kevent_vnode_note_delete(void)
49 {
50     const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE)";
51     struct kevent kev;
52 
53     test_begin(test_id);
54 
55     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
56     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
57         err(1, "%s", test_id);
58 
59     if (unlink("./kqueue-test.tmp") < 0)
60         err(1, "unlink");
61 
62     kevent_cmp(&kev, kevent_get(kqfd));
63 
64     success();
65 }
66 
67 static void
68 test_kevent_vnode_note_delete_fifo(void)
69 {
70     const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE, FIFO)";
71     const char *fifo_path = "./kqueue-fifo.tmp";
72     struct kevent kev;
73     int fd;
74     pid_t pid;
75 
76     test_begin(test_id);
77 
78     if (mkfifo(fifo_path, 0600) != 0)
79         err(1, "mkfifo");
80 
81     pid = fork();
82     if (pid == -1)
83         err(1, "fork");
84 
85     if (pid == 0) {
86         char buf[4];
87 
88         fd = open(fifo_path, O_RDONLY);
89         if (fd == -1)
90             _exit(1);
91 
92         while (read(fd, buf, sizeof(buf)) != 0) {
93         }
94 
95         _exit(0);
96     }
97 
98     sleep(1);
99     if (waitpid(pid, NULL, WNOHANG) == pid) {
100         unlink(fifo_path);
101         err(1, "open");
102     }
103 
104     fd = open(fifo_path, O_WRONLY);
105     if (fd < 0) {
106         unlink(fifo_path);
107         err(1, "open");
108     }
109 
110     EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
111     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) {
112         unlink(fifo_path);
113         err(1, "%s", test_id);
114     }
115 
116     if (unlink(fifo_path) < 0)
117         err(1, "unlink");
118 
119     kevent_cmp(&kev, kevent_get(kqfd));
120     close(fd);
121 
122     success();
123 }
124 
125 static void
126 test_kevent_vnode_note_write(void)
127 {
128     const char *test_id = "kevent(EVFILT_VNODE, NOTE_WRITE)";
129     struct kevent kev;
130 
131     test_begin(test_id);
132 
133     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL);
134     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
135         err(1, "%s", test_id);
136 
137     if (system("echo hello >> ./kqueue-test.tmp") < 0)
138         err(1, "system");
139 
140     /* BSD kqueue adds NOTE_EXTEND even though it was not requested */
141     /* BSD kqueue removes EV_ENABLE */
142     kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue
143     kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
144     kevent_cmp(&kev, kevent_get(kqfd));
145 
146     success();
147 }
148 
149 static void
150 test_kevent_vnode_note_attrib(void)
151 {
152     const char *test_id = "kevent(EVFILT_VNODE, NOTE_ATTRIB)";
153     struct kevent kev;
154     int nfds;
155 
156     test_begin(test_id);
157 
158     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
159     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
160         err(1, "%s", test_id);
161 
162     if (system("touch ./kqueue-test.tmp") < 0)
163         err(1, "system");
164 
165     nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
166     if (nfds < 1)
167         err(1, "%s", test_id);
168     if (kev.ident != (uintptr_t)vnode_fd ||
169             kev.filter != EVFILT_VNODE ||
170             kev.fflags != NOTE_ATTRIB)
171         err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
172                 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
173 
174     success();
175 }
176 
177 static void
178 test_kevent_vnode_note_rename(void)
179 {
180     const char *test_id = "kevent(EVFILT_VNODE, NOTE_RENAME)";
181     struct kevent kev;
182     int nfds;
183 
184     test_begin(test_id);
185 
186     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL);
187     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
188         err(1, "%s", test_id);
189 
190     if (system("mv ./kqueue-test.tmp ./kqueue-test2.tmp") < 0)
191         err(1, "system");
192 
193     nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
194     if (nfds < 1)
195         err(1, "%s", test_id);
196     if (kev.ident != (uintptr_t)vnode_fd ||
197             kev.filter != EVFILT_VNODE ||
198             kev.fflags != NOTE_RENAME)
199         err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
200                 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
201 
202     if (system("mv ./kqueue-test2.tmp ./kqueue-test.tmp") < 0)
203         err(1, "system");
204 
205     success();
206 }
207 
208 static void
209 test_kevent_vnode_del(void)
210 {
211     const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)";
212     struct kevent kev;
213 
214     test_begin(test_id);
215 
216     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
217     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
218         err(1, "%s", test_id);
219 
220     success();
221 }
222 
223 static void
224 test_kevent_vnode_disable_and_enable(void)
225 {
226     const char *test_id = "kevent(EVFILT_VNODE, EV_DISABLE and EV_ENABLE)";
227     struct kevent kev;
228     int nfds;
229 
230     test_begin(test_id);
231 
232     test_no_kevents();
233 
234     /* Add the watch and immediately disable it */
235     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
236     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
237         err(1, "%s", test_id);
238     kev.flags = EV_DISABLE;
239     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
240         err(1, "%s", test_id);
241 
242     /* Confirm that the watch is disabled */
243     if (system("touch ./kqueue-test.tmp") < 0)
244         err(1, "system");
245     test_no_kevents();
246 
247     /* Re-enable and check again */
248     kev.flags = EV_ENABLE;
249     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
250         err(1, "%s", test_id);
251     if (system("touch ./kqueue-test.tmp") < 0)
252         err(1, "system");
253     nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
254     if (nfds < 1)
255         err(1, "%s", test_id);
256     if (kev.ident != (uintptr_t)vnode_fd ||
257             kev.filter != EVFILT_VNODE ||
258             kev.fflags != NOTE_ATTRIB)
259         err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
260                 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
261 
262     success();
263 }
264 
265 #if HAVE_EV_DISPATCH
266 static void
267 test_kevent_vnode_dispatch(void)
268 {
269     const char *test_id = "kevent(EVFILT_VNODE, EV_DISPATCH)";
270     struct kevent kev;
271     int nfds;
272 
273     test_begin(test_id);
274 
275     test_no_kevents();
276 
277     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL);
278     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
279         err(1, "%s", test_id);
280 
281     if (system("touch ./kqueue-test.tmp") < 0)
282         err(1, "system");
283 
284     nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
285     if (nfds < 1)
286         err(1, "%s", test_id);
287     if (kev.ident != (uintptr_t)vnode_fd ||
288             kev.filter != EVFILT_VNODE ||
289             kev.fflags != NOTE_ATTRIB)
290         err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
291                 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
292 
293     /* Confirm that the watch is disabled automatically */
294     puts("-- checking that watch is disabled");
295     if (system("touch ./kqueue-test.tmp") < 0)
296         err(1, "system");
297     test_no_kevents();
298 
299     /* Delete the watch */
300     EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL);
301     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
302         err(1, "remove watch failed: %s", test_id);
303 
304     success();
305 }
306 #endif 	/* HAVE_EV_DISPATCH */
307 
308 void
309 test_evfilt_vnode(void)
310 {
311     kqfd = kqueue();
312     test_kevent_vnode_add();
313     test_kevent_vnode_del();
314     test_kevent_vnode_disable_and_enable();
315 #if HAVE_EV_DISPATCH
316     test_kevent_vnode_dispatch();
317 #endif
318     test_kevent_vnode_note_write();
319     test_kevent_vnode_note_attrib();
320     test_kevent_vnode_note_rename();
321     test_kevent_vnode_note_delete();
322     test_kevent_vnode_note_delete_fifo();
323     close(kqfd);
324 }
325