aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libc/sys/mount.216
-rw-r--r--sbin/mount/mntopts.h7
-rw-r--r--sbin/mount/mount.87
-rw-r--r--sbin/mount/mount.c4
-rw-r--r--sys/kern/vfs_mount.c39
-rw-r--r--sys/kern/vfs_subr.c70
-rw-r--r--sys/sys/mount.h4
-rw-r--r--sys/sys/vnode.h1
8 files changed, 138 insertions, 10 deletions
diff --git a/lib/libc/sys/mount.2 b/lib/libc/sys/mount.2
index 2d296e6f5b1b..6f5a199be9ab 100644
--- a/lib/libc/sys/mount.2
+++ b/lib/libc/sys/mount.2
@@ -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
diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h
index 924ead253890..183d6d9e501d 100644
--- a/sbin/mount/mntopts.h
+++ b/sbin/mount/mntopts.h
@@ -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 *);
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
index 7ff94fb7c165..3aee1bb86151 100644
--- a/sbin/mount/mount.8
+++ b/sbin/mount/mount.8
@@ -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
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
index 102e87009b0f..938a220b41c3 100644
--- a/sbin/mount/mount.c
+++ b/sbin/mount/mount.c
@@ -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);
}
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 76c483c5640e..633fc27a6c98 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -668,19 +668,21 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
* 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, struct uio *fsoptions)
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, struct uio *fsoptions)
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);
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 3bdc4d1e3f65..7d8ea63c7a6f 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -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)
{
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 562fa191fe24..4b60055ca6dd 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -373,9 +373,11 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
#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.
*
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 18134ecca814..967b1c066c95 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -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);