union_statfs should only return free space information
[unix-history] / usr / src / sys / miscfs / union / union_vfsops.c
index 9346c23..8bb8cb0 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)union_vfsops.c      8.6 (Berkeley) %G%
+ *     @(#)union_vfsops.c      8.14 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
@@ -43,7 +43,7 @@ union_mount(mp, path, data, ndp, p)
        struct union_args args;
        struct vnode *lowerrootvp = NULLVP;
        struct vnode *upperrootvp = NULLVP;
        struct union_args args;
        struct vnode *lowerrootvp = NULLVP;
        struct vnode *upperrootvp = NULLVP;
-       struct union_mount *um;
+       struct union_mount *um = 0;
        struct ucred *cred = 0;
        struct ucred *scred;
        struct vattr va;
        struct ucred *cred = 0;
        struct ucred *scred;
        struct vattr va;
@@ -68,34 +68,6 @@ union_mount(mp, path, data, ndp, p)
                goto bad;
        }
 
                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 and which they also own.
-        */
-       error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
-       if (error)
-               goto bad;
-       if ((va.va_uid != cred->cr_uid) && 
-           (cred->cr_uid != 0)) {
-               error = EACCES;
-               goto bad;
-       }
-       error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
-       if (error)
-               goto bad;
-
        /*
         * Get argument
         */
        /*
         * Get argument
         */
@@ -106,18 +78,10 @@ union_mount(mp, path, data, ndp, p)
        VREF(lowerrootvp);
 
        /*
        VREF(lowerrootvp);
 
        /*
-        * Find upper node.  Use the real process credentials,
-        * not the effective ones since this will have come
-        * through a setuid process (mount_union).  All this
-        * messing around with permissions is entirely bogus
-        * and should be removed by allowing any user straight
-        * past the mount system call.
+        * Find upper node.
         */
         */
-       scred = p->p_ucred;
-       p->p_ucred = cred;
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
               UIO_USERSPACE, args.target, p);
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
               UIO_USERSPACE, args.target, p);
-       p->p_ucred = scred;
 
        if (error = namei(ndp))
                goto bad;
 
        if (error = namei(ndp))
                goto bad;
@@ -167,7 +131,18 @@ union_mount(mp, path, data, ndp, p)
                goto bad;
        }
 
                goto bad;
        }
 
-       um->um_cred = cred;
+       /*
+        * Unless the mount is readonly, ensure that the top layer
+        * supports whiteout operations
+        */
+       if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+               error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
+               if (error)
+                       goto bad;
+       }
+
+       um->um_cred = p->p_ucred;
+       crhold(um->um_cred);
        um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
 
        /*
        um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
 
        /*
@@ -195,12 +170,6 @@ union_mount(mp, path, data, ndp, p)
         */
        mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
 
         */
        mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
 
-       /*
-        * This is a user mount.  Privilege check for unmount
-        * will be done in union_unmount.
-        */
-       mp->mnt_flag |= MNT_USER;
-
        mp->mnt_data = (qaddr_t) um;
        getnewfsid(mp, MOUNT_UNION);
 
        mp->mnt_data = (qaddr_t) um;
        getnewfsid(mp, MOUNT_UNION);
 
@@ -209,13 +178,13 @@ union_mount(mp, path, data, ndp, p)
 
        switch (um->um_op) {
        case UNMNT_ABOVE:
 
        switch (um->um_op) {
        case UNMNT_ABOVE:
-               cp = "un-above:";
+               cp = "<above>:";
                break;
        case UNMNT_BELOW:
                break;
        case UNMNT_BELOW:
-               cp = "un-below:";
+               cp = "<below>:";
                break;
        case UNMNT_REPLACE:
                break;
        case UNMNT_REPLACE:
-               cp = "replace:";
+               cp = "";
                break;
        }
        len = strlen(cp);
                break;
        }
        len = strlen(cp);
@@ -234,6 +203,8 @@ union_mount(mp, path, data, ndp, p)
        return (0);
 
 bad:
        return (0);
 
 bad:
+       if (um)
+               free(um, M_UFSMNT);
        if (cred)
                crfree(cred);
        if (upperrootvp)
        if (cred)
                crfree(cred);
        if (upperrootvp)
@@ -270,6 +241,7 @@ union_unmount(mp, mntflags, p)
        struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
        struct vnode *um_rootvp;
        int error;
        struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
        struct vnode *um_rootvp;
        int error;
+       int freeing;
        int flags = 0;
        extern int doforce;
 
        int flags = 0;
        extern int doforce;
 
@@ -277,11 +249,6 @@ union_unmount(mp, mntflags, p)
        printf("union_unmount(mp = %x)\n", mp);
 #endif
 
        printf("union_unmount(mp = %x)\n", mp);
 #endif
 
-       /* only the mounter, or superuser can unmount */
-       if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
-           (error = suser(p->p_ucred, &p->p_acflag)))
-               return (error);
-
        if (mntflags & MNT_FORCE) {
                /* union can never be rootfs so don't check for it */
                if (!doforce)
        if (mntflags & MNT_FORCE) {
                /* union can never be rootfs so don't check for it */
                if (!doforce)
@@ -291,17 +258,42 @@ union_unmount(mp, mntflags, p)
 
        if (error = union_root(mp, &um_rootvp))
                return (error);
 
        if (error = union_root(mp, &um_rootvp))
                return (error);
+
+       /*
+        * Keep flushing vnodes from the mount list.
+        * This is needed because of the un_pvp held
+        * reference to the parent vnode.
+        * If more vnodes have been freed on a given pass,
+        * the try again.  The loop will iterate at most
+        * (d) times, where (d) is the maximum tree depth
+        * in the filesystem.
+        */
+       for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
+               struct vnode *vp;
+               int n;
+
+               /* count #vnodes held on mount list */
+               for (n = 0, vp = mp->mnt_vnodelist.lh_first;
+                               vp != NULLVP;
+                               vp = vp->v_mntvnodes.le_next)
+                       n++;
+
+               /* if this is unchanged then stop */
+               if (n == freeing)
+                       break;
+
+               /* otherwise try once more time */
+               freeing = n;
+       }
+
+       /* At this point the root vnode should have a single reference */
        if (um_rootvp->v_usecount > 1) {
                vput(um_rootvp);
                return (EBUSY);
        }
        if (um_rootvp->v_usecount > 1) {
                vput(um_rootvp);
                return (EBUSY);
        }
-       if (error = vflush(mp, um_rootvp, flags)) {
-               vput(um_rootvp);
-               return (error);
-       }
 
 #ifdef UNION_DIAGNOSTIC
 
 #ifdef UNION_DIAGNOSTIC
-       vprint("alias root of lower", um_rootvp);
+       vprint("union root", um_rootvp);
 #endif  
        /*
         * Discard references to upper and lower target vnodes.
 #endif  
        /*
         * Discard references to upper and lower target vnodes.
@@ -335,12 +327,6 @@ union_root(mp, vpp)
        int error;
        int loselock;
 
        int error;
        int loselock;
 
-#ifdef UNION_DIAGNOSTIC
-       printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
-                       um->um_lowervp,
-                       um->um_uppervp);
-#endif
-
        /*
         * Return locked reference to root.
         */
        /*
         * Return locked reference to root.
         */
@@ -359,7 +345,8 @@ union_root(mp, vpp)
                              (struct vnode *) 0,
                              (struct componentname *) 0,
                              um->um_uppervp,
                              (struct vnode *) 0,
                              (struct componentname *) 0,
                              um->um_uppervp,
-                             um->um_lowervp);
+                             um->um_lowervp,
+                             1);
 
        if (error) {
                if (!loselock)
 
        if (error) {
                if (!loselock)
@@ -368,7 +355,6 @@ union_root(mp, vpp)
                if (um->um_lowervp)
                        vrele(um->um_lowervp);
        } else {
                if (um->um_lowervp)
                        vrele(um->um_lowervp);
        } else {
-               (*vpp)->v_flag |= VROOT;
                if (loselock)
                        VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
        }
                if (loselock)
                        VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
        }
@@ -442,16 +428,20 @@ union_statfs(mp, sbp, p)
         * kind of sense.  none of this makes sense though.
         */
 
         * kind of sense.  none of this makes sense though.
         */
 
-       if (mstat.f_bsize != lbsize) {
+       if (mstat.f_bsize != lbsize)
                sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
                sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
-               sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
-               sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
-       }
+
+       /*
+        * The "total" fields count total resources in all layers,
+        * the "free" fields count only those resources which are
+        * free in the upper layer (since only the upper layer
+        * is writeable).
+        */
        sbp->f_blocks += mstat.f_blocks;
        sbp->f_blocks += mstat.f_blocks;
-       sbp->f_bfree += mstat.f_bfree;
-       sbp->f_bavail += mstat.f_bavail;
+       sbp->f_bfree = mstat.f_bfree;
+       sbp->f_bavail = mstat.f_bavail;
        sbp->f_files += mstat.f_files;
        sbp->f_files += mstat.f_files;
-       sbp->f_ffree += mstat.f_ffree;
+       sbp->f_ffree = mstat.f_ffree;
 
        if (sbp != &mp->mnt_stat) {
                bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
 
        if (sbp != &mp->mnt_stat) {
                bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));