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
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
fuse_vnode_destroy(struct vnode * vp)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
fuse_vnode_cmp(struct vnode * vp,void * nidp)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
fuse_vnode_alloc(struct mount * mp,struct thread * td,uint64_t nodeid,__enum_uint8 (vtype)vtyp,struct vnode ** vpp)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
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)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
fuse_vnode_open(struct vnode * vp,int32_t fuse_open_flags,struct thread * td)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
fuse_vnode_savesize(struct vnode * vp,struct ucred * cred,pid_t pid)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
fuse_vnode_setsize(struct vnode * vp,off_t newsize,bool from_server)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
fuse_vnode_size(struct vnode * vp,off_t * filesize,struct ucred * cred,struct thread * td)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
fuse_vnode_undirty_cached_timestamps(struct vnode * vp,bool atime)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
fuse_vnode_update(struct vnode * vp,int flags)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
fuse_node_init(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
fuse_node_destroy(void)538560a55d0SAlan Somers fuse_node_destroy(void)
539560a55d0SAlan Somers {
540560a55d0SAlan Somers counter_u64_free(fuse_node_count);
541560a55d0SAlan Somers }
542