1 /* 2 * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry. 3 * Copyright (c) 1992, 1993, 1994, 1995 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Jan-Simon Pendry. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)union_vnops.c 8.26 (Berkeley) 05/11/95 12 */ 13 14 #include <sys/param.h> 15 #include <sys/systm.h> 16 #include <sys/proc.h> 17 #include <sys/file.h> 18 #include <sys/time.h> 19 #include <sys/stat.h> 20 #include <sys/types.h> 21 #include <sys/vnode.h> 22 #include <sys/mount.h> 23 #include <sys/namei.h> 24 #include <sys/malloc.h> 25 #include <sys/buf.h> 26 #include <sys/queue.h> 27 #include <miscfs/union/union.h> 28 29 #define FIXUP(un) { \ 30 if (((un)->un_flags & UN_ULOCK) == 0) { \ 31 union_fixup(un); \ 32 } \ 33 } 34 35 static void 36 union_fixup(un) 37 struct union_node *un; 38 { 39 40 VOP_LOCK(un->un_uppervp); 41 un->un_flags |= UN_ULOCK; 42 } 43 44 static int 45 union_lookup1(udvp, dvpp, vpp, cnp) 46 struct vnode *udvp; 47 struct vnode **dvpp; 48 struct vnode **vpp; 49 struct componentname *cnp; 50 { 51 int error; 52 struct vnode *tdvp; 53 struct vnode *dvp; 54 struct mount *mp; 55 56 dvp = *dvpp; 57 58 /* 59 * If stepping up the directory tree, check for going 60 * back across the mount point, in which case do what 61 * lookup would do by stepping back down the mount 62 * hierarchy. 63 */ 64 if (cnp->cn_flags & ISDOTDOT) { 65 while ((dvp != udvp) && (dvp->v_flag & VROOT)) { 66 /* 67 * Don't do the NOCROSSMOUNT check 68 * at this level. By definition, 69 * union fs deals with namespaces, not 70 * filesystems. 71 */ 72 tdvp = dvp; 73 *dvpp = dvp = dvp->v_mount->mnt_vnodecovered; 74 vput(tdvp); 75 VREF(dvp); 76 VOP_LOCK(dvp); 77 } 78 } 79 80 error = VOP_LOOKUP(dvp, &tdvp, cnp); 81 if (error) 82 return (error); 83 84 /* 85 * The parent directory will have been unlocked, unless lookup 86 * found the last component. In which case, re-lock the node 87 * here to allow it to be unlocked again (phew) in union_lookup. 88 */ 89 if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN)) 90 VOP_LOCK(dvp); 91 92 dvp = tdvp; 93 94 /* 95 * Lastly check if the current node is a mount point in 96 * which case walk up the mount hierarchy making sure not to 97 * bump into the root of the mount tree (ie. dvp != udvp). 98 */ 99 while (dvp != udvp && (dvp->v_type == VDIR) && 100 (mp = dvp->v_mountedhere)) { 101 102 if (mp->mnt_flag & MNT_MLOCK) { 103 mp->mnt_flag |= MNT_MWAIT; 104 sleep((caddr_t) mp, PVFS); 105 continue; 106 } 107 108 if (error = VFS_ROOT(mp, &tdvp)) { 109 vput(dvp); 110 return (error); 111 } 112 113 vput(dvp); 114 dvp = tdvp; 115 } 116 117 *vpp = dvp; 118 return (0); 119 } 120 121 int 122 union_lookup(ap) 123 struct vop_lookup_args /* { 124 struct vnodeop_desc *a_desc; 125 struct vnode *a_dvp; 126 struct vnode **a_vpp; 127 struct componentname *a_cnp; 128 } */ *ap; 129 { 130 int error; 131 int uerror, lerror; 132 struct vnode *uppervp, *lowervp; 133 struct vnode *upperdvp, *lowerdvp; 134 struct vnode *dvp = ap->a_dvp; 135 struct union_node *dun = VTOUNION(dvp); 136 struct componentname *cnp = ap->a_cnp; 137 int lockparent = cnp->cn_flags & LOCKPARENT; 138 int rdonly = cnp->cn_flags & RDONLY; 139 struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); 140 struct ucred *saved_cred; 141 int iswhiteout; 142 struct vattr va; 143 144 #ifdef notyet 145 if (cnp->cn_namelen == 3 && 146 cnp->cn_nameptr[2] == '.' && 147 cnp->cn_nameptr[1] == '.' && 148 cnp->cn_nameptr[0] == '.') { 149 dvp = *ap->a_vpp = LOWERVP(ap->a_dvp); 150 if (dvp == NULLVP) 151 return (ENOENT); 152 VREF(dvp); 153 VOP_LOCK(dvp); 154 if (!lockparent || !(cnp->cn_flags & ISLASTCN)) 155 VOP_UNLOCK(ap->a_dvp); 156 return (0); 157 } 158 #endif 159 160 cnp->cn_flags |= LOCKPARENT; 161 162 upperdvp = dun->un_uppervp; 163 lowerdvp = dun->un_lowervp; 164 uppervp = NULLVP; 165 lowervp = NULLVP; 166 iswhiteout = 0; 167 168 /* 169 * do the lookup in the upper level. 170 * if that level comsumes additional pathnames, 171 * then assume that something special is going 172 * on and just return that vnode. 173 */ 174 if (upperdvp != NULLVP) { 175 FIXUP(dun); 176 uerror = union_lookup1(um->um_uppervp, &upperdvp, 177 &uppervp, cnp); 178 /*if (uppervp == upperdvp) 179 dun->un_flags |= UN_KLOCK;*/ 180 181 if (cnp->cn_consume != 0) { 182 *ap->a_vpp = uppervp; 183 if (!lockparent) 184 cnp->cn_flags &= ~LOCKPARENT; 185 return (uerror); 186 } 187 if (uerror == ENOENT || uerror == EJUSTRETURN) { 188 if (cnp->cn_flags & ISWHITEOUT) { 189 iswhiteout = 1; 190 } else if (lowerdvp != NULLVP) { 191 lerror = VOP_GETATTR(upperdvp, &va, 192 cnp->cn_cred, cnp->cn_proc); 193 if (lerror == 0 && (va.va_flags & OPAQUE)) 194 iswhiteout = 1; 195 } 196 } 197 } else { 198 uerror = ENOENT; 199 } 200 201 /* 202 * in a similar way to the upper layer, do the lookup 203 * in the lower layer. this time, if there is some 204 * component magic going on, then vput whatever we got 205 * back from the upper layer and return the lower vnode 206 * instead. 207 */ 208 if (lowerdvp != NULLVP && !iswhiteout) { 209 int nameiop; 210 211 VOP_LOCK(lowerdvp); 212 213 /* 214 * Only do a LOOKUP on the bottom node, since 215 * we won't be making changes to it anyway. 216 */ 217 nameiop = cnp->cn_nameiop; 218 cnp->cn_nameiop = LOOKUP; 219 if (um->um_op == UNMNT_BELOW) { 220 saved_cred = cnp->cn_cred; 221 cnp->cn_cred = um->um_cred; 222 } 223 lerror = union_lookup1(um->um_lowervp, &lowerdvp, 224 &lowervp, cnp); 225 if (um->um_op == UNMNT_BELOW) 226 cnp->cn_cred = saved_cred; 227 cnp->cn_nameiop = nameiop; 228 229 if (lowervp != lowerdvp) 230 VOP_UNLOCK(lowerdvp); 231 232 if (cnp->cn_consume != 0) { 233 if (uppervp != NULLVP) { 234 if (uppervp == upperdvp) 235 vrele(uppervp); 236 else 237 vput(uppervp); 238 uppervp = NULLVP; 239 } 240 *ap->a_vpp = lowervp; 241 if (!lockparent) 242 cnp->cn_flags &= ~LOCKPARENT; 243 return (lerror); 244 } 245 } else { 246 lerror = ENOENT; 247 if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) { 248 lowervp = LOWERVP(dun->un_pvp); 249 if (lowervp != NULLVP) { 250 VREF(lowervp); 251 VOP_LOCK(lowervp); 252 lerror = 0; 253 } 254 } 255 } 256 257 if (!lockparent) 258 cnp->cn_flags &= ~LOCKPARENT; 259 260 /* 261 * at this point, we have uerror and lerror indicating 262 * possible errors with the lookups in the upper and lower 263 * layers. additionally, uppervp and lowervp are (locked) 264 * references to existing vnodes in the upper and lower layers. 265 * 266 * there are now three cases to consider. 267 * 1. if both layers returned an error, then return whatever 268 * error the upper layer generated. 269 * 270 * 2. if the top layer failed and the bottom layer succeeded 271 * then two subcases occur. 272 * a. the bottom vnode is not a directory, in which 273 * case just return a new union vnode referencing 274 * an empty top layer and the existing bottom layer. 275 * b. the bottom vnode is a directory, in which case 276 * create a new directory in the top-level and 277 * continue as in case 3. 278 * 279 * 3. if the top layer succeeded then return a new union 280 * vnode referencing whatever the new top layer and 281 * whatever the bottom layer returned. 282 */ 283 284 *ap->a_vpp = NULLVP; 285 286 /* case 1. */ 287 if ((uerror != 0) && (lerror != 0)) { 288 return (uerror); 289 } 290 291 /* case 2. */ 292 if (uerror != 0 /* && (lerror == 0) */ ) { 293 if (lowervp->v_type == VDIR) { /* case 2b. */ 294 dun->un_flags &= ~UN_ULOCK; 295 VOP_UNLOCK(upperdvp); 296 uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); 297 VOP_LOCK(upperdvp); 298 dun->un_flags |= UN_ULOCK; 299 300 if (uerror) { 301 if (lowervp != NULLVP) { 302 vput(lowervp); 303 lowervp = NULLVP; 304 } 305 return (uerror); 306 } 307 } 308 } 309 310 if (lowervp != NULLVP) 311 VOP_UNLOCK(lowervp); 312 313 error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, 314 uppervp, lowervp, 1); 315 316 if (error) { 317 if (uppervp != NULLVP) 318 vput(uppervp); 319 if (lowervp != NULLVP) 320 vrele(lowervp); 321 } else { 322 if (*ap->a_vpp != dvp) 323 if (!lockparent || !(cnp->cn_flags & ISLASTCN)) 324 VOP_UNLOCK(dvp); 325 } 326 327 return (error); 328 } 329 330 int 331 union_create(ap) 332 struct vop_create_args /* { 333 struct vnode *a_dvp; 334 struct vnode **a_vpp; 335 struct componentname *a_cnp; 336 struct vattr *a_vap; 337 } */ *ap; 338 { 339 struct union_node *un = VTOUNION(ap->a_dvp); 340 struct vnode *dvp = un->un_uppervp; 341 342 if (dvp != NULLVP) { 343 int error; 344 struct vnode *vp; 345 struct mount *mp; 346 347 FIXUP(un); 348 349 VREF(dvp); 350 un->un_flags |= UN_KLOCK; 351 mp = ap->a_dvp->v_mount; 352 vput(ap->a_dvp); 353 error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap); 354 if (error) 355 return (error); 356 357 error = union_allocvp( 358 ap->a_vpp, 359 mp, 360 NULLVP, 361 NULLVP, 362 ap->a_cnp, 363 vp, 364 NULLVP, 365 1); 366 if (error) 367 vput(vp); 368 return (error); 369 } 370 371 vput(ap->a_dvp); 372 return (EROFS); 373 } 374 375 int 376 union_whiteout(ap) 377 struct vop_whiteout_args /* { 378 struct vnode *a_dvp; 379 struct componentname *a_cnp; 380 int a_flags; 381 } */ *ap; 382 { 383 struct union_node *un = VTOUNION(ap->a_dvp); 384 385 if (un->un_uppervp == NULLVP) 386 return (EOPNOTSUPP); 387 388 FIXUP(un); 389 return (VOP_WHITEOUT(un->un_uppervp, ap->a_cnp, ap->a_flags)); 390 } 391 392 int 393 union_mknod(ap) 394 struct vop_mknod_args /* { 395 struct vnode *a_dvp; 396 struct vnode **a_vpp; 397 struct componentname *a_cnp; 398 struct vattr *a_vap; 399 } */ *ap; 400 { 401 struct union_node *un = VTOUNION(ap->a_dvp); 402 struct vnode *dvp = un->un_uppervp; 403 404 if (dvp != NULLVP) { 405 int error; 406 struct vnode *vp; 407 struct mount *mp; 408 409 FIXUP(un); 410 411 VREF(dvp); 412 un->un_flags |= UN_KLOCK; 413 mp = ap->a_dvp->v_mount; 414 vput(ap->a_dvp); 415 error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap); 416 if (error) 417 return (error); 418 419 if (vp != NULLVP) { 420 error = union_allocvp( 421 ap->a_vpp, 422 mp, 423 NULLVP, 424 NULLVP, 425 ap->a_cnp, 426 vp, 427 NULLVP, 428 1); 429 if (error) 430 vput(vp); 431 } 432 return (error); 433 } 434 435 vput(ap->a_dvp); 436 return (EROFS); 437 } 438 439 int 440 union_open(ap) 441 struct vop_open_args /* { 442 struct vnodeop_desc *a_desc; 443 struct vnode *a_vp; 444 int a_mode; 445 struct ucred *a_cred; 446 struct proc *a_p; 447 } */ *ap; 448 { 449 struct union_node *un = VTOUNION(ap->a_vp); 450 struct vnode *tvp; 451 int mode = ap->a_mode; 452 struct ucred *cred = ap->a_cred; 453 struct proc *p = ap->a_p; 454 int error; 455 456 /* 457 * If there is an existing upper vp then simply open that. 458 */ 459 tvp = un->un_uppervp; 460 if (tvp == NULLVP) { 461 /* 462 * If the lower vnode is being opened for writing, then 463 * copy the file contents to the upper vnode and open that, 464 * otherwise can simply open the lower vnode. 465 */ 466 tvp = un->un_lowervp; 467 if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) { 468 error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p); 469 if (error == 0) 470 error = VOP_OPEN(un->un_uppervp, mode, cred, p); 471 return (error); 472 } 473 474 /* 475 * Just open the lower vnode 476 */ 477 un->un_openl++; 478 VOP_LOCK(tvp); 479 error = VOP_OPEN(tvp, mode, cred, p); 480 VOP_UNLOCK(tvp); 481 482 return (error); 483 } 484 485 FIXUP(un); 486 487 error = VOP_OPEN(tvp, mode, cred, p); 488 489 return (error); 490 } 491 492 int 493 union_close(ap) 494 struct vop_close_args /* { 495 struct vnode *a_vp; 496 int a_fflag; 497 struct ucred *a_cred; 498 struct proc *a_p; 499 } */ *ap; 500 { 501 struct union_node *un = VTOUNION(ap->a_vp); 502 struct vnode *vp; 503 504 if (un->un_uppervp != NULLVP) { 505 vp = un->un_uppervp; 506 } else { 507 #ifdef UNION_DIAGNOSTIC 508 if (un->un_openl <= 0) 509 panic("union: un_openl cnt"); 510 #endif 511 --un->un_openl; 512 vp = un->un_lowervp; 513 } 514 515 return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p)); 516 } 517 518 /* 519 * Check access permission on the union vnode. 520 * The access check being enforced is to check 521 * against both the underlying vnode, and any 522 * copied vnode. This ensures that no additional 523 * file permissions are given away simply because 524 * the user caused an implicit file copy. 525 */ 526 int 527 union_access(ap) 528 struct vop_access_args /* { 529 struct vnodeop_desc *a_desc; 530 struct vnode *a_vp; 531 int a_mode; 532 struct ucred *a_cred; 533 struct proc *a_p; 534 } */ *ap; 535 { 536 struct union_node *un = VTOUNION(ap->a_vp); 537 int error = EACCES; 538 struct vnode *vp; 539 540 if ((vp = un->un_uppervp) != NULLVP) { 541 FIXUP(un); 542 return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p)); 543 } 544 545 if ((vp = un->un_lowervp) != NULLVP) { 546 VOP_LOCK(vp); 547 error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p); 548 if (error == 0) { 549 struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount); 550 551 if (um->um_op == UNMNT_BELOW) 552 error = VOP_ACCESS(vp, ap->a_mode, 553 um->um_cred, ap->a_p); 554 } 555 VOP_UNLOCK(vp); 556 if (error) 557 return (error); 558 } 559 560 return (error); 561 } 562 563 /* 564 * We handle getattr only to change the fsid and 565 * track object sizes 566 */ 567 int 568 union_getattr(ap) 569 struct vop_getattr_args /* { 570 struct vnode *a_vp; 571 struct vattr *a_vap; 572 struct ucred *a_cred; 573 struct proc *a_p; 574 } */ *ap; 575 { 576 int error; 577 struct union_node *un = VTOUNION(ap->a_vp); 578 struct vnode *vp = un->un_uppervp; 579 struct vattr *vap; 580 struct vattr va; 581 582 583 /* 584 * Some programs walk the filesystem hierarchy by counting 585 * links to directories to avoid stat'ing all the time. 586 * This means the link count on directories needs to be "correct". 587 * The only way to do that is to call getattr on both layers 588 * and fix up the link count. The link count will not necessarily 589 * be accurate but will be large enough to defeat the tree walkers. 590 */ 591 592 vap = ap->a_vap; 593 594 vp = un->un_uppervp; 595 if (vp != NULLVP) { 596 /* 597 * It's not clear whether VOP_GETATTR is to be 598 * called with the vnode locked or not. stat() calls 599 * it with (vp) locked, and fstat calls it with 600 * (vp) unlocked. 601 * In the mean time, compensate here by checking 602 * the union_node's lock flag. 603 */ 604 if (un->un_flags & UN_LOCKED) 605 FIXUP(un); 606 607 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); 608 if (error) 609 return (error); 610 union_newsize(ap->a_vp, vap->va_size, VNOVAL); 611 } 612 613 if (vp == NULLVP) { 614 vp = un->un_lowervp; 615 } else if (vp->v_type == VDIR) { 616 vp = un->un_lowervp; 617 vap = &va; 618 } else { 619 vp = NULLVP; 620 } 621 622 if (vp != NULLVP) { 623 error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); 624 if (error) 625 return (error); 626 union_newsize(ap->a_vp, VNOVAL, vap->va_size); 627 } 628 629 if ((vap != ap->a_vap) && (vap->va_type == VDIR)) 630 ap->a_vap->va_nlink += vap->va_nlink; 631 632 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; 633 return (0); 634 } 635 636 int 637 union_setattr(ap) 638 struct vop_setattr_args /* { 639 struct vnode *a_vp; 640 struct vattr *a_vap; 641 struct ucred *a_cred; 642 struct proc *a_p; 643 } */ *ap; 644 { 645 struct union_node *un = VTOUNION(ap->a_vp); 646 int error; 647 648 /* 649 * Handle case of truncating lower object to zero size, 650 * by creating a zero length upper object. This is to 651 * handle the case of open with O_TRUNC and O_CREAT. 652 */ 653 if ((un->un_uppervp == NULLVP) && 654 /* assert(un->un_lowervp != NULLVP) */ 655 (un->un_lowervp->v_type == VREG)) { 656 error = union_copyup(un, (ap->a_vap->va_size != 0), 657 ap->a_cred, ap->a_p); 658 if (error) 659 return (error); 660 } 661 662 /* 663 * Try to set attributes in upper layer, 664 * otherwise return read-only filesystem error. 665 */ 666 if (un->un_uppervp != NULLVP) { 667 FIXUP(un); 668 error = VOP_SETATTR(un->un_uppervp, ap->a_vap, 669 ap->a_cred, ap->a_p); 670 if ((error == 0) && (ap->a_vap->va_size != VNOVAL)) 671 union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL); 672 } else { 673 error = EROFS; 674 } 675 676 return (error); 677 } 678 679 int 680 union_read(ap) 681 struct vop_read_args /* { 682 struct vnode *a_vp; 683 struct uio *a_uio; 684 int a_ioflag; 685 struct ucred *a_cred; 686 } */ *ap; 687 { 688 int error; 689 struct vnode *vp = OTHERVP(ap->a_vp); 690 int dolock = (vp == LOWERVP(ap->a_vp)); 691 692 if (dolock) 693 VOP_LOCK(vp); 694 else 695 FIXUP(VTOUNION(ap->a_vp)); 696 error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); 697 if (dolock) 698 VOP_UNLOCK(vp); 699 700 /* 701 * XXX 702 * perhaps the size of the underlying object has changed under 703 * our feet. take advantage of the offset information present 704 * in the uio structure. 705 */ 706 if (error == 0) { 707 struct union_node *un = VTOUNION(ap->a_vp); 708 off_t cur = ap->a_uio->uio_offset; 709 710 if (vp == un->un_uppervp) { 711 if (cur > un->un_uppersz) 712 union_newsize(ap->a_vp, cur, VNOVAL); 713 } else { 714 if (cur > un->un_lowersz) 715 union_newsize(ap->a_vp, VNOVAL, cur); 716 } 717 } 718 719 return (error); 720 } 721 722 int 723 union_write(ap) 724 struct vop_read_args /* { 725 struct vnode *a_vp; 726 struct uio *a_uio; 727 int a_ioflag; 728 struct ucred *a_cred; 729 } */ *ap; 730 { 731 int error; 732 struct vnode *vp; 733 struct union_node *un = VTOUNION(ap->a_vp); 734 735 vp = UPPERVP(ap->a_vp); 736 if (vp == NULLVP) 737 panic("union: missing upper layer in write"); 738 739 FIXUP(un); 740 error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); 741 742 /* 743 * the size of the underlying object may be changed by the 744 * write. 745 */ 746 if (error == 0) { 747 off_t cur = ap->a_uio->uio_offset; 748 749 if (cur > un->un_uppersz) 750 union_newsize(ap->a_vp, cur, VNOVAL); 751 } 752 753 return (error); 754 } 755 756 union_lease(ap) 757 struct vop_lease_args /* { 758 struct vnode *a_vp; 759 struct proc *a_p; 760 struct ucred *a_cred; 761 int a_flag; 762 } */ *ap; 763 { 764 765 return (VOP_LEASE(OTHERVP(ap->a_vp), ap->a_p, ap->a_cred, ap->a_flag)); 766 } 767 768 int 769 union_ioctl(ap) 770 struct vop_ioctl_args /* { 771 struct vnode *a_vp; 772 int a_command; 773 caddr_t a_data; 774 int a_fflag; 775 struct ucred *a_cred; 776 struct proc *a_p; 777 } */ *ap; 778 { 779 780 return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data, 781 ap->a_fflag, ap->a_cred, ap->a_p)); 782 } 783 784 int 785 union_select(ap) 786 struct vop_select_args /* { 787 struct vnode *a_vp; 788 int a_which; 789 int a_fflags; 790 struct ucred *a_cred; 791 struct proc *a_p; 792 } */ *ap; 793 { 794 795 return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags, 796 ap->a_cred, ap->a_p)); 797 } 798 799 int 800 union_revoke(ap) 801 struct vop_revoke_args /* { 802 struct vnode *a_vp; 803 int a_flags; 804 } */ *ap; 805 { 806 struct vnode *vp = ap->a_vp; 807 808 if (UPPERVP(vp)) 809 VOP_REVOKE(UPPERVP(vp), ap->a_flags); 810 if (LOWERVP(vp)) 811 VOP_REVOKE(UPPERVP(vp), ap->a_flags); 812 vgone(vp); 813 } 814 815 int 816 union_mmap(ap) 817 struct vop_mmap_args /* { 818 struct vnode *a_vp; 819 int a_fflags; 820 struct ucred *a_cred; 821 struct proc *a_p; 822 } */ *ap; 823 { 824 825 return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags, 826 ap->a_cred, ap->a_p)); 827 } 828 829 int 830 union_fsync(ap) 831 struct vop_fsync_args /* { 832 struct vnode *a_vp; 833 struct ucred *a_cred; 834 int a_waitfor; 835 struct proc *a_p; 836 } */ *ap; 837 { 838 int error = 0; 839 struct vnode *targetvp = OTHERVP(ap->a_vp); 840 841 if (targetvp != NULLVP) { 842 int dolock = (targetvp == LOWERVP(ap->a_vp)); 843 844 if (dolock) 845 VOP_LOCK(targetvp); 846 else 847 FIXUP(VTOUNION(ap->a_vp)); 848 error = VOP_FSYNC(targetvp, ap->a_cred, 849 ap->a_waitfor, ap->a_p); 850 if (dolock) 851 VOP_UNLOCK(targetvp); 852 } 853 854 return (error); 855 } 856 857 int 858 union_seek(ap) 859 struct vop_seek_args /* { 860 struct vnode *a_vp; 861 off_t a_oldoff; 862 off_t a_newoff; 863 struct ucred *a_cred; 864 } */ *ap; 865 { 866 867 return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred)); 868 } 869 870 int 871 union_remove(ap) 872 struct vop_remove_args /* { 873 struct vnode *a_dvp; 874 struct vnode *a_vp; 875 struct componentname *a_cnp; 876 } */ *ap; 877 { 878 int error; 879 struct union_node *dun = VTOUNION(ap->a_dvp); 880 struct union_node *un = VTOUNION(ap->a_vp); 881 882 if (dun->un_uppervp == NULLVP) 883 panic("union remove: null upper vnode"); 884 885 if (un->un_uppervp != NULLVP) { 886 struct vnode *dvp = dun->un_uppervp; 887 struct vnode *vp = un->un_uppervp; 888 struct componentname *cnp = ap->a_cnp; 889 890 FIXUP(dun); 891 VREF(dvp); 892 dun->un_flags |= UN_KLOCK; 893 vput(ap->a_dvp); 894 FIXUP(un); 895 VREF(vp); 896 un->un_flags |= UN_KLOCK; 897 vput(ap->a_vp); 898 899 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) 900 cnp->cn_flags |= DOWHITEOUT; 901 error = VOP_REMOVE(dvp, vp, cnp); 902 if (!error) 903 union_removed_upper(un); 904 } else { 905 FIXUP(dun); 906 error = union_mkwhiteout( 907 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 908 dun->un_uppervp, ap->a_cnp, un->un_path); 909 vput(ap->a_dvp); 910 vput(ap->a_vp); 911 } 912 913 return (error); 914 } 915 916 int 917 union_link(ap) 918 struct vop_link_args /* { 919 struct vnode *a_vp; 920 struct vnode *a_tdvp; 921 struct componentname *a_cnp; 922 } */ *ap; 923 { 924 int error = 0; 925 struct union_node *un; 926 struct vnode *vp; 927 struct vnode *tdvp; 928 929 un = VTOUNION(ap->a_tdvp); 930 931 if (ap->a_tdvp->v_op != ap->a_vp->v_op) { 932 vp = ap->a_vp; 933 } else { 934 struct union_node *tun = VTOUNION(ap->a_vp); 935 if (tun->un_uppervp == NULLVP) { 936 VOP_LOCK(ap->a_vp); 937 if (un->un_uppervp == tun->un_dirvp) { 938 un->un_flags &= ~UN_ULOCK; 939 VOP_UNLOCK(un->un_uppervp); 940 } 941 error = union_copyup(tun, 1, ap->a_cnp->cn_cred, 942 ap->a_cnp->cn_proc); 943 if (un->un_uppervp == tun->un_dirvp) { 944 VOP_LOCK(un->un_uppervp); 945 un->un_flags |= UN_ULOCK; 946 } 947 VOP_UNLOCK(ap->a_vp); 948 } 949 vp = tun->un_uppervp; 950 } 951 952 tdvp = un->un_uppervp; 953 if (tdvp == NULLVP) 954 error = EROFS; 955 956 if (error) { 957 vput(ap->a_tdvp); 958 return (error); 959 } 960 961 FIXUP(un); 962 VREF(tdvp); 963 un->un_flags |= UN_KLOCK; 964 vput(ap->a_tdvp); 965 966 return (VOP_LINK(vp, tdvp, ap->a_cnp)); 967 } 968 969 int 970 union_rename(ap) 971 struct vop_rename_args /* { 972 struct vnode *a_fdvp; 973 struct vnode *a_fvp; 974 struct componentname *a_fcnp; 975 struct vnode *a_tdvp; 976 struct vnode *a_tvp; 977 struct componentname *a_tcnp; 978 } */ *ap; 979 { 980 int error; 981 982 struct vnode *fdvp = ap->a_fdvp; 983 struct vnode *fvp = ap->a_fvp; 984 struct vnode *tdvp = ap->a_tdvp; 985 struct vnode *tvp = ap->a_tvp; 986 987 if (fdvp->v_op == union_vnodeop_p) { /* always true */ 988 struct union_node *un = VTOUNION(fdvp); 989 if (un->un_uppervp == NULLVP) { 990 /* 991 * this should never happen in normal 992 * operation but might if there was 993 * a problem creating the top-level shadow 994 * directory. 995 */ 996 error = EXDEV; 997 goto bad; 998 } 999 1000 fdvp = un->un_uppervp; 1001 VREF(fdvp); 1002 vrele(ap->a_fdvp); 1003 } 1004 1005 if (fvp->v_op == union_vnodeop_p) { /* always true */ 1006 struct union_node *un = VTOUNION(fvp); 1007 if (un->un_uppervp == NULLVP) { 1008 /* XXX: should do a copyup */ 1009 error = EXDEV; 1010 goto bad; 1011 } 1012 1013 if (un->un_lowervp != NULLVP) 1014 ap->a_fcnp->cn_flags |= DOWHITEOUT; 1015 1016 fvp = un->un_uppervp; 1017 VREF(fvp); 1018 vrele(ap->a_fvp); 1019 } 1020 1021 if (tdvp->v_op == union_vnodeop_p) { 1022 struct union_node *un = VTOUNION(tdvp); 1023 if (un->un_uppervp == NULLVP) { 1024 /* 1025 * this should never happen in normal 1026 * operation but might if there was 1027 * a problem creating the top-level shadow 1028 * directory. 1029 */ 1030 error = EXDEV; 1031 goto bad; 1032 } 1033 1034 tdvp = un->un_uppervp; 1035 VREF(tdvp); 1036 un->un_flags |= UN_KLOCK; 1037 vput(ap->a_tdvp); 1038 } 1039 1040 if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { 1041 struct union_node *un = VTOUNION(tvp); 1042 1043 tvp = un->un_uppervp; 1044 if (tvp != NULLVP) { 1045 VREF(tvp); 1046 un->un_flags |= UN_KLOCK; 1047 } 1048 vput(ap->a_tvp); 1049 } 1050 1051 return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); 1052 1053 bad: 1054 vrele(fdvp); 1055 vrele(fvp); 1056 vput(tdvp); 1057 if (tvp != NULLVP) 1058 vput(tvp); 1059 1060 return (error); 1061 } 1062 1063 int 1064 union_mkdir(ap) 1065 struct vop_mkdir_args /* { 1066 struct vnode *a_dvp; 1067 struct vnode **a_vpp; 1068 struct componentname *a_cnp; 1069 struct vattr *a_vap; 1070 } */ *ap; 1071 { 1072 struct union_node *un = VTOUNION(ap->a_dvp); 1073 struct vnode *dvp = un->un_uppervp; 1074 1075 if (dvp != NULLVP) { 1076 int error; 1077 struct vnode *vp; 1078 1079 FIXUP(un); 1080 VREF(dvp); 1081 un->un_flags |= UN_KLOCK; 1082 VOP_UNLOCK(ap->a_dvp); 1083 error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap); 1084 if (error) { 1085 vrele(ap->a_dvp); 1086 return (error); 1087 } 1088 1089 error = union_allocvp( 1090 ap->a_vpp, 1091 ap->a_dvp->v_mount, 1092 ap->a_dvp, 1093 NULLVP, 1094 ap->a_cnp, 1095 vp, 1096 NULLVP, 1097 1); 1098 vrele(ap->a_dvp); 1099 if (error) 1100 vput(vp); 1101 return (error); 1102 } 1103 1104 vput(ap->a_dvp); 1105 return (EROFS); 1106 } 1107 1108 int 1109 union_rmdir(ap) 1110 struct vop_rmdir_args /* { 1111 struct vnode *a_dvp; 1112 struct vnode *a_vp; 1113 struct componentname *a_cnp; 1114 } */ *ap; 1115 { 1116 int error; 1117 struct union_node *dun = VTOUNION(ap->a_dvp); 1118 struct union_node *un = VTOUNION(ap->a_vp); 1119 1120 if (dun->un_uppervp == NULLVP) 1121 panic("union rmdir: null upper vnode"); 1122 1123 if (un->un_uppervp != NULLVP) { 1124 struct vnode *dvp = dun->un_uppervp; 1125 struct vnode *vp = un->un_uppervp; 1126 struct componentname *cnp = ap->a_cnp; 1127 1128 FIXUP(dun); 1129 VREF(dvp); 1130 dun->un_flags |= UN_KLOCK; 1131 vput(ap->a_dvp); 1132 FIXUP(un); 1133 VREF(vp); 1134 un->un_flags |= UN_KLOCK; 1135 vput(ap->a_vp); 1136 1137 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) 1138 cnp->cn_flags |= DOWHITEOUT; 1139 error = VOP_RMDIR(dvp, vp, ap->a_cnp); 1140 if (!error) 1141 union_removed_upper(un); 1142 } else { 1143 FIXUP(dun); 1144 error = union_mkwhiteout( 1145 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 1146 dun->un_uppervp, ap->a_cnp, un->un_path); 1147 vput(ap->a_dvp); 1148 vput(ap->a_vp); 1149 } 1150 1151 return (error); 1152 } 1153 1154 int 1155 union_symlink(ap) 1156 struct vop_symlink_args /* { 1157 struct vnode *a_dvp; 1158 struct vnode **a_vpp; 1159 struct componentname *a_cnp; 1160 struct vattr *a_vap; 1161 char *a_target; 1162 } */ *ap; 1163 { 1164 struct union_node *un = VTOUNION(ap->a_dvp); 1165 struct vnode *dvp = un->un_uppervp; 1166 1167 if (dvp != NULLVP) { 1168 int error; 1169 struct vnode *vp; 1170 struct mount *mp = ap->a_dvp->v_mount; 1171 1172 FIXUP(un); 1173 VREF(dvp); 1174 un->un_flags |= UN_KLOCK; 1175 vput(ap->a_dvp); 1176 error = VOP_SYMLINK(dvp, &vp, ap->a_cnp, 1177 ap->a_vap, ap->a_target); 1178 *ap->a_vpp = NULLVP; 1179 return (error); 1180 } 1181 1182 vput(ap->a_dvp); 1183 return (EROFS); 1184 } 1185 1186 /* 1187 * union_readdir works in concert with getdirentries and 1188 * readdir(3) to provide a list of entries in the unioned 1189 * directories. getdirentries is responsible for walking 1190 * down the union stack. readdir(3) is responsible for 1191 * eliminating duplicate names from the returned data stream. 1192 */ 1193 int 1194 union_readdir(ap) 1195 struct vop_readdir_args /* { 1196 struct vnodeop_desc *a_desc; 1197 struct vnode *a_vp; 1198 struct uio *a_uio; 1199 struct ucred *a_cred; 1200 int *a_eofflag; 1201 u_long *a_cookies; 1202 int a_ncookies; 1203 } */ *ap; 1204 { 1205 register struct union_node *un = VTOUNION(ap->a_vp); 1206 register struct vnode *uvp = un->un_uppervp; 1207 1208 if (uvp == NULLVP) 1209 return (0); 1210 1211 FIXUP(un); 1212 ap->a_vp = uvp; 1213 return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap)); 1214 } 1215 1216 int 1217 union_readlink(ap) 1218 struct vop_readlink_args /* { 1219 struct vnode *a_vp; 1220 struct uio *a_uio; 1221 struct ucred *a_cred; 1222 } */ *ap; 1223 { 1224 int error; 1225 struct vnode *vp = OTHERVP(ap->a_vp); 1226 int dolock = (vp == LOWERVP(ap->a_vp)); 1227 1228 if (dolock) 1229 VOP_LOCK(vp); 1230 else 1231 FIXUP(VTOUNION(ap->a_vp)); 1232 error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); 1233 if (dolock) 1234 VOP_UNLOCK(vp); 1235 1236 return (error); 1237 } 1238 1239 int 1240 union_abortop(ap) 1241 struct vop_abortop_args /* { 1242 struct vnode *a_dvp; 1243 struct componentname *a_cnp; 1244 } */ *ap; 1245 { 1246 int error; 1247 struct vnode *vp = OTHERVP(ap->a_dvp); 1248 struct union_node *un = VTOUNION(ap->a_dvp); 1249 int islocked = un->un_flags & UN_LOCKED; 1250 int dolock = (vp == LOWERVP(ap->a_dvp)); 1251 1252 if (islocked) { 1253 if (dolock) 1254 VOP_LOCK(vp); 1255 else 1256 FIXUP(VTOUNION(ap->a_dvp)); 1257 } 1258 error = VOP_ABORTOP(vp, ap->a_cnp); 1259 if (islocked && dolock) 1260 VOP_UNLOCK(vp); 1261 1262 return (error); 1263 } 1264 1265 int 1266 union_inactive(ap) 1267 struct vop_inactive_args /* { 1268 struct vnode *a_vp; 1269 } */ *ap; 1270 { 1271 struct union_node *un = VTOUNION(ap->a_vp); 1272 struct vnode **vpp; 1273 1274 /* 1275 * Do nothing (and _don't_ bypass). 1276 * Wait to vrele lowervp until reclaim, 1277 * so that until then our union_node is in the 1278 * cache and reusable. 1279 * 1280 * NEEDSWORK: Someday, consider inactive'ing 1281 * the lowervp and then trying to reactivate it 1282 * with capabilities (v_id) 1283 * like they do in the name lookup cache code. 1284 * That's too much work for now. 1285 */ 1286 1287 #ifdef UNION_DIAGNOSTIC 1288 if (un->un_flags & UN_LOCKED) 1289 panic("union: inactivating locked node"); 1290 if (un->un_flags & UN_ULOCK) 1291 panic("union: inactivating w/locked upper node"); 1292 #endif 1293 1294 if (un->un_dircache != 0) { 1295 for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) 1296 vrele(*vpp); 1297 free(un->un_dircache, M_TEMP); 1298 un->un_dircache = 0; 1299 } 1300 1301 if ((un->un_flags & UN_CACHED) == 0) 1302 vgone(ap->a_vp); 1303 1304 return (0); 1305 } 1306 1307 int 1308 union_reclaim(ap) 1309 struct vop_reclaim_args /* { 1310 struct vnode *a_vp; 1311 } */ *ap; 1312 { 1313 1314 union_freevp(ap->a_vp); 1315 1316 return (0); 1317 } 1318 1319 int 1320 union_lock(ap) 1321 struct vop_lock_args *ap; 1322 { 1323 struct vnode *vp = ap->a_vp; 1324 struct union_node *un; 1325 1326 start: 1327 while (vp->v_flag & VXLOCK) { 1328 vp->v_flag |= VXWANT; 1329 sleep((caddr_t)vp, PINOD); 1330 } 1331 1332 un = VTOUNION(vp); 1333 1334 if (un->un_uppervp != NULLVP) { 1335 if (((un->un_flags & UN_ULOCK) == 0) && 1336 (vp->v_usecount != 0)) { 1337 VOP_LOCK(un->un_uppervp); 1338 un->un_flags |= UN_ULOCK; 1339 } 1340 #ifdef DIAGNOSTIC 1341 if (un->un_flags & UN_KLOCK) { 1342 vprint("union: dangling klock", vp); 1343 panic("union: dangling upper lock (%lx)", vp); 1344 } 1345 #endif 1346 } 1347 1348 if (un->un_flags & UN_LOCKED) { 1349 #ifdef DIAGNOSTIC 1350 if (curproc && un->un_pid == curproc->p_pid && 1351 un->un_pid > -1 && curproc->p_pid > -1) 1352 panic("union: locking against myself"); 1353 #endif 1354 un->un_flags |= UN_WANT; 1355 sleep((caddr_t) &un->un_flags, PINOD); 1356 goto start; 1357 } 1358 1359 #ifdef DIAGNOSTIC 1360 if (curproc) 1361 un->un_pid = curproc->p_pid; 1362 else 1363 un->un_pid = -1; 1364 #endif 1365 1366 un->un_flags |= UN_LOCKED; 1367 return (0); 1368 } 1369 1370 /* 1371 * When operations want to vput() a union node yet retain a lock on 1372 * the upper vnode (say, to do some further operations like link(), 1373 * mkdir(), ...), they set UN_KLOCK on the union node, then call 1374 * vput() which calls VOP_UNLOCK() and comes here. union_unlock() 1375 * unlocks the union node (leaving the upper vnode alone), clears the 1376 * KLOCK flag, and then returns to vput(). The caller then does whatever 1377 * is left to do with the upper vnode, and ensures that it gets unlocked. 1378 * 1379 * If UN_KLOCK isn't set, then the upper vnode is unlocked here. 1380 */ 1381 int 1382 union_unlock(ap) 1383 struct vop_lock_args *ap; 1384 { 1385 struct union_node *un = VTOUNION(ap->a_vp); 1386 1387 #ifdef DIAGNOSTIC 1388 if ((un->un_flags & UN_LOCKED) == 0) 1389 panic("union: unlock unlocked node"); 1390 if (curproc && un->un_pid != curproc->p_pid && 1391 curproc->p_pid > -1 && un->un_pid > -1) 1392 panic("union: unlocking other process's union node"); 1393 #endif 1394 1395 un->un_flags &= ~UN_LOCKED; 1396 1397 if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK) 1398 VOP_UNLOCK(un->un_uppervp); 1399 1400 un->un_flags &= ~(UN_ULOCK|UN_KLOCK); 1401 1402 if (un->un_flags & UN_WANT) { 1403 un->un_flags &= ~UN_WANT; 1404 wakeup((caddr_t) &un->un_flags); 1405 } 1406 1407 #ifdef DIAGNOSTIC 1408 un->un_pid = 0; 1409 #endif 1410 1411 return (0); 1412 } 1413 1414 int 1415 union_bmap(ap) 1416 struct vop_bmap_args /* { 1417 struct vnode *a_vp; 1418 daddr_t a_bn; 1419 struct vnode **a_vpp; 1420 daddr_t *a_bnp; 1421 int *a_runp; 1422 } */ *ap; 1423 { 1424 int error; 1425 struct vnode *vp = OTHERVP(ap->a_vp); 1426 int dolock = (vp == LOWERVP(ap->a_vp)); 1427 1428 if (dolock) 1429 VOP_LOCK(vp); 1430 else 1431 FIXUP(VTOUNION(ap->a_vp)); 1432 error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); 1433 if (dolock) 1434 VOP_UNLOCK(vp); 1435 1436 return (error); 1437 } 1438 1439 int 1440 union_print(ap) 1441 struct vop_print_args /* { 1442 struct vnode *a_vp; 1443 } */ *ap; 1444 { 1445 struct vnode *vp = ap->a_vp; 1446 1447 printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n", 1448 vp, UPPERVP(vp), LOWERVP(vp)); 1449 if (UPPERVP(vp) != NULLVP) 1450 vprint("union: upper", UPPERVP(vp)); 1451 if (LOWERVP(vp) != NULLVP) 1452 vprint("union: lower", LOWERVP(vp)); 1453 1454 return (0); 1455 } 1456 1457 int 1458 union_islocked(ap) 1459 struct vop_islocked_args /* { 1460 struct vnode *a_vp; 1461 } */ *ap; 1462 { 1463 1464 return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); 1465 } 1466 1467 int 1468 union_pathconf(ap) 1469 struct vop_pathconf_args /* { 1470 struct vnode *a_vp; 1471 int a_name; 1472 int *a_retval; 1473 } */ *ap; 1474 { 1475 int error; 1476 struct vnode *vp = OTHERVP(ap->a_vp); 1477 int dolock = (vp == LOWERVP(ap->a_vp)); 1478 1479 if (dolock) 1480 VOP_LOCK(vp); 1481 else 1482 FIXUP(VTOUNION(ap->a_vp)); 1483 error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval); 1484 if (dolock) 1485 VOP_UNLOCK(vp); 1486 1487 return (error); 1488 } 1489 1490 int 1491 union_advlock(ap) 1492 struct vop_advlock_args /* { 1493 struct vnode *a_vp; 1494 caddr_t a_id; 1495 int a_op; 1496 struct flock *a_fl; 1497 int a_flags; 1498 } */ *ap; 1499 { 1500 1501 return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op, 1502 ap->a_fl, ap->a_flags)); 1503 } 1504 1505 1506 /* 1507 * XXX - vop_strategy must be hand coded because it has no 1508 * vnode in its arguments. 1509 * This goes away with a merged VM/buffer cache. 1510 */ 1511 int 1512 union_strategy(ap) 1513 struct vop_strategy_args /* { 1514 struct buf *a_bp; 1515 } */ *ap; 1516 { 1517 struct buf *bp = ap->a_bp; 1518 int error; 1519 struct vnode *savedvp; 1520 1521 savedvp = bp->b_vp; 1522 bp->b_vp = OTHERVP(bp->b_vp); 1523 1524 #ifdef DIAGNOSTIC 1525 if (bp->b_vp == NULLVP) 1526 panic("union_strategy: nil vp"); 1527 if (((bp->b_flags & B_READ) == 0) && 1528 (bp->b_vp == LOWERVP(savedvp))) 1529 panic("union_strategy: writing to lowervp"); 1530 #endif 1531 1532 error = VOP_STRATEGY(bp); 1533 bp->b_vp = savedvp; 1534 1535 return (error); 1536 } 1537 1538 /* 1539 * Global vfs data structures 1540 */ 1541 int (**union_vnodeop_p)(); 1542 struct vnodeopv_entry_desc union_vnodeop_entries[] = { 1543 { &vop_default_desc, vn_default_error }, 1544 { &vop_lookup_desc, union_lookup }, /* lookup */ 1545 { &vop_create_desc, union_create }, /* create */ 1546 { &vop_whiteout_desc, union_whiteout }, /* whiteout */ 1547 { &vop_mknod_desc, union_mknod }, /* mknod */ 1548 { &vop_open_desc, union_open }, /* open */ 1549 { &vop_close_desc, union_close }, /* close */ 1550 { &vop_access_desc, union_access }, /* access */ 1551 { &vop_getattr_desc, union_getattr }, /* getattr */ 1552 { &vop_setattr_desc, union_setattr }, /* setattr */ 1553 { &vop_read_desc, union_read }, /* read */ 1554 { &vop_write_desc, union_write }, /* write */ 1555 { &vop_lease_desc, union_lease }, /* lease */ 1556 { &vop_ioctl_desc, union_ioctl }, /* ioctl */ 1557 { &vop_select_desc, union_select }, /* select */ 1558 { &vop_revoke_desc, union_revoke }, /* revoke */ 1559 { &vop_mmap_desc, union_mmap }, /* mmap */ 1560 { &vop_fsync_desc, union_fsync }, /* fsync */ 1561 { &vop_seek_desc, union_seek }, /* seek */ 1562 { &vop_remove_desc, union_remove }, /* remove */ 1563 { &vop_link_desc, union_link }, /* link */ 1564 { &vop_rename_desc, union_rename }, /* rename */ 1565 { &vop_mkdir_desc, union_mkdir }, /* mkdir */ 1566 { &vop_rmdir_desc, union_rmdir }, /* rmdir */ 1567 { &vop_symlink_desc, union_symlink }, /* symlink */ 1568 { &vop_readdir_desc, union_readdir }, /* readdir */ 1569 { &vop_readlink_desc, union_readlink }, /* readlink */ 1570 { &vop_abortop_desc, union_abortop }, /* abortop */ 1571 { &vop_inactive_desc, union_inactive }, /* inactive */ 1572 { &vop_reclaim_desc, union_reclaim }, /* reclaim */ 1573 { &vop_lock_desc, union_lock }, /* lock */ 1574 { &vop_unlock_desc, union_unlock }, /* unlock */ 1575 { &vop_bmap_desc, union_bmap }, /* bmap */ 1576 { &vop_strategy_desc, union_strategy }, /* strategy */ 1577 { &vop_print_desc, union_print }, /* print */ 1578 { &vop_islocked_desc, union_islocked }, /* islocked */ 1579 { &vop_pathconf_desc, union_pathconf }, /* pathconf */ 1580 { &vop_advlock_desc, union_advlock }, /* advlock */ 1581 #ifdef notdef 1582 { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */ 1583 { &vop_valloc_desc, union_valloc }, /* valloc */ 1584 { &vop_vfree_desc, union_vfree }, /* vfree */ 1585 { &vop_truncate_desc, union_truncate }, /* truncate */ 1586 { &vop_update_desc, union_update }, /* update */ 1587 { &vop_bwrite_desc, union_bwrite }, /* bwrite */ 1588 #endif 1589 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1590 }; 1591 struct vnodeopv_desc union_vnodeop_opv_desc = 1592 { &union_vnodeop_p, union_vnodeop_entries }; 1593