xref: /freebsd/sys/fs/fuse/fuse_node.c (revision 56a8aca83ab5a93af05f4c8c3a358b71a8392af8)
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
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
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 
16078a7722fSConrad Meyer 	vattr_null(&fvdat->cached_attrs);
16155b80e2cSAlan Somers 	fvdat->cached_attrs.va_birthtime.tv_sec = -1;
16255b80e2cSAlan Somers 	fvdat->cached_attrs.va_birthtime.tv_nsec = 0;
16355b80e2cSAlan Somers 	fvdat->cached_attrs.va_fsid = VNOVAL;
16455b80e2cSAlan Somers 	fvdat->cached_attrs.va_gen = 0;
16555b80e2cSAlan Somers 	fvdat->cached_attrs.va_rdev = NODEV;
16655b80e2cSAlan Somers 
1675fe58019SAttilio Rao 	if (nodeid == FUSE_ROOT_ID) {
1685fe58019SAttilio Rao 		vp->v_vflag |= VV_ROOT;
1695fe58019SAttilio Rao 	}
1705fe58019SAttilio Rao 	vp->v_type = vtyp;
1715fe58019SAttilio Rao 	vp->v_data = fvdat;
1722bfd8992SKonstantin Belousov 	cluster_init_vn(&fvdat->clusterw);
17313d593a5SAlan Somers 	timespecclear(&fvdat->last_local_modify);
1745fe58019SAttilio Rao 
175560a55d0SAlan Somers 	counter_u64_add(fuse_node_count, 1);
1765fe58019SAttilio Rao }
1775fe58019SAttilio Rao 
1785fe58019SAttilio Rao void
1795fe58019SAttilio Rao fuse_vnode_destroy(struct vnode *vp)
1805fe58019SAttilio Rao {
1815fe58019SAttilio Rao 	struct fuse_vnode_data *fvdat = vp->v_data;
1825fe58019SAttilio Rao 
1835fe58019SAttilio Rao 	vp->v_data = NULL;
1841cedd6dfSAlan Somers 	KASSERT(LIST_EMPTY(&fvdat->handles),
1851cedd6dfSAlan Somers 		("Destroying fuse vnode with open files!"));
1865fe58019SAttilio Rao 	free(fvdat, M_FUSEVN);
1875fe58019SAttilio Rao 
188560a55d0SAlan Somers 	counter_u64_add(fuse_node_count, -1);
1895fe58019SAttilio Rao }
1905fe58019SAttilio Rao 
191e5b50fe7SAlan Somers int
1925fe58019SAttilio Rao fuse_vnode_cmp(struct vnode *vp, void *nidp)
1935fe58019SAttilio Rao {
1945fe58019SAttilio Rao 	return (VTOI(vp) != *((uint64_t *)nidp));
1955fe58019SAttilio Rao }
1965fe58019SAttilio Rao 
197ba8cc6d7SMateusz Guzik SDT_PROBE_DEFINE3(fusefs, , node, stale_vnode, "struct vnode*", "uint8_t",
198f82e92e5SAlan Somers 		"uint64_t");
1995fe58019SAttilio Rao static int
2005fe58019SAttilio Rao fuse_vnode_alloc(struct mount *mp,
2015fe58019SAttilio Rao     struct thread *td,
2025fe58019SAttilio Rao     uint64_t nodeid,
203ba8cc6d7SMateusz Guzik     __enum_uint8(vtype) vtyp,
2045fe58019SAttilio Rao     struct vnode **vpp)
2055fe58019SAttilio Rao {
206e97ae4adSAlan Somers 	struct fuse_data *data;
2075fe58019SAttilio Rao 	struct fuse_vnode_data *fvdat;
2085fe58019SAttilio Rao 	struct vnode *vp2;
2095fe58019SAttilio Rao 	int err = 0;
2105fe58019SAttilio Rao 
211e97ae4adSAlan Somers 	data = fuse_get_mpdata(mp);
2125fe58019SAttilio Rao 	if (vtyp == VNON) {
2135fe58019SAttilio Rao 		return EINVAL;
2145fe58019SAttilio Rao 	}
2155fe58019SAttilio Rao 	*vpp = NULL;
2165fe58019SAttilio Rao 	err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,
2175fe58019SAttilio Rao 	    fuse_vnode_cmp, &nodeid);
2185fe58019SAttilio Rao 	if (err)
2195fe58019SAttilio Rao 		return (err);
2205fe58019SAttilio Rao 
2215fe58019SAttilio Rao 	if (*vpp) {
22225927e06SAlan Somers 		if ((*vpp)->v_type == vtyp) {
22325927e06SAlan Somers 			/* Reuse a vnode that hasn't yet been reclaimed */
22425927e06SAlan Somers 			MPASS((*vpp)->v_data != NULL);
22525927e06SAlan Somers 			MPASS(VTOFUD(*vpp)->nid == nodeid);
22625927e06SAlan Somers 			SDT_PROBE2(fusefs, , node, trace, 1,
22725927e06SAlan Somers 				"vnode taken from hash");
22825927e06SAlan Somers 			return (0);
22925927e06SAlan Somers 		} else {
23064f31d4fSAlan Somers 			/*
23125927e06SAlan Somers 			 * The inode changed types!  If we get here, we can't
23225927e06SAlan Somers 			 * tell whether the inode's entry cache had expired
23325927e06SAlan Somers 			 * yet.  So this could be the result of a buggy server,
23425927e06SAlan Somers 			 * but more likely the server just reused an inode
23525927e06SAlan Somers 			 * number following an entry cache expiration.
23664f31d4fSAlan Somers 			 */
237f82e92e5SAlan Somers 			SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp,
238f82e92e5SAlan Somers 				nodeid);
23964f31d4fSAlan Somers 			fuse_internal_vnode_disappear(*vpp);
24025927e06SAlan Somers 			vgone(*vpp);
24172f03b7cSAlan Somers 			lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
24264f31d4fSAlan Somers 		}
2435fe58019SAttilio Rao 	}
2445fe58019SAttilio Rao 	fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
245f9b0e30bSAlan Somers 	switch (vtyp) {
246f9b0e30bSAlan Somers 	case VFIFO:
247f9b0e30bSAlan Somers 		err = getnewvnode("fuse", mp, &fuse_fifoops, vpp);
248f9b0e30bSAlan Somers 		break;
249f9b0e30bSAlan Somers 	default:
2505fe58019SAttilio Rao 		err = getnewvnode("fuse", mp, &fuse_vnops, vpp);
251f9b0e30bSAlan Somers 		break;
252f9b0e30bSAlan Somers 	}
2535fe58019SAttilio Rao 	if (err) {
2545fe58019SAttilio Rao 		free(fvdat, M_FUSEVN);
2555fe58019SAttilio Rao 		return (err);
2565fe58019SAttilio Rao 	}
2575fe58019SAttilio Rao 	lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL);
2585fe58019SAttilio Rao 	fuse_vnode_init(*vpp, fvdat, nodeid, vtyp);
2595fe58019SAttilio Rao 	err = insmntque(*vpp, mp);
2605fe58019SAttilio Rao 	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
2615fe58019SAttilio Rao 	if (err) {
26272f03b7cSAlan Somers 		lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
2635fe58019SAttilio Rao 		free(fvdat, M_FUSEVN);
2645fe58019SAttilio Rao 		*vpp = NULL;
2655fe58019SAttilio Rao 		return (err);
2665fe58019SAttilio Rao 	}
267e97ae4adSAlan Somers 	/* Disallow async reads for fifos because UFS does.  I don't know why */
268e97ae4adSAlan Somers 	if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO)
269e97ae4adSAlan Somers 		VN_LOCK_ASHARE(*vpp);
270e97ae4adSAlan Somers 
271829f0bcbSMateusz Guzik 	vn_set_state(*vpp, VSTATE_CONSTRUCTED);
2725fe58019SAttilio Rao 	err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE,
2735fe58019SAttilio Rao 	    td, &vp2, fuse_vnode_cmp, &nodeid);
27472f03b7cSAlan Somers 	if (err) {
27572f03b7cSAlan Somers 		lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
27672f03b7cSAlan Somers 		free(fvdat, M_FUSEVN);
27772f03b7cSAlan Somers 		*vpp = NULL;
2785fe58019SAttilio Rao 		return (err);
27972f03b7cSAlan Somers 	}
2805fe58019SAttilio Rao 	if (vp2 != NULL) {
2815fe58019SAttilio Rao 		*vpp = vp2;
2825fe58019SAttilio Rao 		return (0);
2835fe58019SAttilio Rao 	}
2845fe58019SAttilio Rao 
2855fe58019SAttilio Rao 	ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
2865fe58019SAttilio Rao 
2875fe58019SAttilio Rao 	return (0);
2885fe58019SAttilio Rao }
2895fe58019SAttilio Rao 
2905fe58019SAttilio Rao int
2915fe58019SAttilio Rao fuse_vnode_get(struct mount *mp,
29209176f09SConrad Meyer     struct fuse_entry_out *feo,
2935fe58019SAttilio Rao     uint64_t nodeid,
2945fe58019SAttilio Rao     struct vnode *dvp,
2955fe58019SAttilio Rao     struct vnode **vpp,
2965fe58019SAttilio Rao     struct componentname *cnp,
297ba8cc6d7SMateusz Guzik     __enum_uint8(vtype) vtyp)
2985fe58019SAttilio Rao {
299b4a58fbfSMateusz Guzik 	struct thread *td = curthread;
300db7b0e74SAlan Somers 	/*
301db7b0e74SAlan Somers 	 * feo should only be NULL for the root directory, which (when libfuse
302db7b0e74SAlan Somers 	 * is used) always has generation 0
303db7b0e74SAlan Somers 	 */
304db7b0e74SAlan Somers 	uint64_t generation = feo ? feo->generation : 0;
3055fe58019SAttilio Rao 	int err = 0;
3065fe58019SAttilio Rao 
3070bef4927SAlan Somers 	if (dvp != NULL && VTOFUD(dvp)->nid == nodeid) {
3080bef4927SAlan Somers 		fuse_warn(fuse_get_mpdata(mp), FSESS_WARN_ILLEGAL_INODE,
3090bef4927SAlan Somers 			"Assigned same inode to both parent and child.");
3100bef4927SAlan Somers 		return EIO;
3110bef4927SAlan Somers 	}
3120bef4927SAlan Somers 
3135fe58019SAttilio Rao 	err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp);
3145fe58019SAttilio Rao 	if (err) {
3155fe58019SAttilio Rao 		return err;
3165fe58019SAttilio Rao 	}
3175fe58019SAttilio Rao 	if (dvp != NULL) {
3183885d409SAlan Somers 		MPASS(cnp && (cnp->cn_flags & ISDOTDOT) == 0);
3193885d409SAlan Somers 		MPASS(cnp &&
3203885d409SAlan Somers 			!(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
3215fe58019SAttilio Rao 		fuse_vnode_setparent(*vpp, dvp);
3225fe58019SAttilio Rao 	}
32309176f09SConrad Meyer 	if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 &&
32409176f09SConrad Meyer 	    feo != NULL &&
32509176f09SConrad Meyer 	    (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) {
32644f10c6eSAlan Somers 		struct timespec timeout;
327ccb75e49SAlan Somers 
3285fe58019SAttilio Rao 		ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
3295fe58019SAttilio Rao 		ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
3303f2c630cSAlan Somers 
33144f10c6eSAlan Somers 		fuse_validity_2_timespec(feo, &timeout);
3323f2c630cSAlan Somers 		cache_enter_time(dvp, *vpp, cnp, &timeout, NULL);
3333f2c630cSAlan Somers 	}
3345fe58019SAttilio Rao 
335e5b50fe7SAlan Somers 	VTOFUD(*vpp)->generation = generation;
3365fe58019SAttilio Rao 	/*
3375fe58019SAttilio Rao 	 * In userland, libfuse uses cached lookups for dot and dotdot entries,
3385fe58019SAttilio Rao 	 * thus it does not really bump the nlookup counter for forget.
339e5b50fe7SAlan Somers 	 * Follow the same semantic and avoid the bump in order to keep
3405fe58019SAttilio Rao 	 * nlookup counters consistent.
3415fe58019SAttilio Rao 	 */
3425fe58019SAttilio Rao 	if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 &&
3435fe58019SAttilio Rao 	    (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.')))
3445fe58019SAttilio Rao 		VTOFUD(*vpp)->nlookup++;
3455fe58019SAttilio Rao 
3465fe58019SAttilio Rao 	return 0;
3475fe58019SAttilio Rao }
3485fe58019SAttilio Rao 
3493f105d16SAlan Somers /*
3503f105d16SAlan Somers  * Called for every fusefs vnode open to initialize the vnode (not
3513f105d16SAlan Somers  * fuse_filehandle) for use
3523f105d16SAlan Somers  */
3535fe58019SAttilio Rao void
3545fe58019SAttilio Rao fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
3555fe58019SAttilio Rao {
3563f105d16SAlan Somers 	if (vnode_vtype(vp) == VREG)
357*56a8aca8SPawel Jakub Dawidek 		vnode_create_vobject(vp, VNODE_NO_SIZE, td);
3585fe58019SAttilio Rao }
3595fe58019SAttilio Rao 
3605fe58019SAttilio Rao int
361f8d4af10SAlan Somers fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
3625fe58019SAttilio Rao {
3635fe58019SAttilio Rao 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
3645fe58019SAttilio Rao 	struct thread *td = curthread;
3655fe58019SAttilio Rao 	struct fuse_filehandle *fufh = NULL;
3665fe58019SAttilio Rao 	struct fuse_dispatcher fdi;
3675fe58019SAttilio Rao 	struct fuse_setattr_in *fsai;
3685fe58019SAttilio Rao 	int err = 0;
3695fe58019SAttilio Rao 
3705fe58019SAttilio Rao 	ASSERT_VOP_ELOCKED(vp, "fuse_io_extend");
3715fe58019SAttilio Rao 
3725fe58019SAttilio Rao 	if (fuse_isdeadfs(vp)) {
3735fe58019SAttilio Rao 		return EBADF;
3745fe58019SAttilio Rao 	}
3755fe58019SAttilio Rao 	if (vnode_vtype(vp) == VDIR) {
3765fe58019SAttilio Rao 		return EISDIR;
3775fe58019SAttilio Rao 	}
3785fe58019SAttilio Rao 	if (vfs_isrdonly(vnode_mount(vp))) {
3795fe58019SAttilio Rao 		return EROFS;
3805fe58019SAttilio Rao 	}
3815fe58019SAttilio Rao 	if (cred == NULL) {
3825fe58019SAttilio Rao 		cred = td->td_ucred;
3835fe58019SAttilio Rao 	}
3845fe58019SAttilio Rao 	fdisp_init(&fdi, sizeof(*fsai));
3855fe58019SAttilio Rao 	fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
3865fe58019SAttilio Rao 	fsai = fdi.indata;
3875fe58019SAttilio Rao 	fsai->valid = 0;
3885fe58019SAttilio Rao 
3895fe58019SAttilio Rao 	/* Truncate to a new value. */
3903d15b234SAlan Somers 	MPASS((fvdat->flag & FN_SIZECHANGE) != 0);
3913d15b234SAlan Somers 	fsai->size = fvdat->cached_attrs.va_size;
3925fe58019SAttilio Rao 	fsai->valid |= FATTR_SIZE;
3935fe58019SAttilio Rao 
3949f10f423SAlan Somers 	fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
3955fe58019SAttilio Rao 	if (fufh) {
3965fe58019SAttilio Rao 		fsai->fh = fufh->fh_id;
3975fe58019SAttilio Rao 		fsai->valid |= FATTR_FH;
3985fe58019SAttilio Rao 	}
3995fe58019SAttilio Rao 	err = fdisp_wait_answ(&fdi);
4005fe58019SAttilio Rao 	fdisp_destroy(&fdi);
40113d593a5SAlan Somers 	if (err == 0) {
40213d593a5SAlan Somers 		getnanouptime(&fvdat->last_local_modify);
4035fe58019SAttilio Rao 		fvdat->flag &= ~FN_SIZECHANGE;
40413d593a5SAlan Somers 	}
4055fe58019SAttilio Rao 
4065fe58019SAttilio Rao 	return err;
4075fe58019SAttilio Rao }
4085fe58019SAttilio Rao 
4093d15b234SAlan Somers /*
4105d94aaacSAlan Somers  * Adjust the vnode's size to a new value.
4115d94aaacSAlan Somers  *
4125d94aaacSAlan Somers  * If the new value came from the server, such as from a FUSE_GETATTR
4135d94aaacSAlan Somers  * operation, set `from_server` true.  But if it came from a local operation,
4145d94aaacSAlan Somers  * such as write(2) or truncate(2), set `from_server` false.
4153d15b234SAlan Somers  */
4165fe58019SAttilio Rao int
4175d94aaacSAlan Somers fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
4185fe58019SAttilio Rao {
4195fe58019SAttilio Rao 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
420cad67791SAlan Somers 	struct vattr *attrs;
4215fe58019SAttilio Rao 	off_t oldsize;
422e312493bSAlan Somers 	size_t iosize;
423e312493bSAlan Somers 	struct buf *bp = NULL;
4245fe58019SAttilio Rao 	int err = 0;
4255fe58019SAttilio Rao 
4265fe58019SAttilio Rao 	ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize");
4275fe58019SAttilio Rao 
428e312493bSAlan Somers 	iosize = fuse_iosize(vp);
4293d15b234SAlan Somers 	oldsize = fvdat->cached_attrs.va_size;
4303d15b234SAlan Somers 	fvdat->cached_attrs.va_size = newsize;
431cad67791SAlan Somers 	if ((attrs = VTOVA(vp)) != NULL)
432cad67791SAlan Somers 		attrs->va_size = newsize;
4335fe58019SAttilio Rao 
4345fe58019SAttilio Rao 	if (newsize < oldsize) {
435e312493bSAlan Somers 		daddr_t lbn;
436e312493bSAlan Somers 
43765417f5eSAlan Somers 		err = vtruncbuf(vp, newsize, fuse_iosize(vp));
438e312493bSAlan Somers 		if (err)
439e312493bSAlan Somers 			goto out;
440e312493bSAlan Somers 		if (newsize % iosize == 0)
441e312493bSAlan Somers 			goto out;
442e312493bSAlan Somers 		/*
443e312493bSAlan Somers 		 * Zero the contents of the last partial block.
444e312493bSAlan Somers 		 * Sure seems like vtruncbuf should do this for us.
445e312493bSAlan Somers 		 */
446e312493bSAlan Somers 
447e312493bSAlan Somers 		lbn = newsize / iosize;
448e312493bSAlan Somers 		bp = getblk(vp, lbn, iosize, PCATCH, 0, 0);
449e312493bSAlan Somers 		if (!bp) {
450e312493bSAlan Somers 			err = EINTR;
451e312493bSAlan Somers 			goto out;
4525fe58019SAttilio Rao 		}
453e312493bSAlan Somers 		if (!(bp->b_flags & B_CACHE))
454e312493bSAlan Somers 			goto out;	/* Nothing to do */
455bad3de43SAlan Somers 		MPASS(bp->b_flags & B_VMIO);
456bad3de43SAlan Somers 		vfs_bio_clrbuf(bp);
45793c0c1d4SAlan Somers 		bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
4585d94aaacSAlan Somers 	} else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
4595d94aaacSAlan Somers 		/*
4605d94aaacSAlan Somers 		 * The FUSE server changed the file size behind our back.  We
4615d94aaacSAlan Somers 		 * should invalidate the entire cache.
4625d94aaacSAlan Somers 		 */
4633d856234SMark Johnston 		daddr_t end_lbn;
4645d94aaacSAlan Somers 
4655d94aaacSAlan Somers 		end_lbn = howmany(newsize, iosize);
4665d94aaacSAlan Somers 		v_inval_buf_range(vp, 0, end_lbn, iosize);
467e312493bSAlan Somers 	}
468e312493bSAlan Somers out:
469e312493bSAlan Somers 	if (bp)
470e312493bSAlan Somers 		brelse(bp);
4715fe58019SAttilio Rao 	vnode_pager_setsize(vp, newsize);
4725fe58019SAttilio Rao 	return err;
4735fe58019SAttilio Rao }
4743d15b234SAlan Somers 
4753d15b234SAlan Somers /* Get the current, possibly dirty, size of the file */
4763d15b234SAlan Somers int
4773d15b234SAlan Somers fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
4783d15b234SAlan Somers 	struct thread *td)
4793d15b234SAlan Somers {
4803d15b234SAlan Somers 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
4813d15b234SAlan Somers 	int error = 0;
4823d15b234SAlan Somers 
4833d15b234SAlan Somers 	if (!(fvdat->flag & FN_SIZECHANGE) &&
484b0ecfb42SAlan Somers 		(!fuse_vnode_attr_cache_valid(vp) ||
485b0ecfb42SAlan Somers 		  fvdat->cached_attrs.va_size == VNOVAL))
4863d15b234SAlan Somers 		error = fuse_internal_do_getattr(vp, NULL, cred, td);
4873d15b234SAlan Somers 
4883d15b234SAlan Somers 	if (!error)
4893d15b234SAlan Somers 		*filesize = fvdat->cached_attrs.va_size;
4903d15b234SAlan Somers 
4913d15b234SAlan Somers 	return error;
4923d15b234SAlan Somers }
493788af953SAlan Somers 
494788af953SAlan Somers void
49591972cfcSAlan Somers fuse_vnode_undirty_cached_timestamps(struct vnode *vp, bool atime)
496788af953SAlan Somers {
497788af953SAlan Somers 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
498788af953SAlan Somers 
499788af953SAlan Somers 	fvdat->flag &= ~(FN_MTIMECHANGE | FN_CTIMECHANGE);
50091972cfcSAlan Somers 	if (atime)
50191972cfcSAlan Somers 		fvdat->flag &= ~FN_ATIMECHANGE;
502788af953SAlan Somers }
503788af953SAlan Somers 
504788af953SAlan Somers /* Update a fuse file's cached timestamps */
505788af953SAlan Somers void
506788af953SAlan Somers fuse_vnode_update(struct vnode *vp, int flags)
507788af953SAlan Somers {
508788af953SAlan Somers 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
50991972cfcSAlan Somers 	struct mount *mp = vnode_mount(vp);
51091972cfcSAlan Somers 	struct fuse_data *data = fuse_get_mpdata(mp);
511788af953SAlan Somers 	struct timespec ts;
512788af953SAlan Somers 
513788af953SAlan Somers 	vfs_timestamp(&ts);
514788af953SAlan Somers 
515fef46454SAlan Somers 	if (data->time_gran > 1)
516fef46454SAlan Somers 		ts.tv_nsec = rounddown(ts.tv_nsec, data->time_gran);
517fef46454SAlan Somers 
51891972cfcSAlan Somers 	if (mp->mnt_flag & MNT_NOATIME)
51991972cfcSAlan Somers 		flags &= ~FN_ATIMECHANGE;
52091972cfcSAlan Somers 
52191972cfcSAlan Somers 	if (flags & FN_ATIMECHANGE)
52291972cfcSAlan Somers 		fvdat->cached_attrs.va_atime = ts;
523788af953SAlan Somers 	if (flags & FN_MTIMECHANGE)
524788af953SAlan Somers 		fvdat->cached_attrs.va_mtime = ts;
525788af953SAlan Somers 	if (flags & FN_CTIMECHANGE)
526788af953SAlan Somers 		fvdat->cached_attrs.va_ctime = ts;
527788af953SAlan Somers 
528788af953SAlan Somers 	fvdat->flag |= flags;
529788af953SAlan Somers }
530560a55d0SAlan Somers 
531560a55d0SAlan Somers void
532560a55d0SAlan Somers fuse_node_init(void)
533560a55d0SAlan Somers {
534560a55d0SAlan Somers 	fuse_node_count = counter_u64_alloc(M_WAITOK);
535560a55d0SAlan Somers }
536560a55d0SAlan Somers 
537560a55d0SAlan Somers void
538560a55d0SAlan Somers fuse_node_destroy(void)
539560a55d0SAlan Somers {
540560a55d0SAlan Somers 	counter_u64_free(fuse_node_count);
541560a55d0SAlan Somers }
542