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@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) { _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"