Home | History | Annotate | Line # | Download | only in libpuffs
dispatcher.c revision 1.32.4.2
      1 /*	$NetBSD: dispatcher.c,v 1.32.4.2 2011/07/17 15:36:03 riz Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006, 2007, 2008 Antti Kantee.  All Rights Reserved.
      5  *
      6  * Development of this software was supported by the
      7  * Ulla Tuominen Foundation, the Finnish Cultural Foundation and
      8  * Research Foundation of Helsinki University of Technology.
      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  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #if !defined(lint)
     34 __RCSID("$NetBSD: dispatcher.c,v 1.32.4.2 2011/07/17 15:36:03 riz Exp $");
     35 #endif /* !lint */
     36 
     37 #include <sys/types.h>
     38 #include <sys/poll.h>
     39 
     40 #include <assert.h>
     41 #include <errno.h>
     42 #ifdef PUFFS_WITH_THREADS
     43 #include <pthread.h>
     44 #endif
     45 #include <puffs.h>
     46 #include <puffsdump.h>
     47 #include <stdio.h>
     48 #include <stdlib.h>
     49 #include <unistd.h>
     50 
     51 #include "puffs_priv.h"
     52 
     53 #if 0 /* me not worka now */
     54 /*
     55  * Set the following to 1 to handle each request in a separate pthread.
     56  * This is not exported as it should not be used yet unless having a
     57  * very good knowledge of what you're signing up for (libpuffs is not
     58  * threadsafe).
     59  */
     60 int puffs_usethreads;
     61 #endif
     62 
     63 static void dispatch(struct puffs_cc *);
     64 
     65 /* for our eyes only */
     66 void
     67 puffs__ml_dispatch(struct puffs_usermount *pu, struct puffs_framebuf *pb)
     68 {
     69 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
     70 	struct puffs_req *preq;
     71 
     72 	pcc->pcc_pb = pb;
     73 	pcc->pcc_flags |= PCC_MLCONT;
     74 	dispatch(pcc);
     75 
     76 	/* Put result to kernel sendqueue if necessary */
     77 	preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
     78 	if (PUFFSOP_WANTREPLY(preq->preq_opclass)) {
     79 		if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
     80 			puffsdump_rv(preq);
     81 
     82 		puffs_framev_enqueue_justsend(pu, pu->pu_fd,
     83 		    pcc->pcc_pb, 0, 0);
     84 	} else {
     85 		puffs_framebuf_destroy(pcc->pcc_pb);
     86 	}
     87 
     88 	/* who needs information when you're living on borrowed time? */
     89 	if (pcc->pcc_flags & PCC_BORROWED) {
     90 		puffs_cc_yield(pcc); /* back to borrow source */
     91 	}
     92 	pcc->pcc_flags = 0;
     93 }
     94 
     95 /* public, but not really tested and only semi-supported */
     96 int
     97 puffs_dispatch_create(struct puffs_usermount *pu, struct puffs_framebuf *pb,
     98 	struct puffs_cc **pccp)
     99 {
    100 	struct puffs_cc *pcc;
    101 
    102 	if (puffs__cc_create(pu, dispatch, &pcc) == -1)
    103 		return -1;
    104 
    105 	pcc->pcc_pb = pb;
    106 	*pccp = pcc;
    107 
    108 	return 0;
    109 }
    110 
    111 int
    112 puffs_dispatch_exec(struct puffs_cc *pcc, struct puffs_framebuf **pbp)
    113 {
    114 	int rv;
    115 
    116 	puffs_cc_continue(pcc);
    117 
    118 	if (pcc->pcc_flags & PCC_DONE) {
    119 		rv = 1;
    120 		*pbp = pcc->pcc_pb;
    121 		pcc->pcc_flags = 0;
    122 		puffs__cc_destroy(pcc, 0);
    123 	} else {
    124 		rv = 0;
    125 	}
    126 
    127 	return rv;
    128 }
    129 
    130 static void
    131 dispatch(struct puffs_cc *pcc)
    132 {
    133 	struct puffs_usermount *pu = pcc->pcc_pu;
    134 	struct puffs_ops *pops = &pu->pu_ops;
    135 	struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
    136 	void *auxbuf; /* help with typecasting */
    137 	puffs_cookie_t opcookie;
    138 	int error = 0, buildpath;
    139 
    140 	/* XXX: smaller hammer, please */
    141 	if ((PUFFSOP_OPCLASS(preq->preq_opclass == PUFFSOP_VFS &&
    142 	    preq->preq_optype == PUFFS_VFS_VPTOFH)) ||
    143 	    (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN &&
    144 	    (preq->preq_optype == PUFFS_VN_READDIR
    145 	    || preq->preq_optype == PUFFS_VN_READ))) {
    146 		if (puffs_framebuf_reserve_space(pcc->pcc_pb,
    147 		    PUFFS_MSG_MAXSIZE) == -1)
    148 			error = errno;
    149 		preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
    150 	}
    151 
    152 	auxbuf = preq;
    153 	opcookie = preq->preq_cookie;
    154 
    155 	assert((pcc->pcc_flags & PCC_DONE) == 0);
    156 
    157 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
    158 	preq->preq_setbacks = 0;
    159 
    160 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
    161 		puffsdump_req(preq);
    162 
    163 	puffs__cc_setcaller(pcc, preq->preq_pid, preq->preq_lid);
    164 
    165 	/* pre-operation */
    166 	if (pu->pu_oppre)
    167 		pu->pu_oppre(pu);
    168 
    169 	if (error)
    170 		goto out;
    171 
    172 	/* Execute actual operation */
    173 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
    174 		switch (preq->preq_optype) {
    175 		case PUFFS_VFS_UNMOUNT:
    176 		{
    177 			struct puffs_vfsmsg_unmount *auxt = auxbuf;
    178 
    179 			PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTING);
    180 			error = pops->puffs_fs_unmount(pu, auxt->pvfsr_flags);
    181 			if (!error)
    182 				PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
    183 			else
    184 				PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
    185 			break;
    186 		}
    187 
    188 		case PUFFS_VFS_STATVFS:
    189 		{
    190 			struct puffs_vfsmsg_statvfs *auxt = auxbuf;
    191 
    192 			error = pops->puffs_fs_statvfs(pu, &auxt->pvfsr_sb);
    193 			break;
    194 		}
    195 
    196 		case PUFFS_VFS_SYNC:
    197 		{
    198 			struct puffs_vfsmsg_sync *auxt = auxbuf;
    199 			PUFFS_MAKECRED(pcr, &auxt->pvfsr_cred);
    200 
    201 			error = pops->puffs_fs_sync(pu,
    202 			    auxt->pvfsr_waitfor, pcr);
    203 			break;
    204 		}
    205 
    206 		case PUFFS_VFS_FHTOVP:
    207 		{
    208 			struct puffs_vfsmsg_fhtonode *auxt = auxbuf;
    209 			struct puffs_newinfo pni;
    210 
    211 			pni.pni_cookie = &auxt->pvfsr_fhcookie;
    212 			pni.pni_vtype = &auxt->pvfsr_vtype;
    213 			pni.pni_size = &auxt->pvfsr_size;
    214 			pni.pni_rdev = &auxt->pvfsr_rdev;
    215 
    216 			error = pops->puffs_fs_fhtonode(pu, auxt->pvfsr_data,
    217 			    auxt->pvfsr_dsize, &pni);
    218 
    219 			break;
    220 		}
    221 
    222 		case PUFFS_VFS_VPTOFH:
    223 		{
    224 			struct puffs_vfsmsg_nodetofh *auxt = auxbuf;
    225 
    226 			error = pops->puffs_fs_nodetofh(pu,
    227 			    auxt->pvfsr_fhcookie, auxt->pvfsr_data,
    228 			    &auxt->pvfsr_dsize);
    229 
    230 			break;
    231 		}
    232 
    233 		case PUFFS_VFS_SUSPEND:
    234 		{
    235 			struct puffs_vfsmsg_suspend *auxt = auxbuf;
    236 
    237 			error = 0;
    238 			if (pops->puffs_fs_suspend == NULL)
    239 				break;
    240 
    241 			pops->puffs_fs_suspend(pu, auxt->pvfsr_status);
    242 			break;
    243 		}
    244 
    245 		case PUFFS_VFS_EXTATTRCTL:
    246 		{
    247 			struct puffs_vfsmsg_extattrctl *auxt = auxbuf;
    248 			const char *attrname;
    249 			int flags;
    250 
    251 			if (pops->puffs_fs_extattrctl == NULL) {
    252 				error = EOPNOTSUPP;
    253 				break;
    254 			}
    255 
    256 			if (auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASATTRNAME)
    257 				attrname = auxt->pvfsr_attrname;
    258 			else
    259 				attrname = NULL;
    260 
    261 			flags = auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASNODE;
    262 			error = pops->puffs_fs_extattrctl(pu, auxt->pvfsr_cmd,
    263 			    opcookie, flags,
    264 			    auxt->pvfsr_attrnamespace, attrname);
    265 			break;
    266 		}
    267 
    268 		default:
    269 			/*
    270 			 * I guess the kernel sees this one coming
    271 			 */
    272 			error = EINVAL;
    273 			break;
    274 		}
    275 
    276 	/* XXX: audit return values */
    277 	/* XXX: sync with kernel */
    278 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
    279 		switch (preq->preq_optype) {
    280 		case PUFFS_VN_LOOKUP:
    281 		{
    282 			struct puffs_vnmsg_lookup *auxt = auxbuf;
    283 			struct puffs_newinfo pni;
    284 			struct puffs_cn pcn;
    285 
    286 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    287 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    288 			pni.pni_cookie = &auxt->pvnr_newnode;
    289 			pni.pni_vtype = &auxt->pvnr_vtype;
    290 			pni.pni_size = &auxt->pvnr_size;
    291 			pni.pni_rdev = &auxt->pvnr_rdev;
    292 
    293 			if (buildpath) {
    294 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    295 				if (error)
    296 					break;
    297 			}
    298 
    299 			/* lookup *must* be present */
    300 			error = pops->puffs_node_lookup(pu, opcookie,
    301 			    &pni, &pcn);
    302 
    303 			if (buildpath) {
    304 				if (error) {
    305 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    306 				} else {
    307 					struct puffs_node *pn;
    308 
    309 					/*
    310 					 * did we get a new node or a
    311 					 * recycled node?
    312 					 */
    313 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    314 					if (pn->pn_po.po_path == NULL)
    315 						pn->pn_po = pcn.pcn_po_full;
    316 					else
    317 						pu->pu_pathfree(pu,
    318 						    &pcn.pcn_po_full);
    319 				}
    320 			}
    321 
    322 			break;
    323 		}
    324 
    325 		case PUFFS_VN_CREATE:
    326 		{
    327 			struct puffs_vnmsg_create *auxt = auxbuf;
    328 			struct puffs_newinfo pni;
    329 			struct puffs_cn pcn;
    330 
    331 			if (pops->puffs_node_create == NULL) {
    332 				error = 0;
    333 				break;
    334 			}
    335 
    336 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    337 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    338 
    339 			memset(&pni, 0, sizeof(pni));
    340 			pni.pni_cookie = &auxt->pvnr_newnode;
    341 
    342 			if (buildpath) {
    343 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    344 				if (error)
    345 					break;
    346 			}
    347 
    348 			error = pops->puffs_node_create(pu,
    349 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
    350 
    351 			if (buildpath) {
    352 				if (error) {
    353 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    354 				} else {
    355 					struct puffs_node *pn;
    356 
    357 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    358 					pn->pn_po = pcn.pcn_po_full;
    359 				}
    360 			}
    361 
    362 			break;
    363 		}
    364 
    365 		case PUFFS_VN_MKNOD:
    366 		{
    367 			struct puffs_vnmsg_mknod *auxt = auxbuf;
    368 			struct puffs_newinfo pni;
    369 			struct puffs_cn pcn;
    370 
    371 			if (pops->puffs_node_mknod == NULL) {
    372 				error = 0;
    373 				break;
    374 			}
    375 
    376 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    377 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    378 
    379 			memset(&pni, 0, sizeof(pni));
    380 			pni.pni_cookie = &auxt->pvnr_newnode;
    381 
    382 			if (buildpath) {
    383 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    384 				if (error)
    385 					break;
    386 			}
    387 
    388 			error = pops->puffs_node_mknod(pu,
    389 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
    390 
    391 			if (buildpath) {
    392 				if (error) {
    393 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    394 				} else {
    395 					struct puffs_node *pn;
    396 
    397 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    398 					pn->pn_po = pcn.pcn_po_full;
    399 				}
    400 			}
    401 
    402 			break;
    403 		}
    404 
    405 		case PUFFS_VN_OPEN:
    406 		{
    407 			struct puffs_vnmsg_open *auxt = auxbuf;
    408 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    409 
    410 			if (pops->puffs_node_open == NULL) {
    411 				error = 0;
    412 				break;
    413 			}
    414 
    415 			error = pops->puffs_node_open(pu,
    416 			    opcookie, auxt->pvnr_mode, pcr);
    417 			break;
    418 		}
    419 
    420 		case PUFFS_VN_CLOSE:
    421 		{
    422 			struct puffs_vnmsg_close *auxt = auxbuf;
    423 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    424 
    425 			if (pops->puffs_node_close == NULL) {
    426 				error = 0;
    427 				break;
    428 			}
    429 
    430 			error = pops->puffs_node_close(pu,
    431 			    opcookie, auxt->pvnr_fflag, pcr);
    432 			break;
    433 		}
    434 
    435 		case PUFFS_VN_ACCESS:
    436 		{
    437 			struct puffs_vnmsg_access *auxt = auxbuf;
    438 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    439 
    440 			if (pops->puffs_node_access == NULL) {
    441 				error = 0;
    442 				break;
    443 			}
    444 
    445 			error = pops->puffs_node_access(pu,
    446 			    opcookie, auxt->pvnr_mode, pcr);
    447 			break;
    448 		}
    449 
    450 		case PUFFS_VN_GETATTR:
    451 		{
    452 			struct puffs_vnmsg_getattr *auxt = auxbuf;
    453 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    454 
    455 			if (pops->puffs_node_getattr == NULL) {
    456 				error = EOPNOTSUPP;
    457 				break;
    458 			}
    459 
    460 			error = pops->puffs_node_getattr(pu,
    461 			    opcookie, &auxt->pvnr_va, pcr);
    462 			break;
    463 		}
    464 
    465 		case PUFFS_VN_SETATTR:
    466 		{
    467 			struct puffs_vnmsg_setattr *auxt = auxbuf;
    468 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    469 
    470 			if (pops->puffs_node_setattr == NULL) {
    471 				error = EOPNOTSUPP;
    472 				break;
    473 			}
    474 
    475 			error = pops->puffs_node_setattr(pu,
    476 			    opcookie, &auxt->pvnr_va, pcr);
    477 			break;
    478 		}
    479 
    480 		case PUFFS_VN_MMAP:
    481 		{
    482 			struct puffs_vnmsg_mmap *auxt = auxbuf;
    483 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    484 
    485 			if (pops->puffs_node_mmap == NULL) {
    486 				error = 0;
    487 				break;
    488 			}
    489 
    490 			error = pops->puffs_node_mmap(pu,
    491 			    opcookie, auxt->pvnr_prot, pcr);
    492 			break;
    493 		}
    494 
    495 		case PUFFS_VN_FSYNC:
    496 		{
    497 			struct puffs_vnmsg_fsync *auxt = auxbuf;
    498 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    499 
    500 			if (pops->puffs_node_fsync == NULL) {
    501 				error = 0;
    502 				break;
    503 			}
    504 
    505 			error = pops->puffs_node_fsync(pu, opcookie, pcr,
    506 			    auxt->pvnr_flags, auxt->pvnr_offlo,
    507 			    auxt->pvnr_offhi);
    508 			break;
    509 		}
    510 
    511 		case PUFFS_VN_SEEK:
    512 		{
    513 			struct puffs_vnmsg_seek *auxt = auxbuf;
    514 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    515 
    516 			if (pops->puffs_node_seek == NULL) {
    517 				error = 0;
    518 				break;
    519 			}
    520 
    521 			error = pops->puffs_node_seek(pu,
    522 			    opcookie, auxt->pvnr_oldoff,
    523 			    auxt->pvnr_newoff, pcr);
    524 			break;
    525 		}
    526 
    527 		case PUFFS_VN_REMOVE:
    528 		{
    529 			struct puffs_vnmsg_remove *auxt = auxbuf;
    530 			struct puffs_cn pcn;
    531 			if (pops->puffs_node_remove == NULL) {
    532 				error = 0;
    533 				break;
    534 			}
    535 
    536 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    537 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    538 
    539 			error = pops->puffs_node_remove(pu,
    540 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    541 			break;
    542 		}
    543 
    544 		case PUFFS_VN_LINK:
    545 		{
    546 			struct puffs_vnmsg_link *auxt = auxbuf;
    547 			struct puffs_cn pcn;
    548 			if (pops->puffs_node_link == NULL) {
    549 				error = 0;
    550 				break;
    551 			}
    552 
    553 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    554 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    555 
    556 			if (buildpath) {
    557 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    558 				if (error)
    559 					break;
    560 			}
    561 
    562 			error = pops->puffs_node_link(pu,
    563 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    564 			if (buildpath)
    565 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
    566 
    567 			break;
    568 		}
    569 
    570 		case PUFFS_VN_RENAME:
    571 		{
    572 			struct puffs_vnmsg_rename *auxt = auxbuf;
    573 			struct puffs_cn pcn_src, pcn_targ;
    574 			struct puffs_node *pn_src;
    575 
    576 			if (pops->puffs_node_rename == NULL) {
    577 				error = 0;
    578 				break;
    579 			}
    580 
    581 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
    582 			PUFFS_KCREDTOCRED(pcn_src.pcn_cred,
    583 			    &auxt->pvnr_cn_src_cred);
    584 
    585 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
    586 			PUFFS_KCREDTOCRED(pcn_targ.pcn_cred,
    587 			    &auxt->pvnr_cn_targ_cred);
    588 
    589 			if (buildpath) {
    590 				pn_src = auxt->pvnr_cookie_src;
    591 				pcn_src.pcn_po_full = pn_src->pn_po;
    592 
    593 				error = puffs_path_pcnbuild(pu, &pcn_targ,
    594 				    auxt->pvnr_cookie_targdir);
    595 				if (error)
    596 					break;
    597 			}
    598 
    599 			error = pops->puffs_node_rename(pu,
    600 			    opcookie, auxt->pvnr_cookie_src,
    601 			    &pcn_src, auxt->pvnr_cookie_targdir,
    602 			    auxt->pvnr_cookie_targ, &pcn_targ);
    603 
    604 			if (buildpath) {
    605 				if (error) {
    606 					pu->pu_pathfree(pu,
    607 					    &pcn_targ.pcn_po_full);
    608 				} else {
    609 					struct puffs_pathinfo pi;
    610 					struct puffs_pathobj po_old;
    611 
    612 					/* handle this node */
    613 					po_old = pn_src->pn_po;
    614 					pn_src->pn_po = pcn_targ.pcn_po_full;
    615 
    616 					if (pn_src->pn_va.va_type != VDIR) {
    617 						pu->pu_pathfree(pu, &po_old);
    618 						break;
    619 					}
    620 
    621 					/* handle all child nodes for DIRs */
    622 					pi.pi_old = &pcn_src.pcn_po_full;
    623 					pi.pi_new = &pcn_targ.pcn_po_full;
    624 
    625 					PU_LOCK();
    626 					if (puffs_pn_nodewalk(pu,
    627 					    puffs_path_prefixadj, &pi) != NULL)
    628 						error = ENOMEM;
    629 					PU_UNLOCK();
    630 					pu->pu_pathfree(pu, &po_old);
    631 				}
    632 			}
    633 			break;
    634 		}
    635 
    636 		case PUFFS_VN_MKDIR:
    637 		{
    638 			struct puffs_vnmsg_mkdir *auxt = auxbuf;
    639 			struct puffs_newinfo pni;
    640 			struct puffs_cn pcn;
    641 
    642 			if (pops->puffs_node_mkdir == NULL) {
    643 				error = 0;
    644 				break;
    645 			}
    646 
    647 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    648 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    649 
    650 			memset(&pni, 0, sizeof(pni));
    651 			pni.pni_cookie = &auxt->pvnr_newnode;
    652 
    653 			if (buildpath) {
    654 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    655 				if (error)
    656 					break;
    657 			}
    658 
    659 			error = pops->puffs_node_mkdir(pu,
    660 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
    661 
    662 			if (buildpath) {
    663 				if (error) {
    664 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    665 				} else {
    666 					struct puffs_node *pn;
    667 
    668 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    669 					pn->pn_po = pcn.pcn_po_full;
    670 				}
    671 			}
    672 
    673 			break;
    674 		}
    675 
    676 		case PUFFS_VN_RMDIR:
    677 		{
    678 			struct puffs_vnmsg_rmdir *auxt = auxbuf;
    679 			struct puffs_cn pcn;
    680 			if (pops->puffs_node_rmdir == NULL) {
    681 				error = 0;
    682 				break;
    683 			}
    684 
    685 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    686 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    687 
    688 			error = pops->puffs_node_rmdir(pu,
    689 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
    690 			break;
    691 		}
    692 
    693 		case PUFFS_VN_SYMLINK:
    694 		{
    695 			struct puffs_vnmsg_symlink *auxt = auxbuf;
    696 			struct puffs_newinfo pni;
    697 			struct puffs_cn pcn;
    698 
    699 			if (pops->puffs_node_symlink == NULL) {
    700 				error = 0;
    701 				break;
    702 			}
    703 
    704 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    705 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    706 
    707 			memset(&pni, 0, sizeof(pni));
    708 			pni.pni_cookie = &auxt->pvnr_newnode;
    709 
    710 			if (buildpath) {
    711 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
    712 				if (error)
    713 					break;
    714 			}
    715 
    716 			error = pops->puffs_node_symlink(pu,
    717 			    opcookie, &pni, &pcn,
    718 			    &auxt->pvnr_va, auxt->pvnr_link);
    719 
    720 			if (buildpath) {
    721 				if (error) {
    722 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
    723 				} else {
    724 					struct puffs_node *pn;
    725 
    726 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
    727 					pn->pn_po = pcn.pcn_po_full;
    728 				}
    729 			}
    730 
    731 			break;
    732 		}
    733 
    734 		case PUFFS_VN_READDIR:
    735 		{
    736 			struct puffs_vnmsg_readdir *auxt = auxbuf;
    737 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    738 			struct dirent *dent;
    739 			off_t *cookies;
    740 			size_t res, origcookies;
    741 
    742 			if (pops->puffs_node_readdir == NULL) {
    743 				error = 0;
    744 				break;
    745 			}
    746 
    747 			if (auxt->pvnr_ncookies) {
    748 				/* LINTED: pvnr_data is __aligned() */
    749 				cookies = (off_t *)auxt->pvnr_data;
    750 				origcookies = auxt->pvnr_ncookies;
    751 			} else {
    752 				cookies = NULL;
    753 				origcookies = 0;
    754 			}
    755 			/* LINTED: dentoff is aligned in the kernel */
    756 			dent = (struct dirent *)
    757 			    (auxt->pvnr_data + auxt->pvnr_dentoff);
    758 
    759 			res = auxt->pvnr_resid;
    760 			error = pops->puffs_node_readdir(pu,
    761 			    opcookie, dent, &auxt->pvnr_offset,
    762 			    &auxt->pvnr_resid, pcr, &auxt->pvnr_eofflag,
    763 			    cookies, &auxt->pvnr_ncookies);
    764 
    765 			/* much easier to track non-working NFS */
    766 			assert(auxt->pvnr_ncookies <= origcookies);
    767 
    768 			/* need to move a bit more */
    769 			preq->preq_buflen = sizeof(struct puffs_vnmsg_readdir)
    770 			    + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
    771 			break;
    772 		}
    773 
    774 		case PUFFS_VN_READLINK:
    775 		{
    776 			struct puffs_vnmsg_readlink *auxt = auxbuf;
    777 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    778 
    779 			if (pops->puffs_node_readlink == NULL) {
    780 				error = EOPNOTSUPP;
    781 				break;
    782 			}
    783 
    784 			/*LINTED*/
    785 			error = pops->puffs_node_readlink(pu, opcookie, pcr,
    786 			    auxt->pvnr_link, &auxt->pvnr_linklen);
    787 			break;
    788 		}
    789 
    790 		case PUFFS_VN_RECLAIM:
    791 		{
    792 
    793 			if (pops->puffs_node_reclaim == NULL) {
    794 				error = 0;
    795 				break;
    796 			}
    797 
    798 			error = pops->puffs_node_reclaim(pu, opcookie);
    799 			break;
    800 		}
    801 
    802 		case PUFFS_VN_INACTIVE:
    803 		{
    804 
    805 			if (pops->puffs_node_inactive == NULL) {
    806 				error = EOPNOTSUPP;
    807 				break;
    808 			}
    809 
    810 			error = pops->puffs_node_inactive(pu, opcookie);
    811 			break;
    812 		}
    813 
    814 		case PUFFS_VN_PATHCONF:
    815 		{
    816 			struct puffs_vnmsg_pathconf *auxt = auxbuf;
    817 			if (pops->puffs_node_pathconf == NULL) {
    818 				error = 0;
    819 				break;
    820 			}
    821 
    822 			error = pops->puffs_node_pathconf(pu,
    823 			    opcookie, auxt->pvnr_name,
    824 			    &auxt->pvnr_retval);
    825 			break;
    826 		}
    827 
    828 		case PUFFS_VN_ADVLOCK:
    829 		{
    830 			struct puffs_vnmsg_advlock *auxt = auxbuf;
    831 			if (pops->puffs_node_advlock == NULL) {
    832 				error = 0;
    833 				break;
    834 			}
    835 
    836 			error = pops->puffs_node_advlock(pu,
    837 			    opcookie, auxt->pvnr_id, auxt->pvnr_op,
    838 			    &auxt->pvnr_fl, auxt->pvnr_flags);
    839 			break;
    840 		}
    841 
    842 		case PUFFS_VN_PRINT:
    843 		{
    844 			if (pops->puffs_node_print == NULL) {
    845 				error = 0;
    846 				break;
    847 			}
    848 
    849 			error = pops->puffs_node_print(pu,
    850 			    opcookie);
    851 			break;
    852 		}
    853 
    854 		case PUFFS_VN_ABORTOP:
    855 		{
    856 			struct puffs_vnmsg_abortop *auxt = auxbuf;
    857 			struct puffs_cn pcn;
    858 
    859 			if (pops->puffs_node_abortop == NULL) {
    860 				error = 0;
    861 				break;
    862 			}
    863 
    864 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
    865 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
    866 
    867 			error = pops->puffs_node_abortop(pu, opcookie, &pcn);
    868 
    869 			break;
    870 		}
    871 
    872 		case PUFFS_VN_READ:
    873 		{
    874 			struct puffs_vnmsg_read *auxt = auxbuf;
    875 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    876 			size_t res;
    877 
    878 			if (pops->puffs_node_read == NULL) {
    879 				error = EIO;
    880 				break;
    881 			}
    882 
    883 			res = auxt->pvnr_resid;
    884 			error = pops->puffs_node_read(pu,
    885 			    opcookie, auxt->pvnr_data,
    886 			    auxt->pvnr_offset, &auxt->pvnr_resid,
    887 			    pcr, auxt->pvnr_ioflag);
    888 
    889 			/* need to move a bit more */
    890 			preq->preq_buflen = sizeof(struct puffs_vnmsg_read)
    891 			    + (res - auxt->pvnr_resid);
    892 			break;
    893 		}
    894 
    895 		case PUFFS_VN_WRITE:
    896 		{
    897 			struct puffs_vnmsg_write *auxt = auxbuf;
    898 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    899 
    900 			if (pops->puffs_node_write == NULL) {
    901 				error = EIO;
    902 				break;
    903 			}
    904 
    905 			error = pops->puffs_node_write(pu,
    906 			    opcookie, auxt->pvnr_data,
    907 			    auxt->pvnr_offset, &auxt->pvnr_resid,
    908 			    pcr, auxt->pvnr_ioflag);
    909 
    910 			/* don't need to move data back to the kernel */
    911 			preq->preq_buflen = sizeof(struct puffs_vnmsg_write);
    912 			break;
    913 		}
    914 
    915 		case PUFFS_VN_POLL:
    916 		{
    917 			struct puffs_vnmsg_poll *auxt = auxbuf;
    918 
    919 			if (pops->puffs_node_poll == NULL) {
    920 				error = 0;
    921 
    922 				/* emulate genfs_poll() */
    923 				auxt->pvnr_events &= (POLLIN | POLLOUT
    924 						    | POLLRDNORM | POLLWRNORM);
    925 
    926 				break;
    927 			}
    928 
    929 			error = pops->puffs_node_poll(pu,
    930 			    opcookie, &auxt->pvnr_events);
    931 			break;
    932 		}
    933 
    934 		case PUFFS_VN_GETEXTATTR:
    935 		{
    936 			struct puffs_vnmsg_getextattr *auxt = auxbuf;
    937 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    938 			size_t res, *resp, *sizep;
    939 			uint8_t *data;
    940 
    941 			if (pops->puffs_node_getextattr == NULL) {
    942 				error = EOPNOTSUPP;
    943 				break;
    944 			}
    945 
    946 			if (auxt->pvnr_datasize)
    947 				sizep = &auxt->pvnr_datasize;
    948 			else
    949 				sizep = NULL;
    950 
    951 			res = auxt->pvnr_resid;
    952 			if (res > 0) {
    953 				data = auxt->pvnr_data;
    954 				resp = &auxt->pvnr_resid;
    955 			} else {
    956 				data = NULL;
    957 				resp = NULL;
    958 			}
    959 
    960 			error = pops->puffs_node_getextattr(pu,
    961 			    opcookie, auxt->pvnr_attrnamespace,
    962 			    auxt->pvnr_attrname, sizep, data, resp, pcr);
    963 
    964 			/* need to move a bit more? */
    965 			preq->preq_buflen =
    966 			    sizeof(struct puffs_vnmsg_getextattr)
    967 			    + (res - auxt->pvnr_resid);
    968 			break;
    969 		}
    970 
    971 		case PUFFS_VN_SETEXTATTR:
    972 		{
    973 			struct puffs_vnmsg_setextattr *auxt = auxbuf;
    974 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
    975 			size_t *resp;
    976 			uint8_t *data;
    977 
    978 			if (pops->puffs_node_setextattr == NULL) {
    979 				error = EOPNOTSUPP;
    980 				break;
    981 			}
    982 
    983 			if (auxt->pvnr_resid > 0) {
    984 				data = auxt->pvnr_data;
    985 				resp = &auxt->pvnr_resid;
    986 			} else {
    987 				data = NULL;
    988 				resp = NULL;
    989 			}
    990 
    991 			error = pops->puffs_node_setextattr(pu,
    992 			    opcookie, auxt->pvnr_attrnamespace,
    993 			    auxt->pvnr_attrname, data, resp, pcr);
    994 			break;
    995 		}
    996 
    997 		case PUFFS_VN_LISTEXTATTR:
    998 		{
    999 			struct puffs_vnmsg_listextattr *auxt = auxbuf;
   1000 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
   1001 			size_t res, *resp, *sizep;
   1002 			int flag;
   1003 			uint8_t *data;
   1004 
   1005 			if (pops->puffs_node_listextattr == NULL) {
   1006 				error = EOPNOTSUPP;
   1007 				break;
   1008 			}
   1009 
   1010 			if (auxt->pvnr_datasize)
   1011 				sizep = &auxt->pvnr_datasize;
   1012 			else
   1013 				sizep = NULL;
   1014 
   1015 			res = auxt->pvnr_resid;
   1016 			if (res > 0) {
   1017 				data = auxt->pvnr_data;
   1018 				resp = &auxt->pvnr_resid;
   1019 			} else {
   1020 				data = NULL;
   1021 				resp = NULL;
   1022 			}
   1023 
   1024 			res = auxt->pvnr_resid;
   1025 			flag = auxt->pvnr_flag;
   1026 			error = pops->puffs_node_listextattr(pu,
   1027 			    opcookie, auxt->pvnr_attrnamespace,
   1028 			    sizep, data, resp, flag, pcr);
   1029 
   1030 			/* need to move a bit more? */
   1031 			preq->preq_buflen =
   1032 			    sizeof(struct puffs_vnmsg_listextattr)
   1033 			    + (res - auxt->pvnr_resid);
   1034 			break;
   1035 		}
   1036 
   1037 		case PUFFS_VN_DELETEEXTATTR:
   1038 		{
   1039 			struct puffs_vnmsg_deleteextattr *auxt = auxbuf;
   1040 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
   1041 
   1042 			if (pops->puffs_node_deleteextattr == NULL) {
   1043 				error = EOPNOTSUPP;
   1044 				break;
   1045 			}
   1046 
   1047 			error = pops->puffs_node_deleteextattr(pu,
   1048 			    opcookie, auxt->pvnr_attrnamespace,
   1049 			    auxt->pvnr_attrname, pcr);
   1050 			break;
   1051 		}
   1052 
   1053 		default:
   1054 			printf("inval op %d\n", preq->preq_optype);
   1055 			error = EINVAL;
   1056 			break;
   1057 		}
   1058 
   1059 #if 0
   1060 	/* not issued by kernel currently */
   1061 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
   1062 		struct puffs_cacheinfo *pci = (void *)preq;
   1063 
   1064 		if (pu->pu_ops.puffs_cache_write) {
   1065 			pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
   1066 			    pci->pcache_nruns, pci->pcache_runs);
   1067 		}
   1068 		error = 0;
   1069 #endif
   1070 
   1071 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_ERROR) {
   1072 		struct puffs_error *perr = (void *)preq;
   1073 
   1074 		pu->pu_errnotify(pu, preq->preq_optype,
   1075 		    perr->perr_error, perr->perr_str, preq->preq_cookie);
   1076 		error = 0;
   1077 	} else {
   1078 		/*
   1079 		 * I guess the kernel sees this one coming also
   1080 		 */
   1081 		error = EINVAL;
   1082 	}
   1083 
   1084  out:
   1085 	preq->preq_rv = error;
   1086 
   1087 	if (pu->pu_oppost)
   1088 		pu->pu_oppost(pu);
   1089 
   1090 	pcc->pcc_flags |= PCC_DONE;
   1091 }
   1092