puffs.c revision 1.21
1/*	$NetBSD: puffs.c,v 1.21 2007/01/06 18:22:09 pooka Exp $	*/
2
3/*
4 * Copyright (c) 2005, 2006  Antti Kantee.  All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the company nor the name of the author may be used to
19 *    endorse or promote products derived from this software without specific
20 *    prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#if !defined(lint)
37__RCSID("$NetBSD: puffs.c,v 1.21 2007/01/06 18:22:09 pooka Exp $");
38#endif /* !lint */
39
40#include <sys/param.h>
41#include <sys/mount.h>
42
43#include <assert.h>
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <mntopts.h>
48#include <puffs.h>
49#include <puffsdump.h>
50#include <stdarg.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <syslog.h>
55#include <unistd.h>
56
57#include "puffs_priv.h"
58
59/* Most file systems want this for opts, so just give it to them */
60const struct mntopt puffsmopts[] = {
61	MOPT_STDOPTS,
62	PUFFSMOPT_STD,
63	MOPT_NULL,
64};
65
66static int do_buildpath(struct puffs_cn *, void *);
67
68#define FILLOP(lower, upper)						\
69do {									\
70	if (pops->puffs_node_##lower)					\
71		opmask[PUFFS_VN_##upper] = 1;				\
72} while (/*CONSTCOND*/0)
73static void
74fillvnopmask(struct puffs_ops *pops, uint8_t *opmask)
75{
76
77	memset(opmask, 0, PUFFS_VN_MAX);
78
79	FILLOP(create,   CREATE);
80	FILLOP(mknod,    MKNOD);
81	FILLOP(open,     OPEN);
82	FILLOP(close,    CLOSE);
83	FILLOP(access,   ACCESS);
84	FILLOP(getattr,  GETATTR);
85	FILLOP(setattr,  SETATTR);
86	FILLOP(poll,     POLL); /* XXX: not ready in kernel */
87	FILLOP(revoke,   REVOKE);
88	FILLOP(mmap,     MMAP);
89	FILLOP(fsync,    FSYNC);
90	FILLOP(seek,     SEEK);
91	FILLOP(remove,   REMOVE);
92	FILLOP(link,     LINK);
93	FILLOP(rename,   RENAME);
94	FILLOP(mkdir,    MKDIR);
95	FILLOP(rmdir,    RMDIR);
96	FILLOP(symlink,  SYMLINK);
97	FILLOP(readdir,  READDIR);
98	FILLOP(readlink, READLINK);
99	FILLOP(reclaim,  RECLAIM);
100	FILLOP(inactive, INACTIVE);
101	FILLOP(print,    PRINT);
102	FILLOP(read,     READ);
103	FILLOP(write,    WRITE);
104
105	/* XXX: not implemented in the kernel */
106	FILLOP(getextattr, GETEXTATTR);
107	FILLOP(setextattr, SETEXTATTR);
108	FILLOP(listextattr, LISTEXTATTR);
109}
110#undef FILLOP
111
112int
113puffs_getselectable(struct puffs_usermount *pu)
114{
115
116	return pu->pu_fd;
117}
118
119int
120puffs_setblockingmode(struct puffs_usermount *pu, int mode)
121{
122	int x;
123
124	x = mode;
125	return ioctl(pu->pu_fd, FIONBIO, &x);
126}
127
128int
129puffs_getstate(struct puffs_usermount *pu)
130{
131
132	return pu->pu_state;
133}
134
135void
136puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
137{
138
139	pu->pu_cc_stacksize = ss;
140}
141
142/*
143 * in case the server wants to use some other scheme for paths, it
144 * can (*must*) call this in mount.
145 */
146int
147puffs_setrootpath(struct puffs_usermount *pu, const char *rootpath)
148{
149	struct puffs_node *pnr;
150
151	pnr = pu->pu_pn_root;
152	if (pnr == NULL) {
153		errno = ENOENT;
154		return -1;
155	}
156
157	free(pnr->pn_path);
158	pnr->pn_path = strdup(rootpath);
159	if (pnr->pn_path == NULL)
160		return -1;
161	pnr->pn_plen = strlen(pnr->pn_path) + 1; /* yes, count nul */
162
163	return 0;
164}
165
166
167enum {PUFFCALL_ANSWER, PUFFCALL_IGNORE, PUFFCALL_AGAIN};
168
169struct puffs_usermount *
170_puffs_mount(int develv, struct puffs_ops *pops, const char *dir, int mntflags,
171	const char *puffsname, void *priv, uint32_t pflags, size_t maxreqlen)
172{
173	struct puffs_args pargs;
174	struct puffs_usermount *pu;
175	int fd = 0;
176
177	if (develv != PUFFS_DEVEL_LIBVERSION) {
178		warnx("puffs_mount: mounting with lib version %d, need %d",
179		    develv, PUFFS_DEVEL_LIBVERSION);
180		errno = EINVAL;
181		return NULL;
182	}
183
184	fd = open("/dev/puffs", O_RDONLY);
185	if (fd == -1)
186		return NULL;
187	if (fd <= 2)
188		warnx("puffs_mount: device fd %d (<= 2), sure this is "
189		    "what you want?", fd);
190
191	pargs.pa_vers = PUFFSDEVELVERS | PUFFSVERSION;
192	pargs.pa_flags = PUFFS_FLAG_KERN(pflags);
193	pargs.pa_fd = fd;
194	pargs.pa_maxreqlen = maxreqlen;
195	fillvnopmask(pops, pargs.pa_vnopmask);
196	(void)strlcpy(pargs.pa_name, puffsname, sizeof(pargs.pa_name));
197
198	pu = malloc(sizeof(struct puffs_usermount));
199	if (!pu)
200		return NULL;
201
202	pu->pu_flags = pflags;
203	pu->pu_ops = *pops;
204	free(pops); /* XXX */
205	pu->pu_fd = fd;
206	pu->pu_privdata = priv;
207	pu->pu_cc_stacksize = PUFFS_CC_STACKSIZE_DEFAULT;
208	LIST_INIT(&pu->pu_pnodelst);
209
210	pu->pu_state = PUFFS_STATE_MOUNTING;
211	if (mount(MOUNT_PUFFS, dir, mntflags, &pargs) == -1)
212		goto failfree;
213	pu->pu_maxreqlen = pargs.pa_maxreqlen;
214
215	return pu;
216
217 failfree:
218	/* can't unmount() from here for obvious reasons */
219	if (fd)
220		close(fd);
221	free(pu);
222	return NULL;
223}
224
225int
226puffs_start(struct puffs_usermount *pu, void *rootcookie, struct statvfs *sbp)
227{
228	struct puffs_startreq sreq;
229
230	memset(&sreq, 0, sizeof(struct puffs_startreq));
231	sreq.psr_cookie = rootcookie;
232	sreq.psr_sb = *sbp;
233
234	/* tell kernel we're flying */
235	if (ioctl(pu->pu_fd, PUFFSSTARTOP, &sreq) == -1)
236		return -1;
237
238	pu->pu_state = PUFFS_STATE_RUNNING;
239
240	return 0;
241}
242
243/*
244 * XXX: there's currently no clean way to request unmount from
245 * within the user server, so be very brutal about it.
246 */
247/*ARGSUSED*/
248int
249puffs_exit(struct puffs_usermount *pu, int force)
250{
251
252	force = 1; /* currently */
253
254	if (pu->pu_fd)
255		close(pu->pu_fd);
256
257	/* XXX: should free all contents like pnodes */
258
259	free(pu);
260
261	return 0; /* always succesful for now, WILL CHANGE */
262}
263
264int
265puffs_mainloop(struct puffs_usermount *pu, int flags)
266{
267	struct puffs_getreq *pgr;
268	struct puffs_putreq *ppr;
269	int rv;
270
271	rv = -1;
272	pgr = puffs_makegetreq(pu, pu->pu_maxreqlen, 0);
273	if (pgr == NULL)
274		return -1;
275
276	ppr = puffs_makeputreq(pu);
277	if (ppr == NULL) {
278		puffs_destroygetreq(pgr);
279		return -1;
280	}
281
282	if ((flags & PUFFSLOOP_NODAEMON) == 0)
283		if (daemon(0, 0) == -1)
284			goto out;
285
286	/* XXX: should be a bit more robust with errors here */
287	while (puffs_getstate(pu) == PUFFS_STATE_RUNNING
288	    || puffs_getstate(pu) == PUFFS_STATE_UNMOUNTING) {
289		puffs_resetputreq(ppr);
290
291		if (puffs_handlereqs(pu, pgr, ppr, 0) == -1) {
292			rv = -1;
293			break;
294		}
295		if (puffs_putputreq(ppr) == -1) {
296			rv = -1;
297			break;
298		}
299	}
300
301 out:
302	puffs_destroyputreq(ppr);
303	puffs_destroygetreq(pgr);
304	return rv;
305}
306
307int
308puffs_handlereqs(struct puffs_usermount *pu, struct puffs_getreq *pgr,
309	struct puffs_putreq *ppr, int maxops)
310{
311	struct puffs_req *preq;
312	int pval;
313
314	puffs_setmaxgetreq(pgr, maxops);
315	if (puffs_loadgetreq(pgr) == -1)
316		return -1;
317
318	/* interlink pgr and ppr for diagnostic asserts */
319	pgr->pgr_nppr++;
320	ppr->ppr_pgr = pgr;
321
322	pval = 0;
323	while ((preq = puffs_getreq(pgr)) != NULL
324	    && pu->pu_state != PUFFS_STATE_UNMOUNTED)
325		pval = puffs_dopreq(pu, ppr, preq);
326
327	return pval;
328}
329
330int
331puffs_dopreq(struct puffs_usermount *pu, struct puffs_putreq *ppr,
332	struct puffs_req *preq)
333{
334	struct puffs_cc *pcc;
335	int rv;
336
337	if (pu->pu_flags & PUFFS_FLAG_OPDUMP)
338		puffsdump_req(preq);
339
340	pcc = puffs_cc_create(pu);
341
342	/* XXX: temporary kludging */
343	pcc->pcc_preq = malloc(preq->preq_buflen);
344	if (pcc->pcc_preq == NULL)
345		return -1;
346	(void) memcpy(pcc->pcc_preq, preq, preq->preq_buflen);
347
348	rv = puffs_docc(ppr, pcc);
349
350	if ((pcc->pcc_flags & PCC_DONE) == 0)
351		return 0;
352
353	return rv;
354}
355
356int
357puffs_docc(struct puffs_putreq *ppr, struct puffs_cc *pcc)
358{
359	int rv;
360
361	assert((pcc->pcc_flags & PCC_DONE) == 0);
362
363	puffs_cc_continue(pcc);
364	rv = pcc->pcc_rv;
365
366	if ((pcc->pcc_flags & PCC_DONE) == 0)
367		rv = PUFFCALL_AGAIN;
368
369	/* check if we need to store this reply */
370	switch (rv) {
371	case PUFFCALL_ANSWER:
372		puffs_putreq_cc(ppr, pcc);
373		break;
374	case PUFFCALL_IGNORE:
375		puffs_cc_destroy(pcc);
376		break;
377	case PUFFCALL_AGAIN:
378		break;
379	default:
380		assert(/*CONSTCOND*/0);
381	}
382
383	return 0;
384}
385
386/* library private, but linked from callcontext.c */
387
388void
389puffs_calldispatcher(struct puffs_cc *pcc)
390{
391	struct puffs_usermount *pu = pcc->pcc_pu;
392	struct puffs_ops *pops = &pu->pu_ops;
393	struct puffs_req *preq = pcc->pcc_preq;
394	void *auxbuf = preq; /* help with typecasting */
395	int error, rv, buildpath;
396
397	assert(pcc->pcc_flags & (PCC_ONCE | PCC_REALCC));
398
399	if (PUFFSOP_WANTREPLY(preq->preq_opclass))
400		rv = PUFFCALL_ANSWER;
401	else
402		rv = PUFFCALL_IGNORE;
403
404	buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH;
405
406	if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) {
407		switch (preq->preq_optype) {
408		case PUFFS_VFS_UNMOUNT:
409		{
410			struct puffs_vfsreq_unmount *auxt = auxbuf;
411
412			pu->pu_state = PUFFS_STATE_UNMOUNTING;
413			error = pops->puffs_fs_unmount(pcc,
414			    auxt->pvfsr_flags, auxt->pvfsr_pid);
415			if (!error)
416				pu->pu_state = PUFFS_STATE_UNMOUNTED;
417			else
418				pu->pu_state = PUFFS_STATE_RUNNING;
419			break;
420		}
421		case PUFFS_VFS_STATVFS:
422		{
423			struct puffs_vfsreq_statvfs *auxt = auxbuf;
424
425			error = pops->puffs_fs_statvfs(pcc,
426			    &auxt->pvfsr_sb, auxt->pvfsr_pid);
427			break;
428		}
429		case PUFFS_VFS_SYNC:
430		{
431			struct puffs_vfsreq_sync *auxt = auxbuf;
432
433			error = pops->puffs_fs_sync(pcc,
434			    auxt->pvfsr_waitfor, &auxt->pvfsr_cred,
435			    auxt->pvfsr_pid);
436			break;
437		}
438		default:
439			/*
440			 * I guess the kernel sees this one coming
441			 */
442			error = EINVAL;
443			break;
444		}
445
446	/* XXX: audit return values */
447	/* XXX: sync with kernel */
448	} else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) {
449		switch (preq->preq_optype) {
450		case PUFFS_VN_LOOKUP:
451		{
452			struct puffs_vnreq_lookup *auxt = auxbuf;
453			struct puffs_cn pcn;
454
455			pcn.pcn_pkcnp = &auxt->pvnr_cn;
456			if (buildpath) {
457				if (pcn.pcn_flags & PUFFS_ISDOTDOT) {
458					buildpath = 0;
459				} else {
460					error = do_buildpath(&pcn,
461					    preq->preq_cookie);
462					if (error)
463						break;
464				}
465			}
466
467			/* lookup *must* be present */
468			error = pops->puffs_node_lookup(pcc, preq->preq_cookie,
469			    &auxt->pvnr_newnode, &auxt->pvnr_vtype,
470			    &auxt->pvnr_size, &auxt->pvnr_rdev, &pcn);
471
472			if (buildpath) {
473				if (error) {
474					free(pcn.pcn_fullpath);
475				} else {
476					struct puffs_node *pn;
477
478					/*
479					 * did we get a new node or a
480					 * recycled node?
481					 * XXX: mapping assumption
482					 */
483					pn = auxt->pvnr_newnode;
484					if (pn->pn_path == NULL) {
485						pn->pn_path = pcn.pcn_fullpath;
486						pn->pn_plen
487						    = pcn.pcn_fullplen;
488					}
489				}
490			}
491
492			break;
493		}
494
495		case PUFFS_VN_CREATE:
496		{
497			struct puffs_vnreq_create *auxt = auxbuf;
498			struct puffs_cn pcn;
499			if (pops->puffs_node_create == NULL) {
500				error = 0;
501				break;
502			}
503
504			pcn.pcn_pkcnp = &auxt->pvnr_cn;
505			if (buildpath) {
506				error = do_buildpath(&pcn, preq->preq_cookie);
507				if (error)
508					break;
509			}
510
511			error = pops->puffs_node_create(pcc,
512			    preq->preq_cookie, &auxt->pvnr_newnode,
513			    &pcn, &auxt->pvnr_va);
514
515			if (buildpath) {
516				if (error) {
517					free(pcn.pcn_fullpath);
518				} else {
519					struct puffs_node *pn;
520
521					pn = auxt->pvnr_newnode;
522					pn->pn_path = pcn.pcn_fullpath;
523					pn->pn_plen = pcn.pcn_fullplen;
524				}
525			}
526
527			break;
528		}
529
530		case PUFFS_VN_MKNOD:
531		{
532			struct puffs_vnreq_mknod *auxt = auxbuf;
533			struct puffs_cn pcn;
534			if (pops->puffs_node_mknod == NULL) {
535				error = 0;
536				break;
537			}
538
539			pcn.pcn_pkcnp = &auxt->pvnr_cn;
540			if (buildpath) {
541				error = do_buildpath(&pcn, preq->preq_cookie);
542				if (error)
543					break;
544			}
545
546			error = pops->puffs_node_mknod(pcc,
547			    preq->preq_cookie, &auxt->pvnr_newnode,
548			    &pcn, &auxt->pvnr_va);
549
550			if (buildpath) {
551				if (error) {
552					free(pcn.pcn_fullpath);
553				} else {
554					struct puffs_node *pn;
555
556					pn = auxt->pvnr_newnode;
557					pn->pn_path = pcn.pcn_fullpath;
558					pn->pn_plen = pcn.pcn_fullplen;
559				}
560			}
561
562			break;
563		}
564
565		case PUFFS_VN_OPEN:
566		{
567			struct puffs_vnreq_open *auxt = auxbuf;
568			if (pops->puffs_node_open == NULL) {
569				error = 0;
570				break;
571			}
572
573			error = pops->puffs_node_open(pcc,
574			    preq->preq_cookie, auxt->pvnr_mode,
575			    &auxt->pvnr_cred, auxt->pvnr_pid);
576			break;
577		}
578
579		case PUFFS_VN_CLOSE:
580		{
581			struct puffs_vnreq_close *auxt = auxbuf;
582			if (pops->puffs_node_close == NULL) {
583				error = 0;
584				break;
585			}
586
587			error = pops->puffs_node_close(pcc,
588			    preq->preq_cookie, auxt->pvnr_fflag,
589			    &auxt->pvnr_cred, auxt->pvnr_pid);
590			break;
591		}
592
593		case PUFFS_VN_ACCESS:
594		{
595			struct puffs_vnreq_access *auxt = auxbuf;
596			if (pops->puffs_node_access == NULL) {
597				error = 0;
598				break;
599			}
600
601			error = pops->puffs_node_access(pcc,
602			    preq->preq_cookie, auxt->pvnr_mode,
603			    &auxt->pvnr_cred, auxt->pvnr_pid);
604			break;
605		}
606
607		case PUFFS_VN_GETATTR:
608		{
609			struct puffs_vnreq_getattr *auxt = auxbuf;
610			if (pops->puffs_node_getattr == NULL) {
611				error = EOPNOTSUPP;
612				break;
613			}
614
615			error = pops->puffs_node_getattr(pcc,
616			    preq->preq_cookie, &auxt->pvnr_va,
617			    &auxt->pvnr_cred, auxt->pvnr_pid);
618			break;
619		}
620
621		case PUFFS_VN_SETATTR:
622		{
623			struct puffs_vnreq_setattr *auxt = auxbuf;
624			if (pops->puffs_node_setattr == NULL) {
625				error = EOPNOTSUPP;
626				break;
627			}
628
629			error = pops->puffs_node_setattr(pcc,
630			    preq->preq_cookie, &auxt->pvnr_va,
631			    &auxt->pvnr_cred, auxt->pvnr_pid);
632			break;
633		}
634
635		case PUFFS_VN_MMAP:
636		{
637			struct puffs_vnreq_mmap *auxt = auxbuf;
638			if (pops->puffs_node_mmap == NULL) {
639				error = 0;
640				break;
641			}
642
643			error = pops->puffs_node_mmap(pcc,
644			    preq->preq_cookie, auxt->pvnr_fflags,
645			    &auxt->pvnr_cred, auxt->pvnr_pid);
646			break;
647		}
648
649		case PUFFS_VN_REVOKE:
650		{
651			struct puffs_vnreq_revoke *auxt = auxbuf;
652			if (pops->puffs_node_revoke == NULL) {
653				error = 0;
654				break;
655			}
656
657			error = pops->puffs_node_revoke(pcc,
658			    preq->preq_cookie, auxt->pvnr_flags);
659			break;
660		}
661
662		case PUFFS_VN_FSYNC:
663		{
664			struct puffs_vnreq_fsync *auxt = auxbuf;
665			if (pops->puffs_node_fsync == NULL) {
666				error = 0;
667				break;
668			}
669
670			error = pops->puffs_node_fsync(pcc,
671			    preq->preq_cookie, &auxt->pvnr_cred,
672			    auxt->pvnr_flags, auxt->pvnr_offlo,
673			    auxt->pvnr_offhi, auxt->pvnr_pid);
674			break;
675		}
676
677		case PUFFS_VN_SEEK:
678		{
679			struct puffs_vnreq_seek *auxt = auxbuf;
680			if (pops->puffs_node_seek == NULL) {
681				error = 0;
682				break;
683			}
684
685			error = pops->puffs_node_seek(pcc,
686			    preq->preq_cookie, auxt->pvnr_oldoff,
687			    auxt->pvnr_newoff, &auxt->pvnr_cred);
688			break;
689		}
690
691		case PUFFS_VN_REMOVE:
692		{
693			struct puffs_vnreq_remove *auxt = auxbuf;
694			struct puffs_cn pcn;
695			if (pops->puffs_node_remove == NULL) {
696				error = 0;
697				break;
698			}
699
700			pcn.pcn_pkcnp = &auxt->pvnr_cn;
701
702			error = pops->puffs_node_remove(pcc,
703			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
704			break;
705		}
706
707		case PUFFS_VN_LINK:
708		{
709			struct puffs_vnreq_link *auxt = auxbuf;
710			struct puffs_cn pcn;
711			if (pops->puffs_node_link == NULL) {
712				error = 0;
713				break;
714			}
715
716			pcn.pcn_pkcnp = &auxt->pvnr_cn;
717
718			error = pops->puffs_node_link(pcc,
719			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
720			break;
721		}
722
723		case PUFFS_VN_RENAME:
724		{
725			struct puffs_vnreq_rename *auxt = auxbuf;
726			struct puffs_cn pcn_src, pcn_targ;
727			struct puffs_node *pn_src;
728
729			if (pops->puffs_node_rename == NULL) {
730				error = 0;
731				break;
732			}
733
734			pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src;
735			pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ;
736			if (buildpath) {
737				pn_src = auxt->pvnr_cookie_src;
738				pcn_src.pcn_fullpath = pn_src->pn_path;
739				pcn_src.pcn_fullplen = pn_src->pn_plen;
740
741				error = do_buildpath(&pcn_targ,
742				    auxt->pvnr_cookie_targdir);
743				if (error)
744					break;
745			}
746
747			error = pops->puffs_node_rename(pcc,
748			    preq->preq_cookie, auxt->pvnr_cookie_src,
749			    &pcn_src, auxt->pvnr_cookie_targdir,
750			    auxt->pvnr_cookie_targ, &pcn_targ);
751
752			if (buildpath) {
753				if (error) {
754					free(pcn_targ.pcn_fullpath);
755				} else {
756					free(pn_src->pn_path);
757					pn_src->pn_path = pcn_targ.pcn_fullpath;
758					pn_src->pn_plen = pcn_targ.pcn_fullplen;
759				}
760			}
761			break;
762		}
763
764		case PUFFS_VN_MKDIR:
765		{
766			struct puffs_vnreq_mkdir *auxt = auxbuf;
767			struct puffs_cn pcn;
768			if (pops->puffs_node_mkdir == NULL) {
769				error = 0;
770				break;
771			}
772
773			pcn.pcn_pkcnp = &auxt->pvnr_cn;
774			if (buildpath) {
775				error = do_buildpath(&pcn, preq->preq_cookie);
776				if (error)
777					break;
778			}
779
780			error = pops->puffs_node_mkdir(pcc,
781			    preq->preq_cookie, &auxt->pvnr_newnode,
782			    &pcn, &auxt->pvnr_va);
783
784			if (buildpath) {
785				if (error) {
786					free(pcn.pcn_fullpath);
787				} else {
788					struct puffs_node *pn;
789
790					pn = auxt->pvnr_newnode;
791					pn->pn_path = pcn.pcn_fullpath;
792					pn->pn_plen = pcn.pcn_fullplen;
793				}
794			}
795
796			break;
797		}
798
799		case PUFFS_VN_RMDIR:
800		{
801			struct puffs_vnreq_rmdir *auxt = auxbuf;
802			struct puffs_cn pcn;
803			if (pops->puffs_node_rmdir == NULL) {
804				error = 0;
805				break;
806			}
807
808			pcn.pcn_pkcnp = &auxt->pvnr_cn;
809
810			error = pops->puffs_node_rmdir(pcc,
811			    preq->preq_cookie, auxt->pvnr_cookie_targ, &pcn);
812			break;
813		}
814
815		case PUFFS_VN_SYMLINK:
816		{
817			struct puffs_vnreq_symlink *auxt = auxbuf;
818			struct puffs_cn pcn;
819			if (pops->puffs_node_symlink == NULL) {
820				error = 0;
821				break;
822			}
823
824			pcn.pcn_pkcnp = &auxt->pvnr_cn;
825			if (buildpath) {
826				error = do_buildpath(&pcn, preq->preq_cookie);
827				if (error)
828					break;
829			}
830
831			error = pops->puffs_node_symlink(pcc,
832			    preq->preq_cookie, &auxt->pvnr_newnode,
833			    &pcn, &auxt->pvnr_va, auxt->pvnr_link);
834
835			if (buildpath) {
836				if (error) {
837					free(pcn.pcn_fullpath);
838				} else {
839					struct puffs_node *pn;
840
841					pn = auxt->pvnr_newnode;
842					pn->pn_path = pcn.pcn_fullpath;
843					pn->pn_plen = pcn.pcn_fullplen;
844				}
845			}
846
847			break;
848		}
849
850		case PUFFS_VN_READDIR:
851		{
852			struct puffs_vnreq_readdir *auxt = auxbuf;
853			size_t res;
854
855			if (pops->puffs_node_readdir == NULL) {
856				error = 0;
857				break;
858			}
859
860			res = auxt->pvnr_resid;
861			error = pops->puffs_node_readdir(pcc,
862			    preq->preq_cookie, auxt->pvnr_dent,
863			    &auxt->pvnr_cred, &auxt->pvnr_offset,
864			    &auxt->pvnr_resid);
865
866			/* need to move a bit more */
867			preq->preq_buflen = sizeof(struct puffs_vnreq_readdir)
868			    + (res - auxt->pvnr_resid);
869			break;
870		}
871
872		case PUFFS_VN_READLINK:
873		{
874			struct puffs_vnreq_readlink *auxt = auxbuf;
875			if (pops->puffs_node_readlink == NULL) {
876				error = EOPNOTSUPP;
877				break;
878			}
879
880			error = pops->puffs_node_readlink(pcc,
881			    preq->preq_cookie, &auxt->pvnr_cred,
882			    auxt->pvnr_link, &auxt->pvnr_linklen);
883			break;
884		}
885
886		case PUFFS_VN_RECLAIM:
887		{
888			struct puffs_vnreq_reclaim *auxt = auxbuf;
889			if (pops->puffs_node_reclaim == NULL) {
890				error = 0;
891				break;
892			}
893
894			error = pops->puffs_node_reclaim(pcc,
895			    preq->preq_cookie, auxt->pvnr_pid);
896			break;
897		}
898
899		case PUFFS_VN_INACTIVE:
900		{
901			struct puffs_vnreq_inactive *auxt = auxbuf;
902			if (pops->puffs_node_inactive == NULL) {
903				error = EOPNOTSUPP;
904				break;
905			}
906
907			error = pops->puffs_node_inactive(pcc,
908			    preq->preq_cookie, auxt->pvnr_pid,
909			    &auxt->pvnr_backendrefs);
910			break;
911		}
912
913		case PUFFS_VN_PATHCONF:
914		{
915			struct puffs_vnreq_pathconf *auxt = auxbuf;
916			if (pops->puffs_node_pathconf == NULL) {
917				error = 0;
918				break;
919			}
920
921			error = pops->puffs_node_pathconf(pcc,
922			    preq->preq_cookie, auxt->pvnr_name,
923			    &auxt->pvnr_retval);
924			break;
925		}
926
927		case PUFFS_VN_ADVLOCK:
928		{
929			struct puffs_vnreq_advlock *auxt = auxbuf;
930			if (pops->puffs_node_advlock == NULL) {
931				error = 0;
932				break;
933			}
934
935			error = pops->puffs_node_advlock(pcc,
936			    preq->preq_cookie, auxt->pvnr_id, auxt->pvnr_op,
937			    &auxt->pvnr_fl, auxt->pvnr_flags);
938			break;
939		}
940
941		case PUFFS_VN_PRINT:
942		{
943			if (pops->puffs_node_print == NULL) {
944				error = 0;
945				break;
946			}
947
948			error = pops->puffs_node_print(pcc,
949			    preq->preq_cookie);
950			break;
951		}
952
953		case PUFFS_VN_READ:
954		{
955			struct puffs_vnreq_read *auxt = auxbuf;
956			size_t res;
957
958			if (pops->puffs_node_read == NULL) {
959				error = EIO;
960				break;
961			}
962
963			res = auxt->pvnr_resid;
964			error = pops->puffs_node_read(pcc,
965			    preq->preq_cookie, auxt->pvnr_data,
966			    auxt->pvnr_offset, &auxt->pvnr_resid,
967			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
968
969			/* need to move a bit more */
970			preq->preq_buflen = sizeof(struct puffs_vnreq_read)
971			    + (res - auxt->pvnr_resid);
972			break;
973		}
974
975		case PUFFS_VN_WRITE:
976		{
977			struct puffs_vnreq_write *auxt = auxbuf;
978
979			if (pops->puffs_node_write == NULL) {
980				error = EIO;
981				break;
982			}
983
984			error = pops->puffs_node_write(pcc,
985			    preq->preq_cookie, auxt->pvnr_data,
986			    auxt->pvnr_offset, &auxt->pvnr_resid,
987			    &auxt->pvnr_cred, auxt->pvnr_ioflag);
988
989			/* don't need to move data back to the kernel */
990			preq->preq_buflen = sizeof(struct puffs_vnreq_write);
991			break;
992		}
993
994/* holy bitrot, ryydman! */
995#if 0
996		case PUFFS_VN_IOCTL:
997			error = pops->puffs_node_ioctl1(pcc, preq->preq_cookie,
998			     (struct puffs_vnreq_ioctl *)auxbuf, &pop);
999			if (error != 0)
1000				break;
1001			pop.pso_reqid = preq->preq_id;
1002
1003			/* let the kernel do it's intermediate duty */
1004			error = ioctl(pu->pu_fd, PUFFSSIZEOP, &pop);
1005			/*
1006			 * XXX: I don't actually know what the correct
1007			 * thing to do in case of an error is, so I'll
1008			 * just ignore it for the time being.
1009			 */
1010			error = pops->puffs_node_ioctl2(pcc, preq->preq_cookie,
1011			    (struct puffs_vnreq_ioctl *)auxbuf, &pop);
1012			break;
1013
1014		case PUFFS_VN_FCNTL:
1015			error = pops->puffs_node_fcntl1(pcc, preq->preq_cookie,
1016			     (struct puffs_vnreq_fcntl *)auxbuf, &pop);
1017			if (error != 0)
1018				break;
1019			pop.pso_reqid = preq->preq_id;
1020
1021			/* let the kernel do it's intermediate duty */
1022			error = ioctl(pu->pu_fd, PUFFSSIZEOP, &pop);
1023			/*
1024			 * XXX: I don't actually know what the correct
1025			 * thing to do in case of an error is, so I'll
1026			 * just ignore it for the time being.
1027			 */
1028			error = pops->puffs_node_fcntl2(pcc, preq->preq_cookie,
1029			    (struct puffs_vnreq_fcntl *)auxbuf, &pop);
1030			break;
1031#endif
1032
1033		default:
1034			printf("inval op %d\n", preq->preq_optype);
1035			error = EINVAL;
1036			break;
1037		}
1038	} else {
1039		/*
1040		 * this one also
1041		 */
1042		error = EINVAL;
1043	}
1044
1045	preq->preq_rv = error;
1046
1047	pcc->pcc_rv = rv;
1048	pcc->pcc_flags |= PCC_DONE;
1049}
1050
1051static int
1052do_buildpath(struct puffs_cn *pcn, void *parent)
1053{
1054	struct puffs_node *pn_parent;
1055	size_t plen;
1056
1057	pn_parent = parent; /* XXX */
1058	assert(pn_parent->pn_path != NULL);
1059
1060	/* +1 not for nul but for / */
1061	plen = pn_parent->pn_plen + pcn->pcn_namelen + 1;
1062	pcn->pcn_fullpath = malloc(plen);
1063	if (!pcn->pcn_fullpath)
1064		return errno;
1065
1066	strcpy(pcn->pcn_fullpath, pn_parent->pn_path);
1067	strcat(pcn->pcn_fullpath, "/");
1068	strcat(pcn->pcn_fullpath, pcn->pcn_name);
1069	pcn->pcn_fullpath[plen-1] = '\0'; /* paranoia */
1070	pcn->pcn_fullplen = plen;
1071
1072	return 0;
1073}
1074
1075
1076#if 0
1077		case PUFFS_VN_POLL:
1078		{
1079			struct puffs_vnreq_poll *auxt = auxbuf;
1080			if (pops->puffs_node_poll == NULL) {
1081				error = 0;
1082				break;
1083			}
1084
1085			error = pops->puffs_node_poll(pcc,
1086			    preq->preq_cookie, preq-);
1087			break;
1088		}
1089
1090		case PUFFS_VN_KQFILTER:
1091		{
1092			struct puffs_vnreq_kqfilter *auxt = auxbuf;
1093			if (pops->puffs_node_kqfilter == NULL) {
1094				error = 0;
1095				break;
1096			}
1097
1098			error = pops->puffs_node_kqfilter(pcc,
1099			    preq->preq_cookie, );
1100			break;
1101		}
1102
1103		case PUFFS_VN_CLOSEEXTATTR:
1104		{
1105			struct puffs_vnreq_closeextattr *auxt = auxbuf;
1106			if (pops->puffs_closeextattr == NULL) {
1107				error = 0;
1108				break;
1109			}
1110
1111			error = pops->puffs_closeextattr(pcc,
1112			    preq->preq_cookie, );
1113			break;
1114		}
1115
1116		case PUFFS_VN_GETEXTATTR:
1117		{
1118			struct puffs_vnreq_getextattr *auxt = auxbuf;
1119			if (pops->puffs_getextattr == NULL) {
1120				error = 0;
1121				break;
1122			}
1123
1124			error = pops->puffs_getextattr(pcc,
1125			    preq->preq_cookie, );
1126			break;
1127		}
1128
1129		case PUFFS_VN_LISTEXTATTR:
1130		{
1131			struct puffs_vnreq_listextattr *auxt = auxbuf;
1132			if (pops->puffs_listextattr == NULL) {
1133				error = 0;
1134				break;
1135			}
1136
1137			error = pops->puffs_listextattr(pcc,
1138			    preq->preq_cookie, );
1139			break;
1140		}
1141
1142		case PUFFS_VN_OPENEXTATTR:
1143		{
1144			struct puffs_vnreq_openextattr *auxt = auxbuf;
1145			if (pops->puffs_openextattr == NULL) {
1146				error = 0;
1147				break;
1148			}
1149
1150			error = pops->puffs_openextattr(pcc,
1151			    preq->preq_cookie, );
1152			break;
1153		}
1154
1155		case PUFFS_VN_DELETEEXTATTR:
1156		{
1157			struct puffs_vnreq_deleteextattr *auxt = auxbuf;
1158			if (pops->puffs_deleteextattr == NULL) {
1159				error = 0;
1160				break;
1161			}
1162
1163			error = pops->puffs_deleteextattr(pcc,
1164			    preq->preq_cookie, );
1165			break;
1166		}
1167
1168		case PUFFS_VN_SETEXTATTR:
1169		{
1170			struct puffs_vnreq_setextattr *auxt = auxbuf;
1171			if (pops->puffs_setextattr == NULL) {
1172				error = 0;
1173				break;
1174			}
1175
1176			error = pops->puffs_setextattr(pcc,
1177			    preq->preq_cookie, );
1178			break;
1179		}
1180
1181#endif
1182