nfs_diskless: Add support for an NFSv4 root fs
Without this patch, diskless root NFS file systems could only be mounted via NFSv3 (or NFSv2). This patch adds the basic support needed to mount a root fs via NFSv4. At this time, the NFSv4 mount will only work if the following is done on the NFS server configuration: - The root directory specified in the "V4:" line in /etc/exports must be "/". This is needed since the path to mount must be the same for NFSv3 and NFSv4. - The NFS server must be configured to do both NFSv3 and NFSv4, since the bootstrap code still uses NFSv3. - The NFSv4 server must be configured with: vfs.nfs.enable_uidtostring=1 vfs.nfsd.enable_stringtouid=1 since the NFSv4 root fs cannot be running nfsuserd(8) when it is booting. (This limitation may be removed in a future commit by hard-wiring enough id<-->name mapping entries to handle things until the nfsuserd(8) is running.) To enable the root fs to be mounted via NFSv4, it needs: - in the root file system's /boot/loader.conf boot.nfsroot.options="nfsv4" (Additional options like rsize=65536,wsize=65536 can also be specified.) - in the root file system's /etc/sysctl.conf vfs.nfs.enable_uidtostring=1 Requested by: Dan Shelton <dan.f.dhelton@gmail.com> MFC after: 1 week
This commit is contained in:
@@ -609,6 +609,7 @@ int nfscl_relbytelock(vnode_t, u_int64_t, u_int64_t,
|
||||
int nfscl_checkwritelocked(vnode_t, struct flock *,
|
||||
struct ucred *, NFSPROC_T *, void *, int);
|
||||
void nfscl_lockrelease(struct nfscllockowner *, int, int);
|
||||
void nfscl_uuidcheck(char *);
|
||||
void nfscl_fillclid(u_int64_t, char *, u_int8_t *, u_int16_t);
|
||||
void nfscl_filllockowner(void *, u_int8_t *, int);
|
||||
void nfscl_freeopen(struct nfsclopen *, int, bool);
|
||||
|
||||
@@ -659,6 +659,37 @@ ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep)
|
||||
return (setnsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the uuid passed in is the DEFAULT_UUID, try and find an
|
||||
* alternate to replace it with.
|
||||
* If no alternate is available, set uuid to "" so that nfscl_fillclid()
|
||||
* will use random bytes.
|
||||
*/
|
||||
void
|
||||
nfscl_uuidcheck(char *uuid)
|
||||
{
|
||||
int ucplen, uuidlen;
|
||||
char *ucp;
|
||||
|
||||
/*
|
||||
* If the uuid is the DEFAULT_UUID, try and get an alternative.
|
||||
*/
|
||||
uuidlen = strlen(uuid);
|
||||
ucp = NULL;
|
||||
if (uuidlen == strlen(DEFAULT_HOSTUUID) &&
|
||||
NFSBCMP(uuid, DEFAULT_HOSTUUID, uuidlen) == 0) {
|
||||
*uuid = '\0';
|
||||
/* Use smbios.system.uuid if it exists. */
|
||||
if ((ucp = kern_getenv("smbios.system.uuid")) != NULL) {
|
||||
ucplen = strlen(ucp);
|
||||
if (ucplen < HOSTUUIDLEN && ucplen > 0)
|
||||
strlcpy(uuid, ucp, HOSTUUIDLEN);
|
||||
}
|
||||
}
|
||||
if (ucp != NULL)
|
||||
freeenv(ucp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the client id name. For these bytes:
|
||||
* 1 - they must be unique
|
||||
|
||||
@@ -67,6 +67,7 @@ SYSCTL_U64(_vfs_nfs, OID_AUTO, maxcopyrange, CTLFLAG_RW,
|
||||
/*
|
||||
* Global variables
|
||||
*/
|
||||
uint32_t nfs_exchangeboot = 0;
|
||||
extern struct nfsstatsv1 nfsstatsv1;
|
||||
extern int nfs_numnfscbd;
|
||||
extern struct timeval nfsboottime;
|
||||
@@ -5537,7 +5538,7 @@ nfsrpc_exchangeid(struct nfsmount *nmp, struct nfsclclient *clp,
|
||||
nfscl_reqstart(nd, NFSPROC_EXCHANGEID, nmp, NULL, 0, NULL, NULL,
|
||||
NFS_VER4, minorvers, NULL);
|
||||
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
||||
*tl++ = txdr_unsigned(nfsboottime.tv_sec); /* Client owner */
|
||||
*tl++ = txdr_unsigned(nfs_exchangeboot); /* Client owner */
|
||||
*tl = txdr_unsigned(clp->nfsc_rev);
|
||||
(void) nfsm_strtom(nd, clp->nfsc_id, clp->nfsc_idlen);
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ extern u_int32_t newnfs_false, newnfs_true;
|
||||
extern int nfscl_debuglevel;
|
||||
extern int nfscl_enablecallb;
|
||||
extern int nfs_numnfscbd;
|
||||
extern struct timeval nfsboottime;
|
||||
extern uint32_t nfs_exchangeboot;
|
||||
NFSREQSPINLOCK;
|
||||
NFSCLSTATEMUTEX;
|
||||
int nfscl_inited = 0;
|
||||
@@ -883,9 +885,11 @@ nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
|
||||
if (cred != NULL) {
|
||||
getcredhostuuid(cred, uuid, sizeof uuid);
|
||||
idlen = strlen(uuid);
|
||||
if (idlen > 0)
|
||||
if (idlen > 0) {
|
||||
nfscl_uuidcheck(uuid);
|
||||
idlen = strlen(uuid);
|
||||
idlen += sizeof (u_int64_t);
|
||||
else
|
||||
} else
|
||||
idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
|
||||
newclp = malloc(
|
||||
sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
|
||||
@@ -996,6 +1000,15 @@ nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
|
||||
* such that the server throws away the clientid before
|
||||
* receiving the SetClientIDConfirm.
|
||||
*/
|
||||
/*
|
||||
* Must be done here while locked and before calling
|
||||
* nfsrpc_setclient().
|
||||
*/
|
||||
if (nfs_exchangeboot == 0) {
|
||||
nfs_exchangeboot = nfsboottime.tv_sec;
|
||||
if (nfs_exchangeboot == 0)
|
||||
nfs_exchangeboot = arc4random();
|
||||
}
|
||||
if (clp->nfsc_renew > 0)
|
||||
clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
|
||||
else
|
||||
|
||||
@@ -567,7 +567,7 @@ nfs_mountdiskless(char *path,
|
||||
struct vnode **vpp, struct mount *mp)
|
||||
{
|
||||
struct sockaddr *nam;
|
||||
int dirlen, error;
|
||||
int dirlen, error, minvers;
|
||||
char *dirpath;
|
||||
|
||||
/*
|
||||
@@ -580,9 +580,12 @@ nfs_mountdiskless(char *path,
|
||||
else
|
||||
dirlen = 0;
|
||||
nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
|
||||
minvers = 0;
|
||||
if ((args->flags & NFSMNT_NFSV4) != 0)
|
||||
minvers = -1;
|
||||
if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
|
||||
NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO,
|
||||
NFS_DEFAULT_NEGNAMETIMEO, 0, 0, NULL, 0)) != 0) {
|
||||
NFS_DEFAULT_NEGNAMETIMEO, minvers, 0, NULL, 0)) != 0) {
|
||||
printf("nfs_mountroot: mount %s on /: %d\n", path, error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
+25
-12
@@ -119,6 +119,10 @@ nfs_parse_options(const char *envopts, struct nfs_args *nd)
|
||||
else if (strcmp(o, "nfsv3") == 0) {
|
||||
nd->flags &= ~NFSMNT_NFSV4;
|
||||
nd->flags |= NFSMNT_NFSV3;
|
||||
} else if (strcmp(o, "nfsv4") == 0) {
|
||||
nd->flags &= ~NFSMNT_NFSV3;
|
||||
nd->flags |= NFSMNT_NFSV4;
|
||||
nd->sotype = SOCK_STREAM;
|
||||
} else if (strcmp(o, "tcp") == 0)
|
||||
nd->sotype = SOCK_STREAM;
|
||||
else if (strcmp(o, "udp") == 0)
|
||||
@@ -271,24 +275,33 @@ nfs_setup_diskless(void)
|
||||
return;
|
||||
}
|
||||
nd3->root_saddr.sin_port = htons(NFS_PORT);
|
||||
fhlen = decode_nfshandle("boot.nfsroot.nfshandle",
|
||||
&nd3->root_fh[0], NFSX_V3FHMAX);
|
||||
if (fhlen == 0) {
|
||||
printf("nfs_diskless: no NFS handle\n");
|
||||
return;
|
||||
if ((cp = kern_getenv("boot.nfsroot.options")) != NULL) {
|
||||
nfs_parse_options(cp, &nd3->root_args);
|
||||
freeenv(cp);
|
||||
}
|
||||
if (fhlen != nd3->root_fhsize) {
|
||||
printf("nfs_diskless: bad NFS handle len=%d\n", fhlen);
|
||||
return;
|
||||
if ((nd3->root_args.flags & NFSMNT_NFSV4) == 0) {
|
||||
fhlen = decode_nfshandle("boot.nfsroot.nfshandle",
|
||||
&nd3->root_fh[0], NFSX_V3FHMAX);
|
||||
if (fhlen == 0) {
|
||||
printf("nfs_diskless: no NFS handle\n");
|
||||
return;
|
||||
}
|
||||
if (fhlen != nd3->root_fhsize) {
|
||||
printf("nfs_diskless: bad NFS handle len=%d\n",
|
||||
fhlen);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* For NFSv4, the file handle is derived from the
|
||||
* boot.nfsroot.path during mounting by NFSv4.
|
||||
*/
|
||||
nd3->root_fhsize = 0;
|
||||
}
|
||||
if ((cp = kern_getenv("boot.nfsroot.path")) != NULL) {
|
||||
strncpy(nd3->root_hostnam, cp, MNAMELEN - 1);
|
||||
freeenv(cp);
|
||||
}
|
||||
if ((cp = kern_getenv("boot.nfsroot.options")) != NULL) {
|
||||
nfs_parse_options(cp, &nd3->root_args);
|
||||
freeenv(cp);
|
||||
}
|
||||
|
||||
nfs_diskless_valid = 3;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user