diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c index a653a0e5c8e4..f5b442d337b2 100644 --- a/sys/nfsclient/nfs_vfsops.c +++ b/sys/nfsclient/nfs_vfsops.c @@ -721,8 +721,10 @@ static const char *nfs_opts[] = { "from", "nfs_args", "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union", "noclusterr", "noclusterw", "multilabel", "acls", "force", "update", "async", "dumbtimer", "noconn", "nolockd", "intr", "rdirplus", "resvport", - "readdirsize", "soft", "hard", "mntudp", "tcp", "wsize", "rsize", + "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize", "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", + "deadthresh", "hostname", "timeout", "addr", "fh", "nfsv3", + "maxgroups", NULL }; /* @@ -763,12 +765,19 @@ nfs_mount(struct mount *mp, struct thread *td) .acdirmin = NFS_MINDIRATTRTIMO, .acdirmax = NFS_MAXDIRATTRTIMO, }; - int error; + int error, ret, has_nfs_args_opt; + int has_addr_opt, has_fh_opt, has_hostname_opt; struct sockaddr *nam; struct vnode *vp; char hst[MNAMELEN]; size_t len; u_char nfh[NFSX_V3FHMAX]; + char *opt; + + has_nfs_args_opt = 0; + has_addr_opt = 0; + has_fh_opt = 0; + has_hostname_opt = 0; if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { error = EINVAL; @@ -780,12 +789,219 @@ nfs_mount(struct mount *mp, struct thread *td) goto out; } - error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, sizeof args); - if (error) - goto out; + /* + * The old mount_nfs program passed the struct nfs_args + * from userspace to kernel. The new mount_nfs program + * passes string options via nmount() from userspace to kernel + * and we populate the struct nfs_args in the kernel. + */ + if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) { + error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, + sizeof args); + if (error) + goto out; - if (args.version != NFS_ARGSVERSION) { - error = EPROGMISMATCH; + if (args.version != NFS_ARGSVERSION) { + error = EPROGMISMATCH; + goto out; + } + has_nfs_args_opt = 1; + } + + if (vfs_getopt(mp->mnt_optnew, "dumbtimer", NULL, NULL) == 0) + args.flags |= NFSMNT_DUMBTIMR; + if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0) + args.flags |= NFSMNT_NOCONN; + if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0) + args.flags |= NFSMNT_NOCONN; + if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0) + args.flags |= NFSMNT_NOLOCKD; + if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0) + args.flags &= ~NFSMNT_NOLOCKD; + if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0) + args.flags |= NFSMNT_INT; + if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0) + args.flags |= NFSMNT_RDIRPLUS; + if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0) + args.flags |= NFSMNT_RESVPORT; + if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0) + args.flags &= ~NFSMNT_RESVPORT; + if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0) + args.flags |= NFSMNT_SOFT; + if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0) + args.flags &= ~NFSMNT_SOFT; + if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0) + args.sotype = SOCK_DGRAM; + if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0) + args.sotype = SOCK_DGRAM; + if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0) + args.sotype = SOCK_STREAM; + if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0) + args.flags |= NFSMNT_NFSV3; + if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { + if (opt == NULL) { + vfs_mount_error(mp, "illegal readdirsize"); + error = EINVAL; + goto out; + } + ret = sscanf(opt, "%d", &args.readdirsize); + if (ret != 1 || args.readdirsize <= 0) { + vfs_mount_error(mp, "illegal readdirsize: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_READDIRSIZE; + } + if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) { + if (opt == NULL) { + vfs_mount_error(mp, "illegal readahead"); + error = EINVAL; + goto out; + } + ret = sscanf(opt, "%d", &args.readahead); + if (ret != 1 || args.readahead <= 0) { + vfs_mount_error(mp, "illegal readahead: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_READAHEAD; + } + if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) { + if (opt == NULL) { + vfs_mount_error(mp, "illegal wsize"); + error = EINVAL; + goto out; + } + ret = sscanf(opt, "%d", &args.wsize); + if (ret != 1 || args.wsize <= 0) { + vfs_mount_error(mp, "illegal wsize: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_WSIZE; + } + if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) { + if (opt == NULL) { + vfs_mount_error(mp, "illegal rsize"); + error = EINVAL; + goto out; + } + ret = sscanf(opt, "%d", &args.rsize); + if (ret != 1 || args.rsize <= 0) { + vfs_mount_error(mp, "illegal wsize: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_RSIZE; + } + if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) { + if (opt == NULL) { + vfs_mount_error(mp, "illegal retrans"); + error = EINVAL; + goto out; + } + ret = sscanf(opt, "%d", &args.retrans); + if (ret != 1 || args.retrans <= 0) { + vfs_mount_error(mp, "illegal retrans: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_RETRANS; + } + if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.acregmin); + if (ret != 1 || args.acregmin <= 0) { + vfs_mount_error(mp, "illegal acregmin: %s", + opt); + error = EINVAL; + goto out; + } + } + if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.acregmax); + if (ret != 1 || args.acregmax <= 0) { + vfs_mount_error(mp, "illegal acregmax: %s", + opt); + error = EINVAL; + goto out; + } + } + if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.acdirmin); + if (ret != 1 || args.acdirmin <= 0) { + vfs_mount_error(mp, "illegal acdirmin: %s", + opt); + error = EINVAL; + goto out; + } + } + if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.acdirmax); + if (ret != 1 || args.acdirmax <= 0) { + vfs_mount_error(mp, "illegal acdirmax: %s", + opt); + error = EINVAL; + goto out; + } + } + if (vfs_getopt(mp->mnt_optnew, "deadthresh", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.deadthresh); + if (ret != 1 || args.deadthresh <= 0) { + vfs_mount_error(mp, "illegal deadthresh: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_DEADTHRESH; + } + if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.timeo); + if (ret != 1 || args.timeo <= 0) { + vfs_mount_error(mp, "illegal timeout: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_TIMEO; + } + if (vfs_getopt(mp->mnt_optnew, "maxgroups", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &args.maxgrouplist); + if (ret != 1 || args.timeo <= 0) { + vfs_mount_error(mp, "illegal maxgroups: %s", + opt); + error = EINVAL; + goto out; + } + args.flags |= NFSMNT_MAXGRPS; + } + if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr, + &args.addrlen) == 0) { + has_addr_opt = 1; + if (args.addrlen > SOCK_MAXADDRLEN) { + error = ENAMETOOLONG; + goto out; + } + MALLOC(nam, struct sockaddr *, args.addrlen, M_SONAME, + M_WAITOK); + bcopy(args.addr, nam, args.addrlen); + nam->sa_len = args.addrlen; + } + if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh, + &args.fhsize) == 0) { + has_fh_opt = 1; + } + if (vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname, + NULL) == 0) { + has_hostname_opt = 1; + } + if (args.hostname == NULL) { + vfs_mount_error(mp, "Invalid hostname"); + error = EINVAL; goto out; } @@ -819,23 +1035,38 @@ nfs_mount(struct mount *mp, struct thread *td) */ if (nfs_ip_paranoia == 0) args.flags |= NFSMNT_NOCONN; - if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) { - error = EINVAL; - goto out; + + if (has_nfs_args_opt) { + /* + * In the 'nfs_args' case, the pointers in the args + * structure are in userland - we copy them in here. + */ + if (!has_fh_opt) { + error = copyin((caddr_t)args.fh, (caddr_t)nfh, + args.fhsize); + if (error) { + goto out; + } + args.fh = nfh; + } + if (!has_hostname_opt) { + error = copyinstr(args.hostname, hst, MNAMELEN-1, &len); + if (error) { + goto out; + } + bzero(&hst[len], MNAMELEN - len); + args.hostname = hst; + } + if (!has_addr_opt) { + /* sockargs() call must be after above copyin() calls */ + error = getsockaddr(&nam, (caddr_t)args.addr, + args.addrlen); + if (error) { + goto out; + } + } } - error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize); - if (error) - goto out; - error = copyinstr(args.hostname, hst, MNAMELEN-1, &len); - if (error) - goto out; - bzero(&hst[len], MNAMELEN - len); - /* sockargs() call must be after above copyin() calls */ - error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen); - if (error) - goto out; - args.fh = nfh; - error = mountnfs(&args, mp, nam, hst, &vp, td->td_ucred); + error = mountnfs(&args, mp, nam, args.hostname, &vp, td->td_ucred); out: if (!error) { MNT_ILOCK(mp);