--- /dev/null
+text-based mount command: Fix retry= option
+
+Steinar Gunderson reports:
+
+"It seems retry= is now additive with the text-based mount interface. In
+particular, "mount -o retry=0" still gives a two-minute timeout."
+
+Make retry option parsing more robust, while we're at it.
+
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+---
+
+ utils/mount/stropts.c | 55 ++++++++++++++++++++++++++++++++++++++-----------
+ 1 files changed, 43 insertions(+), 12 deletions(-)
+
+diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
+index cadb1f4..4df9ad9 100644
+--- a/utils/mount/stropts.c
++++ b/utils/mount/stropts.c
+@@ -65,6 +65,14 @@
+ #define NFS_MAXPATHNAME (1024)
+ #endif
+
++#ifndef NFS_DEF_FG_TIMEOUT_MINUTES
++#define NFS_DEF_FG_TIMEOUT_MINUTES (2ul)
++#endif
++
++#ifndef NFS_DEF_BG_TIMEOUT_MINUTES
++#define NFS_DEF_BG_TIMEOUT_MINUTES (10000ul)
++#endif
++
+ extern int nfs_mount_data_version;
+ extern char *progname;
+ extern int verbose;
+@@ -141,6 +149,35 @@ static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
+ }
+
+ /*
++ * Set up a timeout value based on the value of the "retry=" option.
++ *
++ * Returns 1 if the option was parsed successfully, otherwise zero.
++ * Returns the parsed timeout, or the default, in *timeout.
++ */
++static int parse_retry_option(time_t *timeout, struct mount_options *options,
++ unsigned long timeout_minutes)
++{
++ char *retry_option, *endptr;
++
++ retry_option = po_get(options, "retry");
++ if (retry_option) {
++ long tmp;
++
++ errno = 0;
++ tmp = strtol(retry_option, &endptr, 10);
++ if (errno || endptr == retry_option || tmp < 0) {
++ nfs_error(_("%s: incorrect retry timeout specified"),
++ progname);
++ return 0;
++ }
++ timeout_minutes = tmp;
++ }
++
++ *timeout = time(NULL) + (time_t)(timeout_minutes * 60);
++ return 1;
++}
++
++/*
+ * Append the 'addr=' option to the options string to pass a resolved
+ * server address to the kernel. After a successful mount, this address
+ * is also added to /etc/mtab for use when unmounting.
+@@ -535,13 +572,10 @@ static int nfsmount_fg(const char *spec, const char *node,
+ char **extra_opts)
+ {
+ unsigned int secs = 1;
+- time_t timeout = time(NULL);
+- char *retry;
++ time_t timeout;
+
+- timeout += 60 * 2; /* default: 2 minutes */
+- retry = po_get(options, "retry");
+- if (retry)
+- timeout += 60 * atoi(retry);
++ if (!parse_retry_option(&timeout, options, NFS_DEF_FG_TIMEOUT_MINUTES))
++ return EX_FAIL;
+
+ if (verbose)
+ printf(_("%s: timeout set for %s"),
+@@ -612,13 +646,10 @@ static int nfsmount_child(const char *spec, const char *node,
+ int fake, char **extra_opts)
+ {
+ unsigned int secs = 1;
+- time_t timeout = time(NULL);
+- char *retry;
++ time_t timeout;
+
+- timeout += 60 * 10000; /* default: 10,000 minutes */
+- retry = po_get(options, "retry");
+- if (retry)
+- timeout += 60 * atoi(retry);
++ if (!parse_retry_option(&timeout, options, NFS_DEF_BG_TIMEOUT_MINUTES))
++ return EX_FAIL;
+
+ for (;;) {
+ if (sleep(secs))
--- /dev/null
+Index: nfs-utils-1.1.2/utils/mount/fstab.c
+===================================================================
+--- nfs-utils-1.1.2.orig/utils/mount/fstab.c
++++ nfs-utils-1.1.2/utils/mount/fstab.c
+@@ -52,7 +52,7 @@ mtab_does_not_exist(void) {
+ return var_mtab_does_not_exist;
+ }
+
+-static int
++int
+ mtab_is_a_symlink(void) {
+ get_mtab_info();
+ return var_mtab_is_a_symlink;
+Index: nfs-utils-1.1.2/utils/mount/fstab.h
+===================================================================
+--- nfs-utils-1.1.2.orig/utils/mount/fstab.h
++++ nfs-utils-1.1.2/utils/mount/fstab.h
+@@ -7,6 +7,7 @@
+ #define _PATH_FSTAB "/etc/fstab"
+ #endif
+
++int mtab_is_a_symlink(void);
+ int mtab_is_writable(void);
+ int mtab_does_not_exist(void);
+
+Index: nfs-utils-1.1.2/utils/mount/mount.c
+===================================================================
+--- nfs-utils-1.1.2.orig/utils/mount/mount.c
++++ nfs-utils-1.1.2/utils/mount/mount.c
+@@ -257,6 +257,13 @@ static int add_mtab(char *spec, char *mo
+ return EX_SUCCESS;
+ }
+
++ /* Avoid writing if the mtab is a symlink to /proc/mounts, since
++ that would create a file /proc/mounts in case the proc filesystem
++ is not mounted, and the fchmod below would also fail. */
++ if (mtab_is_a_symlink()) {
++ return EX_SUCCESS;
++ }
++
+ lock_mtab();
+
+ mtab = nfs_setmntent(MOUNTED, "a+");