]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/stropts.c
mount.nfs: Fix sockaddr pointer aliasing in stropts.c
[nfs-utils.git] / utils / mount / stropts.c
index 7b63b4572128909dbe6bf61fd6084caef1a8e2b6..c5b92fc12f4f58633d5fd17b37acf68aa23e8b4e 100644 (file)
@@ -38,6 +38,7 @@
 #include "xcommon.h"
 #include "mount.h"
 #include "nls.h"
+#include "nfsrpc.h"
 #include "mount_constants.h"
 #include "stropts.h"
 #include "error.h"
@@ -76,12 +77,18 @@ extern char *progname;
 extern int verbose;
 extern int sloppy;
 
+union nfs_sockaddr {
+       struct sockaddr         sa;
+       struct sockaddr_in      s4;
+       struct sockaddr_in6     s6;
+};
+
 struct nfsmount_info {
        const char              *spec,          /* server:/path */
                                *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 +100,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 +116,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 +211,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;
@@ -222,9 +230,9 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
  */
 static int nfs_fix_mounthost_option(struct mount_options *options)
 {
-       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);
        char *mounthost;
 
        mounthost = po_get(options, "mounthost");
@@ -276,6 +284,41 @@ static int nfs_append_sloppy_option(struct mount_options *options)
                return 0;
        return 1;
 }
+
+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 {
+               char *option = po_get(mi->options, "proto");
+               if (option && strcmp(option, "rdma") == 0)
+                       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.
  *
@@ -283,7 +326,7 @@ static int nfs_append_sloppy_option(struct mount_options *options)
  */
 static int nfs_validate_options(struct nfsmount_info *mi)
 {
-       struct sockaddr *sap = (struct sockaddr *)&mi->address;
+       struct sockaddr *sap = &mi->address.sa;
 
        if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
                return 0;
@@ -292,21 +335,8 @@ static int nfs_validate_options(struct nfsmount_info *mi)
        if (!nfs_name_to_address(mi->hostname, sap, &mi->salen))
                return 0;
 
-       if (!nfs_nfs_version(mi->options, &mi->version))
+       if (!nfs_set_version(mi))
                return 0;
-       if (strncmp(mi->type, "nfs4", 4) == 0)
-               mi->version = 4;
-       else {
-               char *option = po_get(mi->options, "proto");
-               if (option && strcmp(option, "rdma") == 0)
-                       mi->version = 3;
-       }
-
-       /*
-        * If enabled, see if the default version was
-        * set in the config file
-        */
-       set_default_version(mi);
 
        if (!nfs_append_sloppy_option(mi->options))
                return 0;
@@ -348,10 +378,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 +401,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 +425,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 +459,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;
@@ -487,7 +508,8 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
                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;
        }
@@ -578,7 +600,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 +610,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 +632,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 +663,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: