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