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
This commit is contained in:
parent
7505cffa56
commit
ba7a55d934
|
@ -28,7 +28,7 @@
|
||||||
.\" @(#)mount.2 8.3 (Berkeley) 5/24/95
|
.\" @(#)mount.2 8.3 (Berkeley) 5/24/95
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd December 1, 2017
|
.Dd August 28, 2019
|
||||||
.Dt MOUNT 2
|
.Dt MOUNT 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -157,6 +157,10 @@ mount even if some files are open for writing.
|
||||||
Disable read clustering.
|
Disable read clustering.
|
||||||
.It Dv MNT_NOCLUSTERW
|
.It Dv MNT_NOCLUSTERW
|
||||||
Disable write clustering.
|
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
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The flag
|
The flag
|
||||||
|
@ -260,6 +264,11 @@ is not a directory.
|
||||||
.It Bq Er EBUSY
|
.It Bq Er EBUSY
|
||||||
Another process currently holds a reference to
|
Another process currently holds a reference to
|
||||||
.Fa dir .
|
.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
|
.It Bq Er EFAULT
|
||||||
The
|
The
|
||||||
.Fa dir
|
.Fa dir
|
||||||
|
@ -280,6 +289,11 @@ The
|
||||||
.Fa fspec
|
.Fa fspec
|
||||||
argument
|
argument
|
||||||
is not a block device.
|
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
|
.It Bq Er ENXIO
|
||||||
The major device number of
|
The major device number of
|
||||||
.Fa fspec
|
.Fa fspec
|
||||||
|
|
|
@ -65,7 +65,8 @@ struct mntopt {
|
||||||
#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
|
#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
|
||||||
#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
|
#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
|
||||||
#define MOPT_RW { "rw", 1, 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. */
|
/* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */
|
||||||
#define MOPT_AUTO { "auto", 0, 0, 0 }
|
#define MOPT_AUTO { "auto", 0, 0, 0 }
|
||||||
|
|
||||||
|
@ -95,7 +96,9 @@ struct mntopt {
|
||||||
MOPT_ACLS, \
|
MOPT_ACLS, \
|
||||||
MOPT_NFS4ACLS, \
|
MOPT_NFS4ACLS, \
|
||||||
MOPT_AUTOMOUNTED, \
|
MOPT_AUTOMOUNTED, \
|
||||||
MOPT_UNTRUSTED
|
MOPT_UNTRUSTED, \
|
||||||
|
MOPT_NOCOVER, \
|
||||||
|
MOPT_EMPTYDIR
|
||||||
|
|
||||||
void getmntopts(const char *, const struct mntopt *, int *, int *);
|
void getmntopts(const char *, const struct mntopt *, int *, int *);
|
||||||
void rmslashes(char *, char *);
|
void rmslashes(char *, char *);
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
|
.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd March 22, 2017
|
.Dd August 28, 2019
|
||||||
.Dt MOUNT 8
|
.Dt MOUNT 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -162,6 +162,8 @@ When used with the
|
||||||
.Fl u
|
.Fl u
|
||||||
flag, this is the same as specifying the options currently in effect for
|
flag, this is the same as specifying the options currently in effect for
|
||||||
the mounted file system.
|
the mounted file system.
|
||||||
|
.It Cm emptydir
|
||||||
|
Require that the mount point directory be empty.
|
||||||
.It Cm force
|
.It Cm force
|
||||||
The same as
|
The same as
|
||||||
.Fl f ;
|
.Fl f ;
|
||||||
|
@ -237,6 +239,9 @@ flag.
|
||||||
Disable read clustering.
|
Disable read clustering.
|
||||||
.It Cm noclusterw
|
.It Cm noclusterw
|
||||||
Disable write clustering.
|
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
|
.It Cm noexec
|
||||||
Do not allow execution of any binaries on the mounted file system.
|
Do not allow execution of any binaries on the mounted file system.
|
||||||
This option is useful for a server that has file systems containing
|
This option is useful for a server that has file systems containing
|
||||||
|
|
|
@ -119,6 +119,8 @@ static struct opt {
|
||||||
{ MNT_AUTOMOUNTED, "automounted" },
|
{ MNT_AUTOMOUNTED, "automounted" },
|
||||||
{ MNT_VERIFIED, "verified" },
|
{ MNT_VERIFIED, "verified" },
|
||||||
{ MNT_UNTRUSTED, "untrusted" },
|
{ MNT_UNTRUSTED, "untrusted" },
|
||||||
|
{ MNT_NOCOVER, "nocover" },
|
||||||
|
{ MNT_EMPTYDIR, "emptydir" },
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -975,6 +977,8 @@ flags2opts(int flags)
|
||||||
if (flags & MNT_ACLS) res = catopt(res, "acls");
|
if (flags & MNT_ACLS) res = catopt(res, "acls");
|
||||||
if (flags & MNT_NFS4ACLS) res = catopt(res, "nfsv4acls");
|
if (flags & MNT_NFS4ACLS) res = catopt(res, "nfsv4acls");
|
||||||
if (flags & MNT_UNTRUSTED) res = catopt(res, "untrusted");
|
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);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,19 +668,21 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||||
* when we want to update the root filesystem.
|
* when we want to update the root filesystem.
|
||||||
*/
|
*/
|
||||||
TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
|
TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
|
||||||
|
int do_freeopt = 0;
|
||||||
|
|
||||||
if (strcmp(opt->name, "update") == 0) {
|
if (strcmp(opt->name, "update") == 0) {
|
||||||
fsflags |= MNT_UPDATE;
|
fsflags |= MNT_UPDATE;
|
||||||
vfs_freeopt(optlist, opt);
|
do_freeopt = 1;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "async") == 0)
|
else if (strcmp(opt->name, "async") == 0)
|
||||||
fsflags |= MNT_ASYNC;
|
fsflags |= MNT_ASYNC;
|
||||||
else if (strcmp(opt->name, "force") == 0) {
|
else if (strcmp(opt->name, "force") == 0) {
|
||||||
fsflags |= MNT_FORCE;
|
fsflags |= MNT_FORCE;
|
||||||
vfs_freeopt(optlist, opt);
|
do_freeopt = 1;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "reload") == 0) {
|
else if (strcmp(opt->name, "reload") == 0) {
|
||||||
fsflags |= MNT_RELOAD;
|
fsflags |= MNT_RELOAD;
|
||||||
vfs_freeopt(optlist, opt);
|
do_freeopt = 1;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "multilabel") == 0)
|
else if (strcmp(opt->name, "multilabel") == 0)
|
||||||
fsflags |= MNT_MULTILABEL;
|
fsflags |= MNT_MULTILABEL;
|
||||||
|
@ -741,7 +743,7 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||||
autoro = false;
|
autoro = false;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "autoro") == 0) {
|
else if (strcmp(opt->name, "autoro") == 0) {
|
||||||
vfs_freeopt(optlist, opt);
|
do_freeopt = 1;
|
||||||
autoro = true;
|
autoro = true;
|
||||||
}
|
}
|
||||||
else if (strcmp(opt->name, "suiddir") == 0)
|
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;
|
fsflags |= MNT_UNION;
|
||||||
else if (strcmp(opt->name, "automounted") == 0) {
|
else if (strcmp(opt->name, "automounted") == 0) {
|
||||||
fsflags |= MNT_AUTOMOUNTED;
|
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__);
|
ASSERT_VOP_ELOCKED(vp, __func__);
|
||||||
KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here"));
|
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
|
* If the jail of the calling thread lacks permission for this type of
|
||||||
* file system, deny immediately.
|
* file system, deny immediately.
|
||||||
|
@ -1229,6 +1253,11 @@ vfs_domount(
|
||||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||||
vp = nd.ni_vp;
|
vp = nd.ni_vp;
|
||||||
if ((fsflags & MNT_UPDATE) == 0) {
|
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);
|
pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
|
||||||
strcpy(pathbuf, fspath);
|
strcpy(pathbuf, fspath);
|
||||||
error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
|
error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
|
||||||
|
|
|
@ -5535,6 +5535,76 @@ filt_vfsvnode(struct knote *kn, long hint)
|
||||||
return (res);
|
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
|
int
|
||||||
vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off)
|
vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off)
|
||||||
{
|
{
|
||||||
|
|
|
@ -373,9 +373,11 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
|
||||||
#define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */
|
#define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */
|
||||||
#define MNT_NONBUSY 0x0000000004000000ULL /* check vnode use counts. */
|
#define MNT_NONBUSY 0x0000000004000000ULL /* check vnode use counts. */
|
||||||
#define MNT_BYFSID 0x0000000008000000ULL /* specify filesystem by ID. */
|
#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 | \
|
#define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \
|
||||||
MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | \
|
MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | \
|
||||||
MNT_BYFSID)
|
MNT_BYFSID | MNT_NOCOVER | MNT_EMPTYDIR)
|
||||||
/*
|
/*
|
||||||
* Internal filesystem control flags stored in mnt_kern_flag.
|
* Internal filesystem control flags stored in mnt_kern_flag.
|
||||||
*
|
*
|
||||||
|
|
|
@ -930,6 +930,7 @@ int vfs_kqfilter(struct vop_kqfilter_args *);
|
||||||
void vfs_mark_atime(struct vnode *vp, struct ucred *cred);
|
void vfs_mark_atime(struct vnode *vp, struct ucred *cred);
|
||||||
struct dirent;
|
struct dirent;
|
||||||
int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off);
|
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);
|
int vfs_unixify_accmode(accmode_t *accmode);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue