*
* %sccs.include.redist.c%
*
- * @(#)union_vnops.c 8.5 (Berkeley) %G%
+ * @(#)union_vnops.c 8.10 (Berkeley) %G%
*/
#include <sys/param.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
-union_lookup1(udvp, dvp, vpp, cnp)
+union_lookup1(udvp, dvpp, vpp, cnp)
struct vnode *udvp;
- struct vnode *dvp;
+ struct vnode **dvpp;
struct vnode **vpp;
struct componentname *cnp;
{
int error;
struct vnode *tdvp;
+ struct vnode *dvp;
struct mount *mp;
+ dvp = *dvpp;
+
/*
* If stepping up the directory tree, check for going
* back across the mount point, in which case do what
* 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.
*/
- if ((dvp->v_flag & VROOT) == 0)
- break;
-
tdvp = dvp;
- dvp = dvp->v_mount->mnt_vnodecovered;
+ *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
vput(tdvp);
VREF(dvp);
VOP_LOCK(dvp);
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;
* 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;*/
* back from the upper layer and return the lower vnode
* instead.
*/
- if (lowerdvp) {
+ if (lowerdvp != NULLVP) {
int nameiop;
VOP_LOCK(lowerdvp);
*/
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);
+ 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) {
- if (uppervp) {
+ if (uppervp != NULLVP) {
if (uppervp == upperdvp)
vrele(uppervp);
else
dun->un_flags |= UN_ULOCK;
if (uerror) {
- if (lowervp) {
+ if (lowervp != NULLVP) {
vput(lowervp);
lowervp = NULLVP;
}
}
}
- if (lowervp)
+ if (lowervp != NULLVP)
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);
- if (lowervp)
+ if (lowervp != NULLVP)
vrele(lowervp);
} else {
if (*ap->a_vpp != dvp)
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
- if (dvp) {
+ if (dvp != NULLVP) {
int error;
struct vnode *vp;
+ FIXUP(un);
+
VREF(dvp);
un->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
- if (dvp) {
+ if (dvp != NULLVP) {
int error;
struct vnode *vp;
+ FIXUP(un);
+
VREF(dvp);
un->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
if (error)
return (error);
- if (vp) {
+ if (vp != NULLVP) {
error = union_allocvp(
ap->a_vpp,
ap->a_dvp->v_mount,
VOP_UNLOCK(tvp);
}
+#ifdef UNION_DIAGNOSTIC
if (!error)
uprintf("union: copied up %s\n",
un->un_path);
+#endif
}
un->un_flags &= ~UN_ULOCK;
return (error);
}
+ FIXUP(un);
+
error = VOP_OPEN(tvp, mode, cred, p);
return (error);
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
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);
+ 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);
}
- if (vp = un->un_uppervp)
- error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
-
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)
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);
+ union_newsize(ap->a_vp, vap->va_size, VNOVAL);
}
if (vp == NULLVP) {
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))
* 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);
+ if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
+ union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
} else {
error = EROFS;
}
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);
+ /*
+ * 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);
}
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);
+ /*
+ * 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);
}
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);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
error = VOP_FSYNC(targetvp, ap->a_cred,
ap->a_waitfor, ap->a_p);
if (dolock)
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;
+ FIXUP(dun);
VREF(dvp);
dun->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
+ FIXUP(un);
VREF(vp);
un->un_flags |= UN_KLOCK;
vput(ap->a_vp);
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;
+ FIXUP(dun);
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 {
/*
- * XXX: need to copy to upper layer
+ * XXX: perhaps could copy to upper layer
* and do the link there.
*/
vput(ap->a_vp);
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;
}
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);
- if (un->un_uppervp == NULLVP) {
- error = EROFS;
- goto bad;
- }
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);
}
vrele(fdvp);
vrele(fvp);
vput(tdvp);
- if (tvp)
+ if (tvp != NULLVP)
vput(tvp);
return (error);
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
- if (dvp) {
+ if (dvp != NULLVP) {
int error;
struct vnode *vp;
+ FIXUP(un);
VREF(dvp);
un->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
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;
+ FIXUP(dun);
VREF(dvp);
dun->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
+ FIXUP(un);
VREF(vp);
un->un_flags |= UN_KLOCK;
vput(ap->a_vp);
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;
+ FIXUP(un);
VREF(dvp);
un->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
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);
+ }
return (error);
}
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);
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);
struct vnode *a_vp;
} */ *ap;
{
+ struct union_node *un = VTOUNION(ap->a_vp);
/*
* Do nothing (and _don't_ bypass).
*/
#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_ULOCK)
+ panic("union: inactivating w/locked upper node");
#endif
+ if ((un->un_flags & UN_CACHED) == 0)
+ vgone(ap->a_vp);
+
return (0);
}
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 (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);
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);