checkpoint
authorJan-Simon Pendry <pendry@ucbvax.Berkeley.EDU>
Wed, 9 Feb 1994 08:16:05 +0000 (00:16 -0800)
committerJan-Simon Pendry <pendry@ucbvax.Berkeley.EDU>
Wed, 9 Feb 1994 08:16:05 +0000 (00:16 -0800)
SCCS-vsn: sys/miscfs/union/union.h 1.9
SCCS-vsn: sys/miscfs/union/union_subr.c 1.9
SCCS-vsn: sys/miscfs/union/union_vnops.c 1.9
SCCS-vsn: sys/miscfs/union/union_vfsops.c 1.9

usr/src/sys/miscfs/union/union.h
usr/src/sys/miscfs/union/union_subr.c
usr/src/sys/miscfs/union/union_vfsops.c
usr/src/sys/miscfs/union/union_vnops.c

index c8172a7..9657ba9 100644 (file)
@@ -8,13 +8,19 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union.h     1.8 (Berkeley) %G%
+ *     @(#)union.h     1.9 (Berkeley) %G%
  */
 
 struct union_args {
        char            *target;        /* Target of loopback  */
  */
 
 struct union_args {
        char            *target;        /* Target of loopback  */
+       int             mntflags;       /* Options on the mount */
 };
 
 };
 
+#define UNMNT_ABOVE    0x0001          /* Target appears below mount point */
+#define UNMNT_BELOW    0x0002          /* Target appears below mount point */
+#define UNMNT_REPLACE  0x0003          /* Target replaces mount point */
+#define UNMNT_OPMASK   0x0003
+
 struct union_mount {
        struct vnode    *um_uppervp;
        struct vnode    *um_lowervp;
 struct union_mount {
        struct vnode    *um_uppervp;
        struct vnode    *um_lowervp;
index 3da44e3..5e13601 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_subr.c        1.8 (Berkeley) %G%
+ *     @(#)union_subr.c        1.9 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -330,12 +330,10 @@ union_mkshadow(um, dvp, cnp, vpp)
 
        /*
         * policy: when creating the shadow directory in the
 
        /*
         * 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).
+        * 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)
         */
 
        /*
         */
 
        /*
@@ -356,7 +354,7 @@ union_mkshadow(um, dvp, cnp, vpp)
        cn.cn_nameiop = CREATE;
        cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|ISLASTCN);
        cn.cn_proc = cnp->cn_proc;
        cn.cn_nameiop = CREATE;
        cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|ISLASTCN);
        cn.cn_proc = cnp->cn_proc;
-       cn.cn_cred = cnp->cn_cred;
+       cn.cn_cred = um->um_cred;
        cn.cn_nameptr = cn.cn_pnbuf;
        cn.cn_namelen = cnp->cn_namelen;
        cn.cn_hash = cnp->cn_hash;
        cn.cn_nameptr = cn.cn_pnbuf;
        cn.cn_namelen = cnp->cn_namelen;
        cn.cn_hash = cnp->cn_hash;
@@ -377,7 +375,7 @@ union_mkshadow(um, dvp, cnp, vpp)
 
        VATTR_NULL(&va);
        va.va_type = VDIR;
 
        VATTR_NULL(&va);
        va.va_type = VDIR;
-       va.va_mode = UN_DIRMODE & ~p->p_fd->fd_cmask;
+       va.va_mode = um->um_cmode;
 
        /* LEASE_CHECK: dvp is locked */
        LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
 
        /* LEASE_CHECK: dvp is locked */
        LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
@@ -413,6 +411,15 @@ union_vn_create(vpp, un, p)
 
        *vpp = NULLVP;
 
 
        *vpp = NULLVP;
 
+       /*
+        * Build a new componentname structure (for the same
+        * reasons outlines in union_mkshadow).
+        * The difference here is that the file is owned by
+        * the current user, rather than by the person who
+        * did the mount, since the current user needs to be
+        * able to write the file (that's why it is being
+        * copied in the first place).
+        */
        cn.cn_namelen = strlen(un->un_path);
        cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
        bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
        cn.cn_namelen = strlen(un->un_path);
        cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
        bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
@@ -430,6 +437,16 @@ union_vn_create(vpp, un, p)
        vrele(un->un_dirvp);
 
        if (vp == NULLVP) {
        vrele(un->un_dirvp);
 
        if (vp == NULLVP) {
+               /*
+                * Good - there was no race to create the file
+                * so go ahead and create it.  The permissions
+                * on the file will be 0666 modified by the
+                * current user's umask.  Access to the file, while
+                * it is unioned, will require access to the top *and*
+                * bottom files.  Access when not unioned will simply
+                * require access to the top-level file.
+                * TODO: confirm choice of access permissions.
+                */
                VATTR_NULL(vap);
                vap->va_type = VREG;
                vap->va_mode = cmode;
                VATTR_NULL(vap);
                vap->va_type = VREG;
                vap->va_mode = cmode;
index 091f3ac..2139392 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_vfsops.c      1.8 (Berkeley) %G%
+ *     @(#)union_vfsops.c      1.9 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
@@ -40,9 +40,12 @@ union_mount(mp, path, data, ndp, p)
 {
        int error = 0;
        struct union_args args;
 {
        int error = 0;
        struct union_args args;
-       struct vnode *lowerrootvp;
-       struct vnode *upperrootvp;
+       struct vnode *lowerrootvp = NULLVP;
+       struct vnode *upperrootvp = NULLVP;
        struct union_mount *um;
        struct union_mount *um;
+       struct ucred *cred = 0;
+       char *cp;
+       int len;
        u_int size;
 
 #ifdef UNION_DIAGNOSTIC
        u_int size;
 
 #ifdef UNION_DIAGNOSTIC
@@ -58,14 +61,35 @@ union_mount(mp, path, data, ndp, p)
                 * 1. a way to convert between rdonly and rdwr mounts.
                 * 2. support for nfs exports.
                 */
                 * 1. a way to convert between rdonly and rdwr mounts.
                 * 2. support for nfs exports.
                 */
-               return (EOPNOTSUPP);
+               error = EOPNOTSUPP;
+               goto bad;
        }
 
        }
 
+       /*
+        * Take a copy of the process's credentials.  This isn't
+        * quite right since the euid will always be zero and we
+        * want to get the "real" users credentials.  So fix up
+        * the uid field after taking the copy.
+        */
+       cred = crdup(p->p_ucred);
+       cred->cr_uid = p->p_cred->p_ruid;
+
+       /*
+        * Ensure the *real* user has write permission on the
+        * mounted-on directory.  This allows the mount_union
+        * command to be made setuid root so allowing anyone
+        * to do union mounts onto any directory on which they
+        * have write permission.
+        */
+       error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
+       if (error)
+               goto bad;
+
        /*
         * Get argument
         */
        if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
        /*
         * Get argument
         */
        if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
-               return (error);
+               goto bad;
 
        lowerrootvp = mp->mnt_vnodecovered;
        VREF(lowerrootvp);
 
        lowerrootvp = mp->mnt_vnodecovered;
        VREF(lowerrootvp);
@@ -75,18 +99,16 @@ union_mount(mp, path, data, ndp, p)
         */
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
               UIO_USERSPACE, args.target, p);
         */
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
               UIO_USERSPACE, args.target, p);
-       if (error = namei(ndp)) {
-               vrele(lowerrootvp);
-               return (error);
-       }
+       if (error = namei(ndp))
+               goto bad;
+
        upperrootvp = ndp->ni_vp;
        vrele(ndp->ni_dvp);
        ndp->ni_dvp = NULL;
 
        if (upperrootvp->v_type != VDIR) {
        upperrootvp = ndp->ni_vp;
        vrele(ndp->ni_dvp);
        ndp->ni_dvp = NULL;
 
        if (upperrootvp->v_type != VDIR) {
-               vrele(lowerrootvp);
-               vrele(upperrootvp);
-               return (EINVAL);
+               error = EINVAL;
+               goto bad;
        }
        
        um = (struct union_mount *) malloc(sizeof(struct union_mount),
        }
        
        um = (struct union_mount *) malloc(sizeof(struct union_mount),
@@ -95,17 +117,36 @@ union_mount(mp, path, data, ndp, p)
        /*
         * Keep a held reference to the target vnodes.
         * They are vrele'd in union_unmount.
        /*
         * Keep a held reference to the target vnodes.
         * They are vrele'd in union_unmount.
+        *
+        * Depending on the _BELOW flag, the filesystems are
+        * viewed in a different order.  In effect, this is the
+        * same as providing a mount under option to the mount syscall.
         */
         */
-       um->um_lowervp = lowerrootvp;
-       um->um_uppervp = upperrootvp;
-       /*
-        * Take a copy of the process's credentials.  This isn't
-        * quite right since the euid will always be zero and we
-        * want to get the "real" users credentials.  So fix up
-        * the uid field after taking the copy.
-        */
-       um->um_cred = crdup(p->p_ucred);
-       um->um_cred->cr_uid = p->p_cred->p_ruid;
+
+       switch (args.mntflags & UNMNT_OPMASK) {
+       case UNMNT_ABOVE:
+               um->um_lowervp = lowerrootvp;
+               um->um_uppervp = upperrootvp;
+               break;
+
+       case UNMNT_BELOW:
+               um->um_lowervp = upperrootvp;
+               um->um_uppervp = lowerrootvp;
+               break;
+
+       case UNMNT_REPLACE:
+               vrele(lowerrootvp);
+               lowerrootvp = NULLVP;
+               um->um_uppervp = upperrootvp;
+               um->um_lowervp = lowerrootvp;
+               break;
+
+       default:
+               error = EINVAL;
+               goto bad;
+       }
+
+       um->um_cred = cred;
        um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
 
        /*
        um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
 
        /*
@@ -117,8 +158,9 @@ union_mount(mp, path, data, ndp, p)
         * flag implies that some of the files might be stored locally
         * then you will want to change the conditional.
         */
         * flag implies that some of the files might be stored locally
         * then you will want to change the conditional.
         */
-       if ((lowerrootvp->v_mount->mnt_flag & MNT_LOCAL) &&
-           (upperrootvp->v_mount->mnt_flag & MNT_LOCAL))
+       if (((um->um_lowervp == NULLVP) ||
+            (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
+           (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
                mp->mnt_flag |= MNT_LOCAL;
 
        /*
                mp->mnt_flag |= MNT_LOCAL;
 
        /*
@@ -128,21 +170,47 @@ union_mount(mp, path, data, ndp, p)
         * mount of the underlying filesystem to go from rdonly to rdwr
         * will leave the unioned view as read-only.
         */
         * mount of the underlying filesystem to go from rdonly to rdwr
         * will leave the unioned view as read-only.
         */
-       mp->mnt_flag |= (upperrootvp->v_mount->mnt_flag & MNT_RDONLY);
+       mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
        mp->mnt_data = (qaddr_t) um;
        getnewfsid(mp, MOUNT_UNION);
 
        (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
        bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
        mp->mnt_data = (qaddr_t) um;
        getnewfsid(mp, MOUNT_UNION);
 
        (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
        bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
-       bcopy("union:", mp->mnt_stat.f_mntfromname, 6);
-       (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname + 6,
-                       MNAMELEN - 1 - 6, &size);
-       bzero(mp->mnt_stat.f_mntfromname + 6 + size, MNAMELEN - 6 - size);
+
+       switch (args.mntflags & UNMNT_OPMASK) {
+       case UNMNT_ABOVE:
+               cp = "un-above:";
+               break;
+       case UNMNT_BELOW:
+               cp = "un-below:";
+               break;
+       case UNMNT_REPLACE:
+               cp = "replace:";
+               break;
+       }
+       len = strlen(cp);
+       bcopy(cp, mp->mnt_stat.f_mntfromname, len);
+
+       cp = mp->mnt_stat.f_mntfromname + len;
+       len = MNAMELEN - len;
+
+       (void) copyinstr(args.target, cp, len - 1, &size);
+       bzero(cp + size, len - size);
+
 #ifdef UNION_DIAGNOSTIC
 #ifdef UNION_DIAGNOSTIC
-       printf("union_mount: upper %s, lower at %s\n",
+       printf("union_mount: from %s, on %s\n",
                mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
 #endif
        return (0);
                mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
 #endif
        return (0);
+
+bad:
+       if (cred)
+               crfree(cred);
+       if (upperrootvp)
+               vrele(upperrootvp);
+       if (lowerrootvp)
+               vrele(lowerrootvp);
+       return (error);
 }
 
 /*
 }
 
 /*
@@ -203,7 +271,8 @@ union_unmount(mp, mntflags, p)
        /*
         * Discard references to upper and lower target vnodes.
         */
        /*
         * Discard references to upper and lower target vnodes.
         */
-       vrele(um->um_lowervp);
+       if (um->um_lowervp)
+               vrele(um->um_lowervp);
        vrele(um->um_uppervp);
        crfree(um->um_cred);
        /*
        vrele(um->um_uppervp);
        crfree(um->um_cred);
        /*
@@ -240,7 +309,8 @@ union_root(mp, vpp)
         * Return locked reference to root.
         */
        VREF(um->um_uppervp);
         * Return locked reference to root.
         */
        VREF(um->um_uppervp);
-       VREF(um->um_lowervp);
+       if (um->um_lowervp)
+               VREF(um->um_lowervp);
        error = union_allocvp(vpp, mp,
                              (struct vnode *) 0,
                              (struct vnode *) 0,
        error = union_allocvp(vpp, mp,
                              (struct vnode *) 0,
                              (struct vnode *) 0,
@@ -250,7 +320,8 @@ union_root(mp, vpp)
 
        if (error) {
                vrele(um->um_uppervp);
 
        if (error) {
                vrele(um->um_uppervp);
-               vrele(um->um_lowervp);
+               if (um->um_lowervp)
+                       vrele(um->um_lowervp);
        } else {
                (*vpp)->v_flag |= VROOT;
        }
        } else {
                (*vpp)->v_flag |= VROOT;
        }
@@ -289,9 +360,11 @@ union_statfs(mp, sbp, p)
 
        bzero(&mstat, sizeof(mstat));
 
 
        bzero(&mstat, sizeof(mstat));
 
-       error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
-       if (error)
-               return (error);
+       if (um->um_lowervp) {
+               error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
+               if (error)
+                       return (error);
+       }
 
        /* now copy across the "interesting" information and fake the rest */
 #if 0
 
        /* now copy across the "interesting" information and fake the rest */
 #if 0
index 0885775..6363f71 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_vnops.c       1.8 (Berkeley) %G%
+ *     @(#)union_vnops.c       1.9 (Berkeley) %G%
  */
 
 #include <sys/param.h>
  */
 
 #include <sys/param.h>
@@ -43,8 +43,13 @@ union_lookup1(udvp, dvp, vpp, cnp)
         */
        if (cnp->cn_flags & ISDOTDOT) {
                for (;;) {
         */
        if (cnp->cn_flags & ISDOTDOT) {
                for (;;) {
-                       if ((dvp->v_flag & VROOT) == 0 ||
-                           (cnp->cn_flags & NOCROSSMOUNT))
+                       /*
+                        * Don't do the NOCROSSMOUNT check
+                        * at this level.  By definition,
+                        * union fs deals with namespaces, not
+                        * filesystems.
+                        */
+                       if ((dvp->v_flag & VROOT) == 0)
                                break;
 
                        tdvp = dvp;
                                break;
 
                        tdvp = dvp;
@@ -71,12 +76,11 @@ union_lookup1(udvp, dvp, vpp, cnp)
 
        /*
         * Lastly check if the current node is a mount point in
 
        /*
         * Lastly check if the current node is a mount point in
-        * which cse walk up the mount hierarchy making sure not to
+        * 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) &&
         * bump into the root of the mount tree (ie. dvp != udvp).
         */
        while (dvp != udvp && (dvp->v_type == VDIR) &&
-              (mp = dvp->v_mountedhere) &&
-              (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+              (mp = dvp->v_mountedhere)) {
 
                if (mp->mnt_flag & MNT_MLOCK) {
                        mp->mnt_flag |= MNT_MWAIT;
 
                if (mp->mnt_flag & MNT_MLOCK) {
                        mp->mnt_flag |= MNT_MWAIT;