1 /* $NetBSD: t_basic.c,v 1.6 2010/07/12 13:09:19 pooka Exp $ */ 2 3 #include <sys/types.h> 4 #include <sys/mount.h> 5 #include <sys/socket.h> 6 7 #include <assert.h> 8 #include <atf-c.h> 9 #include <err.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <pthread.h> 13 #include <puffs.h> 14 #include <puffsdump.h> 15 #include <stdio.h> 16 #include <unistd.h> 17 #include <string.h> 18 #include <stdlib.h> 19 20 #include <rump/rump.h> 21 #include <rump/rump_syscalls.h> 22 23 #include "../../h_macros.h" 24 25 struct puffs_args { 26 uint8_t *us_pargs; 27 size_t us_pargslen; 28 29 int us_pflags; 30 int us_servfd; 31 pid_t us_childpid; 32 }; 33 34 #define BUFSIZE (64*1024) 35 #define DTFS_DUMP "-o","dump" 36 37 struct thefds { 38 int rumpfd; 39 int servfd; 40 }; 41 42 int vfs_toserv_ops[PUFFS_VFS_MAX]; 43 int vn_toserv_ops[PUFFS_VN_MAX]; 44 45 /* 46 * Do a synchronous operation. When this returns, all FAF operations 47 * have at least been delivered to the file system. 48 * 49 * XXX: is this really good enough considering puffs(9)-issued 50 * callback operations? 51 */ 52 static void 53 syncbar(const char *fs) 54 { 55 struct statvfs svb; 56 57 rump_sys_statvfs1(fs, &svb, ST_WAIT); 58 } 59 60 #ifdef PUFFSDUMP 61 static void __unused 62 dumpopcount(void) 63 { 64 size_t i; 65 66 printf("VFS OPS:\n"); 67 for (i = 0; i < MIN(puffsdump_vfsop_count, PUFFS_VFS_MAX); i++) { 68 printf("\t%s: %d\n", 69 puffsdump_vfsop_revmap[i], vfs_toserv_ops[i]); 70 } 71 72 printf("VN OPS:\n"); 73 for (i = 0; i < MIN(puffsdump_vnop_count, PUFFS_VN_MAX); i++) { 74 printf("\t%s: %d\n", 75 puffsdump_vnop_revmap[i], vn_toserv_ops[i]); 76 } 77 } 78 #endif 79 80 /* 81 * Threads which shovel data between comfd and /dev/puffs. 82 * (cannot use polling since fd's are in different namespaces) 83 */ 84 static void * 85 readshovel(void *arg) 86 { 87 struct putter_hdr *phdr; 88 struct puffs_req *preq; 89 struct thefds *fds = arg; 90 char buf[BUFSIZE]; 91 int comfd, puffsfd; 92 93 comfd = fds->servfd; 94 puffsfd = fds->rumpfd; 95 96 phdr = (void *)buf; 97 preq = (void *)buf; 98 99 /* use static thread id */ 100 rump_pub_lwp_alloc_and_switch(0, 10); 101 102 for (;;) { 103 ssize_t n; 104 105 n = rump_sys_read(puffsfd, buf, sizeof(*phdr)); 106 if (n <= 0) 107 break; 108 109 assert(phdr->pth_framelen < BUFSIZE); 110 n = rump_sys_read(puffsfd, buf+sizeof(*phdr), 111 phdr->pth_framelen - sizeof(*phdr)); 112 if (n <= 0) 113 break; 114 115 /* Analyze request */ 116 if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) { 117 assert(preq->preq_optype < PUFFS_VFS_MAX); 118 vfs_toserv_ops[preq->preq_optype]++; 119 } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) { 120 assert(preq->preq_optype < PUFFS_VN_MAX); 121 vn_toserv_ops[preq->preq_optype]++; 122 } 123 124 n = phdr->pth_framelen; 125 if (write(comfd, buf, n) != n) 126 break; 127 } 128 129 return NULL; 130 } 131 132 static void * 133 writeshovel(void *arg) 134 { 135 struct thefds *fds = arg; 136 struct putter_hdr *phdr; 137 char buf[BUFSIZE]; 138 size_t toread; 139 int comfd, puffsfd; 140 141 /* use static thread id */ 142 rump_pub_lwp_alloc_and_switch(0, 11); 143 144 comfd = fds->servfd; 145 puffsfd = fds->rumpfd; 146 147 phdr = (struct putter_hdr *)buf; 148 149 for (;;) { 150 off_t off; 151 ssize_t n; 152 153 /* 154 * Need to write everything to the "kernel" in one chunk, 155 * so make sure we have it here. 156 */ 157 off = 0; 158 toread = sizeof(struct putter_hdr); 159 assert(toread < BUFSIZE); 160 do { 161 n = read(comfd, buf+off, toread); 162 if (n <= 0) { 163 break; 164 } 165 off += n; 166 if (off >= sizeof(struct putter_hdr)) 167 toread = phdr->pth_framelen - off; 168 else 169 toread = off - sizeof(struct putter_hdr); 170 } while (toread); 171 172 n = rump_sys_write(puffsfd, buf, phdr->pth_framelen); 173 if (n != phdr->pth_framelen) 174 break; 175 } 176 177 return NULL; 178 } 179 180 static void 181 rumpshovels(int rumpfd, int servfd) 182 { 183 struct thefds *fds; 184 pthread_t pt; 185 int rv; 186 187 if ((rv = rump_init()) == -1) 188 err(1, "rump_init"); 189 190 fds = malloc(sizeof(*fds)); 191 fds->rumpfd = rumpfd; 192 fds->servfd = servfd; 193 if (pthread_create(&pt, NULL, readshovel, fds) == -1) 194 err(1, "read shovel"); 195 pthread_detach(pt); 196 if (pthread_create(&pt, NULL, writeshovel, fds) == -1) 197 err(1, "write shovel"); 198 pthread_detach(pt); 199 } 200 201 static int 202 parseargs(int argc, char *argv[], 203 struct puffs_args *args, int *mntflags, 204 char *canon_dev, char *canon_dir) 205 { 206 pid_t childpid; 207 int *pflags = &args->us_pflags; 208 char comfd[16]; 209 int sv[2]; 210 size_t len; 211 ssize_t n; 212 213 /* Create sucketpair for communication with the real file server */ 214 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) == -1) 215 err(1, "socketpair"); 216 217 switch ((childpid = fork())) { 218 case 0: 219 close(sv[1]); 220 snprintf(comfd, sizeof(sv[0]), "%d", sv[0]); 221 if (setenv("PUFFS_COMFD", comfd, 1) == -1) 222 atf_tc_fail_errno("setenv"); 223 224 if (execvp(argv[0], argv) == -1) 225 atf_tc_fail_errno("execvp"); 226 /*NOTREACHED*/ 227 case -1: 228 atf_tc_fail_errno("fork"); 229 /*NOTREACHED*/ 230 default: 231 close(sv[0]); 232 break; 233 } 234 235 /* read args */ 236 if ((n = read(sv[1], &len, sizeof(len))) != sizeof(len)) 237 err(1, "mp 1 %zd", n); 238 if (len > MAXPATHLEN) 239 err(1, "mntpath > MAXPATHLEN"); 240 if ((size_t)read(sv[1], canon_dir, len) != len) 241 err(1, "mp 2"); 242 if (read(sv[1], &len, sizeof(len)) != sizeof(len)) 243 err(1, "fn 1"); 244 if (len > MAXPATHLEN) 245 err(1, "devpath > MAXPATHLEN"); 246 if ((size_t)read(sv[1], canon_dev, len) != len) 247 err(1, "fn 2"); 248 if (read(sv[1], mntflags, sizeof(*mntflags)) != sizeof(*mntflags)) 249 err(1, "mntflags"); 250 if (read(sv[1], &args->us_pargslen, sizeof(args->us_pargslen)) != sizeof(args->us_pargslen)) 251 err(1, "puffs_args len"); 252 args->us_pargs = malloc(args->us_pargslen); 253 if (args->us_pargs == NULL) 254 err(1, "malloc"); 255 if (read(sv[1], args->us_pargs, args->us_pargslen) != args->us_pargslen) 256 err(1, "puffs_args"); 257 if (read(sv[1], pflags, sizeof(*pflags)) != sizeof(*pflags)) 258 err(1, "pflags"); 259 260 args->us_childpid = childpid; 261 args->us_servfd = sv[1]; 262 263 return 0; 264 } 265 266 #define dtfsmountv(a, b) \ 267 struct puffs_args pa; \ 268 dtfsmount1(a, __arraycount(b), b, atf_tc_get_config_var(tc, "srcdir"), &pa) 269 static void 270 dtfsmount1(const char *mp, int optcount, char *opts[], const char *srcdir, 271 struct puffs_args *pa) 272 { 273 char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN]; 274 char dtfs_path[MAXPATHLEN], mountpath[MAXPATHLEN]; 275 char **dtfsargv; 276 int mntflag; 277 int rv, i; 278 int fd; 279 280 dtfsargv = malloc(sizeof(char *) * (optcount+3)); 281 282 /* build dtfs exec path for atf */ 283 sprintf(dtfs_path, "%s/h_dtfs/h_dtfs", srcdir); 284 dtfsargv[0] = dtfs_path; 285 286 for (i = 0; i < optcount; i++) { 287 dtfsargv[i+1] = opts[i]; 288 } 289 290 strlcpy(mountpath, mp, sizeof(mountpath)); 291 dtfsargv[optcount+1] = mountpath; 292 dtfsargv[optcount+2] = NULL; 293 294 rv = parseargs(optcount+3, dtfsargv, 295 pa, &mntflag, canon_dev, canon_dir); 296 if (rv) 297 atf_tc_fail("comfd parseargs"); 298 299 rump_init(); 300 fd = rump_sys_open("/dev/puffs", O_RDWR); 301 if (fd == -1) 302 atf_tc_fail_errno("open puffs fd"); 303 #if 0 304 pa->pa_fd = fd; 305 #else 306 assert(fd == 0); /* XXX: FIXME */ 307 #endif 308 309 if (rump_sys_mkdir(mp, 0777) == -1) 310 atf_tc_fail_errno("mkdir mountpoint"); 311 312 if (rump_sys_mount(MOUNT_PUFFS, mp, 0, 313 pa->us_pargs, pa->us_pargslen) == -1) 314 atf_tc_fail_errno("mount"); 315 316 rumpshovels(fd, pa->us_servfd); 317 } 318 319 ATF_TC(mount); 320 ATF_TC_HEAD(mount, tc) 321 { 322 323 atf_tc_set_md_var(tc, "descr", "puffs+dtfs un/mount test"); 324 } 325 326 ATF_TC_BODY(mount, tc) 327 { 328 char *myopts[] = { 329 "dtfs", 330 }; 331 332 dtfsmountv("/mp", myopts); 333 if (rump_sys_unmount("/mp", 0) == -1) 334 atf_tc_fail_errno("unmount"); 335 } 336 337 ATF_TC(root_reg); 338 ATF_TC_HEAD(root_reg, tc) 339 { 340 atf_tc_set_md_var(tc, "descr", "root is a regular file"); 341 } 342 343 ATF_TC_BODY(root_reg, tc) 344 { 345 char *myopts[] = { 346 "-r","reg", 347 "dtfs", 348 }; 349 int fd, rv; 350 351 dtfsmountv("/mp", myopts); 352 353 fd = rump_sys_open("/mp", O_RDWR); 354 if (fd == -1) 355 atf_tc_fail_errno("open root"); 356 if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd)) 357 atf_tc_fail_errno("write to root"); 358 rv = rump_sys_mkdir("/mp/test", 0777); 359 ATF_REQUIRE(errno == ENOTDIR); 360 ATF_REQUIRE(rv == -1); 361 rump_sys_close(fd); 362 363 if (rump_sys_unmount("/mp", 0) == -1) 364 atf_tc_fail_errno("unmount"); 365 } 366 367 ATF_TC(root_lnk); 368 ATF_TC_HEAD(root_lnk, tc) 369 { 370 371 atf_tc_set_md_var(tc, "descr", "root is a symbolic link"); 372 } 373 374 #define LINKSTR "/path/to/nowhere" 375 ATF_TC_BODY(root_lnk, tc) 376 { 377 char *myopts[] = { 378 "-r", "lnk " LINKSTR, 379 "-s", 380 "dtfs", 381 }; 382 char buf[PATH_MAX]; 383 ssize_t len; 384 385 dtfsmountv("/mp", myopts); 386 387 if ((len = rump_sys_readlink("/mp", buf, sizeof(buf)-1)) == -1) 388 atf_tc_fail_errno("readlink"); 389 buf[len] = '\0'; 390 391 ATF_REQUIRE_STREQ(buf, LINKSTR); 392 393 #if 0 /* XXX: unmount uses FOLLOW */ 394 if (rump_sys_unmount("/mp", 0) == -1) 395 atf_tc_fail_errno("unmount"); 396 #endif 397 398 /* 399 * XXX2: due to atf issue #53, we must make sure the child dies 400 * before we exit. 401 */ 402 if (kill(pa.us_childpid, SIGTERM) == -1) 403 err(1, "kill"); 404 } 405 406 ATF_TC(root_fifo); 407 ATF_TC_HEAD(root_fifo, tc) 408 { 409 410 atf_tc_set_md_var(tc, "descr", "root is a symbolic link"); 411 } 412 413 #define MAGICSTR "nakit ja muusiperunat maustevoilla" 414 static void * 415 dofifow(void *arg) 416 { 417 int fd = (int)(uintptr_t)arg; 418 char buf[512]; 419 420 printf("writing\n"); 421 strcpy(buf, MAGICSTR); 422 if (rump_sys_write(fd, buf, strlen(buf)+1) != strlen(buf)+1) 423 atf_tc_fail_errno("write to fifo"); 424 425 return NULL; 426 } 427 428 ATF_TC_BODY(root_fifo, tc) 429 { 430 char *myopts[] = { 431 "-r", "fifo", 432 "dtfs", 433 }; 434 pthread_t pt; 435 char buf[512]; 436 int fd; 437 438 dtfsmountv("/mp", myopts); 439 fd = rump_sys_open("/mp", O_RDWR); 440 if (fd == -1) 441 atf_tc_fail_errno("open fifo"); 442 443 pthread_create(&pt, NULL, dofifow, (void *)(uintptr_t)fd); 444 445 printf("reading\n"); 446 memset(buf, 0, sizeof(buf)); 447 if (rump_sys_read(fd, buf, sizeof(buf)) == -1) 448 atf_tc_fail_errno("read fifo"); 449 450 ATF_REQUIRE_STREQ(buf, MAGICSTR); 451 452 rump_sys_close(fd); 453 if (rump_sys_unmount("/mp", 0) == -1) 454 atf_tc_fail_errno("unmount"); 455 } 456 457 ATF_TC(root_chrdev); 458 ATF_TC_HEAD(root_chrdev, tc) 459 { 460 461 atf_tc_set_md_var(tc, "descr", "root is /dev/null"); 462 } 463 464 ATF_TC_BODY(root_chrdev, tc) 465 { 466 char *myopts[] = { 467 "-r", "chr 2 0", 468 "dtfs", 469 }; 470 ssize_t rv; 471 char buf[512]; 472 int fd; 473 474 dtfsmountv("/mp", myopts); 475 fd = rump_sys_open("/mp", O_RDWR); 476 if (fd == -1) 477 atf_tc_fail_errno("open null"); 478 479 rv = rump_sys_write(fd, buf, sizeof(buf)); 480 ATF_REQUIRE(rv == sizeof(buf)); 481 482 rv = rump_sys_read(fd, buf, sizeof(buf)); 483 ATF_REQUIRE(rv == 0); 484 485 rump_sys_close(fd); 486 if (rump_sys_unmount("/mp", 0) == -1) 487 atf_tc_fail_errno("unmount"); 488 } 489 490 /* 491 * Inactive/reclaim tests 492 */ 493 494 ATF_TC(inactive_basic); 495 ATF_TC_HEAD(inactive_basic, tc) 496 { 497 498 atf_tc_set_md_var(tc, "descr", "inactive gets called"); 499 } 500 501 ATF_TC_BODY(inactive_basic, tc) 502 { 503 char *myopts[] = { 504 "-i", 505 "dtfs", 506 }; 507 int fd; 508 509 dtfsmountv("/mp", myopts); 510 fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777); 511 if (fd == -1) 512 atf_tc_fail_errno("create"); 513 514 /* one for /mp */ 515 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 516 517 rump_sys_close(fd); 518 519 /* one for /mp/file */ 520 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 2); 521 522 if (rump_sys_unmount("/mp", 0) == -1) 523 atf_tc_fail_errno("unmount"); 524 525 /* one for /mp again */ 526 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 3); 527 } 528 529 ATF_TC(inactive_reclaim); 530 ATF_TC_HEAD(inactive_reclaim, tc) 531 { 532 533 atf_tc_set_md_var(tc, "descr", "inactive/reclaim gets called"); 534 } 535 536 ATF_TC_BODY(inactive_reclaim, tc) 537 { 538 char *myopts[] = { 539 "-i", 540 "dtfs", 541 }; 542 int fd; 543 544 dtfsmountv("/mp", myopts); 545 fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777); 546 if (fd == -1) 547 atf_tc_fail_errno("create"); 548 549 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 1); 550 551 if (rump_sys_unlink("/mp/file") == -1) 552 atf_tc_fail_errno("remove"); 553 554 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 2); 555 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 556 557 rump_sys_close(fd); 558 syncbar("/mp"); 559 560 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], 4); 561 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 562 563 if (rump_sys_unmount("/mp", 0) == -1) 564 atf_tc_fail_errno("unmount"); 565 } 566 567 ATF_TC(reclaim_hardlink); 568 ATF_TC_HEAD(reclaim_hardlink, tc) 569 { 570 571 atf_tc_set_md_var(tc, "descr", "reclaim gets called only after " 572 "final link is gone"); 573 } 574 575 ATF_TC_BODY(reclaim_hardlink, tc) 576 { 577 char *myopts[] = { 578 "-i", 579 "dtfs", 580 }; 581 int fd; 582 int ianow; 583 584 dtfsmountv("/mp", myopts); 585 fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777); 586 if (fd == -1) 587 atf_tc_fail_errno("create"); 588 589 if (rump_sys_link("/mp/file", "/mp/anotherfile") == -1) 590 atf_tc_fail_errno("create link"); 591 rump_sys_close(fd); 592 593 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 594 595 /* unlink first hardlink */ 596 if (rump_sys_unlink("/mp/file") == -1) 597 atf_tc_fail_errno("unlink 1"); 598 599 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 600 ianow = vn_toserv_ops[PUFFS_VN_INACTIVE]; 601 602 /* unlink second hardlink */ 603 if (rump_sys_unlink("/mp/anotherfile") == -1) 604 atf_tc_fail_errno("unlink 2"); 605 606 syncbar("/mp"); 607 608 ATF_REQUIRE(ianow < vn_toserv_ops[PUFFS_VN_INACTIVE]); 609 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 610 611 if (rump_sys_unmount("/mp", 0) == -1) 612 atf_tc_fail_errno("unmount"); 613 } 614 615 ATF_TC(unlink_accessible); 616 ATF_TC_HEAD(unlink_accessible, tc) 617 { 618 619 atf_tc_set_md_var(tc, "descr", "open file is accessible after " 620 "having been unlinked"); 621 } 622 623 ATF_TC_BODY(unlink_accessible, tc) 624 { 625 char *myopts[] = { 626 "-i", 627 "-o","nopagecache", 628 "dtfs", 629 }; 630 char buf[512]; 631 int fd, ianow; 632 633 assert(sizeof(buf) > sizeof(MAGICSTR)); 634 635 dtfsmountv("/mp", myopts); 636 fd = rump_sys_open("/mp/file", O_CREAT | O_RDWR, 0777); 637 if (fd == -1) 638 atf_tc_fail_errno("create"); 639 640 if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR)) 641 atf_tc_fail_errno("write"); 642 if (rump_sys_unlink("/mp/file") == -1) 643 atf_tc_fail_errno("unlink"); 644 645 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 0); 646 ianow = vn_toserv_ops[PUFFS_VN_INACTIVE]; 647 648 if (rump_sys_pread(fd, buf, sizeof(buf), 0) == -1) 649 atf_tc_fail_errno("read"); 650 rump_sys_close(fd); 651 652 syncbar("/mp"); 653 654 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_RECLAIM], 1); 655 ATF_REQUIRE_EQ(vn_toserv_ops[PUFFS_VN_INACTIVE], ianow+2); 656 657 ATF_REQUIRE_STREQ(buf, MAGICSTR); 658 659 if (rump_sys_unmount("/mp", 0) == -1) 660 atf_tc_fail_errno("unmount"); 661 } 662 663 ATF_TP_ADD_TCS(tp) 664 { 665 666 ATF_TP_ADD_TC(tp, mount); 667 668 ATF_TP_ADD_TC(tp, root_fifo); 669 ATF_TP_ADD_TC(tp, root_lnk); 670 ATF_TP_ADD_TC(tp, root_reg); 671 ATF_TP_ADD_TC(tp, root_chrdev); 672 673 ATF_TP_ADD_TC(tp, inactive_basic); 674 ATF_TP_ADD_TC(tp, inactive_reclaim); 675 ATF_TP_ADD_TC(tp, reclaim_hardlink); 676 ATF_TP_ADD_TC(tp, unlink_accessible); 677 678 return atf_no_error(); 679 } 680