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. 5 * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc. 6 * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org> 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Jan-Simon Pendry. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95 37 * $FreeBSD: src/sys/fs/unionfs/union_vnops.c,v 1.152 2008/01/13 14:44:06 attilio Exp $ 38 * 39 */ 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/conf.h> 44#include <sys/kernel.h> 45#include <sys/lock.h> 46#include <sys/malloc.h> 47#include <sys/mount.h> 48#include <sys/mutex.h> 49#include <sys/namei.h> 50#include <sys/sysctl.h> 51#include <sys/vnode.h> 52#include <sys/buf.h> 53#include <sys/fcntl.h> 54#include <sys/stat.h> 55#include <sys/dirent.h> 56#include <sys/proc.h> 57 58#include <fs/unionfs/unionfs.h> 59#include <miscfs/genfs/genfs.h> 60#include <miscfs/specfs/specdev.h> 61 62#if 0 63#define UNIONFS_INTERNAL_DEBUG(msg, args...) printf(msg, ## args) 64#define UNIONFS_IDBG_RENAME 65#else 66#define UNIONFS_INTERNAL_DEBUG(msg, args...) 67#endif 68 69static int 70unionfs_parsepath(void *v) 71{ 72 struct vop_parsepath_args /* { 73 struct vnode *a_dvp; 74 const char *a_name; 75 size_t *a_retval; 76 } */ *ap = v; 77 struct unionfs_node *dunp; 78 struct vnode *upperdvp, *lowerdvp; 79 size_t upper, lower; 80 int error; 81 82 dunp = VTOUNIONFS(ap->a_dvp); 83 upperdvp = dunp->un_uppervp; 84 lowerdvp = dunp->un_lowervp; 85 86 error = VOP_PARSEPATH(upperdvp, ap->a_name, &upper); 87 if (error) { 88 return error; 89 } 90 91 error = VOP_PARSEPATH(lowerdvp, ap->a_name, &lower); 92 if (error) { 93 return error; 94 } 95 96 /* 97 * If they're different, use the larger one. This is not a 98 * comprehensive solution, but it's sufficient for the 99 * non-default cases of parsepath that currently exist. 100 */ 101 *ap->a_retval = MAX(upper, lower); 102 return 0; 103} 104 105static int 106unionfs_lookup(void *v) 107{ 108 struct vop_lookup_v2_args /* { 109 struct vnodeop_desc *a_desc; 110 struct vnode *a_dvp; 111 struct vnode **a_vpp; 112 struct componentname *a_cnp; 113 } */ *ap = v; 114 int iswhiteout; 115 int error , uerror, lerror; 116 u_long nameiop; 117 u_long cnflags, cnflagsbk; 118 struct unionfs_node *dunp; 119 struct vnode *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp; 120 struct vattr va; 121 struct componentname *cnp; 122 123 iswhiteout = 0; 124 error = uerror = lerror = ENOENT; 125 cnp = ap->a_cnp; 126 nameiop = cnp->cn_nameiop; 127 cnflags = cnp->cn_flags; 128 dvp = ap->a_dvp; 129 dunp = VTOUNIONFS(dvp); 130 udvp = dunp->un_uppervp; 131 ldvp = dunp->un_lowervp; 132 vp = uvp = lvp = NULLVP; 133 *(ap->a_vpp) = NULLVP; 134 135 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr); 136 137 if (dvp->v_type != VDIR) 138 return (ENOTDIR); 139 140 /* 141 * If read-only and op is not LOOKUP, will return EROFS. 142 */ 143 if ((cnflags & ISLASTCN) && 144 (dvp->v_mount->mnt_flag & MNT_RDONLY) && 145 LOOKUP != nameiop) 146 return (EROFS); 147 148 /* 149 * lookup dotdot 150 */ 151 if (cnflags & ISDOTDOT) { 152 if (LOOKUP != nameiop && udvp == NULLVP) 153 return (EROFS); 154 155 if (udvp != NULLVP) { 156 dtmpvp = udvp; 157 if (ldvp != NULLVP) 158 VOP_UNLOCK(ldvp); 159 } 160 else 161 dtmpvp = ldvp; 162 163 error = VOP_LOOKUP(dtmpvp, &vp, cnp); 164 165 if (dtmpvp == udvp && ldvp != NULLVP) { 166 VOP_UNLOCK(udvp); 167 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 168 } 169 170 if (error == 0) { 171 /* 172 * Exchange lock and reference from vp to 173 * dunp->un_dvp. vp is upper/lower vnode, but it 174 * will need to return the unionfs vnode. 175 */ 176 if (nameiop == DELETE || nameiop == RENAME) 177 VOP_UNLOCK(vp); 178 vrele(vp); 179 180 VOP_UNLOCK(dvp); 181 *(ap->a_vpp) = dunp->un_dvp; 182 vref(dunp->un_dvp); 183 184 vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY); 185 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 186 } else if (error == ENOENT && nameiop != CREATE) 187 cache_enter(dvp, NULLVP, cnp->cn_nameptr, 188 cnp->cn_namelen, cnp->cn_flags); 189 190 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error); 191 192 return (error); 193 } 194 195 /* 196 * lookup upper layer 197 */ 198 if (udvp != NULLVP) { 199 uerror = VOP_LOOKUP(udvp, &uvp, cnp); 200 201 if (uerror == 0) { 202 if (udvp == uvp) { /* is dot */ 203 vrele(uvp); 204 *(ap->a_vpp) = dvp; 205 vref(dvp); 206 207 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror); 208 209 return (uerror); 210 } 211 } 212 213 /* check whiteout */ 214 if (uerror == ENOENT || uerror == EJUSTRETURN) 215 if (cnp->cn_flags & ISWHITEOUT) 216 iswhiteout = 1; /* don't lookup lower */ 217 if (iswhiteout == 0 && ldvp != NULLVP) 218 if (VOP_GETATTR(udvp, &va, cnp->cn_cred) == 0 && 219 (va.va_flags & OPAQUE)) 220 iswhiteout = 1; /* don't lookup lower */ 221#if 0 222 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr); 223#endif 224 } 225 226 /* 227 * lookup lower layer 228 */ 229 if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) { 230 /* always op is LOOKUP */ 231 cnp->cn_nameiop = LOOKUP; 232 cnflagsbk = cnp->cn_flags; 233 cnp->cn_flags = cnflags; 234 235 lerror = VOP_LOOKUP(ldvp, &lvp, cnp); 236 237 cnp->cn_nameiop = nameiop; 238 if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN)) 239 cnp->cn_flags = cnflagsbk; 240 241 if (lerror == 0) { 242 if (ldvp == lvp) { /* is dot */ 243 if (uvp != NULLVP) 244 vrele(uvp); /* no need? */ 245 vrele(lvp); 246 *(ap->a_vpp) = dvp; 247 vref(dvp); 248 249 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror); 250 if (uvp != NULL) 251 VOP_UNLOCK(uvp); 252 return (lerror); 253 } 254 } 255 } 256 257 /* 258 * check lookup result 259 */ 260 if (uvp == NULLVP && lvp == NULLVP) { 261 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", 262 (udvp != NULLVP ? uerror : lerror)); 263 return (udvp != NULLVP ? uerror : lerror); 264 } 265 266 /* 267 * check vnode type 268 */ 269 if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) { 270 vput(lvp); 271 lvp = NULLVP; 272 } 273 274 /* 275 * check shadow dir 276 */ 277 if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP && 278 lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR && 279 !(dvp->v_mount->mnt_flag & MNT_RDONLY) && 280 (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) { 281 /* get unionfs vnode in order to create a new shadow dir. */ 282 error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp, 283 cnp); 284 if (error != 0) 285 goto unionfs_lookup_out; 286 error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount), 287 udvp, VTOUNIONFS(vp), cnp); 288 if (error != 0) { 289 UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir."); 290 vput(vp); 291 goto unionfs_lookup_out; 292 } 293 } 294 /* 295 * get unionfs vnode. 296 */ 297 else { 298 if (uvp != NULLVP) 299 error = uerror; 300 else 301 error = lerror; 302 if (error != 0) 303 goto unionfs_lookup_out; 304 error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp, cnp); 305 if (error != 0) { 306 UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode."); 307 goto unionfs_lookup_out; 308 } 309 } 310 311 *(ap->a_vpp) = vp; 312 313 cache_enter(dvp, vp, cnp->cn_nameptr, cnp->cn_namelen, 314 cnp->cn_flags); 315 316 /* XXXAD lock status on error */ 317unionfs_lookup_out: 318 if (uvp != NULLVP) 319 vrele(uvp); 320 if (lvp != NULLVP) 321 vrele(lvp); 322 323 if (error == ENOENT && nameiop != CREATE) 324 cache_enter(dvp, NULLVP, cnp->cn_nameptr, cnp->cn_namelen, 325 cnp->cn_flags); 326 327 UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error); 328 329 return (error); 330} 331 332static int 333unionfs_create(void *v) 334{ 335 struct vop_create_v3_args /* { 336 struct vnode *a_dvp; 337 struct vnode **a_vpp; 338 struct componentname *a_cnp; 339 struct vattr *a_vap; 340 } */ *ap = v; 341 struct unionfs_node *dunp; 342 struct componentname *cnp; 343 struct vnode *udvp; 344 struct vnode *vp; 345 int error; 346 347 UNIONFS_INTERNAL_DEBUG("unionfs_create: enter\n"); 348 349 dunp = VTOUNIONFS(ap->a_dvp); 350 cnp = ap->a_cnp; 351 udvp = dunp->un_uppervp; 352 error = EROFS; 353 354 if (udvp != NULLVP) { 355 if ((error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap)) == 0) { 356 error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP, 357 ap->a_dvp, ap->a_vpp, cnp); 358 if (error) { 359 vput(vp); 360 } else { 361 vrele(vp); 362 } 363 } 364 } 365 366 UNIONFS_INTERNAL_DEBUG("unionfs_create: leave (%d)\n", error); 367 368 return (error); 369} 370 371static int 372unionfs_whiteout(void *v) 373{ 374 struct vop_whiteout_args *ap = v; 375 struct unionfs_node *dunp; 376 struct componentname *cnp; 377 struct vnode *udvp; 378 int error; 379 380 UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: enter\n"); 381 382 dunp = VTOUNIONFS(ap->a_dvp); 383 cnp = ap->a_cnp; 384 udvp = dunp->un_uppervp; 385 error = EOPNOTSUPP; 386 387 if (udvp != NULLVP) { 388 switch (ap->a_flags) { 389 case CREATE: 390 case DELETE: 391 case LOOKUP: 392 error = VOP_WHITEOUT(udvp, cnp, ap->a_flags); 393 break; 394 default: 395 error = EINVAL; 396 break; 397 } 398 } 399 400 UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: leave (%d)\n", error); 401 402 return (error); 403} 404 405static int 406unionfs_mknod(void *v) 407{ 408 struct vop_mknod_v3_args /* { 409 struct vnode *a_dvp; 410 struct vnode **a_vpp; 411 struct componentname *a_cnp; 412 struct vattr *a_vap; 413 } */ *ap = v; 414 struct unionfs_node *dunp; 415 struct componentname *cnp; 416 struct vnode *udvp; 417 struct vnode *vp; 418 int error; 419 420 UNIONFS_INTERNAL_DEBUG("unionfs_mknod: enter\n"); 421 422 dunp = VTOUNIONFS(ap->a_dvp); 423 cnp = ap->a_cnp; 424 udvp = dunp->un_uppervp; 425 error = EROFS; 426 427 if (udvp != NULLVP) { 428 if ((error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap)) == 0) { 429 error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP, 430 ap->a_dvp, ap->a_vpp, cnp); 431 if (error) { 432 vput(vp); 433 } else { 434 vrele(vp); 435 } 436 } 437 } 438 439 UNIONFS_INTERNAL_DEBUG("unionfs_mknod: leave (%d)\n", error); 440 441 return (error); 442} 443 444static int 445unionfs_open(void *v) 446{ 447 struct vop_open_args *ap = v; 448 int error; 449 struct unionfs_node *unp; 450 struct unionfs_node_status *unsp; 451 struct vnode *uvp; 452 struct vnode *lvp; 453 struct vnode *targetvp; 454 kauth_cred_t cred; 455 456 UNIONFS_INTERNAL_DEBUG("unionfs_open: enter\n"); 457 458 error = 0; 459 unp = VTOUNIONFS(ap->a_vp); 460 uvp = unp->un_uppervp; 461 lvp = unp->un_lowervp; 462 targetvp = NULLVP; 463 cred = ap->a_cred; 464 465 unionfs_get_node_status(unp, &unsp); 466 467 if (unsp->uns_lower_opencnt > 0 || unsp->uns_upper_opencnt > 0) { 468 /* vnode is already opend. */ 469 if (unsp->uns_upper_opencnt > 0) 470 targetvp = uvp; 471 else 472 targetvp = lvp; 473 474 if (targetvp == lvp && 475 (ap->a_mode & FWRITE) && lvp->v_type == VREG) 476 targetvp = NULLVP; 477 } 478 if (targetvp == NULLVP) { 479 if (uvp == NULLVP) { 480 if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) { 481 error = unionfs_copyfile(unp, 482 !(ap->a_mode & O_TRUNC), cred); 483 if (error != 0) 484 goto unionfs_open_abort; 485 targetvp = uvp = unp->un_uppervp; 486 } else 487 targetvp = lvp; 488 } else 489 targetvp = uvp; 490 } 491 492 error = VOP_OPEN(targetvp, ap->a_mode, cred); 493 if (error == 0) { 494 if (targetvp == uvp) { 495 if (uvp->v_type == VDIR && lvp != NULLVP && 496 unsp->uns_lower_opencnt <= 0) { 497 /* open lower for readdir */ 498 error = VOP_OPEN(lvp, FREAD, cred); 499 if (error != 0) { 500 VOP_CLOSE(uvp, ap->a_mode, cred); 501 goto unionfs_open_abort; 502 } 503 unsp->uns_node_flag |= UNS_OPENL_4_READDIR; 504 unsp->uns_lower_opencnt++; 505 } 506 unsp->uns_upper_opencnt++; 507 } else { 508 unsp->uns_lower_opencnt++; 509 unsp->uns_lower_openmode = ap->a_mode; 510 } 511 } 512 513unionfs_open_abort: 514 if (error != 0) 515 unionfs_tryrem_node_status(unp, unsp); 516 517 UNIONFS_INTERNAL_DEBUG("unionfs_open: leave (%d)\n", error); 518 519 return (error); 520} 521 522static int 523unionfs_close(void *v) 524{ 525 struct vop_close_args *ap = v; 526 int error; 527 struct unionfs_node *unp; 528 struct unionfs_node_status *unsp; 529 kauth_cred_t cred; 530 struct vnode *ovp; 531 532 UNIONFS_INTERNAL_DEBUG("unionfs_close: enter\n"); 533 534 KASSERT(VOP_ISLOCKED(ap->a_vp) == LK_EXCLUSIVE); 535 unp = VTOUNIONFS(ap->a_vp); 536 cred = ap->a_cred; 537 538 unionfs_get_node_status(unp, &unsp); 539 540 if (unsp->uns_lower_opencnt <= 0 && unsp->uns_upper_opencnt <= 0) { 541#ifdef DIAGNOSTIC 542 printf("unionfs_close: warning: open count is 0\n"); 543#endif 544 if (unp->un_uppervp != NULLVP) 545 ovp = unp->un_uppervp; 546 else 547 ovp = unp->un_lowervp; 548 } else if (unsp->uns_upper_opencnt > 0) 549 ovp = unp->un_uppervp; 550 else 551 ovp = unp->un_lowervp; 552 553 error = VOP_CLOSE(ovp, ap->a_fflag, cred); 554 555 if (error != 0) 556 goto unionfs_close_abort; 557 558 if (ovp == unp->un_uppervp) { 559 unsp->uns_upper_opencnt--; 560 if (unsp->uns_upper_opencnt == 0) { 561 if (unsp->uns_node_flag & UNS_OPENL_4_READDIR) { 562 VOP_CLOSE(unp->un_lowervp, FREAD, cred); 563 unsp->uns_node_flag &= ~UNS_OPENL_4_READDIR; 564 unsp->uns_lower_opencnt--; 565 } 566 } 567 } else 568 unsp->uns_lower_opencnt--; 569 570unionfs_close_abort: 571 unionfs_tryrem_node_status(unp, unsp); 572 573 UNIONFS_INTERNAL_DEBUG("unionfs_close: leave (%d)\n", error); 574 575 return (error); 576} 577 578/* 579 * Check the access mode toward shadow file/dir. 580 */ 581static int 582unionfs_check_corrected_access(u_short mode, struct vattr *va, kauth_cred_t cred) 583{ 584 uid_t uid; /* upper side vnode's uid */ 585 gid_t gid; /* upper side vnode's gid */ 586 u_short vmode; /* upper side vnode's mode */ 587 u_short mask; 588 589 mask = 0; 590 uid = va->va_uid; 591 gid = va->va_gid; 592 vmode = va->va_mode; 593 594 /* check owner */ 595 if (kauth_cred_getuid(cred) == uid) { 596 if (mode & VEXEC) 597 mask |= S_IXUSR; 598 if (mode & VREAD) 599 mask |= S_IRUSR; 600 if (mode & VWRITE) 601 mask |= S_IWUSR; 602 return ((vmode & mask) == mask ? 0 : EACCES); 603 } 604 605 /* check group */ 606 if (kauth_cred_groupmember(cred, gid) == 0) { 607 if (mode & VEXEC) 608 mask |= S_IXGRP; 609 if (mode & VREAD) 610 mask |= S_IRGRP; 611 if (mode & VWRITE) 612 mask |= S_IWGRP; 613 return ((vmode & mask) == mask ? 0 : EACCES); 614 } 615 616 /* check other */ 617 if (mode & VEXEC) 618 mask |= S_IXOTH; 619 if (mode & VREAD) 620 mask |= S_IROTH; 621 if (mode & VWRITE) 622 mask |= S_IWOTH; 623 624 return ((vmode & mask) == mask ? 0 : EACCES); 625} 626 627static int 628unionfs_access(void *v) 629{ 630 struct vop_access_args /* { 631 struct vnodeop_desc *a_desc; 632 struct vnode *a_vp; 633 accmode_t a_accmode; 634 kauth_cred_t a_cred; 635 } */ *ap = v; 636 struct unionfs_mount *ump; 637 struct unionfs_node *unp; 638 struct vnode *uvp; 639 struct vnode *lvp; 640 struct vattr va; 641 int accmode; 642 int error; 643 644 UNIONFS_INTERNAL_DEBUG("unionfs_access: enter\n"); 645 646 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); 647 unp = VTOUNIONFS(ap->a_vp); 648 uvp = unp->un_uppervp; 649 lvp = unp->un_lowervp; 650 accmode = ap->a_accmode; 651 error = EACCES; 652 653 if ((accmode & VWRITE) && 654 (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) { 655 switch (ap->a_vp->v_type) { 656 case VREG: 657 case VDIR: 658 case VLNK: 659 return (EROFS); 660 default: 661 break; 662 } 663 } 664 665 if (uvp != NULLVP) { 666 error = VOP_ACCESS(uvp, accmode, ap->a_cred); 667 668 UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error); 669 670 return (error); 671 } 672 673 if (lvp != NULLVP) { 674 if (accmode & VWRITE) { 675 if (ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY) { 676 switch (ap->a_vp->v_type) { 677 case VREG: 678 case VDIR: 679 case VLNK: 680 return (EROFS); 681 default: 682 break; 683 } 684 } else if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) { 685 /* check shadow file/dir */ 686 if (ump->um_copymode != UNIONFS_TRANSPARENT) { 687 error = unionfs_create_uppervattr(ump, 688 lvp, &va, ap->a_cred); 689 if (error != 0) 690 return (error); 691 692 error = unionfs_check_corrected_access( 693 accmode, &va, ap->a_cred); 694 if (error != 0) 695 return (error); 696 } 697 } 698 accmode &= ~VWRITE; 699 accmode |= VREAD; /* will copy to upper */ 700 } 701 error = VOP_ACCESS(lvp, accmode, ap->a_cred); 702 } 703 704 UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error); 705 706 return (error); 707} 708 709static int 710unionfs_getattr(void *v) 711{ 712 struct vop_getattr_args *ap = v; 713 int error; 714 struct unionfs_node *unp; 715 struct unionfs_mount *ump; 716 struct vnode *uvp; 717 struct vnode *lvp; 718 struct vattr va; 719 720 UNIONFS_INTERNAL_DEBUG("unionfs_getattr: enter\n"); 721 722 unp = VTOUNIONFS(ap->a_vp); 723 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); 724 uvp = unp->un_uppervp; 725 lvp = unp->un_lowervp; 726 727 if (uvp != NULLVP) { 728 if ((error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred)) == 0) 729 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid; 730 731 UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n", 732 ap->a_vap->va_mode, ap->a_vap->va_uid, 733 ap->a_vap->va_gid, error); 734 735 return (error); 736 } 737 738 error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred); 739 740 if (error == 0 && !(ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY)) { 741 /* correct the attr toward shadow file/dir. */ 742 if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) { 743 unionfs_create_uppervattr_core(ump, ap->a_vap, &va); 744 ap->a_vap->va_mode = va.va_mode; 745 ap->a_vap->va_uid = va.va_uid; 746 ap->a_vap->va_gid = va.va_gid; 747 } 748 } 749 750 if (error == 0) 751 ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid; 752 753 UNIONFS_INTERNAL_DEBUG("unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n", 754 ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error); 755 756 return (error); 757} 758 759static int 760unionfs_setattr(void *v) 761{ 762 struct vop_setattr_args /* { 763 struct vnode *a_vp; 764 struct vattr *a_vap; 765 kauth_cred_t a_cred; 766 } */ *ap = v; 767 int error; 768 struct unionfs_node *unp; 769 struct vnode *uvp; 770 struct vnode *lvp; 771 struct vattr *vap; 772 773 UNIONFS_INTERNAL_DEBUG("unionfs_setattr: enter\n"); 774 775 error = EROFS; 776 unp = VTOUNIONFS(ap->a_vp); 777 uvp = unp->un_uppervp; 778 lvp = unp->un_lowervp; 779 vap = ap->a_vap; 780 781 if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) && 782 (vap->va_flags != (unsigned long )VNOVAL || 783 vap->va_uid != (uid_t)VNOVAL || 784 vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || 785 vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) 786 return (EROFS); 787 788 if (uvp == NULLVP && lvp->v_type == VREG) { 789 error = unionfs_copyfile(unp, (vap->va_size != 0), 790 ap->a_cred); 791 if (error != 0) 792 return (error); 793 uvp = unp->un_uppervp; 794 } 795 796 if (uvp != NULLVP) 797 error = VOP_SETATTR(uvp, vap, ap->a_cred); 798 799 UNIONFS_INTERNAL_DEBUG("unionfs_setattr: leave (%d)\n", error); 800 801 return (error); 802} 803 804static int 805unionfs_read(void *v) 806{ 807 struct vop_read_args *ap = v; 808 int error; 809 struct unionfs_node *unp; 810 struct vnode *tvp; 811 812 /* UNIONFS_INTERNAL_DEBUG("unionfs_read: enter\n"); */ 813 814 unp = VTOUNIONFS(ap->a_vp); 815 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 816 817 error = VOP_READ(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred); 818 819 /* UNIONFS_INTERNAL_DEBUG("unionfs_read: leave (%d)\n", error); */ 820 821 return (error); 822} 823 824static int 825unionfs_write(void *v) 826{ 827 struct vop_write_args *ap = v; 828 int error; 829 struct unionfs_node *unp; 830 struct vnode *tvp; 831 832 /* UNIONFS_INTERNAL_DEBUG("unionfs_write: enter\n"); */ 833 834 unp = VTOUNIONFS(ap->a_vp); 835 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 836 837 error = VOP_WRITE(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred); 838 839 /* UNIONFS_INTERNAL_DEBUG("unionfs_write: leave (%d)\n", error); */ 840 841 return (error); 842} 843 844static int 845unionfs_ioctl(void *v) 846{ 847 struct vop_ioctl_args *ap = v; 848 int error; 849 struct unionfs_node *unp; 850 struct unionfs_node_status *unsp; 851 struct vnode *ovp; 852 853 UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: enter\n"); 854 855 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY); 856 unp = VTOUNIONFS(ap->a_vp); 857 unionfs_get_node_status(unp, &unsp); 858 ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); 859 unionfs_tryrem_node_status(unp, unsp); 860 VOP_UNLOCK(ap->a_vp); 861 862 if (ovp == NULLVP) 863 return (EBADF); 864 865 error = VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag, 866 ap->a_cred); 867 868 UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: lease (%d)\n", error); 869 870 return (error); 871} 872 873static int 874unionfs_poll(void *v) 875{ 876 struct vop_poll_args *ap = v; 877 struct unionfs_node *unp; 878 struct unionfs_node_status *unsp; 879 struct vnode *ovp; 880 881 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY); 882 unp = VTOUNIONFS(ap->a_vp); 883 unionfs_get_node_status(unp, &unsp); 884 ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); 885 unionfs_tryrem_node_status(unp, unsp); 886 VOP_UNLOCK(ap->a_vp); 887 888 if (ovp == NULLVP) 889 return (EBADF); 890 891 return (VOP_POLL(ovp, ap->a_events)); 892} 893 894static int 895unionfs_fsync(void *v) 896{ 897 struct vop_fsync_args *ap = v; 898 struct unionfs_node *unp; 899 struct unionfs_node_status *unsp; 900 struct vnode *ovp; 901 902 unp = VTOUNIONFS(ap->a_vp); 903 unionfs_get_node_status(unp, &unsp); 904 ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp); 905 unionfs_tryrem_node_status(unp, unsp); 906 907 if (ovp == NULLVP) 908 return (EBADF); 909 910 return (VOP_FSYNC(ovp, ap->a_cred, ap->a_flags, ap->a_offlo, ap->a_offhi)); 911} 912 913static int 914unionfs_remove(void *v) 915{ 916 struct vop_remove_v3_args *ap = v; 917 int error; 918 struct unionfs_node *dunp; 919 struct unionfs_node *unp; 920 struct unionfs_mount *ump; 921 struct vnode *udvp; 922 struct vnode *uvp; 923 struct vnode *lvp; 924 struct componentname *cnp; 925 926 UNIONFS_INTERNAL_DEBUG("unionfs_remove: enter\n"); 927 928 error = 0; 929 dunp = VTOUNIONFS(ap->a_dvp); 930 unp = VTOUNIONFS(ap->a_vp); 931 udvp = dunp->un_uppervp; 932 uvp = unp->un_uppervp; 933 lvp = unp->un_lowervp; 934 cnp = ap->a_cnp; 935 936 if (udvp == NULLVP) { 937 vput(ap->a_vp); 938 return (EROFS); 939 } 940 941 if (uvp != NULLVP) { 942 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); 943 if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP) 944 cnp->cn_flags |= DOWHITEOUT; 945 error = VOP_REMOVE(udvp, uvp, cnp); 946 } else if (lvp != NULLVP) 947 error = unionfs_mkwhiteout(udvp, cnp, unp->un_path); 948 949 UNIONFS_INTERNAL_DEBUG("unionfs_remove: leave (%d)\n", error); 950 951 return (error); 952} 953 954static int 955unionfs_link(void *v) 956{ 957#if 0 958 struct vop_link_v2_args *ap = v; 959 int error; 960 int needrelookup; 961 struct unionfs_node *dunp; 962 struct unionfs_node *unp; 963 struct vnode *udvp; 964 struct vnode *uvp; 965 struct componentname *cnp; 966 967 UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n"); 968 969 error = 0; 970 needrelookup = 0; 971 dunp = VTOUNIONFS(ap->a_tdvp); 972 unp = NULL; 973 udvp = dunp->un_uppervp; 974 uvp = NULLVP; 975 cnp = ap->a_cnp; 976 977 if (udvp == NULLVP) 978 return (EROFS); 979 980 if (ap->a_vp->v_op != unionfs_vnodeop_p) 981 uvp = ap->a_vp; 982 else { 983 unp = VTOUNIONFS(ap->a_vp); 984 985 if (unp->un_uppervp == NULLVP) { 986 if (ap->a_vp->v_type != VREG) 987 return (EOPNOTSUPP); 988 989 error = unionfs_copyfile(unp, 1, cnp->cn_cred); 990 if (error != 0) 991 return (error); 992 needrelookup = 1; 993 } 994 uvp = unp->un_uppervp; 995 } 996 997 if (needrelookup != 0) 998 error = unionfs_relookup_for_create(ap->a_tdvp, cnp); 999 1000 if (error == 0) 1001 error = VOP_LINK(udvp, uvp, cnp); 1002 1003 UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error); 1004 1005 return (error); 1006#else 1007 panic("XXXAD"); 1008 return 0; 1009#endif 1010} 1011 1012static int 1013unionfs_rename(void *v) 1014{ 1015 struct vop_rename_args *ap = v; 1016 int error; 1017 struct vnode *fdvp; 1018 struct vnode *fvp; 1019 struct componentname *fcnp; 1020 struct vnode *tdvp; 1021 struct vnode *tvp; 1022 struct componentname *tcnp; 1023 struct vnode *ltdvp; 1024 struct vnode *ltvp; 1025 1026 /* rename target vnodes */ 1027 struct vnode *rfdvp; 1028 struct vnode *rfvp; 1029 struct vnode *rtdvp; 1030 struct vnode *rtvp; 1031 1032 int needrelookup; 1033 struct unionfs_mount *ump; 1034 struct unionfs_node *unp; 1035 1036 UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n"); 1037 1038 error = 0; 1039 fdvp = ap->a_fdvp; 1040 fvp = ap->a_fvp; 1041 fcnp = ap->a_fcnp; 1042 tdvp = ap->a_tdvp; 1043 tvp = ap->a_tvp; 1044 tcnp = ap->a_tcnp; 1045 ltdvp = NULLVP; 1046 ltvp = NULLVP; 1047 rfdvp = fdvp; 1048 rfvp = fvp; 1049 rtdvp = tdvp; 1050 rtvp = tvp; 1051 needrelookup = 0; 1052 1053 /* check for cross device rename */ 1054 if (fvp->v_mount != tdvp->v_mount || 1055 (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) { 1056 error = EXDEV; 1057 goto unionfs_rename_abort; 1058 } 1059 1060 /* Renaming a file to itself has no effect. */ 1061 if (fvp == tvp) 1062 goto unionfs_rename_abort; 1063 1064 /* 1065 * from/to vnode is unionfs node. 1066 */ 1067 1068 unp = VTOUNIONFS(fdvp); 1069#ifdef UNIONFS_IDBG_RENAME 1070 UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n", fdvp, unp->un_uppervp, unp->un_lowervp); 1071#endif 1072 if (unp->un_uppervp == NULLVP) { 1073 error = ENODEV; 1074 goto unionfs_rename_abort; 1075 } 1076 rfdvp = unp->un_uppervp; 1077 vref(rfdvp); 1078 1079 unp = VTOUNIONFS(fvp); 1080#ifdef UNIONFS_IDBG_RENAME 1081 UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n", fvp, unp->un_uppervp, unp->un_lowervp); 1082#endif 1083 ump = MOUNTTOUNIONFSMOUNT(fvp->v_mount); 1084 if (unp->un_uppervp == NULLVP) { 1085 switch (fvp->v_type) { 1086 case VREG: 1087 if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) 1088 goto unionfs_rename_abort; 1089 error = unionfs_copyfile(unp, 1, fcnp->cn_cred); 1090 VOP_UNLOCK(fvp); 1091 if (error != 0) 1092 goto unionfs_rename_abort; 1093 break; 1094 case VDIR: 1095 if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) 1096 goto unionfs_rename_abort; 1097 error = unionfs_mkshadowdir(ump, rfdvp, unp, fcnp); 1098 VOP_UNLOCK(fvp); 1099 if (error != 0) 1100 goto unionfs_rename_abort; 1101 break; 1102 default: 1103 error = ENODEV; 1104 goto unionfs_rename_abort; 1105 } 1106 1107 needrelookup = 1; 1108 } 1109 1110 if (unp->un_lowervp != NULLVP) 1111 fcnp->cn_flags |= DOWHITEOUT; 1112 rfvp = unp->un_uppervp; 1113 vref(rfvp); 1114 1115 unp = VTOUNIONFS(tdvp); 1116#ifdef UNIONFS_IDBG_RENAME 1117 UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n", tdvp, unp->un_uppervp, unp->un_lowervp); 1118#endif 1119 if (unp->un_uppervp == NULLVP) { 1120 error = ENODEV; 1121 goto unionfs_rename_abort; 1122 } 1123 rtdvp = unp->un_uppervp; 1124 ltdvp = unp->un_lowervp; 1125 vref(rtdvp); 1126 1127 if (tdvp == tvp) { 1128 rtvp = rtdvp; 1129 vref(rtvp); 1130 } else if (tvp != NULLVP) { 1131 unp = VTOUNIONFS(tvp); 1132#ifdef UNIONFS_IDBG_RENAME 1133 UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n", tvp, unp->un_uppervp, unp->un_lowervp); 1134#endif 1135 if (unp->un_uppervp == NULLVP) 1136 rtvp = NULLVP; 1137 else { 1138 if (tvp->v_type == VDIR) { 1139 error = EINVAL; 1140 goto unionfs_rename_abort; 1141 } 1142 rtvp = unp->un_uppervp; 1143 ltvp = unp->un_lowervp; 1144 vref(rtvp); 1145 } 1146 } 1147 1148 if (needrelookup != 0) { 1149 if ((error = vn_lock(fdvp, LK_EXCLUSIVE)) != 0) 1150 goto unionfs_rename_abort; 1151 error = unionfs_relookup_for_delete(fdvp, fcnp); 1152 VOP_UNLOCK(fdvp); 1153 if (error != 0) 1154 goto unionfs_rename_abort; 1155 1156 /* Locke of tvp is canceled in order to avoid recursive lock. */ 1157 if (tvp != NULLVP && tvp != tdvp) 1158 VOP_UNLOCK(tvp); 1159 error = unionfs_relookup_for_rename(tdvp, tcnp); 1160 if (tvp != NULLVP && tvp != tdvp) 1161 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY); 1162 if (error != 0) 1163 goto unionfs_rename_abort; 1164 } 1165 1166 error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp); 1167 1168 if (error == 0) { 1169 if (rtvp != NULLVP && rtvp->v_type == VDIR) 1170 cache_purge(tdvp); 1171 if (fvp->v_type == VDIR && fdvp != tdvp) 1172 cache_purge(fdvp); 1173 } 1174 1175 if (fdvp != rfdvp) 1176 vrele(fdvp); 1177 if (fvp != rfvp) 1178 vrele(fvp); 1179 if (ltdvp != NULLVP) 1180 VOP_UNLOCK(ltdvp); 1181 if (tdvp != rtdvp) 1182 vrele(tdvp); 1183 if (ltvp != NULLVP) 1184 VOP_UNLOCK(ltvp); 1185 if (tvp != rtvp && tvp != NULLVP) { 1186 if (rtvp == NULLVP) 1187 vput(tvp); 1188 else 1189 vrele(tvp); 1190 } 1191 1192 UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error); 1193 1194 return (error); 1195 1196unionfs_rename_abort: 1197 if (fdvp != rfdvp) 1198 vrele(rfdvp); 1199 if (fvp != rfvp) 1200 vrele(rfvp); 1201 if (tdvp != rtdvp) 1202 vrele(rtdvp); 1203 vput(tdvp); 1204 if (tvp != rtvp && rtvp != NULLVP) 1205 vrele(rtvp); 1206 if (tvp != NULLVP) { 1207 if (tdvp != tvp) 1208 vput(tvp); 1209 else 1210 vrele(tvp); 1211 } 1212 vrele(fdvp); 1213 vrele(fvp); 1214 1215 UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error); 1216 1217 return (error); 1218} 1219 1220static int 1221unionfs_mkdir(void *v) 1222{ 1223 struct vop_mkdir_v3_args /* { 1224 struct vnode *a_dvp; 1225 struct vnode **a_vpp; 1226 struct componentname *a_cnp; 1227 struct vattr *a_vap; 1228 } */ *ap = v; 1229 int error; 1230 struct unionfs_node *dunp; 1231 struct componentname *cnp; 1232 struct vnode *udvp; 1233 struct vnode *uvp; 1234 struct vattr va; 1235 1236 UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: enter\n"); 1237 1238 error = EROFS; 1239 dunp = VTOUNIONFS(ap->a_dvp); 1240 cnp = ap->a_cnp; 1241 udvp = dunp->un_uppervp; 1242 1243 if (udvp != NULLVP) { 1244 /* check opaque */ 1245 if (!(cnp->cn_flags & ISWHITEOUT)) { 1246 error = VOP_GETATTR(udvp, &va, cnp->cn_cred); 1247 if (error != 0) 1248 return (error); 1249 if (va.va_flags & OPAQUE) 1250 cnp->cn_flags |= ISWHITEOUT; 1251 } 1252 1253 if ((error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap)) == 0) { 1254 error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP, 1255 ap->a_dvp, ap->a_vpp, cnp); 1256 if (error) { 1257 vput(uvp); 1258 } else { 1259 vrele(uvp); 1260 } 1261 } 1262 } 1263 1264 UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: leave (%d)\n", error); 1265 1266 return (error); 1267} 1268 1269static int 1270unionfs_rmdir(void *v) 1271{ 1272 struct vop_rmdir_v2_args *ap = v; 1273 int error; 1274 struct unionfs_node *dunp; 1275 struct unionfs_node *unp; 1276 struct unionfs_mount *ump; 1277 struct componentname *cnp; 1278 struct vnode *udvp; 1279 struct vnode *uvp; 1280 struct vnode *lvp; 1281 1282 UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: enter\n"); 1283 1284 error = 0; 1285 dunp = VTOUNIONFS(ap->a_dvp); 1286 unp = VTOUNIONFS(ap->a_vp); 1287 cnp = ap->a_cnp; 1288 udvp = dunp->un_uppervp; 1289 uvp = unp->un_uppervp; 1290 lvp = unp->un_lowervp; 1291 1292 if (udvp == NULLVP) { 1293 vput(ap->a_vp); 1294 return (EROFS); 1295 } 1296 1297 if (udvp == uvp) 1298 return (EOPNOTSUPP); 1299 1300 if (uvp != NULLVP) { 1301 if (lvp != NULLVP) { 1302 error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred); 1303 if (error != 0) 1304 return (error); 1305 } 1306 ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); 1307 if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP) 1308 cnp->cn_flags |= DOWHITEOUT; 1309 error = VOP_RMDIR(udvp, uvp, cnp); 1310 } 1311 else if (lvp != NULLVP) 1312 error = unionfs_mkwhiteout(udvp, cnp, unp->un_path); 1313 1314 if (error == 0) { 1315 cache_purge(ap->a_dvp); 1316 cache_purge(ap->a_vp); 1317 } 1318 1319 UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: leave (%d)\n", error); 1320 1321 return (error); 1322} 1323 1324static int 1325unionfs_symlink(void *v) 1326{ 1327 struct vop_symlink_v3_args /* { 1328 struct vnode *a_dvp; 1329 struct vnode **a_vpp; 1330 struct componentname *a_cnp; 1331 struct vattr *a_vap; 1332 char *a_target; 1333 } */ *ap = v; 1334 int error; 1335 struct unionfs_node *dunp; 1336 struct componentname *cnp; 1337 struct vnode *udvp; 1338 struct vnode *uvp; 1339 1340 UNIONFS_INTERNAL_DEBUG("unionfs_symlink: enter\n"); 1341 1342 error = EROFS; 1343 dunp = VTOUNIONFS(ap->a_dvp); 1344 cnp = ap->a_cnp; 1345 udvp = dunp->un_uppervp; 1346 1347 if (udvp != NULLVP) { 1348 error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target); 1349 if (error == 0) { 1350 error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP, 1351 ap->a_dvp, ap->a_vpp, cnp); 1352 if (error) { 1353 vput(uvp); 1354 } else { 1355 vrele(uvp); 1356 } 1357 } 1358 } 1359 1360 UNIONFS_INTERNAL_DEBUG("unionfs_symlink: leave (%d)\n", error); 1361 1362 return (error); 1363} 1364 1365static int 1366unionfs_readdir(void *v) 1367{ 1368 struct vop_readdir_args *ap = v; 1369 int error; 1370 int eofflag; 1371 struct unionfs_node *unp; 1372 struct unionfs_node_status *unsp; 1373 struct uio *uio; 1374 struct vnode *uvp; 1375 struct vnode *lvp; 1376 struct vattr va; 1377 1378 int ncookies_bk; 1379 off_t *cookies_bk; 1380 1381 UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n"); 1382 1383 error = 0; 1384 eofflag = 0; 1385 unp = VTOUNIONFS(ap->a_vp); 1386 uio = ap->a_uio; 1387 uvp = unp->un_uppervp; 1388 lvp = unp->un_lowervp; 1389 ncookies_bk = 0; 1390 cookies_bk = NULL; 1391 1392 if (ap->a_vp->v_type != VDIR) 1393 return (ENOTDIR); 1394 1395 /* check opaque */ 1396 if (uvp != NULLVP && lvp != NULLVP) { 1397 if ((error = VOP_GETATTR(uvp, &va, ap->a_cred)) != 0) 1398 goto unionfs_readdir_exit; 1399 if (va.va_flags & OPAQUE) 1400 lvp = NULLVP; 1401 } 1402 1403 /* check the open count. unionfs needs to open before readdir. */ 1404 VOP_UNLOCK(ap->a_vp); 1405 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY); 1406 unionfs_get_node_status(unp, &unsp); 1407 if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) || 1408 (lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) { 1409 unionfs_tryrem_node_status(unp, unsp); 1410 error = EBADF; 1411 } 1412 if (error != 0) 1413 goto unionfs_readdir_exit; 1414 1415 /* upper only */ 1416 if (uvp != NULLVP && lvp == NULLVP) { 1417 error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag, 1418 ap->a_cookies, ap->a_ncookies); 1419 unsp->uns_readdir_status = 0; 1420 1421 goto unionfs_readdir_exit; 1422 } 1423 1424 /* lower only */ 1425 if (uvp == NULLVP && lvp != NULLVP) { 1426 error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, 1427 ap->a_cookies, ap->a_ncookies); 1428 unsp->uns_readdir_status = 2; 1429 1430 goto unionfs_readdir_exit; 1431 } 1432 1433 /* 1434 * readdir upper and lower 1435 */ 1436 KASSERT(uvp != NULLVP); 1437 KASSERT(lvp != NULLVP); 1438 if (uio->uio_offset == 0) 1439 unsp->uns_readdir_status = 0; 1440 1441 if (unsp->uns_readdir_status == 0) { 1442 /* read upper */ 1443 error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag, 1444 ap->a_cookies, ap->a_ncookies); 1445 1446 if (error != 0 || eofflag == 0) 1447 goto unionfs_readdir_exit; 1448 unsp->uns_readdir_status = 1; 1449 1450 /* 1451 * ufs(and other fs) needs size of uio_resid larger than 1452 * DIRBLKSIZ. 1453 * size of DIRBLKSIZ equals DEV_BSIZE. 1454 * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h) 1455 */ 1456 if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1))) 1457 goto unionfs_readdir_exit; 1458 1459 /* 1460 * backup cookies 1461 * It prepares to readdir in lower. 1462 */ 1463 if (ap->a_ncookies != NULL) { 1464 ncookies_bk = *(ap->a_ncookies); 1465 *(ap->a_ncookies) = 0; 1466 } 1467 if (ap->a_cookies != NULL) { 1468 cookies_bk = *(ap->a_cookies); 1469 *(ap->a_cookies) = NULL; 1470 } 1471 } 1472 1473 /* initialize for readdir in lower */ 1474 if (unsp->uns_readdir_status == 1) { 1475 unsp->uns_readdir_status = 2; 1476 uio->uio_offset = 0; 1477 } 1478 1479 if (lvp == NULLVP) { 1480 error = EBADF; 1481 goto unionfs_readdir_exit; 1482 } 1483 /* read lower */ 1484 error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag, 1485 ap->a_cookies, ap->a_ncookies); 1486 1487 if (cookies_bk != NULL) { 1488 /* merge cookies */ 1489 int size; 1490 off_t *newcookies, *pos; 1491 1492 size = *(ap->a_ncookies) + ncookies_bk; 1493 newcookies = (off_t *) malloc(size * sizeof(off_t), 1494 M_TEMP, M_WAITOK); 1495 pos = newcookies; 1496 1497 memcpy(pos, cookies_bk, ncookies_bk * sizeof(off_t)); 1498 pos += ncookies_bk * sizeof(off_t); 1499 memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(off_t)); 1500 free(cookies_bk, M_TEMP); 1501 free(*(ap->a_cookies), M_TEMP); 1502 *(ap->a_ncookies) = size; 1503 *(ap->a_cookies) = newcookies; 1504 } 1505 1506unionfs_readdir_exit: 1507 if (error != 0 && ap->a_eofflag != NULL) 1508 *(ap->a_eofflag) = 1; 1509 1510 UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error); 1511 1512 return (error); 1513} 1514 1515static int 1516unionfs_readlink(void *v) 1517{ 1518 struct vop_readlink_args *ap = v; 1519 int error; 1520 struct unionfs_node *unp; 1521 struct vnode *vp; 1522 1523 UNIONFS_INTERNAL_DEBUG("unionfs_readlink: enter\n"); 1524 1525 unp = VTOUNIONFS(ap->a_vp); 1526 vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1527 1528 error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); 1529 1530 UNIONFS_INTERNAL_DEBUG("unionfs_readlink: leave (%d)\n", error); 1531 1532 return (error); 1533} 1534 1535static int 1536unionfs_inactive(void *v) 1537{ 1538 struct vop_inactive_v2_args *ap = v; 1539 *ap->a_recycle = true; 1540 return (0); 1541} 1542 1543static int 1544unionfs_reclaim(void *v) 1545{ 1546 struct vop_reclaim_v2_args *ap = v; 1547 1548 /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: enter\n"); */ 1549 1550 VOP_UNLOCK(ap->a_vp); 1551 1552 unionfs_noderem(ap->a_vp); 1553 1554 /* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: leave\n"); */ 1555 1556 return (0); 1557} 1558 1559static int 1560unionfs_print(void *v) 1561{ 1562 struct vop_print_args *ap = v; 1563 struct unionfs_node *unp; 1564 /* struct unionfs_node_status *unsp; */ 1565 1566 unp = VTOUNIONFS(ap->a_vp); 1567 /* unionfs_get_node_status(unp, &unsp); */ 1568 1569 printf("unionfs_vp=%p, uppervp=%p, lowervp=%p\n", 1570 ap->a_vp, unp->un_uppervp, unp->un_lowervp); 1571 /* 1572 printf("unionfs opencnt: uppervp=%d, lowervp=%d\n", 1573 unsp->uns_upper_opencnt, unsp->uns_lower_opencnt); 1574 */ 1575 1576 if (unp->un_uppervp != NULLVP) 1577 vprint("unionfs: upper", unp->un_uppervp); 1578 if (unp->un_lowervp != NULLVP) 1579 vprint("unionfs: lower", unp->un_lowervp); 1580 1581 return (0); 1582} 1583 1584static int 1585unionfs_lock(void *v) 1586{ 1587 struct vop_lock_args *ap = v; 1588 int error; 1589 int flags; 1590 struct vnode *lvp; 1591 struct vnode *uvp; 1592 struct unionfs_node *unp; 1593 1594 unp = VTOUNIONFS(ap->a_vp); 1595 lvp = unp->un_lowervp; 1596 uvp = unp->un_uppervp; 1597 flags = ap->a_flags; 1598 error = 0; 1599 1600 if (lvp != NULLVP) { 1601 error = VOP_LOCK(lvp, flags); 1602 } 1603 if (error == 0 && uvp != NULLVP) { 1604 error = VOP_LOCK(uvp, flags); 1605 if (error != 0) { 1606 VOP_UNLOCK(lvp); 1607 } 1608 } 1609 1610 return error; 1611} 1612 1613static int 1614unionfs_unlock(void *v) 1615{ 1616 struct vop_unlock_args *ap = v; 1617 int error; 1618 struct vnode *lvp; 1619 struct vnode *uvp; 1620 struct unionfs_node *unp; 1621 1622 unp = VTOUNIONFS(ap->a_vp); 1623 lvp = unp->un_lowervp; 1624 uvp = unp->un_uppervp; 1625 error = 0; 1626 1627 if (lvp != NULLVP) { 1628 error = VOP_UNLOCK(lvp); 1629 } 1630 if (error == 0 && uvp != NULLVP) { 1631 error = VOP_UNLOCK(uvp); 1632 } 1633 1634 return error; 1635} 1636 1637static int 1638unionfs_pathconf(void *v) 1639{ 1640 struct vop_pathconf_args *ap = v; 1641 struct unionfs_node *unp; 1642 struct vnode *vp; 1643 1644 unp = VTOUNIONFS(ap->a_vp); 1645 vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1646 1647 return (VOP_PATHCONF(vp, ap->a_name, ap->a_retval)); 1648} 1649 1650static int 1651unionfs_advlock(void *v) 1652{ 1653 struct vop_advlock_args *ap = v; 1654 int error; 1655 struct unionfs_node *unp; 1656 struct unionfs_node_status *unsp; 1657 struct vnode *vp; 1658 struct vnode *uvp; 1659 kauth_cred_t cred; 1660 1661 UNIONFS_INTERNAL_DEBUG("unionfs_advlock: enter\n"); 1662 1663 vp = ap->a_vp; 1664 cred = kauth_cred_get(); 1665 1666 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 1667 1668 unp = VTOUNIONFS(ap->a_vp); 1669 uvp = unp->un_uppervp; 1670 1671 if (uvp == NULLVP) { 1672 error = unionfs_copyfile(unp, 1, cred); 1673 if (error != 0) 1674 goto unionfs_advlock_abort; 1675 uvp = unp->un_uppervp; 1676 1677 unionfs_get_node_status(unp, &unsp); 1678 if (unsp->uns_lower_opencnt > 0) { 1679 /* try reopen the vnode */ 1680 error = VOP_OPEN(uvp, unsp->uns_lower_openmode, cred); 1681 if (error) 1682 goto unionfs_advlock_abort; 1683 unsp->uns_upper_opencnt++; 1684 VOP_CLOSE(unp->un_lowervp, unsp->uns_lower_openmode, cred); 1685 unsp->uns_lower_opencnt--; 1686 } else 1687 unionfs_tryrem_node_status(unp, unsp); 1688 } 1689 1690 VOP_UNLOCK(vp); 1691 1692 error = VOP_ADVLOCK(uvp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags); 1693 1694 UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error); 1695 1696 return error; 1697 1698unionfs_advlock_abort: 1699 VOP_UNLOCK(vp); 1700 1701 UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error); 1702 1703 return error; 1704} 1705 1706static int 1707unionfs_strategy(void *v) 1708{ 1709 struct vop_strategy_args *ap = v; 1710 struct unionfs_node *unp; 1711 struct vnode *vp; 1712 1713 unp = VTOUNIONFS(ap->a_vp); 1714 vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1715 1716#ifdef DIAGNOSTIC 1717 if (vp == NULLVP) 1718 panic("unionfs_strategy: nullvp"); 1719 if ((ap->a_bp->b_flags & B_READ) == 0 && vp == unp->un_lowervp) 1720 panic("unionfs_strategy: writing to lowervp"); 1721#endif 1722 1723 return (VOP_STRATEGY(vp, ap->a_bp)); 1724} 1725 1726static int 1727unionfs_kqfilter(void *v) 1728{ 1729 struct vop_kqfilter_args *ap = v; 1730 struct unionfs_node *unp; 1731 struct vnode *tvp; 1732 1733 unp = VTOUNIONFS(ap->a_vp); 1734 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1735 1736 return VOP_KQFILTER(tvp, ap->a_kn); 1737} 1738 1739static int 1740unionfs_bmap(void *v) 1741{ 1742 struct vop_bmap_args *ap = v; 1743 struct unionfs_node *unp; 1744 struct vnode *tvp; 1745 1746 unp = VTOUNIONFS(ap->a_vp); 1747 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1748 1749 return VOP_BMAP(tvp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); 1750} 1751 1752static int 1753unionfs_mmap(void *v) 1754{ 1755 struct vop_mmap_args *ap = v; 1756 struct unionfs_node *unp; 1757 struct vnode *tvp; 1758 1759 unp = VTOUNIONFS(ap->a_vp); 1760 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1761 1762 return VOP_MMAP(tvp, ap->a_prot, ap->a_cred); 1763} 1764 1765static int 1766unionfs_abortop(void *v) 1767{ 1768 struct vop_abortop_args *ap = v; 1769 struct unionfs_node *unp; 1770 struct vnode *tvp; 1771 1772 unp = VTOUNIONFS(ap->a_dvp); 1773 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1774 1775 return VOP_ABORTOP(tvp, ap->a_cnp); 1776} 1777 1778static int 1779unionfs_islocked(void *v) 1780{ 1781 struct vop_islocked_args *ap = v; 1782 struct unionfs_node *unp; 1783 struct vnode *tvp; 1784 1785 unp = VTOUNIONFS(ap->a_vp); 1786 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1787 1788 return VOP_ISLOCKED(tvp); 1789} 1790 1791static int 1792unionfs_seek(void *v) 1793{ 1794 struct vop_seek_args *ap = v; 1795 struct unionfs_node *unp; 1796 struct vnode *tvp; 1797 1798 unp = VTOUNIONFS(ap->a_vp); 1799 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1800 1801 return VOP_SEEK(tvp, ap->a_oldoff, ap->a_newoff, ap->a_cred); 1802} 1803 1804static int 1805unionfs_putpages(void *v) 1806{ 1807 struct vop_putpages_args /* { 1808 struct vnode *a_vp; 1809 voff_t a_offlo; 1810 voff_t a_offhi; 1811 int a_flags; 1812 } */ *ap = v; 1813 struct vnode *vp = ap->a_vp, *tvp; 1814 struct unionfs_node *unp; 1815 1816 KASSERT(rw_lock_held(vp->v_uobj.vmobjlock)); 1817 1818 unp = VTOUNIONFS(vp); 1819 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1820 KASSERT(tvp->v_uobj.vmobjlock == vp->v_uobj.vmobjlock); 1821 1822 if (ap->a_flags & PGO_RECLAIM) { 1823 rw_exit(vp->v_uobj.vmobjlock); 1824 return 0; 1825 } 1826 return VOP_PUTPAGES(tvp, ap->a_offlo, ap->a_offhi, ap->a_flags); 1827} 1828 1829static int 1830unionfs_getpages(void *v) 1831{ 1832 struct vop_getpages_args /* { 1833 struct vnode *a_vp; 1834 voff_t a_offset; 1835 struct vm_page **a_m; 1836 int *a_count; 1837 int a_centeridx; 1838 vm_prot_t a_access_type; 1839 int a_advice; 1840 int a_flags; 1841 } */ *ap = v; 1842 struct vnode *vp = ap->a_vp, *tvp; 1843 struct unionfs_node *unp; 1844 1845 KASSERT(rw_lock_held(vp->v_uobj.vmobjlock)); 1846 1847 unp = VTOUNIONFS(vp); 1848 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1849 KASSERT(tvp->v_uobj.vmobjlock == vp->v_uobj.vmobjlock); 1850 1851 if (ap->a_flags & PGO_LOCKED) { 1852 return EBUSY; 1853 } 1854 return VOP_GETPAGES(tvp, ap->a_offset, ap->a_m, ap->a_count, 1855 ap->a_centeridx, ap->a_access_type, ap->a_advice, ap->a_flags); 1856} 1857 1858static int 1859unionfs_revoke(void *v) 1860{ 1861 struct vop_revoke_args *ap = v; 1862 struct unionfs_node *unp; 1863 struct vnode *tvp; 1864 int error; 1865 1866 unp = VTOUNIONFS(ap->a_vp); 1867 tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp); 1868 1869 error = VOP_REVOKE(tvp, ap->a_flags); 1870 if (error == 0) { 1871 vgone(ap->a_vp); /* ??? */ 1872 } 1873 return error; 1874} 1875 1876/* 1877 * Global vfs data structures 1878 */ 1879int (**unionfs_vnodeop_p)(void *); 1880const struct vnodeopv_entry_desc unionfs_vnodeop_entries[] = { 1881 { &vop_default_desc, vn_default_error }, 1882 { &vop_parsepath_desc, unionfs_parsepath }, /* parsepath */ 1883 { &vop_lookup_desc, unionfs_lookup }, /* lookup */ 1884 { &vop_create_desc, unionfs_create }, /* create */ 1885 { &vop_whiteout_desc, unionfs_whiteout }, /* whiteout */ 1886 { &vop_mknod_desc, unionfs_mknod }, /* mknod */ 1887 { &vop_open_desc, unionfs_open }, /* open */ 1888 { &vop_close_desc, unionfs_close }, /* close */ 1889 { &vop_access_desc, unionfs_access }, /* access */ 1890 { &vop_accessx_desc, genfs_accessx }, /* accessx */ 1891 { &vop_getattr_desc, unionfs_getattr }, /* getattr */ 1892 { &vop_setattr_desc, unionfs_setattr }, /* setattr */ 1893 { &vop_read_desc, unionfs_read }, /* read */ 1894 { &vop_write_desc, unionfs_write }, /* write */ 1895 { &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */ 1896 { &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */ 1897 { &vop_ioctl_desc, unionfs_ioctl }, /* ioctl */ 1898 { &vop_poll_desc, unionfs_poll }, /* select */ 1899 { &vop_revoke_desc, unionfs_revoke }, /* revoke */ 1900 { &vop_mmap_desc, unionfs_mmap }, /* mmap */ 1901 { &vop_fsync_desc, unionfs_fsync }, /* fsync */ 1902 { &vop_seek_desc, unionfs_seek }, /* seek */ 1903 { &vop_remove_desc, unionfs_remove }, /* remove */ 1904 { &vop_link_desc, unionfs_link }, /* link */ 1905 { &vop_rename_desc, unionfs_rename }, /* rename */ 1906 { &vop_mkdir_desc, unionfs_mkdir }, /* mkdir */ 1907 { &vop_rmdir_desc, unionfs_rmdir }, /* rmdir */ 1908 { &vop_symlink_desc, unionfs_symlink }, /* symlink */ 1909 { &vop_readdir_desc, unionfs_readdir }, /* readdir */ 1910 { &vop_readlink_desc, unionfs_readlink }, /* readlink */ 1911 { &vop_abortop_desc, unionfs_abortop }, /* abortop */ 1912 { &vop_inactive_desc, unionfs_inactive }, /* inactive */ 1913 { &vop_reclaim_desc, unionfs_reclaim }, /* reclaim */ 1914 { &vop_lock_desc, unionfs_lock }, /* lock */ 1915 { &vop_unlock_desc, unionfs_unlock }, /* unlock */ 1916 { &vop_bmap_desc, unionfs_bmap }, /* bmap */ 1917 { &vop_strategy_desc, unionfs_strategy }, /* strategy */ 1918 { &vop_print_desc, unionfs_print }, /* print */ 1919 { &vop_islocked_desc, unionfs_islocked }, /* islocked */ 1920 { &vop_pathconf_desc, unionfs_pathconf }, /* pathconf */ 1921 { &vop_advlock_desc, unionfs_advlock }, /* advlock */ 1922 { &vop_getpages_desc, unionfs_getpages }, /* getpages */ 1923 { &vop_putpages_desc, unionfs_putpages }, /* putpages */ 1924 { &vop_kqfilter_desc, unionfs_kqfilter }, /* kqfilter */ 1925#ifdef notdef 1926 { &vop_bwrite_desc, unionfs_bwrite }, /* bwrite */ 1927#endif 1928 { NULL, NULL } 1929}; 1930const struct vnodeopv_desc unionfs_vnodeop_opv_desc = 1931 { &unionfs_vnodeop_p, unionfs_vnodeop_entries }; 1932