Home | History | Annotate | Line # | Download | only in libpuffs
puffs.c revision 1.40
      1 /*	$NetBSD: puffs.c,v 1.40 2007/05/07 17:16:07 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.40 2007/05/07 17:16:07 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(mmap,     MMAP);
     86 	FILLOP(fsync,    FSYNC);
     87 	FILLOP(seek,     SEEK);
     88 	FILLOP(remove,   REMOVE);
     89 	FILLOP(link,     LINK);
     90 	FILLOP(rename,   RENAME);
     91 	FILLOP(mkdir,    MKDIR);
     92 	FILLOP(rmdir,    RMDIR);
     93 	FILLOP(symlink,  SYMLINK);
     94 	FILLOP(readdir,  READDIR);
     95 	FILLOP(readlink, READLINK);
     96 	FILLOP(reclaim,  RECLAIM);
     97 	FILLOP(inactive, INACTIVE);
     98 	FILLOP(print,    PRINT);
     99 	FILLOP(read,     READ);
    100 	FILLOP(write,    WRITE);
    101 
    102 	/* XXX: not implemented in the kernel */
    103 	FILLOP(getextattr, GETEXTATTR);
    104 	FILLOP(setextattr, SETEXTATTR);
    105 	FILLOP(listextattr, LISTEXTATTR);
    106 }
    107 #undef FILLOP
    108 
    109 int
    110 puffs_getselectable(struct puffs_usermount *pu)
    111 {
    112 
    113 	return pu->pu_kargs.pa_fd;
    114 }
    115 
    116 int
    117 puffs_setblockingmode(struct puffs_usermount *pu, int mode)
    118 {
    119 	int x;
    120 
    121 	x = mode;
    122 	return ioctl(pu->pu_kargs.pa_fd, FIONBIO, &x);
    123 }
    124 
    125 int
    126 puffs_getstate(struct puffs_usermount *pu)
    127 {
    128 
    129 	return pu->pu_state;
    130 }
    131 
    132 void
    133 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
    134 {
    135 
    136 	pu->pu_cc_stacksize = ss;
    137 }
    138 
    139 struct puffs_pathobj *
    140 puffs_getrootpathobj(struct puffs_usermount *pu)
    141 {
    142 	struct puffs_node *pnr;
    143 
    144 	pnr = pu->pu_pn_root;
    145 	if (pnr == NULL) {
    146 		errno = ENOENT;
    147 		return NULL;
    148 	}
    149 
    150 	return &pnr->pn_po;
    151 }
    152 
    153 void
    154 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
    155 {
    156 
    157 	pu->pu_pn_root = pn;
    158 }
    159 
    160 struct puffs_node *
    161 puffs_getroot(struct puffs_usermount *pu)
    162 {
    163 
    164 	return pu->pu_pn_root;
    165 }
    166 
    167 void *
    168 puffs_getspecific(struct puffs_usermount *pu)
    169 {
    170 
    171 	return pu->pu_privdata;
    172 }
    173 
    174 size_t
    175 puffs_getmaxreqlen(struct puffs_usermount *pu)
    176 {
    177 
    178 	return pu->pu_kargs.pa_maxreqlen;
    179 }
    180 
    181 void
    182 puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
    183 {
    184 
    185 	if (pu->pu_state != PUFFS_STATE_BEFOREMOUNT)
    186 		warnx("puffs_setmaxreqlen: call has effect only "
    187 		    "before mount\n");
    188 
    189 	pu->pu_kargs.pa_maxreqlen = reqlen;
    190 }
    191 
    192 void
    193 puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
    194 {
    195 
    196 	if (pu->pu_state != PUFFS_STATE_BEFOREMOUNT)
    197 		warnx("puffs_setfhsize: call has effect only before mount\n");
    198 
    199 	pu->pu_kargs.pa_fhsize = fhsize;
    200 	pu->pu_kargs.pa_fhflags = flags;
    201 }
    202 
    203 void
    204 puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
    205 {
    206 
    207 	if (pu->pu_state != PUFFS_STATE_BEFOREMOUNT)
    208 		warnx("puffs_setfhsize: call has effect only before mount\n");
    209 
    210 	pu->pu_kargs.pa_nhashbuckets = nhash;
    211 }
    212 
    213 void
    214 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
    215 {
    216 
    217 	pu->pu_pathbuild = fn;
    218 }
    219 
    220 void
    221 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
    222 {
    223 
    224 	pu->pu_pathtransform = fn;
    225 }
    226 
    227 void
    228 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
    229 {
    230 
    231 	pu->pu_pathcmp = fn;
    232 }
    233 
    234 void
    235 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
    236 {
    237 
    238 	pu->pu_pathfree = fn;
    239 }
    240 
    241 void
    242 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
    243 {
    244 
    245 	pu->pu_namemod = fn;
    246 }
    247 
    248 void
    249 puffs_setback(struct puffs_cc *pcc, int whatback)
    250 {
    251 	struct puffs_req *preq = pcc->pcc_preq;
    252 
    253 	assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (
    254 	    preq->preq_optype == PUFFS_VN_OPEN ||
    255 	    preq->preq_optype == PUFFS_VN_MMAP ||
    256 	    preq->preq_optype == PUFFS_VN_REMOVE ||
    257 	    preq->preq_optype == PUFFS_VN_RMDIR));
    258 
    259 	preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK;
    260 }
    261 
    262 int
    263 puffs_domount(struct puffs_usermount *pu, const char *dir, int mntflags)
    264 {
    265 
    266 #if 1
    267 	/* XXXkludgehere */
    268 	/* kauth doesn't provide this service any longer */
    269 	if (geteuid() != 0)
    270 		mntflags |= MNT_NOSUID | MNT_NODEV;
    271 #endif
    272 
    273 	if (mount(MOUNT_PUFFS, dir, mntflags, &pu->pu_kargs) == -1)
    274 		return -1;
    275 	pu->pu_state = PUFFS_STATE_MOUNTING;
    276 
    277 	return 0;
    278 }
    279 
    280 struct puffs_usermount *
    281 _puffs_init(int develv, struct puffs_ops *pops, const char *puffsname,
    282 	void *priv, uint32_t pflags)
    283 {
    284 	struct puffs_usermount *pu;
    285 	struct puffs_kargs *pargs;
    286 	int fd;
    287 
    288 	if (develv != PUFFS_DEVEL_LIBVERSION) {
    289 		warnx("puffs_mount: mounting with lib version %d, need %d",
    290 		    develv, PUFFS_DEVEL_LIBVERSION);
    291 		errno = EINVAL;
    292 		return NULL;
    293 	}
    294 
    295 	fd = open("/dev/puffs", O_RDONLY);
    296 	if (fd == -1)
    297 		return NULL;
    298 	if (fd <= 2)
    299 		warnx("puffs_mount: device fd %d (<= 2), sure this is "
    300 		    "what you want?", fd);
    301 
    302 	pu = malloc(sizeof(struct puffs_usermount));
    303 	if (pu == NULL)
    304 		goto failfree;
    305 
    306 	pargs = &pu->pu_kargs;
    307 	memset(pargs, 0, sizeof(struct puffs_kargs));
    308 	pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION;
    309 	pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
    310 	pargs->pa_fd = fd;
    311 	fillvnopmask(pops, pargs->pa_vnopmask);
    312 	(void)strlcpy(pargs->pa_name, puffsname, sizeof(pargs->pa_name));
    313 
    314 	pu->pu_flags = pflags;
    315 	pu->pu_ops = *pops;
    316 	free(pops); /* XXX */
    317 
    318 	pu->pu_privdata = priv;
    319 	pu->pu_cc_stacksize = PUFFS_CC_STACKSIZE_DEFAULT;
    320 	LIST_INIT(&pu->pu_pnodelst);
    321 
    322 	/* defaults for some user-settable translation functions */
    323 	pu->pu_cmap = NULL; /* identity translation */
    324 
    325 	pu->pu_pathbuild = puffs_stdpath_buildpath;
    326 	pu->pu_pathfree = puffs_stdpath_freepath;
    327 	pu->pu_pathcmp = puffs_stdpath_cmppath;
    328 	pu->pu_pathtransform = NULL;
    329 	pu->pu_namemod = NULL;
    330 
    331 	pu->pu_state = PUFFS_STATE_BEFOREMOUNT;
    332 
    333 	return pu;
    334 
    335  failfree:
    336 	/* can't unmount() from here for obvious reasons */
    337 	close(fd);
    338 	free(pu);
    339 	return NULL;
    340 }
    341 
    342 struct puffs_usermount *
    343 _puffs_mount(int develv, struct puffs_ops *pops, const char *dir, int mntflags,
    344 	const char *puffsname, void *priv, uint32_t pflags)
    345 {
    346 	struct puffs_usermount *pu;
    347 	int sverrno;
    348 
    349 	pu = _puffs_init(develv, pops, puffsname, priv, pflags);
    350 	if (pu == NULL)
    351 		return NULL;
    352 
    353 	if (puffs_domount(pu, dir, mntflags) == -1) {
    354 		sverrno = errno;
    355 		puffs_exit(pu, 1);
    356 		errno = sverrno;
    357 		return NULL;
    358 	}
    359 
    360 	return pu;
    361 }
    362 
    363 int
    364 puffs_start(struct puffs_usermount *pu, void *rootcookie, struct statvfs *sbp)
    365 {
    366 	struct puffs_startreq sreq;
    367 
    368 	memset(&sreq, 0, sizeof(struct puffs_startreq));
    369 	sreq.psr_cookie = rootcookie;
    370 	sreq.psr_sb = *sbp;
    371 
    372 	/* tell kernel we're flying */
    373 	if (ioctl(pu->pu_kargs.pa_fd, PUFFSSTARTOP, &sreq) == -1)
    374 		return -1;
    375 
    376 	pu->pu_state = PUFFS_STATE_RUNNING;
    377 
    378 	return 0;
    379 }
    380 
    381 /*
    382  * XXX: there's currently no clean way to request unmount from
    383  * within the user server, so be very brutal about it.
    384  */
    385 /*ARGSUSED1*/
    386 int
    387 puffs_exit(struct puffs_usermount *pu, int force)
    388 {
    389 	struct puffs_node *pn, *pn_next;
    390 
    391 	force = 1; /* currently */
    392 
    393 	if (pu->pu_kargs.pa_fd)
    394 		close(pu->pu_kargs.pa_fd);
    395 
    396 	pn = LIST_FIRST(&pu->pu_pnodelst);
    397 	while (pn) {
    398 		pn_next = LIST_NEXT(pn, pn_entries);
    399 		puffs_pn_put(pn);
    400 		pn = pn_next;
    401 	}
    402 	free(pu);
    403 
    404 	return 0; /* always succesful for now, WILL CHANGE */
    405 }
    406 
    407 int
    408 puffs_mainloop(struct puffs_usermount *pu, int flags)
    409 {
    410 	struct puffs_getreq *pgr;
    411 	struct puffs_putreq *ppr;
    412 	int rv;
    413 
    414 	rv = -1;
    415 	pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
    416 	if (pgr == NULL)
    417 		return -1;
    418 
    419 	ppr = puffs_req_makeput(pu);
    420 	if (ppr == NULL) {
    421 		puffs_req_destroyget(pgr);
    422 		return -1;
    423 	}
    424 
    425 	if ((flags & PUFFSLOOP_NODAEMON) == 0)
    426 		if (daemon(1, 0) == -1)
    427 			goto out;
    428 
    429 	/* XXX: should be a bit more robust with errors here */
    430 	while (puffs_getstate(pu) == PUFFS_STATE_RUNNING
    431 	    || puffs_getstate(pu) == PUFFS_STATE_UNMOUNTING) {
    432 		puffs_req_resetput(ppr);
    433 
    434 		if (puffs_req_handle(pu, pgr, ppr, 0) == -1) {
    435 			rv = -1;
    436 			break;
    437 		}
    438 		if (puffs_req_putput(ppr) == -1) {
    439 			rv = -1;
    440 			break;
    441 		}
    442 	}
    443 
    444  out:
    445 	puffs_req_destroyput(ppr);
    446 	puffs_req_destroyget(pgr);
    447 	return rv;
    448 }
    449 
    450 int
    451 puffs_dopreq(struct puffs_usermount *pu, struct puffs_req *preq,
    452 	struct puffs_putreq *ppr)
    453 {
    454 	struct puffs_cc *pcc;
    455 	int rv;
    456 
    457 	/*
    458 	 * XXX: the structure is currently a mess.  anyway, trap
    459 	 * the cacheops here already, since they don't need a cc.
    460 	 * I really should get around to revamping the operation
    461 	 * dispatching code one of these days.
    462 	 */
    463 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
    464 		struct puffs_cacheinfo *pci = (void *)preq;
    465 
    466 		if (pu->pu_ops.puffs_cache_write == NULL)
    467 			return 0;
    468 
    469 		pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
    470 		    pci->pcache_nruns, pci->pcache_runs);
    471 	}
    472 
    473 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    474 		puffsdump_req(preq);
    475 
    476 	pcc = puffs_cc_create(pu);
    477 
    478 	/* XXX: temporary kludging */
    479 	pcc->pcc_preq = malloc(preq->preq_buflen);
    480 	if (pcc->pcc_preq == NULL)
    481 		return -1;
    482 	(void) memcpy(pcc->pcc_preq, preq, preq->preq_buflen);
    483 
    484 	rv = puffs_docc(pcc, ppr);
    485 
    486 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    487 		return 0;
    488 
    489 	return rv;
    490 }
    491 
    492 enum {PUFFCALL_ANSWER, PUFFCALL_IGNORE, PUFFCALL_AGAIN};
    493 
    494 int
    495 puffs_docc(struct puffs_cc *pcc, struct puffs_putreq *ppr)
    496 {
    497 	struct puffs_usermount *pu = pcc->pcc_pu;
    498 	int rv;
    499 
    500 	assert((pcc->pcc_flags & PCC_DONE) == 0);
    501 
    502 	puffs_cc_continue(pcc);
    503 	rv = pcc->pcc_rv;
    504 
    505 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    506 		rv = PUFFCALL_AGAIN;
    507 
    508 	/* check if we need to store this reply */
    509 	switch (rv) {
    510 	case PUFFCALL_ANSWER:
    511 		if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    512 			puffsdump_rv(pcc->pcc_preq);
    513 
    514 		puffs_req_putcc(ppr, pcc);
    515 		break;
    516 	case PUFFCALL_IGNORE:
    517 		puffs_cc_destroy(pcc);
    518 		break;
    519 	case PUFFCALL_AGAIN:
    520 		break;
    521 	default:
    522 		assert(/*CONSTCOND*/0);
    523 	}
    524 
    525 	return 0;
    526 }
    527 
    528 /* library private, but linked from callcontext.c */
    529 
    530 void
    531 puffs_calldispatcher(struct puffs_cc *pcc)
    532 {
    533 	struct puffs_usermount *pu = pcc->pcc_pu;
    534 	struct puffs_ops *pops = &pu->pu_ops;
    535 	struct puffs_req *preq = pcc->pcc_preq;
    536 	void *auxbuf = preq; /* help with typecasting */
    537 	void *opcookie = preq->preq_cookie;
    538 	int error, rv, buildpath;
    539 
    540 	assert(pcc->pcc_flags & (PCC_ONCE | PCC_REALCC));
    541 
    542 	if (PUFFSOP_WANTREPLY(preq->preq_opclass))
    543 		rv = PUFFCALL_ANSWER;
    544 	else
    545 		rv = PUFFCALL_IGNORE;
    546 
    547 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
    548 	preq->preq_setbacks = 0;
    549 
    550 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
    551 		switch (preq->preq_optype) {
    552 		case PUFFS_VFS_UNMOUNT:
    553 		{
    554 			struct puffs_vfsreq_unmount *auxt = auxbuf;
    555 
    556 			pu->pu_state = PUFFS_STATE_UNMOUNTING;
    557 			error = pops->puffs_fs_unmount(pcc,
    558 			    auxt->pvfsr_flags, auxt->pvfsr_pid);
    559 			if (!error)
    560 				pu->pu_state = PUFFS_STATE_UNMOUNTED;
    561 			else
    562 				pu->pu_state = PUFFS_STATE_RUNNING;
    563 			break;
    564 		}
    565 
    566 		case PUFFS_VFS_STATVFS:
    567 		{
    568 			struct puffs_vfsreq_statvfs *auxt = auxbuf;
    569 
    570 			error = pops->puffs_fs_statvfs(pcc,
    571 			    &auxt->pvfsr_sb, auxt->pvfsr_pid);
    572 			break;
    573 		}
    574 
    575 		case PUFFS_VFS_SYNC:
    576 		{
    577 			struct puffs_vfsreq_sync *auxt = auxbuf;
    578 
    579 			error = pops->puffs_fs_sync(pcc,
    580 			    auxt->pvfsr_waitfor, &auxt->pvfsr_cred,
    581 			    auxt->pvfsr_pid);
    582 			break;
    583 		}
    584 
    585 		case PUFFS_VFS_FHTOVP:
    586 		{
    587 			struct puffs_vfsreq_fhtonode *auxt = auxbuf;
    588 
    589 			error = pops->puffs_fs_fhtonode(pcc, auxt->pvfsr_data,
    590 			    auxt->pvfsr_dsize, &auxt->pvfsr_fhcookie,
    591 			    &auxt->pvfsr_vtype, &auxt->pvfsr_size,
    592 			    &auxt->pvfsr_rdev);
    593 
    594 			break;
    595 		}
    596 
    597 		case PUFFS_VFS_VPTOFH:
    598 		{
    599 			struct puffs_vfsreq_nodetofh *auxt = auxbuf;
    600 
    601 			error = pops->puffs_fs_nodetofh(pcc,
    602 			    auxt->pvfsr_fhcookie, auxt->pvfsr_data,
    603 			    &auxt->pvfsr_dsize);
    604 
    605 			break;
    606 		}
    607 
    608 		case PUFFS_VFS_SUSPEND:
    609 		{
    610 			struct puffs_vfsreq_suspend *auxt = auxbuf;
    611 
    612 			error = 0;
    613 			if (pops->puffs_fs_suspend == NULL)
    614 				break;
    615 
    616 			pops->puffs_fs_suspend(pcc, auxt->pvfsr_status);
    617 			break;
    618 		}
    619 
    620 		default:
    621 			/*
    622 			 * I guess the kernel sees this one coming
    623 			 */
    624 			error = EINVAL;
    625 			break;
    626 		}
    627 
    628 	/* XXX: audit return values */
    629 	/* XXX: sync with kernel */
    630 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
    631 		switch (preq->preq_optype) {
    632 		case PUFFS_VN_LOOKUP:
    633 		{
    634 			struct puffs_vnreq_lookup *auxt = auxbuf;
    635 			struct puffs_cn pcn;
    636 
    637 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    638 			if (buildpath) {
    639 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    640 				if (error)
    641 					break;
    642 			}
    643 
    644 			/* lookup *must* be present */
    645 			error = pops->puffs_node_lookup(pcc, opcookie,
    646 			    &auxt->pvnr_newnode, &auxt->pvnr_vtype,
    647 			    &auxt->pvnr_size, &auxt->pvnr_rdev, &pcn);
    648 
    649 			if (buildpath) {
    650 				if (error) {
    651 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    652 				} else {
    653 					struct puffs_node *pn;
    654 
    655 					/*
    656 					 * did we get a new node or a
    657 					 * recycled node?
    658 					 */
    659 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    660 					if (pn->pn_po.po_path == NULL)
    661 						pn->pn_po = pcn.pcn_po_full;
    662 					else
    663 						pu->pu_pathfree(pu,
    664 						    &pcn.pcn_po_full);
    665 				}
    666 			}
    667 
    668 			break;
    669 		}
    670 
    671 		case PUFFS_VN_CREATE:
    672 		{
    673 			struct puffs_vnreq_create *auxt = auxbuf;
    674 			struct puffs_cn pcn;
    675 			if (pops->puffs_node_create == NULL) {
    676 				error = 0;
    677 				break;
    678 			}
    679 
    680 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    681 			if (buildpath) {
    682 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    683 				if (error)
    684 					break;
    685 			}
    686 
    687 			error = pops->puffs_node_create(pcc,
    688 			    opcookie, &auxt->pvnr_newnode,
    689 			    &pcn, &auxt->pvnr_va);
    690 
    691 			if (buildpath) {
    692 				if (error) {
    693 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    694 				} else {
    695 					struct puffs_node *pn;
    696 
    697 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    698 					pn->pn_po = pcn.pcn_po_full;
    699 				}
    700 			}
    701 
    702 			break;
    703 		}
    704 
    705 		case PUFFS_VN_MKNOD:
    706 		{
    707 			struct puffs_vnreq_mknod *auxt = auxbuf;
    708 			struct puffs_cn pcn;
    709 			if (pops->puffs_node_mknod == NULL) {
    710 				error = 0;
    711 				break;
    712 			}
    713 
    714 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    715 			if (buildpath) {
    716 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    717 				if (error)
    718 					break;
    719 			}
    720 
    721 			error = pops->puffs_node_mknod(pcc,
    722 			    opcookie, &auxt->pvnr_newnode,
    723 			    &pcn, &auxt->pvnr_va);
    724 
    725 			if (buildpath) {
    726 				if (error) {
    727 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    728 				} else {
    729 					struct puffs_node *pn;
    730 
    731 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    732 					pn->pn_po = pcn.pcn_po_full;
    733 				}
    734 			}
    735 
    736 			break;
    737 		}
    738 
    739 		case PUFFS_VN_OPEN:
    740 		{
    741 			struct puffs_vnreq_open *auxt = auxbuf;
    742 			if (pops->puffs_node_open == NULL) {
    743 				error = 0;
    744 				break;
    745 			}
    746 
    747 			error = pops->puffs_node_open(pcc,
    748 			    opcookie, auxt->pvnr_mode,
    749 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    750 			break;
    751 		}
    752 
    753 		case PUFFS_VN_CLOSE:
    754 		{
    755 			struct puffs_vnreq_close *auxt = auxbuf;
    756 			if (pops->puffs_node_close == NULL) {
    757 				error = 0;
    758 				break;
    759 			}
    760 
    761 			error = pops->puffs_node_close(pcc,
    762 			    opcookie, auxt->pvnr_fflag,
    763 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    764 			break;
    765 		}
    766 
    767 		case PUFFS_VN_ACCESS:
    768 		{
    769 			struct puffs_vnreq_access *auxt = auxbuf;
    770 			if (pops->puffs_node_access == NULL) {
    771 				error = 0;
    772 				break;
    773 			}
    774 
    775 			error = pops->puffs_node_access(pcc,
    776 			    opcookie, auxt->pvnr_mode,
    777 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    778 			break;
    779 		}
    780 
    781 		case PUFFS_VN_GETATTR:
    782 		{
    783 			struct puffs_vnreq_getattr *auxt = auxbuf;
    784 			if (pops->puffs_node_getattr == NULL) {
    785 				error = EOPNOTSUPP;
    786 				break;
    787 			}
    788 
    789 			error = pops->puffs_node_getattr(pcc,
    790 			    opcookie, &auxt->pvnr_va,
    791 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    792 			break;
    793 		}
    794 
    795 		case PUFFS_VN_SETATTR:
    796 		{
    797 			struct puffs_vnreq_setattr *auxt = auxbuf;
    798 			if (pops->puffs_node_setattr == NULL) {
    799 				error = EOPNOTSUPP;
    800 				break;
    801 			}
    802 
    803 			error = pops->puffs_node_setattr(pcc,
    804 			    opcookie, &auxt->pvnr_va,
    805 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    806 			break;
    807 		}
    808 
    809 		case PUFFS_VN_MMAP:
    810 		{
    811 			struct puffs_vnreq_mmap *auxt = auxbuf;
    812 			if (pops->puffs_node_mmap == NULL) {
    813 				error = 0;
    814 				break;
    815 			}
    816 
    817 			error = pops->puffs_node_mmap(pcc,
    818 			    opcookie, auxt->pvnr_fflags,
    819 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    820 			break;
    821 		}
    822 
    823 		case PUFFS_VN_FSYNC:
    824 		{
    825 			struct puffs_vnreq_fsync *auxt = auxbuf;
    826 			if (pops->puffs_node_fsync == NULL) {
    827 				error = 0;
    828 				break;
    829 			}
    830 
    831 			error = pops->puffs_node_fsync(pcc,
    832 			    opcookie, &auxt->pvnr_cred,
    833 			    auxt->pvnr_flags, auxt->pvnr_offlo,
    834 			    auxt->pvnr_offhi, auxt->pvnr_pid);
    835 			break;
    836 		}
    837 
    838 		case PUFFS_VN_SEEK:
    839 		{
    840 			struct puffs_vnreq_seek *auxt = auxbuf;
    841 			if (pops->puffs_node_seek == NULL) {
    842 				error = 0;
    843 				break;
    844 			}
    845 
    846 			error = pops->puffs_node_seek(pcc,
    847 			    opcookie, auxt->pvnr_oldoff,
    848 			    auxt->pvnr_newoff, &auxt->pvnr_cred);
    849 			break;
    850 		}
    851 
    852 		case PUFFS_VN_REMOVE:
    853 		{
    854 			struct puffs_vnreq_remove *auxt = auxbuf;
    855 			struct puffs_cn pcn;
    856 			if (pops->puffs_node_remove == NULL) {
    857 				error = 0;
    858 				break;
    859 			}
    860 
    861 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    862 
    863 			error = pops->puffs_node_remove(pcc,
    864 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    865 			break;
    866 		}
    867 
    868 		case PUFFS_VN_LINK:
    869 		{
    870 			struct puffs_vnreq_link *auxt = auxbuf;
    871 			struct puffs_cn pcn;
    872 			if (pops->puffs_node_link == NULL) {
    873 				error = 0;
    874 				break;
    875 			}
    876 
    877 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    878 			if (buildpath) {
    879 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    880 				if (error)
    881 					break;
    882 			}
    883 
    884 			error = pops->puffs_node_link(pcc,
    885 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    886 			if (buildpath)
    887 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
    888 
    889 			break;
    890 		}
    891 
    892 		case PUFFS_VN_RENAME:
    893 		{
    894 			struct puffs_vnreq_rename *auxt = auxbuf;
    895 			struct puffs_cn pcn_src, pcn_targ;
    896 			struct puffs_node *pn_src;
    897 
    898 			if (pops->puffs_node_rename == NULL) {
    899 				error = 0;
    900 				break;
    901 			}
    902 
    903 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
    904 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
    905 			if (buildpath) {
    906 				pn_src = auxt->pvnr_cookie_src;
    907 				pcn_src.pcn_po_full = pn_src->pn_po;
    908 
    909 				error = puffs_path_pcnbuild(pu, &pcn_targ,
    910 				    auxt->pvnr_cookie_targdir);
    911 				if (error)
    912 					break;
    913 			}
    914 
    915 			error = pops->puffs_node_rename(pcc,
    916 			    opcookie, auxt->pvnr_cookie_src,
    917 			    &pcn_src, auxt->pvnr_cookie_targdir,
    918 			    auxt->pvnr_cookie_targ, &pcn_targ);
    919 
    920 			if (buildpath) {
    921 				if (error) {
    922 					pu->pu_pathfree(pu,
    923 					    &pcn_targ.pcn_po_full);
    924 				} else {
    925 					struct puffs_pathinfo pi;
    926 					struct puffs_pathobj po_old;
    927 
    928 					/* handle this node */
    929 					po_old = pn_src->pn_po;
    930 					pn_src->pn_po = pcn_targ.pcn_po_full;
    931 
    932 					if (pn_src->pn_va.va_type != VDIR) {
    933 						pu->pu_pathfree(pu, &po_old);
    934 						break;
    935 					}
    936 
    937 					/* handle all child nodes for DIRs */
    938 					pi.pi_old = &pcn_src.pcn_po_full;
    939 					pi.pi_new = &pcn_targ.pcn_po_full;
    940 
    941 					if (puffs_pn_nodewalk(pu,
    942 					    puffs_path_prefixadj, &pi) != NULL)
    943 						error = ENOMEM;
    944 					pu->pu_pathfree(pu, &po_old);
    945 				}
    946 			}
    947 			break;
    948 		}
    949 
    950 		case PUFFS_VN_MKDIR:
    951 		{
    952 			struct puffs_vnreq_mkdir *auxt = auxbuf;
    953 			struct puffs_cn pcn;
    954 			if (pops->puffs_node_mkdir == NULL) {
    955 				error = 0;
    956 				break;
    957 			}
    958 
    959 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    960 			if (buildpath) {
    961 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    962 				if (error)
    963 					break;
    964 			}
    965 
    966 			error = pops->puffs_node_mkdir(pcc,
    967 			    opcookie, &auxt->pvnr_newnode,
    968 			    &pcn, &auxt->pvnr_va);
    969 
    970 			if (buildpath) {
    971 				if (error) {
    972 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    973 				} else {
    974 					struct puffs_node *pn;
    975 
    976 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    977 					pn->pn_po = pcn.pcn_po_full;
    978 				}
    979 			}
    980 
    981 			break;
    982 		}
    983 
    984 		case PUFFS_VN_RMDIR:
    985 		{
    986 			struct puffs_vnreq_rmdir *auxt = auxbuf;
    987 			struct puffs_cn pcn;
    988 			if (pops->puffs_node_rmdir == NULL) {
    989 				error = 0;
    990 				break;
    991 			}
    992 
    993 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    994 
    995 			error = pops->puffs_node_rmdir(pcc,
    996 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    997 			break;
    998 		}
    999 
   1000 		case PUFFS_VN_SYMLINK:
   1001 		{
   1002 			struct puffs_vnreq_symlink *auxt = auxbuf;
   1003 			struct puffs_cn pcn;
   1004 			if (pops->puffs_node_symlink == NULL) {
   1005 				error = 0;
   1006 				break;
   1007 			}
   1008 
   1009 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1010 			if (buildpath) {
   1011 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
   1012 				if (error)
   1013 					break;
   1014 			}
   1015 
   1016 			error = pops->puffs_node_symlink(pcc,
   1017 			    opcookie, &auxt->pvnr_newnode,
   1018 			    &pcn, &auxt->pvnr_va, auxt->pvnr_link);
   1019 
   1020 			if (buildpath) {
   1021 				if (error) {
   1022 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
   1023 				} else {
   1024 					struct puffs_node *pn;
   1025 
   1026 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
   1027 					pn->pn_po = pcn.pcn_po_full;
   1028 				}
   1029 			}
   1030 
   1031 			break;
   1032 		}
   1033 
   1034 		case PUFFS_VN_READDIR:
   1035 		{
   1036 			struct puffs_vnreq_readdir *auxt = auxbuf;
   1037 			struct dirent *dent;
   1038 			off_t *cookies;
   1039 			size_t res, origcookies;
   1040 
   1041 			if (pops->puffs_node_readdir == NULL) {
   1042 				error = 0;
   1043 				break;
   1044 			}
   1045 
   1046 			if (auxt->pvnr_ncookies) {
   1047 				/* LINTED: pvnr_data is __aligned() */
   1048 				cookies = (off_t *)auxt->pvnr_data;
   1049 				origcookies = auxt->pvnr_ncookies;
   1050 			} else {
   1051 				cookies = NULL;
   1052 				origcookies = 0;
   1053 			}
   1054 			/* LINTED: dentoff is aligned in the kernel */
   1055 			dent = (struct dirent *)
   1056 			    (auxt->pvnr_data + auxt->pvnr_dentoff);
   1057 
   1058 			res = auxt->pvnr_resid;
   1059 			error = pops->puffs_node_readdir(pcc,
   1060 			    opcookie, dent, &auxt->pvnr_offset,
   1061 			    &auxt->pvnr_resid, &auxt->pvnr_cred,
   1062 			    &auxt->pvnr_eofflag, cookies, &auxt->pvnr_ncookies);
   1063 
   1064 			/* much easier to track non-working NFS */
   1065 			assert(auxt->pvnr_ncookies <= origcookies);
   1066 
   1067 			/* need to move a bit more */
   1068 			preq->preq_buflen = sizeof(struct puffs_vnreq_readdir)
   1069 			    + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
   1070 			break;
   1071 		}
   1072 
   1073 		case PUFFS_VN_READLINK:
   1074 		{
   1075 			struct puffs_vnreq_readlink *auxt = auxbuf;
   1076 			if (pops->puffs_node_readlink == NULL) {
   1077 				error = EOPNOTSUPP;
   1078 				break;
   1079 			}
   1080 
   1081 			error = pops->puffs_node_readlink(pcc,
   1082 			    opcookie, &auxt->pvnr_cred,
   1083 			    auxt->pvnr_link, &auxt->pvnr_linklen);
   1084 			break;
   1085 		}
   1086 
   1087 		case PUFFS_VN_RECLAIM:
   1088 		{
   1089 			struct puffs_vnreq_reclaim *auxt = auxbuf;
   1090 			if (pops->puffs_node_reclaim == NULL) {
   1091 				error = 0;
   1092 				break;
   1093 			}
   1094 
   1095 			error = pops->puffs_node_reclaim(pcc,
   1096 			    opcookie, auxt->pvnr_pid);
   1097 			break;
   1098 		}
   1099 
   1100 		case PUFFS_VN_INACTIVE:
   1101 		{
   1102 			struct puffs_vnreq_inactive *auxt = auxbuf;
   1103 			if (pops->puffs_node_inactive == NULL) {
   1104 				error = EOPNOTSUPP;
   1105 				break;
   1106 			}
   1107 
   1108 			error = pops->puffs_node_inactive(pcc,
   1109 			    opcookie, auxt->pvnr_pid,
   1110 			    &auxt->pvnr_backendrefs);
   1111 			break;
   1112 		}
   1113 
   1114 		case PUFFS_VN_PATHCONF:
   1115 		{
   1116 			struct puffs_vnreq_pathconf *auxt = auxbuf;
   1117 			if (pops->puffs_node_pathconf == NULL) {
   1118 				error = 0;
   1119 				break;
   1120 			}
   1121 
   1122 			error = pops->puffs_node_pathconf(pcc,
   1123 			    opcookie, auxt->pvnr_name,
   1124 			    &auxt->pvnr_retval);
   1125 			break;
   1126 		}
   1127 
   1128 		case PUFFS_VN_ADVLOCK:
   1129 		{
   1130 			struct puffs_vnreq_advlock *auxt = auxbuf;
   1131 			if (pops->puffs_node_advlock == NULL) {
   1132 				error = 0;
   1133 				break;
   1134 			}
   1135 
   1136 			error = pops->puffs_node_advlock(pcc,
   1137 			    opcookie, auxt->pvnr_id, auxt->pvnr_op,
   1138 			    &auxt->pvnr_fl, auxt->pvnr_flags);
   1139 			break;
   1140 		}
   1141 
   1142 		case PUFFS_VN_PRINT:
   1143 		{
   1144 			if (pops->puffs_node_print == NULL) {
   1145 				error = 0;
   1146 				break;
   1147 			}
   1148 
   1149 			error = pops->puffs_node_print(pcc,
   1150 			    opcookie);
   1151 			break;
   1152 		}
   1153 
   1154 		case PUFFS_VN_READ:
   1155 		{
   1156 			struct puffs_vnreq_read *auxt = auxbuf;
   1157 			size_t res;
   1158 
   1159 			if (pops->puffs_node_read == NULL) {
   1160 				error = EIO;
   1161 				break;
   1162 			}
   1163 
   1164 			res = auxt->pvnr_resid;
   1165 			error = pops->puffs_node_read(pcc,
   1166 			    opcookie, auxt->pvnr_data,
   1167 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1168 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1169 
   1170 			/* need to move a bit more */
   1171 			preq->preq_buflen = sizeof(struct puffs_vnreq_read)
   1172 			    + (res - auxt->pvnr_resid);
   1173 			break;
   1174 		}
   1175 
   1176 		case PUFFS_VN_WRITE:
   1177 		{
   1178 			struct puffs_vnreq_write *auxt = auxbuf;
   1179 
   1180 			if (pops->puffs_node_write == NULL) {
   1181 				error = EIO;
   1182 				break;
   1183 			}
   1184 
   1185 			error = pops->puffs_node_write(pcc,
   1186 			    opcookie, auxt->pvnr_data,
   1187 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1188 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1189 
   1190 			/* don't need to move data back to the kernel */
   1191 			preq->preq_buflen = sizeof(struct puffs_vnreq_write);
   1192 			break;
   1193 		}
   1194 
   1195 /* holy bitrot, ryydman! */
   1196 #if 0
   1197 		case PUFFS_VN_IOCTL:
   1198 			error = pops->puffs_node_ioctl1(pcc, opcookie,
   1199 			     (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1200 			if (error != 0)
   1201 				break;
   1202 			pop.pso_reqid = preq->preq_id;
   1203 
   1204 			/* let the kernel do it's intermediate duty */
   1205 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1206 			/*
   1207 			 * XXX: I don't actually know what the correct
   1208 			 * thing to do in case of an error is, so I'll
   1209 			 * just ignore it for the time being.
   1210 			 */
   1211 			error = pops->puffs_node_ioctl2(pcc, opcookie,
   1212 			    (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1213 			break;
   1214 
   1215 		case PUFFS_VN_FCNTL:
   1216 			error = pops->puffs_node_fcntl1(pcc, opcookie,
   1217 			     (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1218 			if (error != 0)
   1219 				break;
   1220 			pop.pso_reqid = preq->preq_id;
   1221 
   1222 			/* let the kernel do it's intermediate duty */
   1223 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1224 			/*
   1225 			 * XXX: I don't actually know what the correct
   1226 			 * thing to do in case of an error is, so I'll
   1227 			 * just ignore it for the time being.
   1228 			 */
   1229 			error = pops->puffs_node_fcntl2(pcc, opcookie,
   1230 			    (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1231 			break;
   1232 #endif
   1233 
   1234 		default:
   1235 			printf("inval op %d\n", preq->preq_optype);
   1236 			error = EINVAL;
   1237 			break;
   1238 		}
   1239 	} else {
   1240 		/*
   1241 		 * this one also
   1242 		 */
   1243 		error = EINVAL;
   1244 	}
   1245 
   1246 	preq->preq_rv = error;
   1247 
   1248 	pcc->pcc_rv = rv;
   1249 	pcc->pcc_flags |= PCC_DONE;
   1250 }
   1251 
   1252 
   1253 #if 0
   1254 		case PUFFS_VN_POLL:
   1255 		{
   1256 			struct puffs_vnreq_poll *auxt = auxbuf;
   1257 			if (pops->puffs_node_poll == NULL) {
   1258 				error = 0;
   1259 				break;
   1260 			}
   1261 
   1262 			error = pops->puffs_node_poll(pcc,
   1263 			    opcookie, preq-);
   1264 			break;
   1265 		}
   1266 
   1267 		case PUFFS_VN_KQFILTER:
   1268 		{
   1269 			struct puffs_vnreq_kqfilter *auxt = auxbuf;
   1270 			if (pops->puffs_node_kqfilter == NULL) {
   1271 				error = 0;
   1272 				break;
   1273 			}
   1274 
   1275 			error = pops->puffs_node_kqfilter(pcc,
   1276 			    opcookie, );
   1277 			break;
   1278 		}
   1279 
   1280 		case PUFFS_VN_CLOSEEXTATTR:
   1281 		{
   1282 			struct puffs_vnreq_closeextattr *auxt = auxbuf;
   1283 			if (pops->puffs_closeextattr == NULL) {
   1284 				error = 0;
   1285 				break;
   1286 			}
   1287 
   1288 			error = pops->puffs_closeextattr(pcc,
   1289 			    opcookie, );
   1290 			break;
   1291 		}
   1292 
   1293 		case PUFFS_VN_GETEXTATTR:
   1294 		{
   1295 			struct puffs_vnreq_getextattr *auxt = auxbuf;
   1296 			if (pops->puffs_getextattr == NULL) {
   1297 				error = 0;
   1298 				break;
   1299 			}
   1300 
   1301 			error = pops->puffs_getextattr(pcc,
   1302 			    opcookie, );
   1303 			break;
   1304 		}
   1305 
   1306 		case PUFFS_VN_LISTEXTATTR:
   1307 		{
   1308 			struct puffs_vnreq_listextattr *auxt = auxbuf;
   1309 			if (pops->puffs_listextattr == NULL) {
   1310 				error = 0;
   1311 				break;
   1312 			}
   1313 
   1314 			error = pops->puffs_listextattr(pcc,
   1315 			    opcookie, );
   1316 			break;
   1317 		}
   1318 
   1319 		case PUFFS_VN_OPENEXTATTR:
   1320 		{
   1321 			struct puffs_vnreq_openextattr *auxt = auxbuf;
   1322 			if (pops->puffs_openextattr == NULL) {
   1323 				error = 0;
   1324 				break;
   1325 			}
   1326 
   1327 			error = pops->puffs_openextattr(pcc,
   1328 			    opcookie, );
   1329 			break;
   1330 		}
   1331 
   1332 		case PUFFS_VN_DELETEEXTATTR:
   1333 		{
   1334 			struct puffs_vnreq_deleteextattr *auxt = auxbuf;
   1335 			if (pops->puffs_deleteextattr == NULL) {
   1336 				error = 0;
   1337 				break;
   1338 			}
   1339 
   1340 			error = pops->puffs_deleteextattr(pcc,
   1341 			    opcookie, );
   1342 			break;
   1343 		}
   1344 
   1345 		case PUFFS_VN_SETEXTATTR:
   1346 		{
   1347 			struct puffs_vnreq_setextattr *auxt = auxbuf;
   1348 			if (pops->puffs_setextattr == NULL) {
   1349 				error = 0;
   1350 				break;
   1351 			}
   1352 
   1353 			error = pops->puffs_setextattr(pcc,
   1354 			    opcookie, );
   1355 			break;
   1356 		}
   1357 
   1358 #endif
   1359