svn commit: r299848 - head/sys/fs/nfsclient

Edward Tomasz Napierala trasz at FreeBSD.org
Sun May 15 08:35:00 UTC 2016


Author: trasz
Date: Sun May 15 08:34:59 2016
New Revision: 299848
URL: https://svnweb.freebsd.org/changeset/base/299848

Log:
  Make it possible to reroot into NFS.  This means one can have
  eg an NFSv4 root over WiFi: boot from md_root (small rootfs image
  preloaded by loader(8)), setup WiFi, and then reroot into the actual
  root, over NFS.
  
  Note that it's currently limited to NFSv4, and due to problems with
  nfsuserd(8) it requres a workaround on the server side: one needs
  to set the vfs.nfsd.enable_stringtouid=1 sysctl and not run nfsuserd(8)
  on either the server or the client side.
  
  Reviewed by:	rmacklem@
  MFC after:	1 month
  Relnotes:	yes
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D6347

Modified:
  head/sys/fs/nfsclient/nfs_clvfsops.c

Modified: head/sys/fs/nfsclient/nfs_clvfsops.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clvfsops.c	Sun May 15 07:02:34 2016	(r299847)
+++ head/sys/fs/nfsclient/nfs_clvfsops.c	Sun May 15 08:34:59 2016	(r299848)
@@ -741,6 +741,101 @@ static const char *nfs_opts[] = { "from"
     NULL };
 
 /*
+ * Parse the "from" mountarg, passed by the generic mount(8) program
+ * or the mountroot code.  This is used when rerooting into NFS.
+ *
+ * Note that the "hostname" is actually a "hostname:/share/path" string.
+ */
+static int
+nfs_mount_parse_from(struct vfsoptlist *opts, char **hostnamep,
+    struct sockaddr_in **sinp, char *dirpath, size_t dirpathsize, int *dirlenp)
+{
+	char nam[MNAMELEN + 1];
+	char *delimp, *hostp, *spec;
+	int error, have_bracket = 0, offset, rv, speclen;
+	struct sockaddr_in *sin;
+	size_t len;
+
+	error = vfs_getopt(opts, "from", (void **)&spec, &speclen);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * This part comes from sbin/mount_nfs/mount_nfs.c:getnfsargs().
+	 */
+        if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL &&
+            *(delimp + 1) == ':') {
+                hostp = spec + 1;
+                spec = delimp + 2;
+                have_bracket = 1;
+        } else if ((delimp = strrchr(spec, ':')) != NULL) {
+                hostp = spec;
+                spec = delimp + 1;
+        } else if ((delimp = strrchr(spec, '@')) != NULL) {
+                printf("%s: path at server syntax is deprecated, "
+		    "use server:path\n", __func__);
+                hostp = delimp + 1;
+        } else {
+                printf("%s: no <host>:<dirpath> nfs-name\n", __func__);
+                return (EINVAL);
+        }
+        *delimp = '\0';
+
+        /*
+         * If there has been a trailing slash at mounttime it seems
+         * that some mountd implementations fail to remove the mount
+         * entries from their mountlist while unmounting.
+         */
+        for (speclen = strlen(spec);
+                speclen > 1 && spec[speclen - 1] == '/';
+                speclen--)
+                spec[speclen - 1] = '\0';
+        if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
+                printf("%s: %s:%s: name too long", __func__, hostp, spec);
+                return (EINVAL);
+        }
+	/* Make both '@' and ':' notations equal */
+	if (*hostp != '\0') {
+		len = strlen(hostp);
+		offset = 0;
+		if (have_bracket)
+			nam[offset++] = '[';
+		memmove(nam + offset, hostp, len);
+		if (have_bracket)
+			nam[len + offset++] = ']';
+		nam[len + offset++] = ':';
+		memmove(nam + len + offset, spec, speclen);
+		nam[len + speclen + offset] = '\0';
+	}
+
+	/*
+	 * XXX: IPv6
+	 */
+	sin = malloc(sizeof(*sin), M_SONAME, M_WAITOK);
+	rv = inet_pton(AF_INET, hostp, &sin->sin_addr);
+	if (rv != 1) {
+		printf("%s: cannot parse '%s', inet_pton() returned %d\n",
+		    __func__, hostp, rv);
+		free(sin, M_SONAME);
+		return (EINVAL);
+	}
+
+	sin->sin_len = sizeof(*sin);
+	sin->sin_family = AF_INET;
+	/*
+	 * XXX: hardcoded port number.
+	 */
+	sin->sin_port = htons(2049);
+
+	*hostnamep = strdup(nam, M_NEWNFSMNT);
+	*sinp = sin;
+	strlcpy(dirpath, spec, dirpathsize);
+	*dirlenp = strlen(dirpath);
+
+	return (0);
+}
+
+/*
  * VFS Operations.
  *
  * mount system call
@@ -785,17 +880,20 @@ nfs_mount(struct mount *mp)
 	int nametimeo = NFS_DEFAULT_NAMETIMEO;
 	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
 	int minvers = 0;
-	int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
+	int dirlen, has_nfs_args_opt, has_nfs_from_opt,
+	    krbnamelen, srvkrbnamelen;
 	size_t hstlen;
 
 	has_nfs_args_opt = 0;
+	has_nfs_from_opt = 0;
 	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
 		error = EINVAL;
 		goto out;
 	}
 
 	td = curthread;
-	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) {
+	if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS &&
+	    nfs_diskless_valid != 0) {
 		error = nfs_mountroot(mp);
 		goto out;
 	}
@@ -1135,6 +1233,19 @@ nfs_mount(struct mount *mp)
 		    args.addrlen);
 		if (error != 0)
 			goto out;
+	} else if (nfs_mount_parse_from(mp->mnt_optnew,
+	    &args.hostname, (struct sockaddr_in **)&nam, dirpath,
+	    sizeof(dirpath), &dirlen) == 0) {
+		has_nfs_from_opt = 1;
+		bcopy(args.hostname, hst, MNAMELEN);
+		hst[MNAMELEN - 1] = '\0';
+
+		/*
+		 * This only works with NFSv4 for now.
+		 */
+		args.fhsize = 0;
+		args.flags |= NFSMNT_NFSV4;
+		args.sotype = SOCK_STREAM;
 	} else {
 		if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh,
 		    &args.fhsize) == 0) {
@@ -1174,13 +1285,16 @@ nfs_mount(struct mount *mp)
 		krbname[0] = '\0';
 	krbnamelen = strlen(krbname);
 
-	if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0)
-		strlcpy(dirpath, name, sizeof (dirpath));
-	else
-		dirpath[0] = '\0';
-	dirlen = strlen(dirpath);
+	if (has_nfs_from_opt == 0) {
+		if (vfs_getopt(mp->mnt_optnew,
+		    "dirpath", (void **)&name, NULL) == 0)
+			strlcpy(dirpath, name, sizeof (dirpath));
+		else
+			dirpath[0] = '\0';
+		dirlen = strlen(dirpath);
+	}
 
-	if (has_nfs_args_opt == 0) {
+	if (has_nfs_args_opt == 0 && has_nfs_from_opt == 0) {
 		if (vfs_getopt(mp->mnt_optnew, "addr",
 		    (void **)&args.addr, &args.addrlen) == 0) {
 			if (args.addrlen > SOCK_MAXADDRLEN) {


More information about the svn-src-head mailing list