1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 Klara, Inc.
5 */
6
7 #include <sys/capsicum.h>
8 #include <sys/filio.h>
9 #include <sys/inotify.h>
10 #include <sys/ioccom.h>
11 #include <sys/mount.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 #include <sys/sysctl.h>
15 #include <sys/un.h>
16
17 #include <dirent.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <mntopts.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <atf-c.h>
28
29 static const char *
ev2name(int event)30 ev2name(int event)
31 {
32 switch (event) {
33 case IN_ACCESS:
34 return ("IN_ACCESS");
35 case IN_ATTRIB:
36 return ("IN_ATTRIB");
37 case IN_CLOSE_WRITE:
38 return ("IN_CLOSE_WRITE");
39 case IN_CLOSE_NOWRITE:
40 return ("IN_CLOSE_NOWRITE");
41 case IN_CREATE:
42 return ("IN_CREATE");
43 case IN_DELETE:
44 return ("IN_DELETE");
45 case IN_DELETE_SELF:
46 return ("IN_DELETE_SELF");
47 case IN_MODIFY:
48 return ("IN_MODIFY");
49 case IN_MOVE_SELF:
50 return ("IN_MOVE_SELF");
51 case IN_MOVED_FROM:
52 return ("IN_MOVED_FROM");
53 case IN_MOVED_TO:
54 return ("IN_MOVED_TO");
55 case IN_OPEN:
56 return ("IN_OPEN");
57 default:
58 return (NULL);
59 }
60 }
61
62 static void
close_checked(int fd)63 close_checked(int fd)
64 {
65 ATF_REQUIRE(close(fd) == 0);
66 }
67
68 /*
69 * Make sure that no other events are pending, and close the inotify descriptor.
70 */
71 static void
close_inotify(int fd)72 close_inotify(int fd)
73 {
74 int n;
75
76 ATF_REQUIRE(ioctl(fd, FIONREAD, &n) == 0);
77 ATF_REQUIRE(n == 0);
78 close_checked(fd);
79 }
80
81 static uint32_t
consume_event_cookie(int ifd,int wd,unsigned int event,unsigned int flags,const char * name)82 consume_event_cookie(int ifd, int wd, unsigned int event, unsigned int flags,
83 const char *name)
84 {
85 struct inotify_event *ev;
86 size_t evsz, namelen;
87 ssize_t n;
88 uint32_t cookie;
89
90 /* Only read one record. */
91 namelen = name == NULL ? 0 : strlen(name);
92 evsz = sizeof(*ev) + _IN_NAMESIZE(namelen);
93 ev = malloc(evsz);
94 ATF_REQUIRE(ev != NULL);
95
96 n = read(ifd, ev, evsz);
97 ATF_REQUIRE_MSG(n >= 0, "failed to read event %s", ev2name(event));
98 ATF_REQUIRE((size_t)n >= sizeof(*ev));
99 ATF_REQUIRE((size_t)n == sizeof(*ev) + ev->len);
100 ATF_REQUIRE((size_t)n == evsz);
101
102 ATF_REQUIRE_MSG((ev->mask & IN_ALL_EVENTS) == event,
103 "expected event %#x, got %#x", event, ev->mask);
104 ATF_REQUIRE_MSG((ev->mask & _IN_ALL_RETFLAGS) == flags,
105 "expected flags %#x, got %#x", flags, ev->mask);
106 ATF_REQUIRE_MSG(ev->wd == wd,
107 "expected wd %d, got %d", wd, ev->wd);
108 ATF_REQUIRE_MSG(name == NULL || strcmp(name, ev->name) == 0,
109 "expected name '%s', got '%s'", name, ev->name);
110 cookie = ev->cookie;
111 if ((ev->mask & (IN_MOVED_FROM | IN_MOVED_TO)) == 0)
112 ATF_REQUIRE(cookie == 0);
113 free(ev);
114 return (cookie);
115 }
116
117 /*
118 * Read an event from the inotify file descriptor and check that it
119 * matches the expected values.
120 */
121 static void
consume_event(int ifd,int wd,unsigned int event,unsigned int flags,const char * name)122 consume_event(int ifd, int wd, unsigned int event, unsigned int flags,
123 const char *name)
124 {
125 (void)consume_event_cookie(ifd, wd, event, flags, name);
126 }
127
128 static int
inotify(int flags)129 inotify(int flags)
130 {
131 int ifd;
132
133 ifd = inotify_init1(flags);
134 ATF_REQUIRE(ifd != -1);
135 return (ifd);
136 }
137
138 static void
mount_nullfs(char * dir,char * src)139 mount_nullfs(char *dir, char *src)
140 {
141 struct iovec *iov;
142 char errmsg[1024];
143 int error, iovlen;
144
145 iov = NULL;
146 iovlen = 0;
147
148 build_iovec(&iov, &iovlen, "fstype", "nullfs", (size_t)-1);
149 build_iovec(&iov, &iovlen, "fspath", dir, (size_t)-1);
150 build_iovec(&iov, &iovlen, "target", src, (size_t)-1);
151 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
152
153 errmsg[0] = '\0';
154 error = nmount(iov, iovlen, 0);
155 ATF_REQUIRE_MSG(error == 0,
156 "mount nullfs %s %s: %s", src, dir,
157 errmsg[0] == '\0' ? strerror(errno) : errmsg);
158
159 free_iovec(&iov, &iovlen);
160 }
161
162 static void
mount_tmpfs(const char * dir)163 mount_tmpfs(const char *dir)
164 {
165 struct iovec *iov;
166 char errmsg[1024];
167 int error, iovlen;
168
169 iov = NULL;
170 iovlen = 0;
171
172 build_iovec(&iov, &iovlen, "fstype", "tmpfs", (size_t)-1);
173 build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
174 (size_t)-1);
175 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
176
177 errmsg[0] = '\0';
178 error = nmount(iov, iovlen, 0);
179 ATF_REQUIRE_MSG(error == 0,
180 "mount tmpfs %s: %s", dir,
181 errmsg[0] == '\0' ? strerror(errno) : errmsg);
182
183 free_iovec(&iov, &iovlen);
184 }
185
186 static int
watch_file(int ifd,int events,char * path)187 watch_file(int ifd, int events, char *path)
188 {
189 int fd, wd;
190
191 strncpy(path, "test.XXXXXX", PATH_MAX);
192 fd = mkstemp(path);
193 ATF_REQUIRE(fd != -1);
194 close_checked(fd);
195
196 wd = inotify_add_watch(ifd, path, events);
197 ATF_REQUIRE(wd != -1);
198
199 return (wd);
200 }
201
202 static int
watch_dir(int ifd,int events,char * path)203 watch_dir(int ifd, int events, char *path)
204 {
205 char *p;
206 int wd;
207
208 strlcpy(path, "test.XXXXXX", PATH_MAX);
209 p = mkdtemp(path);
210 ATF_REQUIRE(p == path);
211
212 wd = inotify_add_watch(ifd, path, events);
213 ATF_REQUIRE(wd != -1);
214
215 return (wd);
216 }
217
218 /*
219 * Verify that Capsicum restrictions are applied as expected.
220 */
221 ATF_TC_WITHOUT_HEAD(inotify_capsicum);
ATF_TC_BODY(inotify_capsicum,tc)222 ATF_TC_BODY(inotify_capsicum, tc)
223 {
224 int error, dfd, ifd, wd;
225
226 ifd = inotify(IN_NONBLOCK);
227 ATF_REQUIRE(ifd != -1);
228
229 dfd = open(".", O_RDONLY | O_DIRECTORY);
230 ATF_REQUIRE(dfd != -1);
231
232 error = mkdirat(dfd, "testdir", 0755);
233 ATF_REQUIRE(error == 0);
234
235 error = cap_enter();
236 ATF_REQUIRE(error == 0);
237
238 /*
239 * Plain inotify_add_watch() is disallowed.
240 */
241 wd = inotify_add_watch(ifd, ".", IN_DELETE_SELF);
242 ATF_REQUIRE_ERRNO(ECAPMODE, wd == -1);
243 wd = inotify_add_watch_at(ifd, dfd, "testdir", IN_DELETE_SELF);
244 ATF_REQUIRE(wd >= 0);
245
246 /*
247 * Generate a record and consume it.
248 */
249 error = unlinkat(dfd, "testdir", AT_REMOVEDIR);
250 ATF_REQUIRE(error == 0);
251 consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
252 consume_event(ifd, wd, 0, IN_IGNORED, NULL);
253
254 close_checked(dfd);
255 close_inotify(ifd);
256 }
257
258 /*
259 * Make sure that duplicate, back-to-back events are coalesced.
260 */
261 ATF_TC_WITHOUT_HEAD(inotify_coalesce);
ATF_TC_BODY(inotify_coalesce,tc)262 ATF_TC_BODY(inotify_coalesce, tc)
263 {
264 char file[PATH_MAX], path[PATH_MAX];
265 int fd, fd1, ifd, n, wd;
266
267 ifd = inotify(IN_NONBLOCK);
268
269 /* Create a directory and watch it. */
270 wd = watch_dir(ifd, IN_OPEN, path);
271 /* Create a file in the directory and open it. */
272 snprintf(file, sizeof(file), "%s/file", path);
273 fd = open(file, O_RDWR | O_CREAT, 0644);
274 ATF_REQUIRE(fd != -1);
275 close_checked(fd);
276 fd = open(file, O_RDWR);
277 ATF_REQUIRE(fd != -1);
278 fd1 = open(file, O_RDONLY);
279 ATF_REQUIRE(fd1 != -1);
280 close_checked(fd1);
281 close_checked(fd);
282
283 consume_event(ifd, wd, IN_OPEN, 0, "file");
284 ATF_REQUIRE(ioctl(ifd, FIONREAD, &n) == 0);
285 ATF_REQUIRE(n == 0);
286
287 close_inotify(ifd);
288 }
289
290 /*
291 * Check handling of IN_MASK_CREATE.
292 */
293 ATF_TC_WITHOUT_HEAD(inotify_mask_create);
ATF_TC_BODY(inotify_mask_create,tc)294 ATF_TC_BODY(inotify_mask_create, tc)
295 {
296 char path[PATH_MAX];
297 int ifd, wd, wd1;
298
299 ifd = inotify(IN_NONBLOCK);
300
301 /* Create a directory and watch it. */
302 wd = watch_dir(ifd, IN_CREATE, path);
303 /* Updating the watch with IN_MASK_CREATE should result in an error. */
304 wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_CREATE);
305 ATF_REQUIRE_ERRNO(EEXIST, wd1 == -1);
306 /* It's an error to specify IN_MASK_ADD with IN_MASK_CREATE. */
307 wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_ADD |
308 IN_MASK_CREATE);
309 ATF_REQUIRE_ERRNO(EINVAL, wd1 == -1);
310 /* Updating the watch without IN_MASK_CREATE should work. */
311 wd1 = inotify_add_watch(ifd, path, IN_MODIFY);
312 ATF_REQUIRE(wd1 != -1);
313 ATF_REQUIRE_EQ(wd, wd1);
314
315 close_inotify(ifd);
316 }
317
318 /*
319 * Make sure that inotify cooperates with nullfs: if a lower vnode is the
320 * subject of an event, the upper vnode should be notified, and if the upper
321 * vnode is the subject of an event, the lower vnode should be notified.
322 */
323 ATF_TC_WITH_CLEANUP(inotify_nullfs);
ATF_TC_HEAD(inotify_nullfs,tc)324 ATF_TC_HEAD(inotify_nullfs, tc)
325 {
326 atf_tc_set_md_var(tc, "require.user", "root");
327 }
ATF_TC_BODY(inotify_nullfs,tc)328 ATF_TC_BODY(inotify_nullfs, tc)
329 {
330 char path[PATH_MAX], *p;
331 int dfd, error, fd, ifd, mask, wd;
332
333 mask = IN_CREATE | IN_OPEN;
334
335 ifd = inotify(IN_NONBLOCK);
336
337 strlcpy(path, "./test.XXXXXX", sizeof(path));
338 p = mkdtemp(path);
339 ATF_REQUIRE(p == path);
340
341 error = mkdir("./mnt", 0755);
342 ATF_REQUIRE(error == 0);
343
344 /* Mount the testdir onto ./mnt. */
345 mount_nullfs("./mnt", path);
346
347 wd = inotify_add_watch(ifd, "./mnt", mask);
348 ATF_REQUIRE(wd != -1);
349
350 /* Create a file in the lower directory and open it. */
351 dfd = open(path, O_RDONLY | O_DIRECTORY);
352 ATF_REQUIRE(dfd != -1);
353 fd = openat(dfd, "file", O_RDWR | O_CREAT, 0644);
354 close_checked(fd);
355 close_checked(dfd);
356
357 /* We should see events via the nullfs mount. */
358 consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
359 consume_event(ifd, wd, IN_CREATE, 0, "file");
360 consume_event(ifd, wd, IN_OPEN, 0, "file");
361
362 error = inotify_rm_watch(ifd, wd);
363 ATF_REQUIRE(error == 0);
364 consume_event(ifd, wd, 0, IN_IGNORED, NULL);
365
366 /* Watch the lower directory. */
367 wd = inotify_add_watch(ifd, path, mask);
368 ATF_REQUIRE(wd != -1);
369 /* ... and create a file in the upper directory and open it. */
370 dfd = open("./mnt", O_RDONLY | O_DIRECTORY);
371 ATF_REQUIRE(dfd != -1);
372 fd = openat(dfd, "file2", O_RDWR | O_CREAT, 0644);
373 ATF_REQUIRE(fd != -1);
374 close_checked(fd);
375 close_checked(dfd);
376
377 /* We should see events via the lower directory. */
378 consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
379 consume_event(ifd, wd, IN_CREATE, 0, "file2");
380 consume_event(ifd, wd, IN_OPEN, 0, "file2");
381
382 close_inotify(ifd);
383 }
ATF_TC_CLEANUP(inotify_nullfs,tc)384 ATF_TC_CLEANUP(inotify_nullfs, tc)
385 {
386 int error;
387
388 error = unmount("./mnt", 0);
389 if (error != 0) {
390 perror("unmount");
391 exit(1);
392 }
393 }
394
395 /*
396 * Make sure that exceeding max_events pending events results in an overflow
397 * event.
398 */
399 ATF_TC_WITHOUT_HEAD(inotify_queue_overflow);
ATF_TC_BODY(inotify_queue_overflow,tc)400 ATF_TC_BODY(inotify_queue_overflow, tc)
401 {
402 char path[PATH_MAX];
403 size_t size;
404 int error, dfd, ifd, max, wd;
405
406 size = sizeof(max);
407 error = sysctlbyname("vfs.inotify.max_queued_events", &max, &size, NULL,
408 0);
409 ATF_REQUIRE(error == 0);
410
411 ifd = inotify(IN_NONBLOCK);
412
413 /* Create a directory and watch it for file creation events. */
414 wd = watch_dir(ifd, IN_CREATE, path);
415 dfd = open(path, O_DIRECTORY);
416 ATF_REQUIRE(dfd != -1);
417 /* Generate max+1 file creation events. */
418 for (int i = 0; i < max + 1; i++) {
419 char name[NAME_MAX];
420 int fd;
421
422 (void)snprintf(name, sizeof(name), "file%d", i);
423 fd = openat(dfd, name, O_CREAT | O_RDWR, 0644);
424 ATF_REQUIRE(fd != -1);
425 close_checked(fd);
426 }
427
428 /*
429 * Read our events. We should see files 0..max-1 and then an overflow
430 * event.
431 */
432 for (int i = 0; i < max; i++) {
433 char name[NAME_MAX];
434
435 (void)snprintf(name, sizeof(name), "file%d", i);
436 consume_event(ifd, wd, IN_CREATE, 0, name);
437 }
438
439 /* Look for an overflow event. */
440 consume_event(ifd, -1, 0, IN_Q_OVERFLOW, NULL);
441
442 close_checked(dfd);
443 close_inotify(ifd);
444 }
445
446 ATF_TC_WITHOUT_HEAD(inotify_event_access_file);
ATF_TC_BODY(inotify_event_access_file,tc)447 ATF_TC_BODY(inotify_event_access_file, tc)
448 {
449 char path[PATH_MAX], buf[16];
450 off_t nb;
451 ssize_t n;
452 int error, fd, fd1, ifd, s[2], wd;
453
454 ifd = inotify(IN_NONBLOCK);
455
456 wd = watch_file(ifd, IN_ACCESS, path);
457
458 fd = open(path, O_RDWR);
459 n = write(fd, "test", 4);
460 ATF_REQUIRE(n == 4);
461
462 /* A simple read(2) should generate an access. */
463 ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
464 n = read(fd, buf, sizeof(buf));
465 ATF_REQUIRE(n == 4);
466 ATF_REQUIRE(memcmp(buf, "test", 4) == 0);
467 consume_event(ifd, wd, IN_ACCESS, 0, NULL);
468
469 /* copy_file_range(2) should as well. */
470 ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
471 fd1 = open("sink", O_RDWR | O_CREAT, 0644);
472 ATF_REQUIRE(fd1 != -1);
473 n = copy_file_range(fd, NULL, fd1, NULL, 4, 0);
474 ATF_REQUIRE(n == 4);
475 close_checked(fd1);
476 consume_event(ifd, wd, IN_ACCESS, 0, NULL);
477
478 /* As should sendfile(2). */
479 error = socketpair(AF_UNIX, SOCK_STREAM, 0, s);
480 ATF_REQUIRE(error == 0);
481 error = sendfile(fd, s[0], 0, 4, NULL, &nb, 0);
482 ATF_REQUIRE(error == 0);
483 ATF_REQUIRE(nb == 4);
484 consume_event(ifd, wd, IN_ACCESS, 0, NULL);
485 close_checked(s[0]);
486 close_checked(s[1]);
487
488 close_checked(fd);
489
490 close_inotify(ifd);
491 }
492
493 ATF_TC_WITHOUT_HEAD(inotify_event_access_dir);
ATF_TC_BODY(inotify_event_access_dir,tc)494 ATF_TC_BODY(inotify_event_access_dir, tc)
495 {
496 char root[PATH_MAX], path[PATH_MAX];
497 struct dirent *ent;
498 DIR *dir;
499 int error, ifd, wd;
500
501 ifd = inotify(IN_NONBLOCK);
502
503 wd = watch_dir(ifd, IN_ACCESS, root);
504 snprintf(path, sizeof(path), "%s/dir", root);
505 error = mkdir(path, 0755);
506 ATF_REQUIRE(error == 0);
507
508 /* Read an entry and generate an access. */
509 dir = opendir(path);
510 ATF_REQUIRE(dir != NULL);
511 ent = readdir(dir);
512 ATF_REQUIRE(ent != NULL);
513 ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
514 strcmp(ent->d_name, "..") == 0);
515 ATF_REQUIRE(closedir(dir) == 0);
516 consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, "dir");
517
518 /*
519 * Reading the watched directory should generate an access event.
520 * This is contrary to Linux's inotify man page, which states that
521 * IN_ACCESS is only generated for accesses to objects in a watched
522 * directory.
523 */
524 dir = opendir(root);
525 ATF_REQUIRE(dir != NULL);
526 ent = readdir(dir);
527 ATF_REQUIRE(ent != NULL);
528 ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
529 strcmp(ent->d_name, "..") == 0);
530 ATF_REQUIRE(closedir(dir) == 0);
531 consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, NULL);
532
533 close_inotify(ifd);
534 }
535
536 ATF_TC_WITHOUT_HEAD(inotify_event_attrib);
ATF_TC_BODY(inotify_event_attrib,tc)537 ATF_TC_BODY(inotify_event_attrib, tc)
538 {
539 char path[PATH_MAX];
540 int error, ifd, fd, wd;
541
542 ifd = inotify(IN_NONBLOCK);
543
544 wd = watch_file(ifd, IN_ATTRIB, path);
545
546 fd = open(path, O_RDWR);
547 ATF_REQUIRE(fd != -1);
548 error = fchmod(fd, 0600);
549 ATF_REQUIRE(error == 0);
550 consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
551
552 error = fchown(fd, getuid(), getgid());
553 ATF_REQUIRE(error == 0);
554 consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
555
556 close_checked(fd);
557 close_inotify(ifd);
558 }
559
560 ATF_TC_WITHOUT_HEAD(inotify_event_close_nowrite);
ATF_TC_BODY(inotify_event_close_nowrite,tc)561 ATF_TC_BODY(inotify_event_close_nowrite, tc)
562 {
563 char file[PATH_MAX], file1[PATH_MAX], dir[PATH_MAX];
564 int ifd, fd, wd1, wd2;
565
566 ifd = inotify(IN_NONBLOCK);
567
568 wd1 = watch_dir(ifd, IN_CLOSE_NOWRITE, dir);
569 wd2 = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, file);
570
571 fd = open(dir, O_DIRECTORY);
572 ATF_REQUIRE(fd != -1);
573 close_checked(fd);
574 consume_event(ifd, wd1, IN_CLOSE_NOWRITE, IN_ISDIR, NULL);
575
576 fd = open(file, O_RDONLY);
577 ATF_REQUIRE(fd != -1);
578 close_checked(fd);
579 consume_event(ifd, wd2, IN_CLOSE_NOWRITE, 0, NULL);
580
581 snprintf(file1, sizeof(file1), "%s/file", dir);
582 fd = open(file1, O_RDONLY | O_CREAT, 0644);
583 ATF_REQUIRE(fd != -1);
584 close_checked(fd);
585 consume_event(ifd, wd1, IN_CLOSE_NOWRITE, 0, "file");
586
587 close_inotify(ifd);
588 }
589
590 ATF_TC_WITHOUT_HEAD(inotify_event_close_write);
ATF_TC_BODY(inotify_event_close_write,tc)591 ATF_TC_BODY(inotify_event_close_write, tc)
592 {
593 char path[PATH_MAX];
594 int ifd, fd, wd;
595
596 ifd = inotify(IN_NONBLOCK);
597
598 wd = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, path);
599
600 fd = open(path, O_RDWR);
601 ATF_REQUIRE(fd != -1);
602 close_checked(fd);
603 consume_event(ifd, wd, IN_CLOSE_WRITE, 0, NULL);
604
605 close_inotify(ifd);
606 }
607
608 /* Verify that various operations in a directory generate IN_CREATE events. */
609 ATF_TC_WITHOUT_HEAD(inotify_event_create);
ATF_TC_BODY(inotify_event_create,tc)610 ATF_TC_BODY(inotify_event_create, tc)
611 {
612 struct sockaddr_un sun;
613 char path[PATH_MAX], path1[PATH_MAX], root[PATH_MAX];
614 ssize_t n;
615 int error, ifd, ifd1, fd, s, wd, wd1;
616 char b;
617
618 ifd = inotify(IN_NONBLOCK);
619
620 wd = watch_dir(ifd, IN_CREATE, root);
621
622 /* Regular file. */
623 snprintf(path, sizeof(path), "%s/file", root);
624 fd = open(path, O_RDWR | O_CREAT, 0644);
625 ATF_REQUIRE(fd != -1);
626 /*
627 * Make sure we get an event triggered by the fd used to create the
628 * file.
629 */
630 ifd1 = inotify(IN_NONBLOCK);
631 wd1 = inotify_add_watch(ifd1, root, IN_MODIFY);
632 b = 42;
633 n = write(fd, &b, sizeof(b));
634 ATF_REQUIRE(n == sizeof(b));
635 close_checked(fd);
636 consume_event(ifd, wd, IN_CREATE, 0, "file");
637 consume_event(ifd1, wd1, IN_MODIFY, 0, "file");
638 close_inotify(ifd1);
639
640 /* Hard link. */
641 snprintf(path1, sizeof(path1), "%s/link", root);
642 error = link(path, path1);
643 ATF_REQUIRE(error == 0);
644 consume_event(ifd, wd, IN_CREATE, 0, "link");
645
646 /* Directory. */
647 snprintf(path, sizeof(path), "%s/dir", root);
648 error = mkdir(path, 0755);
649 ATF_REQUIRE(error == 0);
650 consume_event(ifd, wd, IN_CREATE, IN_ISDIR, "dir");
651
652 /* Symbolic link. */
653 snprintf(path1, sizeof(path1), "%s/symlink", root);
654 error = symlink(path, path1);
655 ATF_REQUIRE(error == 0);
656 consume_event(ifd, wd, IN_CREATE, 0, "symlink");
657
658 /* FIFO. */
659 snprintf(path, sizeof(path), "%s/fifo", root);
660 error = mkfifo(path, 0644);
661 ATF_REQUIRE(error == 0);
662 consume_event(ifd, wd, IN_CREATE, 0, "fifo");
663
664 /* Binding a socket. */
665 s = socket(AF_UNIX, SOCK_STREAM, 0);
666 memset(&sun, 0, sizeof(sun));
667 sun.sun_family = AF_UNIX;
668 sun.sun_len = sizeof(sun);
669 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/socket", root);
670 error = bind(s, (struct sockaddr *)&sun, sizeof(sun));
671 ATF_REQUIRE(error == 0);
672 close_checked(s);
673 consume_event(ifd, wd, IN_CREATE, 0, "socket");
674
675 close_inotify(ifd);
676 }
677
678 ATF_TC_WITHOUT_HEAD(inotify_event_delete);
ATF_TC_BODY(inotify_event_delete,tc)679 ATF_TC_BODY(inotify_event_delete, tc)
680 {
681 char root[PATH_MAX], path[PATH_MAX], file[PATH_MAX];
682 int error, fd, ifd, wd, wd2;
683
684 ifd = inotify(IN_NONBLOCK);
685
686 wd = watch_dir(ifd, IN_DELETE | IN_DELETE_SELF, root);
687
688 snprintf(path, sizeof(path), "%s/file", root);
689 fd = open(path, O_RDWR | O_CREAT, 0644);
690 ATF_REQUIRE(fd != -1);
691 error = unlink(path);
692 ATF_REQUIRE(error == 0);
693 consume_event(ifd, wd, IN_DELETE, 0, "file");
694 close_checked(fd);
695
696 /*
697 * Make sure that renaming over a file generates a delete event when and
698 * only when that file is watched.
699 */
700 fd = open(path, O_RDWR | O_CREAT, 0644);
701 ATF_REQUIRE(fd != -1);
702 close_checked(fd);
703 wd2 = inotify_add_watch(ifd, path, IN_DELETE | IN_DELETE_SELF);
704 ATF_REQUIRE(wd2 != -1);
705 snprintf(file, sizeof(file), "%s/file2", root);
706 fd = open(file, O_RDWR | O_CREAT, 0644);
707 ATF_REQUIRE(fd != -1);
708 close_checked(fd);
709 error = rename(file, path);
710 ATF_REQUIRE(error == 0);
711 consume_event(ifd, wd2, IN_DELETE_SELF, 0, NULL);
712 consume_event(ifd, wd2, 0, IN_IGNORED, NULL);
713
714 error = unlink(path);
715 ATF_REQUIRE(error == 0);
716 consume_event(ifd, wd, IN_DELETE, 0, "file");
717 error = rmdir(root);
718 ATF_REQUIRE(error == 0);
719 consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
720 consume_event(ifd, wd, 0, IN_IGNORED, NULL);
721
722 close_inotify(ifd);
723 }
724
725 ATF_TC_WITHOUT_HEAD(inotify_event_move);
ATF_TC_BODY(inotify_event_move,tc)726 ATF_TC_BODY(inotify_event_move, tc)
727 {
728 char dir1[PATH_MAX], dir2[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX];
729 char path3[PATH_MAX];
730 int error, ifd, fd, wd1, wd2, wd3;
731 uint32_t cookie1, cookie2;
732
733 ifd = inotify(IN_NONBLOCK);
734
735 wd1 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir1);
736 wd2 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir2);
737
738 snprintf(path1, sizeof(path1), "%s/file", dir1);
739 fd = open(path1, O_RDWR | O_CREAT, 0644);
740 ATF_REQUIRE(fd != -1);
741 close_checked(fd);
742 snprintf(path2, sizeof(path2), "%s/file2", dir2);
743 error = rename(path1, path2);
744 ATF_REQUIRE(error == 0);
745 cookie1 = consume_event_cookie(ifd, wd1, IN_MOVED_FROM, 0, "file");
746 cookie2 = consume_event_cookie(ifd, wd2, IN_MOVED_TO, 0, "file2");
747 ATF_REQUIRE_MSG(cookie1 == cookie2,
748 "expected cookie %u, got %u", cookie1, cookie2);
749
750 snprintf(path2, sizeof(path2), "%s/dir", dir2);
751 error = rename(dir1, path2);
752 ATF_REQUIRE(error == 0);
753 consume_event(ifd, wd1, IN_MOVE_SELF, IN_ISDIR, NULL);
754 consume_event(ifd, wd2, IN_MOVED_TO, IN_ISDIR, "dir");
755
756 wd3 = watch_file(ifd, IN_MOVE_SELF, path3);
757 error = rename(path3, "foo");
758 ATF_REQUIRE(error == 0);
759 consume_event(ifd, wd3, IN_MOVE_SELF, 0, NULL);
760
761 close_inotify(ifd);
762 }
763
764 ATF_TC_WITHOUT_HEAD(inotify_event_open);
ATF_TC_BODY(inotify_event_open,tc)765 ATF_TC_BODY(inotify_event_open, tc)
766 {
767 char root[PATH_MAX], path[PATH_MAX];
768 int error, ifd, fd, wd;
769
770 ifd = inotify(IN_NONBLOCK);
771
772 wd = watch_dir(ifd, IN_OPEN, root);
773
774 snprintf(path, sizeof(path), "%s/file", root);
775 fd = open(path, O_RDWR | O_CREAT, 0644);
776 ATF_REQUIRE(fd != -1);
777 close_checked(fd);
778 consume_event(ifd, wd, IN_OPEN, 0, "file");
779
780 fd = open(path, O_PATH);
781 ATF_REQUIRE(fd != -1);
782 close_checked(fd);
783 consume_event(ifd, wd, IN_OPEN, 0, "file");
784
785 fd = open(root, O_DIRECTORY);
786 ATF_REQUIRE(fd != -1);
787 close_checked(fd);
788 consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
789
790 snprintf(path, sizeof(path), "%s/fifo", root);
791 error = mkfifo(path, 0644);
792 ATF_REQUIRE(error == 0);
793 fd = open(path, O_RDWR);
794 ATF_REQUIRE(fd != -1);
795 close_checked(fd);
796 consume_event(ifd, wd, IN_OPEN, 0, "fifo");
797
798 close_inotify(ifd);
799 }
800
801 ATF_TC_WITH_CLEANUP(inotify_event_unmount);
ATF_TC_HEAD(inotify_event_unmount,tc)802 ATF_TC_HEAD(inotify_event_unmount, tc)
803 {
804 atf_tc_set_md_var(tc, "require.user", "root");
805 }
ATF_TC_BODY(inotify_event_unmount,tc)806 ATF_TC_BODY(inotify_event_unmount, tc)
807 {
808 int error, fd, ifd, wd;
809
810 ifd = inotify(IN_NONBLOCK);
811
812 error = mkdir("./root", 0755);
813 ATF_REQUIRE(error == 0);
814
815 mount_tmpfs("./root");
816
817 error = mkdir("./root/dir", 0755);
818 ATF_REQUIRE(error == 0);
819 wd = inotify_add_watch(ifd, "./root/dir", IN_OPEN);
820 ATF_REQUIRE(wd >= 0);
821
822 fd = open("./root/dir", O_RDONLY | O_DIRECTORY);
823 ATF_REQUIRE(fd != -1);
824 consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
825 close_checked(fd);
826
827 /* A regular unmount should fail, as inotify holds a vnode reference. */
828 error = unmount("./root", 0);
829 ATF_REQUIRE_ERRNO(EBUSY, error == -1);
830 error = unmount("./root", MNT_FORCE);
831 ATF_REQUIRE_MSG(error == 0,
832 "unmounting ./root failed: %s", strerror(errno));
833
834 consume_event(ifd, wd, 0, IN_UNMOUNT, NULL);
835 consume_event(ifd, wd, 0, IN_IGNORED, NULL);
836
837 close_inotify(ifd);
838 }
ATF_TC_CLEANUP(inotify_event_unmount,tc)839 ATF_TC_CLEANUP(inotify_event_unmount, tc)
840 {
841 (void)unmount("./root", MNT_FORCE);
842 }
843
ATF_TP_ADD_TCS(tp)844 ATF_TP_ADD_TCS(tp)
845 {
846 /* Tests for the inotify syscalls. */
847 ATF_TP_ADD_TC(tp, inotify_capsicum);
848 ATF_TP_ADD_TC(tp, inotify_coalesce);
849 ATF_TP_ADD_TC(tp, inotify_mask_create);
850 ATF_TP_ADD_TC(tp, inotify_nullfs);
851 ATF_TP_ADD_TC(tp, inotify_queue_overflow);
852 /* Tests for the various inotify event types. */
853 ATF_TP_ADD_TC(tp, inotify_event_access_file);
854 ATF_TP_ADD_TC(tp, inotify_event_access_dir);
855 ATF_TP_ADD_TC(tp, inotify_event_attrib);
856 ATF_TP_ADD_TC(tp, inotify_event_close_nowrite);
857 ATF_TP_ADD_TC(tp, inotify_event_close_write);
858 ATF_TP_ADD_TC(tp, inotify_event_create);
859 ATF_TP_ADD_TC(tp, inotify_event_delete);
860 ATF_TP_ADD_TC(tp, inotify_event_move);
861 ATF_TP_ADD_TC(tp, inotify_event_open);
862 ATF_TP_ADD_TC(tp, inotify_event_unmount);
863 return (atf_no_error());
864 }
865