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.25 (Berkeley) 03/24/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_mmap(ap) 801 struct vop_mmap_args /* { 802 struct vnode *a_vp; 803 int a_fflags; 804 struct ucred *a_cred; 805 struct proc *a_p; 806 } */ *ap; 807 { 808 809 return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags, 810 ap->a_cred, ap->a_p)); 811 } 812 813 int 814 union_fsync(ap) 815 struct vop_fsync_args /* { 816 struct vnode *a_vp; 817 struct ucred *a_cred; 818 int a_waitfor; 819 struct proc *a_p; 820 } */ *ap; 821 { 822 int error = 0; 823 struct vnode *targetvp = OTHERVP(ap->a_vp); 824 825 if (targetvp != NULLVP) { 826 int dolock = (targetvp == LOWERVP(ap->a_vp)); 827 828 if (dolock) 829 VOP_LOCK(targetvp); 830 else 831 FIXUP(VTOUNION(ap->a_vp)); 832 error = VOP_FSYNC(targetvp, ap->a_cred, 833 ap->a_waitfor, ap->a_p); 834 if (dolock) 835 VOP_UNLOCK(targetvp); 836 } 837 838 return (error); 839 } 840 841 int 842 union_seek(ap) 843 struct vop_seek_args /* { 844 struct vnode *a_vp; 845 off_t a_oldoff; 846 off_t a_newoff; 847 struct ucred *a_cred; 848 } */ *ap; 849 { 850 851 return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred)); 852 } 853 854 int 855 union_remove(ap) 856 struct vop_remove_args /* { 857 struct vnode *a_dvp; 858 struct vnode *a_vp; 859 struct componentname *a_cnp; 860 } */ *ap; 861 { 862 int error; 863 struct union_node *dun = VTOUNION(ap->a_dvp); 864 struct union_node *un = VTOUNION(ap->a_vp); 865 866 if (dun->un_uppervp == NULLVP) 867 panic("union remove: null upper vnode"); 868 869 if (un->un_uppervp != NULLVP) { 870 struct vnode *dvp = dun->un_uppervp; 871 struct vnode *vp = un->un_uppervp; 872 struct componentname *cnp = ap->a_cnp; 873 874 FIXUP(dun); 875 VREF(dvp); 876 dun->un_flags |= UN_KLOCK; 877 vput(ap->a_dvp); 878 FIXUP(un); 879 VREF(vp); 880 un->un_flags |= UN_KLOCK; 881 vput(ap->a_vp); 882 883 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) 884 cnp->cn_flags |= DOWHITEOUT; 885 error = VOP_REMOVE(dvp, vp, cnp); 886 if (!error) 887 union_removed_upper(un); 888 } else { 889 FIXUP(dun); 890 error = union_mkwhiteout( 891 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 892 dun->un_uppervp, ap->a_cnp, un->un_path); 893 vput(ap->a_dvp); 894 vput(ap->a_vp); 895 } 896 897 return (error); 898 } 899 900 int 901 union_link(ap) 902 struct vop_link_args /* { 903 struct vnode *a_vp; 904 struct vnode *a_tdvp; 905 struct componentname *a_cnp; 906 } */ *ap; 907 { 908 int error = 0; 909 struct union_node *un; 910 struct vnode *vp; 911 struct vnode *tdvp; 912 913 un = VTOUNION(ap->a_tdvp); 914 915 if (ap->a_tdvp->v_op != ap->a_vp->v_op) { 916 vp = ap->a_vp; 917 } else { 918 struct union_node *tun = VTOUNION(ap->a_vp); 919 if (tun->un_uppervp == NULLVP) { 920 VOP_LOCK(ap->a_vp); 921 if (un->un_uppervp == tun->un_dirvp) { 922 un->un_flags &= ~UN_ULOCK; 923 VOP_UNLOCK(un->un_uppervp); 924 } 925 error = union_copyup(tun, 1, ap->a_cnp->cn_cred, 926 ap->a_cnp->cn_proc); 927 if (un->un_uppervp == tun->un_dirvp) { 928 VOP_LOCK(un->un_uppervp); 929 un->un_flags |= UN_ULOCK; 930 } 931 VOP_UNLOCK(ap->a_vp); 932 } 933 vp = tun->un_uppervp; 934 } 935 936 tdvp = un->un_uppervp; 937 if (tdvp == NULLVP) 938 error = EROFS; 939 940 if (error) { 941 vput(ap->a_tdvp); 942 return (error); 943 } 944 945 FIXUP(un); 946 VREF(tdvp); 947 un->un_flags |= UN_KLOCK; 948 vput(ap->a_tdvp); 949 950 return (VOP_LINK(vp, tdvp, ap->a_cnp)); 951 } 952 953 int 954 union_rename(ap) 955 struct vop_rename_args /* { 956 struct vnode *a_fdvp; 957 struct vnode *a_fvp; 958 struct componentname *a_fcnp; 959 struct vnode *a_tdvp; 960 struct vnode *a_tvp; 961 struct componentname *a_tcnp; 962 } */ *ap; 963 { 964 int error; 965 966 struct vnode *fdvp = ap->a_fdvp; 967 struct vnode *fvp = ap->a_fvp; 968 struct vnode *tdvp = ap->a_tdvp; 969 struct vnode *tvp = ap->a_tvp; 970 971 if (fdvp->v_op == union_vnodeop_p) { /* always true */ 972 struct union_node *un = VTOUNION(fdvp); 973 if (un->un_uppervp == NULLVP) { 974 /* 975 * this should never happen in normal 976 * operation but might if there was 977 * a problem creating the top-level shadow 978 * directory. 979 */ 980 error = EXDEV; 981 goto bad; 982 } 983 984 fdvp = un->un_uppervp; 985 VREF(fdvp); 986 vrele(ap->a_fdvp); 987 } 988 989 if (fvp->v_op == union_vnodeop_p) { /* always true */ 990 struct union_node *un = VTOUNION(fvp); 991 if (un->un_uppervp == NULLVP) { 992 /* XXX: should do a copyup */ 993 error = EXDEV; 994 goto bad; 995 } 996 997 if (un->un_lowervp != NULLVP) 998 ap->a_fcnp->cn_flags |= DOWHITEOUT; 999 1000 fvp = un->un_uppervp; 1001 VREF(fvp); 1002 vrele(ap->a_fvp); 1003 } 1004 1005 if (tdvp->v_op == union_vnodeop_p) { 1006 struct union_node *un = VTOUNION(tdvp); 1007 if (un->un_uppervp == NULLVP) { 1008 /* 1009 * this should never happen in normal 1010 * operation but might if there was 1011 * a problem creating the top-level shadow 1012 * directory. 1013 */ 1014 error = EXDEV; 1015 goto bad; 1016 } 1017 1018 tdvp = un->un_uppervp; 1019 VREF(tdvp); 1020 un->un_flags |= UN_KLOCK; 1021 vput(ap->a_tdvp); 1022 } 1023 1024 if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { 1025 struct union_node *un = VTOUNION(tvp); 1026 1027 tvp = un->un_uppervp; 1028 if (tvp != NULLVP) { 1029 VREF(tvp); 1030 un->un_flags |= UN_KLOCK; 1031 } 1032 vput(ap->a_tvp); 1033 } 1034 1035 return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); 1036 1037 bad: 1038 vrele(fdvp); 1039 vrele(fvp); 1040 vput(tdvp); 1041 if (tvp != NULLVP) 1042 vput(tvp); 1043 1044 return (error); 1045 } 1046 1047 int 1048 union_mkdir(ap) 1049 struct vop_mkdir_args /* { 1050 struct vnode *a_dvp; 1051 struct vnode **a_vpp; 1052 struct componentname *a_cnp; 1053 struct vattr *a_vap; 1054 } */ *ap; 1055 { 1056 struct union_node *un = VTOUNION(ap->a_dvp); 1057 struct vnode *dvp = un->un_uppervp; 1058 1059 if (dvp != NULLVP) { 1060 int error; 1061 struct vnode *vp; 1062 1063 FIXUP(un); 1064 VREF(dvp); 1065 un->un_flags |= UN_KLOCK; 1066 VOP_UNLOCK(ap->a_dvp); 1067 error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap); 1068 if (error) { 1069 vrele(ap->a_dvp); 1070 return (error); 1071 } 1072 1073 error = union_allocvp( 1074 ap->a_vpp, 1075 ap->a_dvp->v_mount, 1076 ap->a_dvp, 1077 NULLVP, 1078 ap->a_cnp, 1079 vp, 1080 NULLVP, 1081 1); 1082 vrele(ap->a_dvp); 1083 if (error) 1084 vput(vp); 1085 return (error); 1086 } 1087 1088 vput(ap->a_dvp); 1089 return (EROFS); 1090 } 1091 1092 int 1093 union_rmdir(ap) 1094 struct vop_rmdir_args /* { 1095 struct vnode *a_dvp; 1096 struct vnode *a_vp; 1097 struct componentname *a_cnp; 1098 } */ *ap; 1099 { 1100 int error; 1101 struct union_node *dun = VTOUNION(ap->a_dvp); 1102 struct union_node *un = VTOUNION(ap->a_vp); 1103 1104 if (dun->un_uppervp == NULLVP) 1105 panic("union rmdir: null upper vnode"); 1106 1107 if (un->un_uppervp != NULLVP) { 1108 struct vnode *dvp = dun->un_uppervp; 1109 struct vnode *vp = un->un_uppervp; 1110 struct componentname *cnp = ap->a_cnp; 1111 1112 FIXUP(dun); 1113 VREF(dvp); 1114 dun->un_flags |= UN_KLOCK; 1115 vput(ap->a_dvp); 1116 FIXUP(un); 1117 VREF(vp); 1118 un->un_flags |= UN_KLOCK; 1119 vput(ap->a_vp); 1120 1121 if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) 1122 cnp->cn_flags |= DOWHITEOUT; 1123 error = VOP_RMDIR(dvp, vp, ap->a_cnp); 1124 if (!error) 1125 union_removed_upper(un); 1126 } else { 1127 FIXUP(dun); 1128 error = union_mkwhiteout( 1129 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 1130 dun->un_uppervp, ap->a_cnp, un->un_path); 1131 vput(ap->a_dvp); 1132 vput(ap->a_vp); 1133 } 1134 1135 return (error); 1136 } 1137 1138 int 1139 union_symlink(ap) 1140 struct vop_symlink_args /* { 1141 struct vnode *a_dvp; 1142 struct vnode **a_vpp; 1143 struct componentname *a_cnp; 1144 struct vattr *a_vap; 1145 char *a_target; 1146 } */ *ap; 1147 { 1148 struct union_node *un = VTOUNION(ap->a_dvp); 1149 struct vnode *dvp = un->un_uppervp; 1150 1151 if (dvp != NULLVP) { 1152 int error; 1153 struct vnode *vp; 1154 struct mount *mp = ap->a_dvp->v_mount; 1155 1156 FIXUP(un); 1157 VREF(dvp); 1158 un->un_flags |= UN_KLOCK; 1159 vput(ap->a_dvp); 1160 error = VOP_SYMLINK(dvp, &vp, ap->a_cnp, 1161 ap->a_vap, ap->a_target); 1162 *ap->a_vpp = NULLVP; 1163 return (error); 1164 } 1165 1166 vput(ap->a_dvp); 1167 return (EROFS); 1168 } 1169 1170 /* 1171 * union_readdir works in concert with getdirentries and 1172 * readdir(3) to provide a list of entries in the unioned 1173 * directories. getdirentries is responsible for walking 1174 * down the union stack. readdir(3) is responsible for 1175 * eliminating duplicate names from the returned data stream. 1176 */ 1177 int 1178 union_readdir(ap) 1179 struct vop_readdir_args /* { 1180 struct vnodeop_desc *a_desc; 1181 struct vnode *a_vp; 1182 struct uio *a_uio; 1183 struct ucred *a_cred; 1184 int *a_eofflag; 1185 u_long *a_cookies; 1186 int a_ncookies; 1187 } */ *ap; 1188 { 1189 register struct union_node *un = VTOUNION(ap->a_vp); 1190 register struct vnode *uvp = un->un_uppervp; 1191 1192 if (uvp == NULLVP) 1193 return (0); 1194 1195 FIXUP(un); 1196 ap->a_vp = uvp; 1197 return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap)); 1198 } 1199 1200 int 1201 union_readlink(ap) 1202 struct vop_readlink_args /* { 1203 struct vnode *a_vp; 1204 struct uio *a_uio; 1205 struct ucred *a_cred; 1206 } */ *ap; 1207 { 1208 int error; 1209 struct vnode *vp = OTHERVP(ap->a_vp); 1210 int dolock = (vp == LOWERVP(ap->a_vp)); 1211 1212 if (dolock) 1213 VOP_LOCK(vp); 1214 else 1215 FIXUP(VTOUNION(ap->a_vp)); 1216 error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); 1217 if (dolock) 1218 VOP_UNLOCK(vp); 1219 1220 return (error); 1221 } 1222 1223 int 1224 union_abortop(ap) 1225 struct vop_abortop_args /* { 1226 struct vnode *a_dvp; 1227 struct componentname *a_cnp; 1228 } */ *ap; 1229 { 1230 int error; 1231 struct vnode *vp = OTHERVP(ap->a_dvp); 1232 struct union_node *un = VTOUNION(ap->a_dvp); 1233 int islocked = un->un_flags & UN_LOCKED; 1234 int dolock = (vp == LOWERVP(ap->a_dvp)); 1235 1236 if (islocked) { 1237 if (dolock) 1238 VOP_LOCK(vp); 1239 else 1240 FIXUP(VTOUNION(ap->a_dvp)); 1241 } 1242 error = VOP_ABORTOP(vp, ap->a_cnp); 1243 if (islocked && dolock) 1244 VOP_UNLOCK(vp); 1245 1246 return (error); 1247 } 1248 1249 int 1250 union_inactive(ap) 1251 struct vop_inactive_args /* { 1252 struct vnode *a_vp; 1253 } */ *ap; 1254 { 1255 struct union_node *un = VTOUNION(ap->a_vp); 1256 struct vnode **vpp; 1257 1258 /* 1259 * Do nothing (and _don't_ bypass). 1260 * Wait to vrele lowervp until reclaim, 1261 * so that until then our union_node is in the 1262 * cache and reusable. 1263 * 1264 * NEEDSWORK: Someday, consider inactive'ing 1265 * the lowervp and then trying to reactivate it 1266 * with capabilities (v_id) 1267 * like they do in the name lookup cache code. 1268 * That's too much work for now. 1269 */ 1270 1271 #ifdef UNION_DIAGNOSTIC 1272 if (un->un_flags & UN_LOCKED) 1273 panic("union: inactivating locked node"); 1274 if (un->un_flags & UN_ULOCK) 1275 panic("union: inactivating w/locked upper node"); 1276 #endif 1277 1278 if (un->un_dircache != 0) { 1279 for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) 1280 vrele(*vpp); 1281 free(un->un_dircache, M_TEMP); 1282 un->un_dircache = 0; 1283 } 1284 1285 if ((un->un_flags & UN_CACHED) == 0) 1286 VOP_REVOKE(ap->a_vp, 0); 1287 1288 return (0); 1289 } 1290 1291 int 1292 union_reclaim(ap) 1293 struct vop_reclaim_args /* { 1294 struct vnode *a_vp; 1295 } */ *ap; 1296 { 1297 1298 union_freevp(ap->a_vp); 1299 1300 return (0); 1301 } 1302 1303 int 1304 union_lock(ap) 1305 struct vop_lock_args *ap; 1306 { 1307 struct vnode *vp = ap->a_vp; 1308 struct union_node *un; 1309 1310 start: 1311 while (vp->v_flag & VXLOCK) { 1312 vp->v_flag |= VXWANT; 1313 sleep((caddr_t)vp, PINOD); 1314 } 1315 1316 un = VTOUNION(vp); 1317 1318 if (un->un_uppervp != NULLVP) { 1319 if (((un->un_flags & UN_ULOCK) == 0) && 1320 (vp->v_usecount != 0)) { 1321 VOP_LOCK(un->un_uppervp); 1322 un->un_flags |= UN_ULOCK; 1323 } 1324 #ifdef DIAGNOSTIC 1325 if (un->un_flags & UN_KLOCK) 1326 vprint("union: dangling klock", vp); 1327 panic("union: dangling upper lock (%lx)", vp); 1328 #endif 1329 } 1330 1331 if (un->un_flags & UN_LOCKED) { 1332 #ifdef DIAGNOSTIC 1333 if (curproc && un->un_pid == curproc->p_pid && 1334 un->un_pid > -1 && curproc->p_pid > -1) 1335 panic("union: locking against myself"); 1336 #endif 1337 un->un_flags |= UN_WANT; 1338 sleep((caddr_t) &un->un_flags, PINOD); 1339 goto start; 1340 } 1341 1342 #ifdef DIAGNOSTIC 1343 if (curproc) 1344 un->un_pid = curproc->p_pid; 1345 else 1346 un->un_pid = -1; 1347 #endif 1348 1349 un->un_flags |= UN_LOCKED; 1350 return (0); 1351 } 1352 1353 /* 1354 * When operations want to vput() a union node yet retain a lock on 1355 * the upper vnode (say, to do some further operations like link(), 1356 * mkdir(), ...), they set UN_KLOCK on the union node, then call 1357 * vput() which calls VOP_UNLOCK() and comes here. union_unlock() 1358 * unlocks the union node (leaving the upper vnode alone), clears the 1359 * KLOCK flag, and then returns to vput(). The caller then does whatever 1360 * is left to do with the upper vnode, and ensures that it gets unlocked. 1361 * 1362 * If UN_KLOCK isn't set, then the upper vnode is unlocked here. 1363 */ 1364 int 1365 union_unlock(ap) 1366 struct vop_lock_args *ap; 1367 { 1368 struct union_node *un = VTOUNION(ap->a_vp); 1369 1370 #ifdef DIAGNOSTIC 1371 if ((un->un_flags & UN_LOCKED) == 0) 1372 panic("union: unlock unlocked node"); 1373 if (curproc && un->un_pid != curproc->p_pid && 1374 curproc->p_pid > -1 && un->un_pid > -1) 1375 panic("union: unlocking other process's union node"); 1376 #endif 1377 1378 un->un_flags &= ~UN_LOCKED; 1379 1380 if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK) 1381 VOP_UNLOCK(un->un_uppervp); 1382 1383 un->un_flags &= ~(UN_ULOCK|UN_KLOCK); 1384 1385 if (un->un_flags & UN_WANT) { 1386 un->un_flags &= ~UN_WANT; 1387 wakeup((caddr_t) &un->un_flags); 1388 } 1389 1390 #ifdef DIAGNOSTIC 1391 un->un_pid = 0; 1392 #endif 1393 1394 return (0); 1395 } 1396 1397 int 1398 union_bmap(ap) 1399 struct vop_bmap_args /* { 1400 struct vnode *a_vp; 1401 daddr_t a_bn; 1402 struct vnode **a_vpp; 1403 daddr_t *a_bnp; 1404 int *a_runp; 1405 } */ *ap; 1406 { 1407 int error; 1408 struct vnode *vp = OTHERVP(ap->a_vp); 1409 int dolock = (vp == LOWERVP(ap->a_vp)); 1410 1411 if (dolock) 1412 VOP_LOCK(vp); 1413 else 1414 FIXUP(VTOUNION(ap->a_vp)); 1415 error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); 1416 if (dolock) 1417 VOP_UNLOCK(vp); 1418 1419 return (error); 1420 } 1421 1422 int 1423 union_print(ap) 1424 struct vop_print_args /* { 1425 struct vnode *a_vp; 1426 } */ *ap; 1427 { 1428 struct vnode *vp = ap->a_vp; 1429 1430 printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n", 1431 vp, UPPERVP(vp), LOWERVP(vp)); 1432 if (UPPERVP(vp) != NULLVP) 1433 vprint("union: upper", UPPERVP(vp)); 1434 if (LOWERVP(vp) != NULLVP) 1435 vprint("union: lower", LOWERVP(vp)); 1436 1437 return (0); 1438 } 1439 1440 int 1441 union_islocked(ap) 1442 struct vop_islocked_args /* { 1443 struct vnode *a_vp; 1444 } */ *ap; 1445 { 1446 1447 return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); 1448 } 1449 1450 int 1451 union_pathconf(ap) 1452 struct vop_pathconf_args /* { 1453 struct vnode *a_vp; 1454 int a_name; 1455 int *a_retval; 1456 } */ *ap; 1457 { 1458 int error; 1459 struct vnode *vp = OTHERVP(ap->a_vp); 1460 int dolock = (vp == LOWERVP(ap->a_vp)); 1461 1462 if (dolock) 1463 VOP_LOCK(vp); 1464 else 1465 FIXUP(VTOUNION(ap->a_vp)); 1466 error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval); 1467 if (dolock) 1468 VOP_UNLOCK(vp); 1469 1470 return (error); 1471 } 1472 1473 int 1474 union_advlock(ap) 1475 struct vop_advlock_args /* { 1476 struct vnode *a_vp; 1477 caddr_t a_id; 1478 int a_op; 1479 struct flock *a_fl; 1480 int a_flags; 1481 } */ *ap; 1482 { 1483 1484 return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op, 1485 ap->a_fl, ap->a_flags)); 1486 } 1487 1488 1489 /* 1490 * XXX - vop_strategy must be hand coded because it has no 1491 * vnode in its arguments. 1492 * This goes away with a merged VM/buffer cache. 1493 */ 1494 int 1495 union_strategy(ap) 1496 struct vop_strategy_args /* { 1497 struct buf *a_bp; 1498 } */ *ap; 1499 { 1500 struct buf *bp = ap->a_bp; 1501 int error; 1502 struct vnode *savedvp; 1503 1504 savedvp = bp->b_vp; 1505 bp->b_vp = OTHERVP(bp->b_vp); 1506 1507 #ifdef DIAGNOSTIC 1508 if (bp->b_vp == NULLVP) 1509 panic("union_strategy: nil vp"); 1510 if (((bp->b_flags & B_READ) == 0) && 1511 (bp->b_vp == LOWERVP(savedvp))) 1512 panic("union_strategy: writing to lowervp"); 1513 #endif 1514 1515 error = VOP_STRATEGY(bp); 1516 bp->b_vp = savedvp; 1517 1518 return (error); 1519 } 1520 1521 /* 1522 * Global vfs data structures 1523 */ 1524 int (**union_vnodeop_p)(); 1525 struct vnodeopv_entry_desc union_vnodeop_entries[] = { 1526 { &vop_default_desc, vn_default_error }, 1527 { &vop_lookup_desc, union_lookup }, /* lookup */ 1528 { &vop_create_desc, union_create }, /* create */ 1529 { &vop_whiteout_desc, union_whiteout }, /* whiteout */ 1530 { &vop_mknod_desc, union_mknod }, /* mknod */ 1531 { &vop_open_desc, union_open }, /* open */ 1532 { &vop_close_desc, union_close }, /* close */ 1533 { &vop_access_desc, union_access }, /* access */ 1534 { &vop_getattr_desc, union_getattr }, /* getattr */ 1535 { &vop_setattr_desc, union_setattr }, /* setattr */ 1536 { &vop_read_desc, union_read }, /* read */ 1537 { &vop_write_desc, union_write }, /* write */ 1538 { &vop_lease_desc, union_lease }, /* lease */ 1539 { &vop_ioctl_desc, union_ioctl }, /* ioctl */ 1540 { &vop_select_desc, union_select }, /* select */ 1541 { &vop_mmap_desc, union_mmap }, /* mmap */ 1542 { &vop_fsync_desc, union_fsync }, /* fsync */ 1543 { &vop_seek_desc, union_seek }, /* seek */ 1544 { &vop_remove_desc, union_remove }, /* remove */ 1545 { &vop_link_desc, union_link }, /* link */ 1546 { &vop_rename_desc, union_rename }, /* rename */ 1547 { &vop_mkdir_desc, union_mkdir }, /* mkdir */ 1548 { &vop_rmdir_desc, union_rmdir }, /* rmdir */ 1549 { &vop_symlink_desc, union_symlink }, /* symlink */ 1550 { &vop_readdir_desc, union_readdir }, /* readdir */ 1551 { &vop_readlink_desc, union_readlink }, /* readlink */ 1552 { &vop_abortop_desc, union_abortop }, /* abortop */ 1553 { &vop_inactive_desc, union_inactive }, /* inactive */ 1554 { &vop_reclaim_desc, union_reclaim }, /* reclaim */ 1555 { &vop_lock_desc, union_lock }, /* lock */ 1556 { &vop_unlock_desc, union_unlock }, /* unlock */ 1557 { &vop_bmap_desc, union_bmap }, /* bmap */ 1558 { &vop_strategy_desc, union_strategy }, /* strategy */ 1559 { &vop_print_desc, union_print }, /* print */ 1560 { &vop_islocked_desc, union_islocked }, /* islocked */ 1561 { &vop_pathconf_desc, union_pathconf }, /* pathconf */ 1562 { &vop_advlock_desc, union_advlock }, /* advlock */ 1563 #ifdef notdef 1564 { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */ 1565 { &vop_valloc_desc, union_valloc }, /* valloc */ 1566 { &vop_vfree_desc, union_vfree }, /* vfree */ 1567 { &vop_truncate_desc, union_truncate }, /* truncate */ 1568 { &vop_update_desc, union_update }, /* update */ 1569 { &vop_bwrite_desc, union_bwrite }, /* bwrite */ 1570 #endif 1571 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1572 }; 1573 struct vnodeopv_desc union_vnodeop_opv_desc = 1574 { &union_vnodeop_p, union_vnodeop_entries }; 1575