151369649SPedro F. Giffuni /*-
251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni *
45fe58019SAttilio Rao * Copyright (c) 2007-2009 Google Inc. and Amit Singh
55fe58019SAttilio Rao * All rights reserved.
65fe58019SAttilio Rao *
75fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
85fe58019SAttilio Rao * modification, are permitted provided that the following conditions are
95fe58019SAttilio Rao * met:
105fe58019SAttilio Rao *
115fe58019SAttilio Rao * * Redistributions of source code must retain the above copyright
125fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
135fe58019SAttilio Rao * * Redistributions in binary form must reproduce the above
145fe58019SAttilio Rao * copyright notice, this list of conditions and the following disclaimer
155fe58019SAttilio Rao * in the documentation and/or other materials provided with the
165fe58019SAttilio Rao * distribution.
175fe58019SAttilio Rao * * Neither the name of Google Inc. nor the names of its
185fe58019SAttilio Rao * contributors may be used to endorse or promote products derived from
195fe58019SAttilio Rao * this software without specific prior written permission.
205fe58019SAttilio Rao *
215fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225fe58019SAttilio Rao * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
235fe58019SAttilio Rao * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
245fe58019SAttilio Rao * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
255fe58019SAttilio Rao * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
265fe58019SAttilio Rao * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
275fe58019SAttilio Rao * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
285fe58019SAttilio Rao * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
295fe58019SAttilio Rao * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
305fe58019SAttilio Rao * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315fe58019SAttilio Rao * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
325fe58019SAttilio Rao *
335fe58019SAttilio Rao * Copyright (C) 2005 Csaba Henk.
345fe58019SAttilio Rao * All rights reserved.
355fe58019SAttilio Rao *
368aafc8c3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
378aafc8c3SAlan Somers *
388aafc8c3SAlan Somers * Portions of this software were developed by BFF Storage Systems, LLC under
398aafc8c3SAlan Somers * sponsorship from the FreeBSD Foundation.
408aafc8c3SAlan Somers *
415fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
425fe58019SAttilio Rao * modification, are permitted provided that the following conditions
435fe58019SAttilio Rao * are met:
445fe58019SAttilio Rao * 1. Redistributions of source code must retain the above copyright
455fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
465fe58019SAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright
475fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer in the
485fe58019SAttilio Rao * documentation and/or other materials provided with the distribution.
495fe58019SAttilio Rao *
505fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
515fe58019SAttilio Rao * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
525fe58019SAttilio Rao * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
535fe58019SAttilio Rao * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
545fe58019SAttilio Rao * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
555fe58019SAttilio Rao * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
565fe58019SAttilio Rao * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
575fe58019SAttilio Rao * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
585fe58019SAttilio Rao * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
595fe58019SAttilio Rao * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
605fe58019SAttilio Rao * SUCH DAMAGE.
615fe58019SAttilio Rao */
625fe58019SAttilio Rao
635fe58019SAttilio Rao #include <sys/types.h>
6407e86257SAlan Somers #include <sys/systm.h>
65560a55d0SAlan Somers #include <sys/counter.h>
665fe58019SAttilio Rao #include <sys/module.h>
675fe58019SAttilio Rao #include <sys/errno.h>
685fe58019SAttilio Rao #include <sys/param.h>
695fe58019SAttilio Rao #include <sys/kernel.h>
705fe58019SAttilio Rao #include <sys/conf.h>
715fe58019SAttilio Rao #include <sys/uio.h>
725fe58019SAttilio Rao #include <sys/malloc.h>
735fe58019SAttilio Rao #include <sys/queue.h>
745fe58019SAttilio Rao #include <sys/lock.h>
755fe58019SAttilio Rao #include <sys/sx.h>
765fe58019SAttilio Rao #include <sys/mutex.h>
775fe58019SAttilio Rao #include <sys/proc.h>
785fe58019SAttilio Rao #include <sys/vnode.h>
795fe58019SAttilio Rao #include <sys/namei.h>
805fe58019SAttilio Rao #include <sys/mount.h>
815fe58019SAttilio Rao #include <sys/sysctl.h>
825fe58019SAttilio Rao #include <sys/fcntl.h>
835fe58019SAttilio Rao #include <sys/priv.h>
84e312493bSAlan Somers #include <sys/buf.h>
855fe58019SAttilio Rao #include <security/mac/mac_framework.h>
865fe58019SAttilio Rao #include <vm/vm.h>
875fe58019SAttilio Rao #include <vm/vm_extern.h>
885fe58019SAttilio Rao
895fe58019SAttilio Rao #include "fuse.h"
905fe58019SAttilio Rao #include "fuse_node.h"
915fe58019SAttilio Rao #include "fuse_internal.h"
925fe58019SAttilio Rao #include "fuse_io.h"
935fe58019SAttilio Rao #include "fuse_ipc.h"
945fe58019SAttilio Rao
95419e7ff6SAlan Somers SDT_PROVIDER_DECLARE(fusefs);
96cf169498SAlan Somers /*
97cf169498SAlan Somers * Fuse trace probe:
98cf169498SAlan Somers * arg0: verbosity. Higher numbers give more verbose messages
99cf169498SAlan Somers * arg1: Textual message
100cf169498SAlan Somers */
101419e7ff6SAlan Somers SDT_PROBE_DEFINE2(fusefs, , node, trace, "int", "char*");
1025fe58019SAttilio Rao
1035fe58019SAttilio Rao MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data");
1045fe58019SAttilio Rao
105c4af8b17SConrad Meyer static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS);
106c4af8b17SConrad Meyer
107560a55d0SAlan Somers static counter_u64_t fuse_node_count;
1085fe58019SAttilio Rao
109560a55d0SAlan Somers SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, node_count, CTLFLAG_RD,
110560a55d0SAlan Somers &fuse_node_count, "Count of FUSE vnodes");
1115fe58019SAttilio Rao
112c4af8b17SConrad Meyer int fuse_data_cache_mode = FUSE_CACHE_WT;
1135fe58019SAttilio Rao
114f8ebf1cdSAlan Somers /*
115c1326c01SAlan Somers * OBSOLETE
116c1326c01SAlan Somers * This sysctl is no longer needed as of fuse protocol 7.23. Now, individual
117f8ebf1cdSAlan Somers * servers can select the cache behavior they need for each mountpoint:
118f8ebf1cdSAlan Somers * - writethrough: the default
119f8ebf1cdSAlan Somers * - writeback: set FUSE_WRITEBACK_CACHE in fuse_init_out.flags
120f8ebf1cdSAlan Somers * - uncached: set FOPEN_DIRECT_IO for every file
121c1326c01SAlan Somers * The sysctl is retained primarily due to the enduring popularity of libfuse2,
122c1326c01SAlan Somers * which is frozen at protocol version 7.19. As of 4-April-2024, 90% of
123c1326c01SAlan Somers * FreeBSD ports that use libfuse still bind to libfuse2.
124f8ebf1cdSAlan Somers */
125388820fbSMateusz Guzik SYSCTL_PROC(_vfs_fusefs, OID_AUTO, data_cache_mode,
126388820fbSMateusz Guzik CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW,
127c4af8b17SConrad Meyer &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I",
128c4af8b17SConrad Meyer "Zero: disable caching of FUSE file data; One: write-through caching "
129c4af8b17SConrad Meyer "(default); Two: write-back caching (generally unsafe)");
1305fe58019SAttilio Rao
131c4af8b17SConrad Meyer static int
sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS)132c4af8b17SConrad Meyer sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS)
133c4af8b17SConrad Meyer {
134c4af8b17SConrad Meyer int val, error;
135c4af8b17SConrad Meyer
136c4af8b17SConrad Meyer val = *(int *)arg1;
137c4af8b17SConrad Meyer error = sysctl_handle_int(oidp, &val, 0, req);
138c4af8b17SConrad Meyer if (error || !req->newptr)
139c4af8b17SConrad Meyer return (error);
140c4af8b17SConrad Meyer
141c4af8b17SConrad Meyer switch (val) {
142c4af8b17SConrad Meyer case FUSE_CACHE_UC:
143c4af8b17SConrad Meyer case FUSE_CACHE_WT:
144c4af8b17SConrad Meyer case FUSE_CACHE_WB:
145c4af8b17SConrad Meyer *(int *)arg1 = val;
146c4af8b17SConrad Meyer break;
147c4af8b17SConrad Meyer default:
148c4af8b17SConrad Meyer return (EDOM);
149c4af8b17SConrad Meyer }
150c4af8b17SConrad Meyer return (0);
151c4af8b17SConrad Meyer }
152c4af8b17SConrad Meyer
1535fe58019SAttilio Rao static void
fuse_vnode_init(struct vnode * vp,struct fuse_vnode_data * fvdat,uint64_t nodeid,__enum_uint8 (vtype)vtyp)1545fe58019SAttilio Rao fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
155ba8cc6d7SMateusz Guzik uint64_t nodeid, __enum_uint8(vtype) vtyp)
1565fe58019SAttilio Rao {
1575fe58019SAttilio Rao fvdat->nid = nodeid;
1581cedd6dfSAlan Somers LIST_INIT(&fvdat->handles);
15955b80e2cSAlan Somers
160*7e68af7cSAlan Somers mtx_init(&fvdat->cached_attr_mtx, "fuse attr cache mutex", NULL,
161*7e68af7cSAlan Somers MTX_DEF);
16278a7722fSConrad Meyer vattr_null(&fvdat->cached_attrs);
16355b80e2cSAlan Somers fvdat->cached_attrs.va_birthtime.tv_sec = -1;
16455b80e2cSAlan Somers fvdat->cached_attrs.va_birthtime.tv_nsec = 0;
16555b80e2cSAlan Somers fvdat->cached_attrs.va_fsid = VNOVAL;
16655b80e2cSAlan Somers fvdat->cached_attrs.va_gen = 0;
16755b80e2cSAlan Somers fvdat->cached_attrs.va_rdev = NODEV;
16855b80e2cSAlan Somers
1695fe58019SAttilio Rao if (nodeid == FUSE_ROOT_ID) {
1705fe58019SAttilio Rao vp->v_vflag |= VV_ROOT;
1715fe58019SAttilio Rao }
1725fe58019SAttilio Rao vp->v_type = vtyp;
1735fe58019SAttilio Rao vp->v_data = fvdat;
1742bfd8992SKonstantin Belousov cluster_init_vn(&fvdat->clusterw);
17513d593a5SAlan Somers timespecclear(&fvdat->last_local_modify);
1765fe58019SAttilio Rao
177560a55d0SAlan Somers counter_u64_add(fuse_node_count, 1);
1785fe58019SAttilio Rao }
1795fe58019SAttilio Rao
1805fe58019SAttilio Rao void
fuse_vnode_destroy(struct vnode * vp)1815fe58019SAttilio Rao fuse_vnode_destroy(struct vnode *vp)
1825fe58019SAttilio Rao {
1835fe58019SAttilio Rao struct fuse_vnode_data *fvdat = vp->v_data;
1845fe58019SAttilio Rao
1855fe58019SAttilio Rao vp->v_data = NULL;
186*7e68af7cSAlan Somers mtx_destroy(&fvdat->cached_attr_mtx);
1871cedd6dfSAlan Somers KASSERT(LIST_EMPTY(&fvdat->handles),
1881cedd6dfSAlan Somers ("Destroying fuse vnode with open files!"));
1895fe58019SAttilio Rao free(fvdat, M_FUSEVN);
1905fe58019SAttilio Rao
191560a55d0SAlan Somers counter_u64_add(fuse_node_count, -1);
1925fe58019SAttilio Rao }
1935fe58019SAttilio Rao
194e5b50fe7SAlan Somers int
fuse_vnode_cmp(struct vnode * vp,void * nidp)1955fe58019SAttilio Rao fuse_vnode_cmp(struct vnode *vp, void *nidp)
1965fe58019SAttilio Rao {
1975fe58019SAttilio Rao return (VTOI(vp) != *((uint64_t *)nidp));
1985fe58019SAttilio Rao }
1995fe58019SAttilio Rao
200ba8cc6d7SMateusz Guzik SDT_PROBE_DEFINE3(fusefs, , node, stale_vnode, "struct vnode*", "uint8_t",
201f82e92e5SAlan Somers "uint64_t");
2025fe58019SAttilio Rao static int
fuse_vnode_alloc(struct mount * mp,struct thread * td,uint64_t nodeid,__enum_uint8 (vtype)vtyp,struct vnode ** vpp)2035fe58019SAttilio Rao fuse_vnode_alloc(struct mount *mp,
2045fe58019SAttilio Rao struct thread *td,
2055fe58019SAttilio Rao uint64_t nodeid,
206ba8cc6d7SMateusz Guzik __enum_uint8(vtype) vtyp,
2075fe58019SAttilio Rao struct vnode **vpp)
2085fe58019SAttilio Rao {
209e97ae4adSAlan Somers struct fuse_data *data;
2105fe58019SAttilio Rao struct fuse_vnode_data *fvdat;
2115fe58019SAttilio Rao struct vnode *vp2;
2125fe58019SAttilio Rao int err = 0;
2135fe58019SAttilio Rao
214e97ae4adSAlan Somers data = fuse_get_mpdata(mp);
2155fe58019SAttilio Rao if (vtyp == VNON) {
2165fe58019SAttilio Rao return EINVAL;
2175fe58019SAttilio Rao }
2185fe58019SAttilio Rao *vpp = NULL;
2195fe58019SAttilio Rao err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,
2205fe58019SAttilio Rao fuse_vnode_cmp, &nodeid);
2215fe58019SAttilio Rao if (err)
2225fe58019SAttilio Rao return (err);
2235fe58019SAttilio Rao
2245fe58019SAttilio Rao if (*vpp) {
22525927e06SAlan Somers if ((*vpp)->v_type == vtyp) {
22625927e06SAlan Somers /* Reuse a vnode that hasn't yet been reclaimed */
22725927e06SAlan Somers MPASS((*vpp)->v_data != NULL);
22825927e06SAlan Somers MPASS(VTOFUD(*vpp)->nid == nodeid);
22925927e06SAlan Somers SDT_PROBE2(fusefs, , node, trace, 1,
23025927e06SAlan Somers "vnode taken from hash");
23125927e06SAlan Somers return (0);
23225927e06SAlan Somers } else {
23364f31d4fSAlan Somers /*
23425927e06SAlan Somers * The inode changed types! If we get here, we can't
23525927e06SAlan Somers * tell whether the inode's entry cache had expired
23625927e06SAlan Somers * yet. So this could be the result of a buggy server,
23725927e06SAlan Somers * but more likely the server just reused an inode
23825927e06SAlan Somers * number following an entry cache expiration.
23964f31d4fSAlan Somers */
240f82e92e5SAlan Somers SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp,
241f82e92e5SAlan Somers nodeid);
24264f31d4fSAlan Somers fuse_internal_vnode_disappear(*vpp);
24325927e06SAlan Somers vgone(*vpp);
24472f03b7cSAlan Somers lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
24564f31d4fSAlan Somers }
2465fe58019SAttilio Rao }
2475fe58019SAttilio Rao fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
248f9b0e30bSAlan Somers switch (vtyp) {
249f9b0e30bSAlan Somers case VFIFO:
250f9b0e30bSAlan Somers err = getnewvnode("fuse", mp, &fuse_fifoops, vpp);
251f9b0e30bSAlan Somers break;
252f9b0e30bSAlan Somers default:
2535fe58019SAttilio Rao err = getnewvnode("fuse", mp, &fuse_vnops, vpp);
254f9b0e30bSAlan Somers break;
255f9b0e30bSAlan Somers }
2565fe58019SAttilio Rao if (err) {
2575fe58019SAttilio Rao free(fvdat, M_FUSEVN);
2585fe58019SAttilio Rao return (err);
2595fe58019SAttilio Rao }
2605fe58019SAttilio Rao lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL);
2615fe58019SAttilio Rao fuse_vnode_init(*vpp, fvdat, nodeid, vtyp);
2625fe58019SAttilio Rao err = insmntque(*vpp, mp);
2635fe58019SAttilio Rao ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
2645fe58019SAttilio Rao if (err) {
26572f03b7cSAlan Somers lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
2665fe58019SAttilio Rao free(fvdat, M_FUSEVN);
2675fe58019SAttilio Rao *vpp = NULL;
2685fe58019SAttilio Rao return (err);
2695fe58019SAttilio Rao }
270e97ae4adSAlan Somers /* Disallow async reads for fifos because UFS does. I don't know why */
271e97ae4adSAlan Somers if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO)
272e97ae4adSAlan Somers VN_LOCK_ASHARE(*vpp);
273e97ae4adSAlan Somers
274829f0bcbSMateusz Guzik vn_set_state(*vpp, VSTATE_CONSTRUCTED);
2755fe58019SAttilio Rao err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE,
2765fe58019SAttilio Rao td, &vp2, fuse_vnode_cmp, &nodeid);
27772f03b7cSAlan Somers if (err) {
27872f03b7cSAlan Somers lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
27972f03b7cSAlan Somers free(fvdat, M_FUSEVN);
28072f03b7cSAlan Somers *vpp = NULL;
2815fe58019SAttilio Rao return (err);
28272f03b7cSAlan Somers }
2835fe58019SAttilio Rao if (vp2 != NULL) {
2845fe58019SAttilio Rao *vpp = vp2;
2855fe58019SAttilio Rao return (0);
2865fe58019SAttilio Rao }
2875fe58019SAttilio Rao
2885fe58019SAttilio Rao ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
2895fe58019SAttilio Rao
2905fe58019SAttilio Rao return (0);
2915fe58019SAttilio Rao }
2925fe58019SAttilio Rao
2935fe58019SAttilio Rao int
fuse_vnode_get(struct mount * mp,struct fuse_entry_out * feo,uint64_t nodeid,struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp,__enum_uint8 (vtype)vtyp)2945fe58019SAttilio Rao fuse_vnode_get(struct mount *mp,
29509176f09SConrad Meyer struct fuse_entry_out *feo,
2965fe58019SAttilio Rao uint64_t nodeid,
2975fe58019SAttilio Rao struct vnode *dvp,
2985fe58019SAttilio Rao struct vnode **vpp,
2995fe58019SAttilio Rao struct componentname *cnp,
300ba8cc6d7SMateusz Guzik __enum_uint8(vtype) vtyp)
3015fe58019SAttilio Rao {
302b4a58fbfSMateusz Guzik struct thread *td = curthread;
303f1ec3bc0SAlan Somers bool exportable = fuse_get_mpdata(mp)->dataflags & FSESS_EXPORT_SUPPORT;
304f1ec3bc0SAlan Somers
305db7b0e74SAlan Somers /*
306db7b0e74SAlan Somers * feo should only be NULL for the root directory, which (when libfuse
307db7b0e74SAlan Somers * is used) always has generation 0
308db7b0e74SAlan Somers */
309db7b0e74SAlan Somers uint64_t generation = feo ? feo->generation : 0;
3105fe58019SAttilio Rao int err = 0;
3115fe58019SAttilio Rao
3120bef4927SAlan Somers if (dvp != NULL && VTOFUD(dvp)->nid == nodeid) {
3130bef4927SAlan Somers fuse_warn(fuse_get_mpdata(mp), FSESS_WARN_ILLEGAL_INODE,
3140bef4927SAlan Somers "Assigned same inode to both parent and child.");
3150bef4927SAlan Somers return EIO;
3160bef4927SAlan Somers }
317f1ec3bc0SAlan Somers if (feo && feo->nodeid != feo->attr.ino && exportable) {
318f1ec3bc0SAlan Somers /*
319f1ec3bc0SAlan Somers * NFS servers (both kernelspace and userspace) rely on
320f1ec3bc0SAlan Somers * VFS_VGET to lookup inodes. But that's only possible if the
321f1ec3bc0SAlan Somers * file's inode number matches its nodeid, which isn't
322f1ec3bc0SAlan Somers * necessarily the case for FUSE. If they don't match, then we
323f1ec3bc0SAlan Somers * can complete the current operation, but future VFS_VGET
324f1ec3bc0SAlan Somers * operations will almost certainly return spurious results.
325f1ec3bc0SAlan Somers * Warn the operator.
326f1ec3bc0SAlan Somers *
327f1ec3bc0SAlan Somers * But only warn the operator if the file system reports
328f1ec3bc0SAlan Somers * NFS-compatibility, because that's the only time that this
329f1ec3bc0SAlan Somers * matters, and dumb fuse servers abound.
330f1ec3bc0SAlan Somers */
331f1ec3bc0SAlan Somers fuse_warn(fuse_get_mpdata(mp), FSESS_WARN_INODE_MISMATCH,
332f1ec3bc0SAlan Somers "file has different inode number and nodeid.");
333f1ec3bc0SAlan Somers }
3340bef4927SAlan Somers
3355fe58019SAttilio Rao err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp);
3365fe58019SAttilio Rao if (err) {
3375fe58019SAttilio Rao return err;
3385fe58019SAttilio Rao }
3395fe58019SAttilio Rao if (dvp != NULL) {
3403885d409SAlan Somers MPASS(cnp && (cnp->cn_flags & ISDOTDOT) == 0);
3413885d409SAlan Somers MPASS(cnp &&
3423885d409SAlan Somers !(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
3435fe58019SAttilio Rao fuse_vnode_setparent(*vpp, dvp);
3445fe58019SAttilio Rao }
34509176f09SConrad Meyer if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 &&
34609176f09SConrad Meyer feo != NULL &&
34709176f09SConrad Meyer (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) {
34844f10c6eSAlan Somers struct timespec timeout;
349ccb75e49SAlan Somers
3505fe58019SAttilio Rao ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
3515fe58019SAttilio Rao ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
3523f2c630cSAlan Somers
35344f10c6eSAlan Somers fuse_validity_2_timespec(feo, &timeout);
3543f2c630cSAlan Somers cache_enter_time(dvp, *vpp, cnp, &timeout, NULL);
3553f2c630cSAlan Somers }
3565fe58019SAttilio Rao
357e5b50fe7SAlan Somers VTOFUD(*vpp)->generation = generation;
3585fe58019SAttilio Rao /*
3595fe58019SAttilio Rao * In userland, libfuse uses cached lookups for dot and dotdot entries,
3605fe58019SAttilio Rao * thus it does not really bump the nlookup counter for forget.
361e5b50fe7SAlan Somers * Follow the same semantic and avoid the bump in order to keep
3625fe58019SAttilio Rao * nlookup counters consistent.
3635fe58019SAttilio Rao */
3645fe58019SAttilio Rao if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 &&
3655fe58019SAttilio Rao (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.')))
3665fe58019SAttilio Rao VTOFUD(*vpp)->nlookup++;
3675fe58019SAttilio Rao
3685fe58019SAttilio Rao return 0;
3695fe58019SAttilio Rao }
3705fe58019SAttilio Rao
3713f105d16SAlan Somers /*
3723f105d16SAlan Somers * Called for every fusefs vnode open to initialize the vnode (not
3733f105d16SAlan Somers * fuse_filehandle) for use
3743f105d16SAlan Somers */
3755fe58019SAttilio Rao void
fuse_vnode_open(struct vnode * vp,int32_t fuse_open_flags,struct thread * td)3765fe58019SAttilio Rao fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
3775fe58019SAttilio Rao {
3783f105d16SAlan Somers if (vnode_vtype(vp) == VREG)
37956a8aca8SPawel Jakub Dawidek vnode_create_vobject(vp, VNODE_NO_SIZE, td);
3805fe58019SAttilio Rao }
3815fe58019SAttilio Rao
3825fe58019SAttilio Rao int
fuse_vnode_savesize(struct vnode * vp,struct ucred * cred,pid_t pid)383f8d4af10SAlan Somers fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
3845fe58019SAttilio Rao {
3855fe58019SAttilio Rao struct fuse_vnode_data *fvdat = VTOFUD(vp);
3865fe58019SAttilio Rao struct thread *td = curthread;
3875fe58019SAttilio Rao struct fuse_filehandle *fufh = NULL;
3885fe58019SAttilio Rao struct fuse_dispatcher fdi;
3895fe58019SAttilio Rao struct fuse_setattr_in *fsai;
3905fe58019SAttilio Rao int err = 0;
3915fe58019SAttilio Rao
392*7e68af7cSAlan Somers ASSERT_VOP_ELOCKED(vp, __func__); /* For flag and last_local_modify */
393*7e68af7cSAlan Somers ASSERT_CACHED_ATTRS_LOCKED(vp);
3945fe58019SAttilio Rao
3955fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
3965fe58019SAttilio Rao return EBADF;
3975fe58019SAttilio Rao }
3985fe58019SAttilio Rao if (vnode_vtype(vp) == VDIR) {
3995fe58019SAttilio Rao return EISDIR;
4005fe58019SAttilio Rao }
4015fe58019SAttilio Rao if (vfs_isrdonly(vnode_mount(vp))) {
4025fe58019SAttilio Rao return EROFS;
4035fe58019SAttilio Rao }
4045fe58019SAttilio Rao if (cred == NULL) {
4055fe58019SAttilio Rao cred = td->td_ucred;
4065fe58019SAttilio Rao }
4075fe58019SAttilio Rao fdisp_init(&fdi, sizeof(*fsai));
4085fe58019SAttilio Rao fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
4095fe58019SAttilio Rao fsai = fdi.indata;
4105fe58019SAttilio Rao fsai->valid = 0;
4115fe58019SAttilio Rao
4125fe58019SAttilio Rao /* Truncate to a new value. */
4133d15b234SAlan Somers MPASS((fvdat->flag & FN_SIZECHANGE) != 0);
4143d15b234SAlan Somers fsai->size = fvdat->cached_attrs.va_size;
4155fe58019SAttilio Rao fsai->valid |= FATTR_SIZE;
4165fe58019SAttilio Rao
4179f10f423SAlan Somers fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
4185fe58019SAttilio Rao if (fufh) {
4195fe58019SAttilio Rao fsai->fh = fufh->fh_id;
4205fe58019SAttilio Rao fsai->valid |= FATTR_FH;
4215fe58019SAttilio Rao }
4225fe58019SAttilio Rao err = fdisp_wait_answ(&fdi);
4235fe58019SAttilio Rao fdisp_destroy(&fdi);
42413d593a5SAlan Somers if (err == 0) {
42513d593a5SAlan Somers getnanouptime(&fvdat->last_local_modify);
4265fe58019SAttilio Rao fvdat->flag &= ~FN_SIZECHANGE;
42713d593a5SAlan Somers }
4285fe58019SAttilio Rao
4295fe58019SAttilio Rao return err;
4305fe58019SAttilio Rao }
4315fe58019SAttilio Rao
4323d15b234SAlan Somers /*
4335d94aaacSAlan Somers * Adjust the vnode's size to a new value.
4345d94aaacSAlan Somers *
4355d94aaacSAlan Somers * If the new value came from the server, such as from a FUSE_GETATTR
4365d94aaacSAlan Somers * operation, set `from_server` true. But if it came from a local operation,
4375d94aaacSAlan Somers * such as write(2) or truncate(2), set `from_server` false.
4383d15b234SAlan Somers */
4395fe58019SAttilio Rao int
fuse_vnode_setsize(struct vnode * vp,off_t newsize,bool from_server)4405d94aaacSAlan Somers fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
4415fe58019SAttilio Rao {
4425fe58019SAttilio Rao struct fuse_vnode_data *fvdat = VTOFUD(vp);
443cad67791SAlan Somers struct vattr *attrs;
4445fe58019SAttilio Rao off_t oldsize;
445e312493bSAlan Somers size_t iosize;
4465fe58019SAttilio Rao int err = 0;
4475fe58019SAttilio Rao
448*7e68af7cSAlan Somers ASSERT_VOP_LOCKED(vp, __func__);
449*7e68af7cSAlan Somers ASSERT_CACHED_ATTRS_LOCKED(vp);
4505fe58019SAttilio Rao
451e312493bSAlan Somers iosize = fuse_iosize(vp);
4523d15b234SAlan Somers oldsize = fvdat->cached_attrs.va_size;
4533d15b234SAlan Somers fvdat->cached_attrs.va_size = newsize;
454cad67791SAlan Somers if ((attrs = VTOVA(vp)) != NULL)
455cad67791SAlan Somers attrs->va_size = newsize;
4565fe58019SAttilio Rao
457*7e68af7cSAlan Somers if (from_server && newsize > oldsize && oldsize != VNOVAL) {
458*7e68af7cSAlan Somers /*
459*7e68af7cSAlan Somers * The FUSE server changed the file size behind our back. We
460*7e68af7cSAlan Somers * should invalidate the entire cache.
461*7e68af7cSAlan Somers */
462*7e68af7cSAlan Somers daddr_t end_lbn;
463*7e68af7cSAlan Somers
464*7e68af7cSAlan Somers end_lbn = howmany(newsize, iosize);
465*7e68af7cSAlan Somers v_inval_buf_range(vp, 0, end_lbn, iosize);
466*7e68af7cSAlan Somers }
467*7e68af7cSAlan Somers
468*7e68af7cSAlan Somers if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) {
469*7e68af7cSAlan Somers err = fuse_vnode_setsize_immediate(vp, newsize < oldsize);
470*7e68af7cSAlan Somers } else {
471*7e68af7cSAlan Somers /* Without an exclusive vnode lock, we must defer the operation */
472*7e68af7cSAlan Somers fvdat->flag |= FN_DELAYED_TRUNCATE;
473*7e68af7cSAlan Somers vn_delayed_setsize(vp);
474*7e68af7cSAlan Somers }
475*7e68af7cSAlan Somers
476*7e68af7cSAlan Somers return err;
477*7e68af7cSAlan Somers }
478*7e68af7cSAlan Somers
479*7e68af7cSAlan Somers /* Immediately set the vnode's size in the pager */
480*7e68af7cSAlan Somers int
fuse_vnode_setsize_immediate(struct vnode * vp,bool shrink)481*7e68af7cSAlan Somers fuse_vnode_setsize_immediate(struct vnode *vp, bool shrink)
482*7e68af7cSAlan Somers {
483*7e68af7cSAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
484*7e68af7cSAlan Somers struct buf *bp = NULL;
485*7e68af7cSAlan Somers size_t iosize;
486*7e68af7cSAlan Somers off_t newsize;
487*7e68af7cSAlan Somers int err = 0;
488*7e68af7cSAlan Somers
489*7e68af7cSAlan Somers MPASS(fvdat);
490*7e68af7cSAlan Somers ASSERT_VOP_ELOCKED(vp, __func__);
491*7e68af7cSAlan Somers
492*7e68af7cSAlan Somers iosize = fuse_iosize(vp);
493*7e68af7cSAlan Somers newsize = fvdat->cached_attrs.va_size;
494*7e68af7cSAlan Somers
495*7e68af7cSAlan Somers if (shrink) {
496e312493bSAlan Somers daddr_t lbn;
497e312493bSAlan Somers
49865417f5eSAlan Somers err = vtruncbuf(vp, newsize, fuse_iosize(vp));
499e312493bSAlan Somers if (err)
500e312493bSAlan Somers goto out;
501e312493bSAlan Somers if (newsize % iosize == 0)
502e312493bSAlan Somers goto out;
503e312493bSAlan Somers /*
504e312493bSAlan Somers * Zero the contents of the last partial block.
505e312493bSAlan Somers * Sure seems like vtruncbuf should do this for us.
506e312493bSAlan Somers */
507e312493bSAlan Somers
508e312493bSAlan Somers lbn = newsize / iosize;
509e312493bSAlan Somers bp = getblk(vp, lbn, iosize, PCATCH, 0, 0);
510e312493bSAlan Somers if (!bp) {
511e312493bSAlan Somers err = EINTR;
512e312493bSAlan Somers goto out;
5135fe58019SAttilio Rao }
514e312493bSAlan Somers if (!(bp->b_flags & B_CACHE))
515e312493bSAlan Somers goto out; /* Nothing to do */
516bad3de43SAlan Somers MPASS(bp->b_flags & B_VMIO);
517bad3de43SAlan Somers vfs_bio_clrbuf(bp);
51893c0c1d4SAlan Somers bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
519e312493bSAlan Somers }
520e312493bSAlan Somers out:
521e312493bSAlan Somers if (bp)
522e312493bSAlan Somers brelse(bp);
5235fe58019SAttilio Rao vnode_pager_setsize(vp, newsize);
524*7e68af7cSAlan Somers
525*7e68af7cSAlan Somers return (err);
5265fe58019SAttilio Rao }
5273d15b234SAlan Somers
5283d15b234SAlan Somers /* Get the current, possibly dirty, size of the file */
5293d15b234SAlan Somers int
fuse_vnode_size(struct vnode * vp,off_t * filesize,struct ucred * cred,struct thread * td)5303d15b234SAlan Somers fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
5313d15b234SAlan Somers struct thread *td)
5323d15b234SAlan Somers {
5333d15b234SAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
534*7e68af7cSAlan Somers struct vattr va;
5353d15b234SAlan Somers int error = 0;
5363d15b234SAlan Somers
537*7e68af7cSAlan Somers ASSERT_VOP_LOCKED(vp, __func__);
538*7e68af7cSAlan Somers
539*7e68af7cSAlan Somers CACHED_ATTR_LOCK(vp);
5403d15b234SAlan Somers if (!(fvdat->flag & FN_SIZECHANGE) &&
541b0ecfb42SAlan Somers (!fuse_vnode_attr_cache_valid(vp) ||
542*7e68af7cSAlan Somers fvdat->cached_attrs.va_size == VNOVAL)) {
543*7e68af7cSAlan Somers CACHED_ATTR_UNLOCK(vp);
544*7e68af7cSAlan Somers /*
545*7e68af7cSAlan Somers * It incurs a large struct copy, but we supply &va so we don't
546*7e68af7cSAlan Somers * have to acquire the lock a second time after
547*7e68af7cSAlan Somers * fuse_internal_do_getattr returns.
548*7e68af7cSAlan Somers */
549*7e68af7cSAlan Somers error = fuse_internal_do_getattr(vp, &va, cred, td);
5503d15b234SAlan Somers if (!error)
551*7e68af7cSAlan Somers *filesize = va.va_size;
552*7e68af7cSAlan Somers } else {
5533d15b234SAlan Somers *filesize = fvdat->cached_attrs.va_size;
554*7e68af7cSAlan Somers CACHED_ATTR_UNLOCK(vp);
555*7e68af7cSAlan Somers }
5563d15b234SAlan Somers
5573d15b234SAlan Somers return error;
5583d15b234SAlan Somers }
559788af953SAlan Somers
560788af953SAlan Somers void
fuse_vnode_undirty_cached_timestamps(struct vnode * vp,bool atime)56191972cfcSAlan Somers fuse_vnode_undirty_cached_timestamps(struct vnode *vp, bool atime)
562788af953SAlan Somers {
563788af953SAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
564788af953SAlan Somers
565*7e68af7cSAlan Somers ASSERT_CACHED_ATTRS_LOCKED(vp);
566*7e68af7cSAlan Somers
567788af953SAlan Somers fvdat->flag &= ~(FN_MTIMECHANGE | FN_CTIMECHANGE);
56891972cfcSAlan Somers if (atime)
56991972cfcSAlan Somers fvdat->flag &= ~FN_ATIMECHANGE;
570788af953SAlan Somers }
571788af953SAlan Somers
572788af953SAlan Somers /* Update a fuse file's cached timestamps */
573788af953SAlan Somers void
fuse_vnode_update(struct vnode * vp,int flags)574788af953SAlan Somers fuse_vnode_update(struct vnode *vp, int flags)
575788af953SAlan Somers {
576788af953SAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
57791972cfcSAlan Somers struct mount *mp = vnode_mount(vp);
57891972cfcSAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
579788af953SAlan Somers struct timespec ts;
580788af953SAlan Somers
581788af953SAlan Somers vfs_timestamp(&ts);
582788af953SAlan Somers
583fef46454SAlan Somers if (data->time_gran > 1)
584fef46454SAlan Somers ts.tv_nsec = rounddown(ts.tv_nsec, data->time_gran);
585fef46454SAlan Somers
58691972cfcSAlan Somers if (mp->mnt_flag & MNT_NOATIME)
58791972cfcSAlan Somers flags &= ~FN_ATIMECHANGE;
58891972cfcSAlan Somers
589*7e68af7cSAlan Somers CACHED_ATTR_LOCK(vp);
590*7e68af7cSAlan Somers
59191972cfcSAlan Somers if (flags & FN_ATIMECHANGE)
59291972cfcSAlan Somers fvdat->cached_attrs.va_atime = ts;
593788af953SAlan Somers if (flags & FN_MTIMECHANGE)
594788af953SAlan Somers fvdat->cached_attrs.va_mtime = ts;
595788af953SAlan Somers if (flags & FN_CTIMECHANGE)
596788af953SAlan Somers fvdat->cached_attrs.va_ctime = ts;
597788af953SAlan Somers
598788af953SAlan Somers fvdat->flag |= flags;
599*7e68af7cSAlan Somers
600*7e68af7cSAlan Somers CACHED_ATTR_UNLOCK(vp);
601788af953SAlan Somers }
602560a55d0SAlan Somers
603560a55d0SAlan Somers void
fuse_node_init(void)604560a55d0SAlan Somers fuse_node_init(void)
605560a55d0SAlan Somers {
606560a55d0SAlan Somers fuse_node_count = counter_u64_alloc(M_WAITOK);
607560a55d0SAlan Somers }
608560a55d0SAlan Somers
609560a55d0SAlan Somers void
fuse_node_destroy(void)610560a55d0SAlan Somers fuse_node_destroy(void)
611560a55d0SAlan Somers {
612560a55d0SAlan Somers counter_u64_free(fuse_node_count);
613560a55d0SAlan Somers }
614