summaryrefslogtreecommitdiff
path: root/sys/miscfs/union/union_vnops.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/miscfs/union/union_vnops.c')
-rw-r--r--sys/miscfs/union/union_vnops.c601
1 files changed, 362 insertions, 239 deletions
diff --git a/sys/miscfs/union/union_vnops.c b/sys/miscfs/union/union_vnops.c
index 2988085190f..657fee3db1b 100644
--- a/sys/miscfs/union/union_vnops.c
+++ b/sys/miscfs/union/union_vnops.c
@@ -1,10 +1,10 @@
-/* $OpenBSD: union_vnops.c,v 1.19 2002/06/08 18:43:34 art Exp $ */
-/* $NetBSD: union_vnops.c,v 1.30.4.1 1996/05/25 22:10:14 jtc Exp $ */
+/* $OpenBSD: union_vnops.c,v 1.20 2003/05/12 21:45:35 tedu Exp $ */
+/* $NetBSD: union_vnops.c,v 1.59 2002/09/27 15:37:48 provos Exp $ */
/*
- * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
- * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
- * All rights reserved.
+ * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
+ * Copyright (c) 1992, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
@@ -37,27 +37,29 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)union_vnops.c 8.22 (Berkeley) 12/10/94
+ * @(#)union_vnops.c 8.33 (Berkeley) 7/31/95
*/
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/queue.h>
+#include <sys/lock.h>
+
+#if 0
+#include <miscfs/genfs/genfs.h>
+#endif
#include <miscfs/union/union.h>
-/*
- * Global vfs data structures
- */
int union_lookup(void *);
int union_create(void *);
@@ -95,6 +97,14 @@ int union_pathconf(void *);
int union_advlock(void *);
int union_strategy(void *);
+static void union_fixup(struct union_node *, struct proc *);
+static int union_lookup1(struct vnode *, struct vnode **,
+ struct vnode **, struct componentname *);
+
+
+/*
+ * Global vfs data structures
+ */
int (**union_vnodeop_p)(void *);
struct vnodeopv_entry_desc union_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
@@ -133,26 +143,31 @@ struct vnodeopv_entry_desc union_vnodeop_entries[] = {
{ &vop_islocked_desc, union_islocked }, /* islocked */
{ &vop_pathconf_desc, union_pathconf }, /* pathconf */
{ &vop_advlock_desc, union_advlock }, /* advlock */
- { (struct vnodeop_desc*)NULL, (int(*)(void *))NULL }
+#ifdef notdef
+ { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, union_valloc }, /* valloc */
+ { &vop_vfree_desc, union_vfree }, /* vfree */
+ { &vop_truncate_desc, union_truncate }, /* truncate */
+ { &vop_update_desc, union_update }, /* update */
+ { &vop_bwrite_desc, union_bwrite }, /* bwrite */
+#endif
+ { NULL, NULL }
};
-struct vnodeopv_desc union_vnodeop_opv_desc =
+const struct vnodeopv_desc union_vnodeop_opv_desc =
{ &union_vnodeop_p, union_vnodeop_entries };
-#define FIXUP(un, p) { \
+#define FIXUP(un, _p) { \
if (((un)->un_flags & UN_ULOCK) == 0) { \
- union_fixup(un, p); \
+ union_fixup(un, _p); \
} \
}
-static void union_fixup(struct union_node *, struct proc *);
-static int union_lookup1(struct vnode *, struct vnode **,
- struct vnode **, struct componentname *);
-
static void
union_fixup(un, p)
struct union_node *un;
struct proc *p;
{
+
vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
un->un_flags |= UN_ULOCK;
}
@@ -194,7 +209,7 @@ union_lookup1(udvp, dvpp, vpp, cnp)
}
}
- error = VOP_LOOKUP(dvp, &tdvp, cnp);
+ error = VOP_LOOKUP(dvp, &tdvp, cnp);
if (error)
return (error);
@@ -214,19 +229,16 @@ union_lookup1(udvp, dvpp, vpp, cnp)
* bump into the root of the mount tree (ie. dvp != udvp).
*/
while (dvp != udvp && (dvp->v_type == VDIR) &&
- (mp = dvp->v_mountedhere)) {
+ (mp = dvp->v_mountedhere)) {
if (vfs_busy(mp, 0, 0, p))
continue;
-
+
error = VFS_ROOT(mp, &tdvp);
vfs_unbusy(mp, p);
-
vput(dvp);
-
- if (error) {
+ if (error)
return (error);
- }
dvp = tdvp;
}
@@ -270,11 +282,16 @@ union_lookup(v)
VREF(dvp);
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
if (!lockparent || !(cnp->cn_flags & ISLASTCN))
- VOP_UNLOCK(ap->a_dvp, 0, p, 0, p);
+ VOP_UNLOCK(ap->a_dvp, 0, p);
return (0);
}
#endif
+ if ((cnp->cn_flags & ISLASTCN) &&
+ (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+ (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
+ return (EROFS);
+
cnp->cn_flags |= LOCKPARENT;
upperdvp = dun->un_uppervp;
@@ -300,12 +317,13 @@ union_lookup(v)
* we would be hosed.
*/
if (cnp->cn_flags & ISDOTDOT) {
- /* retain lock on underlying VP: */
+ /* retain lock on underlying VP */
dun->un_flags |= UN_KLOCK;
VOP_UNLOCK(dvp, 0, p);
}
uerror = union_lookup1(um->um_uppervp, &upperdvp,
&uppervp, cnp);
+
if (cnp->cn_flags & ISDOTDOT) {
if (dun->un_uppervp == upperdvp) {
/*
@@ -324,10 +342,6 @@ union_lookup(v)
*/
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
}
-
- /*if (uppervp == upperdvp)
- dun->un_flags |= UN_KLOCK;*/
-
if (cnp->cn_consume != 0) {
*ap->a_vpp = uppervp;
if (!lockparent)
@@ -339,8 +353,7 @@ union_lookup(v)
iswhiteout = 1;
} else if (lowerdvp != NULLVP) {
lerror = VOP_GETATTR(upperdvp, &va,
- cnp->cn_cred,
- cnp->cn_proc);
+ cnp->cn_cred, cnp->cn_proc);
if (lerror == 0 && (va.va_flags & OPAQUE))
iswhiteout = 1;
}
@@ -415,6 +428,19 @@ union_lookup(v)
cnp->cn_flags &= ~LOCKPARENT;
/*
+ * EJUSTRETURN is used by underlying filesystems to indicate that
+ * a directory modification op was started successfully.
+ * This will only happen in the upper layer, since
+ * the lower layer only does LOOKUPs.
+ * If this union is mounted read-only, bounce it now.
+ */
+
+ if ((uerror == EJUSTRETURN) && (cnp->cn_flags & ISLASTCN) &&
+ (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+ ((cnp->cn_nameiop == CREATE) || (cnp->cn_nameiop == RENAME)))
+ uerror = EROFS;
+
+ /*
* at this point, we have uerror and lerror indicating
* possible errors with the lookups in the upper and lower
* layers. additionally, uppervp and lowervp are (locked)
@@ -440,6 +466,7 @@ union_lookup(v)
*ap->a_vpp = NULLVP;
+
/* case 1. */
if ((uerror != 0) && (lerror != 0)) {
return (uerror);
@@ -473,7 +500,7 @@ union_lookup(v)
VOP_UNLOCK(lowervp, 0, p);
error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
- uppervp, lowervp, 1);
+ uppervp, lowervp, 1);
if (error) {
if (uppervp != NULLVP)
@@ -485,12 +512,11 @@ union_lookup(v)
if (!lockparent || !(cnp->cn_flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
if (cnp->cn_namelen == 1 &&
- cnp->cn_nameptr[0] == '.' &&
- *ap->a_vpp != dvp) {
- panic("union_lookup returning. (%x) not same as startdir (%x)",
+ cnp->cn_nameptr[0] == '.' &&
+ *ap->a_vpp != dvp) {
+ panic("union_lookup -> . (%p) != startdir (%p)",
ap->a_vpp, dvp);
}
-
}
return (error);
@@ -507,11 +533,11 @@ union_create(v)
struct vattr *a_vap;
} */ *ap = v;
struct union_node *un = VTOUNION(ap->a_dvp);
- struct vnode *dvp;
+ struct vnode *dvp = un->un_uppervp;
struct componentname *cnp = ap->a_cnp;
struct proc *p = cnp->cn_proc;
- if ((dvp = un->un_uppervp) != NULLVP) {
+ if (dvp != NULLVP) {
int error;
struct vnode *vp;
struct mount *mp;
@@ -526,15 +552,8 @@ union_create(v)
if (error)
return (error);
- error = union_allocvp(
- ap->a_vpp,
- mp,
- NULLVP,
- NULLVP,
- cnp,
- vp,
- NULLVP,
- 1);
+ error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp,
+ NULLVP, 1);
if (error)
vput(vp);
return (error);
@@ -554,13 +573,13 @@ union_whiteout(v)
int a_flags;
} */ *ap = v;
struct union_node *un = VTOUNION(ap->a_dvp);
- struct proc *p = curproc;
+ struct componentname *cnp = ap->a_cnp;
if (un->un_uppervp == NULLVP)
return (EOPNOTSUPP);
- FIXUP(un, p);
- return (VOP_WHITEOUT(un->un_uppervp, ap->a_cnp, ap->a_flags));
+ FIXUP(un, curproc);
+ return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags));
}
int
@@ -574,10 +593,11 @@ union_mknod(v)
struct vattr *a_vap;
} */ *ap = v;
struct union_node *un = VTOUNION(ap->a_dvp);
- struct vnode *dvp;
- struct proc *p = ap->a_cnp->cn_proc;
+ struct vnode *dvp = un->un_uppervp;
+ struct componentname *cnp = ap->a_cnp;
+ struct proc *p = cnp->cn_proc;
- if ((dvp = un->un_uppervp) != NULLVP) {
+ if (dvp != NULLVP) {
int error;
struct vnode *vp;
struct mount *mp;
@@ -588,23 +608,14 @@ union_mknod(v)
un->un_flags |= UN_KLOCK;
mp = ap->a_dvp->v_mount;
vput(ap->a_dvp);
- error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
+ error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap);
if (error)
return (error);
- if (vp != NULLVP) {
- error = union_allocvp(
- ap->a_vpp,
- mp,
- NULLVP,
- NULLVP,
- ap->a_cnp,
- vp,
- NULLVP,
- 1);
- if (error)
- vput(vp);
- }
+ error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP,
+ cnp, vp, NULLVP, 1);
+ if (error)
+ vput(vp);
return (error);
}
@@ -649,8 +660,11 @@ union_open(v)
}
/*
- * Just open the lower vnode
+ * Just open the lower vnode, but check for nodev mount flag
*/
+ if ((tvp->v_type == VBLK || tvp->v_type == VCHR) &&
+ (ap->a_vp->v_mount->mnt_flag & MNT_NODEV))
+ return ENXIO;
un->un_openl++;
vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
error = VOP_OPEN(tvp, mode, cred, p);
@@ -658,6 +672,12 @@ union_open(v)
return (error);
}
+ /*
+ * Just open the upper vnode, checking for nodev mount flag first
+ */
+ if ((tvp->v_type == VBLK || tvp->v_type == VCHR) &&
+ (ap->a_vp->v_mount->mnt_flag & MNT_NODEV))
+ return ENXIO;
FIXUP(un, p);
@@ -679,7 +699,8 @@ union_close(v)
struct union_node *un = VTOUNION(ap->a_vp);
struct vnode *vp;
- if ((vp = un->un_uppervp) == NULLVP) {
+ vp = un->un_uppervp;
+ if (vp == NULLVP) {
#ifdef UNION_DIAGNOSTIC
if (un->un_openl <= 0)
panic("union: un_openl cnt");
@@ -687,14 +708,11 @@ union_close(v)
--un->un_openl;
vp = un->un_lowervp;
}
-#ifdef UNION_DIAGNOSTIC
- /*
- * A stranded union node may end up here with both vnodes NULL,
- * in which case we don't do anything.
- */
+
+#ifdef DIAGNOSTIC
if (vp == NULLVP) {
- vprint("empty union vnode", vp);
- panic("union_close empty vnode");
+ vprint("empty union vnode", vp);
+ panic("union_close empty vnode");
}
#endif
@@ -721,25 +739,52 @@ union_access(v)
struct ucred *a_cred;
struct proc *a_p;
} */ *ap = v;
- struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un = VTOUNION(vp);
int error = EACCES;
- struct vnode *vp;
struct proc *p = ap->a_p;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount);
+
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket, fifo, or a block or
+ * character device resident on the file system.
+ */
+ if (ap->a_mode & VWRITE) {
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ break;
+ case VBAD:
+ case VBLK:
+ case VCHR:
+ case VSOCK:
+ case VFIFO:
+ case VNON:
+ default:
+ break;
+ }
+ }
+
if ((vp = un->un_uppervp) != NULLVP) {
FIXUP(un, p);
- return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_access), ap));
}
if ((vp = un->un_lowervp) != NULLVP) {
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
- error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ ap->a_vp = vp;
+ error = VCALL(vp, VOFFSET(vop_access), ap);
if (error == 0) {
- struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
-
- if (um->um_op == UNMNT_BELOW)
- error = VOP_ACCESS(vp, ap->a_mode,
- um->um_cred, ap->a_p);
+ if (um->um_op == UNMNT_BELOW) {
+ ap->a_cred = um->um_cred;
+ error = VCALL(vp, VOFFSET(vop_access), ap);
+ }
}
VOP_UNLOCK(vp, 0, p);
if (error)
@@ -770,6 +815,7 @@ union_getattr(v)
struct vattr va;
struct proc *p = ap->a_p;
+
/*
* Some programs walk the filesystem hierarchy by counting
* links to directories to avoid stat'ing all the time.
@@ -777,11 +823,17 @@ union_getattr(v)
* 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.
+ *
+ * To make life more interesting, some filesystems don't keep
+ * track of link counts in the expected way, and return a
+ * link count of `1' for those directories; if either of the
+ * component directories returns a link count of `1', we return a 1.
*/
vap = ap->a_vap;
- if ((vp = un->un_uppervp) != 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
@@ -803,7 +855,8 @@ union_getattr(v)
vp = un->un_lowervp;
} else if (vp->v_type == VDIR) {
vp = un->un_lowervp;
- vap = &va;
+ if (vp != NULLVP)
+ vap = &va;
} else {
vp = NULLVP;
}
@@ -815,9 +868,22 @@ union_getattr(v)
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;
-
+ if ((vap != ap->a_vap) && (vap->va_type == VDIR)) {
+ /*
+ * Link count manipulation:
+ * - If both return "2", return 2 (no subdirs)
+ * - If one or the other return "1", return "1" (ENOCLUE)
+ */
+ if ((ap->a_vap->va_nlink == 2) &&
+ (vap->va_nlink == 2))
+ ;
+ else if (ap->a_vap->va_nlink != 1) {
+ if (vap->va_nlink == 1)
+ ap->a_vap->va_nlink = 1;
+ else
+ ap->a_vap->va_nlink += vap->va_nlink;
+ }
+ }
ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
return (0);
}
@@ -832,10 +898,38 @@ union_setattr(v)
struct ucred *a_cred;
struct proc *a_p;
} */ *ap = v;
- struct union_node *un = VTOUNION(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un = VTOUNION(vp);
struct proc *p = ap->a_p;
int error;
+ if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
+ vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
+ vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
+ (vp->v_mount->mnt_flag & MNT_RDONLY))
+ return (EROFS);
+ if (vap->va_size != VNOVAL) {
+ switch (vp->v_type) {
+ case VDIR:
+ return (EISDIR);
+ case VCHR:
+ case VBLK:
+ case VSOCK:
+ case VFIFO:
+ break;
+ case VREG:
+ case VLNK:
+ default:
+ /*
+ * Disallow write attempts if the filesystem is
+ * mounted read-only.
+ */
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ }
+ }
+
/*
* Handle case of truncating lower object to zero size,
* by creating a zero length upper object. This is to
@@ -844,7 +938,7 @@ union_setattr(v)
if ((un->un_uppervp == NULLVP) &&
/* assert(un->un_lowervp != NULLVP) */
(un->un_lowervp->v_type == VREG)) {
- error = union_copyup(un, (ap->a_vap->va_size != 0),
+ error = union_copyup(un, (vap->va_size != 0),
ap->a_cred, ap->a_p);
if (error)
return (error);
@@ -856,10 +950,10 @@ union_setattr(v)
*/
if (un->un_uppervp != NULLVP) {
FIXUP(un, p);
- error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
+ error = VOP_SETATTR(un->un_uppervp, 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);
+ if ((error == 0) && (vap->va_size != VNOVAL))
+ union_newsize(ap->a_vp, vap->va_size, VNOVAL);
} else {
error = EROFS;
}
@@ -958,10 +1052,10 @@ union_lease(v)
struct ucred *a_cred;
int a_flag;
} */ *ap = v;
- register struct vnode *vp = OTHERVP(ap->a_vp);
+ struct vnode *ovp = OTHERVP(ap->a_vp);
- ap->a_vp = vp;
- return (VCALL(vp, VOFFSET(vop_lease), ap));
+ ap->a_vp = ovp;
+ return (VCALL(ovp, VOFFSET(vop_lease), ap));
}
int
@@ -1013,6 +1107,18 @@ union_fsync(v)
struct vnode *targetvp = OTHERVP(ap->a_vp);
struct proc *p = ap->a_p;
+#if 0
+ /*
+ * If vinvalbuf is calling us, it's a "shallow fsync" -- don't
+ * bother syncing the underlying vnodes, since (a) they'll be
+ * fsync'ed when reclaimed and (b) we could deadlock if
+ * they're locked; otherwise, pass it through to the
+ * underlying layer.
+ */
+ if (ap->a_flags & FSYNC_RECLAIM)
+ return (0);
+#endif
+
if (targetvp != NULLVP) {
int dolock = (targetvp == LOWERVP(ap->a_vp));
@@ -1020,8 +1126,7 @@ union_fsync(v)
vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
else
FIXUP(VTOUNION(ap->a_vp), p);
- error = VOP_FSYNC(targetvp, ap->a_cred,
- ap->a_waitfor, ap->a_p);
+ error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p);
if (dolock)
VOP_UNLOCK(targetvp, 0, p);
}
@@ -1029,10 +1134,6 @@ union_fsync(v)
return (error);
}
-/* a_dvp: directory in which to link
- a_vp: new target of the link
- a_cnp: name for the link
- */
int
union_remove(v)
void *v;
@@ -1045,7 +1146,8 @@ union_remove(v)
int error;
struct union_node *dun = VTOUNION(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_vp);
- struct proc *p = ap->a_cnp->cn_proc;
+ struct componentname *cnp = ap->a_cnp;
+ struct proc *p = cnp->cn_proc;
if (dun->un_uppervp == NULLVP)
panic("union remove: null upper vnode");
@@ -1053,7 +1155,6 @@ union_remove(v)
if (un->un_uppervp != NULLVP) {
struct vnode *dvp = dun->un_uppervp;
struct vnode *vp = un->un_uppervp;
- struct componentname *cnp = ap->a_cnp;
FIXUP(dun, p);
VREF(dvp);
@@ -1064,7 +1165,7 @@ union_remove(v)
un->un_flags |= UN_KLOCK;
vput(ap->a_vp);
- if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
+ if (union_dowhiteout(un, cnp->cn_cred, p))
cnp->cn_flags |= DOWHITEOUT;
error = VOP_REMOVE(dvp, vp, cnp);
if (!error)
@@ -1081,10 +1182,6 @@ union_remove(v)
return (error);
}
-/* a_dvp: directory in which to link
- a_vp: new target of the link
- a_cnp: name for the link
- */
int
union_link(v)
void *v;
@@ -1095,10 +1192,11 @@ union_link(v)
struct componentname *a_cnp;
} */ *ap = v;
int error = 0;
+ struct componentname *cnp = ap->a_cnp;
+ struct proc *p = cnp->cn_proc;
struct union_node *dun;
- struct vnode *dvp;
struct vnode *vp;
- struct proc *p = ap->a_cnp->cn_proc;
+ struct vnode *dvp;
dun = VTOUNION(ap->a_dvp);
@@ -1108,47 +1206,53 @@ union_link(v)
error = EIO; /* need some error code for "caller is a bozo" */
} else
#endif
+
+
if (ap->a_dvp->v_op != ap->a_vp->v_op) {
vp = ap->a_vp;
} else {
struct union_node *un = VTOUNION(ap->a_vp);
-
if (un->un_uppervp == NULLVP) {
/*
- * needs to be copied up before we can link it.
+ * Needs to be copied before we can link it.
*/
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (dun->un_uppervp == un->un_dirvp) {
- VOP_UNLOCK(ap->a_dvp, 0, p);
+ dun->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(dun->un_uppervp, 0, p);
}
- error = union_copyup(un, 1, ap->a_cnp->cn_cred,
- ap->a_cnp->cn_proc);
+ error = union_copyup(un, 1, cnp->cn_cred, p);
if (dun->un_uppervp == un->un_dirvp) {
- /* During copyup, we dropped the lock on the
+ /*
+ * During copyup, we dropped the lock on the
* dir and invalidated any saved namei lookup
* state for the directory we'll be entering
* the link in. We need to re-run the lookup
* in that directory to reset any state needed
* for VOP_LINK.
- * Call relookup on the union-layer to
- * reset the state.
+ * Call relookup on the union-layer to reset
+ * the state.
+ */
+ vp = NULLVP;
+ if (dun->un_uppervp == NULLVP)
+ panic("union: null upperdvp?");
+ /*
+ * relookup starts with an unlocked node,
+ * and since LOCKPARENT is set returns
+ * the starting directory locked.
*/
- vp = NULLVP;
- if (dun->un_uppervp == NULLVP ||
- /*
- * relookup starts with an unlocked node,
- * and since LOCKPARENT is set returns
- * the starting directory locked.
- */
- (error = relookup(ap->a_dvp,
- &dvp, ap->a_cnp))) {
+ VOP_UNLOCK(ap->a_dvp, 0, p);
+ error = relookup(ap->a_dvp, &vp, ap->a_cnp);
+ if (error) {
vrele(ap->a_dvp);
VOP_UNLOCK(ap->a_vp, 0, p);
- return EROFS;
+ return EROFS; /* ? */
}
- if (dvp != NULLVP) {
- /* The name we want to create has
- mysteriously appeared (a race?) */
+ if (vp != NULLVP) {
+ /*
+ * The name we want to create has
+ * mysteriously appeared (a race?)
+ */
error = EEXIST;
VOP_UNLOCK(ap->a_vp, 0, p);
goto croak;
@@ -1174,7 +1278,7 @@ croak:
dun->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
- return (VOP_LINK(dvp, vp, ap->a_cnp));
+ return (VOP_LINK(dvp, vp, cnp));
}
int
@@ -1195,7 +1299,6 @@ union_rename(v)
struct vnode *fvp = ap->a_fvp;
struct vnode *tdvp = ap->a_tdvp;
struct vnode *tvp = ap->a_tvp;
- struct union_node *unfile = (struct union_node *)0;
if (fdvp->v_op == union_vnodeop_p) { /* always true */
struct union_node *un = VTOUNION(fdvp);
@@ -1216,19 +1319,19 @@ union_rename(v)
}
if (fvp->v_op == union_vnodeop_p) { /* always true */
- unfile = VTOUNION(fvp);
- if (unfile->un_uppervp == NULLVP) {
+ struct union_node *un = VTOUNION(fvp);
+ if (un->un_uppervp == NULLVP) {
/* XXX: should do a copyup */
error = EXDEV;
goto bad;
}
- if (unfile->un_lowervp != NULLVP)
+ if (un->un_lowervp != NULLVP)
ap->a_fcnp->cn_flags |= DOWHITEOUT;
- fvp = unfile->un_uppervp;
+ fvp = un->un_uppervp;
VREF(fvp);
- /* vrele(ap->a_fvp); */ /* hold for later */
+ vrele(ap->a_fvp);
}
if (tdvp->v_op == union_vnodeop_p) {
@@ -1241,7 +1344,7 @@ union_rename(v)
* directory.
*/
error = EXDEV;
- goto badrele;
+ goto bad;
}
tdvp = un->un_uppervp;
@@ -1258,24 +1361,11 @@ union_rename(v)
VREF(tvp);
un->un_flags |= UN_KLOCK;
}
-#if 0
- /* XXX should we toss from the cache? */
- if (un->un_flags & UN_CACHED) {
- un->un_flags &= ~UN_CACHED;
- LIST_REMOVE(un, un_cache);
- }
-#endif
vput(ap->a_tvp);
}
- error = VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp);
- if (!error && unfile)
- union_removed_upper(unfile);
- vrele(ap->a_fvp);
- return error;
+ return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
-badrele:
- vrele(ap->a_fvp);
bad:
vrele(fdvp);
vrele(fvp);
@@ -1299,6 +1389,7 @@ union_mkdir(v)
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
struct proc *p = ap->a_cnp->cn_proc;
+ struct componentname *cnp = ap->a_cnp;
if (dvp != NULLVP) {
int error;
@@ -1308,21 +1399,14 @@ union_mkdir(v)
VREF(dvp);
un->un_flags |= UN_KLOCK;
VOP_UNLOCK(ap->a_dvp, 0, p);
- error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
+ error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap);
if (error) {
vrele(ap->a_dvp);
return (error);
}
- error = union_allocvp(
- ap->a_vpp,
- ap->a_dvp->v_mount,
- ap->a_dvp,
- NULLVP,
- ap->a_cnp,
- vp,
- NULLVP,
- 1);
+ error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp,
+ NULLVP, cnp, vp, NULLVP, 1);
vrele(ap->a_dvp);
if (error)
vput(vp);
@@ -1345,7 +1429,8 @@ union_rmdir(v)
int error;
struct union_node *dun = VTOUNION(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_vp);
- struct proc *p = ap->a_cnp->cn_proc;
+ struct componentname *cnp = ap->a_cnp;
+ struct proc *p = cnp->cn_proc;
if (dun->un_uppervp == NULLVP)
panic("union rmdir: null upper vnode");
@@ -1353,7 +1438,6 @@ union_rmdir(v)
if (un->un_uppervp != NULLVP) {
struct vnode *dvp = dun->un_uppervp;
struct vnode *vp = un->un_uppervp;
- struct componentname *cnp = ap->a_cnp;
FIXUP(dun, p);
VREF(dvp);
@@ -1366,7 +1450,7 @@ union_rmdir(v)
if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
cnp->cn_flags |= DOWHITEOUT;
- error = VOP_RMDIR(dvp, vp, cnp);
+ error = VOP_RMDIR(dvp, vp, ap->a_cnp);
if (!error)
union_removed_upper(un);
} else {
@@ -1394,19 +1478,18 @@ union_symlink(v)
} */ *ap = v;
struct union_node *un = VTOUNION(ap->a_dvp);
struct vnode *dvp = un->un_uppervp;
- struct proc *p = ap->a_cnp->cn_proc;
+ struct componentname *cnp = ap->a_cnp;
+ struct proc *p = cnp->cn_proc;
if (dvp != NULLVP) {
int error;
- struct vnode *vp;
FIXUP(un, p);
VREF(dvp);
un->un_flags |= UN_KLOCK;
vput(ap->a_dvp);
- error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
- ap->a_vap, ap->a_target);
- *ap->a_vpp = NULLVP;
+ error = VOP_SYMLINK(dvp, ap->a_vpp, cnp, ap->a_vap,
+ ap->a_target);
return (error);
}
@@ -1431,19 +1514,19 @@ union_readdir(v)
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
- int *a_ncookies;
- u_long **a_cookies;
-
+ u_long *a_cookies;
+ int a_ncookies;
} */ *ap = v;
- register struct union_node *un = VTOUNION(ap->a_vp);
- register struct vnode *vp;
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *uvp = un->un_uppervp;
struct proc *p = curproc;
- if ((vp = un->un_uppervp) == NULLVP)
+
+ if (uvp == NULLVP)
return (0);
FIXUP(un, p);
- ap->a_vp = vp;
- return (VCALL(vp, VOFFSET(vop_readdir), ap));
+ ap->a_vp = uvp;
+ return (VCALL(uvp, VOFFSET(vop_readdir), ap));
}
int
@@ -1457,9 +1540,8 @@ union_readlink(v)
} */ *ap = v;
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
- struct proc *p = curproc;
-
int dolock = (vp == LOWERVP(ap->a_vp));
+ struct proc *p = curproc;
if (dolock)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
@@ -1473,18 +1555,6 @@ union_readlink(v)
return (error);
}
-/*
- * When operations want to vput() a union node yet retain a lock on
- * the upper VP (say, to do some further operations like link(),
- * mkdir(), ...), they set UN_KLOCK on the union node, then call
- * vput() which calls VOP_UNLOCK(, 0, p) and comes here. union_unlock()
- * unlocks the union node (leaving the upper VP alone), clears the
- * KLOCK flag, and then returns to vput(). The caller then does whatever
- * is left to do with the upper VP, and insures that it gets unlocked.
- *
- * If UN_KLOCK isn't set, then the upper VP is unlocked here.
- */
-
int
union_abortop(v)
void *v;
@@ -1497,7 +1567,6 @@ union_abortop(v)
struct vnode *vp = OTHERVP(ap->a_dvp);
struct union_node *un = VTOUNION(ap->a_dvp);
struct proc *p = ap->a_cnp->cn_proc;
-
int islocked = un->un_flags & UN_LOCKED;
int dolock = (vp == LOWERVP(ap->a_dvp));
@@ -1523,7 +1592,10 @@ union_inactive(v)
struct vnode *a_vp;
struct proc *a_p;
} */ *ap = v;
- struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un = VTOUNION(vp);
+ struct proc *p = ap->a_p;
+ struct vnode **vpp;
/*
* Do nothing (and _don't_ bypass).
@@ -1538,19 +1610,17 @@ union_inactive(v)
* 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
-
- union_diruncache(un);
+ if (un->un_dircache != 0) {
+ for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
+ vrele(*vpp);
+ free(un->un_dircache, M_TEMP);
+ un->un_dircache = 0;
+ }
- VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
+ VOP_UNLOCK(vp, 0, p);
if ((un->un_flags & UN_CACHED) == 0)
- vrecycle(ap->a_vp, (struct simplelock *)0, ap->a_p);
+ vgone(vp);
return (0);
}
@@ -1572,14 +1642,24 @@ int
union_lock(v)
void *v;
{
- struct vop_lock_args *ap = v;
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ int a_flags;
+ } */ *ap = v;
struct vnode *vp = ap->a_vp;
+ int flags = ap->a_flags;
struct union_node *un;
struct proc *p = ap->a_p;
- int flags = ap->a_flags;
- int error = 0;
+ int error;
+#ifdef DIAGNOSTIC
+ int drain = 0;
+#endif
+#if 0
+ genfs_nolock(ap);
+#else
vop_generic_lock(ap);
+#endif
/*
* Need to do real lockmgr-style locking here.
* in the mean time, draining won't work quite right,
@@ -1591,12 +1671,42 @@ union_lock(v)
*/
flags &= ~LK_INTERLOCK;
+ un = VTOUNION(vp);
+#ifdef DIAGNOSTIC
+ if (un->un_flags & (UN_DRAINING|UN_DRAINED)) {
+ if (un->un_flags & UN_DRAINED)
+ panic("union: %p: warning: locking decommissioned lock", vp);
+ if ((flags & LK_TYPE_MASK) != LK_RELEASE)
+ panic("union: %p: non-release on draining lock: %d",
+ vp, flags & LK_TYPE_MASK);
+ un->un_flags &= ~UN_DRAINING;
+ if ((flags & LK_REENABLE) == 0)
+ un->un_flags |= UN_DRAINED;
+ }
+#endif
+
+ /*
+ * Don't pass DRAIN through to sub-vnode lock; keep track of
+ * DRAIN state at this level, and just get an exclusive lock
+ * on the underlying vnode.
+ */
+ if ((flags & LK_TYPE_MASK) == LK_DRAIN) {
+#ifdef DIAGNOSTIC
+ drain = 1;
+#endif
+ flags = LK_EXCLUSIVE | (flags & ~LK_TYPE_MASK);
+ }
start:
- un = VTOUNION(vp);
+ un = VTOUNION(vp);
if (un->un_uppervp != NULLVP) {
if (((un->un_flags & UN_ULOCK) == 0) &&
(vp->v_usecount != 0)) {
+ /*
+ * We MUST always use the order of: take upper
+ * vp lock, manipulate union node flags, drop
+ * upper vp lock. This code must not be an
+ */
error = vn_lock(un->un_uppervp, flags, p);
if (error)
return (error);
@@ -1605,19 +1715,20 @@ start:
#ifdef DIAGNOSTIC
if (un->un_flags & UN_KLOCK) {
vprint("union: dangling klock", vp);
- panic("union: dangling upper lock (%lx)", vp);
+ panic("union: dangling upper lock (%p)", vp);
}
#endif
- }
+ }
+ /* XXX ignores LK_NOWAIT */
if (un->un_flags & UN_LOCKED) {
#ifdef DIAGNOSTIC
if (curproc && un->un_pid == curproc->p_pid &&
- un->un_pid > -1 && curproc->p_pid > -1)
+ un->un_pid > -1 && curproc->p_pid > -1)
panic("union: locking against myself");
#endif
un->un_flags |= UN_WANTED;
- tsleep((caddr_t)un, PINOD, "unionlk", 0);
+ tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0);
goto start;
}
@@ -1626,6 +1737,8 @@ start:
un->un_pid = curproc->p_pid;
else
un->un_pid = -1;
+ if (drain)
+ un->un_flags |= UN_DRAINING;
#endif
un->un_flags |= UN_LOCKED;
@@ -1634,21 +1747,23 @@ start:
/*
* When operations want to vput() a union node yet retain a lock on
- * the upper VP (say, to do some further operations like link(),
+ * the upper vnode (say, to do some further operations like link(),
* mkdir(), ...), they set UN_KLOCK on the union node, then call
- * vput() which calls VOP_UNLOCK(, 0, p) and comes here. union_unlock()
- * unlocks the union node (leaving the upper VP alone), clears the
+ * vput() which calls VOP_UNLOCK() and comes here. union_unlock()
+ * unlocks the union node (leaving the upper vnode alone), clears the
* KLOCK flag, and then returns to vput(). The caller then does whatever
- * is left to do with the upper VP, and insures that it gets unlocked.
+ * is left to do with the upper vnode, and ensures that it gets unlocked.
*
- * If UN_KLOCK isn't set, then the upper VP is unlocked here.
+ * If UN_KLOCK isn't set, then the upper vnode is unlocked here.
*/
-
int
union_unlock(v)
void *v;
{
- struct vop_lock_args *ap = v;
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ int a_flags;
+ } */ *ap = v;
struct union_node *un = VTOUNION(ap->a_vp);
struct proc *p = ap->a_p;
@@ -1658,6 +1773,8 @@ union_unlock(v)
if (curproc && un->un_pid != curproc->p_pid &&
curproc->p_pid > -1 && un->un_pid > -1)
panic("union: unlocking other process's union node");
+ if (un->un_flags & UN_DRAINED)
+ panic("union: %p: warning: unlocking decommissioned lock", ap->a_vp);
#endif
un->un_flags &= ~UN_LOCKED;
@@ -1669,14 +1786,22 @@ union_unlock(v)
if (un->un_flags & UN_WANTED) {
un->un_flags &= ~UN_WANTED;
- wakeup((caddr_t)un);
+ wakeup((caddr_t)&un->un_flags);
}
#ifdef DIAGNOSTIC
un->un_pid = 0;
+ if (un->un_flags & UN_DRAINING) {
+ un->un_flags |= UN_DRAINED;
+ un->un_flags &= ~UN_DRAINING;
+ }
#endif
- vop_generic_unlock(v);
+#if 0
+ genfs_nounlock(ap);
+#else
+ vop_generic_unlock(ap);
+#endif
return (0);
}
@@ -1720,16 +1845,16 @@ union_print(v)
printf("\ttag VT_UNION, vp=%p, uppervp=%p, lowervp=%p\n",
vp, UPPERVP(vp), LOWERVP(vp));
- if (UPPERVP(vp))
- vprint("uppervp", UPPERVP(vp));
- if (LOWERVP(vp))
- vprint("lowervp", LOWERVP(vp));
+ if (UPPERVP(vp) != NULLVP)
+ vprint("union: upper", UPPERVP(vp));
+ if (LOWERVP(vp) != NULLVP)
+ vprint("union: lower", LOWERVP(vp));
if (VTOUNION(vp)->un_dircache) {
struct vnode **vpp;
-
for (vpp = VTOUNION(vp)->un_dircache; *vpp != NULLVP; vpp++)
vprint("dircache:", *vpp);
}
+
return (0);
}
@@ -1751,7 +1876,7 @@ union_pathconf(v)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
- register_t *a_retval;
+ int *a_retval;
} */ *ap = v;
int error;
struct vnode *vp = OTHERVP(ap->a_vp);
@@ -1780,13 +1905,12 @@ union_revoke(v)
struct proc *a_p;
} */ *ap = v;
struct vnode *vp = ap->a_vp;
-
+
if (UPPERVP(vp))
VOP_REVOKE(UPPERVP(vp), ap->a_flags);
if (LOWERVP(vp))
VOP_REVOKE(LOWERVP(vp), ap->a_flags);
vgone(vp);
-
return (0);
}
@@ -1802,10 +1926,10 @@ union_advlock(v)
struct flock *a_fl;
int a_flags;
} */ *ap = v;
- register struct vnode *vp = OTHERVP(ap->a_vp);
+ struct vnode *ovp = OTHERVP(ap->a_vp);
- ap->a_vp = vp;
- return (VCALL(vp, VOFFSET(vop_advlock), ap));
+ ap->a_vp = ovp;
+ return (VCALL(ovp, VOFFSET(vop_advlock), ap));
}
@@ -1841,4 +1965,3 @@ union_strategy(v)
return (error);
}
-