xref: /freebsd/tests/sys/kern/inotify_test.c (revision a02d794f5acd12ba3cf1de5c204a8dd56af47edd)
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  * Watch a file in a nullfs mount, and remove it from the lower mount.  Make
397  * sure that we get an IN_DELETE_SELF event and that the watch is removed.
398  */
399 ATF_TC_WITH_CLEANUP(inotify_nullfs_remove);
ATF_TC_HEAD(inotify_nullfs_remove,tc)400 ATF_TC_HEAD(inotify_nullfs_remove, tc)
401 {
402 	atf_tc_set_md_var(tc, "require.user", "root");
403 }
ATF_TC_BODY(inotify_nullfs_remove,tc)404 ATF_TC_BODY(inotify_nullfs_remove, tc)
405 {
406 	char dir[PATH_MAX], path[PATH_MAX], *p;
407 	int error, fd, ifd, wd;
408 
409 	strlcpy(dir, "./test.XXXXXX", sizeof(dir));
410 	p = mkdtemp(dir);
411 	ATF_REQUIRE(p == dir);
412 
413 	error = mkdir("./mnt", 0755);
414 	ATF_REQUIRE(error == 0);
415 
416 	/* Mount the testdir onto ./mnt. */
417 	mount_nullfs("./mnt", dir);
418 
419 	snprintf(path, sizeof(path), "%s/file", dir);
420 	fd = open(path, O_RDWR | O_CREAT, 0644);
421 	ATF_REQUIRE(fd != -1);
422 	close_checked(fd);
423 
424 	ifd = inotify(IN_NONBLOCK);
425 	wd = inotify_add_watch(ifd, "./mnt/file", IN_DELETE_SELF);
426 	ATF_REQUIRE(wd != -1);
427 
428 	error = unlink(path);
429 	ATF_REQUIRE(error == 0);
430 
431 	consume_event(ifd, wd, IN_DELETE_SELF, 0, NULL);
432 	consume_event(ifd, wd, 0, IN_IGNORED, NULL);
433 
434 	close_inotify(ifd);
435 }
ATF_TC_CLEANUP(inotify_nullfs_remove,tc)436 ATF_TC_CLEANUP(inotify_nullfs_remove, tc)
437 {
438 	int error;
439 
440 	error = unmount("./mnt", 0);
441 	if (error != 0) {
442 		perror("unmount");
443 		exit(1);
444 	}
445 }
446 
447 /*
448  * Exercise a scenario where a watched lower vnode is deleted by a rename.  The
449  * deletion causes the upper vnode to be reclaimed, and after that point it
450  * should stop trying to forward events back to the (now detached) lower vnode.
451  */
452 ATF_TC_WITH_CLEANUP(inotify_nullfs_rename);
ATF_TC_HEAD(inotify_nullfs_rename,tc)453 ATF_TC_HEAD(inotify_nullfs_rename, tc)
454 {
455 	atf_tc_set_md_var(tc, "require.user", "root");
456 }
ATF_TC_BODY(inotify_nullfs_rename,tc)457 ATF_TC_BODY(inotify_nullfs_rename, tc)
458 {
459 	char dir[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX], *p;
460 	int error, fd, ifd, wd;
461 
462 	strlcpy(dir, "./test.XXXXXX", sizeof(dir));
463 	p = mkdtemp(dir);
464 	ATF_REQUIRE(p == dir);
465 
466 	error = mkdir("./mnt", 0755);
467 	ATF_REQUIRE(error == 0);
468 
469 	/* Mount the testdir onto ./mnt. */
470 	mount_nullfs("./mnt", dir);
471 
472 	ifd = inotify(IN_NONBLOCK);
473 
474 	/* Create two files, they will be renamed in the upper layer. */
475 	snprintf(path1, sizeof(path1), "%s/file1", dir);
476 	fd = open(path1, O_RDWR | O_CREAT, 0644);
477 	ATF_REQUIRE(fd != -1);
478 	close_checked(fd);
479 	snprintf(path2, sizeof(path2), "%s/file2", dir);
480 	fd = open(path2, O_RDWR | O_CREAT, 0644);
481 	ATF_REQUIRE(fd != -1);
482 	close_checked(fd);
483 
484 	wd = inotify_add_watch(ifd, "./mnt/file1", IN_DELETE_SELF);
485 	ATF_REQUIRE(wd != -1);
486 	error = rename("./mnt/file2", "./mnt/file1");
487 	ATF_REQUIRE(error == 0);
488 
489 	consume_event(ifd, wd, IN_DELETE_SELF, 0, NULL);
490 	consume_event(ifd, wd, 0, IN_IGNORED, NULL);
491 
492 	close_inotify(ifd);
493 }
ATF_TC_CLEANUP(inotify_nullfs_rename,tc)494 ATF_TC_CLEANUP(inotify_nullfs_rename, tc)
495 {
496 	int error;
497 
498 	error = unmount("./mnt", 0);
499 	if (error != 0) {
500 		perror("unmount");
501 		exit(1);
502 	}
503 }
504 
505 /*
506  * Make sure that exceeding max_events pending events results in an overflow
507  * event.
508  */
509 ATF_TC_WITHOUT_HEAD(inotify_queue_overflow);
ATF_TC_BODY(inotify_queue_overflow,tc)510 ATF_TC_BODY(inotify_queue_overflow, tc)
511 {
512 	char path[PATH_MAX];
513 	size_t size;
514 	int error, dfd, ifd, max, wd;
515 
516 	size = sizeof(max);
517 	error = sysctlbyname("vfs.inotify.max_queued_events", &max, &size, NULL,
518 	    0);
519 	ATF_REQUIRE(error == 0);
520 
521 	ifd = inotify(IN_NONBLOCK);
522 
523 	/* Create a directory and watch it for file creation events. */
524 	wd = watch_dir(ifd, IN_CREATE, path);
525 	dfd = open(path, O_DIRECTORY);
526 	ATF_REQUIRE(dfd != -1);
527 	/* Generate max+1 file creation events. */
528 	for (int i = 0; i < max + 1; i++) {
529 		char name[NAME_MAX];
530 		int fd;
531 
532 		(void)snprintf(name, sizeof(name), "file%d", i);
533 		fd = openat(dfd, name, O_CREAT | O_RDWR, 0644);
534 		ATF_REQUIRE(fd != -1);
535 		close_checked(fd);
536 	}
537 
538 	/*
539 	 * Read our events.  We should see files 0..max-1 and then an overflow
540 	 * event.
541 	 */
542 	for (int i = 0; i < max; i++) {
543 		char name[NAME_MAX];
544 
545 		(void)snprintf(name, sizeof(name), "file%d", i);
546 		consume_event(ifd, wd, IN_CREATE, 0, name);
547 	}
548 
549 	/* Look for an overflow event. */
550 	consume_event(ifd, -1, 0, IN_Q_OVERFLOW, NULL);
551 
552 	close_checked(dfd);
553 	close_inotify(ifd);
554 }
555 
556 ATF_TC_WITHOUT_HEAD(inotify_event_access_file);
ATF_TC_BODY(inotify_event_access_file,tc)557 ATF_TC_BODY(inotify_event_access_file, tc)
558 {
559 	char path[PATH_MAX], buf[16];
560 	off_t nb;
561 	ssize_t n;
562 	int error, fd, fd1, ifd, s[2], wd;
563 
564 	ifd = inotify(IN_NONBLOCK);
565 
566 	wd = watch_file(ifd, IN_ACCESS, path);
567 
568 	fd = open(path, O_RDWR);
569 	n = write(fd, "test", 4);
570 	ATF_REQUIRE(n == 4);
571 
572 	/* A simple read(2) should generate an access. */
573 	ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
574 	n = read(fd, buf, sizeof(buf));
575 	ATF_REQUIRE(n == 4);
576 	ATF_REQUIRE(memcmp(buf, "test", 4) == 0);
577 	consume_event(ifd, wd, IN_ACCESS, 0, NULL);
578 
579 	/* copy_file_range(2) should as well. */
580 	ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
581 	fd1 = open("sink", O_RDWR | O_CREAT, 0644);
582 	ATF_REQUIRE(fd1 != -1);
583 	n = copy_file_range(fd, NULL, fd1, NULL, 4, 0);
584 	ATF_REQUIRE(n == 4);
585 	close_checked(fd1);
586 	consume_event(ifd, wd, IN_ACCESS, 0, NULL);
587 
588 	/* As should sendfile(2). */
589 	error = socketpair(AF_UNIX, SOCK_STREAM, 0, s);
590 	ATF_REQUIRE(error == 0);
591 	error = sendfile(fd, s[0], 0, 4, NULL, &nb, 0);
592 	ATF_REQUIRE(error == 0);
593 	ATF_REQUIRE(nb == 4);
594 	consume_event(ifd, wd, IN_ACCESS, 0, NULL);
595 	close_checked(s[0]);
596 	close_checked(s[1]);
597 
598 	close_checked(fd);
599 
600 	close_inotify(ifd);
601 }
602 
603 ATF_TC_WITHOUT_HEAD(inotify_event_access_dir);
ATF_TC_BODY(inotify_event_access_dir,tc)604 ATF_TC_BODY(inotify_event_access_dir, tc)
605 {
606 	char root[PATH_MAX], path[PATH_MAX];
607 	struct dirent *ent;
608 	DIR *dir;
609 	int error, ifd, wd;
610 
611 	ifd = inotify(IN_NONBLOCK);
612 
613 	wd = watch_dir(ifd, IN_ACCESS, root);
614 	snprintf(path, sizeof(path), "%s/dir", root);
615 	error = mkdir(path, 0755);
616 	ATF_REQUIRE(error == 0);
617 
618 	/* Read an entry and generate an access. */
619 	dir = opendir(path);
620 	ATF_REQUIRE(dir != NULL);
621 	ent = readdir(dir);
622 	ATF_REQUIRE(ent != NULL);
623 	ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
624 	    strcmp(ent->d_name, "..") == 0);
625 	ATF_REQUIRE(closedir(dir) == 0);
626 	consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, "dir");
627 
628 	/*
629 	 * Reading the watched directory should generate an access event.
630 	 * This is contrary to Linux's inotify man page, which states that
631 	 * IN_ACCESS is only generated for accesses to objects in a watched
632 	 * directory.
633 	 */
634 	dir = opendir(root);
635 	ATF_REQUIRE(dir != NULL);
636 	ent = readdir(dir);
637 	ATF_REQUIRE(ent != NULL);
638 	ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
639 	    strcmp(ent->d_name, "..") == 0);
640 	ATF_REQUIRE(closedir(dir) == 0);
641 	consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, NULL);
642 
643 	close_inotify(ifd);
644 }
645 
646 ATF_TC_WITHOUT_HEAD(inotify_event_attrib);
ATF_TC_BODY(inotify_event_attrib,tc)647 ATF_TC_BODY(inotify_event_attrib, tc)
648 {
649 	char path[PATH_MAX];
650 	int error, ifd, fd, wd;
651 
652 	ifd = inotify(IN_NONBLOCK);
653 
654 	wd = watch_file(ifd, IN_ATTRIB, path);
655 
656 	fd = open(path, O_RDWR);
657 	ATF_REQUIRE(fd != -1);
658 	error = fchmod(fd, 0600);
659 	ATF_REQUIRE(error == 0);
660 	consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
661 
662 	error = fchown(fd, getuid(), getgid());
663 	ATF_REQUIRE(error == 0);
664 	consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
665 
666 	close_checked(fd);
667 	close_inotify(ifd);
668 }
669 
670 ATF_TC_WITHOUT_HEAD(inotify_event_close_nowrite);
ATF_TC_BODY(inotify_event_close_nowrite,tc)671 ATF_TC_BODY(inotify_event_close_nowrite, tc)
672 {
673 	char file[PATH_MAX], file1[PATH_MAX], dir[PATH_MAX];
674 	int ifd, fd, wd1, wd2;
675 
676 	ifd = inotify(IN_NONBLOCK);
677 
678 	wd1 = watch_dir(ifd, IN_CLOSE_NOWRITE, dir);
679 	wd2 = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, file);
680 
681 	fd = open(dir, O_DIRECTORY);
682 	ATF_REQUIRE(fd != -1);
683 	close_checked(fd);
684 	consume_event(ifd, wd1, IN_CLOSE_NOWRITE, IN_ISDIR, NULL);
685 
686 	fd = open(file, O_RDONLY);
687 	ATF_REQUIRE(fd != -1);
688 	close_checked(fd);
689 	consume_event(ifd, wd2, IN_CLOSE_NOWRITE, 0, NULL);
690 
691 	snprintf(file1, sizeof(file1), "%s/file", dir);
692 	fd = open(file1, O_RDONLY | O_CREAT, 0644);
693 	ATF_REQUIRE(fd != -1);
694 	close_checked(fd);
695 	consume_event(ifd, wd1, IN_CLOSE_NOWRITE, 0, "file");
696 
697 	close_inotify(ifd);
698 }
699 
700 ATF_TC_WITHOUT_HEAD(inotify_event_close_write);
ATF_TC_BODY(inotify_event_close_write,tc)701 ATF_TC_BODY(inotify_event_close_write, tc)
702 {
703 	char path[PATH_MAX];
704 	int ifd, fd, wd;
705 
706 	ifd = inotify(IN_NONBLOCK);
707 
708 	wd = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, path);
709 
710 	fd = open(path, O_RDWR);
711 	ATF_REQUIRE(fd != -1);
712 	close_checked(fd);
713 	consume_event(ifd, wd, IN_CLOSE_WRITE, 0, NULL);
714 
715 	close_inotify(ifd);
716 }
717 
718 /* Verify that various operations in a directory generate IN_CREATE events. */
719 ATF_TC_WITHOUT_HEAD(inotify_event_create);
ATF_TC_BODY(inotify_event_create,tc)720 ATF_TC_BODY(inotify_event_create, tc)
721 {
722 	struct sockaddr_un sun;
723 	char path[PATH_MAX], path1[PATH_MAX], root[PATH_MAX];
724 	ssize_t n;
725 	int error, ifd, ifd1, fd, s, wd, wd1;
726 	char b;
727 
728 	ifd = inotify(IN_NONBLOCK);
729 
730 	wd = watch_dir(ifd, IN_CREATE, root);
731 
732 	/* Regular file. */
733 	snprintf(path, sizeof(path), "%s/file", root);
734 	fd = open(path, O_RDWR | O_CREAT, 0644);
735 	ATF_REQUIRE(fd != -1);
736 	/*
737 	 * Make sure we get an event triggered by the fd used to create the
738 	 * file.
739 	 */
740 	ifd1 = inotify(IN_NONBLOCK);
741 	wd1 = inotify_add_watch(ifd1, root, IN_MODIFY);
742 	b = 42;
743 	n = write(fd, &b, sizeof(b));
744 	ATF_REQUIRE(n == sizeof(b));
745 	close_checked(fd);
746 	consume_event(ifd, wd, IN_CREATE, 0, "file");
747 	consume_event(ifd1, wd1, IN_MODIFY, 0, "file");
748 	close_inotify(ifd1);
749 
750 	/* Hard link. */
751 	snprintf(path1, sizeof(path1), "%s/link", root);
752 	error = link(path, path1);
753 	ATF_REQUIRE(error == 0);
754 	consume_event(ifd, wd, IN_CREATE, 0, "link");
755 
756 	/* Directory. */
757 	snprintf(path, sizeof(path), "%s/dir", root);
758 	error = mkdir(path, 0755);
759 	ATF_REQUIRE(error == 0);
760 	consume_event(ifd, wd, IN_CREATE, IN_ISDIR, "dir");
761 
762 	/* Symbolic link. */
763 	snprintf(path1, sizeof(path1), "%s/symlink", root);
764 	error = symlink(path, path1);
765 	ATF_REQUIRE(error == 0);
766 	consume_event(ifd, wd, IN_CREATE, 0, "symlink");
767 
768 	/* FIFO. */
769 	snprintf(path, sizeof(path), "%s/fifo", root);
770 	error = mkfifo(path, 0644);
771 	ATF_REQUIRE(error == 0);
772 	consume_event(ifd, wd, IN_CREATE, 0, "fifo");
773 
774 	/* Binding a socket. */
775 	s = socket(AF_UNIX, SOCK_STREAM, 0);
776 	memset(&sun, 0, sizeof(sun));
777 	sun.sun_family = AF_UNIX;
778 	sun.sun_len = sizeof(sun);
779 	snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/socket", root);
780 	error = bind(s, (struct sockaddr *)&sun, sizeof(sun));
781 	ATF_REQUIRE(error == 0);
782 	close_checked(s);
783 	consume_event(ifd, wd, IN_CREATE, 0, "socket");
784 
785 	close_inotify(ifd);
786 }
787 
788 ATF_TC_WITHOUT_HEAD(inotify_event_delete);
ATF_TC_BODY(inotify_event_delete,tc)789 ATF_TC_BODY(inotify_event_delete, tc)
790 {
791 	char root[PATH_MAX], path[PATH_MAX], file[PATH_MAX];
792 	int error, fd, ifd, wd, wd2;
793 
794 	ifd = inotify(IN_NONBLOCK);
795 
796 	wd = watch_dir(ifd, IN_DELETE | IN_DELETE_SELF, root);
797 
798 	snprintf(path, sizeof(path), "%s/file", root);
799 	fd = open(path, O_RDWR | O_CREAT, 0644);
800 	ATF_REQUIRE(fd != -1);
801 	error = unlink(path);
802 	ATF_REQUIRE(error == 0);
803 	consume_event(ifd, wd, IN_DELETE, 0, "file");
804 	close_checked(fd);
805 
806 	/*
807 	 * Make sure that renaming over a file generates a delete event when and
808 	 * only when that file is watched.
809 	 */
810 	fd = open(path, O_RDWR | O_CREAT, 0644);
811 	ATF_REQUIRE(fd != -1);
812 	close_checked(fd);
813 	wd2 = inotify_add_watch(ifd, path, IN_DELETE | IN_DELETE_SELF);
814 	ATF_REQUIRE(wd2 != -1);
815 	snprintf(file, sizeof(file), "%s/file2", root);
816 	fd = open(file, O_RDWR | O_CREAT, 0644);
817 	ATF_REQUIRE(fd != -1);
818 	close_checked(fd);
819 	error = rename(file, path);
820 	ATF_REQUIRE(error == 0);
821 	consume_event(ifd, wd2, IN_DELETE_SELF, 0, NULL);
822 	consume_event(ifd, wd2, 0, IN_IGNORED, NULL);
823 
824 	error = unlink(path);
825 	ATF_REQUIRE(error == 0);
826 	consume_event(ifd, wd, IN_DELETE, 0, "file");
827 	error = rmdir(root);
828 	ATF_REQUIRE(error == 0);
829 	consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
830 	consume_event(ifd, wd, 0, IN_IGNORED, NULL);
831 
832 	close_inotify(ifd);
833 }
834 
835 ATF_TC_WITHOUT_HEAD(inotify_event_move);
ATF_TC_BODY(inotify_event_move,tc)836 ATF_TC_BODY(inotify_event_move, tc)
837 {
838 	char dir1[PATH_MAX], dir2[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX];
839 	char path3[PATH_MAX];
840 	int error, ifd, fd, wd1, wd2, wd3;
841 	uint32_t cookie1, cookie2;
842 
843 	ifd = inotify(IN_NONBLOCK);
844 
845 	wd1 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir1);
846 	wd2 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir2);
847 
848 	snprintf(path1, sizeof(path1), "%s/file", dir1);
849 	fd = open(path1, O_RDWR | O_CREAT, 0644);
850 	ATF_REQUIRE(fd != -1);
851 	close_checked(fd);
852 	snprintf(path2, sizeof(path2), "%s/file2", dir2);
853 	error = rename(path1, path2);
854 	ATF_REQUIRE(error == 0);
855 	cookie1 = consume_event_cookie(ifd, wd1, IN_MOVED_FROM, 0, "file");
856 	cookie2 = consume_event_cookie(ifd, wd2, IN_MOVED_TO, 0, "file2");
857 	ATF_REQUIRE_MSG(cookie1 == cookie2,
858 	    "expected cookie %u, got %u", cookie1, cookie2);
859 
860 	snprintf(path2, sizeof(path2), "%s/dir", dir2);
861 	error = rename(dir1, path2);
862 	ATF_REQUIRE(error == 0);
863 	consume_event(ifd, wd1, IN_MOVE_SELF, IN_ISDIR, NULL);
864 	consume_event(ifd, wd2, IN_MOVED_TO, IN_ISDIR, "dir");
865 
866 	wd3 = watch_file(ifd, IN_MOVE_SELF, path3);
867 	error = rename(path3, "foo");
868 	ATF_REQUIRE(error == 0);
869 	consume_event(ifd, wd3, IN_MOVE_SELF, 0, NULL);
870 
871 	close_inotify(ifd);
872 }
873 
874 ATF_TC_WITHOUT_HEAD(inotify_event_move_dir);
ATF_TC_BODY(inotify_event_move_dir,tc)875 ATF_TC_BODY(inotify_event_move_dir, tc)
876 {
877 	char dir[PATH_MAX], subdir1[PATH_MAX], subdir2[PATH_MAX];
878 	uint32_t cookie1, cookie2;
879 	int error, ifd, wd1, wd2;
880 
881 	ifd = inotify(IN_NONBLOCK);
882 
883 	wd1 = watch_dir(ifd, IN_MOVE, dir);
884 	snprintf(subdir1, sizeof(subdir1), "%s/subdir", dir);
885 	error = mkdir(subdir1, 0755);
886 	ATF_REQUIRE(error == 0);
887 	wd2 = inotify_add_watch(ifd, subdir1, IN_MOVE);
888 	ATF_REQUIRE(wd2 != -1);
889 
890 	snprintf(subdir2, sizeof(subdir2), "%s/newsubdir", dir);
891 	error = rename(subdir1, subdir2);
892 	ATF_REQUIRE(error == 0);
893 
894 	cookie1 = consume_event_cookie(ifd, wd1, IN_MOVED_FROM, IN_ISDIR,
895 	    "subdir");
896 	cookie2 = consume_event_cookie(ifd, wd1, IN_MOVED_TO, IN_ISDIR,
897 	    "newsubdir");
898 	ATF_REQUIRE_MSG(cookie1 == cookie2,
899 	    "expected cookie %u, got %u", cookie1, cookie2);
900 
901 	close_inotify(ifd);
902 }
903 
904 ATF_TC_WITHOUT_HEAD(inotify_event_open);
ATF_TC_BODY(inotify_event_open,tc)905 ATF_TC_BODY(inotify_event_open, tc)
906 {
907 	char root[PATH_MAX], path[PATH_MAX];
908 	int error, ifd, fd, wd;
909 
910 	ifd = inotify(IN_NONBLOCK);
911 
912 	wd = watch_dir(ifd, IN_OPEN, root);
913 
914 	snprintf(path, sizeof(path), "%s/file", root);
915 	fd = open(path, O_RDWR | O_CREAT, 0644);
916 	ATF_REQUIRE(fd != -1);
917 	close_checked(fd);
918 	consume_event(ifd, wd, IN_OPEN, 0, "file");
919 
920 	fd = open(path, O_PATH);
921 	ATF_REQUIRE(fd != -1);
922 	close_checked(fd);
923 	consume_event(ifd, wd, IN_OPEN, 0, "file");
924 
925 	fd = open(root, O_DIRECTORY);
926 	ATF_REQUIRE(fd != -1);
927 	close_checked(fd);
928 	consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
929 
930 	snprintf(path, sizeof(path), "%s/fifo", root);
931 	error = mkfifo(path, 0644);
932 	ATF_REQUIRE(error == 0);
933 	fd = open(path, O_RDWR);
934 	ATF_REQUIRE(fd != -1);
935 	close_checked(fd);
936 	consume_event(ifd, wd, IN_OPEN, 0, "fifo");
937 
938 	close_inotify(ifd);
939 }
940 
941 ATF_TC_WITH_CLEANUP(inotify_event_unmount);
ATF_TC_HEAD(inotify_event_unmount,tc)942 ATF_TC_HEAD(inotify_event_unmount, tc)
943 {
944 	atf_tc_set_md_var(tc, "require.user", "root");
945 }
ATF_TC_BODY(inotify_event_unmount,tc)946 ATF_TC_BODY(inotify_event_unmount, tc)
947 {
948 	int error, fd, ifd, wd;
949 
950 	ifd = inotify(IN_NONBLOCK);
951 
952 	error = mkdir("./root", 0755);
953 	ATF_REQUIRE(error == 0);
954 
955 	mount_tmpfs("./root");
956 
957 	error = mkdir("./root/dir", 0755);
958 	ATF_REQUIRE(error == 0);
959 	wd = inotify_add_watch(ifd, "./root/dir", IN_OPEN);
960 	ATF_REQUIRE(wd >= 0);
961 
962 	fd = open("./root/dir", O_RDONLY | O_DIRECTORY);
963 	ATF_REQUIRE(fd != -1);
964 	consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
965 	close_checked(fd);
966 
967 	/* A regular unmount should fail, as inotify holds a vnode reference. */
968 	error = unmount("./root", 0);
969 	ATF_REQUIRE_ERRNO(EBUSY, error == -1);
970 	error = unmount("./root", MNT_FORCE);
971 	ATF_REQUIRE_MSG(error == 0,
972 	    "unmounting ./root failed: %s", strerror(errno));
973 
974 	consume_event(ifd, wd, 0, IN_UNMOUNT, NULL);
975 	consume_event(ifd, wd, 0, IN_IGNORED, NULL);
976 
977 	close_inotify(ifd);
978 }
ATF_TC_CLEANUP(inotify_event_unmount,tc)979 ATF_TC_CLEANUP(inotify_event_unmount, tc)
980 {
981 	(void)unmount("./root", MNT_FORCE);
982 }
983 
ATF_TP_ADD_TCS(tp)984 ATF_TP_ADD_TCS(tp)
985 {
986 	/* Tests for the inotify syscalls. */
987 	ATF_TP_ADD_TC(tp, inotify_capsicum);
988 	ATF_TP_ADD_TC(tp, inotify_coalesce);
989 	ATF_TP_ADD_TC(tp, inotify_mask_create);
990 	ATF_TP_ADD_TC(tp, inotify_nullfs);
991 	ATF_TP_ADD_TC(tp, inotify_nullfs_remove);
992 	ATF_TP_ADD_TC(tp, inotify_nullfs_rename);
993 	ATF_TP_ADD_TC(tp, inotify_queue_overflow);
994 	/* Tests for the various inotify event types. */
995 	ATF_TP_ADD_TC(tp, inotify_event_access_file);
996 	ATF_TP_ADD_TC(tp, inotify_event_access_dir);
997 	ATF_TP_ADD_TC(tp, inotify_event_attrib);
998 	ATF_TP_ADD_TC(tp, inotify_event_close_nowrite);
999 	ATF_TP_ADD_TC(tp, inotify_event_close_write);
1000 	ATF_TP_ADD_TC(tp, inotify_event_create);
1001 	ATF_TP_ADD_TC(tp, inotify_event_delete);
1002 	ATF_TP_ADD_TC(tp, inotify_event_move);
1003 	ATF_TP_ADD_TC(tp, inotify_event_move_dir);
1004 	ATF_TP_ADD_TC(tp, inotify_event_open);
1005 	ATF_TP_ADD_TC(tp, inotify_event_unmount);
1006 	return (atf_no_error());
1007 }
1008