keep track of vnode size changes.
[unix-history] / usr / src / sys / miscfs / union / union_vnops.c
index be50978..f165b98 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_vnops.c       8.5 (Berkeley) %G%
+ *     @(#)union_vnops.c       8.10 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
 #include <sys/queue.h>
 #include <miscfs/union/union.h>
 
 #include <sys/queue.h>
 #include <miscfs/union/union.h>
 
+#define FIXUP(un) { \
+       if (((un)->un_flags & UN_ULOCK) == 0) { \
+               union_fixup(un); \
+       } \
+}
+
+static void
+union_fixup(un)
+       struct union_node *un;
+{
+
+       VOP_LOCK(un->un_uppervp);
+       un->un_flags |= UN_ULOCK;
+}
+
 static int
 static int
-union_lookup1(udvp, dvp, vpp, cnp)
+union_lookup1(udvp, dvpp, vpp, cnp)
        struct vnode *udvp;
        struct vnode *udvp;
-       struct vnode *dvp;
+       struct vnode **dvpp;
        struct vnode **vpp;
        struct componentname *cnp;
 {
        int error;
        struct vnode *tdvp;
        struct vnode **vpp;
        struct componentname *cnp;
 {
        int error;
        struct vnode *tdvp;
+       struct vnode *dvp;
        struct mount *mp;
 
        struct mount *mp;
 
+       dvp = *dvpp;
+
        /*
         * If stepping up the directory tree, check for going
         * back across the mount point, in which case do what
        /*
         * If stepping up the directory tree, check for going
         * back across the mount point, in which case do what
@@ -43,18 +61,15 @@ union_lookup1(udvp, dvp, vpp, cnp)
         * hierarchy.
         */
        if (cnp->cn_flags & ISDOTDOT) {
         * hierarchy.
         */
        if (cnp->cn_flags & ISDOTDOT) {
-               for (;;) {
+               while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
                        /*
                         * Don't do the NOCROSSMOUNT check
                         * at this level.  By definition,
                         * union fs deals with namespaces, not
                         * filesystems.
                         */
                        /*
                         * 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;
                        tdvp = dvp;
-                       dvp = dvp->v_mount->mnt_vnodecovered;
+                       *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
                        vput(tdvp);
                        VREF(dvp);
                        VOP_LOCK(dvp);
                        vput(tdvp);
                        VREF(dvp);
                        VOP_LOCK(dvp);
@@ -121,6 +136,7 @@ union_lookup(ap)
        int lockparent = cnp->cn_flags & LOCKPARENT;
        int rdonly = cnp->cn_flags & RDONLY;
        struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
        int lockparent = cnp->cn_flags & LOCKPARENT;
        int rdonly = cnp->cn_flags & RDONLY;
        struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+       struct ucred *saved_cred;
 
        cnp->cn_flags |= LOCKPARENT;
 
 
        cnp->cn_flags |= LOCKPARENT;
 
@@ -135,8 +151,9 @@ union_lookup(ap)
         * then assume that something special is going
         * on and just return that vnode.
         */
         * then assume that something special is going
         * on and just return that vnode.
         */
-       if (upperdvp) {
-               uerror = union_lookup1(um->um_uppervp, upperdvp,
+       if (upperdvp != NULLVP) {
+               FIXUP(dun);
+               uerror = union_lookup1(um->um_uppervp, &upperdvp,
                                        &uppervp, cnp);
                /*if (uppervp == upperdvp)
                        dun->un_flags |= UN_KLOCK;*/
                                        &uppervp, cnp);
                /*if (uppervp == upperdvp)
                        dun->un_flags |= UN_KLOCK;*/
@@ -158,7 +175,7 @@ union_lookup(ap)
         * back from the upper layer and return the lower vnode
         * instead.
         */
         * back from the upper layer and return the lower vnode
         * instead.
         */
-       if (lowerdvp) {
+       if (lowerdvp != NULLVP) {
                int nameiop;
 
                VOP_LOCK(lowerdvp);
                int nameiop;
 
                VOP_LOCK(lowerdvp);
@@ -169,15 +186,21 @@ union_lookup(ap)
                 */
                nameiop = cnp->cn_nameiop;
                cnp->cn_nameiop = LOOKUP;
                 */
                nameiop = cnp->cn_nameiop;
                cnp->cn_nameiop = LOOKUP;
-               lerror = union_lookup1(um->um_lowervp, lowerdvp,
+               if (um->um_op == UNMNT_BELOW) {
+                       saved_cred = cnp->cn_cred;
+                       cnp->cn_cred = um->um_cred;
+               }
+               lerror = union_lookup1(um->um_lowervp, &lowerdvp,
                                &lowervp, cnp);
                                &lowervp, cnp);
+               if (um->um_op == UNMNT_BELOW)
+                       cnp->cn_cred = saved_cred;
                cnp->cn_nameiop = nameiop;
 
                if (lowervp != lowerdvp)
                        VOP_UNLOCK(lowerdvp);
 
                if (cnp->cn_consume != 0) {
                cnp->cn_nameiop = nameiop;
 
                if (lowervp != lowerdvp)
                        VOP_UNLOCK(lowerdvp);
 
                if (cnp->cn_consume != 0) {
-                       if (uppervp) {
+                       if (uppervp != NULLVP) {
                                if (uppervp == upperdvp)
                                        vrele(uppervp);
                                else
                                if (uppervp == upperdvp)
                                        vrele(uppervp);
                                else
@@ -237,7 +260,7 @@ union_lookup(ap)
                        dun->un_flags |= UN_ULOCK;
 
                        if (uerror) {
                        dun->un_flags |= UN_ULOCK;
 
                        if (uerror) {
-                               if (lowervp) {
+                               if (lowervp != NULLVP) {
                                        vput(lowervp);
                                        lowervp = NULLVP;
                                }
                                        vput(lowervp);
                                        lowervp = NULLVP;
                                }
@@ -246,16 +269,16 @@ union_lookup(ap)
                }
        }
 
                }
        }
 
-       if (lowervp)
+       if (lowervp != NULLVP)
                VOP_UNLOCK(lowervp);
 
        error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
                              uppervp, lowervp);
 
        if (error) {
                VOP_UNLOCK(lowervp);
 
        error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
                              uppervp, lowervp);
 
        if (error) {
-               if (uppervp)
+               if (uppervp != NULLVP)
                        vput(uppervp);
                        vput(uppervp);
-               if (lowervp)
+               if (lowervp != NULLVP)
                        vrele(lowervp);
        } else {
                if (*ap->a_vpp != dvp)
                        vrele(lowervp);
        } else {
                if (*ap->a_vpp != dvp)
@@ -278,10 +301,12 @@ union_create(ap)
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
-       if (dvp) {
+       if (dvp != NULLVP) {
                int error;
                struct vnode *vp;
 
                int error;
                struct vnode *vp;
 
+               FIXUP(un);
+
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
@@ -318,10 +343,12 @@ union_mknod(ap)
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
-       if (dvp) {
+       if (dvp != NULLVP) {
                int error;
                struct vnode *vp;
 
                int error;
                struct vnode *vp;
 
+               FIXUP(un);
+
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
@@ -329,7 +356,7 @@ union_mknod(ap)
                if (error)
                        return (error);
 
                if (error)
                        return (error);
 
-               if (vp) {
+               if (vp != NULLVP) {
                        error = union_allocvp(
                                        ap->a_vpp,
                                        ap->a_dvp->v_mount,
                        error = union_allocvp(
                                        ap->a_vpp,
                                        ap->a_dvp->v_mount,
@@ -421,9 +448,11 @@ union_open(ap)
                                        VOP_UNLOCK(tvp);
                                }
 
                                        VOP_UNLOCK(tvp);
                                }
 
+#ifdef UNION_DIAGNOSTIC
                                if (!error)
                                        uprintf("union: copied up %s\n",
                                                                un->un_path);
                                if (!error)
                                        uprintf("union: copied up %s\n",
                                                                un->un_path);
+#endif
                        }
 
                        un->un_flags &= ~UN_ULOCK;
                        }
 
                        un->un_flags &= ~UN_ULOCK;
@@ -462,6 +491,8 @@ union_open(ap)
                return (error);
        }
 
                return (error);
        }
 
+       FIXUP(un);
+
        error = VOP_OPEN(tvp, mode, cred, p);
 
        return (error);
        error = VOP_OPEN(tvp, mode, cred, p);
 
        return (error);
@@ -479,7 +510,7 @@ union_close(ap)
        struct union_node *un = VTOUNION(ap->a_vp);
        struct vnode *vp;
 
        struct union_node *un = VTOUNION(ap->a_vp);
        struct vnode *vp;
 
-       if (un->un_uppervp) {
+       if (un->un_uppervp != NULLVP) {
                vp = un->un_uppervp;
        } else {
 #ifdef UNION_DIAGNOSTIC
                vp = un->un_uppervp;
        } else {
 #ifdef UNION_DIAGNOSTIC
@@ -515,22 +546,32 @@ union_access(ap)
        int error = EACCES;
        struct vnode *vp;
 
        int error = EACCES;
        struct vnode *vp;
 
-       if (vp = un->un_lowervp) {
+       if ((vp = un->un_uppervp) != NULLVP) {
+               FIXUP(un);
+               return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+       }
+
+       if ((vp = un->un_lowervp) != NULLVP) {
                VOP_LOCK(vp);
                error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
                VOP_LOCK(vp);
                error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+               if (error == 0) {
+                       struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
+
+                       if (um->um_op == UNMNT_BELOW)
+                               error = VOP_ACCESS(vp, ap->a_mode,
+                                               um->um_cred, ap->a_p);
+               }
                VOP_UNLOCK(vp);
                if (error)
                        return (error);
        }
 
                VOP_UNLOCK(vp);
                if (error)
                        return (error);
        }
 
-       if (vp = un->un_uppervp)
-               error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
-
        return (error);
 }
 
 /*
        return (error);
 }
 
 /*
- *  We handle getattr only to change the fsid.
+ * We handle getattr only to change the fsid and
+ * track object sizes
  */
 int
 union_getattr(ap)
  */
 int
 union_getattr(ap)
@@ -561,9 +602,21 @@ union_getattr(ap)
 
        vp = un->un_uppervp;
        if (vp != NULLVP) {
 
        vp = un->un_uppervp;
        if (vp != NULLVP) {
+               /*
+                * It's not clear whether VOP_GETATTR is to be
+                * called with the vnode locked or not.  stat() calls
+                * it with (vp) locked, and fstat calls it with
+                * (vp) unlocked.
+                * In the mean time, compensate here by checking
+                * the union_node's lock flag.
+                */
+               if (un->un_flags & UN_LOCKED)
+                       FIXUP(un);
+
                error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
                if (error)
                        return (error);
                error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
                if (error)
                        return (error);
+               union_newsize(ap->a_vp, vap->va_size, VNOVAL);
        }
 
        if (vp == NULLVP) {
        }
 
        if (vp == NULLVP) {
@@ -581,6 +634,7 @@ union_getattr(ap)
                VOP_UNLOCK(vp);
                if (error)
                        return (error);
                VOP_UNLOCK(vp);
                if (error)
                        return (error);
+               union_newsize(ap->a_vp, VNOVAL, vap->va_size);
        }
 
        if ((vap != ap->a_vap) && (vap->va_type == VDIR))
        }
 
        if ((vap != ap->a_vap) && (vap->va_type == VDIR))
@@ -631,8 +685,11 @@ union_setattr(ap)
         * otherwise return read-only filesystem error.
         */
        if (un->un_uppervp != NULLVP) {
         * otherwise return read-only filesystem error.
         */
        if (un->un_uppervp != NULLVP) {
+               FIXUP(un);
                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);
+               if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
+                       union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
        } else {
                error = EROFS;
        }
        } else {
                error = EROFS;
        }
@@ -655,10 +712,31 @@ union_read(ap)
 
        if (dolock)
                VOP_LOCK(vp);
 
        if (dolock)
                VOP_LOCK(vp);
+       else
+               FIXUP(VTOUNION(ap->a_vp));
        error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
 
        error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
 
+       /*
+        * XXX
+        * perhaps the size of the underlying object has changed under
+        * our feet.  take advantage of the offset information present
+        * in the uio structure.
+        */
+       if (error == 0) {
+               struct union_node *un = VTOUNION(ap->a_vp);
+               off_t cur = ap->a_uio->uio_offset;
+
+               if (vp == un->un_uppervp) {
+                       if (cur > un->un_uppersz)
+                               union_newsize(ap->a_vp, cur, VNOVAL);
+               } else {
+                       if (cur > un->un_lowersz)
+                               union_newsize(ap->a_vp, VNOVAL, cur);
+               }
+       }
+
        return (error);
 }
 
        return (error);
 }
 
@@ -677,10 +755,29 @@ union_write(ap)
 
        if (dolock)
                VOP_LOCK(vp);
 
        if (dolock)
                VOP_LOCK(vp);
+       else
+               FIXUP(VTOUNION(ap->a_vp));
        error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
 
        error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
 
+       /*
+        * the size of the underlying object may be changed by the
+        * write.
+        */
+       if (error == 0) {
+               struct union_node *un = VTOUNION(ap->a_vp);
+               off_t cur = ap->a_uio->uio_offset;
+
+               if (vp == un->un_uppervp) {
+                       if (cur > un->un_uppersz)
+                               union_newsize(ap->a_vp, cur, VNOVAL);
+               } else {
+                       if (cur > un->un_lowersz)
+                               union_newsize(ap->a_vp, VNOVAL, cur);
+               }
+       }
+
        return (error);
 }
 
        return (error);
 }
 
@@ -741,11 +838,13 @@ union_fsync(ap)
        int error = 0;
        struct vnode *targetvp = OTHERVP(ap->a_vp);
 
        int error = 0;
        struct vnode *targetvp = OTHERVP(ap->a_vp);
 
-       if (targetvp) {
+       if (targetvp != NULLVP) {
                int dolock = (targetvp == LOWERVP(ap->a_vp));
 
                if (dolock)
                        VOP_LOCK(targetvp);
                int dolock = (targetvp == LOWERVP(ap->a_vp));
 
                if (dolock)
                        VOP_LOCK(targetvp);
+               else
+                       FIXUP(VTOUNION(ap->a_vp));
                error = VOP_FSYNC(targetvp, ap->a_cred,
                                        ap->a_waitfor, ap->a_p);
                if (dolock)
                error = VOP_FSYNC(targetvp, ap->a_cred,
                                        ap->a_waitfor, ap->a_p);
                if (dolock)
@@ -780,13 +879,15 @@ union_remove(ap)
        struct union_node *dun = VTOUNION(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_vp);
 
        struct union_node *dun = VTOUNION(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_vp);
 
-       if (dun->un_uppervp && un->un_uppervp) {
+       if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
+               FIXUP(dun);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
+               FIXUP(un);
                VREF(vp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
                VREF(vp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
@@ -822,20 +923,22 @@ union_link(ap)
        struct union_node *dun = VTOUNION(ap->a_vp);
        struct union_node *un = VTOUNION(ap->a_tdvp);
 
        struct union_node *dun = VTOUNION(ap->a_vp);
        struct union_node *un = VTOUNION(ap->a_tdvp);
 
-       if (dun->un_uppervp && un->un_uppervp) {
+       if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
+               FIXUP(dun);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
+               FIXUP(un);
                VREF(vp);
                vrele(ap->a_tdvp);
 
                error = VOP_LINK(dvp, vp, ap->a_cnp);
        } else {
                /*
                VREF(vp);
                vrele(ap->a_tdvp);
 
                error = VOP_LINK(dvp, vp, ap->a_cnp);
        } else {
                /*
-                * XXX: need to copy to upper layer
+                * XXX: perhaps could copy to upper layer
                 * and do the link there.
                 */
                vput(ap->a_vp);
                 * and do the link there.
                 */
                vput(ap->a_vp);
@@ -891,6 +994,12 @@ union_rename(ap)
        if (tdvp->v_op == union_vnodeop_p) {
                struct union_node *un = VTOUNION(tdvp);
                if (un->un_uppervp == NULLVP) {
        if (tdvp->v_op == union_vnodeop_p) {
                struct union_node *un = VTOUNION(tdvp);
                if (un->un_uppervp == NULLVP) {
+                       /*
+                        * this should never happen in normal
+                        * operation but might if there was
+                        * a problem creating the top-level shadow
+                        * directory.
+                        */
                        error = EROFS;
                        goto bad;
                }
                        error = EROFS;
                        goto bad;
                }
@@ -901,16 +1010,14 @@ union_rename(ap)
                vput(ap->a_tdvp);
        }
 
                vput(ap->a_tdvp);
        }
 
-       if (tvp && tvp->v_op == union_vnodeop_p) {
+       if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
                struct union_node *un = VTOUNION(tvp);
                struct union_node *un = VTOUNION(tvp);
-               if (un->un_uppervp == NULLVP) {
-                       error = EROFS;
-                       goto bad;
-               }
 
                tvp = un->un_uppervp;
 
                tvp = un->un_uppervp;
-               VREF(tvp);
-               un->un_flags |= UN_KLOCK;
+               if (tvp != NULLVP) {
+                       VREF(tvp);
+                       un->un_flags |= UN_KLOCK;
+               }
                vput(ap->a_tvp);
        }
 
                vput(ap->a_tvp);
        }
 
@@ -920,7 +1027,7 @@ bad:
        vrele(fdvp);
        vrele(fvp);
        vput(tdvp);
        vrele(fdvp);
        vrele(fvp);
        vput(tdvp);
-       if (tvp)
+       if (tvp != NULLVP)
                vput(tvp);
 
        return (error);
                vput(tvp);
 
        return (error);
@@ -938,10 +1045,11 @@ union_mkdir(ap)
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
-       if (dvp) {
+       if (dvp != NULLVP) {
                int error;
                struct vnode *vp;
 
                int error;
                struct vnode *vp;
 
+               FIXUP(un);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
@@ -978,13 +1086,15 @@ union_rmdir(ap)
        struct union_node *dun = VTOUNION(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_vp);
 
        struct union_node *dun = VTOUNION(ap->a_dvp);
        struct union_node *un = VTOUNION(ap->a_vp);
 
-       if (dun->un_uppervp && un->un_uppervp) {
+       if (dun->un_uppervp != NULLVP && un->un_uppervp != NULLVP) {
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
                struct vnode *dvp = dun->un_uppervp;
                struct vnode *vp = un->un_uppervp;
 
+               FIXUP(dun);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                dun->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
+               FIXUP(un);
                VREF(vp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
                VREF(vp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_vp);
@@ -1021,11 +1131,12 @@ union_symlink(ap)
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
        struct union_node *un = VTOUNION(ap->a_dvp);
        struct vnode *dvp = un->un_uppervp;
 
-       if (dvp) {
+       if (dvp != NULLVP) {
                int error;
                struct vnode *vp;
                struct mount *mp = ap->a_dvp->v_mount;
 
                int error;
                struct vnode *vp;
                struct mount *mp = ap->a_dvp->v_mount;
 
+               FIXUP(un);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
                VREF(dvp);
                un->un_flags |= UN_KLOCK;
                vput(ap->a_dvp);
@@ -1058,8 +1169,10 @@ 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)
+       if (un->un_uppervp != NULLVP) {
+               FIXUP(un);
                error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
                error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
+       }
 
        return (error);
 }
 
        return (error);
 }
@@ -1078,6 +1191,8 @@ union_readlink(ap)
 
        if (dolock)
                VOP_LOCK(vp);
 
        if (dolock)
                VOP_LOCK(vp);
+       else
+               FIXUP(VTOUNION(ap->a_vp));
        error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
        error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
        if (dolock)
                VOP_UNLOCK(vp);
@@ -1098,8 +1213,12 @@ union_abortop(ap)
        int islocked = un->un_flags & UN_LOCKED;
        int dolock = (vp == LOWERVP(ap->a_dvp));
 
        int islocked = un->un_flags & UN_LOCKED;
        int dolock = (vp == LOWERVP(ap->a_dvp));
 
-       if (islocked && dolock)
-               VOP_LOCK(vp);
+       if (islocked) {
+               if (dolock)
+                       VOP_LOCK(vp);
+               else
+                       FIXUP(VTOUNION(ap->a_dvp));
+       }
        error = VOP_ABORTOP(vp, ap->a_cnp);
        if (islocked && dolock)
                VOP_UNLOCK(vp);
        error = VOP_ABORTOP(vp, ap->a_cnp);
        if (islocked && dolock)
                VOP_UNLOCK(vp);
@@ -1113,6 +1232,7 @@ union_inactive(ap)
                struct vnode *a_vp;
        } */ *ap;
 {
                struct vnode *a_vp;
        } */ *ap;
 {
+       struct union_node *un = VTOUNION(ap->a_vp);
 
        /*
         * Do nothing (and _don't_ bypass).
 
        /*
         * Do nothing (and _don't_ bypass).
@@ -1128,12 +1248,15 @@ union_inactive(ap)
         */
 
 #ifdef UNION_DIAGNOSTIC
         */
 
 #ifdef UNION_DIAGNOSTIC
-       struct union_node *un = VTOUNION(ap->a_vp);
-
        if (un->un_flags & UN_LOCKED)
                panic("union: inactivating locked node");
        if (un->un_flags & UN_LOCKED)
                panic("union: inactivating locked node");
+       if (un->un_flags & UN_ULOCK)
+               panic("union: inactivating w/locked upper node");
 #endif
 
 #endif
 
+       if ((un->un_flags & UN_CACHED) == 0)
+               vgone(ap->a_vp);
+
        return (0);
 }
 
        return (0);
 }
 
@@ -1164,7 +1287,7 @@ start:
 
        un = VTOUNION(vp);
 
 
        un = VTOUNION(vp);
 
-       if (un->un_uppervp) {
+       if (un->un_uppervp != NULLVP) {
                if ((un->un_flags & UN_ULOCK) == 0) {
                        un->un_flags |= UN_ULOCK;
                        VOP_LOCK(un->un_uppervp);
                if ((un->un_flags & UN_ULOCK) == 0) {
                        un->un_flags |= UN_ULOCK;
                        VOP_LOCK(un->un_uppervp);
@@ -1246,6 +1369,8 @@ union_bmap(ap)
 
        if (dolock)
                VOP_LOCK(vp);
 
        if (dolock)
                VOP_LOCK(vp);
+       else
+               FIXUP(VTOUNION(ap->a_vp));
        error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
        if (dolock)
                VOP_UNLOCK(vp);
        error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
        if (dolock)
                VOP_UNLOCK(vp);
@@ -1290,6 +1415,8 @@ union_pathconf(ap)
 
        if (dolock)
                VOP_LOCK(vp);
 
        if (dolock)
                VOP_LOCK(vp);
+       else
+               FIXUP(VTOUNION(ap->a_vp));
        error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
        if (dolock)
                VOP_UNLOCK(vp);
        error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
        if (dolock)
                VOP_UNLOCK(vp);