From owner-svn-src-head@freebsd.org Sun May 15 08:35:00 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 76526B3276C; Sun, 15 May 2016 08:35:00 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 43FDA1259; Sun, 15 May 2016 08:35:00 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u4F8YxX6054450; Sun, 15 May 2016 08:34:59 GMT (envelope-from trasz@FreeBSD.org) Received: (from trasz@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u4F8YxAU054449; Sun, 15 May 2016 08:34:59 GMT (envelope-from trasz@FreeBSD.org) Message-Id: <201605150834.u4F8YxAU054449@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: trasz set sender to trasz@FreeBSD.org using -f From: Edward Tomasz Napierala Date: Sun, 15 May 2016 08:34:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r299848 - head/sys/fs/nfsclient X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 15 May 2016 08:35:00 -0000 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 : 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) {