Page MenuHomeFreeBSD

D59930.id188993.diff
No OneTemporary

D59930.id188993.diff

diff --git a/lib/libsys/mount.2 b/lib/libsys/mount.2
--- a/lib/libsys/mount.2
+++ b/lib/libsys/mount.2
@@ -83,13 +83,31 @@
.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 "fstype" -offset indent
+.It Li fstype
+file system type name (e.g., Dq Li procfs )
+.It Li fspath
+mount point pathname (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 +296,10 @@
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
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -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 @@
{
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 @@
} 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 @@
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 @@
" 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 @@
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 @@
struct nameidata nd;
struct vnode *vp;
char *pathbuf;
+ fsid_t check_fsid;
int error;
/*
@@ -1646,6 +1653,16 @@
if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
return (ENAMETOOLONG);
+ if (check_fsid_str != NULL) {
+ error = sscanf(check_fsid_str, "FSID:%d:%d",
+ &check_fsid.val[0], &check_fsid.val[1]) != 2;
+ free(check_fsid_str, M_MOUNT);
+ if (error) {
+ *bailmsg_p = "Invalid check_fsid value";
+ return (ENOENT);
+ }
+ }
+
if (jail_export) {
error = priv_check(td, PRIV_NFS_DAEMON);
if (error)
@@ -1681,8 +1698,10 @@
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 +1717,20 @@
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.
diff --git a/tests/sys/vfs/Makefile b/tests/sys/vfs/Makefile
--- a/tests/sys/vfs/Makefile
+++ b/tests/sys/vfs/Makefile
@@ -5,8 +5,13 @@
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
diff --git a/tests/sys/vfs/nmount_check_fsid.c b/tests/sys/vfs/nmount_check_fsid.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/vfs/nmount_check_fsid.c
@@ -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());
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 10, 8:36 PM (9 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40567842
Default Alt Text
D59930.id188993.diff (12 KB)

Event Timeline