xref: /netbsd-src/lib/libpuffs/dispatcher.c (revision 267197ec1eebfcb9810ea27a89625b6ddf68e3e7)
1 /*	$NetBSD: dispatcher.c,v 1.30 2008/01/28 18:35:50 pooka 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.30 2008/01/28 18:35:50 pooka 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 = preq; /* help with typecasting */
137 	void *opcookie = preq->preq_cookie;
138 	int error, buildpath;
139 
140 	assert((pcc->pcc_flags & PCC_DONE) == 0);
141 
142 	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
143 	preq->preq_setbacks = 0;
144 
145 	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
146 		puffsdump_req(preq);
147 
148 	puffs__cc_setcaller(pcc, preq->preq_pid, preq->preq_lid);
149 
150 	/* pre-operation */
151 	if (pu->pu_oppre)
152 		pu->pu_oppre(pu);
153 
154 	/* Execute actual operation */
155 	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
156 		switch (preq->preq_optype) {
157 		case PUFFS_VFS_UNMOUNT:
158 		{
159 			struct puffs_vfsmsg_unmount *auxt = auxbuf;
160 
161 			PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTING);
162 			error = pops->puffs_fs_unmount(pu, auxt->pvfsr_flags);
163 			if (!error)
164 				PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
165 			else
166 				PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
167 			break;
168 		}
169 
170 		case PUFFS_VFS_STATVFS:
171 		{
172 			struct puffs_vfsmsg_statvfs *auxt = auxbuf;
173 
174 			error = pops->puffs_fs_statvfs(pu, &auxt->pvfsr_sb);
175 			break;
176 		}
177 
178 		case PUFFS_VFS_SYNC:
179 		{
180 			struct puffs_vfsmsg_sync *auxt = auxbuf;
181 			PUFFS_MAKECRED(pcr, &auxt->pvfsr_cred);
182 
183 			error = pops->puffs_fs_sync(pu,
184 			    auxt->pvfsr_waitfor, pcr);
185 			break;
186 		}
187 
188 		case PUFFS_VFS_FHTOVP:
189 		{
190 			struct puffs_vfsmsg_fhtonode *auxt = auxbuf;
191 			struct puffs_newinfo pni;
192 
193 			pni.pni_cookie = &auxt->pvfsr_fhcookie;
194 			pni.pni_vtype = &auxt->pvfsr_vtype;
195 			pni.pni_size = &auxt->pvfsr_size;
196 			pni.pni_rdev = &auxt->pvfsr_rdev;
197 
198 			error = pops->puffs_fs_fhtonode(pu, auxt->pvfsr_data,
199 			    auxt->pvfsr_dsize, &pni);
200 
201 			break;
202 		}
203 
204 		case PUFFS_VFS_VPTOFH:
205 		{
206 			struct puffs_vfsmsg_nodetofh *auxt = auxbuf;
207 
208 			error = pops->puffs_fs_nodetofh(pu,
209 			    auxt->pvfsr_fhcookie, auxt->pvfsr_data,
210 			    &auxt->pvfsr_dsize);
211 
212 			break;
213 		}
214 
215 		case PUFFS_VFS_SUSPEND:
216 		{
217 			struct puffs_vfsmsg_suspend *auxt = auxbuf;
218 
219 			error = 0;
220 			if (pops->puffs_fs_suspend == NULL)
221 				break;
222 
223 			pops->puffs_fs_suspend(pu, auxt->pvfsr_status);
224 			break;
225 		}
226 
227 		default:
228 			/*
229 			 * I guess the kernel sees this one coming
230 			 */
231 			error = EINVAL;
232 			break;
233 		}
234 
235 	/* XXX: audit return values */
236 	/* XXX: sync with kernel */
237 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
238 		switch (preq->preq_optype) {
239 		case PUFFS_VN_LOOKUP:
240 		{
241 			struct puffs_vnmsg_lookup *auxt = auxbuf;
242 			struct puffs_newinfo pni;
243 			struct puffs_cn pcn;
244 
245 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
246 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
247 			pni.pni_cookie = &auxt->pvnr_newnode;
248 			pni.pni_vtype = &auxt->pvnr_vtype;
249 			pni.pni_size = &auxt->pvnr_size;
250 			pni.pni_rdev = &auxt->pvnr_rdev;
251 
252 			if (buildpath) {
253 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
254 				if (error)
255 					break;
256 			}
257 
258 			/* lookup *must* be present */
259 			error = pops->puffs_node_lookup(pu, opcookie,
260 			    &pni, &pcn);
261 
262 			if (buildpath) {
263 				if (error) {
264 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
265 				} else {
266 					struct puffs_node *pn;
267 
268 					/*
269 					 * did we get a new node or a
270 					 * recycled node?
271 					 */
272 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
273 					if (pn->pn_po.po_path == NULL)
274 						pn->pn_po = pcn.pcn_po_full;
275 					else
276 						pu->pu_pathfree(pu,
277 						    &pcn.pcn_po_full);
278 				}
279 			}
280 
281 			break;
282 		}
283 
284 		case PUFFS_VN_CREATE:
285 		{
286 			struct puffs_vnmsg_create *auxt = auxbuf;
287 			struct puffs_newinfo pni;
288 			struct puffs_cn pcn;
289 
290 			if (pops->puffs_node_create == NULL) {
291 				error = 0;
292 				break;
293 			}
294 
295 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
296 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
297 
298 			memset(&pni, 0, sizeof(pni));
299 			pni.pni_cookie = &auxt->pvnr_newnode;
300 
301 			if (buildpath) {
302 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
303 				if (error)
304 					break;
305 			}
306 
307 			error = pops->puffs_node_create(pu,
308 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
309 
310 			if (buildpath) {
311 				if (error) {
312 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
313 				} else {
314 					struct puffs_node *pn;
315 
316 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
317 					pn->pn_po = pcn.pcn_po_full;
318 				}
319 			}
320 
321 			break;
322 		}
323 
324 		case PUFFS_VN_MKNOD:
325 		{
326 			struct puffs_vnmsg_mknod *auxt = auxbuf;
327 			struct puffs_newinfo pni;
328 			struct puffs_cn pcn;
329 
330 			if (pops->puffs_node_mknod == NULL) {
331 				error = 0;
332 				break;
333 			}
334 
335 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
336 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
337 
338 			memset(&pni, 0, sizeof(pni));
339 			pni.pni_cookie = &auxt->pvnr_newnode;
340 
341 			if (buildpath) {
342 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
343 				if (error)
344 					break;
345 			}
346 
347 			error = pops->puffs_node_mknod(pu,
348 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
349 
350 			if (buildpath) {
351 				if (error) {
352 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
353 				} else {
354 					struct puffs_node *pn;
355 
356 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
357 					pn->pn_po = pcn.pcn_po_full;
358 				}
359 			}
360 
361 			break;
362 		}
363 
364 		case PUFFS_VN_OPEN:
365 		{
366 			struct puffs_vnmsg_open *auxt = auxbuf;
367 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
368 
369 			if (pops->puffs_node_open == NULL) {
370 				error = 0;
371 				break;
372 			}
373 
374 			error = pops->puffs_node_open(pu,
375 			    opcookie, auxt->pvnr_mode, pcr);
376 			break;
377 		}
378 
379 		case PUFFS_VN_CLOSE:
380 		{
381 			struct puffs_vnmsg_close *auxt = auxbuf;
382 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
383 
384 			if (pops->puffs_node_close == NULL) {
385 				error = 0;
386 				break;
387 			}
388 
389 			error = pops->puffs_node_close(pu,
390 			    opcookie, auxt->pvnr_fflag, pcr);
391 			break;
392 		}
393 
394 		case PUFFS_VN_ACCESS:
395 		{
396 			struct puffs_vnmsg_access *auxt = auxbuf;
397 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
398 
399 			if (pops->puffs_node_access == NULL) {
400 				error = 0;
401 				break;
402 			}
403 
404 			error = pops->puffs_node_access(pu,
405 			    opcookie, auxt->pvnr_mode, pcr);
406 			break;
407 		}
408 
409 		case PUFFS_VN_GETATTR:
410 		{
411 			struct puffs_vnmsg_getattr *auxt = auxbuf;
412 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
413 
414 			if (pops->puffs_node_getattr == NULL) {
415 				error = EOPNOTSUPP;
416 				break;
417 			}
418 
419 			error = pops->puffs_node_getattr(pu,
420 			    opcookie, &auxt->pvnr_va, pcr);
421 			break;
422 		}
423 
424 		case PUFFS_VN_SETATTR:
425 		{
426 			struct puffs_vnmsg_setattr *auxt = auxbuf;
427 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
428 
429 			if (pops->puffs_node_setattr == NULL) {
430 				error = EOPNOTSUPP;
431 				break;
432 			}
433 
434 			error = pops->puffs_node_setattr(pu,
435 			    opcookie, &auxt->pvnr_va, pcr);
436 			break;
437 		}
438 
439 		case PUFFS_VN_MMAP:
440 		{
441 			struct puffs_vnmsg_mmap *auxt = auxbuf;
442 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
443 
444 			if (pops->puffs_node_mmap == NULL) {
445 				error = 0;
446 				break;
447 			}
448 
449 			error = pops->puffs_node_mmap(pu,
450 			    opcookie, auxt->pvnr_prot, pcr);
451 			break;
452 		}
453 
454 		case PUFFS_VN_FSYNC:
455 		{
456 			struct puffs_vnmsg_fsync *auxt = auxbuf;
457 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
458 
459 			if (pops->puffs_node_fsync == NULL) {
460 				error = 0;
461 				break;
462 			}
463 
464 			error = pops->puffs_node_fsync(pu, opcookie, pcr,
465 			    auxt->pvnr_flags, auxt->pvnr_offlo,
466 			    auxt->pvnr_offhi);
467 			break;
468 		}
469 
470 		case PUFFS_VN_SEEK:
471 		{
472 			struct puffs_vnmsg_seek *auxt = auxbuf;
473 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
474 
475 			if (pops->puffs_node_seek == NULL) {
476 				error = 0;
477 				break;
478 			}
479 
480 			error = pops->puffs_node_seek(pu,
481 			    opcookie, auxt->pvnr_oldoff,
482 			    auxt->pvnr_newoff, pcr);
483 			break;
484 		}
485 
486 		case PUFFS_VN_REMOVE:
487 		{
488 			struct puffs_vnmsg_remove *auxt = auxbuf;
489 			struct puffs_cn pcn;
490 			if (pops->puffs_node_remove == NULL) {
491 				error = 0;
492 				break;
493 			}
494 
495 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
496 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
497 
498 			error = pops->puffs_node_remove(pu,
499 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
500 			break;
501 		}
502 
503 		case PUFFS_VN_LINK:
504 		{
505 			struct puffs_vnmsg_link *auxt = auxbuf;
506 			struct puffs_cn pcn;
507 			if (pops->puffs_node_link == NULL) {
508 				error = 0;
509 				break;
510 			}
511 
512 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
513 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
514 
515 			if (buildpath) {
516 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
517 				if (error)
518 					break;
519 			}
520 
521 			error = pops->puffs_node_link(pu,
522 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
523 			if (buildpath)
524 				pu->pu_pathfree(pu, &pcn.pcn_po_full);
525 
526 			break;
527 		}
528 
529 		case PUFFS_VN_RENAME:
530 		{
531 			struct puffs_vnmsg_rename *auxt = auxbuf;
532 			struct puffs_cn pcn_src, pcn_targ;
533 			struct puffs_node *pn_src;
534 
535 			if (pops->puffs_node_rename == NULL) {
536 				error = 0;
537 				break;
538 			}
539 
540 			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
541 			PUFFS_KCREDTOCRED(pcn_src.pcn_cred,
542 			    &auxt->pvnr_cn_src_cred);
543 
544 			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
545 			PUFFS_KCREDTOCRED(pcn_targ.pcn_cred,
546 			    &auxt->pvnr_cn_targ_cred);
547 
548 			if (buildpath) {
549 				pn_src = auxt->pvnr_cookie_src;
550 				pcn_src.pcn_po_full = pn_src->pn_po;
551 
552 				error = puffs_path_pcnbuild(pu, &pcn_targ,
553 				    auxt->pvnr_cookie_targdir);
554 				if (error)
555 					break;
556 			}
557 
558 			error = pops->puffs_node_rename(pu,
559 			    opcookie, auxt->pvnr_cookie_src,
560 			    &pcn_src, auxt->pvnr_cookie_targdir,
561 			    auxt->pvnr_cookie_targ, &pcn_targ);
562 
563 			if (buildpath) {
564 				if (error) {
565 					pu->pu_pathfree(pu,
566 					    &pcn_targ.pcn_po_full);
567 				} else {
568 					struct puffs_pathinfo pi;
569 					struct puffs_pathobj po_old;
570 
571 					/* handle this node */
572 					po_old = pn_src->pn_po;
573 					pn_src->pn_po = pcn_targ.pcn_po_full;
574 
575 					if (pn_src->pn_va.va_type != VDIR) {
576 						pu->pu_pathfree(pu, &po_old);
577 						break;
578 					}
579 
580 					/* handle all child nodes for DIRs */
581 					pi.pi_old = &pcn_src.pcn_po_full;
582 					pi.pi_new = &pcn_targ.pcn_po_full;
583 
584 					PU_LOCK();
585 					if (puffs_pn_nodewalk(pu,
586 					    puffs_path_prefixadj, &pi) != NULL)
587 						error = ENOMEM;
588 					PU_UNLOCK();
589 					pu->pu_pathfree(pu, &po_old);
590 				}
591 			}
592 			break;
593 		}
594 
595 		case PUFFS_VN_MKDIR:
596 		{
597 			struct puffs_vnmsg_mkdir *auxt = auxbuf;
598 			struct puffs_newinfo pni;
599 			struct puffs_cn pcn;
600 
601 			if (pops->puffs_node_mkdir == NULL) {
602 				error = 0;
603 				break;
604 			}
605 
606 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
607 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
608 
609 			memset(&pni, 0, sizeof(pni));
610 			pni.pni_cookie = &auxt->pvnr_newnode;
611 
612 			if (buildpath) {
613 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
614 				if (error)
615 					break;
616 			}
617 
618 			error = pops->puffs_node_mkdir(pu,
619 			    opcookie, &pni, &pcn, &auxt->pvnr_va);
620 
621 			if (buildpath) {
622 				if (error) {
623 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
624 				} else {
625 					struct puffs_node *pn;
626 
627 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
628 					pn->pn_po = pcn.pcn_po_full;
629 				}
630 			}
631 
632 			break;
633 		}
634 
635 		case PUFFS_VN_RMDIR:
636 		{
637 			struct puffs_vnmsg_rmdir *auxt = auxbuf;
638 			struct puffs_cn pcn;
639 			if (pops->puffs_node_rmdir == NULL) {
640 				error = 0;
641 				break;
642 			}
643 
644 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
645 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
646 
647 			error = pops->puffs_node_rmdir(pu,
648 			    opcookie, auxt->pvnr_cookie_targ, &pcn);
649 			break;
650 		}
651 
652 		case PUFFS_VN_SYMLINK:
653 		{
654 			struct puffs_vnmsg_symlink *auxt = auxbuf;
655 			struct puffs_newinfo pni;
656 			struct puffs_cn pcn;
657 
658 			if (pops->puffs_node_symlink == NULL) {
659 				error = 0;
660 				break;
661 			}
662 
663 			pcn.pcn_pkcnp = &auxt->pvnr_cn;
664 			PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred);
665 
666 			memset(&pni, 0, sizeof(pni));
667 			pni.pni_cookie = &auxt->pvnr_newnode;
668 
669 			if (buildpath) {
670 				error = puffs_path_pcnbuild(pu, &pcn, opcookie);
671 				if (error)
672 					break;
673 			}
674 
675 			error = pops->puffs_node_symlink(pu,
676 			    opcookie, &pni, &pcn,
677 			    &auxt->pvnr_va, auxt->pvnr_link);
678 
679 			if (buildpath) {
680 				if (error) {
681 					pu->pu_pathfree(pu, &pcn.pcn_po_full);
682 				} else {
683 					struct puffs_node *pn;
684 
685 					pn = PU_CMAP(pu, auxt->pvnr_newnode);
686 					pn->pn_po = pcn.pcn_po_full;
687 				}
688 			}
689 
690 			break;
691 		}
692 
693 		case PUFFS_VN_READDIR:
694 		{
695 			struct puffs_vnmsg_readdir *auxt = auxbuf;
696 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
697 			struct dirent *dent;
698 			off_t *cookies;
699 			size_t res, origcookies;
700 
701 			if (pops->puffs_node_readdir == NULL) {
702 				error = 0;
703 				break;
704 			}
705 
706 			if (auxt->pvnr_ncookies) {
707 				/* LINTED: pvnr_data is __aligned() */
708 				cookies = (off_t *)auxt->pvnr_data;
709 				origcookies = auxt->pvnr_ncookies;
710 			} else {
711 				cookies = NULL;
712 				origcookies = 0;
713 			}
714 			/* LINTED: dentoff is aligned in the kernel */
715 			dent = (struct dirent *)
716 			    (auxt->pvnr_data + auxt->pvnr_dentoff);
717 
718 			res = auxt->pvnr_resid;
719 			error = pops->puffs_node_readdir(pu,
720 			    opcookie, dent, &auxt->pvnr_offset,
721 			    &auxt->pvnr_resid, pcr, &auxt->pvnr_eofflag,
722 			    cookies, &auxt->pvnr_ncookies);
723 
724 			/* much easier to track non-working NFS */
725 			assert(auxt->pvnr_ncookies <= origcookies);
726 
727 			/* need to move a bit more */
728 			preq->preq_buflen = sizeof(struct puffs_vnmsg_readdir)
729 			    + auxt->pvnr_dentoff + (res - auxt->pvnr_resid);
730 			break;
731 		}
732 
733 		case PUFFS_VN_READLINK:
734 		{
735 			struct puffs_vnmsg_readlink *auxt = auxbuf;
736 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
737 
738 			if (pops->puffs_node_readlink == NULL) {
739 				error = EOPNOTSUPP;
740 				break;
741 			}
742 
743 			/*LINTED*/
744 			error = pops->puffs_node_readlink(pu, opcookie, pcr,
745 			    auxt->pvnr_link, &auxt->pvnr_linklen);
746 			break;
747 		}
748 
749 		case PUFFS_VN_RECLAIM:
750 		{
751 
752 			if (pops->puffs_node_reclaim == NULL) {
753 				error = 0;
754 				break;
755 			}
756 
757 			error = pops->puffs_node_reclaim(pu, opcookie);
758 			break;
759 		}
760 
761 		case PUFFS_VN_INACTIVE:
762 		{
763 
764 			if (pops->puffs_node_inactive == NULL) {
765 				error = EOPNOTSUPP;
766 				break;
767 			}
768 
769 			error = pops->puffs_node_inactive(pu, opcookie);
770 			break;
771 		}
772 
773 		case PUFFS_VN_PATHCONF:
774 		{
775 			struct puffs_vnmsg_pathconf *auxt = auxbuf;
776 			if (pops->puffs_node_pathconf == NULL) {
777 				error = 0;
778 				break;
779 			}
780 
781 			error = pops->puffs_node_pathconf(pu,
782 			    opcookie, auxt->pvnr_name,
783 			    &auxt->pvnr_retval);
784 			break;
785 		}
786 
787 		case PUFFS_VN_ADVLOCK:
788 		{
789 			struct puffs_vnmsg_advlock *auxt = auxbuf;
790 			if (pops->puffs_node_advlock == NULL) {
791 				error = 0;
792 				break;
793 			}
794 
795 			error = pops->puffs_node_advlock(pu,
796 			    opcookie, auxt->pvnr_id, auxt->pvnr_op,
797 			    &auxt->pvnr_fl, auxt->pvnr_flags);
798 			break;
799 		}
800 
801 		case PUFFS_VN_PRINT:
802 		{
803 			if (pops->puffs_node_print == NULL) {
804 				error = 0;
805 				break;
806 			}
807 
808 			error = pops->puffs_node_print(pu,
809 			    opcookie);
810 			break;
811 		}
812 
813 		case PUFFS_VN_READ:
814 		{
815 			struct puffs_vnmsg_read *auxt = auxbuf;
816 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
817 			size_t res;
818 
819 			if (pops->puffs_node_read == NULL) {
820 				error = EIO;
821 				break;
822 			}
823 
824 			res = auxt->pvnr_resid;
825 			error = pops->puffs_node_read(pu,
826 			    opcookie, auxt->pvnr_data,
827 			    auxt->pvnr_offset, &auxt->pvnr_resid,
828 			    pcr, auxt->pvnr_ioflag);
829 
830 			/* need to move a bit more */
831 			preq->preq_buflen = sizeof(struct puffs_vnmsg_read)
832 			    + (res - auxt->pvnr_resid);
833 			break;
834 		}
835 
836 		case PUFFS_VN_WRITE:
837 		{
838 			struct puffs_vnmsg_write *auxt = auxbuf;
839 			PUFFS_MAKECRED(pcr, &auxt->pvnr_cred);
840 
841 			if (pops->puffs_node_write == NULL) {
842 				error = EIO;
843 				break;
844 			}
845 
846 			error = pops->puffs_node_write(pu,
847 			    opcookie, auxt->pvnr_data,
848 			    auxt->pvnr_offset, &auxt->pvnr_resid,
849 			    pcr, auxt->pvnr_ioflag);
850 
851 			/* don't need to move data back to the kernel */
852 			preq->preq_buflen = sizeof(struct puffs_vnmsg_write);
853 			break;
854 		}
855 
856 		case PUFFS_VN_POLL:
857 		{
858 			struct puffs_vnmsg_poll *auxt = auxbuf;
859 
860 			if (pops->puffs_node_poll == NULL) {
861 				error = 0;
862 
863 				/* emulate genfs_poll() */
864 				auxt->pvnr_events &= (POLLIN | POLLOUT
865 						    | POLLRDNORM | POLLWRNORM);
866 
867 				break;
868 			}
869 
870 			error = pops->puffs_node_poll(pu,
871 			    opcookie, &auxt->pvnr_events);
872 			break;
873 		}
874 
875 		default:
876 			printf("inval op %d\n", preq->preq_optype);
877 			error = EINVAL;
878 			break;
879 		}
880 
881 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) {
882 		struct puffs_cacheinfo *pci = (void *)preq;
883 
884 		if (pu->pu_ops.puffs_cache_write) {
885 			pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie,
886 			    pci->pcache_nruns, pci->pcache_runs);
887 		}
888 		error = 0;
889 
890 	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_ERROR) {
891 		struct puffs_error *perr = (void *)preq;
892 
893 		pu->pu_errnotify(pu, preq->preq_optype,
894 		    perr->perr_error, perr->perr_str, preq->preq_cookie);
895 		error = 0;
896 	} else {
897 		/*
898 		 * I guess the kernel sees this one coming also
899 		 */
900 		error = EINVAL;
901 	}
902 	preq->preq_rv = error;
903 
904 	if (pu->pu_oppost)
905 		pu->pu_oppost(pu);
906 
907 	pcc->pcc_flags |= PCC_DONE;
908 }
909