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, 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 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 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 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 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 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 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); 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); 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); 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); 324 ATF_TC_HEAD(inotify_nullfs, tc) 325 { 326 atf_tc_set_md_var(tc, "require.user", "root"); 327 } 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 } 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 802 ATF_TC_HEAD(inotify_event_unmount, tc) 803 { 804 atf_tc_set_md_var(tc, "require.user", "root"); 805 } 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 } 839 ATF_TC_CLEANUP(inotify_event_unmount, tc) 840 { 841 (void)unmount("./root", MNT_FORCE); 842 } 843 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