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