diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8 index 2ddbf5a07ce6..0f672b8853ad 100644 --- a/sbin/umount/umount.8 +++ b/sbin/umount/umount.8 @@ -28,7 +28,7 @@ .\" @(#)umount.8 8.2 (Berkeley) 5/8/95 .\" $FreeBSD$ .\" -.Dd June 17, 2015 +.Dd July 7, 2016 .Dt UMOUNT 8 .Os .Sh NAME @@ -36,12 +36,12 @@ .Nd unmount file systems .Sh SYNOPSIS .Nm -.Op Fl fv +.Op Fl fnv .Ar special ... | node ... | fsid ... .Nm .Fl a | A .Op Fl F Ar fstab -.Op Fl fv +.Op Fl fnv .Op Fl h Ar host .Op Fl t Ar type .Sh DESCRIPTION @@ -94,6 +94,15 @@ option and, unless otherwise specified with the option, will only unmount .Tn NFS file systems. +.It Fl n +Unless the +.Fl f +is used, the +.Nm +will not unmount an active file system. +It will, however, perform a flush. +This flag disables this behaviour, preventing the flush +if there are any files open. .It Fl t Ar type Is used to indicate the actions should only be taken on file systems of the specified type. diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index 6cd8ea76349c..20b78043de14 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -91,7 +91,7 @@ main(int argc, char *argv[]) struct addrinfo hints; all = errs = 0; - while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1) + while ((ch = getopt(argc, argv, "AaF:fh:nt:v")) != -1) switch (ch) { case 'A': all = 2; @@ -103,12 +103,15 @@ main(int argc, char *argv[]) setfstab(optarg); break; case 'f': - fflag = MNT_FORCE; + fflag |= MNT_FORCE; break; case 'h': /* -h implies -A. */ all = 2; nfshost = optarg; break; + case 'n': + fflag |= MNT_NONBUSY; + break; case 't': if (typelist != NULL) err(1, "only one -t option may be specified"); @@ -124,8 +127,11 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0) + err(1, "-f and -n are mutually exclusive"); + /* Start disks transferring immediately. */ - if ((fflag & MNT_FORCE) == 0) + if ((fflag & (MNT_FORCE | MNT_NONBUSY)) == 0) sync(); if ((argc == 0 && !all) || (argc != 0 && all)) @@ -609,7 +615,7 @@ usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: umount [-fv] special ... | node ... | fsid ...", - " umount -a | -A [-F fstab] [-fv] [-h host] [-t type]"); + "usage: umount [-fnv] special ... | node ... | fsid ...", + " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); exit(1); } diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 1f4592f79548..247714f07cb4 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -1204,6 +1204,28 @@ sys_unmount(struct thread *td, struct unmount_args *uap) return (error); } +/* + * Return error if any of the vnodes, ignoring the root vnode + * and the syncer vnode, have non-zero usecount. + */ +static int +vfs_check_usecounts(struct mount *mp) +{ + struct vnode *vp, *mvp; + + MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { + if ((vp->v_vflag & VV_ROOT) == 0 && vp->v_type != VNON && + vp->v_usecount != 0) { + VI_UNLOCK(vp); + MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); + return (EBUSY); + } + VI_UNLOCK(vp); + } + + return (0); +} + /* * Do the actual filesystem unmount. */ @@ -1260,6 +1282,21 @@ dounmount(struct mount *mp, int flags, struct thread *td) return (EBUSY); } mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ; + if (flags & MNT_NONBUSY) { + MNT_IUNLOCK(mp); + error = vfs_check_usecounts(mp); + MNT_ILOCK(mp); + if (error != 0) { + mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_NOINSMNTQ); + MNT_IUNLOCK(mp); + if (coveredvp != NULL) { + VOP_UNLOCK(coveredvp, 0); + vdrop(coveredvp); + } + vn_finished_write(mp); + return (error); + } + } /* Allow filesystems to detect that a forced unmount is in progress. */ if (flags & MNT_FORCE) { mp->mnt_kern_flag |= MNTK_UNMOUNTF; diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 5438140dc365..7c2856ab2c32 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -312,17 +312,21 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *); * External filesystem command modifier flags. * Unmount can use the MNT_FORCE flag. * XXX: These are not STATES and really should be somewhere else. - * XXX: MNT_BYFSID collides with MNT_ACLS, but because MNT_ACLS is only used for - * mount(2) and MNT_BYFSID is only used for unmount(2) it's harmless. + * XXX: MNT_BYFSID and MNT_NONBUSY collide with MNT_ACLS and MNT_MULTILABEL, + * but because MNT_ACLS and MNT_MULTILABEL are only used for mount(2), + * and MNT_BYFSID and MNT_NONBUSY are only used for unmount(2), + * it's harmless. */ #define MNT_UPDATE 0x0000000000010000ULL /* not real mount, just update */ #define MNT_DELEXPORT 0x0000000000020000ULL /* delete export host lists */ #define MNT_RELOAD 0x0000000000040000ULL /* reload filesystem data */ #define MNT_FORCE 0x0000000000080000ULL /* force unmount or readonly */ #define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */ +#define MNT_NONBUSY 0x0000000004000000ULL /* check vnode use counts. */ #define MNT_BYFSID 0x0000000008000000ULL /* specify filesystem by ID. */ #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ - MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID) + MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | \ + MNT_BYFSID) /* * Internal filesystem control flags stored in mnt_kern_flag. * diff --git a/usr.sbin/autofs/autounmountd.c b/usr.sbin/autofs/autounmountd.c index 02971cf72418..e64736b1884b 100644 --- a/usr.sbin/autofs/autounmountd.c +++ b/usr.sbin/autofs/autounmountd.c @@ -161,7 +161,7 @@ unmount_by_fsid(const fsid_t fsid, const char *mountpoint) if (ret < 0) log_err(1, "asprintf"); - error = unmount(fsid_str, MNT_BYFSID); + error = unmount(fsid_str, MNT_NONBUSY | MNT_BYFSID); if (error != 0) { if (errno == EBUSY) { log_debugx("cannot unmount %s (%s): %s",