--- util-linux-2.12pre-vanilla/mount/nfsmount.c	2002-04-05 13:35:53.000000000 +0200
+++ util-linux-2.12pre-mount-order/mount/nfsmount.c	2003-12-23 12:01:19.000000000 +0200
@@ -26,6 +26,9 @@
  * 
  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
  * plus NFSv3 stuff.
+ *
+ * 2003-12-22 Muli Ben-Yehuda <mulix@mulix.org>
+ * - added tcp-udp mount ordering for NFS 
  */
 
 /*
@@ -188,60 +191,67 @@
 	return &p;
 }
 
-int nfsmount(const char *spec, const char *node, int *flags,
-	     char **extra_opts, char **mount_opts, int *nfs_mount_vers,
-	     int running_bg)
-{
-	static char *prev_bg_host;
-	char hostdir[1024];
-	CLIENT *mclient;
-	char *hostname, *dirname, *old_opts, *mounthost = NULL;
-	char new_opts[1024];
-	struct timeval total_timeout;
-	enum clnt_stat clnt_stat;
-	static struct nfs_mount_data data;
-	char *opt, *opteq;
-	int nfs_mount_version;
-	int val;
-	struct hostent *hp;
-	struct sockaddr_in server_addr;
-	struct sockaddr_in mount_server_addr;
-	struct pmap *pm_mnt;
-	int msock, fsock;
-	struct timeval retry_timeout;
-	union {
-		struct fhstatus nfsv2;
-		struct mountres3 nfsv3;
-	} status;
-	struct stat statbuf;
-	char *s;
-	int port, mountport, proto, bg, soft, intr;
-	int posix, nocto, noac, nolock, broken_suid;
-	int retry, tcp;
-	int mountprog, mountvers, nfsprog, nfsvers;
-	int retval;
-	time_t t;
-	time_t prevt;
-	time_t timeout;
+union status_union {
+	struct fhstatus nfsv2;
+	struct mountres3 nfsv3;
+}; 
+
+enum mount_protos { 
+	MO_TCP = 0, 
+	MO_UDP, 
+	MO_TCP_THEN_UDP, 
+	MO_UDP_THEN_TCP
+}; 
+
+struct nfs_mount_opts { 
+	int port; 
+	int mountport; 
+	int bg; 
+	int soft; 
+	int intr;
+	int posix; 
+	int nocto; 
+	int noac; 
+	int nolock; 
+	int broken_suid;
+	int retry; 
+	int mountprog; 
+	int mountvers; 
+	int nfsprog; 
+	int nfsvers;
+	char* mounthost; 
+	enum mount_protos protos; 
+}; 
 
+static int get_nfs_mount_version(int* nfs_mount_vers)
+{
 	/* The version to try is either specified or 0
 	   In case it is 0 we tell the caller what we tried */
 	if (!*nfs_mount_vers)
 		*nfs_mount_vers = find_kernel_nfs_mount_version();
-	nfs_mount_version = *nfs_mount_vers;
+	
+	return *nfs_mount_vers;
+}
 
-	retval = EX_FAIL;
-	msock = fsock = -1;
-	mclient = NULL;
-	if (strlen(spec) >= sizeof(hostdir)) {
+static int check_mount_spec_len(const char* spec, size_t maxlen)
+{
+	if (strlen(spec) >= maxlen) {
 		fprintf(stderr, _("mount: "
 				  "excessively long host:dir argument\n"));
-		goto fail;
+		return -ENOSPC; 
 	}
-	strcpy(hostdir, spec);
+	return 0; 
+}
+
+
+static int parse_hostname_dirname(char* hostdir, char** hostname, 
+				  char** dirname)
+{
+	char* s; 
+
 	if ((s = strchr(hostdir, ':'))) {
-		hostname = hostdir;
-		dirname = s + 1;
+		*hostname = hostdir;
+		*dirname = s + 1;
 		*s = '\0';
 		/* Ignore all but first hostname in replicated mounts
 		   until they can be fully supported. (mack@sgi.com) */
@@ -255,135 +265,161 @@
 		fprintf(stderr,
 			_("mount: "
 			  "directory to mount not in host:dir format\n"));
-		goto fail;
+		return -EINVAL; 
 	}
+	return 0; 
+}
+
+static int 
+get_mount_server_addr(const char* hostname, struct sockaddr_in* server_addr, 
+		      struct sockaddr_in* mount_server_addr, char** old_opts, 
+		      char** extra_opts)
+{
+	struct hostent* hp; 
+	char* s; 
+	char new_opts[1024];
+	char* old; 
+
+	server_addr->sin_family = AF_INET;
 
-	server_addr.sin_family = AF_INET;
 #ifdef HAVE_inet_aton
-	if (!inet_aton(hostname, &server_addr.sin_addr))
+	if (!inet_aton(hostname, &server_addr->sin_addr))
 #endif
 	{
 		if ((hp = gethostbyname(hostname)) == NULL) {
 			fprintf(stderr, _("mount: can't get address for %s\n"),
 				hostname);
-			goto fail;
+			return -ENOENT; 
 		} else {
 			if (hp->h_length > sizeof(struct in_addr)) {
 				fprintf(stderr,
 					_("mount: got bad hp->h_length\n"));
 				hp->h_length = sizeof(struct in_addr);
 			}
-			memcpy(&server_addr.sin_addr,
-			       hp->h_addr, hp->h_length);
+			memcpy(&server_addr->sin_addr, hp->h_addr, 
+			       hp->h_length);
 		}
 	}
 
-	memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
+	memcpy (mount_server_addr, server_addr, sizeof(*mount_server_addr));
 
 	/* add IP address to mtab options for use when unmounting */
-
-	s = inet_ntoa(server_addr.sin_addr);
-	old_opts = *extra_opts;
-	if (!old_opts)
-		old_opts = "";
-	if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
+	s = inet_ntoa(server_addr->sin_addr);
+	old = *extra_opts;
+	if (!old)
+		old = "";
+	if (strlen(old) + strlen(s) + 10 >= sizeof(new_opts)) {
 		fprintf(stderr, _("mount: "
 				  "excessively long option argument\n"));
-		goto fail;
+		return -ENOSPC; 
 	}
-	sprintf(new_opts, "%s%saddr=%s",
-		old_opts, *old_opts ? "," : "", s);
+	sprintf(new_opts, "%s%saddr=%s", old, *old ? "," : "", s);
 	*extra_opts = xstrdup(new_opts);
 
+	*old_opts = old; 
+
+	return 0; 
+}
+
+static enum mount_protos parse_protos(const char* val)
+{
+	if (!strncmp(val, "tcp-udp", 7))
+		return MO_TCP_THEN_UDP; 
+	else if (!strncmp(val, "udp-tcp", 7))
+		return MO_UDP_THEN_TCP; 
+	else if (!strncmp(val, "tcp", 3))
+		return MO_TCP;
+	else if (!strncmp(val, "udp", 3))
+		return MO_UDP;
+	else {
+		printf(_("Warning: Unrecognized proto=' option '%s'.\n"), val);
+		return MO_UDP; 
+		
+	}
+}
+
+static int 
+nfs_parse_options(char* old_opts, struct nfs_mount_opts* opts, 
+		  struct nfs_mount_data* data, int nfs_mount_version)
+{
+	char* opt; 
+	char* opteq; 
+	int val; 
+
+
 	/* Set default options.
 	 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
 	 * let the kernel decide.
 	 * timeo is filled in after we know whether it'll be TCP or UDP. */
-	memset(&data, 0, sizeof(data));
-	data.retrans	= 3;
-	data.acregmin	= 3;
-	data.acregmax	= 60;
-	data.acdirmin	= 30;
-	data.acdirmax	= 60;
+	memset(data, 0, sizeof(*data));
+	data->retrans	= 3;
+	data->acregmin	= 3;
+	data->acregmax	= 60;
+	data->acdirmin	= 30;
+	data->acdirmax	= 60;
 #if NFS_MOUNT_VERSION >= 2
-	data.namlen	= NAME_MAX;
+	data->namlen	= NAME_MAX;
 #endif
 
-	bg = 0;
-	soft = 0;
-	intr = 0;
-	posix = 0;
-	nocto = 0;
-	nolock = 0;
-	broken_suid = 0;
-	noac = 0;
-	retry = 10000;		/* 10000 minutes ~ 1 week */
-	tcp = 0;
-
-	mountprog = MOUNTPROG;
-	mountvers = 0;
-	port = 0;
-	mountport = 0;
-	nfsprog = NFS_PROGRAM;
-	nfsvers = 0;
+	memset(opts, 0, sizeof(*opts)); 
+	/* everything is now set to 0 */ 
+	/* initialize these explicitly */ 
+	opts->retry = 10000;		/* 10000 minutes ~ 1 week */
+	opts->mountprog = MOUNTPROG;
+	opts->nfsprog = NFS_PROGRAM;
+	opts->protos = MO_UDP; /* default */ 
 
 	/* parse options */
-
 	for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
 		if ((opteq = strchr(opt, '='))) {
 			val = atoi(opteq + 1);	
 			*opteq = '\0';
 			if (!strcmp(opt, "rsize"))
-				data.rsize = val;
+				data->rsize = val;
 			else if (!strcmp(opt, "wsize"))
-				data.wsize = val;
+				data->wsize = val;
 			else if (!strcmp(opt, "timeo"))
-				data.timeo = val;
+				data->timeo = val;
 			else if (!strcmp(opt, "retrans"))
-				data.retrans = val;
+				data->retrans = val;
 			else if (!strcmp(opt, "acregmin"))
-				data.acregmin = val;
+				data->acregmin = val;
 			else if (!strcmp(opt, "acregmax"))
-				data.acregmax = val;
+				data->acregmax = val;
 			else if (!strcmp(opt, "acdirmin"))
-				data.acdirmin = val;
+				data->acdirmin = val;
 			else if (!strcmp(opt, "acdirmax"))
-				data.acdirmax = val;
+				data->acdirmax = val;
 			else if (!strcmp(opt, "actimeo")) {
-				data.acregmin = val;
-				data.acregmax = val;
-				data.acdirmin = val;
-				data.acdirmax = val;
+				data->acregmin = val;
+				data->acregmax = val;
+				data->acdirmin = val;
+				data->acdirmax = val;
 			}
 			else if (!strcmp(opt, "retry"))
-				retry = val;
+				opts->retry = val;
 			else if (!strcmp(opt, "port"))
-				port = val;
+				opts->port = val;
 			else if (!strcmp(opt, "mountport"))
-			        mountport = val;
+			        opts->mountport = val;
 			else if (!strcmp(opt, "mounthost"))
-			        mounthost=xstrndup(opteq+1,
-						   strcspn(opteq+1," \t\n\r,"));
+			        opts->mounthost=xstrndup(opteq+1,
+							 strcspn(opteq+1," \t\n\r,"));
 			else if (!strcmp(opt, "mountprog"))
-				mountprog = val;
+				opts->mountprog = val;
 			else if (!strcmp(opt, "mountvers"))
-				mountvers = val;
+				opts->mountvers = val;
 			else if (!strcmp(opt, "nfsprog"))
-				nfsprog = val;
+				opts->nfsprog = val;
 			else if (!strcmp(opt, "nfsvers") ||
 				 !strcmp(opt, "vers"))
-				nfsvers = val;
+				opts->nfsvers = val;
 			else if (!strcmp(opt, "proto")) {
-				if (!strncmp(opteq+1, "tcp", 3))
-					tcp = 1;
-				else if (!strncmp(opteq+1, "udp", 3))
-					tcp = 0;
-				else
-					printf(_("Warning: Unrecognized proto= option.\n"));
+				opts->protos = parse_protos(opteq + 1);
 			} else if (!strcmp(opt, "namlen")) {
 #if NFS_MOUNT_VERSION >= 2
 				if (nfs_mount_version >= 2)
-					data.namlen = val;
+					data->namlen = val;
 				else
 #endif
 					printf(_("Warning: Option namlen is not supported.\n"));
@@ -392,7 +428,7 @@
 			} else {
 				printf(_("unknown nfs mount parameter: "
 					 "%s=%d\n"), opt, val);
-				goto fail;
+				return -EINVAL; 
 			}
 		} else {
 			val = 1;
@@ -401,100 +437,135 @@
 				opt += 2;
 			}
 			if (!strcmp(opt, "bg")) 
-				bg = val;
+				opts->bg = val;
 			else if (!strcmp(opt, "fg")) 
-				bg = !val;
+				opts->bg = !val;
 			else if (!strcmp(opt, "soft"))
-				soft = val;
+				opts->soft = val;
 			else if (!strcmp(opt, "hard"))
-				soft = !val;
+				opts->soft = !val;
 			else if (!strcmp(opt, "intr"))
-				intr = val;
+				opts->intr = val;
 			else if (!strcmp(opt, "posix"))
-				posix = val;
+				opts->posix = val;
 			else if (!strcmp(opt, "cto"))
-				nocto = !val;
+				opts->nocto = !val;
 			else if (!strcmp(opt, "ac"))
-				noac = !val;
+				opts->noac = !val;
 			else if (!strcmp(opt, "tcp"))
-				tcp = val;
+				opts->protos = (val ? MO_TCP : MO_UDP);
 			else if (!strcmp(opt, "udp"))
-				tcp = !val;
+				opts->protos = (val ? MO_UDP: MO_TCP);
 			else if (!strcmp(opt, "lock")) {
 				if (nfs_mount_version >= 3)
-					nolock = !val;
+					opts->nolock = !val;
 				else
 					printf(_("Warning: option nolock is not supported.\n"));
 			} else if (!strcmp(opt, "broken_suid")) {
-				broken_suid = val;
+				opts->broken_suid = val;
 			} else {
 				if (!sloppy) {
 					printf(_("unknown nfs mount option: "
 						 "%s%s\n"), val ? "" : "no", opt);
-					goto fail;
+					return -EINVAL; 
 				}
 			}
 		}
 	}
+
+	return 0; 
+}
+
+static int 
+do_nfsmount(const char *node, int *flags, char **mount_opts, 
+	    int nfs_mount_version, int running_bg, 
+	    struct nfs_mount_opts* opts,  struct nfs_mount_data* data, 
+	    const char* hostname, struct sockaddr_in* server_addr, 
+	    struct sockaddr_in* mount_server_addr, char** dirname, 
+			int tcp, int ignore_rpc_error)
+{
+	time_t t;
+	time_t prevt;
+	time_t timeout;
+	int proto; 
+	struct stat statbuf; 
+	union {
+		struct fhstatus nfsv2;
+		struct mountres3 nfsv3;
+	} status;
+	struct timeval retry_timeout;
+	struct timeval total_timeout; 
+	struct pmap *pm_mnt;
+	enum clnt_stat clnt_stat;
+	static char *prev_bg_host;
+	int retval = EX_FAIL; 
+	struct hostent* hp; 
+	int val;
+	int msock, fsock; 
+	CLIENT* mclient; 
+
+	msock = fsock = -1;
+	mclient = NULL;
+
 	proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
 
-	data.flags = (soft ? NFS_MOUNT_SOFT : 0)
-		| (intr ? NFS_MOUNT_INTR : 0)
-		| (posix ? NFS_MOUNT_POSIX : 0)
-		| (nocto ? NFS_MOUNT_NOCTO : 0)
-		| (noac ? NFS_MOUNT_NOAC : 0);
+	data->flags = (opts->soft ? NFS_MOUNT_SOFT : 0)
+		| (opts->intr ? NFS_MOUNT_INTR : 0)
+		| (opts->posix ? NFS_MOUNT_POSIX : 0)
+		| (opts->nocto ? NFS_MOUNT_NOCTO : 0)
+		| (opts->noac ? NFS_MOUNT_NOAC : 0);
 #if NFS_MOUNT_VERSION >= 2
 	if (nfs_mount_version >= 2)
-		data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+		data->flags |= (tcp ? NFS_MOUNT_TCP : 0);
 #endif
 #if NFS_MOUNT_VERSION >= 3
 	if (nfs_mount_version >= 3)
-		data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
+		data->flags |= (opts->nolock ? NFS_MOUNT_NONLM : 0);
 #endif
 #if NFS_MOUNT_VERSION >= 4
 	if (nfs_mount_version >= 4)
-		data.flags |= (broken_suid ? NFS_MOUNT_BROKEN_SUID : 0);
+		data->flags |= (opts->broken_suid ? NFS_MOUNT_BROKEN_SUID : 0);
 #endif
-	if (nfsvers > MAX_NFSPROT) {
-		fprintf(stderr, "NFSv%d not supported!\n", nfsvers);
+	if (opts->nfsvers > MAX_NFSPROT) {
+		fprintf(stderr, "NFSv%d not supported!\n", opts->nfsvers);
 		return 0;
 	}
-	if (mountvers > MAX_NFSPROT) {
-		fprintf(stderr, "NFSv%d not supported!\n", nfsvers);
+	if (opts->mountvers > MAX_NFSPROT) {
+		fprintf(stderr, "NFSv%d not supported!\n", opts->nfsvers);
 		return 0;
 	}
-	if (nfsvers && !mountvers)
-		mountvers = (nfsvers < 3) ? 1 : nfsvers;
-	if (nfsvers && nfsvers < mountvers)
-		mountvers = nfsvers;
+	if (opts->nfsvers && !opts->mountvers)
+		opts->mountvers = (opts->nfsvers < 3) ? 1 : opts->nfsvers;
+	if (opts->nfsvers && opts->nfsvers < opts->mountvers)
+		opts->mountvers = opts->nfsvers;
 
 	/* Adjust options if none specified */
-	if (!data.timeo)
-		data.timeo = tcp ? 70 : 7;
+	if (!data->timeo)
+		data->timeo = tcp ? 70 : 7;
 
 #ifdef NFS_MOUNT_DEBUG
 	printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
-	       data.rsize, data.wsize, data.timeo, data.retrans);
+	       data->rsize, data->wsize, data->timeo, data->retrans);
 	printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
-	       data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+	       data->acregmin, data->acregmax, data->acdirmin, data->acdirmax);
 	printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
-	       port, bg, retry, data.flags);
-	printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
-	       mountprog, mountvers, nfsprog, nfsvers);
-	printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
-	       (data.flags & NFS_MOUNT_SOFT) != 0,
-	       (data.flags & NFS_MOUNT_INTR) != 0,
-	       (data.flags & NFS_MOUNT_POSIX) != 0,
-	       (data.flags & NFS_MOUNT_NOCTO) != 0,
-	       (data.flags & NFS_MOUNT_NOAC) != 0);
+	       port, opts->bg, retry, data->flags);
+	printf("mountprog = %d, opts->mountvers = %d, nfsprog = %d, opts->nfsvers = %d\n",
+	       mountprog, opts->mountvers, nfsprog, opts->nfsvers);
+	printf("optssoft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
+	       (data->flags & NFS_MOUNT_SOFT) != 0,
+	       (data->flags & NFS_MOUNT_INTR) != 0,
+	       (data->flags & NFS_MOUNT_POSIX) != 0,
+	       (data->flags & NFS_MOUNT_NOCTO) != 0,
+	       (data->flags & NFS_MOUNT_NOAC) != 0);
 #if NFS_MOUNT_VERSION >= 2
 	printf("tcp = %d\n",
-	       (data.flags & NFS_MOUNT_TCP) != 0);
+	       (data->flags & NFS_MOUNT_TCP) != 0);
 #endif
 #endif
 
-	data.version = nfs_mount_version;
-	*mount_opts = (char *) &data;
+	data->version = nfs_mount_version;
+	*mount_opts = (char *)data;
 
 	if (*flags & MS_REMOUNT)
 		return 0;
@@ -504,23 +575,23 @@
 	 * backgrounded, and the "bg" for this mount is also set,
 	 * give up immediately, to avoid the initial timeout.
 	 */
-	if (bg && !running_bg &&
+	if (opts->bg && !running_bg &&
 	    prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
-		if (retry > 0)
+		if (opts->retry > 0)
 			retval = EX_BG;
 		return retval;
 	}
 
 	/* create mount deamon client */
 	/* See if the nfs host = mount host. */
-	if (mounthost) {
-		if (mounthost[0] >= '0' && mounthost[0] <= '9') {
-			mount_server_addr.sin_family = AF_INET;
-			mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+	if (opts->mounthost) {
+		if (opts->mounthost[0] >= '0' && opts->mounthost[0] <= '9') {
+			mount_server_addr->sin_family = AF_INET;
+			mount_server_addr->sin_addr.s_addr = inet_addr(hostname);
 		} else {
-			if ((hp = gethostbyname(mounthost)) == NULL) {
+			if ((hp = gethostbyname(opts->mounthost)) == NULL) {
 				fprintf(stderr, _("mount: can't get address for %s\n"),
-					mounthost);
+					opts->mounthost);
 				goto fail;
 			} else {
 				if (hp->h_length > sizeof(struct in_addr)) {
@@ -528,8 +599,8 @@
 						_("mount: got bad hp->h_length?\n"));
 					hp->h_length = sizeof(struct in_addr);
 				}
-				mount_server_addr.sin_family = AF_INET;
-				memcpy(&mount_server_addr.sin_addr,
+				mount_server_addr->sin_family = AF_INET;
+				memcpy(&mount_server_addr->sin_addr,
 				       hp->h_addr, hp->h_length);
 			}
 		}
@@ -555,13 +626,13 @@
 	retry_timeout.tv_usec = 0;
 	total_timeout.tv_sec = 20;
 	total_timeout.tv_usec = 0;
-	timeout = time(NULL) + 60 * retry;
+	timeout = time(NULL) + 60 * opts->retry;
 	prevt = 0;
 	t = 30;
 	val = 1;
 
 	for (;;) {
-		if (bg && stat(node, &statbuf) == -1) {
+		if (opts->bg && stat(node, &statbuf) == -1) {
 			/* no mount point yet - sleep */
 			if (running_bg) {
 				sleep(val);	/* 1, 2, 4, 8, 16, 30, ... */
@@ -574,31 +645,31 @@
 			if (t - prevt < 30)
 				sleep(30);
 
-			pm_mnt = get_mountport(&mount_server_addr,
-					       mountprog,
-					       mountvers,
+			pm_mnt = get_mountport(mount_server_addr,
+					       opts->mountprog,
+					       opts->mountvers,
 					       proto,
-					       mountport,
+					       opts->mountport,
 					       nfs_mount_version);
 
 			/* contact the mount daemon via TCP */
-			mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+			mount_server_addr->sin_port = htons(pm_mnt->pm_port);
 			msock = RPC_ANYSOCK;
 
 			switch (pm_mnt->pm_prot) {
 			case IPPROTO_UDP:
-				mclient = clntudp_create(&mount_server_addr,
+				mclient = clntudp_create(mount_server_addr,
 							 pm_mnt->pm_prog,
 							 pm_mnt->pm_vers,
 							 retry_timeout,
 							 &msock);
 				if (mclient)
 					break;
-				mount_server_addr.sin_port =
+				mount_server_addr->sin_port =
 					htons(pm_mnt->pm_port);
 				msock = RPC_ANYSOCK;
 			case IPPROTO_TCP:
-				mclient = clnttcp_create(&mount_server_addr,
+				mclient = clnttcp_create(mount_server_addr,
 							 pm_mnt->pm_prog,
 							 pm_mnt->pm_vers,
 							 &msock, 0, 0);
@@ -620,7 +691,7 @@
 					clnt_stat = clnt_call(mclient,
 						     MOUNTPROC3_MNT,
 						     (xdrproc_t) xdr_dirpath,
-						     (caddr_t) &dirname,
+						     (caddr_t) dirname,
 						     (xdrproc_t) xdr_mountres3,
 						     (caddr_t) &status,
 						     total_timeout);
@@ -628,7 +699,7 @@
 					clnt_stat = clnt_call(mclient,
 						     MOUNTPROC_MNT,
 						     (xdrproc_t) xdr_dirpath,
-						     (caddr_t) &dirname,
+						     (caddr_t) dirname,
 						     (xdrproc_t) xdr_fhstatus,
 						     (caddr_t) &status,
 						     total_timeout);
@@ -638,29 +709,29 @@
 #if 0
 				/* errno? who sets errno? */
 				/* this fragment breaks bg mounting */
-				if (errno != ECONNREFUSED) {
+				if (errno != ECONNREFUSED && !ignore_rpc_error) {
 					clnt_perror(mclient, "mount");
 					goto fail;	/* don't retry */
 				}
 #endif
-				if (!running_bg && prevt == 0)
+				if (!running_bg && prevt == 0 && !ignore_rpc_error)
 					clnt_perror(mclient, "mount");
 				auth_destroy(mclient->cl_auth);
 				clnt_destroy(mclient);
 				mclient = 0;
 				close(msock);
 			} else {
-				if (!running_bg && prevt == 0)
+				if (!running_bg && prevt == 0 && !ignore_rpc_error)
 					clnt_pcreateerror("mount");
 			}
 			prevt = t;
 		}
 
-		if (!bg)
+		if (!opts->bg)
 		        goto fail;
 		if (!running_bg) {
 			prev_bg_host = xstrdup(hostname);
-			if (retry > 0)
+			if (opts->retry > 0)
 				retval = EX_BG;
 			goto fail;
 		}
@@ -668,22 +739,22 @@
 		if (t >= timeout)
 			goto fail;
 	}
-	nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
+	opts->nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
 
-	if (nfsvers == 2) {
+	if (opts->nfsvers == 2) {
 		if (status.nfsv2.fhs_status != 0) {
 			fprintf(stderr,
 				"mount: %s:%s failed, reason given by server: %s\n",
-				hostname, dirname,
+				hostname, *dirname,
 				nfs_strerror(status.nfsv2.fhs_status));
 			goto fail;
 		}
-		memcpy(data.root.data,
+		memcpy(data->root.data,
 		       (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
 		       NFS_FHSIZE);
 #if NFS_MOUNT_VERSION >= 4
-		data.root.size = NFS_FHSIZE;
-		memcpy(data.old_root.data,
+		data->root.size = NFS_FHSIZE;
+		memcpy(data->old_root.data,
 		       (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
 		       NFS_FHSIZE);
 #endif
@@ -693,19 +764,19 @@
 		if (status.nfsv3.fhs_status != 0) {
 			fprintf(stderr,
 				"mount: %s:%s failed, reason given by server: %s\n",
-				hostname, dirname,
+				hostname, *dirname,
 				nfs_strerror(status.nfsv3.fhs_status));
 			goto fail;
 		}
 		fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
-		memset(data.old_root.data, 0, NFS_FHSIZE);
-		memset(&data.root, 0, sizeof(data.root));
-		data.root.size = fhandle->fhandle3_len;
-		memcpy(data.root.data,
+		memset(data->old_root.data, 0, NFS_FHSIZE);
+		memset(&data->root, 0, sizeof(data->root));
+		data->root.size = fhandle->fhandle3_len;
+		memcpy(data->root.data,
 		       (char *) fhandle->fhandle3_val,
 		       fhandle->fhandle3_len);
 
-		data.flags |= NFS_MOUNT_VER3;
+		data->flags |= NFS_MOUNT_VER3;
 #endif
 	}
 
@@ -727,24 +798,24 @@
 		perror(_("nfs bindresvport"));
 		goto fail;
 	}
-	if (port == 0) {
-		server_addr.sin_port = PMAPPORT;
-		port = pmap_getport(&server_addr, nfsprog, nfsvers,
-				    tcp ? IPPROTO_TCP : IPPROTO_UDP);
+	if (opts->port == 0) {
+		server_addr->sin_port = PMAPPORT;
+		opts->port = pmap_getport(server_addr, opts->nfsprog, opts->nfsvers, 
+					  tcp ? IPPROTO_TCP : IPPROTO_UDP);
 #if 1
 		/* Here we check to see if user is mounting with the
 		 * tcp option.  If so, and if the portmap returns a
 		 * '0' for port (service unavailable), we then exit,
 		 * notifying the user, rather than hanging up mount.
 		 */
-		if (port == 0 && tcp == 1) {
+		if (opts->port == 0 && tcp == 1) {
 			perror(_("nfs server reported service unavailable"));
 			goto fail;
 		}
 #endif
 
-		if (port == 0)
-			port = NFS_PORT;
+		if (opts->port == 0)
+			opts->port = NFS_PORT;
 #ifdef NFS_MOUNT_DEBUG
 		else
 			printf(_("used portmapper to find NFS port\n"));
@@ -753,24 +824,24 @@
 #ifdef NFS_MOUNT_DEBUG
 	printf(_("using port %d for nfs deamon\n"), port);
 #endif
-	server_addr.sin_port = htons(port);
+	server_addr->sin_port = htons(opts->port);
 	/*
 	 * connect() the socket for kernels 1.3.10 and below only,
 	 * to avoid problems with multihomed hosts.
 	 * --Swen
 	 */
 	if (linux_version_code() <= 66314
-	    && connect(fsock, (struct sockaddr *) &server_addr,
-		       sizeof (server_addr)) < 0) {
+	    && connect(fsock, (struct sockaddr *) server_addr,
+		       sizeof (*server_addr)) < 0) {
 		perror(_("nfs connect"));
 		goto fail;
 	}
 
 	/* prepare data structure for kernel */
 
-	data.fd = fsock;
-	memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
-	strncpy(data.hostname, hostname, sizeof(data.hostname));
+	data->fd = fsock;
+	memcpy((char *) &data->addr, (char *) server_addr, sizeof(data->addr));
+	strncpy(data->hostname, hostname, sizeof(data->hostname));
 
 	/* clean up */
 
@@ -792,7 +863,144 @@
 	if (fsock != -1)
 		close(fsock);
 	return retval;
-}	
+}
+
+/* replace occurences of "proto=tcp-udp" or "proto=udp-tcp" */
+/* with either 'tcp' or 'udp', so that umount does the right thing */ 
+static void fix_protocol_string(char** extra_opts, int tcp)
+{
+	char* str = *extra_opts;
+	char* found;
+
+	found = strstr(str, "proto=tcp-udp");
+	if (!found)
+		found = strstr(str, "proto=udp-tcp");
+	if (!found)
+		return;
+	
+	/* fix it.. */ 
+	str = found + 10; 
+	str[0] = (tcp ? 't' : 'u'); /* tcp or udp */ 
+	str[1] = (tcp ? 'c' : 'd'); /* tcp or udp */
+
+	*extra_opts = str; 
+}
+
+static int proto_first_try(enum mount_protos protos)
+{
+	static const unsigned int tcp = 1;
+	static const unsigned int udp = 0;
+
+	switch (protos) {
+	  case MO_TCP:
+	  case MO_TCP_THEN_UDP:
+		return tcp; 
+	  case MO_UDP:
+	  case MO_UDP_THEN_TCP:
+		return udp;
+	  default:
+		return tcp; 
+	}
+}
+
+static int proto_has_second_try(enum mount_protos protos)
+{
+	return ((protos == MO_TCP_THEN_UDP) ||
+			(protos == MO_UDP_THEN_TCP));
+}
+
+static int proto_second_try(enum mount_protos protos)
+{
+	static const unsigned int tcp = 1;
+	static const unsigned int udp = 0;
+
+	switch (protos) {
+	  case MO_TCP_THEN_UDP:
+		return udp; 
+	  case MO_UDP_THEN_TCP:
+		return tcp;
+	  default:
+		fprintf(stderr, _("unkonwn proto - %d\n"), (int)protos);
+		return 0; 
+	}	
+}
+
+int nfsmount(const char *spec, const char *node, int *flags,
+	     char **extra_opts, char **mount_opts, int *nfs_mount_vers,
+	     int running_bg)
+{
+	char hostdir[1024];
+	CLIENT *mclient;
+	char *hostname, *dirname, *old_opts; 
+	static struct nfs_mount_data data;
+	int nfs_mount_version;
+	struct sockaddr_in server_addr;
+	struct sockaddr_in mount_server_addr;
+	int msock, fsock;
+	int retval; /* external return value */ 
+	int ret;  /* internal return value */ 
+	struct nfs_mount_opts opts; 
+	int tcp;
+	int ignore_rpc_error; 
+
+	msock = fsock = -1;
+	mclient = NULL;
+
+	nfs_mount_version = get_nfs_mount_version(nfs_mount_vers); 
+
+	retval = EX_FAIL;
+	if (check_mount_spec_len(spec, sizeof(hostdir)) < 0)
+		goto done; 
+
+	strncpy(hostdir, spec, sizeof(hostdir) - 1);
+	hostdir[sizeof(hostdir) - 1] = '\0';  /* make sure it's NULL terminated */ 
+
+	if (parse_hostname_dirname(hostdir, &hostname, &dirname) < 0)
+		goto done; 
+
+	ret = get_mount_server_addr(hostname, &server_addr, &mount_server_addr, 
+				    &old_opts, extra_opts); 
+	if (ret < 0)
+		goto done; 
+
+	ret = nfs_parse_options(old_opts, &opts, &data, nfs_mount_version); 
+	if (ret < 0) { 
+		/* we historically return 0 for an unsupported NFS version */ 
+		/* no idea why -- mulix */ 
+		if (ret == -EPROTONOSUPPORT) 
+			retval = 0; 
+		goto done; 
+	}
+
+	/* finally, time to mount */ 
+	/* which protocol to try first? */ 
+	tcp  = proto_first_try(opts.protos);
+	ignore_rpc_error = proto_has_second_try(opts.protos); 
+	retval = do_nfsmount(node, flags, mount_opts, nfs_mount_version,
+						 running_bg, &opts, &data, hostname, &server_addr,
+						 &mount_server_addr, &dirname, tcp,
+						 ignore_rpc_error); 
+	/* on success, just return */ 
+	if (!retval) { 
+		fix_protocol_string(extra_opts, tcp); 
+		return retval; 
+	}
+
+	/* did we get 'tcp-then-udp' or 'udp-then-tcp' and thus should */ 
+	/* call do_nfsmount again? */
+	if (proto_has_second_try(opts.protos)) {
+		tcp = proto_second_try(opts.protos);
+		ignore_rpc_error = 0; 
+		retval = do_nfsmount(node, flags, mount_opts, nfs_mount_version,
+							 running_bg, &opts, &data, hostname, &server_addr,
+							 &mount_server_addr, &dirname, tcp,
+							 ignore_rpc_error); 
+		if (!retval)
+			fix_protocol_string(extra_opts, tcp); 
+	}
+done:
+	return retval; 
+}
 
 /*
  * We need to translate between nfs status return values and
