mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 19:34:43 +01:00
nmount: Introduce the "check_fsid" option
See the manpage addition for rationale Reviewed by: ziaee, kib Approved by: ziaee, kib Differential Revision: https://reviews.freebsd.org/D59930
This commit is contained in:
1 parent
21f2245de2
commit
c3ac06161d
4 files changed
+316
-14
No files matched your search
+29
-5
@@ -83,13 +83,33 @@ containing
|
||||
.Fa niov
|
||||
elements.
|
||||
The following options are required by all file systems:
|
||||
.Bl -column fstype -offset indent
|
||||
.It
|
||||
.Li fstype Ta file system type name (e.g., Dq Li procfs )
|
||||
.It
|
||||
.Li fspath Ta mount point pathname (e.g., Dq Li /proc )
|
||||
.Bl -tag -width "check_fsid" -offset indent
|
||||
.It Li fstype
|
||||
file system type name
|
||||
.Pq e.g., Dq Li procfs
|
||||
.It Li fspath
|
||||
mount point pathname
|
||||
.Pq e.g., Dq Li /proc
|
||||
.El
|
||||
.Pp
|
||||
The following options may be specified when mounting any file system:
|
||||
.Bl -tag -width "check_fsid" -offset indent
|
||||
.It Li check_fsid
|
||||
FSID of a mount point being covered by the mount in the
|
||||
.Dq Li FSID:%d:%d
|
||||
format
|
||||
.El
|
||||
.Pp
|
||||
If the
|
||||
.Dq Li check_fsid
|
||||
option is specified, and the FSID of the mount point the covered vnode belongs
|
||||
to does not match the passed value, the
|
||||
.Fn nmount
|
||||
call will fail.
|
||||
.Dq Li check_fsid
|
||||
may also be passed when doing a mount update, in which case the caller should
|
||||
pass a FSID of the FS being updated, not the one covered.
|
||||
.Pp
|
||||
Depending on the file system type, other options may be
|
||||
recognized or required;
|
||||
for example, most disk-based file systems require a
|
||||
@@ -278,6 +298,10 @@ An I/O error occurred while reading data from
|
||||
The backing store for
|
||||
.Fa special
|
||||
detected corrupted data while reading.
|
||||
.It Bq Er ENOENT
|
||||
The file system ID specified using
|
||||
.Fa check_fsid
|
||||
did not match the target path.
|
||||
.El
|
||||
.Pp
|
||||
The following errors can occur for a
|
||||
|
||||
+44
-9
@@ -79,6 +79,7 @@
|
||||
|
||||
static int vfs_domount(struct thread *td, const char *fstype, char *fspath,
|
||||
uint64_t fsflags, bool only_export, bool jail_export,
|
||||
char *check_fsid_str, char **bailmsg_p,
|
||||
struct vfsoptlist **optlist);
|
||||
static void free_mntarg(struct mntarg *ma);
|
||||
static void pnfsd_waitreplenish(struct mount *mp);
|
||||
@@ -809,11 +810,11 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||
{
|
||||
struct vfsoptlist *optlist;
|
||||
struct vfsopt *opt, *tmp_opt;
|
||||
char *fstype, *fspath, *errmsg;
|
||||
char *fstype, *fspath, *errmsg, *bailmsg, *check_fsid_str;
|
||||
int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
|
||||
bool autoro, has_nonexport, only_export, jail_export;
|
||||
|
||||
errmsg = fspath = NULL;
|
||||
errmsg = fspath = check_fsid_str = bailmsg = NULL;
|
||||
errmsg_len = fspathlen = 0;
|
||||
errmsg_pos = -1;
|
||||
autoro = default_autoro;
|
||||
@@ -977,6 +978,9 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||
} else if (strcmp(opt->name, "noemptydir") == 0) {
|
||||
fsflags &= ~MNT_EMPTYDIR;
|
||||
do_freeopt = 1;
|
||||
} else if (strcmp(opt->name, "check_fsid") == 0) {
|
||||
check_fsid_str = strndup(opt->value, opt->len, M_MOUNT);
|
||||
do_freeopt = 1;
|
||||
}
|
||||
if (do_freeopt)
|
||||
vfs_freeopt(optlist, opt);
|
||||
@@ -1008,11 +1012,10 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||
jail_export = true;
|
||||
|
||||
error = vfs_domount(td, fstype, fspath, fsflags, only_export,
|
||||
jail_export, &optlist);
|
||||
if (error == ENODEV) {
|
||||
error = EINVAL;
|
||||
jail_export, check_fsid_str, &bailmsg, &optlist);
|
||||
if (bailmsg != NULL) {
|
||||
if (errmsg != NULL)
|
||||
strncpy(errmsg, "Invalid fstype", errmsg_len);
|
||||
strncpy(errmsg, bailmsg, errmsg_len);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
@@ -1027,7 +1030,7 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
|
||||
" trying R/O mount\n", __func__);
|
||||
fsflags |= MNT_RDONLY;
|
||||
error = vfs_domount(td, fstype, fspath, fsflags, only_export,
|
||||
jail_export, &optlist);
|
||||
jail_export, check_fsid_str, &bailmsg, &optlist);
|
||||
}
|
||||
bail:
|
||||
/* copyout the errmsg */
|
||||
@@ -1629,6 +1632,9 @@ vfs_domount(
|
||||
uint64_t fsflags, /* Flags common to all filesystems. */
|
||||
bool only_export, /* Got export option. */
|
||||
bool jail_export, /* Got export option in vnet prison. */
|
||||
char *check_fsid_str, /* fsid of an inode we're expecting
|
||||
to mount over. */
|
||||
char **bailmsg_p, /* textual error returned to caller */
|
||||
struct vfsoptlist **optlist /* Options local to the filesystem. */
|
||||
)
|
||||
{
|
||||
@@ -1636,6 +1642,7 @@ vfs_domount(
|
||||
struct nameidata nd;
|
||||
struct vnode *vp;
|
||||
char *pathbuf;
|
||||
fsid_t check_fsid;
|
||||
int error;
|
||||
|
||||
/*
|
||||
@@ -1646,6 +1653,18 @@ vfs_domount(
|
||||
if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
|
||||
return (ENAMETOOLONG);
|
||||
|
||||
if (check_fsid_str != NULL) {
|
||||
bool parsed;
|
||||
|
||||
parsed = sscanf(check_fsid_str, "FSID:%d:%d",
|
||||
&check_fsid.val[0], &check_fsid.val[1]) == 2;
|
||||
free(check_fsid_str, M_MOUNT);
|
||||
if (!parsed) {
|
||||
*bailmsg_p = "Invalid check_fsid value";
|
||||
return (ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (jail_export) {
|
||||
error = priv_check(td, PRIV_NFS_DAEMON);
|
||||
if (error)
|
||||
@@ -1681,8 +1700,10 @@ vfs_domount(
|
||||
if ((fsflags & MNT_UPDATE) == 0) {
|
||||
/* Don't try to load KLDs if we're mounting the root. */
|
||||
if (fsflags & MNT_ROOTFS) {
|
||||
if ((vfsp = vfs_byname(fstype)) == NULL)
|
||||
return (ENODEV);
|
||||
if ((vfsp = vfs_byname(fstype)) == NULL) {
|
||||
*bailmsg_p = "Invalid fstype";
|
||||
return (EINVAL);
|
||||
}
|
||||
} else {
|
||||
if ((vfsp = vfs_byname_kld(fstype, td, &error)) == NULL)
|
||||
return (error);
|
||||
@@ -1698,6 +1719,20 @@ vfs_domount(
|
||||
if (error != 0)
|
||||
return (error);
|
||||
vp = nd.ni_vp;
|
||||
if (check_fsid_str != NULL) {
|
||||
struct mount *mp;
|
||||
|
||||
mp = vfs_getvfs(&check_fsid);
|
||||
if (mp != vp->v_mount) {
|
||||
vput(vp);
|
||||
error = ENOENT;
|
||||
*bailmsg_p = "vnode's FSID does not match check_fsid";
|
||||
}
|
||||
if (mp != NULL)
|
||||
vfs_rel(mp);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Don't allow stacking file mounts to work around problems with the way
|
||||
* that namei sets nd.ni_dvp to vp_crossmp for these.
|
||||
|
||||
@@ -5,8 +5,13 @@ TESTSDIR= ${TESTSBASE}/sys/vfs
|
||||
ATF_TESTS_C+= lookup_cap_dotdot
|
||||
CFLAGS.lookup_cap_dotdot.c+= -I${SRCTOP}/tests
|
||||
|
||||
ATF_TESTS_C+= nmount_check_fsid
|
||||
CFLAGS.nmount_check_fsid.c+= -I${SRCTOP}/tests
|
||||
|
||||
ATF_TESTS_SH+= abi_root_symlink
|
||||
|
||||
LDFLAGS= -lutil
|
||||
|
||||
#ATF_TESTS_SH+= lookup_test
|
||||
|
||||
TAP_TESTS_SH+= trailing_slash
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*-
|
||||
* Copyright (c) 2026 Gleb Popov <arrowd@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <mntopts.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "freebsd_test_suite/macros.h"
|
||||
|
||||
|
||||
static char *
|
||||
fsid_to_str(fsid_t fsid)
|
||||
{
|
||||
char* ret;
|
||||
asprintf(&ret, "FSID:%d:%d", fsid.val[0], fsid.val[1]);
|
||||
ATF_REQUIRE(ret != NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ATF_TC_WITH_CLEANUP(mount_check_fsid_positive);
|
||||
ATF_TC_HEAD(mount_check_fsid_positive, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Positive testcase for nmount() with check_fsid");
|
||||
}
|
||||
ATF_TC_BODY(mount_check_fsid_positive, tc)
|
||||
{
|
||||
int mnt_fd = -1;
|
||||
char mnt[PATH_MAX];
|
||||
char *fsid;
|
||||
struct statfs statfs_buf;
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, mkdir("mnt", S_IRWXU));
|
||||
ATF_REQUIRE(realpath("mnt", mnt) != NULL);
|
||||
|
||||
ATF_REQUIRE((mnt_fd = open(mnt, O_RDONLY)) >= 0);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, fstatfs(mnt_fd, &statfs_buf));
|
||||
|
||||
fsid = fsid_to_str(statfs_buf.f_fsid);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
build_iovec(&iov, &iovlen, "check_fsid", fsid, -1);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, nmount(iov, iovlen, 0));
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
free(fsid);
|
||||
}
|
||||
ATF_TC_CLEANUP(mount_check_fsid_positive, tc)
|
||||
{
|
||||
unmount("mnt", MNT_FORCE);
|
||||
}
|
||||
|
||||
ATF_TC_WITH_CLEANUP(mount_check_fsid_negative);
|
||||
ATF_TC_HEAD(mount_check_fsid_negative, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Negative testcase for nmount() with check_fsid");
|
||||
}
|
||||
ATF_TC_BODY(mount_check_fsid_negative, tc)
|
||||
{
|
||||
int mnt_fd = -1;
|
||||
char mnt[PATH_MAX];
|
||||
char *fsid;
|
||||
struct statfs statfs_buf;
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, mkdir("mnt", S_IRWXU));
|
||||
ATF_REQUIRE(realpath("mnt", mnt) != NULL);
|
||||
|
||||
ATF_REQUIRE((mnt_fd = open("mnt", O_RDONLY)) >= 0);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, fstatfs(mnt_fd, &statfs_buf));
|
||||
|
||||
/* create invalid fsid */
|
||||
statfs_buf.f_fsid.val[0]++;
|
||||
statfs_buf.f_fsid.val[1]--;
|
||||
|
||||
fsid = fsid_to_str(statfs_buf.f_fsid);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
build_iovec(&iov, &iovlen, "check_fsid", fsid, -1);
|
||||
|
||||
ATF_REQUIRE_ERRNO(ENOENT, nmount(iov, iovlen, 0) < 0);
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
free(fsid);
|
||||
}
|
||||
ATF_TC_CLEANUP(mount_check_fsid_negative, tc)
|
||||
{
|
||||
unmount("mnt", MNT_FORCE);
|
||||
}
|
||||
|
||||
ATF_TC_WITH_CLEANUP(update_check_fsid_positive);
|
||||
ATF_TC_HEAD(update_check_fsid_positive, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Positive testcase for updating a mount with check_fsid");
|
||||
}
|
||||
ATF_TC_BODY(update_check_fsid_positive, tc)
|
||||
{
|
||||
int mnt_fd = -1;
|
||||
char mnt[PATH_MAX];
|
||||
char *fsid;
|
||||
struct statfs statfs_buf;
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, mkdir("mnt", S_IRWXU));
|
||||
ATF_REQUIRE(realpath("mnt", mnt) != NULL);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, nmount(iov, iovlen, 0));
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
|
||||
ATF_REQUIRE((mnt_fd = open(mnt, O_RDONLY)) >= 0);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, fstatfs(mnt_fd, &statfs_buf));
|
||||
|
||||
fsid = fsid_to_str(statfs_buf.f_fsid);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
build_iovec(&iov, &iovlen, "ro", NULL, -1);
|
||||
build_iovec(&iov, &iovlen, "update", NULL, -1);
|
||||
build_iovec(&iov, &iovlen, "check_fsid", fsid, -1);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, nmount(iov, iovlen, 0));
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
free(fsid);
|
||||
}
|
||||
ATF_TC_CLEANUP(update_check_fsid_positive, tc)
|
||||
{
|
||||
unmount("mnt", MNT_FORCE);
|
||||
}
|
||||
|
||||
ATF_TC_WITH_CLEANUP(update_check_fsid_negative);
|
||||
ATF_TC_HEAD(update_check_fsid_negative, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "Negative testcase for updating a mount with check_fsid");
|
||||
}
|
||||
ATF_TC_BODY(update_check_fsid_negative, tc)
|
||||
{
|
||||
int mnt_fd = -1;
|
||||
char mnt[PATH_MAX];
|
||||
char *fsid;
|
||||
struct statfs statfs_buf;
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, mkdir("mnt", S_IRWXU));
|
||||
ATF_REQUIRE(realpath("mnt", mnt) != NULL);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, nmount(iov, iovlen, 0));
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
|
||||
ATF_REQUIRE((mnt_fd = open(mnt, O_RDONLY)) >= 0);
|
||||
|
||||
ATF_REQUIRE_INTEQ(0, fstatfs(mnt_fd, &statfs_buf));
|
||||
|
||||
/* create invalid fsid */
|
||||
statfs_buf.f_fsid.val[0]++;
|
||||
statfs_buf.f_fsid.val[1]--;
|
||||
|
||||
fsid = fsid_to_str(statfs_buf.f_fsid);
|
||||
|
||||
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "tmpfs"), -1);
|
||||
build_iovec(&iov, &iovlen, "fspath", mnt, -1);
|
||||
build_iovec(&iov, &iovlen, "ro", NULL, -1);
|
||||
build_iovec(&iov, &iovlen, "update", NULL, -1);
|
||||
build_iovec(&iov, &iovlen, "check_fsid", fsid, -1);
|
||||
|
||||
ATF_REQUIRE_ERRNO(ENOENT, nmount(iov, iovlen, 0) < 0);
|
||||
|
||||
free_iovec(&iov, &iovlen);
|
||||
free(fsid);
|
||||
}
|
||||
ATF_TC_CLEANUP(update_check_fsid_negative, tc)
|
||||
{
|
||||
unmount("mnt", MNT_FORCE);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
|
||||
ATF_TP_ADD_TC(tp, mount_check_fsid_positive);
|
||||
ATF_TP_ADD_TC(tp, mount_check_fsid_negative);
|
||||
|
||||
ATF_TP_ADD_TC(tp, update_check_fsid_positive);
|
||||
ATF_TP_ADD_TC(tp, update_check_fsid_negative);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Reference in new issue
Block a user