make sure open(..., O_TRUNC) works right.
[unix-history] / usr / src / sys / miscfs / union / union_vnops.c
index 0885775..1f55175 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_vnops.c       1.8 (Berkeley) %G%
+ *     @(#)union_vnops.c       8.3 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -22,7 +22,8 @@
 #include <sys/namei.h>
 #include <sys/malloc.h>
 #include <sys/buf.h>
 #include <sys/namei.h>
 #include <sys/malloc.h>
 #include <sys/buf.h>
-#include "union.h"
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
 
 static int
 union_lookup1(udvp, dvp, vpp, cnp)
 
 static int
 union_lookup1(udvp, dvp, vpp, cnp)
@@ -43,8 +44,13 @@ union_lookup1(udvp, dvp, vpp, cnp)
         */
        if (cnp->cn_flags & ISDOTDOT) {
                for (;;) {
         */
        if (cnp->cn_flags & ISDOTDOT) {
                for (;;) {
-                       if ((dvp->v_flag & VROOT) == 0 ||
-                           (cnp->cn_flags & NOCROSSMOUNT))
+                       /*
+                        * Don't do the NOCROSSMOUNT check
+                        * at this level.  By definition,
+                        * union fs deals with namespaces, not
+                        * filesystems.
+                        */
+                       if ((dvp->v_flag & VROOT) == 0)
                                break;
 
                        tdvp = dvp;
                                break;
 
                        tdvp = dvp;
@@ -71,12 +77,11 @@ union_lookup1(udvp, dvp, vpp, cnp)
 
        /*
         * Lastly check if the current node is a mount point in
 
        /*
         * Lastly check if the current node is a mount point in
-        * which cse walk up the mount hierarchy making sure not to
+        * which case walk up the mount hierarchy making sure not to
         * bump into the root of the mount tree (ie. dvp != udvp).
         */
        while (dvp != udvp && (dvp->v_type == VDIR) &&
         * bump into the root of the mount tree (ie. dvp != udvp).
         */
        while (dvp != udvp && (dvp->v_type == VDIR) &&
-              (mp = dvp->v_mountedhere) &&
-              (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+              (mp = dvp->v_mountedhere)) {
 
                if (mp->mnt_flag & MNT_MLOCK) {
                        mp->mnt_flag |= MNT_MWAIT;
 
                if (mp->mnt_flag & MNT_MLOCK) {
                        mp->mnt_flag |= MNT_MWAIT;
@@ -131,11 +136,10 @@ union_lookup(ap)
         * on and just return that vnode.
         */
        if (upperdvp) {
         * on and just return that vnode.
         */
        if (upperdvp) {
-               VOP_LOCK(upperdvp);
                uerror = union_lookup1(um->um_uppervp, upperdvp,
                                        &uppervp, cnp);
                uerror = union_lookup1(um->um_uppervp, upperdvp,
                                        &uppervp, cnp);
-               if (uppervp != upperdvp)
-                       VOP_UNLOCK(upperdvp);
+               /*if (uppervp == upperdvp)
+                       dun->un_flags |= UN_KLOCK;*/
 
                if (cnp->cn_consume != 0) {
                        *ap->a_vpp = uppervp;
 
                if (cnp->cn_consume != 0) {
                        *ap->a_vpp = uppervp;
@@ -155,15 +159,29 @@ union_lookup(ap)
         * instead.
         */
        if (lowerdvp) {
         * instead.
         */
        if (lowerdvp) {
+               int nameiop;
+
                VOP_LOCK(lowerdvp);
                VOP_LOCK(lowerdvp);
+
+               /*
+                * Only do a LOOKUP on the bottom node, since
+                * we won't be making changes to it anyway.
+                */
+               nameiop = cnp->cn_nameiop;
+               cnp->cn_nameiop = LOOKUP;
                lerror = union_lookup1(um->um_lowervp, lowerdvp,
                lerror = union_lookup1(um->um_lowervp, lowerdvp,
-                                       &lowervp, cnp);
+                               &lowervp, cnp);
+               cnp->cn_nameiop = nameiop;
+
                if (lowervp != lowerdvp)
                        VOP_UNLOCK(lowerdvp);
 
                if (cnp->cn_consume != 0) {
                        if (uppervp) {
                if (lowervp != lowerdvp)
                        VOP_UNLOCK(lowerdvp);
 
                if (cnp->cn_consume != 0) {
                        if (uppervp) {
-                               vput(uppervp);
+                               if (uppervp == upperdvp)
+                                       vrele(uppervp);
+                               else
+                                       vput(uppervp);
                                uppervp = NULLVP;
                        }
                        *ap->a_vpp = lowervp;
                                uppervp = NULLVP;
                        }
                        *ap->a_vpp = lowervp;
@@ -212,7 +230,12 @@ union_lookup(ap)
        /* case 2. */
        if (uerror != 0 /* && (lerror == 0) */ ) {
                if (lowervp->v_type == VDIR) { /* case 2b. */
        /* case 2. */
        if (uerror != 0 /* && (lerror == 0) */ ) {
                if (lowervp->v_type == VDIR) { /* case 2b. */
+                       dun->un_flags &= ~UN_ULOCK;
+                       VOP_UNLOCK(upperdvp);
                        uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
                        uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
+                       VOP_LOCK(upperdvp);
+                       dun->un_flags |= UN_ULOCK;
+
                        if (uerror) {
                                if (lowervp) {
                                        vput(lowervp);
                        if (uerror) {
                                if (lowervp) {
                                        vput(lowervp);
@@ -223,8 +246,6 @@ union_lookup(ap)
                }
        }
 
                }
        }
 
-       if (uppervp)
-               VOP_UNLOCK(uppervp);
        if (lowervp)
                VOP_UNLOCK(lowervp);
 
        if (lowervp)
                VOP_UNLOCK(lowervp);
 
@@ -233,7 +254,7 @@ union_lookup(ap)
 
        if (error) {
                if (uppervp)
 
        if (error) {
                if (uppervp)
-                       vrele(uppervp);
+                       vput(uppervp);
                if (lowervp)
                        vrele(lowervp);
        } else {
                if (lowervp)
                        vrele(lowervp);
        } else {
@@ -262,14 +283,12 @@ union_create(ap)
                struct vnode *vp;
 
                VREF(dvp);
                struct vnode *vp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
                vput(ap->a_dvp);
                error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
-               VOP_UNLOCK(vp);
-
                error = union_allocvp(
                                ap->a_vpp,
                                ap->a_dvp->v_mount,
                error = union_allocvp(
                                ap->a_vpp,
                                ap->a_dvp->v_mount,
@@ -279,7 +298,7 @@ union_create(ap)
                                vp,
                                NULLVP);
                if (error)
                                vp,
                                NULLVP);
                if (error)
-                       vrele(vp);
+                       vput(vp);
                return (error);
        }
 
                return (error);
        }
 
@@ -304,15 +323,13 @@ union_mknod(ap)
                struct vnode *vp;
 
                VREF(dvp);
                struct vnode *vp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
                if (vp) {
                vput(ap->a_dvp);
                error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
                if (vp) {
-                       VOP_UNLOCK(vp);
-
                        error = union_allocvp(
                                        ap->a_vpp,
                                        ap->a_dvp->v_mount,
                        error = union_allocvp(
                                        ap->a_vpp,
                                        ap->a_dvp->v_mount,
@@ -322,7 +339,7 @@ union_mknod(ap)
                                        vp,
                                        NULLVP);
                        if (error)
                                        vp,
                                        NULLVP);
                        if (error)
-                               vrele(vp);
+                               vput(vp);
                }
                return (error);
        }
                }
                return (error);
        }
@@ -376,8 +393,10 @@ union_open(ap)
                        error = union_vn_create(&vp, un, p);
                        if (error)
                                return (error);
                        error = union_vn_create(&vp, un, p);
                        if (error)
                                return (error);
-                       un->un_uppervp = vp;    /* XXX */
+
                        /* at this point, uppervp is locked */
                        /* at this point, uppervp is locked */
+                       union_newupper(un, vp);
+                       un->un_flags |= UN_ULOCK;
 
                        /*
                         * Now, if the file is being opened with truncation,
 
                        /*
                         * Now, if the file is being opened with truncation,
@@ -401,14 +420,18 @@ union_open(ap)
                                } else {
                                        VOP_UNLOCK(tvp);
                                }
                                } else {
                                        VOP_UNLOCK(tvp);
                                }
-                               VOP_UNLOCK(un->un_uppervp);
-                               union_vn_close(un->un_uppervp, FWRITE, cred, p);
-                               VOP_LOCK(un->un_uppervp);
+
                                if (!error)
                                        uprintf("union: copied up %s\n",
                                                                un->un_path);
                        }
 
                                if (!error)
                                        uprintf("union: copied up %s\n",
                                                                un->un_path);
                        }
 
+                       un->un_flags &= ~UN_ULOCK;
+                       VOP_UNLOCK(un->un_uppervp);
+                       union_vn_close(un->un_uppervp, FWRITE, cred, p);
+                       VOP_LOCK(un->un_uppervp);
+                       un->un_flags |= UN_ULOCK;
+
                        /*
                         * Subsequent IOs will go to the top layer, so
                         * call close on the lower vnode and open on the
                        /*
                         * Subsequent IOs will go to the top layer, so
                         * call close on the lower vnode and open on the
@@ -425,15 +448,21 @@ union_open(ap)
 
                        if (error == 0)
                                error = VOP_OPEN(un->un_uppervp, mode, cred, p);
 
                        if (error == 0)
                                error = VOP_OPEN(un->un_uppervp, mode, cred, p);
-                       VOP_UNLOCK(un->un_uppervp);
                        return (error);
                }
                        return (error);
                }
+
+               /*
+                * Just open the lower vnode
+                */
                un->un_openl++;
                un->un_openl++;
+               VOP_LOCK(tvp);
+               error = VOP_OPEN(tvp, mode, cred, p);
+               VOP_UNLOCK(tvp);
+
+               return (error);
        }
 
        }
 
-       VOP_LOCK(tvp);
        error = VOP_OPEN(tvp, mode, cred, p);
        error = VOP_OPEN(tvp, mode, cred, p);
-       VOP_UNLOCK(tvp);
 
        return (error);
 }
 
        return (error);
 }
@@ -494,11 +523,8 @@ union_access(ap)
                        return (error);
        }
 
                        return (error);
        }
 
-       if (vp = un->un_uppervp) {
-               VOP_LOCK(vp);
+       if (vp = un->un_uppervp)
                error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
                error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
-               VOP_UNLOCK(vp);
-       }
 
        return (error);
 }
 
        return (error);
 }
@@ -517,10 +543,13 @@ union_getattr(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_GETATTR(vp, ap->a_vap, ap->a_cred, ap->a_p);
        error = VOP_GETATTR(vp, ap->a_vap, ap->a_cred, ap->a_p);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        /* Requires that arguments be restored. */
        ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
 
        /* Requires that arguments be restored. */
        ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
@@ -539,17 +568,38 @@ union_setattr(ap)
        struct union_node *un = VTOUNION(ap->a_vp);
        int error;
 
        struct union_node *un = VTOUNION(ap->a_vp);
        int error;
 
-       if (un->un_uppervp) {
-               VOP_LOCK(un->un_uppervp);
+       /*
+        * Handle case of truncating lower object to zero size,
+        * by creating a zero length upper object.  This is to
+        * handle the case of open with O_TRUNC and O_CREAT.
+        */
+       if ((un->un_uppervp == NULLVP) &&
+           /* assert(un->un_lowervp != NULLVP) */
+           (un->un_lowervp->v_type == VREG) &&
+           (ap->a_vap->va_size == 0)) {
+               struct vnode *vp;
+
+               error = union_vn_create(&vp, un, ap->a_p);
+               if (error)
+                       return (error);
+
+               /* at this point, uppervp is locked */
+               union_newupper(un, vp);
+
+               VOP_UNLOCK(vp);
+               union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
+               VOP_LOCK(vp);
+               un->un_flags |= UN_ULOCK;
+       }
+
+       /*
+        * Try to set attributes in upper layer,
+        * otherwise return read-only filesystem error.
+        */
+       if (un->un_uppervp != NULLVP) {
                error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
                                        ap->a_cred, ap->a_p);
                error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
                                        ap->a_cred, ap->a_p);
-               VOP_UNLOCK(un->un_uppervp);
        } else {
        } else {
-               /*
-                * XXX should do a copyfile (perhaps only if
-                * the file permission change, which would not
-                * track va_ctime correctly).
-                */
                error = EROFS;
        }
 
                error = EROFS;
        }
 
@@ -567,10 +617,13 @@ union_read(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        return (error);
 }
 
        return (error);
 }
@@ -586,10 +639,13 @@ union_write(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        return (error);
 }
 
        return (error);
 }
@@ -652,10 +708,14 @@ union_fsync(ap)
        struct vnode *targetvp = OTHERVP(ap->a_vp);
 
        if (targetvp) {
        struct vnode *targetvp = OTHERVP(ap->a_vp);
 
        if (targetvp) {
-               VOP_LOCK(targetvp);
+               int dolock = (targetvp == LOWERVP(ap->a_vp));
+
+               if (dolock)
+                       VOP_LOCK(targetvp);
                error = VOP_FSYNC(targetvp, ap->a_cred,
                                        ap->a_waitfor, ap->a_p);
                error = VOP_FSYNC(targetvp, ap->a_cred,
                                        ap->a_waitfor, ap->a_p);
-               VOP_UNLOCK(targetvp);
+               if (dolock)
+                       VOP_UNLOCK(targetvp);
        }
 
        return (error);
        }
 
        return (error);
@@ -691,10 +751,10 @@ union_remove(ap)
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(vp);
                vput(ap->a_dvp);
                VREF(vp);
-               VOP_LOCK(vp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
 
                error = VOP_REMOVE(dvp, vp, ap->a_cnp);
                vput(ap->a_vp);
 
                error = VOP_REMOVE(dvp, vp, ap->a_cnp);
@@ -733,7 +793,7 @@ union_link(ap)
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               dun->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
                VREF(vp);
                vrele(ap->a_tdvp);
                vput(ap->a_vp);
                VREF(vp);
                vrele(ap->a_tdvp);
@@ -803,7 +863,7 @@ union_rename(ap)
 
                tdvp = un->un_uppervp;
                VREF(tdvp);
 
                tdvp = un->un_uppervp;
                VREF(tdvp);
-               VOP_LOCK(tdvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_tdvp);
        }
 
                vput(ap->a_tdvp);
        }
 
@@ -816,7 +876,7 @@ union_rename(ap)
 
                tvp = un->un_uppervp;
                VREF(tvp);
 
                tvp = un->un_uppervp;
                VREF(tvp);
-               VOP_LOCK(tvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_tvp);
        }
 
                vput(ap->a_tvp);
        }
 
@@ -849,13 +909,12 @@ union_mkdir(ap)
                struct vnode *vp;
 
                VREF(dvp);
                struct vnode *vp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
                vput(ap->a_dvp);
                error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
                if (error)
                        return (error);
 
-               VOP_UNLOCK(vp);
                error = union_allocvp(
                                ap->a_vpp,
                                ap->a_dvp->v_mount,
                error = union_allocvp(
                                ap->a_vpp,
                                ap->a_dvp->v_mount,
@@ -865,7 +924,7 @@ union_mkdir(ap)
                                vp,
                                NULLVP);
                if (error)
                                vp,
                                NULLVP);
                if (error)
-                       vrele(vp);
+                       vput(vp);
                return (error);
        }
 
                return (error);
        }
 
@@ -890,13 +949,13 @@ union_rmdir(ap)
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
                struct vnode *vp = un->un_uppervp;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(vp);
                vput(ap->a_dvp);
                VREF(vp);
-               VOP_LOCK(vp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
 
                vput(ap->a_vp);
 
-               error = VOP_REMOVE(dvp, vp, ap->a_cnp);
+               error = VOP_RMDIR(dvp, vp, ap->a_cnp);
                if (!error)
                        union_removed_upper(un);
 
                if (!error)
                        union_removed_upper(un);
 
@@ -934,7 +993,7 @@ union_symlink(ap)
                struct mount *mp = ap->a_dvp->v_mount;
 
                VREF(dvp);
                struct mount *mp = ap->a_dvp->v_mount;
 
                VREF(dvp);
-               VOP_LOCK(dvp);
+               un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
                                        ap->a_vap, ap->a_target);
                vput(ap->a_dvp);
                error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
                                        ap->a_vap, ap->a_target);
@@ -965,13 +1024,8 @@ union_readdir(ap)
        int error = 0;
        struct union_node *un = VTOUNION(ap->a_vp);
 
        int error = 0;
        struct union_node *un = VTOUNION(ap->a_vp);
 
-       if (un->un_uppervp) {
-               struct vnode *vp = un->un_uppervp;
-
-               VOP_LOCK(vp);
-               error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
-               VOP_UNLOCK(vp);
-       }
+       if (un->un_uppervp)
+               error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
 
        return (error);
 }
 
        return (error);
 }
@@ -986,10 +1040,13 @@ union_readlink(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
        error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        return (error);
 }
 
        return (error);
 }
@@ -1005,11 +1062,12 @@ union_abortop(ap)
        struct vnode *vp = OTHERVP(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_dvp);
        int islocked = un->un_flags & UN_LOCKED;
        struct vnode *vp = OTHERVP(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_dvp);
        int islocked = un->un_flags & UN_LOCKED;
+       int dolock = (vp == LOWERVP(ap->a_dvp));
 
 
-       if (islocked)
+       if (islocked && dolock)
                VOP_LOCK(vp);
        error = VOP_ABORTOP(vp, ap->a_cnp);
                VOP_LOCK(vp);
        error = VOP_ABORTOP(vp, ap->a_cnp);
-       if (islocked)
+       if (islocked && dolock)
                VOP_UNLOCK(vp);
 
        return (error);
                VOP_UNLOCK(vp);
 
        return (error);
@@ -1051,31 +1109,9 @@ union_reclaim(ap)
                struct vnode *a_vp;
        } */ *ap;
 {
                struct vnode *a_vp;
        } */ *ap;
 {
-       struct vnode *vp = ap->a_vp;
-       struct union_node *un = VTOUNION(vp);
-       struct vnode *uppervp = un->un_uppervp;
-       struct vnode *lowervp = un->un_lowervp;
-       struct vnode *dirvp = un->un_dirvp;
-       char *path = un->un_path;
 
 
-       /*
-        * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p,
-        * so we can't call VOPs on ourself.
-        */
-       /* After this assignment, this node will not be re-used. */
-       un->un_uppervp = NULLVP;
-       un->un_lowervp = NULLVP;
-       un->un_dirvp = NULLVP;
-       un->un_path = NULL;
-       union_freevp(vp);
-       if (uppervp)
-               vrele(uppervp);
-       if (lowervp)
-               vrele(lowervp);
-       if (dirvp)
-               vrele(dirvp);
-       if (path)
-               free(path, M_TEMP);
+       union_freevp(ap->a_vp);
+
        return (0);
 }
 
        return (0);
 }
 
@@ -1085,6 +1121,17 @@ union_lock(ap)
 {
        struct union_node *un = VTOUNION(ap->a_vp);
 
 {
        struct union_node *un = VTOUNION(ap->a_vp);
 
+       if (un->un_uppervp) {
+               if ((un->un_flags & UN_ULOCK) == 0) {
+                       VOP_LOCK(un->un_uppervp);
+                       un->un_flags |= UN_ULOCK;
+               }
+#ifdef DIAGNOSTIC
+               if (un->un_flags & UN_KLOCK)
+                       panic("union: dangling upper lock");
+#endif
+       }
+
        while (un->un_flags & UN_LOCKED) {
 #ifdef DIAGNOSTIC
                if (curproc && un->un_pid == curproc->p_pid &&
        while (un->un_flags & UN_LOCKED) {
 #ifdef DIAGNOSTIC
                if (curproc && un->un_pid == curproc->p_pid &&
@@ -1121,6 +1168,12 @@ union_unlock(ap)
 #endif
 
        un->un_flags &= ~UN_LOCKED;
 #endif
 
        un->un_flags &= ~UN_LOCKED;
+
+       if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
+               VOP_UNLOCK(un->un_uppervp);
+
+       un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
+
        if (un->un_flags & UN_WANT) {
                un->un_flags &= ~UN_WANT;
                wakeup((caddr_t) &un->un_flags);
        if (un->un_flags & UN_WANT) {
                un->un_flags &= ~UN_WANT;
                wakeup((caddr_t) &un->un_flags);
@@ -1145,10 +1198,13 @@ union_bmap(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
        error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        return (error);
 }
 
        return (error);
 }
@@ -1186,10 +1242,13 @@ union_pathconf(ap)
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
 {
        int error;
        struct vnode *vp = OTHERVP(ap->a_vp);
+       int dolock = (vp == LOWERVP(ap->a_vp));
 
 
-       VOP_LOCK(vp);
+       if (dolock)
+               VOP_LOCK(vp);
        error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
        error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
-       VOP_UNLOCK(vp);
+       if (dolock)
+               VOP_UNLOCK(vp);
 
        return (error);
 }
 
        return (error);
 }