Home | History | Annotate | Line # | Download | only in libpuffs
puffs.c revision 1.42
      1 /*	$NetBSD: puffs.c,v 1.42 2007/05/09 18:36:52 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.42 2007/05/09 18:36:52 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 	rv = 0;
    431 	while (puffs_getstate(pu) == PUFFS_STATE_RUNNING
    432 	    || puffs_getstate(pu) == PUFFS_STATE_UNMOUNTING) {
    433 		puffs_req_resetput(ppr);
    434 
    435 		if (puffs_req_handle(pgr, ppr, 0) == -1) {
    436 			rv = -1;
    437 			break;
    438 		}
    439 		if (puffs_req_putput(ppr) == -1) {
    440 			rv = -1;
    441 			break;
    442 		}
    443 	}
    444 
    445  out:
    446 	puffs_req_destroyput(ppr);
    447 	puffs_req_destroyget(pgr);
    448 	return rv;
    449 }
    450 
    451 int
    452 puffs_dopreq(struct puffs_usermount *pu, struct puffs_req *preq,
    453 	struct puffs_putreq *ppr)
    454 {
    455 	struct puffs_cc *pcc;
    456 	int rv;
    457 
    458 	/*
    459 	 * XXX: the structure is currently a mess.  anyway, trap
    460 	 * the cacheops here already, since they don't need a cc.
    461 	 * I really should get around to revamping the operation
    462 	 * dispatching code one of these days.
    463 	 */
    464 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
    465 		struct puffs_cacheinfo *pci = (void *)preq;
    466 
    467 		if (pu->pu_ops.puffs_cache_write == NULL)
    468 			return 0;
    469 
    470 		pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
    471 		    pci->pcache_nruns, pci->pcache_runs);
    472 	}
    473 
    474 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    475 		puffsdump_req(preq);
    476 
    477 	pcc = puffs_cc_create(pu);
    478 
    479 	/* XXX: temporary kludging */
    480 	pcc->pcc_preq = malloc(preq->preq_buflen);
    481 	if (pcc->pcc_preq == NULL)
    482 		return -1;
    483 	(void) memcpy(pcc->pcc_preq, preq, preq->preq_buflen);
    484 
    485 	rv = puffs_docc(pcc, ppr);
    486 
    487 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    488 		return 0;
    489 
    490 	return rv;
    491 }
    492 
    493 enum {PUFFCALL_ANSWER, PUFFCALL_IGNORE, PUFFCALL_AGAIN};
    494 
    495 int
    496 puffs_docc(struct puffs_cc *pcc, struct puffs_putreq *ppr)
    497 {
    498 	struct puffs_usermount *pu = pcc->pcc_pu;
    499 	int rv;
    500 
    501 	assert((pcc->pcc_flags & PCC_DONE) == 0);
    502 
    503 	puffs_cc_continue(pcc);
    504 	rv = pcc->pcc_rv;
    505 
    506 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    507 		rv = PUFFCALL_AGAIN;
    508 
    509 	/* check if we need to store this reply */
    510 	switch (rv) {
    511 	case PUFFCALL_ANSWER:
    512 		if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    513 			puffsdump_rv(pcc->pcc_preq);
    514 
    515 		puffs_req_putcc(ppr, pcc);
    516 		break;
    517 	case PUFFCALL_IGNORE:
    518 		puffs_cc_destroy(pcc);
    519 		break;
    520 	case PUFFCALL_AGAIN:
    521 		break;
    522 	default:
    523 		assert(/*CONSTCOND*/0);
    524 	}
    525 
    526 	return 0;
    527 }
    528 
    529 /* library private, but linked from callcontext.c */
    530 
    531 void
    532 puffs_calldispatcher(struct puffs_cc *pcc)
    533 {
    534 	struct puffs_usermount *pu = pcc->pcc_pu;
    535 	struct puffs_ops *pops = &pu->pu_ops;
    536 	struct puffs_req *preq = pcc->pcc_preq;
    537 	void *auxbuf = preq; /* help with typecasting */
    538 	void *opcookie = preq->preq_cookie;
    539 	int error, rv, buildpath;
    540 
    541 	assert(pcc->pcc_flags & (PCC_ONCE | PCC_REALCC));
    542 
    543 	if (PUFFSOP_WANTREPLY(preq->preq_opclass))
    544 		rv = PUFFCALL_ANSWER;
    545 	else
    546 		rv = PUFFCALL_IGNORE;
    547 
    548 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
    549 	preq->preq_setbacks = 0;
    550 
    551 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
    552 		switch (preq->preq_optype) {
    553 		case PUFFS_VFS_UNMOUNT:
    554 		{
    555 			struct puffs_vfsreq_unmount *auxt = auxbuf;
    556 
    557 			pu->pu_state = PUFFS_STATE_UNMOUNTING;
    558 			error = pops->puffs_fs_unmount(pcc,
    559 			    auxt->pvfsr_flags, auxt->pvfsr_pid);
    560 			if (!error)
    561 				pu->pu_state = PUFFS_STATE_UNMOUNTED;
    562 			else
    563 				pu->pu_state = PUFFS_STATE_RUNNING;
    564 			break;
    565 		}
    566 
    567 		case PUFFS_VFS_STATVFS:
    568 		{
    569 			struct puffs_vfsreq_statvfs *auxt = auxbuf;
    570 
    571 			error = pops->puffs_fs_statvfs(pcc,
    572 			    &auxt->pvfsr_sb, auxt->pvfsr_pid);
    573 			break;
    574 		}
    575 
    576 		case PUFFS_VFS_SYNC:
    577 		{
    578 			struct puffs_vfsreq_sync *auxt = auxbuf;
    579 
    580 			error = pops->puffs_fs_sync(pcc,
    581 			    auxt->pvfsr_waitfor, &auxt->pvfsr_cred,
    582 			    auxt->pvfsr_pid);
    583 			break;
    584 		}
    585 
    586 		case PUFFS_VFS_FHTOVP:
    587 		{
    588 			struct puffs_vfsreq_fhtonode *auxt = auxbuf;
    589 
    590 			error = pops->puffs_fs_fhtonode(pcc, auxt->pvfsr_data,
    591 			    auxt->pvfsr_dsize, &auxt->pvfsr_fhcookie,
    592 			    &auxt->pvfsr_vtype, &auxt->pvfsr_size,
    593 			    &auxt->pvfsr_rdev);
    594 
    595 			break;
    596 		}
    597 
    598 		case PUFFS_VFS_VPTOFH:
    599 		{
    600 			struct puffs_vfsreq_nodetofh *auxt = auxbuf;
    601 
    602 			error = pops->puffs_fs_nodetofh(pcc,
    603 			    auxt->pvfsr_fhcookie, auxt->pvfsr_data,
    604 			    &auxt->pvfsr_dsize);
    605 
    606 			break;
    607 		}
    608 
    609 		case PUFFS_VFS_SUSPEND:
    610 		{
    611 			struct puffs_vfsreq_suspend *auxt = auxbuf;
    612 
    613 			error = 0;
    614 			if (pops->puffs_fs_suspend == NULL)
    615 				break;
    616 
    617 			pops->puffs_fs_suspend(pcc, auxt->pvfsr_status);
    618 			break;
    619 		}
    620 
    621 		default:
    622 			/*
    623 			 * I guess the kernel sees this one coming
    624 			 */
    625 			error = EINVAL;
    626 			break;
    627 		}
    628 
    629 	/* XXX: audit return values */
    630 	/* XXX: sync with kernel */
    631 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
    632 		switch (preq->preq_optype) {
    633 		case PUFFS_VN_LOOKUP:
    634 		{
    635 			struct puffs_vnreq_lookup *auxt = auxbuf;
    636 			struct puffs_cn pcn;
    637 
    638 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    639 			if (buildpath) {
    640 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    641 				if (error)
    642 					break;
    643 			}
    644 
    645 			/* lookup *must* be present */
    646 			error = pops->puffs_node_lookup(pcc, opcookie,
    647 			    &auxt->pvnr_newnode, &auxt->pvnr_vtype,
    648 			    &auxt->pvnr_size, &auxt->pvnr_rdev, &pcn);
    649 
    650 			if (buildpath) {
    651 				if (error) {
    652 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    653 				} else {
    654 					struct puffs_node *pn;
    655 
    656 					/*
    657 					 * did we get a new node or a
    658 					 * recycled node?
    659 					 */
    660 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    661 					if (pn->pn_po.po_path == NULL)
    662 						pn->pn_po = pcn.pcn_po_full;
    663 					else
    664 						pu->pu_pathfree(pu,
    665 						    &pcn.pcn_po_full);
    666 				}
    667 			}
    668 
    669 			break;
    670 		}
    671 
    672 		case PUFFS_VN_CREATE:
    673 		{
    674 			struct puffs_vnreq_create *auxt = auxbuf;
    675 			struct puffs_cn pcn;
    676 			if (pops->puffs_node_create == NULL) {
    677 				error = 0;
    678 				break;
    679 			}
    680 
    681 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    682 			if (buildpath) {
    683 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    684 				if (error)
    685 					break;
    686 			}
    687 
    688 			error = pops->puffs_node_create(pcc,
    689 			    opcookie, &auxt->pvnr_newnode,
    690 			    &pcn, &auxt->pvnr_va);
    691 
    692 			if (buildpath) {
    693 				if (error) {
    694 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    695 				} else {
    696 					struct puffs_node *pn;
    697 
    698 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    699 					pn->pn_po = pcn.pcn_po_full;
    700 				}
    701 			}
    702 
    703 			break;
    704 		}
    705 
    706 		case PUFFS_VN_MKNOD:
    707 		{
    708 			struct puffs_vnreq_mknod *auxt = auxbuf;
    709 			struct puffs_cn pcn;
    710 			if (pops->puffs_node_mknod == NULL) {
    711 				error = 0;
    712 				break;
    713 			}
    714 
    715 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    716 			if (buildpath) {
    717 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    718 				if (error)
    719 					break;
    720 			}
    721 
    722 			error = pops->puffs_node_mknod(pcc,
    723 			    opcookie, &auxt->pvnr_newnode,
    724 			    &pcn, &auxt->pvnr_va);
    725 
    726 			if (buildpath) {
    727 				if (error) {
    728 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    729 				} else {
    730 					struct puffs_node *pn;
    731 
    732 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    733 					pn->pn_po = pcn.pcn_po_full;
    734 				}
    735 			}
    736 
    737 			break;
    738 		}
    739 
    740 		case PUFFS_VN_OPEN:
    741 		{
    742 			struct puffs_vnreq_open *auxt = auxbuf;
    743 			if (pops->puffs_node_open == NULL) {
    744 				error = 0;
    745 				break;
    746 			}
    747 
    748 			error = pops->puffs_node_open(pcc,
    749 			    opcookie, auxt->pvnr_mode,
    750 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    751 			break;
    752 		}
    753 
    754 		case PUFFS_VN_CLOSE:
    755 		{
    756 			struct puffs_vnreq_close *auxt = auxbuf;
    757 			if (pops->puffs_node_close == NULL) {
    758 				error = 0;
    759 				break;
    760 			}
    761 
    762 			error = pops->puffs_node_close(pcc,
    763 			    opcookie, auxt->pvnr_fflag,
    764 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    765 			break;
    766 		}
    767 
    768 		case PUFFS_VN_ACCESS:
    769 		{
    770 			struct puffs_vnreq_access *auxt = auxbuf;
    771 			if (pops->puffs_node_access == NULL) {
    772 				error = 0;
    773 				break;
    774 			}
    775 
    776 			error = pops->puffs_node_access(pcc,
    777 			    opcookie, auxt->pvnr_mode,
    778 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    779 			break;
    780 		}
    781 
    782 		case PUFFS_VN_GETATTR:
    783 		{
    784 			struct puffs_vnreq_getattr *auxt = auxbuf;
    785 			if (pops->puffs_node_getattr == NULL) {
    786 				error = EOPNOTSUPP;
    787 				break;
    788 			}
    789 
    790 			error = pops->puffs_node_getattr(pcc,
    791 			    opcookie, &auxt->pvnr_va,
    792 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    793 			break;
    794 		}
    795 
    796 		case PUFFS_VN_SETATTR:
    797 		{
    798 			struct puffs_vnreq_setattr *auxt = auxbuf;
    799 			if (pops->puffs_node_setattr == NULL) {
    800 				error = EOPNOTSUPP;
    801 				break;
    802 			}
    803 
    804 			error = pops->puffs_node_setattr(pcc,
    805 			    opcookie, &auxt->pvnr_va,
    806 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    807 			break;
    808 		}
    809 
    810 		case PUFFS_VN_MMAP:
    811 		{
    812 			struct puffs_vnreq_mmap *auxt = auxbuf;
    813 			if (pops->puffs_node_mmap == NULL) {
    814 				error = 0;
    815 				break;
    816 			}
    817 
    818 			error = pops->puffs_node_mmap(pcc,
    819 			    opcookie, auxt->pvnr_fflags,
    820 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    821 			break;
    822 		}
    823 
    824 		case PUFFS_VN_FSYNC:
    825 		{
    826 			struct puffs_vnreq_fsync *auxt = auxbuf;
    827 			if (pops->puffs_node_fsync == NULL) {
    828 				error = 0;
    829 				break;
    830 			}
    831 
    832 			error = pops->puffs_node_fsync(pcc,
    833 			    opcookie, &auxt->pvnr_cred,
    834 			    auxt->pvnr_flags, auxt->pvnr_offlo,
    835 			    auxt->pvnr_offhi, auxt->pvnr_pid);
    836 			break;
    837 		}
    838 
    839 		case PUFFS_VN_SEEK:
    840 		{
    841 			struct puffs_vnreq_seek *auxt = auxbuf;
    842 			if (pops->puffs_node_seek == NULL) {
    843 				error = 0;
    844 				break;
    845 			}
    846 
    847 			error = pops->puffs_node_seek(pcc,
    848 			    opcookie, auxt->pvnr_oldoff,
    849 			    auxt->pvnr_newoff, &auxt->pvnr_cred);
    850 			break;
    851 		}
    852 
    853 		case PUFFS_VN_REMOVE:
    854 		{
    855 			struct puffs_vnreq_remove *auxt = auxbuf;
    856 			struct puffs_cn pcn;
    857 			if (pops->puffs_node_remove == NULL) {
    858 				error = 0;
    859 				break;
    860 			}
    861 
    862 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    863 
    864 			error = pops->puffs_node_remove(pcc,
    865 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    866 			break;
    867 		}
    868 
    869 		case PUFFS_VN_LINK:
    870 		{
    871 			struct puffs_vnreq_link *auxt = auxbuf;
    872 			struct puffs_cn pcn;
    873 			if (pops->puffs_node_link == NULL) {
    874 				error = 0;
    875 				break;
    876 			}
    877 
    878 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    879 			if (buildpath) {
    880 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    881 				if (error)
    882 					break;
    883 			}
    884 
    885 			error = pops->puffs_node_link(pcc,
    886 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    887 			if (buildpath)
    888 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
    889 
    890 			break;
    891 		}
    892 
    893 		case PUFFS_VN_RENAME:
    894 		{
    895 			struct puffs_vnreq_rename *auxt = auxbuf;
    896 			struct puffs_cn pcn_src, pcn_targ;
    897 			struct puffs_node *pn_src;
    898 
    899 			if (pops->puffs_node_rename == NULL) {
    900 				error = 0;
    901 				break;
    902 			}
    903 
    904 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
    905 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
    906 			if (buildpath) {
    907 				pn_src = auxt->pvnr_cookie_src;
    908 				pcn_src.pcn_po_full = pn_src->pn_po;
    909 
    910 				error = puffs_path_pcnbuild(pu, &pcn_targ,
    911 				    auxt->pvnr_cookie_targdir);
    912 				if (error)
    913 					break;
    914 			}
    915 
    916 			error = pops->puffs_node_rename(pcc,
    917 			    opcookie, auxt->pvnr_cookie_src,
    918 			    &pcn_src, auxt->pvnr_cookie_targdir,
    919 			    auxt->pvnr_cookie_targ, &pcn_targ);
    920 
    921 			if (buildpath) {
    922 				if (error) {
    923 					pu->pu_pathfree(pu,
    924 					    &pcn_targ.pcn_po_full);
    925 				} else {
    926 					struct puffs_pathinfo pi;
    927 					struct puffs_pathobj po_old;
    928 
    929 					/* handle this node */
    930 					po_old = pn_src->pn_po;
    931 					pn_src->pn_po = pcn_targ.pcn_po_full;
    932 
    933 					if (pn_src->pn_va.va_type != VDIR) {
    934 						pu->pu_pathfree(pu, &po_old);
    935 						break;
    936 					}
    937 
    938 					/* handle all child nodes for DIRs */
    939 					pi.pi_old = &pcn_src.pcn_po_full;
    940 					pi.pi_new = &pcn_targ.pcn_po_full;
    941 
    942 					if (puffs_pn_nodewalk(pu,
    943 					    puffs_path_prefixadj, &pi) != NULL)
    944 						error = ENOMEM;
    945 					pu->pu_pathfree(pu, &po_old);
    946 				}
    947 			}
    948 			break;
    949 		}
    950 
    951 		case PUFFS_VN_MKDIR:
    952 		{
    953 			struct puffs_vnreq_mkdir *auxt = auxbuf;
    954 			struct puffs_cn pcn;
    955 			if (pops->puffs_node_mkdir == NULL) {
    956 				error = 0;
    957 				break;
    958 			}
    959 
    960 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    961 			if (buildpath) {
    962 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    963 				if (error)
    964 					break;
    965 			}
    966 
    967 			error = pops->puffs_node_mkdir(pcc,
    968 			    opcookie, &auxt->pvnr_newnode,
    969 			    &pcn, &auxt->pvnr_va);
    970 
    971 			if (buildpath) {
    972 				if (error) {
    973 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    974 				} else {
    975 					struct puffs_node *pn;
    976 
    977 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    978 					pn->pn_po = pcn.pcn_po_full;
    979 				}
    980 			}
    981 
    982 			break;
    983 		}
    984 
    985 		case PUFFS_VN_RMDIR:
    986 		{
    987 			struct puffs_vnreq_rmdir *auxt = auxbuf;
    988 			struct puffs_cn pcn;
    989 			if (pops->puffs_node_rmdir == NULL) {
    990 				error = 0;
    991 				break;
    992 			}
    993 
    994 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    995 
    996 			error = pops->puffs_node_rmdir(pcc,
    997 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    998 			break;
    999 		}
   1000 
   1001 		case PUFFS_VN_SYMLINK:
   1002 		{
   1003 			struct puffs_vnreq_symlink *auxt = auxbuf;
   1004 			struct puffs_cn pcn;
   1005 			if (pops->puffs_node_symlink == NULL) {
   1006 				error = 0;
   1007 				break;
   1008 			}
   1009 
   1010 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1011 			if (buildpath) {
   1012 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
   1013 				if (error)
   1014 					break;
   1015 			}
   1016 
   1017 			error = pops->puffs_node_symlink(pcc,
   1018 			    opcookie, &auxt->pvnr_newnode,
   1019 			    &pcn, &auxt->pvnr_va, auxt->pvnr_link);
   1020 
   1021 			if (buildpath) {
   1022 				if (error) {
   1023 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
   1024 				} else {
   1025 					struct puffs_node *pn;
   1026 
   1027 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
   1028 					pn->pn_po = pcn.pcn_po_full;
   1029 				}
   1030 			}
   1031 
   1032 			break;
   1033 		}
   1034 
   1035 		case PUFFS_VN_READDIR:
   1036 		{
   1037 			struct puffs_vnreq_readdir *auxt = auxbuf;
   1038 			struct dirent *dent;
   1039 			off_t *cookies;
   1040 			size_t res, origcookies;
   1041 
   1042 			if (pops->puffs_node_readdir == NULL) {
   1043 				error = 0;
   1044 				break;
   1045 			}
   1046 
   1047 			if (auxt->pvnr_ncookies) {
   1048 				/* LINTED: pvnr_data is __aligned() */
   1049 				cookies = (off_t *)auxt->pvnr_data;
   1050 				origcookies = auxt->pvnr_ncookies;
   1051 			} else {
   1052 				cookies = NULL;
   1053 				origcookies = 0;
   1054 			}
   1055 			/* LINTED: dentoff is aligned in the kernel */
   1056 			dent = (struct dirent *)
   1057 			    (auxt->pvnr_data + auxt->pvnr_dentoff);
   1058 
   1059 			res = auxt->pvnr_resid;
   1060 			error = pops->puffs_node_readdir(pcc,
   1061 			    opcookie, dent, &auxt->pvnr_offset,
   1062 			    &auxt->pvnr_resid, &auxt->pvnr_cred,
   1063 			    &auxt->pvnr_eofflag, cookies, &auxt->pvnr_ncookies);
   1064 
   1065 			/* much easier to track non-working NFS */
   1066 			assert(auxt->pvnr_ncookies <= origcookies);
   1067 
   1068 			/* need to move a bit more */
   1069 			preq->preq_buflen = sizeof(struct puffs_vnreq_readdir)
   1070 			    + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
   1071 			break;
   1072 		}
   1073 
   1074 		case PUFFS_VN_READLINK:
   1075 		{
   1076 			struct puffs_vnreq_readlink *auxt = auxbuf;
   1077 			if (pops->puffs_node_readlink == NULL) {
   1078 				error = EOPNOTSUPP;
   1079 				break;
   1080 			}
   1081 
   1082 			error = pops->puffs_node_readlink(pcc,
   1083 			    opcookie, &auxt->pvnr_cred,
   1084 			    auxt->pvnr_link, &auxt->pvnr_linklen);
   1085 			break;
   1086 		}
   1087 
   1088 		case PUFFS_VN_RECLAIM:
   1089 		{
   1090 			struct puffs_vnreq_reclaim *auxt = auxbuf;
   1091 			if (pops->puffs_node_reclaim == NULL) {
   1092 				error = 0;
   1093 				break;
   1094 			}
   1095 
   1096 			error = pops->puffs_node_reclaim(pcc,
   1097 			    opcookie, auxt->pvnr_pid);
   1098 			break;
   1099 		}
   1100 
   1101 		case PUFFS_VN_INACTIVE:
   1102 		{
   1103 			struct puffs_vnreq_inactive *auxt = auxbuf;
   1104 			if (pops->puffs_node_inactive == NULL) {
   1105 				error = EOPNOTSUPP;
   1106 				break;
   1107 			}
   1108 
   1109 			error = pops->puffs_node_inactive(pcc,
   1110 			    opcookie, auxt->pvnr_pid,
   1111 			    &auxt->pvnr_backendrefs);
   1112 			break;
   1113 		}
   1114 
   1115 		case PUFFS_VN_PATHCONF:
   1116 		{
   1117 			struct puffs_vnreq_pathconf *auxt = auxbuf;
   1118 			if (pops->puffs_node_pathconf == NULL) {
   1119 				error = 0;
   1120 				break;
   1121 			}
   1122 
   1123 			error = pops->puffs_node_pathconf(pcc,
   1124 			    opcookie, auxt->pvnr_name,
   1125 			    &auxt->pvnr_retval);
   1126 			break;
   1127 		}
   1128 
   1129 		case PUFFS_VN_ADVLOCK:
   1130 		{
   1131 			struct puffs_vnreq_advlock *auxt = auxbuf;
   1132 			if (pops->puffs_node_advlock == NULL) {
   1133 				error = 0;
   1134 				break;
   1135 			}
   1136 
   1137 			error = pops->puffs_node_advlock(pcc,
   1138 			    opcookie, auxt->pvnr_id, auxt->pvnr_op,
   1139 			    &auxt->pvnr_fl, auxt->pvnr_flags);
   1140 			break;
   1141 		}
   1142 
   1143 		case PUFFS_VN_PRINT:
   1144 		{
   1145 			if (pops->puffs_node_print == NULL) {
   1146 				error = 0;
   1147 				break;
   1148 			}
   1149 
   1150 			error = pops->puffs_node_print(pcc,
   1151 			    opcookie);
   1152 			break;
   1153 		}
   1154 
   1155 		case PUFFS_VN_READ:
   1156 		{
   1157 			struct puffs_vnreq_read *auxt = auxbuf;
   1158 			size_t res;
   1159 
   1160 			if (pops->puffs_node_read == NULL) {
   1161 				error = EIO;
   1162 				break;
   1163 			}
   1164 
   1165 			res = auxt->pvnr_resid;
   1166 			error = pops->puffs_node_read(pcc,
   1167 			    opcookie, auxt->pvnr_data,
   1168 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1169 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1170 
   1171 			/* need to move a bit more */
   1172 			preq->preq_buflen = sizeof(struct puffs_vnreq_read)
   1173 			    + (res - auxt->pvnr_resid);
   1174 			break;
   1175 		}
   1176 
   1177 		case PUFFS_VN_WRITE:
   1178 		{
   1179 			struct puffs_vnreq_write *auxt = auxbuf;
   1180 
   1181 			if (pops->puffs_node_write == NULL) {
   1182 				error = EIO;
   1183 				break;
   1184 			}
   1185 
   1186 			error = pops->puffs_node_write(pcc,
   1187 			    opcookie, auxt->pvnr_data,
   1188 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1189 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1190 
   1191 			/* don't need to move data back to the kernel */
   1192 			preq->preq_buflen = sizeof(struct puffs_vnreq_write);
   1193 			break;
   1194 		}
   1195 
   1196 /* holy bitrot, ryydman! */
   1197 #if 0
   1198 		case PUFFS_VN_IOCTL:
   1199 			error = pops->puffs_node_ioctl1(pcc, opcookie,
   1200 			     (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1201 			if (error != 0)
   1202 				break;
   1203 			pop.pso_reqid = preq->preq_id;
   1204 
   1205 			/* let the kernel do it's intermediate duty */
   1206 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1207 			/*
   1208 			 * XXX: I don't actually know what the correct
   1209 			 * thing to do in case of an error is, so I'll
   1210 			 * just ignore it for the time being.
   1211 			 */
   1212 			error = pops->puffs_node_ioctl2(pcc, opcookie,
   1213 			    (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1214 			break;
   1215 
   1216 		case PUFFS_VN_FCNTL:
   1217 			error = pops->puffs_node_fcntl1(pcc, opcookie,
   1218 			     (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1219 			if (error != 0)
   1220 				break;
   1221 			pop.pso_reqid = preq->preq_id;
   1222 
   1223 			/* let the kernel do it's intermediate duty */
   1224 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1225 			/*
   1226 			 * XXX: I don't actually know what the correct
   1227 			 * thing to do in case of an error is, so I'll
   1228 			 * just ignore it for the time being.
   1229 			 */
   1230 			error = pops->puffs_node_fcntl2(pcc, opcookie,
   1231 			    (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1232 			break;
   1233 #endif
   1234 
   1235 		default:
   1236 			printf("inval op %d\n", preq->preq_optype);
   1237 			error = EINVAL;
   1238 			break;
   1239 		}
   1240 	} else {
   1241 		/*
   1242 		 * this one also
   1243 		 */
   1244 		error = EINVAL;
   1245 	}
   1246 
   1247 	preq->preq_rv = error;
   1248 
   1249 	pcc->pcc_rv = rv;
   1250 	pcc->pcc_flags |= PCC_DONE;
   1251 }
   1252 
   1253 
   1254 #if 0
   1255 		case PUFFS_VN_POLL:
   1256 		{
   1257 			struct puffs_vnreq_poll *auxt = auxbuf;
   1258 			if (pops->puffs_node_poll == NULL) {
   1259 				error = 0;
   1260 				break;
   1261 			}
   1262 
   1263 			error = pops->puffs_node_poll(pcc,
   1264 			    opcookie, preq-);
   1265 			break;
   1266 		}
   1267 
   1268 		case PUFFS_VN_KQFILTER:
   1269 		{
   1270 			struct puffs_vnreq_kqfilter *auxt = auxbuf;
   1271 			if (pops->puffs_node_kqfilter == NULL) {
   1272 				error = 0;
   1273 				break;
   1274 			}
   1275 
   1276 			error = pops->puffs_node_kqfilter(pcc,
   1277 			    opcookie, );
   1278 			break;
   1279 		}
   1280 
   1281 		case PUFFS_VN_CLOSEEXTATTR:
   1282 		{
   1283 			struct puffs_vnreq_closeextattr *auxt = auxbuf;
   1284 			if (pops->puffs_closeextattr == NULL) {
   1285 				error = 0;
   1286 				break;
   1287 			}
   1288 
   1289 			error = pops->puffs_closeextattr(pcc,
   1290 			    opcookie, );
   1291 			break;
   1292 		}
   1293 
   1294 		case PUFFS_VN_GETEXTATTR:
   1295 		{
   1296 			struct puffs_vnreq_getextattr *auxt = auxbuf;
   1297 			if (pops->puffs_getextattr == NULL) {
   1298 				error = 0;
   1299 				break;
   1300 			}
   1301 
   1302 			error = pops->puffs_getextattr(pcc,
   1303 			    opcookie, );
   1304 			break;
   1305 		}
   1306 
   1307 		case PUFFS_VN_LISTEXTATTR:
   1308 		{
   1309 			struct puffs_vnreq_listextattr *auxt = auxbuf;
   1310 			if (pops->puffs_listextattr == NULL) {
   1311 				error = 0;
   1312 				break;
   1313 			}
   1314 
   1315 			error = pops->puffs_listextattr(pcc,
   1316 			    opcookie, );
   1317 			break;
   1318 		}
   1319 
   1320 		case PUFFS_VN_OPENEXTATTR:
   1321 		{
   1322 			struct puffs_vnreq_openextattr *auxt = auxbuf;
   1323 			if (pops->puffs_openextattr == NULL) {
   1324 				error = 0;
   1325 				break;
   1326 			}
   1327 
   1328 			error = pops->puffs_openextattr(pcc,
   1329 			    opcookie, );
   1330 			break;
   1331 		}
   1332 
   1333 		case PUFFS_VN_DELETEEXTATTR:
   1334 		{
   1335 			struct puffs_vnreq_deleteextattr *auxt = auxbuf;
   1336 			if (pops->puffs_deleteextattr == NULL) {
   1337 				error = 0;
   1338 				break;
   1339 			}
   1340 
   1341 			error = pops->puffs_deleteextattr(pcc,
   1342 			    opcookie, );
   1343 			break;
   1344 		}
   1345 
   1346 		case PUFFS_VN_SETEXTATTR:
   1347 		{
   1348 			struct puffs_vnreq_setextattr *auxt = auxbuf;
   1349 			if (pops->puffs_setextattr == NULL) {
   1350 				error = 0;
   1351 				break;
   1352 			}
   1353 
   1354 			error = pops->puffs_setextattr(pcc,
   1355 			    opcookie, );
   1356 			break;
   1357 		}
   1358 
   1359 #endif
   1360