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
|
||||
.\" $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
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user