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