NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/58901: nfs server should allow specifying fsid in exports(5)
> Date: Wed, 26 Aug 2026 03:51:26 +0000
> From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
>
> The attached patch aims to implement a new exports(5) option
> [...]
Coulda sworn I confirmed my mailer said it was attached before I hit
send!
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1787601051 0
# Mon Aug 24 19:50:51 2026 +0000
# Branch trunk
# Node ID 3c615e360a5e8c96d95f1de403834c27fb2799bb
# Parent 2db9820dc602fe20366ec34e15e1cface0847cc8
# EXP-Topic riastradh-pr58901-nfsexportfsid
WIP: exports(5): New -fsid option to verify and control exported fsids.
This extends nfssvc(2) to support specifying the expected fsid and
the fsid to be published. Rather than just use up more flag bits
(which has always been a little weird for what is mostly a set of
mutually exclusive operations), I took an existing unused combination
of flag bits, NFSSVC_SETEXPORTSLIST | NFSSVC_REPLACEEXPORTSLIST, to
make a new extensible operation, NFSSVC_REPLACEEXPORTSEXT.
NFSSVC_REPLACEEXPORTSEXT passes a buffer of export commands for a
single mount point to the kernel, with two commands defined now --
MOUNTD_EXPORT_ARGS for the export args as previously passed to
NFSSVC_REPLACEEXPORTSLIST, and MOUNTD_EXPORT_SET_FSID to verify the
internal fsid and set the external one fsid -- and ample room for
future changes later.
The interface uses all fixed-size types so there is no need for new
compat32 logic. MOUNTD_EXPORT_ARGS commands are processed just like
NFSSVC_REPLACEEXPORTSLIST, so both paths still exercise the same
logic after the arguments are copied in. This keeps the amount of
hard-to-exercise compat code down.
PR kern/58901: nfs server should allow specifying fsid in exports(5)
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs.h
--- a/sys/nfs/nfs.h Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs.h Mon Aug 24 19:50:51 2026 +0000
@@ -44,6 +44,9 @@
#include <sys/rbtree.h>
#endif
+#include <sys/stdalign.h>
+#include <sys/stddef.h>
+
#include <nfs/rpcv2.h>
/*
@@ -230,6 +233,40 @@ struct mountd_exports_list {
struct export_args *mel_exports;
};
+struct mountd_exports_ext {
+ uint64_t mext_pathptr;
+ uint64_t mext_entriesptr;
+ uint16_t mext_entriesbytes;
+ uint8_t mext_pad[6];
+};
+
+struct mountd_export_entry {
+ uint8_t ment_cmd;
+ uint8_t ment_len;
+ uint8_t ment_pad[6];
+ uint8_t ment_data[/*ment_len*/];
+} __aligned(8);
+
+#define MOUNTD_EXPORT_ENTRYSIZE(size) \
+ roundup(offsetof(struct mountd_export_entry, ment_data[(size)]), \
+ alignof(struct mountd_export_entry))
+#define MOUNTD_EXPORT_ENTRYNEXT(ment) \
+ ((struct mountd_export_entry *)&(ment)->ment_data[(ment)->ment_len])
+
+#define MOUNTD_EXPORT_ARGS 0
+#define MOUNTD_EXPORT_SET_FSID 1
+
+struct mountd_export_entry_args {
+ uint64_t mea_uptr;
+ uint32_t mea_nexports;
+ uint32_t mea_pad;
+};
+
+struct mountd_export_entry_set_fsid {
+ fsid_t mesf_int_fsid;
+ fsid_t mesf_ext_fsid;
+};
+
/*
* try to keep nfsstats, which is exposed to userland via sysctl,
* compatible after NQNFS removal.
@@ -288,6 +325,7 @@ struct nfsstats {
#define NFSSVC_MNTD 0x100
#define NFSSVC_SETEXPORTSLIST 0x200
#define NFSSVC_REPLACEEXPORTSLIST 0x400
+#define NFSSVC_REPLACEEXPORTSEXT 0x600
/*
* fs.nfs sysctl(3) identifiers
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs_export.c
--- a/sys/nfs/nfs_export.c Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs_export.c Mon Aug 24 19:50:51 2026 +0000
@@ -121,6 +121,7 @@ struct netexport {
TAILQ_ENTRY(netexport) ne_list;
unsigned ne_nexports;
struct mount *ne_mount;
+ fsid_t ne_fsid;
struct netcred ne_defexported; /* Default export */
struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
};
@@ -240,6 +241,25 @@ int
mountd_set_exports_list(const struct mountd_exports_list *mel, struct lwp *l,
struct mount *nmp, int cmd)
{
+
+ return mountd_set_exports_ext(mel->mel_path, l, nmp, mel->mel_exports,
+ mel->mel_nexports, /*int_fsidp*/NULL, /*ext_fsidp*/NULL, cmd);
+}
+
+/*
+ * mountd_set_exports_ext(upath, l, mp, exports, nexports, int_fsid, ext_fsid,
+ * cmd)
+ *
+ * Set up the exports list of a mount point.
+ *
+ * This is NOT part of the user ABI or kernel module ABI, so it
+ * can freely be extended.
+ */
+int
+mountd_set_exports_ext(const char *upath, struct lwp *l, struct mount *nmp,
+ const struct export_args *exports, size_t nexports,
+ const fsid_t *int_fsidp, const fsid_t *ext_fsidp, int cmd)
+{
int error;
size_t i;
struct mount *mp;
@@ -254,7 +274,7 @@ mountd_set_exports_list(const struct mou
return EPERM;
/* Look up the file system path. */
- error = pathbuf_copyin(mel->mel_path, &pb);
+ error = pathbuf_copyin(upath, &pb);
if (error) {
return error;
}
@@ -270,6 +290,19 @@ mountd_set_exports_list(const struct mou
pathbuf_destroy(pb);
/*
+ * If the caller specified an expected internal fsid, check it.
+ */
+ if (int_fsidp) {
+ if ((int_fsidp->__fsid_val[0] !=
+ mp->mnt_stat.f_fsidx.__fsid_val[0]) ||
+ (int_fsidp->__fsid_val[0] !=
+ mp->mnt_stat.f_fsidx.__fsid_val[1])) {
+ vput(vp);
+ return EXDEV; /* XXX which errno? */
+ }
+ }
+
+ /*
* Make sure the file system can do vptofh. If the file system
* knows the handle's size, just trust it's able to do the
* actual translation also (otherwise we should check fhtovp
@@ -301,10 +334,10 @@ mountd_set_exports_list(const struct mou
KASSERT(ne->ne_mount == mp);
if (cmd == NFSSVC_SETEXPORTSLIST) {
- if (mel->mel_nexports == 0)
+ if (nexports == 0)
netexport_clear(ne);
- else if (mel->mel_nexports == 1)
- error = export(ne, &mel->mel_exports[0]);
+ else if (nexports == 1)
+ error = export(ne, &exports[0]);
else {
printf("%s: Cannot set more than one "
"entry at once (unimplemented)\n", __func__);
@@ -312,12 +345,19 @@ mountd_set_exports_list(const struct mou
}
} else if (cmd == NFSSVC_REPLACEEXPORTSLIST) {
netexport_clear(ne);
- for (i = 0; error == 0 && i < mel->mel_nexports; i++)
- error = export(ne, &mel->mel_exports[i]);
+ for (i = 0; error == 0 && i < nexports; i++)
+ error = export(ne, &exports[i]);
} else {
printf("%s: Command %#x not implemented\n", __func__, cmd);
error = EOPNOTSUPP;
}
+ if (error)
+ goto out;
+
+ /*
+ * All good. Set the external fsid for the export.
+ */
+ ne->ne_fsid = (ext_fsidp ? *ext_fsidp : mp->mnt_stat.f_fsidx);
out:
netexport_wrunlock();
@@ -362,10 +402,8 @@ netexport_lookup_byfsid(const fsid_t *fs
struct netexport *ne;
TAILQ_FOREACH(ne, &netexport_list, ne_list) {
- const struct mount *mp = ne->ne_mount;
-
- if (mp->mnt_stat.f_fsidx.__fsid_val[0] == fsid->__fsid_val[0] &&
- mp->mnt_stat.f_fsidx.__fsid_val[1] == fsid->__fsid_val[1]) {
+ if (ne->ne_fsid.__fsid_val[0] == fsid->__fsid_val[0] &&
+ ne->ne_fsid.__fsid_val[1] == fsid->__fsid_val[1]) {
goto done;
}
}
@@ -435,6 +473,7 @@ nfs_export_update_30(struct mount *mp, c
/* Request to delete exports. The mask above holds the
* value that used to be in MNT_DELEXPORT. */
mel.mel_nexports = 0;
+ mel.mel_exports = NULL;
} else {
/*
* The following code assumes export_args has not
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs_serv.c
--- a/sys/nfs/nfs_serv.c Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs_serv.c Mon Aug 24 19:50:51 2026 +0000
@@ -180,7 +180,7 @@ nfsrv3_access(struct nfsrv_descript *nfs
nfsm_srvmtofh(&nsfh);
nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam, &rdonly,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam, &rdonly,
(nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -242,7 +242,7 @@ nfsrv_getattr(struct nfsrv_descript *nfs
u_quad_t frev;
nfsm_srvmtofh(&nsfh);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam, &rdonly,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam, &rdonly,
(nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(0);
@@ -330,7 +330,7 @@ nfsrv_setattr(struct nfsrv_descript *nfs
/*
* Now that we have all the fields, lets do it.
*/
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam, &rdonly,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam, &rdonly,
(nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(2 * NFSX_UNSIGNED);
@@ -401,6 +401,7 @@ nfsrv_lookup(struct nfsrv_descript *nfsd
struct pathbuf *ipb = NULL;
struct vnode *vp, *dirp;
nfsrvfh_t nsfh;
+ fsid_t fsid;
char *cp;
u_int32_t *tl;
int32_t t1;
@@ -421,7 +422,7 @@ nfsrv_lookup(struct nfsrv_descript *nfsd
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags = LOCKLEAF;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, &fsid, len, slp, nam, &md, &dpos,
&dirp, NULL, NULL,
lwp, (nfsd->nd_flag & ND_KERBAUTH), pubflag);
@@ -488,7 +489,7 @@ nfsrv_lookup(struct nfsrv_descript *nfsd
pathbuf_destroy(ipb);
}
vp = ndp->ni_vp;
- error = nfsrv_composefh(vp, &nsfh, v3);
+ error = nfsrv_composefh(vp, &nsfh, &fsid, v3);
if (!error)
error = VOP_GETATTR(vp, &va, cred);
vput(vp);
@@ -574,7 +575,7 @@ nfsrv_readlink(struct nfsrv_descript *nf
uiop->uio_resid = len;
uiop->uio_rw = UIO_READ;
UIO_SETUP_SYSSPACE(uiop);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
m_freem(mp3);
@@ -655,7 +656,7 @@ nfsrv_read(struct nfsrv_descript *nfsd,
nfsm_dissect(tl, uint32_t *, NFSX_UNSIGNED);
reqlen = fxdr_unsigned(uint32_t, *tl);
reqlen = MIN(reqlen, NFS_SRVMAXDATA(nfsd));
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(2 * NFSX_UNSIGNED);
@@ -924,7 +925,7 @@ nfsrv_write(struct nfsrv_descript *nfsd,
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
return (0);
}
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(2 * NFSX_UNSIGNED);
@@ -1223,7 +1224,7 @@ loop1:
cred = nfsd->nd_cr;
v3 = (nfsd->nd_flag & ND_NFSV3);
forat_ret = aftat_ret = 1;
- error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp,
+ error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, NULL, cred, slp,
nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH),
false);
if (!error) {
@@ -1438,6 +1439,7 @@ nfsrv_create(struct nfsrv_descript *nfsd
struct mbuf *mb, *mreq __unused;
struct vnode *vp = NULL, *dirp = NULL;
nfsrvfh_t nsfh;
+ fsid_t fsid;
u_quad_t frev, tempsize;
u_char cverf[NFSX_V3CREATEVERF];
@@ -1447,7 +1449,7 @@ nfsrv_create(struct nfsrv_descript *nfsd
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, &fsid, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -1589,7 +1591,7 @@ nfsrv_create(struct nfsrv_descript *nfsd
vput(vp);
}
if (!error) {
- error = nfsrv_composefh(vp, &nsfh, v3);
+ error = nfsrv_composefh(vp, &nsfh, &fsid, v3);
if (!error)
error = VOP_GETATTR(vp, &va, cred);
vput(vp);
@@ -1679,6 +1681,7 @@ nfsrv_mknod(struct nfsrv_descript *nfsd,
struct mbuf *mb, *mreq __unused;
struct vnode *vp, *dirp = (struct vnode *)0;
nfsrvfh_t nsfh;
+ fsid_t fsid;
u_quad_t frev;
nd.ni_cnd.cn_nameiop = 0;
@@ -1687,7 +1690,7 @@ nfsrv_mknod(struct nfsrv_descript *nfsd,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, &fsid, len, slp, nam, &md, &dpos,
&dirp, &dirfor_ret, &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -1763,7 +1766,7 @@ abort:
out:
vp = nd.ni_vp;
if (!error) {
- error = nfsrv_composefh(vp, &nsfh, true);
+ error = nfsrv_composefh(vp, &nsfh, &fsid, true);
if (!error)
error = VOP_GETATTR(vp, &va, cred);
vput(vp);
@@ -1842,7 +1845,7 @@ nfsrv_remove(struct nfsrv_descript *nfsd
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, NULL, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (!error) {
@@ -1935,7 +1938,7 @@ nfsrv_rename(struct nfsrv_descript *nfsd
fromnd.ni_cnd.cn_cred = cred;
fromnd.ni_cnd.cn_nameiop = DELETE;
fromnd.ni_cnd.cn_flags = LOCKPARENT;
- error = nfs_namei(&fromnd, &fnsfh, len, slp, nam, &md,
+ error = nfs_namei(&fromnd, &fnsfh, NULL, len, slp, nam, &md,
&dpos, &fdirp, (v3 ? &fdirfor_ret : NULL), &fdirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -2006,7 +2009,7 @@ nfsrv_rename(struct nfsrv_descript *nfsd
tond.ni_cnd.cn_cred = cred;
tond.ni_cnd.cn_nameiop = RENAME;
tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE;
- error = nfs_namei(&tond, &tnsfh, len2, slp, nam, &md,
+ error = nfs_namei(&tond, &tnsfh, NULL, len2, slp, nam, &md,
&dpos, &tdirp, (v3 ? &tdirfor_ret : NULL), &tdirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -2186,7 +2189,7 @@ nfsrv_link(struct nfsrv_descript *nfsd,
nfsm_srvmtofh(&nsfh);
nfsm_srvmtofh(&dnsfh);
nfsm_srvnamesiz(len);
- error = nfsrv_fhtovp(&nsfh, false, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, false, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
@@ -2201,7 +2204,7 @@ nfsrv_link(struct nfsrv_descript *nfsd,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
- error = nfs_namei(&nd, &dnsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &dnsfh, NULL, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error)
@@ -2283,6 +2286,7 @@ nfsrv_symlink(struct nfsrv_descript *nfs
struct mbuf *mb, *mreq __unused;
struct vnode *dirp = (struct vnode *)0;
nfsrvfh_t nsfh;
+ fsid_t fsid;
u_quad_t frev;
nd.ni_cnd.cn_nameiop = 0;
@@ -2291,7 +2295,7 @@ nfsrv_symlink(struct nfsrv_descript *nfs
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, &fsid, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error)
@@ -2346,7 +2350,7 @@ abortop:
if (!error) {
if (v3) {
vn_lock(nd.ni_vp, LK_SHARED | LK_RETRY);
- error = nfsrv_composefh(nd.ni_vp, &nsfh, v3);
+ error = nfsrv_composefh(nd.ni_vp, &nsfh, &fsid, v3);
if (!error)
error = VOP_GETATTR(nd.ni_vp, &va, cred);
vput(nd.ni_vp);
@@ -2427,6 +2431,7 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd,
struct mbuf *mb, *mreq __unused;
struct vnode *vp, *dirp = (struct vnode *)0;
nfsrvfh_t nsfh;
+ fsid_t fsid;
u_quad_t frev;
nfsm_srvmtofh(&nsfh);
@@ -2434,7 +2439,7 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, &fsid, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -2474,7 +2479,7 @@ nfsrv_mkdir(struct nfsrv_descript *nfsd,
if (!error) {
vp = nd.ni_vp;
vn_lock(vp, LK_SHARED | LK_RETRY);
- error = nfsrv_composefh(vp, &nsfh, v3);
+ error = nfsrv_composefh(vp, &nsfh, &fsid, v3);
if (!error)
error = VOP_GETATTR(vp, &va, cred);
vput(vp);
@@ -2556,7 +2561,7 @@ nfsrv_rmdir(struct nfsrv_descript *nfsd,
nd.ni_cnd.cn_cred = cred;
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
- error = nfs_namei(&nd, &nsfh, len, slp, nam, &md, &dpos,
+ error = nfs_namei(&nd, &nsfh, NULL, len, slp, nam, &md, &dpos,
&dirp, (v3 ? &dirfor_ret : NULL), &dirfor,
lwp, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
@@ -2717,7 +2722,7 @@ nfsrv_readdir(struct nfsrv_descript *nfs
if (siz > xfer)
siz = xfer;
fullsiz = siz;
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (!error && vp->v_type != VDIR) {
error = ENOTDIR;
@@ -2948,6 +2953,7 @@ nfsrv_readdirplus(struct nfsrv_descript
struct vnode *vp, *nvp;
struct flrep fl;
nfsrvfh_t nsfh;
+ fsid_t fsid;
struct uio io;
struct iovec iv;
struct vattr va, at, *vap = &va;
@@ -2980,7 +2986,7 @@ nfsrv_readdirplus(struct nfsrv_descript
if (siz > xfer)
siz = xfer;
fullsiz = siz;
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, &fsid, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (!error && vp->v_type != VDIR) {
error = ENOTDIR;
@@ -3132,7 +3138,8 @@ again:
if (VFS_VGET(vp->v_mount, dp->d_fileno, LK_EXCLUSIVE,
&nvp))
goto invalid;
- if (nfsrv_composefh(nvp, &nnsfh, true)) {
+ KASSERT(nvp->v_mount == vp->v_mount);
+ if (nfsrv_composefh(nvp, &nnsfh, &fsid, true)) {
vput(nvp);
goto invalid;
}
@@ -3289,7 +3296,7 @@ nfsrv_commit(struct nfsrv_descript *nfsd
off = fxdr_hyper(tl);
tl += 2;
cnt = fxdr_unsigned(uint32_t, *tl);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(2 * NFSX_UNSIGNED);
@@ -3344,7 +3351,7 @@ nfsrv_statfs(struct nfsrv_descript *nfsd
u_quad_t frev, tval;
nfsm_srvmtofh(&nsfh);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -3414,7 +3421,7 @@ nfsrv_fsinfo(struct nfsrv_descript *nfsd
struct statvfs *sb;
nfsm_srvmtofh(&nsfh);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
@@ -3483,7 +3490,7 @@ nfsrv_pathconf(struct nfsrv_descript *nf
u_quad_t frev;
nfsm_srvmtofh(&nsfh);
- error = nfsrv_fhtovp(&nsfh, 1, &vp, cred, slp, nam,
+ error = nfsrv_fhtovp(&nsfh, 1, &vp, NULL, cred, slp, nam,
&rdonly, (nfsd->nd_flag & ND_KERBAUTH), false);
if (error) {
nfsm_reply(NFSX_UNSIGNED);
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs_srvsubs.c
--- a/sys/nfs/nfs_srvsubs.c Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs_srvsubs.c Mon Aug 24 19:50:51 2026 +0000
@@ -116,7 +116,7 @@
* it is not.
*/
int
-nfs_namei(struct nameidata *ndp, nfsrvfh_t *nsfh, uint32_t len, struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp, char **dposp, struct vnode **retdirp, int *dirattr_retp, struct vattr *dirattrp, struct lwp *l, int kerbflag, int pubflag)
+nfs_namei(struct nameidata *ndp, nfsrvfh_t *nsfh, fsid_t *fsidp, uint32_t len, struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp, char **dposp, struct vnode **retdirp, int *dirattr_retp, struct vattr *dirattrp, struct lwp *l, int kerbflag, int pubflag)
{
int i, rem;
struct mbuf *md;
@@ -174,7 +174,7 @@ nfs_namei(struct nameidata *ndp, nfsrvfh
/*
* Extract and set starting directory.
*/
- error = nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp,
+ error = nfsrv_fhtovp(nsfh, false, &dp, fsidp, ndp->ni_cnd.cn_cred, slp,
nam, &rdonly, kerbflag, pubflag);
if (error)
goto out;
@@ -292,7 +292,7 @@ out:
* - if not lockflag unlock it with VOP_UNLOCK()
*/
int
-nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp,
+nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp, fsid_t *fsidp,
kauth_cred_t cred, struct nfssvc_sock *slp, struct mbuf *nam, int *rdonlyp,
int kerbflag, int pubflag)
{
@@ -363,6 +363,8 @@ nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockfl
*rdonlyp = 0;
if (!lockflag)
VOP_UNLOCK(*vpp);
+ if (fsidp)
+ *fsidp = fhp->fh_fsid;
return (0);
}
@@ -389,7 +391,8 @@ nfs_ispublicfh(const nfsrvfh_t *nsfh)
}
int
-nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh, bool v3)
+nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh,
+ const fsid_t fsidp[static 1], bool v3)
{
int error;
size_t fhsize;
@@ -402,6 +405,7 @@ nfsrv_composefh(struct vnode *vp, nfsrvf
if (error != 0) {
return error;
}
+ NFSRVFH_FHANDLE(nsfh)->fh_fsid = *fsidp;
if (!v3 && fhsize < NFSX_V2FH) {
memset((char *)NFSRVFH_DATA(nsfh) + fhsize, 0,
NFSX_V2FH - fhsize);
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs_syscalls.c
--- a/sys/nfs/nfs_syscalls.c Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs_syscalls.c Mon Aug 24 19:50:51 2026 +0000
@@ -119,6 +119,9 @@ static void nfsd_rt(int, struct nfsrv_de
static int nfssvc_nfsd(struct nfssvc_copy_ops *, struct nfsd_srvargs *, void *,
struct lwp *);
+static int nfssvc_replaceexportsext(const struct nfssvc_copy_ops *,
+ struct lwp *, const struct mountd_exports_ext *);
+
static int nfsd_compare_nodes(void *, const void *, const void *);
static int nfsd_compare_key(void *, const void *, const void *);
@@ -353,6 +356,14 @@ do_nfssvc(struct nfssvc_copy_ops *ops, s
}
error = nfssvc_addsock(fp, nam);
fd_putfile(nfsdarg.sock);
+ } else if ((flag & (NFSSVC_SETEXPORTSLIST | NFSSVC_REPLACEEXPORTSLIST))
+ == NFSSVC_REPLACEEXPORTSEXT) {
+ struct mountd_exports_ext mext;
+
+ error = copyin(argp, &mext, sizeof(mext));
+ if (error != 0)
+ return error;
+ return nfssvc_replaceexportsext(ops, l, &mext);
} else if (flag & (NFSSVC_SETEXPORTSLIST | NFSSVC_REPLACEEXPORTSLIST)) {
struct export_args *args;
struct mountd_exports_list mel;
@@ -479,6 +490,191 @@ do_nfssvc(struct nfssvc_copy_ops *ops, s
return (error);
}
+static int
+nfssvc_replaceexportsext(const struct nfssvc_copy_ops *ops, struct lwp *l,
+ const struct mountd_exports_ext *mext)
+{
+ const struct mountd_export_entry *user_entries;
+ const char *user_path;
+ size_t nbytes;
+ struct mountd_export_entry *ment0 = NULL, *ment;
+ struct export_args *args = NULL;
+ size_t nexports = 0;
+ const fsid_t *int_fsidp = NULL;
+ const fsid_t *ext_fsidp = NULL;
+ int error;
+
+ /*
+ * The buffer of export command entries should be the same for
+ * all platforms (hence no need for compat32 logic) and small
+ * enough to be reasonable to copy into a kernel buffer,
+ * guaranteed by the sizes verified at compile-time.
+ */
+ CTASSERT(sizeof(*mext) == 24); /* 32/64-bit compatible */
+ CTASSERT(__type_max(typeof(mext->mext_entriesbytes)) <= 65535);
+
+ /*
+ * If the user's buffer isn't large enough to have a single
+ * entry in it, fail with EINVAL before we even bother copying
+ * in the entries.
+ */
+ nbytes = mext->mext_entriesbytes;
+ if (nbytes < sizeof(ment[0])) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * If the user's pointers are out-of-range, fail with EFAULT
+ * before we even bother copying in the entries.
+ *
+ * This isn't necessary for LP64 platforms, but for LP32 ones,
+ * it forestalls questions about integer truncation in
+ * conversion from uint64_t to void * later on -- if the high
+ * bits aren't all zero, we won't even reach the conversion.
+ *
+ * (We could check vm_map_max(&l->l_proc->p_vmspace->vm_map),
+ * but there's no additional benefit to that -- this just
+ * pacifies concerns by the compiler or other readers about
+ * integer->pointer truncation.)
+ */
+ if (mext->mext_entriesptr >= VM_MAXUSER_ADDRESS ||
+ mext->mext_pathptr >= VM_MAXUSER_ADDRESS) {
+ error = EFAULT;
+ goto out;
+ }
+ user_entries = (const void *)(uintptr_t)mext->mext_entriesptr;
+ user_path = (const void *)(uintptr_t)mext->mext_pathptr;
+
+ /*
+ * Allocate a temporary kernel buffer and copy the user's
+ * buffer into it.
+ *
+ * The size is CTASSERTed above to be small enough, <=64K, to
+ * be safe to allocate a kernel buffer.
+ */
+ ment0 = malloc(nbytes, M_TEMP, M_WAITOK);
+ error = copyin(user_entries, ment0, nbytes);
+ if (error)
+ goto out;
+
+ /*
+ * Iterate over the entries. Entries are length-encoded, so we
+ * have to be careful to make sure we don't overrun the buffer.
+ */
+ for (ment = ment0;
+ (char *)ment - (char *)ment0 < nbytes - sizeof(ment[0]);
+ ment = MOUNTD_EXPORT_ENTRYNEXT(ment)) {
+
+ /*
+ * If the encoded length goes beyond the end of our
+ * buffer, reject the input with EINVAL.
+ */
+ if (ment->ment_len > nbytes -
+ ((char *)ment->ment_data - (char *)ment0)) {
+ error = EINVAL;
+ goto out;
+ }
+ KASSERT((char *)&ment->ment_data[ment->ment_len] <=
+ (char *)ment0 + nbytes);
+
+ /*
+ * Dispatch on the export command.
+ */
+ switch (ment->ment_cmd) {
+ case MOUNTD_EXPORT_ARGS: {
+ struct mountd_export_entry_args *entry;
+ const struct export_args *user_exports;
+
+ CTASSERT(sizeof(*entry) == 16);
+ if (ment->ment_len != sizeof(*entry)) {
+ error = EINVAL;
+ goto out;
+ }
+ entry = (void *)ment->ment_data;
+
+ /*
+ * Make sure the number of exports per mount is
+ * small enough we can safely copy it into a
+ * kernel buffer.
+ */
+ if (entry->mea_nexports >
+ atomic_load_relaxed(&nfsd_maxexportspermount)) {
+ error = E2BIG;
+ goto out;
+ }
+
+ /*
+ * Verify the user's address is reasonable for
+ * this platform before we potentially truncate
+ * it in integer to pointer conversion.
+ */
+ if (entry->mea_uptr >= VM_MAXUSER_ADDRESS) {
+ error = EFAULT;
+ goto out;
+ }
+ user_exports =
+ (const void *)(uintptr_t)entry->mea_uptr;
+ nexports = entry->mea_nexports;
+
+ /*
+ * Allocate a temporary kernel buffer for the
+ * user's exports and copy them in.
+ */
+ args = malloc(nexports * sizeof(args[0]),
+ M_TEMP, M_WAITOK);
+ error = ops->exp_in(args, user_exports, nexports);
+ if (error)
+ goto out;
+ break;
+ }
+ case MOUNTD_EXPORT_SET_FSID: {
+ struct mountd_export_entry_set_fsid *entry;
+
+ CTASSERT(sizeof(*entry) == 16);
+ if (ment->ment_len != sizeof(*entry)) {
+ error = EINVAL;
+ goto out;
+ }
+ entry = (struct mountd_export_entry_set_fsid *)
+ ment->ment_data;
+
+ /*
+ * Refuse multiple fsid commands with EINVAL.
+ */
+ if (int_fsidp != NULL || ext_fsidp != NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Take the specified internal (expected local)
+ * and external (exported) fsids directly from
+ * the buffer, no copyin needed.
+ */
+ int_fsidp = &entry->mesf_int_fsid;
+ ext_fsidp = &entry->mesf_ext_fsid;
+ break;
+ }
+ default:
+ error = EOPNOTSUPP;
+ goto out;
+ }
+ }
+
+ /*
+ * We have copied in everything except for the user's path.
+ * Configure the mount point's exports.
+ */
+ error = mountd_set_exports_ext(user_path, l, /*nmp*/NULL, args,
+ nexports, int_fsidp, ext_fsidp, NFSSVC_REPLACEEXPORTSLIST);
+
+out: if (args)
+ free(args, M_TEMP);
+ free(ment0, M_TEMP);
+ return error;
+}
+
static struct nfssvc_sock *
nfsrv_sockalloc(void)
{
diff -r 2db9820dc602 -r 3c615e360a5e sys/nfs/nfs_var.h
--- a/sys/nfs/nfs_var.h Wed Aug 26 03:28:28 2026 +0000
+++ b/sys/nfs/nfs_var.h Mon Aug 24 19:50:51 2026 +0000
@@ -273,16 +273,16 @@ int nfs_getattrcache(struct vnode *, str
void nfs_delayedtruncate(struct vnode *);
int nfs_check_wccdata(struct nfsnode *, const struct timespec *,
struct timespec *, bool);
-int nfs_namei(struct nameidata *, nfsrvfh_t *, uint32_t, struct nfssvc_sock *,
- struct mbuf *, struct mbuf **, char **, struct vnode **,
- int *, struct vattr *, struct lwp *, int, int);
+int nfs_namei(struct nameidata *, nfsrvfh_t *, fsid_t *, uint32_t,
+ struct nfssvc_sock *, struct mbuf *, struct mbuf **, char **,
+ struct vnode **, int *, struct vattr *, struct lwp *, int, int);
void nfs_zeropad(struct mbuf *, int, int);
void nfsm_srvwcc(struct nfsrv_descript *, int, struct vattr *, int,
struct vattr *, struct mbuf **, char **);
void nfsm_srvpostopattr(struct nfsrv_descript *, int, struct vattr *,
struct mbuf **, char **);
void nfsm_srvfattr(struct nfsrv_descript *, struct vattr *, struct nfs_fattr *);
-int nfsrv_fhtovp(nfsrvfh_t *, int, struct vnode **, kauth_cred_t,
+int nfsrv_fhtovp(nfsrvfh_t *, int, struct vnode **, fsid_t *, kauth_cred_t,
struct nfssvc_sock *, struct mbuf *, int *, int, int);
int nfs_ispublicfh(const nfsrvfh_t *);
int netaddr_match(int, union nethostaddr *, struct mbuf *);
@@ -306,7 +306,7 @@ void nfs_cookieheuristic(struct vnode *,
u_int32_t nfs_getxid(void);
void nfs_renewxid(struct nfsreq *);
-int nfsrv_composefh(struct vnode *, nfsrvfh_t *, bool);
+int nfsrv_composefh(struct vnode *, nfsrvfh_t *, const fsid_t[static 1], bool);
int nfsrv_comparefh(const nfsrvfh_t *, const nfsrvfh_t *);
void nfsrv_copyfh(nfsrvfh_t *, const nfsrvfh_t *);
@@ -361,6 +361,8 @@ int do_nfssvc(struct nfssvc_copy_ops *,
extern struct nfs_public nfs_pub;
int mountd_set_exports_list(const struct mountd_exports_list *, struct lwp *,
struct mount *, int);
+int mountd_set_exports_ext(const char *, struct lwp *, struct mount *,
+ const struct export_args *, size_t, const fsid_t *, const fsid_t *, int);
int netexport_check(const fsid_t *, struct mbuf *, struct mount **, int *,
kauth_cred_t *);
void netexport_rdlock(void);
diff -r 2db9820dc602 -r 3c615e360a5e usr.sbin/mountd/exports.5
--- a/usr.sbin/mountd/exports.5 Wed Aug 26 03:28:28 2026 +0000
+++ b/usr.sbin/mountd/exports.5 Mon Aug 24 19:50:51 2026 +0000
@@ -293,7 +293,8 @@ WebNFS enables any client to get filehan
Using IP spoofing, a client could then pretend to be a host to which
the same filesystem was exported read/write, and use the handle to
gain access to that filesystem.
-.Bl -tag -width Fl
+.Pp
+.Bl -tag -width Fl -compact
.Sm off
.It Fl network Li = Ar netname Op Li / Ar prefixlength
.Sm on
@@ -326,6 +327,7 @@ is used to specify
on
.Ql ne2
interface.
+.Pp
.Sm off
.It Fl mask No = Ar netmask
.Sm on
@@ -334,6 +336,41 @@ Netmask for
.Fl network
options with no
.Ar prefixlength .
+.Pp
+.Sm off
+.It Fl fsid No = 0x Ar extfsid0 No . 0x Ar extfsid1
+.It Fl fsid No = 0x Ar intfsid0 No . 0x Ar intfsid1 No : 0x Ar extfsid0 No . 0x Ar extfsid1
+.Sm on
+Set the external fsid in file handles exposed over the network.
+The fsid components \(em
+.Ar intfsid0 , Ar intfsid1 , Ar extfsid0 ,
+and
+.Ar extfsid1
+\(em are 32-bit unsigned hexadecimal integers, and correspond to the
+elements of the
+.Vt fsid_t
+structure
+.Pq Xr statvfs 5 .
+.Pp
+In the first form, simply use
+.Ar extfsid0 No / Ar extfsid1
+as the fsid in file handles exposed over the network.
+.Pp
+In the second form,
+.Xr mountd 8
+will verify that the internal file system's fsid matches
+.Ar intfsid0 No / Ar intfsid1 ,
+and only if it matches, export the file system with
+.Ar extfsid0 No / Ar extfsid1 .
+If it does not match,
+.Xr mountd 8
+will fail noisily.
+This way, you can ensure that the wrong file system is not
+inadvertently exported.
+.Pp
+If the same mount point is exported multiple times, or multiple
+.Fl fsid
+options are specified, they must all match for the mount point.
.El
.Sh FILES
.Bl -tag -width Pa -compact
diff -r 2db9820dc602 -r 3c615e360a5e usr.sbin/mountd/mountd.c
--- a/usr.sbin/mountd/mountd.c Wed Aug 26 03:28:28 2026 +0000
+++ b/usr.sbin/mountd/mountd.c Mon Aug 24 19:50:51 2026 +0000
@@ -81,6 +81,7 @@ static char sccsid[] = "@(#)mountd.c
#include <pwd.h>
#include <netgroup.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -137,11 +138,13 @@ struct exportlist {
struct dirlist *ex_defdir;
int ex_flag;
fsid_t ex_fs;
+ fsid_t ex_ext_fs;
char *ex_fsdir;
char *ex_indexfile;
};
/* ex_flag bits */
#define EX_LINKED 0x1
+#define EX_SET_FSID 0x2
union grouptypes {
struct addrinfo *gt_addrinfo;
@@ -210,6 +213,8 @@ static void hang_dirp(struct dirlist *,
static void mntsrv(struct svc_req *, SVCXPRT *);
static void nextfield(char **, char **);
static void parsecred(char *, struct uucred *);
+static int parse_fsid(const char *, fsid_t[static 1], fsid_t[static 1],
+ bool[static 1]);
static int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *);
static int scan_tree(struct dirlist *, struct sockaddr *);
__dead static void send_umntall(int);
@@ -235,7 +240,16 @@ static struct uucred def_anon = {
0,
{ 0 }
};
-static struct mountd_exports_list *mel_tab;
+
+struct mountd_exports {
+ const char *mel_path;
+ size_t mel_nexports;
+ struct export_args *mel_exports;
+ fsid_t mel_int_fsid;
+ fsid_t *mel_ext_fsid;
+};
+
+static struct mountd_exports *mel_tab;
static int mel_tab_len;
int opt_flags;
@@ -1214,8 +1228,8 @@ nextline:
static int
mel_compare(const void *a, const void *b)
{
- const struct mountd_exports_list *mela = a;
- const struct mountd_exports_list *melb = b;
+ const struct mountd_exports *mela = a;
+ const struct mountd_exports *melb = b;
return strcmp(mela->mel_path, melb->mel_path);
}
@@ -1258,11 +1272,17 @@ get_exportlist(int n)
* file systems.
*/
mel_tab_len = getmntinfo(&fsp, MNT_NOWAIT);
+ if (mel_tab_len == -1) {
+ warn("getmntinfo");
+ return;
+ }
mel_tab = ecalloc(mel_tab_len, sizeof(*mel_tab));
for (i = 0; i < mel_tab_len; i++) {
mel_tab[i].mel_path = estrdup(fsp[i].f_mntonname);
mel_tab[i].mel_nexports = 0;
mel_tab[i].mel_exports = NULL;
+ mel_tab[i].mel_int_fsid = fsp->f_fsidx;
+ mel_tab[i].mel_ext_fsid = NULL;
}
qsort(mel_tab, mel_tab_len, sizeof(mel_tab[0]), mel_compare);
@@ -1288,9 +1308,45 @@ get_exportlist(int n)
}
for (i = 0; i < mel_tab_len; i++) {
- struct mountd_exports_list *mel = &mel_tab[i];
+ struct mountd_exports *mel = &mel_tab[i];
+ struct mountd_exports_ext mext;
+ size_t mentbytes;
+ struct mountd_export_entry_args *args_entry;
+ struct mountd_export_entry_set_fsid *set_fsid;
+ union {
+ struct mountd_export_entry ment;
+ char buf[MOUNTD_EXPORT_ENTRYSIZE(sizeof(*args_entry)) +
+ MOUNTD_EXPORT_ENTRYSIZE(sizeof(*set_fsid))];
+ } u;
+ struct mountd_export_entry *ment;
+
+ mentbytes = 0;
- if (nfssvc(NFSSVC_REPLACEEXPORTSLIST, mel) == -1 &&
+ ment = &u.ment;
+ ment->ment_cmd = MOUNTD_EXPORT_ARGS;
+ ment->ment_len = sizeof(*args_entry);
+ args_entry = (void *)ment->ment_data;
+ args_entry->mea_uptr = (uintptr_t)(void *)mel->mel_exports;
+ args_entry->mea_nexports = mel->mel_nexports;
+ mentbytes += MOUNTD_EXPORT_ENTRYSIZE(sizeof(*args_entry));
+
+ if (mel->mel_ext_fsid) {
+ ment = MOUNTD_EXPORT_ENTRYNEXT(ment);
+ ment->ment_cmd = MOUNTD_EXPORT_SET_FSID;
+ ment->ment_len = sizeof(*set_fsid);
+ set_fsid = (void *)ment->ment_data;
+ set_fsid->mesf_int_fsid = mel->mel_int_fsid;
+ set_fsid->mesf_ext_fsid = *mel->mel_ext_fsid;
+ mentbytes +=
+ MOUNTD_EXPORT_ENTRYSIZE(sizeof(*set_fsid));
+ }
+
+ memset(&mext, 0, sizeof(mext));
+ mext.mext_pathptr = (uintptr_t)(const void *)mel->mel_path;
+ mext.mext_entriesptr = (uintptr_t)(const void *)&u.ment;
+ mext.mext_entriesbytes = mentbytes;
+
+ if (nfssvc(NFSSVC_REPLACEEXPORTSEXT, &mext) == -1 &&
(mel->mel_nexports > 0 || errno != EOPNOTSUPP))
syslog(LOG_ERR, "Can't update exports for %s (%m)",
mel_tab[i].mel_path);
@@ -1838,6 +1894,65 @@ do_opt(const char *line, size_t lineno,
opt_flags |= (OP_MAPALL | OP_NORESPORT);
} else if (cpoptarg && !strcmp(cpopt, "index")) {
ep->ex_indexfile = strdup(cpoptarg);
+ } else if (cpoptarg && !strcmp(cpopt, "fsid")) {
+ fsid_t int_fsid, ext_fsid;
+ bool check;
+
+ if (parse_fsid(cpoptarg, &int_fsid, &ext_fsid,
+ &check)) {
+ syslog(LOG_ERR,
+ "\"%s\", line %ld: Bad fsid opt %s",
+ line, (unsigned long)lineno, cpoptarg);
+ return (1);
+ }
+
+ if (mountd_debug) {
+ if (check) {
+ fprintf(stderr, "expect fsid"
+ " {0x%"PRIx32",0x%"PRIx32"}\n",
+ int_fsid.__fsid_val[0],
+ int_fsid.__fsid_val[1]);
+ fprintf(stderr, "actual fsid"
+ " {0x%"PRIx32",0x%"PRIx32"}\n",
+ ep->ex_fs.__fsid_val[0],
+ ep->ex_fs.__fsid_val[1]);
+ }
+ fprintf(stderr, "export with fsid"
+ " {0x%"PRIx32",0x%"PRIx32"}\n",
+ ext_fsid.__fsid_val[0],
+ ext_fsid.__fsid_val[1]);
+ }
+
+ if (check) {
+ if ((int_fsid.__fsid_val[0] !=
+ ep->ex_fs.__fsid_val[0]) ||
+ (int_fsid.__fsid_val[1] !=
+ ep->ex_fs.__fsid_val[1])) {
+ syslog(LOG_ERR, "\"%s\", line %ld:"
+ " Wrong internal fsid: internal"
+ " 0x%"PRIx32",0x%"PRIx32" !="
+ " expected 0x%"PRIx32",0x%"PRIx32,
+ line, (unsigned long)lineno,
+ ep->ex_fs.__fsid_val[0],
+ ep->ex_fs.__fsid_val[1],
+ int_fsid.__fsid_val[0],
+ int_fsid.__fsid_val[1]);
+ return (1);
+ }
+ }
+ if (ep->ex_flag & EX_SET_FSID) {
+ if ((ext_fsid.__fsid_val[0] !=
+ ep->ex_fs.__fsid_val[0]) ||
+ (ext_fsid.__fsid_val[1] !=
+ ep->ex_fs.__fsid_val[1])) {
+ syslog(LOG_ERR, "\"%s\", line %ld:"
+ " Duplicate external fsid",
+ line, (unsigned long)lineno);
+ return (1);
+ }
+ }
+ ep->ex_flag |= EX_SET_FSID;
+ ep->ex_ext_fs = ext_fsid;
} else {
syslog(LOG_ERR,
"\"%s\", line %ld: Bad opt %s",
@@ -1953,10 +2068,10 @@ get_ht(void)
static int
add_export_arg(const char *path, int exflags, struct uucred *anoncrp,
struct sockaddr *addrp, int addrlen, struct sockaddr *maskp, int masklen,
- char *indexfile)
+ struct exportlist *ep)
{
- const struct mountd_exports_list mel_key = { .mel_path = path };
- struct mountd_exports_list *mel;
+ const struct mountd_exports mel_key = { .mel_path = path };
+ struct mountd_exports *mel;
struct export_args *export;
if (addrp != NULL && addrp->sa_family == AF_INET6 && have_v6 == 0)
@@ -1969,6 +2084,19 @@ add_export_arg(const char *path, int exf
path);
return 1;
}
+ if (ep->ex_flag & EX_SET_FSID) {
+ if (mel->mel_ext_fsid) {
+ if ((ep->ex_ext_fs.__fsid_val[0] !=
+ mel->mel_ext_fsid->__fsid_val[0]) ||
+ (ep->ex_ext_fs.__fsid_val[1] !=
+ mel->mel_ext_fsid->__fsid_val[1])) {
+ syslog(LOG_ERR, "Conflicting external fsids"
+ " for %s", path);
+ return 1;
+ }
+ }
+ mel->mel_ext_fsid = &ep->ex_ext_fs;
+ }
ereallocarr(&mel->mel_exports, mel->mel_nexports + 1,
sizeof(*mel->mel_exports));
export = &mel->mel_exports[mel->mel_nexports++];
@@ -1976,8 +2104,8 @@ add_export_arg(const char *path, int exf
export->ex_flags = exflags;
export->ex_anon = *anoncrp;
- if (indexfile)
- export->ex_indexfile = estrdup(indexfile);
+ if (ep->ex_indexfile)
+ export->ex_indexfile = estrdup(ep->ex_indexfile);
if (addrlen > 0) {
export->ex_addr = emalloc(addrlen);
export->ex_addrlen = addrlen;
@@ -2010,7 +2138,7 @@ do_nfssvc(const char *line, size_t linen
addrp = ai->ai_addr;
addrlen = ai->ai_addrlen;
if (add_export_arg(fsb->f_mntonname, exflags, anoncrp,
- addrp, addrlen, NULL, 0, ep->ex_indexfile) != 0)
+ addrp, addrlen, NULL, 0, ep) != 0)
return 1;
}
} else if (grp->gr_type == GT_NET) {
@@ -2026,7 +2154,7 @@ do_nfssvc(const char *line, size_t linen
}
if (add_export_arg(fsb->f_mntonname, exflags, anoncrp,
addrp, addrlen, (struct sockaddr *)&ss, ss.ss_len,
- ep->ex_indexfile) != 0)
+ ep) != 0)
return 1;
} else {
syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
@@ -2135,6 +2263,90 @@ parsecred(char *namelist, struct uucred
syslog(LOG_ERR, "Too many groups");
}
+/*
+ * parse_fsid(fsidstr, &int_fsid, &ext_fsid, &check)
+ *
+ * Parse an fsid setting. One of:
+ *
+ * 0x<hex32>.0x<hex32>
+ * 0x<hex32>.0x<hex32>:0x<hex32>.0x<hex32>
+ *
+ * In the first case, set check = false; in the second case, set
+ * check = true.
+ *
+ * Return zero on success, nonzero on failure.
+ */
+static int
+parse_fsid(const char *fsidstr,
+ fsid_t int_fsid[static 1], fsid_t ext_fsid[static 1],
+ bool check[static 1])
+{
+ unsigned long x;
+ fsid_t fsid0, fsid1;
+ char *endptr;
+
+ memset(&fsid0, 0, sizeof(fsid0));
+ memset(&fsid1, 0, sizeof(fsid1));
+
+ if (strncmp(fsidstr, "0x", 2) != 0)
+ return 1;
+ fsidstr += 2;
+
+ errno = 0;
+ x = strtoul(fsidstr, &endptr, 0x10);
+ if (endptr == fsidstr || endptr[0] != '.' || errno || x > UINT32_MAX)
+ return 1;
+ fsid0.__fsid_val[0] = (int32_t)x;
+ fsidstr = endptr;
+
+ if (strncmp(fsidstr, ".0x", 3) != 0)
+ return 1;
+ fsidstr += 3;
+
+ errno = 0;
+ x = strtoul(fsidstr, &endptr, 0x10);
+ if (endptr == fsidstr ||
+ (endptr[0] != '\0' && endptr[0] != ':') ||
+ errno ||
+ x > UINT32_MAX)
+ return 1;
+ fsid0.__fsid_val[1] = (int32_t)x;
+ fsidstr = endptr;
+
+ if (fsidstr[0] == '\0') {
+ *ext_fsid = fsid0;
+ *check = false;
+ return 0;
+ }
+
+ if (strncmp(fsidstr, ":0x", 3) != 0)
+ return 1;
+ fsidstr += 3;
+
+ errno = 0;
+ x = strtoul(fsidstr, &endptr, 0x10);
+ if (endptr == fsidstr || endptr[0] != '.' || errno || x > UINT32_MAX)
+ return 1;
+ fsid1.__fsid_val[0] = (int32_t)x;
+ fsidstr = endptr;
+
+ if (strncmp(fsidstr, ".0x", 3) != 0)
+ return 1;
+ fsidstr++;
+
+ errno = 0;
+ x = strtoul(fsidstr, &endptr, 0x10);
+ if (endptr == fsidstr || endptr[0] != '\0' || errno || x > UINT32_MAX)
+ return 1;
+ fsid1.__fsid_val[1] = (int32_t)x;
+ fsidstr = endptr;
+
+ *int_fsid = fsid0;
+ *ext_fsid = fsid1;
+ *check = true;
+ return 0;
+}
+
#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
/*
* Routines that maintain the remote mounttab
Home |
Main Index |
Thread Index |
Old Index