tmpfs_vnops.c revision 1.84
1/* $NetBSD: tmpfs_vnops.c,v 1.84 2011/05/24 23:16:16 rmind Exp $ */ 2 3/* 4 * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9 * 2005 program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* 34 * tmpfs vnode interface. 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: tmpfs_vnops.c,v 1.84 2011/05/24 23:16:16 rmind Exp $"); 39 40#include <sys/param.h> 41#include <sys/dirent.h> 42#include <sys/fcntl.h> 43#include <sys/event.h> 44#include <sys/malloc.h> 45#include <sys/namei.h> 46#include <sys/stat.h> 47#include <sys/uio.h> 48#include <sys/unistd.h> 49#include <sys/vnode.h> 50#include <sys/lockf.h> 51#include <sys/kauth.h> 52 53#include <uvm/uvm.h> 54 55#include <miscfs/fifofs/fifo.h> 56#include <miscfs/genfs/genfs.h> 57#include <fs/tmpfs/tmpfs_vnops.h> 58#include <fs/tmpfs/tmpfs.h> 59 60/* 61 * vnode operations vector used for files stored in a tmpfs file system. 62 */ 63int (**tmpfs_vnodeop_p)(void *); 64const struct vnodeopv_entry_desc tmpfs_vnodeop_entries[] = { 65 { &vop_default_desc, vn_default_error }, 66 { &vop_lookup_desc, tmpfs_lookup }, 67 { &vop_create_desc, tmpfs_create }, 68 { &vop_mknod_desc, tmpfs_mknod }, 69 { &vop_open_desc, tmpfs_open }, 70 { &vop_close_desc, tmpfs_close }, 71 { &vop_access_desc, tmpfs_access }, 72 { &vop_getattr_desc, tmpfs_getattr }, 73 { &vop_setattr_desc, tmpfs_setattr }, 74 { &vop_read_desc, tmpfs_read }, 75 { &vop_write_desc, tmpfs_write }, 76 { &vop_ioctl_desc, tmpfs_ioctl }, 77 { &vop_fcntl_desc, tmpfs_fcntl }, 78 { &vop_poll_desc, tmpfs_poll }, 79 { &vop_kqfilter_desc, tmpfs_kqfilter }, 80 { &vop_revoke_desc, tmpfs_revoke }, 81 { &vop_mmap_desc, tmpfs_mmap }, 82 { &vop_fsync_desc, tmpfs_fsync }, 83 { &vop_seek_desc, tmpfs_seek }, 84 { &vop_remove_desc, tmpfs_remove }, 85 { &vop_link_desc, tmpfs_link }, 86 { &vop_rename_desc, tmpfs_rename }, 87 { &vop_mkdir_desc, tmpfs_mkdir }, 88 { &vop_rmdir_desc, tmpfs_rmdir }, 89 { &vop_symlink_desc, tmpfs_symlink }, 90 { &vop_readdir_desc, tmpfs_readdir }, 91 { &vop_readlink_desc, tmpfs_readlink }, 92 { &vop_abortop_desc, tmpfs_abortop }, 93 { &vop_inactive_desc, tmpfs_inactive }, 94 { &vop_reclaim_desc, tmpfs_reclaim }, 95 { &vop_lock_desc, tmpfs_lock }, 96 { &vop_unlock_desc, tmpfs_unlock }, 97 { &vop_bmap_desc, tmpfs_bmap }, 98 { &vop_strategy_desc, tmpfs_strategy }, 99 { &vop_print_desc, tmpfs_print }, 100 { &vop_pathconf_desc, tmpfs_pathconf }, 101 { &vop_islocked_desc, tmpfs_islocked }, 102 { &vop_advlock_desc, tmpfs_advlock }, 103 { &vop_bwrite_desc, tmpfs_bwrite }, 104 { &vop_getpages_desc, tmpfs_getpages }, 105 { &vop_putpages_desc, tmpfs_putpages }, 106#if TMPFS_WHITEOUT 107 { &vop_whiteout_desc, tmpfs_whiteout }, 108#endif 109 { NULL, NULL } 110}; 111 112const struct vnodeopv_desc tmpfs_vnodeop_opv_desc = { 113 &tmpfs_vnodeop_p, tmpfs_vnodeop_entries 114}; 115 116/* 117 * tmpfs_lookup: path name traversal routine. 118 * 119 * Arguments: dvp (directory being searched), vpp (result), 120 * cnp (component name - path). 121 * 122 * => Caller holds a reference and lock on dvp. 123 * => We return looked-up vnode (vpp) locked, with a reference held. 124 */ 125int 126tmpfs_lookup(void *v) 127{ 128 struct vop_lookup_args /* { 129 struct vnode *a_dvp; 130 struct vnode **a_vpp; 131 struct componentname *a_cnp; 132 } */ *ap = v; 133 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp; 134 struct componentname *cnp = ap->a_cnp; 135 const bool lastcn = (cnp->cn_flags & ISLASTCN) != 0; 136 tmpfs_node_t *dnode, *tnode; 137 tmpfs_dirent_t *de; 138 int error; 139 140 KASSERT(VOP_ISLOCKED(dvp)); 141 142 dnode = VP_TO_TMPFS_DIR(dvp); 143 *vpp = NULL; 144 145 /* Check accessibility of requested node as a first step. */ 146 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred); 147 if (error) { 148 goto out; 149 } 150 /* 151 * If requesting the last path component on a read-only file system 152 * with a write operation, deny it. 153 */ 154 if (lastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) != 0 && 155 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 156 error = EROFS; 157 goto out; 158 } 159 160 /* 161 * Avoid doing a linear scan of the directory if the requested 162 * directory/name couple is already in the cache. 163 */ 164 error = cache_lookup(dvp, vpp, cnp); 165 if (error >= 0) { 166 /* Both cache-hit or an error case. */ 167 goto out; 168 } 169 170 if (cnp->cn_flags & ISDOTDOT) { 171 tmpfs_node_t *pnode; 172 /* 173 * Lookup of ".." case. 174 */ 175 pnode = dnode->tn_spec.tn_dir.tn_parent; 176 KASSERT(dnode->tn_type == VDIR && pnode != dnode); 177 VOP_UNLOCK(dvp); 178 179 /* Allocate a new vnode on the matching entry. */ 180 error = tmpfs_alloc_vp(dvp->v_mount, pnode, vpp); 181 182 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 183 goto out; 184 185 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 186 /* 187 * Lookup of "." case. 188 */ 189 if (lastcn && cnp->cn_nameiop == RENAME) { 190 error = EISDIR; 191 goto out; 192 } 193 vref(dvp); 194 *vpp = dvp; 195 error = 0; 196 goto done; 197 } 198 199 /* 200 * Other lookup cases: perform directory scan. 201 */ 202 de = tmpfs_dir_lookup(dnode, cnp); 203 if (de == NULL || de->td_node == TMPFS_NODE_WHITEOUT) { 204 /* 205 * The entry was not found in the directory. This is valid 206 * if we are creating or renaming an entry and are working 207 * on the last component of the path name. 208 */ 209 if (lastcn && (cnp->cn_nameiop == CREATE || 210 cnp->cn_nameiop == RENAME)) { 211 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); 212 if (error) { 213 goto out; 214 } 215 error = EJUSTRETURN; 216 } else { 217 error = ENOENT; 218 } 219 if (de) { 220 KASSERT(de->td_node == TMPFS_NODE_WHITEOUT); 221 cnp->cn_flags |= ISWHITEOUT; 222 } 223 goto done; 224 } 225 226 tnode = de->td_node; 227 228 /* 229 * If it is not the last path component and found a non-directory 230 * or non-link entry (which may itself be pointing to a directory), 231 * raise an error. 232 */ 233 if (!lastcn && tnode->tn_type != VDIR && tnode->tn_type != VLNK) { 234 error = ENOTDIR; 235 goto out; 236 } 237 238 /* Check the permissions. */ 239 if (lastcn && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 240 kauth_action_t action = 0; 241 242 /* This is the file-system's decision. */ 243 if ((dnode->tn_mode & S_ISTXT) != 0 && 244 kauth_cred_geteuid(cnp->cn_cred) != dnode->tn_uid && 245 kauth_cred_geteuid(cnp->cn_cred) != tnode->tn_uid) { 246 error = EPERM; 247 } else { 248 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); 249 } 250 251 if (cnp->cn_nameiop == DELETE) { 252 action |= KAUTH_VNODE_DELETE; 253 } else { 254 KASSERT(cnp->cn_nameiop == RENAME); 255 action |= KAUTH_VNODE_RENAME; 256 } 257 error = kauth_authorize_vnode(cnp->cn_cred, 258 action, *vpp, dvp, error); 259 if (error) { 260 goto out; 261 } 262 } 263 264 /* Allocate a new vnode on the matching entry. */ 265 error = tmpfs_alloc_vp(dvp->v_mount, tnode, vpp); 266 267done: 268 /* 269 * Cache the result, unless request was for creation (as it does 270 * not improve the performance). 271 */ 272 if ((cnp->cn_flags & MAKEENTRY) != 0 && cnp->cn_nameiop != CREATE) { 273 cache_enter(dvp, *vpp, cnp); 274 } 275out: 276 KASSERT((*vpp && VOP_ISLOCKED(*vpp)) || error); 277 KASSERT(VOP_ISLOCKED(dvp)); 278 279 return error; 280} 281 282int 283tmpfs_create(void *v) 284{ 285 struct vop_create_args /* { 286 struct vnode *a_dvp; 287 struct vnode **a_vpp; 288 struct componentname *a_cnp; 289 struct vattr *a_vap; 290 } */ *ap = v; 291 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp; 292 struct componentname *cnp = ap->a_cnp; 293 struct vattr *vap = ap->a_vap; 294 295 KASSERT(VOP_ISLOCKED(dvp)); 296 KASSERT(vap->va_type == VREG || vap->va_type == VSOCK); 297 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 298} 299 300int 301tmpfs_mknod(void *v) 302{ 303 struct vop_mknod_args /* { 304 struct vnode *a_dvp; 305 struct vnode **a_vpp; 306 struct componentname *a_cnp; 307 struct vattr *a_vap; 308 } */ *ap = v; 309 vnode_t *dvp = ap->a_dvp, **vpp = ap->a_vpp; 310 struct componentname *cnp = ap->a_cnp; 311 struct vattr *vap = ap->a_vap; 312 enum vtype vt = vap->va_type; 313 314 if (vt != VBLK && vt != VCHR && vt != VFIFO) { 315 vput(dvp); 316 return EINVAL; 317 } 318 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 319} 320 321int 322tmpfs_open(void *v) 323{ 324 struct vop_open_args /* { 325 struct vnode *a_vp; 326 int a_mode; 327 kauth_cred_t a_cred; 328 } */ *ap = v; 329 vnode_t *vp = ap->a_vp; 330 mode_t mode = ap->a_mode; 331 tmpfs_node_t *node; 332 333 KASSERT(VOP_ISLOCKED(vp)); 334 335 node = VP_TO_TMPFS_NODE(vp); 336 if (node->tn_links < 1) { 337 /* 338 * The file is still active, but all its names have been 339 * removed (e.g. by a "rmdir $(pwd)"). It cannot be opened 340 * any more, as it is about to be destroyed. 341 */ 342 return ENOENT; 343 } 344 345 /* If the file is marked append-only, deny write requests. */ 346 if ((node->tn_flags & APPEND) != 0 && 347 (mode & (FWRITE | O_APPEND)) == FWRITE) { 348 return EPERM; 349 } 350 return 0; 351} 352 353int 354tmpfs_close(void *v) 355{ 356 struct vop_close_args /* { 357 struct vnode *a_vp; 358 int a_fflag; 359 kauth_cred_t a_cred; 360 } */ *ap = v; 361 vnode_t *vp = ap->a_vp; 362 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 363 364 KASSERT(VOP_ISLOCKED(vp)); 365 366 if (node->tn_links > 0) { 367 tmpfs_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); 368 } 369 return 0; 370} 371 372static int 373tmpfs_check_possible(vnode_t *vp, tmpfs_node_t *node, mode_t mode) 374{ 375 const bool writing = (mode & VWRITE) != 0; 376 377 switch (vp->v_type) { 378 case VDIR: 379 case VLNK: 380 case VREG: 381 if (writing && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) { 382 return EROFS; 383 } 384 break; 385 case VBLK: 386 case VCHR: 387 case VSOCK: 388 case VFIFO: 389 break; 390 default: 391 return EINVAL; 392 } 393 return (writing && (node->tn_flags & IMMUTABLE) != 0) ? EPERM : 0; 394} 395 396static int 397tmpfs_check_permitted(vnode_t *vp, tmpfs_node_t *node, mode_t mode, 398 kauth_cred_t cred) 399{ 400 401 return genfs_can_access(vp->v_type, node->tn_mode, node->tn_uid, 402 node->tn_gid, mode, cred); 403} 404 405int 406tmpfs_access(void *v) 407{ 408 struct vop_access_args /* { 409 struct vnode *a_vp; 410 int a_mode; 411 kauth_cred_t a_cred; 412 } */ *ap = v; 413 vnode_t *vp = ap->a_vp; 414 mode_t mode = ap->a_mode; 415 kauth_cred_t cred = ap->a_cred; 416 tmpfs_node_t *node; 417 int error; 418 419 KASSERT(VOP_ISLOCKED(vp)); 420 421 node = VP_TO_TMPFS_NODE(vp); 422 error = tmpfs_check_possible(vp, node, mode); 423 if (error) { 424 return error; 425 } 426 return kauth_authorize_vnode(cred, kauth_mode_to_action(mode), vp, 427 NULL, tmpfs_check_permitted(vp, node, mode, cred)); 428} 429 430int 431tmpfs_getattr(void *v) 432{ 433 struct vop_getattr_args /* { 434 struct vnode *a_vp; 435 struct vattr *a_vap; 436 kauth_cred_t a_cred; 437 } */ *ap = v; 438 vnode_t *vp = ap->a_vp; 439 struct vattr *vap = ap->a_vap; 440 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 441 442 vattr_null(vap); 443 444 vap->va_type = vp->v_type; 445 vap->va_mode = node->tn_mode; 446 vap->va_nlink = node->tn_links; 447 vap->va_uid = node->tn_uid; 448 vap->va_gid = node->tn_gid; 449 vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; 450 vap->va_fileid = node->tn_id; 451 vap->va_size = node->tn_size; 452 vap->va_blocksize = PAGE_SIZE; 453 vap->va_atime = node->tn_atime; 454 vap->va_mtime = node->tn_mtime; 455 vap->va_ctime = node->tn_ctime; 456 vap->va_birthtime = node->tn_birthtime; 457 vap->va_gen = node->tn_gen; 458 vap->va_flags = node->tn_flags; 459 vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 460 node->tn_spec.tn_dev.tn_rdev : VNOVAL; 461 vap->va_bytes = round_page(node->tn_size); 462 vap->va_filerev = VNOVAL; 463 vap->va_vaflags = 0; 464 vap->va_spare = VNOVAL; /* XXX */ 465 466 tmpfs_update(vp, NULL, NULL, NULL, 0); 467 return 0; 468} 469 470#define GOODTIME(tv) ((tv)->tv_sec != VNOVAL || (tv)->tv_nsec != VNOVAL) 471/* XXX Should this operation be atomic? I think it should, but code in 472 * XXX other places (e.g., ufs) doesn't seem to be... */ 473int 474tmpfs_setattr(void *v) 475{ 476 struct vop_setattr_args /* { 477 struct vnode *a_vp; 478 struct vattr *a_vap; 479 kauth_cred_t a_cred; 480 } */ *ap = v; 481 vnode_t *vp = ap->a_vp; 482 struct vattr *vap = ap->a_vap; 483 kauth_cred_t cred = ap->a_cred; 484 lwp_t *l = curlwp; 485 int error = 0; 486 487 KASSERT(VOP_ISLOCKED(vp)); 488 489 /* Abort if any unsettable attribute is given. */ 490 if (vap->va_type != VNON || vap->va_nlink != VNOVAL || 491 vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL || 492 vap->va_blocksize != VNOVAL || GOODTIME(&vap->va_ctime) || 493 vap->va_gen != VNOVAL || vap->va_rdev != VNOVAL || 494 vap->va_bytes != VNOVAL) { 495 return EINVAL; 496 } 497 if (error == 0 && (vap->va_flags != VNOVAL)) 498 error = tmpfs_chflags(vp, vap->va_flags, cred, l); 499 500 if (error == 0 && (vap->va_size != VNOVAL)) 501 error = tmpfs_chsize(vp, vap->va_size, cred, l); 502 503 if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) 504 error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, l); 505 506 if (error == 0 && (vap->va_mode != VNOVAL)) 507 error = tmpfs_chmod(vp, vap->va_mode, cred, l); 508 509 if (error == 0 && (GOODTIME(&vap->va_atime) || GOODTIME(&vap->va_mtime) 510 || GOODTIME(&vap->va_birthtime))) { 511 error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, 512 &vap->va_birthtime, vap->va_vaflags, cred, l); 513 if (error == 0) 514 return 0; 515 } 516 tmpfs_update(vp, NULL, NULL, NULL, 0); 517 return error; 518} 519 520int 521tmpfs_read(void *v) 522{ 523 struct vop_read_args /* { 524 struct vnode *a_vp; 525 struct uio *a_uio; 526 int a_ioflag; 527 kauth_cred_t a_cred; 528 } */ *ap = v; 529 vnode_t *vp = ap->a_vp; 530 struct uio *uio = ap->a_uio; 531 const int ioflag = ap->a_ioflag; 532 tmpfs_node_t *node; 533 struct uvm_object *uobj; 534 int error; 535 536 KASSERT(VOP_ISLOCKED(vp)); 537 538 if (vp->v_type != VREG) { 539 return EISDIR; 540 } 541 if (uio->uio_offset < 0) { 542 return EINVAL; 543 } 544 545 node = VP_TO_TMPFS_NODE(vp); 546 node->tn_status |= TMPFS_NODE_ACCESSED; 547 uobj = node->tn_spec.tn_reg.tn_aobj; 548 error = 0; 549 550 while (error == 0 && uio->uio_resid > 0) { 551 vsize_t len; 552 553 if (node->tn_size <= uio->uio_offset) { 554 break; 555 } 556 len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid); 557 if (len == 0) { 558 break; 559 } 560 error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag), 561 UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); 562 } 563 return error; 564} 565 566int 567tmpfs_write(void *v) 568{ 569 struct vop_write_args /* { 570 struct vnode *a_vp; 571 struct uio *a_uio; 572 int a_ioflag; 573 kauth_cred_t a_cred; 574 } */ *ap = v; 575 vnode_t *vp = ap->a_vp; 576 struct uio *uio = ap->a_uio; 577 const int ioflag = ap->a_ioflag; 578 tmpfs_node_t *node; 579 struct uvm_object *uobj; 580 off_t oldsize; 581 bool extended; 582 int error; 583 584 KASSERT(VOP_ISLOCKED(vp)); 585 586 node = VP_TO_TMPFS_NODE(vp); 587 oldsize = node->tn_size; 588 589 if (uio->uio_offset < 0 || vp->v_type != VREG) { 590 error = EINVAL; 591 goto out; 592 } 593 if (uio->uio_resid == 0) { 594 error = 0; 595 goto out; 596 } 597 if (ioflag & IO_APPEND) { 598 uio->uio_offset = node->tn_size; 599 } 600 601 extended = uio->uio_offset + uio->uio_resid > node->tn_size; 602 if (extended) { 603 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); 604 if (error) 605 goto out; 606 } 607 608 uobj = node->tn_spec.tn_reg.tn_aobj; 609 error = 0; 610 while (error == 0 && uio->uio_resid > 0) { 611 vsize_t len; 612 613 len = MIN(node->tn_size - uio->uio_offset, uio->uio_resid); 614 if (len == 0) { 615 break; 616 } 617 error = ubc_uiomove(uobj, uio, len, IO_ADV_DECODE(ioflag), 618 UBC_WRITE | UBC_UNMAP_FLAG(vp)); 619 } 620 if (error) { 621 (void)tmpfs_reg_resize(vp, oldsize); 622 } 623 624 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 625 (extended ? TMPFS_NODE_CHANGED : 0); 626 VN_KNOTE(vp, NOTE_WRITE); 627out: 628 if (error) { 629 KASSERT(oldsize == node->tn_size); 630 } else { 631 KASSERT(uio->uio_resid == 0); 632 } 633 return error; 634} 635 636int 637tmpfs_fsync(void *v) 638{ 639 struct vop_fsync_args /* { 640 struct vnode *a_vp; 641 kauth_cred_t a_cred; 642 int a_flags; 643 off_t a_offlo; 644 off_t a_offhi; 645 struct lwp *a_l; 646 } */ *ap = v; 647 vnode_t *vp = ap->a_vp; 648 649 /* Nothing to do. Just update. */ 650 KASSERT(VOP_ISLOCKED(vp)); 651 tmpfs_update(vp, NULL, NULL, NULL, 0); 652 return 0; 653} 654 655/* 656 * tmpfs_remove: unlink a file. 657 * 658 * => Both directory (dvp) and file (vp) are locked. 659 * => We unlock and drop the reference on both. 660 */ 661int 662tmpfs_remove(void *v) 663{ 664 struct vop_remove_args /* { 665 struct vnode *a_dvp; 666 struct vnode *a_vp; 667 struct componentname *a_cnp; 668 } */ *ap = v; 669 vnode_t *dvp = ap->a_dvp, *vp = ap->a_vp; 670 struct componentname *cnp = ap->a_cnp; 671 tmpfs_node_t *dnode, *node; 672 tmpfs_dirent_t *de; 673 tmpfs_mount_t *tmp; 674 int error; 675 676 KASSERT(VOP_ISLOCKED(dvp)); 677 KASSERT(VOP_ISLOCKED(vp)); 678 679 if (vp->v_type == VDIR) { 680 error = EPERM; 681 goto out; 682 } 683 node = VP_TO_TMPFS_NODE(vp); 684 685 /* Files marked as immutable or append-only cannot be deleted. */ 686 if (node->tn_flags & (IMMUTABLE | APPEND)) { 687 error = EPERM; 688 goto out; 689 } 690 691 /* 692 * Lookup and remove the entry from the directory. Note that since 693 * it is a file, we do not need to change the number of hard links. 694 */ 695 dnode = VP_TO_TMPFS_DIR(dvp); 696 de = tmpfs_dir_lookup(dnode, cnp); 697 KASSERT(de && de->td_node == node); 698 tmpfs_dir_detach(dvp, de); 699 700 /* 701 * Free removed directory entry. Note that the node referred by it 702 * will not be removed until the vnode is really reclaimed. 703 */ 704 tmp = VFS_TO_TMPFS(vp->v_mount); 705 tmpfs_free_dirent(tmp, de, true); 706 error = 0; 707out: 708 /* Drop the references and unlock the vnodes. */ 709 vput(vp); 710 if (dvp == vp) { 711 vrele(dvp); 712 } else { 713 vput(dvp); 714 } 715 return error; 716} 717 718/* 719 * tmpfs_link: create a hard link. 720 */ 721int 722tmpfs_link(void *v) 723{ 724 struct vop_link_args /* { 725 struct vnode *a_dvp; 726 struct vnode *a_vp; 727 struct componentname *a_cnp; 728 } */ *ap = v; 729 vnode_t *dvp = ap->a_dvp; 730 vnode_t *vp = ap->a_vp; 731 struct componentname *cnp = ap->a_cnp; 732 tmpfs_node_t *dnode, *node; 733 tmpfs_dirent_t *de; 734 int error; 735 736 KASSERT(dvp != vp); 737 KASSERT(VOP_ISLOCKED(dvp)); 738 KASSERT(vp->v_type != VDIR); 739 KASSERT(dvp->v_mount == vp->v_mount); 740 741 dnode = VP_TO_TMPFS_DIR(dvp); 742 node = VP_TO_TMPFS_NODE(vp); 743 744 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 745 746 /* Check for maximum number of links limit. */ 747 KASSERT(node->tn_links <= LINK_MAX); 748 if (node->tn_links == LINK_MAX) { 749 error = EMLINK; 750 goto out; 751 } 752 753 /* We cannot create links of files marked immutable or append-only. */ 754 if (node->tn_flags & (IMMUTABLE | APPEND)) { 755 error = EPERM; 756 goto out; 757 } 758 759 /* Allocate a new directory entry to represent the node. */ 760 error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node, 761 cnp->cn_nameptr, cnp->cn_namelen, &de); 762 if (error) { 763 goto out; 764 } 765 766 /* Insert the new directory entry into the directory. */ 767 tmpfs_dir_attach(dvp, de); 768 769 /* Node link count has changed, so update node times. */ 770 node->tn_status |= TMPFS_NODE_CHANGED; 771 tmpfs_update(vp, NULL, NULL, NULL, 0); 772 error = 0; 773out: 774 VOP_UNLOCK(vp); 775 vput(dvp); 776 return error; 777} 778 779/* 780 * tmpfs_rename: rename routine. 781 * 782 * Arguments: fdvp (from-parent vnode), fvp (from-leaf), tdvp (to-parent) 783 * and tvp (to-leaf), if exists (NULL if not). 784 * 785 * => Caller holds a reference on fdvp and fvp, they are unlocked. 786 * Note: fdvp and fvp can refer to the same object (i.e. when it is root). 787 * 788 * => Both tdvp and tvp are referenced and locked. It is our responsibility 789 * to release the references and unlock them (or destroy). 790 */ 791int 792tmpfs_rename(void *v) 793{ 794 struct vop_rename_args /* { 795 struct vnode *a_fdvp; 796 struct vnode *a_fvp; 797 struct componentname *a_fcnp; 798 struct vnode *a_tdvp; 799 struct vnode *a_tvp; 800 struct componentname *a_tcnp; 801 } */ *ap = v; 802 vnode_t *fdvp = ap->a_fdvp; 803 vnode_t *fvp = ap->a_fvp; 804 struct componentname *fcnp = ap->a_fcnp; 805 vnode_t *tdvp = ap->a_tdvp; 806 vnode_t *tvp = ap->a_tvp; 807 struct componentname *tcnp = ap->a_tcnp; 808 tmpfs_node_t *fdnode, *fnode, *tnode, *tdnode; 809 tmpfs_dirent_t *de; 810 tmpfs_mount_t *tmp; 811 size_t namelen; 812 char *newname; 813 int error; 814 815 KASSERT(VOP_ISLOCKED(tdvp)); 816 KASSERT(tvp == NULL || VOP_ISLOCKED(tvp) == LK_EXCLUSIVE); 817 818 newname = NULL; 819 namelen = 0; 820 tmp = NULL; 821 822 /* Disallow cross-device renames. */ 823 if (fvp->v_mount != tdvp->v_mount || 824 (tvp != NULL && fvp->v_mount != tvp->v_mount)) { 825 error = EXDEV; 826 goto out_unlocked; 827 } 828 829 fnode = VP_TO_TMPFS_NODE(fvp); 830 fdnode = VP_TO_TMPFS_DIR(fdvp); 831 tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp); 832 tdnode = VP_TO_TMPFS_DIR(tdvp); 833 tmp = VFS_TO_TMPFS(tdvp->v_mount); 834 835 if (fdvp == tvp) { 836 error = 0; 837 goto out_unlocked; 838 } 839 840 /* Allocate memory, if necessary, for a new name. */ 841 namelen = tcnp->cn_namelen; 842 if (tmpfs_strname_neqlen(fcnp, tcnp)) { 843 newname = tmpfs_strname_alloc(tmp, namelen); 844 if (newname == NULL) { 845 error = ENOSPC; 846 goto out_unlocked; 847 } 848 } 849 850 /* XXX: Lock order violation! */ 851 if (fdnode != tdnode) { 852 vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY); 853 } 854 855 /* If the inode we were renaming has scarpered, just give up. */ 856 de = tmpfs_dir_lookup(fdnode, fcnp); 857 if (de == NULL || de->td_node != fnode) { 858 error = ENOENT; 859 goto out; 860 } 861 862 /* If source and target is the same vnode, remove the source link. */ 863 if (fvp == tvp) { 864 /* 865 * Detach and free the directory entry. Drops the link 866 * count on the inode. 867 */ 868 tmpfs_dir_detach(fdvp, de); 869 tmpfs_free_dirent(VFS_TO_TMPFS(fvp->v_mount), de, true); 870 VN_KNOTE(fdvp, NOTE_WRITE); 871 goto out_ok; 872 } 873 874 /* If replacing an existing entry, ensure we can do the operation. */ 875 if (tvp != NULL) { 876 KASSERT(tnode != NULL); 877 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) { 878 if (tnode->tn_size > 0) { 879 error = ENOTEMPTY; 880 goto out; 881 } 882 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) { 883 error = ENOTDIR; 884 goto out; 885 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) { 886 error = EISDIR; 887 goto out; 888 } else { 889 KASSERT(fnode->tn_type != VDIR); 890 KASSERT(tnode->tn_type != VDIR); 891 } 892 } 893 894 /* Are we moving the inode to a different directory? */ 895 if (fdnode != tdnode) { 896 /* Are we moving a directory? */ 897 if (de->td_node->tn_type == VDIR) { 898 tmpfs_node_t *upnode; 899 900 /* 901 * Ensure the target directory is not a child of the 902 * directory being moved. Otherwise, it would result 903 * in stale nodes. 904 */ 905 upnode = tdnode; 906 while (upnode != upnode->tn_spec.tn_dir.tn_parent) { 907 if (upnode == fnode) { 908 error = EINVAL; 909 goto out; 910 } 911 upnode = upnode->tn_spec.tn_dir.tn_parent; 912 } 913 914 /* Adjust the parent pointer. */ 915 TMPFS_VALIDATE_DIR(fnode); 916 de->td_node->tn_spec.tn_dir.tn_parent = tdnode; 917 918 /* Adjust the link counts. */ 919 fdnode->tn_links--; 920 tdnode->tn_links++; 921 } 922 923 /* 924 * Perform the move: detach from the source directory and 925 * attach into the target directory. 926 */ 927 tmpfs_dir_detach(fdvp, de); 928 tmpfs_dir_attach(tdvp, de); 929 930 /* Trigger the event. */ 931 VN_KNOTE(fdvp, NOTE_WRITE); 932 } 933 934 /* Are we overwriting the entry? */ 935 if (tvp != NULL) { 936 tmpfs_dirent_t *de2; 937 938 /* 939 * Remove the old entry from the target directory. 940 * Note: This relies on tmpfs_dir_attach() putting the new 941 * node on the end of the target's node list. 942 */ 943 de2 = tmpfs_dir_lookup(tdnode, tcnp); 944 KASSERT(de2 && de2->td_node == tnode); 945 tmpfs_dir_detach(tdvp, de2); 946 947 /* Destroy the detached directory entry. */ 948 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de2, true); 949 } 950 951 /* If the name has changed, update directory entry. */ 952 if (newname != NULL) { 953 KASSERT(tcnp->cn_namelen < MAXNAMLEN); 954 955 tmpfs_strname_free(tmp, de->td_name, de->td_namelen); 956 de->td_namelen = (uint16_t)namelen; 957 memcpy(newname, tcnp->cn_nameptr, namelen); 958 de->td_name = newname; 959 newname = NULL; 960 961 fnode->tn_status |= TMPFS_NODE_CHANGED; 962 tdnode->tn_status |= TMPFS_NODE_MODIFIED; 963 } 964out_ok: 965 /* Notify listeners of source and target directories. */ 966 VN_KNOTE(tdvp, NOTE_WRITE); 967 VN_KNOTE(fvp, NOTE_RENAME); 968 error = 0; 969out: 970 if (fdnode != tdnode) { 971 VOP_UNLOCK(fdvp); 972 } 973out_unlocked: 974 /* Release target nodes. */ 975 if (tdvp == tvp) { 976 vrele(tdvp); 977 } else { 978 vput(tdvp); 979 } 980 if (tvp != NULL) { 981 vput(tvp); 982 } 983 984 /* Release source nodes. */ 985 vrele(fdvp); 986 vrele(fvp); 987 988 if (newname != NULL) { 989 tmpfs_strname_free(tmp, newname, namelen); 990 } 991 return error; 992} 993 994int 995tmpfs_mkdir(void *v) 996{ 997 struct vop_mkdir_args /* { 998 struct vnode *a_dvp; 999 struct vnode **a_vpp; 1000 struct componentname *a_cnp; 1001 struct vattr *a_vap; 1002 } */ *ap = v; 1003 vnode_t *dvp = ap->a_dvp; 1004 vnode_t **vpp = ap->a_vpp; 1005 struct componentname *cnp = ap->a_cnp; 1006 struct vattr *vap = ap->a_vap; 1007 1008 KASSERT(vap->va_type == VDIR); 1009 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 1010} 1011 1012int 1013tmpfs_rmdir(void *v) 1014{ 1015 struct vop_rmdir_args /* { 1016 struct vnode *a_dvp; 1017 struct vnode *a_vp; 1018 struct componentname *a_cnp; 1019 } */ *ap = v; 1020 vnode_t *dvp = ap->a_dvp; 1021 vnode_t *vp = ap->a_vp; 1022 struct componentname *cnp = ap->a_cnp; 1023 tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); 1024 tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); 1025 tmpfs_node_t *node = VP_TO_TMPFS_DIR(vp); 1026 tmpfs_dirent_t *de; 1027 int error = 0; 1028 1029 KASSERT(VOP_ISLOCKED(dvp)); 1030 KASSERT(VOP_ISLOCKED(vp)); 1031 KASSERT(node->tn_spec.tn_dir.tn_parent == dnode); 1032 1033 /* 1034 * Directories with more than two entries ('.' and '..') cannot 1035 * be removed. 1036 */ 1037 if (node->tn_size > 0) { 1038 error = ENOTEMPTY; 1039 goto out; 1040 } 1041 1042 /* Get the directory entry associated with inode (vp). */ 1043 de = tmpfs_dir_lookup(dnode, cnp); 1044 KASSERT(de && de->td_node == node); 1045 1046 /* Check flags to see if we are allowed to remove the directory. */ 1047 if (dnode->tn_flags & APPEND || node->tn_flags & (IMMUTABLE | APPEND)) { 1048 error = EPERM; 1049 goto out; 1050 } 1051 1052 /* Detach the directory entry from the directory (dnode). */ 1053 tmpfs_dir_detach(dvp, de); 1054 1055 node->tn_links--; 1056 node->tn_status |= TMPFS_NODE_STATUSALL; 1057 node->tn_spec.tn_dir.tn_parent->tn_links--; 1058 node->tn_spec.tn_dir.tn_parent->tn_status |= TMPFS_NODE_STATUSALL; 1059 1060 /* Purge the cache for parent. */ 1061 cache_purge(dvp); 1062 1063 /* 1064 * Destroy the directory entry. Note: the inode referred by it 1065 * will not be destroyed until the vnode is reclaimed. 1066 */ 1067 tmpfs_free_dirent(tmp, de, true); 1068 KASSERT(node->tn_links == 0); 1069out: 1070 /* Release the nodes. */ 1071 vput(dvp); 1072 vput(vp); 1073 return error; 1074} 1075 1076int 1077tmpfs_symlink(void *v) 1078{ 1079 struct vop_symlink_args /* { 1080 struct vnode *a_dvp; 1081 struct vnode **a_vpp; 1082 struct componentname *a_cnp; 1083 struct vattr *a_vap; 1084 char *a_target; 1085 } */ *ap = v; 1086 vnode_t *dvp = ap->a_dvp; 1087 vnode_t **vpp = ap->a_vpp; 1088 struct componentname *cnp = ap->a_cnp; 1089 struct vattr *vap = ap->a_vap; 1090 char *target = ap->a_target; 1091 1092 KASSERT(vap->va_type == VLNK); 1093 return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); 1094} 1095 1096int 1097tmpfs_readdir(void *v) 1098{ 1099 struct vop_readdir_args /* { 1100 struct vnode *a_vp; 1101 struct uio *a_uio; 1102 kauth_cred_t a_cred; 1103 int *a_eofflag; 1104 off_t **a_cookies; 1105 int *ncookies; 1106 } */ *ap = v; 1107 vnode_t *vp = ap->a_vp; 1108 struct uio *uio = ap->a_uio; 1109 int *eofflag = ap->a_eofflag; 1110 off_t **cookies = ap->a_cookies; 1111 int *ncookies = ap->a_ncookies; 1112 off_t startoff, cnt; 1113 tmpfs_node_t *node; 1114 int error; 1115 1116 KASSERT(VOP_ISLOCKED(vp)); 1117 1118 /* This operation only makes sense on directory nodes. */ 1119 if (vp->v_type != VDIR) { 1120 return ENOTDIR; 1121 } 1122 node = VP_TO_TMPFS_DIR(vp); 1123 startoff = uio->uio_offset; 1124 cnt = 0; 1125 1126 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { 1127 error = tmpfs_dir_getdotdent(node, uio); 1128 if (error != 0) { 1129 if (error == -1) 1130 error = 0; 1131 goto out; 1132 } 1133 cnt++; 1134 } 1135 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { 1136 error = tmpfs_dir_getdotdotdent(node, uio); 1137 if (error != 0) { 1138 if (error == -1) 1139 error = 0; 1140 goto out; 1141 } 1142 cnt++; 1143 } 1144 error = tmpfs_dir_getdents(node, uio, &cnt); 1145 if (error == -1) { 1146 error = 0; 1147 } 1148 KASSERT(error >= 0); 1149out: 1150 if (eofflag != NULL) { 1151 *eofflag = (!error && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); 1152 } 1153 if (error || cookies == NULL || ncookies == NULL) { 1154 return error; 1155 } 1156 1157 /* Update NFS-related variables, if any. */ 1158 off_t i, off = startoff; 1159 tmpfs_dirent_t *de = NULL; 1160 1161 *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); 1162 *ncookies = cnt; 1163 1164 for (i = 0; i < cnt; i++) { 1165 KASSERT(off != TMPFS_DIRCOOKIE_EOF); 1166 if (off != TMPFS_DIRCOOKIE_DOT) { 1167 if (off == TMPFS_DIRCOOKIE_DOTDOT) { 1168 de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); 1169 } else if (de != NULL) { 1170 de = TAILQ_NEXT(de, td_entries); 1171 } else { 1172 de = tmpfs_dir_lookupbycookie(node, off); 1173 KASSERT(de != NULL); 1174 de = TAILQ_NEXT(de, td_entries); 1175 } 1176 if (de == NULL) { 1177 off = TMPFS_DIRCOOKIE_EOF; 1178 } else { 1179 off = tmpfs_dircookie(de); 1180 } 1181 } else { 1182 off = TMPFS_DIRCOOKIE_DOTDOT; 1183 } 1184 (*cookies)[i] = off; 1185 } 1186 KASSERT(uio->uio_offset == off); 1187 return error; 1188} 1189 1190int 1191tmpfs_readlink(void *v) 1192{ 1193 struct vop_readlink_args /* { 1194 struct vnode *a_vp; 1195 struct uio *a_uio; 1196 kauth_cred_t a_cred; 1197 } */ *ap = v; 1198 vnode_t *vp = ap->a_vp; 1199 struct uio *uio = ap->a_uio; 1200 tmpfs_node_t *node; 1201 int error; 1202 1203 KASSERT(VOP_ISLOCKED(vp)); 1204 KASSERT(uio->uio_offset == 0); 1205 KASSERT(vp->v_type == VLNK); 1206 1207 node = VP_TO_TMPFS_NODE(vp); 1208 error = uiomove(node->tn_spec.tn_lnk.tn_link, 1209 MIN(node->tn_size, uio->uio_resid), uio); 1210 node->tn_status |= TMPFS_NODE_ACCESSED; 1211 1212 return error; 1213} 1214 1215int 1216tmpfs_inactive(void *v) 1217{ 1218 struct vop_inactive_args /* { 1219 struct vnode *a_vp; 1220 bool *a_recycle; 1221 } */ *ap = v; 1222 vnode_t *vp = ap->a_vp; 1223 tmpfs_node_t *node; 1224 1225 KASSERT(VOP_ISLOCKED(vp)); 1226 1227 node = VP_TO_TMPFS_NODE(vp); 1228 *ap->a_recycle = (node->tn_links == 0); 1229 VOP_UNLOCK(vp); 1230 1231 return 0; 1232} 1233 1234int 1235tmpfs_reclaim(void *v) 1236{ 1237 struct vop_reclaim_args /* { 1238 struct vnode *a_vp; 1239 } */ *ap = v; 1240 vnode_t *vp = ap->a_vp; 1241 tmpfs_mount_t *tmp = VFS_TO_TMPFS(vp->v_mount); 1242 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 1243 1244 /* Disassociate inode from vnode. */ 1245 tmpfs_free_vp(vp); 1246 KASSERT(vp->v_data == NULL); 1247 1248 /* If inode is not referenced, i.e. no links, then destroy it. */ 1249 if (node->tn_links == 0) { 1250 tmpfs_free_node(tmp, node); 1251 } 1252 return 0; 1253} 1254 1255int 1256tmpfs_pathconf(void *v) 1257{ 1258 struct vop_pathconf_args /* { 1259 struct vnode *a_vp; 1260 int a_name; 1261 register_t *a_retval; 1262 } */ *ap = v; 1263 const int name = ap->a_name; 1264 register_t *retval = ap->a_retval; 1265 int error = 0; 1266 1267 switch (name) { 1268 case _PC_LINK_MAX: 1269 *retval = LINK_MAX; 1270 break; 1271 case _PC_NAME_MAX: 1272 *retval = NAME_MAX; 1273 break; 1274 case _PC_PATH_MAX: 1275 *retval = PATH_MAX; 1276 break; 1277 case _PC_PIPE_BUF: 1278 *retval = PIPE_BUF; 1279 break; 1280 case _PC_CHOWN_RESTRICTED: 1281 *retval = 1; 1282 break; 1283 case _PC_NO_TRUNC: 1284 *retval = 1; 1285 break; 1286 case _PC_SYNC_IO: 1287 *retval = 1; 1288 break; 1289 case _PC_FILESIZEBITS: 1290 *retval = 0; /* FIXME */ 1291 break; 1292 default: 1293 error = EINVAL; 1294 } 1295 return error; 1296} 1297 1298int 1299tmpfs_advlock(void *v) 1300{ 1301 struct vop_advlock_args /* { 1302 struct vnode *a_vp; 1303 void * a_id; 1304 int a_op; 1305 struct flock *a_fl; 1306 int a_flags; 1307 } */ *ap = v; 1308 vnode_t *vp = ap->a_vp; 1309 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 1310 1311 return lf_advlock(v, &node->tn_lockf, node->tn_size); 1312} 1313 1314int 1315tmpfs_getpages(void *v) 1316{ 1317 struct vop_getpages_args /* { 1318 struct vnode *a_vp; 1319 voff_t a_offset; 1320 struct vm_page **a_m; 1321 int *a_count; 1322 int a_centeridx; 1323 vm_prot_t a_access_type; 1324 int a_advice; 1325 int a_flags; 1326 } */ * const ap = v; 1327 vnode_t *vp = ap->a_vp; 1328 const voff_t offset = ap->a_offset; 1329 struct vm_page **pgs = ap->a_m; 1330 const int centeridx = ap->a_centeridx; 1331 const vm_prot_t access_type = ap->a_access_type; 1332 const int advice = ap->a_advice; 1333 const int flags = ap->a_flags; 1334 int error, npages = *ap->a_count; 1335 tmpfs_node_t *node; 1336 struct uvm_object *uobj; 1337 1338 KASSERT(vp->v_type == VREG); 1339 KASSERT(mutex_owned(&vp->v_interlock)); 1340 1341 node = VP_TO_TMPFS_NODE(vp); 1342 uobj = node->tn_spec.tn_reg.tn_aobj; 1343 1344 /* 1345 * Currently, PGO_PASTEOF is not supported. 1346 */ 1347 if (vp->v_size <= offset + (centeridx << PAGE_SHIFT)) { 1348 if ((flags & PGO_LOCKED) == 0) 1349 mutex_exit(&vp->v_interlock); 1350 return EINVAL; 1351 } 1352 1353 if (vp->v_size < offset + (npages << PAGE_SHIFT)) { 1354 npages = (round_page(vp->v_size) - offset) >> PAGE_SHIFT; 1355 } 1356 1357 if ((flags & PGO_LOCKED) != 0) 1358 return EBUSY; 1359 1360 if ((flags & PGO_NOTIMESTAMP) == 0) { 1361 if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0) 1362 node->tn_status |= TMPFS_NODE_ACCESSED; 1363 1364 if ((access_type & VM_PROT_WRITE) != 0) 1365 node->tn_status |= TMPFS_NODE_MODIFIED; 1366 } 1367 1368 mutex_exit(&vp->v_interlock); 1369 1370 /* 1371 * Invoke the pager. 1372 * 1373 * Clean the array of pages before. XXX: PR/32166 1374 * Note that vnode lock is shared with underlying UVM object. 1375 */ 1376 if (pgs) { 1377 memset(pgs, 0, sizeof(struct vm_pages *) * npages); 1378 } 1379 mutex_enter(&uobj->vmobjlock); 1380 error = (*uobj->pgops->pgo_get)(uobj, offset, pgs, &npages, centeridx, 1381 access_type, advice, flags | PGO_ALLPAGES); 1382 1383#if defined(DEBUG) 1384 if (!error && pgs) { 1385 for (int i = 0; i < npages; i++) { 1386 KASSERT(pgs[i] != NULL); 1387 } 1388 } 1389#endif 1390 return error; 1391} 1392 1393int 1394tmpfs_putpages(void *v) 1395{ 1396 struct vop_putpages_args /* { 1397 struct vnode *a_vp; 1398 voff_t a_offlo; 1399 voff_t a_offhi; 1400 int a_flags; 1401 } */ * const ap = v; 1402 vnode_t *vp = ap->a_vp; 1403 const voff_t offlo = ap->a_offlo; 1404 const voff_t offhi = ap->a_offhi; 1405 const int flags = ap->a_flags; 1406 tmpfs_node_t *node; 1407 struct uvm_object *uobj; 1408 int error; 1409 1410 KASSERT(mutex_owned(&vp->v_interlock)); 1411 1412 node = VP_TO_TMPFS_NODE(vp); 1413 1414 if (vp->v_type != VREG) { 1415 mutex_exit(&vp->v_interlock); 1416 return 0; 1417 } 1418 1419 uobj = node->tn_spec.tn_reg.tn_aobj; 1420 mutex_exit(&vp->v_interlock); 1421 1422 mutex_enter(&uobj->vmobjlock); 1423 error = (*uobj->pgops->pgo_put)(uobj, offlo, offhi, flags); 1424 1425 /* XXX mtime */ 1426 1427 return error; 1428} 1429 1430#ifdef TMPFS_WHITEOUT 1431int 1432tmpfs_whiteout(void *v) 1433{ 1434 struct vop_whiteout_args /* { 1435 struct vnode *a_dvp; 1436 struct componentname *a_cnp; 1437 int a_flags; 1438 } */ *ap = v; 1439 vnode_t *dvp = ap->a_dvp; 1440 struct componentname *cnp = ap->a_cnp; 1441 const int flags = ap->a_flags; 1442 tmpfs_mount_t *tmp = VFS_TO_TMPFS(dvp->v_mount); 1443 tmpfs_dirent_t *de; 1444 int error; 1445 1446 switch (flags) { 1447 case LOOKUP: 1448 break; 1449 case CREATE: 1450 error = tmpfs_alloc_dirent(tmp, TMPFS_NODE_WHITEOUT, 1451 cnp->cn_nameptr, cnp->cn_namelen, &de); 1452 if (error) 1453 return error; 1454 tmpfs_dir_attach(dvp, de); 1455 break; 1456 case DELETE: 1457 cnp->cn_flags &= ~DOWHITEOUT; /* when in doubt, cargo cult */ 1458 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), cnp); 1459 if (de == NULL) 1460 return ENOENT; 1461 tmpfs_dir_detach(dvp, de); 1462 tmpfs_free_dirent(tmp, de, true); 1463 break; 1464 } 1465 return 0; 1466} 1467#endif 1468 1469int 1470tmpfs_print(void *v) 1471{ 1472 struct vop_print_args /* { 1473 struct vnode *a_vp; 1474 } */ *ap = v; 1475 vnode_t *vp = ap->a_vp; 1476 tmpfs_node_t *node = VP_TO_TMPFS_NODE(vp); 1477 1478 printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n" 1479 "\tmode 0%o, owner %d, group %d, size %" PRIdMAX ", status 0x%x", 1480 node, node->tn_flags, node->tn_links, node->tn_mode, node->tn_uid, 1481 node->tn_gid, (uintmax_t)node->tn_size, node->tn_status); 1482 if (vp->v_type == VFIFO) { 1483 VOCALL(fifo_vnodeop_p, VOFFSET(vop_print), v); 1484 } 1485 printf("\n"); 1486 return 0; 1487} 1488