*
* %sccs.include.redist.c%
*
- * @(#)union_vnops.c 1.2 (Berkeley) %G%
+ * @(#)union_vnops.c 8.10 (Berkeley) %G%
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/file.h>
-#include <sys/filedesc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.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_mkshadow(dvp, cnp, vpp)
- struct vnode *dvp;
- struct componentname *cnp;
- struct vnode *vpp;
-{
- int error;
- struct vattr va;
- struct proc *p = cnp->cn_proc;
- int lockparent = (cnp->cn_flags & LOCKPARENT);
+#define FIXUP(un) { \
+ if (((un)->un_flags & UN_ULOCK) == 0) { \
+ union_fixup(un); \
+ } \
+}
- /*
- * policy: when creating the shadow directory in the
- * upper layer, create it owned by the current user,
- * group from parent directory, and mode 777 modified
- * by umask (ie mostly identical to the mkdir syscall).
- * (jsp, kb)
- * TODO: create the directory owned by the user who
- * did the mount (um->um_cred).
- */
+static void
+union_fixup(un)
+ struct union_node *un;
+{
- VATTR_NULL(&va);
- va.va_type = VDIR;
- va.va_mode = UN_DIRMODE &~ p->p_fd->fd_cmask;
- if (lockparent)
- VOP_UNLOCK(dvp);
- LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
- VOP_LOCK(dvp);
- error = VOP_MKDIR(dvp, vpp, cnp, &va);
- if (lockparent)
- VOP_LOCK(dvp);
- return (error);
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
}
static int
-union_lookup1(dvp, vpp, cnp)
- struct vnode *dvp;
+union_lookup1(udvp, dvpp, vpp, cnp)
+ struct vnode *udvp;
+ struct vnode **dvpp;
struct vnode **vpp;
struct componentname *cnp;
{
int error;
struct vnode *tdvp;
+ struct vnode *dvp;
struct mount *mp;
- if (cnp->cn_flags & ISDOTDOT) {
- for (;;) {
- if ((dvp->v_flag & VROOT) == 0 ||
- (cnp->cn_flags & NOCROSSMOUNT))
- break;
+ dvp = *dvpp;
+ /*
+ * If stepping up the directory tree, check for going
+ * back across the mount point, in which case do what
+ * lookup would do by stepping back down the mount
+ * hierarchy.
+ */
+ if (cnp->cn_flags & ISDOTDOT) {
+ 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.
+ */
tdvp = dvp;
- dvp = dvp->v_mount->mnt_vnodecovered;
+ *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
vput(tdvp);
VREF(dvp);
VOP_LOCK(dvp);
}
}
-
+
error = VOP_LOOKUP(dvp, &tdvp, cnp);
if (error)
return (error);
+ /*
+ * The parent directory will have been unlocked, unless lookup
+ * found the last component. In which case, re-lock the node
+ * here to allow it to be unlocked again (phew) in union_lookup.
+ */
+ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
+ VOP_LOCK(dvp);
+
dvp = tdvp;
- while ((dvp->v_type == VDIR) && (mp = dvp->v_mountedhere) &&
- (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+
+ /*
+ * Lastly check if the current node is a mount point in
+ * 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) &&
+ (mp = dvp->v_mountedhere)) {
if (mp->mnt_flag & MNT_MLOCK) {
mp->mnt_flag |= MNT_MWAIT;
return (error);
}
- vput(tdvp);
+ vput(dvp);
dvp = tdvp;
}
struct componentname *a_cnp;
} */ *ap;
{
+ int error;
int uerror, lerror;
struct vnode *uppervp, *lowervp;
struct vnode *upperdvp, *lowerdvp;
struct vnode *dvp = ap->a_dvp;
- struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *dun = VTOUNION(dvp);
struct componentname *cnp = ap->a_cnp;
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;
upperdvp = dun->un_uppervp;
lowerdvp = dun->un_lowervp;
+ uppervp = NULLVP;
+ lowervp = NULLVP;
/*
* do the lookup in the upper level.
* then assume that something special is going
* on and just return that vnode.
*/
- uppervp = 0;
- if (upperdvp) {
- uerror = union_lookup1(upperdvp, &uppervp, cnp);
+ if (upperdvp != NULLVP) {
+ FIXUP(dun);
+ uerror = union_lookup1(um->um_uppervp, &upperdvp,
+ &uppervp, cnp);
+ /*if (uppervp == upperdvp)
+ dun->un_flags |= UN_KLOCK;*/
+
if (cnp->cn_consume != 0) {
*ap->a_vpp = uppervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
return (uerror);
}
- if (!lockparent)
- VOP_LOCK(upperdvp);
} else {
uerror = ENOENT;
}
* back from the upper layer and return the lower vnode
* instead.
*/
- lowervp = 0;
- if (lowerdvp) {
- lerror = union_lookup1(lowerdvp, &lowervp, cnp);
+ if (lowerdvp != NULLVP) {
+ int nameiop;
+
+ 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;
+ 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);
- uppervp = 0;
+ if (uppervp != NULLVP) {
+ if (uppervp == upperdvp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ uppervp = NULLVP;
}
*ap->a_vpp = lowervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
return (lerror);
}
- if (!lockparent)
- VOP_LOCK(lowerdvp);
} else {
lerror = ENOENT;
}
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+
/*
* at this point, we have uerror and lerror indicating
* possible errors with the lookups in the upper and lower
* whatever the bottom layer returned.
*/
+ *ap->a_vpp = NULLVP;
+
/* case 1. */
if ((uerror != 0) && (lerror != 0)) {
- *ap->a_vpp = 0;
return (uerror);
}
/* case 2. */
if (uerror != 0 /* && (lerror == 0) */ ) {
if (lowervp->v_type == VDIR) { /* case 2b. */
- uerror = union_mkshadow(upperdvp, cnp, &uppervp);
+ 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 = 0;
+ lowervp = NULLVP;
}
return (uerror);
}
}
}
- return (union_allocvp(ap->a_vpp, dvp->v_mount, dvp, cnp,
- uppervp, 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 != NULLVP)
+ vput(uppervp);
+ if (lowervp != NULLVP)
+ vrele(lowervp);
+ } else {
+ if (*ap->a_vpp != dvp)
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(dvp);
+ }
+
+ return (error);
}
int
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_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
if (error)
error = union_allocvp(
ap->a_vpp,
- mp,
- un->un_uppervp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
ap->a_cnp,
vp,
NULLVP);
+ if (error)
+ 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;
- 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_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
if (error)
return (error);
- error = union_allocvp(
- ap->a_vpp,
- mp,
- un->un_uppervp,
- ap->a_cnp,
- vp,
- NULLVP);
+ if (vp != NULLVP) {
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ }
return (error);
}
return (EROFS);
}
-/*
- * copyfile. copy the vnode (fvp) to the vnode (tvp)
- * using a sequence of reads and writes. both (fvp)
- * and (tvp) are locked on entry and exit.
- */
-static int
-union_copyfile(p, cred, fvp, tvp)
- struct proc *p;
- struct ucred *cred;
- struct vnode *fvp;
- struct vnode *tvp;
-{
- char *buf;
- struct uio uio;
- struct iovec iov;
- int error = 0;
- off_t offset;
-
- /*
- * strategy:
- * allocate a buffer of size MAXBSIZE.
- * loop doing reads and writes, keeping track
- * of the current uio offset.
- * give up at the first sign of trouble.
- */
-
- uio.uio_procp = p;
- uio.uio_segflg = UIO_SYSSPACE;
- offset = 0;
-
- VOP_UNLOCK(fvp); /* XXX */
- LEASE_CHECK(fvp, p, cred, LEASE_READ);
- VOP_LOCK(fvp); /* XXX */
- VOP_UNLOCK(tvp); /* XXX */
- LEASE_CHECK(tvp, p, cred, LEASE_WRITE);
- VOP_LOCK(tvp); /* XXX */
-
- buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
- do {
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- iov.iov_base = buf;
- iov.iov_len = MAXBSIZE;
- uio.uio_resid = iov.iov_len;
- uio.uio_offset = offset;
- uio.uio_rw = UIO_READ;
- error = VOP_READ(fvp, &uio, 0, cred);
-
- if (error == 0) {
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- iov.iov_base = buf;
- iov.iov_len = MAXBSIZE - uio.uio_resid;
- uio.uio_rw = UIO_WRITE;
- uio.uio_resid = iov.iov_len;
- uio.uio_offset = offset;
-
- do {
- error = VOP_WRITE(tvp, &uio, 0, cred);
- } while (error == 0 && uio.uio_resid > 0);
- if (error == 0)
- offset = uio.uio_offset;
- }
- } while ((uio.uio_resid == 0) && (error == 0));
-
- free(buf, M_TEMP);
- return (error);
-}
-
int
union_open(ap)
struct vop_open_args /* {
} */ *ap;
{
struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *tvp;
int mode = ap->a_mode;
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
+ int error;
/*
* If there is an existing upper vp then simply open that.
*/
- if (un->un_uppervp) {
- int error;
-
- VOP_LOCK(un->un_uppervp);
- error = VOP_OPEN(un->un_uppervp, mode, cred, p);
- VOP_UNLOCK(un->un_lowervp);
-
- return (error);
- }
-
- /*
- * If the lower vnode is being opened for writing, then
- * copy the file contents to the upper vnode and open that,
- * otherwise can simply open the lower vnode.
- */
- if ((ap->a_mode & FWRITE) && (un->un_lowervp->v_type == VREG)) {
- int error;
- struct nameidata nd;
- struct filedesc *fdp = p->p_fd;
- int fmode;
- int cmode;
-
+ tvp = un->un_uppervp;
+ if (tvp == NULLVP) {
/*
- * 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 shoudl re-lookup the node (once more with
- * feeling) and simply open that. Who knows.
+ * If the lower vnode is being opened for writing, then
+ * copy the file contents to the upper vnode and open that,
+ * otherwise can simply open the lower vnode.
*/
- NDINIT(&nd, CREATE, 0, UIO_SYSSPACE, un->un_path, p);
- fmode = (O_CREAT|O_TRUNC|O_EXCL);
- cmode = UN_FILEMODE & ~fdp->fd_cmask;
- error = vn_open(&nd, fmode, cmode);
- if (error)
- return (error);
- un->un_uppervp = nd.ni_vp; /* XXX */
- /* at this point, uppervp is locked */
+ 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);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * 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);
+ }
- /*
- * 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(un->un_lowervp);
- error = VOP_OPEN(un->un_lowervp, FREAD, cred, p);
- if (error == 0) {
- error = union_copyfile(p, cred,
- un->un_lowervp, un->un_uppervp);
- (void) VOP_CLOSE(un->un_lowervp, FREAD);
+#ifdef UNION_DIAGNOSTIC
+ if (!error)
+ uprintf("union: copied up %s\n",
+ un->un_path);
+#endif
}
- VOP_UNLOCK(un->un_lowervp);
+
+ un->un_flags &= ~UN_ULOCK;
VOP_UNLOCK(un->un_uppervp);
- (void) VOP_CLOSE(un->un_uppervp, FWRITE);
+ 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
+ * 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;
+
+ if (error == 0)
+ error = VOP_OPEN(un->un_uppervp, mode, cred, p);
+ return (error);
}
- if (error == 0)
- error = VOP_OPEN(un->un_uppervp, FREAD, cred, p);
+
+ /*
+ * Just open the lower vnode
+ */
+ un->un_openl++;
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, mode, cred, p);
+ VOP_UNLOCK(tvp);
+
return (error);
}
- return (VOP_OPEN(un->un_lowervp, mode, cred, p));
+ FIXUP(un);
+
+ error = VOP_OPEN(tvp, mode, cred, p);
+
+ return (error);
}
int
struct proc *a_p;
} */ *ap;
{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp;
+
+ if (un->un_uppervp != NULLVP) {
+ vp = un->un_uppervp;
+ } else {
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_openl <= 0)
+ panic("union: un_openl cnt");
+#endif
+ --un->un_openl;
+ vp = un->un_lowervp;
+ }
- return (VOP_CLOSE(OTHERVP(ap->a_vp), ap->a_fflag, ap->a_cred, ap->a_p));
+ return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
}
/*
} */ *ap;
{
struct union_node *un = VTOUNION(ap->a_vp);
+ int error = EACCES;
struct vnode *vp;
- if (vp = un->un_lowervp) {
- int 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_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)
- return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
-
- return (0);
+ 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 union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = un->un_uppervp;
+ struct vattr *vap;
+ struct vattr va;
- if (error = union_bypass(ap))
- return (error);
- /* Requires that arguments be restored. */
- ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+
+ /*
+ * 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);
+ }
+
+ 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;
+
+ vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
return (0);
}
int
-lofs_setattr(ap)
+union_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
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_LOCKvp);
+ 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_UNLOCKvp);
+ 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);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
} else {
/*
* XXX: should create a whiteout here
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);
- VOP_LOCK(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 (fdvp->v_op == union_vnodeop_p) { /* always true */
struct union_node *un = VTOUNION(fdvp);
- if (un->un_uppervp == 0) {
+ if (un->un_uppervp == NULLVP) {
error = EROFS;
goto bad;
}
if (fvp->v_op == union_vnodeop_p) { /* always true */
struct union_node *un = VTOUNION(fvp);
- if (un->un_uppervp == 0) {
+ if (un->un_uppervp == NULLVP) {
error = EROFS;
goto bad;
}
if (tdvp->v_op == union_vnodeop_p) {
struct union_node *un = VTOUNION(tdvp);
- if (un->un_uppervp == 0) {
+ 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);
- vput(ap->a_fdvp);
+ 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 == 0) {
- 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;
- 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_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
if (error)
error = union_allocvp(
ap->a_vpp,
- mp,
- un->un_uppervp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
ap->a_cnp,
vp,
NULLVP);
+ if (error)
+ 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);
+
+ /*
+ * XXX: should create a whiteout here
+ */
} else {
/*
* XXX: should create a whiteout here
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);
- if (error)
- return (error);
-
- error = union_allocvp(
- ap->a_vpp,
- mp,
- un->un_uppervp,
- ap->a_cnp,
- vp,
- NULLVP);
- vput(*ap->a_vpp);
+ *ap->a_vpp = NULLVP;
return (error);
}
int error = 0;
struct union_node *un = VTOUNION(ap->a_vp);
- if (un->un_uppervp) {
- struct vnode *vp = OTHERVP(ap->a_vp);
-
- VOP_LOCK(vp);
- error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
- VOP_UNLOCK(vp);
+ if (un->un_uppervp != NULLVP) {
+ FIXUP(un);
+ error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
}
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_READLINK(vp, ap->a_uio, ap->a_cred);
- VOP_UNLOCK(vp);
+ if (dolock)
+ VOP_UNLOCK(vp);
return (error);
}
} */ *ap;
{
int error;
- struct vnode *vp = OTHERVP(a->a_dvp);
+ 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).
* like they do in the name lookup cache code.
* That's too much work for now.
*/
+
+#ifdef UNION_DIAGNOSTIC
+ 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 = 0;
- un->un_lowervp = 0;
- un->un_dirvp = 0;
- 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) {
+ un->un_flags |= UN_ULOCK;
+ VOP_LOCK(un->un_uppervp);
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK)
+ panic("union: dangling upper lock");
+#endif
+ }
+
+ if (un->un_flags & UN_LOCKED) {
#ifdef DIAGNOSTIC
- if (un->un_pid == curproc->p_pid)
- panic("union: locking agsinst myself");
+ if (curproc && un->un_pid == curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: locking against myself");
#endif
- while (un->un_flags & UN_LOCKED) {
un->un_flags |= UN_WANT;
sleep((caddr_t) &un->un_flags, PINOD);
+ goto start;
}
- un->un_flags |= UN_LOCKED;
+
#ifdef DIAGNOSTIC
- un->un_pid = curproc->p_pid;
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
#endif
+
+ un->un_flags |= UN_LOCKED;
+ return (0);
}
int
struct union_node *un = VTOUNION(ap->a_vp);
#ifdef DIAGNOSTIC
- if (un->un_pid != curproc->p_pid)
- panic("union: unlocking other process's union node");
if ((un->un_flags & UN_LOCKED) == 0)
panic("union: unlock unlocked node");
+ if (curproc && un->un_pid != curproc->p_pid &&
+ curproc->p_pid > -1 && un->un_pid > -1)
+ panic("union: unlocking other process's union node");
#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);
#ifdef DIAGNOSTIC
un->un_pid = 0;
#endif
+
+ return (0);
}
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_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);
}
bp->b_vp = OTHERVP(bp->b_vp);
#ifdef DIAGNOSTIC
- if (bp->b_vp == 0)
+ if (bp->b_vp == NULLVP)
panic("union_strategy: nil vp");
if (((bp->b_flags & B_READ) == 0) &&
(bp->b_vp == LOWERVP(savedvp)))
* Global vfs data structures
*/
int (**union_vnodeop_p)();
-struct vnodeopv_entry_desc lofs_vnodeop_entries[] = {
+struct vnodeopv_entry_desc union_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, union_lookup }, /* lookup */
{ &vop_create_desc, union_create }, /* create */