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