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