]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/stropts.c
text-based mount: Retry when server can't be reached
[nfs-utils.git] / utils / mount / stropts.c
index 7b63b4572128909dbe6bf61fd6084caef1a8e2b6..74224ffdad2a0e477b65b05748384c2d3d51f279 100644 (file)
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#include "sockaddr.h"
 #include "xcommon.h"
 #include "mount.h"
 #include "nls.h"
+#include "nfsrpc.h"
 #include "mount_constants.h"
 #include "stropts.h"
 #include "error.h"
@@ -81,7 +83,7 @@ struct nfsmount_info {
                                *node,          /* mounted-on dir */
                                *type;          /* "nfs" or "nfs4" */
        char                    *hostname;      /* server's hostname */
-       struct sockaddr_storage address;        /* server's address */
+       union nfs_sockaddr      address;
        socklen_t               salen;          /* size of server's address */
 
        struct mount_options    *options;       /* parsed mount options */
@@ -93,9 +95,10 @@ struct nfsmount_info {
                                child;          /* forked bg child? */
 };
 
-inline void set_default_version(struct nfsmount_info *mi);
 #ifdef MOUNT_CONFIG
-inline void set_default_version(struct nfsmount_info *mi)
+static void nfs_default_version(struct nfsmount_info *mi);
+
+static void nfs_default_version(struct nfsmount_info *mi)
 {
        extern unsigned long config_default_vers;
        /*
@@ -108,7 +111,7 @@ inline void set_default_version(struct nfsmount_info *mi)
        }
 }
 #else
-inline void set_default_version(struct nfsmount_info *mi) {}
+inline void nfs_default_version(struct nfsmount_info *mi) {}
 #endif /* MOUNT_CONFIG */
 
 /*
@@ -203,9 +206,9 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
                                        socklen_t salen,
                                        struct mount_options *options)
 {
-       struct sockaddr_storage dummy;
-       struct sockaddr *my_addr = (struct sockaddr *)&dummy;
-       socklen_t my_len = sizeof(dummy);
+       union nfs_sockaddr address;
+       struct sockaddr *my_addr = &address.sa;
+       socklen_t my_len = sizeof(address);
 
        if (po_contains(options, "clientaddr") == PO_FOUND)
                return 1;
@@ -217,21 +220,33 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
 }
 
 /*
- * Resolve the 'mounthost=' hostname and append a new option using
- * the resulting address.
+ * Determine whether to append a 'mountaddr=' option.  The option is needed if:
+ *
+ *   1. "mounthost=" was specified, or
+ *   2. The address families for proto= and mountproto= are different.
  */
-static int nfs_fix_mounthost_option(struct mount_options *options)
+static int nfs_fix_mounthost_option(struct mount_options *options,
+               const char *nfs_hostname)
 {
-       struct sockaddr_storage dummy;
-       struct sockaddr *sap = (struct sockaddr *)&dummy;
-       socklen_t salen = sizeof(dummy);
+       union nfs_sockaddr address;
+       struct sockaddr *sap = &address.sa;
+       socklen_t salen = sizeof(address);
+       sa_family_t nfs_family, mnt_family;
        char *mounthost;
 
+       if (!nfs_nfs_proto_family(options, &nfs_family))
+               return 0;
+       if (!nfs_mount_proto_family(options, &mnt_family))
+               return 0;
+
        mounthost = po_get(options, "mounthost");
-       if (!mounthost)
-               return 1;
+       if (mounthost == NULL) {
+               if (nfs_family == mnt_family)
+                       return 1;
+               mounthost = (char *)nfs_hostname;
+       }
 
-       if (!nfs_name_to_address(mounthost, sap, &salen)) {
+       if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
                nfs_error(_("%s: unable to determine mount server's address"),
                                progname);
                return 0;
@@ -276,24 +291,12 @@ static int nfs_append_sloppy_option(struct mount_options *options)
                return 0;
        return 1;
 }
-/*
- * Set up mandatory non-version specific NFS mount options.
- *
- * Returns 1 if successful; otherwise zero.
- */
-static int nfs_validate_options(struct nfsmount_info *mi)
-{
-       struct sockaddr *sap = (struct sockaddr *)&mi->address;
-
-       if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
-               return 0;
-
-       mi->salen = sizeof(mi->address);
-       if (!nfs_name_to_address(mi->hostname, sap, &mi->salen))
-               return 0;
 
+static int nfs_set_version(struct nfsmount_info *mi)
+{
        if (!nfs_nfs_version(mi->options, &mi->version))
                return 0;
+
        if (strncmp(mi->type, "nfs4", 4) == 0)
                mi->version = 4;
        else {
@@ -302,11 +305,48 @@ static int nfs_validate_options(struct nfsmount_info *mi)
                        mi->version = 3;
        }
 
+       /*
+        * If we still don't know, check for version-specific
+        * mount options.
+        */
+       if (mi->version == 0) {
+               if (po_contains(mi->options, "mounthost") ||
+                   po_contains(mi->options, "mountaddr") ||
+                   po_contains(mi->options, "mountvers") ||
+                   po_contains(mi->options, "mountproto"))
+                       mi->version = 3;
+       }
+
        /*
         * If enabled, see if the default version was
         * set in the config file
         */
-       set_default_version(mi);
+       nfs_default_version(mi);
+       
+       return 1;
+}
+
+/*
+ * Set up mandatory non-version specific NFS mount options.
+ *
+ * Returns 1 if successful; otherwise zero.
+ */
+static int nfs_validate_options(struct nfsmount_info *mi)
+{
+       struct sockaddr *sap = &mi->address.sa;
+       sa_family_t family;
+
+       if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+               return 0;
+
+       if (!nfs_nfs_proto_family(mi->options, &family))
+               return 0;
+       mi->salen = sizeof(mi->address);
+       if (!nfs_lookup(mi->hostname, family, sap, &mi->salen))
+               return 0;
+
+       if (!nfs_set_version(mi))
+               return 0;
 
        if (!nfs_append_sloppy_option(mi->options))
                return 0;
@@ -348,10 +388,13 @@ static int nfs_extract_server_addresses(struct mount_options *options,
 }
 
 static int nfs_construct_new_options(struct mount_options *options,
+                                    struct sockaddr *nfs_saddr,
                                     struct pmap *nfs_pmap,
+                                    struct sockaddr *mnt_saddr,
                                     struct pmap *mnt_pmap)
 {
        char new_option[64];
+       char *netid;
 
        po_remove_all(options, "nfsprog");
        po_remove_all(options, "mountprog");
@@ -368,20 +411,14 @@ static int nfs_construct_new_options(struct mount_options *options,
        po_remove_all(options, "proto");
        po_remove_all(options, "udp");
        po_remove_all(options, "tcp");
-       switch (nfs_pmap->pm_prot) {
-       case IPPROTO_TCP:
-               snprintf(new_option, sizeof(new_option) - 1,
-                        "proto=tcp");
-               if (po_append(options, new_option) == PO_FAILED)
-                       return 0;
-               break;
-       case IPPROTO_UDP:
-               snprintf(new_option, sizeof(new_option) - 1,
-                        "proto=udp");
-               if (po_append(options, new_option) == PO_FAILED)
-                       return 0;
-               break;
-       }
+       netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
+       if (netid == NULL)
+               return 0;
+       snprintf(new_option, sizeof(new_option) - 1,
+                        "proto=%s", netid);
+       free(netid);
+       if (po_append(options, new_option) == PO_FAILED)
+               return 0;
 
        po_remove_all(options, "port");
        if (nfs_pmap->pm_port != NFS_PORT) {
@@ -398,20 +435,14 @@ static int nfs_construct_new_options(struct mount_options *options,
                return 0;
 
        po_remove_all(options, "mountproto");
-       switch (mnt_pmap->pm_prot) {
-       case IPPROTO_TCP:
-               snprintf(new_option, sizeof(new_option) - 1,
-                        "mountproto=tcp");
-               if (po_append(options, new_option) == PO_FAILED)
-                       return 0;
-               break;
-       case IPPROTO_UDP:
-               snprintf(new_option, sizeof(new_option) - 1,
-                        "mountproto=udp");
-               if (po_append(options, new_option) == PO_FAILED)
-                       return 0;
-               break;
-       }
+       netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
+       if (netid == NULL)
+               return 0;
+       snprintf(new_option, sizeof(new_option) - 1,
+                        "mountproto=%s", netid);
+       free(netid);
+       if (po_append(options, new_option) == PO_FAILED)
+               return 0;
 
        po_remove_all(options, "mountport");
        snprintf(new_option, sizeof(new_option) - 1,
@@ -438,12 +469,12 @@ static int nfs_construct_new_options(struct mount_options *options,
 static int
 nfs_rewrite_pmap_mount_options(struct mount_options *options)
 {
-       struct sockaddr_storage nfs_address;
-       struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address;
+       union nfs_sockaddr nfs_address;
+       struct sockaddr *nfs_saddr = &nfs_address.sa;
        socklen_t nfs_salen = sizeof(nfs_address);
        struct pmap nfs_pmap;
-       struct sockaddr_storage mnt_address;
-       struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address;
+       union nfs_sockaddr mnt_address;
+       struct sockaddr *mnt_saddr = &mnt_address.sa;
        socklen_t mnt_salen = sizeof(mnt_address);
        struct pmap mnt_pmap;
        char *option;
@@ -484,10 +515,15 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
        if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
                                 nfs_saddr, nfs_salen, &nfs_pmap)) {
                errno = ESPIPE;
+               if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
+                       errno = EOPNOTSUPP;
+               else if (rpc_createerr.cf_error.re_errno != 0)
+                       errno = rpc_createerr.cf_error.re_errno;
                return 0;
        }
 
-       if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) {
+       if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
+                                       mnt_saddr, &mnt_pmap)) {
                errno = EINVAL;
                return 0;
        }
@@ -543,7 +579,7 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
                return result;
        }
 
-       if (!nfs_fix_mounthost_option(options)) {
+       if (!nfs_fix_mounthost_option(options, mi->hostname)) {
                errno = EINVAL;
                goto out_fail;
        }
@@ -578,7 +614,7 @@ out_fail:
  */
 static int nfs_try_mount_v4(struct nfsmount_info *mi)
 {
-       struct sockaddr *sap = (struct sockaddr *)&mi->address;
+       struct sockaddr *sap = &mi->address.sa;
        struct mount_options *options = po_dup(mi->options);
        int result = 0;
 
@@ -588,6 +624,18 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi)
        }
 
        if (mi->version == 0) {
+               if (po_contains(options, "mounthost") ||
+                       po_contains(options, "mountaddr") ||
+                       po_contains(options, "mountvers") ||
+                       po_contains(options, "mountproto")) {
+               /*
+                * Since these mountd options are set assume version 3
+                * is wanted so error out with EPROTONOSUPPORT so the
+                * protocol negation starts with v3.
+                */
+                       errno = EPROTONOSUPPORT;
+                       goto out_fail;
+               }
                if (po_append(options, "vers=4") == PO_FAILED) {
                        errno = EINVAL;
                        goto out_fail;
@@ -598,12 +646,13 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi)
                errno = EINVAL;
                goto out_fail;
        }
+
        /*
         * Update option string to be recorded in /etc/mtab.
         */
        if (po_join(options, mi->extra_opts) == PO_FAILED) {
                errno = ENOMEM;
-               return 0;
+               goto out_fail;
        }
 
        result = nfs_sys_mount(mi, options);
@@ -628,8 +677,16 @@ static int nfs_try_mount(struct nfsmount_info *mi)
                if (linux_version_code() > MAKE_VERSION(2, 6, 31)) {
                        errno = 0;
                        result = nfs_try_mount_v4(mi);
-                       if (errno != EPROTONOSUPPORT)
-                               break;
+                       if (errno != EPROTONOSUPPORT) {
+                               /* 
+                                * To deal with legacy Linux servers that don't
+                                * automatically export a pseudo root, retry
+                                * ENOENT errors using version 3. And for
+                                * Linux servers prior to 2.6.25, retry EPERM
+                                */
+                               if (errno != ENOENT && errno != EPERM)
+                                       break;
+                       }
                }
        case 2:
        case 3: