From a8b74a7bb7a85970ff1ec4a5554545c7f65dcdb7 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Thu, 16 Sep 2010 16:32:49 -0400 Subject: [PATCH] Update to upstream RC release: nfs-utils-1-2-3-rc6 Signed-off-by: Steve Dickson --- nfs-utils-1-2-3-rc6.patch | 6521 +++++++++++++++++++++++++++++++++++++ nfs-utils.spec | 5 +- 2 files changed, 6525 insertions(+), 1 deletion(-) create mode 100644 nfs-utils-1-2-3-rc6.patch diff --git a/nfs-utils-1-2-3-rc6.patch b/nfs-utils-1-2-3-rc6.patch new file mode 100644 index 0000000..019b92a --- /dev/null +++ b/nfs-utils-1-2-3-rc6.patch @@ -0,0 +1,6521 @@ +diff -up nfs-utils-1.2.2/aclocal/libcap.m4.orig nfs-utils-1.2.2/aclocal/libcap.m4 +--- nfs-utils-1.2.2/aclocal/libcap.m4.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/aclocal/libcap.m4 2010-09-16 16:29:35.224032527 -0400 +@@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ + dnl look for prctl + AC_CHECK_FUNC([prctl], , ) + +- dnl look for the library; do not add to LIBS if found +- AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) +- AC_SUBST(LIBCAP) ++ AC_ARG_ENABLE([caps], ++ [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) ++ ++ LIBCAP= ++ ++ if test "x$enable_caps" != "xno" ; then ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) + +- AC_CHECK_HEADERS([sys/capability.h], , +- [AC_MSG_ERROR([libcap headers not found.])]) ++ AC_CHECK_HEADERS([sys/capability.h], , ++ [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) ++ fi ++ ++ AC_SUBST(LIBCAP) + + ])dnl +diff -up nfs-utils-1.2.2/configure.ac.orig nfs-utils-1.2.2/configure.ac +--- nfs-utils-1.2.2/configure.ac.orig 2010-09-16 15:40:15.598012398 -0400 ++++ nfs-utils-1.2.2/configure.ac 2010-09-16 16:29:35.224032527 -0400 +@@ -89,7 +89,7 @@ AC_ARG_ENABLE(nfsv41, + if test "$enable_nfsv41" = yes; then + AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + else +- enable_nfsv4= ++ enable_nfsv41= + fi + AC_SUBST(enable_nfsv41) + AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) +@@ -411,7 +411,7 @@ case $host in + ARCHFLAGS="" ;; + esac + +-my_am_cflags="-Wall -Wstrict-prototypes $ARCHFLAGS -pipe" ++my_am_cflags="-Wall -Wextra -Wstrict-prototypes $ARCHFLAGS -pipe" + + AC_SUBST([AM_CFLAGS], ["$my_am_cflags"]) + +@@ -436,6 +436,8 @@ AC_CONFIG_FILES([ + tools/nlmtest/Makefile + tools/rpcdebug/Makefile + tools/rpcgen/Makefile ++ tools/mountstats/Makefile ++ tools/nfs-iostat/Makefile + utils/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile +diff -up nfs-utils-1.2.2/support/export/client.c.orig nfs-utils-1.2.2/support/export/client.c +--- nfs-utils-1.2.2/support/export/client.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/client.c 2010-09-16 16:29:35.225032543 -0400 +@@ -17,7 +17,9 @@ + #include + #include + #include +-#include "xmalloc.h" ++#include ++ ++#include "sockaddr.h" + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +@@ -28,58 +30,260 @@ + #if !defined(__GLIBC__) || __GLIBC__ < 2 + extern int innetgr(char *netgr, char *host, char *, char *); + #endif +-static void client_init(nfs_client *clp, const char *hname, +- struct hostent *hp); +-static int client_checkaddr(nfs_client *clp, struct in_addr addr); ++ ++static char *add_name(char *old, const char *add); + + nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; + + +-/* if canonical is set, then we *know* this is already a canonical name +- * so hostname lookup is avoided. +- * This is used when reading /proc/fs/nfs/exports ++static void ++init_addrlist(nfs_client *clp, const struct addrinfo *ai) ++{ ++ int i; ++ ++ if (ai == NULL) ++ return; ++ ++ for (i = 0; (ai != NULL) && (i < NFSCLNT_ADDRMAX); i++) { ++ set_addrlist(clp, i, ai->ai_addr); ++ ai = ai->ai_next; ++ } ++ ++ clp->m_naddr = i; ++} ++ ++static void ++client_free(nfs_client *clp) ++{ ++ free(clp->m_hostname); ++ free(clp); ++} ++ ++static int ++init_netmask4(nfs_client *clp, const char *slash) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ uint32_t shift; ++ ++ /* ++ * Decide what kind of netmask was specified. If there's ++ * no '/' present, assume the netmask is all ones. If ++ * there is a '/' and at least one '.', look for a spelled- ++ * out netmask. Otherwise, assume it was a prefixlen. ++ */ ++ if (slash == NULL) ++ shift = 0; ++ else { ++ unsigned long prefixlen; ++ ++ if (strchr(slash + 1, '.') != NULL) { ++ if (inet_pton(AF_INET, slash + 1, ++ &sin.sin_addr.s_addr) == 0) ++ goto out_badmask; ++ set_addrlist_in(clp, 1, &sin); ++ return 1; ++ } else { ++ char *endptr; ++ ++ prefixlen = strtoul(slash + 1, &endptr, 10); ++ if (*endptr != '\0' && prefixlen != ULONG_MAX && ++ errno != ERANGE) ++ goto out_badprefix; ++ } ++ if (prefixlen > 32) ++ goto out_badprefix; ++ shift = 32 - (uint32_t)prefixlen; ++ } ++ ++ /* ++ * Now construct the full netmask bitmask in a sockaddr_in, ++ * and plant it in the nfs_client record. ++ */ ++ sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift); ++ set_addrlist_in(clp, 1, &sin); ++ ++ return 1; ++ ++out_badmask: ++ xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname); ++ return 0; ++ ++out_badprefix: ++ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); ++ return 0; ++} ++ ++#ifdef IPV6_SUPPORTED ++static int ++init_netmask6(nfs_client *clp, const char *slash) ++{ ++ struct sockaddr_in6 sin6 = { ++ .sin6_family = AF_INET6, ++ }; ++ unsigned long prefixlen; ++ uint32_t shift; ++ int i; ++ ++ /* ++ * Decide what kind of netmask was specified. If there's ++ * no '/' present, assume the netmask is all ones. If ++ * there is a '/' and at least one ':', look for a spelled- ++ * out netmask. Otherwise, assume it was a prefixlen. ++ */ ++ if (slash == NULL) ++ prefixlen = 128; ++ else { ++ if (strchr(slash + 1, ':') != NULL) { ++ if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr)) ++ goto out_badmask; ++ set_addrlist_in6(clp, 1, &sin6); ++ return 1; ++ } else { ++ char *endptr; ++ ++ prefixlen = strtoul(slash + 1, &endptr, 10); ++ if (*endptr != '\0' && prefixlen != ULONG_MAX && ++ errno != ERANGE) ++ goto out_badprefix; ++ } ++ if (prefixlen > 128) ++ goto out_badprefix; ++ } ++ ++ /* ++ * Now construct the full netmask bitmask in a sockaddr_in6, ++ * and plant it in the nfs_client record. ++ */ ++ for (i = 0; prefixlen > 32; i++) { ++ sin6.sin6_addr.s6_addr32[i] = 0xffffffff; ++ prefixlen -= 32; ++ } ++ shift = 32 - (uint32_t)prefixlen; ++ sin6.sin6_addr.s6_addr32[i] = htonl((uint32_t)~0 << shift); ++ set_addrlist_in6(clp, 1, &sin6); ++ ++ return 1; ++ ++out_badmask: ++ xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname); ++ return 0; ++ ++out_badprefix: ++ xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); ++ return 0; ++} ++#else /* IPV6_SUPPORTED */ ++static int ++init_netmask6(nfs_client *UNUSED(clp), const char *UNUSED(slash)) ++{ ++} ++#endif /* IPV6_SUPPORTED */ ++ ++/* ++ * Parse the network mask for M_SUBNETWORK type clients. ++ * ++ * Return TRUE if successful, or FALSE if some error occurred. ++ */ ++static int ++init_subnetwork(nfs_client *clp) ++{ ++ struct addrinfo *ai; ++ sa_family_t family; ++ int result = 0; ++ char *slash; ++ ++ slash = strchr(clp->m_hostname, '/'); ++ if (slash != NULL) { ++ *slash = '\0'; ++ ai = host_pton(clp->m_hostname); ++ *slash = '/'; ++ } else ++ ai = host_pton(clp->m_hostname); ++ if (ai == NULL) { ++ xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname); ++ return result; ++ } ++ ++ set_addrlist(clp, 0, ai->ai_addr); ++ family = ai->ai_addr->sa_family; ++ ++ freeaddrinfo(ai); ++ ++ switch (family) { ++ case AF_INET: ++ result = init_netmask4(clp, slash); ++ break; ++ case AF_INET6: ++ result = init_netmask6(clp, slash); ++ break; ++ default: ++ xlog(L_ERROR, "Unsupported address family for %s", ++ clp->m_hostname); ++ } ++ ++ return result; ++} ++ ++static int ++client_init(nfs_client *clp, const char *hname, const struct addrinfo *ai) ++{ ++ clp->m_hostname = strdup(hname); ++ if (clp->m_hostname == NULL) ++ return 0; ++ ++ clp->m_exported = 0; ++ clp->m_count = 0; ++ clp->m_naddr = 0; ++ ++ if (clp->m_type == MCL_SUBNETWORK) ++ return init_subnetwork(clp); ++ ++ init_addrlist(clp, ai); ++ return 1; ++} ++ ++static void ++client_add(nfs_client *clp) ++{ ++ nfs_client **cpp; ++ ++ cpp = &clientlist[clp->m_type]; ++ while (*cpp != NULL) ++ cpp = &((*cpp)->m_next); ++ clp->m_next = NULL; ++ *cpp = clp; ++} ++ ++/** ++ * client_lookup - look for @hname in our list of cached nfs_clients ++ * @hname: '\0'-terminated ASCII string containing hostname to look for ++ * @canonical: if set, @hname is known to be canonical DNS name ++ * ++ * Returns pointer to a matching or freshly created nfs_client. NULL ++ * is returned if some problem occurs. + */ + nfs_client * + client_lookup(char *hname, int canonical) + { + nfs_client *clp = NULL; + int htype; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + + htype = client_gettype(hname); + + if (htype == MCL_FQDN && !canonical) { +- struct hostent *hp2; +- hp = gethostbyname(hname); +- if (hp == NULL || hp->h_addrtype != AF_INET) { +- xlog(L_ERROR, "%s has non-inet addr", hname); +- return NULL; ++ ai = host_addrinfo(hname); ++ if (!ai) { ++ xlog(L_ERROR, "Failed to resolve %s", hname); ++ goto out; + } +- /* make sure we have canonical name */ +- hp2 = hostent_dup(hp); +- hp = gethostbyaddr(hp2->h_addr, hp2->h_length, +- hp2->h_addrtype); +- if (hp) { +- hp = hostent_dup(hp); +- /* but now we might not have all addresses... */ +- if (hp2->h_addr_list[1]) { +- struct hostent *hp3 = +- gethostbyname(hp->h_name); +- if (hp3) { +- free(hp); +- hp = hostent_dup(hp3); +- } +- } +- free(hp2); +- } else +- hp = hp2; +- +- hname = (char *) hp->h_name; ++ hname = ai->ai_canonname; + +- for (clp = clientlist[htype]; clp; clp = clp->m_next) { +- if (client_check(clp, hp)) ++ for (clp = clientlist[htype]; clp; clp = clp->m_next) ++ if (client_check(clp, ai)) + break; +- } + } else { + for (clp = clientlist[htype]; clp; clp = clp->m_next) { + if (strcasecmp(hname, clp->m_hostname)==0) +@@ -87,106 +291,60 @@ client_lookup(char *hname, int canonical + } + } + +- if (!clp) { +- clp = (nfs_client *) xmalloc(sizeof(*clp)); +- memset(clp, 0, sizeof(*clp)); ++ if (clp == NULL) { ++ clp = calloc(1, sizeof(*clp)); ++ if (clp == NULL) ++ goto out; + clp->m_type = htype; +- client_init(clp, hname, NULL); ++ if (!client_init(clp, hname, NULL)) { ++ client_free(clp); ++ clp = NULL; ++ goto out; ++ } + client_add(clp); + } + +- if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) { +- char **ap = hp->h_addr_list; +- int i; +- +- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) +- clp->m_addrlist[i] = *(struct in_addr *)*ap; +- clp->m_naddr = i; +- } +- +- if (hp) +- free (hp); ++ if (htype == MCL_FQDN && clp->m_naddr == 0) ++ init_addrlist(clp, ai); + ++out: ++ freeaddrinfo(ai); + return clp; + } + ++/** ++ * client_dup - create a copy of an nfs_client ++ * @clp: pointer to nfs_client to copy ++ * @ai: pointer to addrinfo used to initialize the new client's addrlist ++ * ++ * Returns a dynamically allocated nfs_client if successful, or ++ * NULL if some problem occurs. Caller must free the returned ++ * nfs_client with free(3). ++ */ + nfs_client * +-client_dup(nfs_client *clp, struct hostent *hp) ++client_dup(const nfs_client *clp, const struct addrinfo *ai) + { + nfs_client *new; + +- new = (nfs_client *) xmalloc(sizeof(*new)); ++ new = (nfs_client *)malloc(sizeof(*new)); ++ if (new == NULL) ++ return NULL; + memcpy(new, clp, sizeof(*new)); + new->m_type = MCL_FQDN; + new->m_hostname = NULL; + +- client_init(new, (char *) hp->h_name, hp); ++ if (!client_init(new, ai->ai_canonname, ai)) { ++ client_free(new); ++ return NULL; ++ } + client_add(new); + return new; + } + +-static void +-client_init(nfs_client *clp, const char *hname, struct hostent *hp) +-{ +- xfree(clp->m_hostname); +- if (hp) +- clp->m_hostname = xstrdup(hp->h_name); +- else +- clp->m_hostname = xstrdup(hname); +- +- clp->m_exported = 0; +- clp->m_count = 0; +- +- if (clp->m_type == MCL_SUBNETWORK) { +- char *cp = strchr(clp->m_hostname, '/'); +- static char slash32[] = "/32"; +- +- if(!cp) cp = slash32; +- *cp = '\0'; +- clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname); +- if (strchr(cp + 1, '.')) { +- clp->m_addrlist[1].s_addr = inet_addr(cp+1); +- } +- else { +- int netmask = atoi(cp + 1); +- if (0 < netmask && netmask <= 32) { +- clp->m_addrlist[1].s_addr = +- htonl ((uint32_t) ~0 << (32 - netmask)); +- } +- else { +- xlog(L_FATAL, "invalid netmask `%s' for %s", +- cp + 1, clp->m_hostname); +- } +- } +- *cp = '/'; +- clp->m_naddr = 0; +- } else if (!hp) { +- clp->m_naddr = 0; +- } else { +- char **ap = hp->h_addr_list; +- int i; +- +- for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) { +- clp->m_addrlist[i] = *(struct in_addr *)*ap; +- } +- clp->m_naddr = i; +- } +-} +- +-void +-client_add(nfs_client *clp) +-{ +- nfs_client **cpp; +- +- if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES) +- xlog(L_FATAL, "unknown client type in client_add"); +- cpp = clientlist + clp->m_type; +- while (*cpp) +- cpp = &((*cpp)->m_next); +- clp->m_next = NULL; +- *cpp = clp; +-} +- ++/** ++ * client_release - drop a reference to an nfs_client record ++ * ++ */ + void + client_release(nfs_client *clp) + { +@@ -195,6 +353,10 @@ client_release(nfs_client *clp) + clp->m_count--; + } + ++/** ++ * client_freeall - deallocate all nfs_client records ++ * ++ */ + void + client_freeall(void) + { +@@ -205,57 +367,45 @@ client_freeall(void) + head = clientlist + i; + while (*head) { + *head = (clp = *head)->m_next; +- xfree(clp->m_hostname); +- xfree(clp); ++ client_free(clp); + } + } + } + +-nfs_client * +-client_find(struct hostent *hp) +-{ +- nfs_client *clp; +- int i; +- +- for (i = 0; i < MCL_MAXTYPES; i++) { +- for (clp = clientlist[i]; clp; clp = clp->m_next) { +- if (!client_check(clp, hp)) +- continue; +-#ifdef notdef +- if (clp->m_type == MCL_FQDN) +- return clp; +- return client_dup(clp, hp); +-#else +- return clp; +-#endif +- } +- } +- return NULL; +-} +- +-struct hostent * +-client_resolve(struct in_addr addr) ++/** ++ * client_resolve - look up an IP address ++ * @sap: pointer to socket address to resolve ++ * ++ * Returns an addrinfo structure, or NULL if some problem occurred. ++ * Caller must free the result with freeaddrinfo(3). ++ */ ++struct addrinfo * ++client_resolve(const struct sockaddr *sap) + { +- struct hostent *he = NULL; ++ struct addrinfo *ai = NULL; + + if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP]) +- he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET); +- if (he == NULL) +- he = get_hostent((const char*)&addr, sizeof(addr), AF_INET); ++ ai = host_reliable_addrinfo(sap); ++ if (ai == NULL) ++ ai = host_numeric_addrinfo(sap); + +- return he; ++ return ai; + } + +-/* +- * Find client name given an IP address +- * This is found by gathering all known names that match that IP address, +- * sorting them and joining them with '+' ++/** ++ * client_compose - Make a list of cached hostnames that match an IP address ++ * @ai: pointer to addrinfo containing IP address information to match ++ * ++ * Gather all known client hostnames that match the IP address, and sort ++ * the result into a comma-separated list. + * ++ * Returns a '\0'-terminated ASCII string containing a comma-separated ++ * sorted list of client hostnames, or NULL if no client records matched ++ * the IP address or memory could not be allocated. Caller must free the ++ * returned string with free(3). + */ +-static char *add_name(char *old, char *add); +- + char * +-client_compose(struct hostent *he) ++client_compose(const struct addrinfo *ai) + { + char *name = NULL; + int i; +@@ -263,7 +413,7 @@ client_compose(struct hostent *he) + for (i = 0 ; i < MCL_MAXTYPES; i++) { + nfs_client *clp; + for (clp = clientlist[i]; clp ; clp = clp->m_next) { +- if (!client_check(clp, he)) ++ if (!client_check(clp, ai)) + continue; + name = add_name(name, clp->m_hostname); + } +@@ -271,13 +421,19 @@ client_compose(struct hostent *he) + return name; + } + ++/** ++ * client_member - check if @name is contained in the list @client ++ * @client: '\0'-terminated ASCII string containing ++ * comma-separated list of hostnames ++ * @name: '\0'-terminated ASCII string containing hostname to look for ++ * ++ * Returns 1 if @name was found in @client, otherwise zero is returned. ++ */ + int +-client_member(char *client, char *name) ++client_member(const char *client, const char *name) + { +- /* check if "client" (a ',' separated list of names) +- * contains 'name' as a member +- */ +- int l = strlen(name); ++ size_t l = strlen(name); ++ + while (*client) { + if (strncmp(client, name, l) == 0 && + (client[l] == ',' || client[l] == '\0')) +@@ -290,9 +446,8 @@ client_member(char *client, char *name) + return 0; + } + +- +-int +-name_cmp(char *a, char *b) ++static int ++name_cmp(const char *a, const char *b) + { + /* compare strings a and b, but only upto ',' in a */ + while (*a && *b && *a != ',' && *a == *b) +@@ -305,9 +460,9 @@ name_cmp(char *a, char *b) + } + + static char * +-add_name(char *old, char *add) ++add_name(char *old, const char *add) + { +- int len = strlen(add)+2; ++ size_t len = strlen(add) + 2; + char *new; + char *cp; + if (old) len += strlen(old); +@@ -340,108 +495,257 @@ add_name(char *old, char *add) + } + + /* +- * Match a host (given its hostent record) to a client record. This +- * is usually called from mountd. ++ * Check each address listed in @ai against each address ++ * stored in @clp. Return 1 if a match is found, otherwise ++ * zero. + */ +-int +-client_check(nfs_client *clp, struct hostent *hp) ++static int ++check_fqdn(const nfs_client *clp, const struct addrinfo *ai) + { +- char *hname = (char *) hp->h_name; +- char *cname = clp->m_hostname; +- char **ap; ++ int i; + +- switch (clp->m_type) { +- case MCL_FQDN: +- case MCL_SUBNETWORK: +- for (ap = hp->h_addr_list; *ap; ap++) { +- if (client_checkaddr(clp, *(struct in_addr *) *ap)) ++ for (; ai; ai = ai->ai_next) ++ for (i = 0; i < clp->m_naddr; i++) ++ if (nfs_compare_sockaddr(ai->ai_addr, ++ get_addrlist(clp, i))) + return 1; +- } +- return 0; +- case MCL_WILDCARD: +- if (wildmat(hname, cname)) ++ ++ return 0; ++} ++ ++static _Bool ++mask_match(const uint32_t a, const uint32_t b, const uint32_t m) ++{ ++ return ((a ^ b) & m) == 0; ++} ++ ++static int ++check_subnet_v4(const struct sockaddr_in *address, ++ const struct sockaddr_in *mask, const struct addrinfo *ai) ++{ ++ for (; ai; ai = ai->ai_next) { ++ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; ++ ++ if (sin->sin_family != AF_INET) ++ continue; ++ ++ if (mask_match(address->sin_addr.s_addr, ++ sin->sin_addr.s_addr, ++ mask->sin_addr.s_addr)) + return 1; +- else { +- for (ap = hp->h_aliases; *ap; ap++) +- if (wildmat(*ap, cname)) +- return 1; +- } +- return 0; +- case MCL_NETGROUP: +-#ifdef HAVE_INNETGR +- { +- char *dot; +- int match, i; +- struct hostent *nhp = NULL; +- struct sockaddr_in addr; +- +- /* First, try to match the hostname without +- * splitting off the domain */ +- if (innetgr(cname+1, hname, NULL, NULL)) +- return 1; ++ } ++ return 0; ++} + +- /* try the aliases as well */ +- for (i = 0; hp->h_aliases[i]; i++) { +- if (innetgr(cname+1, hp->h_aliases[i], NULL, NULL)) +- return 1; +- } ++#ifdef IPV6_SUPPORTED ++static int ++check_subnet_v6(const struct sockaddr_in6 *address, ++ const struct sockaddr_in6 *mask, const struct addrinfo *ai) ++{ ++ for (; ai; ai = ai->ai_next) { ++ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; + +- /* If hname is ip address convert to FQDN */ +- if (inet_aton(hname, &addr.sin_addr) && +- (nhp = gethostbyaddr((const char *)&(addr.sin_addr), +- sizeof(addr.sin_addr), AF_INET))) { +- hname = (char *)nhp->h_name; +- if (innetgr(cname+1, hname, NULL, NULL)) +- return 1; +- } ++ if (sin6->sin6_family != AF_INET6) ++ continue; + +- /* Okay, strip off the domain (if we have one) */ +- if ((dot = strchr(hname, '.')) == NULL) +- return 0; +- +- *dot = '\0'; +- match = innetgr(cname+1, hname, NULL, NULL); +- *dot = '.'; ++ if (mask_match(address->sin6_addr.s6_addr32[0], ++ sin6->sin6_addr.s6_addr32[0], ++ mask->sin6_addr.s6_addr32[0]) && ++ mask_match(address->sin6_addr.s6_addr32[1], ++ sin6->sin6_addr.s6_addr32[1], ++ mask->sin6_addr.s6_addr32[1]) && ++ mask_match(address->sin6_addr.s6_addr32[2], ++ sin6->sin6_addr.s6_addr32[2], ++ mask->sin6_addr.s6_addr32[2]) && ++ mask_match(address->sin6_addr.s6_addr32[3], ++ sin6->sin6_addr.s6_addr32[3], ++ mask->sin6_addr.s6_addr32[3])) ++ return 1; ++ } ++ return 0; ++} ++#else /* !IPV6_SUPPORTED */ ++static int ++check_subnet_v6(const struct sockaddr_in6 *UNUSED(address), ++ const struct sockaddr_in6 *UNUSED(mask), ++ const struct addrinfo *UNUSED(ai)) ++{ ++ return 0; ++} ++#endif /* !IPV6_SUPPORTED */ + +- return match; +- } +-#else +- return 0; +-#endif +- case MCL_ANONYMOUS: ++/* ++ * Check each address listed in @ai against the subnetwork or ++ * host address stored in @clp. Return 1 if an address in @hp ++ * matches the host address stored in @clp, otherwise zero. ++ */ ++static int ++check_subnetwork(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ switch (get_addrlist(clp, 0)->sa_family) { ++ case AF_INET: ++ return check_subnet_v4(get_addrlist_in(clp, 0), ++ get_addrlist_in(clp, 1), ai); ++ case AF_INET6: ++ return check_subnet_v6(get_addrlist_in6(clp, 0), ++ get_addrlist_in6(clp, 1), ai); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Check if a wildcard nfs_client record matches the canonical name ++ * or the aliases of a host. Return 1 if a match is found, otherwise ++ * zero. ++ */ ++static int ++check_wildcard(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ char *cname = clp->m_hostname; ++ char *hname = ai->ai_canonname; ++ struct hostent *hp; ++ char **ap; ++ ++ if (wildmat(hname, cname)) + return 1; +- case MCL_GSS: +- return 0; +- default: +- xlog(L_FATAL, "internal: bad client type %d", clp->m_type); ++ ++ /* See if hname aliases listed in /etc/hosts or nis[+] ++ * match the requested wildcard */ ++ hp = gethostbyname(hname); ++ if (hp != NULL) { ++ for (ap = hp->h_aliases; *ap; ap++) ++ if (wildmat(*ap, cname)) ++ return 1; + } + + return 0; + } + ++/* ++ * Check if @ai's hostname or aliases fall in a given netgroup. ++ * Return 1 if @ai represents a host in the netgroup, otherwise ++ * zero. ++ */ ++#ifdef HAVE_INNETGR ++static int ++check_netgroup(const nfs_client *clp, const struct addrinfo *ai) ++{ ++ const char *netgroup = clp->m_hostname + 1; ++ struct addrinfo *tmp = NULL; ++ struct hostent *hp; ++ char *dot, *hname; ++ int i, match; ++ ++ match = 0; ++ ++ hname = strdup(ai->ai_canonname); ++ if (hname == NULL) { ++ xlog(D_GENERAL, "%s: no memory for strdup", __func__); ++ goto out; ++ } ++ ++ /* First, try to match the hostname without ++ * splitting off the domain */ ++ if (innetgr(netgroup, hname, NULL, NULL)) { ++ match = 1; ++ goto out; ++ } ++ ++ /* See if hname aliases listed in /etc/hosts or nis[+] ++ * match the requested netgroup */ ++ hp = gethostbyname(hname); ++ if (hp != NULL) { ++ for (i = 0; hp->h_aliases[i]; i++) ++ if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) { ++ match = 1; ++ goto out; ++ } ++ } ++ ++ /* If hname happens to be an IP address, convert it ++ * to a the canonical DNS name bound to this address. */ ++ tmp = host_pton(hname); ++ if (tmp != NULL) { ++ char *cname = host_canonname(tmp->ai_addr); ++ freeaddrinfo(tmp); ++ ++ /* The resulting FQDN may be in our netgroup. */ ++ if (cname != NULL) { ++ free(hname); ++ hname = cname; ++ if (innetgr(netgroup, hname, NULL, NULL)) { ++ match = 1; ++ goto out; ++ } ++ } ++ } ++ ++ /* Okay, strip off the domain (if we have one) */ ++ dot = strchr(hname, '.'); ++ if (dot == NULL) ++ goto out; ++ ++ *dot = '\0'; ++ match = innetgr(netgroup, hname, NULL, NULL); ++ ++out: ++ free(hname); ++ return match; ++} ++#else /* !HAVE_INNETGR */ + static int +-client_checkaddr(nfs_client *clp, struct in_addr addr) ++check_netgroup(__attribute__((unused)) const nfs_client *clp, ++ __attribute__((unused)) const struct addrinfo *ai) + { +- int i; ++ return 0; ++} ++#endif /* !HAVE_INNETGR */ + ++/** ++ * client_check - check if IP address information matches a cached nfs_client ++ * @clp: pointer to a cached nfs_client record ++ * @ai: pointer to addrinfo to compare it with ++ * ++ * Returns 1 if the address information matches the cached nfs_client, ++ * otherwise zero. ++ */ ++int ++client_check(const nfs_client *clp, const struct addrinfo *ai) ++{ + switch (clp->m_type) { + case MCL_FQDN: +- for (i = 0; i < clp->m_naddr; i++) { +- if (clp->m_addrlist[i].s_addr == addr.s_addr) +- return 1; +- } +- return 0; ++ return check_fqdn(clp, ai); + case MCL_SUBNETWORK: +- return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) +- & clp->m_addrlist[1].s_addr); ++ return check_subnetwork(clp, ai); ++ case MCL_WILDCARD: ++ return check_wildcard(clp, ai); ++ case MCL_NETGROUP: ++ return check_netgroup(clp, ai); ++ case MCL_ANONYMOUS: ++ return 1; ++ case MCL_GSS: ++ return 0; ++ default: ++ xlog(D_GENERAL, "%s: unrecognized client type: %d", ++ __func__, clp->m_type); + } ++ + return 0; + } + ++/** ++ * client_gettype - determine type of nfs_client given an identifier ++ * @ident: '\0'-terminated ASCII string containing a client identifier ++ * ++ * Returns the type of nfs_client record that would be used for ++ * this client. ++ */ + int + client_gettype(char *ident) + { +- char *sp; ++ struct addrinfo *ai; ++ char *sp; + + if (ident[0] == '\0' || strcmp(ident, "*")==0) + return MCL_ANONYMOUS; +@@ -461,12 +765,16 @@ client_gettype(char *ident) + if (*sp == '\\' && sp[1]) + sp++; + } +- /* check for N.N.N.N */ +- sp = ident; +- if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; +- sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; +- sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; +- sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '\0') return MCL_FQDN; +- /* we lie here a bit. but technically N.N.N.N == N.N.N.N/32 :) */ +- return MCL_SUBNETWORK; ++ ++ /* ++ * Treat unadorned IP addresses as MCL_SUBNETWORK. ++ * Everything else is MCL_FQDN. ++ */ ++ ai = host_pton(ident); ++ if (ai != NULL) { ++ freeaddrinfo(ai); ++ return MCL_SUBNETWORK; ++ } ++ ++ return MCL_FQDN; + } +diff -up nfs-utils-1.2.2/support/export/export.c.orig nfs-utils-1.2.2/support/export/export.c +--- nfs-utils-1.2.2/support/export/export.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/export.c 2010-09-16 16:29:35.226032506 -0400 +@@ -24,9 +24,24 @@ static int export_hash(char *); + + static void export_init(nfs_export *exp, nfs_client *clp, + struct exportent *nep); +-static int export_check(nfs_export *, struct hostent *, char *); ++static void export_add(nfs_export *exp); ++static int export_check(const nfs_export *exp, const struct addrinfo *ai, ++ const char *path); + static nfs_export * +- export_allowed_internal(struct hostent *hp, char *path); ++ export_allowed_internal(const struct addrinfo *ai, ++ const char *path); ++ ++static void ++export_free(nfs_export *exp) ++{ ++ xfree(exp->m_export.e_squids); ++ xfree(exp->m_export.e_sqgids); ++ free(exp->m_export.e_mountpoint); ++ free(exp->m_export.e_fslocdata); ++ ++ xfree(exp->m_export.e_hostname); ++ xfree(exp); ++} + + static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) + { +@@ -44,7 +59,12 @@ static void warn_duplicated_exports(nfs_ + } + } + +-int ++/** ++ * export_read - read entries from /etc/exports ++ * @fname: name of file to read from ++ * ++ */ ++void + export_read(char *fname) + { + struct exportent *eep; +@@ -59,11 +79,15 @@ export_read(char *fname) + warn_duplicated_exports(exp, eep); + } + endexportent(); +- return 0; + } + +-/* +- * Create an in-core export struct from an export entry. ++/** ++ * export_create - create an in-core nfs_export record from an export entry ++ * @xep: export entry to lookup ++ * @canonical: if set, e_hostname is known to be canonical DNS name ++ * ++ * Returns a freshly instantiated export record, or NULL if ++ * a problem occurred. + */ + nfs_export * + export_create(struct exportent *xep, int canonical) +@@ -105,8 +129,8 @@ export_init(nfs_export *exp, nfs_client + * original hostname from /etc/exports, while the in-core client struct + * gets the newly found FQDN. + */ +-nfs_export * +-export_dup(nfs_export *exp, struct hostent *hp) ++static nfs_export * ++export_dup(nfs_export *exp, const struct addrinfo *ai) + { + nfs_export *new; + nfs_client *clp; +@@ -116,7 +140,11 @@ export_dup(nfs_export *exp, struct hoste + dupexportent(&new->m_export, &exp->m_export); + if (exp->m_export.e_hostname) + new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); +- clp = client_dup(exp->m_client, hp); ++ clp = client_dup(exp->m_client, ai); ++ if (clp == NULL) { ++ export_free(new); ++ return NULL; ++ } + clp->m_count++; + new->m_client = clp; + new->m_mayexport = exp->m_mayexport; +@@ -128,10 +156,8 @@ export_dup(nfs_export *exp, struct hoste + + return new; + } +-/* +- * Add export entry to hash table +- */ +-void ++ ++static void + export_add(nfs_export *exp) + { + exp_hash_table *p_tbl; +@@ -159,19 +185,27 @@ export_add(nfs_export *exp) + } + } + ++/** ++ * export_find - find or create a suitable nfs_export for @ai and @path ++ * @ai: pointer to addrinfo for client ++ * @path: '\0'-terminated ASCII string containing export path ++ * ++ * Returns a pointer to nfs_export data matching @ai and @path, ++ * or NULL if an error occurs. ++ */ + nfs_export * +-export_find(struct hostent *hp, char *path) ++export_find(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + int i; + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { +- if (!export_check(exp, hp, path)) ++ if (!export_check(exp, ai, path)) + continue; + if (exp->m_client->m_type == MCL_FQDN) + return exp; +- return export_dup(exp, hp); ++ return export_dup(exp, ai); + } + } + +@@ -179,7 +213,7 @@ export_find(struct hostent *hp, char *pa + } + + static nfs_export * +-export_allowed_internal (struct hostent *hp, char *path) ++export_allowed_internal(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + int i; +@@ -187,7 +221,7 @@ export_allowed_internal (struct hostent + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (!exp->m_mayexport || +- !export_check(exp, hp, path)) ++ !export_check(exp, ai, path)) + continue; + return exp; + } +@@ -196,8 +230,16 @@ export_allowed_internal (struct hostent + return NULL; + } + ++/** ++ * export_allowed - determine if this export is allowed ++ * @ai: pointer to addrinfo for client ++ * @path: '\0'-terminated ASCII string containing export path ++ * ++ * Returns a pointer to nfs_export data matching @ai and @path, ++ * or NULL if the export is not allowed. ++ */ + nfs_export * +-export_allowed(struct hostent *hp, char *path) ++export_allowed(const struct addrinfo *ai, const char *path) + { + nfs_export *exp; + char epath[MAXPATHLEN+1]; +@@ -210,7 +252,7 @@ export_allowed(struct hostent *hp, char + + /* Try the longest matching exported pathname. */ + while (1) { +- exp = export_allowed_internal (hp, epath); ++ exp = export_allowed_internal(ai, epath); + if (exp) + return exp; + /* We have to treat the root, "/", specially. */ +@@ -223,11 +265,17 @@ export_allowed(struct hostent *hp, char + return NULL; + } + +-/* +- * Search hash table for export entry. +- */ ++/** ++ * export_lookup - search hash table for export entry ++ * @hname: '\0'-terminated ASCII string containing client hostname to look for ++ * @path: '\0'-terminated ASCII string containing export path to look for ++ * @canonical: if set, @hname is known to be canonical DNS name ++ * ++ * Returns a pointer to nfs_export record matching @hname and @path, ++ * or NULL if the export was not found. ++ */ + nfs_export * +-export_lookup(char *hname, char *path, int canonical) ++export_lookup(char *hname, char *path, int canonical) + { + nfs_client *clp; + nfs_export *exp; +@@ -251,14 +299,18 @@ export_lookup(char *hname, char *path, i + } + + static int +-export_check(nfs_export *exp, struct hostent *hp, char *path) ++export_check(const nfs_export *exp, const struct addrinfo *ai, const char *path) + { + if (strcmp(path, exp->m_export.e_path)) + return 0; + +- return client_check(exp->m_client, hp); ++ return client_check(exp->m_client, ai); + } + ++/** ++ * export_freeall - deallocate all nfs_export records ++ * ++ */ + void + export_freeall(void) + { +@@ -269,22 +321,13 @@ export_freeall(void) + for (exp = exportlist[i].p_head; exp; exp = nxt) { + nxt = exp->m_next; + client_release(exp->m_client); +- if (exp->m_export.e_squids) +- xfree(exp->m_export.e_squids); +- if (exp->m_export.e_sqgids) +- xfree(exp->m_export.e_sqgids); +- if (exp->m_export.e_mountpoint) +- free(exp->m_export.e_mountpoint); +- if (exp->m_export.e_fslocdata) +- xfree(exp->m_export.e_fslocdata); +- xfree(exp->m_export.e_hostname); +- xfree(exp); ++ export_free(exp); ++ } ++ for (j = 0; j < HASH_TABLE_SIZE; j++) { ++ exportlist[i].entries[j].p_first = NULL; ++ exportlist[i].entries[j].p_last = NULL; + } +- for(j = 0; j < HASH_TABLE_SIZE; j++) { +- exportlist[i].entries[j].p_first = NULL; +- exportlist[i].entries[j].p_last = NULL; +- } +- exportlist[i].p_head = NULL; ++ exportlist[i].p_head = NULL; + } + client_freeall(); + } +diff -up nfs-utils-1.2.2/support/export/hostname.c.orig nfs-utils-1.2.2/support/export/hostname.c +--- nfs-utils-1.2.2/support/export/hostname.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/hostname.c 2010-09-16 16:29:35.227032188 -0400 +@@ -1,315 +1,376 @@ + /* +- * support/export/hostname.c ++ * Copyright 2010 Oracle. All rights reserved. + * +- * Functions for hostname. ++ * This file is part of nfs-utils. + * ++ * nfs-utils is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * nfs-utils is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with nfs-utils. If not, see . + */ + + #ifdef HAVE_CONFIG_H + #include + #endif + +-/* +-#define TEST +-*/ +- + #include +-#include +-#include +-#include + #include +-#include +-#ifdef TEST +-#define xmalloc malloc +-#else +-#include "xmalloc.h" +-#include "misc.h" ++#include ++#include ++#include ++ ++#include "sockaddr.h" ++#include "exportfs.h" ++ ++#ifndef HAVE_DECL_AI_ADDRCONFIG ++#define AI_ADDRCONFIG 0 + #endif + +-#define ALIGNMENT sizeof (char *) ++/** ++ * host_ntop - generate presentation address given a sockaddr ++ * @sap: pointer to socket address ++ * @buf: working storage ++ * @buflen: size of @buf in bytes ++ * ++ * Returns a pointer to a @buf. ++ */ ++#ifdef HAVE_GETNAMEINFO ++char * ++host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) ++{ ++ socklen_t salen = nfs_sockaddr_length(sap); ++ int error; ++ ++ memset(buf, 0, buflen); ++ ++ if (salen == 0) { ++ (void)strncpy(buf, "bad family", buflen - 1); ++ return buf; ++ } ++ ++ error = getnameinfo(sap, salen, buf, (socklen_t)buflen, ++ NULL, 0, NI_NUMERICHOST); ++ if (error != 0) { ++ buf[0] = '\0'; ++ (void)strncpy(buf, "bad address", buflen - 1); ++ } + +-static int +-align (int len, int al) ++ return buf; ++} ++#else /* !HAVE_GETNAMEINFO */ ++char * ++host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) + { +- int i; +- i = len % al; +- if (i) +- len += al - i; +- return len; ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; ++ ++ memset(buf, 0, buflen); ++ ++ if (sin->sin_family != AF_INET) ++ (void)strncpy(buf, "bad family", buflen - 1); ++ return buf; ++ } ++ ++ if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL) ++ return buf; ++ ++ buf[0] = '\0'; ++ (void)strncpy(buf, "bad address", buflen - 1); ++ return buf; + } ++#endif /* !HAVE_GETNAMEINFO */ + +-struct hostent * +-get_hostent (const char *addr, int len, int type) ++/** ++ * host_pton - return addrinfo for a given presentation address ++ * @paddr: pointer to a '\0'-terminated ASCII string containing an ++ * IP presentation address ++ * ++ * Returns address info structure, or NULL if an error occurs. Caller ++ * must free the returned structure with freeaddrinfo(3). ++ */ ++__attribute_malloc__ ++struct addrinfo * ++host_pton(const char *paddr) + { +- struct hostent *cp; +- int len_ent; +- const char *name; +- int len_name; +- int num_aliases = 1; +- int len_aliases = sizeof (char *); +- int num_addr_list = 1; +- int len_addr_list = sizeof (char *); +- int pos; +- struct in_addr *ipv4; +- +- switch (type) +- { +- case AF_INET: +- ipv4 = (struct in_addr *) addr; +- name = inet_ntoa (*ipv4); +- break; +- +- default: +- return NULL; +- } +- +- len_ent = align (sizeof (*cp), ALIGNMENT); +- len_name = align (strlen (name) + 1, ALIGNMENT); +- +- num_addr_list++; +- len_addr_list += align (len, ALIGNMENT) + sizeof (char *); +- +- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases +- + len_addr_list); +- +- cp->h_addrtype = type; +- cp->h_length = len; +- pos = len_ent; +- cp->h_name = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_name, name); +- +- pos += len_name; +- cp->h_aliases = (char **) &(((char *) cp) [pos]); +- pos += num_aliases * sizeof (char *); +- cp->h_aliases [0] = NULL; +- +- pos = len_ent + len_name + len_aliases; +- cp->h_addr_list = (char **) &(((char *) cp) [pos]); +- pos += num_addr_list * sizeof (char *); +- cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]); +- memcpy (cp->h_addr_list [0], addr, cp->h_length); +- pos += align (cp->h_length, ALIGNMENT); +- cp->h_addr_list [1] = NULL; ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ /* don't return duplicates */ ++ .ai_protocol = (int)IPPROTO_UDP, ++ .ai_flags = AI_NUMERICHOST, ++ .ai_family = AF_UNSPEC, ++ }; ++ struct sockaddr_in sin; ++ int error, inet4; ++ ++ /* ++ * Although getaddrinfo(3) is easier to use and supports ++ * IPv6, it recognizes incomplete addresses like "10.4" ++ * as valid AF_INET addresses. It also accepts presentation ++ * addresses that end with a blank. ++ * ++ * inet_pton(3) is much stricter. Use it to be certain we ++ * have a real AF_INET presentation address, before invoking ++ * getaddrinfo(3) to generate the full addrinfo list. ++ */ ++ inet4 = 1; ++ if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0) ++ inet4 = 0; ++ ++ error = getaddrinfo(paddr, NULL, &hint, &ai); ++ switch (error) { ++ case 0: ++ if (!inet4 && ai->ai_addr->sa_family == AF_INET) { ++ freeaddrinfo(ai); ++ break; ++ } ++ return ai; ++ case EAI_NONAME: ++ if (paddr == NULL) ++ xlog(D_GENERAL, "%s: passed a NULL presentation address", ++ __func__); ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m", ++ __func__, paddr, errno); ++ break; ++ default: ++ xlog(D_GENERAL, "%s: failed to convert %s: %s", ++ __func__, paddr, gai_strerror(error)); ++ break; ++ } + +- return cp; ++ return NULL; + } + +-struct hostent * +-hostent_dup (struct hostent *hp) ++/** ++ * host_addrinfo - return addrinfo for a given hostname ++ * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname ++ * ++ * Returns address info structure with ai_canonname filled in, or NULL ++ * if no information is available for @hostname. Caller must free the ++ * returned structure with freeaddrinfo(3). ++ */ ++__attribute_malloc__ ++struct addrinfo * ++host_addrinfo(const char *hostname) + { +- int len_ent = align (sizeof (*hp), ALIGNMENT); +- int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT); +- int num_aliases = 1; +- int len_aliases = sizeof (char *); +- int num_addr_list = 1; +- int len_addr_list = sizeof (char *); +- int pos, i; +- char **sp; +- struct hostent *cp; +- +- for (sp = hp->h_aliases; sp && *sp; sp++) +- { +- num_aliases++; +- len_aliases += align (strlen (*sp) + 1, ALIGNMENT) +- + sizeof (char *); +- } +- +- for (sp = hp->h_addr_list; *sp; sp++) +- { +- num_addr_list++; +- len_addr_list += align (hp->h_length, ALIGNMENT) +- + sizeof (char *); +- } +- +- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases +- + len_addr_list); +- +- *cp = *hp; +- pos = len_ent; +- cp->h_name = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_name, hp->h_name); +- +- pos += len_name; +- cp->h_aliases = (char **) &(((char *) cp) [pos]); +- pos += num_aliases * sizeof (char *); +- for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++) +- if (sp && *sp) +- { +- cp->h_aliases [i] = (char *) &(((char *) cp) [pos]); +- strcpy (cp->h_aliases [i], *sp); +- pos += align (strlen (*sp) + 1, ALIGNMENT); +- } +- else +- cp->h_aliases [i] = NULL; +- +- pos = len_ent + len_name + len_aliases; +- cp->h_addr_list = (char **) &(((char *) cp) [pos]); +- pos += num_addr_list * sizeof (char *); +- for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++) +- if (*sp) +- { +- cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]); +- memcpy (cp->h_addr_list [i], *sp, hp->h_length); +- pos += align (hp->h_length, ALIGNMENT); +- } +- else +- cp->h_addr_list [i] = *sp; ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++#ifdef IPV6_SUPPORTED ++ .ai_family = AF_UNSPEC, ++#else ++ .ai_family = AF_INET, ++#endif ++ /* don't return duplicates */ ++ .ai_protocol = (int)IPPROTO_UDP, ++ .ai_flags = AI_ADDRCONFIG | AI_CANONNAME, ++ }; ++ int error; ++ ++ error = getaddrinfo(hostname, NULL, &hint, &ai); ++ switch (error) { ++ case 0: ++ return ai; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m", ++ __func__, hostname, errno); ++ break; ++ default: ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, hostname, gai_strerror(error)); ++ break; ++ } + +- return cp; ++ return NULL; + } + +-static int +-is_hostname(const char *sp) ++/** ++ * host_canonname - return canonical hostname bound to an address ++ * @sap: pointer to socket address to look up ++ * ++ * Discover the canonical hostname associated with the given socket ++ * address. The host's reverse mapping is verified in the process. ++ * ++ * Returns a '\0'-terminated ASCII string containing a hostname, or ++ * NULL if no hostname can be found for @sap. Caller must free ++ * the string. ++ */ ++#ifdef HAVE_GETNAMEINFO ++__attribute_malloc__ ++char * ++host_canonname(const struct sockaddr *sap) + { +- if (*sp == '\0' || *sp == '@') +- return 0; ++ socklen_t salen = nfs_sockaddr_length(sap); ++ char buf[NI_MAXHOST]; ++ int error; ++ ++ if (salen == 0) { ++ xlog(D_GENERAL, "%s: unsupported address family %d", ++ __func__, sap->sa_family); ++ return NULL; ++ } + +- for (; *sp; sp++) +- { +- if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') +- return 0; +- if (*sp == '\\' && sp[1]) +- sp++; +- } ++ memset(buf, 0, sizeof(buf)); ++ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NAMEREQD); ++ switch (error) { ++ case 0: ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", ++ __func__, errno); ++ return NULL; ++ default: ++ (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NUMERICHOST); ++ xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ __func__, buf, gai_strerror(error)); ++ return NULL; ++ } + +- return 1; ++ return strdup(buf); + } +- +-int +-matchhostname (const char *h1, const char *h2) ++#else /* !HAVE_GETNAMEINFO */ ++__attribute_malloc__ ++char * ++host_canonname(const struct sockaddr *sap) + { +- struct hostent *hp1, *hp2; +- int status; ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; ++ const struct in_addr *addr = &sin->sin_addr; ++ struct hostent *hp; + +- if (strcasecmp (h1, h2) == 0) +- return 1; +- +- if (!is_hostname (h1) || !is_hostname (h2)) +- return 0; ++ if (sap->sa_family != AF_INET) ++ return NULL; + +- hp1 = gethostbyname (h1); +- if (hp1 == NULL) +- return 0; +- +- hp1 = hostent_dup (hp1); +- +- hp2 = gethostbyname (h2); +- if (hp2) +- { +- if (strcasecmp (hp1->h_name, hp2->h_name) == 0) +- status = 1; +- else +- { +- char **ap1, **ap2; +- +- status = 0; +- for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++) +- for (ap2 = hp2->h_addr_list; *ap2; ap2++) +- if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0) +- { +- status = 1; +- break; +- } +- } +- } +- else +- status = 0; ++ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET); ++ if (hp == NULL) ++ return NULL; + +- free (hp1); +- return status; ++ return strdup(hp->h_name); + } ++#endif /* !HAVE_GETNAMEINFO */ + +- +-/* Map IP to hostname, and then map back to addr to make sure it is a +- * reliable hostname ++/** ++ * host_reliable_addrinfo - return addrinfo for a given address ++ * @sap: pointer to socket address to look up ++ * ++ * Reverse and forward lookups are performed to ensure the address has ++ * proper forward and reverse mappings. ++ * ++ * Returns address info structure with ai_canonname filled in, or NULL ++ * if no information is available for @sap. Caller must free the returned ++ * structure with freeaddrinfo(3). + */ +-struct hostent * +-get_reliable_hostbyaddr(const char *addr, int len, int type) ++__attribute_malloc__ ++struct addrinfo * ++host_reliable_addrinfo(const struct sockaddr *sap) + { +- struct hostent *hp = NULL; ++ struct addrinfo *ai; ++ char *hostname; + +- struct hostent *reverse; +- struct hostent *forward; +- char **sp; +- +- reverse = gethostbyaddr (addr, len, type); +- if (!reverse) ++ hostname = host_canonname(sap); ++ if (hostname == NULL) + return NULL; + +- /* must make sure the hostent is authorative. */ ++ ai = host_addrinfo(hostname); + +- reverse = hostent_dup (reverse); +- forward = gethostbyname (reverse->h_name); ++ free(hostname); ++ return ai; ++} + +- if (forward) { +- /* now make sure the "addr" is in the list */ +- for (sp = forward->h_addr_list ; *sp ; sp++) { +- if (memcmp (*sp, addr, forward->h_length) == 0) +- break; +- } ++/** ++ * host_numeric_addrinfo - return addrinfo without doing DNS queries ++ * @sap: pointer to socket address ++ * ++ * Returns address info structure, or NULL if an error occurred. ++ * Caller must free the returned structure with freeaddrinfo(3). ++ */ ++#ifdef HAVE_GETNAMEINFO ++__attribute_malloc__ ++struct addrinfo * ++host_numeric_addrinfo(const struct sockaddr *sap) ++{ ++ socklen_t salen = nfs_sockaddr_length(sap); ++ char buf[INET6_ADDRSTRLEN]; ++ struct addrinfo *ai; ++ int error; ++ ++ if (salen == 0) { ++ xlog(D_GENERAL, "%s: unsupported address family %d", ++ __func__, sap->sa_family); ++ return NULL; ++ } + +- if (*sp) { +- /* it's valid */ +- hp = hostent_dup (forward); +- } +- else { +- /* it was a FAKE */ +- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse", +- reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); +- } ++ memset(buf, 0, sizeof(buf)); ++ error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), ++ NULL, 0, NI_NUMERICHOST); ++ switch (error) { ++ case 0: ++ break; ++ case EAI_SYSTEM: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", ++ __func__, errno); ++ return NULL; ++ default: ++ xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s", ++ __func__, gai_strerror(error)); ++ return NULL; + } +- else { +- /* never heard of it. misconfigured DNS? */ +- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist", +- reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); ++ ++ ai = host_pton(buf); ++ ++ /* ++ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname ++ */ ++ if (ai != NULL) { ++ free(ai->ai_canonname); /* just in case */ ++ ai->ai_canonname = strdup(buf); ++ if (ai->ai_canonname == NULL) { ++ freeaddrinfo(ai); ++ ai = NULL; ++ } + } + +- free (reverse); +- return hp; ++ return ai; + } ++#else /* !HAVE_GETNAMEINFO */ ++__attribute_malloc__ ++struct addrinfo * ++host_numeric_addrinfo(const struct sockaddr *sap) ++{ ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ const struct in_addr *addr = &sin->sin_addr; ++ char buf[INET_ADDRSTRLEN]; ++ struct addrinfo *ai; + ++ if (sap->sa_family != AF_INET) ++ return NULL; + +-#ifdef TEST +-void +-print_host (struct hostent *hp) +-{ +- char **sp; ++ memset(buf, 0, sizeof(buf)); ++ if (inet_ntop(AF_INET, (char *)addr, buf, ++ (socklen_t)sizeof(buf)) == NULL) ++ return NULL; + +- if (hp) +- { +- printf ("official hostname: %s\n", hp->h_name); +- printf ("aliases:\n"); +- for (sp = hp->h_aliases; *sp; sp++) +- printf (" %s\n", *sp); +- printf ("IP addresses:\n"); +- for (sp = hp->h_addr_list; *sp; sp++) +- printf (" %s\n", inet_ntoa (*(struct in_addr *) *sp)); +- } +- else +- printf ("Not host information\n"); +-} ++ ai = host_pton(buf); + +-int +-main (int argc, char **argv) +-{ +- struct hostent *hp = gethostbyname (argv [1]); +- struct hostent *cp; +- struct in_addr addr; +- +- print_host (hp); +- +- if (hp) +- { +- cp = hostent_dup (hp); +- print_host (cp); +- free (cp); +- } +- printf ("127.0.0.1 == %s: %d\n", argv [1], +- matchhostname ("127.0.0.1", argv [1])); +- addr.s_addr = inet_addr(argv [2]); +- printf ("%s\n", inet_ntoa (addr)); +- cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET); +- print_host (cp); +- return 0; ++ /* ++ * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname ++ */ ++ if (ai != NULL) { ++ ai->ai_canonname = strdup(buf); ++ if (ai->ai_canonname == NULL) { ++ freeaddrinfo(ai); ++ ai = NULL; ++ } ++ } ++ ++ return ai; + } +-#endif ++#endif /* !HAVE_GETNAMEINFO */ +diff -up nfs-utils-1.2.2/support/export/nfsctl.c.orig nfs-utils-1.2.2/support/export/nfsctl.c +--- nfs-utils-1.2.2/support/export/nfsctl.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/nfsctl.c 2010-09-16 16:29:35.228032467 -0400 +@@ -66,7 +66,7 @@ str_tolower(char *s) + static int + cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) + { +- int i; ++ int i, j; + + if (clp->m_type != MCL_FQDN) { + xlog(L_ERROR, "internal: can't export non-FQDN host"); +@@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, n + strncpy(cltarg->cl_ident, clp->m_hostname, + sizeof (cltarg->cl_ident) - 1); + str_tolower(cltarg->cl_ident); +- cltarg->cl_naddr = clp->m_naddr; +- for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) +- cltarg->cl_addrlist[i] = clp->m_addrlist[i]; + ++ j = 0; ++ for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { ++ const struct sockaddr_in *sin = get_addrlist_in(clp, i); ++ if (sin->sin_family == AF_INET) ++ cltarg->cl_addrlist[j++] = sin->sin_addr; ++ } ++ if (j == 0) { ++ xlog(L_ERROR, "internal: no supported addresses in nfs_client"); ++ return 0; ++ } ++ ++ cltarg->cl_naddr = j; + return 1; + } + +@@ -100,7 +109,7 @@ expsetup(struct nfsctl_export *exparg, n + str_tolower(exparg->ex_client); + exparg->ex_flags = exp->m_export.e_flags; + exparg->ex_dev = (!unexport && (exp->m_export.e_flags & NFSEXP_FSID)) ? +- exp->m_export.e_fsid : stb.st_dev; ++ (__nfsd_dev_t)exp->m_export.e_fsid : stb.st_dev; + exparg->ex_ino = stb.st_ino; + exparg->ex_anon_uid = exp->m_export.e_anonuid; + exparg->ex_anon_gid = exp->m_export.e_anongid; +diff -up nfs-utils-1.2.2/support/export/rmtab.c.orig nfs-utils-1.2.2/support/export/rmtab.c +--- nfs-utils-1.2.2/support/export/rmtab.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/rmtab.c 2010-09-16 16:29:35.228032467 -0400 +@@ -19,39 +19,54 @@ + #include "xio.h" + #include "xlog.h" + ++/* ++ * See if the entry already exists. If not, ++ * this was an instantiated wild card, and we ++ * must add it. ++ */ ++static void ++rmtab_read_wildcard(struct rmtabent *rep) ++{ ++ nfs_export *exp, *exp2; ++ struct addrinfo *ai; ++ ++ ai = host_addrinfo(rep->r_client); ++ if (ai == NULL) ++ return; ++ ++ exp = export_allowed(ai, rep->r_path); ++ freeaddrinfo(ai); ++ if (exp == NULL) ++ return; ++ ++ exp2 = export_lookup(rep->r_client, exp->m_export.e_path, 0); ++ if (exp2 == NULL) { ++ struct exportent ee; ++ ++ memset(&ee, 0, sizeof(ee)); ++ dupexportent(&ee, &exp->m_export); ++ ++ ee.e_hostname = rep->r_client; ++ exp2 = export_create(&ee, 0); ++ exp2->m_changed = exp->m_changed; ++ } ++ exp2->m_mayexport = 1; ++} ++ + int + rmtab_read(void) + { + struct rmtabent *rep; +- nfs_export *exp = NULL; + + setrmtabent("r"); + while ((rep = getrmtabent(1, NULL)) != NULL) { +- struct hostent *hp = NULL; + int htype; +- ++ + htype = client_gettype(rep->r_client); +- if ((htype == MCL_FQDN || htype == MCL_SUBNETWORK) +- && (hp = gethostbyname (rep->r_client)) +- && (hp = hostent_dup (hp), +- exp = export_allowed (hp, rep->r_path))) { +- /* see if the entry already exists, otherwise this was an instantiated +- * wild card, and we must add it +- */ +- nfs_export *exp2 = export_lookup(rep->r_client, +- exp->m_export.e_path, 0); +- if (!exp2) { +- struct exportent ee; +- dupexportent(&ee, &exp->m_export); +- ee.e_hostname = rep->r_client; +- exp2 = export_create(&ee, 0); +- exp2->m_changed = exp->m_changed; +- } +- free (hp); +- exp2->m_mayexport = 1; +- } else if (hp) /* export_allowed failed */ +- free(hp); ++ if (htype == MCL_FQDN || htype == MCL_SUBNETWORK) ++ rmtab_read_wildcard(rep); + } ++ + if (errno == EINVAL) { + /* Something goes wrong. We need to fix the rmtab + file. */ +diff -up nfs-utils-1.2.2/support/include/exportfs.h.orig nfs-utils-1.2.2/support/include/exportfs.h +--- nfs-utils-1.2.2/support/include/exportfs.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/exportfs.h 2010-09-16 16:29:35.229229714 -0400 +@@ -10,6 +10,8 @@ + #define EXPORTFS_H + + #include ++ ++#include "sockaddr.h" + #include "nfslib.h" + + enum { +@@ -35,11 +37,56 @@ typedef struct mclient { + char * m_hostname; + int m_type; + int m_naddr; +- struct in_addr m_addrlist[NFSCLNT_ADDRMAX]; ++ union nfs_sockaddr m_addrlist[NFSCLNT_ADDRMAX]; + int m_exported; /* exported to nfsd */ + int m_count; + } nfs_client; + ++static inline const struct sockaddr * ++get_addrlist(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].sa; ++} ++ ++static inline const struct sockaddr_in * ++get_addrlist_in(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s4; ++} ++ ++static inline const struct sockaddr_in6 * ++get_addrlist_in6(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s6; ++} ++ ++static inline void ++set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin) ++{ ++ memcpy(&clp->m_addrlist[i].s4, sin, sizeof(*sin)); ++} ++ ++static inline void ++set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6) ++{ ++ memcpy(&clp->m_addrlist[i].s6, sin6, sizeof(*sin6)); ++} ++ ++static inline void ++set_addrlist(nfs_client *clp, const int i, const struct sockaddr *sap) ++{ ++ switch (sap->sa_family) { ++ case AF_INET: ++ memcpy(&clp->m_addrlist[i].s4, sap, sizeof(struct sockaddr_in)); ++ break; ++#ifdef IPV6_SUPPORTED ++ case AF_INET6: ++ memcpy(&clp->m_addrlist[i].s6, sap, sizeof(struct sockaddr_in6)); ++ break; ++#endif ++ } ++} ++ + typedef struct mexport { + struct mexport * m_next; + struct mclient * m_client; +@@ -69,26 +116,26 @@ extern exp_hash_table exportlist[MCL_MAX + extern nfs_client * clientlist[MCL_MAXTYPES]; + + nfs_client * client_lookup(char *hname, int canonical); +-nfs_client * client_find(struct hostent *); +-void client_add(nfs_client *); +-nfs_client * client_dup(nfs_client *, struct hostent *); ++nfs_client * client_dup(const nfs_client *clp, ++ const struct addrinfo *ai); + int client_gettype(char *hname); +-int client_check(nfs_client *, struct hostent *); +-int client_match(nfs_client *, char *hname); ++int client_check(const nfs_client *clp, ++ const struct addrinfo *ai); + void client_release(nfs_client *); + void client_freeall(void); +-char * client_compose(struct hostent *he); +-struct hostent * client_resolve(struct in_addr addr); +-int client_member(char *client, char *name); ++char * client_compose(const struct addrinfo *ai); ++struct addrinfo * client_resolve(const struct sockaddr *sap); ++int client_member(const char *client, ++ const char *name); + +-int export_read(char *fname); +-void export_add(nfs_export *); ++void export_read(char *fname); + void export_reset(nfs_export *); + nfs_export * export_lookup(char *hname, char *path, int caconical); +-nfs_export * export_find(struct hostent *, char *path); +-nfs_export * export_allowed(struct hostent *, char *path); ++nfs_export * export_find(const struct addrinfo *ai, ++ const char *path); ++nfs_export * export_allowed(const struct addrinfo *ai, ++ const char *path); + nfs_export * export_create(struct exportent *, int canonical); +-nfs_export * export_dup(nfs_export *, struct hostent *); + void export_freeall(void); + int export_export(nfs_export *); + int export_unexport(nfs_export *); +@@ -101,6 +148,19 @@ void xtab_append(nfs_export *); + + int secinfo_addflavor(struct flav_info *, struct exportent *); + ++char * host_ntop(const struct sockaddr *sap, ++ char *buf, const size_t buflen); ++__attribute_malloc__ ++struct addrinfo * host_pton(const char *paddr); ++__attribute_malloc__ ++struct addrinfo * host_addrinfo(const char *hostname); ++__attribute_malloc__ ++char * host_canonname(const struct sockaddr *sap); ++__attribute_malloc__ ++struct addrinfo * host_reliable_addrinfo(const struct sockaddr *sap); ++__attribute_malloc__ ++struct addrinfo * host_numeric_addrinfo(const struct sockaddr *sap); ++ + int rmtab_read(void); + + struct nfskey * key_lookup(char *hname); +diff -up nfs-utils-1.2.2/support/include/misc.h.orig nfs-utils-1.2.2/support/include/misc.h +--- nfs-utils-1.2.2/support/include/misc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/misc.h 2010-09-16 16:29:35.229229714 -0400 +@@ -15,13 +15,6 @@ + int randomkey(unsigned char *keyout, int len); + int weakrandomkey(unsigned char *keyout, int len); + +-int matchhostname(const char *h1, const char *h2); +- +-struct hostent; +-struct hostent *hostent_dup(struct hostent *hp); +-struct hostent *get_hostent (const char *addr, int len, int type); +-struct hostent *get_reliable_hostbyaddr(const char *addr, int len, int type); +- + extern int is_mountpoint(char *path); + + #endif /* MISC_H */ +diff -up nfs-utils-1.2.2/support/include/nfslib.h.orig nfs-utils-1.2.2/support/include/nfslib.h +--- nfs-utils-1.2.2/support/include/nfslib.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/nfslib.h 2010-09-16 16:29:35.230230090 -0400 +@@ -83,7 +83,7 @@ struct exportent { + int e_nsquids; + int * e_sqgids; + int e_nsqgids; +- int e_fsid; ++ unsigned int e_fsid; + char * e_mountpoint; + int e_fslocmethod; + char * e_fslocdata; +@@ -134,9 +134,12 @@ int nfsaddclient(struct nfsctl_client + int nfsdelclient(struct nfsctl_client *clp); + int nfsexport(struct nfsctl_export *exp); + int nfsunexport(struct nfsctl_export *exp); +-struct nfs_fh_len * getfh_old(struct sockaddr *addr, dev_t dev, ino_t ino); +-struct nfs_fh_len * getfh(struct sockaddr *addr, const char *); +-struct nfs_fh_len * getfh_size(struct sockaddr *addr, const char *, int size); ++ ++struct nfs_fh_len * getfh_old(const struct sockaddr_in *sin, ++ const dev_t dev, const ino_t ino); ++struct nfs_fh_len * getfh(const struct sockaddr_in *sin, const char *path); ++struct nfs_fh_len * getfh_size(const struct sockaddr_in *sin, ++ const char *path, int const size); + + void qword_print(FILE *f, char *str); + void qword_printhex(FILE *f, char *str, int slen); +@@ -152,10 +155,15 @@ void qword_addhex(char **bpp, int *lp, c + void qword_addint(char **bpp, int *lp, int n); + void qword_adduint(char **bpp, int *lp, unsigned int n); + void qword_addeol(char **bpp, int *lp); ++int qword_get_uint(char **bpp, unsigned int *anint); ++void qword_printuint(FILE *f, unsigned int num); + + void closeall(int min); + + int svctcp_socket (u_long __number, int __reuse); +-int svcudp_socket (u_long __number, int __reuse); ++int svcudp_socket (u_long __number); ++ ++ ++#define UNUSED(x) UNUSED_ ## x __attribute__((unused)) + + #endif /* NFSLIB_H */ +diff -up nfs-utils-1.2.2/support/include/nfsrpc.h.orig nfs-utils-1.2.2/support/include/nfsrpc.h +--- nfs-utils-1.2.2/support/include/nfsrpc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/nfsrpc.h 2010-09-16 16:29:35.230230090 -0400 +@@ -27,6 +27,12 @@ + #include + + /* ++ * IANA does not define an IP protocol number for RDMA transports. ++ * Choose an arbitrary value we can use locally. ++ */ ++#define NFSPROTO_RDMA (3939) ++ ++/* + * Conventional RPC program numbers + */ + #ifndef RPCBPROG +@@ -160,4 +166,7 @@ extern int nfs_rpc_ping(const struct so + const unsigned short protocol, + const struct timeval *timeout); + ++/* create AUTH_SYS handle with no supplemental groups */ ++extern AUTH * nfs_authsys_create(void); ++ + #endif /* !__NFS_UTILS_NFSRPC_H */ +diff -up nfs-utils-1.2.2/support/include/rpcmisc.h.orig nfs-utils-1.2.2/support/include/rpcmisc.h +--- nfs-utils-1.2.2/support/include/rpcmisc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/rpcmisc.h 2010-09-16 16:29:35.231240528 -0400 +@@ -60,12 +60,12 @@ extern int _rpcsvcdirty; + + static inline struct sockaddr_in *nfs_getrpccaller_in(SVCXPRT *xprt) + { +- return (struct sockaddr_in *)svc_getcaller(xprt); ++ return (struct sockaddr_in *)(char *)svc_getcaller(xprt); + } + + static inline struct sockaddr *nfs_getrpccaller(SVCXPRT *xprt) + { +- return (struct sockaddr *)svc_getcaller(xprt); ++ return (struct sockaddr *)(char *)svc_getcaller(xprt); + } + + #endif /* RPCMISC_H */ +diff -up nfs-utils-1.2.2/support/nfs/cacheio.c.orig nfs-utils-1.2.2/support/nfs/cacheio.c +--- nfs-utils-1.2.2/support/nfs/cacheio.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/cacheio.c 2010-09-16 16:29:35.232229968 -0400 +@@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num) + fprintf(f, "%d ", num); + } + ++void qword_printuint(FILE *f, unsigned int num) ++{ ++ fprintf(f, "%u ", num); ++} ++ + int qword_eol(FILE *f) + { + int err; +@@ -236,6 +241,20 @@ int qword_get_int(char **bpp, int *anint + return 0; + } + ++int qword_get_uint(char **bpp, unsigned int *anint) ++{ ++ char buf[50]; ++ char *ep; ++ unsigned int rv; ++ int len = qword_get(bpp, buf, 50); ++ if (len < 0) return -1; ++ if (len ==0) return -1; ++ rv = strtoul(buf, &ep, 0); ++ if (*ep) return -1; ++ *anint = rv; ++ return 0; ++} ++ + #define READLINE_BUFFER_INCREMENT 2048 + + int readline(int fd, char **buf, int *lenp) +@@ -330,7 +349,7 @@ cache_flush(int force) + sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]); + fd = open(path, O_RDWR); + if (fd >= 0) { +- if (write(fd, stime, strlen(stime)) != strlen(stime)) { ++ if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { + xlog_warn("Writing to '%s' failed: errno %d (%s)", + path, errno, strerror(errno)); + } +diff -up nfs-utils-1.2.2/support/nfs/conffile.c.orig nfs-utils-1.2.2/support/nfs/conffile.c +--- nfs-utils-1.2.2/support/nfs/conffile.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/conffile.c 2010-09-16 16:29:35.232229968 -0400 +@@ -49,7 +49,7 @@ + #include "conffile.h" + #include "xlog.h" + +-static void conf_load_defaults (int); ++static void conf_load_defaults(void); + static int conf_set(int , char *, char *, char *, + char *, int , int ); + +@@ -212,7 +212,7 @@ conf_parse_line(int trans, char *line, s + { + char *val, *ptr; + size_t i; +- int j; ++ size_t j; + static char *section = 0; + static char *arg = 0; + static int ln = 0; +@@ -353,7 +353,7 @@ conf_parse(int trans, char *buf, size_t + } + + static void +-conf_load_defaults(int tr) ++conf_load_defaults(void) + { + /* No defaults */ + return; +@@ -412,7 +412,7 @@ conf_reinit(void) + trans = conf_begin(); + + /* Load default configuration values. */ +- conf_load_defaults(trans); ++ conf_load_defaults(); + + /* Free potential existing configuration. */ + if (conf_addr) { +diff -up nfs-utils-1.2.2/support/nfs/getfh.c.orig nfs-utils-1.2.2/support/nfs/getfh.c +--- nfs-utils-1.2.2/support/nfs/getfh.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/getfh.c 2010-09-16 16:29:35.233230434 -0400 +@@ -19,60 +19,112 @@ + #include + #include "nfslib.h" + ++/** ++ * getfh_old - ask the kernel for an NFSv2 file handle via nfsctl() ++ * @sin: pointer to IPv4 address of a client ++ * @dev: device number of device where requested object resides ++ * @ino: inode number of requested object ++ * ++ * Returns a pointer to an NFSv2 file handle, or NULL if some error ++ * occurred. errno is set to reflect the specifics of the error. ++ */ + struct nfs_fh_len * +-getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino) ++getfh_old(const struct sockaddr_in *sin, const dev_t dev, const ino_t ino) + { + union nfsctl_res res; + struct nfsctl_arg arg; + static struct nfs_fh_len rfh; + ++ if (sin->sin_family != AF_INET) { ++ errno = EAFNOSUPPORT; ++ return NULL; ++ } ++ ++ memset(&arg, 0, sizeof(arg)); ++ memset(&res, 0, sizeof(res)); ++ + arg.ca_version = NFSCTL_VERSION; + arg.ca_getfh.gf_version = 2; /* obsolete */ + arg.ca_getfh.gf_dev = dev; + arg.ca_getfh.gf_ino = ino; +- memcpy(&arg.ca_getfh.gf_addr, addr, sizeof(struct sockaddr_in)); ++ memcpy(&arg.ca_getfh.gf_addr, sin, sizeof(*sin)); + + if (nfsctl(NFSCTL_GETFH, &arg, &res) < 0) + return NULL; + ++ memset(&rfh, 0, sizeof(rfh)); + rfh.fh_size = 32; + memcpy(rfh.fh_handle, &res.cr_getfh, 32); + return &rfh; + } + ++/** ++ * getfh - ask the kernel for an NFSv2 file handle via nfsctl() ++ * @sin: pointer to IPv4 address of a client ++ * @path: pointer to a '\0'-terminated ASCII string containing an pathname ++ * ++ * Returns a pointer to an NFSv2 file handle, or NULL if some error ++ * occurred. errno is set to reflect the specifics of the error. ++ */ + struct nfs_fh_len * +-getfh(struct sockaddr *addr, const char *path) ++getfh(const struct sockaddr_in *sin, const char *path) + { + static union nfsctl_res res; + struct nfsctl_arg arg; + static struct nfs_fh_len rfh; + ++ if (sin->sin_family != AF_INET) { ++ errno = EAFNOSUPPORT; ++ return NULL; ++ } ++ ++ memset(&arg, 0, sizeof(arg)); ++ memset(&res, 0, sizeof(res)); ++ + arg.ca_version = NFSCTL_VERSION; + arg.ca_getfd.gd_version = 2; /* obsolete */ + strncpy(arg.ca_getfd.gd_path, path, + sizeof(arg.ca_getfd.gd_path) - 1); + arg.ca_getfd.gd_path[sizeof (arg.ca_getfd.gd_path) - 1] = '\0'; +- memcpy(&arg.ca_getfd.gd_addr, addr, sizeof(struct sockaddr_in)); ++ memcpy(&arg.ca_getfd.gd_addr, sin, sizeof(*sin)); + + if (nfsctl(NFSCTL_GETFD, &arg, &res) < 0) + return NULL; + ++ memset(&rfh, 0, sizeof(rfh)); + rfh.fh_size = 32; + memcpy(rfh.fh_handle, &res.cr_getfh, 32); + return &rfh; + } + ++/** ++ * getfh_size - ask the kernel for a file handle via nfsctl() ++ * @sin: pointer to IPv4 address of a client ++ * @path: pointer to a '\0'-terminated ASCII string containing an pathname ++ * @size: maximum size, in bytes, of the returned file handle ++ * ++ * Returns a pointer to an NFSv3 file handle, or NULL if some error ++ * occurred. errno is set to reflect the specifics of the error. ++ */ + struct nfs_fh_len * +-getfh_size(struct sockaddr *addr, const char *path, int size) ++getfh_size(const struct sockaddr_in *sin, const char *path, const int size) + { + static union nfsctl_res res; + struct nfsctl_arg arg; + ++ if (sin->sin_family != AF_INET) { ++ errno = EAFNOSUPPORT; ++ return NULL; ++ } ++ ++ memset(&arg, 0, sizeof(arg)); ++ memset(&res, 0, sizeof(res)); ++ + arg.ca_version = NFSCTL_VERSION; + strncpy(arg.ca_getfs.gd_path, path, + sizeof(arg.ca_getfs.gd_path) - 1); + arg.ca_getfs.gd_path[sizeof (arg.ca_getfs.gd_path) - 1] = '\0'; +- memcpy(&arg.ca_getfs.gd_addr, addr, sizeof(struct sockaddr_in)); ++ memcpy(&arg.ca_getfs.gd_addr, sin, sizeof(*sin)); + arg.ca_getfs.gd_maxlen = size; + + if (nfsctl(NFSCTL_GETFS, &arg, &res) < 0) +diff -up nfs-utils-1.2.2/support/nfs/getport.c.orig nfs-utils-1.2.2/support/nfs/getport.c +--- nfs-utils-1.2.2/support/nfs/getport.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/getport.c 2010-09-16 16:29:35.234229813 -0400 +@@ -216,6 +216,21 @@ nfs_get_proto(const char *netid, sa_fami + struct netconfig *nconf; + struct protoent *proto; + ++ /* ++ * IANA does not define a protocol number for rdma netids, ++ * since "rdma" is not an IP protocol. ++ */ ++ if (strcmp(netid, "rdma") == 0) { ++ *family = AF_INET; ++ *protocol = NFSPROTO_RDMA; ++ return 1; ++ } ++ if (strcmp(netid, "rdma6") == 0) { ++ *family = AF_INET6; ++ *protocol = NFSPROTO_RDMA; ++ return 1; ++ } ++ + nconf = getnetconfigent(netid); + if (nconf == NULL) + return 0; +@@ -242,6 +257,16 @@ nfs_get_proto(const char *netid, sa_fami + { + struct protoent *proto; + ++ /* ++ * IANA does not define a protocol number for rdma netids, ++ * since "rdma" is not an IP protocol. ++ */ ++ if (strcmp(netid, "rdma") == 0) { ++ *family = AF_INET; ++ *protocol = NFSPROTO_RDMA; ++ return 1; ++ } ++ + proto = getprotobyname(netid); + if (proto == NULL) + return 0; +diff -up nfs-utils-1.2.2/support/nfs/nfs_mntent.c.orig nfs-utils-1.2.2/support/nfs/nfs_mntent.c +--- nfs-utils-1.2.2/support/nfs/nfs_mntent.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/nfs_mntent.c 2010-09-16 16:29:35.234229813 -0400 +@@ -28,7 +28,7 @@ static char * + mangle(const char *arg) { + const unsigned char *s = (const unsigned char *)arg; + char *ss, *sp; +- int n; ++ unsigned int n; + + n = strlen(arg); + ss = sp = xmalloc(4*n+1); +diff -up nfs-utils-1.2.2/support/nfs/rmtab.c.orig nfs-utils-1.2.2/support/nfs/rmtab.c +--- nfs-utils-1.2.2/support/nfs/rmtab.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/rmtab.c 2010-09-16 16:29:35.235219822 -0400 +@@ -19,6 +19,18 @@ + #include + #include "nfslib.h" + ++/* ++ * Colons in incoming IPv6 presentation addresses have to ++ * replaced with another character, since rmtab already ++ * uses colons to delineate fields. ++ * ++ * Use a printable character, but one that would never be ++ * found in a presentation address or domain name ++ */ ++#define IPV6_COLON ';' ++ ++#define LINELEN (2048) ++ + static FILE *rmfp = NULL; + + int +@@ -56,7 +68,8 @@ struct rmtabent * + fgetrmtabent(FILE *fp, int log, long *pos) + { + static struct rmtabent re; +- char buf[2048], *count, *host, *path; ++ char *count, *host, *path, *c; ++ static char buf[LINELEN]; + + errno = 0; + if (!fp) +@@ -84,10 +97,16 @@ fgetrmtabent(FILE *fp, int log, long *po + else + re.r_count = 1; + } while (0); ++ + strncpy(re.r_client, host, sizeof (re.r_client) - 1); + re.r_client[sizeof (re.r_client) - 1] = '\0'; ++ for (c = re.r_client; *c != '\0'; c++) ++ if (*c == IPV6_COLON) ++ *c = ':'; ++ + strncpy(re.r_path, path, sizeof (re.r_path) - 1); + re.r_path[sizeof (re.r_path) - 1] = '\0'; ++ + return &re; + } + +@@ -100,10 +119,27 @@ putrmtabent(struct rmtabent *rep, long * + void + fputrmtabent(FILE *fp, struct rmtabent *rep, long *pos) + { ++ static char buf[LINELEN]; ++ char *c; ++ + if (!fp || (pos && fseek (fp, *pos, SEEK_SET) != 0)) + return; +- fprintf(fp, "%s:%s:0x%.8x\n", rep->r_client, rep->r_path, +- rep->r_count); ++ ++ /* ++ * To avoid confusing the token parser in fgetrmtabent(), ++ * convert colons in incoming IPv6 presentation addresses ++ * to semicolons. ++ */ ++ if (strlen(rep->r_client) > sizeof(buf)) { ++ xlog(L_ERROR, "client name too large"); ++ return; ++ } ++ strncpy(buf, rep->r_client, sizeof(buf)); ++ for (c = buf; *c != '\0'; c++) ++ if (*c == ':') ++ *c = IPV6_COLON; ++ ++ (void)fprintf(fp, "%s:%s:0x%.8x\n", buf, rep->r_path, rep->r_count); + } + + void +diff -up nfs-utils-1.2.2/support/nfs/rpcdispatch.c.orig nfs-utils-1.2.2/support/nfs/rpcdispatch.c +--- nfs-utils-1.2.2/support/nfs/rpcdispatch.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/rpcdispatch.c 2010-09-16 16:29:35.236229903 -0400 +@@ -27,12 +27,12 @@ rpc_dispatch(struct svc_req *rqstp, SVCX + { + struct rpc_dentry *dent; + +- if (rqstp->rq_vers > nvers) { ++ if (((int)rqstp->rq_vers) > nvers) { + svcerr_progvers(transp, 1, nvers); + return; + } + dtable += (rqstp->rq_vers - 1); +- if (rqstp->rq_proc > dtable->nproc) { ++ if (((int)rqstp->rq_proc) > dtable->nproc) { + svcerr_noproc(transp); + return; + } +diff -up nfs-utils-1.2.2/support/nfs/rpcmisc.c.orig nfs-utils-1.2.2/support/nfs/rpcmisc.c +--- nfs-utils-1.2.2/support/nfs/rpcmisc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/rpcmisc.c 2010-09-16 16:29:35.237240603 -0400 +@@ -154,7 +154,7 @@ rpc_init(char *name, int prog, int vers, + sock = makesock(defport, IPPROTO_UDP); + } + if (sock == RPC_ANYSOCK) +- sock = svcudp_socket (prog, 1); ++ sock = svcudp_socket (prog); + transp = svcudp_create(sock); + if (transp == NULL) { + xlog(L_FATAL, "cannot create udp service."); +diff -up nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig nfs-utils-1.2.2/support/nfs/rpc_socket.c +--- nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/rpc_socket.c 2010-09-16 16:29:35.236229903 -0400 +@@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog + + return program; + } ++ ++/* ++ * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. ++ * If there are more than that, trying to determine which ones to include ++ * in the list is problematic. This function creates an auth handle that ++ * only has the primary gid in the supplemental gids list. It's intended to ++ * be used for protocols where credentials really don't matter much (the MNT ++ * protocol, for instance). ++ */ ++AUTH * ++nfs_authsys_create(void) ++{ ++ char machname[MAXHOSTNAMELEN + 1]; ++ uid_t uid = geteuid(); ++ gid_t gid = getegid(); ++ ++ if (gethostname(machname, sizeof(machname)) == -1) ++ return NULL; ++ ++ return authunix_create(machname, uid, gid, 1, &gid); ++} +diff -up nfs-utils-1.2.2/support/nfs/svc_socket.c.orig nfs-utils-1.2.2/support/nfs/svc_socket.c +--- nfs-utils-1.2.2/support/nfs/svc_socket.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/svc_socket.c 2010-09-16 16:29:35.237240603 -0400 +@@ -157,9 +157,9 @@ svctcp_socket (u_long number, int reuse) + * Create and bind a UDP socket based on program number + */ + int +-svcudp_socket (u_long number, int reuse) ++svcudp_socket (u_long number) + { +- return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, 0); ++ return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, FALSE); + } + + #ifdef TEST +@@ -174,7 +174,7 @@ check (u_long number, u_short port, int + if (protocol == IPPROTO_TCP) + socket = svctcp_socket (number, reuse); + else +- socket = svcudp_socket (number, reuse); ++ socket = svcudp_socket (number); + + if (socket < 0) + return 1; +diff -up nfs-utils-1.2.2/support/nsm/file.c.orig nfs-utils-1.2.2/support/nsm/file.c +--- nfs-utils-1.2.2/support/nsm/file.c.orig 2010-09-16 15:40:15.598012398 -0400 ++++ nfs-utils-1.2.2/support/nsm/file.c 2010-09-16 16:29:35.238240456 -0400 +@@ -67,7 +67,9 @@ + #endif + + #include ++#ifdef HAVE_SYS_CAPABILITY_H + #include ++#endif + #include + #include + +@@ -348,6 +350,7 @@ nsm_is_default_parentdir(void) + static _Bool + nsm_clear_capabilities(void) + { ++#ifdef HAVE_SYS_CAPABILITY_H + cap_t caps; + + caps = cap_from_text("cap_net_bind_service=ep"); +@@ -363,6 +366,7 @@ nsm_clear_capabilities(void) + } + + (void)cap_free(caps); ++#endif + return true; + } + +diff -up nfs-utils-1.2.2/tools/Makefile.am.orig nfs-utils-1.2.2/tools/Makefile.am +--- nfs-utils-1.2.2/tools/Makefile.am.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/tools/Makefile.am 2010-09-16 16:29:35.239219806 -0400 +@@ -6,6 +6,6 @@ if CONFIG_RPCGEN + OPTDIRS += rpcgen + endif + +-SUBDIRS = locktest rpcdebug nlmtest $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff -up nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig nfs-utils-1.2.2/tools/mountstats/Makefile.am +--- nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig 2010-09-16 16:29:35.239219806 -0400 ++++ nfs-utils-1.2.2/tools/mountstats/Makefile.am 2010-09-16 16:29:35.239219806 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = mountstats.py ++ ++man8_MANS = mountstats.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 mountstats.py $(DESTDIR)$(sbindir)/mountstats ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig nfs-utils-1.2.2/tools/mountstats/mountstats.man +--- nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig 2010-09-16 16:29:35.240054628 -0400 ++++ nfs-utils-1.2.2/tools/mountstats/mountstats.man 2010-09-16 16:29:35.240054628 -0400 +@@ -0,0 +1,32 @@ ++.\" ++.\" mountstats(8) ++.\" ++.TH mountstats 8 "15 Apr 2010" ++.SH NAME ++mountstats \- Displays NFS client per-mount statistics ++.SH SYNOPSIS ++.BI "mountstats [" "] " " [ " "]" ++.SH DESCRIPTION ++The ++.B mountstats ++command displays NFS client statisitics on each given ++.I ++.SH OPTIONS ++.TP ++.B " \-\-nfs ++display only the NFS statistics ++.TP ++.B " \-\-rpc ++display only the RPC statistics ++.TP ++.B " \-\-version ++display the version of this command ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR nfsiostat (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff -up nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am +--- nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig 2010-09-16 16:29:35.240054628 -0400 ++++ nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am 2010-09-16 16:29:35.240054628 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = nfs-iostat.py ++ ++man8_MANS = nfsiostat.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 nfs-iostat.py $(DESTDIR)$(sbindir)/nfsiostat ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man +--- nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig 2010-09-16 16:29:35.241239795 -0400 ++++ nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man 2010-09-16 16:29:35.241239795 -0400 +@@ -0,0 +1,71 @@ ++.\" ++.\" nfsiostat(8) ++.\" ++.TH nfsiostat 8 "15 Apr 2010" ++.SH NAME ++nfsiostat \- Emulate iostat for NFS mount points using /proc/self/mountstats ++.SH SYNOPSIS ++.BI "nfsiostat [[" "] [" "]] [" "][" "] ++.SH DESCRIPTION ++The ++.B nfsiostat ++command displays NFS client per-mount statisitics. ++.TP ++ ++specifies the amount of time in seconds between each report. ++The first report contains statistics for the time since each file ++system was mounted. Each subsequent report contains statistics collected ++during the interval since the previous report. ++.TP ++ ++If the ++.I ++parameter is ++specified, the value of ++.I ++determines the number of reports generated at ++. ++seconds apart. if the interval parameter is ++specified without the ++.I ++parameter, the command generates reports continuously. ++.TP ++ ++Define below ++.TP ++ ++If one or more ++.I ++names are specified, statistics for only these mount points will ++be displayed. Otherwise, all NFS mount points on the client are listed. ++.SH OPTIONS ++.TP ++.B \-a " or " \-\-attr ++displays statistics related to the attribute cache ++.TP ++.B \-d " or " \-\-dir ++displays statistics related to directory operations ++.TP ++.B \-h " or " \-\-help ++shows help message and exit ++.TP ++.B \-l LIST or " \-\-list=LIST ++only print stats for first LIST mount points ++.TP ++.B \-p " or " \-\-page ++displays statistics related to the page cache ++.TP ++.B \-s " or " \-\-sort ++Sort NFS mount points by ops/second ++.TP ++.B \-\-version ++show program's version number and exit ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR mountstats (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff -up nfs-utils-1.2.2/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-1.2.2/tools/nfs-iostat/nfs-iostat.py +--- nfs-utils-1.2.2/tools/nfs-iostat/nfs-iostat.py.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/tools/nfs-iostat/nfs-iostat.py 2010-09-16 16:29:35.241239795 -0400 +@@ -366,6 +366,12 @@ class DeviceData: + sends = float(self.__rpc_data['rpcsends']) + if sample_time == 0: + sample_time = float(self.__nfs_data['age']) ++ # sample_time could still be zero if the export was just mounted. ++ # Set it to 1 to avoid divide by zero errors in this case since we'll ++ # likely still have relevant mount statistics to show. ++ # ++ if sample_time == 0: ++ sample_time = 1; + if sends != 0: + backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time + else: +diff -up nfs-utils-1.2.2/utils/exportfs/exportfs.c.orig nfs-utils-1.2.2/utils/exportfs/exportfs.c +--- nfs-utils-1.2.2/utils/exportfs/exportfs.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/exportfs/exportfs.c 2010-09-16 16:29:35.242230383 -0400 +@@ -12,20 +12,24 @@ + #include + #endif + ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include +-#include "xmalloc.h" ++ ++#include "sockaddr.h" + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "xmalloc.h" + #include "xlog.h" + + static void export_all(int verbose); +@@ -34,13 +38,15 @@ static void unexportfs(char *arg, int ve + static void exports_update(int verbose); + static void dump(int verbose); + static void error(nfs_export *exp, int err); +-static void usage(void); ++static void usage(const char *progname); + static void validate_export(nfs_export *exp); ++static int matchhostname(const char *hostname1, const char *hostname2); + + int + main(int argc, char **argv) + { + char *options = NULL; ++ char *progname = NULL; + int f_export = 1; + int f_all = 0; + int f_verbose = 0; +@@ -50,7 +56,14 @@ main(int argc, char **argv) + int new_cache = 0; + int force_flush = 0; + +- xlog_open("exportfs"); ++ if ((progname = strrchr(argv[0], '/')) != NULL) ++ progname++; ++ else ++ progname = argv[0]; ++ ++ xlog_open(progname); ++ xlog_stderr(1); ++ xlog_syslog(0); + + export_errno = 0; + +@@ -79,21 +92,21 @@ main(int argc, char **argv) + force_flush = 1; + break; + default: +- usage(); ++ usage(progname); + break; + } + } + + if (optind != argc && f_all) { +- fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n"); ++ xlog(L_ERROR, "extra arguments are not permitted with -a or -r"); + return 1; + } + if (f_ignore && (f_all || ! f_export)) { +- fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n"); ++ xlog(L_ERROR, "-i not meaningful with -a, -r or -u"); + return 1; + } + if (f_reexport && ! f_export) { +- fprintf(stderr, "exportfs: -r and -u are incompatible.\n"); ++ xlog(L_ERROR, "-r and -u are incompatible"); + return 1; + } + new_cache = check_new_cache(); +@@ -102,8 +115,10 @@ main(int argc, char **argv) + if (new_cache) + cache_flush(1); + else { +- fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n"); +- exit(1); ++ xlog(L_ERROR, "-f is available only " ++ "with new cache controls. " ++ "Mount /proc/fs/nfsd first"); ++ return 1; + } + return 0; + } else { +@@ -232,7 +247,7 @@ exportfs(char *arg, char *options, int v + { + struct exportent *eep; + nfs_export *exp; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + char *path; + char *hname = arg; + int htype; +@@ -241,36 +256,25 @@ exportfs(char *arg, char *options, int v + *path++ = '\0'; + + if (!path || *path != '/') { +- fprintf(stderr, "Invalid exporting option: %s\n", arg); ++ xlog(L_ERROR, "Invalid exporting option: %s", arg); + return; + } + +- if ((htype = client_gettype(hname)) == MCL_FQDN && +- (hp = gethostbyname(hname)) != NULL) { +- struct hostent *hp2 = hostent_dup (hp); +- hp = gethostbyaddr(hp2->h_addr, hp2->h_length, +- hp2->h_addrtype); +- if (hp) { +- free(hp2); +- hp = hostent_dup(hp); +- } else +- hp = hp2; +- exp = export_find(hp, path); +- hname = hp->h_name; +- } else { ++ if ((htype = client_gettype(hname)) == MCL_FQDN) { ++ ai = host_addrinfo(hname); ++ if (ai != NULL) { ++ exp = export_find(ai, path); ++ hname = ai->ai_canonname; ++ } ++ } else + exp = export_lookup(hname, path, 0); +- } + + if (!exp) { + if (!(eep = mkexportent(hname, path, options)) || +- !(exp = export_create(eep, 0))) { +- if (hp) free (hp); +- return; +- } +- } else if (!updateexportent(&exp->m_export, options)) { +- if (hp) free (hp); +- return; +- } ++ !(exp = export_create(eep, 0))) ++ goto out; ++ } else if (!updateexportent(&exp->m_export, options)) ++ goto out; + + if (verbose) + printf("exporting %s:%s\n", exp->m_client->m_hostname, +@@ -280,14 +284,16 @@ exportfs(char *arg, char *options, int v + exp->m_changed = 1; + exp->m_warned = 0; + validate_export(exp); +- if (hp) free (hp); ++ ++out: ++ freeaddrinfo(ai); + } + + static void + unexportfs(char *arg, int verbose) + { + nfs_export *exp; +- struct hostent *hp = NULL; ++ struct addrinfo *ai = NULL; + char *path; + char *hname = arg; + int htype; +@@ -296,16 +302,14 @@ unexportfs(char *arg, int verbose) + *path++ = '\0'; + + if (!path || *path != '/') { +- fprintf(stderr, "Invalid unexporting option: %s\n", +- arg); ++ xlog(L_ERROR, "Invalid unexporting option: %s", arg); + return; + } + + if ((htype = client_gettype(hname)) == MCL_FQDN) { +- if ((hp = gethostbyname(hname)) != 0) { +- hp = hostent_dup (hp); +- hname = (char *) hp->h_name; +- } ++ ai = host_addrinfo(hname); ++ if (ai) ++ hname = ai->ai_canonname; + } + + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { +@@ -341,7 +345,7 @@ unexportfs(char *arg, int verbose) + exp->m_mayexport = 0; + } + +- if (hp) free (hp); ++ freeaddrinfo(ai); + } + + static int can_test(void) +@@ -393,14 +397,12 @@ validate_export(nfs_export *exp) + int fs_has_fsid = 0; + + if (stat(path, &stb) < 0) { +- fprintf(stderr, "exportfs: Warning: %s does not exist\n", +- path); ++ xlog(L_ERROR, "Failed to stat %s: %m \n", path); + return; + } + if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { +- fprintf(stderr, "exportfs: Warning: %s is neither " +- "a directory nor a file.\n" +- " remote access will fail\n", path); ++ xlog(L_ERROR, "%s is neither a directory nor a file. " ++ "Remote access will fail", path); + return; + } + if (!can_test()) +@@ -413,24 +415,75 @@ validate_export(nfs_export *exp) + if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid || + fs_has_fsid) { + if ( !test_export(path, 1)) { +- fprintf(stderr, "exportfs: Warning: %s does not " +- "support NFS export.\n", +- path); ++ xlog(L_ERROR, "%s does not support NFS export", path); + return; + } + } else if ( ! test_export(path, 0)) { + if (test_export(path, 1)) +- fprintf(stderr, "exportfs: Warning: %s requires fsid= " +- "for NFS export\n", path); ++ xlog(L_ERROR, "%s requires fsid= for NFS export", path); + else +- fprintf(stderr, "exportfs: Warning: %s does not " +- "support NFS export.\n", +- path); ++ xlog(L_ERROR, "%s does not support NFS export", path); + return; + + } + } + ++static _Bool ++is_hostname(const char *sp) ++{ ++ if (*sp == '\0' || *sp == '@') ++ return false; ++ ++ for (; *sp != '\0'; sp++) { ++ if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') ++ return false; ++ if (*sp == '\\' && sp[1] != '\0') ++ sp++; ++ } ++ ++ return true; ++} ++ ++static int ++matchhostname(const char *hostname1, const char *hostname2) ++{ ++ struct addrinfo *results1 = NULL, *results2 = NULL; ++ struct addrinfo *ai1, *ai2; ++ int result = 0; ++ ++ if (strcasecmp(hostname1, hostname2) == 0) ++ return 1; ++ ++ /* ++ * Don't pass export wildcards or netgroup names to DNS ++ */ ++ if (!is_hostname(hostname1) || !is_hostname(hostname2)) ++ return 0; ++ ++ results1 = host_addrinfo(hostname1); ++ if (results1 == NULL) ++ goto out; ++ results2 = host_addrinfo(hostname2); ++ if (results2 == NULL) ++ goto out; ++ ++ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) { ++ result = 1; ++ goto out; ++ } ++ ++ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next) ++ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next) ++ if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) { ++ result = 1; ++ break; ++ } ++ ++out: ++ freeaddrinfo(results1); ++ freeaddrinfo(results2); ++ return result; ++} + + static char + dumpopt(char c, char *fmt, ...) +@@ -532,13 +585,13 @@ dump(int verbose) + static void + error(nfs_export *exp, int err) + { +- fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, ++ xlog(L_ERROR, "%s:%s: %s\n", exp->m_client->m_hostname, + exp->m_export.e_path, strerror(err)); + } + + static void +-usage(void) ++usage(const char *progname) + { +- fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n"); ++ fprintf(stderr, "usage: %s [-aruv] [host:/path]\n", progname); + exit(1); + } +diff -up nfs-utils-1.2.2/utils/gssd/context.h.orig nfs-utils-1.2.2/utils/gssd/context.h +--- nfs-utils-1.2.2/utils/gssd/context.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context.h 2010-09-16 16:29:35.243229775 -0400 +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004,2008 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -36,6 +36,10 @@ + /* Hopefully big enough to hold any serialized context */ + #define MAX_CTX_LEN 4096 + ++/* New context format flag values */ ++#define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++#define KRB5_CTX_FLAG_CFX 0x00000002 ++#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + + int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, + gss_OID mech, int32_t *endtime); +diff -up nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig nfs-utils-1.2.2/utils/gssd/context_lucid.c +--- nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context_lucid.c 2010-09-16 16:29:35.243229775 -0400 +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + +@@ -76,7 +77,7 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc + unsigned char fakeseed[FAKESEED_SIZE]; + uint32_t word_send_seq; + gss_krb5_lucid_key_t enc_key; +- int i; ++ uint32_t i; + char *skd, *dkd; + gss_buffer_desc fakeoid; + +@@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc + * Note that the rfc1964 version only supports DES enctypes. + */ + if (lctx->rfc1964_kd.ctx_key.type != 4) { +- printerr(1, "prepare_krb5_rfc1964_buffer: " +- "overriding heimdal keytype (%d => %d)\n", +- lctx->rfc1964_kd.ctx_key.type, 4); ++ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); + lctx->rfc1964_kd.ctx_key.type = 4; + } + #endif +- printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " +- "enctype %d and length %d\n", +- lctx->rfc1964_kd.ctx_key.type, ++ printerr(2, "%s: serializing keys with enctype %d and length %d\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, + lctx->rfc1964_kd.ctx_key.length); + + /* derive the encryption key and copy it into buffer */ +@@ -158,11 +157,102 @@ out_err: + return -1; + } + ++/* Flags for version 2 context flags */ ++#define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++#define KRB5_CTX_FLAG_CFX 0x00000002 ++#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ ++/* ++ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx), ++ * to send to the kernel for newer encryption types -- or for DES3. ++ * ++ * The new format is: ++ * ++ * u32 flags; ++ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++ * #define KRB5_CTX_FLAG_CFX 0x00000002 ++ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ * s32 endtime; ++ * u64 seq_send; ++ * u32 enctype; ( encrption type of key ) ++ * raw key; ( raw key bytes (kernel will derive)) ++ * ++ */ + static int +-prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, ++prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx, + gss_buffer_desc *buf, int32_t *endtime) + { +- printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); ++ char *p, *end; ++ uint32_t v2_flags = 0; ++ uint32_t enctype; ++ uint32_t keysize; ++ ++ if (!(buf->value = calloc(1, MAX_CTX_LEN))) ++ goto out_err; ++ p = buf->value; ++ end = buf->value + MAX_CTX_LEN; ++ ++ /* Version 2 */ ++ if (lctx->initiate) ++ v2_flags |= KRB5_CTX_FLAG_INITIATOR; ++ if (lctx->protocol != 0) ++ v2_flags |= KRB5_CTX_FLAG_CFX; ++ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) ++ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; ++ ++ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; ++ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; ++ if (endtime) ++ *endtime = lctx->endtime; ++ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; ++ ++ /* Protocol 0 here implies DES3 or RC4 */ ++ printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); ++ if (lctx->protocol == 0) { ++ enctype = lctx->rfc1964_kd.ctx_key.type; ++ keysize = lctx->rfc1964_kd.ctx_key.length; ++ } else { ++ if (lctx->cfx_kd.have_acceptor_subkey) { ++ enctype = lctx->cfx_kd.acceptor_subkey.type; ++ keysize = lctx->cfx_kd.acceptor_subkey.length; ++ } else { ++ enctype = lctx->cfx_kd.ctx_key.type; ++ keysize = lctx->cfx_kd.ctx_key.length; ++ } ++ } ++ printerr(2, "%s: serializing key with enctype %d and size %d\n", ++ __FUNCTION__, enctype, keysize); ++ ++ if (WRITE_BYTES(&p, end, enctype)) goto out_err; ++ ++ if (lctx->protocol == 0) { ++ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, ++ lctx->rfc1964_kd.ctx_key.length)) ++ goto out_err; ++ } else { ++ if (lctx->cfx_kd.have_acceptor_subkey) { ++ if (write_bytes(&p, end, ++ lctx->cfx_kd.acceptor_subkey.data, ++ lctx->cfx_kd.acceptor_subkey.length)) ++ goto out_err; ++ } else { ++ if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data, ++ lctx->cfx_kd.ctx_key.length)) ++ goto out_err; ++ } ++ } ++ ++ buf->length = p - (char *)buf->value; ++ return 0; ++ ++out_err: ++ printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n", ++ __FUNCTION__); ++ if (buf->value) { ++ free(buf->value); ++ buf->value = NULL; ++ } ++ buf->length = 0; + return -1; + } + +@@ -176,7 +266,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + gss_krb5_lucid_context_v1_t *lctx = 0; + int retcode = 0; + +- printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); ++ printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); + maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, + 1, &return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -198,11 +288,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + break; + } + +- /* Now lctx points to a lucid context that we can send down to kernel */ +- if (lctx->protocol == 0) ++ /* ++ * Now lctx points to a lucid context that we can send down to kernel ++ * ++ * Note: we send down different information to the kernel depending ++ * on the protocol version and the enctyption type. ++ * For protocol version 0 with all enctypes besides DES3, we use ++ * the original format. For protocol version != 0 or DES3, we ++ * send down the new style information. ++ */ ++ ++ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) + retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); + else +- retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf, endtime); ++ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime); + + maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -212,8 +311,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + } + + if (retcode) { +- printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " +- "failed (retcode = %d)\n", retcode); ++ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", ++ __FUNCTION__, retcode); + goto out_err; + } + +@@ -223,4 +322,7 @@ out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); + return -1; + } ++ ++ ++ + #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ +diff -up nfs-utils-1.2.2/utils/gssd/context_mit.c.orig nfs-utils-1.2.2/utils/gssd/context_mit.c +--- nfs-utils-1.2.2/utils/gssd/context_mit.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context_mit.c 2010-09-16 16:29:35.244230359 -0400 +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004-2006 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -52,8 +53,7 @@ + /* XXX argggg, there's gotta be a better way than just duplicating this + * whole struct. Unfortunately, this is in a "private" header file, + * so this is our best choice at this point :-/ +- * +- * XXX Does this match the Heimdal definition? */ ++ */ + + typedef struct _krb5_gss_ctx_id_rec { + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ +@@ -156,50 +156,122 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + { + krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; + char *p, *end; +- static int constant_one = 1; + static int constant_zero = 0; ++ static int constant_one = 1; ++ static int constant_two = 2; + uint32_t word_seq_send; ++ u_int64_t seq_send_64bit; ++ uint32_t v2_flags = 0; + + if (!(buf->value = calloc(1, MAX_CTX_LEN))) + goto out_err; + p = buf->value; + end = buf->value + MAX_CTX_LEN; + +- if (kctx->initiate) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (kctx->seed_init) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ switch (kctx->enc->enctype) { ++ case ENCTYPE_DES_CBC_CRC: ++ case ENCTYPE_DES_CBC_MD4: ++ case ENCTYPE_DES_CBC_MD5: ++ case ENCTYPE_DES_CBC_RAW: ++ /* Old format of context to the kernel */ ++ if (kctx->initiate) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (kctx->seed_init) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ if (endtime) ++ *endtime = kctx->endtime; ++ word_seq_send = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; ++ if (write_oid(&p, end, kctx->mech_used)) goto out_err; ++ ++ printerr(2, "serialize_krb5_ctx: serializing keys with " ++ "enctype %d and length %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_keyblock(&p, end, kctx->enc)) goto out_err; ++ if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ break; ++ case ENCTYPE_DES3_CBC_RAW: ++ case ENCTYPE_DES3_CBC_SHA1: ++ case ENCTYPE_ARCFOUR_HMAC: ++ case ENCTYPE_ARCFOUR_HMAC_EXP: ++ case ENCTYPE_AES128_CTS_HMAC_SHA1_96: ++ case ENCTYPE_AES256_CTS_HMAC_SHA1_96: ++ /* New format of context to the kernel */ ++ /* u32 flags; ++ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++ * #define KRB5_CTX_FLAG_CFX 0x00000002 ++ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ * s32 endtime; ++ * u64 seq_send; ++ * u32 enctype; ++ * rawkey data ++ */ ++ ++ if (kctx->initiate) ++ v2_flags |= KRB5_CTX_FLAG_INITIATOR; ++ if (kctx->proto == 1) ++ v2_flags |= KRB5_CTX_FLAG_CFX; ++ if (kctx->have_acceptor_subkey) ++ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; ++ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ ++ seq_send_64bit = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; ++ ++ if (kctx->have_acceptor_subkey) { ++ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing subkey " ++ "with enctype %d and size %d\n", ++ kctx->acceptor_subkey->enctype, ++ kctx->acceptor_subkey->length); ++ ++ if (write_bytes(&p, end, ++ kctx->acceptor_subkey->contents, ++ kctx->acceptor_subkey->length)) ++ goto out_err; ++ } else { ++ if (WRITE_BYTES(&p, end, kctx->enc->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing key " ++ "with enctype %d and size %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_bytes(&p, end, kctx->enc->contents, ++ kctx->enc->length)) ++ goto out_err; ++ } ++ break; ++ default: ++ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption " ++ "algorithm %d\n", kctx->enc->enctype); + goto out_err; +- if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; +- if (endtime) +- *endtime = kctx->endtime; +- word_seq_send = kctx->seq_send; +- if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; +- if (write_oid(&p, end, kctx->mech_used)) goto out_err; +- +- printerr(2, "serialize_krb5_ctx: serializing keys with " +- "enctype %d and length %d\n", +- kctx->enc->enctype, kctx->enc->length); +- +- if (write_keyblock(&p, end, kctx->enc)) goto out_err; +- if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ } + + buf->length = p - (char *)buf->value; + return 0; ++ + out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); +- if (buf->value) free(buf->value); ++ if (buf->value) { ++ free(buf->value); ++ } ++ buf->value = NULL; + buf->length = 0; + return -1; + } +diff -up nfs-utils-1.2.2/utils/gssd/gssd.c.orig nfs-utils-1.2.2/utils/gssd/gssd.c +--- nfs-utils-1.2.2/utils/gssd/gssd.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/gssd.c 2010-09-16 16:29:35.245220304 -0400 +@@ -78,7 +78,7 @@ void + sig_hup(int signal) + { + /* don't exit on SIGHUP */ +- printerr(1, "Received SIGHUP... Ignoring.\n"); ++ printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal); + return; + } + +diff -up nfs-utils-1.2.2/utils/gssd/gssd_main_loop.c.orig nfs-utils-1.2.2/utils/gssd/gssd_main_loop.c +--- nfs-utils-1.2.2/utils/gssd/gssd_main_loop.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/gssd_main_loop.c 2010-09-16 16:29:35.245220304 -0400 +@@ -63,6 +63,8 @@ static volatile int dir_changed = 1; + + static void dir_notify_handler(int sig, siginfo_t *si, void *data) + { ++ printerr(2, "dir_notify_handler: sig %d si %p data %p\n", sig, si, data); ++ + dir_changed = 1; + } + +diff -up nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.2/utils/gssd/gssd_proc.c +--- nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/gssd_proc.c 2010-09-16 16:29:35.246249932 -0400 +@@ -600,6 +600,67 @@ update_client_list(void) + return retval; + } + ++/* Encryption types supported by the kernel rpcsec_gss code */ ++int num_krb5_enctypes = 0; ++krb5_enctype *krb5_enctypes = NULL; ++ ++/* ++ * Parse the supported encryption type information ++ */ ++static int ++parse_enctypes(char *enctypes) ++{ ++ int n = 0; ++ char *curr, *comma; ++ int i; ++ static char *cached_types; ++ ++ if (cached_types && strcmp(cached_types, enctypes) == 0) ++ return 0; ++ free(cached_types); ++ ++ if (krb5_enctypes != NULL) { ++ free(krb5_enctypes); ++ krb5_enctypes = NULL; ++ num_krb5_enctypes = 0; ++ } ++ ++ /* count the number of commas */ ++ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { ++ comma = strchr(curr, ','); ++ if (comma != NULL) ++ n++; ++ else ++ break; ++ } ++ /* If no more commas and we're not at the end, there's one more value */ ++ if (*curr != '\0') ++ n++; ++ ++ /* Empty string, return an error */ ++ if (n == 0) ++ return ENOENT; ++ ++ /* Allocate space for enctypes array */ ++ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { ++ return ENOMEM; ++ } ++ ++ /* Now parse each value into the array */ ++ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { ++ krb5_enctypes[i++] = atoi(curr); ++ comma = strchr(curr, ','); ++ if (comma == NULL) ++ break; ++ } ++ ++ num_krb5_enctypes = n; ++ if ((cached_types = malloc(strlen(enctypes)+1))) ++ strcpy(cached_types, enctypes); ++ ++ return 0; ++} ++ + static int + do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + gss_buffer_desc *context_token) +@@ -798,7 +859,7 @@ int create_auth_rpc_client(struct clnt_i + * Do this before creating rpc connection since we won't need + * rpc connection if it fails! + */ +- if (limit_krb5_enctypes(&sec, uid)) { ++ if (limit_krb5_enctypes(&sec)) { + printerr(1, "WARNING: Failed while limiting krb5 " + "encryption types for user with uid %d\n", + uid); +@@ -875,7 +936,7 @@ int create_auth_rpc_client(struct clnt_i + if (sec.cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&min_stat, &sec.cred); + /* Restore euid to original value */ +- if ((save_uid != -1) && (setfsuid(save_uid) != uid)) { ++ if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) { + printerr(0, "WARNING: Failed to restore fsuid" + " to uid %d from %d\n", save_uid, uid); + } +@@ -1100,7 +1161,7 @@ handle_krb5_upcall(struct clnt_info *clp + { + uid_t uid; + +- if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { ++ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { + printerr(0, "WARNING: failed reading uid from krb5 " + "upcall pipe: %s\n", strerror(errno)); + return; +@@ -1114,7 +1175,7 @@ handle_spkm3_upcall(struct clnt_info *cl + { + uid_t uid; + +- if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { ++ if (read(clp->spkm3_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { + printerr(0, "WARNING: failed reading uid from spkm3 " + "upcall pipe: %s\n", strerror(errno)); + return; +@@ -1133,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp + char *mech = NULL; + char *target = NULL; + char *service = NULL; ++ char *enctypes = NULL; + + printerr(1, "handling gssd upcall (%s)\n", clp->dirname); + +@@ -1176,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp + goto out; + } + ++ /* read supported encryption types if supplied */ ++ if ((p = strstr(lbuf, "enctypes=")) != NULL) { ++ enctypes = malloc(lbuflen); ++ if (!enctypes) ++ goto out; ++ if (sscanf(p, "enctypes=%s", enctypes) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse target name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ if (parse_enctypes(enctypes) != 0) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "parsing encryption types failed: errno %d\n", errno); ++ } ++ } ++ + /* read target name */ + if ((p = strstr(lbuf, "target=")) != NULL) { + target = malloc(lbuflen); +@@ -1222,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp + out: + free(lbuf); + free(mech); ++ free(enctypes); + free(target); + free(service); + return; +diff -up nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig nfs-utils-1.2.2/utils/gssd/krb5_util.c +--- nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/krb5_util.c 2010-09-16 16:29:35.247230807 -0400 +@@ -224,6 +224,13 @@ gssd_find_existing_krb5_ccache(uid_t uid + free(namelist[i]); + continue; + } ++ if (uid == 0 && !root_uses_machine_creds && ++ strstr(namelist[i]->d_name, "_machine_")) { ++ printerr(3, "CC file '%s' not available to root\n", ++ statname); ++ free(namelist[i]); ++ continue; ++ } + if (!query_krb5_ccache(buf, &princname, &realm)) { + printerr(3, "CC file '%s' is expired or corrupt\n", + statname); +@@ -292,61 +299,6 @@ gssd_find_existing_krb5_ccache(uid_t uid + return err; + } + +- +-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +-/* +- * this routine obtains a credentials handle via gss_acquire_cred() +- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption +- * types negotiated. +- * +- * XXX Should call some function to determine the enctypes supported +- * by the kernel. (Only need to do that once!) +- * +- * Returns: +- * 0 => all went well +- * -1 => there was an error +- */ +- +-int +-limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) +-{ +- u_int maj_stat, min_stat; +- gss_cred_id_t credh; +- gss_OID_set_desc desired_mechs; +- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, +- ENCTYPE_DES_CBC_MD5, +- ENCTYPE_DES_CBC_MD4 }; +- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); +- +- /* We only care about getting a krb5 cred */ +- desired_mechs.count = 1; +- desired_mechs.elements = &krb5oid; +- +- maj_stat = gss_acquire_cred(&min_stat, NULL, 0, +- &desired_mechs, GSS_C_INITIATE, +- &credh, NULL, NULL); +- +- if (maj_stat != GSS_S_COMPLETE) { +- if (get_verbosity() > 0) +- pgsserr("gss_acquire_cred", +- maj_stat, min_stat, &krb5oid); +- return -1; +- } +- +- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, +- num_enctypes, &enctypes); +- if (maj_stat != GSS_S_COMPLETE) { +- pgsserr("gss_set_allowable_enctypes", +- maj_stat, min_stat, &krb5oid); +- gss_release_cred(&min_stat, &credh); +- return -1; +- } +- sec->cred = credh; +- +- return 0; +-} +-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +- + /* + * Obtain credentials via a key in the keytab given + * a keytab handle and a gssd_k5_kt_princ structure. +@@ -661,24 +613,32 @@ out: + * and has *any* instance (hostname), return 1. + * Otherwise return 0, indicating no match. + */ ++#ifdef HAVE_KRB5 + static int +-realm_and_service_match(krb5_context context, krb5_principal p, +- const char *realm, const char *service) ++realm_and_service_match(krb5_principal p, const char *realm, const char *service) + { +-#ifdef HAVE_KRB5 + /* Must have two components */ + if (p->length != 2) + return 0; ++ + if ((strlen(realm) == p->realm.length) + && (strncmp(realm, p->realm.data, p->realm.length) == 0) + && (strlen(service) == p->data[0].length) + && (strncmp(service, p->data[0].data, p->data[0].length) == 0)) + return 1; ++ ++ return 0; ++} + #else ++static int ++realm_and_service_match(krb5_context context, krb5_principal p, ++ const char *realm, const char *service) ++{ + const char *name, *inst; + + if (p->name.name_string.len != 2) + return 0; ++ + name = krb5_principal_get_comp_string(context, p, 0); + inst = krb5_principal_get_comp_string(context, p, 1); + if (name == NULL || inst == NULL) +@@ -686,9 +646,10 @@ realm_and_service_match(krb5_context con + if ((strcmp(realm, p->realm) == 0) + && (strcmp(service, name) == 0)) + return 1; +-#endif ++ + return 0; + } ++#endif + + /* + * Search the given keytab file looking for an entry with the given +@@ -710,7 +671,7 @@ gssd_search_krb5_keytab(krb5_context con + krb5_kt_cursor cursor; + krb5_error_code code; + struct gssd_k5_kt_princ *ple; +- int retval = -1; ++ int retval = -1, status; + char kt_name[BUFSIZ]; + char *pname; + char *k5err = NULL; +@@ -753,8 +714,12 @@ gssd_search_krb5_keytab(krb5_context con + printerr(4, "Processing keytab entry for principal '%s'\n", + pname); + /* Use the first matching keytab entry found */ +- if ((realm_and_service_match(context, kte->principal, realm, +- service))) { ++#ifdef HAVE_KRB5 ++ status = realm_and_service_match(kte->principal, realm, service); ++#else ++ status = realm_and_service_match(context, kte->principal, realm, service); ++#endif ++ if (status) { + printerr(4, "We WILL use this entry (%s)\n", pname); + ple = get_ple_by_princ(context, kte->principal); + /* +@@ -1304,3 +1269,68 @@ gssd_k5_get_default_realm(char **def_rea + + krb5_free_context(context); + } ++ ++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++/* ++ * this routine obtains a credentials handle via gss_acquire_cred() ++ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption ++ * types negotiated. ++ * ++ * XXX Should call some function to determine the enctypes supported ++ * by the kernel. (Only need to do that once!) ++ * ++ * Returns: ++ * 0 => all went well ++ * -1 => there was an error ++ */ ++ ++int ++limit_krb5_enctypes(struct rpc_gss_sec *sec) ++{ ++ u_int maj_stat, min_stat; ++ gss_cred_id_t credh; ++ gss_OID_set_desc desired_mechs; ++ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, ++ ENCTYPE_DES_CBC_MD5, ++ ENCTYPE_DES_CBC_MD4 }; ++ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); ++ extern int num_krb5_enctypes; ++ extern krb5_enctype *krb5_enctypes; ++ ++ /* We only care about getting a krb5 cred */ ++ desired_mechs.count = 1; ++ desired_mechs.elements = &krb5oid; ++ ++ maj_stat = gss_acquire_cred(&min_stat, NULL, 0, ++ &desired_mechs, GSS_C_INITIATE, ++ &credh, NULL, NULL); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ if (get_verbosity() > 0) ++ pgsserr("gss_acquire_cred", ++ maj_stat, min_stat, &krb5oid); ++ return -1; ++ } ++ ++ /* ++ * If we failed for any reason to produce global ++ * list of supported enctypes, use local default here. ++ */ ++ if (krb5_enctypes == NULL) ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_enctypes, enctypes); ++ else ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_krb5_enctypes, krb5_enctypes); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ pgsserr("gss_set_allowable_enctypes", ++ maj_stat, min_stat, &krb5oid); ++ gss_release_cred(&min_stat, &credh); ++ return -1; ++ } ++ sec->cred = credh; ++ ++ return 0; ++} ++#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +diff -up nfs-utils-1.2.2/utils/gssd/krb5_util.h.orig nfs-utils-1.2.2/utils/gssd/krb5_util.h +--- nfs-utils-1.2.2/utils/gssd/krb5_util.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/krb5_util.h 2010-09-16 16:29:35.248210613 -0400 +@@ -36,7 +36,7 @@ char *gssd_k5_err_msg(krb5_context conte + void gssd_k5_get_default_realm(char **def_realm); + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES +-int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid); ++int limit_krb5_enctypes(struct rpc_gss_sec *sec); + #endif + + /* +diff -up nfs-utils-1.2.2/utils/gssd/svcgssd.c.orig nfs-utils-1.2.2/utils/gssd/svcgssd.c +--- nfs-utils-1.2.2/utils/gssd/svcgssd.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/svcgssd.c 2010-09-16 16:29:35.248210613 -0400 +@@ -160,7 +160,7 @@ void + sig_hup(int signal) + { + /* don't exit on SIGHUP */ +- printerr(1, "Received SIGHUP... Ignoring.\n"); ++ printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal); + return; + } + +diff -up nfs-utils-1.2.2/utils/gssd/svcgssd_proc.c.orig nfs-utils-1.2.2/utils/gssd/svcgssd_proc.c +--- nfs-utils-1.2.2/utils/gssd/svcgssd_proc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/svcgssd_proc.c 2010-09-16 16:29:35.249273951 -0400 +@@ -132,7 +132,7 @@ struct gss_verifier { + #define RPCSEC_GSS_SEQ_WIN 5 + + static int +-send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token, ++send_response(gss_buffer_desc *in_handle, gss_buffer_desc *in_token, + u_int32_t maj_stat, u_int32_t min_stat, + gss_buffer_desc *out_handle, gss_buffer_desc *out_token) + { +@@ -431,12 +431,6 @@ handle_nullreq(FILE *f) { + print_hexl("in_tok", in_tok.value, in_tok.length); + #endif + +- if (in_tok.length < 0) { +- printerr(0, "WARNING: handle_nullreq: " +- "failed parsing request\n"); +- goto out_err; +- } +- + if (in_handle.length != 0) { /* CONTINUE_INIT case */ + if (in_handle.length != sizeof(ctx)) { + printerr(0, "WARNING: handle_nullreq: " +@@ -498,7 +492,7 @@ handle_nullreq(FILE *f) { + do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime, + hostbased_name); + continue_needed: +- send_response(f, &in_handle, &in_tok, maj_stat, min_stat, ++ send_response(&in_handle, &in_tok, maj_stat, min_stat, + &out_handle, &out_tok); + out: + if (ctx_token.value != NULL) +@@ -514,7 +508,7 @@ out: + out_err: + if (ctx != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok); +- send_response(f, &in_handle, &in_tok, maj_stat, min_stat, ++ send_response(&in_handle, &in_tok, maj_stat, min_stat, + &null_token, &null_token); + goto out; + } +diff -up nfs-utils-1.2.2/utils/idmapd/atomicio.c.orig nfs-utils-1.2.2/utils/idmapd/atomicio.c +--- nfs-utils-1.2.2/utils/idmapd/atomicio.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/idmapd/atomicio.c 2010-09-16 16:29:35.250239931 -0400 +@@ -43,7 +43,8 @@ atomicio( + size_t n) + { + char *s = _s; +- ssize_t res, pos = 0; ++ ssize_t res; ++ size_t pos = 0; + + while (n > pos) { + res = (f) (fd, s + pos, n - pos); +diff -up nfs-utils-1.2.2/utils/idmapd/idmapd.c.orig nfs-utils-1.2.2/utils/idmapd/idmapd.c +--- nfs-utils-1.2.2/utils/idmapd/idmapd.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/idmapd/idmapd.c 2010-09-16 16:29:35.250239931 -0400 +@@ -117,8 +117,24 @@ struct idmap_client { + TAILQ_ENTRY(idmap_client) ic_next; + }; + static struct idmap_client nfsd_ic[2] = { +-{IC_IDNAME, "Server", "", IC_IDNAME_CHAN, -1, -1, 0}, +-{IC_NAMEID, "Server", "", IC_NAMEID_CHAN, -1, -1, 0}, ++{ ++ .ic_which = IC_IDNAME, ++ .ic_clid = "", ++ .ic_id = "Server", ++ .ic_path = IC_IDNAME_CHAN, ++ .ic_fd = -1, ++ .ic_dirfd = -1, ++ .ic_scanned = 0 ++}, ++{ ++ .ic_which = IC_NAMEID, ++ .ic_clid = "", ++ .ic_id = "Server", ++ .ic_path = IC_NAMEID_CHAN, ++ .ic_fd = -1, ++ .ic_dirfd = -1, ++ .ic_scanned = 0 ++}, + }; + + TAILQ_HEAD(idmap_clientq, idmap_client); +@@ -170,7 +186,7 @@ flush_nfsd_cache(char *path, time_t now) + fd = open(path, O_RDWR); + if (fd == -1) + return -1; +- if (write(fd, stime, strlen(stime)) != strlen(stime)) { ++ if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { + errx(1, "Flushing nfsd cache failed: errno %d (%s)", + errno, strerror(errno)); + } +@@ -381,7 +397,7 @@ main(int argc, char **argv) + } + + static void +-dirscancb(int fd, short which, void *data) ++dirscancb(int UNUSED(fd), short UNUSED(which), void *data) + { + int nent, i; + struct dirent **ents; +@@ -465,13 +481,13 @@ out: + } + + static void +-svrreopen(int fd, short which, void *data) ++svrreopen(int UNUSED(fd), short UNUSED(which), void *UNUSED(data)) + { + nfsdreopen(); + } + + static void +-clntscancb(int fd, short which, void *data) ++clntscancb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct idmap_clientq *icq = data; + struct idmap_client *ic; +@@ -485,7 +501,7 @@ clntscancb(int fd, short which, void *da + } + + static void +-nfsdcb(int fd, short which, void *data) ++nfsdcb(int UNUSED(fd), short which, void *data) + { + struct idmap_client *ic = data; + struct idmap_msg im; +@@ -660,7 +676,7 @@ imconv(struct idmap_client *ic, struct i + } + + static void +-nfscb(int fd, short which, void *data) ++nfscb(int UNUSED(fd), short which, void *data) + { + struct idmap_client *ic = data; + struct idmap_msg im; +@@ -845,7 +861,7 @@ nametoidres(struct idmap_msg *im) + static int + validateascii(char *string, u_int32_t len) + { +- int i; ++ u_int32_t i; + + for (i = 0; i < len; i++) { + if (string[i] == '\0') +@@ -901,7 +917,7 @@ static int + getfield(char **bpp, char *fld, size_t fldsz) + { + char *bp; +- u_int val, n; ++ int val, n; + + while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0') + ; +diff -up nfs-utils-1.2.2/utils/mount/configfile.c.orig nfs-utils-1.2.2/utils/mount/configfile.c +--- nfs-utils-1.2.2/utils/mount/configfile.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/configfile.c 2010-09-16 16:29:35.251230426 -0400 +@@ -192,7 +192,8 @@ void free_all(void) + } + } + static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL}; +-int inline check_vers(char *mopt, char *field) ++static int ++check_vers(char *mopt, char *field) + { + int i, found=0; + +@@ -229,7 +230,8 @@ extern sa_family_t config_default_family + * If so, set the appropriate global value which will + * be used as the initial value in the server negation. + */ +-int inline default_value(char *mopt) ++static int ++default_value(char *mopt) + { + struct mount_options *options = NULL; + int dftlen = strlen("default"); +diff -up nfs-utils-1.2.2/utils/mountd/auth.c.orig nfs-utils-1.2.2/utils/mountd/auth.c +--- nfs-utils-1.2.2/utils/mountd/auth.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/auth.c 2010-09-16 16:29:35.257230538 -0400 +@@ -15,6 +15,8 @@ + #include + #include + #include ++ ++#include "sockaddr.h" + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +@@ -110,13 +112,16 @@ auth_reload() + return counter; + } + +-static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error) ++static char * ++get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, ++ enum auth_error *error) + { ++ char buf[INET6_ADDRSTRLEN]; + char *n; + + if (use_ipaddr) +- return strdup(inet_ntoa(caller->sin_addr)); +- n = client_compose(hp); ++ return strdup(host_ntop(caller, buf, sizeof(buf))); ++ n = client_compose(ai); + *error = unknown_host; + if (!n) + return NULL; +@@ -128,8 +133,8 @@ static char *get_client_hostname(struct + + /* return static nfs_export with details filled in */ + static nfs_export * +-auth_authenticate_newcache(char *what, struct sockaddr_in *caller, +- char *path, struct hostent *hp, ++auth_authenticate_newcache(const struct sockaddr *caller, ++ const char *path, struct addrinfo *ai, + enum auth_error *error) + { + nfs_export *exp; +@@ -137,12 +142,12 @@ auth_authenticate_newcache(char *what, s + + free(my_client.m_hostname); + +- my_client.m_hostname = get_client_hostname(caller, hp, error); ++ my_client.m_hostname = get_client_hostname(caller, ai, error); + if (my_client.m_hostname == NULL) + return NULL; + + my_client.m_naddr = 1; +- my_client.m_addrlist[0] = caller->sin_addr; ++ set_addrlist(&my_client, 0, caller); + my_exp.m_client = &my_client; + + exp = NULL; +@@ -152,7 +157,7 @@ auth_authenticate_newcache(char *what, s + continue; + if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) + continue; +- if (use_ipaddr && !client_check(exp->m_client, hp)) ++ if (use_ipaddr && !client_check(exp->m_client, ai)) + continue; + break; + } +@@ -166,18 +171,18 @@ auth_authenticate_newcache(char *what, s + } + + static nfs_export * +-auth_authenticate_internal(char *what, struct sockaddr_in *caller, +- char *path, struct hostent *hp, +- enum auth_error *error) ++auth_authenticate_internal(const struct sockaddr *caller, const char *path, ++ struct addrinfo *ai, enum auth_error *error) + { + nfs_export *exp; + + if (new_cache) { +- exp = auth_authenticate_newcache(what, caller, path, hp, error); ++ exp = auth_authenticate_newcache(caller, path, ai, error); + if (!exp) + return NULL; + } else { +- if (!(exp = export_find(hp, path))) { ++ exp = export_find(ai, path); ++ if (exp == NULL) { + *error = no_entry; + return NULL; + } +@@ -187,7 +192,7 @@ auth_authenticate_internal(char *what, s + return NULL; + } + if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && +- ntohs(caller->sin_port) >= IPPORT_RESERVED) { ++ nfs_get_port(caller) >= IPPORT_RESERVED) { + *error = illegal_port; + return NULL; + } +@@ -197,18 +202,19 @@ auth_authenticate_internal(char *what, s + } + + nfs_export * +-auth_authenticate(char *what, struct sockaddr_in *caller, char *path) ++auth_authenticate(const char *what, const struct sockaddr *caller, ++ const char *path) + { + nfs_export *exp = NULL; + char epath[MAXPATHLEN+1]; + char *p = NULL; +- struct hostent *hp = NULL; +- struct in_addr addr = caller->sin_addr; ++ char buf[INET6_ADDRSTRLEN]; ++ struct addrinfo *ai = NULL; + enum auth_error error = bad_path; + +- if (path [0] != '/') { +- xlog(L_WARNING, "bad path in %s request from %s: \"%s\"", +- what, inet_ntoa(addr), path); ++ if (path[0] != '/') { ++ xlog(L_WARNING, "Bad path in %s request from %s: \"%s\"", ++ what, host_ntop(caller, buf, sizeof(buf)), path); + return exp; + } + +@@ -216,14 +222,13 @@ auth_authenticate(char *what, struct soc + epath[sizeof (epath) - 1] = '\0'; + auth_fixpath(epath); /* strip duplicate '/' etc */ + +- hp = client_resolve(caller->sin_addr); +- if (!hp) ++ ai = client_resolve(caller); ++ if (ai == NULL) + return exp; + + /* Try the longest matching exported pathname. */ + while (1) { +- exp = auth_authenticate_internal(what, caller, epath, +- hp, &error); ++ exp = auth_authenticate_internal(caller, epath, ai, &error); + if (exp || (error != not_exported && error != no_entry)) + break; + /* We have to treat the root, "/", specially. */ +@@ -236,41 +241,40 @@ auth_authenticate(char *what, struct soc + switch (error) { + case bad_path: + xlog(L_WARNING, "bad path in %s request from %s: \"%s\"", +- what, inet_ntoa(addr), path); ++ what, host_ntop(caller, buf, sizeof(buf)), path); + break; + + case unknown_host: + xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host", +- what, inet_ntoa(addr), path, epath); ++ what, host_ntop(caller, buf, sizeof(buf)), path, epath); + break; + + case no_entry: + xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry", +- what, hp->h_name, path, epath); ++ what, ai->ai_canonname, path, epath); + break; + + case not_exported: + xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported", +- what, hp->h_name, path, epath); ++ what, ai->ai_canonname, path, epath); + break; + + case illegal_port: +- xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d", +- what, hp->h_name, path, epath, ntohs(caller->sin_port)); ++ xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %u", ++ what, ai->ai_canonname, path, epath, nfs_get_port(caller)); + break; + + case success: +- xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)", +- what, hp->h_name, ntohs(caller->sin_port), path, epath); ++ xlog(L_NOTICE, "authenticated %s request from %s:%u for %s (%s)", ++ what, ai->ai_canonname, nfs_get_port(caller), path, epath); + break; + default: +- xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d", +- what, hp->h_name, ntohs(caller->sin_port), path, epath, error); ++ xlog(L_NOTICE, "%s request from %s:%u for %s (%s) gave %d", ++ what, ai->ai_canonname, nfs_get_port(caller), ++ path, epath, error); + } + +- if (hp) +- free (hp); +- ++ freeaddrinfo(ai); + return exp; + } + +diff -up nfs-utils-1.2.2/utils/mountd/cache.c.orig nfs-utils-1.2.2/utils/mountd/cache.c +--- nfs-utils-1.2.2/utils/mountd/cache.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/cache.c 2010-09-16 16:29:35.258229814 -0400 +@@ -37,6 +37,11 @@ + #include "blkid/blkid.h" + #endif + ++/* ++ * Invoked by RPC service loop ++ */ ++void cache_set_fds(fd_set *fdset); ++int cache_process_req(fd_set *readfds); + + enum nfsd_fsid { + FSID_DEV = 0, +@@ -57,14 +62,14 @@ enum nfsd_fsid { + * Record is terminated with newline. + * + */ +-int cache_export_ent(char *domain, struct exportent *exp, char *p); ++static int cache_export_ent(char *domain, struct exportent *exp, char *p); + + + char *lbuf = NULL; + int lbuflen = 0; + extern int use_ipaddr; + +-void auth_unix_ip(FILE *f) ++static void auth_unix_ip(FILE *f) + { + /* requests are + * class IP-ADDR +@@ -75,10 +80,10 @@ void auth_unix_ip(FILE *f) + */ + char *cp; + char class[20]; +- char ipaddr[20]; ++ char ipaddr[INET6_ADDRSTRLEN]; + char *client = NULL; +- struct in_addr addr; +- struct hostent *he = NULL; ++ struct addrinfo *tmp = NULL; ++ struct addrinfo *ai = NULL; + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + +@@ -90,20 +95,23 @@ void auth_unix_ip(FILE *f) + strcmp(class, "nfsd") != 0) + return; + +- if (qword_get(&cp, ipaddr, 20) <= 0) ++ if (qword_get(&cp, ipaddr, sizeof(ipaddr)) <= 0) + return; + +- if (inet_aton(ipaddr, &addr)==0) ++ tmp = host_pton(ipaddr); ++ if (tmp == NULL) + return; + + auth_reload(); + + /* addr is a valid, interesting address, find the domain name... */ + if (!use_ipaddr) { +- he = client_resolve(addr); +- client = client_compose(he); ++ ai = client_resolve(tmp->ai_addr); ++ client = client_compose(ai); ++ freeaddrinfo(ai); + } +- ++ freeaddrinfo(tmp); ++ + qword_print(f, "nfsd"); + qword_print(f, ipaddr); + qword_printint(f, time(0)+30*60); +@@ -114,18 +122,17 @@ void auth_unix_ip(FILE *f) + qword_eol(f); + xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT"); + +- if (client) free(client); +- free(he); ++ free(client); + } + +-void auth_unix_gid(FILE *f) ++static void auth_unix_gid(FILE *f) + { + /* Request are + * uid + * reply is + * uid expiry count list of group ids + */ +- int uid; ++ uid_t uid; + struct passwd *pw; + gid_t glist[100], *groups = glist; + int ngroups = 100; +@@ -136,7 +143,7 @@ void auth_unix_gid(FILE *f) + return; + + cp = lbuf; +- if (qword_get_int(&cp, &uid) != 0) ++ if (qword_get_uint(&cp, &uid) != 0) + return; + + pw = getpwuid(uid); +@@ -153,14 +160,14 @@ void auth_unix_gid(FILE *f) + groups, &ngroups); + } + } +- qword_printint(f, uid); +- qword_printint(f, time(0)+30*60); ++ qword_printuint(f, uid); ++ qword_printuint(f, time(0)+30*60); + if (rv >= 0) { +- qword_printint(f, ngroups); ++ qword_printuint(f, ngroups); + for (i=0; imnt_dir; + } + +-void nfsd_fh(FILE *f) ++static void nfsd_fh(FILE *f) + { + /* request are: + * domain fsidtype fsid +@@ -294,8 +345,7 @@ void nfsd_fh(FILE *f) + unsigned int fsidnum=0; + char fsid[32]; + struct exportent *found = NULL; +- struct hostent *he = NULL; +- struct in_addr addr; ++ struct addrinfo *ai = NULL; + char *found_path = NULL; + nfs_export *exp; + int i; +@@ -398,6 +448,7 @@ void nfsd_fh(FILE *f) + struct stat stb; + char u[16]; + char *path; ++ int type; + + if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) { + static nfs_export *prev = NULL; +@@ -461,22 +512,29 @@ void nfsd_fh(FILE *f) + continue; + check_uuid: + if (exp->m_export.e_uuid) +- get_uuid(NULL, exp->m_export.e_uuid, ++ get_uuid(exp->m_export.e_uuid, + uuidlen, u); +- else if (get_uuid(path, NULL, uuidlen, u) == 0) +- continue; ++ else ++ for (type = 0; ++ uuid_by_path(path, type, uuidlen, u); ++ type++) ++ if (memcmp(u, fhuuid, uuidlen) == 0) ++ break; + + if (memcmp(u, fhuuid, uuidlen) != 0) + continue; + break; + } + if (use_ipaddr) { +- if (he == NULL) { +- if (!inet_aton(dom, &addr)) ++ if (ai == NULL) { ++ struct addrinfo *tmp; ++ tmp = host_pton(dom); ++ if (tmp == NULL) + goto out; +- he = client_resolve(addr); ++ ai = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); + } +- if (!client_check(exp->m_client, he)) ++ if (!client_check(exp->m_client, ai)) + continue; + } + /* It's a match !! */ +@@ -534,21 +592,20 @@ void nfsd_fh(FILE *f) + out: + if (found_path) + free(found_path); +- if (he) +- free(he); ++ freeaddrinfo(ai); + free(dom); + xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); + return; + } + +-static void write_fsloc(FILE *f, struct exportent *ep, char *path) ++static void write_fsloc(FILE *f, struct exportent *ep) + { + struct servers *servers; + + if (ep->e_fslocmethod == FSLOC_NONE) + return; + +- servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path); ++ servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata); + if (!servers) + return; + qword_print(f, "fsloc"); +@@ -596,17 +653,17 @@ static int dump_to_cache(FILE *f, char * + qword_printint(f, exp->e_anonuid); + qword_printint(f, exp->e_anongid); + qword_printint(f, exp->e_fsid); +- write_fsloc(f, exp, path); ++ write_fsloc(f, exp); + write_secinfo(f, exp, flag_mask); + if (exp->e_uuid == NULL || different_fs) { + char u[16]; +- if (get_uuid(path, NULL, 16, u)) { ++ if (uuid_by_path(path, 0, 16, u)) { + qword_print(f, "uuid"); + qword_printhex(f, u, 16); + } + } else { + char u[16]; +- get_uuid(NULL, exp->e_uuid, 16, u); ++ get_uuid(exp->e_uuid, 16, u); + qword_print(f, "uuid"); + qword_printhex(f, u, 16); + } +@@ -614,12 +671,12 @@ static int dump_to_cache(FILE *f, char * + return qword_eol(f); + } + +-static int is_subdirectory(char *subpath, char *path) ++static int is_subdirectory(char *child, char *parent) + { +- int l = strlen(path); ++ int l = strlen(parent); + +- return strcmp(subpath, path) == 0 +- || (strncmp(subpath, path, l) == 0 && path[l] == '/'); ++ return strcmp(child, parent) == 0 ++ || (strncmp(child, parent, l) == 0 && child[l] == '/'); + } + + static int path_matches(nfs_export *exp, char *path) +@@ -629,19 +686,22 @@ static int path_matches(nfs_export *exp, + return strcmp(path, exp->m_export.e_path) == 0; + } + +-static int client_matches(nfs_export *exp, char *dom, struct hostent *he) ++static int ++client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) + { + if (use_ipaddr) +- return client_check(exp->m_client, he); ++ return client_check(exp->m_client, ai); + return client_member(dom, exp->m_client->m_hostname); + } + +-static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he) ++static int ++export_matches(nfs_export *exp, char *dom, char *path, struct addrinfo *ai) + { +- return path_matches(exp, path) && client_matches(exp, dom, he); ++ return path_matches(exp, path) && client_matches(exp, dom, ai); + } + +-static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) ++static nfs_export * ++lookup_export(char *dom, char *path, struct addrinfo *ai) + { + nfs_export *exp; + nfs_export *found = NULL; +@@ -650,7 +710,7 @@ static nfs_export *lookup_export(char *d + + for (i=0 ; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { +- if (!export_matches(exp, dom, path, he)) ++ if (!export_matches(exp, dom, path, ai)) + continue; + if (!found) { + found = exp; +@@ -687,7 +747,7 @@ static nfs_export *lookup_export(char *d + return found; + } + +-void nfsd_export(FILE *f) ++static void nfsd_export(FILE *f) + { + /* requests are: + * domain path +@@ -698,9 +758,7 @@ void nfsd_export(FILE *f) + char *cp; + char *dom, *path; + nfs_export *found = NULL; +- struct in_addr addr; +- struct hostent *he = NULL; +- ++ struct addrinfo *ai = NULL; + + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; +@@ -722,12 +780,16 @@ void nfsd_export(FILE *f) + auth_reload(); + + if (use_ipaddr) { +- if (!inet_aton(dom, &addr)) ++ struct addrinfo *tmp; ++ tmp = host_pton(dom); ++ if (tmp == NULL) ++ goto out; ++ ai = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); + goto out; +- he = client_resolve(addr); + } + +- found = lookup_export(dom, path, he); ++ found = lookup_export(dom, path, ai); + + if (found) { + if (dump_to_cache(f, dom, path, &found->m_export) < 0) { +@@ -743,7 +805,7 @@ void nfsd_export(FILE *f) + xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL); + if (dom) free(dom); + if (path) free(path); +- if (he) free(he); ++ freeaddrinfo(ai); + } + + +@@ -752,14 +814,19 @@ struct { + void (*cache_handle)(FILE *f); + FILE *f; + } cachelist[] = { +- { "auth.unix.ip", auth_unix_ip}, +- { "auth.unix.gid", auth_unix_gid}, +- { "nfsd.export", nfsd_export}, +- { "nfsd.fh", nfsd_fh}, +- { NULL, NULL } ++ { "auth.unix.ip", auth_unix_ip, NULL}, ++ { "auth.unix.gid", auth_unix_gid, NULL}, ++ { "nfsd.export", nfsd_export, NULL}, ++ { "nfsd.fh", nfsd_fh, NULL}, ++ { NULL, NULL, NULL } + }; + + extern int manage_gids; ++ ++/** ++ * cache_open - prepare communications channels with kernel RPC caches ++ * ++ */ + void cache_open(void) + { + int i; +@@ -772,6 +839,10 @@ void cache_open(void) + } + } + ++/** ++ * cache_set_fds - prepare cache file descriptors for one iteration of the service loop ++ * @fdset: pointer to fd_set to prepare ++ */ + void cache_set_fds(fd_set *fdset) + { + int i; +@@ -781,6 +852,10 @@ void cache_set_fds(fd_set *fdset) + } + } + ++/** ++ * cache_process_req - process any active cache file descriptors during service loop iteration ++ * @fdset: pointer to fd_set to examine for activity ++ */ + int cache_process_req(fd_set *readfds) + { + int i; +@@ -803,7 +878,7 @@ int cache_process_req(fd_set *readfds) + * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel + */ + +-int cache_export_ent(char *domain, struct exportent *exp, char *path) ++static int cache_export_ent(char *domain, struct exportent *exp, char *path) + { + int err; + FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w"); +@@ -824,8 +899,8 @@ int cache_export_ent(char *domain, struc + * and export them with the same options + */ + struct stat stb; +- int l = strlen(exp->e_path); +- int dev; ++ size_t l = strlen(exp->e_path); ++ __dev_t dev; + + if (strlen(path) <= l || path[l] != '/' || + strncmp(exp->e_path, path, l) != 0) +@@ -861,8 +936,14 @@ int cache_export_ent(char *domain, struc + return err; + } + ++/** ++ * cache_export - Inform kernel of a new nfs_export ++ * @exp: target nfs_export ++ * @path: NUL-terminated C string containing export path ++ */ + int cache_export(nfs_export *exp, char *path) + { ++ char buf[INET6_ADDRSTRLEN]; + int err; + FILE *f; + +@@ -870,8 +951,10 @@ int cache_export(nfs_export *exp, char * + if (!f) + return -1; + ++ + qword_print(f, "nfsd"); +- qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); ++ qword_print(f, ++ host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf))); + qword_printint(f, time(0)+30*60); + qword_print(f, exp->m_client->m_hostname); + err = qword_eol(f); +@@ -883,7 +966,14 @@ int cache_export(nfs_export *exp, char * + return err; + } + +-/* Get a filehandle. ++/** ++ * cache_get_filehandle - given an nfs_export, get its root filehandle ++ * @exp: target nfs_export ++ * @len: length of requested file handle ++ * @p: NUL-terminated C string containing export path ++ * ++ * Returns pointer to NFS file handle of root directory of export ++ * + * { + * echo $domain $path $length + * read filehandle <&0 +@@ -917,4 +1007,3 @@ cache_get_filehandle(nfs_export *exp, in + fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE); + return &fh; + } +- +diff -up nfs-utils-1.2.2/utils/mountd/fsloc.c.orig nfs-utils-1.2.2/utils/mountd/fsloc.c +--- nfs-utils-1.2.2/utils/mountd/fsloc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/fsloc.c 2010-09-16 16:29:35.259209794 -0400 +@@ -146,7 +146,7 @@ static struct servers *method_list(char + } + + /* Returns appropriately filled struct servers, or NULL if had a problem */ +-struct servers *replicas_lookup(int method, char *data, char *key) ++struct servers *replicas_lookup(int method, char *data) + { + struct servers *sp=NULL; + switch(method) { +diff -up nfs-utils-1.2.2/utils/mountd/fsloc.h.orig nfs-utils-1.2.2/utils/mountd/fsloc.h +--- nfs-utils-1.2.2/utils/mountd/fsloc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/fsloc.h 2010-09-16 16:29:35.260229826 -0400 +@@ -44,7 +44,7 @@ struct servers { + int h_referral; /* 0=replica, 1=referral */ + }; + +-struct servers *replicas_lookup(int method, char *data, char *key); ++struct servers *replicas_lookup(int method, char *data); + void release_replicas(struct servers *server); + + #endif /* FSLOC_H */ +diff -up nfs-utils-1.2.2/utils/mountd/mountd.c.orig nfs-utils-1.2.2/utils/mountd/mountd.c +--- nfs-utils-1.2.2/utils/mountd/mountd.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/mountd.c 2010-09-16 16:29:35.260229826 -0400 +@@ -28,10 +28,6 @@ + #include "rpcmisc.h" + #include "pseudoflavors.h" + +-extern void cache_open(void); +-extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len, char *p); +-extern int cache_export(nfs_export *exp, char *path); +- + extern void my_svc_run(void); + + static void usage(const char *, int exitcode); +@@ -80,10 +76,10 @@ static int nfs_version = -1; + static void + unregister_services (void) + { +- if (nfs_version & 0x1) ++ if (nfs_version & (0x1 << 1)) { + pmap_unset (MOUNTPROG, MOUNTVERS); +- if (nfs_version & (0x1 << 1)) + pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); ++ } + if (nfs_version & (0x1 << 2)) + pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); + } +@@ -192,17 +188,28 @@ sig_hup (int sig) + } + + bool_t +-mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp) ++mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp), ++ void *UNUSED(resp)) + { ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; ++ ++ xlog(D_CALL, "Received NULL request from %s", ++ host_ntop(sap, buf, sizeof(buf))); ++ + return 1; + } + + bool_t + mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) + { ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; + struct nfs_fh_len *fh; + +- xlog(D_CALL, "MNT1(%s) called", *path); ++ xlog(D_CALL, "Received MNT1(%s) request from %s", *path, ++ host_ntop(sap, buf, sizeof(buf))); ++ + fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0); + if (fh) + memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32); +@@ -210,23 +217,27 @@ mount_mnt_1_svc(struct svc_req *rqstp, d + } + + bool_t +-mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res) ++mount_dump_1_svc(struct svc_req *rqstp, void *UNUSED(argp), mountlist *res) + { +- struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; ++ ++ xlog(D_CALL, "Received DUMP request from %s", ++ host_ntop(sap, buf, sizeof(buf))); + +- xlog(D_CALL, "dump request from %s.", inet_ntoa(addr->sin_addr)); + *res = mountlist_list(); + + return 1; + } + + bool_t +-mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) ++mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *UNUSED(resp)) + { +- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + nfs_export *exp; + char *p = *argp; + char rpath[MAXPATHLEN+1]; ++ char buf[INET6_ADDRSTRLEN]; + + if (*p == '\0') + p = "/"; +@@ -236,41 +247,57 @@ mount_umnt_1_svc(struct svc_req *rqstp, + p = rpath; + } + +- if (!(exp = auth_authenticate("unmount", sin, p))) { ++ xlog(D_CALL, "Received UMNT(%s) request from %s", p, ++ host_ntop(sap, buf, sizeof(buf))); ++ ++ exp = auth_authenticate("unmount", sap, p); ++ if (exp == NULL) + return 1; +- } + +- mountlist_del(inet_ntoa(sin->sin_addr), p); ++ mountlist_del(host_ntop(sap, buf, sizeof(buf)), p); + return 1; + } + + bool_t +-mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp) ++mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), ++ void *UNUSED(resp)) + { ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; ++ ++ xlog(D_CALL, "Received UMNTALL request from %s", ++ host_ntop(sap, buf, sizeof(buf))); ++ + /* Reload /etc/xtab if necessary */ + auth_reload(); + +- mountlist_del_all(nfs_getrpccaller_in(rqstp->rq_xprt)); ++ mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt)); + return 1; + } + + bool_t +-mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp) ++mount_export_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) + { +- struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; ++ ++ xlog(D_CALL, "Received EXPORT request from %s.", ++ host_ntop(sap, buf, sizeof(buf))); + +- xlog(D_CALL, "export request from %s.", inet_ntoa(addr->sin_addr)); + *resp = get_exportlist(); + + return 1; + } + + bool_t +-mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) ++mount_exportall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) + { +- struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); ++ char buf[INET6_ADDRSTRLEN]; ++ ++ xlog(D_CALL, "Received EXPORTALL request from %s.", ++ host_ntop(sap, buf, sizeof(buf))); + +- xlog(D_CALL, "exportall request from %s.", inet_ntoa(addr->sin_addr)); + *resp = get_exportlist(); + + return 1; +@@ -290,11 +317,12 @@ mount_exportall_1_svc(struct svc_req *rq + bool_t + mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) + { +- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct stat stb; + nfs_export *exp; + char rpath[MAXPATHLEN+1]; + char *p = *path; ++ char buf[INET6_ADDRSTRLEN]; + + memset(res, 0, sizeof(*res)); + +@@ -310,11 +338,14 @@ mount_pathconf_2_svc(struct svc_req *rqs + p = rpath; + } + ++ xlog(D_CALL, "Received PATHCONF(%s) request from %s", p, ++ host_ntop(sap, buf, sizeof(buf))); ++ + /* Now authenticate the intruder... */ +- exp = auth_authenticate("pathconf", sin, p); +- if (!exp) { ++ exp = auth_authenticate("pathconf", sap, p); ++ if (exp == NULL) + return 1; +- } else if (stat(p, &stb) < 0) { ++ else if (stat(p, &stb) < 0) { + xlog(L_WARNING, "can't stat exported dir %s: %s", + p, strerror(errno)); + return 1; +@@ -374,11 +405,15 @@ static void set_authflavors(struct mount + bool_t + mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res) + { ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct mountres3_ok *ok = &res->mountres3_u.mountinfo; ++ char buf[INET6_ADDRSTRLEN]; + nfs_export *exp; + struct nfs_fh_len *fh; + +- xlog(D_CALL, "MNT3(%s) called", *path); ++ xlog(D_CALL, "Received MNT3(%s) request from %s", *path, ++ host_ntop(sap, buf, sizeof(buf))); ++ + fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1); + if (!fh) + return 1; +@@ -393,12 +428,13 @@ static struct nfs_fh_len * + get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, + mountstat3 *error, int v3) + { +- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); ++ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct stat stb, estb; + nfs_export *exp; + struct nfs_fh_len *fh; + char rpath[MAXPATHLEN+1]; + char *p = *path; ++ char buf[INET6_ADDRSTRLEN]; + + if (*p == '\0') + p = "/"; +@@ -413,8 +449,8 @@ get_rootfh(struct svc_req *rqstp, dirpat + } + + /* Now authenticate the intruder... */ +- exp = auth_authenticate("mount", sin, p); +- if (!exp) { ++ exp = auth_authenticate("mount", sap, p); ++ if (exp == NULL) { + *error = NFSERR_ACCES; + return NULL; + } +@@ -482,13 +518,13 @@ get_rootfh(struct svc_req *rqstp, dirpat + xtab_append(exp); + + if (v3) +- fh = getfh_size ((struct sockaddr *) sin, p, 64); ++ fh = getfh_size((struct sockaddr_in *)sap, p, 64); + if (!v3 || (fh == NULL && errno == EINVAL)) { + /* We first try the new nfs syscall. */ +- fh = getfh ((struct sockaddr *) sin, p); ++ fh = getfh((struct sockaddr_in *)sap, p); + if (fh == NULL && errno == EINVAL) + /* Let's try the old one. */ +- fh = getfh_old ((struct sockaddr *) sin, ++ fh = getfh_old((struct sockaddr_in *)sap, + stb.st_dev, stb.st_ino); + } + if (fh == NULL && !did_export) { +@@ -503,7 +539,7 @@ get_rootfh(struct svc_req *rqstp, dirpat + } + } + *error = NFS_OK; +- mountlist_add(inet_ntoa(sin->sin_addr), p); ++ mountlist_add(host_ntop(sap, buf, sizeof(buf)), p); + if (expret) + *expret = exp; + return fh; +@@ -536,22 +572,21 @@ static void free_exportlist(exports *eli + + static void prune_clients(nfs_export *exp, struct exportnode *e) + { +- struct hostent *hp; ++ struct addrinfo *ai = NULL; + struct groupnode *c, **cp; + + cp = &e->ex_groups; + while ((c = *cp) != NULL) { + if (client_gettype(c->gr_name) == MCL_FQDN +- && (hp = gethostbyname(c->gr_name))) { +- hp = hostent_dup(hp); +- if (client_check(exp->m_client, hp)) { ++ && (ai = host_addrinfo(c->gr_name))) { ++ if (client_check(exp->m_client, ai)) { + *cp = c->gr_next; + xfree(c->gr_name); + xfree(c); +- xfree (hp); ++ freeaddrinfo(ai); + continue; + } +- xfree (hp); ++ freeaddrinfo(ai); + } + cp = &(c->gr_next); + } +@@ -712,8 +747,10 @@ main(int argc, char **argv) + usage(argv [0], 1); + } + +- /* No more arguments allowed. */ +- if (optind != argc || !(nfs_version & 0x7)) ++ /* No more arguments allowed. ++ * Require at least one valid version (2, 3, or 4) ++ */ ++ if (optind != argc || !(nfs_version & 0xE)) + usage(argv [0], 1); + + if (chdir(state_dir)) { +@@ -761,12 +798,12 @@ main(int argc, char **argv) + if (new_cache) + cache_open(); + +- if (nfs_version & 0x1) ++ if (nfs_version & (0x1 << 1)) { + rpc_init("mountd", MOUNTPROG, MOUNTVERS, + mount_dispatch, port); +- if (nfs_version & (0x1 << 1)) + rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX, + mount_dispatch, port); ++ } + if (nfs_version & (0x1 << 2)) + rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3, + mount_dispatch, port); +diff -up nfs-utils-1.2.2/utils/mountd/mountd.h.orig nfs-utils-1.2.2/utils/mountd/mountd.h +--- nfs-utils-1.2.2/utils/mountd/mountd.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/mountd.h 2010-09-16 16:29:35.261220190 -0400 +@@ -41,14 +41,19 @@ bool_t mount_mnt_3_svc(struct svc_req * + void mount_dispatch(struct svc_req *, SVCXPRT *); + void auth_init(char *export_file); + unsigned int auth_reload(void); +-nfs_export * auth_authenticate(char *what, struct sockaddr_in *sin, +- char *path); ++nfs_export * auth_authenticate(const char *what, ++ const struct sockaddr *caller, ++ const char *path); + void auth_export(nfs_export *exp); + + void mountlist_add(char *host, const char *path); + void mountlist_del(char *host, const char *path); +-void mountlist_del_all(struct sockaddr_in *sin); ++void mountlist_del_all(const struct sockaddr *sap); + mountlist mountlist_list(void); + ++void cache_open(void); ++struct nfs_fh_len * ++ cache_get_filehandle(nfs_export *exp, int len, char *p); ++int cache_export(nfs_export *exp, char *path); + + #endif /* MOUNTD_H */ +diff -up nfs-utils-1.2.2/utils/mountd/rmtab.c.orig nfs-utils-1.2.2/utils/mountd/rmtab.c +--- nfs-utils-1.2.2/utils/mountd/rmtab.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/rmtab.c 2010-09-16 16:29:35.261220190 -0400 +@@ -16,7 +16,7 @@ + #include + #include + #include +-#include "xmalloc.h" ++ + #include "misc.h" + #include "exportfs.h" + #include "xio.h" +@@ -131,22 +131,22 @@ mountlist_del(char *hname, const char *p + } + + void +-mountlist_del_all(struct sockaddr_in *sin) ++mountlist_del_all(const struct sockaddr *sap) + { +- struct in_addr addr = sin->sin_addr; +- struct hostent *hp; ++ char *hostname; + struct rmtabent *rep; +- nfs_export *exp; + FILE *fp; + int lockid; + + if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) + return; +- if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) { +- xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr)); ++ hostname = host_canonname(sap); ++ if (hostname == NULL) { ++ char buf[INET6_ADDRSTRLEN]; ++ xlog(L_ERROR, "can't get hostname of %s", ++ host_ntop(sap, buf, sizeof(buf))); + goto out_unlock; + } +- hp = hostent_dup (hp); + + if (!setrmtabent("r")) + goto out_free; +@@ -155,8 +155,8 @@ mountlist_del_all(struct sockaddr_in *si + goto out_close; + + while ((rep = getrmtabent(1, NULL)) != NULL) { +- if (strcmp(rep->r_client, hp->h_name) == 0 && +- (exp = auth_authenticate("umountall", sin, rep->r_path))) ++ if (strcmp(rep->r_client, hostname) == 0 && ++ auth_authenticate("umountall", sap, rep->r_path) != NULL) + continue; + fputrmtabent(fp, rep, NULL); + } +@@ -168,11 +168,23 @@ mountlist_del_all(struct sockaddr_in *si + out_close: + endrmtabent(); /* close & unlink */ + out_free: +- free (hp); ++ free(hostname); + out_unlock: + xfunlock(lockid); + } + ++static void ++mountlist_freeall(mountlist list) ++{ ++ while (list != NULL) { ++ mountlist m = list; ++ list = m->ml_next; ++ free(m->ml_hostname); ++ free(m->ml_directory); ++ free(m); ++ } ++} ++ + mountlist + mountlist_list(void) + { +@@ -182,8 +194,6 @@ mountlist_list(void) + struct rmtabent *rep; + struct stat stb; + int lockid; +- struct in_addr addr; +- struct hostent *he; + + if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0) + return NULL; +@@ -194,26 +204,41 @@ mountlist_list(void) + return NULL; + } + if (stb.st_mtime != last_mtime) { +- while (mlist) { +- mlist = (m = mlist)->ml_next; +- xfree(m->ml_hostname); +- xfree(m->ml_directory); +- xfree(m); +- } ++ mountlist_freeall(mlist); + last_mtime = stb.st_mtime; + + setrmtabent("r"); + while ((rep = getrmtabent(1, NULL)) != NULL) { +- m = (mountlist) xmalloc(sizeof(*m)); +- +- if (reverse_resolve && +- inet_aton((const char *) rep->r_client, &addr) && +- (he = gethostbyaddr(&addr, sizeof(addr), AF_INET))) +- m->ml_hostname = xstrdup(he->h_name); +- else +- m->ml_hostname = xstrdup(rep->r_client); ++ m = calloc(1, sizeof(*m)); ++ if (m == NULL) { ++ mountlist_freeall(mlist); ++ mlist = NULL; ++ xlog(L_ERROR, "%s: memory allocation failed", ++ __func__); ++ break; ++ } ++ ++ if (reverse_resolve) { ++ struct addrinfo *ai; ++ ai = host_pton(rep->r_client); ++ if (ai != NULL) { ++ m->ml_hostname = host_canonname(ai->ai_addr); ++ freeaddrinfo(ai); ++ } ++ } ++ if (m->ml_hostname == NULL) ++ m->ml_hostname = strdup(rep->r_client); ++ ++ m->ml_directory = strdup(rep->r_path); ++ ++ if (m->ml_hostname == NULL || m->ml_directory == NULL) { ++ mountlist_freeall(mlist); ++ mlist = NULL; ++ xlog(L_ERROR, "%s: memory allocation failed", ++ __func__); ++ break; ++ } + +- m->ml_directory = xstrdup(rep->r_path); + m->ml_next = mlist; + mlist = m; + } +diff -up nfs-utils-1.2.2/utils/mount/network.c.orig nfs-utils-1.2.2/utils/mount/network.c +--- nfs-utils-1.2.2/utils/mount/network.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/network.c 2010-09-16 16:29:35.252229853 -0400 +@@ -53,6 +53,7 @@ + #include "parse_opt.h" + #include "network.h" + #include "conffile.h" ++#include "nfslib.h" + + #define PMAP_TIMEOUT (10) + #define CONNECT_TIMEOUT (20) +@@ -82,6 +83,7 @@ static const char *nfs_nfs_pgmtbl[] = { + static const char *nfs_transport_opttbl[] = { + "udp", + "tcp", ++ "rdma", + "proto", + NULL, + }; +@@ -857,7 +859,14 @@ int nfs_advise_umount(const struct socka + return 0; + } + +- client->cl_auth = authunix_create_default(); ++ client->cl_auth = nfs_authsys_create(); ++ if (client->cl_auth == NULL) { ++ if (verbose) ++ nfs_error(_("%s: Failed to create RPC auth handle"), ++ progname); ++ CLNT_DESTROY(client); ++ return 0; ++ } + + res = CLNT_CALL(client, MOUNTPROC_UMNT, + (xdrproc_t)xdr_dirpath, (caddr_t)argp, +@@ -957,8 +966,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_se + } + if (clnt) { + /* try to mount hostname:dirname */ +- clnt->cl_auth = authunix_create_default(); +- return clnt; ++ clnt->cl_auth = nfs_authsys_create(); ++ if (clnt->cl_auth) ++ return clnt; ++ CLNT_DESTROY(clnt); + } + return NULL; + } +@@ -1203,6 +1214,8 @@ nfs_nfs_program(struct mount_options *op + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'nfsprog=' option"), ++ progname); + return 0; + } + +@@ -1242,9 +1255,12 @@ nfs_nfs_version(struct mount_options *op + } + return 0; + case PO_NOT_FOUND: +- nfs_error(_("%s: option parsing error\n"), ++ nfs_error(_("%s: parsing error on 'vers=' option\n"), + progname); ++ return 0; + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'vers=' option"), ++ progname); + return 0; + } + case 4: /* nfsvers */ +@@ -1256,9 +1272,12 @@ nfs_nfs_version(struct mount_options *op + } + return 0; + case PO_NOT_FOUND: +- nfs_error(_("%s: option parsing error\n"), ++ nfs_error(_("%s: parsing error on 'nfsvers=' option\n"), + progname); ++ return 0; + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'nfsvers=' option"), ++ progname); + return 0; + } + } +@@ -1289,11 +1308,16 @@ nfs_nfs_protocol(struct mount_options *o + case 1: /* tcp */ + *protocol = IPPROTO_TCP; + return 1; +- case 2: /* proto */ ++ case 2: /* rdma */ ++ *protocol = NFSPROTO_RDMA; ++ return 1; ++ case 3: /* proto */ + option = po_get(options, "proto"); + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); + return 0; + } + return 1; +@@ -1327,6 +1351,8 @@ nfs_nfs_port(struct mount_options *optio + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'port=' option"), ++ progname); + return 0; + } + +@@ -1342,7 +1368,7 @@ nfs_nfs_port(struct mount_options *optio + sa_family_t config_default_family = AF_UNSPEC; + + static int +-nfs_verify_family(sa_family_t family) ++nfs_verify_family(sa_family_t UNUSED(family)) + { + return 1; + } +@@ -1374,14 +1400,20 @@ int nfs_nfs_proto_family(struct mount_op + switch (po_rightmost(options, nfs_transport_opttbl)) { + case 0: /* udp */ + case 1: /* tcp */ ++ case 2: /* rdma */ + /* for compatibility; these are always AF_INET */ + *family = AF_INET; + return 1; +- case 2: /* proto */ ++ case 3: /* proto */ + option = po_get(options, "proto"); + if (option != NULL && +- !nfs_get_proto(option, &tmp_family, &protocol)) +- goto out_err; ++ !nfs_get_proto(option, &tmp_family, &protocol)) { ++ ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); ++ errno = EPROTONOSUPPORT; ++ return 0; ++ } + } + + if (!nfs_verify_family(tmp_family)) +@@ -1414,6 +1446,8 @@ nfs_mount_program(struct mount_options * + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountprog=' option"), ++ progname); + return 0; + } + +@@ -1443,6 +1477,8 @@ nfs_mount_version(struct mount_options * + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountvers=' option"), ++ progname); + return 0; + } + +@@ -1469,6 +1505,8 @@ nfs_mount_protocol(struct mount_options + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); + return 0; + } + return 1; +@@ -1501,6 +1539,8 @@ nfs_mount_port(struct mount_options *opt + return 1; + } + case PO_BAD_VALUE: ++ nfs_error(_("%s: invalid value for 'mountport=' option"), ++ progname); + return 0; + } + +@@ -1526,8 +1566,12 @@ int nfs_mount_proto_family(struct mount_ + + option = po_get(options, "mountproto"); + if (option != NULL) { +- if (!nfs_get_proto(option, &tmp_family, &protocol)) ++ if (!nfs_get_proto(option, &tmp_family, &protocol)) { ++ nfs_error(_("%s: Failed to find '%s' protocol"), ++ progname, option); ++ errno = EPROTONOSUPPORT; + goto out_err; ++ } + if (!nfs_verify_family(tmp_family)) + goto out_err; + *family = tmp_family; +diff -up nfs-utils-1.2.2/utils/mount/nfs4mount.c.orig nfs-utils-1.2.2/utils/mount/nfs4mount.c +--- nfs-utils-1.2.2/utils/mount/nfs4mount.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/nfs4mount.c 2010-09-16 16:29:35.254210443 -0400 +@@ -146,7 +146,7 @@ static int fill_ipv4_sockaddr(const char + progname, hostname); + return -1; + } +- if (hp->h_length > sizeof(struct in_addr)) { ++ if (hp->h_length > (int)sizeof(struct in_addr)) { + nfs_error(_("%s: got bad hp->h_length"), progname); + hp->h_length = sizeof(struct in_addr); + } +diff -up nfs-utils-1.2.2/utils/mount/nfs.man.orig nfs-utils-1.2.2/utils/mount/nfs.man +--- nfs-utils-1.2.2/utils/mount/nfs.man.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/nfs.man 2010-09-16 16:29:35.253220019 -0400 +@@ -3,8 +3,6 @@ + .SH NAME + nfs \- fstab format and options for the + .B nfs +-and +-.B nfs4 + file systems + .SH SYNOPSIS + .I /etc/fstab +@@ -71,14 +69,10 @@ for details on specifying raw IPv6 addre + .P + The + .I fstype +-field contains either "nfs" (for version 2 or version 3 NFS mounts) +-or "nfs4" (for NFS version 4 mounts). ++field contains "nfs", for whatever version of the protocol. + The + .B nfs +-and +-.B nfs4 +-file system types share similar mount options, +-which are described below. ++allow several mount options, which are described below. + .SH "MOUNT OPTIONS" + Refer to + .BR mount (8) +@@ -89,14 +83,8 @@ specify any mount options, use the gener + in + .IR /etc/fstab . + .DT +-.SS "Valid options for either the nfs or nfs4 file system type" +-These options are valid to use when mounting either +-.B nfs +-or +-.B nfs4 +-file system types. +-They imply the same behavior +-and have the same default for both file system types. ++.SS "Options supported by all versions" ++These options are valid to use with any NFS version. + .TP 1.5i + .BR soft " / " hard + Determines the recovery behavior of the NFS client +@@ -476,11 +464,9 @@ by other clients, but can impact applica + .IP + The DATA AND METADATA COHERENCE section contains a + detailed discussion of these trade-offs. +-.SS "Valid options for the nfs file system type" ++.SS "Options for versions 2 and 3 only" + Use these options, along with the options in the above subsection, +-for mounting the +-.B nfs +-file system type. ++for NFSv2/v3 only. They will be ignored for newer versions. + .TP 1.5i + .BI proto= netid + The transport protocol name and protocol family the NFS client uses +@@ -495,7 +481,10 @@ command, + .I netid + is a valid netid listed in + .IR /etc/netconfig . +-Otherwise, ++The value "rdma" may also be specified. ++If the ++.B mount.nfs ++command does not have TI-RPC support, then + .I netid + is one of "tcp," "udp," or "rdma," and only IPv4 may be used. + .IP +@@ -537,6 +526,12 @@ option is an alternative to specifying + .BR proto=tcp. + It is included for compatibility with other operating systems. + .TP 1.5i ++.B rdma ++The ++.B rdma ++option is an alternative to specifying ++.BR proto=rdma. ++.TP 1.5i + .BI port= n + The numeric value of the server's NFS service port. + If the server's NFS service is not available on the specified port, +@@ -623,14 +618,9 @@ in such cases. + .TP 1.5i + .BI nfsvers= n + The NFS protocol version number used to contact the server's NFS service. +-The Linux client supports version 2 and version 3 of the NFS protocol +-when using the file system type +-.BR nfs . +-If the server does not support the requested version, +-the mount request fails. +-If this option is not specified, the client attempts to use version 3, +-but negotiates the NFS version with the server if version 3 support +-is not available. ++If the server does not support the requested version, the mount request fails. ++If this option is not specified, the client negociate a suitable version with ++the server, trying version 4 first, version 3 second, and version 2 last. + .TP 1.5i + .BI vers= n + This option is an alternative to the +@@ -727,11 +717,9 @@ If this option is not specified, the NFS + on NFS version 3 mounts to read small directories. + Some applications perform better if the client uses only READDIR requests + for all directories. +-.SS "Valid options for the nfs4 file system type" ++.SS "Options for version 4 only" + Use these options, along with the options in the first subsection above, +-for mounting the +-.B nfs4 +-file system type. ++for NFSv4 only. They will be ignored with older versions. + .TP 1.5i + .BI proto= netid + The transport protocol name and protocol family the NFS client uses +@@ -828,6 +816,13 @@ In the presence of multiple client netwo + special routing policies, + or atypical network topologies, + the exact address to use for callbacks may be nontrivial to determine. ++.SH nfs4 FILE SYSTEM TYPE ++The ++.BR nfs4 ++file system type is an old syntax for specifying NFSv4 usage. It can still ++be used with all NFSv4-specific and common options, excepted the ++.B nfsvers ++mount option. + .SH MOUNT CONFIGURATION FILE + If the mount command is configured to do so, all of the mount options + described in the previous section can also be configured in the +@@ -849,12 +844,11 @@ file system type and specify the + .B nfsvers=3 + mount option. + To mount using NFS version 4, +-use the +-.B nfs4 +-file system type. +-The +-.B nfsvers +-mount option is not supported for the ++use either the ++.B nfs ++file system type, with the ++.B nfsvers=4 ++mount option, or the + .B nfs4 + file system type. + .P +diff -up nfs-utils-1.2.2/utils/mount/nfsmount.c.orig nfs-utils-1.2.2/utils/mount/nfsmount.c +--- nfs-utils-1.2.2/utils/mount/nfsmount.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/nfsmount.c 2010-09-16 16:29:35.255249968 -0400 +@@ -510,8 +510,12 @@ nfsmount(const char *spec, const char *n + int val; + static int doonce = 0; + +- clnt_addr_t mnt_server = { &mounthost, }; +- clnt_addr_t nfs_server = { &hostname, }; ++ clnt_addr_t mnt_server = { ++ .hostname = &mounthost ++ }; ++ clnt_addr_t nfs_server = { ++ .hostname = &hostname ++ }; + struct sockaddr_in *nfs_saddr = &nfs_server.saddr; + struct pmap *mnt_pmap = &mnt_server.pmap, + *nfs_pmap = &nfs_server.pmap; +diff -up nfs-utils-1.2.2/utils/mount/nfsumount.c.orig nfs-utils-1.2.2/utils/mount/nfsumount.c +--- nfs-utils-1.2.2/utils/mount/nfsumount.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/nfsumount.c 2010-09-16 16:29:35.255249968 -0400 +@@ -179,10 +179,8 @@ static int nfs_umount_do_umnt(struct mou + struct pmap nfs_pmap, mnt_pmap; + sa_family_t family; + +- if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { +- nfs_error(_("%s: bad mount options"), progname); ++ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) + return EX_FAIL; +- } + + /* Skip UMNT call for vers=4 mounts */ + if (nfs_pmap.pm_vers == 4) +diff -up nfs-utils-1.2.2/utils/mount/stropts.c.orig nfs-utils-1.2.2/utils/mount/stropts.c +--- nfs-utils-1.2.2/utils/mount/stropts.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/stropts.c 2010-09-16 16:29:35.256219797 -0400 +@@ -302,11 +302,16 @@ static int nfs_set_version(struct nfsmou + + if (strncmp(mi->type, "nfs4", 4) == 0) + mi->version = 4; +- else { +- char *option = po_get(mi->options, "proto"); +- if (option && strcmp(option, "rdma") == 0) +- mi->version = 3; +- } ++ ++ /* ++ * Before 2.6.32, the kernel NFS client didn't ++ * support "-t nfs vers=4" mounts, so NFS version ++ * 4 cannot be included when autonegotiating ++ * while running on those kernels. ++ */ ++ if (mi->version == 0 && ++ linux_version_code() <= MAKE_VERSION(2, 6, 31)) ++ mi->version = 3; + + /* + * If we still don't know, check for version-specific +@@ -490,14 +495,18 @@ nfs_rewrite_pmap_mount_options(struct mo + union nfs_sockaddr mnt_address; + struct sockaddr *mnt_saddr = &mnt_address.sa; + socklen_t mnt_salen = sizeof(mnt_address); ++ unsigned long protocol; + struct pmap mnt_pmap; +- char *option; + + /* +- * Skip option negotiation for proto=rdma mounts. ++ * Version and transport negotiation is not required ++ * and does not work for RDMA mounts. + */ +- option = po_get(options, "proto"); +- if (option && strcmp(option, "rdma") == 0) ++ if (!nfs_nfs_protocol(options, &protocol)) { ++ errno = EINVAL; ++ return 0; ++ } ++ if (protocol == NFSPROTO_RDMA) + goto out; + + /* +@@ -538,7 +547,10 @@ nfs_rewrite_pmap_mount_options(struct mo + + if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap, + mnt_saddr, &mnt_pmap)) { +- errno = EINVAL; ++ if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) ++ errno = EPROTONOSUPPORT; ++ else ++ errno = EINVAL; + return 0; + } + +@@ -586,18 +598,21 @@ static int nfs_do_mount_v3v2(struct nfsm + errno = ENOMEM; + return result; + } +- ++ errno = 0; + if (!nfs_append_addr_option(sap, salen, options)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + + if (!nfs_fix_mounthost_option(options, mi->hostname)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + if (!mi->fake && !nfs_verify_lock_option(options)) { +- errno = EINVAL; ++ if (errno == 0) ++ errno = EINVAL; + goto out_fail; + } + +@@ -741,6 +756,47 @@ static int nfs_try_mount_v4(struct nfsmo + } + + /* ++ * Handle NFS version and transport protocol ++ * autonegotiation. ++ * ++ * When no version or protocol is specified on the ++ * command line, mount.nfs negotiates with the server ++ * to determine appropriate settings for the new ++ * mount point. ++ * ++ * Returns TRUE if successful, otherwise FALSE. ++ * "errno" is set to reflect the individual error. ++ */ ++static int nfs_autonegotiate(struct nfsmount_info *mi) ++{ ++ int result; ++ ++ result = nfs_try_mount_v4(mi); ++ if (result) ++ return result; ++ ++ switch (errno) { ++ case EPROTONOSUPPORT: ++ /* A clear indication that the server or our ++ * client does not support NFS version 4. */ ++ goto fall_back; ++ case ENOENT: ++ /* Legacy Linux servers don't export an NFS ++ * version 4 pseudoroot. */ ++ goto fall_back; ++ case EPERM: ++ /* Linux servers prior to 2.6.25 may return ++ * EPERM when NFS version 4 is not supported. */ ++ goto fall_back; ++ default: ++ return result; ++ } ++ ++fall_back: ++ return nfs_try_mount_v3v2(mi); ++} ++ ++/* + * This is a single pass through the fg/bg loop. + * + * Returns TRUE if successful, otherwise FALSE. +@@ -752,20 +808,8 @@ static int nfs_try_mount(struct nfsmount + + switch (mi->version) { + case 0: +- if (linux_version_code() > MAKE_VERSION(2, 6, 31)) { +- errno = 0; +- result = nfs_try_mount_v4(mi); +- if (errno != EPROTONOSUPPORT) { +- /* +- * To deal with legacy Linux servers that don't +- * automatically export a pseudo root, retry +- * ENOENT errors using version 3. And for +- * Linux servers prior to 2.6.25, retry EPERM +- */ +- if (errno != ENOENT && errno != EPERM) +- break; +- } +- } ++ result = nfs_autonegotiate(mi); ++ break; + case 2: + case 3: + result = nfs_try_mount_v3v2(mi); +@@ -799,6 +843,7 @@ static int nfs_is_permanent_error(int er + case ESTALE: + case ETIMEDOUT: + case ECONNREFUSED: ++ case EHOSTUNREACH: + return 0; /* temporary */ + default: + return 1; /* permanent */ +@@ -922,6 +967,26 @@ static int nfsmount_bg(struct nfsmount_i + } + + /* ++ * Usually all that is needed for an NFS remount is to change ++ * generic mount options like "sync" or "ro". These generic ++ * options are controlled by mi->flags, not by text-based ++ * options, and no contact with the server is needed. ++ * ++ * Take care with the /etc/mtab entry for this mount; just ++ * calling update_mtab() will change an "-t nfs -o vers=4" ++ * mount to an "-t nfs -o remount" mount, and that will ++ * confuse umount.nfs. ++ * ++ * Returns a valid mount command exit code. ++ */ ++static int nfs_remount(struct nfsmount_info *mi) ++{ ++ if (nfs_sys_mount(mi, mi->options)) ++ return EX_SUCCESS; ++ return EX_FAIL; ++} ++ ++/* + * Process mount options and try a mount system call. + * + * Returns a valid mount command exit code. +@@ -937,6 +1002,12 @@ static int nfsmount_start(struct nfsmoun + if (!nfs_validate_options(mi)) + return EX_FAIL; + ++ /* ++ * Avoid retry and negotiation logic when remounting ++ */ ++ if (mi->flags & MS_REMOUNT) ++ return nfs_remount(mi); ++ + if (po_rightmost(mi->options, nfs_background_opttbl) == 0) + return nfsmount_bg(mi); + else +@@ -953,6 +1024,8 @@ static int nfsmount_start(struct nfsmoun + * (input and output argument) + * @fake: flag indicating whether to carry out the whole operation + * @child: one if this is a mount daemon (bg) ++ * ++ * Returns a valid mount command exit code. + */ + int nfsmount_string(const char *spec, const char *node, const char *type, + int flags, char **extra_opts, int fake, int child) +diff -up nfs-utils-1.2.2/utils/nfsd/nfsd.c.orig nfs-utils-1.2.2/utils/nfsd/nfsd.c +--- nfs-utils-1.2.2/utils/nfsd/nfsd.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/nfsd/nfsd.c 2010-09-16 16:29:35.262240028 -0400 +@@ -246,6 +246,9 @@ main(int argc, char **argv) + exit(1); + } + ++ /* make sure nfsdfs is mounted if it's available */ ++ nfssvc_mount_nfsdfs(progname); ++ + /* can only change number of threads if nfsd is already up */ + if (nfssvc_inuse()) { + socket_up = 1; +diff -up nfs-utils-1.2.2/utils/nfsd/nfssvc.c.orig nfs-utils-1.2.2/utils/nfsd/nfssvc.c +--- nfs-utils-1.2.2/utils/nfsd/nfssvc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/nfsd/nfssvc.c 2010-09-16 16:29:35.263239998 -0400 +@@ -15,9 +15,11 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + + #include "nfslib.h" + #include "xlog.h" +@@ -31,9 +33,13 @@ + */ + #undef IPV6_SUPPORTED + +-#define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" +-#define NFSD_VERS_FILE "/proc/fs/nfsd/versions" +-#define NFSD_THREAD_FILE "/proc/fs/nfsd/threads" ++#ifndef NFSD_FS_DIR ++#define NFSD_FS_DIR "/proc/fs/nfsd" ++#endif ++ ++#define NFSD_PORTS_FILE NFSD_FS_DIR "/portlist" ++#define NFSD_VERS_FILE NFSD_FS_DIR "/versions" ++#define NFSD_THREAD_FILE NFSD_FS_DIR "/threads" + + /* + * declaring a common static scratch buffer here keeps us from having to +@@ -44,6 +50,46 @@ + char buf[128]; + + /* ++ * Using the "new" interfaces for nfsd requires that /proc/fs/nfsd is ++ * actually mounted. Make an attempt to mount it here if it doesn't appear ++ * to be. If the mount attempt fails, no big deal -- fall back to using nfsctl ++ * instead. ++ */ ++void ++nfssvc_mount_nfsdfs(char *progname) ++{ ++ int err; ++ struct stat statbuf; ++ ++ err = stat(NFSD_THREAD_FILE, &statbuf); ++ if (err == 0) ++ return; ++ ++ if (errno != ENOENT) { ++ xlog(L_ERROR, "Unable to stat %s: errno %d (%m)", ++ NFSD_THREAD_FILE, errno); ++ return; ++ } ++ ++ /* ++ * this call can return an error if modprobe is set up to automatically ++ * mount nfsdfs when nfsd.ko is plugged in. So, ignore the return ++ * code from it and just check for the "threads" file afterward. ++ */ ++ system("/bin/mount -t nfsd nfsd " NFSD_FS_DIR " >/dev/null 2>&1"); ++ ++ err = stat(NFSD_THREAD_FILE, &statbuf); ++ if (err == 0) ++ return; ++ ++ xlog(L_WARNING, "Unable to access " NFSD_FS_DIR " errno %d (%m)." ++ "\nPlease try, as root, 'mount -t nfsd nfsd " NFSD_FS_DIR ++ "' and then restart %s to correct the problem", errno, progname); ++ ++ return; ++} ++ ++/* + * Are there already sockets configured? If not, then it is safe to try to + * open some and pass them through. + * +@@ -181,7 +227,7 @@ nfssvc_setfds(const struct addrinfo *hin + } + + snprintf(buf, sizeof(buf), "%d\n", sockfd); +- if (write(fd, buf, strlen(buf)) != strlen(buf)) { ++ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { + /* + * this error may be common on older kernels that don't + * support IPv6, so turn into a debug message. +@@ -251,7 +297,7 @@ nfssvc_setvers(unsigned int ctlbits, int + } + xlog(D_GENERAL, "Writing version string to kernel: %s", buf); + snprintf(ptr+off, sizeof(buf) - off, "\n"); +- if (write(fd, buf, strlen(buf)) != strlen(buf)) ++ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) + xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno); + + close(fd); +@@ -277,7 +323,7 @@ nfssvc_threads(unsigned short port, cons + snprintf(buf, sizeof(buf), "%d\n", nrservs); + n = write(fd, buf, strlen(buf)); + close(fd); +- if (n != strlen(buf)) ++ if (n != (ssize_t)strlen(buf)) + return -1; + else + return 0; +diff -up nfs-utils-1.2.2/utils/nfsd/nfssvc.h.orig nfs-utils-1.2.2/utils/nfsd/nfssvc.h +--- nfs-utils-1.2.2/utils/nfsd/nfssvc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/nfsd/nfssvc.h 2010-09-16 16:29:35.263239998 -0400 +@@ -20,6 +20,7 @@ + * + */ + ++void nfssvc_mount_nfsdfs(char *progname); + int nfssvc_inuse(void); + int nfssvc_set_sockets(const int family, const unsigned int protobits, + const char *host, const char *port); +diff -up nfs-utils-1.2.2/utils/nfsstat/nfsstat.c.orig nfs-utils-1.2.2/utils/nfsstat/nfsstat.c +--- nfs-utils-1.2.2/utils/nfsstat/nfsstat.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/nfsstat/nfsstat.c 2010-09-16 16:29:35.264239903 -0400 +@@ -31,7 +31,7 @@ enum { + SRVPROC3_SZ = 22, + CLTPROC3_SZ = 22, + SRVPROC4_SZ = 2, +- CLTPROC4_SZ = 48, ++ CLTPROC4_SZ = 49, + SRVPROC4OPS_SZ = 59, + }; + +@@ -118,6 +118,7 @@ static const char * nfscltproc4name[CLTP + "remove", "rename", "link", "symlink", "create", "pathconf", + "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl", + "setacl", "fs_locations", ++ "rel_lkowner", + /* nfsv4.1 client ops */ + "exchange_id", + "create_ses", +@@ -791,7 +792,7 @@ print_callstats(const char *hdr, const c + { + unsigned long long total; + unsigned long long pct; +- int i, j; ++ unsigned int i, j; + + fputs(hdr, stdout); + for (i = 0, total = 0; i < nr; i++) +@@ -816,7 +817,7 @@ print_callstats_list(const char *hdr, co + unsigned int *callinfo, unsigned int nr) + { + unsigned long long calltotal; +- int i; ++ unsigned int i; + + for (i = 0, calltotal = 0; i < nr; i++) { + calltotal += callinfo[i]; +@@ -1118,7 +1119,7 @@ unpause(int sig) + time_diff = difftime(endtime, starttime); + minutes = time_diff / 60; + seconds = (int)time_diff % 60; +- printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds); ++ printf("Signal %d received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", sig, minutes, seconds); + } + + static void +diff -up nfs-utils-1.2.2/utils/showmount/showmount.c.orig nfs-utils-1.2.2/utils/showmount/showmount.c +--- nfs-utils-1.2.2/utils/showmount/showmount.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/showmount/showmount.c 2010-09-16 16:29:35.264239903 -0400 +@@ -194,7 +194,13 @@ int main(int argc, char **argv) + } + + mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); +- mclient->cl_auth = authunix_create_default(); ++ mclient->cl_auth = nfs_authsys_create(); ++ if (mclient->cl_auth == NULL) { ++ fprintf(stderr, "%s: unable to create RPC auth handle.\n", ++ program_name); ++ clnt_destroy(mclient); ++ exit(1); ++ } + total_timeout.tv_sec = TOTAL_TIMEOUT; + total_timeout.tv_usec = 0; + +diff -up nfs-utils-1.2.2/utils/statd/hostname.c.orig nfs-utils-1.2.2/utils/statd/hostname.c +--- nfs-utils-1.2.2/utils/statd/hostname.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/statd/hostname.c 2010-09-16 16:29:35.265220019 -0400 +@@ -212,7 +212,9 @@ statd_canonical_name(const char *hostnam + buf, (socklen_t)sizeof(buf)); + freeaddrinfo(ai); + if (!result) +- return NULL; ++ /* OK to use presentation address, ++ * if no reverse map exists */ ++ return strdup(hostname); + return strdup(buf); + } + +diff -up nfs-utils-1.2.2/utils/statd/sm-notify.c.orig nfs-utils-1.2.2/utils/statd/sm-notify.c +--- nfs-utils-1.2.2/utils/statd/sm-notify.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/statd/sm-notify.c 2010-09-16 16:29:35.266239946 -0400 +@@ -54,7 +54,7 @@ struct nsm_host { + uint32_t xid; + }; + +-static char nsm_hostname[256]; ++static char nsm_hostname[SM_MAXSTRLEN + 1]; + static int nsm_state; + static int nsm_family = AF_INET; + static int opt_debug = 0; +@@ -412,12 +412,33 @@ usage: fprintf(stderr, + } + } + +- if (opt_srcaddr) { +- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); +- } else +- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { +- xlog(L_ERROR, "Failed to obtain name of local host: %m"); +- exit(1); ++ if (opt_srcaddr != NULL) { ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ .ai_family = AF_UNSPEC, ++ .ai_flags = AI_NUMERICHOST, ++ }; ++ ++ if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) ++ /* not a presentation address - use it */ ++ strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); ++ else { ++ /* was a presentation address - look it up in ++ * /etc/hosts, so it can be used for my_name */ ++ int error; ++ ++ freeaddrinfo(ai); ++ hint.ai_flags = AI_CANONNAME; ++ error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); ++ if (error != 0) { ++ xlog(L_ERROR, "Bind address %s is unusable: %s", ++ opt_srcaddr, gai_strerror(error)); ++ exit(1); ++ } ++ strncpy(nsm_hostname, ai->ai_canonname, ++ sizeof(nsm_hostname)); ++ freeaddrinfo(ai); ++ } + } + + (void)nsm_retire_monitored_hosts(); +@@ -535,6 +556,8 @@ notify(const int sock) + static int + notify_host(int sock, struct nsm_host *host) + { ++ const char *my_name = (opt_srcaddr != NULL ? ++ nsm_hostname : host->my_name); + struct sockaddr *sap; + socklen_t salen; + +@@ -580,8 +603,8 @@ notify_host(int sock, struct nsm_host *h + host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); + else + host->xid = nsm_xmit_notify(sock, sap, salen, +- SM_PROG, nsm_hostname, nsm_state); +- ++ SM_PROG, my_name, nsm_state); ++ + return 0; + } + +@@ -611,15 +634,28 @@ recv_rpcbind_reply(struct sockaddr *sap, + } + + /* +- * Successful NOTIFY call. Server returns void, so nothing +- * we need to do here. ++ * Successful NOTIFY call. Server returns void. ++ * ++ * Try sending another SM_NOTIFY with an unqualified "my_name" ++ * argument. Reuse the port number. If "my_name" is already ++ * unqualified, we're done. + */ + static void + recv_notify_reply(struct nsm_host *host) + { +- xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ char *dot = strchr(host->my_name, '.'); + +- smn_forget_host(host); ++ if (dot != NULL) { ++ *dot = '\0'; ++ host->send_next = time(NULL); ++ host->xid = 0; ++ if (host->timeout >= NSM_MAX_TIMEOUT / 4) ++ host->timeout = NSM_MAX_TIMEOUT / 4; ++ insert_host(host); ++ } else { ++ xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ smn_forget_host(host); ++ } + } + + /* +diff -up nfs-utils-1.2.2/utils/statd/sm-notify.man.orig nfs-utils-1.2.2/utils/statd/sm-notify.man +--- nfs-utils-1.2.2/utils/statd/sm-notify.man.orig 2010-09-16 15:40:15.595012524 -0400 ++++ nfs-utils-1.2.2/utils/statd/sm-notify.man 2010-09-16 16:29:35.266239946 -0400 +@@ -97,11 +97,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command normally sends + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY r + If this option is not specified, + .B sm-notify + uses a wildcard address as the transport bind address, +-and uses the results of +-.BR gethostname (3) +-as the ++and uses the ++.I my_name ++recorded when the remote was monitored as the + .I mon_name +-argument. ++argument when sending SM_NOTIFY requests. + .IP + The + .I ipaddr + form can be expressed as either an IPv4 or an IPv6 presentation address. ++If the ++.I ipaddr ++form is used, the ++.B sm-notify ++command converts this address to a hostname for use as the ++.I mon_name ++argument when sending SM_NOTIFY requests. + .IP + This option can be useful in multi-homed configurations where + the remote requires notification from a specific network address. +@@ -252,13 +257,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. +diff -up nfs-utils-1.2.2/utils/statd/statd.man.orig nfs-utils-1.2.2/utils/statd/statd.man +--- nfs-utils-1.2.2/utils/statd/statd.man.orig 2010-09-16 15:40:15.595012524 -0400 ++++ nfs-utils-1.2.2/utils/statd/statd.man 2010-09-16 16:29:35.267229816 -0400 +@@ -100,11 +100,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command sends the + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -292,7 +290,6 @@ man pages. + .SH ADDITIONAL NOTES + Lock recovery after a reboot is critical to maintaining data integrity + and preventing unnecessary application hangs. +-.PP + To help + .B rpc.statd + match SM_NOTIFY requests to NLM requests, a number of best practices +@@ -309,13 +306,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. diff --git a/nfs-utils.spec b/nfs-utils.spec index 67cac40..34a2b43 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -18,7 +18,7 @@ Source13: rpcgssd.init Source14: rpcsvcgssd.init Source15: nfs.sysconfig -Patch001: nfs-utils-1-2-3-rc5.patch +Patch001: nfs-utils-1-2-3-rc6.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.2-statdpath.patch @@ -251,6 +251,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Thu Sep 16 2010 Steve Dickson 1.2.2-8 +- Update to upstream RC release: nfs-utils-1-2-3-rc6 + * Thu Sep 9 2010 Steve Dickson 1.2.2-7 - Update to upstream RC release: nfs-utils-1-2-3-rc5