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