xref: /netbsd-src/lib/libperfuse/ops.c (revision 179b12252ecaf3553d9c2b7458ce62b6a2203d0c)
1 /*  $NetBSD: ops.c,v 1.4 2010/08/28 03:46:21 manu Exp $ */
2 
3 /*-
4  *  Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions
8  *  are met:
9  *  1. Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16  *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19  *  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  *  POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <libgen.h>
32 #include <errno.h>
33 #include <err.h>
34 #include <sysexits.h>
35 #include <syslog.h>
36 #include <puffs.h>
37 #include <sys/vnode.h>
38 #include <sys/socket.h>
39 #include <machine/vmparam.h>
40 
41 #include "perfuse_priv.h"
42 #include "fuse.h"
43 
44 static int no_access(puffs_cookie_t, const struct puffs_cred *, mode_t);
45 static void fuse_attr_to_vap(struct perfuse_state *,
46     struct vattr *, struct fuse_attr *);
47 static int node_lookup_dir_nodot(struct puffs_usermount *,
48     puffs_cookie_t, char *, size_t, struct puffs_node **);
49 static int node_lookup_common(struct puffs_usermount *, puffs_cookie_t,
50     const char*, struct puffs_node **);
51 static int node_mk_common(struct puffs_usermount *, puffs_cookie_t,
52     struct puffs_newinfo *, const struct puffs_cn *pcn,
53     const struct vattr *, perfuse_msg_t *);
54 static const char *basename_r(const char *);
55 static ssize_t fuse_to_dirent(struct puffs_usermount *, puffs_cookie_t,
56     struct fuse_dirent *, size_t);
57 static int readdir_buffered(struct perfuse_state *, puffs_cookie_t,
58     struct dirent *, off_t *, size_t *, const struct puffs_cred *,
59     int *, off_t *, size_t *);
60 static void requeue_request(struct puffs_usermount *,
61     puffs_cookie_t opc, enum perfuse_qtype);
62 static void dequeue_requests(struct perfuse_state *,
63      puffs_cookie_t opc, enum perfuse_qtype, int);
64 #define DEQUEUE_ALL 0
65 
66 /*
67  *  From <sys/vnode>, inside #ifdef _KERNEL section
68  */
69 #define IO_SYNC		(0x40|IO_DSYNC)
70 #define IO_DSYNC	0x00200
71 #define IO_DIRECT	0x02000
72 
73 /*
74  *  From <fcntl>, inside #ifdef _KERNEL section
75  */
76 #define F_WAIT		0x010
77 #define F_FLOCK		0x020
78 
79 /*
80  * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h
81  */
82 const enum vtype iftovt_tab[16] = {
83 	VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
84         VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
85 };
86 const int vttoif_tab[9] = {
87 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
88         S_IFSOCK, S_IFIFO, S_IFMT,
89 };
90 
91 #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
92 #define VTTOIF(indx) (vttoif_tab[(int)(indx)])
93 
94 
95 static int
96 no_access(opc, pcr, mode)
97 	puffs_cookie_t opc;
98 	const struct puffs_cred *pcr;
99 	mode_t mode;
100 {
101 	struct puffs_node *pn;
102 	struct vattr *va;
103 
104 	pn = (struct puffs_node *)opc;
105 	va = puffs_pn_getvap(pn);
106 
107 	return puffs_access(va->va_type, va->va_mode,
108 			    va->va_uid, va->va_gid,
109 			    mode, pcr);
110 }
111 
112 static void
113 fuse_attr_to_vap(ps, vap, fa)
114 	struct perfuse_state *ps;
115 	struct vattr *vap;
116 	struct fuse_attr *fa;
117 {
118 	vap->va_type = IFTOVT(fa->mode);
119 	vap->va_mode = fa->mode;
120 	vap->va_nlink = fa->nlink;
121 	vap->va_uid = fa->uid;
122 	vap->va_gid = fa->gid;
123 	vap->va_fsid = ps->ps_fsid;
124 	vap->va_fileid = fa->ino;
125 	vap->va_size = fa->size;
126 	vap->va_blocksize = fa->blksize;
127 	vap->va_atime.tv_sec = (long)fa->atime;
128 	vap->va_atime.tv_nsec = fa->atimensec;
129 	vap->va_mtime.tv_sec = (long)fa->mtime;
130 	vap->va_mtime.tv_nsec = fa->mtimensec;
131 	vap->va_ctime.tv_sec = (long)fa->ctime;
132 	vap->va_ctime.tv_nsec = fa->ctimensec;
133 	vap->va_birthtime.tv_sec = 0;
134 	vap->va_birthtime.tv_nsec = 0;
135 	vap->va_gen = 0;
136 	vap->va_flags = 0;
137 	vap->va_rdev = fa->rdev;
138 	vap->va_bytes = fa->size;
139 	vap->va_filerev = 0;
140 	vap->va_vaflags = 0;
141 
142 	if (vap->va_blocksize == 0)
143 		vap->va_blocksize = DEV_BSIZE;
144 
145 	if (vap->va_size == (size_t)-1) /* XXX */
146 		vap->va_size = 0;
147 
148 	return;
149 }
150 
151 
152 /*
153  * Lookup name in directory opc
154  * We take special care of name being . or ..
155  * These are returned by readdir and deserve tweaks.
156  */
157 static int
158 node_lookup_dir_nodot(pu, opc, name, namelen, pnp)
159 	struct puffs_usermount *pu;
160 	puffs_cookie_t opc;
161 	char *name;
162 	size_t namelen;
163 	struct puffs_node **pnp;
164 {
165 	char *path;
166 	struct puffs_node *dpn = (struct puffs_node *)opc;
167 	int error;
168 
169 	/*
170 	 *  is easy as we already know it
171 	 */
172 	if (strncmp(name, ".", namelen) == 0) {
173 		*pnp = (struct puffs_node *)opc;
174 		return 0;
175 	}
176 
177 	/*
178 	 * For .. we just forget the name part
179 	 */
180 	if (strncmp(name, "..", namelen) == 0)
181 		namelen = 0;
182 
183 	namelen = PNPLEN(dpn) + 1 + namelen + 1;
184 	if ((path = malloc(namelen)) == NULL)
185 		DERR(EX_OSERR, "malloc failed");
186 	(void)snprintf(path, namelen, "%s/%s", (char *)PNPATH(dpn), name);
187 
188 	error = node_lookup_common(pu, opc, path, pnp);
189 
190 	free(path);
191 
192 	return error;
193 }
194 
195 static int
196 node_lookup_common(pu, opc, path, pnp)
197 	struct puffs_usermount *pu;
198 	puffs_cookie_t opc;
199 	const char *path;
200 	struct puffs_node **pnp;
201 {
202 	struct perfuse_state *ps;
203 	perfuse_msg_t *pm;
204 	struct fuse_entry_out *feo;
205 	struct puffs_node *pn;
206 	size_t len;
207 	int error;
208 
209 	ps = puffs_getspecific(pu);
210 
211 	path = basename_r(path);
212 	len = strlen(path) + 1;
213 
214 	pm = ps->ps_new_msg(pu, opc, FUSE_LOOKUP, len, NULL);
215 	(void)strlcpy(_GET_INPAYLOAD(ps, pm, char *), path, len);
216 
217 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
218 		goto out;
219 
220 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
221 
222 	pn = perfuse_new_pn(pu, opc);
223 	PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
224 
225 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
226 
227 	if (pnp != NULL)
228 		*pnp = pn;
229 
230 out:
231 	ps->ps_destroy_msg(pm);
232 
233 	return error;
234 }
235 
236 
237 /*
238  * Common final code for methods that create objects:
239  * perfuse_node_mkdir
240  * perfuse_node_mknod
241  * perfuse_node_symlink
242  */
243 static int
244 node_mk_common(pu, opc, pni, pcn, vap, pm)
245 	struct puffs_usermount *pu;
246 	puffs_cookie_t opc;
247 	struct puffs_newinfo *pni;
248 	const struct puffs_cn *pcn;
249 	const struct vattr *vap;
250 	perfuse_msg_t *pm;
251 {
252 	struct perfuse_state *ps;
253 	struct puffs_node *pn;
254 	struct fuse_entry_out *feo;
255 	struct fuse_setattr_in *fsi;
256 	int error;
257 
258 	ps =  puffs_getspecific(pu);
259 
260 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*feo))) != 0)
261 		goto out;
262 
263 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
264 	if (feo->nodeid == PERFUSE_UNKNOWN_INO)
265 		DERRX(EX_SOFTWARE, "%s: no ino", __func__);
266 
267 	pn = perfuse_new_pn(pu, opc);
268 	PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
269 
270 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
271 	puffs_newinfo_setcookie(pni, pn);
272 	ps->ps_destroy_msg(pm);
273 
274 	/*
275 	 * Set owner and group
276 	 */
277 	(void)puffs_cred_getuid(pcn->pcn_cred, &pn->pn_va.va_uid);
278 	(void)puffs_cred_getgid(pcn->pcn_cred, &pn->pn_va.va_gid);
279 
280 	pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn,
281 			    FUSE_SETATTR, sizeof(*fsi), NULL);
282 	fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
283 	fsi->uid = pn->pn_va.va_uid;
284 	fsi->gid = pn->pn_va.va_gid;
285 	fsi->valid = FUSE_FATTR_UID|FUSE_FATTR_GID;
286 
287 	/*
288 	 * A fuse_attr_out is returned, but we ignore it.
289 	 */
290 	error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
291 
292 out:
293 	ps->ps_destroy_msg(pm);
294 
295 	return error;
296 }
297 
298 static const char *
299 basename_r(string)
300 	const char *string;
301 {
302 	char *result;
303 
304 	if ((result = rindex(string, '/')) == NULL)
305 		return string;
306 
307 	/*
308 	 * We are finished if this is not a trailing /
309 	 */
310 	if (result[1] != '\0')
311 		return result + 1;
312 
313 
314 	/*
315 	 * Go back until we found something else than a /
316 	 */
317 	while (result != string) {
318 		result--;
319 		if (result[0] != '/')
320 			break;
321 	}
322 
323 	if (result == string)
324 		return string;
325 
326 	if ((result = rindex(string, '/')) == NULL)
327 		return string;
328 
329 	return result + 1;
330 
331 }
332 
333 static ssize_t
334 fuse_to_dirent(pu, opc, fd, fd_len)
335 	struct puffs_usermount *pu;
336 	puffs_cookie_t opc;
337 	struct fuse_dirent *fd;
338 	size_t fd_len;
339 {
340 	struct dirent *dents;
341 	size_t dents_len;
342 	ssize_t written;
343 	uint64_t fd_offset;
344 	struct fuse_dirent *fd_base;
345 	size_t len;
346 
347 	fd_base = fd;
348 	fd_offset = 0;
349 	written = 0;
350 	dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
351 	dents_len = PERFUSE_NODE_DATA(opc)->pnd_dirent_len;
352 
353 	do {
354 		char *ndp;
355 		size_t reclen;
356 
357 		reclen = _DIRENT_RECLEN(dents, fd->namelen);
358 
359 		/*
360 		 * Check we do not overflow the output buffer
361 		 * struct fuse_dirent is bigger than struct dirent,
362 		 * so we should always use fd_len and never reallocate
363 		 * later.
364 		 * If we have to reallocate,try to double the buffer
365 		 * each time so that we do not have to do it too often.
366 		 */
367 		if (written + reclen > dents_len) {
368 			if (dents_len == 0)
369 				dents_len = fd_len;
370 			else
371 				dents_len =
372 				   MAX(2 * dents_len, written + reclen);
373 
374 			dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
375 			if ((dents = realloc(dents, dents_len)) == NULL)
376 				DERR(EX_OSERR, "malloc failed");
377 
378 			PERFUSE_NODE_DATA(opc)->pnd_dirent = dents;
379 			PERFUSE_NODE_DATA(opc)->pnd_dirent_len = dents_len;
380 
381 			/*
382 			 * (void *) for delint
383 			 */
384 			ndp = (char *)(void *)dents + written;
385 			dents = (struct dirent *)(void *)ndp;
386 		}
387 
388 
389 
390 		/*
391 		 * Filesystem was mounted without -o use_ino
392 		 * Perform a lookup to find it.
393 		 * XXX still broken
394 		 */
395 		if (fd->ino == PERFUSE_UNKNOWN_INO) {
396 			struct puffs_node *pn;
397 
398 			if (node_lookup_dir_nodot(pu, opc, fd->name,
399 						  fd->namelen, &pn) != 0)
400 				DERRX(EX_SOFTWARE,
401 				     "node_lookup_dir_nodot failed");
402 
403 			fd->ino = PERFUSE_NODE_DATA(pn)->pnd_ino;
404 		}
405 
406 		dents->d_fileno = fd->ino;
407 		dents->d_reclen = reclen;
408 		dents->d_namlen = fd->namelen;
409 		dents->d_type = fd->type;
410 		strlcpy(dents->d_name, fd->name, fd->namelen + 1);
411 
412 #ifdef PERFUSE_DEBUG
413 		if (perfuse_diagflags & PDF_READDIR)
414 			DPRINTF("%s: translated \"%s\" ino = %lld\n",
415 				__func__, dents->d_name, dents->d_fileno);
416 #endif
417 
418 		dents = _DIRENT_NEXT(dents);
419 		written += reclen;
420 
421 		/*
422 		 * Move to the next record.
423 		 * fd->off seems unreliable, for instance, flusterfs
424 		 * does not clear the unused bits, and we get
425 		 * 0xffffffffb9b95040 instead of just 0x40. Use
426 		 * record alignement instead.
427 		 */
428 		len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen);
429 #ifdef PERFUSE_DEBUG
430 		if (perfuse_diagflags & PDF_READDIR)
431 			DPRINTF("%s: record at %lld/0x%llx length = %d/0x%x. "
432 				"next record at %lld/0x%llx, max %d/0x%x\n",
433 				__func__, fd_offset, fd_offset, len, len,
434 				fd_offset + len, fd_offset + len,
435 				fd_len, fd_len);
436 #endif
437 		fd_offset += len;
438 
439 		/*
440 		 * Check if next record is still within the packet
441 		 * If it is not, we reached the end of the buffer.
442 		 */
443 		if (fd_offset >= fd_len)
444 			break;
445 
446 		/*
447 		 * (void *) for delint
448 		 */
449 		ndp = (char *)(void *)fd_base + (size_t)fd_offset;
450 		fd = (struct fuse_dirent *)(void *)ndp;
451 
452 	} while (1 /* CONSTCOND */);
453 
454 	/*
455 	 * Adjust the dirent output length
456 	 */
457 	if (written != -1)
458 		PERFUSE_NODE_DATA(opc)->pnd_dirent_len = written;
459 
460 	return written;
461 }
462 
463 /* ARGSUSED0 */
464 static int
465 readdir_buffered(ps, opc, dent, readoff,
466 		     reslen, pcr, eofflag, cookies, ncookies)
467 	struct perfuse_state *ps;
468 	puffs_cookie_t opc;
469 	struct dirent *dent;
470 	off_t *readoff;
471 	size_t *reslen;
472 	const struct puffs_cred *pcr;
473 	int *eofflag;
474 	off_t *cookies;
475 	size_t *ncookies;
476 {
477 	struct dirent *fromdent;
478 	struct perfuse_node_data *pnd;
479 	char *ndp;
480 
481 	pnd = PERFUSE_NODE_DATA(opc);
482 
483 	while (*readoff < pnd->pnd_dirent_len) {
484 		/*
485 		 * (void *) for delint
486 		 */
487 		ndp = (char *)(void *)pnd->pnd_dirent + (size_t)*readoff;
488 		fromdent = (struct dirent *)(void *)ndp;
489 
490 		if (*reslen < _DIRENT_SIZE(fromdent))
491 			break;
492 
493 		memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
494 		*readoff += _DIRENT_SIZE(fromdent);
495 		*reslen -= _DIRENT_SIZE(fromdent);
496 
497 		dent = _DIRENT_NEXT(dent);
498 	}
499 
500 #ifdef PERFUSE_DEBUG
501 	if (perfuse_diagflags & PDF_READDIR)
502 		DPRINTF("%s: readoff = %lld,  pnd->pnd_dirent_len = %d\n",
503 			__func__, *readoff, pnd->pnd_dirent_len);
504 #endif
505 	if (*readoff >=  pnd->pnd_dirent_len) {
506 		free(pnd->pnd_dirent);
507 		pnd->pnd_dirent = NULL;
508 		pnd->pnd_dirent_len = 0;
509 		*eofflag = 1;
510 	}
511 
512 	return 0;
513 }
514 
515 /* ARGSUSED0 */
516 static void
517 requeue_request(pu, opc, type)
518 	struct puffs_usermount *pu;
519 	puffs_cookie_t opc;
520 	enum perfuse_qtype type;
521 {
522 	struct perfuse_cc_queue pcq;
523 	struct perfuse_node_data *pnd;
524 #ifdef PERFUSE_DEBUG
525 	struct perfuse_state *ps;
526 
527 	ps = perfuse_getspecific(pu);
528 #endif
529 
530 	/*
531 	 * XXX Add a lock he day we go multithreaded
532 	 */
533 	pnd = PERFUSE_NODE_DATA(opc);
534 	pcq.pcq_type = type;
535 	pcq.pcq_cc = puffs_cc_getcc(pu);
536 	TAILQ_INSERT_TAIL(&pnd->pnd_pcq, &pcq, pcq_next);
537 
538 #ifdef PERFUSE_DEBUG
539 
540 	if (perfuse_diagflags & PDF_REQUEUE)
541 		DPRINTF("%s: REQUEUE opc = %p, pcc = %p\n",
542 		       __func__, (void *)opc, pcq.pcq_cc);
543 #endif
544 
545 	puffs_cc_yield(pcq.pcq_cc);
546 
547 #ifdef PERFUSE_DEBUG
548 	if (perfuse_diagflags & PDF_REQUEUE)
549 		DPRINTF("%s: RESUME opc = %p, pcc = %p\n",
550 		        __func__, (void *)opc, pcq.pcq_cc);
551 #endif
552 
553 	return;
554 }
555 
556 /* ARGSUSED0 */
557 static void
558 dequeue_requests(ps, opc, type, max)
559 	struct perfuse_state *ps;
560 	puffs_cookie_t opc;
561 	enum perfuse_qtype type;
562 	int max;
563 {
564 	struct perfuse_cc_queue *pcq;
565 	struct perfuse_node_data *pnd;
566 	int dequeued;
567 
568 	/*
569 	 * XXX Add a lock he day we go multithreaded
570 	 */
571 	pnd = PERFUSE_NODE_DATA(opc);
572 	dequeued = 0;
573 	TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) {
574 		if (pcq->pcq_type != type)
575 			continue;
576 
577 		puffs_cc_schedule(pcq->pcq_cc);
578 		TAILQ_REMOVE(&pnd->pnd_pcq, pcq, pcq_next);
579 
580 #ifdef PERFUSE_DEBUG
581 		if (perfuse_diagflags & PDF_REQUEUE)
582 			DPRINTF("%s: SCHEDULE opc = %p, pcc = %p\n",
583 				__func__, (void *)opc, pcq->pcq_cc);
584 #endif
585 
586 		if (++dequeued == max)
587 			break;
588 	}
589 
590 #ifdef PERFUSE_DEBUG
591 	if (perfuse_diagflags & PDF_REQUEUE)
592 		DPRINTF("%s: DONE  opc = %p\n", __func__, (void *)opc);
593 #endif
594 
595 	return;
596 }
597 
598 void
599 perfuse_fs_init(pu)
600 	struct puffs_usermount *pu;
601 {
602 	struct perfuse_state *ps;
603 	perfuse_msg_t *pm;
604 	struct fuse_init_in *fii;
605 	struct fuse_init_out *fio;
606 	int error;
607 
608 	ps = puffs_getspecific(pu);
609 
610         if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0)
611                 DERR(EX_OSERR, "puffs_mount failed");
612 
613 	/*
614 	 * Linux 2.6.34.1 sends theses flags:
615 	 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC
616 	 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK
617 	 *
618 	 * Linux also sets max_readahead at 32 pages (128 kB)
619 	 */
620 	pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL);
621 	fii = GET_INPAYLOAD(ps, pm, fuse_init_in);
622 	fii->major = FUSE_KERNEL_VERSION;
623 	fii->minor = FUSE_KERNEL_MINOR_VERSION;
624 	fii->max_readahead = 32 * PAGE_SIZE;
625 	fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC);
626 
627 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fio))) != 0)
628 		DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error);
629 
630 	fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out);
631 	ps->ps_max_readahead = fio->max_readahead;
632 	ps->ps_max_write = fio->max_write;
633 
634 	ps->ps_destroy_msg(pm);
635 
636 	return;
637 }
638 
639 int
640 perfuse_fs_unmount(pu, flags)
641 	struct puffs_usermount *pu;
642 	int flags;
643 {
644 	perfuse_msg_t *pm;
645 	struct perfuse_state *ps;
646 	puffs_cookie_t opc;
647 	int error;
648 
649 	ps = puffs_getspecific(pu);
650 
651 	opc = (puffs_cookie_t)puffs_getroot(pu);
652 	pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL);
653 
654 	if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0) {
655 		DWARN("unmount %s", ps->ps_target);
656 		if (!(flags & MNT_FORCE))
657 			goto out;
658 	}
659 
660 	DPRINTF("%s unmounted, exit\n", ps->ps_target);
661 
662 	exit(0);
663 out:
664 	ps->ps_destroy_msg(pm);
665 
666 	return error;
667 }
668 
669 int
670 perfuse_fs_statvfs(pu, svfsb)
671 	struct puffs_usermount *pu;
672 	struct statvfs *svfsb;
673 {
674 	struct perfuse_state *ps;
675 	perfuse_msg_t *pm;
676 	puffs_cookie_t opc;
677 	struct fuse_statfs_out *fso;
678 	int error;
679 
680 	ps = puffs_getspecific(pu);
681 	opc = (puffs_cookie_t)puffs_getroot(pu);
682 	pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
683 
684 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fso))) != 0)
685 		goto out;
686 
687 	fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
688 	svfsb->f_flag = ps->ps_mountflags;
689 	svfsb->f_bsize = fso->st.bsize;
690 	svfsb->f_frsize = fso->st.frsize;
691 	svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize;
692 	svfsb->f_blocks = fso->st.blocks;
693 	svfsb->f_bfree = fso->st.bfree;
694 	svfsb->f_bavail = fso->st.bavail;
695 	svfsb->f_bresvd = fso->st.bfree - fso->st.bavail;
696 	svfsb->f_files = fso->st.files;
697 	svfsb->f_ffree = fso->st.ffree;
698 	svfsb->f_favail = fso->st.ffree;/* files not reserved for root */
699 	svfsb->f_fresvd = 0;		/* files reserved for root */
700 
701 	svfsb->f_syncreads = ps->ps_syncreads;
702 	svfsb->f_syncwrites = ps->ps_syncwrites;
703 
704 	svfsb->f_asyncreads = ps->ps_asyncreads;
705 	svfsb->f_asyncwrites = ps->ps_asyncwrites;
706 
707 	svfsb->f_fsidx.__fsid_val[0] = ps->ps_fsid;
708 	svfsb->f_fsidx.__fsid_val[1] = 0;
709 	svfsb->f_fsid = ps->ps_fsid;
710 	svfsb->f_namemax = MAXPATHLEN;	/* XXX */
711 	svfsb->f_owner = ps->ps_owner_uid;
712 
713 	(void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN);
714 
715 	if (ps->ps_filesystemtype != NULL)
716 		(void)strlcpy(svfsb->f_fstypename,
717 			      ps->ps_filesystemtype, _VFS_NAMELEN);
718 	else
719 		(void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN);
720 
721 	if (ps->ps_source != NULL)
722 		strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN);
723 	else
724 		strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN);
725 out:
726 	ps->ps_destroy_msg(pm);
727 
728 	return error;
729 }
730 
731 int
732 perfuse_fs_sync(pu, waitfor, pcr)
733 	struct puffs_usermount *pu;
734 	int waitfor;
735 	const struct puffs_cred *pcr;
736 {
737 	/*
738 	 * FUSE does not seem to have a FS sync callback.
739 	 * Maybe do not even register this callback
740 	 */
741 	return puffs_fsnop_sync(pu, waitfor, pcr);
742 }
743 
744 /* ARGSUSED0 */
745 int
746 perfuse_fs_fhtonode(pu, fid, fidsize, pni)
747 	struct puffs_usermount *pu;
748 	void *fid;
749 	size_t fidsize;
750 	struct puffs_newinfo *pni;
751 {
752 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
753 	return 0;
754 }
755 
756 /* ARGSUSED0 */
757 int
758 perfuse_fs_nodetofh(pu, cookie, fid, fidsize)
759 	struct puffs_usermount *pu;
760 	puffs_cookie_t cookie;
761 	void *fid;
762 	size_t *fidsize;
763 {
764 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
765 	return 0;
766 }
767 
768 #if 0
769 /* ARGSUSED0 */
770 void
771 perfuse_fs_extattrctl(pu, cmd, cookie, flags, namespace, attrname)
772 	struct puffs_usermount *pu;
773 	int cmd,
774 	puffs_cookie_t *cookie;
775 	int flags;
776 	int namespace;
777 	const char *attrname;
778 {
779 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
780 	return 0;
781 }
782 #endif /* 0 */
783 
784 /* ARGSUSED0 */
785 void
786 perfuse_fs_suspend(pu, status)
787 	struct puffs_usermount *pu;
788 	int status;
789 {
790 	return;
791 }
792 
793 
794 
795 int
796 perfuse_node_lookup(pu, opc, pni, pcn)
797 	struct puffs_usermount *pu;
798 	puffs_cookie_t opc;
799 	struct puffs_newinfo *pni;
800 	const struct puffs_cn *pcn;
801 {
802 	struct puffs_node *pn;
803 	int error;
804 
805 	/*
806 	 * XXX This is borrowed from librefuse,
807 	 * and __UNCONST is said to be fixed.
808 	 */
809         pn = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
810         		       __UNCONST(&pcn->pcn_po_full));
811 
812 	if (pn == NULL) {
813 		error = node_lookup_common(pu, opc, (char *)PCNPATH(pcn), &pn);
814 		if (error != 0)
815 			return error;
816 	}
817 
818 	/*
819 	 * If that node had a pending reclaim, wipe it out.
820 	 */
821 	PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED;
822 
823 	puffs_newinfo_setcookie(pni, pn);
824 	puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
825 	puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size);
826 	puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
827 
828 	return 0;
829 }
830 
831 int
832 perfuse_node_create(pu, opc, pni, pcn, vap)
833 	struct puffs_usermount *pu;
834 	puffs_cookie_t opc;
835 	struct puffs_newinfo *pni;
836 	const struct puffs_cn *pcn;
837 	const struct vattr *vap;
838 {
839 	perfuse_msg_t *pm;
840 	struct perfuse_state *ps;
841 	struct fuse_create_in *fci;
842 	struct fuse_entry_out *feo;
843 	struct fuse_open_out *foo;
844 	struct puffs_node *pn;
845 	const char *name;
846 	size_t namelen;
847 	size_t len;
848 	int error;
849 
850 	/*
851 	 * Create an object require -WX permission in the parent directory
852 	 */
853 	if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
854 		return EACCES;
855 
856 	/*
857 	 * If create is unimplemented: Check that it does not
858 	 * already exists, and if not, do mknod and open
859 	 */
860 	ps = puffs_getspecific(pu);
861 	if (ps->ps_flags & PS_NO_CREAT) {
862 		error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn);
863 		if (error == 0)
864 			return EEXIST;
865 
866 		error = perfuse_node_mknod(pu, opc, pni, pcn, vap);
867 		if (error != 0)
868 			return error;
869 
870 		error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred);
871 		if (error != 0)
872 			return error;
873 
874 		return 0;
875 	}
876 
877 	name = basename_r((char *)PCNPATH(pcn));
878 	namelen = strlen(name) + 1;
879 	len = sizeof(*fci) + namelen;
880 
881 	pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred);
882 	fci = GET_INPAYLOAD(ps, pm, fuse_create_in);
883 	fci->flags = 0; 	/* No flags seems available */
884 	fci->mode = vap->va_mode;
885 	fci->umask = 0; 	/* Seems unused bu libfuse */
886 	(void)strlcpy((char*)(void *)(fci + 1), name, namelen);
887 
888 	len = sizeof(*feo) + sizeof(*foo);
889 	if ((error = XCHG_MSG(ps, pu, pm, len)) != 0)
890 		goto out;
891 
892 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
893 	foo = (struct fuse_open_out *)(void *)(feo + 1);
894 	if (feo->nodeid == PERFUSE_UNKNOWN_INO)
895 		DERRX(EX_SOFTWARE, "%s: no ino", __func__);
896 
897 	/*
898 	 * Save the file handle and inode in node private data
899 	 * so that we can reuse it later
900 	 */
901 	pn = perfuse_new_pn(pu, opc);
902 	perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
903 	PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid;
904 
905 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
906 	puffs_newinfo_setcookie(pni, pn);
907 
908 out:
909 	ps->ps_destroy_msg(pm);
910 
911 	/*
912 	 * create is unimplmented, remember it for later,
913 	 * and start over using mknod and open instead.
914 	 */
915 	if (error == ENOSYS) {
916 		ps->ps_flags |= PS_NO_CREAT;
917 		return perfuse_node_create(pu, opc, pni, pcn, vap);
918 	}
919 
920 	return error;
921 }
922 
923 
924 int
925 perfuse_node_mknod(pu, opc, pni, pcn, vap)
926 	struct puffs_usermount *pu;
927 	puffs_cookie_t opc;
928 	struct puffs_newinfo *pni;
929 	const struct puffs_cn *pcn;
930 	const struct vattr *vap;
931 {
932 	struct perfuse_state *ps;
933 	perfuse_msg_t *pm;
934 	struct fuse_mknod_in *fmi;
935 	const char* path;
936 	size_t len;
937 
938 	/*
939 	 * Only superuser can mknod objects other than
940 	 * directories, files, socks, fifo and links.
941 	 *
942 	 * Create an object require -WX permission in the parent directory
943 	 */
944 	switch (vap->va_type) {
945 	case VDIR:	/* FALLTHROUGH */
946 	case VREG:	/* FALLTHROUGH */
947 	case VFIFO:	/* FALLTHROUGH */
948 	case VSOCK:	/* FALLTHROUGH */
949 	case VLNK:
950 		if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
951 			return EACCES;
952 		break;
953 	default:	/* VNON, VBLK, VCHR, VBAD */
954 		if (!puffs_cred_isjuggernaut(pcn->pcn_cred))
955 			return EACCES;
956 		break;
957 	}
958 
959 
960 	ps = puffs_getspecific(pu);
961 	path = basename_r((char *)PCNPATH(pcn));
962 	len = sizeof(*fmi) + strlen(path) + 1;
963 
964 	pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred);
965 	fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in);
966 	fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
967 	fmi->rdev = vap->va_rdev;
968 	fmi->umask = 0; 	/* Seems unused bu libfuse */
969 	(void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
970 
971 	return node_mk_common(pu, opc, pni, pcn, vap, pm);
972 }
973 
974 
975 int
976 perfuse_node_open(pu, opc, mode, pcr)
977 	struct puffs_usermount *pu;
978 	puffs_cookie_t opc;
979 	int mode;
980 	const struct puffs_cred *pcr;
981 {
982 	struct perfuse_state *ps;
983 	perfuse_msg_t *pm;
984 	mode_t pmode;
985 	int op;
986 	struct fuse_open_in *foi;
987 	struct fuse_open_out *foo;
988 	struct puffs_node *pn;
989 	int error;
990 
991 	ps = puffs_getspecific(pu);
992 
993 	pn = (struct puffs_node *)opc;
994 	if (puffs_pn_getvap(pn)->va_type == VDIR) {
995 		op = FUSE_OPENDIR;
996 		pmode = PUFFS_VREAD|PUFFS_VEXEC;
997 	} else {
998 		op = FUSE_OPEN;
999 		if (mode & (O_RDWR|O_WRONLY))
1000 			pmode = PUFFS_VWRITE;
1001 		else
1002 			pmode = PUFFS_VREAD;
1003 	}
1004 
1005 	/*
1006 	 * Opening a directory require R-X on the directory
1007 	 * Opening a file requires R-- for reading, -W- for writing
1008 	 * In both cases, --X is required on the parent.
1009 	 */
1010 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1011 	    pcr, PUFFS_VEXEC))
1012 		return EACCES;
1013 
1014 	if (no_access(opc, pcr, pmode))
1015 		return EACCES;
1016 
1017 	/*
1018 	 * libfuse docs say O_CREAT should not be set.
1019 	 */
1020 	mode &= ~O_CREAT;
1021 
1022 	pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
1023 	foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
1024 	foi->flags = mode & ~O_ACCMODE;
1025 	switch (mode & (FREAD|FWRITE)) {
1026 	case FREAD|FWRITE:
1027 		foi->flags |= O_RDWR;
1028 		break;
1029 	case FREAD:
1030 		foi->flags |= O_RDONLY;
1031 		break;
1032 	case FWRITE:
1033 		foi->flags |= O_WRONLY;
1034 		break;
1035 	default:
1036 		break;
1037 	}
1038 	foi->unused = 0;
1039 
1040 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0)
1041 		goto out;
1042 
1043 	foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out);
1044 
1045 	/*
1046 	 * Save the file handle in node private data
1047 	 * so that we can reuse it later
1048 	 */
1049 	perfuse_new_fh((puffs_cookie_t)pn, foo->fh);
1050 
1051 #ifdef PERFUSE_DEBUG
1052 	if (perfuse_diagflags & PDF_FH)
1053 		DPRINTF("%s: opc = %p, file = \"%s\", "
1054 			"ino = %lld, fh = 0x%llx\n",
1055 			__func__, (void *)opc,
1056 			(char *)PNPATH((struct puffs_node *)opc),
1057 			PERFUSE_NODE_DATA(opc)->pnd_ino, foo->fh);
1058 #endif
1059 out:
1060 	ps->ps_destroy_msg(pm);
1061 
1062 	return error;
1063 }
1064 
1065 /* ARGSUSED2 */
1066 int
1067 perfuse_node_close(pu, opc, flags, pcr)
1068 	struct puffs_usermount *pu;
1069 	puffs_cookie_t opc;
1070 	int flags;
1071 	const struct puffs_cred *pcr;
1072 {
1073 	struct perfuse_state *ps;
1074 	perfuse_msg_t *pm;
1075 	int op;
1076 	uint64_t fh;
1077 	struct perfuse_node_data *pnd;
1078 	struct fuse_release_in *fri;
1079 	struct puffs_node *pn;
1080 	int error;
1081 
1082 	ps = puffs_getspecific(pu);
1083 	pn = (struct puffs_node *)opc;
1084 	pnd = PERFUSE_NODE_DATA(pn);
1085 
1086 	if (puffs_pn_getvap(pn)->va_type == VDIR)
1087 		op = FUSE_RELEASEDIR;
1088 	else
1089 		op = FUSE_RELEASE;
1090 
1091 	if (!(pnd->pnd_flags & PND_OPEN))
1092 		return EBADF;
1093 
1094 	fh = perfuse_get_fh(opc);
1095 
1096 	/*
1097 	 * Sync before close for files
1098 	 */
1099 	if ((op == FUSE_RELEASE) && (pnd->pnd_flags & PND_DIRTY)) {
1100 #ifdef PERFUSE_DEBUG
1101 		if (perfuse_diagflags & PDF_SYNC)
1102 			DPRINTF("%s: SYNC opc = %p, file = \"%s\"\n",
1103 				__func__, (void*)opc, (char *)PNPATH(pn));
1104 #endif
1105 		if ((error = perfuse_node_fsync(pu, opc, pcr, 0, 0, 0)) != 0)
1106 			return error;
1107 
1108 		pnd->pnd_flags &= ~PND_DIRTY;
1109 
1110 #ifdef PERFUSE_DEBUG
1111 		if (perfuse_diagflags & PDF_SYNC)
1112 			DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1113 				__func__, (void*)opc, (char *)PNPATH((pn)));
1114 #endif
1115 	}
1116 
1117 	/*
1118 	 * Destroy the filehandle before sending the
1119 	 * request to the FUSE filesystem, otherwise
1120 	 * we may get a second close() while we wait
1121 	 * for the reply, and we would end up closing
1122 	 * the same fh twice instead of closng both.
1123 	 */
1124 	perfuse_destroy_fh(pn, fh);
1125 
1126 #ifdef PERFUSE_DEBUG
1127 	if (perfuse_diagflags & PDF_FH)
1128 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1129 			__func__, (void *)opc, pnd->pnd_ino, fh);
1130 #endif
1131 
1132 	/*
1133 	 * release_flags may be set to FUSE_RELEASE_FLUSH
1134 	 * to flush locks. lock_owner must be set in that case
1135 	 */
1136 	pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
1137 	fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
1138 	fri->fh = fh;
1139 	fri->flags = 0;
1140 	fri->release_flags = 0;
1141 	fri->lock_owner = PERFUSE_NODE_DATA(pn)->pnd_lock_owner;
1142 	fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
1143 
1144 #ifdef PERFUSE_DEBUG
1145 	if (perfuse_diagflags & PDF_FH)
1146 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1147 			 __func__, (void *)opc, pnd->pnd_ino, fri->fh);
1148 #endif
1149 
1150 	if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1151 		goto out;
1152 
1153 out:
1154 	if (error != 0)
1155 		DWARNX("%s: freed fh = 0x%llx but filesystem returned error = %d",
1156 		       __func__, fh, error);
1157 
1158 	ps->ps_destroy_msg(pm);
1159 
1160 	return error;
1161 }
1162 
1163 int
1164 perfuse_node_access(pu, opc, mode, pcr)
1165 	struct puffs_usermount *pu;
1166 	puffs_cookie_t opc;
1167 	int mode;
1168 	const struct puffs_cred *pcr;
1169 {
1170 	perfuse_msg_t *pm;
1171 	struct perfuse_state *ps;
1172 	struct fuse_access_in *fai;
1173 	int error;
1174 
1175 	/*
1176 	 * If we previously detected the filesystem does not
1177 	 * implement access(), short-circuit the call and skip
1178 	 * to libpffs access() emulation.
1179 	 */
1180 	ps = puffs_getspecific(pu);
1181 	if (ps->ps_flags & PS_NO_ACCESS) {
1182 		error = ENOSYS;
1183 	} else {
1184 		pm = ps->ps_new_msg(pu, opc, FUSE_ACCESS, sizeof(*fai), pcr);
1185 		fai = GET_INPAYLOAD(ps, pm, fuse_access_in);
1186 		fai->mask = mode;
1187 
1188 		error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN);
1189 		ps->ps_destroy_msg(pm);
1190 	}
1191 
1192 	if (error == ENOSYS) {
1193 		struct fuse_getattr_in *fgi;
1194 		struct fuse_attr_out *fao;
1195 
1196 		ps->ps_flags |= PS_NO_ACCESS;
1197 
1198 		pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR,
1199 			      sizeof(*fgi), NULL);
1200 		fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1201 		fgi->getattr_flags = 0;
1202 		fgi->dummy = 0;
1203 		fgi->fh = perfuse_get_fh(opc);
1204 
1205 #ifdef PERFUSE_DEBUG
1206 		if (perfuse_diagflags & PDF_FH)
1207 			DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1208 				__func__, (void *)opc,
1209 				PERFUSE_NODE_DATA(opc)->pnd_ino, fgi->fh);
1210 #endif
1211 		if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0) {
1212 			ps->ps_destroy_msg(pm);
1213 			goto out;
1214 		}
1215 
1216 		fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1217 
1218 		error = puffs_access(VREG, fao->attr.mode, fao->attr.uid,
1219 				     fao->attr.gid, (mode_t)mode, pcr);
1220 
1221 		ps->ps_destroy_msg(pm);
1222 	}
1223 
1224 out:
1225 	return error;
1226 }
1227 
1228 int
1229 perfuse_node_getattr(pu, opc, vap, pcr)
1230 	struct puffs_usermount *pu;
1231 	puffs_cookie_t opc;
1232 	struct vattr *vap;
1233 	const struct puffs_cred *pcr;
1234 {
1235 	perfuse_msg_t *pm;
1236 	struct perfuse_state *ps;
1237 	struct fuse_getattr_in *fgi;
1238 	struct fuse_attr_out *fao;
1239 	int error;
1240 
1241 	/*
1242 	 * getattr requires --X on the parent directory
1243 	 */
1244 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1245 	    pcr, PUFFS_VEXEC))
1246 		return EACCES;
1247 
1248 	ps = puffs_getspecific(pu);
1249 
1250 	/*
1251 	 * FUSE_GETATTR_FH must be set in fgi->flags
1252 	 * if we use for fgi->fh, but we do not.
1253 	 */
1254 	pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, sizeof(*fgi), pcr);
1255 	fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
1256 	fgi->getattr_flags = 0;
1257 	fgi->dummy = 0;
1258 	fgi->fh = 0;
1259 
1260 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fao))) != 0)
1261 		goto out;
1262 
1263 	fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
1264 
1265 	/*
1266 	 * The message from filesystem has a cache timeout
1267 	 * XXX this is ignored yet, is that right?
1268 	 *
1269 	 * We also set birthtime, flags, filerev,vaflags to 0.
1270 	 * This seems the best bet, since the information is
1271 	 * not available from filesystem.
1272 	 */
1273 	fuse_attr_to_vap(ps, vap, &fao->attr);
1274 
1275 out:
1276 	ps->ps_destroy_msg(pm);
1277 
1278 	return error;
1279 }
1280 
1281 int
1282 perfuse_node_setattr(pu, opc, vap, pcr)
1283 	struct puffs_usermount *pu;
1284 	puffs_cookie_t opc;
1285 	const struct vattr *vap;
1286 	const struct puffs_cred *pcr;
1287 {
1288 	perfuse_msg_t *pm;
1289 	uint64_t fh;
1290 	struct perfuse_state *ps;
1291 	struct fuse_setattr_in *fsi;
1292 	int error;
1293 	struct vattr *old_va;
1294 
1295 	/*
1296 	 * setattr requires --X on the parent directory
1297 	 */
1298 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1299 	    pcr, PUFFS_VEXEC))
1300 		return EACCES;
1301 
1302 	old_va = puffs_pn_getvap((struct puffs_node *)opc);
1303 
1304 	/*
1305 	 * Check for permission to change size
1306 	 */
1307 	if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1308 	    no_access(opc, pcr, PUFFS_VWRITE))
1309 		return EACCES;
1310 
1311 	/*
1312 	 * Check for permission to change dates
1313 	 */
1314 	if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) ||
1315 	     (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) &&
1316 	    (puffs_access_times(old_va->va_uid, old_va->va_gid,
1317 				old_va->va_mode, 0, pcr) != 0))
1318 		return EACCES;
1319 
1320 	/*
1321 	 * Check for permission to change owner and group
1322 	 */
1323 	if (((vap->va_uid != (uid_t)PUFFS_VNOVAL) ||
1324 	     (vap->va_gid != (gid_t)PUFFS_VNOVAL)) &&
1325 	    (puffs_access_chown(old_va->va_uid, old_va->va_gid,
1326 				vap->va_uid, vap->va_gid, pcr)) != 0)
1327 		return EACCES;
1328 
1329 	/*
1330 	 * Check for permission to change permissions
1331 	 */
1332 	if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1333 	    (puffs_access_chmod(old_va->va_uid, old_va->va_gid,
1334 				 old_va->va_type, vap->va_mode, pcr)) != 0)
1335 		return EACCES;
1336 
1337 
1338 	ps = puffs_getspecific(pu);
1339 
1340 	pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr);
1341 	fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
1342 	fsi->valid = 0;
1343 
1344 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN) {
1345 		fh = perfuse_get_fh(opc);
1346 		fsi->fh = fh;
1347 		if (fh != FUSE_UNKNOWN_FH)
1348 			fsi->valid |= FUSE_FATTR_FH;
1349 	}
1350 
1351 	if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) {
1352 		fsi->size = vap->va_size;
1353 		fsi->valid |= FUSE_FATTR_SIZE;
1354 	}
1355 
1356 	if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) {
1357 		fsi->atime = vap->va_atime.tv_sec;;
1358 		fsi->atimensec = vap->va_atime.tv_nsec;;
1359 		fsi->valid |= (FUSE_FATTR_ATIME|FUSE_FATTR_ATIME_NOW);
1360 	}
1361 
1362 	if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) {
1363 		fsi->mtime = vap->va_mtime.tv_sec;;
1364 		fsi->mtimensec = vap->va_mtime.tv_nsec;;
1365 		fsi->valid |= (FUSE_FATTR_MTIME|FUSE_FATTR_MTIME_NOW);
1366 	}
1367 
1368 	if (vap->va_mode != (mode_t)PUFFS_VNOVAL) {
1369 		fsi->mode = vap->va_mode;
1370 		fsi->valid |= FUSE_FATTR_MODE;
1371 	}
1372 
1373 	if (vap->va_uid != (uid_t)PUFFS_VNOVAL) {
1374 		fsi->uid = vap->va_uid;
1375 		fsi->valid |= FUSE_FATTR_UID;
1376 	}
1377 
1378 	if (vap->va_gid != (gid_t)PUFFS_VNOVAL) {
1379 		fsi->gid = vap->va_gid;
1380 		fsi->valid |= FUSE_FATTR_GID;
1381 	}
1382 
1383 	if (PERFUSE_NODE_DATA(opc)->pnd_lock_owner != 0) {
1384 		fsi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
1385 		fsi->valid |= FUSE_FATTR_LOCKOWNER;
1386 	}
1387 
1388 	/*
1389 	 * A fuse_attr_out is returned, but we ignore it.
1390 	 */
1391 	error = XCHG_MSG(ps, pu, pm, sizeof(struct fuse_attr_out));
1392 
1393 	ps->ps_destroy_msg(pm);
1394 
1395 	return error;
1396 }
1397 
1398 int
1399 perfuse_node_poll(pu, opc, events)
1400 	struct puffs_usermount *pu;
1401 	puffs_cookie_t opc;
1402 	int *events;
1403 {
1404 	struct perfuse_state *ps;
1405 	perfuse_msg_t *pm;
1406 	struct fuse_poll_in *fpi;
1407 	struct fuse_poll_out *fpo;
1408 	int error;
1409 
1410 	ps = puffs_getspecific(pu);
1411 	/*
1412 	 * kh is set if FUSE_POLL_SCHEDULE_NOTIFY is set.
1413  	 */
1414 	pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL);
1415 	fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in);
1416 	fpi->fh = perfuse_get_fh(opc);
1417 	fpi->kh = 0;
1418 	fpi->flags = 0;
1419 
1420 #ifdef PERFUSE_DEBUG
1421 	if (perfuse_diagflags & PDF_FH)
1422 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1423 			__func__, (void *)opc,
1424 			PERFUSE_NODE_DATA(opc)->pnd_ino, fpi->fh);
1425 #endif
1426 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fpo))) != 0)
1427 		goto out;
1428 
1429 	fpo = GET_OUTPAYLOAD(ps, pm, fuse_poll_out);
1430 	*events = fpo->revents;
1431 out:
1432 	ps->ps_destroy_msg(pm);
1433 
1434 	return error;
1435 }
1436 
1437 /* ARGSUSED0 */
1438 int
1439 perfuse_node_mmap(pu, opc, flags, pcr)
1440 	struct puffs_usermount *pu;
1441 	puffs_cookie_t opc;
1442 	int flags;
1443 	const struct puffs_cred *pcr;
1444 {
1445 	/*
1446 	 * Not implemented anymore in libfuse
1447 	 */
1448 	return ENOSYS;
1449 }
1450 
1451 /* ARGSUSED2 */
1452 int
1453 perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi)
1454 	struct puffs_usermount *pu;
1455 	puffs_cookie_t opc;
1456 	const struct puffs_cred *pcr;
1457 	int flags;
1458 	off_t offlo;
1459 	off_t offhi;
1460 {
1461 	perfuse_msg_t *pm;
1462 	struct perfuse_state *ps;
1463 	struct perfuse_node_data *pnd;
1464 	struct fuse_fsync_in *ffi;
1465 	uint64_t fh;
1466 	int open_self;
1467 	int error;
1468 
1469 	pm = NULL;
1470 	open_self = 0;
1471 
1472 	/*
1473 	 * If we previously detected it as unimplemented,
1474 	 * skip the call to the filesystem.
1475 	 */
1476 	ps = puffs_getspecific(pu);
1477 	if (ps->ps_flags == PS_NO_FSYNC)
1478 		return ENOSYS;
1479 
1480 	/*
1481 	 * Do not sync if there are no change to sync
1482 	 * XXX remove that testif we implement mmap
1483 	 */
1484 	pnd = PERFUSE_NODE_DATA(opc);
1485 #ifdef PERFUSE_DEBUG
1486 	if (perfuse_diagflags & PDF_SYNC)
1487 		DPRINTF("%s: TEST opc = %p, file = \"%s\" is %sdirty\n",
1488 			__func__, (void*)opc,
1489 			(char *)PNPATH((struct puffs_node *)opc),
1490 			pnd->pnd_flags & PND_DIRTY ? "" : "not ");
1491 #endif
1492 	if (!(pnd->pnd_flags & PND_DIRTY))
1493 		return 0;
1494 
1495 	/*
1496 	 * It seems NetBSD can call fsync without open first
1497 	 * glusterfs complain in such a situation:
1498 	 * "FSYNC() ERR => -1 (Invalid argument)"
1499 	 */
1500 	if (!(pnd->pnd_flags & PND_OPEN)) {
1501 		if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
1502 			goto out;
1503 		open_self = 1;
1504 	}
1505 
1506 	fh = perfuse_get_fh(opc);
1507 
1508 	/*
1509 	 * If fsync_flags  is set, meta data should not be flushed.
1510 	 */
1511 	pm = ps->ps_new_msg(pu, opc, FUSE_FSYNC, sizeof(*ffi), NULL);
1512 	ffi = GET_INPAYLOAD(ps, pm, fuse_fsync_in);
1513 	ffi->fh = fh;
1514 	ffi->fsync_flags = (flags & FFILESYNC) ? 0 : 1;
1515 
1516 #ifdef PERFUSE_DEBUG
1517 	if (perfuse_diagflags & PDF_FH)
1518 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1519 			__func__, (void *)opc,
1520 			PERFUSE_NODE_DATA(opc)->pnd_ino, ffi->fh);
1521 #endif
1522 
1523 	if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0)
1524 		goto out;
1525 
1526 	/*
1527 	 * No reply beyond fuse_out_header: nothing to do on success
1528 	 * just clear the dirty flag
1529 	 */
1530 	pnd->pnd_flags &= ~PND_DIRTY;
1531 
1532 #ifdef PERFUSE_DEBUG
1533 	if (perfuse_diagflags & PDF_SYNC)
1534 		DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
1535 			__func__, (void*)opc,
1536 			(char *)PNPATH((struct puffs_node *)opc));
1537 #endif
1538 
1539 out:
1540 	if (error == ENOSYS)
1541 		ps->ps_flags |= PS_NO_FSYNC;
1542 
1543 	if (pm != NULL)
1544 		ps->ps_destroy_msg(pm);
1545 
1546 	if (open_self)
1547 		(void)perfuse_node_close(pu, opc, 0, pcr);
1548 
1549 	return error;
1550 }
1551 
1552 /* ARGSUSED0 */
1553 int
1554 perfuse_node_seek(pu, opc, oldoff, newoff,  pcr)
1555 	struct puffs_usermount *pu;
1556 	puffs_cookie_t opc;
1557 	off_t oldoff;
1558 	off_t newoff;
1559 	const struct puffs_cred *pcr;
1560 {
1561 	/*
1562 	 * XXX what should I do with oldoff?
1563 	 * XXX where is the newoffset returned?
1564 	 * XXX the held seek pointer seems just unused
1565 	 */
1566 	PERFUSE_NODE_DATA(opc)->pnd_offset = newoff;
1567 
1568 	return 0;
1569 }
1570 
1571 int
1572 perfuse_node_remove(pu, opc, targ, pcn)
1573 	struct puffs_usermount *pu;
1574 	puffs_cookie_t opc;
1575 	puffs_cookie_t targ;
1576 	const struct puffs_cn *pcn;
1577 {
1578 	struct perfuse_state *ps;
1579 	perfuse_msg_t *pm;
1580 	struct puffs_node *pn;
1581 	char *path;
1582 	const char *name;
1583 	size_t len;
1584 	int error;
1585 
1586 	/*
1587 	 * remove requires -WX on the parent directory
1588 	 * no right required on the object.
1589 	 */
1590 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1591 	    pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1592 		return EACCES;
1593 
1594 	ps = puffs_getspecific(pu);
1595 
1596 	if (targ == NULL)
1597 		DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__);
1598 
1599 	pn = (struct puffs_node *)targ;
1600 	name = basename_r((char *)PNPATH(pn));
1601 	len = strlen(name) + 1;
1602 
1603 	pm = ps->ps_new_msg(pu, opc, FUSE_UNLINK, len, pcn->pcn_cred);
1604 	path = _GET_INPAYLOAD(ps, pm, char *);
1605 	(void)strlcpy(path, name, len);
1606 
1607 	if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1608 		goto out;
1609 
1610 	if (puffs_inval_namecache_dir(pu, opc) != 0)
1611 		DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1612 
1613 	if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
1614 		DERR(EX_OSERR, "puffs_inval_namecache_node failed");
1615 
1616 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1617 
1618 	/*
1619 	 * Reclaim should take care of decreasing pnd_childcount
1620 	 */
1621 out:
1622 	ps->ps_destroy_msg(pm);
1623 
1624 	return error;
1625 }
1626 
1627 int
1628 perfuse_node_link(pu, opc, targ, pcn)
1629 	struct puffs_usermount *pu;
1630 	puffs_cookie_t opc;
1631 	puffs_cookie_t targ;
1632 	const struct puffs_cn *pcn;
1633 {
1634 	struct perfuse_state *ps;
1635 	perfuse_msg_t *pm;
1636 	const char *name;
1637 	size_t len;
1638 	struct puffs_node *pn;
1639 	struct fuse_link_in *fli;
1640 	int error;
1641 
1642 	/*
1643 	 * Create an object require -WX permission in the parent directory
1644 	 */
1645 	if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1646 		return EACCES;
1647 
1648 
1649 	ps = puffs_getspecific(pu);
1650 	pn = (struct puffs_node *)targ;
1651 	name = basename_r((char *)PCNPATH(pcn));
1652 	len =  sizeof(*fli) + strlen(name) + 1;
1653 
1654 	pm = ps->ps_new_msg(pu, opc, FUSE_LINK, len, pcn->pcn_cred);
1655 	fli = GET_INPAYLOAD(ps, pm, fuse_link_in);
1656 	fli->oldnodeid = PERFUSE_NODE_DATA(pn)->pnd_ino;
1657 	(void)strlcpy((char *)(void *)(fli + 1), name, len - sizeof(*fli));
1658 
1659 	error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
1660 
1661 	ps->ps_destroy_msg(pm);
1662 
1663 	return error;
1664 }
1665 
1666 /* targ is unused since the name is in pcn_targ */
1667 /* ARGSUSED5 */
1668 int
1669 perfuse_node_rename(pu, opc, src, pcn_src, targ_dir, targ, pcn_targ)
1670 	struct puffs_usermount *pu;
1671 	puffs_cookie_t opc;
1672 	puffs_cookie_t src;
1673 	const struct puffs_cn *pcn_src;
1674 	puffs_cookie_t targ_dir;
1675 	puffs_cookie_t targ;
1676 	const struct puffs_cn *pcn_targ;
1677 {
1678 	struct perfuse_state *ps;
1679 	perfuse_msg_t *pm;
1680 	struct fuse_rename_in *fri;
1681 	const char *newname;
1682 	const char *oldname;
1683 	char *np;
1684 	int error;
1685 	size_t len;
1686 	size_t newname_len;
1687 	size_t oldname_len;
1688 
1689 	/*
1690 	 * move requires -WX on source and destination directory
1691 	 */
1692 	if (no_access(src, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC) ||
1693 	    no_access(targ,  pcn_targ->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1694 		return EACCES;
1695 
1696 	ps = puffs_getspecific(pu);
1697 	newname =  basename_r((char *)PCNPATH(pcn_targ));
1698 	newname_len = strlen(newname) + 1;
1699 	oldname =  basename_r((char *)PCNPATH(pcn_src));
1700 	oldname_len = strlen(oldname) + 1;
1701 
1702 	len = sizeof(*fri) + oldname_len + newname_len;
1703 	pm = ps->ps_new_msg(pu, opc, FUSE_RENAME, len, pcn_src->pcn_cred);
1704 	fri = GET_INPAYLOAD(ps, pm, fuse_rename_in);
1705 	fri->newdir = PERFUSE_NODE_DATA(targ_dir)->pnd_ino;
1706 	np = (char *)(void *)(fri + 1);
1707 	(void)strlcpy(np, oldname, oldname_len);
1708 	np += oldname_len;
1709 	(void)strlcpy(np, newname, newname_len);
1710 
1711 	if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1712 		goto out;
1713 
1714 	/*
1715 	 * Update source and destination directories child count
1716 	 * Update moved object parent directory
1717 	 */
1718 	PERFUSE_NODE_DATA(opc)->pnd_childcount--;
1719 	PERFUSE_NODE_DATA(targ_dir)->pnd_childcount++;
1720 	PERFUSE_NODE_DATA(src)->pnd_parent = targ_dir;
1721 
1722 out:
1723 	ps->ps_destroy_msg(pm);
1724 
1725 	return error;
1726 }
1727 
1728 int
1729 perfuse_node_mkdir(pu, opc, pni, pcn, vap)
1730 	struct puffs_usermount *pu;
1731 	puffs_cookie_t opc;
1732 	struct puffs_newinfo *pni;
1733 	const struct puffs_cn *pcn;
1734 	const struct vattr *vap;
1735 {
1736 	struct perfuse_state *ps;
1737 	perfuse_msg_t *pm;
1738 	struct fuse_mkdir_in *fmi;
1739 	const char *path;
1740 	size_t len;
1741 
1742 	/*
1743 	 * Create an object require -WX permission in the parent directory
1744 	 */
1745 	if (no_access(opc, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1746 		return EACCES;
1747 
1748 	ps = puffs_getspecific(pu);
1749 	path = basename_r((char *)PCNPATH(pcn));
1750 	len = sizeof(*fmi) + strlen(path) + 1;
1751 
1752 	pm = ps->ps_new_msg(pu, opc, FUSE_MKDIR, len, pcn->pcn_cred);
1753 	fmi = GET_INPAYLOAD(ps, pm, fuse_mkdir_in);
1754 	fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
1755 	fmi->umask = 0; 	/* Seems unused bu libfuse? */
1756 	(void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
1757 
1758 	return node_mk_common(pu, opc, pni, pcn, vap, pm);
1759 }
1760 
1761 
1762 int
1763 perfuse_node_rmdir(pu, opc, targ, pcn)
1764 	struct puffs_usermount *pu;
1765 	puffs_cookie_t opc;
1766 	puffs_cookie_t targ;
1767 	const struct puffs_cn *pcn;
1768 {
1769 	struct perfuse_state *ps;
1770 	perfuse_msg_t *pm;
1771 	struct puffs_node *pn;
1772 	char *path;
1773 	const char *name;
1774 	size_t len;
1775 	int error;
1776 
1777 	/*
1778 	 * remove requires -WX on the parent directory
1779 	 * no right required on the object.
1780 	 */
1781 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
1782 	    pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1783 		return EACCES;
1784 
1785 	ps = puffs_getspecific(pu);
1786 	pn = (struct puffs_node *)targ;
1787 	name = basename_r((char *)PNPATH(pn));
1788 	len = strlen(name) + 1;
1789 
1790 	pm = ps->ps_new_msg(pu, opc, FUSE_RMDIR, len, pcn->pcn_cred);
1791 	path = _GET_INPAYLOAD(ps, pm, char *);
1792 	(void)strlcpy(path, name, len);
1793 
1794 	if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1795 		goto out;
1796 
1797 	if (puffs_inval_namecache_dir(pu, opc) != 0)
1798 		DERR(EX_OSERR, "puffs_inval_namecache_dir failed");
1799 
1800 	if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0)
1801 		DERR(EX_OSERR, "puffs_inval_namecache_node failed");
1802 
1803 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
1804 
1805 out:
1806 	ps->ps_destroy_msg(pm);
1807 
1808 	return error;
1809 }
1810 
1811 /* vap is unused */
1812 /* ARGSUSED4 */
1813 int
1814 perfuse_node_symlink(pu, opc, pni, pcn_src, vap, link_target)
1815 	struct puffs_usermount *pu;
1816 	puffs_cookie_t opc;
1817 	struct puffs_newinfo *pni;
1818 	const struct puffs_cn *pcn_src;
1819 	const struct vattr *vap;
1820 	const char *link_target;
1821 {
1822 	struct perfuse_state *ps;
1823 	perfuse_msg_t *pm;
1824 	char *np;
1825 	const char *path;
1826 	size_t path_len;
1827 	size_t linkname_len;
1828 	size_t len;
1829 
1830 	/*
1831 	 * Create an object require -WX permission in the parent directory
1832 	 */
1833 	if (no_access(opc, pcn_src->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC))
1834 		return EACCES;
1835 
1836 	ps = puffs_getspecific(pu);
1837 	path = basename_r((char *)PCNPATH(pcn_src));
1838 	path_len = strlen(path) + 1;
1839 	linkname_len = strlen(link_target) + 1;
1840 	len = path_len + linkname_len;
1841 
1842 	pm = ps->ps_new_msg(pu, opc, FUSE_SYMLINK, len, pcn_src->pcn_cred);
1843 	np = _GET_INPAYLOAD(ps, pm, char *);
1844 	(void)strlcpy(np, path, path_len);
1845 	np += path_len;
1846 	(void)strlcpy(np, link_target, linkname_len);
1847 
1848 	return node_mk_common(pu, opc, pni, pcn_src, vap, pm);
1849 }
1850 
1851 int
1852 perfuse_node_readdir(pu, opc, dent, readoff,
1853 		     reslen, pcr, eofflag, cookies, ncookies)
1854 	struct puffs_usermount *pu;
1855 	puffs_cookie_t opc;
1856 	struct dirent *dent;
1857 	off_t *readoff;
1858 	size_t *reslen;
1859 	const struct puffs_cred *pcr;
1860 	int *eofflag;
1861 	off_t *cookies;
1862 	size_t *ncookies;
1863 {
1864 	perfuse_msg_t *pm;
1865 	uint64_t fh;
1866 	struct perfuse_state *ps;
1867 	struct perfuse_node_data *pnd;
1868 	struct fuse_read_in *fri;
1869 	struct fuse_out_header *foh;
1870 	struct fuse_dirent *fd;
1871 	size_t foh_len;
1872 	int error;
1873 	int open_self;
1874 	uint64_t fd_offset;
1875 
1876 	pm = NULL;
1877 	error = 0;
1878 	open_self = 0;
1879 	ps = puffs_getspecific(pu);
1880 
1881 	/*
1882 	 * readdir state is kept at node level, and several readdir
1883 	 * requests can be issued at the same time on the same node.
1884 	 * We need to queue requests so that only one is in readdir
1885 	 * code at the same time.
1886 	 */
1887 	pnd = PERFUSE_NODE_DATA(opc);
1888 	while (pnd->pnd_flags & PND_INREADDIR)
1889 		requeue_request(pu, opc, PCQ_READDIR);
1890 	pnd->pnd_flags |= PND_INREADDIR;
1891 
1892 #ifdef PERFUSE_DEBUG
1893 	if (perfuse_diagflags & PDF_READDIR)
1894 		DPRINTF("%s: READDIR opc = %p enter critical section\n",
1895 			__func__, (void *)opc);
1896 #endif
1897 	/*
1898 	 * Do we already have the data bufered?
1899 	 */
1900 	if (pnd->pnd_dirent != NULL)
1901 		goto out;
1902 	pnd->pnd_dirent_len = 0;
1903 
1904 	/*
1905 	 * It seems NetBSD can call readdir without open first
1906 	 * libfuse will crash if it is done that way, hence open first.
1907 	 */
1908 	if (!(PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN)) {
1909 		if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
1910 			goto out;
1911 		open_self = 1;
1912 	}
1913 
1914 	fh = perfuse_get_fh(opc);
1915 
1916 #ifdef PERFUSE_DEBUG
1917 	if (perfuse_diagflags & PDF_FH)
1918 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
1919 			__func__, (void *)opc,
1920 			PERFUSE_NODE_DATA(opc)->pnd_ino, fh);
1921 #endif
1922 
1923 	pnd->pnd_all_fd = NULL;
1924 	pnd->pnd_all_fd_len = 0;
1925 	fd_offset = 0;
1926 
1927 	do {
1928 		size_t fd_len;
1929 		char *afdp;
1930 
1931 		pm = ps->ps_new_msg(pu, opc, FUSE_READDIR, sizeof(*fri), pcr);
1932 
1933 		/*
1934 		 * read_flags, lock_owner and flags are unused in libfuse
1935 		 *
1936 		 * XXX if fri->size is too big (bigger than PAGE_SIZE?), 			 * we get strange bugs. ktrace shows 16 bytes or garbage
1937 		 * at the end of sent frames, but perfused does not receive
1938 		 * that data. The data length is hoverver the same, which
1939 		 * cause perfused to use the last 16 bytes of the frame
1940 		 * as the frame header of the next frame.
1941 		 *
1942 		 * This may be a kernel bug.
1943 		 */
1944 		fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
1945 		fri->fh = fh;
1946 		fri->offset = fd_offset;
1947 		fri->size = PAGE_SIZE - sizeof(struct fuse_out_header);
1948 		fri->read_flags = 0;
1949 		fri->lock_owner = 0;
1950 		fri->flags = 0;
1951 
1952 		if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
1953 			goto out;
1954 
1955 		/*
1956 		 * There are many puffs_framebufs calls later,
1957 		 * therefore foh will not be valid for a long time.
1958 		 * Just get the length and forget it.
1959 		 */
1960 		foh = GET_OUTHDR(ps, pm);
1961 		foh_len = foh->len;
1962 
1963 		/*
1964 		 * It seems that the only way to discover the end
1965 		 * of the buffer is to get an empty read
1966 		 */
1967 		if (foh_len == sizeof(*foh))
1968 			break;
1969 
1970 		/*
1971 		 * Corrupted message.
1972 		 */
1973 		if (foh_len < sizeof(*foh) + sizeof(*fd)) {
1974 			DWARNX("readdir reply too short");
1975 			error = EIO;
1976 			goto out;
1977 		}
1978 
1979 
1980 		fd = GET_OUTPAYLOAD(ps, pm, fuse_dirent);
1981 		fd_len = foh_len - sizeof(*foh);
1982 
1983 		pnd->pnd_all_fd = realloc(pnd->pnd_all_fd,
1984 					  pnd->pnd_all_fd_len + fd_len);
1985 		if (pnd->pnd_all_fd  == NULL)
1986 			DERR(EX_OSERR, "malloc failed");
1987 
1988 		afdp = (char *)(void *)pnd->pnd_all_fd + pnd->pnd_all_fd_len;
1989 		(void)memcpy(afdp, fd, fd_len);
1990 
1991 		pnd->pnd_all_fd_len += fd_len;
1992 		fd_offset += fd_len;
1993 
1994 		ps->ps_destroy_msg(pm);
1995 		pm = NULL;
1996 	} while (1 /* CONSTCOND */);
1997 
1998 	if (fuse_to_dirent(pu, opc, pnd->pnd_all_fd, pnd->pnd_all_fd_len) == -1)
1999 		error = EIO;
2000 
2001 out:
2002 	if (pnd->pnd_all_fd != NULL) {
2003 		free(pnd->pnd_all_fd);
2004 		pnd->pnd_all_fd = NULL;
2005 		pnd->pnd_all_fd_len = 0;
2006 	}
2007 
2008 	if (pm != NULL)
2009 		ps->ps_destroy_msg(pm);
2010 
2011 	/*
2012 	 * If we opened the directory ourselves, close now
2013 	 * errors are ignored.
2014 	 */
2015 	if (open_self)
2016 		(void)perfuse_node_close(pu, opc, 0, pcr);
2017 
2018 	if (error == 0)
2019 		error = readdir_buffered(ps, opc, dent, readoff,
2020 			reslen, pcr, eofflag, cookies, ncookies);
2021 
2022 	/*
2023 	 * Schedule queued readdir requests
2024 	 */
2025 	dequeue_requests(ps, opc, PCQ_READDIR, DEQUEUE_ALL);
2026 	pnd->pnd_flags &= ~PND_INREADDIR;
2027 
2028 #ifdef PERFUSE_DEBUG
2029 	if (perfuse_diagflags & PDF_READDIR)
2030 		DPRINTF("%s: READDIR opc = %p exit critical section\n",
2031 			__func__, (void *)opc);
2032 #endif
2033 
2034 	return error;
2035 }
2036 
2037 int
2038 perfuse_node_readlink(pu, opc, pcr, linkname, linklen)
2039 	struct puffs_usermount *pu;
2040 	puffs_cookie_t opc;
2041 	const struct puffs_cred *pcr;
2042 	char *linkname;
2043 	size_t *linklen;
2044 {
2045 	struct perfuse_state *ps;
2046 	perfuse_msg_t *pm;
2047 	int error;
2048 	size_t len;
2049 	struct fuse_out_header *foh;
2050 
2051 	/*
2052 	 * --X required on parent, R-- required on link
2053 	 */
2054 	if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent,
2055 	    pcr, PUFFS_VEXEC) ||
2056 	   no_access(opc, pcr, PUFFS_VREAD))
2057 		return EACCES;
2058 
2059 	ps = puffs_getspecific(pu);
2060 
2061 	pm = ps->ps_new_msg(pu, opc, FUSE_READLINK, 0, pcr);
2062 
2063 	if ((error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN)) != 0)
2064 		goto out;
2065 
2066 	foh = GET_OUTHDR(ps, pm);
2067 	len = foh->len - sizeof(*foh) + 1;
2068 	if (len > *linklen)
2069 		DERRX(EX_PROTOCOL, "path len = %d too long", len);
2070 
2071 	*linklen = len;
2072 	(void)strlcpy(linkname, _GET_OUTPAYLOAD(ps, pm, char *), len);
2073 out:
2074 	ps->ps_destroy_msg(pm);
2075 
2076 	return error;
2077 }
2078 
2079 int
2080 perfuse_node_reclaim(pu, opc)
2081 	struct puffs_usermount *pu;
2082 	puffs_cookie_t opc;
2083 {
2084 	struct perfuse_state *ps;
2085 	perfuse_msg_t *pm;
2086 	struct perfuse_node_data *pnd;
2087 	struct fuse_forget_in *ffi;
2088 	struct puffs_node *pn;
2089 	struct puffs_node *pn_root;
2090 
2091 	ps = puffs_getspecific(pu);
2092 	pnd = PERFUSE_NODE_DATA(opc);
2093 
2094 	/*
2095 	 * Make sure open files are properly closed when reclaimed.
2096 	 */
2097 	while (pnd->pnd_flags & PND_OPEN)
2098 		(void)perfuse_node_close(pu, opc, 0, NULL);
2099 
2100 	/*
2101 	 * Never forget the root.
2102 	 */
2103 	if (pnd->pnd_ino == FUSE_ROOT_ID)
2104 		return 0;
2105 
2106 	pnd->pnd_flags |= PND_RECLAIMED;
2107 
2108 #ifdef PERFUSE_DEBUG
2109 	if (perfuse_diagflags & PDF_RECLAIM)
2110 		DPRINTF("%s (nodeid %lld) reclaimed\n",
2111 		       (char *)PNPATH((struct puffs_node *)opc), pnd->pnd_ino);
2112 #endif
2113 
2114 	pn_root = puffs_getroot(pu);
2115 	pn = (struct puffs_node *)opc;
2116 	while (pn != pn_root) {
2117 		struct puffs_node *parent_pn;
2118 
2119 		pnd = PERFUSE_NODE_DATA(pn);
2120 
2121 #ifdef PERFUSE_DEBUG
2122 	if (perfuse_diagflags & PDF_RECLAIM)
2123 		DPRINTF("%s (nodeid %lld) is %sreclaimed, "
2124 			"has childcount %d, %sopen\n",
2125 		        (char *)PNPATH(pn), pnd->pnd_ino,
2126 		        pnd->pnd_flags & PND_RECLAIMED ? "" : "not ",
2127 		        pnd->pnd_childcount,
2128 			pnd->pnd_flags & PND_OPEN ? "" : "not ");
2129 
2130 	if (pnd->pnd_flags & PND_OPEN)
2131 		DWARNX("%s: (nodeid %lld) %s is still open",
2132 		       __func__, pnd->pnd_ino, (char *)PNPATH(pn));
2133 #endif
2134 
2135 		if (!(pnd->pnd_flags & PND_RECLAIMED) ||
2136 		    (pnd->pnd_childcount != 0))
2137 			return 0;
2138 
2139 		/*
2140 		 * If the file is still open, close all file handles
2141 		 * XXX no pcr arguement to send.
2142 		 */
2143 		while(pnd->pnd_flags & PND_OPEN)
2144 			(void)perfuse_node_close(pu, opc, 0, NULL);
2145 
2146 		pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET,
2147 			      sizeof(*ffi), NULL);
2148 		ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in);
2149 		ffi->nlookup = pnd->pnd_nlookup;
2150 
2151 		/*
2152 		 * No reply is expected, pm is freed in XCHG_MSG
2153 		 */
2154 		(void)XCHG_MSG_NOREPLY(ps, pu, pm, UNSPEC_REPLY_LEN);
2155 
2156 		parent_pn = pnd->pnd_parent;
2157 
2158 		perfuse_destroy_pn(pn);
2159 		puffs_pn_put(pn);
2160 
2161 		pn = parent_pn;
2162 	}
2163 
2164 	return 0;
2165 }
2166 
2167 /* ARGSUSED0 */
2168 int
2169 perfuse_node_inactive(pu, opc)
2170 	struct puffs_usermount *pu;
2171 	puffs_cookie_t opc;
2172 {
2173 	return 0;
2174 }
2175 
2176 
2177 /* ARGSUSED0 */
2178 int
2179 perfuse_node_print(pu, opc)
2180 	struct puffs_usermount *pu;
2181 	puffs_cookie_t opc;
2182 {
2183 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2184 	return 0;
2185 }
2186 
2187 /* ARGSUSED0 */
2188 int
2189 perfuse_node_pathconf(pu, opc, name, retval)
2190 	struct puffs_usermount *pu;
2191 	puffs_cookie_t opc;
2192 	int name;
2193 	int *retval;
2194 {
2195 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
2196 	return 0;
2197 }
2198 
2199 /* id is unused */
2200 /* ARGSUSED2 */
2201 int
2202 perfuse_node_advlock(pu, opc, id, op, fl, flags)
2203 	struct puffs_usermount *pu;
2204 	puffs_cookie_t opc;
2205 	void *id;
2206 	int op;
2207 	struct flock *fl;
2208 	int flags;
2209 {
2210 	struct perfuse_state *ps;
2211 	int fop;
2212 	perfuse_msg_t *pm;
2213 	struct fuse_lk_in *fli;
2214 	struct fuse_lk_out *flo;
2215 	int error;
2216 
2217 	ps = puffs_getspecific(pu);
2218 
2219 	if (op == F_GETLK)
2220 		fop = FUSE_GETLK;
2221 	else
2222 		fop = (flags & F_WAIT) ? FUSE_SETLKW : FUSE_SETLK;
2223 
2224 	pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL);
2225 	fli = GET_INPAYLOAD(ps, pm, fuse_lk_in);
2226 	fli->fh = perfuse_get_fh(opc);
2227 	fli->owner = fl->l_pid;
2228 	fli->lk.start = fl->l_start;
2229 	fli->lk.end = fl->l_start + fl->l_len;
2230 	fli->lk.type = fl->l_type;
2231 	fli->lk.pid = fl->l_pid;
2232 	fli->lk_flags = (flags & F_FLOCK) ? FUSE_LK_FLOCK : 0;
2233 
2234 #ifdef PERFUSE_DEBUG
2235 	if (perfuse_diagflags & PDF_FH)
2236 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2237 			__func__, (void *)opc,
2238 			PERFUSE_NODE_DATA(opc)->pnd_ino, fli->fh);
2239 #endif
2240 
2241 	if ((error = XCHG_MSG(ps, pu, pm, sizeof(*flo))) != 0)
2242 		goto out;
2243 
2244 	flo = GET_OUTPAYLOAD(ps, pm, fuse_lk_out);
2245 	fl->l_start = flo->lk.start;
2246 	fl->l_len = flo->lk.end - flo->lk.start;
2247 	fl->l_pid = flo->lk.pid;
2248 	fl->l_type = flo->lk.type;
2249 	fl->l_whence = SEEK_SET;	/* libfuse hardcodes it */
2250 
2251 	/*
2252 	 * Save or clear the lock
2253 	 */
2254 	switch (op) {
2255 	case F_SETLK:
2256 		PERFUSE_NODE_DATA(opc)->pnd_lock_owner = flo->lk.pid;
2257 		break;
2258 	case F_UNLCK:
2259 		PERFUSE_NODE_DATA(opc)->pnd_lock_owner = 0;
2260 		break;
2261 	default:
2262 		break;
2263 	}
2264 
2265 out:
2266 	ps->ps_destroy_msg(pm);
2267 
2268 	return error;
2269 }
2270 
2271 int
2272 perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag)
2273 	struct puffs_usermount *pu;
2274 	puffs_cookie_t opc;
2275 	uint8_t *buf;
2276 	off_t offset;
2277 	size_t *resid;
2278 	const struct puffs_cred *pcr;
2279 	int ioflag;
2280 {
2281 	struct perfuse_state *ps;
2282 	perfuse_msg_t *pm;
2283 	struct fuse_read_in *fri;
2284 	struct fuse_out_header *foh;
2285 	size_t readen;
2286 	size_t requested;
2287 	int error;
2288 
2289 	ps = puffs_getspecific(pu);
2290 	pm = NULL;
2291 
2292 	requested = *resid;
2293 	if ((ps->ps_readahead + requested) > ps->ps_max_readahead) {
2294 		if (perfuse_diagflags & PDF_REQUEUE)
2295 			DPRINTF("readahead = %d\n", ps->ps_readahead);
2296 		requeue_request(pu, opc, PCQ_READ);
2297 	}
2298 	ps->ps_readahead += requested;
2299 
2300 	do {
2301 		/*
2302 		 * flags may be set to FUSE_READ_LOCKOWNER
2303 		 * if lock_owner is provided.
2304 		 *
2305 		 * XXX See comment about fri->size in perfuse_node_readdir
2306 		 * We encounter the same bug here.
2307 		 */
2308 		pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr);
2309 		fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
2310 		fri->fh = perfuse_get_fh(opc);
2311 		fri->offset = offset;
2312 		fri->size = MIN(*resid, PAGE_SIZE - sizeof(*foh));
2313 		fri->read_flags = 0; /* XXX Unused by libfuse? */
2314 		fri->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
2315 		fri->flags = 0;
2316 		fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0;
2317 
2318 #ifdef PERFUSE_DEBUG
2319 	if (perfuse_diagflags & PDF_FH)
2320 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2321 			__func__, (void *)opc,
2322 			PERFUSE_NODE_DATA(opc)->pnd_ino, fri->fh);
2323 #endif
2324 		error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN);
2325 
2326 		if (error  != 0)
2327 			goto out;
2328 
2329 		foh = GET_OUTHDR(ps, pm);
2330 		readen = foh->len - sizeof(*foh);
2331 
2332 		(void)memcpy(buf,  _GET_OUTPAYLOAD(ps, pm, char *), readen);
2333 
2334 		buf += readen;
2335 		offset += readen;
2336 		*resid -= readen;
2337 
2338 		ps->ps_destroy_msg(pm);
2339 		pm = NULL;
2340 	} while ((*resid != 0) && (readen != 0));
2341 
2342 	if (ioflag & (IO_SYNC|IO_DSYNC))
2343 		ps->ps_syncreads++;
2344 	else
2345 		ps->ps_asyncreads++;
2346 
2347 out:
2348 	if (pm != NULL)
2349 		ps->ps_destroy_msg(pm);
2350 
2351 	ps->ps_readahead -= requested;
2352 	dequeue_requests(ps, opc, PCQ_READ, 1);
2353 
2354 	return error;
2355 }
2356 
2357 int
2358 perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag)
2359 	struct puffs_usermount *pu;
2360 	puffs_cookie_t opc;
2361 	uint8_t *buf;
2362 	off_t offset;
2363 	size_t *resid;
2364 	const struct puffs_cred *pcr;
2365 	int ioflag;
2366 {
2367 	struct perfuse_state *ps;
2368 	perfuse_msg_t *pm;
2369 	struct fuse_write_in *fwi;
2370 	struct fuse_write_out *fwo;
2371 	size_t data_len;
2372 	size_t payload_len;
2373 	size_t written;
2374 	size_t requested;
2375 	int error;
2376 
2377 	ps = puffs_getspecific(pu);
2378 	pm = NULL;
2379 	written = 0;
2380 
2381 	requested = *resid;
2382 	if ((ps->ps_write + requested) > ps->ps_max_write) {
2383 		if (perfuse_diagflags & PDF_REQUEUE)
2384 			DPRINTF("write = %d\n", ps->ps_write);
2385 		requeue_request(pu, opc, PCQ_WRITE);
2386 	}
2387 	ps->ps_write += requested;
2388 
2389 	do {
2390 		/*
2391 		 * It seems libfuse does not expects big chunks, so
2392 		 * send it page per page. The writepage feature is
2393 		 * probably there to minmize data movement.
2394 		 * XXX use ps->ps_maxwrite?
2395 		 */
2396 		data_len = MIN(*resid, PAGE_SIZE);
2397 		payload_len = data_len + sizeof(*fwi);
2398 
2399 		/*
2400 		 * flags may be set to FUSE_WRITE_CACHE (XXX usage?)
2401 		 * or FUSE_WRITE_LOCKOWNER, if lock_owner is provided.
2402 		 * write_flags is set to 1 for writepage.
2403 		 */
2404 		pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr);
2405 		fwi = GET_INPAYLOAD(ps, pm, fuse_write_in);
2406 		fwi->fh = perfuse_get_fh(opc);
2407 		fwi->offset = offset;
2408 		fwi->size = data_len;
2409 		fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1;
2410 		fwi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner;
2411 		fwi->flags = 0;
2412 		fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0;
2413 		fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE;
2414 		(void)memcpy((fwi + 1), buf + written, data_len);
2415 
2416 #ifdef PERFUSE_DEBUG
2417 	if (perfuse_diagflags & PDF_FH)
2418 		DPRINTF("%s: opc = %p, ino = %lld, fh = 0x%llx\n",
2419 			__func__, (void *)opc,
2420 			PERFUSE_NODE_DATA(opc)->pnd_ino, fwi->fh);
2421 #endif
2422 		if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0)
2423 			goto out;
2424 
2425 		fwo = GET_OUTPAYLOAD(ps, pm, fuse_write_out);
2426 		written = fwo->size;
2427 		*resid -= written;
2428 		offset += written;
2429 		buf += written;
2430 
2431 		ps->ps_destroy_msg(pm);
2432 		pm = NULL;
2433 	} while (*resid != 0);
2434 
2435 	/*
2436 	 * puffs_ops(3) says
2437 	 *  "everything must be written or an error will be generated"
2438 	 */
2439 	if (*resid != 0)
2440 		error = EFBIG;
2441 
2442 	if (ioflag & (IO_SYNC|IO_DSYNC))
2443 		ps->ps_syncwrites++;
2444 	else
2445 		ps->ps_asyncwrites++;
2446 
2447 	/*
2448 	 * Remember to sync the file
2449 	 */
2450 	PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
2451 
2452 #ifdef PERFUSE_DEBUG
2453 	if (perfuse_diagflags & PDF_SYNC)
2454 		DPRINTF("%s: DIRTY opc = %p, file = \"%s\"\n",
2455 			__func__, (void*)opc,
2456 			(char *)PNPATH((struct puffs_node *)opc));
2457 #endif
2458 out:
2459 	if (pm != NULL)
2460 		ps->ps_destroy_msg(pm);
2461 
2462 	ps->ps_write -= requested;
2463 	dequeue_requests(ps, opc, PCQ_WRITE, 1);
2464 
2465 	return error;
2466 }
2467 
2468 /* ARGSUSED0 */
2469 void
2470 perfuse_cache_write(pu, opc, size, runs)
2471 	struct puffs_usermount *pu;
2472 	puffs_cookie_t opc;
2473 	size_t size;
2474 	struct puffs_cacherun *runs;
2475 {
2476 	return;
2477 }
2478 
2479