xref: /netbsd-src/lib/libperfuse/ops.c (revision 37a23ecf39b616ebc8f7f8be96b814ad1bd168f5)
1*37a23ecfSmsaitoh /*  $NetBSD: ops.c,v 1.92 2023/06/24 05:18:13 msaitoh Exp $ */
27b1d1ee6Smanu 
37b1d1ee6Smanu /*-
4c3c545a5Smanu  *  Copyright (c) 2010-2011 Emmanuel Dreyfus. All rights reserved.
57b1d1ee6Smanu  *
67b1d1ee6Smanu  *  Redistribution and use in source and binary forms, with or without
77b1d1ee6Smanu  *  modification, are permitted provided that the following conditions
87b1d1ee6Smanu  *  are met:
97b1d1ee6Smanu  *  1. Redistributions of source code must retain the above copyright
107b1d1ee6Smanu  *     notice, this list of conditions and the following disclaimer.
117b1d1ee6Smanu  *  2. Redistributions in binary form must reproduce the above copyright
127b1d1ee6Smanu  *     notice, this list of conditions and the following disclaimer in the
137b1d1ee6Smanu  *     documentation and/or other materials provided with the distribution.
147b1d1ee6Smanu  *
157b1d1ee6Smanu  *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
167b1d1ee6Smanu  *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
177b1d1ee6Smanu  *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
187b1d1ee6Smanu  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
197b1d1ee6Smanu  *  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
207b1d1ee6Smanu  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
217b1d1ee6Smanu  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
227b1d1ee6Smanu  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
237b1d1ee6Smanu  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
247b1d1ee6Smanu  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
257b1d1ee6Smanu  *  POSSIBILITY OF SUCH DAMAGE.
267b1d1ee6Smanu  */
277b1d1ee6Smanu 
287b1d1ee6Smanu #include <stdio.h>
297b1d1ee6Smanu #include <unistd.h>
307b1d1ee6Smanu #include <stdlib.h>
317b1d1ee6Smanu #include <libgen.h>
327b1d1ee6Smanu #include <errno.h>
337b1d1ee6Smanu #include <err.h>
347b1d1ee6Smanu #include <sysexits.h>
357b1d1ee6Smanu #include <syslog.h>
367b1d1ee6Smanu #include <puffs.h>
37075ba0e5Smanu #include <sys/socket.h>
387b1d1ee6Smanu #include <sys/socket.h>
398ae0a67dSmanu #include <sys/extattr.h>
404fba06adSmanu #include <sys/time.h>
417b1d1ee6Smanu #include <machine/vmparam.h>
427b1d1ee6Smanu 
437b1d1ee6Smanu #include "perfuse_priv.h"
447b1d1ee6Smanu #include "fuse.h"
457b1d1ee6Smanu 
461e672db8Smanu extern int perfuse_diagflags;
471e672db8Smanu 
4855557eb9Smanu #if 0
4955557eb9Smanu static void print_node(const char *, puffs_cookie_t);
5055557eb9Smanu #endif
5170d81924Smanu #ifdef PUFFS_KFLAG_CACHE_FS_TTL
5270d81924Smanu static void perfuse_newinfo_setttl(struct puffs_newinfo *,
53075ba0e5Smanu     struct puffs_node *, struct fuse_entry_out *, struct fuse_attr_out *);
546bceb418Smanu #endif /* PUFFS_KFLAG_CACHE_FS_TTL */
553d6861b5Smanu static int xchg_msg(struct puffs_usermount *, puffs_cookie_t,
563d6861b5Smanu     perfuse_msg_t *, size_t, enum perfuse_xchg_pb_reply);
57c3c545a5Smanu static int mode_access(puffs_cookie_t, const struct puffs_cred *, mode_t);
58075ba0e5Smanu static int sticky_access(puffs_cookie_t, struct puffs_node *,
59075ba0e5Smanu     const struct puffs_cred *);
607b1d1ee6Smanu static void fuse_attr_to_vap(struct perfuse_state *,
617b1d1ee6Smanu     struct vattr *, struct fuse_attr *);
627b1d1ee6Smanu static int node_lookup_common(struct puffs_usermount *, puffs_cookie_t,
6370d81924Smanu     struct puffs_newinfo *, const char *, const struct puffs_cred *,
6470d81924Smanu     struct puffs_node **);
657b1d1ee6Smanu static int node_mk_common(struct puffs_usermount *, puffs_cookie_t,
66518513ecSmanu     struct puffs_newinfo *, const struct puffs_cn *pcn, perfuse_msg_t *);
676e1ab723Smanu static uint64_t readdir_last_cookie(struct fuse_dirent *, size_t);
687b1d1ee6Smanu static ssize_t fuse_to_dirent(struct puffs_usermount *, puffs_cookie_t,
697b1d1ee6Smanu     struct fuse_dirent *, size_t);
70075ba0e5Smanu static void readdir_buffered(puffs_cookie_t, struct dirent *, off_t *,
716e1ab723Smanu     size_t *);
72075ba0e5Smanu static void node_ref(puffs_cookie_t);
73075ba0e5Smanu static void node_rele(puffs_cookie_t);
747b1d1ee6Smanu static void requeue_request(struct puffs_usermount *,
757b1d1ee6Smanu     puffs_cookie_t opc, enum perfuse_qtype);
76075ba0e5Smanu static int dequeue_requests(puffs_cookie_t opc, enum perfuse_qtype, int);
777b1d1ee6Smanu #define DEQUEUE_ALL 0
787b1d1ee6Smanu 
797b1d1ee6Smanu /*
807b1d1ee6Smanu  *  From <sys/vnode>, inside #ifdef _KERNEL section
817b1d1ee6Smanu  */
827b1d1ee6Smanu #define IO_SYNC		(0x40|IO_DSYNC)
837b1d1ee6Smanu #define IO_DSYNC	0x00200
847b1d1ee6Smanu #define IO_DIRECT	0x02000
857b1d1ee6Smanu 
867b1d1ee6Smanu /*
877b1d1ee6Smanu  *  From <fcntl>, inside #ifdef _KERNEL section
887b1d1ee6Smanu  */
897b1d1ee6Smanu #define F_WAIT		0x010
907b1d1ee6Smanu #define F_FLOCK		0x020
9128d5b640Smanu #define OFLAGS(fflags)  ((fflags) - 1)
927b1d1ee6Smanu 
937b1d1ee6Smanu /*
947b1d1ee6Smanu  * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h
957b1d1ee6Smanu  */
967b1d1ee6Smanu const enum vtype iftovt_tab[16] = {
977b1d1ee6Smanu 	VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
987b1d1ee6Smanu         VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD,
997b1d1ee6Smanu };
1007b1d1ee6Smanu const int vttoif_tab[9] = {
1017b1d1ee6Smanu 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
1027b1d1ee6Smanu         S_IFSOCK, S_IFIFO, S_IFMT,
1037b1d1ee6Smanu };
1047b1d1ee6Smanu 
1057b1d1ee6Smanu #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
1067b1d1ee6Smanu #define VTTOIF(indx) (vttoif_tab[(int)(indx)])
1077b1d1ee6Smanu 
108ef486683Smanu #define PN_ISDIR(opc) \
109ef486683Smanu 	(puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR)
110ef486683Smanu 
11155557eb9Smanu #if 0
11255557eb9Smanu static void
113e1a2f47fSmatt print_node(const char *func, puffs_cookie_t opc)
11455557eb9Smanu {
11555557eb9Smanu 	struct puffs_node *pn;
11655557eb9Smanu 	struct perfuse_node_data *pnd;
11755557eb9Smanu 	struct vattr *vap;
11855557eb9Smanu 
11955557eb9Smanu 	pn = (struct puffs_node *)opc;
12055557eb9Smanu 	pnd = PERFUSE_NODE_DATA(opc);
12155557eb9Smanu 	vap = &pn->pn_va;
12255557eb9Smanu 
12355557eb9Smanu 	printf("%s: \"%s\", opc = %p, nodeid = 0x%"PRIx64" ino = %"PRIu64"\n",
12455557eb9Smanu 	       func, pnd->pnd_name, opc, pnd->pnd_nodeid, vap->va_fileid);
12555557eb9Smanu 
12655557eb9Smanu 	return;
12755557eb9Smanu }
12855557eb9Smanu #endif /* PERFUSE_DEBUG */
12955557eb9Smanu 
1303d6861b5Smanu int
perfuse_node_close_common(struct puffs_usermount * pu,puffs_cookie_t opc,int mode)131e1a2f47fSmatt perfuse_node_close_common(struct puffs_usermount *pu, puffs_cookie_t opc,
132e1a2f47fSmatt 	int mode)
13328d5b640Smanu {
13428d5b640Smanu 	struct perfuse_state *ps;
13528d5b640Smanu 	perfuse_msg_t *pm;
13628d5b640Smanu 	int op;
13728d5b640Smanu 	uint64_t fh;
13828d5b640Smanu 	struct fuse_release_in *fri;
13928d5b640Smanu 	struct perfuse_node_data *pnd;
14028d5b640Smanu 	struct puffs_node *pn;
14128d5b640Smanu 	int error;
14228d5b640Smanu 
14328d5b640Smanu 	ps = puffs_getspecific(pu);
14428d5b640Smanu 	pn = (struct puffs_node *)opc;
14528d5b640Smanu 	pnd = PERFUSE_NODE_DATA(pn);
14628d5b640Smanu 
147ef486683Smanu 	if (PN_ISDIR(opc)) {
14828d5b640Smanu 		op = FUSE_RELEASEDIR;
14928d5b640Smanu 		mode = FREAD;
15028d5b640Smanu 	} else {
15128d5b640Smanu 		op = FUSE_RELEASE;
15228d5b640Smanu 	}
15328d5b640Smanu 
15428d5b640Smanu 	/*
15528d5b640Smanu 	 * Destroy the filehandle before sending the
15628d5b640Smanu 	 * request to the FUSE filesystem, otherwise
15728d5b640Smanu 	 * we may get a second close() while we wait
15828d5b640Smanu 	 * for the reply, and we would end up closing
15928d5b640Smanu 	 * the same fh twice instead of closng both.
16028d5b640Smanu 	 */
16128d5b640Smanu 	fh = perfuse_get_fh(opc, mode);
16228d5b640Smanu 	perfuse_destroy_fh(pn, fh);
16328d5b640Smanu 
16428d5b640Smanu 	/*
16528d5b640Smanu 	 * release_flags may be set to FUSE_RELEASE_FLUSH
16628d5b640Smanu 	 * to flush locks. lock_owner must be set in that case
167fb0fa57fSmanu 	 *
168fb0fa57fSmanu 	 * ps_new_msg() is called with NULL creds, which will
169fb0fa57fSmanu 	 * be interpreted as FUSE superuser. We come here from the
170fb0fa57fSmanu 	 * inactive method, which provides no creds, but obviously
171fb0fa57fSmanu 	 * runs with kernel privilege.
17228d5b640Smanu 	 */
17328d5b640Smanu 	pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL);
17428d5b640Smanu 	fri = GET_INPAYLOAD(ps, pm, fuse_release_in);
17528d5b640Smanu 	fri->fh = fh;
17628d5b640Smanu 	fri->flags = 0;
17728d5b640Smanu 	fri->release_flags = 0;
17828d5b640Smanu 	fri->lock_owner = pnd->pnd_lock_owner;
17928d5b640Smanu 	fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0;
18028d5b640Smanu 
18128d5b640Smanu #ifdef PERFUSE_DEBUG
18228d5b640Smanu 	if (perfuse_diagflags & PDF_FH)
1832bc8acd8Smanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", fh = 0x%"PRIx64"\n",
1842bc8acd8Smanu 			 __func__, (void *)opc, pnd->pnd_nodeid, fri->fh);
18528d5b640Smanu #endif
18628d5b640Smanu 
1873d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm,
1883d6861b5Smanu 			      NO_PAYLOAD_REPLY_LEN, wait_reply)) != 0)
18928d5b640Smanu 		DERRX(EX_SOFTWARE, "%s: freed fh = 0x%"PRIx64" but filesystem "
19028d5b640Smanu 		      "returned error = %d", __func__, fh, error);
19128d5b640Smanu 
19271a2942bSmanu 	ps->ps_destroy_msg(pm);
19371a2942bSmanu 
19471a2942bSmanu 	return 0;
19528d5b640Smanu }
1967b1d1ee6Smanu 
1973d6861b5Smanu static int
xchg_msg(struct puffs_usermount * pu,puffs_cookie_t opc,perfuse_msg_t * pm,size_t len,enum perfuse_xchg_pb_reply wait)198e1a2f47fSmatt xchg_msg(struct puffs_usermount *pu, puffs_cookie_t opc, perfuse_msg_t *pm,
199e1a2f47fSmatt 	size_t len, enum perfuse_xchg_pb_reply wait)
2003d6861b5Smanu {
2013d6861b5Smanu 	struct perfuse_state *ps;
202f7174423Smanu 	struct perfuse_node_data *pnd;
2034fba06adSmanu 	struct perfuse_trace *pt = NULL;
2043d6861b5Smanu 	int error;
2053d6861b5Smanu 
2063d6861b5Smanu 	ps = puffs_getspecific(pu);
207f7174423Smanu 	pnd = NULL;
208f7174423Smanu 	if ((struct puffs_node *)opc != NULL)
209f7174423Smanu 		pnd = PERFUSE_NODE_DATA(opc);
2103d6861b5Smanu 
2113d6861b5Smanu #ifdef PERFUSE_DEBUG
212bcf6f2f3Smanu 	if ((perfuse_diagflags & PDF_FILENAME) && (opc != 0))
2132bc8acd8Smanu 		DPRINTF("file = \"%s\", ino = %"PRIu64" flags = 0x%x\n",
214075ba0e5Smanu 			perfuse_node_path(ps, opc),
2152bc8acd8Smanu 			((struct puffs_node *)opc)->pn_va.va_fileid,
216bcf6f2f3Smanu 			PERFUSE_NODE_DATA(opc)->pnd_flags);
2173d6861b5Smanu #endif
218075ba0e5Smanu 	ps->ps_xchgcount++;
219f7174423Smanu 	if (pnd)
220075ba0e5Smanu 		pnd->pnd_inxchg++;
221f7174423Smanu 
2224fba06adSmanu 	/*
2234fba06adSmanu 	 * Record FUSE call start if requested
2244fba06adSmanu 	 */
2258fcbc707Smanu 	if (perfuse_diagflags & PDF_TRACE)
2268fcbc707Smanu 		pt = perfuse_trace_begin(ps, opc, pm);
2274fba06adSmanu 
2284fba06adSmanu 	/*
2294fba06adSmanu 	 * Do actual FUSE exchange
2304fba06adSmanu 	 */
23171a2942bSmanu 	if ((error = ps->ps_xchg_msg(pu, pm, len, wait)) != 0)
23271a2942bSmanu 		ps->ps_destroy_msg(pm);
2333d6861b5Smanu 
2344fba06adSmanu 	/*
2354fba06adSmanu 	 * Record FUSE call end if requested
2364fba06adSmanu 	 */
2378fcbc707Smanu 	if (pt != NULL)
2388fcbc707Smanu 		perfuse_trace_end(ps, pt, error);
2394fba06adSmanu 
240075ba0e5Smanu 	ps->ps_xchgcount--;
241f7174423Smanu 	if (pnd) {
242075ba0e5Smanu 		pnd->pnd_inxchg--;
243075ba0e5Smanu 		(void)dequeue_requests(opc, PCQ_AFTERXCHG, DEQUEUE_ALL);
244f7174423Smanu 	}
245f7174423Smanu 
2463d6861b5Smanu 	return error;
2473d6861b5Smanu }
2483d6861b5Smanu 
249374b973dSmanu static int
mode_access(puffs_cookie_t opc,const struct puffs_cred * pcr,mode_t mode)250e1a2f47fSmatt mode_access(puffs_cookie_t opc, const struct puffs_cred *pcr, mode_t mode)
251374b973dSmanu {
252374b973dSmanu 	struct puffs_node *pn;
253374b973dSmanu 	struct vattr *va;
254374b973dSmanu 
2552ff0ea03Smanu 	/*
2562ff0ea03Smanu 	 * pcr is NULL for self open through fsync or readdir.
2572ff0ea03Smanu 	 * In both case, access control is useless, as it was
2582ff0ea03Smanu 	 * done before, at open time.
2592ff0ea03Smanu 	 */
2602ff0ea03Smanu 	if (pcr == NULL)
2612ff0ea03Smanu 		return 0;
2622ff0ea03Smanu 
263374b973dSmanu 	pn = (struct puffs_node *)opc;
264374b973dSmanu 	va = puffs_pn_getvap(pn);
265374b973dSmanu 	return puffs_access(va->va_type, va->va_mode,
266374b973dSmanu 			    va->va_uid, va->va_gid,
267374b973dSmanu 			    mode, pcr);
268374b973dSmanu }
269374b973dSmanu 
270c3c545a5Smanu static int
sticky_access(puffs_cookie_t opc,struct puffs_node * targ,const struct puffs_cred * pcr)271075ba0e5Smanu sticky_access(puffs_cookie_t opc, struct puffs_node *targ,
272075ba0e5Smanu 	      const struct puffs_cred *pcr)
273c3c545a5Smanu {
274c3c545a5Smanu 	uid_t uid;
275bcfebaffSmanu 	int sticky, owner, parent_owner;
276c3c545a5Smanu 
277fb0fa57fSmanu 	/*
278fb0fa57fSmanu 	 * This covers the case where the kernel requests a DELETE
279fb0fa57fSmanu 	 * or RENAME on its own, and where puffs_cred_getuid would
280fb0fa57fSmanu 	 * return -1. While such a situation should not happen,
281fb0fa57fSmanu 	 * we allow it here.
282fb0fa57fSmanu 	 *
283fb0fa57fSmanu 	 * This also allows root to tamper with other users' files
284fb0fa57fSmanu 	 * that have the sticky bit.
285fb0fa57fSmanu 	 */
286fb0fa57fSmanu 	if (puffs_cred_isjuggernaut(pcr))
287fb0fa57fSmanu 		return 0;
288fb0fa57fSmanu 
289fb0fa57fSmanu 	if (puffs_cred_getuid(pcr, &uid) != 0)
290fb0fa57fSmanu 		DERRX(EX_SOFTWARE, "puffs_cred_getuid fails in %s", __func__);
291c3c545a5Smanu 
292075ba0e5Smanu 	sticky = puffs_pn_getvap(opc)->va_mode & S_ISTXT;
293c3c545a5Smanu 	owner = puffs_pn_getvap(targ)->va_uid == uid;
294bcfebaffSmanu 	parent_owner = puffs_pn_getvap(opc)->va_uid == uid;
295c3c545a5Smanu 
296bcfebaffSmanu 	if (sticky && !owner && !parent_owner)
297bcfebaffSmanu 		return EPERM;
298c3c545a5Smanu 
2994c793394Smanu 	return 0;
300c3c545a5Smanu }
301c3c545a5Smanu 
302c3c545a5Smanu 
3037b1d1ee6Smanu static void
fuse_attr_to_vap(struct perfuse_state * ps,struct vattr * vap,struct fuse_attr * fa)304e1a2f47fSmatt fuse_attr_to_vap(struct perfuse_state *ps, struct vattr *vap,
305e1a2f47fSmatt 	struct fuse_attr *fa)
3067b1d1ee6Smanu {
3077b1d1ee6Smanu 	vap->va_type = IFTOVT(fa->mode);
308c3c545a5Smanu 	vap->va_mode = fa->mode & ALLPERMS;
3097b1d1ee6Smanu 	vap->va_nlink = fa->nlink;
3107b1d1ee6Smanu 	vap->va_uid = fa->uid;
3117b1d1ee6Smanu 	vap->va_gid = fa->gid;
312e0a6df40Smanu 	vap->va_fsid = (long)ps->ps_fsid;
3137b1d1ee6Smanu 	vap->va_fileid = fa->ino;
3147b1d1ee6Smanu 	vap->va_size = fa->size;
3157b1d1ee6Smanu 	vap->va_blocksize = fa->blksize;
316ef7e1877Smanu 	vap->va_atime.tv_sec = (time_t)fa->atime;
317ef7e1877Smanu 	vap->va_atime.tv_nsec = (long) fa->atimensec;
318ef7e1877Smanu 	vap->va_mtime.tv_sec = (time_t)fa->mtime;
319ef7e1877Smanu 	vap->va_mtime.tv_nsec = (long)fa->mtimensec;
320ef7e1877Smanu 	vap->va_ctime.tv_sec = (time_t)fa->ctime;
321ef7e1877Smanu 	vap->va_ctime.tv_nsec = (long)fa->ctimensec;
3227b1d1ee6Smanu 	vap->va_birthtime.tv_sec = 0;
3237b1d1ee6Smanu 	vap->va_birthtime.tv_nsec = 0;
3247b1d1ee6Smanu 	vap->va_gen = 0;
3257b1d1ee6Smanu 	vap->va_flags = 0;
3267b1d1ee6Smanu 	vap->va_rdev = fa->rdev;
327f14b5899Smanu 	vap->va_bytes = fa->blocks * S_BLKSIZE;
3285b646d77Smanu 	vap->va_filerev = (u_quad_t)PUFFS_VNOVAL;
3297b1d1ee6Smanu 	vap->va_vaflags = 0;
3307b1d1ee6Smanu 
3317b1d1ee6Smanu 	if (vap->va_blocksize == 0)
3327b1d1ee6Smanu 		vap->va_blocksize = DEV_BSIZE;
3337b1d1ee6Smanu 
3345b646d77Smanu 	if (vap->va_size == (size_t)PUFFS_VNOVAL) /* XXX */
3357b1d1ee6Smanu 		vap->va_size = 0;
3367b1d1ee6Smanu 
3377b1d1ee6Smanu 	return;
3387b1d1ee6Smanu }
3397b1d1ee6Smanu 
34070d81924Smanu #ifdef PUFFS_KFLAG_CACHE_FS_TTL
341075ba0e5Smanu static void
perfuse_newinfo_setttl(struct puffs_newinfo * pni,struct puffs_node * pn,struct fuse_entry_out * feo,struct fuse_attr_out * fao)342075ba0e5Smanu perfuse_newinfo_setttl(struct puffs_newinfo *pni,
343075ba0e5Smanu     struct puffs_node *pn, struct fuse_entry_out *feo,
344075ba0e5Smanu     struct fuse_attr_out *fao)
34570d81924Smanu {
34670d81924Smanu #ifdef PERFUSE_DEBUG
34770d81924Smanu 	if ((feo == NULL) && (fao == NULL))
34870d81924Smanu 		DERRX(EX_SOFTWARE, "%s: feo and fao NULL", __func__);
34970d81924Smanu 
35070d81924Smanu 	if ((feo != NULL) && (fao != NULL))
35170d81924Smanu 		DERRX(EX_SOFTWARE, "%s: feo and fao != NULL", __func__);
35270d81924Smanu #endif /* PERFUSE_DEBUG */
35370d81924Smanu 
35470d81924Smanu 	if (fao != NULL) {
35570d81924Smanu 		struct timespec va_ttl;
35670d81924Smanu 
35770d81924Smanu 		va_ttl.tv_sec = fao->attr_valid;
35870d81924Smanu 		va_ttl.tv_nsec = fao->attr_valid_nsec;
35970d81924Smanu 
36070d81924Smanu 		puffs_newinfo_setvattl(pni, &va_ttl);
36170d81924Smanu 	}
36270d81924Smanu 
36370d81924Smanu 	if (feo != NULL) {
36470d81924Smanu 		struct timespec va_ttl;
36570d81924Smanu 		struct timespec cn_ttl;
366075ba0e5Smanu 		struct timespec now;
367075ba0e5Smanu 		struct perfuse_node_data *pnd = PERFUSE_NODE_DATA(pn);
36870d81924Smanu 
36970d81924Smanu 		va_ttl.tv_sec = feo->attr_valid;
37070d81924Smanu 		va_ttl.tv_nsec = feo->attr_valid_nsec;
37170d81924Smanu 		cn_ttl.tv_sec = feo->entry_valid;
37270d81924Smanu 		cn_ttl.tv_nsec = feo->entry_valid_nsec;
37370d81924Smanu 
37470d81924Smanu 		puffs_newinfo_setvattl(pni, &va_ttl);
37570d81924Smanu 		puffs_newinfo_setcnttl(pni, &cn_ttl);
376075ba0e5Smanu 
377075ba0e5Smanu 		if (clock_gettime(CLOCK_REALTIME, &now) != 0)
378075ba0e5Smanu 			DERR(EX_OSERR, "clock_gettime failed");
379075ba0e5Smanu 
380075ba0e5Smanu                 timespecadd(&now, &cn_ttl, &pnd->pnd_cn_expire);
38170d81924Smanu 	}
38270d81924Smanu 
38370d81924Smanu 	return;
38470d81924Smanu }
3856bceb418Smanu #endif /* PUFFS_KFLAG_CACHE_FS_TTL */
3862bc8acd8Smanu 
3877b1d1ee6Smanu static int
node_lookup_common(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const char * path,const struct puffs_cred * pcr,struct puffs_node ** pnp)388e1a2f47fSmatt node_lookup_common(struct puffs_usermount *pu, puffs_cookie_t opc,
38970d81924Smanu 	struct puffs_newinfo *pni, const char *path,
39070d81924Smanu 	const struct puffs_cred *pcr, struct puffs_node **pnp)
3917b1d1ee6Smanu {
3927b1d1ee6Smanu 	struct perfuse_state *ps;
3932bc8acd8Smanu 	struct perfuse_node_data *oldpnd;
3947b1d1ee6Smanu 	perfuse_msg_t *pm;
3957b1d1ee6Smanu 	struct fuse_entry_out *feo;
3967b1d1ee6Smanu 	struct puffs_node *pn;
3977b1d1ee6Smanu 	size_t len;
3987b1d1ee6Smanu 	int error;
3997b1d1ee6Smanu 
40055557eb9Smanu 	/*
40155557eb9Smanu 	 * Prevent further lookups if the parent was removed
40255557eb9Smanu 	 */
40355557eb9Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
40455557eb9Smanu 		return ESTALE;
40555557eb9Smanu 
4062bc8acd8Smanu 	if (pnp == NULL)
4072bc8acd8Smanu 		DERRX(EX_SOFTWARE, "pnp must be != NULL");
4082bc8acd8Smanu 
4097b1d1ee6Smanu 	ps = puffs_getspecific(pu);
4107b1d1ee6Smanu 
4112ff0ea03Smanu #ifdef PERFUSE_DEBUG
4122ff0ea03Smanu 	if (perfuse_diagflags & PDF_FILENAME)
4132ff0ea03Smanu 		DPRINTF("%s: opc = %p, file = \"%s\" looking up \"%s\"\n",
414075ba0e5Smanu 			__func__, (void *)opc,
415075ba0e5Smanu 			perfuse_node_path(ps, opc), path);
4162ff0ea03Smanu 
417075ba0e5Smanu 	if (strcmp(path, ".") == 0)
418075ba0e5Smanu 		DERRX(EX_SOFTWARE, "unexpected dot-lookup");
4192bc8acd8Smanu 
420075ba0e5Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_RECLAIMED)
421075ba0e5Smanu 		DERRX(EX_SOFTWARE,
422075ba0e5Smanu 		      "looking up reclaimed node opc = %p, name = \"%s\"",
423075ba0e5Smanu 		      opc, path);
424075ba0e5Smanu 
425075ba0e5Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_INVALID)
426075ba0e5Smanu 		DERRX(EX_SOFTWARE,
427075ba0e5Smanu 		      "looking up freed node opc = %p, name = \"%s\"",
428075ba0e5Smanu 		      opc, path);
429075ba0e5Smanu #endif /* PERFUSE_DEBUG */
4302ff0ea03Smanu 
4317b1d1ee6Smanu 	len = strlen(path) + 1;
432fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_LOOKUP, len, pcr);
4337b1d1ee6Smanu 	(void)strlcpy(_GET_INPAYLOAD(ps, pm, char *), path, len);
4347b1d1ee6Smanu 
435075ba0e5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*feo), wait_reply)) != 0)
43671a2942bSmanu 		return error;
4377b1d1ee6Smanu 
4387b1d1ee6Smanu 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
4397b1d1ee6Smanu 
440075ba0e5Smanu 	/*
441299c0866Smanu 	 * Starting with ABI 7.4, inode number 0 means ENOENT,
442299c0866Smanu 	 * with entry_valid / entry_valid_nsec giving negative
443299c0866Smanu 	 * cache timeout (which we do not implement yet).
444299c0866Smanu 	 */
445299c0866Smanu 	if (feo->attr.ino == 0) {
446299c0866Smanu 		ps->ps_destroy_msg(pm);
447299c0866Smanu 		return ENOENT;
448299c0866Smanu 	}
449299c0866Smanu 
450299c0866Smanu 	/*
451075ba0e5Smanu 	 * Check for a known node, not reclaimed, with another name.
452075ba0e5Smanu 	 * It may have been moved, or we can lookup ../
453075ba0e5Smanu 	 */
454075ba0e5Smanu 	if (((oldpnd = perfuse_node_bynodeid(ps, feo->nodeid)) != NULL) &&
455075ba0e5Smanu 	    !(oldpnd->pnd_flags & PND_RECLAIMED)) {
456075ba0e5Smanu 		/*
457075ba0e5Smanu 		 * Save the new node name if not ..
458075ba0e5Smanu 		 */
459075ba0e5Smanu 		if (strncmp(path, "..", len) != 0)
460075ba0e5Smanu 			(void)strlcpy(oldpnd->pnd_name,
461075ba0e5Smanu 				      path, MAXPATHLEN);
46270d81924Smanu 		pn = oldpnd->pnd_pn;
4632bc8acd8Smanu 
464075ba0e5Smanu 	} else {
4652ff0ea03Smanu 		pn = perfuse_new_pn(pu, path, opc);
4662bc8acd8Smanu 		PERFUSE_NODE_DATA(pn)->pnd_nodeid = feo->nodeid;
467075ba0e5Smanu 		perfuse_node_cache(ps, pn);
46870d81924Smanu 	}
4697b1d1ee6Smanu 
470075ba0e5Smanu #ifdef PERFUSE_DEBUG
471075ba0e5Smanu 	if (PERFUSE_NODE_DATA(pn)->pnd_flags & PND_RECLAIMED)
472075ba0e5Smanu 		DERRX(EX_SOFTWARE,
473075ba0e5Smanu 		      "reclaimed in lookup opc = %p, name = \"%s\", ck = %p",
474075ba0e5Smanu 		      opc, path, pn);
475075ba0e5Smanu 
476075ba0e5Smanu 	if (PERFUSE_NODE_DATA(pn)->pnd_flags & PND_INVALID)
477075ba0e5Smanu 		DERRX(EX_SOFTWARE,
478075ba0e5Smanu 		      "freed in lookup opc = %p, name = \"%s\", ck = %p",
479075ba0e5Smanu 		      opc, path, pn);
480075ba0e5Smanu #endif /* PERFUSE_DEBUG */
481075ba0e5Smanu 
4827b1d1ee6Smanu 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
4835b646d77Smanu 	pn->pn_va.va_gen = (u_long)(feo->generation);
484075ba0e5Smanu 	PERFUSE_NODE_DATA(pn)->pnd_fuse_nlookup++;
485ef486683Smanu 	PERFUSE_NODE_DATA(pn)->pnd_puffs_nlookup++;
4867b1d1ee6Smanu 
4877b1d1ee6Smanu 	*pnp = pn;
4887b1d1ee6Smanu 
489bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
490bcf6f2f3Smanu 	if (perfuse_diagflags & PDF_FILENAME)
4912bc8acd8Smanu 		DPRINTF("%s: opc = %p, looked up opc = %p, "
4922bc8acd8Smanu 			"nodeid = 0x%"PRIx64" file = \"%s\"\n", __func__,
4932bc8acd8Smanu 			(void *)opc, pn, feo->nodeid, path);
494bcf6f2f3Smanu #endif
4952bc8acd8Smanu 
49670d81924Smanu 	if (pni != NULL) {
49770d81924Smanu #ifdef PUFFS_KFLAG_CACHE_FS_TTL
49870d81924Smanu 		puffs_newinfo_setva(pni, &pn->pn_va);
499075ba0e5Smanu 		perfuse_newinfo_setttl(pni, pn, feo, NULL);
50070d81924Smanu #endif /* PUFFS_KFLAG_CACHE_FS_TTL */
50170d81924Smanu 		puffs_newinfo_setcookie(pni, pn);
50270d81924Smanu 		puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
50370d81924Smanu 		puffs_newinfo_setsize(pni, (voff_t)pn->pn_va.va_size);
50470d81924Smanu 		puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
50570d81924Smanu 	}
50670d81924Smanu 
5077b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
5087b1d1ee6Smanu 
50971a2942bSmanu 	return 0;
5107b1d1ee6Smanu }
5117b1d1ee6Smanu 
5127b1d1ee6Smanu 
5137b1d1ee6Smanu /*
514c3c545a5Smanu  * Common code for methods that create objects:
5157b1d1ee6Smanu  * perfuse_node_mkdir
5167b1d1ee6Smanu  * perfuse_node_mknod
5177b1d1ee6Smanu  * perfuse_node_symlink
5187b1d1ee6Smanu  */
5197b1d1ee6Smanu static int
node_mk_common(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,perfuse_msg_t * pm)520e1a2f47fSmatt node_mk_common(struct puffs_usermount *pu, puffs_cookie_t opc,
521e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
522e1a2f47fSmatt 	perfuse_msg_t *pm)
5237b1d1ee6Smanu {
5247b1d1ee6Smanu 	struct perfuse_state *ps;
5257b1d1ee6Smanu 	struct puffs_node *pn;
5267b1d1ee6Smanu 	struct fuse_entry_out *feo;
5277b1d1ee6Smanu 	int error;
5287b1d1ee6Smanu 
5297b1d1ee6Smanu 	ps =  puffs_getspecific(pu);
5307b1d1ee6Smanu 
5313d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*feo), wait_reply)) != 0)
53271a2942bSmanu 		return error;
5337b1d1ee6Smanu 
5347b1d1ee6Smanu 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
5352bc8acd8Smanu 	if (feo->nodeid == PERFUSE_UNKNOWN_NODEID)
5362bc8acd8Smanu 		DERRX(EX_SOFTWARE, "%s: no nodeid", __func__);
5377b1d1ee6Smanu 
5382ff0ea03Smanu 	pn = perfuse_new_pn(pu, pcn->pcn_name, opc);
5392bc8acd8Smanu 	PERFUSE_NODE_DATA(pn)->pnd_nodeid = feo->nodeid;
540ef486683Smanu 	PERFUSE_NODE_DATA(pn)->pnd_fuse_nlookup++;
541075ba0e5Smanu 	PERFUSE_NODE_DATA(pn)->pnd_puffs_nlookup++;
542075ba0e5Smanu 	perfuse_node_cache(ps, pn);
5437b1d1ee6Smanu 
5447b1d1ee6Smanu 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
5455b646d77Smanu 	pn->pn_va.va_gen = (u_long)(feo->generation);
5465b646d77Smanu 
5477b1d1ee6Smanu 	puffs_newinfo_setcookie(pni, pn);
54870d81924Smanu #ifdef PUFFS_KFLAG_CACHE_FS_TTL
54970d81924Smanu 	puffs_newinfo_setva(pni, &pn->pn_va);
550075ba0e5Smanu 	perfuse_newinfo_setttl(pni, pn, feo, NULL);
551075ba0e5Smanu #endif /* PUFFS_KFLAG_CACHE_FS_TTL */
55270d81924Smanu 
5532ff0ea03Smanu 
5542ff0ea03Smanu #ifdef PERFUSE_DEBUG
5552ff0ea03Smanu 	if (perfuse_diagflags & PDF_FILENAME)
5562ff0ea03Smanu 		DPRINTF("%s: opc = %p, file = \"%s\", flags = 0x%x "
5572bc8acd8Smanu 			"nodeid = 0x%"PRIx64"\n",
5582ff0ea03Smanu 			__func__, (void *)pn, pcn->pcn_name,
5592ff0ea03Smanu 			PERFUSE_NODE_DATA(pn)->pnd_flags, feo->nodeid);
5602ff0ea03Smanu #endif
561374b973dSmanu 	ps->ps_destroy_msg(pm);
562374b973dSmanu 
56370d81924Smanu 	/* Parents is now dirty */
56417ce0ff6Smanu 	PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
5652ff0ea03Smanu 
56671a2942bSmanu 	return 0;
5677b1d1ee6Smanu }
5687b1d1ee6Smanu 
5696e1ab723Smanu static uint64_t
readdir_last_cookie(struct fuse_dirent * fd,size_t fd_len)570e1a2f47fSmatt readdir_last_cookie(struct fuse_dirent *fd, size_t fd_len)
5716e1ab723Smanu {
5726e1ab723Smanu 	size_t len;
5736e1ab723Smanu 	size_t seen = 0;
5746e1ab723Smanu 	char *ndp;
5756e1ab723Smanu 
5766e1ab723Smanu 	do {
5776e1ab723Smanu 		len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen);
5786e1ab723Smanu 		seen += len;
5796e1ab723Smanu 
5806e1ab723Smanu 		if (seen >= fd_len)
5816e1ab723Smanu 			break;
5826e1ab723Smanu 
5836e1ab723Smanu 		ndp = (char *)(void *)fd + (size_t)len;
5846e1ab723Smanu 		fd = (struct fuse_dirent *)(void *)ndp;
5856e1ab723Smanu 	} while (1 /* CONSTCOND */);
5866e1ab723Smanu 
5876e1ab723Smanu 	return fd->off;
5886e1ab723Smanu }
589c3c545a5Smanu 
5907b1d1ee6Smanu static ssize_t
fuse_to_dirent(struct puffs_usermount * pu,puffs_cookie_t opc,struct fuse_dirent * fd,size_t fd_len)591e1a2f47fSmatt fuse_to_dirent(struct puffs_usermount *pu, puffs_cookie_t opc,
592e1a2f47fSmatt 	struct fuse_dirent *fd, size_t fd_len)
5937b1d1ee6Smanu {
5947b1d1ee6Smanu 	struct dirent *dents;
5957b1d1ee6Smanu 	size_t dents_len;
5967b1d1ee6Smanu 	ssize_t written;
5977b1d1ee6Smanu 	uint64_t fd_offset;
5987b1d1ee6Smanu 	struct fuse_dirent *fd_base;
5997b1d1ee6Smanu 	size_t len;
6007b1d1ee6Smanu 
6017b1d1ee6Smanu 	fd_base = fd;
6027b1d1ee6Smanu 	fd_offset = 0;
6037b1d1ee6Smanu 	written = 0;
6047b1d1ee6Smanu 	dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
6056f8501feSmanu 	dents_len = (size_t)PERFUSE_NODE_DATA(opc)->pnd_dirent_len;
6067b1d1ee6Smanu 
6077b1d1ee6Smanu 	do {
6087b1d1ee6Smanu 		char *ndp;
6097b1d1ee6Smanu 		size_t reclen;
61095e87e43Smanu 		char name[MAXPATHLEN];
6117b1d1ee6Smanu 
6127b1d1ee6Smanu 		reclen = _DIRENT_RECLEN(dents, fd->namelen);
6137b1d1ee6Smanu 
6147b1d1ee6Smanu 		/*
6157b1d1ee6Smanu 		 * Check we do not overflow the output buffer
6167b1d1ee6Smanu 		 * struct fuse_dirent is bigger than struct dirent,
6177b1d1ee6Smanu 		 * so we should always use fd_len and never reallocate
6187b1d1ee6Smanu 		 * later.
6197b1d1ee6Smanu 		 * If we have to reallocate, try to double the buffer
6207b1d1ee6Smanu 		 * each time so that we do not have to do it too often.
6217b1d1ee6Smanu 		 */
6227b1d1ee6Smanu 		if (written + reclen > dents_len) {
6237b1d1ee6Smanu 			if (dents_len == 0)
6247b1d1ee6Smanu 				dents_len = fd_len;
6257b1d1ee6Smanu 			else
6267b1d1ee6Smanu 				dents_len =
6277b1d1ee6Smanu 				   MAX(2 * dents_len, written + reclen);
6287b1d1ee6Smanu 
6297b1d1ee6Smanu 			dents = PERFUSE_NODE_DATA(opc)->pnd_dirent;
6307b1d1ee6Smanu 			if ((dents = realloc(dents, dents_len)) == NULL)
631dda15b03Schristos 				DERR(EX_OSERR, "%s: malloc failed", __func__);
6327b1d1ee6Smanu 
6337b1d1ee6Smanu 			PERFUSE_NODE_DATA(opc)->pnd_dirent = dents;
6347b1d1ee6Smanu 			PERFUSE_NODE_DATA(opc)->pnd_dirent_len = dents_len;
6357b1d1ee6Smanu 
6367b1d1ee6Smanu 			/*
6377b1d1ee6Smanu 			 * (void *) for delint
6387b1d1ee6Smanu 			 */
6397b1d1ee6Smanu 			ndp = (char *)(void *)dents + written;
6407b1d1ee6Smanu 			dents = (struct dirent *)(void *)ndp;
6417b1d1ee6Smanu 		}
6427b1d1ee6Smanu 
64395e87e43Smanu 		strncpy(name, fd->name, fd->namelen);
64495e87e43Smanu 		name[fd->namelen] = '\0';
64595e87e43Smanu 
6467b1d1ee6Smanu 		/*
6477b1d1ee6Smanu 		 * Filesystem was mounted without -o use_ino
6487b1d1ee6Smanu 		 * Perform a lookup to find it.
6497b1d1ee6Smanu 		 */
6507b1d1ee6Smanu 		if (fd->ino == PERFUSE_UNKNOWN_INO) {
6517b1d1ee6Smanu 			struct puffs_node *pn;
652b76d3b3fSmanu 			struct perfuse_node_data *pnd = PERFUSE_NODE_DATA(opc);
6537b1d1ee6Smanu 
6544c4ecb78Smanu 			if (strcmp(name, "..") == 0) {
655b76d3b3fSmanu 				/*
656b76d3b3fSmanu 				 * Avoid breaking out of fs
657b76d3b3fSmanu 				 * by lookup to .. on root
658b76d3b3fSmanu 				 */
6594c4ecb78Smanu 				if (pnd->pnd_nodeid == FUSE_ROOT_ID)
660b76d3b3fSmanu 					fd->ino = FUSE_ROOT_ID;
6614c4ecb78Smanu 				else
6624c4ecb78Smanu 					fd->ino = pnd->pnd_parent_nodeid;
6634c4ecb78Smanu 			} else if (strcmp(name, ".") == 0 ) {
6644c4ecb78Smanu 				fd->ino = pnd->pnd_nodeid;
665b76d3b3fSmanu 			} else {
66695e87e43Smanu 				int error;
66795e87e43Smanu 
66895e87e43Smanu 				error = node_lookup_common(pu, opc, NULL,
66995e87e43Smanu 							   name, NULL, &pn);
67095e87e43Smanu 				if (error != 0) {
67195e87e43Smanu 					DWARNX("node_lookup_common %s "
67295e87e43Smanu 					       "failed: %d", name, error);
6732bc8acd8Smanu 				} else {
6742bc8acd8Smanu 					fd->ino = pn->pn_va.va_fileid;
675b553c427Smanu 					(void)perfuse_node_reclaim2(pu, pn, 1);
6762bc8acd8Smanu 				}
6777b1d1ee6Smanu 			}
678b76d3b3fSmanu 		}
6797b1d1ee6Smanu 
6807b1d1ee6Smanu 		dents->d_fileno = fd->ino;
681ef7e1877Smanu 		dents->d_reclen = (unsigned short)reclen;
6827b1d1ee6Smanu 		dents->d_namlen = fd->namelen;
6837b1d1ee6Smanu 		dents->d_type = fd->type;
68495e87e43Smanu 		strlcpy(dents->d_name, name, fd->namelen + 1);
6857b1d1ee6Smanu 
6867b1d1ee6Smanu #ifdef PERFUSE_DEBUG
6877b1d1ee6Smanu 		if (perfuse_diagflags & PDF_READDIR)
6882bc8acd8Smanu 			DPRINTF("%s: translated \"%s\" ino = %"PRIu64"\n",
6897b1d1ee6Smanu 				__func__, dents->d_name, dents->d_fileno);
6907b1d1ee6Smanu #endif
6917b1d1ee6Smanu 
6927b1d1ee6Smanu 		dents = _DIRENT_NEXT(dents);
6937b1d1ee6Smanu 		written += reclen;
6947b1d1ee6Smanu 
6957b1d1ee6Smanu 		/*
6967b1d1ee6Smanu 		 * Move to the next record.
6976e1ab723Smanu 		 * fd->off is not the offset, it is an opaque cookie
6986e1ab723Smanu 		 * given by the filesystem to keep state across multiple
6996e1ab723Smanu 		 * readdir() operation.
70080f20270Sandvar 		 * Use record alignment instead.
7017b1d1ee6Smanu 		 */
7027b1d1ee6Smanu 		len = FUSE_DIRENT_ALIGN(sizeof(*fd) + fd->namelen);
7037b1d1ee6Smanu #ifdef PERFUSE_DEBUG
7047b1d1ee6Smanu 		if (perfuse_diagflags & PDF_READDIR)
705ef7e1877Smanu 			DPRINTF("%s: record at %"PRId64"/0x%"PRIx64" "
706ef7e1877Smanu 				"length = %zd/0x%zx. "
707ef7e1877Smanu 				"next record at %"PRId64"/0x%"PRIx64" "
708ef7e1877Smanu 				"max %zd/0x%zx\n",
7097b1d1ee6Smanu 				__func__, fd_offset, fd_offset, len, len,
7107b1d1ee6Smanu 				fd_offset + len, fd_offset + len,
7117b1d1ee6Smanu 				fd_len, fd_len);
7127b1d1ee6Smanu #endif
7137b1d1ee6Smanu 		fd_offset += len;
7147b1d1ee6Smanu 
7157b1d1ee6Smanu 		/*
7167b1d1ee6Smanu 		 * Check if next record is still within the packet
7177b1d1ee6Smanu 		 * If it is not, we reached the end of the buffer.
7187b1d1ee6Smanu 		 */
7197b1d1ee6Smanu 		if (fd_offset >= fd_len)
7207b1d1ee6Smanu 			break;
7217b1d1ee6Smanu 
7227b1d1ee6Smanu 		/*
7237b1d1ee6Smanu 		 * (void *) for delint
7247b1d1ee6Smanu 		 */
7257b1d1ee6Smanu 		ndp = (char *)(void *)fd_base + (size_t)fd_offset;
7267b1d1ee6Smanu 		fd = (struct fuse_dirent *)(void *)ndp;
7277b1d1ee6Smanu 
7287b1d1ee6Smanu 	} while (1 /* CONSTCOND */);
7297b1d1ee6Smanu 
7307b1d1ee6Smanu 	/*
7317b1d1ee6Smanu 	 * Adjust the dirent output length
7327b1d1ee6Smanu 	 */
7337b1d1ee6Smanu 	if (written != -1)
7347b1d1ee6Smanu 		PERFUSE_NODE_DATA(opc)->pnd_dirent_len = written;
7357b1d1ee6Smanu 
7367b1d1ee6Smanu 	return written;
7377b1d1ee6Smanu }
7387b1d1ee6Smanu 
739075ba0e5Smanu static void
readdir_buffered(puffs_cookie_t opc,struct dirent * dent,off_t * readoff,size_t * reslen)740e1a2f47fSmatt readdir_buffered(puffs_cookie_t opc, struct dirent *dent, off_t *readoff,
741e1a2f47fSmatt 	size_t *reslen)
7427b1d1ee6Smanu {
7437b1d1ee6Smanu 	struct dirent *fromdent;
7447b1d1ee6Smanu 	struct perfuse_node_data *pnd;
7457b1d1ee6Smanu 	char *ndp;
7467b1d1ee6Smanu 
7477b1d1ee6Smanu 	pnd = PERFUSE_NODE_DATA(opc);
7487b1d1ee6Smanu 
7497b1d1ee6Smanu 	while (*readoff < pnd->pnd_dirent_len) {
7507b1d1ee6Smanu 		/*
7517b1d1ee6Smanu 		 * (void *) for delint
7527b1d1ee6Smanu 		 */
7537b1d1ee6Smanu 		ndp = (char *)(void *)pnd->pnd_dirent + (size_t)*readoff;
7547b1d1ee6Smanu 		fromdent = (struct dirent *)(void *)ndp;
7557b1d1ee6Smanu 
7567b1d1ee6Smanu 		if (*reslen < _DIRENT_SIZE(fromdent))
7577b1d1ee6Smanu 			break;
7587b1d1ee6Smanu 
7597b1d1ee6Smanu 		memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
7607b1d1ee6Smanu 		*readoff += _DIRENT_SIZE(fromdent);
7617b1d1ee6Smanu 		*reslen -= _DIRENT_SIZE(fromdent);
7627b1d1ee6Smanu 
7637b1d1ee6Smanu 		dent = _DIRENT_NEXT(dent);
7647b1d1ee6Smanu 	}
7657b1d1ee6Smanu 
7667b1d1ee6Smanu #ifdef PERFUSE_DEBUG
7677b1d1ee6Smanu 	if (perfuse_diagflags & PDF_READDIR)
7686f8501feSmanu 		DPRINTF("%s: readoff = %"PRId64",  "
7696f8501feSmanu 			"pnd->pnd_dirent_len = %"PRId64"\n",
7707b1d1ee6Smanu 			__func__, *readoff, pnd->pnd_dirent_len);
7717b1d1ee6Smanu #endif
7727b1d1ee6Smanu 	if (*readoff >=  pnd->pnd_dirent_len) {
7737b1d1ee6Smanu 		free(pnd->pnd_dirent);
7747b1d1ee6Smanu 		pnd->pnd_dirent = NULL;
7757b1d1ee6Smanu 		pnd->pnd_dirent_len = 0;
7767b1d1ee6Smanu 	}
7777b1d1ee6Smanu 
778075ba0e5Smanu 	return;
779075ba0e5Smanu }
780075ba0e5Smanu 
781075ba0e5Smanu 
782075ba0e5Smanu static void
node_ref(puffs_cookie_t opc)783075ba0e5Smanu node_ref(puffs_cookie_t opc)
784075ba0e5Smanu {
785075ba0e5Smanu 	struct perfuse_node_data *pnd = PERFUSE_NODE_DATA(opc);
786075ba0e5Smanu 
787075ba0e5Smanu #ifdef PERFUSE_DEBUG
788075ba0e5Smanu 	if (pnd->pnd_flags & PND_INVALID)
789075ba0e5Smanu 		DERRX(EX_SOFTWARE, "Use of freed node opc = %p", opc);
790075ba0e5Smanu #endif /* PERFUSE_DEBUG */
791075ba0e5Smanu 
792075ba0e5Smanu 	pnd->pnd_ref++;
793075ba0e5Smanu 	return;
794075ba0e5Smanu }
795075ba0e5Smanu 
796075ba0e5Smanu static void
node_rele(puffs_cookie_t opc)797075ba0e5Smanu node_rele(puffs_cookie_t opc)
798075ba0e5Smanu {
799075ba0e5Smanu 	struct perfuse_node_data *pnd = PERFUSE_NODE_DATA(opc);
800075ba0e5Smanu 
801075ba0e5Smanu #ifdef PERFUSE_DEBUG
802075ba0e5Smanu 	if (pnd->pnd_flags & PND_INVALID)
803075ba0e5Smanu 		DERRX(EX_SOFTWARE, "Use of freed node opc = %p", opc);
804075ba0e5Smanu #endif /* PERFUSE_DEBUG */
805075ba0e5Smanu 
806075ba0e5Smanu 	pnd->pnd_ref--;
807075ba0e5Smanu 
808075ba0e5Smanu 	if (pnd->pnd_ref == 0)
809075ba0e5Smanu 		(void)dequeue_requests(opc, PCQ_REF, DEQUEUE_ALL);
810075ba0e5Smanu 
811075ba0e5Smanu 	return;
8127b1d1ee6Smanu }
8137b1d1ee6Smanu 
8147b1d1ee6Smanu static void
requeue_request(struct puffs_usermount * pu,puffs_cookie_t opc,enum perfuse_qtype type)815e1a2f47fSmatt requeue_request(struct puffs_usermount *pu, puffs_cookie_t opc,
816e1a2f47fSmatt 	enum perfuse_qtype type)
8177b1d1ee6Smanu {
8187b1d1ee6Smanu 	struct perfuse_cc_queue pcq;
8197b1d1ee6Smanu 	struct perfuse_node_data *pnd;
8207b1d1ee6Smanu 
8217b1d1ee6Smanu 	pnd = PERFUSE_NODE_DATA(opc);
8227b1d1ee6Smanu 	pcq.pcq_type = type;
8237b1d1ee6Smanu 	pcq.pcq_cc = puffs_cc_getcc(pu);
8247b1d1ee6Smanu 	TAILQ_INSERT_TAIL(&pnd->pnd_pcq, &pcq, pcq_next);
8257b1d1ee6Smanu 
8267b1d1ee6Smanu #ifdef PERFUSE_DEBUG
8277b1d1ee6Smanu 	if (perfuse_diagflags & PDF_REQUEUE)
828e9a8a6acSmanu 		DPRINTF("%s: REQUEUE opc = %p, pcc = %p (%s)\n",
829e9a8a6acSmanu 		        __func__, (void *)opc, pcq.pcq_cc,
830e9a8a6acSmanu 			perfuse_qtypestr[type]);
8317b1d1ee6Smanu #endif
8327b1d1ee6Smanu 
8337b1d1ee6Smanu 	puffs_cc_yield(pcq.pcq_cc);
834518513ecSmanu 	TAILQ_REMOVE(&pnd->pnd_pcq, &pcq, pcq_next);
8357b1d1ee6Smanu 
8367b1d1ee6Smanu #ifdef PERFUSE_DEBUG
8377b1d1ee6Smanu 	if (perfuse_diagflags & PDF_REQUEUE)
838e9a8a6acSmanu 		DPRINTF("%s: RESUME opc = %p, pcc = %p (%s)\n",
839e9a8a6acSmanu 		        __func__, (void *)opc, pcq.pcq_cc,
840e9a8a6acSmanu 			perfuse_qtypestr[type]);
8417b1d1ee6Smanu #endif
8427b1d1ee6Smanu 
8437b1d1ee6Smanu 	return;
8447b1d1ee6Smanu }
8457b1d1ee6Smanu 
846374c4263Smanu static int
dequeue_requests(puffs_cookie_t opc,enum perfuse_qtype type,int max)847075ba0e5Smanu dequeue_requests(puffs_cookie_t opc, enum perfuse_qtype type, int max)
8487b1d1ee6Smanu {
8497b1d1ee6Smanu 	struct perfuse_cc_queue *pcq;
8507b1d1ee6Smanu 	struct perfuse_node_data *pnd;
8517b1d1ee6Smanu 	int dequeued;
8527b1d1ee6Smanu 
8537b1d1ee6Smanu 	pnd = PERFUSE_NODE_DATA(opc);
8547b1d1ee6Smanu 	dequeued = 0;
8557b1d1ee6Smanu 	TAILQ_FOREACH(pcq, &pnd->pnd_pcq, pcq_next) {
8567b1d1ee6Smanu 		if (pcq->pcq_type != type)
8577b1d1ee6Smanu 			continue;
8587b1d1ee6Smanu 
8597b1d1ee6Smanu #ifdef PERFUSE_DEBUG
8607b1d1ee6Smanu 		if (perfuse_diagflags & PDF_REQUEUE)
861e9a8a6acSmanu 			DPRINTF("%s: SCHEDULE opc = %p, pcc = %p (%s)\n",
862e9a8a6acSmanu 				__func__, (void *)opc, pcq->pcq_cc,
863e9a8a6acSmanu 				 perfuse_qtypestr[type]);
8647b1d1ee6Smanu #endif
865518513ecSmanu 		puffs_cc_schedule(pcq->pcq_cc);
8667b1d1ee6Smanu 
8677b1d1ee6Smanu 		if (++dequeued == max)
8687b1d1ee6Smanu 			break;
8697b1d1ee6Smanu 	}
8707b1d1ee6Smanu 
8717b1d1ee6Smanu #ifdef PERFUSE_DEBUG
8727b1d1ee6Smanu 	if (perfuse_diagflags & PDF_REQUEUE)
8737b1d1ee6Smanu 		DPRINTF("%s: DONE  opc = %p\n", __func__, (void *)opc);
8747b1d1ee6Smanu #endif
8757b1d1ee6Smanu 
876374c4263Smanu 	return dequeued;
8777b1d1ee6Smanu }
8787b1d1ee6Smanu 
8797b1d1ee6Smanu void
perfuse_fs_init(struct puffs_usermount * pu)880e1a2f47fSmatt perfuse_fs_init(struct puffs_usermount *pu)
8817b1d1ee6Smanu {
8827b1d1ee6Smanu 	struct perfuse_state *ps;
8837b1d1ee6Smanu 	perfuse_msg_t *pm;
8847b1d1ee6Smanu 	struct fuse_init_in *fii;
8857b1d1ee6Smanu 	struct fuse_init_out *fio;
8867b1d1ee6Smanu 	int error;
8877b1d1ee6Smanu 
8887b1d1ee6Smanu 	ps = puffs_getspecific(pu);
8897b1d1ee6Smanu 
8907b1d1ee6Smanu         if (puffs_mount(pu, ps->ps_target, ps->ps_mountflags, ps->ps_root) != 0)
891dda15b03Schristos                 DERR(EX_OSERR, "%s: puffs_mount failed", __func__);
8927b1d1ee6Smanu 
8937b1d1ee6Smanu 	/*
8947b1d1ee6Smanu 	 * Linux 2.6.34.1 sends theses flags:
8957b1d1ee6Smanu 	 * FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC
8967b1d1ee6Smanu 	 * FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK
8977b1d1ee6Smanu 	 *
8987b1d1ee6Smanu 	 * Linux also sets max_readahead at 32 pages (128 kB)
899fb0fa57fSmanu 	 *
900fb0fa57fSmanu 	 * ps_new_msg() is called with NULL creds, which will
901fb0fa57fSmanu 	 * be interpreted as FUSE superuser.
9027b1d1ee6Smanu 	 */
9037b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, 0, FUSE_INIT, sizeof(*fii), NULL);
9047b1d1ee6Smanu 	fii = GET_INPAYLOAD(ps, pm, fuse_init_in);
9057b1d1ee6Smanu 	fii->major = FUSE_KERNEL_VERSION;
9067b1d1ee6Smanu 	fii->minor = FUSE_KERNEL_MINOR_VERSION;
90792ad06d8Schristos 	fii->max_readahead = (unsigned int)(32 * sysconf(_SC_PAGESIZE));
9087b1d1ee6Smanu 	fii->flags = (FUSE_ASYNC_READ|FUSE_POSIX_LOCKS|FUSE_ATOMIC_O_TRUNC);
9097b1d1ee6Smanu 
9103d6861b5Smanu 	if ((error = xchg_msg(pu, 0, pm, sizeof(*fio), wait_reply)) != 0)
9117b1d1ee6Smanu 		DERRX(EX_SOFTWARE, "init message exchange failed (%d)", error);
9127b1d1ee6Smanu 
9137b1d1ee6Smanu 	fio = GET_OUTPAYLOAD(ps, pm, fuse_init_out);
9147b1d1ee6Smanu 	ps->ps_max_readahead = fio->max_readahead;
9157b1d1ee6Smanu 	ps->ps_max_write = fio->max_write;
9167b1d1ee6Smanu 
9177b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
9187b1d1ee6Smanu 
9197b1d1ee6Smanu 	return;
9207b1d1ee6Smanu }
9217b1d1ee6Smanu 
9227b1d1ee6Smanu int
perfuse_fs_unmount(struct puffs_usermount * pu,int flags)923e1a2f47fSmatt perfuse_fs_unmount(struct puffs_usermount *pu, int flags)
9247b1d1ee6Smanu {
9257b1d1ee6Smanu 	perfuse_msg_t *pm;
9267b1d1ee6Smanu 	struct perfuse_state *ps;
9277b1d1ee6Smanu 	puffs_cookie_t opc;
9287b1d1ee6Smanu 	int error;
9297b1d1ee6Smanu 
9307b1d1ee6Smanu 	ps = puffs_getspecific(pu);
9317b1d1ee6Smanu 	opc = (puffs_cookie_t)puffs_getroot(pu);
932fb0fa57fSmanu 
933fb0fa57fSmanu 	/*
934fb0fa57fSmanu 	 * ps_new_msg() is called with NULL creds, which will
935fb0fa57fSmanu 	 * be interpreted as FUSE superuser.
936fb0fa57fSmanu 	 */
9377b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_DESTROY, 0, NULL);
9387b1d1ee6Smanu 
9393d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0){
9407b1d1ee6Smanu 		DWARN("unmount %s", ps->ps_target);
9417b1d1ee6Smanu 		if (!(flags & MNT_FORCE))
94271a2942bSmanu 			return error;
94371a2942bSmanu 		else
94471a2942bSmanu 			error = 0;
94571a2942bSmanu 	} else {
94671a2942bSmanu 		ps->ps_destroy_msg(pm);
9477b1d1ee6Smanu 	}
9487b1d1ee6Smanu 
94926381d51Smanu 	ps->ps_umount(pu);
95026381d51Smanu 
95126381d51Smanu 	if (perfuse_diagflags & PDF_MISC)
9527b1d1ee6Smanu 		DPRINTF("%s unmounted, exit\n", ps->ps_target);
9537b1d1ee6Smanu 
95426381d51Smanu 	return 0;
9557b1d1ee6Smanu }
9567b1d1ee6Smanu 
9577b1d1ee6Smanu int
perfuse_fs_statvfs(struct puffs_usermount * pu,struct puffs_statvfs * svfsb)95838a0431bSchristos perfuse_fs_statvfs(struct puffs_usermount *pu, struct puffs_statvfs *svfsb)
9597b1d1ee6Smanu {
9607b1d1ee6Smanu 	struct perfuse_state *ps;
9617b1d1ee6Smanu 	perfuse_msg_t *pm;
9627b1d1ee6Smanu 	puffs_cookie_t opc;
9637b1d1ee6Smanu 	struct fuse_statfs_out *fso;
9647b1d1ee6Smanu 	int error;
9657b1d1ee6Smanu 
9667b1d1ee6Smanu 	ps = puffs_getspecific(pu);
9677b1d1ee6Smanu 	opc = (puffs_cookie_t)puffs_getroot(pu);
968fb0fa57fSmanu 
969fb0fa57fSmanu 	/*
970fb0fa57fSmanu 	 * ps_new_msg() is called with NULL creds, which will
971fb0fa57fSmanu 	 * be interpreted as FUSE superuser.
972fb0fa57fSmanu 	 */
9737b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
9747b1d1ee6Smanu 
9753d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*fso), wait_reply)) != 0)
97671a2942bSmanu 		return error;
9777b1d1ee6Smanu 
9787b1d1ee6Smanu 	fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
9797b1d1ee6Smanu 	svfsb->f_flag = ps->ps_mountflags;
9807b1d1ee6Smanu 	svfsb->f_bsize = fso->st.bsize;
9817b1d1ee6Smanu 	svfsb->f_frsize = fso->st.frsize;
9827b1d1ee6Smanu 	svfsb->f_iosize = ((struct puffs_node *)opc)->pn_va.va_blocksize;
9837b1d1ee6Smanu 	svfsb->f_blocks = fso->st.blocks;
9847b1d1ee6Smanu 	svfsb->f_bfree = fso->st.bfree;
9857b1d1ee6Smanu 	svfsb->f_bavail = fso->st.bavail;
9867b1d1ee6Smanu 	svfsb->f_bresvd = fso->st.bfree - fso->st.bavail;
9877b1d1ee6Smanu 	svfsb->f_files = fso->st.files;
9887b1d1ee6Smanu 	svfsb->f_ffree = fso->st.ffree;
9897b1d1ee6Smanu 	svfsb->f_favail = fso->st.ffree;/* files not reserved for root */
9907b1d1ee6Smanu 	svfsb->f_fresvd = 0;		/* files reserved for root */
9917b1d1ee6Smanu 
9927b1d1ee6Smanu 	svfsb->f_syncreads = ps->ps_syncreads;
9937b1d1ee6Smanu 	svfsb->f_syncwrites = ps->ps_syncwrites;
9947b1d1ee6Smanu 
9957b1d1ee6Smanu 	svfsb->f_asyncreads = ps->ps_asyncreads;
9967b1d1ee6Smanu 	svfsb->f_asyncwrites = ps->ps_asyncwrites;
9977b1d1ee6Smanu 
998c3c545a5Smanu 	(void)memcpy(&svfsb->f_fsidx, &ps->ps_fsid, sizeof(ps->ps_fsid));
999c3c545a5Smanu 	svfsb->f_fsid = (unsigned long)ps->ps_fsid;
10007b1d1ee6Smanu 	svfsb->f_namemax = MAXPATHLEN;	/* XXX */
100113260297Smanu 	svfsb->f_owner = ps->ps_owner_uid;
10027b1d1ee6Smanu 
10037b1d1ee6Smanu 	(void)strlcpy(svfsb->f_mntonname, ps->ps_target, _VFS_NAMELEN);
10047b1d1ee6Smanu 
10057b1d1ee6Smanu 	if (ps->ps_filesystemtype != NULL)
10067b1d1ee6Smanu 		(void)strlcpy(svfsb->f_fstypename,
10077b1d1ee6Smanu 			      ps->ps_filesystemtype, _VFS_NAMELEN);
10087b1d1ee6Smanu 	else
10097b1d1ee6Smanu 		(void)strlcpy(svfsb->f_fstypename, "fuse", _VFS_NAMELEN);
10107b1d1ee6Smanu 
10117b1d1ee6Smanu 	if (ps->ps_source != NULL)
10127b1d1ee6Smanu 		strlcpy(svfsb->f_mntfromname, ps->ps_source, _VFS_NAMELEN);
10137b1d1ee6Smanu 	else
10147b1d1ee6Smanu 		strlcpy(svfsb->f_mntfromname, _PATH_FUSE, _VFS_NAMELEN);
101571a2942bSmanu 
10167b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
10177b1d1ee6Smanu 
101871a2942bSmanu 	return 0;
10197b1d1ee6Smanu }
10207b1d1ee6Smanu 
10217b1d1ee6Smanu int
perfuse_fs_sync(struct puffs_usermount * pu,int waitfor,const struct puffs_cred * pcr)1022e1a2f47fSmatt perfuse_fs_sync(struct puffs_usermount *pu, int waitfor,
1023e1a2f47fSmatt 	const struct puffs_cred *pcr)
10247b1d1ee6Smanu {
10257b1d1ee6Smanu 	/*
10267b1d1ee6Smanu 	 * FUSE does not seem to have a FS sync callback.
10277b1d1ee6Smanu 	 * Maybe do not even register this callback
10287b1d1ee6Smanu 	 */
10297b1d1ee6Smanu 	return puffs_fsnop_sync(pu, waitfor, pcr);
10307b1d1ee6Smanu }
10317b1d1ee6Smanu 
10327b1d1ee6Smanu /* ARGSUSED0 */
10337b1d1ee6Smanu int
perfuse_fs_fhtonode(struct puffs_usermount * pu,void * fid,size_t fidsize,struct puffs_newinfo * pni)1034e1a2f47fSmatt perfuse_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
1035e1a2f47fSmatt 	struct puffs_newinfo *pni)
10367b1d1ee6Smanu {
10377b1d1ee6Smanu 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
10387b1d1ee6Smanu 	return 0;
10397b1d1ee6Smanu }
10407b1d1ee6Smanu 
10417b1d1ee6Smanu /* ARGSUSED0 */
10427b1d1ee6Smanu int
perfuse_fs_nodetofh(struct puffs_usermount * pu,puffs_cookie_t cookie,void * fid,size_t * fidsize)1043e1a2f47fSmatt perfuse_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t cookie,
1044e1a2f47fSmatt 	void *fid, size_t *fidsize)
10457b1d1ee6Smanu {
10467b1d1ee6Smanu 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
10477b1d1ee6Smanu 	return 0;
10487b1d1ee6Smanu }
10497b1d1ee6Smanu 
10507b1d1ee6Smanu #if 0
10517b1d1ee6Smanu /* ARGSUSED0 */
10527b1d1ee6Smanu void
1053e1a2f47fSmatt perfuse_fs_extattrctl(struct puffs_usermount *pu, int cmd,
1054e1a2f47fSmatt 	puffs_cookie_t *cookie, int flags, int namespace, const char *attrname)
10557b1d1ee6Smanu {
10567b1d1ee6Smanu 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
10577b1d1ee6Smanu 	return 0;
10587b1d1ee6Smanu }
10597b1d1ee6Smanu #endif /* 0 */
10607b1d1ee6Smanu 
10617b1d1ee6Smanu /* ARGSUSED0 */
10627b1d1ee6Smanu void
perfuse_fs_suspend(struct puffs_usermount * pu,int status)1063e1a2f47fSmatt perfuse_fs_suspend(struct puffs_usermount *pu, int status)
10647b1d1ee6Smanu {
10657b1d1ee6Smanu 	return;
10667b1d1ee6Smanu }
10677b1d1ee6Smanu 
10687b1d1ee6Smanu 
10697b1d1ee6Smanu int
perfuse_node_lookup(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)1070e1a2f47fSmatt perfuse_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
1071e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
10727b1d1ee6Smanu {
1073075ba0e5Smanu 	struct perfuse_state *ps;
10747b1d1ee6Smanu 	struct puffs_node *pn;
1075c3c545a5Smanu 	mode_t mode;
10767b1d1ee6Smanu 	int error;
10777b1d1ee6Smanu 
1078075ba0e5Smanu 	ps = puffs_getspecific(pu);
1079075ba0e5Smanu 	node_ref(opc);
1080075ba0e5Smanu 
1081c3c545a5Smanu 	/*
1082c3c545a5Smanu 	 * Check permissions
1083c3c545a5Smanu 	 */
1084c3c545a5Smanu 	switch(pcn->pcn_nameiop) {
1085c3c545a5Smanu 	case NAMEI_DELETE: /* FALLTHROUGH */
1086c3c545a5Smanu 	case NAMEI_RENAME: /* FALLTHROUGH */
1087c3c545a5Smanu 	case NAMEI_CREATE:
1088e0a6df40Smanu 		if (pcn->pcn_flags & NAMEI_ISLASTCN)
1089c3c545a5Smanu 			mode = PUFFS_VEXEC|PUFFS_VWRITE;
1090e0a6df40Smanu 		else
1091e0a6df40Smanu 			mode = PUFFS_VEXEC;
1092c3c545a5Smanu 		break;
1093c3c545a5Smanu 	case NAMEI_LOOKUP: /* FALLTHROUGH */
1094c3c545a5Smanu 	default:
1095c3c545a5Smanu 		mode = PUFFS_VEXEC;
1096c3c545a5Smanu 		break;
1097c3c545a5Smanu 	}
1098c3c545a5Smanu 
1099c3c545a5Smanu 	if ((error = mode_access(opc, pcn->pcn_cred, mode)) != 0)
1100075ba0e5Smanu 		goto out;
1101f7174423Smanu 
110270d81924Smanu 	error = node_lookup_common(pu, (puffs_cookie_t)opc, pni,
1103fb0fa57fSmanu 				   pcn->pcn_name, pcn->pcn_cred, &pn);
1104075ba0e5Smanu 
11057b1d1ee6Smanu 	if (error != 0)
1106075ba0e5Smanu 		goto out;
1107f7174423Smanu 
1108c3c545a5Smanu 	/*
110955557eb9Smanu 	 * Kernel would kill us if the filesystem returned the parent
111055557eb9Smanu 	 * itself. If we want to live, hide that!
111155557eb9Smanu 	 */
111255557eb9Smanu 	if ((opc == (puffs_cookie_t)pn) && (strcmp(pcn->pcn_name, ".") != 0)) {
11136bceb418Smanu 		DERRX(EX_SOFTWARE, "lookup \"%s\" in \"%s\" returned parent",
1114075ba0e5Smanu 		      pcn->pcn_name, perfuse_node_path(ps, opc));
11156bceb418Smanu 		/* NOTREACHED */
1116075ba0e5Smanu 		error = ESTALE;
1117075ba0e5Smanu 		goto out;
111855557eb9Smanu 	}
111955557eb9Smanu 
112055557eb9Smanu 	/*
1121c3c545a5Smanu 	 * Removed node
1122c3c545a5Smanu 	 */
1123075ba0e5Smanu 	if (PERFUSE_NODE_DATA(pn)->pnd_flags & PND_REMOVED) {
1124075ba0e5Smanu 		error = ENOENT;
1125075ba0e5Smanu 		goto out;
1126075ba0e5Smanu 	}
11277b1d1ee6Smanu 
11287b1d1ee6Smanu 	/*
1129c3c545a5Smanu 	 * Check for sticky bit. Unfortunately there is no way to
1130c3c545a5Smanu 	 * do this before creating the puffs_node, since we require
1131c3c545a5Smanu 	 * this operation to get the node owner.
1132c3c545a5Smanu 	 */
1133c3c545a5Smanu 	switch (pcn->pcn_nameiop) {
1134c3c545a5Smanu 	case NAMEI_DELETE: /* FALLTHROUGH */
1135c3c545a5Smanu 	case NAMEI_RENAME:
1136075ba0e5Smanu 		error = sticky_access(opc, pn, pcn->pcn_cred);
1137c3c545a5Smanu 		if (error != 0) {
1138b553c427Smanu 			(void)perfuse_node_reclaim2(pu, pn, 1);
1139075ba0e5Smanu 			goto out;
1140c3c545a5Smanu 		}
1141c3c545a5Smanu 		break;
1142c3c545a5Smanu 	default:
1143c3c545a5Smanu 		break;
1144c3c545a5Smanu 	}
1145c3c545a5Smanu 
1146ef486683Smanu 	PERFUSE_NODE_DATA(pn)->pnd_fuse_nlookup++;
1147075ba0e5Smanu 	PERFUSE_NODE_DATA(pn)->pnd_puffs_nlookup++;
11487b1d1ee6Smanu 
1149075ba0e5Smanu 	error = 0;
1150075ba0e5Smanu 
1151075ba0e5Smanu out:
1152075ba0e5Smanu 	node_rele(opc);
1153f7174423Smanu 	return error;
11547b1d1ee6Smanu }
11557b1d1ee6Smanu 
11567b1d1ee6Smanu int
perfuse_node_create(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * vap)1157e1a2f47fSmatt perfuse_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
1158e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
1159e1a2f47fSmatt 	const struct vattr *vap)
11607b1d1ee6Smanu {
11617b1d1ee6Smanu 	perfuse_msg_t *pm;
11627b1d1ee6Smanu 	struct perfuse_state *ps;
11637b1d1ee6Smanu 	struct fuse_create_in *fci;
11647b1d1ee6Smanu 	struct fuse_entry_out *feo;
11657b1d1ee6Smanu 	struct fuse_open_out *foo;
11667b1d1ee6Smanu 	struct puffs_node *pn;
11677b1d1ee6Smanu 	const char *name;
11687b1d1ee6Smanu 	size_t namelen;
11697b1d1ee6Smanu 	size_t len;
11707b1d1ee6Smanu 	int error;
11717b1d1ee6Smanu 
1172f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
1173f7174423Smanu 		return ENOENT;
1174f7174423Smanu 
1175075ba0e5Smanu 	node_ref(opc);
1176075ba0e5Smanu 
1177374b973dSmanu 	/*
11787b1d1ee6Smanu 	 * If create is unimplemented: Check that it does not
11797b1d1ee6Smanu 	 * already exists, and if not, do mknod and open
11807b1d1ee6Smanu 	 */
11817b1d1ee6Smanu 	ps = puffs_getspecific(pu);
11827b1d1ee6Smanu 	if (ps->ps_flags & PS_NO_CREAT) {
118370d81924Smanu 		error = node_lookup_common(pu, opc, NULL, pcn->pcn_name,
1184fb0fa57fSmanu 					   pcn->pcn_cred, &pn);
1185075ba0e5Smanu 		if (error == 0)	{
1186b553c427Smanu 			(void)perfuse_node_reclaim2(pu, pn, 1);
1187075ba0e5Smanu 			error = EEXIST;
1188075ba0e5Smanu 			goto out;
1189075ba0e5Smanu 		}
11907b1d1ee6Smanu 
11917b1d1ee6Smanu 		error = perfuse_node_mknod(pu, opc, pni, pcn, vap);
11927b1d1ee6Smanu 		if (error != 0)
1193075ba0e5Smanu 			goto out;
11947b1d1ee6Smanu 
119570d81924Smanu 		error = node_lookup_common(pu, opc, NULL, pcn->pcn_name,
1196fb0fa57fSmanu 					   pcn->pcn_cred, &pn);
119728d5b640Smanu 		if (error != 0)
1198075ba0e5Smanu 			goto out;
119928d5b640Smanu 
12005b646d77Smanu 		/*
12015b646d77Smanu 		 * FUSE does the open at create time, while
12025b646d77Smanu 		 * NetBSD will open in a subsequent operation.
12035b646d77Smanu 		 * We need to open now, in order to retain FUSE
1204c3c545a5Smanu 		 * semantics. The calling process will not get
1205c3c545a5Smanu 		 * a file descriptor before the kernel sends
1206c3c545a5Smanu 		 * the open operation.
12075b646d77Smanu 		 */
1208075ba0e5Smanu 		error = perfuse_node_open(pu, (puffs_cookie_t)pn,
1209075ba0e5Smanu 					  FWRITE, pcn->pcn_cred);
1210075ba0e5Smanu 		goto out;
12117b1d1ee6Smanu 	}
12127b1d1ee6Smanu 
1213f7174423Smanu 	name = pcn->pcn_name;
1214f7174423Smanu 	namelen = pcn->pcn_namelen + 1;
12157b1d1ee6Smanu 	len = sizeof(*fci) + namelen;
12167b1d1ee6Smanu 
1217374c4263Smanu 	/*
1218374c4263Smanu 	 * flags should use O_WRONLY instead of O_RDWR, but it
1219374c4263Smanu 	 * breaks when the caller tries to read from file.
1220fac2d0c0Smanu 	 *
1221fac2d0c0Smanu 	 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type)
1222374c4263Smanu 	 */
1223fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_CREATE, len, pcn->pcn_cred);
12247b1d1ee6Smanu 	fci = GET_INPAYLOAD(ps, pm, fuse_create_in);
1225374c4263Smanu 	fci->flags = O_CREAT | O_TRUNC | O_RDWR;
1226fac2d0c0Smanu 	fci->mode = vap->va_mode | VTTOIF(vap->va_type);
1227374c4263Smanu 	fci->umask = 0; 	/* Seems unused by libfuse */
12287b1d1ee6Smanu 	(void)strlcpy((char*)(void *)(fci + 1), name, namelen);
12297b1d1ee6Smanu 
12307b1d1ee6Smanu 	len = sizeof(*feo) + sizeof(*foo);
123171a2942bSmanu 	if ((error = xchg_msg(pu, opc, pm, len, wait_reply)) != 0) {
123271a2942bSmanu 		/*
1233*37a23ecfSmsaitoh 		 * create is unimplemented, remember it for later,
123471a2942bSmanu 		 * and start over using mknod and open instead.
123571a2942bSmanu 		 */
123671a2942bSmanu 		if (error == ENOSYS) {
123771a2942bSmanu 			ps->ps_flags |= PS_NO_CREAT;
1238075ba0e5Smanu 			error = perfuse_node_create(pu, opc, pni, pcn, vap);
123971a2942bSmanu 		}
124071a2942bSmanu 
1241075ba0e5Smanu 		goto out;
124271a2942bSmanu 	}
12437b1d1ee6Smanu 
12447b1d1ee6Smanu 	feo = GET_OUTPAYLOAD(ps, pm, fuse_entry_out);
12457b1d1ee6Smanu 	foo = (struct fuse_open_out *)(void *)(feo + 1);
12462bc8acd8Smanu 	if (feo->nodeid == PERFUSE_UNKNOWN_NODEID)
12472bc8acd8Smanu 		DERRX(EX_SOFTWARE, "%s: no nodeid", __func__);
12487b1d1ee6Smanu 
12497b1d1ee6Smanu 	/*
12507b1d1ee6Smanu 	 * Save the file handle and inode in node private data
12517b1d1ee6Smanu 	 * so that we can reuse it later
12527b1d1ee6Smanu 	 */
12532ff0ea03Smanu 	pn = perfuse_new_pn(pu, name, opc);
1254bcf6f2f3Smanu 	perfuse_new_fh((puffs_cookie_t)pn, foo->fh, FWRITE);
12552bc8acd8Smanu 	PERFUSE_NODE_DATA(pn)->pnd_nodeid = feo->nodeid;
1256ef486683Smanu 	PERFUSE_NODE_DATA(pn)->pnd_fuse_nlookup++;
1257075ba0e5Smanu 	PERFUSE_NODE_DATA(pn)->pnd_puffs_nlookup++;
1258075ba0e5Smanu 	perfuse_node_cache(ps, pn);
12597b1d1ee6Smanu 
12607b1d1ee6Smanu 	fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr);
12615b646d77Smanu 	pn->pn_va.va_gen = (u_long)(feo->generation);
12625b646d77Smanu 
12637b1d1ee6Smanu 	puffs_newinfo_setcookie(pni, pn);
126470d81924Smanu #ifdef PUFFS_KFLAG_CACHE_FS_TTL
126570d81924Smanu 	puffs_newinfo_setva(pni, &pn->pn_va);
1266075ba0e5Smanu 	perfuse_newinfo_setttl(pni, pn, feo, NULL);
126770d81924Smanu #endif /* PUFFS_KFLAG_CACHE_FS_TTL */
12687b1d1ee6Smanu 
1269bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
1270bcf6f2f3Smanu 	if (perfuse_diagflags & (PDF_FH|PDF_FILENAME))
1271bcf6f2f3Smanu 		DPRINTF("%s: opc = %p, file = \"%s\", flags = 0x%x "
12722bc8acd8Smanu 			"nodeid = 0x%"PRIx64", wfh = 0x%"PRIx64"\n",
1273f7174423Smanu 			__func__, (void *)pn, pcn->pcn_name,
12742bc8acd8Smanu 			PERFUSE_NODE_DATA(pn)->pnd_flags, feo->nodeid,
12752bc8acd8Smanu 			foo->fh);
1276bcf6f2f3Smanu #endif
12773d6861b5Smanu 
1278c3c545a5Smanu 	ps->ps_destroy_msg(pm);
1279075ba0e5Smanu 	error = 0;
1280c3c545a5Smanu 
1281075ba0e5Smanu out:
1282075ba0e5Smanu 	node_rele(opc);
1283075ba0e5Smanu 	return error;
12847b1d1ee6Smanu }
12857b1d1ee6Smanu 
12867b1d1ee6Smanu 
12877b1d1ee6Smanu int
perfuse_node_mknod(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * vap)1288e1a2f47fSmatt perfuse_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc,
1289e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
1290e1a2f47fSmatt 	const struct vattr *vap)
12917b1d1ee6Smanu {
12927b1d1ee6Smanu 	struct perfuse_state *ps;
12937b1d1ee6Smanu 	perfuse_msg_t *pm;
12947b1d1ee6Smanu 	struct fuse_mknod_in *fmi;
12957b1d1ee6Smanu 	const char* path;
12967b1d1ee6Smanu 	size_t len;
1297075ba0e5Smanu 	int error;
12987b1d1ee6Smanu 
1299f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
1300f7174423Smanu 		return ENOENT;
1301f7174423Smanu 
1302075ba0e5Smanu 	node_ref(opc);
1303075ba0e5Smanu 
1304374b973dSmanu 	/*
1305374b973dSmanu 	 * Only superuser can mknod objects other than
1306374b973dSmanu 	 * directories, files, socks, fifo and links.
1307374b973dSmanu 	 *
1308374b973dSmanu 	 * Create an object require -WX permission in the parent directory
1309374b973dSmanu 	 */
1310374b973dSmanu 	switch (vap->va_type) {
1311374b973dSmanu 	case VDIR:	/* FALLTHROUGH */
1312374b973dSmanu 	case VREG:	/* FALLTHROUGH */
1313374b973dSmanu 	case VFIFO:	/* FALLTHROUGH */
1314c3c545a5Smanu 	case VSOCK:
1315374b973dSmanu 		break;
1316374b973dSmanu 	default:	/* VNON, VBLK, VCHR, VBAD */
1317075ba0e5Smanu 		if (!puffs_cred_isjuggernaut(pcn->pcn_cred)) {
1318bcfebaffSmanu 			error = EPERM;
1319075ba0e5Smanu 			goto out;
1320075ba0e5Smanu 		}
1321374b973dSmanu 		break;
1322374b973dSmanu 	}
1323374b973dSmanu 
1324374b973dSmanu 
13257b1d1ee6Smanu 	ps = puffs_getspecific(pu);
1326f7174423Smanu 	path = pcn->pcn_name;
1327f7174423Smanu 	len = sizeof(*fmi) + pcn->pcn_namelen + 1;
13287b1d1ee6Smanu 
1329374c4263Smanu 	/*
1330fac2d0c0Smanu 	 * mode must contain file type (ie: S_IFREG), use VTTOIF(vap->va_type)
1331374c4263Smanu 	 */
1332fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_MKNOD, len, pcn->pcn_cred);
13337b1d1ee6Smanu 	fmi = GET_INPAYLOAD(ps, pm, fuse_mknod_in);
13347b1d1ee6Smanu 	fmi->mode = vap->va_mode | VTTOIF(vap->va_type);
13356f8501feSmanu 	fmi->rdev = (uint32_t)vap->va_rdev;
13367b1d1ee6Smanu 	fmi->umask = 0; 	/* Seems unused bu libfuse */
13377b1d1ee6Smanu 	(void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
13387b1d1ee6Smanu 
1339075ba0e5Smanu 	error = node_mk_common(pu, opc, pni, pcn, pm);
1340075ba0e5Smanu 
1341075ba0e5Smanu out:
1342075ba0e5Smanu 	node_rele(opc);
1343075ba0e5Smanu 	return error;
13447b1d1ee6Smanu }
13457b1d1ee6Smanu 
13467b1d1ee6Smanu 
13477b1d1ee6Smanu int
perfuse_node_open(struct puffs_usermount * pu,puffs_cookie_t opc,int mode,const struct puffs_cred * pcr)1348e1a2f47fSmatt perfuse_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
1349e1a2f47fSmatt 	const struct puffs_cred *pcr)
13507b1d1ee6Smanu {
1351781f78b8Smanu 	return perfuse_node_open2(pu, opc, mode, pcr, NULL);
1352781f78b8Smanu }
1353781f78b8Smanu 
1354781f78b8Smanu int
perfuse_node_open2(struct puffs_usermount * pu,puffs_cookie_t opc,int mode,const struct puffs_cred * pcr,int * oflags)1355781f78b8Smanu perfuse_node_open2(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
1356781f78b8Smanu 	const struct puffs_cred *pcr, int *oflags)
1357781f78b8Smanu {
13587b1d1ee6Smanu 	struct perfuse_state *ps;
135928d5b640Smanu 	struct perfuse_node_data *pnd;
13607b1d1ee6Smanu 	perfuse_msg_t *pm;
136128d5b640Smanu 	mode_t fmode;
13627b1d1ee6Smanu 	int op;
13637b1d1ee6Smanu 	struct fuse_open_in *foi;
13647b1d1ee6Smanu 	struct fuse_open_out *foo;
13657b1d1ee6Smanu 	int error;
13667b1d1ee6Smanu 
13677b1d1ee6Smanu 	ps = puffs_getspecific(pu);
1368bcf6f2f3Smanu 	pnd = PERFUSE_NODE_DATA(opc);
1369bcf6f2f3Smanu 	error = 0;
1370bcf6f2f3Smanu 
1371f7174423Smanu 	if (pnd->pnd_flags & PND_REMOVED)
1372f7174423Smanu 		return ENOENT;
1373f7174423Smanu 
1374075ba0e5Smanu 	node_ref(opc);
1375075ba0e5Smanu 
1376ef486683Smanu 	if (PN_ISDIR(opc))
13777b1d1ee6Smanu 		op = FUSE_OPENDIR;
1378374b973dSmanu 	else
1379c3c545a5Smanu 		op = FUSE_OPEN;
1380374b973dSmanu 
1381374b973dSmanu 	/*
1382c3c545a5Smanu 	 * libfuse docs says
1383e0a6df40Smanu 	 * - O_CREAT and O_EXCL should never be set.
1384c3c545a5Smanu 	 * - O_TRUNC may be used if mount option atomic_o_trunc is used XXX
1385c3c545a5Smanu 	 *
1386c3c545a5Smanu 	 * O_APPEND makes no sense since FUSE always sends
1387c3c545a5Smanu 	 * the file offset for write operations. If the
1388c3c545a5Smanu 	 * filesystem uses pwrite(), O_APPEND would cause
1389c3c545a5Smanu 	 * the offset to be ignored and cause file corruption.
1390374b973dSmanu 	 */
1391e0a6df40Smanu 	mode &= ~(O_CREAT|O_EXCL|O_APPEND);
13927b1d1ee6Smanu 
139328d5b640Smanu 	/*
139428d5b640Smanu 	 * Do not open twice, and do not reopen for reading
13953a9497b9Smanu 	 * if we already have write handle.
139628d5b640Smanu 	 */
1397f7b64496Smanu 	switch (mode & (FREAD|FWRITE)) {
1398f7b64496Smanu 	case FREAD:
1399f7b64496Smanu 		if (pnd->pnd_flags & (PND_RFH|PND_WFH))
1400bcf6f2f3Smanu 			goto out;
1401f7b64496Smanu 		break;
1402f7b64496Smanu 	case FWRITE:
1403f7b64496Smanu 		if (pnd->pnd_flags & PND_WFH)
1404f7b64496Smanu 			goto out;
1405f7b64496Smanu 		break;
1406f7b64496Smanu 	case FREAD|FWRITE:
1407f7b64496Smanu 		if (pnd->pnd_flags & PND_WFH)
1408f7b64496Smanu 			goto out;
1409f7b64496Smanu 
1410f7b64496Smanu 		/*
1411f7b64496Smanu 		 * Corner case: if already open for reading (PND_RFH)
1412f7b64496Smanu 		 * and re-opening FREAD|FWRITE, we need to reopen,
1413f7b64496Smanu 		 * but only for writing. Note the change on mode
1414f7b64496Smanu 		 * will only affect perfuse_new_fh()
1415f7b64496Smanu 		 */
1416f7b64496Smanu 		if (pnd->pnd_flags & PND_RFH)
1417f7b64496Smanu 			mode &= ~FREAD;
1418f7b64496Smanu 		break;
141942e0a87cSmanu 	default:
142042e0a87cSmanu 		DWARNX("open without either FREAD nor FWRITE");
142142e0a87cSmanu 		error = EPERM;
142242e0a87cSmanu 		goto out;
1423f7b64496Smanu 	}
14243d6861b5Smanu 
1425bcf6f2f3Smanu 	/*
14262ff0ea03Smanu 	 * Queue open on a node so that we do not open
14272ff0ea03Smanu 	 * twice. This would be better with read and
14282ff0ea03Smanu 	 * write distinguished.
14292ff0ea03Smanu 	 */
14302ff0ea03Smanu 	while (pnd->pnd_flags & PND_INOPEN)
14312ff0ea03Smanu 		requeue_request(pu, opc, PCQ_OPEN);
14322ff0ea03Smanu 	pnd->pnd_flags |= PND_INOPEN;
14332ff0ea03Smanu 
14342ff0ea03Smanu 	/*
143528d5b640Smanu 	 * Convert PUFFS mode to FUSE mode: convert FREAD/FWRITE
143628d5b640Smanu 	 * to O_RDONLY/O_WRONLY while perserving the other options.
143728d5b640Smanu 	 */
143828d5b640Smanu 	fmode = mode & ~(FREAD|FWRITE);
143928d5b640Smanu 	fmode |= (mode & FWRITE) ? O_RDWR : O_RDONLY;
144028d5b640Smanu 
14417b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr);
14427b1d1ee6Smanu 	foi = GET_INPAYLOAD(ps, pm, fuse_open_in);
144328d5b640Smanu 	foi->flags = fmode;
14447b1d1ee6Smanu 	foi->unused = 0;
14457b1d1ee6Smanu 
14463d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*foo), wait_reply)) != 0)
14477b1d1ee6Smanu 		goto out;
14487b1d1ee6Smanu 
14497b1d1ee6Smanu 	foo = GET_OUTPAYLOAD(ps, pm, fuse_open_out);
14507b1d1ee6Smanu 
14517b1d1ee6Smanu 	/*
14527b1d1ee6Smanu 	 * Save the file handle in node private data
14537b1d1ee6Smanu 	 * so that we can reuse it later
14547b1d1ee6Smanu 	 */
1455bcf6f2f3Smanu 	perfuse_new_fh(opc, foo->fh, mode);
1456bcf6f2f3Smanu 
1457781f78b8Smanu 	/*
1458781f78b8Smanu 	 * Set direct I/O if the filesystems forces it
1459781f78b8Smanu 	 */
1460781f78b8Smanu 	if ((foo->open_flags & FUSE_FOPEN_DIRECT_IO) && (oflags != NULL))
1461781f78b8Smanu 		*oflags |= PUFFS_OPEN_IO_DIRECT;
1462781f78b8Smanu 
14637b1d1ee6Smanu #ifdef PERFUSE_DEBUG
1464bcf6f2f3Smanu 	if (perfuse_diagflags & (PDF_FH|PDF_FILENAME))
1465516b1c90Smanu 		DPRINTF("%s: opc = %p, file = \"%s\", "
14662bc8acd8Smanu 			"nodeid = 0x%"PRIx64", %s%sfh = 0x%"PRIx64"\n",
1467075ba0e5Smanu 			__func__, (void *)opc, perfuse_node_path(ps, opc),
14682bc8acd8Smanu 			pnd->pnd_nodeid, mode & FREAD ? "r" : "",
146928d5b640Smanu 			mode & FWRITE ? "w" : "", foo->fh);
14707b1d1ee6Smanu #endif
14713d6861b5Smanu 
14727b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
147371a2942bSmanu out:
14747b1d1ee6Smanu 
1475bcf6f2f3Smanu 	pnd->pnd_flags &= ~PND_INOPEN;
1476075ba0e5Smanu 	(void)dequeue_requests(opc, PCQ_OPEN, DEQUEUE_ALL);
1477bcf6f2f3Smanu 
1478075ba0e5Smanu 	node_rele(opc);
14797b1d1ee6Smanu 	return error;
14807b1d1ee6Smanu }
14817b1d1ee6Smanu 
148228d5b640Smanu /* ARGSUSED0 */
14837b1d1ee6Smanu int
perfuse_node_close(struct puffs_usermount * pu,puffs_cookie_t opc,int flags,const struct puffs_cred * pcr)1484e1a2f47fSmatt perfuse_node_close(struct puffs_usermount *pu, puffs_cookie_t opc, int flags,
1485e1a2f47fSmatt 	const struct puffs_cred *pcr)
14867b1d1ee6Smanu {
148728d5b640Smanu 	struct perfuse_node_data *pnd;
14887b1d1ee6Smanu 
148928d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
1490516b1c90Smanu 
1491516b1c90Smanu 	if (!(pnd->pnd_flags & PND_OPEN))
14927b1d1ee6Smanu 		return EBADF;
14937b1d1ee6Smanu 
149428d5b640Smanu 	/*
1495bcf6f2f3Smanu 	 * Actual close is postponed at inactive time.
1496516b1c90Smanu 	 */
149728d5b640Smanu 	return 0;
14987b1d1ee6Smanu }
14997b1d1ee6Smanu 
15007b1d1ee6Smanu int
perfuse_node_access(struct puffs_usermount * pu,puffs_cookie_t opc,int mode,const struct puffs_cred * pcr)1501e1a2f47fSmatt perfuse_node_access(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
1502e1a2f47fSmatt 	const struct puffs_cred *pcr)
15037b1d1ee6Smanu {
15047b1d1ee6Smanu 	perfuse_msg_t *pm;
15057b1d1ee6Smanu 	struct perfuse_state *ps;
15067b1d1ee6Smanu 	struct fuse_access_in *fai;
15077b1d1ee6Smanu 	int error;
15087b1d1ee6Smanu 
1509374c4263Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
1510374c4263Smanu 		return ENOENT;
1511374c4263Smanu 
1512075ba0e5Smanu 	node_ref(opc);
1513075ba0e5Smanu 
15147b1d1ee6Smanu 	/*
15157b1d1ee6Smanu 	 * If we previously detected the filesystem does not
15167b1d1ee6Smanu 	 * implement access(), short-circuit the call and skip
1517c3c545a5Smanu 	 * to libpuffs access() emulation.
15187b1d1ee6Smanu 	 */
15197b1d1ee6Smanu 	ps = puffs_getspecific(pu);
15207b1d1ee6Smanu 	if (ps->ps_flags & PS_NO_ACCESS) {
1521c3c545a5Smanu 		const struct vattr *vap;
1522c3c545a5Smanu 
1523c3c545a5Smanu 		vap = puffs_pn_getvap((struct puffs_node *)opc);
1524c3c545a5Smanu 
1525c3c545a5Smanu 		error = puffs_access(IFTOVT(vap->va_mode),
1526c3c545a5Smanu 				     vap->va_mode & ACCESSPERMS,
1527c3c545a5Smanu 				     vap->va_uid, vap->va_gid,
1528c3c545a5Smanu 				     (mode_t)mode, pcr);
1529075ba0e5Smanu 		goto out;
1530c3c545a5Smanu 	}
1531c3c545a5Smanu 
1532c3c545a5Smanu 	/*
1533c3c545a5Smanu 	 * Plain access call
1534c3c545a5Smanu 	 */
15357b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_ACCESS, sizeof(*fai), pcr);
15367b1d1ee6Smanu 	fai = GET_INPAYLOAD(ps, pm, fuse_access_in);
1537c3c545a5Smanu 	fai->mask = 0;
1538c3c545a5Smanu 	fai->mask |= (mode & PUFFS_VREAD) ? R_OK : 0;
1539c3c545a5Smanu 	fai->mask |= (mode & PUFFS_VWRITE) ? W_OK : 0;
1540c3c545a5Smanu 	fai->mask |= (mode & PUFFS_VEXEC) ? X_OK : 0;
15417b1d1ee6Smanu 
15423d6861b5Smanu 	error = xchg_msg(pu, opc, pm, NO_PAYLOAD_REPLY_LEN, wait_reply);
15437b1d1ee6Smanu 
1544c3c545a5Smanu 	ps->ps_destroy_msg(pm);
1545c3c545a5Smanu 
1546c3c545a5Smanu 	/*
1547c3c545a5Smanu 	 * If unimplemented, start over with emulation
1548c3c545a5Smanu 	 */
15497b1d1ee6Smanu 	if (error == ENOSYS) {
15507b1d1ee6Smanu 		ps->ps_flags |= PS_NO_ACCESS;
1551075ba0e5Smanu 		error = perfuse_node_access(pu, opc, mode, pcr);
15523a9497b9Smanu 	}
15537b1d1ee6Smanu 
1554075ba0e5Smanu out:
1555075ba0e5Smanu 	node_rele(opc);
15567b1d1ee6Smanu 	return error;
15577b1d1ee6Smanu }
15587b1d1ee6Smanu 
15597b1d1ee6Smanu int
perfuse_node_getattr(struct puffs_usermount * pu,puffs_cookie_t opc,struct vattr * vap,const struct puffs_cred * pcr)1560e1a2f47fSmatt perfuse_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
1561e1a2f47fSmatt 	struct vattr *vap, const struct puffs_cred *pcr)
15627b1d1ee6Smanu {
156370d81924Smanu 	return perfuse_node_getattr_ttl(pu, opc, vap, pcr, NULL);
156470d81924Smanu }
156570d81924Smanu 
156670d81924Smanu int
perfuse_node_getattr_ttl(struct puffs_usermount * pu,puffs_cookie_t opc,struct vattr * vap,const struct puffs_cred * pcr,struct timespec * va_ttl)156770d81924Smanu perfuse_node_getattr_ttl(struct puffs_usermount *pu, puffs_cookie_t opc,
156870d81924Smanu 	struct vattr *vap, const struct puffs_cred *pcr,
156970d81924Smanu 	struct timespec *va_ttl)
157070d81924Smanu {
15712bc8acd8Smanu 	perfuse_msg_t *pm = NULL;
15727b1d1ee6Smanu 	struct perfuse_state *ps;
15735a6d3e75Smanu 	struct perfuse_node_data *pnd = PERFUSE_NODE_DATA(opc);
15747b1d1ee6Smanu 	struct fuse_getattr_in *fgi;
15757b1d1ee6Smanu 	struct fuse_attr_out *fao;
15762bc8acd8Smanu 	int error = 0;
15777b1d1ee6Smanu 
15784bef47b3Smanu 	if ((pnd->pnd_flags & PND_REMOVED) && !(pnd->pnd_flags & PND_OPEN))
1579374c4263Smanu 		return ENOENT;
1580374c4263Smanu 
1581075ba0e5Smanu 	node_ref(opc);
1582075ba0e5Smanu 
15835a6d3e75Smanu 	/*
15845a6d3e75Smanu 	 * Serialize size access, see comment in perfuse_node_setattr().
15855a6d3e75Smanu 	 */
15865a6d3e75Smanu 	while (pnd->pnd_flags & PND_INRESIZE)
15875a6d3e75Smanu 		requeue_request(pu, opc, PCQ_RESIZE);
15885a6d3e75Smanu 	pnd->pnd_flags |= PND_INRESIZE;
15895a6d3e75Smanu 
15907b1d1ee6Smanu 	ps = puffs_getspecific(pu);
15917b1d1ee6Smanu 
15922bc8acd8Smanu 	/*
15937b1d1ee6Smanu 	 * FUSE_GETATTR_FH must be set in fgi->flags
1594bcf6f2f3Smanu 	 * if we use for fgi->fh
15957b1d1ee6Smanu 	 */
15967b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_GETATTR, sizeof(*fgi), pcr);
15977b1d1ee6Smanu 	fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in);
15987b1d1ee6Smanu 	fgi->getattr_flags = 0;
15997b1d1ee6Smanu 	fgi->dummy = 0;
1600ef486683Smanu 	fgi->fh = FUSE_UNKNOWN_FH;
1601bcf6f2f3Smanu 
1602ef486683Smanu 	if (!PN_ISDIR(opc) && PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN) {
16033a9497b9Smanu 		fgi->fh = perfuse_get_fh(opc, FREAD);
1604bcf6f2f3Smanu 		fgi->getattr_flags |= FUSE_GETATTR_FH;
16053a9497b9Smanu 	}
16067b1d1ee6Smanu 
16075a6d3e75Smanu #ifdef PERFUSE_DEBUG
16085a6d3e75Smanu 	if (perfuse_diagflags & PDF_RESIZE)
160992ad06d8Schristos 		DPRINTF(">> %s %p %" PRIu64 "\n", __func__, (void *)opc,
161092ad06d8Schristos 		    vap->va_size);
16115a6d3e75Smanu #endif
16125a6d3e75Smanu 
16133d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*fao), wait_reply)) != 0)
16147b1d1ee6Smanu 		goto out;
16157b1d1ee6Smanu 
16167b1d1ee6Smanu 	fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
16177b1d1ee6Smanu 
16185a6d3e75Smanu #ifdef PERFUSE_DEBUG
16195a6d3e75Smanu 	if (perfuse_diagflags & PDF_RESIZE)
162092ad06d8Schristos 		DPRINTF("<< %s %p %" PRIu64 " -> %" PRIu64 "\n", __func__,
162192ad06d8Schristos 		    (void *)opc, vap->va_size, fao->attr.size);
16225a6d3e75Smanu #endif
16235a6d3e75Smanu 
16247b1d1ee6Smanu 	/*
16252bc8acd8Smanu 	 * We set birthtime, flags, filerev,vaflags to 0.
16267b1d1ee6Smanu 	 * This seems the best bet, since the information is
16277b1d1ee6Smanu 	 * not available from filesystem.
16287b1d1ee6Smanu 	 */
16297b1d1ee6Smanu 	fuse_attr_to_vap(ps, vap, &fao->attr);
163070d81924Smanu 
163170d81924Smanu 	if (va_ttl != NULL) {
163270d81924Smanu 		va_ttl->tv_sec = fao->attr_valid;
163370d81924Smanu 		va_ttl->tv_nsec = fao->attr_valid_nsec;
163470d81924Smanu 	}
16357b1d1ee6Smanu 
16367b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
1637075ba0e5Smanu 	error = 0;
163871a2942bSmanu out:
16397b1d1ee6Smanu 
16405a6d3e75Smanu 	pnd->pnd_flags &= ~PND_INRESIZE;
1641075ba0e5Smanu 	(void)dequeue_requests(opc, PCQ_RESIZE, DEQUEUE_ALL);
16425a6d3e75Smanu 
1643075ba0e5Smanu 	node_rele(opc);
16447b1d1ee6Smanu 	return error;
16457b1d1ee6Smanu }
16467b1d1ee6Smanu 
16477b1d1ee6Smanu int
perfuse_node_setattr(struct puffs_usermount * pu,puffs_cookie_t opc,const struct vattr * vap,const struct puffs_cred * pcr)1648e1a2f47fSmatt perfuse_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
1649e1a2f47fSmatt 	const struct vattr *vap, const struct puffs_cred *pcr)
16507b1d1ee6Smanu {
165170d81924Smanu 	return perfuse_node_setattr_ttl(pu, opc,
1652f8f196e8Sabs 					__UNCONST(vap), pcr, NULL, 0);
165370d81924Smanu }
165470d81924Smanu 
165570d81924Smanu int
perfuse_node_setattr_ttl(struct puffs_usermount * pu,puffs_cookie_t opc,struct vattr * vap,const struct puffs_cred * pcr,struct timespec * va_ttl,int xflag)165670d81924Smanu perfuse_node_setattr_ttl(struct puffs_usermount *pu, puffs_cookie_t opc,
165770d81924Smanu 	struct vattr *vap, const struct puffs_cred *pcr,
1658075ba0e5Smanu 	struct timespec *va_ttl, int xflag)
165970d81924Smanu {
16607b1d1ee6Smanu 	perfuse_msg_t *pm;
16617b1d1ee6Smanu 	uint64_t fh;
16627b1d1ee6Smanu 	struct perfuse_state *ps;
166328d5b640Smanu 	struct perfuse_node_data *pnd;
16647b1d1ee6Smanu 	struct fuse_setattr_in *fsi;
1665c3c545a5Smanu 	struct fuse_attr_out *fao;
1666374b973dSmanu 	struct vattr *old_va;
1667075ba0e5Smanu 	enum perfuse_xchg_pb_reply reply;
1668bcf6f2f3Smanu 	int error;
16695a6d3e75Smanu #ifdef PERFUSE_DEBUG
16705a6d3e75Smanu 	struct vattr *old_vap;
16715a6d3e75Smanu 	int resize_debug = 0;
16725a6d3e75Smanu #endif
167328d5b640Smanu 	ps = puffs_getspecific(pu);
167428d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
167528d5b640Smanu 
1676374b973dSmanu 	/*
1677374c4263Smanu 	 * The only operation we can do once the file is removed
1678374c4263Smanu 	 * is to resize it, and we can do it only if it is open.
1679bcf6f2f3Smanu 	 * Do not even send the operation to the filesystem: the
1680bcf6f2f3Smanu 	 * file is not there anymore.
1681374c4263Smanu 	 */
1682374c4263Smanu 	if (pnd->pnd_flags & PND_REMOVED) {
1683374c4263Smanu 		if (!(pnd->pnd_flags & PND_OPEN))
1684374c4263Smanu 			return ENOENT;
1685374c4263Smanu 
1686075ba0e5Smanu 		return 0;
1687374c4263Smanu 	}
1688374c4263Smanu 
1689374b973dSmanu 	old_va = puffs_pn_getvap((struct puffs_node *)opc);
1690374b973dSmanu 
1691374b973dSmanu 	/*
1692374b973dSmanu 	 * Check for permission to change size
1693075ba0e5Smanu 	 * It is always allowed if we already have a write file handle
1694374b973dSmanu 	 */
1695075ba0e5Smanu 	if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1696075ba0e5Smanu 	    !(pnd->pnd_flags & PND_WFH) &&
1697075ba0e5Smanu 	    (error = mode_access(opc, pcr, PUFFS_VWRITE)) != 0)
1698c3c545a5Smanu 		return error;
1699574c0972Smanu 
1700574c0972Smanu 	/*
1701574c0972Smanu 	 * Check for permission to change dates
1702574c0972Smanu 	 */
1703075ba0e5Smanu 	if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) ||
1704075ba0e5Smanu 	     (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) &&
1705574c0972Smanu 	    (puffs_access_times(old_va->va_uid, old_va->va_gid,
1706574c0972Smanu 				old_va->va_mode, 0, pcr) != 0))
1707bcfebaffSmanu 		return EPERM;
1708574c0972Smanu 
1709574c0972Smanu 	/*
1710075ba0e5Smanu 	 * Check for permission to change owner and group
1711574c0972Smanu 	 */
1712075ba0e5Smanu 	if (((vap->va_uid != (uid_t)PUFFS_VNOVAL) ||
1713075ba0e5Smanu 	     (vap->va_gid != (gid_t)PUFFS_VNOVAL)) &&
1714075ba0e5Smanu 	    (puffs_access_chown(old_va->va_uid, old_va->va_gid,
1715075ba0e5Smanu 				vap->va_uid, vap->va_gid, pcr)) != 0)
1716bcfebaffSmanu 		return EPERM;
1717bcfebaffSmanu 
1718bcfebaffSmanu 	/*
1719bcfebaffSmanu 	 * Check for sticky bit on non-directory by non root user
1720bcfebaffSmanu 	 */
1721bcfebaffSmanu 	if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1722bcfebaffSmanu 	    (vap->va_mode & S_ISTXT) && (old_va->va_type != VDIR) &&
1723bcfebaffSmanu 	    !puffs_cred_isjuggernaut(pcr))
1724bcfebaffSmanu 		return EFTYPE;
1725574c0972Smanu 
1726075ba0e5Smanu 	/*
1727075ba0e5Smanu 	 * Check for permission to change permissions
1728075ba0e5Smanu 	 */
1729075ba0e5Smanu 	if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1730075ba0e5Smanu 	    (puffs_access_chmod(old_va->va_uid, old_va->va_gid,
1731075ba0e5Smanu 				old_va->va_type, vap->va_mode, pcr)) != 0)
1732bcfebaffSmanu 		return EPERM;
1733075ba0e5Smanu 
1734075ba0e5Smanu 	node_ref(opc);
1735075ba0e5Smanu 
1736ef486683Smanu 	if (!PN_ISDIR(opc) && pnd->pnd_flags & PND_WFH)
1737075ba0e5Smanu 		fh = perfuse_get_fh(opc, FWRITE);
1738075ba0e5Smanu 	else
1739075ba0e5Smanu 		fh = FUSE_UNKNOWN_FH;
1740075ba0e5Smanu 
1741075ba0e5Smanu 	/*
1742075ba0e5Smanu 	 * fchmod() sets mode and fh, and it may carry
1743075ba0e5Smanu 	 * a resize as well. That may break if the
1744075ba0e5Smanu 	 * filesystem does chmod then resize, and fails
1745075ba0e5Smanu 	 * because it does not have permission anymore.
1746075ba0e5Smanu 	 * We work this around by splitting into two setattr.
1747075ba0e5Smanu 	 */
1748075ba0e5Smanu 	if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1749075ba0e5Smanu 	    (vap->va_mode != (mode_t)PUFFS_VNOVAL) &&
1750075ba0e5Smanu 	    (fh != FUSE_UNKNOWN_FH)) {
1751075ba0e5Smanu 		struct vattr resize_va;
1752075ba0e5Smanu 
1753075ba0e5Smanu 		(void)memcpy(&resize_va, vap, sizeof(resize_va));
1754075ba0e5Smanu 		resize_va.va_mode = (mode_t)PUFFS_VNOVAL;
1755075ba0e5Smanu 		if ((error = perfuse_node_setattr_ttl(pu, opc, &resize_va,
1756075ba0e5Smanu 						      pcr, va_ttl, xflag)) != 0)
1757075ba0e5Smanu 			goto out2;
1758075ba0e5Smanu 
1759075ba0e5Smanu 		vap->va_size = (u_quad_t)PUFFS_VNOVAL;
1760075ba0e5Smanu 	}
1761374b973dSmanu 
17627b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr);
17637b1d1ee6Smanu 	fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in);
17647b1d1ee6Smanu 	fsi->valid = 0;
17657b1d1ee6Smanu 
1766bcf6f2f3Smanu 	/*
1767bcf6f2f3Smanu 	 * Get a fh if the node is open for writing
1768bcf6f2f3Smanu 	 */
1769075ba0e5Smanu 	if (fh != FUSE_UNKNOWN_FH) {
17707b1d1ee6Smanu 		fsi->fh = fh;
17717b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_FH;
17727b1d1ee6Smanu 	}
17737b1d1ee6Smanu 
1774075ba0e5Smanu 
1775075ba0e5Smanu 	if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) {
17765a6d3e75Smanu 		fsi->size = vap->va_size;
17777b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_SIZE;
17785a6d3e75Smanu 
17795a6d3e75Smanu 		/*
17805a6d3e75Smanu 		 * Serialize anything that can touch file size
17815a6d3e75Smanu 		 * to avoid reordered GETATTR and SETATTR.
17825a6d3e75Smanu 		 * Out of order SETATTR can report stale size,
17835a6d3e75Smanu 		 * which will cause the kernel to truncate the file.
17846bceb418Smanu 		 * XXX Probably useless now we have a lock on GETATTR
17855a6d3e75Smanu 		 */
17865a6d3e75Smanu 		while (pnd->pnd_flags & PND_INRESIZE)
17875a6d3e75Smanu 			requeue_request(pu, opc, PCQ_RESIZE);
17885a6d3e75Smanu 		pnd->pnd_flags |= PND_INRESIZE;
17897b1d1ee6Smanu 	}
17907b1d1ee6Smanu 
17915b646d77Smanu 	/*
1792575ae722Smanu  	 * When not sending a time field, still fill with
1793575ae722Smanu 	 * current value, as the filesystem may just reset
1794575ae722Smanu 	 * the field to Epoch even if fsi->valid bit is
1795575ae722Smanu 	 * not set (GlusterFS does that).
17965b646d77Smanu  	 */
1797075ba0e5Smanu 	if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) {
1798bcf6f2f3Smanu 		fsi->atime = vap->va_atime.tv_sec;
17995b646d77Smanu 		fsi->atimensec = (uint32_t)vap->va_atime.tv_nsec;
1800575ae722Smanu 		fsi->valid |= FUSE_FATTR_ATIME;
18015b646d77Smanu 	} else {
18025b646d77Smanu 		fsi->atime = old_va->va_atime.tv_sec;
18035b646d77Smanu 		fsi->atimensec = (uint32_t)old_va->va_atime.tv_nsec;
18047b1d1ee6Smanu 	}
18057b1d1ee6Smanu 
1806075ba0e5Smanu 	if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) {
18075b646d77Smanu 		fsi->mtime = vap->va_mtime.tv_sec;
18085b646d77Smanu 		fsi->mtimensec = (uint32_t)vap->va_mtime.tv_nsec;
1809575ae722Smanu 		fsi->valid |= FUSE_FATTR_MTIME;
18105b646d77Smanu 	} else {
18115b646d77Smanu 		fsi->mtime = old_va->va_mtime.tv_sec;
18125b646d77Smanu 		fsi->mtimensec = (uint32_t)old_va->va_mtime.tv_nsec;
18137b1d1ee6Smanu 	}
18147b1d1ee6Smanu 
1815075ba0e5Smanu 	if (vap->va_mode != (mode_t)PUFFS_VNOVAL) {
18167b1d1ee6Smanu 		fsi->mode = vap->va_mode;
18177b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_MODE;
18187b1d1ee6Smanu 	}
18197b1d1ee6Smanu 
1820075ba0e5Smanu 	if (vap->va_uid != (uid_t)PUFFS_VNOVAL) {
18217b1d1ee6Smanu 		fsi->uid = vap->va_uid;
18227b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_UID;
18237b1d1ee6Smanu 	}
18247b1d1ee6Smanu 
1825075ba0e5Smanu 	if (vap->va_gid != (gid_t)PUFFS_VNOVAL) {
18267b1d1ee6Smanu 		fsi->gid = vap->va_gid;
18277b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_GID;
18287b1d1ee6Smanu 	}
18297b1d1ee6Smanu 
183028d5b640Smanu 	if (pnd->pnd_lock_owner != 0) {
183128d5b640Smanu 		fsi->lock_owner = pnd->pnd_lock_owner;
18327b1d1ee6Smanu 		fsi->valid |= FUSE_FATTR_LOCKOWNER;
18337b1d1ee6Smanu 	}
18347b1d1ee6Smanu 
18358abab6b7Smanu #ifndef PUFFS_KFLAG_NOFLUSH_META
1836075ba0e5Smanu 	/*
1837075ba0e5Smanu 	 * ftruncate() sends only va_size, and metadata cache
1838075ba0e5Smanu 	 * flush adds va_atime and va_mtime. Some FUSE
1839075ba0e5Smanu 	 * filesystems will attempt to detect ftruncate by
1840075ba0e5Smanu 	 * checking for FATTR_SIZE being set without
1841075ba0e5Smanu 	 * FATTR_UID|FATTR_GID|FATTR_ATIME|FATTR_MTIME|FATTR_MODE
1842075ba0e5Smanu 	 *
1843075ba0e5Smanu 	 * Try to adapt and remove FATTR_ATIME|FATTR_MTIME
1844075ba0e5Smanu 	 * if we suspect a ftruncate().
1845075ba0e5Smanu 	 */
1846075ba0e5Smanu 	if ((vap->va_size != (u_quad_t)PUFFS_VNOVAL) &&
1847075ba0e5Smanu 	    ((vap->va_mode == (mode_t)PUFFS_VNOVAL) &&
1848075ba0e5Smanu 	     (vap->va_uid == (uid_t)PUFFS_VNOVAL) &&
1849075ba0e5Smanu 	     (vap->va_gid == (gid_t)PUFFS_VNOVAL))) {
1850075ba0e5Smanu 		fsi->atime = 0;
1851075ba0e5Smanu 		fsi->atimensec = 0;
1852075ba0e5Smanu 		fsi->mtime = 0;
1853075ba0e5Smanu 		fsi->mtimensec = 0;
1854075ba0e5Smanu 		fsi->valid &= ~(FUSE_FATTR_ATIME|FUSE_FATTR_MTIME);
1855075ba0e5Smanu 	}
1856075ba0e5Smanu 
1857075ba0e5Smanu 	/*
1858575ae722Smanu 	 * If only atime is changed, discard the operation: it
1859575ae722Smanu 	 * happens after read, and in that case the filesystem
1860a299e660Sandvar 	 * already updated atime. NB: utimes() also change mtime.
1861575ae722Smanu 	 */
1862575ae722Smanu 	if (fsi->valid == FUSE_FATTR_ATIME)
1863575ae722Smanu 		fsi->valid &= ~FUSE_FATTR_ATIME;
18648abab6b7Smanu #endif /* PUFFS_KFLAG_NOFLUSH_META */
1865575ae722Smanu 
1866575ae722Smanu 	/*
1867075ba0e5Smanu 	 * If nothing remain, discard the operation.
1868075ba0e5Smanu 	 */
1869075ba0e5Smanu 	if (!(fsi->valid & (FUSE_FATTR_SIZE|FUSE_FATTR_ATIME|FUSE_FATTR_MTIME|
1870075ba0e5Smanu 			    FUSE_FATTR_MODE|FUSE_FATTR_UID|FUSE_FATTR_GID))) {
1871075ba0e5Smanu 		error = 0;
1872075ba0e5Smanu 		ps->ps_destroy_msg(pm);
1873075ba0e5Smanu 		goto out;
1874075ba0e5Smanu 	}
1875374c4263Smanu 
18765a6d3e75Smanu #ifdef PERFUSE_DEBUG
18775a6d3e75Smanu 	old_vap = puffs_pn_getvap((struct puffs_node *)opc);
18785a6d3e75Smanu 
18795a6d3e75Smanu 	if ((perfuse_diagflags & PDF_RESIZE) &&
18805a6d3e75Smanu 	    (old_vap->va_size != (u_quad_t)PUFFS_VNOVAL)) {
18815a6d3e75Smanu 		resize_debug = 1;
18825a6d3e75Smanu 
188392ad06d8Schristos 		DPRINTF(">> %s %p %" PRIu64 " -> %" PRIu64 "\n", __func__,
188492ad06d8Schristos 		    (void *)opc,
18855a6d3e75Smanu 		    puffs_pn_getvap((struct puffs_node *)opc)->va_size,
18865a6d3e75Smanu 		    fsi->size);
18875a6d3e75Smanu 	}
18885a6d3e75Smanu #endif
18895a6d3e75Smanu 
1890075ba0e5Smanu 	/*
1891075ba0e5Smanu 	 * Do not honour FAF when changing size. How do
1892075ba0e5Smanu 	 * you want such a thing to work?
1893075ba0e5Smanu 	 */
1894075ba0e5Smanu 	reply = wait_reply;
1895075ba0e5Smanu #ifdef PUFFS_SETATTR_FAF
1896075ba0e5Smanu 	if ((xflag & PUFFS_SETATTR_FAF) && !(fsi->valid & FUSE_FATTR_SIZE))
1897075ba0e5Smanu 		reply = no_reply;
1898075ba0e5Smanu #endif
1899075ba0e5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*fao), reply)) != 0)
1900075ba0e5Smanu 		goto out;
1901075ba0e5Smanu 
1902075ba0e5Smanu 	if (reply == no_reply)
1903c3c545a5Smanu 		goto out;
19047b1d1ee6Smanu 
1905c3c545a5Smanu 	/*
1906c3c545a5Smanu 	 * Copy back the new values
1907c3c545a5Smanu 	 */
1908c3c545a5Smanu 	fao = GET_OUTPAYLOAD(ps, pm, fuse_attr_out);
19095a6d3e75Smanu 
19105a6d3e75Smanu #ifdef PERFUSE_DEBUG
19115a6d3e75Smanu 	if (resize_debug)
191292ad06d8Schristos 		DPRINTF("<< %s %p %" PRIu64 " -> %" PRIu64 "\n", __func__,
191392ad06d8Schristos 		    (void *)opc, old_vap->va_size, fao->attr.size);
19145a6d3e75Smanu #endif
19155a6d3e75Smanu 
1916c3c545a5Smanu 	fuse_attr_to_vap(ps, old_va, &fao->attr);
191770d81924Smanu 
191870d81924Smanu 	if (va_ttl != NULL) {
191970d81924Smanu 		va_ttl->tv_sec = fao->attr_valid;
192070d81924Smanu 		va_ttl->tv_nsec = fao->attr_valid_nsec;
192170d81924Smanu 		(void)memcpy(vap, old_va, sizeof(*vap));
192270d81924Smanu 	}
19232bc8acd8Smanu 
19247b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
1925075ba0e5Smanu 	error = 0;
19267b1d1ee6Smanu 
192771a2942bSmanu out:
19285a6d3e75Smanu 	if (pnd->pnd_flags & PND_INRESIZE) {
19295a6d3e75Smanu 		pnd->pnd_flags &= ~PND_INRESIZE;
1930075ba0e5Smanu 		(void)dequeue_requests(opc, PCQ_RESIZE, DEQUEUE_ALL);
19315a6d3e75Smanu 	}
19325a6d3e75Smanu 
1933075ba0e5Smanu out2:
1934075ba0e5Smanu 	node_rele(opc);
19357b1d1ee6Smanu 	return error;
19367b1d1ee6Smanu }
19377b1d1ee6Smanu 
19387b1d1ee6Smanu int
perfuse_node_poll(struct puffs_usermount * pu,puffs_cookie_t opc,int * events)1939e1a2f47fSmatt perfuse_node_poll(struct puffs_usermount *pu, puffs_cookie_t opc, int *events)
19407b1d1ee6Smanu {
19417b1d1ee6Smanu 	struct perfuse_state *ps;
19427b1d1ee6Smanu 	perfuse_msg_t *pm;
19437b1d1ee6Smanu 	struct fuse_poll_in *fpi;
19447b1d1ee6Smanu 	struct fuse_poll_out *fpo;
19457b1d1ee6Smanu 	int error;
19467b1d1ee6Smanu 
1947075ba0e5Smanu 	node_ref(opc);
19487b1d1ee6Smanu 	ps = puffs_getspecific(pu);
19497b1d1ee6Smanu 	/*
19507b1d1ee6Smanu 	 * kh is set if FUSE_POLL_SCHEDULE_NOTIFY is set.
1951fb0fa57fSmanu 	 *
1952fb0fa57fSmanu 	 * XXX ps_new_msg() is called with NULL creds, which will
1953fb0fa57fSmanu 	 * be interpreted as FUSE superuser. We have no way to
1954fb0fa57fSmanu 	 * know the requesting process' credential, but since poll
1955fb0fa57fSmanu 	 * is supposed to operate on a file that has been open,
1956fb0fa57fSmanu 	 * permission should have already been checked at open time.
1957fb0fa57fSmanu 	 * That still may breaks on filesystems that provides odd
1958fb0fa57fSmanu 	 * semantics.
19597b1d1ee6Smanu  	 */
19607b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL);
19617b1d1ee6Smanu 	fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in);
1962ef486683Smanu 	fpi->fh = PN_ISDIR(opc) ? FUSE_UNKNOWN_FH : perfuse_get_fh(opc, FREAD);
19637b1d1ee6Smanu 	fpi->kh = 0;
19647b1d1ee6Smanu 	fpi->flags = 0;
19657b1d1ee6Smanu 
1966516b1c90Smanu #ifdef PERFUSE_DEBUG
1967516b1c90Smanu 	if (perfuse_diagflags & PDF_FH)
196871a2942bSmanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", "
196971a2942bSmanu 			"fh = 0x%"PRIx64"\n", __func__, (void *)opc,
19702bc8acd8Smanu 			PERFUSE_NODE_DATA(opc)->pnd_nodeid, fpi->fh);
1971516b1c90Smanu #endif
19723d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, sizeof(*fpo), wait_reply)) != 0)
1973075ba0e5Smanu 		goto out;
19747b1d1ee6Smanu 
19757b1d1ee6Smanu 	fpo = GET_OUTPAYLOAD(ps, pm, fuse_poll_out);
19767b1d1ee6Smanu 	*events = fpo->revents;
197771a2942bSmanu 
19787b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
1979075ba0e5Smanu 	error = 0;
19807b1d1ee6Smanu 
1981075ba0e5Smanu out:
1982075ba0e5Smanu 	node_rele(opc);
1983075ba0e5Smanu 	return error;
19847b1d1ee6Smanu }
19857b1d1ee6Smanu 
19867b1d1ee6Smanu /* ARGSUSED2 */
19877b1d1ee6Smanu int
perfuse_node_fsync(struct puffs_usermount * pu,puffs_cookie_t opc,const struct puffs_cred * pcr,int flags,off_t offlo,off_t offhi)1988e1a2f47fSmatt perfuse_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
1989e1a2f47fSmatt 	const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi)
19907b1d1ee6Smanu {
199117ce0ff6Smanu 	int op;
19927b1d1ee6Smanu 	perfuse_msg_t *pm;
19937b1d1ee6Smanu 	struct perfuse_state *ps;
1994516b1c90Smanu 	struct perfuse_node_data *pnd;
19957b1d1ee6Smanu 	struct fuse_fsync_in *ffi;
1996516b1c90Smanu 	uint64_t fh;
1997075ba0e5Smanu 	int error = 0;
19987b1d1ee6Smanu 
1999516b1c90Smanu 	pm = NULL;
20007b1d1ee6Smanu 	ps = puffs_getspecific(pu);
2001bcf6f2f3Smanu 	pnd = PERFUSE_NODE_DATA(opc);
2002bcf6f2f3Smanu 
2003bcf6f2f3Smanu 	/*
2004bcf6f2f3Smanu 	 * No need to sync a removed node
2005bcf6f2f3Smanu 	 */
2006bcf6f2f3Smanu 	if (pnd->pnd_flags & PND_REMOVED)
2007bcf6f2f3Smanu 		return 0;
2008bcf6f2f3Smanu 
2009bcf6f2f3Smanu 	/*
2010bcf6f2f3Smanu 	 * We do not sync closed files. They have been
2011bcf6f2f3Smanu 	 * sync at inactive time already.
2012bcf6f2f3Smanu 	 */
2013bcf6f2f3Smanu 	if (!(pnd->pnd_flags & PND_OPEN))
2014bcf6f2f3Smanu 		return 0;
201517ce0ff6Smanu 
2016075ba0e5Smanu 	node_ref(opc);
2017075ba0e5Smanu 
2018ef486683Smanu 	if (PN_ISDIR(opc))
201917ce0ff6Smanu 		op = FUSE_FSYNCDIR;
202017ce0ff6Smanu 	else 		/* VREG but also other types such as VLNK */
202117ce0ff6Smanu 		op = FUSE_FSYNC;
20227b1d1ee6Smanu 
20237b1d1ee6Smanu 	/*
2024516b1c90Smanu 	 * Do not sync if there are no change to sync
202517ce0ff6Smanu 	 * XXX remove that test on files if we implement mmap
2026516b1c90Smanu 	 */
2027516b1c90Smanu #ifdef PERFUSE_DEBUG
2028516b1c90Smanu 	if (perfuse_diagflags & PDF_SYNC)
2029516b1c90Smanu 		DPRINTF("%s: TEST opc = %p, file = \"%s\" is %sdirty\n",
2030075ba0e5Smanu 			__func__, (void*)opc, perfuse_node_path(ps, opc),
2031516b1c90Smanu 			pnd->pnd_flags & PND_DIRTY ? "" : "not ");
2032516b1c90Smanu #endif
2033516b1c90Smanu 	if (!(pnd->pnd_flags & PND_DIRTY))
2034075ba0e5Smanu 		goto out;
2035516b1c90Smanu 
2036f7174423Smanu 	/*
2037f7174423Smanu 	 * It seems NetBSD can call fsync without open first
2038f7174423Smanu 	 * glusterfs complain in such a situation:
2039f7174423Smanu 	 * "FSYNC() ERR => -1 (Invalid argument)"
2040f7174423Smanu 	 * The file will be closed at inactive time.
2041f7174423Smanu 	 *
2042f7174423Smanu 	 * We open the directory for reading in order to sync.
2043f7174423Smanu 	 * This sounds rather counterintuitive, but it works.
2044f7174423Smanu 	 */
2045f7174423Smanu 	if (!(pnd->pnd_flags & PND_WFH)) {
2046f7174423Smanu 		if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
2047f7174423Smanu 			goto out;
2048f7174423Smanu 	}
2049f7174423Smanu 
2050bcf6f2f3Smanu 	if (op == FUSE_FSYNCDIR)
2051bcf6f2f3Smanu 		fh = perfuse_get_fh(opc, FREAD);
2052bcf6f2f3Smanu 	else
205328d5b640Smanu 		fh = perfuse_get_fh(opc, FWRITE);
2054516b1c90Smanu 
2055516b1c90Smanu 	/*
20567b1d1ee6Smanu 	 * If fsync_flags  is set, meta data should not be flushed.
20577b1d1ee6Smanu 	 */
2058fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, op, sizeof(*ffi), pcr);
20597b1d1ee6Smanu 	ffi = GET_INPAYLOAD(ps, pm, fuse_fsync_in);
2060516b1c90Smanu 	ffi->fh = fh;
20617b1d1ee6Smanu 	ffi->fsync_flags = (flags & FFILESYNC) ? 0 : 1;
20627b1d1ee6Smanu 
2063516b1c90Smanu #ifdef PERFUSE_DEBUG
2064516b1c90Smanu 	if (perfuse_diagflags & PDF_FH)
20652bc8acd8Smanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", fh = 0x%"PRIx64"\n",
2066516b1c90Smanu 			__func__, (void *)opc,
20672bc8acd8Smanu 			PERFUSE_NODE_DATA(opc)->pnd_nodeid, ffi->fh);
2068516b1c90Smanu #endif
2069516b1c90Smanu 
20703d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm,
20713d6861b5Smanu 			      NO_PAYLOAD_REPLY_LEN, wait_reply)) != 0)
20727b1d1ee6Smanu 		goto out;
20737b1d1ee6Smanu 
20747b1d1ee6Smanu 	/*
2075516b1c90Smanu 	 * No reply beyond fuse_out_header: nothing to do on success
2076516b1c90Smanu 	 * just clear the dirty flag
20777b1d1ee6Smanu 	 */
2078516b1c90Smanu 	pnd->pnd_flags &= ~PND_DIRTY;
2079516b1c90Smanu 
2080516b1c90Smanu #ifdef PERFUSE_DEBUG
2081516b1c90Smanu 	if (perfuse_diagflags & PDF_SYNC)
2082516b1c90Smanu 		DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n",
2083075ba0e5Smanu 			__func__, (void*)opc, perfuse_node_path(ps, opc));
2084516b1c90Smanu #endif
2085516b1c90Smanu 
208671a2942bSmanu 	ps->ps_destroy_msg(pm);
2087075ba0e5Smanu 	error = 0;
208871a2942bSmanu 
20897b1d1ee6Smanu out:
209017ce0ff6Smanu 	/*
209117ce0ff6Smanu 	 * ENOSYS is not returned to kernel,
209217ce0ff6Smanu 	 */
20937b1d1ee6Smanu 	if (error == ENOSYS)
209417ce0ff6Smanu 		error = 0;
20957b1d1ee6Smanu 
2096075ba0e5Smanu 	node_rele(opc);
20977b1d1ee6Smanu 	return error;
20987b1d1ee6Smanu }
20997b1d1ee6Smanu 
21007b1d1ee6Smanu int
perfuse_node_remove(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)2101e1a2f47fSmatt perfuse_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
2102e1a2f47fSmatt 	puffs_cookie_t targ, const struct puffs_cn *pcn)
21037b1d1ee6Smanu {
21047b1d1ee6Smanu 	struct perfuse_state *ps;
210528d5b640Smanu 	struct perfuse_node_data *pnd;
210628d5b640Smanu 	perfuse_msg_t *pm;
21077b1d1ee6Smanu 	char *path;
21087b1d1ee6Smanu 	const char *name;
21097b1d1ee6Smanu 	size_t len;
21107b1d1ee6Smanu 	int error;
21117b1d1ee6Smanu 
211228d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
211328d5b640Smanu 
2114f7174423Smanu 	if ((pnd->pnd_flags & PND_REMOVED) ||
2115f7174423Smanu 	    (PERFUSE_NODE_DATA(targ)->pnd_flags & PND_REMOVED))
2116f7174423Smanu 		return ENOENT;
2117f7174423Smanu 
2118bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
21197b1d1ee6Smanu 	if (targ == NULL)
21207b1d1ee6Smanu 		DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__);
21217b1d1ee6Smanu 
2122bcf6f2f3Smanu 	if (perfuse_diagflags & (PDF_FH|PDF_FILENAME))
2123bcf6f2f3Smanu 		DPRINTF("%s: opc = %p, remove opc = %p, file = \"%s\"\n",
2124f7174423Smanu 			__func__, (void *)opc, (void *)targ, pcn->pcn_name);
2125bcf6f2f3Smanu #endif
2126075ba0e5Smanu 	node_ref(opc);
2127075ba0e5Smanu 	node_ref(targ);
2128075ba0e5Smanu 
2129bcf6f2f3Smanu 	/*
2130f7174423Smanu 	 * Await for all operations on the deleted node to drain,
2131f7174423Smanu 	 * as the filesystem may be confused to have it deleted
2132f7174423Smanu 	 * during a getattr
2133bcf6f2f3Smanu 	 */
2134075ba0e5Smanu 	while (PERFUSE_NODE_DATA(targ)->pnd_inxchg)
2135f7174423Smanu 		requeue_request(pu, targ, PCQ_AFTERXCHG);
2136bcf6f2f3Smanu 
21372ff0ea03Smanu 	ps = puffs_getspecific(pu);
21382ff0ea03Smanu 	pnd = PERFUSE_NODE_DATA(opc);
21392ff0ea03Smanu 	name = pcn->pcn_name;
21402ff0ea03Smanu 	len = pcn->pcn_namelen + 1;
21412ff0ea03Smanu 
2142fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_UNLINK, len, pcn->pcn_cred);
21437b1d1ee6Smanu 	path = _GET_INPAYLOAD(ps, pm, char *);
21447b1d1ee6Smanu 	(void)strlcpy(path, name, len);
21457b1d1ee6Smanu 
21463d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
2147075ba0e5Smanu 		goto out;
21487b1d1ee6Smanu 
2149075ba0e5Smanu 	perfuse_cache_flush(targ);
2150f7174423Smanu 	PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED;
2151075ba0e5Smanu 
2152f7174423Smanu 	if (!(PERFUSE_NODE_DATA(targ)->pnd_flags & PND_OPEN))
21537b1d1ee6Smanu 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
21547b1d1ee6Smanu 
21557b1d1ee6Smanu 	/*
215617ce0ff6Smanu 	 * The parent directory needs a sync
215717ce0ff6Smanu 	 */
215817ce0ff6Smanu 	PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
2159bcf6f2f3Smanu 
2160bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
2161bcf6f2f3Smanu 	if (perfuse_diagflags & PDF_FILENAME)
21622bc8acd8Smanu 		DPRINTF("%s: remove nodeid = 0x%"PRIx64" file = \"%s\"\n",
21632bc8acd8Smanu 			__func__, PERFUSE_NODE_DATA(targ)->pnd_nodeid,
2164f7174423Smanu 			pcn->pcn_name);
2165bcf6f2f3Smanu #endif
21667b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
2167075ba0e5Smanu 	error = 0;
21687b1d1ee6Smanu 
2169075ba0e5Smanu out:
2170075ba0e5Smanu 	node_rele(opc);
2171075ba0e5Smanu 	node_rele(targ);
2172075ba0e5Smanu 	return error;
21737b1d1ee6Smanu }
21747b1d1ee6Smanu 
21757b1d1ee6Smanu int
perfuse_node_link(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)2176e1a2f47fSmatt perfuse_node_link(struct puffs_usermount *pu, puffs_cookie_t opc,
2177e1a2f47fSmatt 	puffs_cookie_t targ, const struct puffs_cn *pcn)
21787b1d1ee6Smanu {
21797b1d1ee6Smanu 	struct perfuse_state *ps;
21807b1d1ee6Smanu 	perfuse_msg_t *pm;
21817b1d1ee6Smanu 	const char *name;
21827b1d1ee6Smanu 	size_t len;
21837b1d1ee6Smanu 	struct puffs_node *pn;
21847b1d1ee6Smanu 	struct fuse_link_in *fli;
21857b1d1ee6Smanu 	int error;
21867b1d1ee6Smanu 
2187f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
2188f7174423Smanu 		return ENOENT;
2189f7174423Smanu 
2190075ba0e5Smanu 	node_ref(opc);
2191075ba0e5Smanu 	node_ref(targ);
21927b1d1ee6Smanu 	ps = puffs_getspecific(pu);
21937b1d1ee6Smanu 	pn = (struct puffs_node *)targ;
2194f7174423Smanu 	name = pcn->pcn_name;
2195f7174423Smanu 	len =  sizeof(*fli) + pcn->pcn_namelen + 1;
21967b1d1ee6Smanu 
2197fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_LINK, len, pcn->pcn_cred);
21987b1d1ee6Smanu 	fli = GET_INPAYLOAD(ps, pm, fuse_link_in);
21992bc8acd8Smanu 	fli->oldnodeid = PERFUSE_NODE_DATA(pn)->pnd_nodeid;
22007b1d1ee6Smanu 	(void)strlcpy((char *)(void *)(fli + 1), name, len - sizeof(*fli));
22017b1d1ee6Smanu 
220271a2942bSmanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
2203075ba0e5Smanu 		goto out;
22047b1d1ee6Smanu 
22057b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
2206075ba0e5Smanu 	error = 0;
22077b1d1ee6Smanu 
2208075ba0e5Smanu out:
2209075ba0e5Smanu 	node_rele(opc);
2210075ba0e5Smanu 	node_rele(targ);
2211075ba0e5Smanu 	return error;
22127b1d1ee6Smanu }
22137b1d1ee6Smanu 
22147b1d1ee6Smanu int
perfuse_node_rename(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t src,const struct puffs_cn * pcn_src,puffs_cookie_t targ_dir,puffs_cookie_t targ,const struct puffs_cn * pcn_targ)2215e1a2f47fSmatt perfuse_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc,
2216e1a2f47fSmatt 	puffs_cookie_t src, const struct puffs_cn *pcn_src,
2217e1a2f47fSmatt 	puffs_cookie_t targ_dir, puffs_cookie_t targ,
2218e1a2f47fSmatt 	const struct puffs_cn *pcn_targ)
22197b1d1ee6Smanu {
22207b1d1ee6Smanu 	struct perfuse_state *ps;
2221075ba0e5Smanu 	struct perfuse_node_data *dstdir_pnd;
22227b1d1ee6Smanu 	perfuse_msg_t *pm;
22237b1d1ee6Smanu 	struct fuse_rename_in *fri;
22247b1d1ee6Smanu 	const char *newname;
22257b1d1ee6Smanu 	const char *oldname;
22267b1d1ee6Smanu 	char *np;
22277b1d1ee6Smanu 	int error;
22287b1d1ee6Smanu 	size_t len;
22297b1d1ee6Smanu 	size_t newname_len;
22307b1d1ee6Smanu 	size_t oldname_len;
22317b1d1ee6Smanu 
2232f7174423Smanu 	if ((PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED) ||
2233f7174423Smanu 	    (PERFUSE_NODE_DATA(src)->pnd_flags & PND_REMOVED) ||
2234f7174423Smanu 	    (PERFUSE_NODE_DATA(targ_dir)->pnd_flags & PND_REMOVED))
2235f7174423Smanu 		return ENOENT;
2236f7174423Smanu 
2237075ba0e5Smanu 	node_ref(opc);
2238075ba0e5Smanu 	node_ref(src);
2239075ba0e5Smanu 
2240f7174423Smanu 	/*
2241f7174423Smanu 	 * Await for all operations on the deleted node to drain,
2242f7174423Smanu 	 * as the filesystem may be confused to have it deleted
2243f7174423Smanu 	 * during a getattr
2244f7174423Smanu 	 */
2245f7174423Smanu 	if ((struct puffs_node *)targ != NULL) {
2246075ba0e5Smanu 		node_ref(targ);
2247075ba0e5Smanu 		while (PERFUSE_NODE_DATA(targ)->pnd_inxchg)
2248f7174423Smanu 			requeue_request(pu, targ, PCQ_AFTERXCHG);
2249f7174423Smanu 	} else {
2250075ba0e5Smanu 		while (PERFUSE_NODE_DATA(src)->pnd_inxchg)
2251f7174423Smanu 			requeue_request(pu, src, PCQ_AFTERXCHG);
2252f7174423Smanu 	}
2253f7174423Smanu 
22547b1d1ee6Smanu 	ps = puffs_getspecific(pu);
2255f7174423Smanu 	newname =  pcn_targ->pcn_name;
2256f7174423Smanu 	newname_len = pcn_targ->pcn_namelen + 1;
2257f7174423Smanu 	oldname =  pcn_src->pcn_name;
2258f7174423Smanu 	oldname_len = pcn_src->pcn_namelen + 1;
22597b1d1ee6Smanu 
22607b1d1ee6Smanu 	len = sizeof(*fri) + oldname_len + newname_len;
2261fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_RENAME, len, pcn_targ->pcn_cred);
22627b1d1ee6Smanu 	fri = GET_INPAYLOAD(ps, pm, fuse_rename_in);
22632bc8acd8Smanu 	fri->newdir = PERFUSE_NODE_DATA(targ_dir)->pnd_nodeid;
22647b1d1ee6Smanu 	np = (char *)(void *)(fri + 1);
22657b1d1ee6Smanu 	(void)strlcpy(np, oldname, oldname_len);
22667b1d1ee6Smanu 	np += oldname_len;
22677b1d1ee6Smanu 	(void)strlcpy(np, newname, newname_len);
22687b1d1ee6Smanu 
22693d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
2270075ba0e5Smanu 		goto out;
22717b1d1ee6Smanu 
2272f7174423Smanu 
2273075ba0e5Smanu 	/*
2274075ba0e5Smanu 	 * Record new parent nodeid
2275075ba0e5Smanu 	 */
22762ff0ea03Smanu 	dstdir_pnd = PERFUSE_NODE_DATA(targ_dir);
2277075ba0e5Smanu 	PERFUSE_NODE_DATA(src)->pnd_parent_nodeid = dstdir_pnd->pnd_nodeid;
2278f7174423Smanu 
2279075ba0e5Smanu 	if (opc != targ_dir)
2280075ba0e5Smanu 		dstdir_pnd->pnd_flags |= PND_DIRTY;
22812ff0ea03Smanu 
2282075ba0e5Smanu 	if (strcmp(newname, "..") != 0)
2283075ba0e5Smanu 		(void)strlcpy(PERFUSE_NODE_DATA(src)->pnd_name,
2284075ba0e5Smanu 		    newname, MAXPATHLEN);
2285075ba0e5Smanu 	else
2286075ba0e5Smanu 		PERFUSE_NODE_DATA(src)->pnd_name[0] = 0; /* forget name */
22872ff0ea03Smanu 
22882ff0ea03Smanu 	PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
22892ff0ea03Smanu 
2290075ba0e5Smanu 	if ((struct puffs_node *)targ != NULL) {
2291075ba0e5Smanu 		perfuse_cache_flush(targ);
22922ff0ea03Smanu 		PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED;
2293075ba0e5Smanu 	}
22942ff0ea03Smanu 
2295bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
2296bcf6f2f3Smanu 	if (perfuse_diagflags & PDF_FILENAME)
22972bc8acd8Smanu 		DPRINTF("%s: nodeid = 0x%"PRIx64" file = \"%s\" renamed \"%s\" "
22982bc8acd8Smanu 			"nodeid = 0x%"PRIx64" -> nodeid = 0x%"PRIx64" \"%s\"\n",
22992bc8acd8Smanu 	 		__func__, PERFUSE_NODE_DATA(src)->pnd_nodeid,
23002ff0ea03Smanu 			pcn_src->pcn_name, pcn_targ->pcn_name,
23012bc8acd8Smanu 			PERFUSE_NODE_DATA(opc)->pnd_nodeid,
23022bc8acd8Smanu 			PERFUSE_NODE_DATA(targ_dir)->pnd_nodeid,
2303075ba0e5Smanu 			perfuse_node_path(ps, targ_dir));
2304bcf6f2f3Smanu #endif
2305bcf6f2f3Smanu 
23067b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
2307075ba0e5Smanu 	error = 0;
23087b1d1ee6Smanu 
2309075ba0e5Smanu out:
2310075ba0e5Smanu 	node_rele(opc);
2311075ba0e5Smanu 	node_rele(src);
2312075ba0e5Smanu 	if ((struct puffs_node *)targ != NULL)
2313075ba0e5Smanu 		node_rele(targ);
2314075ba0e5Smanu 
2315075ba0e5Smanu 	return error;
23167b1d1ee6Smanu }
23177b1d1ee6Smanu 
23187b1d1ee6Smanu int
perfuse_node_mkdir(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * vap)2319e1a2f47fSmatt perfuse_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
2320e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
2321e1a2f47fSmatt 	const struct vattr *vap)
23227b1d1ee6Smanu {
23237b1d1ee6Smanu 	struct perfuse_state *ps;
23247b1d1ee6Smanu 	perfuse_msg_t *pm;
23257b1d1ee6Smanu 	struct fuse_mkdir_in *fmi;
23267b1d1ee6Smanu 	const char *path;
23277b1d1ee6Smanu 	size_t len;
2328075ba0e5Smanu 	int error;
23297b1d1ee6Smanu 
2330f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
2331f7174423Smanu 		return ENOENT;
2332f7174423Smanu 
2333075ba0e5Smanu 	node_ref(opc);
23347b1d1ee6Smanu 	ps = puffs_getspecific(pu);
2335f7174423Smanu 	path = pcn->pcn_name;
2336f7174423Smanu 	len = sizeof(*fmi) + pcn->pcn_namelen + 1;
23377b1d1ee6Smanu 
2338fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_MKDIR, len, pcn->pcn_cred);
23397b1d1ee6Smanu 	fmi = GET_INPAYLOAD(ps, pm, fuse_mkdir_in);
2340374c4263Smanu 	fmi->mode = vap->va_mode;
2341374c4263Smanu 	fmi->umask = 0; 	/* Seems unused by libfuse? */
23427b1d1ee6Smanu 	(void)strlcpy((char *)(void *)(fmi + 1), path, len - sizeof(*fmi));
23437b1d1ee6Smanu 
2344075ba0e5Smanu 	error = node_mk_common(pu, opc, pni, pcn, pm);
2345075ba0e5Smanu 
2346075ba0e5Smanu 	node_rele(opc);
2347075ba0e5Smanu 	return error;
23487b1d1ee6Smanu }
23497b1d1ee6Smanu 
23507b1d1ee6Smanu 
23517b1d1ee6Smanu int
perfuse_node_rmdir(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)2352e1a2f47fSmatt perfuse_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
2353e1a2f47fSmatt 	puffs_cookie_t targ, const struct puffs_cn *pcn)
23547b1d1ee6Smanu {
23557b1d1ee6Smanu 	struct perfuse_state *ps;
235628d5b640Smanu 	struct perfuse_node_data *pnd;
23577b1d1ee6Smanu 	perfuse_msg_t *pm;
23587b1d1ee6Smanu 	char *path;
23597b1d1ee6Smanu 	const char *name;
23607b1d1ee6Smanu 	size_t len;
23617b1d1ee6Smanu 	int error;
23627b1d1ee6Smanu 
236328d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
236455557eb9Smanu 
236555557eb9Smanu 	if ((pnd->pnd_flags & PND_REMOVED) ||
236655557eb9Smanu 	    (PERFUSE_NODE_DATA(targ)->pnd_flags & PND_REMOVED))
2367f7174423Smanu 		return ENOENT;
2368f7174423Smanu 
23694f16513eSmanu 	/*
2370de0a2812Sandvar 	 * Attempt to rmdir dir/.. should raise ENOTEMPTY
23714f16513eSmanu 	 */
23724f16513eSmanu 	if (PERFUSE_NODE_DATA(targ)->pnd_nodeid == pnd->pnd_parent_nodeid)
23734f16513eSmanu 		return ENOTEMPTY;
23744f16513eSmanu 
2375075ba0e5Smanu 	node_ref(opc);
2376075ba0e5Smanu 	node_ref(targ);
2377075ba0e5Smanu 
2378bcf6f2f3Smanu 	/*
2379f7174423Smanu 	 * Await for all operations on the deleted node to drain,
2380f7174423Smanu 	 * as the filesystem may be confused to have it deleted
2381f7174423Smanu 	 * during a getattr
2382bcf6f2f3Smanu 	 */
2383075ba0e5Smanu 	while (PERFUSE_NODE_DATA(targ)->pnd_inxchg)
2384f7174423Smanu 		requeue_request(pu, targ, PCQ_AFTERXCHG);
2385f7174423Smanu 
2386f7174423Smanu 	ps = puffs_getspecific(pu);
2387f7174423Smanu 	name = pcn->pcn_name;
2388f7174423Smanu 	len = pcn->pcn_namelen + 1;
2389bcf6f2f3Smanu 
2390fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_RMDIR, len, pcn->pcn_cred);
23917b1d1ee6Smanu 	path = _GET_INPAYLOAD(ps, pm, char *);
23927b1d1ee6Smanu 	(void)strlcpy(path, name, len);
23937b1d1ee6Smanu 
23943d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
2395075ba0e5Smanu 		goto out;
23967b1d1ee6Smanu 
2397075ba0e5Smanu 	perfuse_cache_flush(targ);
2398f7174423Smanu 	PERFUSE_NODE_DATA(targ)->pnd_flags |= PND_REMOVED;
2399075ba0e5Smanu 
2400f7174423Smanu 	if (!(PERFUSE_NODE_DATA(targ)->pnd_flags & PND_OPEN))
24017b1d1ee6Smanu 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
2402516b1c90Smanu 
240317ce0ff6Smanu 	/*
240417ce0ff6Smanu 	 * The parent directory needs a sync
240517ce0ff6Smanu 	 */
240617ce0ff6Smanu 	PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY;
2407bcf6f2f3Smanu 
2408bcf6f2f3Smanu #ifdef PERFUSE_DEBUG
2409bcf6f2f3Smanu 	if (perfuse_diagflags & PDF_FILENAME)
24102bc8acd8Smanu 		DPRINTF("%s: remove nodeid = 0x%"PRIx64" file = \"%s\"\n",
24112bc8acd8Smanu 			__func__, PERFUSE_NODE_DATA(targ)->pnd_nodeid,
2412075ba0e5Smanu 			perfuse_node_path(ps, targ));
2413bcf6f2f3Smanu #endif
24147b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
2415075ba0e5Smanu 	error = 0;
24167b1d1ee6Smanu 
2417075ba0e5Smanu out:
2418075ba0e5Smanu 	node_rele(opc);
2419075ba0e5Smanu 	node_rele(targ);
2420075ba0e5Smanu 	return error;
24217b1d1ee6Smanu }
24227b1d1ee6Smanu 
24237b1d1ee6Smanu /* vap is unused */
24247b1d1ee6Smanu /* ARGSUSED4 */
24257b1d1ee6Smanu int
perfuse_node_symlink(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn_src,const struct vattr * vap,const char * link_target)2426e1a2f47fSmatt perfuse_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
2427e1a2f47fSmatt 	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
2428e1a2f47fSmatt 	const struct vattr *vap, const char *link_target)
24297b1d1ee6Smanu {
24307b1d1ee6Smanu 	struct perfuse_state *ps;
24317b1d1ee6Smanu 	perfuse_msg_t *pm;
24327b1d1ee6Smanu 	char *np;
24337b1d1ee6Smanu 	const char *path;
24347b1d1ee6Smanu 	size_t path_len;
24357b1d1ee6Smanu 	size_t linkname_len;
24367b1d1ee6Smanu 	size_t len;
2437075ba0e5Smanu 	int error;
24387b1d1ee6Smanu 
2439f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
2440f7174423Smanu 		return ENOENT;
2441f7174423Smanu 
2442075ba0e5Smanu 	node_ref(opc);
24437b1d1ee6Smanu 	ps = puffs_getspecific(pu);
2444f7174423Smanu 	path = pcn_src->pcn_name;
2445f7174423Smanu 	path_len = pcn_src->pcn_namelen + 1;
24467b1d1ee6Smanu 	linkname_len = strlen(link_target) + 1;
24477b1d1ee6Smanu 	len = path_len + linkname_len;
24487b1d1ee6Smanu 
2449fb0fa57fSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_SYMLINK, len, pcn_src->pcn_cred);
24507b1d1ee6Smanu 	np = _GET_INPAYLOAD(ps, pm, char *);
24517b1d1ee6Smanu 	(void)strlcpy(np, path, path_len);
24527b1d1ee6Smanu 	np += path_len;
24537b1d1ee6Smanu 	(void)strlcpy(np, link_target, linkname_len);
24547b1d1ee6Smanu 
2455075ba0e5Smanu 	error = node_mk_common(pu, opc, pni, pcn_src, pm);
2456075ba0e5Smanu 
2457075ba0e5Smanu 	node_rele(opc);
2458075ba0e5Smanu 	return error;
24597b1d1ee6Smanu }
24607b1d1ee6Smanu 
24616e1ab723Smanu /* ARGSUSED4 */
24627b1d1ee6Smanu int
perfuse_node_readdir(struct puffs_usermount * pu,puffs_cookie_t opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)2463e1a2f47fSmatt perfuse_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
2464e1a2f47fSmatt 	struct dirent *dent, off_t *readoff, size_t *reslen,
2465e1a2f47fSmatt 	const struct puffs_cred *pcr, int *eofflag, off_t *cookies,
2466e1a2f47fSmatt 	size_t *ncookies)
24677b1d1ee6Smanu {
24687b1d1ee6Smanu 	perfuse_msg_t *pm;
24697b1d1ee6Smanu 	uint64_t fh;
24707b1d1ee6Smanu 	struct perfuse_state *ps;
24717b1d1ee6Smanu 	struct perfuse_node_data *pnd;
24727b1d1ee6Smanu 	struct fuse_read_in *fri;
24737b1d1ee6Smanu 	struct fuse_out_header *foh;
24747b1d1ee6Smanu 	struct fuse_dirent *fd;
24757b1d1ee6Smanu 	size_t foh_len;
24767b1d1ee6Smanu 	int error;
24773d6861b5Smanu 	size_t fd_maxlen;
24787b1d1ee6Smanu 
24797b1d1ee6Smanu 	error = 0;
2480075ba0e5Smanu 	node_ref(opc);
24817b1d1ee6Smanu 	ps = puffs_getspecific(pu);
24827b1d1ee6Smanu 
24837b1d1ee6Smanu 	/*
24847b1d1ee6Smanu 	 * readdir state is kept at node level, and several readdir
24857b1d1ee6Smanu 	 * requests can be issued at the same time on the same node.
24867b1d1ee6Smanu 	 * We need to queue requests so that only one is in readdir
24877b1d1ee6Smanu 	 * code at the same time.
24887b1d1ee6Smanu 	 */
24897b1d1ee6Smanu 	pnd = PERFUSE_NODE_DATA(opc);
24907b1d1ee6Smanu 	while (pnd->pnd_flags & PND_INREADDIR)
24917b1d1ee6Smanu 		requeue_request(pu, opc, PCQ_READDIR);
24927b1d1ee6Smanu 	pnd->pnd_flags |= PND_INREADDIR;
24937b1d1ee6Smanu 
24947b1d1ee6Smanu #ifdef PERFUSE_DEBUG
24957b1d1ee6Smanu 	if (perfuse_diagflags & PDF_READDIR)
24967b1d1ee6Smanu 		DPRINTF("%s: READDIR opc = %p enter critical section\n",
24977b1d1ee6Smanu 			__func__, (void *)opc);
24987b1d1ee6Smanu #endif
24997b1d1ee6Smanu 	/*
25006e1ab723Smanu 	 * Re-initialize pnd->pnd_fd_cookie on the first readdir for a node
25016e1ab723Smanu 	 */
25026e1ab723Smanu 	if (*readoff == 0)
25036e1ab723Smanu 		pnd->pnd_fd_cookie = 0;
25046e1ab723Smanu 
25056e1ab723Smanu 	/*
2506a299e660Sandvar 	 * Do we already have the data buffered?
25077b1d1ee6Smanu 	 */
25087b1d1ee6Smanu 	if (pnd->pnd_dirent != NULL)
25097b1d1ee6Smanu 		goto out;
25107b1d1ee6Smanu 	pnd->pnd_dirent_len = 0;
25117b1d1ee6Smanu 
2512f7174423Smanu 	/*
2513f7174423Smanu 	 * It seems NetBSD can call readdir without open first
2514f7174423Smanu 	 * libfuse will crash if it is done that way, hence open first.
2515f7174423Smanu 	 */
2516f7174423Smanu 	if (!(pnd->pnd_flags & PND_OPEN)) {
2517f7174423Smanu 		if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0)
2518f7174423Smanu 			goto out;
2519f7174423Smanu 	}
2520f7174423Smanu 
252128d5b640Smanu 	fh = perfuse_get_fh(opc, FREAD);
2522516b1c90Smanu 
2523516b1c90Smanu #ifdef PERFUSE_DEBUG
2524516b1c90Smanu 	if (perfuse_diagflags & PDF_FH)
252571a2942bSmanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", "
252671a2942bSmanu 			"rfh = 0x%"PRIx64"\n", __func__, (void *)opc,
25272bc8acd8Smanu 			PERFUSE_NODE_DATA(opc)->pnd_nodeid, fh);
2528516b1c90Smanu #endif
2529516b1c90Smanu 
25307b1d1ee6Smanu 	pnd->pnd_all_fd = NULL;
25317b1d1ee6Smanu 	pnd->pnd_all_fd_len = 0;
25323d6861b5Smanu 	fd_maxlen = ps->ps_max_readahead - sizeof(*foh);
25337b1d1ee6Smanu 
25347b1d1ee6Smanu 	do {
25357b1d1ee6Smanu 		size_t fd_len;
25367b1d1ee6Smanu 		char *afdp;
25377b1d1ee6Smanu 
25387b1d1ee6Smanu 		pm = ps->ps_new_msg(pu, opc, FUSE_READDIR, sizeof(*fri), pcr);
25397b1d1ee6Smanu 
25407b1d1ee6Smanu 		/*
25417b1d1ee6Smanu 		 * read_flags, lock_owner and flags are unused in libfuse
25427b1d1ee6Smanu 		 */
25437b1d1ee6Smanu 		fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
25447b1d1ee6Smanu 		fri->fh = fh;
25456e1ab723Smanu 		fri->offset = pnd->pnd_fd_cookie;
2546c3c545a5Smanu 		fri->size = (uint32_t)fd_maxlen;
25477b1d1ee6Smanu 		fri->read_flags = 0;
25487b1d1ee6Smanu 		fri->lock_owner = 0;
25497b1d1ee6Smanu 		fri->flags = 0;
25507b1d1ee6Smanu 
25513d6861b5Smanu 		if ((error = xchg_msg(pu, opc, pm,
25523d6861b5Smanu 				      UNSPEC_REPLY_LEN, wait_reply)) != 0)
25537b1d1ee6Smanu 			goto out;
25547b1d1ee6Smanu 
25557b1d1ee6Smanu 		/*
25567b1d1ee6Smanu 		 * There are many puffs_framebufs calls later,
25577b1d1ee6Smanu 		 * therefore foh will not be valid for a long time.
25587b1d1ee6Smanu 		 * Just get the length and forget it.
25597b1d1ee6Smanu 		 */
25607b1d1ee6Smanu 		foh = GET_OUTHDR(ps, pm);
25617b1d1ee6Smanu 		foh_len = foh->len;
25627b1d1ee6Smanu 
25637b1d1ee6Smanu 		/*
25643d6861b5Smanu 		 * Empty read: we reached the end of the buffer.
25657b1d1ee6Smanu 		 */
25666e1ab723Smanu 		if (foh_len == sizeof(*foh)) {
256771a2942bSmanu 			ps->ps_destroy_msg(pm);
25686e1ab723Smanu 			*eofflag = 1;
25697b1d1ee6Smanu 			break;
25706e1ab723Smanu 		}
25717b1d1ee6Smanu 
25727b1d1ee6Smanu 		/*
25736e1ab723Smanu 		 * Check for corrupted message.
25747b1d1ee6Smanu 		 */
25757b1d1ee6Smanu 		if (foh_len < sizeof(*foh) + sizeof(*fd)) {
257671a2942bSmanu 			ps->ps_destroy_msg(pm);
25777b1d1ee6Smanu 			DWARNX("readdir reply too short");
25787b1d1ee6Smanu 			error = EIO;
25797b1d1ee6Smanu 			goto out;
25807b1d1ee6Smanu 		}
25817b1d1ee6Smanu 
25827b1d1ee6Smanu 
25837b1d1ee6Smanu 		fd = GET_OUTPAYLOAD(ps, pm, fuse_dirent);
25847b1d1ee6Smanu 		fd_len = foh_len - sizeof(*foh);
25857b1d1ee6Smanu 
25867b1d1ee6Smanu 		pnd->pnd_all_fd = realloc(pnd->pnd_all_fd,
25877b1d1ee6Smanu 					  pnd->pnd_all_fd_len + fd_len);
25887b1d1ee6Smanu 		if (pnd->pnd_all_fd  == NULL)
2589dda15b03Schristos 			DERR(EX_OSERR, "%s: malloc failed", __func__);
25907b1d1ee6Smanu 
25917b1d1ee6Smanu 		afdp = (char *)(void *)pnd->pnd_all_fd + pnd->pnd_all_fd_len;
25927b1d1ee6Smanu 		(void)memcpy(afdp, fd, fd_len);
25937b1d1ee6Smanu 
25947b1d1ee6Smanu 		pnd->pnd_all_fd_len += fd_len;
25956e1ab723Smanu 
25966e1ab723Smanu 		/*
25976e1ab723Smanu 		 * The fd->off field is used as a cookie for
25986e1ab723Smanu 		 * resuming the next readdir() where this one was left.
25996e1ab723Smanu 	 	 */
26006e1ab723Smanu 		pnd->pnd_fd_cookie = readdir_last_cookie(fd, fd_len);
26017b1d1ee6Smanu 
26027b1d1ee6Smanu 		ps->ps_destroy_msg(pm);
26036e1ab723Smanu 	} while (1 /* CONSTCOND */);
26043d6861b5Smanu 
26056e1ab723Smanu 	if (pnd->pnd_all_fd != NULL) {
26066e1ab723Smanu 		if (fuse_to_dirent(pu, opc, pnd->pnd_all_fd,
26076e1ab723Smanu 				   pnd->pnd_all_fd_len) == -1)
26087b1d1ee6Smanu 			error = EIO;
26096e1ab723Smanu 	}
26107b1d1ee6Smanu 
26117b1d1ee6Smanu out:
26127b1d1ee6Smanu 	if (pnd->pnd_all_fd != NULL) {
26137b1d1ee6Smanu 		free(pnd->pnd_all_fd);
26147b1d1ee6Smanu 		pnd->pnd_all_fd = NULL;
26157b1d1ee6Smanu 		pnd->pnd_all_fd_len = 0;
26167b1d1ee6Smanu 	}
26177b1d1ee6Smanu 
26187b1d1ee6Smanu 	if (error == 0)
2619075ba0e5Smanu 		readdir_buffered(opc, dent, readoff, reslen);
26207b1d1ee6Smanu 
26217b1d1ee6Smanu 	/*
26227b1d1ee6Smanu 	 * Schedule queued readdir requests
26237b1d1ee6Smanu 	 */
26247b1d1ee6Smanu 	pnd->pnd_flags &= ~PND_INREADDIR;
2625075ba0e5Smanu 	(void)dequeue_requests(opc, PCQ_READDIR, DEQUEUE_ALL);
26267b1d1ee6Smanu 
26277b1d1ee6Smanu #ifdef PERFUSE_DEBUG
26287b1d1ee6Smanu 	if (perfuse_diagflags & PDF_READDIR)
26297b1d1ee6Smanu 		DPRINTF("%s: READDIR opc = %p exit critical section\n",
26307b1d1ee6Smanu 			__func__, (void *)opc);
26317b1d1ee6Smanu #endif
26327b1d1ee6Smanu 
2633075ba0e5Smanu 	node_rele(opc);
26347b1d1ee6Smanu 	return error;
26357b1d1ee6Smanu }
26367b1d1ee6Smanu 
26377b1d1ee6Smanu int
perfuse_node_readlink(struct puffs_usermount * pu,puffs_cookie_t opc,const struct puffs_cred * pcr,char * linkname,size_t * linklen)2638e1a2f47fSmatt perfuse_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
2639e1a2f47fSmatt 	const struct puffs_cred *pcr, char *linkname, size_t *linklen)
26407b1d1ee6Smanu {
26417b1d1ee6Smanu 	struct perfuse_state *ps;
26427b1d1ee6Smanu 	perfuse_msg_t *pm;
26437b1d1ee6Smanu 	int error;
26447b1d1ee6Smanu 	size_t len;
26457b1d1ee6Smanu 	struct fuse_out_header *foh;
26467b1d1ee6Smanu 
2647f7174423Smanu 	if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_REMOVED)
2648f7174423Smanu 		return ENOENT;
2649f7174423Smanu 
2650075ba0e5Smanu 	node_ref(opc);
26517b1d1ee6Smanu 	ps = puffs_getspecific(pu);
26527b1d1ee6Smanu 
26537b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_READLINK, 0, pcr);
26547b1d1ee6Smanu 
26553d6861b5Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
2656075ba0e5Smanu 		goto out;
26577b1d1ee6Smanu 
26587b1d1ee6Smanu 	foh = GET_OUTHDR(ps, pm);
2659f782f0a9Smanu 	len = foh->len - sizeof(*foh);
26607b1d1ee6Smanu 	if (len > *linklen)
2661ef7e1877Smanu 		DERRX(EX_PROTOCOL, "path len = %zd too long", len);
2662f782f0a9Smanu 	if (len == 0)
2663f782f0a9Smanu 		DERRX(EX_PROTOCOL, "path len = %zd too short", len);
26647b1d1ee6Smanu 
266538582b4fSmanu 	(void)memcpy(linkname, _GET_OUTPAYLOAD(ps, pm, char *), len);
266638582b4fSmanu 
2667f782f0a9Smanu 	/*
2668f782f0a9Smanu 	 * FUSE filesystems return a NUL terminated string, we
266938582b4fSmanu 	 * do not want the trailing \0
2670f782f0a9Smanu 	 */
267138582b4fSmanu 	while (len > 0 && linkname[len - 1] == '\0')
267238582b4fSmanu 		len--;
267338582b4fSmanu 
267438582b4fSmanu 	*linklen = len;
267571a2942bSmanu 
26767b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
2677075ba0e5Smanu 	error = 0;
26787b1d1ee6Smanu 
2679075ba0e5Smanu out:
2680075ba0e5Smanu 	node_rele(opc);
2681075ba0e5Smanu 	return error;
26827b1d1ee6Smanu }
26837b1d1ee6Smanu 
26847b1d1ee6Smanu int
perfuse_node_reclaim2(struct puffs_usermount * pu,puffs_cookie_t opc,int nlookup)2685b553c427Smanu perfuse_node_reclaim2(struct puffs_usermount *pu,
2686b553c427Smanu 		      puffs_cookie_t opc, int nlookup)
26877b1d1ee6Smanu {
26887b1d1ee6Smanu 	struct perfuse_state *ps;
26897b1d1ee6Smanu 	perfuse_msg_t *pm;
2690516b1c90Smanu 	struct perfuse_node_data *pnd;
26917b1d1ee6Smanu 	struct fuse_forget_in *ffi;
26927b1d1ee6Smanu 
2693b553c427Smanu #ifdef PERFUSE_DEBUG
2694b553c427Smanu 		if (perfuse_diagflags & PDF_RECLAIM)
2695b553c427Smanu 			DPRINTF("%s called with opc = %p, nlookup = %d\n",
2696b553c427Smanu 				__func__, (void *)opc, nlookup);
2697b553c427Smanu #endif
2698b553c427Smanu 	if (opc == 0 || nlookup == 0) {
269970d81924Smanu 		return 0;
2700b553c427Smanu 	}
270170d81924Smanu 
27027b1d1ee6Smanu 	ps = puffs_getspecific(pu);
2703516b1c90Smanu 	pnd = PERFUSE_NODE_DATA(opc);
27047b1d1ee6Smanu 
27057b1d1ee6Smanu 	/*
27067b1d1ee6Smanu 	 * Never forget the root.
27077b1d1ee6Smanu 	 */
27082bc8acd8Smanu 	if (pnd->pnd_nodeid == FUSE_ROOT_ID)
27097b1d1ee6Smanu 		return 0;
27107b1d1ee6Smanu 
2711b553c427Smanu #ifdef PERFUSE_DEBUG
2712b553c427Smanu 	if (perfuse_diagflags & PDF_RECLAIM)
2713b553c427Smanu 		DPRINTF("%s (nodeid %"PRId64") reclaimed, nlookup = %d/%d\n",
2714b553c427Smanu 			perfuse_node_path(ps, opc), pnd->pnd_nodeid,
2715b553c427Smanu 			nlookup, pnd->pnd_puffs_nlookup);
2716b553c427Smanu #endif
2717075ba0e5Smanu 	/*
2718b553c427Smanu 	 * The kernel tells us how many lookups it made, which allows
2719b553c427Smanu 	 * us to detect that we have an uncompleted lookup and that the
2720a299e660Sandvar 	 * node should not disappear.
2721075ba0e5Smanu 	 */
2722b553c427Smanu 	pnd->pnd_puffs_nlookup -= nlookup;
2723b553c427Smanu 	if (pnd->pnd_puffs_nlookup > 0)
2724075ba0e5Smanu 		return 0;
2725075ba0e5Smanu 
2726075ba0e5Smanu 	node_ref(opc);
2727516b1c90Smanu 	pnd->pnd_flags |= PND_RECLAIMED;
27287b1d1ee6Smanu 
27297b1d1ee6Smanu #ifdef PERFUSE_DEBUG
27307b1d1ee6Smanu 	if (perfuse_diagflags & PDF_RECLAIM)
2731202e6de0Smanu 		DPRINTF("%s (nodeid %"PRId64") is %sreclaimed, nlookup = %d "
2732075ba0e5Smanu 			"%s%s%s%s, pending ops:%s%s%s\n",
2733075ba0e5Smanu 		        perfuse_node_path(ps, opc), pnd->pnd_nodeid,
2734516b1c90Smanu 		        pnd->pnd_flags & PND_RECLAIMED ? "" : "not ",
2735075ba0e5Smanu 			pnd->pnd_puffs_nlookup,
273628d5b640Smanu 			pnd->pnd_flags & PND_OPEN ? "open " : "not open",
273728d5b640Smanu 			pnd->pnd_flags & PND_RFH ? "r" : "",
273828d5b640Smanu 			pnd->pnd_flags & PND_WFH ? "w" : "",
2739b553c427Smanu 			pnd->pnd_flags & PND_BUSY ? " busy" : "",
274028d5b640Smanu 			pnd->pnd_flags & PND_INREADDIR ? " readdir" : "",
2741bcf6f2f3Smanu 			pnd->pnd_flags & PND_INWRITE ? " write" : "",
2742bcf6f2f3Smanu 			pnd->pnd_flags & PND_INOPEN ? " open" : "");
27437b1d1ee6Smanu #endif
2744075ba0e5Smanu 	/*
2745075ba0e5Smanu 	 * Make sure it is not looked up again
2746075ba0e5Smanu 	 */
2747075ba0e5Smanu 	if (!(pnd->pnd_flags & PND_REMOVED))
2748075ba0e5Smanu 		perfuse_cache_flush(opc);
27497b1d1ee6Smanu 
27506bceb418Smanu 	/*
2751075ba0e5Smanu 	 * Purge any activity on the node, while checking
2752075ba0e5Smanu 	 * that it remains eligible for a reclaim.
27536bceb418Smanu 	 */
2754075ba0e5Smanu 	while (pnd->pnd_ref > 1)
2755075ba0e5Smanu 		requeue_request(pu, opc, PCQ_REF);
2756075ba0e5Smanu 
275728d5b640Smanu #ifdef PERFUSE_DEBUG
2758bcf6f2f3Smanu 	if ((pnd->pnd_flags & PND_OPEN) ||
2759bcf6f2f3Smanu 	       !TAILQ_EMPTY(&pnd->pnd_pcq))
2760f7b64496Smanu 		DERRX(EX_SOFTWARE, "%s: opc = %p \"%s\": still open",
2761f7b64496Smanu 		      __func__, opc, pnd->pnd_name);
2762bcf6f2f3Smanu 
2763bcf6f2f3Smanu 	if ((pnd->pnd_flags & PND_BUSY) ||
276428d5b640Smanu 	       !TAILQ_EMPTY(&pnd->pnd_pcq))
2765075ba0e5Smanu 		DERRX(EX_SOFTWARE, "%s: opc = %p: queued operations",
2766075ba0e5Smanu 		      __func__, opc);
2767075ba0e5Smanu 
2768075ba0e5Smanu 	if (pnd->pnd_inxchg != 0)
276928d5b640Smanu 		DERRX(EX_SOFTWARE, "%s: opc = %p: ongoing operations",
2770075ba0e5Smanu 		      __func__, opc);
277128d5b640Smanu #endif
277228d5b640Smanu 
277328d5b640Smanu 	/*
2774bcf6f2f3Smanu 	 * Send the FORGET message
2775fb0fa57fSmanu 	 *
2776fb0fa57fSmanu 	 * ps_new_msg() is called with NULL creds, which will
2777fb0fa57fSmanu 	 * be interpreted as FUSE superuser. This is obviously
2778fb0fa57fSmanu 	 * fine since we operate with kernel creds here.
277928d5b640Smanu 	 */
2780075ba0e5Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_FORGET,
27817b1d1ee6Smanu 		      sizeof(*ffi), NULL);
27827b1d1ee6Smanu 	ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in);
2783202e6de0Smanu 	ffi->nlookup = pnd->pnd_fuse_nlookup;
27847b1d1ee6Smanu 
27857b1d1ee6Smanu 	/*
27863d6861b5Smanu 	 * No reply is expected, pm is freed in xchg_msg
27877b1d1ee6Smanu 	 */
2788075ba0e5Smanu 	(void)xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, no_reply);
27897b1d1ee6Smanu 
2790075ba0e5Smanu 	perfuse_destroy_pn(pu, opc);
27917b1d1ee6Smanu 
27927b1d1ee6Smanu 	return 0;
27937b1d1ee6Smanu }
27947b1d1ee6Smanu 
27957b1d1ee6Smanu int
perfuse_node_reclaim(struct puffs_usermount * pu,puffs_cookie_t opc)2796b553c427Smanu perfuse_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
2797b553c427Smanu {
2798b553c427Smanu #ifdef PERFUSE_DEBUG
2799b553c427Smanu 	if (perfuse_diagflags & PDF_RECLAIM)
2800b553c427Smanu 		DPRINTF("perfuse_node_reclaim called\n");
2801b553c427Smanu #endif
2802b553c427Smanu 	return perfuse_node_reclaim2(pu, opc, 1);
2803b553c427Smanu }
2804b553c427Smanu 
2805b553c427Smanu int
perfuse_node_inactive(struct puffs_usermount * pu,puffs_cookie_t opc)2806e1a2f47fSmatt perfuse_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
28077b1d1ee6Smanu {
2808bcf6f2f3Smanu 	struct perfuse_node_data *pnd;
28096bceb418Smanu 	int error;
2810bcf6f2f3Smanu 
281170d81924Smanu 	if (opc == 0)
281270d81924Smanu 		return 0;
281370d81924Smanu 
2814bcf6f2f3Smanu 	pnd = PERFUSE_NODE_DATA(opc);
2815f7174423Smanu 	if (!(pnd->pnd_flags & (PND_OPEN|PND_REMOVED)))
2816ec8879c9Smanu 		return 0;
2817ec8879c9Smanu 
2818ec8879c9Smanu 	node_ref(opc);
2819bcf6f2f3Smanu 
2820bcf6f2f3Smanu 	/*
2821bcf6f2f3Smanu 	 * Make sure all operation are finished
2822bcf6f2f3Smanu 	 * There can be an ongoing write. Other
2823f7174423Smanu 	 * operation wait for all data before
2824bcf6f2f3Smanu 	 * the close/inactive.
2825bcf6f2f3Smanu 	 */
2826bcf6f2f3Smanu 	while (pnd->pnd_flags & PND_INWRITE)
2827bcf6f2f3Smanu 		requeue_request(pu, opc, PCQ_AFTERWRITE);
2828bcf6f2f3Smanu 
2829bcf6f2f3Smanu 	/*
28306bceb418Smanu 	 * The inactive operation may be cancelled,
2831bcf6f2f3Smanu 	 * If no open is in progress, set PND_INOPEN
2832bcf6f2f3Smanu 	 * so that a new open will be queued.
2833bcf6f2f3Smanu 	 */
2834bcf6f2f3Smanu 	if (pnd->pnd_flags & PND_INOPEN)
2835075ba0e5Smanu 		goto out;
2836bcf6f2f3Smanu 
2837bcf6f2f3Smanu 	pnd->pnd_flags |= PND_INOPEN;
2838bcf6f2f3Smanu 
2839bcf6f2f3Smanu 	/*
2840bcf6f2f3Smanu 	 * Sync data
2841bcf6f2f3Smanu 	 */
28426bceb418Smanu 	if (pnd->pnd_flags & PND_DIRTY) {
28436bceb418Smanu 		if ((error = perfuse_node_fsync(pu, opc, NULL, 0, 0, 0)) != 0)
28446bceb418Smanu 			DWARN("%s: perfuse_node_fsync failed error = %d",
28456bceb418Smanu 			      __func__, error);
28466bceb418Smanu 	}
28476bceb418Smanu 
2848bcf6f2f3Smanu 
2849bcf6f2f3Smanu 	/*
2850bcf6f2f3Smanu 	 * Close handles
2851bcf6f2f3Smanu 	 */
28526bceb418Smanu 	if (pnd->pnd_flags & PND_WFH) {
28536bceb418Smanu 		if ((error = perfuse_node_close_common(pu, opc, FWRITE)) != 0)
28546bceb418Smanu 			DWARN("%s: close write FH failed error = %d",
28556bceb418Smanu 			      __func__, error);
28566bceb418Smanu 	}
2857bcf6f2f3Smanu 
28586bceb418Smanu 	if (pnd->pnd_flags & PND_RFH) {
28596bceb418Smanu 		if ((error = perfuse_node_close_common(pu, opc, FREAD)) != 0)
28606bceb418Smanu 			DWARN("%s: close read FH failed error = %d",
28616bceb418Smanu 			      __func__, error);
28626bceb418Smanu 	}
2863bcf6f2f3Smanu 
28642ff0ea03Smanu 	/*
28652ff0ea03Smanu 	 * This will cause a reclaim to be sent
28662ff0ea03Smanu 	 */
2867f7174423Smanu 	if (pnd->pnd_flags & PND_REMOVED)
2868f7174423Smanu 		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
2869f7174423Smanu 
28702ff0ea03Smanu 	/*
28712ff0ea03Smanu 	 * Schedule awaiting operations
28722ff0ea03Smanu 	 */
28732ff0ea03Smanu 	pnd->pnd_flags &= ~PND_INOPEN;
2874075ba0e5Smanu 	(void)dequeue_requests(opc, PCQ_OPEN, DEQUEUE_ALL);
28752ff0ea03Smanu 
2876075ba0e5Smanu 	/*
2877075ba0e5Smanu 	 * errors are ignored, since the kernel ignores the return code.
2878075ba0e5Smanu 	 */
2879075ba0e5Smanu out:
2880075ba0e5Smanu 	node_rele(opc);
28817b1d1ee6Smanu 	return 0;
28827b1d1ee6Smanu }
28837b1d1ee6Smanu 
28847b1d1ee6Smanu 
28857b1d1ee6Smanu /* ARGSUSED0 */
28867b1d1ee6Smanu int
perfuse_node_print(struct puffs_usermount * pu,puffs_cookie_t opc)2887e1a2f47fSmatt perfuse_node_print(struct puffs_usermount *pu, puffs_cookie_t opc)
28887b1d1ee6Smanu {
28897b1d1ee6Smanu 	DERRX(EX_SOFTWARE, "%s: UNIMPLEMENTED (FATAL)", __func__);
28907b1d1ee6Smanu 	return 0;
28917b1d1ee6Smanu }
28927b1d1ee6Smanu 
28937b1d1ee6Smanu int
perfuse_node_pathconf(struct puffs_usermount * pu,puffs_cookie_t opc,int name,register_t * retval)2894e1a2f47fSmatt perfuse_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc,
289521a90045Senami 	int name, register_t *retval)
28967b1d1ee6Smanu {
2897bcfebaffSmanu 	perfuse_msg_t *pm;
2898bcfebaffSmanu 	struct perfuse_state *ps;
2899bcfebaffSmanu 	struct fuse_statfs_out *fso;
2900bcfebaffSmanu 	int error = 0;
2901bcfebaffSmanu 
2902bcfebaffSmanu 	/*
2903bcfebaffSmanu 	 * Static values copied from UFS
2904bcfebaffSmanu 	 * in src/sys/ufs/ufs/ufs_vnops.c
2905bcfebaffSmanu 	 */
2906bcfebaffSmanu 	switch (name) {
2907bcfebaffSmanu 	case _PC_LINK_MAX:
2908bcfebaffSmanu 		*retval = LINK_MAX;
2909bcfebaffSmanu 		break;
2910bcfebaffSmanu 	case _PC_PATH_MAX:
2911bcfebaffSmanu 		*retval = PATH_MAX;
2912bcfebaffSmanu 		break;
2913bcfebaffSmanu 	case _PC_PIPE_BUF:
2914bcfebaffSmanu 		*retval = PIPE_BUF;
2915bcfebaffSmanu 		break;
2916bcfebaffSmanu 	case _PC_CHOWN_RESTRICTED:
2917bcfebaffSmanu 		*retval = 1;
2918bcfebaffSmanu 		break;
2919bcfebaffSmanu 	case _PC_NO_TRUNC:
2920bcfebaffSmanu 		*retval = 1;
2921bcfebaffSmanu 		break;
2922bcfebaffSmanu 	case _PC_SYNC_IO:
2923bcfebaffSmanu 		*retval = 1;
2924bcfebaffSmanu 		break;
2925bcfebaffSmanu 	case _PC_FILESIZEBITS:
2926bcfebaffSmanu 		*retval = 42;
2927bcfebaffSmanu 		break;
2928bcfebaffSmanu 	case _PC_SYMLINK_MAX:
2929bcfebaffSmanu 		*retval = MAXPATHLEN;
2930bcfebaffSmanu 		break;
2931bcfebaffSmanu 	case _PC_2_SYMLINKS:
2932bcfebaffSmanu 		*retval = 1;
2933bcfebaffSmanu 		break;
2934bcfebaffSmanu 	case _PC_NAME_MAX:
2935bcfebaffSmanu 		ps = puffs_getspecific(pu);
2936bcfebaffSmanu 		pm = ps->ps_new_msg(pu, opc, FUSE_STATFS, 0, NULL);
2937bcfebaffSmanu 
2938bcfebaffSmanu 		error = xchg_msg(pu, opc, pm, sizeof(*fso), wait_reply);
2939bcfebaffSmanu 		if (error != 0)
2940bcfebaffSmanu 			return error;
2941bcfebaffSmanu 
2942bcfebaffSmanu 		fso = GET_OUTPAYLOAD(ps, pm, fuse_statfs_out);
2943bcfebaffSmanu 		*retval = fso->st.namelen;
2944bcfebaffSmanu 
2945bcfebaffSmanu 		ps->ps_destroy_msg(pm);
2946bcfebaffSmanu 
2947bcfebaffSmanu 		break;
2948bcfebaffSmanu 	default:
2949bcfebaffSmanu 		DWARN("Unimplemented pathconf for name = %d", name);
2950bcfebaffSmanu 		error = ENOSYS;
2951bcfebaffSmanu 		break;
2952bcfebaffSmanu 	}
2953bcfebaffSmanu 
2954bcfebaffSmanu 	return error;
29557b1d1ee6Smanu }
29567b1d1ee6Smanu 
29577b1d1ee6Smanu int
perfuse_node_advlock(struct puffs_usermount * pu,puffs_cookie_t opc,void * id,int op,struct flock * fl,int flags)2958e1a2f47fSmatt perfuse_node_advlock(struct puffs_usermount *pu, puffs_cookie_t opc,
2959e1a2f47fSmatt 	void *id, int op, struct flock *fl, int flags)
29607b1d1ee6Smanu {
29617b1d1ee6Smanu 	struct perfuse_state *ps;
29627b1d1ee6Smanu 	int fop;
29637b1d1ee6Smanu 	perfuse_msg_t *pm;
296498c38e3eSmanu 	uint64_t fh;
29657b1d1ee6Smanu 	struct fuse_lk_in *fli;
296638ecbcf4Smanu 	struct fuse_out_header *foh;
29677b1d1ee6Smanu 	struct fuse_lk_out *flo;
296838ecbcf4Smanu 	uint32_t owner;
296938ecbcf4Smanu 	size_t len;
29707b1d1ee6Smanu 	int error;
29717b1d1ee6Smanu 
2972075ba0e5Smanu 	node_ref(opc);
2973075ba0e5Smanu 
297498c38e3eSmanu 	/*
297598c38e3eSmanu 	 * Make sure we do have a filehandle, as the FUSE filesystem
297698c38e3eSmanu 	 * expect one. E.g.: if we provide none, GlusterFS logs an error
297798c38e3eSmanu 	 * "0-glusterfs-fuse: xl is NULL"
297898c38e3eSmanu 	 *
297998c38e3eSmanu 	 * We need the read file handle if the file is open read only,
298098c38e3eSmanu 	 * in order to support shared locks on read-only files.
298198c38e3eSmanu 	 * NB: The kernel always sends advlock for read-only
298298c38e3eSmanu 	 * files at exit time when the process used lock, see
298398c38e3eSmanu 	 * sys_exit -> exit1 -> fd_free -> fd_close -> VOP_ADVLOCK
298498c38e3eSmanu 	 */
2985075ba0e5Smanu 	if ((fh = perfuse_get_fh(opc, FREAD)) == FUSE_UNKNOWN_FH) {
2986075ba0e5Smanu 		error = EBADF;
2987075ba0e5Smanu 		goto out;
2988075ba0e5Smanu 	}
298998c38e3eSmanu 
29907b1d1ee6Smanu 	ps = puffs_getspecific(pu);
29917b1d1ee6Smanu 
29927b1d1ee6Smanu 	if (op == F_GETLK)
29937b1d1ee6Smanu 		fop = FUSE_GETLK;
29947b1d1ee6Smanu 	else
29957b1d1ee6Smanu 		fop = (flags & F_WAIT) ? FUSE_SETLKW : FUSE_SETLK;
29967b1d1ee6Smanu 
2997fb0fa57fSmanu 	/*
2998fb0fa57fSmanu 	 * XXX ps_new_msg() is called with NULL creds, which will
2999fb0fa57fSmanu 	 * be interpreted as FUSE superuser. We have no way to
3000fb0fa57fSmanu 	 * know the requesting process' credential, but since advlock()
3001fb0fa57fSmanu 	 * is supposed to operate on a file that has been open(),
3002fb0fa57fSmanu 	 * permission should have already been checked at open() time.
3003fb0fa57fSmanu 	 */
30047b1d1ee6Smanu 	pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL);
30057b1d1ee6Smanu 	fli = GET_INPAYLOAD(ps, pm, fuse_lk_in);
300698c38e3eSmanu 	fli->fh = fh;
300798c38e3eSmanu 	fli->owner = (uint64_t)(vaddr_t)id;
30087b1d1ee6Smanu 	fli->lk.start = fl->l_start;
30097b1d1ee6Smanu 	fli->lk.end = fl->l_start + fl->l_len;
30107b1d1ee6Smanu 	fli->lk.type = fl->l_type;
30117b1d1ee6Smanu 	fli->lk.pid = fl->l_pid;
30127b1d1ee6Smanu 	fli->lk_flags = (flags & F_FLOCK) ? FUSE_LK_FLOCK : 0;
30137b1d1ee6Smanu 
301455557eb9Smanu 	owner = (uint32_t)(vaddr_t)id;
301538ecbcf4Smanu 
3016516b1c90Smanu #ifdef PERFUSE_DEBUG
3017516b1c90Smanu 	if (perfuse_diagflags & PDF_FH)
30182bc8acd8Smanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", fh = 0x%"PRIx64"\n",
3019516b1c90Smanu 			__func__, (void *)opc,
30202bc8acd8Smanu 			PERFUSE_NODE_DATA(opc)->pnd_nodeid, fli->fh);
3021516b1c90Smanu #endif
3022516b1c90Smanu 
302338ecbcf4Smanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
3024075ba0e5Smanu 		goto out;
30257b1d1ee6Smanu 
302638ecbcf4Smanu 	foh = GET_OUTHDR(ps, pm);
302738ecbcf4Smanu 	len = foh->len - sizeof(*foh);
302838ecbcf4Smanu 
302938ecbcf4Smanu 	/*
303038ecbcf4Smanu 	 * Save or clear the lock
303138ecbcf4Smanu 	 */
303238ecbcf4Smanu 	switch (op) {
303338ecbcf4Smanu 	case F_GETLK:
303438ecbcf4Smanu 		if (len != sizeof(*flo))
303538ecbcf4Smanu 			DERRX(EX_SOFTWARE,
303638ecbcf4Smanu 			      "%s: Unexpected lock reply len %zd",
303738ecbcf4Smanu 			      __func__, len);
303838ecbcf4Smanu 
30397b1d1ee6Smanu 		flo = GET_OUTPAYLOAD(ps, pm, fuse_lk_out);
30407b1d1ee6Smanu 		fl->l_start = flo->lk.start;
30417b1d1ee6Smanu 		fl->l_len = flo->lk.end - flo->lk.start;
30427b1d1ee6Smanu 		fl->l_pid = flo->lk.pid;
30437b1d1ee6Smanu 		fl->l_type = flo->lk.type;
30447b1d1ee6Smanu 		fl->l_whence = SEEK_SET;	/* libfuse hardcodes it */
30457b1d1ee6Smanu 
30467b1d1ee6Smanu 		PERFUSE_NODE_DATA(opc)->pnd_lock_owner = flo->lk.pid;
30477b1d1ee6Smanu 		break;
30487b1d1ee6Smanu 	case F_UNLCK:
304938ecbcf4Smanu 		owner = 0;
305038ecbcf4Smanu 		/* FALLTHROUGH */
305138ecbcf4Smanu 	case F_SETLK:
305238ecbcf4Smanu 		/* FALLTHROUGH */
305338ecbcf4Smanu 	case F_SETLKW:
305438ecbcf4Smanu 		if (error != 0)
305538ecbcf4Smanu 			PERFUSE_NODE_DATA(opc)->pnd_lock_owner = owner;
305638ecbcf4Smanu 
305738ecbcf4Smanu 		if (len != 0)
305838ecbcf4Smanu 			DERRX(EX_SOFTWARE,
305938ecbcf4Smanu 			      "%s: Unexpected unlock reply len %zd",
306038ecbcf4Smanu 			      __func__, len);
306138ecbcf4Smanu 
30627b1d1ee6Smanu 		break;
30637b1d1ee6Smanu 	default:
306438ecbcf4Smanu 		DERRX(EX_SOFTWARE, "%s: Unexpected op %d", __func__, op);
30657b1d1ee6Smanu 		break;
30667b1d1ee6Smanu 	}
30677b1d1ee6Smanu 
30687b1d1ee6Smanu 	ps->ps_destroy_msg(pm);
3069075ba0e5Smanu 	error = 0;
30707b1d1ee6Smanu 
3071075ba0e5Smanu out:
3072075ba0e5Smanu 	node_rele(opc);
3073075ba0e5Smanu 	return error;
30747b1d1ee6Smanu }
30757b1d1ee6Smanu 
30767b1d1ee6Smanu int
perfuse_node_read(struct puffs_usermount * pu,puffs_cookie_t opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)3077e1a2f47fSmatt perfuse_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf,
3078e1a2f47fSmatt 	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
30797b1d1ee6Smanu {
30807b1d1ee6Smanu 	struct perfuse_state *ps;
308128d5b640Smanu 	struct perfuse_node_data *pnd;
30825a6d3e75Smanu 	const struct vattr *vap;
30837b1d1ee6Smanu 	perfuse_msg_t *pm;
3084ef486683Smanu 	uint64_t fh;
30857b1d1ee6Smanu 	struct fuse_read_in *fri;
30867b1d1ee6Smanu 	struct fuse_out_header *foh;
30877b1d1ee6Smanu 	size_t readen;
30887b1d1ee6Smanu 	int error;
30897b1d1ee6Smanu 
30907b1d1ee6Smanu 	ps = puffs_getspecific(pu);
309128d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
30925a6d3e75Smanu 	vap = puffs_pn_getvap((struct puffs_node *)opc);
30937b1d1ee6Smanu 
3094ec1aa8a5Smanu 	/*
3095ec1aa8a5Smanu 	 * NetBSD turns that into a getdents(2) output
3096ec1aa8a5Smanu 	 * We just do a EISDIR as this feature is of little use.
3097ec1aa8a5Smanu 	 */
3098ec1aa8a5Smanu 	if (vap->va_type == VDIR)
3099ec1aa8a5Smanu 		return EISDIR;
3100ec1aa8a5Smanu 
3101ef486683Smanu 	fh =  perfuse_get_fh(opc, FREAD); /* Cannot be VDIR */
3102ef486683Smanu 
31037b1d1ee6Smanu 	do {
31043d6861b5Smanu 		size_t max_read;
31053d6861b5Smanu 
31063d6861b5Smanu 		max_read = ps->ps_max_readahead - sizeof(*foh);
31077b1d1ee6Smanu 		/*
31087b1d1ee6Smanu 		 * flags may be set to FUSE_READ_LOCKOWNER
31097b1d1ee6Smanu 		 * if lock_owner is provided.
31107b1d1ee6Smanu 		 */
31117b1d1ee6Smanu 		pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr);
31127b1d1ee6Smanu 		fri = GET_INPAYLOAD(ps, pm, fuse_read_in);
3113ef486683Smanu 		fri->fh = fh;
31147b1d1ee6Smanu 		fri->offset = offset;
31153d6861b5Smanu 		fri->size = (uint32_t)MIN(*resid, max_read);
31167b1d1ee6Smanu 		fri->read_flags = 0; /* XXX Unused by libfuse? */
311728d5b640Smanu 		fri->lock_owner = pnd->pnd_lock_owner;
31187b1d1ee6Smanu 		fri->flags = 0;
31197b1d1ee6Smanu 		fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0;
31207b1d1ee6Smanu 
3121516b1c90Smanu #ifdef PERFUSE_DEBUG
3122516b1c90Smanu 	if (perfuse_diagflags & PDF_FH)
31232bc8acd8Smanu 		DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", fh = 0x%"PRIx64"\n",
31242bc8acd8Smanu 			__func__, (void *)opc, pnd->pnd_nodeid, fri->fh);
3125516b1c90Smanu #endif
31263d6861b5Smanu 		error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply);
31277b1d1ee6Smanu 		if (error  != 0)
312871a2942bSmanu 			return error;
31297b1d1ee6Smanu 
31307b1d1ee6Smanu 		foh = GET_OUTHDR(ps, pm);
31317b1d1ee6Smanu 		readen = foh->len - sizeof(*foh);
31327b1d1ee6Smanu 
31333d6861b5Smanu #ifdef PERFUSE_DEBUG
31343d6861b5Smanu 		if (readen > *resid)
31353d6861b5Smanu 			DERRX(EX_SOFTWARE, "%s: Unexpected big read %zd",
31363d6861b5Smanu 			      __func__, readen);
31373d6861b5Smanu #endif
31383d6861b5Smanu 
31397b1d1ee6Smanu 		(void)memcpy(buf,  _GET_OUTPAYLOAD(ps, pm, char *), readen);
31407b1d1ee6Smanu 
31417b1d1ee6Smanu 		buf += readen;
31427b1d1ee6Smanu 		offset += readen;
31437b1d1ee6Smanu 		*resid -= readen;
31447b1d1ee6Smanu 
31457b1d1ee6Smanu 		ps->ps_destroy_msg(pm);
31467b1d1ee6Smanu 	} while ((*resid != 0) && (readen != 0));
31477b1d1ee6Smanu 
31487b1d1ee6Smanu 	if (ioflag & (IO_SYNC|IO_DSYNC))
31497b1d1ee6Smanu 		ps->ps_syncreads++;
31507b1d1ee6Smanu 	else
31517b1d1ee6Smanu 		ps->ps_asyncreads++;
31527b1d1ee6Smanu 
315371a2942bSmanu 	return 0;
31547b1d1ee6Smanu }
31557b1d1ee6Smanu 
31567b1d1ee6Smanu int
perfuse_node_write(struct puffs_usermount * pu,puffs_cookie_t opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)3157075ba0e5Smanu perfuse_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
3158075ba0e5Smanu 	uint8_t *buf, off_t offset, size_t *resid,
3159075ba0e5Smanu 	const struct puffs_cred *pcr, int ioflag)
3160075ba0e5Smanu {
3161075ba0e5Smanu 	return perfuse_node_write2(pu, opc, buf, offset, resid, pcr, ioflag, 0);
3162075ba0e5Smanu }
3163075ba0e5Smanu 
3164075ba0e5Smanu /* ARGSUSED7 */
3165075ba0e5Smanu int
perfuse_node_write2(struct puffs_usermount * pu,puffs_cookie_t opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag,int xflag)3166075ba0e5Smanu perfuse_node_write2(struct puffs_usermount *pu, puffs_cookie_t opc,
3167075ba0e5Smanu 	uint8_t *buf, off_t offset, size_t *resid,
3168075ba0e5Smanu 	const struct puffs_cred *pcr, int ioflag, int xflag)
31697b1d1ee6Smanu {
31707b1d1ee6Smanu 	struct perfuse_state *ps;
317128d5b640Smanu 	struct perfuse_node_data *pnd;
3172c3c545a5Smanu 	struct vattr *vap;
31737b1d1ee6Smanu 	perfuse_msg_t *pm;
3174ef486683Smanu 	uint64_t fh;
31757b1d1ee6Smanu 	struct fuse_write_in *fwi;
31767b1d1ee6Smanu 	struct fuse_write_out *fwo;
31777b1d1ee6Smanu 	size_t data_len;
31787b1d1ee6Smanu 	size_t payload_len;
31797b1d1ee6Smanu 	size_t written;
31805a6d3e75Smanu 	int inresize;
31817b1d1ee6Smanu 	int error;
31827b1d1ee6Smanu 
31837b1d1ee6Smanu 	ps = puffs_getspecific(pu);
318428d5b640Smanu 	pnd = PERFUSE_NODE_DATA(opc);
3185c3c545a5Smanu 	vap = puffs_pn_getvap((struct puffs_node *)opc);
31867b1d1ee6Smanu 	written = 0;
31875a6d3e75Smanu 	inresize = 0;
318871a2942bSmanu 	error = 0;
31897b1d1ee6Smanu 
3190c3c545a5Smanu 	if (vap->va_type == VDIR)
3191ec1aa8a5Smanu 		return EISDIR;
319228d5b640Smanu 
3193075ba0e5Smanu 	node_ref(opc);
3194075ba0e5Smanu 
31953d6861b5Smanu 	/*
31963d6861b5Smanu 	 * We need to queue write requests in order to avoid
31973d6861b5Smanu 	 * dequeueing PCQ_AFTERWRITE when there are pending writes.
31983d6861b5Smanu 	 */
3199374c4263Smanu 	while (pnd->pnd_flags & PND_INWRITE)
3200374c4263Smanu 		requeue_request(pu, opc, PCQ_WRITE);
320128d5b640Smanu 	pnd->pnd_flags |= PND_INWRITE;
320228d5b640Smanu 
3203f7174423Smanu 	/*
3204c3c545a5Smanu 	 * append flag: re-read the file size so that
3205c3c545a5Smanu 	 * we get the latest value.
3206f7174423Smanu 	 */
3207c3c545a5Smanu 	if (ioflag & PUFFS_IO_APPEND) {
3208c3c545a5Smanu 		if ((error = perfuse_node_getattr(pu, opc, vap, pcr)) != 0)
3209c3c545a5Smanu 			goto out;
3210c3c545a5Smanu 
3211c3c545a5Smanu 		offset = vap->va_size;
3212c3c545a5Smanu 	}
3213f7174423Smanu 
3214e6dc4d24Smanu 	/*
3215e6dc4d24Smanu 	 * Serialize size access, see comment in perfuse_node_setattr().
3216e6dc4d24Smanu 	 */
3217e6dc4d24Smanu 	if ((u_quad_t)offset + *resid > vap->va_size) {
3218e6dc4d24Smanu 		while (pnd->pnd_flags & PND_INRESIZE)
3219e6dc4d24Smanu 			requeue_request(pu, opc, PCQ_RESIZE);
3220e6dc4d24Smanu 		pnd->pnd_flags |= PND_INRESIZE;
3221e6dc4d24Smanu 		inresize = 1;
3222e6dc4d24Smanu 	}
3223e6dc4d24Smanu 
32245a6d3e75Smanu #ifdef PERFUSE_DEBUG
32255a6d3e75Smanu 	if (perfuse_diagflags & PDF_RESIZE)
322692ad06d8Schristos 		DPRINTF(">> %s %p %" PRIu64 "\n", __func__,
32275a6d3e75Smanu 			(void *)opc, vap->va_size);
32285a6d3e75Smanu #endif
32295a6d3e75Smanu 
3230ef486683Smanu 	fh = perfuse_get_fh(opc, FWRITE); /* Cannot be VDIR */
3231ef486683Smanu 
32327b1d1ee6Smanu 	do {
32333d6861b5Smanu 		size_t max_write;
32347b1d1ee6Smanu 		/*
32353d6861b5Smanu 		 * There is a writepage flag when data
323632dec9bdSjakllsch 		 * is aligned to page size. Use it for
32373d6861b5Smanu 		 * everything but the data after the last
32383d6861b5Smanu 		 * page boundary.
32397b1d1ee6Smanu 		 */
32403d6861b5Smanu 		max_write = ps->ps_max_write - sizeof(*fwi);
32413d6861b5Smanu 
32423d6861b5Smanu 		data_len = MIN(*resid, max_write);
324332dec9bdSjakllsch 		if (data_len > (size_t)sysconf(_SC_PAGESIZE))
324432dec9bdSjakllsch 			data_len = data_len & ~(sysconf(_SC_PAGESIZE) - 1);
32453d6861b5Smanu 
32467b1d1ee6Smanu 		payload_len = data_len + sizeof(*fwi);
32477b1d1ee6Smanu 
32487b1d1ee6Smanu 		/*
32497b1d1ee6Smanu 		 * flags may be set to FUSE_WRITE_CACHE (XXX usage?)
32507b1d1ee6Smanu 		 * or FUSE_WRITE_LOCKOWNER, if lock_owner is provided.
32517b1d1ee6Smanu 		 * write_flags is set to 1 for writepage.
32527b1d1ee6Smanu 		 */
32537b1d1ee6Smanu 		pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr);
32547b1d1ee6Smanu 		fwi = GET_INPAYLOAD(ps, pm, fuse_write_in);
3255ef486683Smanu 		fwi->fh = fh;
32567b1d1ee6Smanu 		fwi->offset = offset;
3257ef7e1877Smanu 		fwi->size = (uint32_t)data_len;
325832dec9bdSjakllsch 		fwi->write_flags = (fwi->size % sysconf(_SC_PAGESIZE)) ? 0 : 1;
325928d5b640Smanu 		fwi->lock_owner = pnd->pnd_lock_owner;
32607b1d1ee6Smanu 		fwi->flags = 0;
32617b1d1ee6Smanu 		fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0;
32627b1d1ee6Smanu 		fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE;
3263f7174423Smanu 		(void)memcpy((fwi + 1), buf, data_len);
3264f7174423Smanu 
32657b1d1ee6Smanu 
3266516b1c90Smanu #ifdef PERFUSE_DEBUG
3267516b1c90Smanu 		if (perfuse_diagflags & PDF_FH)
32682bc8acd8Smanu 			DPRINTF("%s: opc = %p, nodeid = 0x%"PRIx64", "
3269c3c545a5Smanu 				"fh = 0x%"PRIx64"\n", __func__,
32702bc8acd8Smanu 				(void *)opc, pnd->pnd_nodeid, fwi->fh);
3271516b1c90Smanu #endif
32723d6861b5Smanu 		if ((error = xchg_msg(pu, opc, pm,
32733d6861b5Smanu 				      sizeof(*fwo), wait_reply)) != 0)
32747b1d1ee6Smanu 			goto out;
32757b1d1ee6Smanu 
32767b1d1ee6Smanu 		fwo = GET_OUTPAYLOAD(ps, pm, fuse_write_out);
32777b1d1ee6Smanu 		written = fwo->size;
3278075ba0e5Smanu 		ps->ps_destroy_msg(pm);
3279075ba0e5Smanu 
32803d6861b5Smanu #ifdef PERFUSE_DEBUG
32813d6861b5Smanu 		if (written > *resid)
32823d6861b5Smanu 			DERRX(EX_SOFTWARE, "%s: Unexpected big write %zd",
32833d6861b5Smanu 			      __func__, written);
32843d6861b5Smanu #endif
32857b1d1ee6Smanu 		*resid -= written;
32867b1d1ee6Smanu 		offset += written;
32877b1d1ee6Smanu 		buf += written;
32887b1d1ee6Smanu 
32897b1d1ee6Smanu 	} while (*resid != 0);
32907b1d1ee6Smanu 
32917b1d1ee6Smanu 	/*
32927b1d1ee6Smanu 	 * puffs_ops(3) says
32937b1d1ee6Smanu 	 *  "everything must be written or an error will be generated"
32947b1d1ee6Smanu 	 */
32957b1d1ee6Smanu 	if (*resid != 0)
32967b1d1ee6Smanu 		error = EFBIG;
32977b1d1ee6Smanu 
329825834497Smanu out:
32995a6d3e75Smanu #ifdef PERFUSE_DEBUG
33005a6d3e75Smanu 	if (perfuse_diagflags & PDF_RESIZE) {
33015a6d3e75Smanu 		if (offset > (off_t)vap->va_size)
330292ad06d8Schristos 			DPRINTF("<< %s %p %" PRIu64 " -> %lld\n", __func__,
330392ad06d8Schristos 				(void *)opc, vap->va_size, (long long)offset);
33045a6d3e75Smanu 		else
33055a6d3e75Smanu 			DPRINTF("<< %s %p \n", __func__, (void *)opc);
33065a6d3e75Smanu 	}
33075a6d3e75Smanu #endif
33085a6d3e75Smanu 
3309c3c545a5Smanu 	/*
3310c3c545a5Smanu 	 * Update file size if we wrote beyond the end
3311c3c545a5Smanu 	 */
3312c3c545a5Smanu 	if (offset > (off_t)vap->va_size)
3313c3c545a5Smanu 		vap->va_size = offset;
3314c3c545a5Smanu 
3315c3c545a5Smanu 	/*
3316c3c545a5Smanu 	 * Statistics
3317c3c545a5Smanu 	 */
33187b1d1ee6Smanu 	if (ioflag & (IO_SYNC|IO_DSYNC))
33197b1d1ee6Smanu 		ps->ps_syncwrites++;
33207b1d1ee6Smanu 	else
33217b1d1ee6Smanu 		ps->ps_asyncwrites++;
33227b1d1ee6Smanu 
3323516b1c90Smanu 	/*
3324516b1c90Smanu 	 * Remember to sync the file
3325516b1c90Smanu 	 */
332628d5b640Smanu 	pnd->pnd_flags |= PND_DIRTY;
3327516b1c90Smanu 
3328516b1c90Smanu #ifdef PERFUSE_DEBUG
3329516b1c90Smanu 	if (perfuse_diagflags & PDF_SYNC)
3330516b1c90Smanu 		DPRINTF("%s: DIRTY opc = %p, file = \"%s\"\n",
3331075ba0e5Smanu 			__func__, (void*)opc, perfuse_node_path(ps, opc));
3332516b1c90Smanu #endif
33337b1d1ee6Smanu 
333425834497Smanu 	if (inresize) {
333525834497Smanu #ifdef PERFUSE_DEBUG
333625834497Smanu 		if (!(pnd->pnd_flags & PND_INRESIZE))
333725834497Smanu 			DERRX(EX_SOFTWARE, "file write grow without resize");
333825834497Smanu #endif
333925834497Smanu 		pnd->pnd_flags &= ~PND_INRESIZE;
334025834497Smanu 		(void)dequeue_requests(opc, PCQ_RESIZE, DEQUEUE_ALL);
334125834497Smanu 	}
334225834497Smanu 
334328d5b640Smanu 	/*
3344075ba0e5Smanu 	 * VOP_PUTPAGE causes FAF write where kernel does not
3345075ba0e5Smanu 	 * check operation result. At least warn if it failed.
3346075ba0e5Smanu 	 */
3347075ba0e5Smanu #ifdef PUFFS_WRITE_FAF
3348075ba0e5Smanu 	if (error && (xflag & PUFFS_WRITE_FAF))
3349075ba0e5Smanu 		DWARN("Data loss caused by FAF write failed on \"%s\"",
3350075ba0e5Smanu 		      pnd->pnd_name);
3351075ba0e5Smanu #endif /* PUFFS_WRITE_FAF */
3352075ba0e5Smanu 
3353075ba0e5Smanu 	/*
3354374c4263Smanu 	 * If there are no more queued write, we can resume
3355374c4263Smanu 	 * an operation awaiting write completion.
335628d5b640Smanu 	 */
3357374c4263Smanu 	pnd->pnd_flags &= ~PND_INWRITE;
3358075ba0e5Smanu 	if (dequeue_requests(opc, PCQ_WRITE, 1) == 0)
3359075ba0e5Smanu 		(void)dequeue_requests(opc, PCQ_AFTERWRITE, DEQUEUE_ALL);
336028d5b640Smanu 
3361075ba0e5Smanu 	node_rele(opc);
33627b1d1ee6Smanu 	return error;
33637b1d1ee6Smanu }
33647b1d1ee6Smanu 
33657b1d1ee6Smanu /* ARGSUSED0 */
33667b1d1ee6Smanu void
perfuse_cache_write(struct puffs_usermount * pu,puffs_cookie_t opc,size_t size,struct puffs_cacherun * runs)3367e1a2f47fSmatt perfuse_cache_write(struct puffs_usermount *pu, puffs_cookie_t opc, size_t size,
3368e1a2f47fSmatt 	struct puffs_cacherun *runs)
33697b1d1ee6Smanu {
33707b1d1ee6Smanu 	return;
33717b1d1ee6Smanu }
33727b1d1ee6Smanu 
33738ae0a67dSmanu /* ARGSUSED4 */
33748ae0a67dSmanu int
perfuse_node_getextattr(struct puffs_usermount * pu,puffs_cookie_t opc,int attrns,const char * attrname,size_t * attrsize,uint8_t * attr,size_t * resid,const struct puffs_cred * pcr)3375e1a2f47fSmatt perfuse_node_getextattr(struct puffs_usermount *pu, puffs_cookie_t opc,
3376e1a2f47fSmatt 	int attrns, const char *attrname, size_t *attrsize, uint8_t *attr,
3377e1a2f47fSmatt 	size_t *resid, const struct puffs_cred *pcr)
33788ae0a67dSmanu {
33798ae0a67dSmanu 	struct perfuse_state *ps;
33808ae0a67dSmanu 	char fuse_attrname[LINUX_XATTR_NAME_MAX + 1];
33818ae0a67dSmanu 	perfuse_msg_t *pm;
33828ae0a67dSmanu 	struct fuse_getxattr_in *fgi;
33838ae0a67dSmanu 	struct fuse_getxattr_out *fgo;
33848ae0a67dSmanu 	struct fuse_out_header *foh;
33858ae0a67dSmanu 	size_t attrnamelen;
33868ae0a67dSmanu 	size_t len;
33878ae0a67dSmanu 	char *np;
33888ae0a67dSmanu 	int error;
33898ae0a67dSmanu 
3390ac713433Smanu 	/* system namespace attrs are not accessible to non root users */
3391ac713433Smanu 	if (attrns == EXTATTR_NAMESPACE_SYSTEM && !puffs_cred_isjuggernaut(pcr))
3392ac713433Smanu 		return EPERM;
3393ac713433Smanu 
3394075ba0e5Smanu 	node_ref(opc);
33958ae0a67dSmanu 	ps = puffs_getspecific(pu);
33968ae0a67dSmanu 	attrname = perfuse_native_ns(attrns, attrname, fuse_attrname);
33978ae0a67dSmanu 	attrnamelen = strlen(attrname) + 1;
33988ae0a67dSmanu 	len = sizeof(*fgi) + attrnamelen;
33998ae0a67dSmanu 
34008ae0a67dSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_GETXATTR, len, pcr);
34018ae0a67dSmanu 	fgi = GET_INPAYLOAD(ps, pm, fuse_getxattr_in);
340292ad06d8Schristos 	fgi->size = (unsigned int)((resid != NULL) ? *resid : 0);
34038ae0a67dSmanu 	np = (char *)(void *)(fgi + 1);
34048ae0a67dSmanu 	(void)strlcpy(np, attrname, attrnamelen);
34058ae0a67dSmanu 
34068ae0a67dSmanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
3407075ba0e5Smanu 		goto out;
34088ae0a67dSmanu 
34098ae0a67dSmanu 	/*
34108ae0a67dSmanu 	 * We just get fuse_getattr_out with list size if we requested
34118ae0a67dSmanu 	 * a null size.
34128ae0a67dSmanu 	 */
34138ae0a67dSmanu 	if (resid == NULL) {
34148ae0a67dSmanu 		fgo = GET_OUTPAYLOAD(ps, pm, fuse_getxattr_out);
34158ae0a67dSmanu 
34168ae0a67dSmanu 		if (attrsize != NULL)
34178ae0a67dSmanu 			*attrsize = fgo->size;
34188ae0a67dSmanu 
341971a2942bSmanu 		ps->ps_destroy_msg(pm);
3420075ba0e5Smanu 		error = 0;
3421075ba0e5Smanu 		goto out;
34228ae0a67dSmanu 	}
34238ae0a67dSmanu 
34248ae0a67dSmanu 	/*
34258ae0a67dSmanu 	 * And with a non null requested size, we get the list just
34268ae0a67dSmanu 	 * after the header
34278ae0a67dSmanu 	 */
34288ae0a67dSmanu 	foh = GET_OUTHDR(ps, pm);
34298ae0a67dSmanu 	np = (char *)(void *)(foh + 1);
34306a9d039fSmanu 	len = foh->len - sizeof(*foh);
34316a9d039fSmanu 
34326a9d039fSmanu 	if (attrsize != NULL)
34336a9d039fSmanu 		*attrsize = len;
34348ae0a67dSmanu 
34358ae0a67dSmanu 	if (resid != NULL) {
34366a9d039fSmanu 		if (*resid < len) {
34376a9d039fSmanu 			error = ERANGE;
3438ac713433Smanu 			ps->ps_destroy_msg(pm);
34396a9d039fSmanu 			goto out;
34406a9d039fSmanu 		}
34416a9d039fSmanu 
34428ae0a67dSmanu 		(void)memcpy(attr, np, len);
34438ae0a67dSmanu 		*resid -= len;
34448ae0a67dSmanu 	}
34458ae0a67dSmanu 
34468ae0a67dSmanu 	ps->ps_destroy_msg(pm);
3447075ba0e5Smanu 	error = 0;
34488ae0a67dSmanu 
3449075ba0e5Smanu out:
3450075ba0e5Smanu 	node_rele(opc);
3451075ba0e5Smanu 	return error;
34528ae0a67dSmanu }
34538ae0a67dSmanu 
34548ae0a67dSmanu int
perfuse_node_setextattr(struct puffs_usermount * pu,puffs_cookie_t opc,int attrns,const char * attrname,uint8_t * attr,size_t * resid,const struct puffs_cred * pcr)3455e1a2f47fSmatt perfuse_node_setextattr(struct puffs_usermount *pu, puffs_cookie_t opc,
3456e1a2f47fSmatt 	int attrns, const char *attrname, uint8_t *attr, size_t *resid,
3457e1a2f47fSmatt 	const struct puffs_cred *pcr)
34588ae0a67dSmanu {
34598ae0a67dSmanu 	struct perfuse_state *ps;
34608ae0a67dSmanu 	char fuse_attrname[LINUX_XATTR_NAME_MAX + 1];
34618ae0a67dSmanu 	perfuse_msg_t *pm;
34628ae0a67dSmanu 	struct fuse_setxattr_in *fsi;
34638ae0a67dSmanu 	size_t attrnamelen;
3464efbefee2Smanu 	size_t datalen;
34658ae0a67dSmanu 	size_t len;
34668ae0a67dSmanu 	char *np;
34678ae0a67dSmanu 	int error;
34688ae0a67dSmanu 
3469ac713433Smanu 	/* system namespace attrs are not accessible to non root users */
3470ac713433Smanu 	if (attrns == EXTATTR_NAMESPACE_SYSTEM && !puffs_cred_isjuggernaut(pcr))
3471ac713433Smanu 		return EPERM;
3472ac713433Smanu 
3473075ba0e5Smanu 	node_ref(opc);
34748ae0a67dSmanu 	ps = puffs_getspecific(pu);
34758ae0a67dSmanu 	attrname = perfuse_native_ns(attrns, attrname, fuse_attrname);
34768ae0a67dSmanu 	attrnamelen = strlen(attrname) + 1;
3477efbefee2Smanu 
3478efbefee2Smanu 	datalen = (resid != NULL) ? *resid : 0;
3479efbefee2Smanu 	len = sizeof(*fsi) + attrnamelen + datalen;
34808ae0a67dSmanu 
34818ae0a67dSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_SETXATTR, len, pcr);
34828ae0a67dSmanu 	fsi = GET_INPAYLOAD(ps, pm, fuse_setxattr_in);
3483efbefee2Smanu 	fsi->size = (unsigned int)datalen;
34848ae0a67dSmanu 	fsi->flags = 0;
34858ae0a67dSmanu 	np = (char *)(void *)(fsi + 1);
34868ae0a67dSmanu 	(void)strlcpy(np, attrname, attrnamelen);
34878ae0a67dSmanu 	np += attrnamelen;
3488efbefee2Smanu 	if (datalen)
3489efbefee2Smanu 		(void)memcpy(np, (char *)attr, datalen);
34908ae0a67dSmanu 
34918ae0a67dSmanu 	if ((error = xchg_msg(pu, opc, pm,
34928ae0a67dSmanu 			      NO_PAYLOAD_REPLY_LEN, wait_reply)) != 0)
3493075ba0e5Smanu 		goto out;
34948ae0a67dSmanu 
34958ae0a67dSmanu 	ps->ps_destroy_msg(pm);
3496efbefee2Smanu 	if (resid)
3497075ba0e5Smanu 		*resid = 0;
3498075ba0e5Smanu 	error = 0;
34998ae0a67dSmanu 
3500075ba0e5Smanu out:
3501075ba0e5Smanu 	node_rele(opc);
3502075ba0e5Smanu 	return error;
35038ae0a67dSmanu }
35048ae0a67dSmanu 
35058ae0a67dSmanu /* ARGSUSED2 */
35068ae0a67dSmanu int
perfuse_node_listextattr(struct puffs_usermount * pu,puffs_cookie_t opc,int attrns,size_t * attrsize,uint8_t * attrs,size_t * resid,int flag,const struct puffs_cred * pcr)3507e1a2f47fSmatt perfuse_node_listextattr(struct puffs_usermount *pu, puffs_cookie_t opc,
3508e1a2f47fSmatt 	int attrns, size_t *attrsize, uint8_t *attrs, size_t *resid, int flag,
3509e1a2f47fSmatt 	const struct puffs_cred *pcr)
35108ae0a67dSmanu {
35118ae0a67dSmanu 	struct perfuse_state *ps;
35128ae0a67dSmanu 	perfuse_msg_t *pm;
35138ae0a67dSmanu 	struct fuse_getxattr_in *fgi;
35148ae0a67dSmanu 	struct fuse_getxattr_out *fgo;
35158ae0a67dSmanu 	struct fuse_out_header *foh;
35168ae0a67dSmanu 	char *np;
3517ac713433Smanu 	size_t len, puffs_len, i, attrlen, outlen;
35188ae0a67dSmanu 	int error;
35198ae0a67dSmanu 
3520ac713433Smanu 	/* system namespace attrs are not accessible to non root users */
3521ac713433Smanu 	if (attrns == EXTATTR_NAMESPACE_SYSTEM && !puffs_cred_isjuggernaut(pcr))
3522ac713433Smanu 		return EPERM;
3523ac713433Smanu 
3524075ba0e5Smanu 	node_ref(opc);
3525075ba0e5Smanu 
35268ae0a67dSmanu 	ps = puffs_getspecific(pu);
35278ae0a67dSmanu 	len = sizeof(*fgi);
35288ae0a67dSmanu 
35298ae0a67dSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_LISTXATTR, len, pcr);
35308ae0a67dSmanu 	fgi = GET_INPAYLOAD(ps, pm, fuse_getxattr_in);
35318ae0a67dSmanu 	if (resid != NULL)
353292ad06d8Schristos 		fgi->size = (unsigned int)*resid;
35338ae0a67dSmanu 	else
35348ae0a67dSmanu 		fgi->size = 0;
35358ae0a67dSmanu 
35368ae0a67dSmanu 	if ((error = xchg_msg(pu, opc, pm, UNSPEC_REPLY_LEN, wait_reply)) != 0)
3537075ba0e5Smanu 		goto out;
35388ae0a67dSmanu 
35398ae0a67dSmanu 	/*
35408ae0a67dSmanu 	 * We just get fuse_getattr_out with list size if we requested
35418ae0a67dSmanu 	 * a null size.
35428ae0a67dSmanu 	 */
35438ae0a67dSmanu 	if (resid == NULL) {
35448ae0a67dSmanu 		fgo = GET_OUTPAYLOAD(ps, pm, fuse_getxattr_out);
35458ae0a67dSmanu 
35468ae0a67dSmanu 		if (attrsize != NULL)
35478ae0a67dSmanu 			*attrsize = fgo->size;
35488ae0a67dSmanu 
354971a2942bSmanu 		ps->ps_destroy_msg(pm);
355071a2942bSmanu 
3551075ba0e5Smanu 		error = 0;
3552075ba0e5Smanu 		goto out;
35538ae0a67dSmanu 	}
35548ae0a67dSmanu 
35558ae0a67dSmanu 	/*
35568ae0a67dSmanu 	 * And with a non null requested size, we get the list just
35578ae0a67dSmanu 	 * after the header
35588ae0a67dSmanu 	 */
35598ae0a67dSmanu 	foh = GET_OUTHDR(ps, pm);
35608ae0a67dSmanu 	np = (char *)(void *)(foh + 1);
35618ae0a67dSmanu 	puffs_len = foh->len - sizeof(*foh);
35628ae0a67dSmanu 
3563ac713433Smanu 	if (attrsize != NULL)
3564ac713433Smanu 		*attrsize = puffs_len;
3565ac713433Smanu 
35668ae0a67dSmanu 	if (attrs != NULL) {
3567ac713433Smanu 		if (*resid < puffs_len) {
3568ac713433Smanu 			error = ERANGE;
3569ac713433Smanu 			ps->ps_destroy_msg(pm);
3570ac713433Smanu 			goto out;
3571ac713433Smanu 		}
3572ac713433Smanu 
3573ac713433Smanu 		outlen = 0;
3574ac713433Smanu 
3575ac713433Smanu 		for (i = 0; i < puffs_len; i += attrlen + 1) {
3576ac713433Smanu 			attrlen = strlen(np + i);
3577ac713433Smanu 
3578ac713433Smanu 			/*
3579ac713433Smanu 			 * Filter attributes per namespace
3580ac713433Smanu 			 */
3581ac713433Smanu 			if (!perfuse_ns_match(attrns, np + i))
3582ac713433Smanu 				continue;
3583ac713433Smanu 
35844b1fc9a3Smanu #ifdef PUFFS_EXTATTR_LIST_LENPREFIX
3585be95d607Smanu 			/*
3586be95d607Smanu 			 * Convert the FUSE reply to length prefixed strings
3587be95d607Smanu 			 * if this is what the kernel wants.
3588be95d607Smanu 			 */
3589be95d607Smanu 			if (flag & PUFFS_EXTATTR_LIST_LENPREFIX) {
3590ac713433Smanu 				(void)memcpy(attrs + outlen + 1,
3591ac713433Smanu 					     np + i, attrlen);
3592ac713433Smanu 				*(attrs + outlen) = (uint8_t)attrlen;
3593ac713433Smanu 			} else
35944b1fc9a3Smanu #endif /* PUFFS_EXTATTR_LIST_LENPREFIX */
3595ac713433Smanu 			(void)memcpy(attrs + outlen, np + i, attrlen + 1);
3596ac713433Smanu 			outlen += attrlen + 1;
35978ae0a67dSmanu 		}
35988ae0a67dSmanu 
3599ac713433Smanu 		*resid -= outlen;
3600ac713433Smanu 	}
36018ae0a67dSmanu 
36028ae0a67dSmanu 	ps->ps_destroy_msg(pm);
3603075ba0e5Smanu 	error = 0;
36048ae0a67dSmanu 
3605075ba0e5Smanu out:
3606075ba0e5Smanu 	node_rele(opc);
3607075ba0e5Smanu 	return error;
36088ae0a67dSmanu }
36098ae0a67dSmanu 
36108ae0a67dSmanu int
perfuse_node_deleteextattr(struct puffs_usermount * pu,puffs_cookie_t opc,int attrns,const char * attrname,const struct puffs_cred * pcr)3611e1a2f47fSmatt perfuse_node_deleteextattr(struct puffs_usermount *pu, puffs_cookie_t opc,
3612e1a2f47fSmatt 	int attrns, const char *attrname, const struct puffs_cred *pcr)
36138ae0a67dSmanu {
36148ae0a67dSmanu 	struct perfuse_state *ps;
36158ae0a67dSmanu 	char fuse_attrname[LINUX_XATTR_NAME_MAX + 1];
36168ae0a67dSmanu 	perfuse_msg_t *pm;
36178ae0a67dSmanu 	size_t attrnamelen;
36188ae0a67dSmanu 	char *np;
36198ae0a67dSmanu 	int error;
36208ae0a67dSmanu 
3621ac713433Smanu 	/* system namespace attrs are not accessible to non root users */
3622ac713433Smanu 	if (attrns == EXTATTR_NAMESPACE_SYSTEM && !puffs_cred_isjuggernaut(pcr))
3623ac713433Smanu 		return EPERM;
3624ac713433Smanu 
3625075ba0e5Smanu 	node_ref(opc);
3626075ba0e5Smanu 
36278ae0a67dSmanu 	ps = puffs_getspecific(pu);
36288ae0a67dSmanu 	attrname = perfuse_native_ns(attrns, attrname, fuse_attrname);
36298ae0a67dSmanu 	attrnamelen = strlen(attrname) + 1;
36308ae0a67dSmanu 
36318ae0a67dSmanu 	pm = ps->ps_new_msg(pu, opc, FUSE_REMOVEXATTR, attrnamelen, pcr);
36328ae0a67dSmanu 	np = _GET_INPAYLOAD(ps, pm, char *);
36338ae0a67dSmanu 	(void)strlcpy(np, attrname, attrnamelen);
36348ae0a67dSmanu 
36358ae0a67dSmanu 	error = xchg_msg(pu, opc, pm, NO_PAYLOAD_REPLY_LEN, wait_reply);
363694c72edbSmanu 	if (error != 0)
363794c72edbSmanu 		goto out;
36388ae0a67dSmanu 
36398ae0a67dSmanu 	ps->ps_destroy_msg(pm);
36406645d525Smanu 
36416645d525Smanu out:
36426645d525Smanu 	node_rele(opc);
36436645d525Smanu 	return error;
36446645d525Smanu }
36456645d525Smanu 
36466645d525Smanu int
perfuse_node_fallocate(struct puffs_usermount * pu,puffs_cookie_t opc,off_t off,off_t len)36476645d525Smanu perfuse_node_fallocate(struct puffs_usermount *pu, puffs_cookie_t opc,
36486645d525Smanu 	off_t off, off_t len)
36496645d525Smanu {
36506645d525Smanu 	struct perfuse_state *ps;
36516645d525Smanu 	perfuse_msg_t *pm;
36526645d525Smanu 	struct fuse_fallocate_in *fai;
36536645d525Smanu 	int error;
36546645d525Smanu 
36556645d525Smanu 	ps = puffs_getspecific(pu);
36566645d525Smanu 	if (ps->ps_flags & PS_NO_FALLOCATE)
36576645d525Smanu 		return EOPNOTSUPP;
36586645d525Smanu 
36596645d525Smanu 	node_ref(opc);
36606645d525Smanu 
36616645d525Smanu 	pm = ps->ps_new_msg(pu, opc, FUSE_FALLOCATE, sizeof(*fai), NULL);
36626645d525Smanu 
36636645d525Smanu 	fai = GET_INPAYLOAD(ps, pm, fuse_fallocate_in);
3664ef486683Smanu 	fai->fh = PN_ISDIR(opc) ? FUSE_UNKNOWN_FH : perfuse_get_fh(opc, FWRITE);
36656645d525Smanu 	fai->offset = off;
36666645d525Smanu 	fai->length = len;
36676645d525Smanu 	fai->mode = 0;
36686645d525Smanu 
36696645d525Smanu 	error = xchg_msg(pu, opc, pm, NO_PAYLOAD_REPLY_LEN, wait_reply);
36706645d525Smanu 	if (error == EOPNOTSUPP || error == ENOSYS) {
36716645d525Smanu 		ps->ps_flags |= PS_NO_FALLOCATE;
36726645d525Smanu 		error = EOPNOTSUPP;
36736645d525Smanu 	}
36746645d525Smanu 	if (error != 0)
36756645d525Smanu 		goto out;
36766645d525Smanu 
36776645d525Smanu 	ps->ps_destroy_msg(pm);
36786645d525Smanu 
367994c72edbSmanu out:
3680075ba0e5Smanu 	node_rele(opc);
36818ae0a67dSmanu 	return error;
36828ae0a67dSmanu }
3683