diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2003-05-12 21:45:36 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2003-05-12 21:45:36 +0000 |
commit | 8cff355393fd0379a813cd846862c67f802d6385 (patch) | |
tree | 8e452725945899b7138e2c33794c5897f74b20de /sys/miscfs/union | |
parent | df31dea66cb048c3fe29c7cdd981d4c71ef6a953 (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.h | 15 | ||||
-rw-r--r-- | sys/miscfs/union/union_subr.c | 422 | ||||
-rw-r--r-- | sys/miscfs/union/union_vnops.c | 601 |
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); } - |