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