]> 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 3a2a237931835eeea19ca311138079442221235b..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 */
@@ -204,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;
@@ -218,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;
@@ -278,24 +292,11 @@ static int nfs_append_sloppy_option(struct mount_options *options)
        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)
+static int nfs_set_version(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;
-
        if (!nfs_nfs_version(mi->options, &mi->version))
                return 0;
+
        if (strncmp(mi->type, "nfs4", 4) == 0)
                mi->version = 4;
        else {
@@ -304,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
         */
        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;
@@ -350,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");
@@ -370,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) {
@@ -400,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,
@@ -440,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;
@@ -486,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;
        }
@@ -545,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;
        }
@@ -580,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;
 
@@ -590,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;
@@ -600,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);
@@ -634,9 +681,10 @@ static int nfs_try_mount(struct nfsmount_info *mi)
                                /* 
                                 * To deal with legacy Linux servers that don't
                                 * automatically export a pseudo root, retry
-                                * ENOENT errors using version 3
+                                * ENOENT errors using version 3. And for
+                                * Linux servers prior to 2.6.25, retry EPERM
                                 */
-                               if (errno != ENOENT)
+                               if (errno != ENOENT && errno != EPERM)
                                        break;
                        }
                }