1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2007-2009 Google Inc. and Amit Singh 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following disclaimer 15 * in the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Google Inc. nor the names of its 18 * contributors may be used to endorse or promote products derived from 19 * this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * Copyright (C) 2005 Csaba Henk. 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __FBSDID("$FreeBSD$"); 60 61 #include <sys/types.h> 62 #include <sys/module.h> 63 #include <sys/systm.h> 64 #include <sys/errno.h> 65 #include <sys/param.h> 66 #include <sys/kernel.h> 67 #include <sys/conf.h> 68 #include <sys/uio.h> 69 #include <sys/malloc.h> 70 #include <sys/queue.h> 71 #include <sys/lock.h> 72 #include <sys/sx.h> 73 #include <sys/mutex.h> 74 #include <sys/proc.h> 75 #include <sys/vnode.h> 76 #include <sys/namei.h> 77 #include <sys/mount.h> 78 #include <sys/sysctl.h> 79 #include <sys/fcntl.h> 80 #include <sys/fnv_hash.h> 81 #include <sys/priv.h> 82 #include <security/mac/mac_framework.h> 83 #include <vm/vm.h> 84 #include <vm/vm_extern.h> 85 86 #include "fuse.h" 87 #include "fuse_node.h" 88 #include "fuse_internal.h" 89 #include "fuse_io.h" 90 #include "fuse_ipc.h" 91 92 #define FUSE_DEBUG_MODULE VNOPS 93 #include "fuse_debug.h" 94 95 MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data"); 96 97 static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS); 98 99 static int fuse_node_count = 0; 100 101 SYSCTL_INT(_vfs_fuse, OID_AUTO, node_count, CTLFLAG_RD, 102 &fuse_node_count, 0, "Count of FUSE vnodes"); 103 104 int fuse_data_cache_mode = FUSE_CACHE_WT; 105 106 SYSCTL_PROC(_vfs_fuse, OID_AUTO, data_cache_mode, CTLTYPE_INT|CTLFLAG_RW, 107 &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I", 108 "Zero: disable caching of FUSE file data; One: write-through caching " 109 "(default); Two: write-back caching (generally unsafe)"); 110 111 int fuse_data_cache_invalidate = 0; 112 113 SYSCTL_INT(_vfs_fuse, OID_AUTO, data_cache_invalidate, CTLFLAG_RW, 114 &fuse_data_cache_invalidate, 0, 115 "If non-zero, discard cached clean file data when there are no active file" 116 " users"); 117 118 int fuse_mmap_enable = 1; 119 120 SYSCTL_INT(_vfs_fuse, OID_AUTO, mmap_enable, CTLFLAG_RW, 121 &fuse_mmap_enable, 0, 122 "If non-zero, and data_cache_mode is also non-zero, enable mmap(2) of " 123 "FUSE files"); 124 125 int fuse_refresh_size = 0; 126 127 SYSCTL_INT(_vfs_fuse, OID_AUTO, refresh_size, CTLFLAG_RW, 128 &fuse_refresh_size, 0, 129 "If non-zero, and no dirty file extension data is buffered, fetch file " 130 "size before write operations"); 131 132 int fuse_sync_resize = 1; 133 134 SYSCTL_INT(_vfs_fuse, OID_AUTO, sync_resize, CTLFLAG_RW, 135 &fuse_sync_resize, 0, 136 "If a cached write extended a file, inform FUSE filesystem of the changed" 137 "size immediately subsequent to the issued writes"); 138 139 int fuse_fix_broken_io = 0; 140 141 SYSCTL_INT(_vfs_fuse, OID_AUTO, fix_broken_io, CTLFLAG_RW, 142 &fuse_fix_broken_io, 0, 143 "If non-zero, print a diagnostic warning if a userspace filesystem returns" 144 " EIO on reads of recently extended portions of files"); 145 146 static int 147 sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS) 148 { 149 int val, error; 150 151 val = *(int *)arg1; 152 error = sysctl_handle_int(oidp, &val, 0, req); 153 if (error || !req->newptr) 154 return (error); 155 156 switch (val) { 157 case FUSE_CACHE_UC: 158 case FUSE_CACHE_WT: 159 case FUSE_CACHE_WB: 160 *(int *)arg1 = val; 161 break; 162 default: 163 return (EDOM); 164 } 165 return (0); 166 } 167 168 static void 169 fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat, 170 uint64_t nodeid, enum vtype vtyp) 171 { 172 int i; 173 174 fvdat->nid = nodeid; 175 vattr_null(&fvdat->cached_attrs); 176 if (nodeid == FUSE_ROOT_ID) { 177 vp->v_vflag |= VV_ROOT; 178 } 179 vp->v_type = vtyp; 180 vp->v_data = fvdat; 181 182 for (i = 0; i < FUFH_MAXTYPE; i++) 183 fvdat->fufh[i].fh_type = FUFH_INVALID; 184 185 atomic_add_acq_int(&fuse_node_count, 1); 186 } 187 188 void 189 fuse_vnode_destroy(struct vnode *vp) 190 { 191 struct fuse_vnode_data *fvdat = vp->v_data; 192 193 vp->v_data = NULL; 194 free(fvdat, M_FUSEVN); 195 196 atomic_subtract_acq_int(&fuse_node_count, 1); 197 } 198 199 static int 200 fuse_vnode_cmp(struct vnode *vp, void *nidp) 201 { 202 return (VTOI(vp) != *((uint64_t *)nidp)); 203 } 204 205 static uint32_t __inline 206 fuse_vnode_hash(uint64_t id) 207 { 208 return (fnv_32_buf(&id, sizeof(id), FNV1_32_INIT)); 209 } 210 211 static int 212 fuse_vnode_alloc(struct mount *mp, 213 struct thread *td, 214 uint64_t nodeid, 215 enum vtype vtyp, 216 struct vnode **vpp) 217 { 218 struct fuse_vnode_data *fvdat; 219 struct vnode *vp2; 220 int err = 0; 221 222 FS_DEBUG("been asked for vno #%ju\n", (uintmax_t)nodeid); 223 224 if (vtyp == VNON) { 225 return EINVAL; 226 } 227 *vpp = NULL; 228 err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp, 229 fuse_vnode_cmp, &nodeid); 230 if (err) 231 return (err); 232 233 if (*vpp) { 234 MPASS((*vpp)->v_type == vtyp && (*vpp)->v_data != NULL); 235 FS_DEBUG("vnode taken from hash\n"); 236 return (0); 237 } 238 fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO); 239 err = getnewvnode("fuse", mp, &fuse_vnops, vpp); 240 if (err) { 241 free(fvdat, M_FUSEVN); 242 return (err); 243 } 244 lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL); 245 fuse_vnode_init(*vpp, fvdat, nodeid, vtyp); 246 err = insmntque(*vpp, mp); 247 ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc"); 248 if (err) { 249 free(fvdat, M_FUSEVN); 250 *vpp = NULL; 251 return (err); 252 } 253 err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, 254 td, &vp2, fuse_vnode_cmp, &nodeid); 255 if (err) 256 return (err); 257 if (vp2 != NULL) { 258 *vpp = vp2; 259 return (0); 260 } 261 262 ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc"); 263 264 return (0); 265 } 266 267 int 268 fuse_vnode_get(struct mount *mp, 269 struct fuse_entry_out *feo, 270 uint64_t nodeid, 271 struct vnode *dvp, 272 struct vnode **vpp, 273 struct componentname *cnp, 274 enum vtype vtyp) 275 { 276 struct thread *td = (cnp != NULL ? cnp->cn_thread : curthread); 277 int err = 0; 278 279 debug_printf("dvp=%p\n", dvp); 280 281 err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp); 282 if (err) { 283 return err; 284 } 285 if (dvp != NULL) { 286 MPASS((cnp->cn_flags & ISDOTDOT) == 0); 287 MPASS(!(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')); 288 fuse_vnode_setparent(*vpp, dvp); 289 } 290 if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 && 291 feo != NULL && 292 (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) { 293 ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get"); 294 ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get"); 295 cache_enter(dvp, *vpp, cnp); 296 } 297 298 /* 299 * In userland, libfuse uses cached lookups for dot and dotdot entries, 300 * thus it does not really bump the nlookup counter for forget. 301 * Follow the same semantic and avoid tu bump it in order to keep 302 * nlookup counters consistent. 303 */ 304 if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 && 305 (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.'))) 306 VTOFUD(*vpp)->nlookup++; 307 308 return 0; 309 } 310 311 void 312 fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td) 313 { 314 /* 315 * Funcation is called for every vnode open. 316 * Merge fuse_open_flags it may be 0 317 */ 318 /* 319 * Ideally speaking, direct io should be enabled on 320 * fd's but do not see of any way of providing that 321 * this implementation. 322 * 323 * Also cannot think of a reason why would two 324 * different fd's on same vnode would like 325 * have DIRECT_IO turned on and off. But linux 326 * based implementation works on an fd not an 327 * inode and provides such a feature. 328 * 329 * XXXIP: Handle fd based DIRECT_IO 330 */ 331 if (fuse_open_flags & FOPEN_DIRECT_IO) { 332 ASSERT_VOP_ELOCKED(vp, __func__); 333 VTOFUD(vp)->flag |= FN_DIRECTIO; 334 fuse_io_invalbuf(vp, td); 335 } else { 336 if ((fuse_open_flags & FOPEN_KEEP_CACHE) == 0) 337 fuse_io_invalbuf(vp, td); 338 VTOFUD(vp)->flag &= ~FN_DIRECTIO; 339 } 340 341 if (vnode_vtype(vp) == VREG) { 342 /* XXXIP prevent getattr, by using cached node size */ 343 vnode_create_vobject(vp, 0, td); 344 } 345 } 346 347 int 348 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred) 349 { 350 struct fuse_vnode_data *fvdat = VTOFUD(vp); 351 struct thread *td = curthread; 352 struct fuse_filehandle *fufh = NULL; 353 struct fuse_dispatcher fdi; 354 struct fuse_setattr_in *fsai; 355 int err = 0; 356 357 FS_DEBUG("inode=%ju size=%ju\n", (uintmax_t)VTOI(vp), 358 (uintmax_t)fvdat->filesize); 359 ASSERT_VOP_ELOCKED(vp, "fuse_io_extend"); 360 361 if (fuse_isdeadfs(vp)) { 362 return EBADF; 363 } 364 if (vnode_vtype(vp) == VDIR) { 365 return EISDIR; 366 } 367 if (vfs_isrdonly(vnode_mount(vp))) { 368 return EROFS; 369 } 370 if (cred == NULL) { 371 cred = td->td_ucred; 372 } 373 fdisp_init(&fdi, sizeof(*fsai)); 374 fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred); 375 fsai = fdi.indata; 376 fsai->valid = 0; 377 378 /* Truncate to a new value. */ 379 fsai->size = fvdat->filesize; 380 fsai->valid |= FATTR_SIZE; 381 382 fuse_filehandle_getrw(vp, FUFH_WRONLY, &fufh); 383 if (fufh) { 384 fsai->fh = fufh->fh_id; 385 fsai->valid |= FATTR_FH; 386 } 387 err = fdisp_wait_answ(&fdi); 388 fdisp_destroy(&fdi); 389 if (err == 0) 390 fvdat->flag &= ~FN_SIZECHANGE; 391 392 return err; 393 } 394 395 void 396 fuse_vnode_refreshsize(struct vnode *vp, struct ucred *cred) 397 { 398 399 struct fuse_vnode_data *fvdat = VTOFUD(vp); 400 struct vattr va; 401 402 if ((fvdat->flag & FN_SIZECHANGE) != 0 || 403 fuse_data_cache_mode == FUSE_CACHE_UC || 404 (fuse_refresh_size == 0 && fvdat->filesize != 0)) 405 return; 406 407 VOP_GETATTR(vp, &va, cred); 408 FS_DEBUG("refreshed file size: %jd\n", (intmax_t)VTOFUD(vp)->filesize); 409 } 410 411 int 412 fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize) 413 { 414 struct fuse_vnode_data *fvdat = VTOFUD(vp); 415 off_t oldsize; 416 int err = 0; 417 418 FS_DEBUG("inode=%ju oldsize=%ju newsize=%ju\n", 419 (uintmax_t)VTOI(vp), (uintmax_t)fvdat->filesize, 420 (uintmax_t)newsize); 421 ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize"); 422 423 oldsize = fvdat->filesize; 424 fvdat->filesize = newsize; 425 fvdat->flag |= FN_SIZECHANGE; 426 427 if (newsize < oldsize) { 428 err = vtruncbuf(vp, cred, newsize, fuse_iosize(vp)); 429 } 430 vnode_pager_setsize(vp, newsize); 431 return err; 432 } 433