text-based mount.nfs: Implement falling back to NFSv2 and UDP
authorChuck Lever <chuck.lever@oracle.com>
Wed, 10 Oct 2007 19:06:35 +0000 (15:06 -0400)
committerNeil Brown <neilb@suse.de>
Thu, 11 Oct 2007 01:03:18 +0000 (11:03 +1000)
If the initial user-specified options fail (with EOPNOTSUPP or
EPROTONOSUPPORT) then the server has rejected the requested NFS version
or transport protocol.

In that case, probe the server, then construct a fresh set of mount
options that ask for the specific mountd and NFS version and transport
protocol that the server supports.  Rewrite the mount options based on
the results of the probe, then try the request again.

An additional kernel patch is required to cause the kernel to return
EOPNOTSUPP when an rpcbind fails during a NULL request.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Neil Brown <neilb@suse.de>
utils/mount/stropts.c

index fcc5b98..1d2cde6 100644 (file)
@@ -368,10 +368,64 @@ err:
 }
 
 /*
- * Attempt an NFSv2/3 mount via a mount(2) system call.
+ * Retry an NFS mount that failed because the requested service isn't
+ * available on the server.
  *
  * Returns 1 if successful.  Otherwise, returns zero.
  * "errno" is set to reflect the individual error.
+ *
+ * Side effect: If the retry is successful, both 'options' and
+ * 'extra_opts' are updated to reflect the mount options that worked.
+ * If the retry fails, 'options' and 'extra_opts' are left unchanged.
+ */
+static int retry_nfsmount(const char *spec, const char *node,
+                       int flags, struct mount_options *options,
+                       int fake, char **extra_opts)
+{
+       struct mount_options *retry_options;
+       char *retry_str = NULL;
+
+       retry_options = rewrite_mount_options(*extra_opts);
+       if (!retry_options) {
+               errno = EIO;
+               return 0;
+       }
+
+       if (po_join(retry_options, &retry_str) == PO_FAILED) {
+               po_destroy(retry_options);
+               errno = EIO;
+               return 0;
+       }
+
+       if (verbose)
+               printf(_("%s: text-based options (retry): '%s'\n"),
+                       progname, retry_str);
+
+       if (!mount(spec, node, "nfs",
+                               flags & ~(MS_USER|MS_USERS), retry_str)) {
+               free(*extra_opts);
+               *extra_opts = retry_str;
+               po_replace(options, retry_options);
+               return 1;
+       }
+
+       po_destroy(retry_options);
+       free(retry_str);
+       return 0;
+}
+
+/*
+ * Attempt an NFSv2/3 mount via a mount(2) system call.  If the kernel
+ * claims the requested service isn't supported on the server, probe
+ * the server to see what's supported, rewrite the mount options,
+ * and retry the request.
+ *
+ * Returns 1 if successful.  Otherwise, returns zero.
+ * "errno" is set to reflect the individual error.
+ *
+ * Side effect: If the retry is successful, both 'options' and
+ * 'extra_opts' are updated to reflect the mount options that worked.
+ * If the retry fails, 'options' and 'extra_opts' are left unchanged.
  */
 static int try_nfs23mount(const char *spec, const char *node,
                          int flags, struct mount_options *options,
@@ -392,7 +446,15 @@ static int try_nfs23mount(const char *spec, const char *node,
        if (!mount(spec, node, "nfs",
                                flags & ~(MS_USER|MS_USERS), *extra_opts))
                return 1;
-       return 0;
+
+       /*
+        * The kernel returns EOPNOTSUPP if the RPC bind failed,
+        * and EPROTONOSUPPORT if the version isn't supported.
+        */
+       if (errno != EOPNOTSUPP && errno != EPROTONOSUPPORT)
+               return 0;
+
+       return retry_nfsmount(spec, node, flags, options, fake, extra_opts);
 }
 
 /*