mount.nfs: fix more nits with error messages
[nfs-utils.git] / utils / mount / nfsmount.c
index b78daad..6371d77 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.
  *
@@ -48,7 +48,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 "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,384 +88,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);
-
-       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)
@@ -547,15 +162,31 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
        struct pmap *mnt_pmap = &mnt_server->pmap;
        struct pmap *nfs_pmap = &nfs_server->pmap;
        int len;
-       char *opt, *opteq;
+       char *opt, *opteq, *p, *opt_b;
        char *mounthost = NULL;
        char cbuf[128];
+       int open_quote = 0;
 
        data->flags = 0;
        *bg = 0;
 
        len = strlen(new_opts);
-       for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
+       for (p=old_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])) {
@@ -604,15 +235,19 @@ 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;
                                else
                                        goto bad_parameter;
 #endif
                        } else if (!strcmp(opt, "addr")) {
                                /* ignore */;
                                continue;
-                       } else
+                       } else if (sloppy)
+                               continue;
+                       else
                                goto bad_parameter;
                        sprintf(cbuf, "%s=%s,", opt, opteq+1);
                } else if (opteq) {
@@ -624,19 +259,22 @@ 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;
 #endif
-                               } else
+                               } 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_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;
@@ -660,6 +298,8 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                        data->pseudoflavor = AUTH_GSS_SPKMI;
                                else if (!strcmp(secflavor, "spkm3p"))
                                        data->pseudoflavor = AUTH_GSS_SPKMP;
+                               else if (sloppy)
+                                       continue;
                                else {
                                        printf(_("Warning: Unrecognized security flavor %s.\n"),
                                                secflavor);
@@ -671,15 +311,28 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
                                mounthost=xstrndup(opteq+1,
                                                   strcspn(opteq+1," \t\n\r,"));
                         else if (!strcmp(opt, "context")) {
-                               char *context = opteq + 1;
-                               
-                               if (strlen(context) > NFS_MAX_CONTEXT_LEN) {
-                                       printf(_("context parameter exceeds limit of %d\n"),
-                                                NFS_MAX_CONTEXT_LEN);
+                               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;
-                               }
-                               strncpy(data->context, context, NFS_MAX_CONTEXT_LEN);
-                       } else
+                               }
+                               /* 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 {
@@ -688,9 +341,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;
@@ -720,7 +373,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;
@@ -732,7 +385,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;
@@ -746,7 +399,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;
                                }
@@ -755,7 +408,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;
                                }
@@ -763,18 +416,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:
-                               printf(_("Unsupported nfs mount option: "
-                                        "%s%s\n"), val ? "" : "no", opt);
+                               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) {
-                       printf(_("mount: excessively long option argument\n"));
+                       nfs_error(_("%s: excessively long option argument"),
+                                       progname);
                        goto out_bad;
                }
                strcat(new_opts, cbuf);
@@ -787,77 +452,77 @@ 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 s 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 *nfs_mount_vers,
-        int running_bg)
+nfsmount(const char *spec, const char *node, int flags,
+        char **extra_opts, int fake)
 {
        static char *prev_bg_host;
        char hostdir[1024];
        char *hostname, *dirname, *old_opts, *mounthost = NULL;
        char new_opts[1024], cbuf[1024];
        static struct nfs_mount_data data;
-       int val;
+       int val, running_bg = 0;
        static int doonce = 0;
 
        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;
 
-       int fsock;
+       int fsock = -1;
 
        mntres_t mntres;
 
        struct stat statbuf;
        char *s;
        int bg, retry;
-       int retval;
+       int retval = EX_FAIL;
        time_t t;
        time_t prevt;
        time_t timeout;
 
-       /* The version to try is either specified or 0
-          In case it is 0 we tell the caller what we tried */
-       if (!*nfs_mount_vers)
-               *nfs_mount_vers = find_kernel_nfs_mount_version();
-       nfs_mount_version = *nfs_mount_vers;
-
-       retval = EX_FAIL;
-       fsock = -1;
        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);
@@ -869,14 +534,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;
        }
 
@@ -904,7 +568,6 @@ nfsmount(const char *spec, const char *node, int *flags,
 #if NFS_MOUNT_VERSION >= 2
        data.namlen     = NAME_MAX;
 #endif
-       data.pseudoflavor = AUTH_SYS;
 
        bg = 0;
        retry = 10000;          /* 10000 minutes ~ 1 week */
@@ -951,14 +614,14 @@ nfsmount(const char *spec, const char *node, int *flags,
 #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_version;
-       *mount_opts = (char *) &data;
+       data.version = nfs_mount_data_version;
 
-       if (*flags & MS_REMOUNT)
+       if (flags & MS_REMOUNT)
                goto out_ok;
 
        /*
@@ -1057,10 +720,9 @@ nfsmount(const char *spec, const char *node, int *flags,
 
        if (nfs_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,
@@ -1078,20 +740,28 @@ nfsmount(const char *spec, const char *node, int *flags,
                fhandle3 *fhandle;
                int i, *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) 
+               if (i <= 0)
                        goto noauth_flavors;
 
                flavor = mountres->auth_flavors.auth_flavors_val;
                while (--i >= 0) {
+                       /* If no flavour requested, use 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
@@ -1100,11 +770,10 @@ nfsmount(const char *spec, const char *node, int *flags,
 #endif
                }
                if (!yum) {
-                       fprintf(stderr,
-                               "mount: %s:%s failed, "
-                               "security flavor not supported\n",
-                               hostname, dirname);
-                       /* server has registered us in mtab, send umount */
+                       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;
                }
@@ -1122,20 +791,22 @@ noauth_flavors:
 #endif
        }
 
-       /* 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;
+       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 %d for nfs deamon\n"), nfs_pmap->pm_port);
 #endif
@@ -1145,7 +816,7 @@ noauth_flavors:
         * to avoid problems with multihomed hosts.
         * --Swen
         */
-       if (linux_version_code() <= 66314
+       if (linux_version_code() <= 0x01030a && fsock != -1
            && connect(fsock, (struct sockaddr *) nfs_saddr,
                       sizeof (*nfs_saddr)) < 0) {
                perror(_("nfs connect"));
@@ -1168,8 +839,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;
        }
 
@@ -1177,6 +848,26 @@ noauth_flavors:
        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 nolocks' 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 0;
 
        /* abort */
@@ -1185,62 +876,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;
-}