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
19static int __thread kqfd;
20static int __thread vnode_fd;
21static char __thread testfile[1024];
22
23
24/* Create an empty file */
25static void
26testfile_create(void)
27{
28    int fd;
29
30    if ((fd = open(testfile, O_CREAT | O_WRONLY, 0600)) < 0)
31        die("open");
32    close(fd);
33}
34
35static void
36testfile_touch(void)
37{
38    char buf[1024];
39
40    snprintf(&buf[0], sizeof(buf), "touch %s", testfile);
41    if (system(buf) != 0)
42        die("system");
43}
44
45static void
46testfile_write(void)
47{
48    char buf[1024];
49
50    snprintf(&buf[0], sizeof(buf), "echo hi >> %s", testfile);
51    if (system(buf) != 0)
52        die("system");
53}
54
55static void
56testfile_rename(int step)
57{
58    char buf[1024];
59
60    snprintf(&buf[0], sizeof(buf), "%s.tmp", testfile);
61    /* XXX-FIXME use of 'step' conceals a major memory corruption
62            when the file is renamed twice.
63            To replicate, remove "if step" conditional so
64            two renames occur in this function.
65            */
66    if (step == 0) {
67        if (rename(testfile,buf) != 0)
68            err(1,"rename");
69    } else {
70        if (rename(buf, testfile) != 0)
71            err(1,"rename");
72    }
73}
74
75void
76test_kevent_vnode_add(void)
77{
78    struct kevent kev;
79
80    testfile_create();
81
82    vnode_fd = open(testfile, O_RDWR);
83    if (vnode_fd < 0)
84        err(1, "open of %s", testfile);
85
86    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD,
87            NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL);
88}
89
90void
91test_kevent_vnode_note_delete(void)
92{
93    struct kevent kev;
94
95    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
96
97    if (unlink(testfile) < 0)
98        die("unlink");
99
100    kevent_cmp(&kev, kevent_get(kqfd));
101}
102
103void
104test_kevent_vnode_note_write(void)
105{
106    struct kevent kev;
107
108    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL);
109
110    testfile_write();
111
112    /* BSD kqueue adds NOTE_EXTEND even though it was not requested */
113    /* BSD kqueue removes EV_ENABLE */
114    kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue
115    kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
116    kevent_cmp(&kev, kevent_get(kqfd));
117}
118
119void
120test_kevent_vnode_note_attrib(void)
121{
122    struct kevent kev;
123    int nfds;
124
125    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
126
127    testfile_touch();
128
129    nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
130    if (nfds < 1)
131        die("kevent");
132    if (kev.ident != vnode_fd ||
133            kev.filter != EVFILT_VNODE ||
134            kev.fflags != NOTE_ATTRIB)
135        err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
136                test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
137}
138
139void
140test_kevent_vnode_note_rename(void)
141{
142    struct kevent kev;
143    int nfds;
144
145    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL);
146
147    testfile_rename(0);
148
149    nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
150    if (nfds < 1)
151        die("kevent");
152    if (kev.ident != vnode_fd ||
153            kev.filter != EVFILT_VNODE ||
154            kev.fflags != NOTE_RENAME)
155        err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
156                test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
157
158    testfile_rename(1);
159
160    test_no_kevents(kqfd);
161}
162
163void
164test_kevent_vnode_del(void)
165{
166    struct kevent kev;
167
168    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
169}
170
171void
172test_kevent_vnode_disable_and_enable(void)
173{
174    struct kevent kev;
175    int nfds;
176
177    test_no_kevents(kqfd);
178
179    /* Add the watch and immediately disable it */
180    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
181    kev.flags = EV_DISABLE;
182    kevent_update(kqfd, &kev);
183
184    /* Confirm that the watch is disabled */
185    testfile_touch();
186    test_no_kevents(kqfd);
187
188    /* Re-enable and check again */
189    kev.flags = EV_ENABLE;
190    kevent_update(kqfd, &kev);
191    testfile_touch();
192    nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
193    if (nfds < 1)
194        die("kevent");
195    if (kev.ident != vnode_fd ||
196            kev.filter != EVFILT_VNODE ||
197            kev.fflags != NOTE_ATTRIB)
198        err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
199                test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
200}
201
202#if HAVE_EV_DISPATCH
203void
204test_kevent_vnode_dispatch(void)
205{
206    struct kevent kev;
207    int nfds;
208
209    test_no_kevents(kqfd);
210
211    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL);
212
213    testfile_touch();
214
215    nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
216    if (nfds < 1)
217        die("kevent");
218    if (kev.ident != vnode_fd ||
219            kev.filter != EVFILT_VNODE ||
220            kev.fflags != NOTE_ATTRIB)
221        err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
222                test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
223
224    /* Confirm that the watch is disabled automatically */
225    testfile_touch();
226    test_no_kevents(kqfd);
227
228    /* Re-enable the kevent */
229    /* FIXME- is EV_DISPATCH needed when rearming ? */
230    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_ENABLE | EV_DISPATCH, 0, 0, NULL);
231    kev.flags = EV_ADD | EV_DISPATCH;   /* FIXME: may not be portable */
232    kev.fflags = NOTE_ATTRIB;
233    testfile_touch();
234    kevent_cmp(&kev, kevent_get(kqfd));
235    test_no_kevents(kqfd);
236
237    /* Delete the watch */
238    kevent_add(kqfd, &kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL);
239}
240#endif 	/* HAVE_EV_DISPATCH */
241
242void
243test_evfilt_vnode(int _kqfd)
244{
245    snprintf(testfile, sizeof(testfile), "/tmp/kqueue-test%d.tmp",
246            testing_make_uid());
247
248	kqfd = _kqfd;
249    test(kevent_vnode_add);
250    test(kevent_vnode_del);
251    test(kevent_vnode_disable_and_enable);
252#if HAVE_EV_DISPATCH
253    test(kevent_vnode_dispatch);
254#endif
255    test(kevent_vnode_note_write);
256    test(kevent_vnode_note_attrib);
257    test(kevent_vnode_note_rename);
258    test(kevent_vnode_note_delete);
259    unlink(testfile);
260}
261