*
* %sccs.include.redist.c%
*
- * @(#)union_vfsops.c 8.6 (Berkeley) %G%
+ * @(#)union_vfsops.c 8.14 (Berkeley) %G%
*/
/*
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;
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
*/
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);
- p->p_ucred = scred;
if (error = namei(ndp))
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;
/*
*/
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);
switch (um->um_op) {
case UNMNT_ABOVE:
- cp = "un-above:";
+ cp = "<above>:";
break;
case UNMNT_BELOW:
- cp = "un-below:";
+ cp = "<below>:";
break;
case UNMNT_REPLACE:
- cp = "replace:";
+ cp = "";
break;
}
len = strlen(cp);
return (0);
bad:
+ if (um)
+ free(um, M_UFSMNT);
if (cred)
crfree(cred);
if (upperrootvp)
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
struct vnode *um_rootvp;
int error;
+ int freeing;
int flags = 0;
extern int doforce;
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 (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 (error = vflush(mp, um_rootvp, flags)) {
- vput(um_rootvp);
- return (error);
- }
#ifdef UNION_DIAGNOSTIC
- vprint("alias root of lower", um_rootvp);
+ vprint("union root", um_rootvp);
#endif
/*
* Discard references to upper and lower target vnodes.
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.
*/
(struct vnode *) 0,
(struct componentname *) 0,
um->um_uppervp,
- um->um_lowervp);
+ um->um_lowervp,
+ 1);
if (error) {
if (!loselock)
if (um->um_lowervp)
vrele(um->um_lowervp);
} else {
- (*vpp)->v_flag |= VROOT;
if (loselock)
VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
}
* 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_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_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_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));