summaryrefslogtreecommitdiff
path: root/sys/miscfs/union
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2003-05-12 21:45:36 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2003-05-12 21:45:36 +0000
commit8cff355393fd0379a813cd846862c67f802d6385 (patch)
tree8e452725945899b7138e2c33794c5897f74b20de /sys/miscfs/union
parentdf31dea66cb048c3fe29c7cdd981d4c71ef6a953 (diff)
fix up locking and some issues with union. derived from netbsd
Diffstat (limited to 'sys/miscfs/union')
-rw-r--r--sys/miscfs/union/union.h15
-rw-r--r--sys/miscfs/union/union_subr.c422
-rw-r--r--sys/miscfs/union/union_vnops.c601
3 files changed, 595 insertions, 443 deletions
diff --git a/sys/miscfs/union/union.h b/sys/miscfs/union/union.h
index 382ee660cb8..7318713db3a 100644
--- a/sys/miscfs/union/union.h
+++ b/sys/miscfs/union/union.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: union.h,v 1.7 2002/03/14 01:27:08 millert Exp $ */
-/* $NetBSD: union.h,v 1.9 1996/02/09 22:41:08 christos Exp $ */
+/* $OpenBSD: union.h,v 1.8 2003/05/12 21:45:35 tedu Exp $ */
+/* $NetBSD: union.h,v 1.13 2002/09/21 18:09:31 christos Exp $ */
/*
* Copyright (c) 1994 The Regents of the University of California.
@@ -40,8 +40,6 @@
* @(#)union.h 8.9 (Berkeley) 12/10/94
*/
-struct vfsconf;
-
struct union_args {
char *target; /* Target of loopback */
int mntflags; /* Options on the mount */
@@ -52,6 +50,9 @@ struct union_args {
#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
#define UNMNT_OPMASK 0x0003
+#define UNMNT_BITS "\177\20" \
+ "b\00above\0b\01below\0b\02replace"
+
struct union_mount {
struct vnode *um_uppervp;
struct vnode *um_lowervp;
@@ -65,8 +66,8 @@ struct union_mount {
/*
* DEFDIRMODE is the mode bits used to create a shadow directory.
*/
-#define UN_DIRMODE (S_IRWXU|S_IRWXG|S_IRWXO)
-#define UN_FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#define UN_DIRMODE (S_IRWXU|S_IRWXG|S_IRWXO)
+#define UN_FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
/*
* A cache of vnode references
@@ -95,6 +96,8 @@ struct union_node {
#define UN_ULOCK 0x04 /* Upper node is locked */
#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
#define UN_CACHED 0x10 /* In union cache */
+#define UN_DRAINING 0x20 /* upper node lock is draining */
+#define UN_DRAINED 0x40 /* upper node lock is drained */
extern int union_allocvp(struct vnode **, struct mount *,
struct vnode *, struct vnode *,
diff --git a/sys/miscfs/union/union_subr.c b/sys/miscfs/union/union_subr.c
index 23e9e81ea9e..82070d944b9 100644
--- a/sys/miscfs/union/union_subr.c
+++ b/sys/miscfs/union/union_subr.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: union_subr.c,v 1.12 2002/06/08 18:43:34 art Exp $ */
-/* $NetBSD: union_subr.c,v 1.18 1996/02/09 22:41:10 christos Exp $ */
+/* $OpenBSD: union_subr.c,v 1.13 2003/05/12 21:45:35 tedu Exp $ */
+/* $NetBSD: union_subr.c,v 1.41 2001/11/10 13:33:45 lukem Exp $ */
/*
* Copyright (c) 1994 Jan-Simon Pendry
@@ -37,9 +37,10 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)union_subr.c 8.16 (Berkeley) 12/10/94
+ * @(#)union_subr.c 8.20 (Berkeley) 5/20/95
*/
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
@@ -53,7 +54,9 @@
#include <sys/queue.h>
#include <sys/mount.h>
#include <sys/stat.h>
-#include <uvm/uvm_extern.h> /* for vnode_pager_setsize */
+
+#include <uvm/uvm_extern.h>
+
#include <miscfs/union/union.h>
#ifdef DIAGNOSTIC
@@ -74,26 +77,26 @@ static int union_list_lock(int);
static void union_list_unlock(int);
void union_updatevp(struct union_node *, struct vnode *, struct vnode *);
static int union_relookup(struct union_mount *, struct vnode *,
- struct vnode **, struct componentname *,
- struct componentname *, char *, int);
+ struct vnode **, struct componentname *,
+ struct componentname *, const char *, int);
int union_vn_close(struct vnode *, int, struct ucred *, struct proc *);
static void union_dircache_r(struct vnode *, struct vnode ***, int *);
struct vnode *union_dircache(struct vnode *, struct proc *);
/*
* This variable is used to hold a pointer to a function
- * that is called from vfs_syscalls.c and vfs_syscalls_43.c
+ * that is called from vfs_syscalls.c and vfs_syscalls_43.c
* - by keeping a pointer to the function we enable the real
* union filesystem code to replace the stub value provided
* by vfs_syscalls.c and thus vfs_syscalls.c no longer needs
* to know if UNION is built in, lkm'ed, or not even there.
*/
extern
-int (*union_check_p)(struct proc *, struct vnode **,
- struct file *, struct uio, int *);
+int (*union_check_p)(struct proc *, struct vnode **,
+ struct file *, struct uio, int *);
int union_check(struct proc *, struct vnode **, struct file *,
- struct uio, int *);
+ struct uio, int *);
int union_check(p, vpp, fp, auio, error)
struct proc *p;
@@ -141,14 +144,12 @@ int union_check(p, vpp, fp, auio, error)
};
int
-union_init(vfsp)
- struct vfsconf *vfsp;
+union_init(struct vfsconf *vfsp)
{
int i;
for (i = 0; i < NHASH; i++)
LIST_INIT(&unhead[i]);
- bzero((caddr_t) unvplock, sizeof(unvplock));
union_check_p = union_check;
return (0);
}
@@ -160,7 +161,7 @@ union_list_lock(ix)
if (unvplock[ix] & UN_LOCKED) {
unvplock[ix] |= UN_WANTED;
- tsleep((caddr_t) &unvplock[ix], PINOD, "unlstlk", 0);
+ tsleep(&unvplock[ix], PINOD, "unionlk", 0);
return (1);
}
@@ -191,19 +192,26 @@ union_updatevp(un, uppervp, lowervp)
int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
int nhash = UNION_HASH(uppervp, lowervp);
int docache = (lowervp != NULLVP || uppervp != NULLVP);
+ int lhash, uhash;
/*
* Ensure locking is ordered from lower to higher
* to avoid deadlocks.
*/
- if (nhash < ohash)
- while (union_list_lock(nhash))
+ if (nhash < ohash) {
+ lhash = nhash;
+ uhash = ohash;
+ } else {
+ lhash = ohash;
+ uhash = nhash;
+ }
+
+ if (lhash != uhash)
+ while (union_list_lock(lhash))
continue;
- while (union_list_lock(ohash))
+
+ while (union_list_lock(uhash))
continue;
- if (nhash > ohash)
- while (union_list_lock(nhash))
- continue;
if (ohash != nhash || !docache) {
if (un->un_flags & UN_CACHED) {
@@ -299,8 +307,8 @@ union_newsize(vp, uppersz, lowersz)
if (sz != VNOVAL) {
#ifdef UNION_DIAGNOSTIC
- printf("union: %s size now %ld\n",
- uppersz != VNOVAL ? "upper" : "lower", (long) sz);
+ printf("union: %s size now %qd\n",
+ uppersz != VNOVAL ? "upper" : "lower", sz);
#endif
uvm_vnp_setsize(vp, sz);
}
@@ -365,7 +373,7 @@ union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache)
}
/* detect the root vnode (and aliases) */
- vflag = 0;
+ vflag = VLAYER;
if ((uppervp == um->um_uppervp) &&
((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
if (lowervp == NULLVP) {
@@ -410,8 +418,7 @@ loop:
(un->un_uppervp == uppervp ||
un->un_uppervp == NULLVP) &&
(UNIONTOV(un)->v_mount == mp)) {
- if (vget(UNIONTOV(un), 0,
- cnp ? cnp->cn_proc : NULL)) {
+ if (vget(UNIONTOV(un), 0, curproc)) {
union_list_unlock(hash);
goto loop;
}
@@ -446,14 +453,14 @@ loop:
if ((un->un_flags & UN_LOCKED) == 0)
panic("union: . not locked");
else 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: allocvp not lock owner");
#endif
} else {
if (un->un_flags & UN_LOCKED) {
vrele(UNIONTOV(un));
un->un_flags |= UN_WANTED;
- tsleep((caddr_t)un, PINOD, "unallvp", 0);
+ tsleep(&un->un_flags, PINOD, "unionalloc", 0);
goto loop;
}
un->un_flags |= UN_LOCKED;
@@ -498,7 +505,7 @@ loop:
un->un_hash = cnp->cn_hash;
un->un_path = malloc(cnp->cn_namelen+1,
M_TEMP, M_WAITOK);
- bcopy(cnp->cn_nameptr, un->un_path,
+ memcpy(un->un_path, cnp->cn_nameptr,
cnp->cn_namelen);
un->un_path[cnp->cn_namelen] = '\0';
VREF(dvp);
@@ -515,7 +522,7 @@ loop:
/*
* otherwise lock the vp list while we call getnewvnode
* since that can block.
- */
+ */
hash = UNION_HASH(uppervp, lowervp);
if (union_list_lock(hash))
@@ -567,7 +574,7 @@ loop:
if (cnp && (lowervp != NULLVP)) {
un->un_hash = cnp->cn_hash;
un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
- bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
+ memcpy(un->un_path, cnp->cn_nameptr, cnp->cn_namelen);
un->un_path[cnp->cn_namelen] = '\0';
VREF(dvp);
un->un_dirvp = dvp;
@@ -651,10 +658,10 @@ union_copyfile(fvp, tvp, cred, p)
VOP_UNLOCK(fvp, 0, p); /* XXX */
VOP_LEASE(fvp, p, cred, LEASE_READ);
- vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p);
+ vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */
VOP_UNLOCK(tvp, 0, p); /* XXX */
VOP_LEASE(tvp, p, cred, LEASE_WRITE);
- vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
+ vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */
buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
@@ -706,6 +713,7 @@ union_copyup(un, docopy, cred, p)
{
int error;
struct vnode *lvp, *uvp;
+ struct vattr lvattr, uvattr;
error = union_vn_create(&uvp, un, p);
if (error)
@@ -723,179 +731,190 @@ union_copyup(un, docopy, cred, p)
* from VOP_CLOSE
*/
vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
- error = VOP_OPEN(lvp, FREAD, cred, p);
+
+ error = VOP_GETATTR(lvp, &lvattr, cred, p);
+ if (error == 0)
+ error = VOP_OPEN(lvp, FREAD, cred, p);
if (error == 0) {
error = union_copyfile(lvp, uvp, cred, p);
- VOP_UNLOCK(lvp, 0, p);
(void) VOP_CLOSE(lvp, FREAD, cred, p);
}
+ if (error == 0) {
+ /* Copy permissions up too */
+ VATTR_NULL(&uvattr);
+ uvattr.va_mode = lvattr.va_mode;
+ uvattr.va_flags = lvattr.va_flags;
+ error = VOP_SETATTR(uvp, &uvattr, cred, p);
+ }
+ VOP_UNLOCK(lvp, 0, p);
#ifdef UNION_DIAGNOSTIC
if (error == 0)
uprintf("union: copied up %s\n", un->un_path);
#endif
}
- un->un_flags &= ~UN_ULOCK;
- VOP_UNLOCK(uvp, 0, p);
union_vn_close(uvp, FWRITE, cred, p);
- vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY, p);
- 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 right, either.
- */
- if (error == 0) {
- int i;
-
- for (i = 0; i < un->un_openl; i++) {
- (void) VOP_CLOSE(lvp, FREAD, cred, p);
- (void) VOP_OPEN(uvp, FREAD, cred, p);
- }
- un->un_openl = 0;
- }
-
- return (error);
-
- }
-
- static int
- union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
- struct union_mount *um;
- struct vnode *dvp;
- struct vnode **vpp;
- struct componentname *cnp;
- struct componentname *cn;
- char *path;
- int pathlen;
- {
- int error;
-
- /*
- * A new componentname structure must be faked up because
- * there is no way to know where the upper level cnp came
- * from or what it is being used for. This must duplicate
- * some of the work done by NDINIT, some of the work done
- * by namei, some of the work done by lookup and some of
- * the work done by VOP_LOOKUP when given a CREATE flag.
- * Conclusion: Horrible.
- *
- * The pathname buffer will be FREEed by VOP_MKDIR.
- */
- cn->cn_namelen = pathlen;
- cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
- bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
- cn->cn_pnbuf[cn->cn_namelen] = '\0';
-
- cn->cn_nameiop = CREATE;
- cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
- cn->cn_proc = cnp->cn_proc;
- if (um->um_op == UNMNT_ABOVE)
- cn->cn_cred = cnp->cn_cred;
- else
- cn->cn_cred = um->um_cred;
- cn->cn_nameptr = cn->cn_pnbuf;
- cn->cn_hash = cnp->cn_hash;
- cn->cn_consume = cnp->cn_consume;
-
- VREF(dvp);
- error = relookup(dvp, vpp, cn);
- if (!error)
- vrele(dvp);
- else {
- free(cn->cn_pnbuf, M_NAMEI);
- cn->cn_pnbuf = 0;
- }
-
- return (error);
- }
-
- /*
- * Create a shadow directory in the upper layer.
- * The new vnode is returned locked.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the shadow directory.
- * it is unlocked on entry and exit.
- * (cnp) is the componentname to be created.
- * (vpp) is the returned newly created shadow directory, which
- * is returned locked.
- */
- int
- union_mkshadow(um, dvp, cnp, vpp)
- struct union_mount *um;
- struct vnode *dvp;
- struct componentname *cnp;
- struct vnode **vpp;
- {
- int error;
- struct vattr va;
- struct proc *p = cnp->cn_proc;
- struct componentname cn;
-
- error = union_relookup(um, dvp, vpp, cnp, &cn,
- cnp->cn_nameptr, cnp->cn_namelen);
- if (error)
- return (error);
-
- if (*vpp) {
- VOP_ABORTOP(dvp, &cn);
- VOP_UNLOCK(dvp, 0, p);
- vrele(*vpp);
- *vpp = NULLVP;
- return (EEXIST);
- }
-
- /*
- * policy: when creating the shadow directory in the
- * upper layer, create it owned by the user who did
- * the mount, group from parent directory, and mode
- * 777 modified by umask (ie mostly identical to the
- * mkdir syscall). (jsp, kb)
- */
-
- VATTR_NULL(&va);
- va.va_type = VDIR;
- va.va_mode = um->um_cmode;
-
- /* VOP_LEASE: dvp is locked */
- VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE);
-
- error = VOP_MKDIR(dvp, vpp, &cn, &va);
- return (error);
- }
-
- /*
- * Create a whiteout entry in the upper layer.
- *
- * (um) points to the union mount structure for access to the
- * the mounting process's credentials.
- * (dvp) is the directory in which to create the whiteout.
- * it is locked on entry and exit.
- * (cnp) is the componentname to be created.
- */
- int
- union_mkwhiteout(um, dvp, cnp, path)
- struct union_mount *um;
- struct vnode *dvp;
- struct componentname *cnp;
- char *path;
- {
- int error;
- struct proc *p = cnp->cn_proc;
- struct vnode *wvp;
- struct componentname cn;
-
- VOP_UNLOCK(dvp, 0, p);
- error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
- if (error) {
- vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
- return (error);
+
+ /*
+ * 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 right, either.
+ */
+ if (error == 0) {
+ int i;
+
+ vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
+ for (i = 0; i < un->un_openl; i++) {
+ (void) VOP_CLOSE(lvp, FREAD, cred, p);
+ (void) VOP_OPEN(uvp, FREAD, cred, p);
+ }
+ un->un_openl = 0;
+ VOP_UNLOCK(lvp, 0, p);
+ }
+
+ return (error);
+
+}
+
+static int
+union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+ struct componentname *cn;
+ const char *path;
+ int pathlen;
+{
+ int error;
+
+ /*
+ * A new componentname structure must be faked up because
+ * there is no way to know where the upper level cnp came
+ * from or what it is being used for. This must duplicate
+ * some of the work done by NDINIT, some of the work done
+ * by namei, some of the work done by lookup and some of
+ * the work done by VOP_LOOKUP when given a CREATE flag.
+ * Conclusion: Horrible.
+ */
+ cn->cn_namelen = pathlen;
+ if ((cn->cn_namelen + 1) > MAXPATHLEN)
+ return (ENAMETOOLONG);
+ cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
+ memcpy(cn->cn_pnbuf, path, cn->cn_namelen);
+ cn->cn_pnbuf[cn->cn_namelen] = '\0';
+
+ cn->cn_nameiop = CREATE;
+ cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn->cn_proc = cnp->cn_proc;
+ if (um->um_op == UNMNT_ABOVE)
+ cn->cn_cred = cnp->cn_cred;
+ else
+ cn->cn_cred = um->um_cred;
+ cn->cn_nameptr = cn->cn_pnbuf;
+ cn->cn_hash = cnp->cn_hash;
+ cn->cn_consume = cnp->cn_consume;
+
+ VREF(dvp);
+ error = relookup(dvp, vpp, cn);
+ if (!error)
+ vrele(dvp);
+ else {
+ free(cn->cn_pnbuf, M_NAMEI);
+ cn->cn_pnbuf = 0;
+ }
+
+ return (error);
+}
+
+/*
+ * Create a shadow directory in the upper layer.
+ * The new vnode is returned locked.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the shadow directory.
+ * it is unlocked on entry and exit.
+ * (cnp) is the componentname to be created.
+ * (vpp) is the returned newly created shadow directory, which
+ * is returned locked.
+ *
+ * N.B. We still attempt to create shadow directories even if the union
+ * is mounted read-only, which is a little nonintuitive.
+ */
+int
+union_mkshadow(um, dvp, cnp, vpp)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ struct vnode **vpp;
+{
+ int error;
+ struct vattr va;
+ struct proc *p = cnp->cn_proc;
+ struct componentname cn;
+
+ error = union_relookup(um, dvp, vpp, cnp, &cn,
+ cnp->cn_nameptr, cnp->cn_namelen);
+ if (error)
+ return (error);
+
+ if (*vpp) {
+ VOP_ABORTOP(dvp, &cn);
+ VOP_UNLOCK(dvp, 0, p);
+ vrele(*vpp);
+ *vpp = NULLVP;
+ return (EEXIST);
+ }
+
+ /*
+ * policy: when creating the shadow directory in the
+ * upper layer, create it owned by the user who did
+ * the mount, group from parent directory, and mode
+ * 777 modified by umask (ie mostly identical to the
+ * mkdir syscall). (jsp, kb)
+ */
+
+ VATTR_NULL(&va);
+ va.va_type = VDIR;
+ va.va_mode = um->um_cmode;
+
+ /* VOP_LEASE: dvp is locked */
+ VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE);
+
+ error = VOP_MKDIR(dvp, vpp, &cn, &va);
+ return (error);
+}
+
+/*
+ * Create a whiteout entry in the upper layer.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the whiteout.
+ * it is locked on entry and exit.
+ * (cnp) is the componentname to be created.
+ */
+int
+union_mkwhiteout(um, dvp, cnp, path)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ char *path;
+{
+ int error;
+ struct proc *p = cnp->cn_proc;
+ struct vnode *wvp;
+ struct componentname cn;
+
+ VOP_UNLOCK(dvp, 0, p);
+ error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
+ if (error) {
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
+ return (error);
}
if (wvp) {
@@ -952,8 +971,10 @@ union_vn_create(vpp, un, p)
* copied in the first place).
*/
cn.cn_namelen = strlen(un->un_path);
+ if ((cn.cn_namelen + 1) > MAXPATHLEN)
+ return (ENAMETOOLONG);
cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen+1, M_NAMEI, M_WAITOK);
- bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
+ memcpy(cn.cn_pnbuf, un->un_path, cn.cn_namelen+1);
cn.cn_nameiop = CREATE;
cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
cn.cn_proc = p;
@@ -1021,8 +1042,7 @@ void
union_removed_upper(un)
struct union_node *un;
{
- struct proc *p = curproc;
-
+#if 1
/*
* We do not set the uppervp to NULLVP here, because lowervp
* may also be NULLVP, so this routine would end up creating
@@ -1033,6 +1053,9 @@ union_removed_upper(un)
* release it, union_inactive() will vgone() it.
*/
union_diruncache(un);
+#else
+ union_newupper(un, NULLVP);
+#endif
if (un->un_flags & UN_CACHED) {
un->un_flags &= ~UN_CACHED;
@@ -1041,7 +1064,7 @@ union_removed_upper(un)
if (un->un_flags & UN_ULOCK) {
un->un_flags &= ~UN_ULOCK;
- VOP_UNLOCK(un->un_uppervp, 0, p);
+ VOP_UNLOCK(un->un_uppervp, 0, curproc);
}
}
@@ -1125,6 +1148,9 @@ union_dircache(vp, p)
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
dircache = VTOUNION(vp)->un_dircache;
+
+ nvp = NULLVP;
+
if (dircache == 0) {
cnt = 0;
union_dircache_r(vp, 0, &cnt);
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);
}
-