+ * Returns 1 if successful; otherwise zero.
+ */
+static int nfs_validate_options(struct nfsmount_info *mi)
+{
+ struct sockaddr_storage dummy;
+ struct sockaddr *sap = (struct sockaddr *)&dummy;
+ socklen_t salen = sizeof(dummy);
+
+ if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+ return 0;
+
+ if (!nfs_name_to_address(mi->hostname, sap, &salen))
+ return 0;
+
+ if (strncmp(mi->type, "nfs4", 4) == 0) {
+ if (!nfs_append_clientaddr_option(sap, salen, mi->options))
+ return 0;
+ } else {
+ if (!nfs_fix_mounthost_option(mi->options))
+ return 0;
+ if (!mi->fake && !nfs_verify_lock_option(mi->options))
+ return 0;
+ }
+
+ if (!nfs_append_sloppy_option(mi->options))
+ return 0;
+
+ return nfs_append_addr_option(sap, salen, mi->options);
+}
+
+/*
+ * Distinguish between permanent and temporary errors.
+ *
+ * Returns 0 if the passed-in error is temporary, thus the
+ * mount system call should be retried; returns one if the
+ * passed-in error is permanent, thus the mount system call
+ * should not be retried.
+ */
+static int nfs_is_permanent_error(int error)
+{
+ switch (error) {
+ case ESTALE:
+ case ETIMEDOUT:
+ case ECONNREFUSED:
+ return 0; /* temporary */
+ default:
+ return 1; /* permanent */
+ }
+}
+
+/*
+ * Get NFS/mnt server addresses from mount options
+ *
+ * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
+ * if all goes well; otherwise zero.
+ */
+static int nfs_extract_server_addresses(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
+ socklen_t *nfs_salen,
+ struct sockaddr *mnt_saddr,
+ socklen_t *mnt_salen)
+{
+ char *option;
+
+ option = po_get(options, "addr");
+ if (option == NULL)
+ return 0;
+ if (!nfs_string_to_sockaddr(option, strlen(option),
+ nfs_saddr, nfs_salen))
+ return 0;
+
+ option = po_get(options, "mountaddr");
+ if (option == NULL) {
+ memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
+ *mnt_salen = *nfs_salen;
+ } else if (!nfs_string_to_sockaddr(option, strlen(option),
+ mnt_saddr, mnt_salen))
+ return 0;
+
+ return 1;
+}
+
+static int nfs_construct_new_options(struct mount_options *options,
+ struct pmap *nfs_pmap,
+ struct pmap *mnt_pmap)
+{
+ char new_option[64];
+
+ po_remove_all(options, "nfsprog");
+ po_remove_all(options, "mountprog");
+
+ po_remove_all(options, "v2");
+ po_remove_all(options, "v3");
+ po_remove_all(options, "vers");
+ po_remove_all(options, "nfsvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "vers=%lu", nfs_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ 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;
+ }
+
+ po_remove_all(options, "port");
+ if (nfs_pmap->pm_port != NFS_PORT) {
+ snprintf(new_option, sizeof(new_option) - 1,
+ "port=%lu", nfs_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+ }
+
+ po_remove_all(options, "mountvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountvers=%lu", mnt_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ 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;
+ }
+
+ po_remove_all(options, "mountport");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountport=%lu", mnt_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Reconstruct the mount option string based on a portmapper probe
+ * of the server. Returns one if the server's portmapper returned
+ * something we can use, otherwise zero.
+ *
+ * To handle version and transport protocol fallback properly, we
+ * need to parse some of the mount options in order to set up a
+ * portmap probe. Mount options that nfs_rewrite_mount_options()
+ * doesn't recognize are left alone.