*
* %sccs.include.redist.c%
*
- * @(#)union_vnops.c 1.9 (Berkeley) %G%
+ * @(#)union_vnops.c 8.18 (Berkeley) %G%
*/
#include <sys/param.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>
+
+#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;
+
+#ifdef notyet
+ if (cnp->cn_namelen == 3 &&
+ cnp->cn_nameptr[2] == '.' &&
+ cnp->cn_nameptr[1] == '.' &&
+ cnp->cn_nameptr[0] == '.') {
+ dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
+ if (dvp == NULLVP)
+ return (ENOENT);
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(ap->a_dvp);
+ return (0);
+ }
+#endif
cnp->cn_flags |= LOCKPARENT;
* then assume that something special is going
* on and just return that vnode.
*/
- if (upperdvp) {
- VOP_LOCK(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)
- VOP_UNLOCK(upperdvp);
+ /*if (uppervp == upperdvp)
+ dun->un_flags |= UN_KLOCK;*/
if (cnp->cn_consume != 0) {
*ap->a_vpp = uppervp;
* back from the upper layer and return the lower vnode
* instead.
*/
- if (lowerdvp) {
+ if (lowerdvp != NULLVP) {
+ int nameiop;
+
VOP_LOCK(lowerdvp);
- lerror = union_lookup1(um->um_lowervp, lowerdvp,
- &lowervp, cnp);
+
+ /*
+ * 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;
+ 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) {
- vput(uppervp);
+ if (uppervp != NULLVP) {
+ if (uppervp == upperdvp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
uppervp = NULLVP;
}
*ap->a_vpp = lowervp;
}
} else {
lerror = ENOENT;
+ if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
+ lowervp = LOWERVP(dun->un_pvp);
+ if (lowervp != NULLVP) {
+ VREF(lowervp);
+ VOP_LOCK(lowervp);
+ lerror = 0;
+ }
+ }
}
if (!lockparent)
/* 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);
+ VOP_LOCK(upperdvp);
+ dun->un_flags |= UN_ULOCK;
+
if (uerror) {
- if (lowervp) {
+ if (lowervp != NULLVP) {
vput(lowervp);
lowervp = NULLVP;
}
}
}
- if (uppervp)
- VOP_UNLOCK(uppervp);
- 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)
- vrele(uppervp);
- if (lowervp)
+ if (uppervp != NULLVP)
+ vput(uppervp);
+ 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);
- 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);
- VOP_UNLOCK(vp);
-
error = union_allocvp(
ap->a_vpp,
ap->a_dvp->v_mount,
vp,
NULLVP);
if (error)
- vrele(vp);
+ vput(vp);
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);
- 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) {
- VOP_UNLOCK(vp);
-
+ if (vp != NULLVP) {
error = union_allocvp(
ap->a_vpp,
ap->a_dvp->v_mount,
vp,
NULLVP);
if (error)
- vrele(vp);
+ vput(vp);
}
return (error);
}
*/
tvp = un->un_lowervp;
if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
- struct vnode *vp;
- int i;
-
- /*
- * Open the named file in the upper layer. Note that
- * the file may have come into existence *since* the
- * lookup was done, since the upper layer may really
- * be a loopback mount of some other filesystem...
- * so open the file with exclusive create and barf if
- * it already exists.
- * XXX - perhaps should re-lookup the node (once more
- * with feeling) and simply open that. Who knows.
- */
- error = union_vn_create(&vp, un, p);
- if (error)
- return (error);
- un->un_uppervp = vp; /* XXX */
- /* at this point, uppervp is locked */
-
- /*
- * Now, if the file is being opened with truncation,
- * then the (new) upper vnode is ready to fly,
- * otherwise the data from the lower vnode must be
- * copied to the upper layer first. This only works
- * for regular files (check is made above).
- */
- if ((mode & O_TRUNC) == 0) {
- /*
- * XXX - should not ignore errors
- * from VOP_CLOSE
- */
- VOP_LOCK(tvp);
- error = VOP_OPEN(tvp, FREAD, cred, p);
- if (error == 0) {
- error = union_copyfile(p, cred,
- tvp, un->un_uppervp);
- VOP_UNLOCK(tvp);
- (void) VOP_CLOSE(tvp, FREAD);
- } 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);
- }
-
- /*
- * Subsequent IOs will go to the top layer, so
- * call close on the lower vnode and open on the
- * upper vnode to ensure that the filesystem keeps
- * its references counts right. This doesn't do
- * the right thing with (cred) and (FREAD) though.
- * Ignoring error returns is not righ, either.
- */
- for (i = 0; i < un->un_openl; i++) {
- (void) VOP_CLOSE(tvp, FREAD);
- (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p);
- }
- un->un_openl = 0;
-
+ error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
if (error == 0)
error = VOP_OPEN(un->un_uppervp, mode, cred, p);
- VOP_UNLOCK(un->un_uppervp);
return (error);
}
+
+ /*
+ * Just open the lower vnode
+ */
un->un_openl++;
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, mode, cred, p);
+ VOP_UNLOCK(tvp);
+
+ return (error);
}
- VOP_LOCK(tvp);
+ FIXUP(un);
+
error = VOP_OPEN(tvp, mode, cred, p);
- VOP_UNLOCK(tvp);
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
} */ *ap;
{
struct union_node *un = VTOUNION(ap->a_vp);
- int error = 0;
+ int error = EACCES;
struct vnode *vp;
- if (vp = un->un_lowervp) {
- VOP_LOCK(vp);
- error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
- VOP_UNLOCK(vp);
- if (error)
- return (error);
+ 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_uppervp) {
+ 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);
}
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)
} */ *ap;
{
int error;
- struct vnode *vp = OTHERVP(ap->a_vp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = un->un_uppervp;
+ struct vattr *vap;
+ struct vattr va;
+
+
+ /*
+ * Some programs walk the filesystem hierarchy by counting
+ * links to directories to avoid stat'ing all the time.
+ * This means the link count on directories needs to be "correct".
+ * The only way to do that is to call getattr on both layers
+ * and fix up the link count. The link count will not necessarily
+ * be accurate but will be large enough to defeat the tree walkers.
+ */
+
+ vap = ap->a_vap;
+
+ 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);
+ }
- VOP_LOCK(vp);
- error = VOP_GETATTR(vp, ap->a_vap, ap->a_cred, ap->a_p);
- VOP_UNLOCK(vp);
+ if (vp == NULLVP) {
+ vp = un->un_lowervp;
+ } else if (vp->v_type == VDIR) {
+ vp = un->un_lowervp;
+ vap = &va;
+ } else {
+ vp = NULLVP;
+ }
+
+ if (vp != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ 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))
+ ap->a_vap->va_nlink += vap->va_nlink;
- /* Requires that arguments be restored. */
ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
return (0);
}
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) {
+ FIXUP(un);
error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
ap->a_cred, ap->a_p);
- VOP_UNLOCK(un->un_uppervp);
+ if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
+ union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
} else {
- /*
- * XXX should do a copyfile (perhaps only if
- * the file permission change, which would not
- * track va_ctime correctly).
- */
error = EROFS;
}
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
- 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);
- VOP_UNLOCK(vp);
+ 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);
}
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
- 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);
- VOP_UNLOCK(vp);
+ 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) {
- VOP_LOCK(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);
- VOP_UNLOCK(targetvp);
+ if (dolock)
+ VOP_UNLOCK(targetvp);
}
return (error);
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);
- VOP_LOCK(dvp);
+ dun->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
+ FIXUP(un);
VREF(vp);
- VOP_LOCK(vp);
+ un->un_flags |= UN_KLOCK;
vput(ap->a_vp);
error = VOP_REMOVE(dvp, vp, ap->a_cnp);
struct componentname *a_cnp;
} */ *ap;
{
- int error;
- struct union_node *dun = VTOUNION(ap->a_vp);
- struct union_node *un = VTOUNION(ap->a_tdvp);
-
- if (dun->un_uppervp && un->un_uppervp) {
- struct vnode *dvp = dun->un_uppervp;
- struct vnode *vp = un->un_uppervp;
+ int error = 0;
+ struct union_node *un;
+ struct vnode *vp;
+ struct vnode *tdvp;
- VREF(dvp);
- VOP_LOCK(dvp);
- vput(ap->a_vp);
- VREF(vp);
- vrele(ap->a_tdvp);
+ un = VTOUNION(ap->a_vp);
- error = VOP_LINK(dvp, vp, ap->a_cnp);
+ if (ap->a_vp->v_op != ap->a_tdvp->v_op) {
+ tdvp = ap->a_tdvp;
} else {
- /*
- * XXX: need to copy to upper layer
- * and do the link there.
- */
- vput(ap->a_vp);
- vrele(ap->a_tdvp);
+ struct union_node *tdun = VTOUNION(ap->a_tdvp);
+ if (tdun->un_uppervp == NULLVP) {
+ VOP_LOCK(ap->a_tdvp);
+ if (un->un_uppervp == tdun->un_dirvp) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+ error = union_copyup(tdun, 1, ap->a_cnp->cn_cred,
+ ap->a_cnp->cn_proc);
+ if (un->un_uppervp == tdun->un_dirvp) {
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+ }
+ VOP_UNLOCK(ap->a_tdvp);
+ }
+ tdvp = tdun->un_uppervp;
+ }
+
+ vp = un->un_uppervp;
+ if (vp == NULLVP)
error = EROFS;
+
+ if (error) {
+ vput(ap->a_vp);
+ return (error);
}
- return (error);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ return (VOP_LINK(vp, tdvp, ap->a_cnp));
}
int
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;
}
tdvp = un->un_uppervp;
VREF(tdvp);
- VOP_LOCK(tdvp);
+ un->un_flags |= UN_KLOCK;
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);
- VOP_LOCK(tvp);
+ 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);
- 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);
- VOP_UNLOCK(vp);
error = union_allocvp(
ap->a_vpp,
ap->a_dvp->v_mount,
vp,
NULLVP);
if (error)
- vrele(vp);
+ vput(vp);
return (error);
}
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);
- VOP_LOCK(dvp);
+ dun->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
+ FIXUP(un);
VREF(vp);
- VOP_LOCK(vp);
+ un->un_flags |= UN_KLOCK;
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);
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);
- 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);
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int a_ncookies;
} */ *ap;
{
- int error = 0;
- struct union_node *un = VTOUNION(ap->a_vp);
-
- if (un->un_uppervp) {
- struct vnode *vp = un->un_uppervp;
+ register struct union_node *un = VTOUNION(ap->a_vp);
+ register struct vnode *uvp = un->un_uppervp;
- VOP_LOCK(vp);
- error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
- VOP_UNLOCK(vp);
- }
+ if (uvp == NULLVP)
+ return (0);
- return (error);
+ FIXUP(un);
+ ap->a_vp = uvp;
+ return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap));
}
int
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
- VOP_LOCK(vp);
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
- VOP_UNLOCK(vp);
+ if (dolock)
+ VOP_UNLOCK(vp);
return (error);
}
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)
- 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)
+ if (islocked && dolock)
VOP_UNLOCK(vp);
return (error);
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);
}
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);
}
union_lock(ap)
struct vop_lock_args *ap;
{
- struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un;
+
+start:
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+
+ un = VTOUNION(vp);
+
+ if (un->un_uppervp != NULLVP) {
+ if (((un->un_flags & UN_ULOCK) == 0) &&
+ (vp->v_usecount != 0)) {
+ un->un_flags |= UN_ULOCK;
+ VOP_LOCK(un->un_uppervp);
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK)
+ panic("union: dangling upper lock");
+#endif
+ }
- while (un->un_flags & UN_LOCKED) {
+ if (un->un_flags & UN_LOCKED) {
#ifdef DIAGNOSTIC
if (curproc && un->un_pid == curproc->p_pid &&
un->un_pid > -1 && curproc->p_pid > -1)
#endif
un->un_flags |= UN_WANT;
sleep((caddr_t) &un->un_flags, PINOD);
+ goto start;
}
- un->un_flags |= UN_LOCKED;
#ifdef DIAGNOSTIC
if (curproc)
un->un_pid = -1;
#endif
+ un->un_flags |= UN_LOCKED;
return (0);
}
#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);
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
- 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);
- VOP_UNLOCK(vp);
+ if (dolock)
+ VOP_UNLOCK(vp);
return (error);
}
{
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
- VOP_LOCK(vp);
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
- VOP_UNLOCK(vp);
+ if (dolock)
+ VOP_UNLOCK(vp);
return (error);
}