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/priv.h> 81 #include <sys/buf.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 SDT_PROVIDER_DECLARE(fusefs); 93 /* 94 * Fuse trace probe: 95 * arg0: verbosity. Higher numbers give more verbose messages 96 * arg1: Textual message 97 */ 98 SDT_PROBE_DEFINE2(fusefs, , node, trace, "int", "char*"); 99 100 MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data"); 101 102 static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS); 103 104 static int fuse_node_count = 0; 105 106 SYSCTL_INT(_vfs_fusefs, OID_AUTO, node_count, CTLFLAG_RD, 107 &fuse_node_count, 0, "Count of FUSE vnodes"); 108 109 int fuse_data_cache_mode = FUSE_CACHE_WT; 110 111 SYSCTL_PROC(_vfs_fusefs, OID_AUTO, data_cache_mode, CTLTYPE_INT|CTLFLAG_RW, 112 &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I", 113 "Zero: disable caching of FUSE file data; One: write-through caching " 114 "(default); Two: write-back caching (generally unsafe)"); 115 116 static int 117 sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS) 118 { 119 int val, error; 120 121 val = *(int *)arg1; 122 error = sysctl_handle_int(oidp, &val, 0, req); 123 if (error || !req->newptr) 124 return (error); 125 126 switch (val) { 127 case FUSE_CACHE_UC: 128 case FUSE_CACHE_WT: 129 case FUSE_CACHE_WB: 130 *(int *)arg1 = val; 131 break; 132 default: 133 return (EDOM); 134 } 135 return (0); 136 } 137 138 static void 139 fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat, 140 uint64_t nodeid, enum vtype vtyp) 141 { 142 fvdat->nid = nodeid; 143 LIST_INIT(&fvdat->handles); 144 vattr_null(&fvdat->cached_attrs); 145 if (nodeid == FUSE_ROOT_ID) { 146 vp->v_vflag |= VV_ROOT; 147 } 148 vp->v_type = vtyp; 149 vp->v_data = fvdat; 150 151 atomic_add_acq_int(&fuse_node_count, 1); 152 } 153 154 void 155 fuse_vnode_destroy(struct vnode *vp) 156 { 157 struct fuse_vnode_data *fvdat = vp->v_data; 158 159 vp->v_data = NULL; 160 KASSERT(LIST_EMPTY(&fvdat->handles), 161 ("Destroying fuse vnode with open files!")); 162 free(fvdat, M_FUSEVN); 163 164 atomic_subtract_acq_int(&fuse_node_count, 1); 165 } 166 167 int 168 fuse_vnode_cmp(struct vnode *vp, void *nidp) 169 { 170 return (VTOI(vp) != *((uint64_t *)nidp)); 171 } 172 173 SDT_PROBE_DEFINE3(fusefs, , node, stale_vnode, "struct vnode*", "enum vtype", 174 "uint64_t"); 175 static int 176 fuse_vnode_alloc(struct mount *mp, 177 struct thread *td, 178 uint64_t nodeid, 179 enum vtype vtyp, 180 struct vnode **vpp) 181 { 182 struct fuse_data *data; 183 struct fuse_vnode_data *fvdat; 184 struct vnode *vp2; 185 int err = 0; 186 187 data = fuse_get_mpdata(mp); 188 if (vtyp == VNON) { 189 return EINVAL; 190 } 191 *vpp = NULL; 192 err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp, 193 fuse_vnode_cmp, &nodeid); 194 if (err) 195 return (err); 196 197 if (*vpp) { 198 if ((*vpp)->v_type != vtyp) { 199 /* 200 * STALE vnode! This probably indicates a buggy 201 * server, but it could also be the result of a race 202 * between FUSE_LOOKUP and another client's 203 * FUSE_UNLINK/FUSE_CREATE 204 */ 205 SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp, 206 nodeid); 207 fuse_internal_vnode_disappear(*vpp); 208 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL); 209 *vpp = NULL; 210 return (EAGAIN); 211 } 212 MPASS((*vpp)->v_data != NULL); 213 MPASS(VTOFUD(*vpp)->nid == nodeid); 214 SDT_PROBE2(fusefs, , node, trace, 1, "vnode taken from hash"); 215 return (0); 216 } 217 fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO); 218 switch (vtyp) { 219 case VFIFO: 220 err = getnewvnode("fuse", mp, &fuse_fifoops, vpp); 221 break; 222 default: 223 err = getnewvnode("fuse", mp, &fuse_vnops, vpp); 224 break; 225 } 226 if (err) { 227 free(fvdat, M_FUSEVN); 228 return (err); 229 } 230 lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL); 231 fuse_vnode_init(*vpp, fvdat, nodeid, vtyp); 232 err = insmntque(*vpp, mp); 233 ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc"); 234 if (err) { 235 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL); 236 free(fvdat, M_FUSEVN); 237 *vpp = NULL; 238 return (err); 239 } 240 /* Disallow async reads for fifos because UFS does. I don't know why */ 241 if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO) 242 VN_LOCK_ASHARE(*vpp); 243 244 err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, 245 td, &vp2, fuse_vnode_cmp, &nodeid); 246 if (err) { 247 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL); 248 free(fvdat, M_FUSEVN); 249 *vpp = NULL; 250 return (err); 251 } 252 if (vp2 != NULL) { 253 *vpp = vp2; 254 return (0); 255 } 256 257 ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc"); 258 259 return (0); 260 } 261 262 int 263 fuse_vnode_get(struct mount *mp, 264 struct fuse_entry_out *feo, 265 uint64_t nodeid, 266 struct vnode *dvp, 267 struct vnode **vpp, 268 struct componentname *cnp, 269 enum vtype vtyp) 270 { 271 struct thread *td = (cnp != NULL ? cnp->cn_thread : curthread); 272 /* 273 * feo should only be NULL for the root directory, which (when libfuse 274 * is used) always has generation 0 275 */ 276 uint64_t generation = feo ? feo->generation : 0; 277 int err = 0; 278 279 err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp); 280 if (err) { 281 return err; 282 } 283 if (dvp != NULL) { 284 MPASS(cnp && (cnp->cn_flags & ISDOTDOT) == 0); 285 MPASS(cnp && 286 !(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')); 287 fuse_vnode_setparent(*vpp, dvp); 288 } 289 if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 && 290 feo != NULL && 291 (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) { 292 struct timespec timeout; 293 294 ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get"); 295 ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get"); 296 297 fuse_validity_2_timespec(feo, &timeout); 298 cache_enter_time(dvp, *vpp, cnp, &timeout, NULL); 299 } 300 301 VTOFUD(*vpp)->generation = generation; 302 /* 303 * In userland, libfuse uses cached lookups for dot and dotdot entries, 304 * thus it does not really bump the nlookup counter for forget. 305 * Follow the same semantic and avoid the bump in order to keep 306 * nlookup counters consistent. 307 */ 308 if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 && 309 (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.'))) 310 VTOFUD(*vpp)->nlookup++; 311 312 return 0; 313 } 314 315 /* 316 * Called for every fusefs vnode open to initialize the vnode (not 317 * fuse_filehandle) for use 318 */ 319 void 320 fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td) 321 { 322 if (vnode_vtype(vp) == VREG) 323 vnode_create_vobject(vp, 0, td); 324 } 325 326 int 327 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid) 328 { 329 struct fuse_vnode_data *fvdat = VTOFUD(vp); 330 struct thread *td = curthread; 331 struct fuse_filehandle *fufh = NULL; 332 struct fuse_dispatcher fdi; 333 struct fuse_setattr_in *fsai; 334 int err = 0; 335 336 ASSERT_VOP_ELOCKED(vp, "fuse_io_extend"); 337 338 if (fuse_isdeadfs(vp)) { 339 return EBADF; 340 } 341 if (vnode_vtype(vp) == VDIR) { 342 return EISDIR; 343 } 344 if (vfs_isrdonly(vnode_mount(vp))) { 345 return EROFS; 346 } 347 if (cred == NULL) { 348 cred = td->td_ucred; 349 } 350 fdisp_init(&fdi, sizeof(*fsai)); 351 fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred); 352 fsai = fdi.indata; 353 fsai->valid = 0; 354 355 /* Truncate to a new value. */ 356 MPASS((fvdat->flag & FN_SIZECHANGE) != 0); 357 fsai->size = fvdat->cached_attrs.va_size; 358 fsai->valid |= FATTR_SIZE; 359 360 fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid); 361 if (fufh) { 362 fsai->fh = fufh->fh_id; 363 fsai->valid |= FATTR_FH; 364 } 365 err = fdisp_wait_answ(&fdi); 366 fdisp_destroy(&fdi); 367 if (err == 0) 368 fvdat->flag &= ~FN_SIZECHANGE; 369 370 return err; 371 } 372 373 /* 374 * Adjust the vnode's size to a new value, such as that provided by 375 * FUSE_GETATTR. 376 */ 377 int 378 fuse_vnode_setsize(struct vnode *vp, off_t newsize) 379 { 380 struct fuse_vnode_data *fvdat = VTOFUD(vp); 381 struct vattr *attrs; 382 off_t oldsize; 383 size_t iosize; 384 struct buf *bp = NULL; 385 int err = 0; 386 387 ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize"); 388 389 iosize = fuse_iosize(vp); 390 oldsize = fvdat->cached_attrs.va_size; 391 fvdat->cached_attrs.va_size = newsize; 392 if ((attrs = VTOVA(vp)) != NULL) 393 attrs->va_size = newsize; 394 395 if (newsize < oldsize) { 396 daddr_t lbn; 397 398 err = vtruncbuf(vp, newsize, fuse_iosize(vp)); 399 if (err) 400 goto out; 401 if (newsize % iosize == 0) 402 goto out; 403 /* 404 * Zero the contents of the last partial block. 405 * Sure seems like vtruncbuf should do this for us. 406 */ 407 408 lbn = newsize / iosize; 409 bp = getblk(vp, lbn, iosize, PCATCH, 0, 0); 410 if (!bp) { 411 err = EINTR; 412 goto out; 413 } 414 if (!(bp->b_flags & B_CACHE)) 415 goto out; /* Nothing to do */ 416 MPASS(bp->b_flags & B_VMIO); 417 vfs_bio_clrbuf(bp); 418 bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize); 419 } 420 out: 421 if (bp) 422 brelse(bp); 423 vnode_pager_setsize(vp, newsize); 424 return err; 425 } 426 427 /* Get the current, possibly dirty, size of the file */ 428 int 429 fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred, 430 struct thread *td) 431 { 432 struct fuse_vnode_data *fvdat = VTOFUD(vp); 433 int error = 0; 434 435 if (!(fvdat->flag & FN_SIZECHANGE) && 436 (VTOVA(vp) == NULL || fvdat->cached_attrs.va_size == VNOVAL)) 437 error = fuse_internal_do_getattr(vp, NULL, cred, td); 438 439 if (!error) 440 *filesize = fvdat->cached_attrs.va_size; 441 442 return error; 443 } 444 445 void 446 fuse_vnode_undirty_cached_timestamps(struct vnode *vp) 447 { 448 struct fuse_vnode_data *fvdat = VTOFUD(vp); 449 450 fvdat->flag &= ~(FN_MTIMECHANGE | FN_CTIMECHANGE); 451 } 452 453 /* Update a fuse file's cached timestamps */ 454 void 455 fuse_vnode_update(struct vnode *vp, int flags) 456 { 457 struct fuse_vnode_data *fvdat = VTOFUD(vp); 458 struct timespec ts; 459 460 vfs_timestamp(&ts); 461 462 if (flags & FN_MTIMECHANGE) 463 fvdat->cached_attrs.va_mtime = ts; 464 if (flags & FN_CTIMECHANGE) 465 fvdat->cached_attrs.va_ctime = ts; 466 467 fvdat->flag |= flags; 468 } 469