1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 29/* 30 * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry. 31 * Copyright (c) 1992, 1993, 1994, 1995 32 * The Regents of the University of California. All rights reserved. 33 * 34 * This code is derived from software contributed to Berkeley by 35 * Jan-Simon Pendry. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. All advertising materials mentioning features or use of this software 46 * must display the following acknowledgement: 47 * This product includes software developed by the University of 48 * California, Berkeley and its contributors. 49 * 4. Neither the name of the University nor the names of its contributors 50 * may be used to endorse or promote products derived from this software 51 * without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 * SUCH DAMAGE. 64 * 65 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95 66 */ 67 68#include <sys/param.h> 69#include <sys/systm.h> 70#include <sys/proc.h> 71#include <sys/kauth.h> 72#include <sys/file.h> 73#include <sys/time.h> 74#include <sys/stat.h> 75#include <sys/types.h> 76#include <sys/vnode_internal.h> 77#include <sys/mount_internal.h> 78#include <sys/namei.h> 79#include <sys/malloc.h> 80#include <sys/buf_internal.h> 81#include <sys/queue.h> 82#include <sys/lock.h> 83#include <miscfs/union/union.h> 84#include <vfs/vfs_support.h> 85#include <sys/ubc.h> 86#include <sys/kdebug.h> 87#include <sys/uio_internal.h> 88 89/* called with no union lock held */ 90static int 91union_lookup1(struct vnode *udvp, struct vnode **dvpp, struct vnode **vpp, 92 struct componentname *cnp) 93{ 94 int error; 95 vfs_context_t ctx = cnp->cn_context; 96 struct vnode *tdvp; 97 struct vnode *dvp; 98 struct mount *mp; 99 100 dvp = *dvpp; 101 102 /* 103 * If stepping up the directory tree, check for going 104 * back across the mount point, in which case do what 105 * lookup would do by stepping back down the mount 106 * hierarchy. 107 */ 108 if (cnp->cn_flags & ISDOTDOT) { 109 while ((dvp != udvp) && (dvp->v_flag & VROOT)) { 110 /* 111 * Don't do the NOCROSSMOUNT check 112 * at this level. By definition, 113 * union fs deals with namespaces, not 114 * filesystems. 115 */ 116 tdvp = dvp; 117 *dvpp = dvp = dvp->v_mount->mnt_vnodecovered; 118 vnode_put(tdvp); 119 vnode_get(dvp); 120 } 121 } 122 123 error = VNOP_LOOKUP(dvp, &tdvp, cnp, ctx); 124 if (error) 125 return (error); 126 127 dvp = tdvp; 128 /* 129 * Lastly check if the current node is a mount point in 130 * which case walk up the mount hierarchy making sure not to 131 * bump into the root of the mount tree (ie. dvp != udvp). 132 */ 133 while (dvp != udvp && (dvp->v_type == VDIR) && 134 (mp = dvp->v_mountedhere)) { 135 if (vfs_busy(mp, LK_NOWAIT)) { 136 vnode_put(dvp); 137 return(ENOENT); 138 } 139 error = VFS_ROOT(mp, &tdvp, ctx); 140 vfs_unbusy(mp); 141 if (error) { 142 vnode_put(dvp); 143 return (error); 144 } 145 146 vnode_put(dvp); 147 dvp = tdvp; 148 } 149 150 *vpp = dvp; 151 return (0); 152} 153 154static int 155union_lookup(struct vnop_lookup_args *ap) 156/* 157 struct vnop_lookup_args { 158 struct vnodeop_desc *a_desc; 159 struct vnode *a_dvp; 160 struct vnode **a_vpp; 161 struct componentname *a_cnp; 162 vfs_context_t a_context; 163 } *ap) 164*/ 165{ 166 int error; 167 int uerror = 0; 168 int lerror = 0; 169 struct vnode *uppervp, *lowervp; 170 struct vnode *upperdvp, *lowerdvp; 171 struct vnode *dvp = ap->a_dvp; 172 struct union_node *dun; 173 struct componentname *cnp = ap->a_cnp; 174 vfs_context_t ctx = cnp->cn_context; 175 int lockparent = cnp->cn_flags & LOCKPARENT; 176 struct union_mount *um; 177 kauth_cred_t saved_cred; 178 int iswhiteout; 179 struct vnode_attr va; 180 int isfaultfs = 0; 181 int upperlookup = 0; 182 int retry_count = 0; 183 184#ifdef notyet 185 if (cnp->cn_namelen == 3 && 186 cnp->cn_nameptr[2] == '.' && 187 cnp->cn_nameptr[1] == '.' && 188 cnp->cn_nameptr[0] == '.') { 189 dvp = *ap->a_vpp = LOWERVP(ap->a_dvp); 190 if (dvp == NULLVP) 191 return (ENOENT); 192 vnode_get(dvp); 193 194 return (0); 195 } 196#endif 197 198 199 200retry: 201 union_lock(); 202 um = MOUNTTOUNIONMOUNT(dvp->v_mount); 203 dun = VTOUNION(dvp); 204 upperdvp = dun->un_uppervp; 205 lowerdvp = dun->un_lowervp; 206 uppervp = NULLVP; 207 lowervp = NULLVP; 208 iswhiteout = 0; 209 210 union_unlock(); 211 212 if(UNION_FAULTIN(um)) 213 isfaultfs = 1; 214 215 if (isfaultfs == 0) 216 cnp->cn_flags |= LOCKPARENT; 217 218 /* 219 * do the lookup in the upper level. 220 * if that level comsumes additional pathnames, 221 * then assume that something special is going 222 * on and just return that vnode. 223 */ 224 if (upperdvp != NULLVP) { 225 if (lockparent != 0) 226 cnp->cn_flags &= ~LOCKPARENT; 227 uerror = union_lookup1(um->um_uppervp, &upperdvp, 228 &uppervp, cnp); 229 upperlookup = 1; 230 231 if (cnp->cn_consume != 0) { 232 *ap->a_vpp = uppervp; 233 if (!lockparent) 234 cnp->cn_flags &= ~LOCKPARENT; 235 else 236 cnp->cn_flags |= LOCKPARENT; 237 return (uerror); 238 } 239 if (uerror == ENOENT || uerror == EJUSTRETURN) { 240 if (cnp->cn_flags & ISWHITEOUT) { 241 iswhiteout = 1; 242 } else if (lowerdvp != NULLVP) { 243 VATTR_INIT(&va); 244 VATTR_WANTED(&va, va_flags); 245 lerror = vnode_getattr(upperdvp, &va, ap->a_context); 246 if (lerror == 0 && (va.va_flags & OPAQUE)) 247 iswhiteout = 1; 248 } 249 } 250 } else { 251 uerror = ENOENT; 252 } 253 254 /* 255 * faultingfs: If upper layer lookup is succesful 256 * we will return that vp if it is regular file. 257 * So so skip lower level lookup 258 */ 259 260 if ((isfaultfs == 1) && (upperlookup == 1) && (uerror == 0) && ((vnode_isreg(uppervp) != 0))) 261 goto donelowerlookup; 262 263 /* 264 * in a similar way to the upper layer, do the lookup 265 * in the lower layer. this time, if there is some 266 * component magic going on, then vnode_put whatever we got 267 * back from the upper layer and return the lower vnode 268 * instead. 269 */ 270 if (lowerdvp != NULLVP && !iswhiteout) { 271 int nameiop; 272 273 /* 274 * Only do a LOOKUP on the bottom node, since 275 * we won't be making changes to it anyway. 276 */ 277 nameiop = cnp->cn_nameiop; 278 cnp->cn_nameiop = LOOKUP; 279 if (um->um_op == UNMNT_BELOW) { 280 /* XXX BOGUS */ 281 saved_cred = cnp->cn_context->vc_ucred; 282 cnp->cn_context->vc_ucred = um->um_cred; 283 if (lockparent != 0) 284 cnp->cn_flags &= ~LOCKPARENT; 285 lerror = union_lookup1(um->um_lowervp, &lowerdvp, 286 &lowervp, cnp); 287 cnp->cn_context->vc_ucred = saved_cred; 288 } else { 289 if (lockparent != 0) 290 cnp->cn_flags &= ~LOCKPARENT; 291 lerror = union_lookup1(um->um_lowervp, &lowerdvp, 292 &lowervp, cnp); 293 } 294 cnp->cn_nameiop = nameiop; 295 296 if (cnp->cn_consume != 0) { 297 if (uppervp != NULLVP) { 298 vnode_put(uppervp); 299 uppervp = NULLVP; 300 } 301 *ap->a_vpp = lowervp; 302 if (!lockparent) 303 cnp->cn_flags &= ~LOCKPARENT; 304 else 305 cnp->cn_flags |= LOCKPARENT; 306 return (lerror); 307 } 308 } else { 309 lerror = ENOENT; 310 if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) { 311 lowervp = LOWERVP(dun->un_pvp); 312 if (lowervp != NULLVP) { 313 lerror = 0; 314 } 315 } 316 } 317 318donelowerlookup: 319 320 if (!lockparent) 321 cnp->cn_flags &= ~LOCKPARENT; 322 323 /* 324 * at this point, we have uerror and lerror indicating 325 * possible errors with the lookups in the upper and lower 326 * layers. additionally, uppervp and lowervp are (locked) 327 * references to existing vnodes in the upper and lower layers. 328 * 329 * there are now three cases to consider. 330 * 1. if both layers returned an error, then return whatever 331 * error the upper layer generated. 332 * 333 * 2. if the top layer failed and the bottom layer succeeded 334 * then two subcases occur. 335 * a. the bottom vnode is not a directory, in which 336 * case just return a new union vnode referencing 337 * an empty top layer and the existing bottom layer. 338 * b. the bottom vnode is a directory, in which case 339 * create a new directory in the top-level and 340 * continue as in case 3. 341 * 342 * 3. if the top layer succeeded then return a new union 343 * vnode referencing whatever the new top layer and 344 * whatever the bottom layer returned. 345 */ 346 347 *ap->a_vpp = NULLVP; 348 349 /* case 1. */ 350 if ((uerror != 0) && (lerror != 0)) { 351 if (!lockparent) 352 cnp->cn_flags &= ~LOCKPARENT; 353 else 354 cnp->cn_flags |= LOCKPARENT; 355 return (uerror); 356 } 357 358 /* case 2. */ 359 if (uerror != 0 /* && (lerror == 0) */ ) { 360 if (lowervp->v_type == VDIR) { /* case 2b. */ 361 /* No need to lock the union here */ 362 /* if the vnode exists it returns it even if it marks error */ 363 364 uppervp = NULLVP; 365 366 uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); 367 368 if ((uerror == EEXIST)){ 369 if (uppervp == NULLVP) { 370 retry_count++; 371 if (retry_count <= 2) { 372 if (lowervp != NULLVP) 373 vnode_put(lowervp); 374 goto retry; 375 } 376 } 377 uerror = 0; 378 } 379 380 if (uerror) { 381 if (uppervp != NULLVP) { 382 vnode_put(uppervp); 383 } 384 if (lowervp != NULLVP) { 385 vnode_put(lowervp); 386 } 387 if (!lockparent) 388 cnp->cn_flags &= ~LOCKPARENT; 389 else 390 cnp->cn_flags |= LOCKPARENT; 391 return (uerror); 392 } 393 } else if ((lowervp->v_type == VREG) && (isfaultfs == 1)) { 394 error = union_faultin_copyup(&uppervp, upperdvp, lowervp, cnp, ctx); 395 uerror = 0; 396 } 397 } 398 399 400 /* if this is faulting filesystem and upper vp exisits skip allocation of union node */ 401 if ((isfaultfs == 1) && (uerror == 0) && (uppervp != NULLVP) && ((vnode_isreg(uppervp) != 0)|| (vnode_islnk(uppervp) != 0))) { 402 vn_checkunionwait(uppervp); 403 *ap->a_vpp = uppervp; 404 if (lowervp != NULLVP) 405 vnode_put(lowervp); 406 if (!lockparent) 407 cnp->cn_flags &= ~LOCKPARENT; 408 else 409 cnp->cn_flags |= LOCKPARENT; 410 return(0); 411 } 412 413 union_lock(); 414 error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, 415 uppervp, lowervp, 1); 416 union_unlock(); 417 418 if (error) { 419 if (uppervp != NULLVP) 420 vnode_put(uppervp); 421 if (lowervp != NULLVP) 422 vnode_put(lowervp); 423 } 424 425 if (!lockparent) 426 cnp->cn_flags &= ~LOCKPARENT; 427 else 428 cnp->cn_flags |= LOCKPARENT; 429 return (error); 430} 431 432static int 433union_create(struct vnop_create_args *ap) 434/* 435 struct vnop_create_args { 436 struct vnode *a_dvp; 437 struct vnode **a_vpp; 438 struct componentname *a_cnp; 439 struct vnode_attr *a_vap; 440 vfs_context_t a_context; 441 } *ap; 442*/ 443{ 444 struct union_node *un = VTOUNION(ap->a_dvp); 445 struct vnode *dvp = un->un_uppervp; 446 struct componentname *cnp = ap->a_cnp; 447 448 if (dvp != NULLVP) { 449 int error; 450 struct vnode *vp; 451 struct mount *mp; 452 453 454 mp = ap->a_dvp->v_mount; 455 456 /* note that this is a direct passthrough to the filesystem */ 457 error = VNOP_CREATE(dvp, &vp, cnp, ap->a_vap, ap->a_context); 458 if (error) 459 return (error); 460 461 /* if this is faulting filesystem and is a reg file, skip allocation of union node */ 462 if (UNNODE_FAULTIN(un) && (vp != NULLVP) && ((vnode_isreg(vp) != 0)|| (vnode_islnk(vp) != 0))) { 463 *ap->a_vpp = vp; 464 return(0); 465 } 466 467 468 union_lock(); 469 error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp, 470 NULLVP, 1); 471 union_unlock(); 472 if (error) 473 vnode_put(vp); 474 return (error); 475 } 476 477 return (EROFS); 478} 479 480static int 481union_whiteout(struct vnop_whiteout_args *ap) 482/* 483 struct vnop_whiteout_args { 484 struct vnode *a_dvp; 485 struct componentname *a_cnp; 486 int a_flags; 487 vfs_context_t a_context; 488 } *ap; 489*/ 490{ 491 struct union_node *un = VTOUNION(ap->a_dvp); 492 struct componentname *cnp = ap->a_cnp; 493 int error; 494 495 if (un->un_uppervp == NULLVP) { 496 return (ENOTSUP); 497 } 498 499 error = (VNOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags, ap->a_context)); 500 return(error); 501} 502 503/* mknod can do fifos, chr, blk or whiteout entries */ 504static int 505union_mknod(struct vnop_mknod_args *ap) 506/* 507 struct vnop_mknod_args { 508 struct vnode *a_dvp; 509 struct vnode **a_vpp; 510 struct componentname *a_cnp; 511 struct vnode_attr *a_vap; 512 vfs_context_t a_context; 513 } *ap; 514*/ 515{ 516 struct union_node *un = VTOUNION(ap->a_dvp); 517 struct vnode *dvp = un->un_uppervp; 518 struct componentname *cnp = ap->a_cnp; 519 520 if (dvp != NULLVP) { 521 int error; 522 struct vnode *vp; 523 struct mount *mp; 524 525 526 mp = ap->a_dvp->v_mount; 527 528 /* note that this is a direct passthrough to the filesystem */ 529 error = VNOP_MKNOD(dvp, &vp, cnp, ap->a_vap, ap->a_context); 530 if (error) 531 return (error); 532 533 if (vp != NULLVP) { 534 union_lock(); 535 error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, 536 cnp, vp, NULLVP, 1); 537 union_unlock(); 538 if (error) 539 vnode_put(vp); 540 } 541 return (error); 542 } 543 return (EROFS); 544} 545 546static int 547union_open(struct vnop_open_args *ap) 548/* 549 struct vnop_open_args { 550 struct vnodeop_desc *a_desc; 551 struct vnode *a_vp; 552 int a_mode; 553 vfs_context_t a_context; 554 } *ap; 555*/ 556{ 557 struct union_node *un = VTOUNION(ap->a_vp); 558 struct vnode *tvp; 559 int mode = ap->a_mode; 560 int error; 561 562 /* 563 * If there is an existing upper vp then simply open that. 564 */ 565 566 tvp = un->un_uppervp; 567 if (tvp == NULLVP) { 568 569 /* 570 * If the lower vnode is being opened for writing, then 571 * copy the file contents to the upper vnode and open that, 572 * otherwise can simply open the lower vnode. 573 */ 574 tvp = un->un_lowervp; 575 if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) { 576 /* For above below mounts we need draining.. */ 577 /* This path is not taken for faultin mode */ 578 /* LOCK the union node as well **/ 579 union_lock(); 580 un->un_flags |= UN_LOCKED; 581 582 error = union_copyup(un, (mode&O_TRUNC) == 0, ap->a_context); 583 un->un_flags &= ~UN_LOCKED; 584 if ((un->un_flags & UN_WANT) == UN_WANT) { 585 un->un_flags &= ~UN_WANT; 586 wakeup(&un->un_flags); 587 } 588 union_unlock(); 589 if (error == 0) 590 error = VNOP_OPEN(un->un_uppervp, mode, ap->a_context); 591 return (error); 592 } 593 594 /* 595 * Just open the lower vnode 596 */ 597 un->un_openl++; 598 599 error = VNOP_OPEN(tvp, mode, ap->a_context); 600 601 return (error); 602 } 603 604 error = VNOP_OPEN(tvp, mode, ap->a_context); 605 606 return (error); 607} 608 609static int 610union_close(struct vnop_close_args *ap) 611/* 612 struct vnop_close_args { 613 struct vnode *a_vp; 614 int a_fflag; 615 vfs_context_t a_context; 616 } *ap; 617*/ 618{ 619 struct union_node *un = VTOUNION(ap->a_vp); 620 struct vnode *vp; 621 int error = 0; 622 623 if ((vp = un->un_uppervp) == NULLVP) { 624#ifdef UNION_DIAGNOSTIC 625 if (un->un_openl <= 0) 626 panic("union: un_openl cnt"); 627#endif 628 --un->un_openl; 629 vp = un->un_lowervp; 630 } 631 632 ap->a_vp = vp; 633 error = (VCALL(vp, VOFFSET(vnop_close), ap)); 634 return(error); 635} 636 637/* 638 * Check access permission on the union vnode. 639 * The access check being enforced is to check 640 * against both the underlying vnode, and any 641 * copied vnode. This ensures that no additional 642 * file permissions are given away simply because 643 * the user caused an implicit file copy. 644 */ 645static int 646union_access(struct vnop_access_args *ap) 647/* 648 struct vnop_access_args { 649 struct vnodeop_desc *a_desc; 650 struct vnode *a_vp; 651 int a_action; 652 vfs_context_t a_context; 653 } *ap; 654*/ 655{ 656 struct union_node *un = VTOUNION(ap->a_vp); 657 int error = EACCES; 658 struct vnode *vp; 659 660 if ((vp = un->un_uppervp) != NULLVP) { 661 ap->a_vp = vp; 662 return (VCALL(vp, VOFFSET(vnop_access), ap)); 663 } 664 665 if ((vp = un->un_lowervp) != NULLVP) { 666 ap->a_vp = vp; 667 error = VCALL(vp, VOFFSET(vnop_access), ap); 668 if (error == 0) { 669 struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount); 670 671 if (um->um_op == UNMNT_BELOW) { 672 error = VCALL(vp, VOFFSET(vnop_access), ap); 673 } 674 } 675 if (error) 676 return (error); 677 } 678 679 return (error); 680} 681 682/* 683 * We handle getattr only to change the fsid and 684 * track object sizes 685 */ 686static int 687union_getattr(struct vnop_getattr_args *ap) 688/* 689 struct vnop_getattr_args { 690 struct vnode *a_vp; 691 struct vnode_attr *a_vap; 692 vfs_context_t a_context; 693 } *ap; 694*/ 695{ 696 int error=0; 697 struct union_node *un = VTOUNION(ap->a_vp); 698 struct vnode *vp = un->un_uppervp; 699 struct vnode_attr *vap; 700 struct vnode_attr va; 701 702 703 /* 704 * Some programs walk the filesystem hierarchy by counting 705 * links to directories to avoid stat'ing all the time. 706 * This means the link count on directories needs to be "correct". 707 * The only way to do that is to call getattr on both layers 708 * and fix up the link count. The link count will not necessarily 709 * be accurate but will be large enough to defeat the tree walkers. 710 */ 711 712 vap = ap->a_vap; 713 714 vp = un->un_uppervp; 715 if (vp != NULLVP) { 716 /* 717 * It's not clear whether vnop_getattr is to be 718 * called with the vnode locked or not. stat() calls 719 * it with (vp) locked, and fstat calls it with 720 * (vp) unlocked. 721 * In the mean time, compensate here by checking 722 * the union_node's lock flag. 723 */ 724 725 error = vnode_getattr(vp, vap, ap->a_context); 726 if (error) { 727 return (error); 728 } 729 union_lock(); 730 union_newsize(ap->a_vp, vap->va_data_size, VNOVAL); 731 union_unlock(); 732 } 733 734 if (vp == NULLVP) { 735 vp = un->un_lowervp; 736 } else if (vp->v_type == VDIR) { 737 vp = un->un_lowervp; 738 VATTR_INIT(&va); 739 /* all we want from the lower node is the link count */ 740 VATTR_WANTED(&va, va_nlink); 741 vap = &va; 742 } else { 743 vp = NULLVP; 744 } 745 746 if (vp != NULLVP) { 747 error = vnode_getattr(vp, vap, ap->a_context); 748 if (error) { 749 return (error); 750 } 751 union_lock(); 752 union_newsize(ap->a_vp, VNOVAL, vap->va_data_size); 753 union_unlock(); 754 } 755 756 if ((vap != ap->a_vap) && (vap->va_type == VDIR)) 757 ap->a_vap->va_nlink += vap->va_nlink; 758 759 VATTR_RETURN(ap->a_vap, va_fsid, ap->a_vp->v_mount->mnt_vfsstat.f_fsid.val[0]); 760 return (0); 761} 762 763static int 764union_setattr(struct vnop_setattr_args *ap) 765/* 766 struct vnop_setattr_args { 767 struct vnode *a_vp; 768 struct vnode_attr *a_vap; 769 vfs_context_t a_context; 770 } *ap; 771*/ 772{ 773 struct union_node *un = VTOUNION(ap->a_vp); 774 int error; 775 776 /* 777 * Handle case of truncating lower object to zero size, 778 * by creating a zero length upper object. This is to 779 * handle the case of open with O_TRUNC and O_CREAT. 780 */ 781 if (VATTR_IS_ACTIVE(ap->a_vap, va_data_size) && 782 (un->un_uppervp == NULLVP) && 783 /* assert(un->un_lowervp != NULLVP) */ 784 (un->un_lowervp->v_type == VREG)) { 785 union_lock(); 786 error = union_copyup(un, (ap->a_vap->va_data_size != 0), ap->a_context); 787 union_unlock(); 788 if (error) { 789 return (error); 790 } 791 } 792 793 /* 794 * Try to set attributes in upper layer, 795 * otherwise return read-only filesystem error. 796 */ 797 if (un->un_uppervp != NULLVP) { 798 error = vnode_setattr(un->un_uppervp, ap->a_vap, ap->a_context); 799 if ((error == 0) && VATTR_IS_ACTIVE(ap->a_vap, va_data_size)) { 800 union_lock(); 801 union_newsize(ap->a_vp, ap->a_vap->va_data_size, VNOVAL); 802 union_unlock(); 803 } 804 } else { 805 error = EROFS; 806 } 807 808 return (error); 809} 810 811static int 812union_read(struct vnop_read_args *ap) 813/* 814 struct vnop_read_args { 815 struct vnode *a_vp; 816 struct uio *a_uio; 817 int a_ioflag; 818 vfs_context_t a_context; 819 } *ap; 820*/ 821{ 822 int error; 823 struct vnode *vp = OTHERVP(ap->a_vp); 824 825 error = VNOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_context); 826 827 /* 828 * XXX 829 * perhaps the size of the underlying object has changed under 830 * our feet. take advantage of the offset information present 831 * in the uio structure. 832 */ 833 if (error == 0) { 834 struct union_node *un = VTOUNION(ap->a_vp); 835 off_t cur = ap->a_uio->uio_offset; 836 837 if (vp == un->un_uppervp) { 838 if (cur > un->un_uppersz) { 839 union_lock(); 840 union_newsize(ap->a_vp, cur, VNOVAL); 841 union_unlock(); 842 } 843 } else { 844 if (cur > un->un_lowersz) { 845 union_lock(); 846 union_newsize(ap->a_vp, VNOVAL, cur); 847 union_unlock(); 848 } 849 } 850 } 851 852 return (error); 853} 854 855static int 856union_write(struct vnop_write_args *ap) 857/* 858 struct vnop_write_args { 859 struct vnode *a_vp; 860 struct uio *a_uio; 861 int a_ioflag; 862 vfs_context_t a_context; 863 } *ap; 864*/ 865{ 866 int error; 867 struct vnode *vp; 868 struct union_node *un = VTOUNION(ap->a_vp); 869 870 vp = UPPERVP(ap->a_vp); 871 if (vp == NULLVP) 872 panic("union: missing upper layer in write"); 873 874 error = VNOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_context); 875 876 /* 877 * the size of the underlying object may be changed by the 878 * write. 879 */ 880 if (error == 0) { 881 off_t cur = ap->a_uio->uio_offset; 882 883 if (cur > un->un_uppersz) { 884 union_lock(); 885 union_newsize(ap->a_vp, cur, VNOVAL); 886 union_unlock(); 887 } 888 } 889 890 return (error); 891} 892 893 894static int 895union_ioctl(struct vnop_ioctl_args *ap) 896/* 897 struct vnop_ioctl_args { 898 struct vnode *a_vp; 899 int a_command; 900 caddr_t a_data; 901 int a_fflag; 902 vfs_context_t a_context; 903 } *ap; 904*/ 905{ 906 register struct vnode *ovp = OTHERVP(ap->a_vp); 907 908 ap->a_vp = ovp; 909 return (VCALL(ovp, VOFFSET(vnop_ioctl), ap)); 910} 911 912static int 913union_select(struct vnop_select_args *ap) 914/* 915 struct vnop_select_args { 916 struct vnode *a_vp; 917 int a_which; 918 int a_fflags; 919 void * a_wql; 920 vfs_context_t a_context; 921 } *ap; 922*/ 923{ 924 register struct vnode *ovp = OTHERVP(ap->a_vp); 925 926 ap->a_vp = ovp; 927 return (VCALL(ovp, VOFFSET(vnop_select), ap)); 928} 929 930static int 931union_revoke(struct vnop_revoke_args *ap) 932/* 933 struct vnop_revoke_args { 934 struct vnode *a_vp; 935 int a_flags; 936 vfs_context_t a_context; 937 } *ap; 938*/ 939{ 940 struct vnode *vp = ap->a_vp; 941 942 if (UPPERVP(vp)) 943 VNOP_REVOKE(UPPERVP(vp), ap->a_flags, ap->a_context); 944 if (LOWERVP(vp)) 945 VNOP_REVOKE(LOWERVP(vp), ap->a_flags, ap->a_context); 946 vnode_reclaim(vp); 947 948 return (0); 949} 950 951static int 952union_mmap(struct vnop_mmap_args *ap) 953/* 954 struct vnop_mmap_args { 955 struct vnode *a_vp; 956 int a_fflags; 957 kauth_cred_t a_cred; 958 struct proc *a_p; 959 } *ap; 960*/ 961{ 962 register struct vnode *ovp = OTHERVP(ap->a_vp); 963 964 ap->a_vp = ovp; 965 return (VCALL(ovp, VOFFSET(vnop_mmap), ap)); 966} 967 968static int 969union_mnomap(struct vnop_mnomap_args *ap) 970/* 971 struct vnop_mnomap_args { 972 struct vnode *a_vp; 973 int a_fflags; 974 kauth_cred_t a_cred; 975 struct proc *a_p; 976 } *ap; 977*/ 978{ 979 register struct vnode *ovp = OTHERVP(ap->a_vp); 980 981 ap->a_vp = ovp; 982 return (VCALL(ovp, VOFFSET(vnop_mnomap), ap)); 983} 984 985static int 986union_fsync(struct vnop_fsync_args *ap) 987/* 988 struct vnop_fsync_args { 989 struct vnode *a_vp; 990 int a_waitfor; 991 vfs_context_t a_context; 992 } *ap; 993*/ 994{ 995 int error = 0; 996 struct vnode *targetvp = OTHERVP(ap->a_vp); 997 998 if (targetvp != NULLVP) { 999 1000 error = VNOP_FSYNC(targetvp, ap->a_waitfor, ap->a_context); 1001 } 1002 1003 return (error); 1004} 1005 1006static int 1007union_remove(struct vnop_remove_args *ap) 1008/* 1009 struct vnop_remove_args { 1010 struct vnode *a_dvp; 1011 struct vnode *a_vp; 1012 struct componentname *a_cnp; 1013 vfs_context_t a_context; 1014 } *ap; 1015*/ 1016{ 1017 int error, flags; 1018 struct union_node *dun = VTOUNION(ap->a_dvp); 1019 struct union_node *un = VTOUNION(ap->a_vp); 1020 struct componentname *cnp = ap->a_cnp; 1021 int busydel = 0; 1022 1023 if (dun->un_uppervp == NULLVP) 1024 panic("union remove: null upper vnode"); 1025 1026 if (UNNODE_FAULTIN(dun) && ((ap->a_vp != NULLVP) && 1027 ((vnode_isreg(ap->a_vp) != 0) || (vnode_islnk(ap->a_vp) != 0)))) { 1028 return(VNOP_REMOVE(dun->un_uppervp, ap->a_vp, ap->a_cnp, ap->a_flags, ap->a_context)); 1029 } 1030 1031 if (un->un_uppervp != NULLVP) { 1032 struct vnode *dvp = dun->un_uppervp; 1033 struct vnode *vp = un->un_uppervp; 1034 1035 flags = ap->a_flags; 1036 if (vnode_isinuse(ap->a_vp, 0)) 1037 busydel = 1; 1038 if ((flags & VNODE_REMOVE_NODELETEBUSY) && (busydel != 0)) { 1039 return(EBUSY); 1040 } 1041 if (union_dowhiteout(un, cnp->cn_context)) 1042 cnp->cn_flags |= DOWHITEOUT; 1043 1044 if (busydel != 0) { 1045 union_lock(); 1046 un->un_flags |= UN_DELETED; 1047 if (un->un_flags & UN_CACHED) { 1048 un->un_flags &= ~UN_CACHED; 1049 LIST_REMOVE(un, un_cache); 1050 } 1051 union_unlock(); 1052 vnode_ref(vp); 1053 } 1054 error = VNOP_REMOVE(dvp, vp, cnp, 0, ap->a_context); 1055 if (!error) { 1056 union_lock(); 1057 if (busydel == 0) 1058 union_removed_upper(un); 1059 union_unlock(); 1060 } 1061 } else { 1062 if (UNNODE_FAULTIN(un)) 1063 panic("faultfs: No uppervp"); 1064 error = union_mkwhiteout( 1065 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 1066 dun->un_uppervp, ap->a_cnp, un->un_path); 1067 } 1068 1069 return (error); 1070} 1071 1072static int 1073union_link(struct vnop_link_args *ap) 1074/* 1075 struct vnop_link_args { 1076 struct vnode *a_vp; 1077 struct vnode *a_tdvp; 1078 struct componentname *a_cnp; 1079 vfs_context_t a_context; 1080 } *ap; 1081*/ 1082{ 1083 int error = 0; 1084 struct componentname *cnp = ap->a_cnp; 1085 struct union_node *un; 1086 struct vnode *vp; 1087 struct vnode *tdvp; 1088 1089 un = VTOUNION(ap->a_tdvp); 1090 1091 if (ap->a_tdvp->v_op != ap->a_vp->v_op) { 1092 vp = ap->a_vp; 1093 } else { 1094 struct union_node *tun = VTOUNION(ap->a_vp); 1095 if (tun->un_uppervp == NULLVP) { 1096 if (UNNODE_FAULTIN(tun)) 1097 panic("faultfs: No uppervp"); 1098 if (un->un_uppervp == tun->un_dirvp) { 1099 } 1100 union_lock(); 1101 /* Would need to drain for above,below mount and faulin does not enter this path */ 1102 un->un_flags |= UN_LOCKED; 1103 error = union_copyup(tun, 1, ap->a_context); 1104 un->un_flags &= ~UN_LOCKED; 1105 if ((un->un_flags & UN_WANT) == UN_WANT) { 1106 un->un_flags &= ~UN_WANT; 1107 wakeup(&un->un_flags); 1108 } 1109 union_unlock(); 1110 } 1111 vp = tun->un_uppervp; 1112 } 1113 tdvp = un->un_uppervp; 1114 if (tdvp == NULLVP) 1115 error = EROFS; 1116 1117 if (error) { 1118 return (error); 1119 } 1120 1121 1122 error = (VNOP_LINK(vp, tdvp, cnp, ap->a_context)); 1123 return(error); 1124} 1125 1126static int 1127union_rename(struct vnop_rename_args *ap) 1128/* 1129 struct vnop_rename_args { 1130 struct vnode *a_fdvp; 1131 struct vnode *a_fvp; 1132 struct componentname *a_fcnp; 1133 struct vnode *a_tdvp; 1134 struct vnode *a_tvp; 1135 struct componentname *a_tcnp; 1136 vfs_context_t a_context; 1137 } *ap; 1138*/ 1139{ 1140 int error; 1141 1142 struct vnode *fdvp = ap->a_fdvp; 1143 struct vnode *fvp = ap->a_fvp; 1144 struct vnode *tdvp = ap->a_tdvp; 1145 struct vnode *tvp = ap->a_tvp; 1146 1147 1148 if (fdvp->v_op == union_vnodeop_p) { /* always true */ 1149 struct union_node *un = VTOUNION(fdvp); 1150 if (un->un_uppervp == NULLVP) { 1151 if (UNNODE_FAULTIN(un)) 1152 panic("faultfs rename: No uppervp"); 1153 /* 1154 * this should never happen in normal 1155 * operation but might if there was 1156 * a problem creating the top-level shadow 1157 * directory. 1158 */ 1159 error = EXDEV; 1160 goto bad; 1161 } 1162 1163 fdvp = un->un_uppervp; 1164 } 1165 1166 if (fvp->v_op == union_vnodeop_p) { /* always true */ 1167 struct union_node *un = VTOUNION(fvp); 1168 if (un->un_uppervp == NULLVP) { 1169 if (UNNODE_FAULTIN(un)) 1170 panic("faultfs rename: No uppervp"); 1171 /* XXX: should do a copyup */ 1172 error = EXDEV; 1173 goto bad; 1174 } 1175 1176 if (un->un_lowervp != NULLVP) 1177 ap->a_fcnp->cn_flags |= DOWHITEOUT; 1178 1179 fvp = un->un_uppervp; 1180 } 1181 1182 if (tdvp->v_op == union_vnodeop_p) { 1183 struct union_node *un = VTOUNION(tdvp); 1184 if (un->un_uppervp == NULLVP) { 1185 /* 1186 * this should never happen in normal 1187 * operation but might if there was 1188 * a problem creating the top-level shadow 1189 * directory. 1190 */ 1191 if (UNNODE_FAULTIN(un)) 1192 panic("faultfs rename: No uppervp"); 1193 error = EXDEV; 1194 goto bad; 1195 } 1196 1197 tdvp = un->un_uppervp; 1198 } 1199 1200 if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { 1201 struct union_node *un = VTOUNION(tvp); 1202 1203 tvp = un->un_uppervp; 1204 } 1205 1206 return (VNOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp, ap->a_context)); 1207 1208bad: 1209 return (error); 1210} 1211 1212static int 1213union_mkdir(struct vnop_mkdir_args *ap) 1214/* 1215 struct vnop_mkdir_args { 1216 struct vnode *a_dvp; 1217 struct vnode **a_vpp; 1218 struct componentname *a_cnp; 1219 struct vnode_attr *a_vap; 1220 vfs_context_t a_context; 1221 } *ap; 1222*/ 1223{ 1224 struct union_node *un = VTOUNION(ap->a_dvp); 1225 struct vnode *dvp = un->un_uppervp; 1226 struct componentname *cnp = ap->a_cnp; 1227 1228 if (dvp != NULLVP) { 1229 int error; 1230 struct vnode *vp; 1231 1232 1233 /* note that this is a direct fallthrough to the filesystem */ 1234 error = VNOP_MKDIR(dvp, &vp, cnp, ap->a_vap, ap->a_context); 1235 if (error) 1236 return (error); 1237 1238 union_lock(); 1239 error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, 1240 NULLVP, cnp, vp, NULLVP, 1); 1241 union_unlock(); 1242 if (error) 1243 vnode_put(vp); 1244 return (error); 1245 } 1246 return (EROFS); 1247} 1248 1249static int 1250union_rmdir(struct vnop_rmdir_args *ap) 1251/* 1252 struct vnop_rmdir_args { 1253 struct vnode *a_dvp; 1254 struct vnode *a_vp; 1255 struct componentname *a_cnp; 1256 vfs_context_t a_context; 1257 } *ap; 1258*/ 1259{ 1260 int error; 1261 struct union_node *dun = VTOUNION(ap->a_dvp); 1262 struct union_node *un = VTOUNION(ap->a_vp); 1263 struct componentname *cnp = ap->a_cnp; 1264 int busydel = 0; 1265 1266 /******* NODE HAS TO BE LOCKED ******/ 1267 if (dun->un_uppervp == NULLVP) 1268 panic("union rmdir: null upper vnode"); 1269 1270 if (un->un_uppervp != NULLVP) { 1271 struct vnode *dvp = dun->un_uppervp; 1272 struct vnode *vp = un->un_uppervp; 1273 1274 if (vnode_isinuse(ap->a_vp, 0)) { 1275 busydel = 1; 1276 union_lock(); 1277 un->un_flags |= UN_DELETED; 1278 if (un->un_flags & UN_CACHED) { 1279 un->un_flags &= ~UN_CACHED; 1280 LIST_REMOVE(un, un_cache); 1281 } 1282 union_unlock(); 1283 vnode_ref(vp); 1284 } 1285 1286 1287 if (union_dowhiteout(un, cnp->cn_context)) 1288 cnp->cn_flags |= DOWHITEOUT; 1289 error = VNOP_RMDIR(dvp, vp, ap->a_cnp, ap->a_context); 1290 if (!error) { 1291 union_lock(); 1292 if (busydel == 0) 1293 union_removed_upper(un); 1294 union_unlock(); 1295 } 1296 } else { 1297 if (UNNODE_FAULTIN(un)) 1298 panic("faultfs: No uppervp"); 1299 error = union_mkwhiteout( 1300 MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), 1301 dun->un_uppervp, ap->a_cnp, un->un_path); 1302 } 1303 return (error); 1304} 1305 1306static int 1307union_symlink(struct vnop_symlink_args *ap) 1308/* 1309 struct vnop_symlink_args { 1310 struct vnode *a_dvp; 1311 struct vnode **a_vpp; 1312 struct componentname *a_cnp; 1313 struct vnode_attr *a_vap; 1314 char *a_target; 1315 vfs_context_t a_context; 1316 } *ap; 1317*/ 1318{ 1319 struct union_node *un = VTOUNION(ap->a_dvp); 1320 struct vnode *dvp = un->un_uppervp; 1321 struct componentname *cnp = ap->a_cnp; 1322 1323 if (dvp != NULLVP) { 1324 int error; 1325 struct vnode *vp; 1326 1327 error = VNOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target, ap->a_context); 1328 *ap->a_vpp = vp; 1329 return (error); 1330 } 1331 return (EROFS); 1332} 1333 1334/* 1335 * union_readdir works in concert with getdirentries and 1336 * readdir(3) to provide a list of entries in the unioned 1337 * directories. getdirentries is responsible for walking 1338 * down the union stack. readdir(3) is responsible for 1339 * eliminating duplicate names from the returned data stream. 1340 */ 1341static int 1342union_readdir(struct vnop_readdir_args *ap) 1343/* 1344 struct vnop_readdir_args { 1345 struct vnodeop_desc *a_desc; 1346 struct vnode *a_vp; 1347 struct uio *a_uio; 1348 int a_flags; 1349 int *a_eofflag; 1350 int *a_numdirent; 1351 vfs_context_t a_context; 1352 } *ap; 1353*/ 1354{ 1355 struct union_node *un = VTOUNION(ap->a_vp); 1356 struct vnode *uvp = un->un_uppervp; 1357 1358 if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) 1359 return (EINVAL); 1360 1361 if (uvp == NULLVP) 1362 return (0); 1363 1364 ap->a_vp = uvp; 1365 return (VCALL(uvp, VOFFSET(vnop_readdir), ap)); 1366} 1367 1368static int 1369union_readlink(struct vnop_readlink_args *ap) 1370/* 1371 struct vnop_readlink_args { 1372 struct vnode *a_vp; 1373 struct uio *a_uio; 1374 vfs_context_t a_context; 1375 } *ap; 1376*/ 1377{ 1378 int error; 1379 struct vnode *vp = OTHERVP(ap->a_vp); 1380 1381 ap->a_vp = vp; 1382 error = VCALL(vp, VOFFSET(vnop_readlink), ap); 1383 1384 return (error); 1385} 1386 1387static int 1388union_inactive(struct vnop_inactive_args *ap) 1389/* 1390 struct vnop_inactive_args { 1391 struct vnode *a_vp; 1392 vfs_context_t a_context; 1393 } *ap; 1394*/ 1395{ 1396 struct vnode *vp = ap->a_vp; 1397 struct union_node *un = VTOUNION(vp); 1398 1399 /* 1400 * Do nothing (and _don't_ bypass). 1401 * Wait to vnode_put lowervp until reclaim, 1402 * so that until then our union_node is in the 1403 * cache and reusable. 1404 * 1405 * NEEDSWORK: Someday, consider inactive'ing 1406 * the lowervp and then trying to reactivate it 1407 * with capabilities (v_id) 1408 * like they do in the name lookup cache code. 1409 * That's too much work for now. 1410 */ 1411 1412 union_lock(); 1413 if (un->un_flags & UN_DELETED) { 1414 if(un->un_uppervp != NULLVP) { 1415 vnode_rele(un->un_uppervp); 1416 } 1417 union_removed_upper(un); 1418 } 1419 1420 if (un->un_dircache != 0) { 1421 union_dircache_free(un); 1422 } 1423 if (un->un_flags & UN_DIRENVN) { 1424 vnode_recycle(vp); 1425 } 1426 1427 union_unlock(); 1428 1429 return (0); 1430} 1431 1432static int 1433union_reclaim(struct vnop_reclaim_args *ap) 1434/* 1435 struct vnop_reclaim_args { 1436 struct vnode *a_vp; 1437 vfs_context_t a_context; 1438 } *ap; 1439*/ 1440{ 1441 1442 union_lock(); 1443 union_freevp(ap->a_vp); 1444 union_unlock(); 1445 1446 return (0); 1447} 1448 1449static int 1450union_blockmap(struct vnop_blockmap_args *ap) 1451/* 1452 struct vnop_blockmap_args { 1453 struct vnode *a_vp; 1454 off_t a_offset; 1455 size_t a_size; 1456 daddr64_t *a_bpn; 1457 size_t *a_run; 1458 void *a_poff; 1459 int a_flags; 1460 } *ap; 1461*/ 1462{ 1463 int error; 1464 struct vnode *vp = OTHERVP(ap->a_vp); 1465 1466 ap->a_vp = vp; 1467 error = VCALL(vp, VOFFSET(vnop_blockmap), ap); 1468 1469 return (error); 1470} 1471 1472static int 1473union_pathconf(struct vnop_pathconf_args *ap) 1474/* 1475 struct vnop_pathconf_args { 1476 struct vnode *a_vp; 1477 int a_name; 1478 int *a_retval; 1479 vfs_context_t a_context; 1480 } *ap; 1481*/ 1482{ 1483 int error; 1484 struct vnode *vp = OTHERVP(ap->a_vp); 1485 1486 ap->a_vp = vp; 1487 error = VCALL(vp, VOFFSET(vnop_pathconf), ap); 1488 1489 return (error); 1490} 1491 1492static int 1493union_advlock(struct vnop_advlock_args *ap) 1494/* 1495 struct vnop_advlock_args { 1496 struct vnode *a_vp; 1497 caddr_t a_id; 1498 int a_op; 1499 struct flock *a_fl; 1500 int a_flags; 1501 vfs_context_t a_context; 1502 } *ap; 1503*/ 1504{ 1505 register struct vnode *ovp = OTHERVP(ap->a_vp); 1506 1507 ap->a_vp = ovp; 1508 return (VCALL(ovp, VOFFSET(vnop_advlock), ap)); 1509} 1510 1511 1512/* 1513 * XXX - vnop_strategy must be hand coded because it has no 1514 * vnode in its arguments. 1515 * This goes away with a merged VM/buffer cache. 1516 */ 1517static int 1518union_strategy(struct vnop_strategy_args *ap) 1519/* 1520 struct vnop_strategy_args { 1521 struct buf *a_bp; 1522 } *ap; 1523*/ 1524{ 1525 struct buf *bp = ap->a_bp; 1526 int error; 1527 struct vnode *savedvp; 1528 1529 savedvp = buf_vnode(bp); 1530 buf_setvnode(bp, OTHERVP(savedvp)); 1531 1532#if DIAGNOSTIC 1533 if (buf_vnode(bp) == NULLVP) 1534 panic("union_strategy: nil vp"); 1535 if (((buf_flags(bp) & B_READ) == 0) && 1536 (buf_vnode(bp) == LOWERVP(savedvp))) 1537 panic("union_strategy: writing to lowervp"); 1538#endif 1539 1540 error = VNOP_STRATEGY(bp); 1541 buf_setvnode(bp, savedvp); 1542 1543 return (error); 1544} 1545 1546/* Pagein */ 1547static int 1548union_pagein(struct vnop_pagein_args *ap) 1549/* 1550 struct vnop_pagein_args { 1551 struct vnode *a_vp, 1552 upl_t a_pl, 1553 vm_offset_t a_pl_offset, 1554 off_t a_f_offset, 1555 size_t a_size, 1556 int a_flags 1557 vfs_context_t a_context; 1558 } *ap; 1559*/ 1560{ 1561 int error; 1562 struct vnode *vp = OTHERVP(ap->a_vp); 1563 1564 error = VNOP_PAGEIN(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset, 1565 ap->a_size, ap->a_flags, ap->a_context); 1566 1567 /* 1568 * XXX 1569 * perhaps the size of the underlying object has changed under 1570 * our feet. take advantage of the offset information present 1571 * in the uio structure. 1572 */ 1573 if (error == 0) { 1574 struct union_node *un = VTOUNION(ap->a_vp); 1575 off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset; 1576 1577 if (vp == un->un_uppervp) { 1578 if (cur > un->un_uppersz) { 1579 union_lock(); 1580 union_newsize(ap->a_vp, cur, VNOVAL); 1581 union_unlock(); 1582 } 1583 } else { 1584 if (cur > un->un_lowersz) { 1585 union_lock(); 1586 union_newsize(ap->a_vp, VNOVAL, cur); 1587 union_unlock(); 1588 } 1589 } 1590 } 1591 1592 return (error); 1593} 1594 1595/* Pageout */ 1596static int 1597union_pageout(struct vnop_pageout_args *ap) 1598/* 1599 struct vnop_pageout_args { 1600 struct vnode *a_vp, 1601 upl_t a_pl, 1602 vm_offset_t a_pl_offset, 1603 off_t a_f_offset, 1604 size_t a_size, 1605 int a_flags 1606 vfs_context_t a_context; 1607 } *ap; 1608*/ 1609{ 1610 int error; 1611 struct vnode *vp; 1612 struct union_node *un = VTOUNION(ap->a_vp); 1613 1614 vp = UPPERVP(ap->a_vp); 1615 if (vp == NULLVP) 1616 panic("union: missing upper layer in pageout"); 1617 1618 error = VNOP_PAGEOUT(vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset, 1619 ap->a_size, ap->a_flags, ap->a_context); 1620 1621 /* 1622 * the size of the underlying object may be changed by the 1623 * write. 1624 */ 1625 if (error == 0) { 1626 off_t cur = ap->a_f_offset + (off_t)ap->a_pl_offset; 1627 1628 if (cur > un->un_uppersz) { 1629 union_lock(); 1630 union_newsize(ap->a_vp, cur, VNOVAL); 1631 union_unlock(); 1632 } 1633 } 1634 1635 return (error); 1636} 1637 1638/* Blktooff derives file offset for the given logical block number */ 1639static int 1640union_blktooff(struct vnop_blktooff_args *ap) 1641/* 1642 struct vnop_blktooff_args { 1643 struct vnode *a_vp; 1644 daddr64_t a_lblkno; 1645 off_t *a_offset; 1646 } *ap; 1647*/ 1648{ 1649 int error; 1650 struct vnode *vp = OTHERVP(ap->a_vp); 1651 1652 error = VNOP_BLKTOOFF(vp, ap->a_lblkno, ap->a_offset); 1653 1654 return(error); 1655} 1656 1657/* offtoblk derives file offset for the given logical block number */ 1658static int 1659union_offtoblk(struct vnop_offtoblk_args *ap) 1660/* 1661 struct vnop_offtoblk_args { 1662 struct vnode *a_vp; 1663 off_t a_offset; 1664 daddr64_t *a_lblkno; 1665 } *ap; 1666*/ 1667{ 1668 int error; 1669 struct vnode *vp = OTHERVP(ap->a_vp); 1670 1671 error = VNOP_OFFTOBLK(vp, ap->a_offset, ap->a_lblkno); 1672 1673 return(error); 1674} 1675 1676#define VOPFUNC int (*)(void *) 1677 1678/* 1679 * Global vfs data structures 1680 */ 1681int (**union_vnodeop_p)(void *); 1682struct vnodeopv_entry_desc union_vnodeop_entries[] = { 1683 { &vnop_default_desc, (VOPFUNC)vn_default_error }, 1684 { &vnop_lookup_desc, (VOPFUNC)union_lookup }, /* lookup */ 1685 { &vnop_create_desc, (VOPFUNC)union_create }, /* create */ 1686 { &vnop_whiteout_desc, (VOPFUNC)union_whiteout }, /* whiteout */ 1687 { &vnop_mknod_desc, (VOPFUNC)union_mknod }, /* mknod */ 1688 { &vnop_open_desc, (VOPFUNC)union_open }, /* open */ 1689 { &vnop_close_desc, (VOPFUNC)union_close }, /* close */ 1690 { &vnop_access_desc, (VOPFUNC)union_access }, /* access */ 1691 { &vnop_getattr_desc, (VOPFUNC)union_getattr }, /* getattr */ 1692 { &vnop_setattr_desc, (VOPFUNC)union_setattr }, /* setattr */ 1693 { &vnop_read_desc, (VOPFUNC)union_read }, /* read */ 1694 { &vnop_write_desc, (VOPFUNC)union_write }, /* write */ 1695 { &vnop_ioctl_desc, (VOPFUNC)union_ioctl }, /* ioctl */ 1696 { &vnop_select_desc, (VOPFUNC)union_select }, /* select */ 1697 { &vnop_revoke_desc, (VOPFUNC)union_revoke }, /* revoke */ 1698 { &vnop_mmap_desc, (VOPFUNC)union_mmap }, /* mmap */ 1699 { &vnop_mnomap_desc, (VOPFUNC)union_mnomap }, /* mnomap */ 1700 { &vnop_fsync_desc, (VOPFUNC)union_fsync }, /* fsync */ 1701 { &vnop_remove_desc, (VOPFUNC)union_remove }, /* remove */ 1702 { &vnop_link_desc, (VOPFUNC)union_link }, /* link */ 1703 { &vnop_rename_desc, (VOPFUNC)union_rename }, /* rename */ 1704 { &vnop_mkdir_desc, (VOPFUNC)union_mkdir }, /* mkdir */ 1705 { &vnop_rmdir_desc, (VOPFUNC)union_rmdir }, /* rmdir */ 1706 { &vnop_symlink_desc, (VOPFUNC)union_symlink }, /* symlink */ 1707 { &vnop_readdir_desc, (VOPFUNC)union_readdir }, /* readdir */ 1708 { &vnop_readlink_desc, (VOPFUNC)union_readlink }, /* readlink */ 1709 { &vnop_inactive_desc, (VOPFUNC)union_inactive }, /* inactive */ 1710 { &vnop_reclaim_desc, (VOPFUNC)union_reclaim }, /* reclaim */ 1711 { &vnop_strategy_desc, (VOPFUNC)union_strategy }, /* strategy */ 1712 { &vnop_pathconf_desc, (VOPFUNC)union_pathconf }, /* pathconf */ 1713 { &vnop_advlock_desc, (VOPFUNC)union_advlock }, /* advlock */ 1714#ifdef notdef 1715 { &vnop_bwrite_desc, (VOPFUNC)union_bwrite }, /* bwrite */ 1716#endif 1717 { &vnop_pagein_desc, (VOPFUNC)union_pagein }, /* Pagein */ 1718 { &vnop_pageout_desc, (VOPFUNC)union_pageout }, /* Pageout */ 1719 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ 1720 { &vnop_blktooff_desc, (VOPFUNC)union_blktooff }, /* blktooff */ 1721 { &vnop_offtoblk_desc, (VOPFUNC)union_offtoblk }, /* offtoblk */ 1722 { &vnop_blockmap_desc, (VOPFUNC)union_blockmap }, /* blockmap */ 1723 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 1724}; 1725struct vnodeopv_desc union_vnodeop_opv_desc = 1726 { &union_vnodeop_p, union_vnodeop_entries }; 1727