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