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