xref: /src/sys/fs/fuse/fuse_node.c (revision 7e68af7ce2c1b892954df415774fe59fd2f1b62f)
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