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