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