2003-08-06 20:26:12

by Steve Dickson

[permalink] [raw]
Subject: NFS Mount Patch: Making NFS over TCP the default

--- util-linux-2.11y/mount/nfsmount.c.diff 2003-08-05 09:26:14.000000000 -0400
+++ util-linux-2.11y/mount/nfsmount.c 2003-08-05 10:47:41.000000000 -0400
@@ -133,6 +133,52 @@ find_kernel_nfs_mount_version(void) {
nfs_mount_version = NFS_MOUNT_VERSION;
return nfs_mount_version;
}
+static inline struct pmaplist *
+get_pmaps(struct sockaddr_in *addr)
+{
+ static struct pmaplist *phead = NULL;
+ static struct sockaddr_in *lastaddr = NULL;
+
+ /*
+ * Make sure we are taking to the same server
+ */
+ if (lastaddr && (addr->sin_addr.s_addr != lastaddr->sin_addr.s_addr))
+ phead = NULL;
+
+ if (phead == NULL)
+ phead = pmap_getmaps(addr);
+
+ lastaddr = addr;
+ return phead;
+}
+#define NFS_TCP_CAP 0x01
+#define NFS_V3_CAP 0x02
+
+static unsigned short
+get_nfs_caps(struct sockaddr_in *server_addr)
+{
+ struct pmaplist *pmap;
+ unsigned short nfs_caps = 0;
+
+ if ((pmap = get_pmaps(server_addr)) == NULL)
+ return 0;
+
+ do {
+ if (pmap->pml_map.pm_prog == NFS_PROGRAM) {
+ if (pmap->pml_map.pm_prot == IPPROTO_TCP)
+ nfs_caps |= NFS_TCP_CAP;
+ if (pmap->pml_map.pm_vers == 3)
+ nfs_caps |= NFS_V3_CAP;
+ /*
+ * Check to see if we are finished
+ */
+ if ((nfs_caps & (NFS_TCP_CAP|NFS_V3_CAP)) == (NFS_TCP_CAP|NFS_V3_CAP))
+ break;
+ }
+ } while ((pmap = pmap->pml_next));
+
+ return nfs_caps;
+}

static struct pmap *
get_mountport(struct sockaddr_in *server_addr,
@@ -155,7 +201,7 @@ get_mountport(struct sockaddr_in *server
p.pm_port = port;

server_addr->sin_port = PMAPPORT;
- pmap = pmap_getmaps(server_addr);
+ pmap = get_pmaps(server_addr);

while (pmap) {
if (pmap->pml_map.pm_prog != prog)
@@ -230,6 +276,7 @@ int nfsmount(const char *spec, const cha
time_t t;
time_t prevt;
time_t timeout;
+ unsigned short nfs_caps;

/* The version to try is either specified or 0
In case it is 0 we tell the caller what we tried */
@@ -443,6 +490,49 @@ int nfsmount(const char *spec, const cha
}
}
}
+
+ /* 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);
+ } else {
+ if ((hp = gethostbyname(mounthost)) == NULL) {
+ fprintf(stderr, _("mount: can't get address for %s\n"),
+ mounthost);
+ goto fail;
+ } 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);
+ }
+ mount_server_addr.sin_family = AF_INET;
+ memcpy(&mount_server_addr.sin_addr,
+ hp->h_addr, hp->h_length);
+ }
+ }
+ }
+ /*
+ * Set the defaults to be NFS v3 over TCP iff the
+ * version or transport have not been explicitly
+ * set and the server is able to support those
+ * options.
+ */
+ if (nfsvers == 0 || tcp == 0) {
+ if ((nfs_caps = get_nfs_caps(&mount_server_addr))) {
+ if (nfsvers == 0) {
+ if (nfs_caps & NFS_V3_CAP)
+ nfsvers = 3;
+ }
+ if (tcp == 0) {
+ if (nfs_caps & NFS_TCP_CAP)
+ tcp = 1;
+ }
+ }
+ }
+
proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;

data.flags = (soft ? NFS_MOUNT_SOFT : 0)
@@ -518,29 +608,6 @@ int nfsmount(const char *spec, const cha
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);
- } else {
- if ((hp = gethostbyname(mounthost)) == NULL) {
- fprintf(stderr, _("mount: can't get address for %s\n"),
- mounthost);
- goto fail;
- } 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);
- }
- mount_server_addr.sin_family = AF_INET;
- memcpy(&mount_server_addr.sin_addr,
- hp->h_addr, hp->h_length);
- }
- }
- }

/*
* The following loop implements the mount retries. On the first
@@ -675,7 +742,8 @@ int nfsmount(const char *spec, const cha
if (t >= timeout)
goto fail;
}
- nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
+ if (nfsvers == 0)
+ nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;

if (nfsvers == 2) {
if (status.nfsv2.fhs_status != 0) {


Attachments:
util-linux-2.11y-mount-nfs-v3tcp.patch (4.33 kB)

2003-08-06 20:58:31

by Dan Kegel

[permalink] [raw]
Subject: [NFS] NFS Mount Patch: Support servers that don't support portmapper over TCP

Steve Dickson wrote:
> This patch changes the default transport of NFS mounts
> from UDP to TCP, iff the transport not explicitly
> specified and the server support TCP mounts.
>
> This patch is backwards compatible with servers that
> don't support TCP mounts since it quarries the server
> (which was already happening for the mount version)
> to see if the server support TCP mounts.

Say, as long as we're on the subject of supporting servers
that don't support TCP mounts, I've found the following
patch neccessary to support servers that don't support
TCP even for enumrating services. (Hint: some operating systems
have an arbitrary limit on TCP sessions.)

Tested in busybox nfsmount, rediffed against util-linux-2.12pre,
compiles ok. I don't know if the change in meaning of the
udp mount option is a good idea in general, but it sure helped
us here.
- Dan

--- util-linux-2.12pre/mount/nfsmount.c.old Wed Aug 6 13:39:24 2003
+++ util-linux-2.12pre/mount/nfsmount.c Wed Aug 6 13:46:51 2003
@@ -74,6 +74,9 @@

static char *nfs_strerror(int stat);

+static struct pmaplist *
+pmap_getmaps_udp (struct sockaddr_in *address);
+
#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))

#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
@@ -148,7 +151,10 @@
p.pm_port = port;

server_addr->sin_port = PMAPPORT;
- pmap = pmap_getmaps(server_addr);
+ if (proto == IPPROTO_TCP)
+ pmap = pmap_getmaps(server_addr);
+ else
+ pmap = pmap_getmaps_udp(server_addr);

while (pmap) {
if (pmap->pml_map.pm_prog != prog)
@@ -883,3 +889,91 @@
return port;
}
#endif
+
+/* added 30 June 2003, [email protected], to support true udp-only mounts */
+
+/* @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)[copy of] pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro";
+#endif
+
+/*
+ * [copy of] pmap_getmap.c
+ * Client interface to pmap rpc service.
+ * contains pmap_getmaps, which is only tcp service involved
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * Get a copy of the current port maps.
+ * Calls the pmap service remotely to do get the maps.
+ */
+struct pmaplist *
+pmap_getmaps_udp(struct sockaddr_in *address)
+{
+ struct pmaplist *head = (struct pmaplist *) NULL;
+ int fd = -1;
+ struct timeval minutetimeout;
+ struct timeval retry;
+ CLIENT *client;
+
+ minutetimeout.tv_sec = 60;
+ minutetimeout.tv_usec = 0;
+ address->sin_port = htons(PMAPPORT);
+#if 0
+ client = clnttcp_create(address, PMAPPROG, PMAPVERS, &fd, 50, 500);
+#else
+ /* FIXME: this retry interval's a bit arbitrary...*/
+ retry.tv_sec = 5;
+ retry.tv_usec = 0;
+ client = clntudp_create(address, PMAPPROG, PMAPVERS, retry, &fd);
+#endif
+ if (client != (CLIENT *) NULL) {
+ if (CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL,
+ (xdrproc_t) xdr_pmaplist, (caddr_t) & head, minutetimeout) != RPC_SUCCESS) {
+ clnt_perror(client, _("pmap_getmaps rpc problem"));
+ }
+ CLNT_DESTROY(client);
+ }
+ /* (void)close(fd); CLNT_DESTROY already closed it */
+ address->sin_port = 0;
+ return head;
+}
+