X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/01dac67ecdefb2cd035518aee003b76c609a4865..728b872648a82b95779fbec9cf890de751346654:/usr/src/sys/miscfs/union/union_vnops.c diff --git a/usr/src/sys/miscfs/union/union_vnops.c b/usr/src/sys/miscfs/union/union_vnops.c index 413e3be610..2de83d4605 100644 --- a/usr/src/sys/miscfs/union/union_vnops.c +++ b/usr/src/sys/miscfs/union/union_vnops.c @@ -1,97 +1,103 @@ /* - * 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. * * %sccs.include.redist.c% * - * @(#)union_vnops.c 1.3 (Berkeley) %G% + * @(#)union_vnops.c 8.26 (Berkeley) %G% */ #include #include #include #include -#include #include +#include #include #include #include #include #include #include -#include "union.h" +#include +#include -/* - * Create a shadow directory in the upper layer. - * The new vnode is returned locked. - */ -static int -union_mkshadow(dvp, cnp, vpp) - struct vnode *dvp; - struct componentname *cnp; - struct vnode *vpp; -{ - int error; - struct vattr va; - struct proc *p = cnp->cn_proc; +#define FIXUP(un) { \ + if (((un)->un_flags & UN_ULOCK) == 0) { \ + union_fixup(un); \ + } \ +} - /* - * policy: when creating the shadow directory in the - * upper layer, create it owned by the current user, - * group from parent directory, and mode 777 modified - * by umask (ie mostly identical to the mkdir syscall). - * (jsp, kb) - * TODO: create the directory owned by the user who - * did the mount (um->um_cred). - */ +static void +union_fixup(un) + struct union_node *un; +{ - VATTR_NULL(&va); - va.va_type = VDIR; - va.va_mode = UN_DIRMODE &~ p->p_fd->fd_cmask; - VOP_UNLOCK(dvp); - LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE); - VREF(dvp); - VOP_LOCK(dvp); - error = VOP_MKDIR(dvp, vpp, cnp, &va); - VOP_LOCK(dvp); - return (error); + VOP_LOCK(un->un_uppervp); + un->un_flags |= UN_ULOCK; } static int -union_lookup1(dvp, vpp, cnp) - struct vnode *dvp; +union_lookup1(udvp, dvpp, vpp, cnp) + struct vnode *udvp; + struct vnode **dvpp; struct vnode **vpp; struct componentname *cnp; { int error; struct vnode *tdvp; + struct vnode *dvp; struct mount *mp; - if (cnp->cn_flags & ISDOTDOT) { - for (;;) { - if ((dvp->v_flag & VROOT) == 0 || - (cnp->cn_flags & NOCROSSMOUNT)) - break; + dvp = *dvpp; + /* + * If stepping up the directory tree, check for going + * back across the mount point, in which case do what + * lookup would do by stepping back down the mount + * hierarchy. + */ + if (cnp->cn_flags & ISDOTDOT) { + while ((dvp != udvp) && (dvp->v_flag & VROOT)) { + /* + * Don't do the NOCROSSMOUNT check + * at this level. By definition, + * union fs deals with namespaces, not + * filesystems. + */ tdvp = dvp; - dvp = dvp->v_mount->mnt_vnodecovered; + *dvpp = dvp = dvp->v_mount->mnt_vnodecovered; vput(tdvp); VREF(dvp); VOP_LOCK(dvp); } } - + error = VOP_LOOKUP(dvp, &tdvp, cnp); if (error) return (error); + /* + * The parent directory will have been unlocked, unless lookup + * found the last component. In which case, re-lock the node + * here to allow it to be unlocked again (phew) in union_lookup. + */ + if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN)) + VOP_LOCK(dvp); + dvp = tdvp; - while ((dvp->v_type == VDIR) && (mp = dvp->v_mountedhere) && - (cnp->cn_flags & NOCROSSMOUNT) == 0) { + + /* + * Lastly check if the current node is a mount point in + * which case walk up the mount hierarchy making sure not to + * bump into the root of the mount tree (ie. dvp != udvp). + */ + while (dvp != udvp && (dvp->v_type == VDIR) && + (mp = dvp->v_mountedhere)) { if (mp->mnt_flag & MNT_MLOCK) { mp->mnt_flag |= MNT_MWAIT; @@ -126,16 +132,38 @@ union_lookup(ap) struct vnode *uppervp, *lowervp; struct vnode *upperdvp, *lowerdvp; struct vnode *dvp = ap->a_dvp; - struct union_node *dun = VTOUNION(ap->a_dvp); + struct union_node *dun = VTOUNION(dvp); struct componentname *cnp = ap->a_cnp; int lockparent = cnp->cn_flags & LOCKPARENT; + int rdonly = cnp->cn_flags & RDONLY; + struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); + struct ucred *saved_cred; + int iswhiteout; + struct vattr va; + +#ifdef notyet + if (cnp->cn_namelen == 3 && + cnp->cn_nameptr[2] == '.' && + cnp->cn_nameptr[1] == '.' && + cnp->cn_nameptr[0] == '.') { + dvp = *ap->a_vpp = LOWERVP(ap->a_dvp); + if (dvp == NULLVP) + return (ENOENT); + VREF(dvp); + VOP_LOCK(dvp); + if (!lockparent || !(cnp->cn_flags & ISLASTCN)) + VOP_UNLOCK(ap->a_dvp); + return (0); + } +#endif cnp->cn_flags |= LOCKPARENT; upperdvp = dun->un_uppervp; lowerdvp = dun->un_lowervp; - uppervp = 0; - lowervp = 0; + uppervp = NULLVP; + lowervp = NULLVP; + iswhiteout = 0; /* * do the lookup in the upper level. @@ -143,11 +171,12 @@ union_lookup(ap) * then assume that something special is going * on and just return that vnode. */ - uppervp = 0; - if (upperdvp) { - VOP_LOCK(upperdvp); - uerror = union_lookup1(upperdvp, &uppervp, cnp); - VOP_UNLOCK(upperdvp); + if (upperdvp != NULLVP) { + FIXUP(dun); + uerror = union_lookup1(um->um_uppervp, &upperdvp, + &uppervp, cnp); + /*if (uppervp == upperdvp) + dun->un_flags |= UN_KLOCK;*/ if (cnp->cn_consume != 0) { *ap->a_vpp = uppervp; @@ -155,6 +184,16 @@ union_lookup(ap) cnp->cn_flags &= ~LOCKPARENT; return (uerror); } + if (uerror == ENOENT || uerror == EJUSTRETURN) { + if (cnp->cn_flags & ISWHITEOUT) { + iswhiteout = 1; + } else if (lowerdvp != NULLVP) { + lerror = VOP_GETATTR(upperdvp, &va, + cnp->cn_cred, cnp->cn_proc); + if (lerror == 0 && (va.va_flags & OPAQUE)) + iswhiteout = 1; + } + } } else { uerror = ENOENT; } @@ -166,16 +205,37 @@ union_lookup(ap) * back from the upper layer and return the lower vnode * instead. */ - lowervp = 0; - if (lowerdvp) { + if (lowerdvp != NULLVP && !iswhiteout) { + int nameiop; + VOP_LOCK(lowerdvp); - lerror = union_lookup1(lowerdvp, &lowervp, cnp); - VOP_UNLOCK(lowerdvp); + + /* + * Only do a LOOKUP on the bottom node, since + * we won't be making changes to it anyway. + */ + nameiop = cnp->cn_nameiop; + cnp->cn_nameiop = LOOKUP; + if (um->um_op == UNMNT_BELOW) { + saved_cred = cnp->cn_cred; + cnp->cn_cred = um->um_cred; + } + lerror = union_lookup1(um->um_lowervp, &lowerdvp, + &lowervp, cnp); + if (um->um_op == UNMNT_BELOW) + cnp->cn_cred = saved_cred; + cnp->cn_nameiop = nameiop; + + if (lowervp != lowerdvp) + VOP_UNLOCK(lowerdvp); if (cnp->cn_consume != 0) { - if (uppervp) { - vput(uppervp); - uppervp = 0; + if (uppervp != NULLVP) { + if (uppervp == upperdvp) + vrele(uppervp); + else + vput(uppervp); + uppervp = NULLVP; } *ap->a_vpp = lowervp; if (!lockparent) @@ -184,6 +244,14 @@ union_lookup(ap) } } else { lerror = ENOENT; + if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) { + lowervp = LOWERVP(dun->un_pvp); + if (lowervp != NULLVP) { + VREF(lowervp); + VOP_LOCK(lowervp); + lerror = 0; + } + } } if (!lockparent) @@ -213,44 +281,47 @@ union_lookup(ap) * whatever the bottom layer returned. */ + *ap->a_vpp = NULLVP; + /* case 1. */ if ((uerror != 0) && (lerror != 0)) { - *ap->a_vpp = 0; return (uerror); } /* case 2. */ if (uerror != 0 /* && (lerror == 0) */ ) { if (lowervp->v_type == VDIR) { /* case 2b. */ - VOP_LOCK(upperdvp); - uerror = union_mkshadow(upperdvp, cnp, &uppervp); + dun->un_flags &= ~UN_ULOCK; VOP_UNLOCK(upperdvp); + uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); + VOP_LOCK(upperdvp); + dun->un_flags |= UN_ULOCK; + if (uerror) { - if (lowervp) { + if (lowervp != NULLVP) { vput(lowervp); - lowervp = 0; + lowervp = NULLVP; } return (uerror); } } } - error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, cnp, - uppervp, lowervp); - - if (uppervp) - VOP_UNLOCK(uppervp); - if (lowervp) + if (lowervp != NULLVP) VOP_UNLOCK(lowervp); + error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, + uppervp, lowervp, 1); + if (error) { - if (uppervp) - vrele(uppervp); - if (lowervp) + if (uppervp != NULLVP) + vput(uppervp); + if (lowervp != NULLVP) vrele(lowervp); } else { - if (!lockparent) - VOP_UNLOCK(*ap->a_vpp); + if (*ap->a_vpp != dvp) + if (!lockparent || !(cnp->cn_flags & ISLASTCN)) + VOP_UNLOCK(dvp); } return (error); @@ -268,13 +339,16 @@ union_create(ap) struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; - if (dvp) { + if (dvp != NULLVP) { int error; struct vnode *vp; - struct mount *mp = ap->a_dvp->v_mount; + struct mount *mp; + + FIXUP(un); VREF(dvp); - VOP_LOCK(dvp); + un->un_flags |= UN_KLOCK; + mp = ap->a_dvp->v_mount; vput(ap->a_dvp); error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) @@ -284,12 +358,13 @@ union_create(ap) ap->a_vpp, mp, NULLVP, + NULLVP, ap->a_cnp, vp, - NULLVP); - VOP_UNLOCK(vp); + NULLVP, + 1); if (error) - vrele(vp); + vput(vp); return (error); } @@ -297,6 +372,23 @@ union_create(ap) return (EROFS); } +int +union_whiteout(ap) + struct vop_whiteout_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + int a_flags; + } */ *ap; +{ + struct union_node *un = VTOUNION(ap->a_dvp); + + if (un->un_uppervp == NULLVP) + return (EOPNOTSUPP); + + FIXUP(un); + return (VOP_WHITEOUT(un->un_uppervp, ap->a_cnp, ap->a_flags)); +} + int union_mknod(ap) struct vop_mknod_args /* { @@ -309,29 +401,33 @@ union_mknod(ap) struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; - if (dvp) { + if (dvp != NULLVP) { int error; struct vnode *vp; - struct mount *mp = ap->a_dvp->v_mount; + struct mount *mp; + + FIXUP(un); VREF(dvp); - VOP_LOCK(dvp); + 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); if (error) return (error); - if (vp) { + if (vp != NULLVP) { error = union_allocvp( ap->a_vpp, mp, NULLVP, + NULLVP, ap->a_cnp, vp, - NULLVP); - VOP_UNLOCK(vp); + NULLVP, + 1); if (error) - vrele(vp); + vput(vp); } return (error); } @@ -340,75 +436,6 @@ union_mknod(ap) return (EROFS); } -/* - * copyfile. copy the vnode (fvp) to the vnode (tvp) - * using a sequence of reads and writes. both (fvp) - * and (tvp) are locked on entry and exit. - */ -static int -union_copyfile(p, cred, fvp, tvp) - struct proc *p; - struct ucred *cred; - struct vnode *fvp; - struct vnode *tvp; -{ - char *buf; - struct uio uio; - struct iovec iov; - int error = 0; - off_t offset; - - /* - * strategy: - * allocate a buffer of size MAXBSIZE. - * loop doing reads and writes, keeping track - * of the current uio offset. - * give up at the first sign of trouble. - */ - - uio.uio_procp = p; - uio.uio_segflg = UIO_SYSSPACE; - offset = 0; - - VOP_UNLOCK(fvp); /* XXX */ - LEASE_CHECK(fvp, p, cred, LEASE_READ); - VOP_LOCK(fvp); /* XXX */ - VOP_UNLOCK(tvp); /* XXX */ - LEASE_CHECK(tvp, p, cred, LEASE_WRITE); - VOP_LOCK(tvp); /* XXX */ - - buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK); - do { - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - iov.iov_base = buf; - iov.iov_len = MAXBSIZE; - uio.uio_resid = iov.iov_len; - uio.uio_offset = offset; - uio.uio_rw = UIO_READ; - error = VOP_READ(fvp, &uio, 0, cred); - - if (error == 0) { - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - iov.iov_base = buf; - iov.iov_len = MAXBSIZE - uio.uio_resid; - uio.uio_rw = UIO_WRITE; - uio.uio_resid = iov.iov_len; - uio.uio_offset = offset; - - do { - error = VOP_WRITE(tvp, &uio, 0, cred); - } while (error == 0 && uio.uio_resid > 0); - if (error == 0) - offset = uio.uio_offset; - } - } while ((uio.uio_resid == 0) && (error == 0)); - - free(buf, M_TEMP); - return (error); -} - int union_open(ap) struct vop_open_args /* { @@ -438,66 +465,26 @@ union_open(ap) */ tvp = un->un_lowervp; if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) { - struct nameidata nd; - struct filedesc *fdp = p->p_fd; - int fmode; - int cmode; - - /* - * Open the named file in the upper layer. Note that - * the file may have come into existence *since* the - * lookup was done, since the upper layer may really - * be a loopback mount of some other filesystem... - * so open the file with exclusive create and barf if - * it already exists. - * XXX - perhaps shoudl re-lookup the node (once more - * with feeling) and simply open that. Who knows. - */ - NDINIT(&nd, CREATE, 0, UIO_SYSSPACE, un->un_path, p); - fmode = (O_CREAT|O_TRUNC|O_EXCL); - cmode = UN_FILEMODE & ~fdp->fd_cmask; - error = vn_open(&nd, fmode, cmode); - if (error) - return (error); - un->un_uppervp = nd.ni_vp; /* XXX */ - /* at this point, uppervp is locked */ - - /* - * Now, if the file is being opened with truncation, - * then the (new) upper vnode is ready to fly, - * otherwise the data from the lower vnode must be - * copied to the upper layer first. This only works - * for regular files (check is made above). - */ - if ((mode & O_TRUNC) == 0) { - /* - * XXX - should not ignore errors - * from VOP_CLOSE - */ - VOP_LOCK(un->un_lowervp); - error = VOP_OPEN(tvp, FREAD, cred, p); - if (error == 0) { - error = union_copyfile(p, cred, - tvp, un->un_uppervp); - VOP_UNLOCK(tvp); - (void) VOP_CLOSE(tvp, FREAD); - } else { - VOP_UNLOCK(tvp); - } - VOP_UNLOCK(un->un_uppervp); - (void) VOP_CLOSE(un->un_uppervp, FWRITE); - VOP_LOCK(un->un_uppervp); - } + error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p); if (error == 0) error = VOP_OPEN(un->un_uppervp, mode, cred, p); - VOP_UNLOCK(un->un_uppervp); return (error); } + + /* + * Just open the lower vnode + */ + un->un_openl++; + VOP_LOCK(tvp); + error = VOP_OPEN(tvp, mode, cred, p); + VOP_UNLOCK(tvp); + + return (error); } - VOP_LOCK(tvp); + FIXUP(un); + error = VOP_OPEN(tvp, mode, cred, p); - VOP_UNLOCK(tvp); return (error); } @@ -511,8 +498,21 @@ union_close(ap) struct proc *a_p; } */ *ap; { + struct union_node *un = VTOUNION(ap->a_vp); + struct vnode *vp; - return (VOP_CLOSE(OTHERVP(ap->a_vp), ap->a_fflag, ap->a_cred, ap->a_p)); + if (un->un_uppervp != NULLVP) { + vp = un->un_uppervp; + } else { +#ifdef UNION_DIAGNOSTIC + if (un->un_openl <= 0) + panic("union: un_openl cnt"); +#endif + --un->un_openl; + vp = un->un_lowervp; + } + + return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p)); } /* @@ -534,28 +534,35 @@ union_access(ap) } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); - int error = 0; + int error = EACCES; struct vnode *vp; - if (vp = un->un_lowervp) { - VOP_LOCK(vp); - error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p); - VOP_UNLOCK(vp); - if (error) - return (error); + if ((vp = un->un_uppervp) != NULLVP) { + FIXUP(un); + return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p)); } - if (vp = un->un_uppervp) { + if ((vp = un->un_lowervp) != NULLVP) { VOP_LOCK(vp); error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p); + if (error == 0) { + struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount); + + if (um->um_op == UNMNT_BELOW) + error = VOP_ACCESS(vp, ap->a_mode, + um->um_cred, ap->a_p); + } VOP_UNLOCK(vp); + if (error) + return (error); } return (error); } /* - * We handle getattr only to change the fsid. + * We handle getattr only to change the fsid and + * track object sizes */ int union_getattr(ap) @@ -567,13 +574,61 @@ union_getattr(ap) } */ *ap; { int error; - struct vnode *vp = OTHERVP(ap->a_vp); + struct union_node *un = VTOUNION(ap->a_vp); + struct vnode *vp = un->un_uppervp; + struct vattr *vap; + struct vattr va; + + + /* + * Some programs walk the filesystem hierarchy by counting + * links to directories to avoid stat'ing all the time. + * This means the link count on directories needs to be "correct". + * The only way to do that is to call getattr on both layers + * and fix up the link count. The link count will not necessarily + * be accurate but will be large enough to defeat the tree walkers. + */ - VOP_LOCK(vp); - error = VOP_GETATTR(vp, ap->a_vap, ap->a_cred, ap->a_p); - VOP_UNLOCK(vp); + vap = ap->a_vap; + + vp = un->un_uppervp; + if (vp != NULLVP) { + /* + * It's not clear whether VOP_GETATTR is to be + * called with the vnode locked or not. stat() calls + * it with (vp) locked, and fstat calls it with + * (vp) unlocked. + * In the mean time, compensate here by checking + * the union_node's lock flag. + */ + if (un->un_flags & UN_LOCKED) + FIXUP(un); + + error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); + if (error) + return (error); + union_newsize(ap->a_vp, vap->va_size, VNOVAL); + } + + if (vp == NULLVP) { + vp = un->un_lowervp; + } else if (vp->v_type == VDIR) { + vp = un->un_lowervp; + vap = &va; + } else { + vp = NULLVP; + } + + if (vp != NULLVP) { + error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); + if (error) + return (error); + union_newsize(ap->a_vp, VNOVAL, vap->va_size); + } + + if ((vap != ap->a_vap) && (vap->va_type == VDIR)) + ap->a_vap->va_nlink += vap->va_nlink; - /* Requires that arguments be restored. */ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } @@ -590,17 +645,31 @@ union_setattr(ap) struct union_node *un = VTOUNION(ap->a_vp); int error; - if (un->un_uppervp) { - VOP_LOCK(un->un_uppervp); + /* + * Handle case of truncating lower object to zero size, + * by creating a zero length upper object. This is to + * handle the case of open with O_TRUNC and O_CREAT. + */ + if ((un->un_uppervp == NULLVP) && + /* assert(un->un_lowervp != NULLVP) */ + (un->un_lowervp->v_type == VREG)) { + error = union_copyup(un, (ap->a_vap->va_size != 0), + ap->a_cred, ap->a_p); + if (error) + return (error); + } + + /* + * Try to set attributes in upper layer, + * otherwise return read-only filesystem error. + */ + if (un->un_uppervp != NULLVP) { + FIXUP(un); error = VOP_SETATTR(un->un_uppervp, ap->a_vap, ap->a_cred, ap->a_p); - VOP_UNLOCK(un->un_uppervp); + if ((error == 0) && (ap->a_vap->va_size != VNOVAL)) + union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL); } else { - /* - * XXX should do a copyfile (perhaps only if - * the file permission change, which would not - * track va_ctime correctly). - */ error = EROFS; } @@ -618,10 +687,34 @@ union_read(ap) { int error; struct vnode *vp = OTHERVP(ap->a_vp); + int dolock = (vp == LOWERVP(ap->a_vp)); - VOP_LOCK(vp); + if (dolock) + VOP_LOCK(vp); + else + FIXUP(VTOUNION(ap->a_vp)); error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); - VOP_UNLOCK(vp); + if (dolock) + VOP_UNLOCK(vp); + + /* + * XXX + * perhaps the size of the underlying object has changed under + * our feet. take advantage of the offset information present + * in the uio structure. + */ + if (error == 0) { + struct union_node *un = VTOUNION(ap->a_vp); + off_t cur = ap->a_uio->uio_offset; + + if (vp == un->un_uppervp) { + if (cur > un->un_uppersz) + union_newsize(ap->a_vp, cur, VNOVAL); + } else { + if (cur > un->un_lowersz) + union_newsize(ap->a_vp, VNOVAL, cur); + } + } return (error); } @@ -636,15 +729,42 @@ union_write(ap) } */ *ap; { int error; - struct vnode *vp = OTHERVP(ap->a_vp); + struct vnode *vp; + struct union_node *un = VTOUNION(ap->a_vp); + + vp = UPPERVP(ap->a_vp); + if (vp == NULLVP) + panic("union: missing upper layer in write"); - VOP_LOCK(vp); + FIXUP(un); error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); - VOP_UNLOCK(vp); + + /* + * the size of the underlying object may be changed by the + * write. + */ + if (error == 0) { + off_t cur = ap->a_uio->uio_offset; + + if (cur > un->un_uppersz) + union_newsize(ap->a_vp, cur, VNOVAL); + } return (error); } +union_lease(ap) + struct vop_lease_args /* { + struct vnode *a_vp; + struct proc *a_p; + struct ucred *a_cred; + int a_flag; + } */ *ap; +{ + + return (VOP_LEASE(OTHERVP(ap->a_vp), ap->a_p, ap->a_cred, ap->a_flag)); +} + int union_ioctl(ap) struct vop_ioctl_args /* { @@ -676,6 +796,22 @@ union_select(ap) ap->a_cred, ap->a_p)); } +int +union_revoke(ap) + struct vop_revoke_args /* { + struct vnode *a_vp; + int a_flags; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + + if (UPPERVP(vp)) + VOP_REVOKE(UPPERVP(vp), ap->a_flags); + if (LOWERVP(vp)) + VOP_REVOKE(UPPERVP(vp), ap->a_flags); + vgone(vp); +} + int union_mmap(ap) struct vop_mmap_args /* { @@ -702,11 +838,17 @@ union_fsync(ap) int error = 0; struct vnode *targetvp = OTHERVP(ap->a_vp); - if (targetvp) { - VOP_LOCK(targetvp); + if (targetvp != NULLVP) { + int dolock = (targetvp == LOWERVP(ap->a_vp)); + + if (dolock) + VOP_LOCK(targetvp); + else + FIXUP(VTOUNION(ap->a_vp)); error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, ap->a_p); - VOP_UNLOCK(targetvp); + if (dolock) + VOP_UNLOCK(targetvp); } return (error); @@ -737,25 +879,35 @@ union_remove(ap) struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); - if (dun->un_uppervp && un->un_uppervp) { + if (dun->un_uppervp == NULLVP) + panic("union remove: null upper vnode"); + + 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); VREF(dvp); - VOP_LOCK(dvp); + dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); + FIXUP(un); VREF(vp); - VOP_LOCK(vp); + un->un_flags |= UN_KLOCK; vput(ap->a_vp); - error = VOP_REMOVE(dvp, vp, ap->a_cnp); + if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) + cnp->cn_flags |= DOWHITEOUT; + error = VOP_REMOVE(dvp, vp, cnp); + if (!error) + union_removed_upper(un); } else { - /* - * XXX: should create a whiteout here - */ + FIXUP(dun); + error = union_mkwhiteout( + MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), + dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); - error = EROFS; } return (error); @@ -769,32 +921,49 @@ union_link(ap) struct componentname *a_cnp; } */ *ap; { - int error; - struct union_node *dun = VTOUNION(ap->a_vp); - struct union_node *un = VTOUNION(ap->a_tdvp); + int error = 0; + struct union_node *un; + struct vnode *vp; + struct vnode *tdvp; - if (dun->un_uppervp && un->un_uppervp) { - struct vnode *dvp = dun->un_uppervp; - struct vnode *vp = un->un_uppervp; + un = VTOUNION(ap->a_tdvp); - VREF(dvp); - VOP_LOCK(dvp); - vput(ap->a_vp); - VREF(vp); - vrele(ap->a_tdvp); - - error = VOP_LINK(dvp, vp, ap->a_cnp); + if (ap->a_tdvp->v_op != ap->a_vp->v_op) { + vp = ap->a_vp; } else { - /* - * XXX: need to copy to upper layer - * and do the link there. - */ - vput(ap->a_vp); - vrele(ap->a_tdvp); + struct union_node *tun = VTOUNION(ap->a_vp); + if (tun->un_uppervp == NULLVP) { + VOP_LOCK(ap->a_vp); + if (un->un_uppervp == tun->un_dirvp) { + un->un_flags &= ~UN_ULOCK; + VOP_UNLOCK(un->un_uppervp); + } + error = union_copyup(tun, 1, ap->a_cnp->cn_cred, + ap->a_cnp->cn_proc); + if (un->un_uppervp == tun->un_dirvp) { + VOP_LOCK(un->un_uppervp); + un->un_flags |= UN_ULOCK; + } + VOP_UNLOCK(ap->a_vp); + } + vp = tun->un_uppervp; + } + + tdvp = un->un_uppervp; + if (tdvp == NULLVP) error = EROFS; + + if (error) { + vput(ap->a_tdvp); + return (error); } - return (error); + FIXUP(un); + VREF(tdvp); + un->un_flags |= UN_KLOCK; + vput(ap->a_tdvp); + + return (VOP_LINK(vp, tdvp, ap->a_cnp)); } int @@ -817,8 +986,14 @@ union_rename(ap) if (fdvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fdvp); - if (un->un_uppervp == 0) { - error = EROFS; + if (un->un_uppervp == NULLVP) { + /* + * this should never happen in normal + * operation but might if there was + * a problem creating the top-level shadow + * directory. + */ + error = EXDEV; goto bad; } @@ -829,11 +1004,15 @@ union_rename(ap) if (fvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fvp); - if (un->un_uppervp == 0) { - error = EROFS; + if (un->un_uppervp == NULLVP) { + /* XXX: should do a copyup */ + error = EXDEV; goto bad; } + if (un->un_lowervp != NULLVP) + ap->a_fcnp->cn_flags |= DOWHITEOUT; + fvp = un->un_uppervp; VREF(fvp); vrele(ap->a_fvp); @@ -841,27 +1020,31 @@ union_rename(ap) if (tdvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tdvp); - if (un->un_uppervp == 0) { - error = EROFS; + if (un->un_uppervp == NULLVP) { + /* + * this should never happen in normal + * operation but might if there was + * a problem creating the top-level shadow + * directory. + */ + error = EXDEV; goto bad; } tdvp = un->un_uppervp; VREF(tdvp); - VOP_LOCK(tdvp); - vput(ap->a_fdvp); + un->un_flags |= UN_KLOCK; + vput(ap->a_tdvp); } - if (tvp && tvp->v_op == union_vnodeop_p) { + if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tvp); - if (un->un_uppervp == 0) { - error = EROFS; - goto bad; - } tvp = un->un_uppervp; - VREF(tvp); - VOP_LOCK(tvp); + if (tvp != NULLVP) { + VREF(tvp); + un->un_flags |= UN_KLOCK; + } vput(ap->a_tvp); } @@ -871,7 +1054,7 @@ bad: vrele(fdvp); vrele(fvp); vput(tdvp); - if (tvp) + if (tvp != NULLVP) vput(tvp); return (error); @@ -889,28 +1072,32 @@ union_mkdir(ap) struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; - if (dvp) { + if (dvp != NULLVP) { int error; struct vnode *vp; - struct mount *mp = ap->a_dvp->v_mount; + FIXUP(un); VREF(dvp); - VOP_LOCK(dvp); - vput(ap->a_dvp); + un->un_flags |= UN_KLOCK; + VOP_UNLOCK(ap->a_dvp); error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap); - if (error) + if (error) { + vrele(ap->a_dvp); return (error); + } error = union_allocvp( ap->a_vpp, - mp, + ap->a_dvp->v_mount, + ap->a_dvp, NULLVP, ap->a_cnp, vp, - NULLVP); - VOP_UNLOCK(vp); + NULLVP, + 1); + vrele(ap->a_dvp); if (error) - vrele(vp); + vput(vp); return (error); } @@ -930,25 +1117,35 @@ union_rmdir(ap) struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); - if (dun->un_uppervp && un->un_uppervp) { + if (dun->un_uppervp == NULLVP) + panic("union rmdir: null upper vnode"); + + 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); VREF(dvp); - VOP_LOCK(dvp); + dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); + FIXUP(un); VREF(vp); - VOP_LOCK(vp); + un->un_flags |= UN_KLOCK; vput(ap->a_vp); - error = VOP_REMOVE(dvp, vp, ap->a_cnp); + if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) + cnp->cn_flags |= DOWHITEOUT; + error = VOP_RMDIR(dvp, vp, ap->a_cnp); + if (!error) + union_removed_upper(un); } else { - /* - * XXX: should create a whiteout here - */ + FIXUP(dun); + error = union_mkwhiteout( + MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), + dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); - error = EROFS; } return (error); @@ -967,17 +1164,18 @@ union_symlink(ap) struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; - if (dvp) { + if (dvp != NULLVP) { int error; struct vnode *vp; struct mount *mp = ap->a_dvp->v_mount; + FIXUP(un); VREF(dvp); - VOP_LOCK(dvp); + un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_SYMLINK(dvp, &vp, ap->a_cnp, ap->a_vap, ap->a_target); - *ap->a_vpp = 0; + *ap->a_vpp = NULLVP; return (error); } @@ -999,20 +1197,20 @@ union_readdir(ap) struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; + int *a_eofflag; + u_long *a_cookies; + int a_ncookies; } */ *ap; { - int error = 0; - struct union_node *un = VTOUNION(ap->a_vp); + register struct union_node *un = VTOUNION(ap->a_vp); + register struct vnode *uvp = un->un_uppervp; - if (un->un_uppervp) { - struct vnode *vp = OTHERVP(ap->a_vp); + if (uvp == NULLVP) + return (0); - VOP_LOCK(vp); - error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); - VOP_UNLOCK(vp); - } - - return (error); + FIXUP(un); + ap->a_vp = uvp; + return (VOCALL(uvp->v_op, VOFFSET(vop_readdir), ap)); } int @@ -1025,10 +1223,15 @@ union_readlink(ap) { int error; struct vnode *vp = OTHERVP(ap->a_vp); + int dolock = (vp == LOWERVP(ap->a_vp)); - VOP_LOCK(vp); + if (dolock) + VOP_LOCK(vp); + else + FIXUP(VTOUNION(ap->a_vp)); error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); - VOP_UNLOCK(vp); + if (dolock) + VOP_UNLOCK(vp); return (error); } @@ -1044,11 +1247,16 @@ union_abortop(ap) struct vnode *vp = OTHERVP(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_dvp); int islocked = un->un_flags & UN_LOCKED; + int dolock = (vp == LOWERVP(ap->a_dvp)); - if (islocked) - VOP_LOCK(vp); + if (islocked) { + if (dolock) + VOP_LOCK(vp); + else + FIXUP(VTOUNION(ap->a_dvp)); + } error = VOP_ABORTOP(vp, ap->a_cnp); - if (islocked) + if (islocked && dolock) VOP_UNLOCK(vp); return (error); @@ -1060,6 +1268,8 @@ union_inactive(ap) struct vnode *a_vp; } */ *ap; { + struct union_node *un = VTOUNION(ap->a_vp); + struct vnode **vpp; /* * Do nothing (and _don't_ bypass). @@ -1073,6 +1283,24 @@ union_inactive(ap) * like they do in the name lookup cache code. * That's too much work for now. */ + +#ifdef UNION_DIAGNOSTIC + if (un->un_flags & UN_LOCKED) + panic("union: inactivating locked node"); + if (un->un_flags & UN_ULOCK) + panic("union: inactivating w/locked upper node"); +#endif + + if (un->un_dircache != 0) { + for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) + vrele(*vpp); + free(un->un_dircache, M_TEMP); + un->un_dircache = 0; + } + + if ((un->un_flags & UN_CACHED) == 0) + vgone(ap->a_vp); + return (0); } @@ -1082,31 +1310,9 @@ union_reclaim(ap) struct vnode *a_vp; } */ *ap; { - struct vnode *vp = ap->a_vp; - struct union_node *un = VTOUNION(vp); - struct vnode *uppervp = un->un_uppervp; - struct vnode *lowervp = un->un_lowervp; - struct vnode *dirvp = un->un_dirvp; - char *path = un->un_path; - /* - * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p, - * so we can't call VOPs on ourself. - */ - /* After this assignment, this node will not be re-used. */ - un->un_uppervp = 0; - un->un_lowervp = 0; - un->un_dirvp = 0; - un->un_path = NULL; - union_freevp(vp); - if (uppervp) - vrele(uppervp); - if (lowervp) - vrele(lowervp); - if (dirvp) - vrele(dirvp); - if (path) - free(path, M_TEMP); + union_freevp(ap->a_vp); + return (0); } @@ -1114,22 +1320,64 @@ int union_lock(ap) struct vop_lock_args *ap; { - struct union_node *un = VTOUNION(ap->a_vp); + struct vnode *vp = ap->a_vp; + struct union_node *un; - while (un->un_flags & UN_LOCKED) { +start: + while (vp->v_flag & VXLOCK) { + vp->v_flag |= VXWANT; + sleep((caddr_t)vp, PINOD); + } + + un = VTOUNION(vp); + + if (un->un_uppervp != NULLVP) { + if (((un->un_flags & UN_ULOCK) == 0) && + (vp->v_usecount != 0)) { + VOP_LOCK(un->un_uppervp); + un->un_flags |= UN_ULOCK; + } #ifdef DIAGNOSTIC - if (un->un_pid == curproc->p_pid) - panic("union: locking agsinst myself"); + if (un->un_flags & UN_KLOCK) { + vprint("union: dangling klock", vp); + panic("union: dangling upper lock (%lx)", vp); + } +#endif + } + + if (un->un_flags & UN_LOCKED) { +#ifdef DIAGNOSTIC + if (curproc && un->un_pid == curproc->p_pid && + un->un_pid > -1 && curproc->p_pid > -1) + panic("union: locking against myself"); #endif un->un_flags |= UN_WANT; sleep((caddr_t) &un->un_flags, PINOD); + goto start; } - un->un_flags |= UN_LOCKED; + #ifdef DIAGNOSTIC - un->un_pid = curproc->p_pid; + if (curproc) + un->un_pid = curproc->p_pid; + else + un->un_pid = -1; #endif + + un->un_flags |= UN_LOCKED; + return (0); } +/* + * When operations want to vput() a union node yet retain a lock on + * 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() 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 vnode, and ensures that it gets unlocked. + * + * If UN_KLOCK isn't set, then the upper vnode is unlocked here. + */ int union_unlock(ap) struct vop_lock_args *ap; @@ -1139,11 +1387,18 @@ union_unlock(ap) #ifdef DIAGNOSTIC if ((un->un_flags & UN_LOCKED) == 0) panic("union: unlock unlocked node"); - if (un->un_pid != curproc->p_pid) + if (curproc && un->un_pid != curproc->p_pid && + curproc->p_pid > -1 && un->un_pid > -1) panic("union: unlocking other process's union node"); #endif un->un_flags &= ~UN_LOCKED; + + if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK) + VOP_UNLOCK(un->un_uppervp); + + un->un_flags &= ~(UN_ULOCK|UN_KLOCK); + if (un->un_flags & UN_WANT) { un->un_flags &= ~UN_WANT; wakeup((caddr_t) &un->un_flags); @@ -1152,6 +1407,8 @@ union_unlock(ap) #ifdef DIAGNOSTIC un->un_pid = 0; #endif + + return (0); } int @@ -1166,10 +1423,15 @@ union_bmap(ap) { int error; struct vnode *vp = OTHERVP(ap->a_vp); + int dolock = (vp == LOWERVP(ap->a_vp)); - VOP_LOCK(vp); + if (dolock) + VOP_LOCK(vp); + else + FIXUP(VTOUNION(ap->a_vp)); error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); - VOP_UNLOCK(vp); + if (dolock) + VOP_UNLOCK(vp); return (error); } @@ -1184,6 +1446,11 @@ union_print(ap) printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n", vp, UPPERVP(vp), LOWERVP(vp)); + if (UPPERVP(vp) != NULLVP) + vprint("union: upper", UPPERVP(vp)); + if (LOWERVP(vp) != NULLVP) + vprint("union: lower", LOWERVP(vp)); + return (0); } @@ -1207,10 +1474,15 @@ union_pathconf(ap) { int error; struct vnode *vp = OTHERVP(ap->a_vp); + int dolock = (vp == LOWERVP(ap->a_vp)); - VOP_LOCK(vp); + if (dolock) + VOP_LOCK(vp); + else + FIXUP(VTOUNION(ap->a_vp)); error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval); - VOP_UNLOCK(vp); + if (dolock) + VOP_UNLOCK(vp); return (error); } @@ -1250,7 +1522,7 @@ union_strategy(ap) bp->b_vp = OTHERVP(bp->b_vp); #ifdef DIAGNOSTIC - if (bp->b_vp == 0) + if (bp->b_vp == NULLVP) panic("union_strategy: nil vp"); if (((bp->b_flags & B_READ) == 0) && (bp->b_vp == LOWERVP(savedvp))) @@ -1271,6 +1543,7 @@ struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, union_lookup }, /* lookup */ { &vop_create_desc, union_create }, /* create */ + { &vop_whiteout_desc, union_whiteout }, /* whiteout */ { &vop_mknod_desc, union_mknod }, /* mknod */ { &vop_open_desc, union_open }, /* open */ { &vop_close_desc, union_close }, /* close */ @@ -1279,8 +1552,10 @@ struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_setattr_desc, union_setattr }, /* setattr */ { &vop_read_desc, union_read }, /* read */ { &vop_write_desc, union_write }, /* write */ + { &vop_lease_desc, union_lease }, /* lease */ { &vop_ioctl_desc, union_ioctl }, /* ioctl */ { &vop_select_desc, union_select }, /* select */ + { &vop_revoke_desc, union_revoke }, /* revoke */ { &vop_mmap_desc, union_mmap }, /* mmap */ { &vop_fsync_desc, union_fsync }, /* fsync */ { &vop_seek_desc, union_seek }, /* seek */