]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/nfsmount.c
Currently nfs4mount() sets the retry value to 10000 on both fg and bg
[nfs-utils.git] / utils / mount / nfsmount.c
index 4049e662ca2d81c16f6049b27b16e058a51fc682..a9dd91764fbb7276521245885133c928640966f7 100644 (file)
@@ -23,7 +23,7 @@
  *
  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  * - added Native Language Support
- * 
+ *
  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
  * plus NFSv3 stuff.
  *
  * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <ctype.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -48,7 +52,6 @@
 #include <rpc/pmap_clnt.h>
 #include <sys/socket.h>
 #include <sys/time.h>
-#include <sys/utsname.h>
 #include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <paths.h>
 #include <syslog.h>
 
-#include "conn.h"
 #include "xcommon.h"
 #include "mount.h"
-#include "nfsumount.h"
 #include "nfs_mount.h"
 #include "mount_constants.h"
 #include "nls.h"
-
-#ifdef HAVE_RPCSVC_NFS_PROT_H
-#include <rpcsvc/nfs_prot.h>
-#else
-#include <linux/nfs.h>
-#define nfsstat nfs_stat
-#endif
+#include "error.h"
+#include "network.h"
 
 #ifndef NFS_PORT
 #define NFS_PORT 2049
 #define NFS_FHSIZE 32
 #endif
 
-static char *nfs_strerror(int stat);
-
-#define MAKE_VERSION(p,q,r)    (65536*(p) + 256*(q) + (r))
-#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
-#define MAX_MNTPROT ((nfs_mount_version >= 4) ? 3 : 2)
-#define HAVE_RELIABLE_TCP (nfs_mount_version >= 4)
-
 #ifndef HAVE_INET_ATON
 #define inet_aton(a,b) (0)
 #endif
@@ -101,385 +90,12 @@ typedef union {
        mnt3res_t nfsv3;
 } mntres_t;
 
-static char errbuf[BUFSIZ];
-static char *erreob = &errbuf[BUFSIZ];
+extern int nfs_mount_data_version;
+extern char *progname;
 extern int verbose;
 extern int sloppy;
 
-/* Convert RPC errors into strings */
-int rpc_strerror(int);
-int rpc_strerror(int spos)
-{
-       int cf_stat = rpc_createerr.cf_stat; 
-       int pos=0, cf_errno = rpc_createerr.cf_error.re_errno;
-       char *ptr, *estr = clnt_sperrno(cf_stat);
-       char *tmp;
-
-       if (estr) {
-               if ((ptr = index(estr, ':')))
-                       estr = ++ptr;
-
-               tmp = &errbuf[spos];
-               if (cf_stat == RPC_SYSTEMERROR)
-                       pos = snprintf(tmp, (erreob - tmp), 
-                               "System Error: %s", strerror(cf_errno));
-               else
-                       pos = snprintf(tmp, (erreob - tmp), "RPC Error:%s", estr);
-       }
-       return (pos);
-}
-void mount_errors(char *, int, int);
-void mount_errors(char *server, int will_retry, int bg)
-{
-       int pos = 0;
-       char *tmp;
-       static int onlyonce = 0;
-
-       tmp = &errbuf[pos];
-       if (bg) 
-               pos = snprintf(tmp, (erreob - tmp), 
-                       "mount to NFS server '%s' failed: ", server);
-       else
-               pos = snprintf(tmp, (erreob - tmp), 
-                       "mount: mount to NFS server '%s' failed: ", server);
-
-       tmp = &errbuf[pos];
-       if (rpc_createerr.cf_stat == RPC_TIMEDOUT) {
-               pos = snprintf(tmp, (erreob - tmp), "timed out %s", 
-                       will_retry ? "(retrying)" : "(giving up)");
-       } else {
-               pos += rpc_strerror(pos);
-               tmp = &errbuf[pos];
-               if (bg) {
-                       pos = snprintf(tmp, (erreob - tmp), " %s",
-                               will_retry ? "(retrying)" : "(giving up)");
-               }
-       }
-       if (bg) {
-               if (onlyonce++ < 1)
-                       openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
-               syslog(LOG_ERR, "%s.", errbuf);
-       } else
-               fprintf(stderr, "%s.\n", errbuf);
-}
-
-/* Define the order in which to probe for UDP/TCP services */
-enum plist {
-       use_tcp = 0,
-       udp_tcp,
-       udp_only,
-};
-static const u_int *
-proto_probelist(enum plist list)
-{
-       static const u_int probe_udp_tcp[] = { IPPROTO_UDP, IPPROTO_TCP, 0 };
-       static const u_int probe_both[] = { IPPROTO_TCP, IPPROTO_UDP, 0 };
-       static const u_int probe_udponly[] = { IPPROTO_UDP, 0 };
-
-       if (list == use_tcp)
-               return probe_both;
-       if (list == udp_tcp)
-               return probe_udp_tcp;
-       return probe_udponly;
-}
-
-/* Define the order in which NFS versions are probed on portmapper */
-static const u_long *
-nfs_probelist(const int vers)
-{
-       static const u_long nfs2_probe[] = { 2, 0};
-       static const u_long nfs3_probe[] = { 3, 2, 0};
-       switch (vers) {
-       case 3:
-               return nfs3_probe;
-       default:
-               return nfs2_probe;
-       }
-}
-
-/* Define the order in which Mountd versions are probed on portmapper */
-static const u_long *
-mnt_probelist(const int vers)
-{
-       static const u_long mnt1_probe[] = { 1, 2, 0 };
-       static const u_long mnt3_probe[] = { 3, 1, 2, 0 };
-       switch (vers) {
-       case 3:
-               return mnt3_probe;
-       default:
-               return mnt1_probe;
-       }
-}
-
-static int
-linux_version_code(void) {
-       struct utsname my_utsname;
-       int p, q, r;
-
-       if (uname(&my_utsname) == 0) {
-               p = atoi(strtok(my_utsname.release, "."));
-               q = atoi(strtok(NULL, "."));
-               r = atoi(strtok(NULL, "."));
-               return MAKE_VERSION(p,q,r);
-       }
-       return 0;
-}
-
-/*
- * Unfortunately, the kernel prints annoying console messages
- * in case of an unexpected nfs mount version (instead of
- * just returning some error).  Therefore we'll have to try
- * and figure out what version the kernel expects.
- *
- * Variables:
- *     NFS_MOUNT_VERSION: these nfsmount sources at compile time
- *     nfs_mount_version: version this source and running kernel can handle
- */
-int nfs_mount_version = NFS_MOUNT_VERSION;
-
-int
-find_kernel_nfs_mount_version(void) {
-       static int kernel_version = -1;
-       int mnt_version = NFS_MOUNT_VERSION;
-
-       if (kernel_version == -1)
-               kernel_version = linux_version_code();
-
-       if (kernel_version) {
-            if (kernel_version < MAKE_VERSION(2,1,32))
-                 mnt_version = 1;
-            else if (kernel_version < MAKE_VERSION(2,2,18))
-                 mnt_version = 3;
-            else if (kernel_version < MAKE_VERSION(2,3,0))
-                 mnt_version = 4; /* since 2.2.18pre9 */
-            else if (kernel_version < MAKE_VERSION(2,3,99))
-                 mnt_version = 3;
-            else if (kernel_version < MAKE_VERSION(2,6,3))
-                 mnt_version = 4;
-            else
-                 mnt_version = 6;
-       }
-       if (mnt_version > NFS_MOUNT_VERSION)
-            mnt_version = NFS_MOUNT_VERSION;
-       return mnt_version;
-}
-
-int nfs_gethostbyname(const char *, struct sockaddr_in *);
-int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
-{
-       struct hostent *hp;
-
-       saddr->sin_family = AF_INET;
-       if (!inet_aton(hostname, &saddr->sin_addr)) {
-               if ((hp = gethostbyname(hostname)) == NULL) {
-                       fprintf(stderr, _("mount: can't get address for %s\n"),
-                               hostname);
-                       return 0;
-               } else {
-                       if (hp->h_length > sizeof(*saddr)) {
-                               fprintf(stderr,
-                                       _("mount: got bad hp->h_length\n"));
-                               hp->h_length = sizeof(*saddr);
-                       }
-                       memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
-               }
-       }
-       return 1;
-}
-
-/*
- * getport() is very similar to pmap_getport() with
- * the exception this version uses a non-reserve ports 
- * instead of reserve ports since reserve ports
- * are not needed for pmap requests.
- */
-static u_short
-getport(
-       struct sockaddr_in *saddr, 
-       u_long prog, 
-       u_long vers, 
-       u_int prot)
-{
-       u_short port;
-       int    socket;
-       CLIENT *clnt = NULL;
-       struct pmap parms;
-       enum clnt_stat stat;
-
-       saddr->sin_port = htons (PMAPPORT);
-       socket = get_socket(saddr, prot, FALSE, FALSE);
-
-       switch (prot) {
-       case IPPROTO_UDP:
-               clnt = clntudp_bufcreate(saddr,
-                                        PMAPPROG, PMAPVERS, TIMEOUT, &socket,
-                                        UDPMSGSIZE, UDPMSGSIZE);
-               break;
-       case IPPROTO_TCP:
-               clnt = clnttcp_create(saddr,
-                       PMAPPROG, PMAPVERS, &socket, 50, 500);
-               break;
-       }
-       if (clnt != NULL) {
-               parms.pm_prog = prog;
-               parms.pm_vers = vers;
-               parms.pm_prot = prot;
-               parms.pm_port = 0;    /* not needed or used */
-
-               stat = clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap,
-                       (caddr_t)&parms, (xdrproc_t)xdr_u_short, (caddr_t)&port, TIMEOUT);
-               if (stat) {
-                       clnt_geterr(clnt, &rpc_createerr.cf_error);
-                       rpc_createerr.cf_stat = stat;
-               }
-               clnt_destroy(clnt);
-               if (stat != RPC_SUCCESS)
-                       port = 0;
-               else if (port == 0)
-                       rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
-       }
-       if (socket != 1)
-               close(socket);
-
-       return port;
-}
-
-/*
- * Use the portmapper to discover whether or not the service we want is
- * available. The lists 'versions' and 'protos' define ordered sequences
- * of service versions and udp/tcp protocols to probe for.
- */
-static int
-probe_port(clnt_addr_t *server, 
-          const u_long *versions,
-          const u_int *protos)
-{
-       struct sockaddr_in *saddr = &server->saddr;
-       struct pmap *pmap = &server->pmap;
-       const u_long prog = pmap->pm_prog, *p_vers;
-       const u_int prot = (u_int)pmap->pm_prot,
-               *p_prot;
-       const u_short port = (u_short) pmap->pm_port;
-       u_long vers = pmap->pm_vers;
-       u_short p_port;
-       p_prot = prot ? &prot : protos;
-       p_vers = vers ? &vers : versions;
-       rpc_createerr.cf_stat = 0;
-       for (;;) {
-               saddr->sin_port = htons(PMAPPORT);
-               p_port = getport(saddr, prog, *p_vers, *p_prot);
-               if (p_port) {
-                       if (!port || port == p_port) {
-                               saddr->sin_port = htons(p_port);
-                               if (verbose) {
-                                       fprintf(stderr, 
-                                               "mount: trying %s prog %ld vers %ld prot %s port %d\n", 
-                                               inet_ntoa(saddr->sin_addr), prog, *p_vers,
-                                               *p_prot == IPPROTO_UDP ? "udp" : "tcp", p_port);
-                               }
-                               if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
-                                       goto out_ok;
-                               if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
-                                       goto out_bad;
-                       }
-               }
-               if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) 
-                       goto out_bad;
-
-               if (!prot) {
-                       if (*++p_prot)
-                               continue;
-                       p_prot = protos;
-               }
-               if (vers == pmap->pm_vers) {
-                       p_vers = versions;
-                       vers = 0;
-               }
-               if (vers || !*++p_vers)
-                       break;
-       }
-out_bad:
-       return 0;
-
- out_ok:
-       if (!vers)
-               pmap->pm_vers = *p_vers;
-       if (!prot)
-               pmap->pm_prot = *p_prot;
-       if (!port)
-               pmap->pm_port = p_port;
-       rpc_createerr.cf_stat = 0;
-       return 1;
-}
-
-static int
-probe_nfsport(clnt_addr_t *nfs_server)
-{
-       const struct pmap *pmap = &nfs_server->pmap;
-       const u_long *probe_vers;
-       const u_int *probe_prot;
-
-       if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
-               return 1;
-       probe_vers = nfs_probelist(MAX_NFSPROT);
-       probe_prot = proto_probelist(HAVE_RELIABLE_TCP ? use_tcp : udp_only);
-       return probe_port(nfs_server, probe_vers, probe_prot);
-}
-
-int probe_mntport(clnt_addr_t *mnt_server)
-{
-       const struct pmap *pmap = &mnt_server->pmap;
-       const u_long *probe_vers;
-       const u_int *probe_prot;
-
-       if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
-               return 1;
-       probe_vers = mnt_probelist(MAX_MNTPROT);
-       probe_prot = proto_probelist(HAVE_RELIABLE_TCP ? udp_tcp : udp_only);
-       return probe_port(mnt_server, probe_vers, probe_prot);
-}
-
-static int
-probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
-{
-       struct pmap *nfs_pmap = &nfs_server->pmap;
-       struct pmap *mnt_pmap = &mnt_server->pmap;
-       struct pmap save_nfs, save_mnt;
-       int res;
-       const u_long *probe_vers;
-
-       if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
-               nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
-       else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
-               mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
-       if (nfs_pmap->pm_vers)
-               goto version_fixed;
-       memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
-       memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
-       for (probe_vers = mnt_probelist(MAX_MNTPROT); *probe_vers; probe_vers++) {
-               nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
-               if ((res = probe_nfsport(nfs_server) != 0)) {
-                       mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
-                       if ((res = probe_mntport(mnt_server)) != 0)
-                               return 1;
-                       memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
-               }
-               switch (rpc_createerr.cf_stat) {
-               case RPC_PROGVERSMISMATCH:
-               case RPC_PROGNOTREGISTERED:
-                       break;
-               default:
-                       goto out_bad;
-               }
-               memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
-       }
- out_bad:
-       return 0;
- version_fixed:
-       if (!probe_nfsport(nfs_server))
-               goto out_bad;
-       return probe_mntport(mnt_server);
-}
+extern int linux_version_code(void);
 
 static inline enum clnt_stat
 nfs3_mount(CLIENT *clnt, mnt3arg_t *mnt3arg, mnt3res_t *mnt3res)
@@ -578,7 +194,6 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                if ((opteq = strchr(opt, '=')) && isdigit(opteq[1])) {
                        int val = atoi(opteq + 1);      
                        *opteq = '\0';
-/* printf("opt=%s\n", opt); */
                        if (!strcmp(opt, "rsize"))
                                data->rsize = val;
                        else if (!strcmp(opt, "wsize"))
@@ -621,7 +236,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                opt = "nfsvers";
 #if NFS_MOUNT_VERSION >= 2
                        } else if (!strcmp(opt, "namlen")) {
-                               if (nfs_mount_version >= 2)
+                               if (nfs_mount_data_version >= 2)
                                        data->namlen = val;
                                else if (sloppy)
                                        continue;
@@ -645,7 +260,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
 #if NFS_MOUNT_VERSION >= 2
                                        data->flags &= ~NFS_MOUNT_TCP;
                                } else if (!strcmp(opteq+1, "tcp") &&
-                                          nfs_mount_version > 2) {
+                                          nfs_mount_data_version > 2) {
                                        nfs_pmap->pm_prot = IPPROTO_TCP;
                                        mnt_pmap->pm_prot = IPPROTO_TCP;
                                        data->flags |= NFS_MOUNT_TCP;
@@ -658,8 +273,9 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                        } else if (!strcmp(opt, "sec")) {
                                char *secflavor = opteq+1;
                                /* see RFC 2623 */
-                               if (nfs_mount_version < 5) {
-                                       printf(_("Warning: ignoring sec=%s option\n"), secflavor);
+                               if (nfs_mount_data_version < 5) {
+                                       printf(_("Warning: ignoring sec=%s option\n"),
+                                                       secflavor);
                                        continue;
                                } else if (!strcmp(secflavor, "none"))
                                        data->pseudoflavor = AUTH_NONE;
@@ -700,8 +316,9 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                int ctxlen = strlen(context);
 
                                if (ctxlen > NFS_MAX_CONTEXT_LEN) {
-                                       printf(_("context parameter exceeds limit of %d\n"),
-                                                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
@@ -725,9 +342,9 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                val = 0;
                                opt += 2;
                        }
-                       if (!strcmp(opt, "bg")) 
+                       if (!strcmp(opt, "bg"))
                                *bg = val;
-                       else if (!strcmp(opt, "fg")) 
+                       else if (!strcmp(opt, "fg"))
                                *bg = !val;
                        else if (!strcmp(opt, "soft")) {
                                data->flags &= ~NFS_MOUNT_SOFT;
@@ -757,7 +374,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                        } else if (!strcmp(opt, "tcp")) {
                                data->flags &= ~NFS_MOUNT_TCP;
                                if (val) {
-                                       if (nfs_mount_version < 2)
+                                       if (nfs_mount_data_version < 2)
                                                goto bad_option;
                                        nfs_pmap->pm_prot = IPPROTO_TCP;
                                        mnt_pmap->pm_prot = IPPROTO_TCP;
@@ -769,7 +386,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                        } else if (!strcmp(opt, "udp")) {
                                data->flags &= ~NFS_MOUNT_TCP;
                                if (!val) {
-                                       if (nfs_mount_version < 2)
+                                       if (nfs_mount_data_version < 2)
                                                goto bad_option;
                                        nfs_pmap->pm_prot = IPPROTO_TCP;
                                        mnt_pmap->pm_prot = IPPROTO_TCP;
@@ -783,7 +400,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                        } else if (!strcmp(opt, "lock")) {
                                data->flags &= ~NFS_MOUNT_NONLM;
                                if (!val) {
-                                       if (nfs_mount_version < 3)
+                                       if (nfs_mount_data_version < 3)
                                                goto bad_option;
                                        data->flags |= NFS_MOUNT_NONLM;
                                }
@@ -792,7 +409,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                        } else if (!strcmp(opt, "broken_suid")) {
                                data->flags &= ~NFS_MOUNT_BROKEN_SUID;
                                if (val) {
-                                       if (nfs_mount_version < 4)
+                                       if (nfs_mount_data_version < 4)
                                                goto bad_option;
                                        data->flags |= NFS_MOUNT_BROKEN_SUID;
                                }
@@ -800,20 +417,30 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                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;
-                               printf(_("Unsupported nfs mount option: "
-                                        "%s%s\n"), val ? "" : "no", opt);
+                               nfs_error(_("%s: Unsupported nfs mount option:"
+                                               " %s%s"), progname,
+                                               val ? "" : "no", opt);
                                goto out_bad;
                        }
-                       sprintf(cbuf, val ? "%s,":"no%s,", opt);
+                       sprintf(cbuf, val ? "%s," : "no%s,", opt);
                }
                len += strlen(cbuf);
                if (len >= opt_size) {
-                       printf(_("mount: excessively long option argument\n"));
+                       nfs_error(_("%s: excessively long option argument"),
+                                       progname);
                        goto out_bad;
                }
                strcat(new_opts, cbuf);
@@ -826,38 +453,46 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
        }
        return 1;
  bad_parameter:
-       printf(_("Bad nfs mount parameter: %s\n"), opt);
+       nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt);
  out_bad:
        return 0;
 }
 
-static inline int
-nfsmnt_check_compat(const struct pmap *nfs_pmap, const struct pmap *mnt_pmap)
+static int nfsmnt_check_compat(const struct pmap *nfs_pmap,
+                               const struct pmap *mnt_pmap)
 {
-       if (nfs_pmap->pm_vers && 
-               (nfs_pmap->pm_vers > MAX_NFSPROT || nfs_pmap->pm_vers < 2)) {
-               if (nfs_pmap->pm_vers == 4)
-                       fprintf(stderr, _("'vers=4' is not supported.  "
-                               "Use '-t nfs4' instead.\n"));
-               else
-                       fprintf(stderr, _("NFS version %ld is not supported.\n"), 
-                               nfs_pmap->pm_vers);
+       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 (mnt_pmap->pm_vers > MAX_MNTPROT) {
-               fprintf(stderr, _("NFS mount version %ld s not supported.\n"), 
-                       mnt_pmap->pm_vers);
+
+       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:
+
+out_bad:
        return 0;
 }
 
 int
-nfsmount(const char *spec, const char *node, int *flags,
-        char **extra_opts, char **mount_opts,
-        int running_bg, int *need_statd)
+nfsmount(const char *spec, const char *node, int flags,
+        char **extra_opts, int fake, int running_bg)
 {
        static char *prev_bg_host;
        char hostdir[1024];
@@ -870,7 +505,7 @@ nfsmount(const char *spec, const char *node, int *flags,
        clnt_addr_t mnt_server = { &mounthost, };
        clnt_addr_t nfs_server = { &hostname, };
        struct sockaddr_in *nfs_saddr = &nfs_server.saddr;
-       struct pmap  *mnt_pmap = &mnt_server.pmap, 
+       struct pmap  *mnt_pmap = &mnt_server.pmap,
                     *nfs_pmap = &nfs_server.pmap;
        struct pmap  save_mnt, save_nfs;
 
@@ -886,11 +521,9 @@ nfsmount(const char *spec, const char *node, int *flags,
        time_t prevt;
        time_t timeout;
 
-       nfs_mount_version = find_kernel_nfs_mount_version();
-
        if (strlen(spec) >= sizeof(hostdir)) {
-               fprintf(stderr, _("mount: "
-                                 "excessively long host:dir argument\n"));
+               nfs_error(_("%s: excessively long host:dir argument"),
+                               progname);
                goto fail;
        }
        strcpy(hostdir, spec);
@@ -902,14 +535,13 @@ nfsmount(const char *spec, const char *node, int *flags,
                   until they can be fully supported. (mack@sgi.com) */
                if ((s = strchr(hostdir, ','))) {
                        *s = '\0';
-                       fprintf(stderr,
-                               _("mount: warning: "
-                                 "multiple hostnames not supported\n"));
+                       nfs_error(_("%s: warning: "
+                                 "multiple hostnames not supported"),
+                                       progname);
                }
        } else {
-               fprintf(stderr,
-                       _("mount: "
-                         "directory to mount not in host:dir format\n"));
+               nfs_error(_("%s: directory to mount not in host:dir format"),
+                               progname);
                goto fail;
        }
 
@@ -939,7 +571,7 @@ nfsmount(const char *spec, const char *node, int *flags,
 #endif
 
        bg = 0;
-       retry = 10000;          /* 10000 minutes ~ 1 week */
+       retry = -1;
 
        memset(mnt_pmap, 0, sizeof(*mnt_pmap));
        mnt_pmap->pm_prog = MOUNTPROG;
@@ -953,44 +585,47 @@ nfsmount(const char *spec, const char *node, int *flags,
                goto fail;
        if (!nfsmnt_check_compat(nfs_pmap, mnt_pmap))
                goto fail;
-       
-       if (retry == 10000 && !bg)
-               retry = 2; /* reset for fg mounts */
-       
+
+       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",
+       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",
+       printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
               data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
-       printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
+       printf(_("port = %lu, bg = %d, retry = %d, flags = %.8x\n"),
               nfs_pmap->pm_port, bg, retry, data.flags);
-       printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
+       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 ",
+       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 ",
+       printf(_(", tcp = %d"),
               (data.flags & NFS_MOUNT_TCP) != 0);
 #endif
 #if NFS_MOUNT_VERSION >= 4
-       printf("noacl = %d ", (data.flags & NFS_MOUNT_NOACL) != 0);
+       printf(_(", noacl = %d"), (data.flags & NFS_MOUNT_NOACL) != 0);
 #endif
 #if NFS_MOUNT_VERSION >= 5
-       printf("sec = %u ", data.pseudoflavor);
+       printf(_(", sec = %u"), data.pseudoflavor);
+       printf(_(", readdirplus = %d"), (data.flags & NFS_MOUNT_NORDIRPLUS) != 0);
 #endif
        printf("\n");
 #endif
 
-       data.version = nfs_mount_version;
-       *mount_opts = (char *) &data;
+       data.version = nfs_mount_data_version;
 
-       if (*flags & MS_REMOUNT)
+       if (flags & MS_REMOUNT)
                goto out_ok;
 
        /*
@@ -1061,15 +696,15 @@ nfsmount(const char *spec, const char *node, int *flags,
                                if (errno == ETIMEDOUT)
                                        break;
                        default:
-                               mount_errors(*nfs_server.hostname, 0, bg);
+                               rpc_mount_errors(*nfs_server.hostname, 0, bg);
                        goto fail;
                        }
                        t = time(NULL);
                        if (t >= timeout) {
-                               mount_errors(*nfs_server.hostname, 0, bg);
+                               rpc_mount_errors(*nfs_server.hostname, 0, bg);
                                goto fail;
                        }
-                       mount_errors(*nfs_server.hostname, 1, bg);
+                       rpc_mount_errors(*nfs_server.hostname, 1, bg);
                        continue;
                }
                if (!running_bg) {
@@ -1080,19 +715,18 @@ nfsmount(const char *spec, const char *node, int *flags,
                }
                t = time(NULL);
                if (t >= timeout) {
-                       mount_errors(*nfs_server.hostname, 0, bg);
+                       rpc_mount_errors(*nfs_server.hostname, 0, bg);
                        goto fail;
                }
                if (doonce++ < 1)
-                       mount_errors(*nfs_server.hostname, 1, bg);
+                       rpc_mount_errors(*nfs_server.hostname, 1, bg);
        }
 
-       if (nfs_pmap->pm_vers == 2) {
+       if (mnt_pmap->pm_vers <= 2) {
                if (mntres.nfsv2.fhs_status != 0) {
-                       fprintf(stderr,
-                               _("mount: %s:%s failed, reason given by server: %s\n"),
-                               hostname, dirname,
-                               nfs_strerror(mntres.nfsv2.fhs_status));
+                       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,
@@ -1108,23 +742,25 @@ nfsmount(const char *spec, const char *node, int *flags,
 #if NFS_MOUNT_VERSION >= 4
                mountres3_ok *mountres;
                fhandle3 *fhandle;
-               int i, *flavor, yum = 0;
+               int i,  n_flavors, *flavor, yum = 0;
                if (mntres.nfsv3.fhs_status != 0) {
-                       fprintf(stderr,
-                               _("mount: %s:%s failed, reason given by server: %s\n"),
-                               hostname, dirname,
-                               nfs_strerror(mntres.nfsv3.fhs_status));
+                       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;
-               i = mountres->auth_flavors.auth_flavors_len;
-               if (i <= 0) 
+               n_flavors = mountres->auth_flavors.auth_flavors_len;
+               if (n_flavors <= 0)
                        goto noauth_flavors;
 
                flavor = mountres->auth_flavors.auth_flavors_val;
-               while (--i >= 0) {
-                       /* If no flavour requested, use first simple
+               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) &&
@@ -1136,15 +772,13 @@ nfsmount(const char *spec, const char *node, int *flags,
                        if (flavor[i] == data.pseudoflavor)
                                yum = 1;
 #ifdef NFS_MOUNT_DEBUG
-                       printf("auth flavor %d: %d\n",
-                               i, flavor[i]);
+                       printf(_("auth flavor %d: %d\n"), i, flavor[i]);
 #endif
                }
                if (!yum) {
-                       fprintf(stderr,
-                               "mount: %s:%s failed, "
-                               "security flavor not supported\n",
-                               hostname, dirname);
+                       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;
@@ -1163,7 +797,7 @@ noauth_flavors:
 #endif
        }
 
-       if (nfs_mount_version == 1) {
+       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);
@@ -1180,7 +814,7 @@ noauth_flavors:
        }
 
 #ifdef NFS_MOUNT_DEBUG
-       printf(_("using port %d for nfs deamon\n"), nfs_pmap->pm_port);
+       printf(_("using port %lu for nfs deamon\n"), nfs_pmap->pm_port);
 #endif
        nfs_saddr->sin_port = htons(nfs_pmap->pm_port);
        /*
@@ -1211,8 +845,8 @@ noauth_flavors:
  out_ok:
        /* Ensure we have enough padding for the following strcat()s */
        if (strlen(new_opts) + strlen(s) + 30 >= sizeof(new_opts)) {
-               fprintf(stderr, _("mount: "
-                                 "excessively long option argument\n"));
+               nfs_error(_("%s: excessively long option argument"),
+                               progname);
                goto fail;
        }
 
@@ -1220,8 +854,27 @@ noauth_flavors:
        strcat(new_opts, cbuf);
 
        *extra_opts = xstrdup(new_opts);
-       *need_statd = ! (data.flags & NFS_MOUNT_NONLM);
-       return 0;
+
+       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:
@@ -1229,62 +882,3 @@ noauth_flavors:
                close(fsock);
        return retval;
 }
-
-/*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
- *
- * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
- * "after #include <errno.h> the symbol errno is reserved for any use,
- *  it cannot even be used as a struct tag or field name".
- */
-
-#ifndef EDQUOT
-#define EDQUOT ENOSPC
-#endif
-
-static struct {
-       enum nfsstat stat;
-       int errnum;
-} nfs_errtbl[] = {
-       { NFS_OK,               0               },
-       { NFSERR_PERM,          EPERM           },
-       { NFSERR_NOENT,         ENOENT          },
-       { NFSERR_IO,            EIO             },
-       { NFSERR_NXIO,          ENXIO           },
-       { NFSERR_ACCES,         EACCES          },
-       { NFSERR_EXIST,         EEXIST          },
-       { NFSERR_NODEV,         ENODEV          },
-       { NFSERR_NOTDIR,        ENOTDIR         },
-       { NFSERR_ISDIR,         EISDIR          },
-#ifdef NFSERR_INVAL
-       { NFSERR_INVAL,         EINVAL          },      /* that Sun forgot */
-#endif
-       { NFSERR_FBIG,          EFBIG           },
-       { NFSERR_NOSPC,         ENOSPC          },
-       { NFSERR_ROFS,          EROFS           },
-       { NFSERR_NAMETOOLONG,   ENAMETOOLONG    },
-       { NFSERR_NOTEMPTY,      ENOTEMPTY       },
-       { NFSERR_DQUOT,         EDQUOT          },
-       { NFSERR_STALE,         ESTALE          },
-#ifdef EWFLUSH
-       { NFSERR_WFLUSH,        EWFLUSH         },
-#endif
-       /* Throw in some NFSv3 values for even more fun (HP returns these) */
-       { 71,                   EREMOTE         },
-
-       { -1,                   EIO             }
-};
-
-static char *nfs_strerror(int stat)
-{
-       int i;
-       static char buf[256];
-
-       for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-               if (nfs_errtbl[i].stat == stat)
-                       return strerror(nfs_errtbl[i].errnum);
-       }
-       sprintf(buf, _("unknown nfs status return value: %d"), stat);
-       return buf;
-}