]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/nfsmount.c
mount.nfs: Always preset nfs_mount_version
[nfs-utils.git] / utils / mount / nfsmount.c
index 507ccdb39902b50dc080eab1b27fe2613e4a7399..2747af4415e9c86755ef74b51213464795e6b25b 100644 (file)
@@ -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"
 
 #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)
+#define MAX_NFSPROT ((nfs_mount_data_version >= 4) ? 3 : 2)
+#define MAX_MNTPROT ((nfs_mount_data_version >= 4) ? 3 : 2)
+#define HAVE_RELIABLE_TCP (nfs_mount_data_version >= 4)
 
 #ifndef HAVE_INET_ATON
 #define inet_aton(a,b) (0)
@@ -101,66 +91,11 @@ typedef union {
        mnt3res_t nfsv3;
 } mntres_t;
 
-static char errbuf[BUFSIZ];
-static char *erreob = &errbuf[BUFSIZ];
+extern int nfs_mount_data_version;
 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);
-}
+extern int linux_version_code();
 
 /* Define the order in which to probe for UDP/TCP services */
 enum plist {
@@ -210,59 +145,6 @@ mnt_probelist(const int vers)
        }
 }
 
-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)
 {
@@ -292,21 +174,21 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
  * instead of reserve ports since reserve ports
  * are not needed for pmap requests.
  */
-static u_short
+u_short
 getport(
        struct sockaddr_in *saddr, 
        u_long prog, 
        u_long vers, 
        u_int prot)
 {
-       u_short port;
+       u_short port = 0;
        int    socket;
        CLIENT *clnt = NULL;
        struct pmap parms;
        enum clnt_stat stat;
 
        saddr->sin_port = htons (PMAPPORT);
-       socket = get_socket(saddr, prot, FALSE);
+       socket = get_socket(saddr, prot, FALSE, FALSE);
 
        switch (prot) {
        case IPPROTO_UDP:
@@ -547,15 +429,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 +502,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,21 +526,25 @@ 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) {
+                               if (nfs_mount_data_version < 5) {
                                        printf(_("Warning: ignoring sec=%s option\n"), secflavor);
                                        continue;
-                               } else if (!strcmp(secflavor, "sys"))
+                               } 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;
@@ -658,6 +564,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);
@@ -669,15 +577,27 @@ 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) {
+                                       printf(_("context parameter exceeds limit of %d\n"),
+                                                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 {
@@ -718,7 +638,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;
@@ -730,7 +650,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;
@@ -744,7 +664,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;
                                }
@@ -753,7 +673,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;
                                }
@@ -761,9 +681,19 @@ 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);
                                goto out_bad;
@@ -815,8 +745,8 @@ nfsmnt_check_compat(const struct pmap *nfs_pmap, const struct pmap *mnt_pmap)
 
 int
 nfsmount(const char *spec, const char *node, int *flags,
-        char **extra_opts, char **mount_opts, int *nfs_mount_vers,
-        int running_bg)
+        char **extra_opts, char **mount_opts,
+        int running_bg, int *need_statd)
 {
        static char *prev_bg_host;
        char hostdir[1024];
@@ -833,26 +763,18 @@ nfsmount(const char *spec, const char *node, int *flags,
                     *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"));
@@ -902,7 +824,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 */
@@ -949,11 +870,12 @@ 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;
+       data.version = nfs_mount_data_version;
        *mount_opts = (char *) &data;
 
        if (*flags & MS_REMOUNT)
@@ -1090,6 +1012,15 @@ nfsmount(const char *spec, const char *node, int *flags,
 
                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
@@ -1102,7 +1033,7 @@ nfsmount(const char *spec, const char *node, int *flags,
                                "mount: %s:%s failed, "
                                "security flavor not supported\n",
                                hostname, dirname);
-                       /* server has registered us in mtab, send umount */
+                       /* server has registered us in rmtab, send umount */
                        nfs_call_umount(&mnt_server, &dirname);
                        goto fail;
                }
@@ -1120,20 +1051,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
@@ -1143,7 +1076,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"));
@@ -1175,6 +1108,7 @@ noauth_flavors:
        strcat(new_opts, cbuf);
 
        *extra_opts = xstrdup(new_opts);
+       *need_statd = ! (data.flags & NFS_MOUNT_NONLM);
        return 0;
 
        /* abort */
@@ -1183,62 +1117,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;
-}