Home | History | Annotate | Line # | Download | only in libpuffs
puffs.c revision 1.24
      1 /*	$NetBSD: puffs.c,v 1.24 2007/01/15 00:39:02 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005, 2006  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Development of this software was supported by the
      7  * Google Summer of Code program and the Ulla Tuominen Foundation.
      8  * The Google SoC project was mentored by Bill Studenmund.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. The name of the company nor the name of the author may be used to
     19  *    endorse or promote products derived from this software without specific
     20  *    prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     23  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     25  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #if !defined(lint)
     37 __RCSID("$NetBSD: puffs.c,v 1.24 2007/01/15 00:39:02 pooka Exp $");
     38 #endif /* !lint */
     39 
     40 #include <sys/param.h>
     41 #include <sys/mount.h>
     42 
     43 #include <assert.h>
     44 #include <err.h>
     45 #include <errno.h>
     46 #include <fcntl.h>
     47 #include <mntopts.h>
     48 #include <puffs.h>
     49 #include <puffsdump.h>
     50 #include <stdarg.h>
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 #include <string.h>
     54 #include <syslog.h>
     55 #include <unistd.h>
     56 
     57 #include "puffs_priv.h"
     58 
     59 /* Most file systems want this for opts, so just give it to them */
     60 const struct mntopt puffsmopts[] = {
     61 	MOPT_STDOPTS,
     62 	PUFFSMOPT_STD,
     63 	MOPT_NULL,
     64 };
     65 
     66 #define FILLOP(lower, upper)						\
     67 do {									\
     68 	if (pops->puffs_node_##lower)					\
     69 		opmask[PUFFS_VN_##upper] = 1;				\
     70 } while (/*CONSTCOND*/0)
     71 static void
     72 fillvnopmask(struct puffs_ops *pops, uint8_t *opmask)
     73 {
     74 
     75 	memset(opmask, 0, PUFFS_VN_MAX);
     76 
     77 	FILLOP(create,   CREATE);
     78 	FILLOP(mknod,    MKNOD);
     79 	FILLOP(open,     OPEN);
     80 	FILLOP(close,    CLOSE);
     81 	FILLOP(access,   ACCESS);
     82 	FILLOP(getattr,  GETATTR);
     83 	FILLOP(setattr,  SETATTR);
     84 	FILLOP(poll,     POLL); /* XXX: not ready in kernel */
     85 	FILLOP(revoke,   REVOKE);
     86 	FILLOP(mmap,     MMAP);
     87 	FILLOP(fsync,    FSYNC);
     88 	FILLOP(seek,     SEEK);
     89 	FILLOP(remove,   REMOVE);
     90 	FILLOP(link,     LINK);
     91 	FILLOP(rename,   RENAME);
     92 	FILLOP(mkdir,    MKDIR);
     93 	FILLOP(rmdir,    RMDIR);
     94 	FILLOP(symlink,  SYMLINK);
     95 	FILLOP(readdir,  READDIR);
     96 	FILLOP(readlink, READLINK);
     97 	FILLOP(reclaim,  RECLAIM);
     98 	FILLOP(inactive, INACTIVE);
     99 	FILLOP(print,    PRINT);
    100 	FILLOP(read,     READ);
    101 	FILLOP(write,    WRITE);
    102 
    103 	/* XXX: not implemented in the kernel */
    104 	FILLOP(getextattr, GETEXTATTR);
    105 	FILLOP(setextattr, SETEXTATTR);
    106 	FILLOP(listextattr, LISTEXTATTR);
    107 }
    108 #undef FILLOP
    109 
    110 int
    111 puffs_getselectable(struct puffs_usermount *pu)
    112 {
    113 
    114 	return pu->pu_fd;
    115 }
    116 
    117 int
    118 puffs_setblockingmode(struct puffs_usermount *pu, int mode)
    119 {
    120 	int x;
    121 
    122 	x = mode;
    123 	return ioctl(pu->pu_fd, FIONBIO, &x);
    124 }
    125 
    126 int
    127 puffs_getstate(struct puffs_usermount *pu)
    128 {
    129 
    130 	return pu->pu_state;
    131 }
    132 
    133 void
    134 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
    135 {
    136 
    137 	pu->pu_cc_stacksize = ss;
    138 }
    139 
    140 struct puffs_pathobj *
    141 puffs_getrootpathobj(struct puffs_usermount *pu)
    142 {
    143 	struct puffs_node *pnr;
    144 
    145 	pnr = pu->pu_pn_root;
    146 	if (pnr == NULL) {
    147 		errno = ENOENT;
    148 		return NULL;
    149 	}
    150 
    151 	return &pnr->pn_po;
    152 }
    153 
    154 
    155 void
    156 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
    157 {
    158 
    159 	pu->pu_pathbuild = fn;
    160 }
    161 
    162 void
    163 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
    164 {
    165 
    166 	pu->pu_pathtransform = fn;
    167 }
    168 
    169 void
    170 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
    171 {
    172 
    173 	pu->pu_pathcmp = fn;
    174 }
    175 
    176 void
    177 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
    178 {
    179 
    180 	pu->pu_pathfree = fn;
    181 }
    182 
    183 void
    184 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
    185 {
    186 
    187 	pu->pu_namemod = fn;
    188 }
    189 
    190 enum {PUFFCALL_ANSWER, PUFFCALL_IGNORE, PUFFCALL_AGAIN};
    191 
    192 struct puffs_usermount *
    193 _puffs_mount(int develv, struct puffs_ops *pops, const char *dir, int mntflags,
    194 	const char *puffsname, void *priv, uint32_t pflags, size_t maxreqlen)
    195 {
    196 	struct puffs_args pargs;
    197 	struct puffs_usermount *pu;
    198 	int fd = 0;
    199 
    200 	if (develv != PUFFS_DEVEL_LIBVERSION) {
    201 		warnx("puffs_mount: mounting with lib version %d, need %d",
    202 		    develv, PUFFS_DEVEL_LIBVERSION);
    203 		errno = EINVAL;
    204 		return NULL;
    205 	}
    206 
    207 	fd = open("/dev/puffs", O_RDONLY);
    208 	if (fd == -1)
    209 		return NULL;
    210 	if (fd <= 2)
    211 		warnx("puffs_mount: device fd %d (<= 2), sure this is "
    212 		    "what you want?", fd);
    213 
    214 	pargs.pa_vers = PUFFSDEVELVERS | PUFFSVERSION;
    215 	pargs.pa_flags = PUFFS_FLAG_KERN(pflags);
    216 	pargs.pa_fd = fd;
    217 	pargs.pa_maxreqlen = maxreqlen;
    218 	fillvnopmask(pops, pargs.pa_vnopmask);
    219 	(void)strlcpy(pargs.pa_name, puffsname, sizeof(pargs.pa_name));
    220 
    221 	pu = malloc(sizeof(struct puffs_usermount));
    222 	if (!pu)
    223 		return NULL;
    224 
    225 	pu->pu_flags = pflags;
    226 	pu->pu_ops = *pops;
    227 	free(pops); /* XXX */
    228 	pu->pu_fd = fd;
    229 	pu->pu_privdata = priv;
    230 	pu->pu_cc_stacksize = PUFFS_CC_STACKSIZE_DEFAULT;
    231 	LIST_INIT(&pu->pu_pnodelst);
    232 
    233 	/* defaults for some user-settable translation functions */
    234 	pu->pu_cmap = NULL; /* identity translation */
    235 
    236 	pu->pu_pathbuild = puffs_path_buildpath;
    237 	pu->pu_pathfree = puffs_path_freepath;
    238 	pu->pu_pathcmp = puffs_path_cmppath;
    239 	pu->pu_pathtransform = NULL;
    240 	pu->pu_namemod = NULL;
    241 
    242 	pu->pu_state = PUFFS_STATE_MOUNTING;
    243 	if (mount(MOUNT_PUFFS, dir, mntflags, &pargs) == -1)
    244 		goto failfree;
    245 	pu->pu_maxreqlen = pargs.pa_maxreqlen;
    246 
    247 	return pu;
    248 
    249  failfree:
    250 	/* can't unmount() from here for obvious reasons */
    251 	if (fd)
    252 		close(fd);
    253 	free(pu);
    254 	return NULL;
    255 }
    256 
    257 int
    258 puffs_start(struct puffs_usermount *pu, void *rootcookie, struct statvfs *sbp)
    259 {
    260 	struct puffs_startreq sreq;
    261 
    262 	memset(&sreq, 0, sizeof(struct puffs_startreq));
    263 	sreq.psr_cookie = rootcookie;
    264 	sreq.psr_sb = *sbp;
    265 
    266 	/* tell kernel we're flying */
    267 	if (ioctl(pu->pu_fd, PUFFSSTARTOP, &sreq) == -1)
    268 		return -1;
    269 
    270 	pu->pu_state = PUFFS_STATE_RUNNING;
    271 
    272 	return 0;
    273 }
    274 
    275 /*
    276  * XXX: there's currently no clean way to request unmount from
    277  * within the user server, so be very brutal about it.
    278  */
    279 /*ARGSUSED*/
    280 int
    281 puffs_exit(struct puffs_usermount *pu, int force)
    282 {
    283 	struct puffs_node *pn, *pn_next;
    284 
    285 	force = 1; /* currently */
    286 
    287 	if (pu->pu_fd)
    288 		close(pu->pu_fd);
    289 
    290 	pn = LIST_FIRST(&pu->pu_pnodelst);
    291 	while (pn) {
    292 		pn_next = LIST_NEXT(pn, pn_entries);
    293 		puffs_pn_put(pn);
    294 		pn = pn_next;
    295 	}
    296 	free(pu);
    297 
    298 	return 0; /* always succesful for now, WILL CHANGE */
    299 }
    300 
    301 int
    302 puffs_mainloop(struct puffs_usermount *pu, int flags)
    303 {
    304 	struct puffs_getreq *pgr;
    305 	struct puffs_putreq *ppr;
    306 	int rv;
    307 
    308 	rv = -1;
    309 	pgr = puffs_makegetreq(pu, pu->pu_maxreqlen, 0);
    310 	if (pgr == NULL)
    311 		return -1;
    312 
    313 	ppr = puffs_makeputreq(pu);
    314 	if (ppr == NULL) {
    315 		puffs_destroygetreq(pgr);
    316 		return -1;
    317 	}
    318 
    319 	if ((flags & PUFFSLOOP_NODAEMON) == 0)
    320 		if (daemon(1, 0) == -1)
    321 			goto out;
    322 
    323 	/* XXX: should be a bit more robust with errors here */
    324 	while (puffs_getstate(pu) == PUFFS_STATE_RUNNING
    325 	    || puffs_getstate(pu) == PUFFS_STATE_UNMOUNTING) {
    326 		puffs_resetputreq(ppr);
    327 
    328 		if (puffs_handlereqs(pu, pgr, ppr, 0) == -1) {
    329 			rv = -1;
    330 			break;
    331 		}
    332 		if (puffs_putputreq(ppr) == -1) {
    333 			rv = -1;
    334 			break;
    335 		}
    336 	}
    337 
    338  out:
    339 	puffs_destroyputreq(ppr);
    340 	puffs_destroygetreq(pgr);
    341 	return rv;
    342 }
    343 
    344 int
    345 puffs_handlereqs(struct puffs_usermount *pu, struct puffs_getreq *pgr,
    346 	struct puffs_putreq *ppr, int maxops)
    347 {
    348 	struct puffs_req *preq;
    349 	int pval;
    350 
    351 	puffs_setmaxgetreq(pgr, maxops);
    352 	if (puffs_loadgetreq(pgr) == -1)
    353 		return -1;
    354 
    355 	/* interlink pgr and ppr for diagnostic asserts */
    356 	pgr->pgr_nppr++;
    357 	ppr->ppr_pgr = pgr;
    358 
    359 	pval = 0;
    360 	while ((preq = puffs_getreq(pgr)) != NULL
    361 	    && pu->pu_state != PUFFS_STATE_UNMOUNTED)
    362 		pval = puffs_dopreq(pu, ppr, preq);
    363 
    364 	return pval;
    365 }
    366 
    367 int
    368 puffs_dopreq(struct puffs_usermount *pu, struct puffs_putreq *ppr,
    369 	struct puffs_req *preq)
    370 {
    371 	struct puffs_cc *pcc;
    372 	int rv;
    373 
    374 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    375 		puffsdump_req(preq);
    376 
    377 	pcc = puffs_cc_create(pu);
    378 
    379 	/* XXX: temporary kludging */
    380 	pcc->pcc_preq = malloc(preq->preq_buflen);
    381 	if (pcc->pcc_preq == NULL)
    382 		return -1;
    383 	(void) memcpy(pcc->pcc_preq, preq, preq->preq_buflen);
    384 
    385 	rv = puffs_docc(ppr, pcc);
    386 
    387 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    388 		return 0;
    389 
    390 	return rv;
    391 }
    392 
    393 int
    394 puffs_docc(struct puffs_putreq *ppr, struct puffs_cc *pcc)
    395 {
    396 	int rv;
    397 
    398 	assert((pcc->pcc_flags & PCC_DONE) == 0);
    399 
    400 	puffs_cc_continue(pcc);
    401 	rv = pcc->pcc_rv;
    402 
    403 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    404 		rv = PUFFCALL_AGAIN;
    405 
    406 	/* check if we need to store this reply */
    407 	switch (rv) {
    408 	case PUFFCALL_ANSWER:
    409 		puffs_putreq_cc(ppr, pcc);
    410 		break;
    411 	case PUFFCALL_IGNORE:
    412 		puffs_cc_destroy(pcc);
    413 		break;
    414 	case PUFFCALL_AGAIN:
    415 		break;
    416 	default:
    417 		assert(/*CONSTCOND*/0);
    418 	}
    419 
    420 	return 0;
    421 }
    422 
    423 /* library private, but linked from callcontext.c */
    424 
    425 void
    426 puffs_calldispatcher(struct puffs_cc *pcc)
    427 {
    428 	struct puffs_usermount *pu = pcc->pcc_pu;
    429 	struct puffs_ops *pops = &pu->pu_ops;
    430 	struct puffs_req *preq = pcc->pcc_preq;
    431 	void *auxbuf = preq; /* help with typecasting */
    432 	int error, rv, buildpath;
    433 
    434 	assert(pcc->pcc_flags & (PCC_ONCE | PCC_REALCC));
    435 
    436 	if (PUFFSOP_WANTREPLY(preq->preq_opclass))
    437 		rv = PUFFCALL_ANSWER;
    438 	else
    439 		rv = PUFFCALL_IGNORE;
    440 
    441 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
    442 
    443 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
    444 		switch (preq->preq_optype) {
    445 		case PUFFS_VFS_UNMOUNT:
    446 		{
    447 			struct puffs_vfsreq_unmount *auxt = auxbuf;
    448 
    449 			pu->pu_state = PUFFS_STATE_UNMOUNTING;
    450 			error = pops->puffs_fs_unmount(pcc,
    451 			    auxt->pvfsr_flags, auxt->pvfsr_pid);
    452 			if (!error)
    453 				pu->pu_state = PUFFS_STATE_UNMOUNTED;
    454 			else
    455 				pu->pu_state = PUFFS_STATE_RUNNING;
    456 			break;
    457 		}
    458 		case PUFFS_VFS_STATVFS:
    459 		{
    460 			struct puffs_vfsreq_statvfs *auxt = auxbuf;
    461 
    462 			error = pops->puffs_fs_statvfs(pcc,
    463 			    &auxt->pvfsr_sb, auxt->pvfsr_pid);
    464 			break;
    465 		}
    466 		case PUFFS_VFS_SYNC:
    467 		{
    468 			struct puffs_vfsreq_sync *auxt = auxbuf;
    469 
    470 			error = pops->puffs_fs_sync(pcc,
    471 			    auxt->pvfsr_waitfor, &auxt->pvfsr_cred,
    472 			    auxt->pvfsr_pid);
    473 			break;
    474 		}
    475 		default:
    476 			/*
    477 			 * I guess the kernel sees this one coming
    478 			 */
    479 			error = EINVAL;
    480 			break;
    481 		}
    482 
    483 	/* XXX: audit return values */
    484 	/* XXX: sync with kernel */
    485 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
    486 		switch (preq->preq_optype) {
    487 		case PUFFS_VN_LOOKUP:
    488 		{
    489 			struct puffs_vnreq_lookup *auxt = auxbuf;
    490 			struct puffs_cn pcn;
    491 
    492 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    493 			if (buildpath) {
    494 				if (pcn.pcn_flags & PUFFS_ISDOTDOT) {
    495 					buildpath = 0;
    496 				} else {
    497 					error = puffs_path_pcnbuild(pu, &pcn,
    498 					    preq->preq_cookie);
    499 					if (error)
    500 						break;
    501 				}
    502 			}
    503 
    504 			/* lookup *must* be present */
    505 			error = pops->puffs_node_lookup(pcc, preq->preq_cookie,
    506 			    &auxt->pvnr_newnode, &auxt->pvnr_vtype,
    507 			    &auxt->pvnr_size, &auxt->pvnr_rdev, &pcn);
    508 
    509 			if (buildpath) {
    510 				if (error) {
    511 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    512 				} else {
    513 					struct puffs_node *pn;
    514 
    515 					/*
    516 					 * did we get a new node or a
    517 					 * recycled node?
    518 					 * XXX: mapping assumption
    519 					 */
    520 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    521 					if (pn->pn_po.po_path == NULL)
    522 						pn->pn_po = pcn.pcn_po_full;
    523 					else
    524 						pu->pu_pathfree(pu,
    525 						    &pcn.pcn_po_full);
    526 				}
    527 			}
    528 
    529 			break;
    530 		}
    531 
    532 		case PUFFS_VN_CREATE:
    533 		{
    534 			struct puffs_vnreq_create *auxt = auxbuf;
    535 			struct puffs_cn pcn;
    536 			if (pops->puffs_node_create == NULL) {
    537 				error = 0;
    538 				break;
    539 			}
    540 
    541 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    542 			if (buildpath) {
    543 				error = puffs_path_pcnbuild(pu, &pcn,
    544 				    preq->preq_cookie);
    545 				if (error)
    546 					break;
    547 			}
    548 
    549 			error = pops->puffs_node_create(pcc,
    550 			    preq->preq_cookie, &auxt->pvnr_newnode,
    551 			    &pcn, &auxt->pvnr_va);
    552 
    553 			if (buildpath) {
    554 				if (error) {
    555 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    556 				} else {
    557 					struct puffs_node *pn;
    558 
    559 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    560 					pn->pn_po = pcn.pcn_po_full;
    561 				}
    562 			}
    563 
    564 			break;
    565 		}
    566 
    567 		case PUFFS_VN_MKNOD:
    568 		{
    569 			struct puffs_vnreq_mknod *auxt = auxbuf;
    570 			struct puffs_cn pcn;
    571 			if (pops->puffs_node_mknod == NULL) {
    572 				error = 0;
    573 				break;
    574 			}
    575 
    576 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    577 			if (buildpath) {
    578 				error = puffs_path_pcnbuild(pu, &pcn,
    579 				    preq->preq_cookie);
    580 				if (error)
    581 					break;
    582 			}
    583 
    584 			error = pops->puffs_node_mknod(pcc,
    585 			    preq->preq_cookie, &auxt->pvnr_newnode,
    586 			    &pcn, &auxt->pvnr_va);
    587 
    588 			if (buildpath) {
    589 				if (error) {
    590 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    591 				} else {
    592 					struct puffs_node *pn;
    593 
    594 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    595 					pn->pn_po = pcn.pcn_po_full;
    596 				}
    597 			}
    598 
    599 			break;
    600 		}
    601 
    602 		case PUFFS_VN_OPEN:
    603 		{
    604 			struct puffs_vnreq_open *auxt = auxbuf;
    605 			if (pops->puffs_node_open == NULL) {
    606 				error = 0;
    607 				break;
    608 			}
    609 
    610 			error = pops->puffs_node_open(pcc,
    611 			    preq->preq_cookie, auxt->pvnr_mode,
    612 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    613 			break;
    614 		}
    615 
    616 		case PUFFS_VN_CLOSE:
    617 		{
    618 			struct puffs_vnreq_close *auxt = auxbuf;
    619 			if (pops->puffs_node_close == NULL) {
    620 				error = 0;
    621 				break;
    622 			}
    623 
    624 			error = pops->puffs_node_close(pcc,
    625 			    preq->preq_cookie, auxt->pvnr_fflag,
    626 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    627 			break;
    628 		}
    629 
    630 		case PUFFS_VN_ACCESS:
    631 		{
    632 			struct puffs_vnreq_access *auxt = auxbuf;
    633 			if (pops->puffs_node_access == NULL) {
    634 				error = 0;
    635 				break;
    636 			}
    637 
    638 			error = pops->puffs_node_access(pcc,
    639 			    preq->preq_cookie, auxt->pvnr_mode,
    640 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    641 			break;
    642 		}
    643 
    644 		case PUFFS_VN_GETATTR:
    645 		{
    646 			struct puffs_vnreq_getattr *auxt = auxbuf;
    647 			if (pops->puffs_node_getattr == NULL) {
    648 				error = EOPNOTSUPP;
    649 				break;
    650 			}
    651 
    652 			error = pops->puffs_node_getattr(pcc,
    653 			    preq->preq_cookie, &auxt->pvnr_va,
    654 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    655 			break;
    656 		}
    657 
    658 		case PUFFS_VN_SETATTR:
    659 		{
    660 			struct puffs_vnreq_setattr *auxt = auxbuf;
    661 			if (pops->puffs_node_setattr == NULL) {
    662 				error = EOPNOTSUPP;
    663 				break;
    664 			}
    665 
    666 			error = pops->puffs_node_setattr(pcc,
    667 			    preq->preq_cookie, &auxt->pvnr_va,
    668 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    669 			break;
    670 		}
    671 
    672 		case PUFFS_VN_MMAP:
    673 		{
    674 			struct puffs_vnreq_mmap *auxt = auxbuf;
    675 			if (pops->puffs_node_mmap == NULL) {
    676 				error = 0;
    677 				break;
    678 			}
    679 
    680 			error = pops->puffs_node_mmap(pcc,
    681 			    preq->preq_cookie, auxt->pvnr_fflags,
    682 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    683 			break;
    684 		}
    685 
    686 		case PUFFS_VN_REVOKE:
    687 		{
    688 			struct puffs_vnreq_revoke *auxt = auxbuf;
    689 			if (pops->puffs_node_revoke == NULL) {
    690 				error = 0;
    691 				break;
    692 			}
    693 
    694 			error = pops->puffs_node_revoke(pcc,
    695 			    preq->preq_cookie, auxt->pvnr_flags);
    696 			break;
    697 		}
    698 
    699 		case PUFFS_VN_FSYNC:
    700 		{
    701 			struct puffs_vnreq_fsync *auxt = auxbuf;
    702 			if (pops->puffs_node_fsync == NULL) {
    703 				error = 0;
    704 				break;
    705 			}
    706 
    707 			error = pops->puffs_node_fsync(pcc,
    708 			    preq->preq_cookie, &auxt->pvnr_cred,
    709 			    auxt->pvnr_flags, auxt->pvnr_offlo,
    710 			    auxt->pvnr_offhi, auxt->pvnr_pid);
    711 			break;
    712 		}
    713 
    714 		case PUFFS_VN_SEEK:
    715 		{
    716 			struct puffs_vnreq_seek *auxt = auxbuf;
    717 			if (pops->puffs_node_seek == NULL) {
    718 				error = 0;
    719 				break;
    720 			}
    721 
    722 			error = pops->puffs_node_seek(pcc,
    723 			    preq->preq_cookie, auxt->pvnr_oldoff,
    724 			    auxt->pvnr_newoff, &auxt->pvnr_cred);
    725 			break;
    726 		}
    727 
    728 		case PUFFS_VN_REMOVE:
    729 		{
    730 			struct puffs_vnreq_remove *auxt = auxbuf;
    731 			struct puffs_cn pcn;
    732 			if (pops->puffs_node_remove == NULL) {
    733 				error = 0;
    734 				break;
    735 			}
    736 
    737 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    738 
    739 			error = pops->puffs_node_remove(pcc,
    740 			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
    741 			break;
    742 		}
    743 
    744 		case PUFFS_VN_LINK:
    745 		{
    746 			struct puffs_vnreq_link *auxt = auxbuf;
    747 			struct puffs_cn pcn;
    748 			if (pops->puffs_node_link == NULL) {
    749 				error = 0;
    750 				break;
    751 			}
    752 
    753 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    754 			if (buildpath) {
    755 				error = puffs_path_pcnbuild(pu, &pcn,
    756 				    preq->preq_cookie);
    757 				if (error)
    758 					break;
    759 			}
    760 
    761 			error = pops->puffs_node_link(pcc,
    762 			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
    763 			if (buildpath)
    764 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
    765 
    766 			break;
    767 		}
    768 
    769 		case PUFFS_VN_RENAME:
    770 		{
    771 			struct puffs_vnreq_rename *auxt = auxbuf;
    772 			struct puffs_cn pcn_src, pcn_targ;
    773 			struct puffs_node *pn_src;
    774 
    775 			if (pops->puffs_node_rename == NULL) {
    776 				error = 0;
    777 				break;
    778 			}
    779 
    780 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
    781 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
    782 			if (buildpath) {
    783 				pn_src = auxt->pvnr_cookie_src;
    784 				pcn_src.pcn_po_full = pn_src->pn_po;
    785 
    786 				error = puffs_path_pcnbuild(pu, &pcn_targ,
    787 				    auxt->pvnr_cookie_targdir);
    788 				if (error)
    789 					break;
    790 			}
    791 
    792 			error = pops->puffs_node_rename(pcc,
    793 			    preq->preq_cookie, auxt->pvnr_cookie_src,
    794 			    &pcn_src, auxt->pvnr_cookie_targdir,
    795 			    auxt->pvnr_cookie_targ, &pcn_targ);
    796 
    797 			if (buildpath) {
    798 				if (error) {
    799 					pu->pu_pathfree(pu,
    800 					    &pcn_targ.pcn_po_full);
    801 				} else {
    802 					struct puffs_pathinfo pi;
    803 					struct puffs_pathobj po_old;
    804 
    805 					/* handle this node */
    806 					po_old = pn_src->pn_po;
    807 					pn_src->pn_po = pcn_targ.pcn_po_full;
    808 
    809 					if (pn_src->pn_va.va_type != VDIR) {
    810 						pu->pu_pathfree(pu, &po_old);
    811 						break;
    812 					}
    813 
    814 					/* handle all child nodes for DIRs */
    815 					pi.pi_old = &pcn_src.pcn_po_full;
    816 					pi.pi_new = &pcn_targ.pcn_po_full;
    817 
    818 					if (puffs_pn_nodewalk(pu,
    819 					    puffs_path_prefixadj, &pi) != NULL)
    820 						error = ENOMEM;
    821 					pu->pu_pathfree(pu, &po_old);
    822 				}
    823 			}
    824 			break;
    825 		}
    826 
    827 		case PUFFS_VN_MKDIR:
    828 		{
    829 			struct puffs_vnreq_mkdir *auxt = auxbuf;
    830 			struct puffs_cn pcn;
    831 			if (pops->puffs_node_mkdir == NULL) {
    832 				error = 0;
    833 				break;
    834 			}
    835 
    836 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    837 			if (buildpath) {
    838 				error = puffs_path_pcnbuild(pu, &pcn,
    839 				    preq->preq_cookie);
    840 				if (error)
    841 					break;
    842 			}
    843 
    844 			error = pops->puffs_node_mkdir(pcc,
    845 			    preq->preq_cookie, &auxt->pvnr_newnode,
    846 			    &pcn, &auxt->pvnr_va);
    847 
    848 			if (buildpath) {
    849 				if (error) {
    850 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    851 				} else {
    852 					struct puffs_node *pn;
    853 
    854 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    855 					pn->pn_po = pcn.pcn_po_full;
    856 				}
    857 			}
    858 
    859 			break;
    860 		}
    861 
    862 		case PUFFS_VN_RMDIR:
    863 		{
    864 			struct puffs_vnreq_rmdir *auxt = auxbuf;
    865 			struct puffs_cn pcn;
    866 			if (pops->puffs_node_rmdir == NULL) {
    867 				error = 0;
    868 				break;
    869 			}
    870 
    871 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    872 
    873 			error = pops->puffs_node_rmdir(pcc,
    874 			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
    875 			break;
    876 		}
    877 
    878 		case PUFFS_VN_SYMLINK:
    879 		{
    880 			struct puffs_vnreq_symlink *auxt = auxbuf;
    881 			struct puffs_cn pcn;
    882 			if (pops->puffs_node_symlink == NULL) {
    883 				error = 0;
    884 				break;
    885 			}
    886 
    887 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    888 			if (buildpath) {
    889 				error = puffs_path_pcnbuild(pu, &pcn,
    890 				    preq->preq_cookie);
    891 				if (error)
    892 					break;
    893 			}
    894 
    895 			error = pops->puffs_node_symlink(pcc,
    896 			    preq->preq_cookie, &auxt->pvnr_newnode,
    897 			    &pcn, &auxt->pvnr_va, auxt->pvnr_link);
    898 
    899 			if (buildpath) {
    900 				if (error) {
    901 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    902 				} else {
    903 					struct puffs_node *pn;
    904 
    905 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    906 					pn->pn_po = pcn.pcn_po_full;
    907 				}
    908 			}
    909 
    910 			break;
    911 		}
    912 
    913 		case PUFFS_VN_READDIR:
    914 		{
    915 			struct puffs_vnreq_readdir *auxt = auxbuf;
    916 			size_t res;
    917 
    918 			if (pops->puffs_node_readdir == NULL) {
    919 				error = 0;
    920 				break;
    921 			}
    922 
    923 			res = auxt->pvnr_resid;
    924 			error = pops->puffs_node_readdir(pcc,
    925 			    preq->preq_cookie, auxt->pvnr_dent,
    926 			    &auxt->pvnr_cred, &auxt->pvnr_offset,
    927 			    &auxt->pvnr_resid);
    928 
    929 			/* need to move a bit more */
    930 			preq->preq_buflen = sizeof(struct puffs_vnreq_readdir)
    931 			    + (res - auxt->pvnr_resid);
    932 			break;
    933 		}
    934 
    935 		case PUFFS_VN_READLINK:
    936 		{
    937 			struct puffs_vnreq_readlink *auxt = auxbuf;
    938 			if (pops->puffs_node_readlink == NULL) {
    939 				error = EOPNOTSUPP;
    940 				break;
    941 			}
    942 
    943 			error = pops->puffs_node_readlink(pcc,
    944 			    preq->preq_cookie, &auxt->pvnr_cred,
    945 			    auxt->pvnr_link, &auxt->pvnr_linklen);
    946 			break;
    947 		}
    948 
    949 		case PUFFS_VN_RECLAIM:
    950 		{
    951 			struct puffs_vnreq_reclaim *auxt = auxbuf;
    952 			if (pops->puffs_node_reclaim == NULL) {
    953 				error = 0;
    954 				break;
    955 			}
    956 
    957 			error = pops->puffs_node_reclaim(pcc,
    958 			    preq->preq_cookie, auxt->pvnr_pid);
    959 			break;
    960 		}
    961 
    962 		case PUFFS_VN_INACTIVE:
    963 		{
    964 			struct puffs_vnreq_inactive *auxt = auxbuf;
    965 			if (pops->puffs_node_inactive == NULL) {
    966 				error = EOPNOTSUPP;
    967 				break;
    968 			}
    969 
    970 			error = pops->puffs_node_inactive(pcc,
    971 			    preq->preq_cookie, auxt->pvnr_pid,
    972 			    &auxt->pvnr_backendrefs);
    973 			break;
    974 		}
    975 
    976 		case PUFFS_VN_PATHCONF:
    977 		{
    978 			struct puffs_vnreq_pathconf *auxt = auxbuf;
    979 			if (pops->puffs_node_pathconf == NULL) {
    980 				error = 0;
    981 				break;
    982 			}
    983 
    984 			error = pops->puffs_node_pathconf(pcc,
    985 			    preq->preq_cookie, auxt->pvnr_name,
    986 			    &auxt->pvnr_retval);
    987 			break;
    988 		}
    989 
    990 		case PUFFS_VN_ADVLOCK:
    991 		{
    992 			struct puffs_vnreq_advlock *auxt = auxbuf;
    993 			if (pops->puffs_node_advlock == NULL) {
    994 				error = 0;
    995 				break;
    996 			}
    997 
    998 			error = pops->puffs_node_advlock(pcc,
    999 			    preq->preq_cookie, auxt->pvnr_id, auxt->pvnr_op,
   1000 			    &auxt->pvnr_fl, auxt->pvnr_flags);
   1001 			break;
   1002 		}
   1003 
   1004 		case PUFFS_VN_PRINT:
   1005 		{
   1006 			if (pops->puffs_node_print == NULL) {
   1007 				error = 0;
   1008 				break;
   1009 			}
   1010 
   1011 			error = pops->puffs_node_print(pcc,
   1012 			    preq->preq_cookie);
   1013 			break;
   1014 		}
   1015 
   1016 		case PUFFS_VN_READ:
   1017 		{
   1018 			struct puffs_vnreq_read *auxt = auxbuf;
   1019 			size_t res;
   1020 
   1021 			if (pops->puffs_node_read == NULL) {
   1022 				error = EIO;
   1023 				break;
   1024 			}
   1025 
   1026 			res = auxt->pvnr_resid;
   1027 			error = pops->puffs_node_read(pcc,
   1028 			    preq->preq_cookie, auxt->pvnr_data,
   1029 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1030 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1031 
   1032 			/* need to move a bit more */
   1033 			preq->preq_buflen = sizeof(struct puffs_vnreq_read)
   1034 			    + (res - auxt->pvnr_resid);
   1035 			break;
   1036 		}
   1037 
   1038 		case PUFFS_VN_WRITE:
   1039 		{
   1040 			struct puffs_vnreq_write *auxt = auxbuf;
   1041 
   1042 			if (pops->puffs_node_write == NULL) {
   1043 				error = EIO;
   1044 				break;
   1045 			}
   1046 
   1047 			error = pops->puffs_node_write(pcc,
   1048 			    preq->preq_cookie, auxt->pvnr_data,
   1049 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1050 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1051 
   1052 			/* don't need to move data back to the kernel */
   1053 			preq->preq_buflen = sizeof(struct puffs_vnreq_write);
   1054 			break;
   1055 		}
   1056 
   1057 /* holy bitrot, ryydman! */
   1058 #if 0
   1059 		case PUFFS_VN_IOCTL:
   1060 			error = pops->puffs_node_ioctl1(pcc, preq->preq_cookie,
   1061 			     (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1062 			if (error != 0)
   1063 				break;
   1064 			pop.pso_reqid = preq->preq_id;
   1065 
   1066 			/* let the kernel do it's intermediate duty */
   1067 			error = ioctl(pu->pu_fd, PUFFSSIZEOP, &pop);
   1068 			/*
   1069 			 * XXX: I don't actually know what the correct
   1070 			 * thing to do in case of an error is, so I'll
   1071 			 * just ignore it for the time being.
   1072 			 */
   1073 			error = pops->puffs_node_ioctl2(pcc, preq->preq_cookie,
   1074 			    (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1075 			break;
   1076 
   1077 		case PUFFS_VN_FCNTL:
   1078 			error = pops->puffs_node_fcntl1(pcc, preq->preq_cookie,
   1079 			     (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1080 			if (error != 0)
   1081 				break;
   1082 			pop.pso_reqid = preq->preq_id;
   1083 
   1084 			/* let the kernel do it's intermediate duty */
   1085 			error = ioctl(pu->pu_fd, PUFFSSIZEOP, &pop);
   1086 			/*
   1087 			 * XXX: I don't actually know what the correct
   1088 			 * thing to do in case of an error is, so I'll
   1089 			 * just ignore it for the time being.
   1090 			 */
   1091 			error = pops->puffs_node_fcntl2(pcc, preq->preq_cookie,
   1092 			    (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1093 			break;
   1094 #endif
   1095 
   1096 		default:
   1097 			printf("inval op %d\n", preq->preq_optype);
   1098 			error = EINVAL;
   1099 			break;
   1100 		}
   1101 	} else {
   1102 		/*
   1103 		 * this one also
   1104 		 */
   1105 		error = EINVAL;
   1106 	}
   1107 
   1108 	preq->preq_rv = error;
   1109 
   1110 	pcc->pcc_rv = rv;
   1111 	pcc->pcc_flags |= PCC_DONE;
   1112 }
   1113 
   1114 
   1115 #if 0
   1116 		case PUFFS_VN_POLL:
   1117 		{
   1118 			struct puffs_vnreq_poll *auxt = auxbuf;
   1119 			if (pops->puffs_node_poll == NULL) {
   1120 				error = 0;
   1121 				break;
   1122 			}
   1123 
   1124 			error = pops->puffs_node_poll(pcc,
   1125 			    preq->preq_cookie, preq-);
   1126 			break;
   1127 		}
   1128 
   1129 		case PUFFS_VN_KQFILTER:
   1130 		{
   1131 			struct puffs_vnreq_kqfilter *auxt = auxbuf;
   1132 			if (pops->puffs_node_kqfilter == NULL) {
   1133 				error = 0;
   1134 				break;
   1135 			}
   1136 
   1137 			error = pops->puffs_node_kqfilter(pcc,
   1138 			    preq->preq_cookie, );
   1139 			break;
   1140 		}
   1141 
   1142 		case PUFFS_VN_CLOSEEXTATTR:
   1143 		{
   1144 			struct puffs_vnreq_closeextattr *auxt = auxbuf;
   1145 			if (pops->puffs_closeextattr == NULL) {
   1146 				error = 0;
   1147 				break;
   1148 			}
   1149 
   1150 			error = pops->puffs_closeextattr(pcc,
   1151 			    preq->preq_cookie, );
   1152 			break;
   1153 		}
   1154 
   1155 		case PUFFS_VN_GETEXTATTR:
   1156 		{
   1157 			struct puffs_vnreq_getextattr *auxt = auxbuf;
   1158 			if (pops->puffs_getextattr == NULL) {
   1159 				error = 0;
   1160 				break;
   1161 			}
   1162 
   1163 			error = pops->puffs_getextattr(pcc,
   1164 			    preq->preq_cookie, );
   1165 			break;
   1166 		}
   1167 
   1168 		case PUFFS_VN_LISTEXTATTR:
   1169 		{
   1170 			struct puffs_vnreq_listextattr *auxt = auxbuf;
   1171 			if (pops->puffs_listextattr == NULL) {
   1172 				error = 0;
   1173 				break;
   1174 			}
   1175 
   1176 			error = pops->puffs_listextattr(pcc,
   1177 			    preq->preq_cookie, );
   1178 			break;
   1179 		}
   1180 
   1181 		case PUFFS_VN_OPENEXTATTR:
   1182 		{
   1183 			struct puffs_vnreq_openextattr *auxt = auxbuf;
   1184 			if (pops->puffs_openextattr == NULL) {
   1185 				error = 0;
   1186 				break;
   1187 			}
   1188 
   1189 			error = pops->puffs_openextattr(pcc,
   1190 			    preq->preq_cookie, );
   1191 			break;
   1192 		}
   1193 
   1194 		case PUFFS_VN_DELETEEXTATTR:
   1195 		{
   1196 			struct puffs_vnreq_deleteextattr *auxt = auxbuf;
   1197 			if (pops->puffs_deleteextattr == NULL) {
   1198 				error = 0;
   1199 				break;
   1200 			}
   1201 
   1202 			error = pops->puffs_deleteextattr(pcc,
   1203 			    preq->preq_cookie, );
   1204 			break;
   1205 		}
   1206 
   1207 		case PUFFS_VN_SETEXTATTR:
   1208 		{
   1209 			struct puffs_vnreq_setextattr *auxt = auxbuf;
   1210 			if (pops->puffs_setextattr == NULL) {
   1211 				error = 0;
   1212 				break;
   1213 			}
   1214 
   1215 			error = pops->puffs_setextattr(pcc,
   1216 			    preq->preq_cookie, );
   1217 			break;
   1218 		}
   1219 
   1220 #endif
   1221