Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 23 Sep 2019 04:28:08 +0000 (UTC)
From:      Sean Eric Fagan <sef@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r352614 - in head: lib/libc/sys sbin/mount sys/kern sys/sys
Message-ID:  <201909230428.x8N4S8ud066140@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sef
Date: Mon Sep 23 04:28:07 2019
New Revision: 352614
URL: https://svnweb.freebsd.org/changeset/base/352614

Log:
  Add two options to allow mount to avoid covering up existing mount points.
  The two options are
  
  * nocover/cover:  Prevent/allow mounting over an existing root mountpoint.
  E.g., "mount -t ufs -o nocover /dev/sd1a /usr/local" will fail if /usr/local
  is already a mountpoint.
  * emptydir/noemptydir:  Prevent/allow mounting on a non-empty directory.
  E.g., "mount -t ufs -o emptydir /dev/sd1a /usr" will fail.
  
  Neither of these options is intended to be a default, for historical and
  compatibility reasons.
  
  Reviewed by:	allanjude, kib
  Differential Revision:	https://reviews.freebsd.org/D21458

Modified:
  head/lib/libc/sys/mount.2
  head/sbin/mount/mntopts.h
  head/sbin/mount/mount.8
  head/sbin/mount/mount.c
  head/sys/kern/vfs_mount.c
  head/sys/kern/vfs_subr.c
  head/sys/sys/mount.h
  head/sys/sys/vnode.h

Modified: head/lib/libc/sys/mount.2
==============================================================================
--- head/lib/libc/sys/mount.2	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/lib/libc/sys/mount.2	Mon Sep 23 04:28:07 2019	(r352614)
@@ -28,7 +28,7 @@
 .\"     @(#)mount.2	8.3 (Berkeley) 5/24/95
 .\" $FreeBSD$
 .\"
-.Dd December 1, 2017
+.Dd August 28, 2019
 .Dt MOUNT 2
 .Os
 .Sh NAME
@@ -157,6 +157,10 @@ mount even if some files are open for writing.
 Disable read clustering.
 .It Dv MNT_NOCLUSTERW
 Disable write clustering.
+.It Dv MNT_NOCOVER
+Do not mount over the root of another mount point.
+.It Dv MNT_EMPTYDIR
+Require an empty directory for the mount point directory.
 .El
 .Pp
 The flag
@@ -260,6 +264,11 @@ is not a directory.
 .It Bq Er EBUSY
 Another process currently holds a reference to
 .Fa dir .
+.It Bq Er EBUSY
+The
+.Dv MNT_NOCOVER
+option was given, and the requested mount point
+is already the root of another mount point.
 .It Bq Er EFAULT
 The
 .Fa dir
@@ -280,6 +289,11 @@ The
 .Fa fspec
 argument
 is not a block device.
+.It Bq Er ENOTEMPTY
+The
+.Dv MNT_EMPTYDIR
+option was specified, and the requested mount point
+is not an empty directory.
 .It Bq Er ENXIO
 The major device number of
 .Fa fspec

Modified: head/sbin/mount/mntopts.h
==============================================================================
--- head/sbin/mount/mntopts.h	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sbin/mount/mntopts.h	Mon Sep 23 04:28:07 2019	(r352614)
@@ -65,7 +65,8 @@ struct mntopt {
 #define MOPT_UPDATE		{ "update",	0, MNT_UPDATE, 0 }
 #define MOPT_RO			{ "ro",		0, MNT_RDONLY, 0 }
 #define MOPT_RW			{ "rw",		1, MNT_RDONLY, 0 }
-
+#define	MOPT_NOCOVER		{ "cover",	1, MNT_NOCOVER, 0 }
+#define	MOPT_EMPTYDIR		{ "emptydir",	0, MNT_EMPTYDIR, 0 }
 /* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */
 #define MOPT_AUTO		{ "auto",	0, 0, 0 }
 
@@ -95,7 +96,9 @@ struct mntopt {
 	MOPT_ACLS,							\
 	MOPT_NFS4ACLS,							\
 	MOPT_AUTOMOUNTED,						\
-	MOPT_UNTRUSTED
+	MOPT_UNTRUSTED,							\
+	MOPT_NOCOVER,							\
+	MOPT_EMPTYDIR
 
 void getmntopts(const char *, const struct mntopt *, int *, int *);
 void rmslashes(char *, char *);

Modified: head/sbin/mount/mount.8
==============================================================================
--- head/sbin/mount/mount.8	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sbin/mount/mount.8	Mon Sep 23 04:28:07 2019	(r352614)
@@ -28,7 +28,7 @@
 .\"     @(#)mount.8	8.8 (Berkeley) 6/16/94
 .\" $FreeBSD$
 .\"
-.Dd March 22, 2017
+.Dd August 28, 2019
 .Dt MOUNT 8
 .Os
 .Sh NAME
@@ -162,6 +162,8 @@ When used with the
 .Fl u
 flag, this is the same as specifying the options currently in effect for
 the mounted file system.
+.It Cm emptydir
+Require that the mount point directory be empty.
 .It Cm force
 The same as
 .Fl f ;
@@ -237,6 +239,9 @@ flag.
 Disable read clustering.
 .It Cm noclusterw
 Disable write clustering.
+.It Cm nocover
+Do not mount if the requested mount point is already
+the root of a mount point.
 .It Cm noexec
 Do not allow execution of any binaries on the mounted file system.
 This option is useful for a server that has file systems containing

Modified: head/sbin/mount/mount.c
==============================================================================
--- head/sbin/mount/mount.c	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sbin/mount/mount.c	Mon Sep 23 04:28:07 2019	(r352614)
@@ -119,6 +119,8 @@ static struct opt {
 	{ MNT_AUTOMOUNTED,	"automounted" },
 	{ MNT_VERIFIED,		"verified" },
 	{ MNT_UNTRUSTED,	"untrusted" },
+	{ MNT_NOCOVER,		"nocover" },
+	{ MNT_EMPTYDIR,		"emptydir" },
 	{ 0, NULL }
 };
 
@@ -975,6 +977,8 @@ flags2opts(int flags)
 	if (flags & MNT_ACLS)		res = catopt(res, "acls");
 	if (flags & MNT_NFS4ACLS)	res = catopt(res, "nfsv4acls");
 	if (flags & MNT_UNTRUSTED)	res = catopt(res, "untrusted");
+	if (flags & MNT_NOCOVER)	res = catopt(res, "nocover");
+	if (flags & MNT_EMPTYDIR)	res = catopt(res, "emptydir");
 
 	return (res);
 }

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sys/kern/vfs_mount.c	Mon Sep 23 04:28:07 2019	(r352614)
@@ -668,19 +668,21 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
 	 * when we want to update the root filesystem.
 	 */
 	TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
+		int do_freeopt = 0;
+
 		if (strcmp(opt->name, "update") == 0) {
 			fsflags |= MNT_UPDATE;
-			vfs_freeopt(optlist, opt);
+			do_freeopt = 1;
 		}
 		else if (strcmp(opt->name, "async") == 0)
 			fsflags |= MNT_ASYNC;
 		else if (strcmp(opt->name, "force") == 0) {
 			fsflags |= MNT_FORCE;
-			vfs_freeopt(optlist, opt);
+			do_freeopt = 1;
 		}
 		else if (strcmp(opt->name, "reload") == 0) {
 			fsflags |= MNT_RELOAD;
-			vfs_freeopt(optlist, opt);
+			do_freeopt = 1;
 		}
 		else if (strcmp(opt->name, "multilabel") == 0)
 			fsflags |= MNT_MULTILABEL;
@@ -741,7 +743,7 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
 			autoro = false;
 		}
 		else if (strcmp(opt->name, "autoro") == 0) {
-			vfs_freeopt(optlist, opt);
+			do_freeopt = 1;
 			autoro = true;
 		}
 		else if (strcmp(opt->name, "suiddir") == 0)
@@ -752,8 +754,22 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru
 			fsflags |= MNT_UNION;
 		else if (strcmp(opt->name, "automounted") == 0) {
 			fsflags |= MNT_AUTOMOUNTED;
-			vfs_freeopt(optlist, opt);
+			do_freeopt = 1;
+		} else if (strcmp(opt->name, "nocover") == 0) {
+			fsflags |= MNT_NOCOVER;
+			do_freeopt = 1;
+		} else if (strcmp(opt->name, "cover") == 0) {
+			fsflags &= ~MNT_NOCOVER;
+			do_freeopt = 1;
+		} else if (strcmp(opt->name, "emptydir") == 0) {
+			fsflags |= MNT_EMPTYDIR;
+			do_freeopt = 1;
+		} else if (strcmp(opt->name, "noemptydir") == 0) {
+			fsflags &= ~MNT_EMPTYDIR;
+			do_freeopt = 1;
 		}
+		if (do_freeopt)
+			vfs_freeopt(optlist, opt);
 	}
 
 	/*
@@ -889,6 +905,14 @@ vfs_domount_first(
 	ASSERT_VOP_ELOCKED(vp, __func__);
 	KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here"));
 
+	if ((fsflags & MNT_EMPTYDIR) != 0) {
+		error = vfs_emptydir(vp);
+		if (error != 0) {
+			vput(vp);
+			return (error);
+		}
+	}
+
 	/*
 	 * If the jail of the calling thread lacks permission for this type of
 	 * file system, deny immediately.
@@ -1229,6 +1253,11 @@ vfs_domount(
 	NDFREE(&nd, NDF_ONLY_PNBUF);
 	vp = nd.ni_vp;
 	if ((fsflags & MNT_UPDATE) == 0) {
+		if ((vp->v_vflag & VV_ROOT) != 0 &&
+		    (fsflags & MNT_NOCOVER) != 0) {
+			vput(vp);
+			return (EBUSY);
+		}
 		pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
 		strcpy(pathbuf, fspath);
 		error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sys/kern/vfs_subr.c	Mon Sep 23 04:28:07 2019	(r352614)
@@ -5535,6 +5535,76 @@ filt_vfsvnode(struct knote *kn, long hint)
 	return (res);
 }
 
+/*
+ * Returns whether the directory is empty or not.
+ * If it is empty, the return value is 0; otherwise
+ * the return value is an error value (which may
+ * be ENOTEMPTY).
+ */
+int
+vfs_emptydir(struct vnode *vp)
+{
+	struct uio uio;
+	struct iovec iov;
+	struct dirent *dirent, *dp, *endp;
+	int error, eof;
+
+	error = 0;
+	eof = 0;
+
+	ASSERT_VOP_LOCKED(vp, "vfs_emptydir");
+
+	dirent = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK);
+	iov.iov_base = dirent;
+	iov.iov_len = sizeof(struct dirent);
+
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_offset = 0;
+	uio.uio_resid = sizeof(struct dirent);
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_rw = UIO_READ;
+	uio.uio_td = curthread;
+
+	while (eof == 0 && error == 0) {
+		error = VOP_READDIR(vp, &uio, curthread->td_ucred, &eof,
+		    NULL, NULL);
+		if (error != 0)
+			break;
+		endp = (void *)((uint8_t *)dirent +
+		    sizeof(struct dirent) - uio.uio_resid);
+		for (dp = dirent; dp < endp;
+		     dp = (void *)((uint8_t *)dp + GENERIC_DIRSIZ(dp))) {
+			if (dp->d_type == DT_WHT)
+				continue;
+			if (dp->d_namlen == 0)
+				continue;
+			if (dp->d_type != DT_DIR &&
+			    dp->d_type != DT_UNKNOWN) {
+				error = ENOTEMPTY;
+				break;
+			}
+			if (dp->d_namlen > 2) {
+				error = ENOTEMPTY;
+				break;
+			}
+			if (dp->d_namlen == 1 &&
+			    dp->d_name[0] != '.') {
+				error = ENOTEMPTY;
+				break;
+			}
+			if (dp->d_namlen == 2 &&
+			    dp->d_name[1] != '.') {
+				error = ENOTEMPTY;
+				break;
+			}
+			uio.uio_resid = sizeof(struct dirent);
+		}
+	}
+	free(dirent, M_TEMP);
+	return (error);
+}
+
 int
 vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off)
 {

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sys/sys/mount.h	Mon Sep 23 04:28:07 2019	(r352614)
@@ -373,9 +373,11 @@ void          __mnt_vnode_markerfree_active(struct vno
 #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_NOCOVER	0x0000001000000000ULL /* Do not cover a mount point */
+#define	MNT_EMPTYDIR	0x0000002000000000ULL /* Only mount on empty dir */
 #define MNT_CMDFLAGS   (MNT_UPDATE	| MNT_DELEXPORT	| MNT_RELOAD	| \
 			MNT_FORCE	| MNT_SNAPSHOT	| MNT_NONBUSY	| \
-			MNT_BYFSID)
+			MNT_BYFSID	| MNT_NOCOVER	| MNT_EMPTYDIR)
 /*
  * Internal filesystem control flags stored in mnt_kern_flag.
  *

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h	Sun Sep 22 20:50:24 2019	(r352613)
+++ head/sys/sys/vnode.h	Mon Sep 23 04:28:07 2019	(r352614)
@@ -930,6 +930,7 @@ int vfs_kqfilter(struct vop_kqfilter_args *);
 void vfs_mark_atime(struct vnode *vp, struct ucred *cred);
 struct dirent;
 int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off);
+int vfs_emptydir(struct vnode *vp);
 
 int vfs_unixify_accmode(accmode_t *accmode);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201909230428.x8N4S8ud066140>