From 4897093455a2bf08f3db3a1132cc2f6f5484d77c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 08:03:02 +0200 Subject: Adding upstream version 1:2.6.4. Signed-off-by: Daniel Baumann --- utils/mount/nfsmount.c | 873 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 873 insertions(+) create mode 100644 utils/mount/nfsmount.c (limited to 'utils/mount/nfsmount.c') diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c new file mode 100644 index 0000000..3d95da9 --- /dev/null +++ b/utils/mount/nfsmount.c @@ -0,0 +1,873 @@ +/* + * nfsmount.c -- Linux NFS mount + * Copyright (C) 1993 Rick Sladkey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port + * numbers to be specified on the command line. + * + * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler : + * Omit the call to connect() for Linux version 1.3.11 or later. + * + * Wed Oct 1 23:55:28 1997: Dick Streefland + * Implemented the "bg", "fg" and "retry" mount options for NFS. + * + * 1999-02-22 Arkadiusz Miskiewicz + * - added Native Language Support + * + * Modified by Olaf Kirch and Trond Myklebust for new NFS code, + * plus NFSv3 stuff. + * + * 2006-06-06 Amit Gud + * - Moved with modifcations to nfs-utils/utils/mount from util-linux/mount. + */ + +/* + * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xcommon.h" +#include "mount.h" +#include "nfs_mount.h" +#include "mount_constants.h" +#include "nls.h" +#include "error.h" +#include "network.h" +#include "version.h" + +#ifdef HAVE_RPCSVC_NFS_PROT_H +#include +#else +#include +#define nfsstat nfs_stat +#endif + +#ifndef NFS_PORT +#define NFS_PORT 2049 +#endif +#ifndef NFS_FHSIZE +#define NFS_FHSIZE 32 +#endif + +#ifndef HAVE_INET_ATON +#define inet_aton(a,b) (0) +#endif + +typedef dirpath mnt2arg_t; +typedef dirpath mnt3arg_t; +typedef dirpath mntarg_t; + +typedef struct fhstatus mnt2res_t; +typedef struct mountres3 mnt3res_t; +typedef union { + mnt2res_t nfsv2; + mnt3res_t nfsv3; +} mntres_t; + +extern int nfs_mount_data_version; +extern char *progname; +extern int verbose; +extern int sloppy; + +static inline enum clnt_stat +nfs3_mount(CLIENT *clnt, mnt3arg_t *mnt3arg, mnt3res_t *mnt3res) +{ + return clnt_call(clnt, MOUNTPROC3_MNT, + (xdrproc_t) xdr_dirpath, (caddr_t) mnt3arg, + (xdrproc_t) xdr_mountres3, (caddr_t) mnt3res, + TIMEOUT); +} + +static inline enum clnt_stat +nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg, mnt2res_t *mnt2res) +{ + return clnt_call(clnt, MOUNTPROC_MNT, + (xdrproc_t) xdr_dirpath, (caddr_t) mnt2arg, + (xdrproc_t) xdr_fhstatus, (caddr_t) mnt2res, + TIMEOUT); +} + +static int +nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server, + mntarg_t *mntarg, mntres_t *mntres) +{ + CLIENT *clnt; + enum clnt_stat stat; + int msock; + + if (!probe_bothports(mnt_server, nfs_server)) + goto out_bad; + + clnt = mnt_openclnt(mnt_server, &msock); + if (!clnt) + goto out_bad; + /* make pointers in xdr_mountres3 NULL so + * that xdr_array allocates memory for us + */ + memset(mntres, 0, sizeof(*mntres)); + switch (mnt_server->pmap.pm_vers) { + case 3: + stat = nfs3_mount(clnt, mntarg, &mntres->nfsv3); + break; + case 2: + case 1: + stat = nfs2_mount(clnt, mntarg, &mntres->nfsv2); + break; + default: + goto out_bad; + } + if (stat != RPC_SUCCESS) { + clnt_geterr(clnt, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = stat; + } + mnt_closeclnt(clnt, msock); + if (stat == RPC_SUCCESS) + return 1; + out_bad: + return 0; +} + +static int +parse_options(char *old_opts, struct nfs_mount_data *data, + int *bg, int *retry, clnt_addr_t *mnt_server, + clnt_addr_t *nfs_server, char *new_opts, const int opt_size) +{ + struct sockaddr_in *mnt_saddr = &mnt_server->saddr; + struct pmap *mnt_pmap = &mnt_server->pmap; + struct pmap *nfs_pmap = &nfs_server->pmap; + int len; + char *opt, *opteq, *p, *opt_b, *tmp_opts; + char *mounthost = NULL; + char cbuf[128]; + int open_quote = 0; + + data->flags = 0; + *bg = 0; + + len = strlen(new_opts); + tmp_opts = xstrdup(old_opts); + for (p=tmp_opts, opt_b=NULL; p && *p; p++) { + if (!opt_b) + opt_b = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in a quoted block */ + if (*p == ',') + *p = '\0'; /* terminate the option item */ + if (*p == '\0' || *(p+1) == '\0') { + opt = opt_b; /* opt is useful now */ + opt_b = NULL; + } + else + continue; /* still somewhere in the option item */ + + if (strlen(opt) >= sizeof(cbuf)) + goto bad_parameter; + if ((opteq = strchr(opt, '=')) && isdigit(opteq[1])) { + int val = atoi(opteq + 1); + *opteq = '\0'; + if (!strcmp(opt, "rsize")) + data->rsize = val; + else if (!strcmp(opt, "wsize")) + data->wsize = val; + else if (!strcmp(opt, "timeo")) + data->timeo = val; + else if (!strcmp(opt, "retrans")) + data->retrans = val; + else if (!strcmp(opt, "acregmin")) + data->acregmin = val; + else if (!strcmp(opt, "acregmax")) + data->acregmax = val; + else if (!strcmp(opt, "acdirmin")) + data->acdirmin = val; + else if (!strcmp(opt, "acdirmax")) + data->acdirmax = val; + else if (!strcmp(opt, "actimeo")) { + data->acregmin = val; + data->acregmax = val; + data->acdirmin = val; + data->acdirmax = val; + } + else if (!strcmp(opt, "retry")) + *retry = val; + else if (!strcmp(opt, "port")) + nfs_pmap->pm_port = val; + else if (!strcmp(opt, "mountport")) + mnt_pmap->pm_port = val; + else if (!strcmp(opt, "mountprog")) + mnt_pmap->pm_prog = val; + else if (!strcmp(opt, "mountvers")) + mnt_pmap->pm_vers = val; + else if (!strcmp(opt, "mounthost")) + mounthost=xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,")); + else if (!strcmp(opt, "nfsprog")) + nfs_pmap->pm_prog = val; + else if (!strcmp(opt, "nfsvers") || + !strcmp(opt, "vers")) { + nfs_pmap->pm_vers = val; + opt = "nfsvers"; +#if NFS_MOUNT_VERSION >= 2 + } else if (!strcmp(opt, "namlen")) { + if (nfs_mount_data_version >= 2) + data->namlen = val; + else if (sloppy) + continue; + else + goto bad_parameter; +#endif + } else if (!strcmp(opt, "addr")) { + /* ignore */; + continue; + } else if (sloppy) + continue; + else + goto bad_parameter; + sprintf(cbuf, "%s=%s,", opt, opteq+1); + } else if (opteq) { + *opteq = '\0'; + if (!strcmp(opt, "proto")) { + if (!strcmp(opteq+1, "udp")) { + nfs_pmap->pm_prot = IPPROTO_UDP; + mnt_pmap->pm_prot = IPPROTO_UDP; +#if NFS_MOUNT_VERSION >= 2 + data->flags &= ~NFS_MOUNT_TCP; + } else if (!strcmp(opteq+1, "tcp") && + nfs_mount_data_version > 2) { + nfs_pmap->pm_prot = IPPROTO_TCP; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; +#endif + } else if (sloppy) + continue; + else + goto bad_parameter; +#if NFS_MOUNT_VERSION >= 5 + } else if (!strcmp(opt, "sec")) { + char *secflavor = opteq+1; + /* see RFC 2623 */ + if (nfs_mount_data_version < 5) { + printf(_("Warning: ignoring sec=%s option\n"), + secflavor); + continue; + } else if (!strcmp(secflavor, "none")) + data->pseudoflavor = AUTH_NONE; + else if (!strcmp(secflavor, "sys")) + data->pseudoflavor = AUTH_SYS; + else if (!strcmp(secflavor, "krb5")) + data->pseudoflavor = AUTH_GSS_KRB5; + else if (!strcmp(secflavor, "krb5i")) + data->pseudoflavor = AUTH_GSS_KRB5I; + else if (!strcmp(secflavor, "krb5p")) + data->pseudoflavor = AUTH_GSS_KRB5P; + else if (sloppy) + continue; + else { + printf(_("Warning: Unrecognized security flavor %s.\n"), + secflavor); + goto bad_parameter; + } + data->flags |= NFS_MOUNT_SECFLAVOUR; +#endif + } else if (!strcmp(opt, "mounthost")) + mounthost=xstrndup(opteq+1, + strcspn(opteq+1," \t\n\r,")); + else if (!strcmp(opt, "context")) { + char *context = opteq + 1; + int ctxlen = strlen(context); + + if (ctxlen > NFS_MAX_CONTEXT_LEN) { + nfs_error(_("context parameter exceeds" + " limit of %d"), + NFS_MAX_CONTEXT_LEN); + goto bad_parameter; + } + /* The context string is in the format of + * "system_u:object_r:...". We only want + * the context str between the quotes. + */ + if (*context == '"') + strncpy(data->context, context+1, + ctxlen-2); + else + strncpy(data->context, context, + NFS_MAX_CONTEXT_LEN); + } else if (sloppy) + continue; + else + goto bad_parameter; + sprintf(cbuf, "%s=%s,", opt, opteq+1); + } else { + int val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + if (!strcmp(opt, "bg")) + *bg = val; + else if (!strcmp(opt, "fg")) + *bg = !val; + else if (!strcmp(opt, "soft")) { + data->flags &= ~NFS_MOUNT_SOFT; + if (val) + data->flags |= NFS_MOUNT_SOFT; + } else if (!strcmp(opt, "hard")) { + data->flags &= ~NFS_MOUNT_SOFT; + if (!val) + data->flags |= NFS_MOUNT_SOFT; + } else if (!strcmp(opt, "intr")) { + data->flags &= ~NFS_MOUNT_INTR; + if (val) + data->flags |= NFS_MOUNT_INTR; + } else if (!strcmp(opt, "posix")) { + data->flags &= ~NFS_MOUNT_POSIX; + if (val) + data->flags |= NFS_MOUNT_POSIX; + } else if (!strcmp(opt, "cto")) { + data->flags &= ~NFS_MOUNT_NOCTO; + if (!val) + data->flags |= NFS_MOUNT_NOCTO; + } else if (!strcmp(opt, "ac")) { + data->flags &= ~NFS_MOUNT_NOAC; + if (!val) + data->flags |= NFS_MOUNT_NOAC; +#if NFS_MOUNT_VERSION >= 2 + } else if (!strcmp(opt, "tcp")) { + data->flags &= ~NFS_MOUNT_TCP; + if (val) { + if (nfs_mount_data_version < 2) + goto bad_option; + nfs_pmap->pm_prot = IPPROTO_TCP; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; + } else { + mnt_pmap->pm_prot = IPPROTO_UDP; + nfs_pmap->pm_prot = IPPROTO_UDP; + } + } else if (!strcmp(opt, "udp")) { + data->flags &= ~NFS_MOUNT_TCP; + if (!val) { + if (nfs_mount_data_version < 2) + goto bad_option; + nfs_pmap->pm_prot = IPPROTO_TCP; + mnt_pmap->pm_prot = IPPROTO_TCP; + data->flags |= NFS_MOUNT_TCP; + } else { + nfs_pmap->pm_prot = IPPROTO_UDP; + mnt_pmap->pm_prot = IPPROTO_UDP; + } +#endif +#if NFS_MOUNT_VERSION >= 3 + } else if (!strcmp(opt, "lock")) { + data->flags &= ~NFS_MOUNT_NONLM; + if (!val) { + if (nfs_mount_data_version < 3) + goto bad_option; + data->flags |= NFS_MOUNT_NONLM; + } +#endif +#if NFS_MOUNT_VERSION >= 4 + } else if (!strcmp(opt, "broken_suid")) { + data->flags &= ~NFS_MOUNT_BROKEN_SUID; + if (val) { + if (nfs_mount_data_version < 4) + goto bad_option; + data->flags |= NFS_MOUNT_BROKEN_SUID; + } + } else if (!strcmp(opt, "acl")) { + data->flags &= ~NFS_MOUNT_NOACL; + if (!val) + data->flags |= NFS_MOUNT_NOACL; + } else if (!strcmp(opt, "rdirplus")) { + data->flags &= ~NFS_MOUNT_NORDIRPLUS; + if (!val) + data->flags |= NFS_MOUNT_NORDIRPLUS; + } else if (!strcmp(opt, "sharecache")) { + data->flags &= ~NFS_MOUNT_UNSHARED; + if (!val) + data->flags |= NFS_MOUNT_UNSHARED; +#endif + } else { + bad_option: + if (sloppy) + continue; + nfs_error(_("%s: Unsupported nfs mount option:" + " %s%s"), progname, + val ? "" : "no", opt); + goto out_bad; + } + sprintf(cbuf, val ? "%s," : "no%s,", opt); + } + len += strlen(cbuf); + if (len >= opt_size) { + nfs_error(_("%s: excessively long option argument"), + progname); + goto out_bad; + } + strcat(new_opts, cbuf); + } + /* See if the nfs host = mount host. */ + if (mounthost) { + if (!nfs_gethostbyname(mounthost, mnt_saddr)) + goto out_bad; + *mnt_server->hostname = mounthost; + } + free(tmp_opts); + return 1; + bad_parameter: + nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt); + out_bad: + free(tmp_opts); + free(mounthost); + return 0; +} + +static int nfsmnt_check_compat(const struct pmap *nfs_pmap, + const struct pmap *mnt_pmap) +{ + unsigned int max_nfs_vers = (nfs_mount_data_version >= 4) ? 3 : 2; + unsigned int max_mnt_vers = (nfs_mount_data_version >= 4) ? 3 : 2; + + if (nfs_pmap->pm_vers == 4) { + nfs_error(_("%s: Please use '-t nfs4' " + "instead of '-o vers=4'"), progname); + goto out_bad; + } + + if (nfs_pmap->pm_vers) { + if (nfs_pmap->pm_vers > max_nfs_vers || nfs_pmap->pm_vers < 2) { + nfs_error(_("%s: NFS version %ld is not supported"), + progname, nfs_pmap->pm_vers); + goto out_bad; + } + } + + if (mnt_pmap->pm_vers > max_mnt_vers) { + nfs_error(_("%s: NFS mount version %ld is not supported"), + progname, mnt_pmap->pm_vers); + goto out_bad; + } + + return 1; + +out_bad: + return 0; +} + +int +nfsmount(const char *spec, const char *node, int flags, + char **extra_opts, int fake, int running_bg) +{ + char hostdir[1024]; + char *hostname, *dirname, *old_opts, *mounthost = NULL; + char new_opts[1024], cbuf[1024]; + static struct nfs_mount_data data; + int val; + static int doonce = 0; + + clnt_addr_t mnt_server = { + .hostname = &mounthost + }; + clnt_addr_t nfs_server = { + .hostname = &hostname + }; + struct sockaddr_in *nfs_saddr = &nfs_server.saddr; + struct pmap *mnt_pmap = &mnt_server.pmap, + *nfs_pmap = &nfs_server.pmap; + struct pmap save_mnt, save_nfs; + + int fsock = -1; + + mntres_t mntres; + + struct stat statbuf; + char *s; + int bg, retry; + int retval = EX_FAIL; + time_t t; + time_t prevt; + time_t timeout; + + if (strlen(spec) >= sizeof(hostdir)) { + nfs_error(_("%s: excessively long host:dir argument"), + progname); + goto fail; + } + strcpy(hostdir, spec); + if ((s = strchr(hostdir, ':'))) { + hostname = hostdir; + dirname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + if ((s = strchr(hostdir, ','))) { + *s = '\0'; + nfs_error(_("%s: warning: " + "multiple hostnames not supported"), + progname); + } + } else { + nfs_error(_("%s: directory to mount not in host:dir format"), + progname); + goto fail; + } + + if (!nfs_gethostbyname(hostname, nfs_saddr)) + goto fail; + mounthost = hostname; + memcpy (&mnt_server.saddr, nfs_saddr, sizeof (mnt_server.saddr)); + + /* add IP address to mtab options for use when unmounting */ + + s = inet_ntoa(nfs_saddr->sin_addr); + old_opts = *extra_opts; + if (!old_opts) + old_opts = ""; + + /* Set default options. + * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to + * let the kernel decide. + * timeo is filled in after we know whether it'll be TCP or UDP. */ + memset(&data, 0, sizeof(data)); + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; +#if NFS_MOUNT_VERSION >= 2 + data.namlen = NAME_MAX; +#endif + + bg = 0; + retry = -1; + + memset(mnt_pmap, 0, sizeof(*mnt_pmap)); + mnt_pmap->pm_prog = MOUNTPROG; + memset(nfs_pmap, 0, sizeof(*nfs_pmap)); + nfs_pmap->pm_prog = NFS_PROGRAM; + + /* parse options */ + new_opts[0] = 0; + if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server, + new_opts, sizeof(new_opts))) + goto fail; + if (!nfsmnt_check_compat(nfs_pmap, mnt_pmap)) + goto fail; + + if (retry == -1) { + if (bg) + retry = 10000; /* 10000 mins == ~1 week*/ + else + retry = 2; /* 2 min default on fg mounts */ + } + +#ifdef NFS_MOUNT_DEBUG + printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"), + data.rsize, data.wsize, data.timeo, data.retrans); + printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"), + data.acregmin, data.acregmax, data.acdirmin, data.acdirmax); + printf(_("port = %lu, bg = %d, retry = %d, flags = %.8x\n"), + nfs_pmap->pm_port, bg, retry, data.flags); + printf(_("mountprog = %lu, mountvers = %lu, nfsprog = %lu, nfsvers = %lu\n"), + mnt_pmap->pm_prog, mnt_pmap->pm_vers, + nfs_pmap->pm_prog, nfs_pmap->pm_vers); + printf(_("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d"), + (data.flags & NFS_MOUNT_SOFT) != 0, + (data.flags & NFS_MOUNT_INTR) != 0, + (data.flags & NFS_MOUNT_POSIX) != 0, + (data.flags & NFS_MOUNT_NOCTO) != 0, + (data.flags & NFS_MOUNT_NOAC) != 0); +#if NFS_MOUNT_VERSION >= 2 + printf(_(", tcp = %d"), + (data.flags & NFS_MOUNT_TCP) != 0); +#endif +#if NFS_MOUNT_VERSION >= 4 + printf(_(", noacl = %d"), (data.flags & NFS_MOUNT_NOACL) != 0); +#endif +#if NFS_MOUNT_VERSION >= 5 + printf(_(", sec = %u"), data.pseudoflavor); + printf(_(", readdirplus = %d"), (data.flags & NFS_MOUNT_NORDIRPLUS) != 0); +#endif + printf("\n"); +#endif + + data.version = nfs_mount_data_version; + + if (flags & MS_REMOUNT) + goto out_ok; + + /* create mount deamon client */ + + /* + * The following loop implements the mount retries. On the first + * call, "running_bg" is 0. When the mount times out, and the + * "bg" option is set, the exit status EX_BG will be returned. + * For a backgrounded mount, there will be a second call by the + * child process with "running_bg" set to 1. + * + * The case where the mount point is not present and the "bg" + * option is set, is treated as a timeout. This is done to + * support nested mounts. + * + * The "retry" count specified by the user is the number of + * minutes to retry before giving up. + * + * Only the first error message will be displayed. + */ + timeout = time(NULL) + 60 * retry; + prevt = 0; + t = 30; + val = 1; + + memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs)); + memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt)); + for (;;) { + if (bg && stat(node, &statbuf) == -1) { + /* no mount point yet - sleep */ + if (running_bg) { + sleep(val); /* 1, 2, 4, 8, 16, 30, ... */ + val *= 2; + if (val > 30) + val = 30; + } + } else { + int stat; + /* be careful not to use too many CPU cycles */ + if (t - prevt < 30) + sleep(30); + + stat = nfs_call_mount(&mnt_server, &nfs_server, + &dirname, &mntres); + if (stat) + break; + memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); + memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); + prevt = t; + } + if (!bg) { + switch(rpc_createerr.cf_stat){ + case RPC_TIMEDOUT: + break; + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; + /* FALLTHRU */ + default: + rpc_mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + t = time(NULL); + if (t >= timeout) { + rpc_mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + rpc_mount_errors(*nfs_server.hostname, 1, bg); + continue; + } + if (!running_bg) { + if (retry > 0) + retval = EX_BG; + goto fail; + } + t = time(NULL); + if (t >= timeout) { + rpc_mount_errors(*nfs_server.hostname, 0, bg); + goto fail; + } + if (doonce++ < 1) + rpc_mount_errors(*nfs_server.hostname, 1, bg); + } + + if (mnt_pmap->pm_vers <= 2) { + if (mntres.nfsv2.fhs_status != 0) { + nfs_error(_("%s: %s:%s failed, reason given by server: %s"), + progname, hostname, dirname, + nfs_strerror(mntres.nfsv2.fhs_status)); + goto fail; + } + memcpy(data.root.data, + (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); +#if NFS_MOUNT_VERSION >= 4 + data.root.size = NFS_FHSIZE; + memcpy(data.old_root.data, + (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle, + NFS_FHSIZE); +#endif + } else { +#if NFS_MOUNT_VERSION >= 4 + mountres3_ok *mountres; + fhandle3 *fhandle; + int i, n_flavors, *flavor, yum = 0; + if (mntres.nfsv3.fhs_status != 0) { + nfs_error(_("%s: %s:%s failed, reason given by server: %s"), + progname, hostname, dirname, + nfs_strerror(mntres.nfsv3.fhs_status)); + goto fail; + } +#if NFS_MOUNT_VERSION >= 5 + mountres = &mntres.nfsv3.mountres3_u.mountinfo; + n_flavors = mountres->auth_flavors.auth_flavors_len; + if (n_flavors <= 0) + goto noauth_flavors; + + flavor = mountres->auth_flavors.auth_flavors_val; + for (i = 0; i < n_flavors; ++i) { + /* + * Per RFC2623, section 2.7, we should prefer the + * flavour listed first. + * If no flavour requested, use the first simple + * flavour that is offered. + */ + if (! (data.flags & NFS_MOUNT_SECFLAVOUR) && + (flavor[i] == AUTH_SYS || + flavor[i] == AUTH_NONE)) { + data.pseudoflavor = flavor[i]; + data.flags |= NFS_MOUNT_SECFLAVOUR; + } + if (flavor[i] == data.pseudoflavor) + yum = 1; +#ifdef NFS_MOUNT_DEBUG + printf(_("auth flavor %d: %d\n"), i, flavor[i]); +#endif + } + if (!yum) { + nfs_error(_("%s: %s:%s failed, security flavor " + "not supported"), + progname, hostname, dirname); + /* server has registered us in rmtab, send umount */ + nfs_call_umount(&mnt_server, &dirname); + goto fail; + } +noauth_flavors: +#endif + fhandle = &mntres.nfsv3.mountres3_u.mountinfo.fhandle; + memset(data.old_root.data, 0, NFS_FHSIZE); + memset(&data.root, 0, sizeof(data.root)); + data.root.size = fhandle->fhandle3_len; + memcpy(data.root.data, + (char *) fhandle->fhandle3_val, + fhandle->fhandle3_len); + + data.flags |= NFS_MOUNT_VER3; +#endif + } + + if (nfs_mount_data_version == 1) { + /* create nfs socket for kernel */ + if (nfs_pmap->pm_prot == IPPROTO_TCP) + fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else + fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fsock < 0) { + perror(_("nfs socket")); + goto fail; + } + if (bindresvport(fsock, 0) < 0) { + perror(_("nfs bindresvport")); + goto fail; + } + } + +#ifdef NFS_MOUNT_DEBUG + printf(_("using port %lu for nfs deamon\n"), nfs_pmap->pm_port); +#endif + nfs_saddr->sin_port = htons(nfs_pmap->pm_port); + /* + * connect() the socket for kernels 1.3.10 and below only, + * to avoid problems with multihomed hosts. + * --Swen + */ + if (linux_version_code() <= MAKE_VERSION(1, 3, 10) && fsock != -1 + && connect(fsock, (struct sockaddr *) nfs_saddr, + sizeof (*nfs_saddr)) < 0) { + perror(_("nfs connect")); + goto fail; + } + +#if NFS_MOUNT_VERSION >= 2 + if (nfs_pmap->pm_prot == IPPROTO_TCP) + data.flags |= NFS_MOUNT_TCP; + else + data.flags &= ~NFS_MOUNT_TCP; +#endif + + /* prepare data structure for kernel */ + + data.fd = fsock; + memcpy((char *) &data.addr, (char *) nfs_saddr, sizeof(data.addr)); + strncpy(data.hostname, hostname, sizeof(data.hostname)-1); + + out_ok: + /* Ensure we have enough padding for the following strcat()s */ + if (strlen(new_opts) + strlen(s) + 30 >= sizeof(new_opts)) { + nfs_error(_("%s: excessively long option argument"), + progname); + goto fail; + } + + snprintf(cbuf, sizeof(cbuf)-1, "addr=%s", s); + strcat(new_opts, cbuf); + + *extra_opts = xstrdup(new_opts); + + if (!fake && !(data.flags & NFS_MOUNT_NONLM)) { + if (!start_statd()) { + nfs_error(_("%s: rpc.statd is not running but is " + "required for remote locking.\n" + " Either use '-o nolock' to keep " + "locks local, or start statd."), + progname); + goto fail; + } + } + + if (!fake) { + if (mount(spec, node, "nfs", + flags & ~(MS_USER|MS_USERS), &data)) { + mount_error(spec, node, errno); + goto fail; + } + } + + return EX_SUCCESS; + + /* abort */ + fail: + if (fsock != -1) + close(fsock); + return retval; +} -- cgit v1.2.3