Home | History | Annotate | Line # | Download | only in libpuffs
puffs.c revision 1.44
      1 /*	$NetBSD: puffs.c,v 1.44 2007/05/11 21:27:13 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2005, 2006, 2007  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.44 2007/05/11 21:27:13 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 & PU_STATEMASK;
    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 (puffs_getstate(pu) != 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 (puffs_getstate(pu) != 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 (puffs_getstate(pu) != 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_SETSTATE(pu, 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 	LIST_INIT(&pu->pu_framectrl.fb_ios);
    329 
    330 	/* defaults for some user-settable translation functions */
    331 	pu->pu_cmap = NULL; /* identity translation */
    332 
    333 	pu->pu_pathbuild = puffs_stdpath_buildpath;
    334 	pu->pu_pathfree = puffs_stdpath_freepath;
    335 	pu->pu_pathcmp = puffs_stdpath_cmppath;
    336 	pu->pu_pathtransform = NULL;
    337 	pu->pu_namemod = NULL;
    338 
    339 	PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
    340 
    341 	return pu;
    342 
    343  failfree:
    344 	/* can't unmount() from here for obvious reasons */
    345 	close(fd);
    346 	free(pu);
    347 	return NULL;
    348 }
    349 
    350 struct puffs_usermount *
    351 _puffs_mount(int develv, struct puffs_ops *pops, const char *dir, int mntflags,
    352 	const char *puffsname, void *priv, uint32_t pflags)
    353 {
    354 	struct puffs_usermount *pu;
    355 	int sverrno;
    356 
    357 	pu = _puffs_init(develv, pops, puffsname, priv, pflags);
    358 	if (pu == NULL)
    359 		return NULL;
    360 
    361 	if (puffs_domount(pu, dir, mntflags) == -1) {
    362 		sverrno = errno;
    363 		puffs_exit(pu, 1);
    364 		errno = sverrno;
    365 		return NULL;
    366 	}
    367 
    368 	return pu;
    369 }
    370 
    371 int
    372 puffs_start(struct puffs_usermount *pu, void *rootcookie, struct statvfs *sbp)
    373 {
    374 	struct puffs_startreq sreq;
    375 
    376 	memset(&sreq, 0, sizeof(struct puffs_startreq));
    377 	sreq.psr_cookie = rootcookie;
    378 	sreq.psr_sb = *sbp;
    379 
    380 	/* tell kernel we're flying */
    381 	if (ioctl(pu->pu_kargs.pa_fd, PUFFSSTARTOP, &sreq) == -1)
    382 		return -1;
    383 
    384 	PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
    385 
    386 	return 0;
    387 }
    388 
    389 /*
    390  * XXX: there's currently no clean way to request unmount from
    391  * within the user server, so be very brutal about it.
    392  */
    393 /*ARGSUSED1*/
    394 int
    395 puffs_exit(struct puffs_usermount *pu, int force)
    396 {
    397 	struct puffs_node *pn;
    398 
    399 	force = 1; /* currently */
    400 
    401 	if (pu->pu_kargs.pa_fd)
    402 		close(pu->pu_kargs.pa_fd);
    403 
    404 	while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL)
    405 		puffs_pn_put(pn);
    406 
    407 	puffs_framebuf_exit(pu);
    408 	if (pu->pu_haskq)
    409 		close(pu->pu_kq);
    410 	free(pu);
    411 
    412 	return 0; /* always succesful for now, WILL CHANGE */
    413 }
    414 
    415 /*
    416  * XXX: should deal with errors way way way better
    417  */
    418 int
    419 puffs_mainloop(struct puffs_usermount *pu, int flags)
    420 {
    421 	struct puffs_getreq *pgr = NULL;
    422 	struct puffs_putreq *ppr = NULL;
    423 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
    424 	struct puffs_fctrl_io *fio;
    425 	struct kevent *curev, *newevs;
    426 	size_t nchanges;
    427 	int puffsfd, sverrno;
    428 	int ndone;
    429 
    430 	assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
    431 
    432 	pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
    433 	if (pgr == NULL)
    434 		goto out;
    435 
    436 	ppr = puffs_req_makeput(pu);
    437 	if (ppr == NULL)
    438 		goto out;
    439 
    440 	newevs = realloc(pfctrl->evs, (2*pfctrl->nfds+1)*sizeof(struct kevent));
    441 	if (newevs == NULL)
    442 		goto out;
    443 	pfctrl->evs = newevs;
    444 
    445 	if ((flags & PUFFSLOOP_NODAEMON) == 0)
    446 		if (daemon(1, 0) == -1)
    447 			goto out;
    448 	pu->pu_state |= PU_INLOOP;
    449 
    450 	pu->pu_kq = kqueue();
    451 	if (pu->pu_kq == -1)
    452 		goto out;
    453 	pu->pu_haskq = 1;
    454 
    455 	curev = pfctrl->evs;
    456 	LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) {
    457 		EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD,
    458 		    0, 0, (uintptr_t)fio);
    459 		curev++;
    460 		EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE,
    461 		    0, 0, (uintptr_t)fio);
    462 		curev++;
    463 	}
    464 	puffsfd = puffs_getselectable(pu);
    465 	EV_SET(curev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0);
    466 	if (kevent(pu->pu_kq, pfctrl->evs, 2*pfctrl->nfds+1,
    467 	    NULL, 0, NULL) == -1)
    468 		goto out;
    469 
    470 	while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
    471 		if (pfctrl->lfb)
    472 			pfctrl->lfb(pu);
    473 
    474 		/*
    475 		 * Build list of which to enable/disable in writecheck.
    476 		 * Don't bother worrying about O(n) for now.
    477 		 */
    478 		nchanges = 0;
    479 		LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) {
    480 			/*
    481 			 * Try to write out everything to avoid the
    482 			 * need for enabling EVFILT_WRITE.  The likely
    483 			 * case is that we can fit everything into the
    484 			 * socket buffer.
    485 			 */
    486 			if (puffs_framebuf_output(pu, pfctrl, fio) == -1)
    487 				goto out;
    488 
    489 			assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0);
    490 			if (FIO_EN_WRITE(fio)) {
    491 				EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
    492 				    EVFILT_WRITE, EV_ENABLE, 0, 0,
    493 				    (uintptr_t)fio);
    494 				fio->wrstat = 1; /* XXX: not before call */
    495 				nchanges++;
    496 			}
    497 			if (FIO_RM_WRITE(fio)) {
    498 				EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
    499 				    EVFILT_WRITE, EV_DISABLE, 0, 0,
    500 				    (uintptr_t)fio);
    501 				fio->wrstat = 0; /* XXX: not before call */
    502 				nchanges++;
    503 			}
    504 		}
    505 
    506 		ndone = kevent(pu->pu_kq, pfctrl->ch_evs, nchanges,
    507 		    pfctrl->evs, pfctrl->nfds+1, NULL);
    508 		if (ndone == -1)
    509 			goto out;
    510 
    511 		/* XXX: handle errors */
    512 
    513 		/* iterate over the results */
    514 		for (curev = pfctrl->evs; ndone--; curev++) {
    515 			/* get & possibly dispatch events from kernel */
    516 			if (curev->ident == puffsfd) {
    517 				if (puffs_req_handle(pgr, ppr, 0) == -1)
    518 					goto out;
    519 				continue;
    520 			}
    521 
    522 			if (curev->filter == EVFILT_READ) {
    523 				if (puffs_framebuf_input(pu, pfctrl,
    524 				    (void *)curev->udata, ppr) == -1)
    525 					goto out;
    526 			}
    527 
    528 			if (curev->filter == EVFILT_WRITE) {
    529 				if (puffs_framebuf_output(pu, pfctrl,
    530 				    (void *)curev->udata) == -1)
    531 					goto out;
    532 			}
    533 		}
    534 
    535 		/* stuff all replies from both of the above into kernel */
    536 		if (puffs_req_putput(ppr) == -1)
    537 			goto out;
    538 		puffs_req_resetput(ppr);
    539 	}
    540 	errno = 0;
    541 
    542  out:
    543 	/* store the real error for a while */
    544 	sverrno = errno;
    545 
    546 	if (ppr)
    547 		puffs_req_destroyput(ppr);
    548 	if (pgr)
    549 		puffs_req_destroyget(pgr);
    550 
    551 	errno = sverrno;
    552 	if (errno)
    553 		return -1;
    554 	else
    555 		return 0;
    556 }
    557 
    558 int
    559 puffs_dopreq(struct puffs_usermount *pu, struct puffs_req *preq,
    560 	struct puffs_putreq *ppr)
    561 {
    562 	struct puffs_cc fakecc;
    563 	struct puffs_cc *pcc;
    564 	int rv;
    565 
    566 	/*
    567 	 * XXX: the structure is currently a mess.  anyway, trap
    568 	 * the cacheops here already, since they don't need a cc.
    569 	 * I really should get around to revamping the operation
    570 	 * dispatching code one of these days.
    571 	 */
    572 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
    573 		struct puffs_cacheinfo *pci = (void *)preq;
    574 
    575 		if (pu->pu_ops.puffs_cache_write == NULL)
    576 			return 0;
    577 
    578 		pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
    579 		    pci->pcache_nruns, pci->pcache_runs);
    580 	}
    581 
    582 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    583 		puffsdump_req(preq);
    584 
    585 	if (puffs_fakecc) {
    586 		pcc = &fakecc;
    587 		pcc_init_local(pcc);
    588 
    589 		pcc->pcc_pu = pu;
    590 		pcc->pcc_preq = preq;
    591 		pcc->pcc_flags = PCC_FAKECC;
    592 	} else {
    593 		pcc = puffs_cc_create(pu);
    594 
    595 		/* XXX: temporary kludging */
    596 		pcc->pcc_preq = malloc(preq->preq_buflen);
    597 		if (pcc->pcc_preq == NULL)
    598 			return -1;
    599 		(void) memcpy(pcc->pcc_preq, preq, preq->preq_buflen);
    600 	}
    601 
    602 	rv = puffs_docc(pcc, ppr);
    603 
    604 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    605 		return 0;
    606 
    607 	return rv;
    608 }
    609 
    610 enum {PUFFCALL_ANSWER, PUFFCALL_IGNORE, PUFFCALL_AGAIN};
    611 
    612 int
    613 puffs_docc(struct puffs_cc *pcc, struct puffs_putreq *ppr)
    614 {
    615 	struct puffs_usermount *pu = pcc->pcc_pu;
    616 	int rv;
    617 
    618 	assert((pcc->pcc_flags & PCC_DONE) == 0);
    619 
    620 	if (pcc->pcc_flags & PCC_REALCC)
    621 		puffs_cc_continue(pcc);
    622 	else
    623 		puffs_calldispatcher(pcc);
    624 	rv = pcc->pcc_rv;
    625 
    626 	if ((pcc->pcc_flags & PCC_DONE) == 0)
    627 		rv = PUFFCALL_AGAIN;
    628 
    629 	/* check if we need to store this reply */
    630 	switch (rv) {
    631 	case PUFFCALL_ANSWER:
    632 		if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    633 			puffsdump_rv(pcc->pcc_preq);
    634 
    635 		if (pcc->pcc_flags & PCC_REALCC)
    636 			puffs_req_putcc(ppr, pcc);
    637 		else
    638 			puffs_req_put(ppr, pcc->pcc_preq);
    639 		break;
    640 	case PUFFCALL_IGNORE:
    641 		if (pcc->pcc_flags & PCC_REALCC)
    642 			puffs_cc_destroy(pcc);
    643 		break;
    644 	case PUFFCALL_AGAIN:
    645 		if (pcc->pcc_flags & PCC_FAKECC)
    646 			assert(pcc->pcc_flags & PCC_DONE);
    647 		break;
    648 	default:
    649 		assert(/*CONSTCOND*/0);
    650 	}
    651 
    652 	return 0;
    653 }
    654 
    655 /* library private, but linked from callcontext.c */
    656 
    657 void
    658 puffs_calldispatcher(struct puffs_cc *pcc)
    659 {
    660 	struct puffs_usermount *pu = pcc->pcc_pu;
    661 	struct puffs_ops *pops = &pu->pu_ops;
    662 	struct puffs_req *preq = pcc->pcc_preq;
    663 	void *auxbuf = preq; /* help with typecasting */
    664 	void *opcookie = preq->preq_cookie;
    665 	int error, rv, buildpath;
    666 
    667 	assert(pcc->pcc_flags & (PCC_FAKECC | PCC_REALCC));
    668 
    669 	if (PUFFSOP_WANTREPLY(preq->preq_opclass))
    670 		rv = PUFFCALL_ANSWER;
    671 	else
    672 		rv = PUFFCALL_IGNORE;
    673 
    674 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
    675 	preq->preq_setbacks = 0;
    676 
    677 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
    678 		switch (preq->preq_optype) {
    679 		case PUFFS_VFS_UNMOUNT:
    680 		{
    681 			struct puffs_vfsreq_unmount *auxt = auxbuf;
    682 
    683 			PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTING);
    684 			error = pops->puffs_fs_unmount(pcc,
    685 			    auxt->pvfsr_flags, auxt->pvfsr_pid);
    686 			if (!error)
    687 				PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
    688 			else
    689 				PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
    690 			break;
    691 		}
    692 
    693 		case PUFFS_VFS_STATVFS:
    694 		{
    695 			struct puffs_vfsreq_statvfs *auxt = auxbuf;
    696 
    697 			error = pops->puffs_fs_statvfs(pcc,
    698 			    &auxt->pvfsr_sb, auxt->pvfsr_pid);
    699 			break;
    700 		}
    701 
    702 		case PUFFS_VFS_SYNC:
    703 		{
    704 			struct puffs_vfsreq_sync *auxt = auxbuf;
    705 
    706 			error = pops->puffs_fs_sync(pcc,
    707 			    auxt->pvfsr_waitfor, &auxt->pvfsr_cred,
    708 			    auxt->pvfsr_pid);
    709 			break;
    710 		}
    711 
    712 		case PUFFS_VFS_FHTOVP:
    713 		{
    714 			struct puffs_vfsreq_fhtonode *auxt = auxbuf;
    715 
    716 			error = pops->puffs_fs_fhtonode(pcc, auxt->pvfsr_data,
    717 			    auxt->pvfsr_dsize, &auxt->pvfsr_fhcookie,
    718 			    &auxt->pvfsr_vtype, &auxt->pvfsr_size,
    719 			    &auxt->pvfsr_rdev);
    720 
    721 			break;
    722 		}
    723 
    724 		case PUFFS_VFS_VPTOFH:
    725 		{
    726 			struct puffs_vfsreq_nodetofh *auxt = auxbuf;
    727 
    728 			error = pops->puffs_fs_nodetofh(pcc,
    729 			    auxt->pvfsr_fhcookie, auxt->pvfsr_data,
    730 			    &auxt->pvfsr_dsize);
    731 
    732 			break;
    733 		}
    734 
    735 		case PUFFS_VFS_SUSPEND:
    736 		{
    737 			struct puffs_vfsreq_suspend *auxt = auxbuf;
    738 
    739 			error = 0;
    740 			if (pops->puffs_fs_suspend == NULL)
    741 				break;
    742 
    743 			pops->puffs_fs_suspend(pcc, auxt->pvfsr_status);
    744 			break;
    745 		}
    746 
    747 		default:
    748 			/*
    749 			 * I guess the kernel sees this one coming
    750 			 */
    751 			error = EINVAL;
    752 			break;
    753 		}
    754 
    755 	/* XXX: audit return values */
    756 	/* XXX: sync with kernel */
    757 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
    758 		switch (preq->preq_optype) {
    759 		case PUFFS_VN_LOOKUP:
    760 		{
    761 			struct puffs_vnreq_lookup *auxt = auxbuf;
    762 			struct puffs_cn pcn;
    763 
    764 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    765 			if (buildpath) {
    766 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    767 				if (error)
    768 					break;
    769 			}
    770 
    771 			/* lookup *must* be present */
    772 			error = pops->puffs_node_lookup(pcc, opcookie,
    773 			    &auxt->pvnr_newnode, &auxt->pvnr_vtype,
    774 			    &auxt->pvnr_size, &auxt->pvnr_rdev, &pcn);
    775 
    776 			if (buildpath) {
    777 				if (error) {
    778 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    779 				} else {
    780 					struct puffs_node *pn;
    781 
    782 					/*
    783 					 * did we get a new node or a
    784 					 * recycled node?
    785 					 */
    786 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    787 					if (pn->pn_po.po_path == NULL)
    788 						pn->pn_po = pcn.pcn_po_full;
    789 					else
    790 						pu->pu_pathfree(pu,
    791 						    &pcn.pcn_po_full);
    792 				}
    793 			}
    794 
    795 			break;
    796 		}
    797 
    798 		case PUFFS_VN_CREATE:
    799 		{
    800 			struct puffs_vnreq_create *auxt = auxbuf;
    801 			struct puffs_cn pcn;
    802 			if (pops->puffs_node_create == NULL) {
    803 				error = 0;
    804 				break;
    805 			}
    806 
    807 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    808 			if (buildpath) {
    809 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    810 				if (error)
    811 					break;
    812 			}
    813 
    814 			error = pops->puffs_node_create(pcc,
    815 			    opcookie, &auxt->pvnr_newnode,
    816 			    &pcn, &auxt->pvnr_va);
    817 
    818 			if (buildpath) {
    819 				if (error) {
    820 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    821 				} else {
    822 					struct puffs_node *pn;
    823 
    824 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    825 					pn->pn_po = pcn.pcn_po_full;
    826 				}
    827 			}
    828 
    829 			break;
    830 		}
    831 
    832 		case PUFFS_VN_MKNOD:
    833 		{
    834 			struct puffs_vnreq_mknod *auxt = auxbuf;
    835 			struct puffs_cn pcn;
    836 			if (pops->puffs_node_mknod == NULL) {
    837 				error = 0;
    838 				break;
    839 			}
    840 
    841 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    842 			if (buildpath) {
    843 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    844 				if (error)
    845 					break;
    846 			}
    847 
    848 			error = pops->puffs_node_mknod(pcc,
    849 			    opcookie, &auxt->pvnr_newnode,
    850 			    &pcn, &auxt->pvnr_va);
    851 
    852 			if (buildpath) {
    853 				if (error) {
    854 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    855 				} else {
    856 					struct puffs_node *pn;
    857 
    858 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    859 					pn->pn_po = pcn.pcn_po_full;
    860 				}
    861 			}
    862 
    863 			break;
    864 		}
    865 
    866 		case PUFFS_VN_OPEN:
    867 		{
    868 			struct puffs_vnreq_open *auxt = auxbuf;
    869 			if (pops->puffs_node_open == NULL) {
    870 				error = 0;
    871 				break;
    872 			}
    873 
    874 			error = pops->puffs_node_open(pcc,
    875 			    opcookie, auxt->pvnr_mode,
    876 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    877 			break;
    878 		}
    879 
    880 		case PUFFS_VN_CLOSE:
    881 		{
    882 			struct puffs_vnreq_close *auxt = auxbuf;
    883 			if (pops->puffs_node_close == NULL) {
    884 				error = 0;
    885 				break;
    886 			}
    887 
    888 			error = pops->puffs_node_close(pcc,
    889 			    opcookie, auxt->pvnr_fflag,
    890 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    891 			break;
    892 		}
    893 
    894 		case PUFFS_VN_ACCESS:
    895 		{
    896 			struct puffs_vnreq_access *auxt = auxbuf;
    897 			if (pops->puffs_node_access == NULL) {
    898 				error = 0;
    899 				break;
    900 			}
    901 
    902 			error = pops->puffs_node_access(pcc,
    903 			    opcookie, auxt->pvnr_mode,
    904 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    905 			break;
    906 		}
    907 
    908 		case PUFFS_VN_GETATTR:
    909 		{
    910 			struct puffs_vnreq_getattr *auxt = auxbuf;
    911 			if (pops->puffs_node_getattr == NULL) {
    912 				error = EOPNOTSUPP;
    913 				break;
    914 			}
    915 
    916 			error = pops->puffs_node_getattr(pcc,
    917 			    opcookie, &auxt->pvnr_va,
    918 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    919 			break;
    920 		}
    921 
    922 		case PUFFS_VN_SETATTR:
    923 		{
    924 			struct puffs_vnreq_setattr *auxt = auxbuf;
    925 			if (pops->puffs_node_setattr == NULL) {
    926 				error = EOPNOTSUPP;
    927 				break;
    928 			}
    929 
    930 			error = pops->puffs_node_setattr(pcc,
    931 			    opcookie, &auxt->pvnr_va,
    932 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    933 			break;
    934 		}
    935 
    936 		case PUFFS_VN_MMAP:
    937 		{
    938 			struct puffs_vnreq_mmap *auxt = auxbuf;
    939 			if (pops->puffs_node_mmap == NULL) {
    940 				error = 0;
    941 				break;
    942 			}
    943 
    944 			error = pops->puffs_node_mmap(pcc,
    945 			    opcookie, auxt->pvnr_fflags,
    946 			    &auxt->pvnr_cred, auxt->pvnr_pid);
    947 			break;
    948 		}
    949 
    950 		case PUFFS_VN_FSYNC:
    951 		{
    952 			struct puffs_vnreq_fsync *auxt = auxbuf;
    953 			if (pops->puffs_node_fsync == NULL) {
    954 				error = 0;
    955 				break;
    956 			}
    957 
    958 			error = pops->puffs_node_fsync(pcc,
    959 			    opcookie, &auxt->pvnr_cred,
    960 			    auxt->pvnr_flags, auxt->pvnr_offlo,
    961 			    auxt->pvnr_offhi, auxt->pvnr_pid);
    962 			break;
    963 		}
    964 
    965 		case PUFFS_VN_SEEK:
    966 		{
    967 			struct puffs_vnreq_seek *auxt = auxbuf;
    968 			if (pops->puffs_node_seek == NULL) {
    969 				error = 0;
    970 				break;
    971 			}
    972 
    973 			error = pops->puffs_node_seek(pcc,
    974 			    opcookie, auxt->pvnr_oldoff,
    975 			    auxt->pvnr_newoff, &auxt->pvnr_cred);
    976 			break;
    977 		}
    978 
    979 		case PUFFS_VN_REMOVE:
    980 		{
    981 			struct puffs_vnreq_remove *auxt = auxbuf;
    982 			struct puffs_cn pcn;
    983 			if (pops->puffs_node_remove == NULL) {
    984 				error = 0;
    985 				break;
    986 			}
    987 
    988 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    989 
    990 			error = pops->puffs_node_remove(pcc,
    991 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    992 			break;
    993 		}
    994 
    995 		case PUFFS_VN_LINK:
    996 		{
    997 			struct puffs_vnreq_link *auxt = auxbuf;
    998 			struct puffs_cn pcn;
    999 			if (pops->puffs_node_link == NULL) {
   1000 				error = 0;
   1001 				break;
   1002 			}
   1003 
   1004 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1005 			if (buildpath) {
   1006 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
   1007 				if (error)
   1008 					break;
   1009 			}
   1010 
   1011 			error = pops->puffs_node_link(pcc,
   1012 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
   1013 			if (buildpath)
   1014 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
   1015 
   1016 			break;
   1017 		}
   1018 
   1019 		case PUFFS_VN_RENAME:
   1020 		{
   1021 			struct puffs_vnreq_rename *auxt = auxbuf;
   1022 			struct puffs_cn pcn_src, pcn_targ;
   1023 			struct puffs_node *pn_src;
   1024 
   1025 			if (pops->puffs_node_rename == NULL) {
   1026 				error = 0;
   1027 				break;
   1028 			}
   1029 
   1030 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
   1031 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
   1032 			if (buildpath) {
   1033 				pn_src = auxt->pvnr_cookie_src;
   1034 				pcn_src.pcn_po_full = pn_src->pn_po;
   1035 
   1036 				error = puffs_path_pcnbuild(pu, &pcn_targ,
   1037 				    auxt->pvnr_cookie_targdir);
   1038 				if (error)
   1039 					break;
   1040 			}
   1041 
   1042 			error = pops->puffs_node_rename(pcc,
   1043 			    opcookie, auxt->pvnr_cookie_src,
   1044 			    &pcn_src, auxt->pvnr_cookie_targdir,
   1045 			    auxt->pvnr_cookie_targ, &pcn_targ);
   1046 
   1047 			if (buildpath) {
   1048 				if (error) {
   1049 					pu->pu_pathfree(pu,
   1050 					    &pcn_targ.pcn_po_full);
   1051 				} else {
   1052 					struct puffs_pathinfo pi;
   1053 					struct puffs_pathobj po_old;
   1054 
   1055 					/* handle this node */
   1056 					po_old = pn_src->pn_po;
   1057 					pn_src->pn_po = pcn_targ.pcn_po_full;
   1058 
   1059 					if (pn_src->pn_va.va_type != VDIR) {
   1060 						pu->pu_pathfree(pu, &po_old);
   1061 						break;
   1062 					}
   1063 
   1064 					/* handle all child nodes for DIRs */
   1065 					pi.pi_old = &pcn_src.pcn_po_full;
   1066 					pi.pi_new = &pcn_targ.pcn_po_full;
   1067 
   1068 					if (puffs_pn_nodewalk(pu,
   1069 					    puffs_path_prefixadj, &pi) != NULL)
   1070 						error = ENOMEM;
   1071 					pu->pu_pathfree(pu, &po_old);
   1072 				}
   1073 			}
   1074 			break;
   1075 		}
   1076 
   1077 		case PUFFS_VN_MKDIR:
   1078 		{
   1079 			struct puffs_vnreq_mkdir *auxt = auxbuf;
   1080 			struct puffs_cn pcn;
   1081 			if (pops->puffs_node_mkdir == NULL) {
   1082 				error = 0;
   1083 				break;
   1084 			}
   1085 
   1086 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1087 			if (buildpath) {
   1088 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
   1089 				if (error)
   1090 					break;
   1091 			}
   1092 
   1093 			error = pops->puffs_node_mkdir(pcc,
   1094 			    opcookie, &auxt->pvnr_newnode,
   1095 			    &pcn, &auxt->pvnr_va);
   1096 
   1097 			if (buildpath) {
   1098 				if (error) {
   1099 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
   1100 				} else {
   1101 					struct puffs_node *pn;
   1102 
   1103 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
   1104 					pn->pn_po = pcn.pcn_po_full;
   1105 				}
   1106 			}
   1107 
   1108 			break;
   1109 		}
   1110 
   1111 		case PUFFS_VN_RMDIR:
   1112 		{
   1113 			struct puffs_vnreq_rmdir *auxt = auxbuf;
   1114 			struct puffs_cn pcn;
   1115 			if (pops->puffs_node_rmdir == NULL) {
   1116 				error = 0;
   1117 				break;
   1118 			}
   1119 
   1120 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1121 
   1122 			error = pops->puffs_node_rmdir(pcc,
   1123 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
   1124 			break;
   1125 		}
   1126 
   1127 		case PUFFS_VN_SYMLINK:
   1128 		{
   1129 			struct puffs_vnreq_symlink *auxt = auxbuf;
   1130 			struct puffs_cn pcn;
   1131 			if (pops->puffs_node_symlink == NULL) {
   1132 				error = 0;
   1133 				break;
   1134 			}
   1135 
   1136 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
   1137 			if (buildpath) {
   1138 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
   1139 				if (error)
   1140 					break;
   1141 			}
   1142 
   1143 			error = pops->puffs_node_symlink(pcc,
   1144 			    opcookie, &auxt->pvnr_newnode,
   1145 			    &pcn, &auxt->pvnr_va, auxt->pvnr_link);
   1146 
   1147 			if (buildpath) {
   1148 				if (error) {
   1149 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
   1150 				} else {
   1151 					struct puffs_node *pn;
   1152 
   1153 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
   1154 					pn->pn_po = pcn.pcn_po_full;
   1155 				}
   1156 			}
   1157 
   1158 			break;
   1159 		}
   1160 
   1161 		case PUFFS_VN_READDIR:
   1162 		{
   1163 			struct puffs_vnreq_readdir *auxt = auxbuf;
   1164 			struct dirent *dent;
   1165 			off_t *cookies;
   1166 			size_t res, origcookies;
   1167 
   1168 			if (pops->puffs_node_readdir == NULL) {
   1169 				error = 0;
   1170 				break;
   1171 			}
   1172 
   1173 			if (auxt->pvnr_ncookies) {
   1174 				/* LINTED: pvnr_data is __aligned() */
   1175 				cookies = (off_t *)auxt->pvnr_data;
   1176 				origcookies = auxt->pvnr_ncookies;
   1177 			} else {
   1178 				cookies = NULL;
   1179 				origcookies = 0;
   1180 			}
   1181 			/* LINTED: dentoff is aligned in the kernel */
   1182 			dent = (struct dirent *)
   1183 			    (auxt->pvnr_data + auxt->pvnr_dentoff);
   1184 
   1185 			res = auxt->pvnr_resid;
   1186 			error = pops->puffs_node_readdir(pcc,
   1187 			    opcookie, dent, &auxt->pvnr_offset,
   1188 			    &auxt->pvnr_resid, &auxt->pvnr_cred,
   1189 			    &auxt->pvnr_eofflag, cookies, &auxt->pvnr_ncookies);
   1190 
   1191 			/* much easier to track non-working NFS */
   1192 			assert(auxt->pvnr_ncookies <= origcookies);
   1193 
   1194 			/* need to move a bit more */
   1195 			preq->preq_buflen = sizeof(struct puffs_vnreq_readdir)
   1196 			    + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
   1197 			break;
   1198 		}
   1199 
   1200 		case PUFFS_VN_READLINK:
   1201 		{
   1202 			struct puffs_vnreq_readlink *auxt = auxbuf;
   1203 			if (pops->puffs_node_readlink == NULL) {
   1204 				error = EOPNOTSUPP;
   1205 				break;
   1206 			}
   1207 
   1208 			error = pops->puffs_node_readlink(pcc,
   1209 			    opcookie, &auxt->pvnr_cred,
   1210 			    auxt->pvnr_link, &auxt->pvnr_linklen);
   1211 			break;
   1212 		}
   1213 
   1214 		case PUFFS_VN_RECLAIM:
   1215 		{
   1216 			struct puffs_vnreq_reclaim *auxt = auxbuf;
   1217 			if (pops->puffs_node_reclaim == NULL) {
   1218 				error = 0;
   1219 				break;
   1220 			}
   1221 
   1222 			error = pops->puffs_node_reclaim(pcc,
   1223 			    opcookie, auxt->pvnr_pid);
   1224 			break;
   1225 		}
   1226 
   1227 		case PUFFS_VN_INACTIVE:
   1228 		{
   1229 			struct puffs_vnreq_inactive *auxt = auxbuf;
   1230 			if (pops->puffs_node_inactive == NULL) {
   1231 				error = EOPNOTSUPP;
   1232 				break;
   1233 			}
   1234 
   1235 			error = pops->puffs_node_inactive(pcc,
   1236 			    opcookie, auxt->pvnr_pid,
   1237 			    &auxt->pvnr_backendrefs);
   1238 			break;
   1239 		}
   1240 
   1241 		case PUFFS_VN_PATHCONF:
   1242 		{
   1243 			struct puffs_vnreq_pathconf *auxt = auxbuf;
   1244 			if (pops->puffs_node_pathconf == NULL) {
   1245 				error = 0;
   1246 				break;
   1247 			}
   1248 
   1249 			error = pops->puffs_node_pathconf(pcc,
   1250 			    opcookie, auxt->pvnr_name,
   1251 			    &auxt->pvnr_retval);
   1252 			break;
   1253 		}
   1254 
   1255 		case PUFFS_VN_ADVLOCK:
   1256 		{
   1257 			struct puffs_vnreq_advlock *auxt = auxbuf;
   1258 			if (pops->puffs_node_advlock == NULL) {
   1259 				error = 0;
   1260 				break;
   1261 			}
   1262 
   1263 			error = pops->puffs_node_advlock(pcc,
   1264 			    opcookie, auxt->pvnr_id, auxt->pvnr_op,
   1265 			    &auxt->pvnr_fl, auxt->pvnr_flags);
   1266 			break;
   1267 		}
   1268 
   1269 		case PUFFS_VN_PRINT:
   1270 		{
   1271 			if (pops->puffs_node_print == NULL) {
   1272 				error = 0;
   1273 				break;
   1274 			}
   1275 
   1276 			error = pops->puffs_node_print(pcc,
   1277 			    opcookie);
   1278 			break;
   1279 		}
   1280 
   1281 		case PUFFS_VN_READ:
   1282 		{
   1283 			struct puffs_vnreq_read *auxt = auxbuf;
   1284 			size_t res;
   1285 
   1286 			if (pops->puffs_node_read == NULL) {
   1287 				error = EIO;
   1288 				break;
   1289 			}
   1290 
   1291 			res = auxt->pvnr_resid;
   1292 			error = pops->puffs_node_read(pcc,
   1293 			    opcookie, auxt->pvnr_data,
   1294 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1295 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1296 
   1297 			/* need to move a bit more */
   1298 			preq->preq_buflen = sizeof(struct puffs_vnreq_read)
   1299 			    + (res - auxt->pvnr_resid);
   1300 			break;
   1301 		}
   1302 
   1303 		case PUFFS_VN_WRITE:
   1304 		{
   1305 			struct puffs_vnreq_write *auxt = auxbuf;
   1306 
   1307 			if (pops->puffs_node_write == NULL) {
   1308 				error = EIO;
   1309 				break;
   1310 			}
   1311 
   1312 			error = pops->puffs_node_write(pcc,
   1313 			    opcookie, auxt->pvnr_data,
   1314 			    auxt->pvnr_offset, &auxt->pvnr_resid,
   1315 			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
   1316 
   1317 			/* don't need to move data back to the kernel */
   1318 			preq->preq_buflen = sizeof(struct puffs_vnreq_write);
   1319 			break;
   1320 		}
   1321 
   1322 /* holy bitrot, ryydman! */
   1323 #if 0
   1324 		case PUFFS_VN_IOCTL:
   1325 			error = pops->puffs_node_ioctl1(pcc, opcookie,
   1326 			     (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1327 			if (error != 0)
   1328 				break;
   1329 			pop.pso_reqid = preq->preq_id;
   1330 
   1331 			/* let the kernel do it's intermediate duty */
   1332 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1333 			/*
   1334 			 * XXX: I don't actually know what the correct
   1335 			 * thing to do in case of an error is, so I'll
   1336 			 * just ignore it for the time being.
   1337 			 */
   1338 			error = pops->puffs_node_ioctl2(pcc, opcookie,
   1339 			    (struct puffs_vnreq_ioctl *)auxbuf, &pop);
   1340 			break;
   1341 
   1342 		case PUFFS_VN_FCNTL:
   1343 			error = pops->puffs_node_fcntl1(pcc, opcookie,
   1344 			     (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1345 			if (error != 0)
   1346 				break;
   1347 			pop.pso_reqid = preq->preq_id;
   1348 
   1349 			/* let the kernel do it's intermediate duty */
   1350 			error = ioctl(pu->pu_kargs.pa_fd, PUFFSSIZEOP, &pop);
   1351 			/*
   1352 			 * XXX: I don't actually know what the correct
   1353 			 * thing to do in case of an error is, so I'll
   1354 			 * just ignore it for the time being.
   1355 			 */
   1356 			error = pops->puffs_node_fcntl2(pcc, opcookie,
   1357 			    (struct puffs_vnreq_fcntl *)auxbuf, &pop);
   1358 			break;
   1359 #endif
   1360 
   1361 		default:
   1362 			printf("inval op %d\n", preq->preq_optype);
   1363 			error = EINVAL;
   1364 			break;
   1365 		}
   1366 	} else {
   1367 		/*
   1368 		 * this one also
   1369 		 */
   1370 		error = EINVAL;
   1371 	}
   1372 
   1373 	preq->preq_rv = error;
   1374 
   1375 	pcc->pcc_rv = rv;
   1376 	pcc->pcc_flags |= PCC_DONE;
   1377 }
   1378 
   1379 
   1380 #if 0
   1381 		case PUFFS_VN_POLL:
   1382 		{
   1383 			struct puffs_vnreq_poll *auxt = auxbuf;
   1384 			if (pops->puffs_node_poll == NULL) {
   1385 				error = 0;
   1386 				break;
   1387 			}
   1388 
   1389 			error = pops->puffs_node_poll(pcc,
   1390 			    opcookie, preq-);
   1391 			break;
   1392 		}
   1393 
   1394 		case PUFFS_VN_KQFILTER:
   1395 		{
   1396 			struct puffs_vnreq_kqfilter *auxt = auxbuf;
   1397 			if (pops->puffs_node_kqfilter == NULL) {
   1398 				error = 0;
   1399 				break;
   1400 			}
   1401 
   1402 			error = pops->puffs_node_kqfilter(pcc,
   1403 			    opcookie, );
   1404 			break;
   1405 		}
   1406 
   1407 		case PUFFS_VN_CLOSEEXTATTR:
   1408 		{
   1409 			struct puffs_vnreq_closeextattr *auxt = auxbuf;
   1410 			if (pops->puffs_closeextattr == NULL) {
   1411 				error = 0;
   1412 				break;
   1413 			}
   1414 
   1415 			error = pops->puffs_closeextattr(pcc,
   1416 			    opcookie, );
   1417 			break;
   1418 		}
   1419 
   1420 		case PUFFS_VN_GETEXTATTR:
   1421 		{
   1422 			struct puffs_vnreq_getextattr *auxt = auxbuf;
   1423 			if (pops->puffs_getextattr == NULL) {
   1424 				error = 0;
   1425 				break;
   1426 			}
   1427 
   1428 			error = pops->puffs_getextattr(pcc,
   1429 			    opcookie, );
   1430 			break;
   1431 		}
   1432 
   1433 		case PUFFS_VN_LISTEXTATTR:
   1434 		{
   1435 			struct puffs_vnreq_listextattr *auxt = auxbuf;
   1436 			if (pops->puffs_listextattr == NULL) {
   1437 				error = 0;
   1438 				break;
   1439 			}
   1440 
   1441 			error = pops->puffs_listextattr(pcc,
   1442 			    opcookie, );
   1443 			break;
   1444 		}
   1445 
   1446 		case PUFFS_VN_OPENEXTATTR:
   1447 		{
   1448 			struct puffs_vnreq_openextattr *auxt = auxbuf;
   1449 			if (pops->puffs_openextattr == NULL) {
   1450 				error = 0;
   1451 				break;
   1452 			}
   1453 
   1454 			error = pops->puffs_openextattr(pcc,
   1455 			    opcookie, );
   1456 			break;
   1457 		}
   1458 
   1459 		case PUFFS_VN_DELETEEXTATTR:
   1460 		{
   1461 			struct puffs_vnreq_deleteextattr *auxt = auxbuf;
   1462 			if (pops->puffs_deleteextattr == NULL) {
   1463 				error = 0;
   1464 				break;
   1465 			}
   1466 
   1467 			error = pops->puffs_deleteextattr(pcc,
   1468 			    opcookie, );
   1469 			break;
   1470 		}
   1471 
   1472 		case PUFFS_VN_SETEXTATTR:
   1473 		{
   1474 			struct puffs_vnreq_setextattr *auxt = auxbuf;
   1475 			if (pops->puffs_setextattr == NULL) {
   1476 				error = 0;
   1477 				break;
   1478 			}
   1479 
   1480 			error = pops->puffs_setextattr(pcc,
   1481 			    opcookie, );
   1482 			break;
   1483 		}
   1484 
   1485 #endif
   1486