tmpfs_vnops.c revision 173725
1/* $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $ */ 2 3/* 4 * Copyright (c) 2005, 2006 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40/* 41 * tmpfs vnode interface. 42 */ 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/sys/fs/tmpfs/tmpfs_vnops.c 173725 2007-11-18 04:52:40Z delphij $"); 45 46#include <sys/param.h> 47#include <sys/fcntl.h> 48#include <sys/lockf.h> 49#include <sys/namei.h> 50#include <sys/priv.h> 51#include <sys/proc.h> 52#include <sys/resourcevar.h> 53#include <sys/stat.h> 54#include <sys/systm.h> 55#include <sys/unistd.h> 56#include <sys/vnode.h> 57 58#include <vm/vm.h> 59#include <vm/vm_object.h> 60#include <vm/vm_page.h> 61#include <vm/vm_pager.h> 62#include <sys/sched.h> 63#include <sys/sf_buf.h> 64#include <machine/_inttypes.h> 65 66#include <fs/fifofs/fifo.h> 67#include <fs/tmpfs/tmpfs_vnops.h> 68#include <fs/tmpfs/tmpfs.h> 69 70/* --------------------------------------------------------------------- */ 71 72static int 73tmpfs_lookup(struct vop_cachedlookup_args *v) 74{ 75 struct vnode *dvp = v->a_dvp; 76 struct vnode **vpp = v->a_vpp; 77 struct componentname *cnp = v->a_cnp; 78 struct thread *td = cnp->cn_thread; 79 80 int error; 81 struct tmpfs_dirent *de; 82 struct tmpfs_node *dnode; 83 84 dnode = VP_TO_TMPFS_DIR(dvp); 85 *vpp = NULLVP; 86 87 /* Check accessibility of requested node as a first step. */ 88 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td); 89 if (error != 0) 90 goto out; 91 92 /* We cannot be requesting the parent directory of the root node. */ 93 MPASS(IMPLIES(dnode->tn_type == VDIR && 94 dnode->tn_dir.tn_parent == dnode, 95 !(cnp->cn_flags & ISDOTDOT))); 96 97 if (cnp->cn_flags & ISDOTDOT) { 98 int ltype = 0; 99 100 ltype = VOP_ISLOCKED(dvp, td); 101 vhold(dvp); 102 VOP_UNLOCK(dvp, 0, td); 103 /* Allocate a new vnode on the matching entry. */ 104 error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent, 105 cnp->cn_lkflags, vpp, td); 106 107 vn_lock(dvp, ltype | LK_RETRY, td); 108 vdrop(dvp); 109 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 110 VREF(dvp); 111 *vpp = dvp; 112 error = 0; 113 } else { 114 de = tmpfs_dir_lookup(dnode, cnp); 115 if (de == NULL) { 116 /* The entry was not found in the directory. 117 * This is OK if we are creating or renaming an 118 * entry and are working on the last component of 119 * the path name. */ 120 if ((cnp->cn_flags & ISLASTCN) && 121 (cnp->cn_nameiop == CREATE || \ 122 cnp->cn_nameiop == RENAME)) { 123 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 124 cnp->cn_thread); 125 if (error != 0) 126 goto out; 127 128 /* Keep the component name in the buffer for 129 * future uses. */ 130 cnp->cn_flags |= SAVENAME; 131 132 error = EJUSTRETURN; 133 } else 134 error = ENOENT; 135 } else { 136 struct tmpfs_node *tnode; 137 138 /* The entry was found, so get its associated 139 * tmpfs_node. */ 140 tnode = de->td_node; 141 142 /* If we are not at the last path component and 143 * found a non-directory or non-link entry (which 144 * may itself be pointing to a directory), raise 145 * an error. */ 146 if ((tnode->tn_type != VDIR && 147 tnode->tn_type != VLNK) && 148 !(cnp->cn_flags & ISLASTCN)) { 149 error = ENOTDIR; 150 goto out; 151 } 152 153 /* If we are deleting or renaming the entry, keep 154 * track of its tmpfs_dirent so that it can be 155 * easily deleted later. */ 156 if ((cnp->cn_flags & ISLASTCN) && 157 (cnp->cn_nameiop == DELETE || 158 cnp->cn_nameiop == RENAME)) { 159 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 160 cnp->cn_thread); 161 if (error != 0) 162 goto out; 163 164 /* Allocate a new vnode on the matching entry. */ 165 error = tmpfs_alloc_vp(dvp->v_mount, tnode, 166 cnp->cn_lkflags, vpp, td); 167 if (error != 0) 168 goto out; 169 170 if ((dnode->tn_mode & S_ISTXT) && 171 VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) && 172 VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) { 173 error = EPERM; 174 vput(*vpp); 175 *vpp = NULL; 176 goto out; 177 } 178 cnp->cn_flags |= SAVENAME; 179 } else { 180 error = tmpfs_alloc_vp(dvp->v_mount, tnode, 181 cnp->cn_lkflags, vpp, td); 182 } 183 } 184 } 185 186 /* Store the result of this lookup in the cache. Avoid this if the 187 * request was for creation, as it does not improve timings on 188 * emprical tests. */ 189 if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) 190 cache_enter(dvp, *vpp, cnp); 191 192out: 193 /* If there were no errors, *vpp cannot be null and it must be 194 * locked. */ 195 MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp, td))); 196 197 return error; 198} 199 200/* --------------------------------------------------------------------- */ 201 202static int 203tmpfs_create(struct vop_create_args *v) 204{ 205 struct vnode *dvp = v->a_dvp; 206 struct vnode **vpp = v->a_vpp; 207 struct componentname *cnp = v->a_cnp; 208 struct vattr *vap = v->a_vap; 209 210 MPASS(vap->va_type == VREG || vap->va_type == VSOCK); 211 212 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 213} 214/* --------------------------------------------------------------------- */ 215 216static int 217tmpfs_mknod(struct vop_mknod_args *v) 218{ 219 struct vnode *dvp = v->a_dvp; 220 struct vnode **vpp = v->a_vpp; 221 struct componentname *cnp = v->a_cnp; 222 struct vattr *vap = v->a_vap; 223 224 if (vap->va_type != VBLK && vap->va_type != VCHR && 225 vap->va_type != VFIFO) 226 return EINVAL; 227 228 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 229} 230 231/* --------------------------------------------------------------------- */ 232 233static int 234tmpfs_open(struct vop_open_args *v) 235{ 236 struct vnode *vp = v->a_vp; 237 int mode = v->a_mode; 238 239 int error; 240 struct tmpfs_node *node; 241 242 MPASS(VOP_ISLOCKED(vp, v->a_td)); 243 244 node = VP_TO_TMPFS_NODE(vp); 245 246 /* The file is still active but all its names have been removed 247 * (e.g. by a "rmdir $(pwd)"). It cannot be opened any more as 248 * it is about to die. */ 249 if (node->tn_links < 1) 250 return (ENOENT); 251 252 /* If the file is marked append-only, deny write requests. */ 253 if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE) 254 error = EPERM; 255 else { 256 error = 0; 257 vnode_create_vobject(vp, node->tn_size, v->a_td); 258 } 259 260 MPASS(VOP_ISLOCKED(vp, v->a_td)); 261 return error; 262} 263 264/* --------------------------------------------------------------------- */ 265 266static int 267tmpfs_close(struct vop_close_args *v) 268{ 269 struct vnode *vp = v->a_vp; 270 271 struct tmpfs_node *node; 272 273 MPASS(VOP_ISLOCKED(vp, v->a_td)); 274 275 node = VP_TO_TMPFS_NODE(vp); 276 277 if (node->tn_links > 0) { 278 /* Update node times. No need to do it if the node has 279 * been deleted, because it will vanish after we return. */ 280 tmpfs_update(vp); 281 } 282 283 return 0; 284} 285 286/* --------------------------------------------------------------------- */ 287 288int 289tmpfs_access(struct vop_access_args *v) 290{ 291 struct vnode *vp = v->a_vp; 292 int mode = v->a_mode; 293 struct ucred *cred = v->a_cred; 294 295 int error; 296 struct tmpfs_node *node; 297 298 MPASS(VOP_ISLOCKED(vp, v->a_td)); 299 300 node = VP_TO_TMPFS_NODE(vp); 301 302 switch (vp->v_type) { 303 case VDIR: 304 /* FALLTHROUGH */ 305 case VLNK: 306 /* FALLTHROUGH */ 307 case VREG: 308 if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) { 309 error = EROFS; 310 goto out; 311 } 312 break; 313 314 case VBLK: 315 /* FALLTHROUGH */ 316 case VCHR: 317 /* FALLTHROUGH */ 318 case VSOCK: 319 /* FALLTHROUGH */ 320 case VFIFO: 321 break; 322 323 default: 324 error = EINVAL; 325 goto out; 326 } 327 328 if (mode & VWRITE && node->tn_flags & IMMUTABLE) { 329 error = EPERM; 330 goto out; 331 } 332 333 error = vaccess(vp->v_type, node->tn_mode, node->tn_uid, 334 node->tn_gid, mode, cred, NULL); 335 336out: 337 MPASS(VOP_ISLOCKED(vp, v->a_td)); 338 339 return error; 340} 341 342/* --------------------------------------------------------------------- */ 343 344int 345tmpfs_getattr(struct vop_getattr_args *v) 346{ 347 struct vnode *vp = v->a_vp; 348 struct vattr *vap = v->a_vap; 349 350 struct tmpfs_node *node; 351 352 node = VP_TO_TMPFS_NODE(vp); 353 354 VATTR_NULL(vap); 355 356 tmpfs_update(vp); 357 358 vap->va_type = vp->v_type; 359 vap->va_mode = node->tn_mode; 360 vap->va_nlink = node->tn_links; 361 vap->va_uid = node->tn_uid; 362 vap->va_gid = node->tn_gid; 363 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 364 vap->va_fileid = node->tn_id; 365 vap->va_size = node->tn_size; 366 vap->va_blocksize = PAGE_SIZE; 367 vap->va_atime = node->tn_atime; 368 vap->va_mtime = node->tn_mtime; 369 vap->va_ctime = node->tn_ctime; 370 vap->va_birthtime = node->tn_birthtime; 371 vap->va_gen = node->tn_gen; 372 vap->va_flags = node->tn_flags; 373 vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 374 node->tn_rdev : VNOVAL; 375 vap->va_bytes = round_page(node->tn_size); 376 vap->va_filerev = VNOVAL; 377 vap->va_vaflags = 0; 378 vap->va_spare = VNOVAL; /* XXX */ 379 380 return 0; 381} 382 383/* --------------------------------------------------------------------- */ 384 385/* XXX Should this operation be atomic? I think it should, but code in 386 * XXX other places (e.g., ufs) doesn't seem to be... */ 387int 388tmpfs_setattr(struct vop_setattr_args *v) 389{ 390 struct vnode *vp = v->a_vp; 391 struct vattr *vap = v->a_vap; 392 struct ucred *cred = v->a_cred; 393 struct thread *l = v->a_td; 394 395 int error; 396 397 MPASS(VOP_ISLOCKED(vp, l)); 398 399 error = 0; 400 401 /* Abort if any unsettable attribute is given. */ 402 if (vap->va_type != VNON || 403 vap->va_nlink != VNOVAL || 404 vap->va_fsid != VNOVAL || 405 vap->va_fileid != VNOVAL || 406 vap->va_blocksize != VNOVAL || 407 vap->va_gen != VNOVAL || 408 vap->va_rdev != VNOVAL || 409 vap->va_bytes != VNOVAL) 410 error = EINVAL; 411 412 if (error == 0 && (vap->va_flags != VNOVAL)) 413 error = tmpfs_chflags(vp, vap->va_flags, cred, l); 414 415 if (error == 0 && (vap->va_size != VNOVAL)) 416 error = tmpfs_chsize(vp, vap->va_size, cred, l); 417 418 if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) 419 error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, 420 l); 421 422 if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) 423 error = tmpfs_chmod(vp, vap->va_mode, cred, l); 424 425 if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL && 426 vap->va_atime.tv_nsec != VNOVAL) || 427 (vap->va_mtime.tv_sec != VNOVAL && 428 vap->va_mtime.tv_nsec != VNOVAL) || 429 (vap->va_birthtime.tv_sec != VNOVAL && 430 vap->va_birthtime.tv_nsec != VNOVAL))) 431 error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, 432 &vap->va_birthtime, vap->va_vaflags, cred, l); 433 434 /* Update the node times. We give preference to the error codes 435 * generated by this function rather than the ones that may arise 436 * from tmpfs_update. */ 437 tmpfs_update(vp); 438 439 MPASS(VOP_ISLOCKED(vp, l)); 440 441 return error; 442} 443 444/* --------------------------------------------------------------------- */ 445 446static int 447tmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio) 448{ 449 vm_pindex_t idx; 450 vm_page_t m; 451 struct sf_buf *sf; 452 off_t offset, addr; 453 size_t tlen; 454 caddr_t va; 455 int error; 456 457 addr = uio->uio_offset; 458 idx = OFF_TO_IDX(addr); 459 offset = addr & PAGE_MASK; 460 tlen = MIN(PAGE_SIZE - offset, len); 461 462 if ((vobj == NULL) || (vobj->resident_page_count == 0)) 463 goto nocache; 464 465 VM_OBJECT_LOCK(vobj); 466lookupvpg: 467 if (((m = vm_page_lookup(vobj, idx)) != NULL) && 468 vm_page_is_valid(m, offset, tlen)) { 469 if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr")) 470 goto lookupvpg; 471 vm_page_busy(m); 472 VM_OBJECT_UNLOCK(vobj); 473 sched_pin(); 474 sf = sf_buf_alloc(m, SFB_CPUPRIVATE); 475 va = (caddr_t)sf_buf_kva(sf); 476 error = uiomove(va + offset, tlen, uio); 477 sf_buf_free(sf); 478 sched_unpin(); 479 VM_OBJECT_LOCK(vobj); 480 vm_page_wakeup(m); 481 VM_OBJECT_UNLOCK(vobj); 482 return (error); 483 } 484 VM_OBJECT_UNLOCK(vobj); 485nocache: 486 VM_OBJECT_LOCK(tobj); 487 vm_object_pip_add(tobj, 1); 488 m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED | 489 VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY); 490 if (m->valid != VM_PAGE_BITS_ALL) { 491 int behind, ahead; 492 if (vm_pager_has_page(tobj, idx, &behind, &ahead)) { 493 error = vm_pager_get_pages(tobj, &m, 1, 0); 494 if (error != 0) { 495 printf("tmpfs get pages from pager error [read]\n"); 496 goto out; 497 } 498 } else 499 vm_page_zero_invalid(m, TRUE); 500 } 501 VM_OBJECT_UNLOCK(tobj); 502 sched_pin(); 503 sf = sf_buf_alloc(m, SFB_CPUPRIVATE); 504 va = (caddr_t)sf_buf_kva(sf); 505 error = uiomove(va + offset, tlen, uio); 506 sf_buf_free(sf); 507 sched_unpin(); 508 VM_OBJECT_LOCK(tobj); 509out: 510 vm_page_lock_queues(); 511 vm_page_unwire(m, 0); 512 vm_page_activate(m); 513 vm_page_unlock_queues(); 514 vm_page_wakeup(m); 515 vm_object_pip_subtract(tobj, 1); 516 VM_OBJECT_UNLOCK(tobj); 517 518 return (error); 519} 520 521static int 522tmpfs_read(struct vop_read_args *v) 523{ 524 struct vnode *vp = v->a_vp; 525 struct uio *uio = v->a_uio; 526 527 struct tmpfs_node *node; 528 vm_object_t uobj; 529 size_t len; 530 int resid; 531 532 int error; 533 534 node = VP_TO_TMPFS_NODE(vp); 535 536 if (vp->v_type != VREG) { 537 error = EISDIR; 538 goto out; 539 } 540 541 if (uio->uio_offset < 0) { 542 error = EINVAL; 543 goto out; 544 } 545 546 node->tn_status |= TMPFS_NODE_ACCESSED; 547 548 uobj = node->tn_reg.tn_aobj; 549 while ((resid = uio->uio_resid) > 0) { 550 error = 0; 551 if (node->tn_size <= uio->uio_offset) 552 break; 553 len = MIN(node->tn_size - uio->uio_offset, resid); 554 if (len == 0) 555 break; 556 error = tmpfs_mappedread(vp->v_object, uobj, len, uio); 557 if ((error != 0) || (resid == uio->uio_resid)) 558 break; 559 } 560 561out: 562 563 return error; 564} 565 566/* --------------------------------------------------------------------- */ 567 568static int 569tmpfs_mappedwrite(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio) 570{ 571 vm_pindex_t idx; 572 vm_page_t vpg, tpg; 573 struct sf_buf *sf; 574 off_t offset, addr; 575 size_t tlen; 576 caddr_t va; 577 int error; 578 579 addr = uio->uio_offset; 580 idx = OFF_TO_IDX(addr); 581 offset = addr & PAGE_MASK; 582 tlen = MIN(PAGE_SIZE - offset, len); 583 584 if ((vobj == NULL) || (vobj->resident_page_count == 0)) { 585 vpg = NULL; 586 goto nocache; 587 } 588 589 VM_OBJECT_LOCK(vobj); 590lookupvpg: 591 if (((vpg = vm_page_lookup(vobj, idx)) != NULL) && 592 vm_page_is_valid(vpg, offset, tlen)) { 593 if (vm_page_sleep_if_busy(vpg, FALSE, "tmfsmw")) 594 goto lookupvpg; 595 vm_page_busy(vpg); 596 vm_page_lock_queues(); 597 vm_page_undirty(vpg); 598 vm_page_unlock_queues(); 599 VM_OBJECT_UNLOCK(vobj); 600 sched_pin(); 601 sf = sf_buf_alloc(vpg, SFB_CPUPRIVATE); 602 va = (caddr_t)sf_buf_kva(sf); 603 error = uiomove(va + offset, tlen, uio); 604 sf_buf_free(sf); 605 sched_unpin(); 606 } else { 607 VM_OBJECT_UNLOCK(vobj); 608 vpg = NULL; 609 } 610nocache: 611 VM_OBJECT_LOCK(tobj); 612 vm_object_pip_add(tobj, 1); 613 tpg = vm_page_grab(tobj, idx, VM_ALLOC_WIRED | 614 VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY); 615 if (tpg->valid != VM_PAGE_BITS_ALL) { 616 int behind, ahead; 617 if (vm_pager_has_page(tobj, idx, &behind, &ahead)) { 618 error = vm_pager_get_pages(tobj, &tpg, 1, 0); 619 if (error != 0) { 620 printf("tmpfs get pages from pager error [write]\n"); 621 goto out; 622 } 623 } else 624 vm_page_zero_invalid(tpg, TRUE); 625 } 626 VM_OBJECT_UNLOCK(tobj); 627 if (vpg == NULL) { 628 sched_pin(); 629 sf = sf_buf_alloc(tpg, SFB_CPUPRIVATE); 630 va = (caddr_t)sf_buf_kva(sf); 631 error = uiomove(va + offset, tlen, uio); 632 sf_buf_free(sf); 633 sched_unpin(); 634 } else { 635 KASSERT(vpg->valid == VM_PAGE_BITS_ALL, ("parts of vpg invalid")); 636 pmap_copy_page(vpg, tpg); 637 } 638 VM_OBJECT_LOCK(tobj); 639out: 640 if (vobj != NULL) 641 VM_OBJECT_LOCK(vobj); 642 vm_page_lock_queues(); 643 if (error == 0) { 644 vm_page_set_validclean(tpg, offset, tlen); 645 vm_page_zero_invalid(tpg, TRUE); 646 vm_page_dirty(tpg); 647 } 648 vm_page_unwire(tpg, 0); 649 vm_page_activate(tpg); 650 vm_page_unlock_queues(); 651 vm_page_wakeup(tpg); 652 if (vpg != NULL) 653 vm_page_wakeup(vpg); 654 if (vobj != NULL) 655 VM_OBJECT_UNLOCK(vobj); 656 vm_object_pip_subtract(tobj, 1); 657 VM_OBJECT_UNLOCK(tobj); 658 659 return (error); 660} 661 662static int 663tmpfs_write(struct vop_write_args *v) 664{ 665 struct vnode *vp = v->a_vp; 666 struct uio *uio = v->a_uio; 667 int ioflag = v->a_ioflag; 668 struct thread *td = uio->uio_td; 669 670 boolean_t extended; 671 int error = 0; 672 off_t oldsize; 673 struct tmpfs_node *node; 674 vm_object_t uobj; 675 size_t len; 676 int resid; 677 678 node = VP_TO_TMPFS_NODE(vp); 679 oldsize = node->tn_size; 680 681 if (uio->uio_offset < 0 || vp->v_type != VREG) { 682 error = EINVAL; 683 goto out; 684 } 685 686 if (uio->uio_resid == 0) { 687 error = 0; 688 goto out; 689 } 690 691 if (ioflag & IO_APPEND) 692 uio->uio_offset = node->tn_size; 693 694 if (uio->uio_offset + uio->uio_resid > 695 VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) 696 return (EFBIG); 697 698 if (vp->v_type == VREG && td != NULL) { 699 PROC_LOCK(td->td_proc); 700 if (uio->uio_offset + uio->uio_resid > 701 lim_cur(td->td_proc, RLIMIT_FSIZE)) { 702 psignal(td->td_proc, SIGXFSZ); 703 PROC_UNLOCK(td->td_proc); 704 return (EFBIG); 705 } 706 PROC_UNLOCK(td->td_proc); 707 } 708 709 extended = uio->uio_offset + uio->uio_resid > node->tn_size; 710 if (extended) { 711 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); 712 if (error != 0) 713 goto out; 714 } 715 716 uobj = node->tn_reg.tn_aobj; 717 while ((resid = uio->uio_resid) > 0) { 718 if (node->tn_size <= uio->uio_offset) 719 break; 720 len = MIN(node->tn_size - uio->uio_offset, resid); 721 if (len == 0) 722 break; 723 error = tmpfs_mappedwrite(vp->v_object, uobj, len, uio); 724 if ((error != 0) || (resid == uio->uio_resid)) 725 break; 726 } 727 728 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 729 (extended ? TMPFS_NODE_CHANGED : 0); 730 731 if (node->tn_mode & (S_ISUID | S_ISGID)) { 732 if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0)) 733 node->tn_mode &= ~(S_ISUID | S_ISGID); 734 } 735 736 if (error != 0) 737 (void)tmpfs_reg_resize(vp, oldsize); 738 739out: 740 MPASS(IMPLIES(error == 0, uio->uio_resid == 0)); 741 MPASS(IMPLIES(error != 0, oldsize == node->tn_size)); 742 743 return error; 744} 745 746/* --------------------------------------------------------------------- */ 747 748static int 749tmpfs_fsync(struct vop_fsync_args *v) 750{ 751 struct vnode *vp = v->a_vp; 752 753 MPASS(VOP_ISLOCKED(vp, v->a_td)); 754 755 tmpfs_update(vp); 756 757 return 0; 758} 759 760/* --------------------------------------------------------------------- */ 761 762static int 763tmpfs_remove(struct vop_remove_args *v) 764{ 765 struct vnode *dvp = v->a_dvp; 766 struct vnode *vp = v->a_vp; 767 768 int error; 769 struct tmpfs_dirent *de; 770 struct tmpfs_mount *tmp; 771 struct tmpfs_node *dnode; 772 struct tmpfs_node *node; 773 774 MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread)); 775 MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread)); 776 777 if (vp->v_type == VDIR) { 778 error = EISDIR; 779 goto out; 780 } 781 782 dnode = VP_TO_TMPFS_DIR(dvp); 783 node = VP_TO_TMPFS_NODE(vp); 784 tmp = VFS_TO_TMPFS(vp->v_mount); 785 de = tmpfs_dir_search(dnode, node); 786 MPASS(de != NULL); 787 788 /* Files marked as immutable or append-only cannot be deleted. */ 789 if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) || 790 (dnode->tn_flags & APPEND)) { 791 error = EPERM; 792 goto out; 793 } 794 795 /* Remove the entry from the directory; as it is a file, we do not 796 * have to change the number of hard links of the directory. */ 797 tmpfs_dir_detach(dvp, de); 798 799 /* Free the directory entry we just deleted. Note that the node 800 * referred by it will not be removed until the vnode is really 801 * reclaimed. */ 802 tmpfs_free_dirent(tmp, de, TRUE); 803 804 if (node->tn_links > 0) 805 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 806 TMPFS_NODE_MODIFIED; 807 error = 0; 808 809out: 810 811 return error; 812} 813 814/* --------------------------------------------------------------------- */ 815 816static int 817tmpfs_link(struct vop_link_args *v) 818{ 819 struct vnode *dvp = v->a_tdvp; 820 struct vnode *vp = v->a_vp; 821 struct componentname *cnp = v->a_cnp; 822 823 int error; 824 struct tmpfs_dirent *de; 825 struct tmpfs_node *node; 826 827 MPASS(VOP_ISLOCKED(dvp, cnp->cn_thread)); 828 MPASS(cnp->cn_flags & HASBUF); 829 MPASS(dvp != vp); /* XXX When can this be false? */ 830 831 node = VP_TO_TMPFS_NODE(vp); 832 833 /* XXX: Why aren't the following two tests done by the caller? */ 834 835 /* Hard links of directories are forbidden. */ 836 if (vp->v_type == VDIR) { 837 error = EPERM; 838 goto out; 839 } 840 841 /* Cannot create cross-device links. */ 842 if (dvp->v_mount != vp->v_mount) { 843 error = EXDEV; 844 goto out; 845 } 846 847 /* Ensure that we do not overflow the maximum number of links imposed 848 * by the system. */ 849 MPASS(node->tn_links <= LINK_MAX); 850 if (node->tn_links == LINK_MAX) { 851 error = EMLINK; 852 goto out; 853 } 854 855 /* We cannot create links of files marked immutable or append-only. */ 856 if (node->tn_flags & (IMMUTABLE | APPEND)) { 857 error = EPERM; 858 goto out; 859 } 860 861 /* Allocate a new directory entry to represent the node. */ 862 error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node, 863 cnp->cn_nameptr, cnp->cn_namelen, &de); 864 if (error != 0) 865 goto out; 866 867 /* Insert the new directory entry into the appropriate directory. */ 868 tmpfs_dir_attach(dvp, de); 869 870 /* vp link count has changed, so update node times. */ 871 node->tn_status |= TMPFS_NODE_CHANGED; 872 tmpfs_update(vp); 873 874 error = 0; 875 876out: 877 return error; 878} 879 880/* --------------------------------------------------------------------- */ 881 882static int 883tmpfs_rename(struct vop_rename_args *v) 884{ 885 struct vnode *fdvp = v->a_fdvp; 886 struct vnode *fvp = v->a_fvp; 887 struct componentname *fcnp = v->a_fcnp; 888 struct vnode *tdvp = v->a_tdvp; 889 struct vnode *tvp = v->a_tvp; 890 struct componentname *tcnp = v->a_tcnp; 891 892 char *newname; 893 int error; 894 struct tmpfs_dirent *de; 895 struct tmpfs_node *fdnode; 896 struct tmpfs_node *fnode; 897 struct tmpfs_node *tnode; 898 struct tmpfs_node *tdnode; 899 900 MPASS(VOP_ISLOCKED(tdvp, tcnp->cn_thread)); 901 MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp, tcnp->cn_thread))); 902 MPASS(fcnp->cn_flags & HASBUF); 903 MPASS(tcnp->cn_flags & HASBUF); 904 905 tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp); 906 907 /* Disallow cross-device renames. 908 * XXX Why isn't this done by the caller? */ 909 if (fvp->v_mount != tdvp->v_mount || 910 (tvp != NULL && fvp->v_mount != tvp->v_mount)) { 911 error = EXDEV; 912 goto out; 913 } 914 915 tdnode = VP_TO_TMPFS_DIR(tdvp); 916 917 /* If source and target are the same file, there is nothing to do. */ 918 if (fvp == tvp) { 919 error = 0; 920 goto out; 921 } 922 923 /* If we need to move the directory between entries, lock the 924 * source so that we can safely operate on it. */ 925 if (tdvp != fdvp) { 926 error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, tcnp->cn_thread); 927 if (error != 0) 928 goto out; 929 } 930 fdnode = VP_TO_TMPFS_DIR(fdvp); 931 fnode = VP_TO_TMPFS_NODE(fvp); 932 de = tmpfs_dir_search(fdnode, fnode); 933 934 /* Avoid manipulating '.' and '..' entries. */ 935 if (de == NULL) { 936 MPASS(fvp->v_type == VDIR); 937 error = EINVAL; 938 goto out_locked; 939 } 940 MPASS(de->td_node == fnode); 941 942 /* If re-naming a directory to another preexisting directory 943 * ensure that the target directory is empty so that its 944 * removal causes no side effects. 945 * Kern_rename gurantees the destination to be a directory 946 * if the source is one. */ 947 if (tvp != NULL) { 948 MPASS(tnode != NULL); 949 950 if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) || 951 (tdnode->tn_flags & (APPEND | IMMUTABLE))) { 952 error = EPERM; 953 goto out_locked; 954 } 955 956 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) { 957 if (tnode->tn_size > 0) { 958 error = ENOTEMPTY; 959 goto out_locked; 960 } 961 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) { 962 error = ENOTDIR; 963 goto out_locked; 964 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) { 965 error = EISDIR; 966 goto out_locked; 967 } else { 968 MPASS(fnode->tn_type != VDIR && 969 tnode->tn_type != VDIR); 970 } 971 } 972 973 if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) 974 || (fdnode->tn_flags & (APPEND | IMMUTABLE))) { 975 error = EPERM; 976 goto out_locked; 977 } 978 979 /* Ensure that we have enough memory to hold the new name, if it 980 * has to be changed. */ 981 if (fcnp->cn_namelen != tcnp->cn_namelen || 982 memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) { 983 newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK); 984 } else 985 newname = NULL; 986 987 /* If the node is being moved to another directory, we have to do 988 * the move. */ 989 if (fdnode != tdnode) { 990 /* In case we are moving a directory, we have to adjust its 991 * parent to point to the new parent. */ 992 if (de->td_node->tn_type == VDIR) { 993 struct tmpfs_node *n; 994 995 /* Ensure the target directory is not a child of the 996 * directory being moved. Otherwise, we'd end up 997 * with stale nodes. */ 998 n = tdnode; 999 while (n != n->tn_dir.tn_parent) { 1000 if (n == fnode) { 1001 error = EINVAL; 1002 if (newname != NULL) 1003 free(newname, M_TMPFSNAME); 1004 goto out_locked; 1005 } 1006 n = n->tn_dir.tn_parent; 1007 } 1008 1009 /* Adjust the parent pointer. */ 1010 TMPFS_VALIDATE_DIR(fnode); 1011 de->td_node->tn_dir.tn_parent = tdnode; 1012 1013 /* As a result of changing the target of the '..' 1014 * entry, the link count of the source and target 1015 * directories has to be adjusted. */ 1016 fdnode->tn_links--; 1017 tdnode->tn_links++; 1018 } 1019 1020 /* Do the move: just remove the entry from the source directory 1021 * and insert it into the target one. */ 1022 tmpfs_dir_detach(fdvp, de); 1023 tmpfs_dir_attach(tdvp, de); 1024 } 1025 1026 /* If the name has changed, we need to make it effective by changing 1027 * it in the directory entry. */ 1028 if (newname != NULL) { 1029 MPASS(tcnp->cn_namelen <= MAXNAMLEN); 1030 1031 free(de->td_name, M_TMPFSNAME); 1032 de->td_namelen = (uint16_t)tcnp->cn_namelen; 1033 memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen); 1034 de->td_name = newname; 1035 1036 fnode->tn_status |= TMPFS_NODE_CHANGED; 1037 tdnode->tn_status |= TMPFS_NODE_MODIFIED; 1038 } 1039 1040 /* If we are overwriting an entry, we have to remove the old one 1041 * from the target directory. */ 1042 if (tvp != NULL) { 1043 /* Remove the old entry from the target directory. */ 1044 de = tmpfs_dir_search(tdnode, tnode); 1045 tmpfs_dir_detach(tdvp, de); 1046 1047 /* Free the directory entry we just deleted. Note that the 1048 * node referred by it will not be removed until the vnode is 1049 * really reclaimed. */ 1050 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE); 1051 } 1052 1053 error = 0; 1054 1055out_locked: 1056 if (fdnode != tdnode) 1057 VOP_UNLOCK(fdvp, 0, tcnp->cn_thread); 1058 1059out: 1060 /* Release target nodes. */ 1061 /* XXX: I don't understand when tdvp can be the same as tvp, but 1062 * other code takes care of this... */ 1063 if (tdvp == tvp) 1064 vrele(tdvp); 1065 else 1066 vput(tdvp); 1067 if (tvp != NULL) 1068 vput(tvp); 1069 1070 /* Release source nodes. */ 1071 vrele(fdvp); 1072 vrele(fvp); 1073 1074 return error; 1075} 1076 1077/* --------------------------------------------------------------------- */ 1078 1079static int 1080tmpfs_mkdir(struct vop_mkdir_args *v) 1081{ 1082 struct vnode *dvp = v->a_dvp; 1083 struct vnode **vpp = v->a_vpp; 1084 struct componentname *cnp = v->a_cnp; 1085 struct vattr *vap = v->a_vap; 1086 1087 MPASS(vap->va_type == VDIR); 1088 1089 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 1090} 1091 1092/* --------------------------------------------------------------------- */ 1093 1094static int 1095tmpfs_rmdir(struct vop_rmdir_args *v) 1096{ 1097 struct vnode *dvp = v->a_dvp; 1098 struct vnode *vp = v->a_vp; 1099 1100 int error; 1101 struct tmpfs_dirent *de; 1102 struct tmpfs_mount *tmp; 1103 struct tmpfs_node *dnode; 1104 struct tmpfs_node *node; 1105 1106 MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread)); 1107 MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread)); 1108 1109 tmp = VFS_TO_TMPFS(dvp->v_mount); 1110 dnode = VP_TO_TMPFS_DIR(dvp); 1111 node = VP_TO_TMPFS_DIR(vp); 1112 1113 /* Directories with more than two entries ('.' and '..') cannot be 1114 * removed. */ 1115 if (node->tn_size > 0) { 1116 error = ENOTEMPTY; 1117 goto out; 1118 } 1119 1120 if ((dnode->tn_flags & APPEND) 1121 || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { 1122 error = EPERM; 1123 goto out; 1124 } 1125 1126 /* This invariant holds only if we are not trying to remove "..". 1127 * We checked for that above so this is safe now. */ 1128 MPASS(node->tn_dir.tn_parent == dnode); 1129 1130 /* Get the directory entry associated with node (vp). This was 1131 * filled by tmpfs_lookup while looking up the entry. */ 1132 de = tmpfs_dir_search(dnode, node); 1133 MPASS(TMPFS_DIRENT_MATCHES(de, 1134 v->a_cnp->cn_nameptr, 1135 v->a_cnp->cn_namelen)); 1136 1137 /* Check flags to see if we are allowed to remove the directory. */ 1138 if (dnode->tn_flags & APPEND 1139 || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { 1140 error = EPERM; 1141 goto out; 1142 } 1143 1144 /* Detach the directory entry from the directory (dnode). */ 1145 tmpfs_dir_detach(dvp, de); 1146 1147 node->tn_links--; 1148 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 1149 TMPFS_NODE_MODIFIED; 1150 node->tn_dir.tn_parent->tn_links--; 1151 node->tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \ 1152 TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; 1153 1154 cache_purge(dvp); 1155 cache_purge(vp); 1156 1157 /* Free the directory entry we just deleted. Note that the node 1158 * referred by it will not be removed until the vnode is really 1159 * reclaimed. */ 1160 tmpfs_free_dirent(tmp, de, TRUE); 1161 1162 /* Release the deleted vnode (will destroy the node, notify 1163 * interested parties and clean it from the cache). */ 1164 1165 dnode->tn_status |= TMPFS_NODE_CHANGED; 1166 tmpfs_update(dvp); 1167 1168 error = 0; 1169 1170out: 1171 return error; 1172} 1173 1174/* --------------------------------------------------------------------- */ 1175 1176static int 1177tmpfs_symlink(struct vop_symlink_args *v) 1178{ 1179 struct vnode *dvp = v->a_dvp; 1180 struct vnode **vpp = v->a_vpp; 1181 struct componentname *cnp = v->a_cnp; 1182 struct vattr *vap = v->a_vap; 1183 char *target = v->a_target; 1184 1185#ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */ 1186 MPASS(vap->va_type == VLNK); 1187#else 1188 vap->va_type = VLNK; 1189#endif 1190 1191 return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); 1192} 1193 1194/* --------------------------------------------------------------------- */ 1195 1196static int 1197tmpfs_readdir(struct vop_readdir_args *v) 1198{ 1199 struct vnode *vp = v->a_vp; 1200 struct uio *uio = v->a_uio; 1201 int *eofflag = v->a_eofflag; 1202 u_long **cookies = v->a_cookies; 1203 int *ncookies = v->a_ncookies; 1204 1205 int error; 1206 off_t startoff; 1207 off_t cnt = 0; 1208 struct tmpfs_node *node; 1209 1210 /* This operation only makes sense on directory nodes. */ 1211 if (vp->v_type != VDIR) 1212 return ENOTDIR; 1213 1214 node = VP_TO_TMPFS_DIR(vp); 1215 1216 startoff = uio->uio_offset; 1217 1218 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { 1219 error = tmpfs_dir_getdotdent(node, uio); 1220 if (error != 0) 1221 goto outok; 1222 cnt++; 1223 } 1224 1225 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { 1226 error = tmpfs_dir_getdotdotdent(node, uio); 1227 if (error != 0) 1228 goto outok; 1229 cnt++; 1230 } 1231 1232 error = tmpfs_dir_getdents(node, uio, &cnt); 1233 1234outok: 1235 MPASS(error >= -1); 1236 1237 if (error == -1) 1238 error = 0; 1239 1240 if (eofflag != NULL) 1241 *eofflag = 1242 (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); 1243 1244 /* Update NFS-related variables. */ 1245 if (error == 0 && cookies != NULL && ncookies != NULL) { 1246 off_t i; 1247 off_t off = startoff; 1248 struct tmpfs_dirent *de = NULL; 1249 1250 *ncookies = cnt; 1251 *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); 1252 1253 for (i = 0; i < cnt; i++) { 1254 MPASS(off != TMPFS_DIRCOOKIE_EOF); 1255 if (off == TMPFS_DIRCOOKIE_DOT) { 1256 off = TMPFS_DIRCOOKIE_DOTDOT; 1257 } else { 1258 if (off == TMPFS_DIRCOOKIE_DOTDOT) { 1259 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); 1260 } else if (de != NULL) { 1261 de = TAILQ_NEXT(de, td_entries); 1262 } else { 1263 de = tmpfs_dir_lookupbycookie(node, 1264 off); 1265 MPASS(de != NULL); 1266 de = TAILQ_NEXT(de, td_entries); 1267 } 1268 if (de == NULL) 1269 off = TMPFS_DIRCOOKIE_EOF; 1270 else 1271 off = tmpfs_dircookie(de); 1272 } 1273 1274 (*cookies)[i] = off; 1275 } 1276 MPASS(uio->uio_offset == off); 1277 } 1278 1279 return error; 1280} 1281 1282/* --------------------------------------------------------------------- */ 1283 1284static int 1285tmpfs_readlink(struct vop_readlink_args *v) 1286{ 1287 struct vnode *vp = v->a_vp; 1288 struct uio *uio = v->a_uio; 1289 1290 int error; 1291 struct tmpfs_node *node; 1292 1293 MPASS(uio->uio_offset == 0); 1294 MPASS(vp->v_type == VLNK); 1295 1296 node = VP_TO_TMPFS_NODE(vp); 1297 1298 error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid), 1299 uio); 1300 node->tn_status |= TMPFS_NODE_ACCESSED; 1301 1302 return error; 1303} 1304 1305/* --------------------------------------------------------------------- */ 1306 1307static int 1308tmpfs_inactive(struct vop_inactive_args *v) 1309{ 1310 struct vnode *vp = v->a_vp; 1311 struct thread *l = v->a_td; 1312 1313 struct tmpfs_node *node; 1314 1315 MPASS(VOP_ISLOCKED(vp, l)); 1316 1317 node = VP_TO_TMPFS_NODE(vp); 1318 1319 if (node->tn_links == 0) 1320 vrecycle(vp, l); 1321 1322 return 0; 1323} 1324 1325/* --------------------------------------------------------------------- */ 1326 1327int 1328tmpfs_reclaim(struct vop_reclaim_args *v) 1329{ 1330 struct vnode *vp = v->a_vp; 1331 1332 struct tmpfs_mount *tmp; 1333 struct tmpfs_node *node; 1334 1335 node = VP_TO_TMPFS_NODE(vp); 1336 tmp = VFS_TO_TMPFS(vp->v_mount); 1337 1338 vnode_destroy_vobject(vp); 1339 cache_purge(vp); 1340 tmpfs_free_vp(vp); 1341 1342 /* If the node referenced by this vnode was deleted by the user, 1343 * we must free its associated data structures (now that the vnode 1344 * is being reclaimed). */ 1345 if (node->tn_links == 0) 1346 tmpfs_free_node(tmp, node); 1347 1348 MPASS(vp->v_data == NULL); 1349 return 0; 1350} 1351 1352/* --------------------------------------------------------------------- */ 1353 1354static int 1355tmpfs_print(struct vop_print_args *v) 1356{ 1357 struct vnode *vp = v->a_vp; 1358 1359 struct tmpfs_node *node; 1360 1361 node = VP_TO_TMPFS_NODE(vp); 1362 1363 printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n", 1364 node, node->tn_flags, node->tn_links); 1365 printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX 1366 ", status 0x%x\n", 1367 node->tn_mode, node->tn_uid, node->tn_gid, 1368 (uintmax_t)node->tn_size, node->tn_status); 1369 1370 if (vp->v_type == VFIFO) 1371 fifo_printinfo(vp); 1372 1373 printf("\n"); 1374 1375 return 0; 1376} 1377 1378/* --------------------------------------------------------------------- */ 1379 1380static int 1381tmpfs_pathconf(struct vop_pathconf_args *v) 1382{ 1383 int name = v->a_name; 1384 register_t *retval = v->a_retval; 1385 1386 int error; 1387 1388 error = 0; 1389 1390 switch (name) { 1391 case _PC_LINK_MAX: 1392 *retval = LINK_MAX; 1393 break; 1394 1395 case _PC_NAME_MAX: 1396 *retval = NAME_MAX; 1397 break; 1398 1399 case _PC_PATH_MAX: 1400 *retval = PATH_MAX; 1401 break; 1402 1403 case _PC_PIPE_BUF: 1404 *retval = PIPE_BUF; 1405 break; 1406 1407 case _PC_CHOWN_RESTRICTED: 1408 *retval = 1; 1409 break; 1410 1411 case _PC_NO_TRUNC: 1412 *retval = 1; 1413 break; 1414 1415 case _PC_SYNC_IO: 1416 *retval = 1; 1417 break; 1418 1419 case _PC_FILESIZEBITS: 1420 *retval = 0; /* XXX Don't know which value should I return. */ 1421 break; 1422 1423 default: 1424 error = EINVAL; 1425 } 1426 1427 return error; 1428} 1429 1430/* --------------------------------------------------------------------- */ 1431 1432static int 1433tmpfs_advlock(struct vop_advlock_args *v) 1434{ 1435 struct vnode *vp = v->a_vp; 1436 1437 struct tmpfs_node *node; 1438 1439 node = VP_TO_TMPFS_NODE(vp); 1440 1441 return lf_advlock(v, &node->tn_lockf, node->tn_size); 1442} 1443 1444/* --------------------------------------------------------------------- */ 1445 1446static int 1447tmpfs_vptofh(struct vop_vptofh_args *ap) 1448{ 1449 struct tmpfs_fid *tfhp; 1450 struct tmpfs_node *node; 1451 1452 tfhp = (struct tmpfs_fid *)ap->a_fhp; 1453 node = VP_TO_TMPFS_NODE(ap->a_vp); 1454 1455 tfhp->tf_len = sizeof(struct tmpfs_fid); 1456 tfhp->tf_id = node->tn_id; 1457 tfhp->tf_gen = node->tn_gen; 1458 1459 return (0); 1460} 1461 1462/* --------------------------------------------------------------------- */ 1463 1464/* 1465 * vnode operations vector used for files stored in a tmpfs file system. 1466 */ 1467struct vop_vector tmpfs_vnodeop_entries = { 1468 .vop_default = &default_vnodeops, 1469 .vop_lookup = vfs_cache_lookup, 1470 .vop_cachedlookup = tmpfs_lookup, 1471 .vop_create = tmpfs_create, 1472 .vop_mknod = tmpfs_mknod, 1473 .vop_open = tmpfs_open, 1474 .vop_close = tmpfs_close, 1475 .vop_access = tmpfs_access, 1476 .vop_getattr = tmpfs_getattr, 1477 .vop_setattr = tmpfs_setattr, 1478 .vop_read = tmpfs_read, 1479 .vop_write = tmpfs_write, 1480 .vop_fsync = tmpfs_fsync, 1481 .vop_remove = tmpfs_remove, 1482 .vop_link = tmpfs_link, 1483 .vop_rename = tmpfs_rename, 1484 .vop_mkdir = tmpfs_mkdir, 1485 .vop_rmdir = tmpfs_rmdir, 1486 .vop_symlink = tmpfs_symlink, 1487 .vop_readdir = tmpfs_readdir, 1488 .vop_readlink = tmpfs_readlink, 1489 .vop_inactive = tmpfs_inactive, 1490 .vop_reclaim = tmpfs_reclaim, 1491 .vop_print = tmpfs_print, 1492 .vop_pathconf = tmpfs_pathconf, 1493 .vop_advlock = tmpfs_advlock, 1494 .vop_vptofh = tmpfs_vptofh, 1495 .vop_bmap = VOP_EOPNOTSUPP, 1496}; 1497 1498