git: 53b4ae3bf0f7 - main - nfs_diskless: Fix handling of nfsuserd case for NFSv4
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 07 Apr 2026 15:53:23 UTC
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=53b4ae3bf0f7e625d51fa263a5bd3859792d61e3
commit 53b4ae3bf0f7e625d51fa263a5bd3859792d61e3
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-04-07 15:50:21 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-04-07 15:50:21 +0000
nfs_diskless: Fix handling of nfsuserd case for NFSv4
Commit 8b9775912cbc added support for an NFSv4 mounted
root file system, but only if the NFSv4 configuration
used id numbers in the strings.
This patch adds support for the case where the NFSv4
configuration uses name<-->id mappings via nfsuserd(8)
by priming the mapping cache with just enough entries
so that it works until the nfsuserd(8) is running.
They are listed in nfs_prime_userd[] in
sys/fs/nfs/nfs_commonsubs.c.
The entries in nfs_prime_userd[] are also wired into
the kernel's cache for name<-->id mappings when nfsuserd(8)
starts up. This is necessary, since an upcall to the
nfsuserd(8) daemon for a mapping when looking up the
path to the passwd/group database files (/etc) will
hang the system, due to a vnode lock being held on
the entry in the path which blocks nfsuserd(8) from
accessing files.
To enable this case, the following must be put in the
NFS root file system's /boot/loader.conf:
boot.nfsroot.options="nfsv4"
boot.nfsroot.user_domain="<user.domain>"
where <user.domain> must be the same as nfsuserd
uses (usually set via the -domain flag).
If boot.nfsroot.user_domain does not exist or is
the empty string, ids is strings is configured.
MFC after: 1 week
Requested by: Dan Shelton <dan.f.shelton@gmail.com>
Fixes: 8b9775912cbc ("nfs_diskless: Add support for an NFSv4 root fs")
---
sys/fs/nfs/nfs_commonsubs.c | 76 +++++++++++++++++++++++++++++++++++++++------
sys/fs/nfs/nfsid.h | 13 ++++++++
sys/fs/nfs/nfsrvstate.h | 1 +
sys/nfs/nfs_diskless.c | 44 ++++++++++++++++++++++++++
4 files changed, 124 insertions(+), 10 deletions(-)
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 78e2fbb72bdb..39d7cb447d6a 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -70,6 +70,7 @@ struct nfsreqhead nfsd_reqq;
int nfsrv_lease = NFSRV_LEASE;
int ncl_mbuf_mlen = MLEN;
int nfsrv_doflexfile = 0;
+bool nfs_nfsv4root = false;
NFSNAMEIDMUTEX;
NFSSOCKMUTEX;
extern int nfsrv_lughashsize;
@@ -80,6 +81,21 @@ extern struct nfsdevicehead nfsrv_devidhead;
extern struct nfsstatsv1 nfsstatsv1;
extern uint32_t nfs_srvmaxio;
+/*
+ * Define just enough NFSv4 id<-->name mappings to make things work
+ * until the nfsuserd(8) is running.
+ * XXX These name/ids must be kept the same as what is in /etc/passwd
+ * and /etc/group.
+ */
+struct nfs_prime_userd nfs_prime_userd[] = {
+ { NFSID_INITIALIZE, UID_NOBODY, GID_NOGROUP, NULL },
+ { NFSID_ADDUID, UID_ROOT, GID_NOGROUP, "root" },
+ { NFSID_ADDUID, UID_BIN, GID_NOGROUP, "bin" },
+ { NFSID_ADDGID, UID_NOBODY, GID_WHEEL, "wheel" },
+ { NFSID_ADDGID, UID_NOBODY, GID_OPERATOR, "operator" },
+ { 0, 0, 0, NULL },
+};
+
NFSD_VNET_DEFINE(int, nfsd_enable_stringtouid) = 0;
NFSD_VNET_DEFINE(struct nfssockreq, nfsrv_nfsuserdsock);
NFSD_VNET_DEFINE(nfsuserd_state, nfsrv_nfsuserd) = NOTRUNNING;
@@ -233,6 +249,7 @@ static int nfsrv_skipace(struct nfsrv_descript *nd, acl_type_t, int *acesizep);
static void nfsv4_wanted(struct nfsv4lock *lp);
static uint32_t nfsv4_filesavail(struct statfs *, struct mount *);
static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name);
+static bool nfs_in_prime(int flag, uid_t uid, gid_t gid);
static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
int *, int *);
@@ -3667,7 +3684,8 @@ tryagain:
mtx_lock(&hp->mtx);
TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_uid == uid) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
break;
/*
* If the name doesn't already have an '@'
@@ -3759,7 +3777,8 @@ tryagain:
mtx_lock(&hp->mtx);
TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_uid == uid) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
break;
if (usrp->lug_cred != NULL) {
newcred = crhold(usrp->lug_cred);
@@ -3859,7 +3878,8 @@ tryagain:
TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
if (usrp->lug_namelen == len &&
!NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
break;
hp2 = NFSUSERHASH(usrp->lug_uid);
mtx_lock(&hp2->mtx);
@@ -3936,7 +3956,8 @@ tryagain:
mtx_lock(&hp->mtx);
TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_gid == gid) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
break;
/*
* If the name doesn't already have an '@'
@@ -4081,7 +4102,8 @@ tryagain:
TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
if (usrp->lug_namelen == len &&
!NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
break;
hp2 = NFSGROUPHASH(usrp->lug_gid);
mtx_lock(&hp2->mtx);
@@ -4282,6 +4304,23 @@ out:
return (error);
}
+/* Check to see if the uid/gid is in the nfs_prime_userd list. */
+static bool
+nfs_in_prime(int flag, uid_t uid, gid_t gid)
+{
+ int i;
+
+ for (i = 0; nfs_prime_userd[i].flag != 0; i++) {
+ if ((nfs_prime_userd[i].flag & flag) == NFSID_ADDUID &&
+ nfs_prime_userd[i].uid == uid)
+ return (true);
+ if ((nfs_prime_userd[i].flag & flag) == NFSID_ADDGID &&
+ nfs_prime_userd[i].gid == gid)
+ return (true);
+ }
+ return (false);
+}
+
/*
* This function is called from the nfssvc(2) system call, to update the
* kernel user/group name list(s) for the V4 owner and ownergroup attributes.
@@ -4305,7 +4344,11 @@ nfssvc_idname(struct nfsd_idargs *nidp)
}
if (nidp->nid_flag & NFSID_INITIALIZE) {
cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
- error = copyin(nidp->nid_name, cp, nidp->nid_namelen);
+ error = 0;
+ if ((nidp->nid_flag & NFSID_SYSSPACE) == 0)
+ error = copyin(nidp->nid_name, cp, nidp->nid_namelen);
+ else
+ NFSBCOPY(nidp->nid_name, cp, nidp->nid_namelen);
if (error != 0) {
free(cp, M_NFSSTRING);
goto out;
@@ -4403,8 +4446,12 @@ nfssvc_idname(struct nfsd_idargs *nidp)
*/
newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
M_NFSUSERGROUP, M_WAITOK | M_ZERO);
- error = copyin(nidp->nid_name, newusrp->lug_name,
- nidp->nid_namelen);
+ error = 0;
+ if ((nidp->nid_flag & NFSID_SYSSPACE) == 0)
+ error = copyin(nidp->nid_name, newusrp->lug_name,
+ nidp->nid_namelen);
+ else
+ NFSBCOPY(nidp->nid_name, newusrp->lug_name, nidp->nid_namelen);
if (error == 0 && nidp->nid_ngroup > 0 &&
(nidp->nid_flag & NFSID_ADDUID) != 0) {
grps = NULL;
@@ -4522,7 +4569,11 @@ nfssvc_idname(struct nfsd_idargs *nidp)
newusrp->lug_expiry = NFSD_MONOSEC + nidp->nid_usertimeout;
else
newusrp->lug_expiry = NFSD_MONOSEC + 5;
+ newusrp->lug_wired = false;
if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
+ if (nfs_nfsv4root && nfs_in_prime(NFSID_ADDUID, nidp->nid_uid,
+ nidp->nid_gid))
+ newusrp->lug_wired = true;
newusrp->lug_uid = nidp->nid_uid;
thp = NFSUSERHASH(newusrp->lug_uid);
mtx_assert(&thp->mtx, MA_OWNED);
@@ -4532,6 +4583,9 @@ nfssvc_idname(struct nfsd_idargs *nidp)
TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
atomic_add_int(&NFSD_VNET(nfsrv_usercnt), 1);
} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
+ if (nfs_nfsv4root && nfs_in_prime(NFSID_ADDGID, nidp->nid_uid,
+ nidp->nid_gid))
+ newusrp->lug_wired = true;
newusrp->lug_gid = nidp->nid_gid;
thp = NFSGROUPHASH(newusrp->lug_gid);
mtx_assert(&thp->mtx, MA_OWNED);
@@ -4580,7 +4634,8 @@ nfssvc_idname(struct nfsd_idargs *nidp)
TAILQ_FOREACH_SAFE(usrp,
&NFSD_VNET(nfsuserhash)[i].lughead, lug_numhash,
nusrp)
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
nfsrv_removeuser(usrp, 1);
}
for (i = 0; i < nfsrv_lughashsize; i++) {
@@ -4610,7 +4665,8 @@ nfssvc_idname(struct nfsd_idargs *nidp)
TAILQ_FOREACH_SAFE(usrp,
&NFSD_VNET(nfsgrouphash)[i].lughead, lug_numhash,
nusrp)
- if (usrp->lug_expiry < NFSD_MONOSEC)
+ if (!usrp->lug_wired &&
+ usrp->lug_expiry < NFSD_MONOSEC)
nfsrv_removeuser(usrp, 0);
}
for (i = 0; i < nfsrv_lughashsize; i++) {
diff --git a/sys/fs/nfs/nfsid.h b/sys/fs/nfs/nfsid.h
index bd9807ca1acc..349fdecfc596 100644
--- a/sys/fs/nfs/nfsid.h
+++ b/sys/fs/nfs/nfsid.h
@@ -61,6 +61,19 @@ struct nfsd_idargs {
#define NFSID_SYSSPACE 0x0200
#if defined(_KERNEL) || defined(KERNEL)
+/*
+ * Define just enough NFSv4 id<-->name mappings to make things work
+ * until the nfsuserd(8) is running.
+ * XXX These name/ids must be kept the same as what is in /etc/passwd
+ * and /etc/group.
+ */
+struct nfs_prime_userd {
+ int flag;
+ uid_t uid;
+ gid_t gid;
+ char *nam;
+};
+
int nfssvc_idname(struct nfsd_idargs *);
#endif
diff --git a/sys/fs/nfs/nfsrvstate.h b/sys/fs/nfs/nfsrvstate.h
index cc19ed6fa1d2..858c52ec6218 100644
--- a/sys/fs/nfs/nfsrvstate.h
+++ b/sys/fs/nfs/nfsrvstate.h
@@ -317,6 +317,7 @@ struct nfsusrgrp {
} lug_un;
struct ucred *lug_cred; /* Cred. with groups list */
int lug_namelen; /* Name length */
+ bool lug_wired; /* Wired into cache */
u_char lug_name[1]; /* malloc'd correct length */
};
#define lug_uid lug_un.un_uid
diff --git a/sys/nfs/nfs_diskless.c b/sys/nfs/nfs_diskless.c
index d5278612d8d9..32dd7f3e997f 100644
--- a/sys/nfs/nfs_diskless.c
+++ b/sys/nfs/nfs_diskless.c
@@ -54,6 +54,7 @@
#include <nfs/nfsproto.h>
#include <nfsclient/nfs.h>
#include <nfs/nfsdiskless.h>
+#include <fs/nfs/nfsid.h>
#define NFS_IFACE_TIMEOUT_SECS 10 /* Timeout for interface to appear. */
@@ -70,6 +71,9 @@ struct nfs_diskless nfs_diskless = { { { 0 } } };
struct nfsv3_diskless nfsv3_diskless = { { { 0 } } };
int nfs_diskless_valid = 0;
+extern struct nfs_prime_userd nfs_prime_userd[];
+extern bool nfs_nfsv4root;
+
/*
* Validate/sanity check a rsize/wsize parameter.
*/
@@ -292,11 +296,51 @@ match_done:
return;
}
} else {
+ struct nfsd_idargs nid;
+ int ret;
+
/*
* For NFSv4, the file handle is derived from the
* boot.nfsroot.path during mounting by NFSv4.
*/
nd3->root_fhsize = 0;
+ nfs_nfsv4root = true;
+
+ /*
+ * Prime the id<-->name mappings just enough to
+ * make things work until the nfsuserd(8) daemon
+ * is started, if the nfsuserd_domain is set to a
+ * non-empty string.
+ */
+ if ((cp = kern_getenv("boot.nfsroot.user_domain")) !=
+ NULL) {
+ for (cnt = 0; *cp != '\0' &&
+ nfs_prime_userd[cnt].flag != 0; cnt++) {
+ nid.nid_flag =
+ nfs_prime_userd[cnt].flag |
+ NFSID_SYSSPACE;
+ if (nfs_prime_userd[cnt].flag ==
+ NFSID_INITIALIZE) {
+ nid.nid_name = cp;
+ nid.nid_usermax = 10;
+ } else {
+ nid.nid_name =
+ nfs_prime_userd[cnt].nam;
+ nid.nid_usertimeout = 3600;
+ }
+ nid.nid_namelen = strlen(nid.nid_name);
+ nid.nid_uid = nfs_prime_userd[cnt].uid;
+ nid.nid_gid = nfs_prime_userd[cnt].gid;
+ nid.nid_ngroup = 0;
+ nid.nid_grps = NULL;
+ ret = nfssvc_idname(&nid);
+ if (ret != 0)
+ printf("nfs_diskless: "
+ "nfssvc_idname failed %d\n",
+ ret);
+ }
+ freeenv(cp);
+ }
}
if ((cp = kern_getenv("boot.nfsroot.path")) != NULL) {
strncpy(nd3->root_hostnam, cp, MNAMELEN - 1);