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