]> git.pld-linux.org Git - packages/nfs-utils.git/commitdiff
- git patch with nfs-utils-1-2-3-rc4 changes
authorJan Rękorajski <baggins@pld-linux.org>
Mon, 30 Aug 2010 12:15:32 +0000 (12:15 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
- real ipv6 support in nfsd and mountd
- support for more krb5 enctypes (AES, ARCFOUR, etc., req kernel 2.6.35+),
  at last no more DES!

Changed files:
    nfs-utils-git.patch -> 1.1
    nfs-utils-heimdal_functions.patch -> 1.4
    nfs-utils-no_libgssapi.patch -> 1.3
    nfs-utils.spec -> 1.187

nfs-utils-git.patch [new file with mode: 0644]
nfs-utils-heimdal_functions.patch
nfs-utils-no_libgssapi.patch
nfs-utils.spec

diff --git a/nfs-utils-git.patch b/nfs-utils-git.patch
new file mode 100644 (file)
index 0000000..c1e0288
--- /dev/null
@@ -0,0 +1,7262 @@
+diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4
+index eabe507..68a624c 100644
+--- a/aclocal/libcap.m4
++++ b/aclocal/libcap.m4
+@@ -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 --git a/autogen.sh b/autogen.sh
+old mode 100644
+new mode 100755
+diff --git a/configure.ac b/configure.ac
+index b7520d8..7c9e61a 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -81,7 +81,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"])
+@@ -400,7 +400,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"])
+@@ -425,6 +425,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 --git a/support/export/client.c b/support/export/client.c
+index 6236561..c74961e 100644
+--- a/support/export/client.c
++++ b/support/export/client.c
+@@ -17,7 +17,9 @@
+ #include <string.h>
+ #include <ctype.h>
+ #include <netdb.h>
+-#include "xmalloc.h"
++#include <errno.h>
++
++#include "sockaddr.h"
+ #include "misc.h"
+ #include "nfslib.h"
+ #include "exportfs.h"
+@@ -28,58 +30,211 @@
+ #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_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
++{
++      struct sockaddr_in sin = {
++              .sin_family             = AF_INET,
++      };
++      unsigned long prefixlen;
++      uint32_t shift;
++#ifdef IPV6_SUPPORTED
++      struct sockaddr_in6 sin6 = {
++              .sin6_family            = AF_INET6,
++      };
++      int i;
++#endif
++
++      /* No slash present; assume netmask is all ones */
++      if (slash == NULL) {
++              switch (family) {
++              case AF_INET:
++                      prefixlen = 32;
++                      break;
++#ifdef IPV6_SUPPORTED
++              case AF_INET6:
++                      prefixlen = 128;
++                      break;
++#endif
++              default:
++                      goto out_badfamily;
++              }
++      } else {
++              char *endptr;
++
++              /* A spelled out netmask address, perhaps? */
++              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;
++              }
++#ifdef IPV6_SUPPORTED
++              if (strchr(slash + 1, ':')) {
++                      if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr))
++                              goto out_badmask;
++                      set_addrlist_in6(clp, 1, &sin6);
++                      return 1;
++              }
++#endif
++
++              /* A prefixlen was given */
++              prefixlen = strtoul(slash + 1, &endptr, 10);
++              if (*endptr != '\0' && prefixlen != ULONG_MAX && errno != ERANGE)
++                      goto out_badprefix;
++      }
++
++      switch (family) {
++      case AF_INET:
++              if (prefixlen > 32)
++                      goto out_badprefix;
++              shift = 32 - (uint32_t)prefixlen;
++              sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift);
++              set_addrlist_in(clp, 1, &sin);
++              return 1;
++#ifdef IPV6_SUPPORTED
++      case AF_INET6:
++              if (prefixlen > 128)
++                      goto out_badprefix;
++              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;
++#endif
++      }
++
++out_badfamily:
++      xlog(L_ERROR, "Unsupported address family for %s", clp->m_hostname);
++      return 0;
++
++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;
++}
++
++static int
++init_subnetwork(nfs_client *clp)
++{
++      struct addrinfo *ai;
++      sa_family_t family;
++      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 false;
++      }
++
++      set_addrlist(clp, 0, ai->ai_addr);
++      family = ai->ai_addr->sa_family;
++
++      freeaddrinfo(ai);
++
++      return init_netmask(clp, slash, family);
++}
++
++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 +242,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 +304,10 @@ client_release(nfs_client *clp)
+       clp->m_count--;
+ }
++/**
++ * client_freeall - deallocate all nfs_client records
++ *
++ */
+ void
+ client_freeall(void)
+ {
+@@ -205,57 +318,45 @@ client_freeall(void)
+               head = clientlist + i;
+               while (*head) {
+                       *head = (clp = *head)->m_next;
+-                      xfree(clp->m_hostname);
+-                      xfree(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
++                      client_free(clp);
+               }
+       }
+-      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 +364,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 +372,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 +397,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 +411,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 +446,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 (sin6->sin6_family != AF_INET6)
++                      continue;
++
++              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 */
+-                      /* 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;
+-                      }
++/*
++ * 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);
++      }
+-                      /* Okay, strip off the domain (if we have one) */
+-                      if ((dot = strchr(hname, '.')) == NULL)
+-                              return 0;
++      return 0;
++}
+-                      *dot = '\0';
+-                      match = innetgr(cname+1, hname, NULL, NULL);
+-                      *dot = '.';
++/*
++ * 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;
+-                      return match;
+-              }
+-#else
+-              return 0;
+-#endif
+-      case MCL_ANONYMOUS:
++      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 +716,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 --git a/support/export/export.c b/support/export/export.c
+index 2943466..4fda30a 100644
+--- a/support/export/export.c
++++ b/support/export/export.c
+@@ -24,9 +24,25 @@ 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);
++      free(exp->m_export.e_uuid);
++
++      xfree(exp->m_export.e_hostname);
++      xfree(exp);
++}
+ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
+ {
+@@ -44,7 +60,12 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
+       }
+ }
+-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 +80,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 +130,8 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
+  * 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 +141,11 @@ export_dup(nfs_export *exp, struct hostent *hp)
+       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 +157,8 @@ export_dup(nfs_export *exp, struct hostent *hp)
+       return new;
+ }
+-/*
+- * Add export entry to hash table
+- */
+-void 
++
++static void
+ export_add(nfs_export *exp)
+ {
+       exp_hash_table *p_tbl;
+@@ -159,19 +186,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 +214,7 @@ export_find(struct hostent *hp, char *path)
+ }
+ 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 +222,7 @@ export_allowed_internal (struct hostent *hp, char *path)
+       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 +231,16 @@ export_allowed_internal (struct hostent *hp, char *path)
+       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 +253,7 @@ export_allowed(struct hostent *hp, char *path)
+       /* 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 +266,17 @@ export_allowed(struct hostent *hp, char *path)
+       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 +300,18 @@ export_lookup(char *hname, char *path, int canonical)
+ }
+ 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 +322,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 --git a/support/export/hostname.c b/support/export/hostname.c
+index 8a23a89..3c55ce7 100644
+--- a/support/export/hostname.c
++++ b/support/export/hostname.c
+@@ -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 <http://www.gnu.org/licenses/>.
+  */
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+-/*
+-#define TEST
+-*/
+-
+ #include <string.h>
+-#include <netdb.h>
+-#include <netinet/in.h>
+-#include <arpa/inet.h>
+ #include <stdlib.h>
+-#include <xlog.h>
+-#ifdef TEST
+-#define xmalloc malloc
+-#else
+-#include "xmalloc.h"
+-#include "misc.h"
+-#endif
++#include <arpa/inet.h>
++#include <netdb.h>
++#include <errno.h>
+-#define ALIGNMENT     sizeof (char *)
++#include "sockaddr.h"
++#include "exportfs.h"
+-static int
+-align (int len, int al)
++#ifndef HAVE_DECL_AI_ADDRCONFIG
++#define AI_ADDRCONFIG 0
++#endif
++
++/**
++ * 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)
+ {
+-  int i;
+-  i = len % al;
+-  if (i)
+-    len += al - i;
+-  return len;
+-}
++      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);
++      }
+-struct hostent *
+-get_hostent (const char *addr, int len, int type)
++      return buf;
++}
++#else /* !HAVE_GETNAMEINFO */
++char *
++host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
+ {
+-  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;
+-
+-  return cp;
++      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 *
+-hostent_dup (struct hostent *hp)
++/**
++ * 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)
+ {
+-  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;
+-
+-  return cp;
++      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 NULL;
+ }
+-static int
+-is_hostname(const char *sp)
++/**
++ * 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)
+ {
+-  if (*sp == '\0' || *sp == '@')
+-    return 0;
+-
+-  for (; *sp; sp++)
+-    {
+-      if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
+-      return 0;
+-      if (*sp == '\\' && sp[1])
+-      sp++;
+-    }
+-
+-  return 1;
++      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 NULL;
+ }
+-int
+-matchhostname (const char *h1, const char *h2)
++/**
++ * 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)
+ {
+-  struct hostent *hp1, *hp2;
+-  int status;
+-
+-  if (strcasecmp (h1, h2) == 0)
+-    return 1;
+-
+-  if (!is_hostname (h1) || !is_hostname (h2))
+-    return 0;
+-
+-  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;
+-              }
++      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;
++      }
++
++      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;
+       }
+-    }
+-  else
+-    status = 0;
+-  free (hp1);
+-  return status;
++      return strdup(buf);
+ }
++#else /* !HAVE_GETNAMEINFO */
++__attribute_malloc__
++char *
++host_canonname(const struct sockaddr *sap)
++{
++      const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
++      const struct in_addr *addr = &sin->sin_addr;
++      struct hostent *hp;
++
++      if (sap->sa_family != AF_INET)
++              return NULL;
++      hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
++      if (hp == NULL)
++              return NULL;
++
++      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;
+-
+-  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");
+-}
++      memset(buf, 0, sizeof(buf));
++      if (inet_ntop(AF_INET, (char *)addr, buf,
++                                      (socklen_t)sizeof(buf)) == NULL)
++              return NULL;
+-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;
++      ai = host_pton(buf);
++
++      /*
++       * 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 --git a/support/export/nfsctl.c b/support/export/nfsctl.c
+index e2877b9..f89c644 100644
+--- a/support/export/nfsctl.c
++++ b/support/export/nfsctl.c
+@@ -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, nfs_client *clp)
+       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, nfs_export *exp, int unexport)
+       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 --git a/support/export/rmtab.c b/support/export/rmtab.c
+index b49e1aa..31c0f50 100644
+--- a/support/export/rmtab.c
++++ b/support/export/rmtab.c
+@@ -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 --git a/support/include/exportfs.h b/support/include/exportfs.h
+index 470b2ec..3cf1ee8 100644
+--- a/support/include/exportfs.h
++++ b/support/include/exportfs.h
+@@ -10,6 +10,8 @@
+ #define EXPORTFS_H
+ #include <netdb.h>
++
++#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_MAXTYPES];
+ 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 --git a/support/include/misc.h b/support/include/misc.h
+index 9a1b25d..bc5ba23 100644
+--- a/support/include/misc.h
++++ b/support/include/misc.h
+@@ -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 --git a/support/include/nfslib.h b/support/include/nfslib.h
+index 537a31e..3db5bec 100644
+--- a/support/include/nfslib.h
++++ b/support/include/nfslib.h
+@@ -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 *clp);
+ 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, char *buf, int blen);
+ 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 --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
+index 4db35ab..6ebefca 100644
+--- a/support/include/nfsrpc.h
++++ b/support/include/nfsrpc.h
+@@ -160,4 +160,7 @@ extern int         nfs_rpc_ping(const struct sockaddr *sap,
+                               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 --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
+index 1b8f411..c5847fa 100644
+--- a/support/include/rpcmisc.h
++++ b/support/include/rpcmisc.h
+@@ -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 --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c
+index bdf5d84..9bad8e6 100644
+--- a/support/nfs/cacheio.c
++++ b/support/nfs/cacheio.c
+@@ -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 --git a/support/nfs/conffile.c b/support/nfs/conffile.c
+index b277c2a..24640f4 100644
+--- a/support/nfs/conffile.c
++++ b/support/nfs/conffile.c
+@@ -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, size_t sz)
+ {
+       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 sz)
+ }
+ 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 --git a/support/nfs/exports.c b/support/nfs/exports.c
+index a93941c..1744ed6 100644
+--- a/support/nfs/exports.c
++++ b/support/nfs/exports.c
+@@ -332,6 +332,8 @@ dupexportent(struct exportent *dst, struct exportent *src)
+               dst->e_mountpoint = strdup(src->e_mountpoint);
+       if (src->e_fslocdata)
+               dst->e_fslocdata = strdup(src->e_fslocdata);
++      if (src->e_uuid)
++              dst->e_uuid = strdup(src->e_uuid);
+       dst->e_hostname = NULL;
+ }
+diff --git a/support/nfs/getfh.c b/support/nfs/getfh.c
+index 81266fd..611459b 100644
+--- a/support/nfs/getfh.c
++++ b/support/nfs/getfh.c
+@@ -19,60 +19,112 @@
+ #include <errno.h>
+ #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 --git a/support/nfs/nfs_mntent.c b/support/nfs/nfs_mntent.c
+index a3fecfc..9c73ae0 100644
+--- a/support/nfs/nfs_mntent.c
++++ b/support/nfs/nfs_mntent.c
+@@ -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 --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c
+index a28abf3..ca789a3 100644
+--- a/support/nfs/rmtab.c
++++ b/support/nfs/rmtab.c
+@@ -19,6 +19,18 @@
+ #include <signal.h>
+ #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 *pos)
+               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 *pos)
+ 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 --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
+index 0e20824..c14efe8 100644
+--- a/support/nfs/rpc_socket.c
++++ b/support/nfs/rpc_socket.c
+@@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
+       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 --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c
+index 502fc5f..984c646 100644
+--- a/support/nfs/rpcdispatch.c
++++ b/support/nfs/rpcdispatch.c
+@@ -27,12 +27,12 @@ rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp,
+ {
+       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 --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c
+index a0854e7..b73187a 100644
+--- a/support/nfs/rpcmisc.c
++++ b/support/nfs/rpcmisc.c
+@@ -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 --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
+index f44217a..03a5325 100644
+--- a/support/nfs/svc_socket.c
++++ b/support/nfs/svc_socket.c
+@@ -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 protocol, int reuse)
+   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 --git a/support/nsm/file.c b/support/nsm/file.c
+index d469219..f4baeb9 100644
+--- a/support/nsm/file.c
++++ b/support/nsm/file.c
+@@ -67,7 +67,9 @@
+ #endif
+ #include <sys/types.h>
++#ifdef HAVE_SYS_CAPABILITY_H
+ #include <sys/capability.h>
++#endif
+ #include <sys/prctl.h>
+ #include <sys/stat.h>
+@@ -347,6 +349,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");
+@@ -362,6 +365,7 @@ nsm_clear_capabilities(void)
+       }
+       (void)cap_free(caps);
++#endif
+       return true;
+ }
+diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh
+old mode 100644
+new mode 100755
+diff --git a/tools/Makefile.am b/tools/Makefile.am
+index db15346..f2ce282 100644
+--- a/tools/Makefile.am
++++ b/tools/Makefile.am
+@@ -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 --git a/tools/mountstats/Makefile.am b/tools/mountstats/Makefile.am
+new file mode 100644
+index 0000000..ca617a2
+--- /dev/null
++++ b/tools/mountstats/Makefile.am
+@@ -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 --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man
+new file mode 100644
+index 0000000..0de31b7
+--- /dev/null
++++ b/tools/mountstats/mountstats.man
+@@ -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 ["<options> "] " <mount_point> " [ " <mount_point> "]" 
++.SH DESCRIPTION
++The
++.B mountstats
++command displays NFS client statisitics on each given
++.I <mount_point>
++.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 <chuck.lever@oracle.com>
+diff --git a/tools/nfs-iostat/Makefile.am b/tools/nfs-iostat/Makefile.am
+new file mode 100644
+index 0000000..30f4054
+--- /dev/null
++++ b/tools/nfs-iostat/Makefile.am
+@@ -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 --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
+index 2d0b143..1207674 100644
+--- a/tools/nfs-iostat/nfs-iostat.py
++++ b/tools/nfs-iostat/nfs-iostat.py
+@@ -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 --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man
+new file mode 100644
+index 0000000..99e04fb
+--- /dev/null
++++ b/tools/nfs-iostat/nfsiostat.man
+@@ -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 [[" <interval> "] [" <count> "]] [" <options> "]["<mount_point> "]
++.SH DESCRIPTION
++The
++.B nfsiostat
++command displays NFS client per-mount statisitics. 
++.TP 
++<interval>
++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
++<count>
++If the
++.I <count>
++parameter is
++specified, the value of 
++.I <count> 
++determines the number of reports generated at
++. <interval> 
++seconds apart. if the interval parameter is 
++specified without the
++.I <count> 
++parameter, the command generates reports continuously.
++.TP
++<options>
++Define below
++.TP
++<mount_point>
++If one or more
++.I <mount point> 
++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 <chuck.lever@oracle.com>
+diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
+index 331e57e..b78957f 100644
+--- a/utils/exportfs/exportfs.c
++++ b/utils/exportfs/exportfs.c
+@@ -12,20 +12,24 @@
+ #include <config.h>
+ #endif
++#include <sys/types.h>
++#include <sys/stat.h>
+ #include <sys/vfs.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <stdbool.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdarg.h>
+ #include <getopt.h>
++#include <fcntl.h>
+ #include <netdb.h>
+ #include <errno.h>
+-#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 verbose);
+ 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 verbose)
+ {
+       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 verbose)
+               *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 verbose)
+       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 --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
+index c7b230a..089f75b 100644
+--- a/utils/exportfs/exportfs.man
++++ b/utils/exportfs/exportfs.man
+@@ -1,11 +1,11 @@
++.\"@(#)exportfs.8"
+ .\"
+-.\" exportfs(8)
+-.\" 
+ .\" Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ .\" Modifications 1999-2003 Neil Brown <neilb@cse.unsw.edu.au>
+-.TH exportfs 8 "18 July 2003"
++.\"
++.TH exportfs 8 "31 December 2009"
+ .SH NAME
+-exportfs \- maintain list of NFS exported file systems
++exportfs \- maintain table of exported NFS file systems
+ .SH SYNOPSIS
+ .BI "/usr/sbin/exportfs [-avi] [-o " "options,.." "] [" "client:/path" " ..]
+ .br
+@@ -18,229 +18,236 @@ exportfs \- maintain list of NFS exported file systems
+ .BI "/usr/sbin/exportfs -f"
+ .br
+ .SH DESCRIPTION
++An NFS server maintains a table of local physical file systems
++that are accessible to NFS clients.
++Each file system in this table is  referred to as an
++.IR "exported file system" ,
++or
++.IR export ,
++for short.
++.PP
+ The
+ .B exportfs
+-command is used to maintain the current table of exported file systems for
+-NFS. This list is kept in a separate file named
+-.BR /var/lib/nfs/etab
+-which is read by
+-.B mountd
+-when a remote host requests access to mount a file tree, and parts of
+-the list which are active are kept in the kernel's export table.
+-.P
+-Normally this 
+-.B etab
+-file is initialized with the list of all file systems named in
+-.B /etc/exports 
++command maintains the current table of exports for the NFS server.
++The master export table is kept in a file named
++.IR /var/lib/nfs/etab .
++This file is read by
++.B rpc.mountd
++when a client sends an NFS MOUNT request.
++.PP
++Normally the master export table is initialized with the contents of
++.I /etc/exports
+ by invoking
+ .BR "exportfs -a" .
+-.P
+-However, administrators can choose to add and delete individual file systems
+-without modifying
+-.B /etc/exports
+-using
+-.BR exportfs .
+-.P
++However, a system administrator can choose to add or delete
++exports without modifying
++.I /etc/exports
++by using the
++.B exportfs
++command.
++.PP
+ .B exportfs
+-and it's partner program
+-.B mountd
+-work in one of two modes, a legacy mode which applies to 2.4 and
++and its partner program
++.B rpc.mountd
++work in one of two modes: a legacy mode which applies to 2.4 and
+ earlier versions of the Linux kernel, and a new mode which applies to
+-2.6 and later versions providing the
++2.6 and later versions, providing the
+ .B nfsd
+ virtual filesystem has been mounted at
+-.B /proc/fs/nfsd
++.I /proc/fs/nfsd
+ or
+-.BR /proc/fs/nfs .
+-If this filesystem is not mounted in 2.6, the legacy mode is used.
+-.P
++.IR /proc/fs/nfs .
++On 2.6 kernels, if this filesystem is not mounted, the legacy mode is used.
++.PP
+ In the new mode,
+ .B exportfs
+-does not give any information to the kernel but only provides it to
+-.B mountd
++does not give any information to the kernel, but provides it only to
++.B rpc.mountd
+ through the
+-.B /var/lib/nfs/etab
++.I /var/lib/nfs/etab
+ file.
+-.B mountd
+-will listen to requests from the kernel and will provide information
+-as needed.
+-.P
++.B rpc.mountd
++then manages kernel requests for information about exports, as needed.
++.PP
+ In the legacy mode,
+-any export requests which identify a specific host (rather than a
+-subnet or netgroup etc) are entered directly into the kernel's export
+-table as well as being written to
+-.BR /var/lib/nfs/etab .
+-Further, any mount points listed in
+-.B /var/lib/nfs/rmtab
++exports which identify a specific host, rather than a subnet or netgroup,
++are entered directly into the kernel's export table,
++as well as being written to
++.IR /var/lib/nfs/etab .
++Further, exports listed in
++.I /var/lib/nfs/rmtab
+ which match a non host-specific export request will cause an
+ appropriate export entry for the host given in
+-.B rmtab
+-to be entered
+-into the kernel's export table.
++.I rmtab
++to be added to the kernel's export table.
+ .SH OPTIONS
+-.TP 
++.TP
+ .B -a
+ Export or unexport all directories.
+ .TP
+ .BI "-o " options,...
+ Specify a list of export options in the same manner as in
+-.BR exports(5) .
++.BR exports (5).
+ .TP
+ .B -i
+ Ignore the
+-.B /etc/exports
+-file, so that only default options and options given on the command
+-line are used.
++.I /etc/exports
++file.  Only default options and options given on the command line are used.
+ .TP
+ .B -r
+-Reexport all directories. It synchronizes /var/lib/nfs/etab
+-with /etc/exports. It removes entries in /var/lib/nfs/etab
+-which are deleted from /etc/exports, and remove any entries from the
++Reexport all directories, synchronizing
++.I /var/lib/nfs/etab
++with
++.IR /etc/exports .
++This option removes entries in
++.I /var/lib/nfs/etab
++which have been deleted from
++.I /etc/exports, and removes any entries from the
+ kernel export table which are no longer valid.
+ .TP
+ .B -u
+ Unexport one or more directories.
+ .TP
+ .B -f
+-In 'new' mode, flush everything out of the kernels export table. Any
+-clients that are active will get new entries added by
+-.B mountd
+-when they make their next request.
++If
++.I /proc/fs/nfsd
++or
++.I /proc/fs/nfs
++is mounted, flush everything out of the kernel's export table.
++Fresh entries for active clients are added to the kernel's export table by
++.B rpc.mountd
++when they make their next NFS mount request.
+ .TP
+ .B -v
+ Be verbose. When exporting or unexporting, show what's going on. When
+ displaying the current export list, also display the list of export
+ options.
+ .SH DISCUSSION
+-.\" -------------------- Exporting Directories --------------------
+ .SS Exporting Directories
+-The first synopsis shows how to invoke the command when adding new
+-entries to the export table.  When using 
++The first synopsis shows how to invoke
++.B exportfs
++when adding new entries to the export table.  When using
+ .BR "exportfs -a" ,
+-all directories in
+-.B exports(5)
++all exports listed in
++.I /etc/exports
+ are added to
+-.B etab
+-and the resulting list is pushed into the kernel.
+-.P
++.IR /var/lib/nfs/etab .
++The kernel's export table is also updated as needed.
++.PP
+ The
+ .I host:/path
+-argument specifies the directory to export along with the host or hosts to
+-export it to. All formats described in
++argument specifies a local directory to export,
++along with the client or clients who are permitted to access it.
++See
+ .B exports(5)
+-are supported; to export a directory to the world, simply specify
++for a description of supported options and access list formats.
++To export a directory to the world, simply specify
+ .IR :/path .
+-.P
++.PP
+ The export options for a particular host/directory pair derive from
+-several sources.  There is a set of default options which can be overridden by
+-entries in
+-.B /etc/exports
+-(unless the
+-.B -i
+-option is given).
+-In addition, the administrator may override any options from these sources
+-using the
++several sources.
++The default export options are
++.BR sync,ro,root_squash,wdelay .
++These can be overridden by entries in
++.IR /etc/exports .
++.PP
++A system administrator may override options from these sources using the
+ .B -o
+-argument which takes a comma-separated list of options in the same fashion
++command-line option on
++.BR exportfs .
++This option takes a comma-separated list of options in the same fashion
+ as one would specify them in
+-.BR exports(5) .
+-Thus,
++.IR /etc/exports .
++In this way
+ .B exportfs
+-can also be used to modify the export options of an already exported
+-directory.
+-.P
+-Modifications of the kernel export table used by
+-.B nfsd(8)
+-take place immediately after parsing the command line and updating the
+-.B etab
+-file.
+-.P
+-The default export options are
+-.BR sync,ro,root_squash,wdelay .
+-.\" -------------------- Unexporting Directories ------------------
++can be used to modify the export options of an already exported directory.
+ .SS Unexporting Directories
+ The third synopsis shows how to unexported a currently exported directory.
+ When using
+ .BR "exportfs -ua" ,
+ all entries listed in
+-.B etab
++.I /var/lib/nfs/etab
+ are removed from the kernel export tables, and the file is cleared. This
+ effectively shuts down all NFS activity.
+-.P
+-To remove an export to a host, specify a
++.PP
++To remove an export, specify a
+ .I host:/path
+ pair. This deletes the specified entry from
+-.B etab
++.I /var/lib/nfs/etab
+ and removes the corresponding kernel entry (if any).
+-To remove one or more exports to several hosts, use
+-.BR "exportfs -ua" .
+-.P
+-.\" -------------------- Dumping the Export Table -----------------
+-.SS Dumping the Export Table 
++.PP
++.SS Dumping the Export Table
+ Invoking
+ .B exportfs
+-without further options shows the current list of exported file systems.
+-When giving the
++without options shows the current list of exported file systems.
++Adding the
+ .B -v
+-option, the list of flags pertaining to each export are shown in addition.
+-.\" -------------------- EXAMPLES ---------------------------------
++option causes
++.B exportfs
++to display the export options for each export.
+ .SH EXAMPLES
+ The following adds all directories listed in
+-.B /etc/exports
++.I /etc/exports
+ to
+-.B /var/lib/nfs/etab
++.I /var/lib/nfs/etab
+ and pushes the resulting export entries into the kernel:
+-.P
++.PP
+ .nf
+ .B "# exportfs -a
+ .fi
+-.P
++.PP
+ To export the
+-.B /usr/tmp
+-directory to host 
++.I /usr/tmp
++directory to host
+ .BR django ,
+-allowing asynchronous writes, one would do this:
+-.P
++allowing insecure file locking requests from clients:
++.PP
+ .nf
+-.B "# exportfs -o async django:/usr/tmp
++.B "# exportfs -o insecure_locks django:/usr/tmp
+ .fi
+-.P
++.PP
+ To unexport the
+-.B /usr/tmp
++.I /usr/tmp
+ directory:
+-.P
++.PP
+ .nf
+ .B "# exportfs -u django:/usr/tmp
+ .fi
+-.P
+-To unexport all the directories listed in
+-.B /etc/exports:
+-.P
++.PP
++To unexport all exports listed in
++.IR /etc/exports :
++.PP
+ .nf
+ .B "# exportfs -au
+ .fi
+-.\" -------------------- DEPENDENCIES -----------------------------
+-.SH DEPENDENCIES
+-Exporting to IP networks, DNS and NIS domains does not enable clients
+-from these groups to access NFS immediately; rather, these sorts of
+-exports are hints to
+-.B mountd(8)
++.SH USAGE NOTES
++Exporting to IP networks or DNS and NIS domains does not enable clients
++from these groups to access NFS immediately.
++Rather, these sorts of exports are hints to
++.BR rpc.mountd (8)
+ to grant any mount requests from these clients.
+-This is usually not a big problem, because any existing mounts are preserved
+-in
+-.B rmtab
++This is usually not a problem, because any existing mounts are preserved in
++.I rmtab
+ across reboots.
+-.P
++.PP
+ When unexporting a network or domain entry, any current exports to members
+ of this group will be checked against the remaining valid exports and
+ if they themselves are no longer valid they will be removed.
+-.P
+-.\" -------------------- SEE ALSO --------------------------------
++.SH FILES
++.TP 2.5i
++.I /etc/exports
++input file listing exports, export options, and access control lists
++.TP 2.5i
++.I /var/lib/nfs/etab
++master table of exports
++.TP 2.5i
++.I /var/lib/nfs/rmtab
++table of clients accessing server's exports
+ .SH SEE ALSO
+-.BR exports(5) ", " mountd(8)
+-.\" -------------------- AUTHOR ----------------------------------
++.BR exports (5),
++.BR rpc.mountd (8),
++.BR netgroup (5)
+ .SH AUTHORS
+-Olaf Kirch, <okir@monad.swb.de>
++Olaf Kirch <okir@monad.swb.de>
+ .br
+-Neil Brown, <neilb@cse.unsw.edu.au>
+-
++Neil Brown <neilb@cse.unsw.edu.au>
+diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
+index ea28ca8..c726dd9 100644
+--- a/utils/exportfs/exports.man
++++ b/utils/exportfs/exports.man
+@@ -1,18 +1,22 @@
+-.TH EXPORTS 5 "4 March 2005" "Linux" "Linux File Formats Manual"
++.\"@(#)exports.5"
++.\"
++.TH exports 5 "31 December 2009"
+ .SH NAME
+-exports \- NFS file systems being exported (for Kernel based NFS)
+-.SH SYNOPSIS
+-.B /etc/exports
++exports \- NFS server export table
+ .SH DESCRIPTION
+ The file
+ .I /etc/exports
+-serves as the access control list for file systems which may be
+-exported to NFS clients.  It is used by
+-.IR exportfs (8)
++contains a table of local physical file systems on an NFS server
++that are accessible to NFS clients.
++The contents of the file are maintained by the server's system
++administrator.
++.PP
++Each file system in this table has a list of options and an
++access control list.
++The table is used by
++.BR exportfs (8)
+ to give information to
+-.IR mountd (8)
+-and to the kernel based NFS file server daemon
+-.IR nfsd (8).
++.BR mountd (8).
+ .PP
+ The file format is similar to the SunOS
+ .I exports
+@@ -34,7 +38,9 @@ double quotes. You can also specify spaces or other unusual character in
+ the export name using a backslash followed by the character code as three
+ octal digits.
+ .PP
+-To apply changes to this file, run exportfs \-ra or restart the NFS server.
++To apply changes to this file, run
++.BR exportfs \-ra
++or restart the NFS server.
+ .PP
+ .SS Machine Name Formats
+ NFS clients may be specified in a number of ways:
+@@ -61,9 +67,10 @@ simultaneously. This is done by specifying an IP address and netmask pair
+ as
+ .IR address/netmask
+ where the netmask can be specified in dotted-decimal format, or as a
+-contiguous mask length (for example, either `/255.255.252.0' or `/22' appended
+-to the network base address result in identical subnetworks with 10 bits of
+-host). Wildcard characters generally do not work on IP addresses, though they
++contiguous mask length.
++For example, either `/255.255.252.0' or `/22' appended
++to the network base IPv4 address results in identical subnetworks with 10 bits of
++host. Wildcard characters generally do not work on IP addresses, though they
+ may work by accident when reverse DNS lookups fail.
+ '''.TP
+ '''.B =public
+@@ -106,7 +113,7 @@ preceding sec= option.  The only options that are permitted to vary in
+ this way are ro, rw, no_root_squash, root_squash, and all_squash.
+ .PP
+ .SS General Options
+-.IR exportfs
++.BR exportfs
+ understands the following export options:
+ .TP
+ .IR secure "\*d
+@@ -144,7 +151,8 @@ default.  In all releases after 1.0.0,
+ is the default, and
+ .I async
+ must be explicitly requested if needed.
+-To help make system administrators aware of this change, 'exportfs'
++To help make system administrators aware of this change,
++.B exportfs
+ will issue a warning if neither
+ .I sync
+ nor
+@@ -188,7 +196,7 @@ The
+ option is currently only effective on
+ .I "single host
+ exports.  It does not work reliably with netgroup, subnet, or wildcard
+-exports. 
++exports.
+ This option can be very useful in some situations, but it should be
+ used with due care, and only after confirming that the client system
+@@ -246,7 +254,7 @@ If you genuinely require subtree checking, you should explicitly put
+ that option in the
+ .B exports
+ file.  If you put neither option,
+-.I exportfs
++.B exportfs
+ will warn you that the change is pending.
+ .TP
+@@ -272,7 +280,9 @@ or
+ .TP
+ .IR no_acl
+ On some specially patched kernels, and when exporting filesystems that
+-support ACLs, this option tells nfsd not to reveal ACLs to clients, so
++support ACLs, this option tells
++.B nfsd
++not to reveal ACLs to clients, so
+ they will see only a subset of actual permissions on the given file
+ system.  This option is safe for filesystems used by NFSv2 clients and
+ old NFSv3 clients that perform access decisions locally.  Current
+@@ -381,7 +391,7 @@ of the filesystem must be handled elsewhere.)
+ .SS User ID Mapping
+ .PP
+-.I nfsd
++.B nfsd
+ bases its access control to files on the server machine on the uid and
+ gid provided in each NFS RPC request. The normal behavior a user would
+ expect is that she can access her files on the server just as she would
+@@ -399,19 +409,19 @@ and can be turned off with
+ .IR no_root_squash .
+ .PP
+ By default,
+-'''.I nfsd
++'''.B nfsd
+ '''tries to obtain the anonymous uid and gid by looking up user
+ '''.I nobody
+ '''in the password file at startup time. If it isn't found, a uid and gid
+-.I exportfs
++.B exportfs
+ chooses a uid and gid
+ of 65534 for squashed access. These values can also be overridden by
+ the
+ .IR anonuid " and " anongid
+ options.
+ '''.PP
+-'''In addition to this, 
+-'''.I nfsd
++'''In addition to this,
++'''.B nfsd
+ '''lets you specify arbitrary uids and gids that should be mapped to user
+ '''nobody as well.
+ Finally, you can map all user requests to the
+@@ -424,7 +434,7 @@ Here's the complete list of mapping options:
+ Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does
+ not apply to any other uids or gids that might be equally sensitive, such as
+ user
+-.IR bin 
++.IR bin
+ or group
+ .IR staff .
+ .TP
+@@ -434,7 +444,7 @@ Turn off root squashing. This option is mainly useful for diskless clients.
+ .IR all_squash
+ Map all uids and gids to the anonymous user. Useful for NFS-exported
+ public FTP directories, news spool directories, etc. The opposite option
+-is 
++is
+ .IR no_all_squash ,
+ which is the default setting.
+ .TP
+@@ -468,7 +478,7 @@ and netgroups (this is the entry `@trusted'). The fourth line shows the
+ entry for the PC/NFS client discussed above. Line 5 exports the
+ public FTP directory to every host in the world, executing all requests
+ under the nobody account. The
+-.I insecure 
++.I insecure
+ option in this entry also allows clients with NFS implementations that
+ don't use a reserved port for NFS.
+ The sixth line exports a directory read-write to the machine 'server'
+@@ -478,15 +488,15 @@ all three mounts with the `sync' option enabled.
+ '''access to the private directory.
+ '''.SH CAVEATS
+ '''Unlike other NFS server implementations, this
+-'''.I nfsd
++'''.B nfsd
+ '''allows you to export both a directory and a subdirectory thereof to
+-'''the same host, for instance 
++'''the same host, for instance
+ '''.IR /usr " and " /usr/X11R6 .
+ '''In this case, the mount options of the most specific entry apply. For
+-'''instance, when a user on the client host accesses a file in 
++'''instance, when a user on the client host accesses a file in
+ '''.IR /usr/X11R6 ,
+-'''the mount options given in the 
+-'''.I /usr/X11R6 
++'''the mount options given in the
++'''.I /usr/X11R6
+ '''entry apply. This is also true when the latter is a wildcard or netgroup
+ '''entry.
+ .SH FILES
+@@ -499,7 +509,15 @@ all three mounts with the `sync' option enabled.
+ .BR showmount (8).
+ '''.SH DIAGNOSTICS
+ '''An error parsing the file is reported using syslogd(8) as level NOTICE from
+-'''a DAEMON whenever nfsd(8) or mountd(8) is started up.  Any unknown
++'''a DAEMON whenever
++'''.BR nfsd (8)
++'''or
++'''.BR mountd (8)
++'''is started up.  Any unknown
+ '''host is reported at that time, but often not all hosts are not yet known
+-'''to named(8) at boot time, thus as hosts are found they are reported
+-'''with the same syslogd(8) parameters.
++'''to
++'''.BR named (8)
++'''at boot time, thus as hosts are found they are reported
++'''with the same
++'''.BR syslogd (8)
++'''parameters.
+diff --git a/utils/gssd/context.h b/utils/gssd/context.h
+index be47f9c..c9cb0bd 100644
+--- a/utils/gssd/context.h
++++ b/utils/gssd/context.h
+@@ -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 --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c
+index 4a682ae..b8d4734 100644
+--- a/utils/gssd/context_lucid.c
++++ b/utils/gssd/context_lucid.c
+@@ -42,6 +42,7 @@
+ #include <stdio.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <errno.h>
+ #include <gssapi/gssapi_krb5.h>
+@@ -76,7 +77,7 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+       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_lucid_context_v1_t *lctx,
+        * 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_buffer_desc *buf, int32_t *endtime)
+       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_buffer_desc *buf, int32_t *endtime)
+               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_buffer_desc *buf, int32_t *endtime)
+       }
+       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 --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c
+index 709a903..e6db9cb 100644
+--- a/utils/gssd/context_mit.c
++++ b/utils/gssd/context_mit.c
+@@ -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 <stdio.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <errno.h>
+ #include <gssapi/gssapi.h>
+ #include <rpc/rpc.h>
+ #include <rpc/auth_gss.h>
+@@ -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_buffer_desc *buf, int32_t *endtime)
+ {
+       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 --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
+index bd37a5f..ccadb07 100644
+--- a/utils/gssd/gssd.c
++++ b/utils/gssd/gssd.c
+@@ -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 --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c
+index f1a68d3..b06c223 100644
+--- a/utils/gssd/gssd_main_loop.c
++++ b/utils/gssd/gssd_main_loop.c
+@@ -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 --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
+index be4fb11..c301d46 100644
+--- a/utils/gssd/gssd_proc.c
++++ b/utils/gssd/gssd_proc.c
+@@ -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_info *clp,
+                * 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_info *clp,
+       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 *clp)
+ {
+       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 --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
+index 1295f57..f071600 100644
+--- a/utils/gssd/krb5_util.c
++++ b/utils/gssd/krb5_util.c
+@@ -224,6 +224,13 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
+                               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, char *dirname, struct dirent **d)
+       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 context, krb5_principal p,
+       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 context, krb5_keytab kt,
+       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 context, krb5_keytab kt,
+               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_realm)
+       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 --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
+index 4602cc3..b42b91e 100644
+--- a/utils/gssd/krb5_util.h
++++ b/utils/gssd/krb5_util.h
+@@ -36,7 +36,7 @@ char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
+ 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 --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
+index 729b6a6..e7375a4 100644
+--- a/utils/gssd/svcgssd.c
++++ b/utils/gssd/svcgssd.c
+@@ -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 --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
+index f1bfbef..3894078 100644
+--- a/utils/gssd/svcgssd_proc.c
++++ b/utils/gssd/svcgssd_proc.c
+@@ -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 --git a/utils/idmapd/atomicio.c b/utils/idmapd/atomicio.c
+index 05e7147..1fb1ff9 100644
+--- a/utils/idmapd/atomicio.c
++++ b/utils/idmapd/atomicio.c
+@@ -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 --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
+index 573abaa..9ecab66 100644
+--- a/utils/idmapd/idmapd.c
++++ b/utils/idmapd/idmapd.c
+@@ -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 = "Server", 
++      .ic_id = "", 
++      .ic_path = IC_IDNAME_CHAN, 
++      .ic_fd = -1, 
++      .ic_dirfd = -1, 
++      .ic_scanned = 0
++},
++{
++      .ic_which = IC_NAMEID, 
++      .ic_clid = "Server", 
++      .ic_id = "", 
++      .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 *data)
+ }
+ 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 idmap_msg *im)
+ }
+ 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 --git a/utils/mount/configfile.c b/utils/mount/configfile.c
+index 5cff009..6f2ee75 100644
+--- a/utils/mount/configfile.c
++++ b/utils/mount/configfile.c
+@@ -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 --git a/utils/mount/network.c b/utils/mount/network.c
+index 8dc183a..d6b5205 100644
+--- a/utils/mount/network.c
++++ b/utils/mount/network.c
+@@ -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)
+@@ -857,7 +858,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
+               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 +965,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
+       }
+       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 +1213,8 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program)
+                       return 1;
+               }
+       case PO_BAD_VALUE:
++              nfs_error(_("%s: invalid value for 'nfsprog=' option"),
++                              progname);
+               return 0;
+       }
+@@ -1242,9 +1254,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version)
+                       }
+                       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 +1271,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version)
+                       }
+                       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;
+               }
+       }
+@@ -1294,6 +1312,8 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
+               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 +1347,8 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port)
+                       return 1;
+               }
+       case PO_BAD_VALUE:
++              nfs_error(_("%s: invalid value for 'port=' option"),
++                              progname);
+               return 0;
+       }
+@@ -1342,7 +1364,7 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port)
+ 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;
+ }
+@@ -1380,8 +1402,13 @@ int nfs_nfs_proto_family(struct mount_options *options,
+       case 2: /* 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 +1441,8 @@ nfs_mount_program(struct mount_options *options, unsigned long *program)
+                       return 1;
+               }
+       case PO_BAD_VALUE:
++              nfs_error(_("%s: invalid value for 'mountprog=' option"),
++                              progname);
+               return 0;
+       }
+@@ -1443,6 +1472,8 @@ nfs_mount_version(struct mount_options *options, unsigned long *version)
+                       return 1;
+               }
+       case PO_BAD_VALUE:
++              nfs_error(_("%s: invalid value for 'mountvers=' option"),
++                              progname);
+               return 0;
+       }
+@@ -1469,6 +1500,8 @@ nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
+       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 +1534,8 @@ nfs_mount_port(struct mount_options *options, unsigned long *port)
+                       return 1;
+               }
+       case PO_BAD_VALUE:
++              nfs_error(_("%s: invalid value for 'mountport=' option"),
++                              progname);
+               return 0;
+       }
+@@ -1526,8 +1561,12 @@ int nfs_mount_proto_family(struct mount_options *options,
+       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 --git a/utils/mount/nfs.man b/utils/mount/nfs.man
+index c64de5f..3806635 100644
+--- a/utils/mount/nfs.man
++++ b/utils/mount/nfs.man
+@@ -623,14 +623,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
+diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
+index 4a2fab7..028e7cd 100644
+--- a/utils/mount/nfs4mount.c
++++ b/utils/mount/nfs4mount.c
+@@ -146,7 +146,7 @@ static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
+                               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 --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
+index 6b3356c..5b934b5 100644
+--- a/utils/mount/nfsmount.c
++++ b/utils/mount/nfsmount.c
+@@ -510,8 +510,12 @@ nfsmount(const char *spec, const char *node, int flags,
+       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 --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
+index 9d798a2..1514340 100644
+--- a/utils/mount/nfsumount.c
++++ b/utils/mount/nfsumount.c
+@@ -179,10 +179,8 @@ static int nfs_umount_do_umnt(struct mount_options *options,
+       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 --git a/utils/mount/stropts.c b/utils/mount/stropts.c
+index 9b8c38f..0241400 100644
+--- a/utils/mount/stropts.c
++++ b/utils/mount/stropts.c
+@@ -538,7 +538,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
+       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 +589,21 @@ static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
+               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;
+       }
+@@ -799,6 +805,7 @@ static int nfs_is_permanent_error(int error)
+       case ESTALE:
+       case ETIMEDOUT:
+       case ECONNREFUSED:
++      case EHOSTUNREACH:
+               return 0;       /* temporary */
+       default:
+               return 1;       /* permanent */
+diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
+index 13eba70..ccc849a 100644
+--- a/utils/mountd/auth.c
++++ b/utils/mountd/auth.c
+@@ -15,6 +15,8 @@
+ #include <arpa/inet.h>
+ #include <errno.h>
+ #include <unistd.h>
++
++#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 sockaddr_in *caller, struct hostent *hp,
+ /* 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, struct sockaddr_in *caller,
+       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, struct sockaddr_in *caller,
+                               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, struct sockaddr_in *caller,
+ }
+ 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, struct sockaddr_in *caller,
+               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, struct sockaddr_in *caller,
+ }
+ 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 sockaddr_in *caller, char *path)
+       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 sockaddr_in *caller, char *path)
+       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 --git a/utils/mountd/cache.c b/utils/mountd/cache.c
+index d63e10a..f70f4d6 100644
+--- a/utils/mountd/cache.c
++++ b/utils/mountd/cache.c
+@@ -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; i<ngroups; i++)
+-                      qword_printint(f, groups[i]);
++                      qword_printuint(f, groups[i]);
+       } else
+-              qword_printint(f, 0);
++              qword_printuint(f, 0);
+       qword_eol(f);
+       if (groups != glist)
+@@ -170,13 +177,16 @@ void auth_unix_gid(FILE *f)
+ #if USE_BLKID
+ static const char *get_uuid_blkdev(char *path)
+ {
++      /* We set *safe if we know that we need the
++       * fsid from statfs too.
++       */
+       static blkid_cache cache = NULL;
+       struct stat stb;
+       char *devname;
+       blkid_tag_iterate iter;
+       blkid_dev dev;
+       const char *type;
+-      const char *val = NULL;
++      const char *val, *uuid = NULL;
+       if (cache == NULL)
+               blkid_get_cache(&cache, NULL);
+@@ -193,42 +203,29 @@ static const char *get_uuid_blkdev(char *path)
+       iter = blkid_tag_iterate_begin(dev);
+       if (!iter)
+               return NULL;
+-      while (blkid_tag_next(iter, &type, &val) == 0)
++      while (blkid_tag_next(iter, &type, &val) == 0) {
+               if (strcmp(type, "UUID") == 0)
++                      uuid = val;
++              if (strcmp(type, "TYPE") == 0 &&
++                  strcmp(val, "btrfs") == 0) {
++                      uuid = NULL;
+                       break;
++              }
++      }
+       blkid_tag_iterate_end(iter);
+-      return val;
++      return uuid;
+ }
+ #else
+ #define get_uuid_blkdev(path) (NULL)
+ #endif
+-int get_uuid(char *path, char *uuid, int uuidlen, char *u)
++static int get_uuid(const char *val, int uuidlen, char *u)
+ {
+       /* extract hex digits from uuidstr and compose a uuid
+        * of the given length (max 16), xoring bytes to make
+-       * a smaller uuid.  Then compare with uuid
++       * a smaller uuid.
+        */
+       int i = 0;
+-      const char *val = NULL;
+-      char fsid_val[17];
+-
+-      if (path) {
+-              val = get_uuid_blkdev(path);
+-              if (!val) {
+-                      struct statfs64 st;
+-
+-                      if (statfs64(path, &st))
+-                              return 0;
+-                      if (!st.f_fsid.__val[0] && !st.f_fsid.__val[1])
+-                              return 0;
+-                      snprintf(fsid_val, 17, "%08x%08x",
+-                               st.f_fsid.__val[0], st.f_fsid.__val[1]);
+-                      val = fsid_val;
+-              }
+-      } else {
+-              val = uuid;
+-      }
+       
+       memset(u, 0, uuidlen);
+       for ( ; *val ; val++) {
+@@ -252,6 +249,60 @@ int get_uuid(char *path, char *uuid, int uuidlen, char *u)
+       return 1;
+ }
++static int uuid_by_path(char *path, int type, int uuidlen, char *uuid)
++{
++      /* get a uuid for the filesystem found at 'path'.
++       * There are several possible ways of generating the
++       * uuids (types).
++       * Type 0 is used for new filehandles, while other types
++       * may be used to interpret old filehandle - to ensure smooth
++       * forward migration.
++       * We return 1 if a uuid was found (and it might be worth 
++       * trying the next type) or 0 if no more uuid types can be
++       * extracted.
++       */
++
++      /* Possible sources of uuid are
++       * - blkid uuid
++       * - statfs64 uuid
++       *
++       * On some filesystems (e.g. vfat) the statfs64 uuid is simply an
++       * encoding of the device that the filesystem is mounted from, so
++       * it we be very bad to use that (as device numbers change).  blkid
++       * must be preferred.
++       * On other filesystems (e.g. btrfs) the statfs64 uuid contains
++       * important info that the blkid uuid cannot contain:  This happens
++       * when multiple subvolumes are exported (they have the same
++       * blkid uuid but different statfs64 uuids).
++       * We rely on get_uuid_blkdev *knowing* which is which and not returning
++       * a uuid for filesystems where the statfs64 uuid is better.
++       *
++       */
++      struct statfs64 st;
++      char fsid_val[17];
++      const char *blkid_val;
++      const char *val;
++
++      blkid_val = get_uuid_blkdev(path);
++
++      if (statfs64(path, &st) == 0 &&
++          (st.f_fsid.__val[0] || st.f_fsid.__val[1]))
++              snprintf(fsid_val, 17, "%08x%08x",
++                       st.f_fsid.__val[0], st.f_fsid.__val[1]);
++      else
++              fsid_val[0] = 0;
++
++      if (blkid_val && (type--) == 0)
++              val = blkid_val;
++      else if (fsid_val[0] && (type--) == 0)
++              val = fsid_val;
++      else
++              return 0;
++
++      get_uuid(val, uuidlen, uuid);
++      return 1;
++}
++
+ /* Iterate through /etc/mtab, finding mountpoints
+  * at or below a given path
+  */
+@@ -277,7 +328,7 @@ static char *next_mnt(void **v, char *p)
+       return me->mnt_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 *domain, char *path, struct exportent *ex
+               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 *domain, char *path, struct exportent *ex
+       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, char *path)
+       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 *dom, char *path, struct hostent *he)
+       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 *dom, char *path, struct hostent *he)
+       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, struct exportent *exp, char *path)
+                * 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, struct exportent *exp, char *path)
+       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 *path)
+       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 *path)
+       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, int len, char *p)
+       fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
+       return &fh;
+ }
+-
+diff --git a/utils/mountd/fsloc.c b/utils/mountd/fsloc.c
+index 5b094b0..e2add2d 100644
+--- a/utils/mountd/fsloc.c
++++ b/utils/mountd/fsloc.c
+@@ -146,7 +146,7 @@ static struct servers *method_list(char *data)
+ }
+ /* 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 --git a/utils/mountd/fsloc.h b/utils/mountd/fsloc.h
+index 8296d1c..1605df4 100644
+--- a/utils/mountd/fsloc.h
++++ b/utils/mountd/fsloc.h
+@@ -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 --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
+index a0a1f2d..d309950 100644
+--- a/utils/mountd/mountd.c
++++ b/utils/mountd/mountd.c
+@@ -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);
+@@ -75,17 +71,40 @@ static struct option longopts[] =
+       { NULL, 0, 0, 0 }
+ };
+-static int nfs_version = -1;
++#define NFSVERSBIT(vers)      (0x1 << (vers - 1))
++#define NFSVERSBIT_ALL                (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4))
++
++static int nfs_version = NFSVERSBIT_ALL;
++
++static int version2(void)
++{
++      return nfs_version & NFSVERSBIT(2);
++}
++
++static int version3(void)
++{
++      return nfs_version & NFSVERSBIT(3);
++}
++
++static int version23(void)
++{
++      return nfs_version & (NFSVERSBIT(2) | NFSVERSBIT(3));
++}
++
++static int version_any(void)
++{
++      return nfs_version & NFSVERSBIT_ALL;
++}
+ static void
+ unregister_services (void)
+ {
+-      if (nfs_version & 0x1)
+-              pmap_unset (MOUNTPROG, MOUNTVERS);
+-      if (nfs_version & (0x1 << 1))
+-              pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
+-      if (nfs_version & (0x1 << 2))
+-              pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
++      if (version2()) {
++              nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
++              nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
++      }
++      if (version3())
++              nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
+ }
+ static void
+@@ -192,17 +211,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 +240,27 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
+ }
+ 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 +270,57 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
+               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 +340,12 @@ mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+ 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 +361,14 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
+               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 +428,15 @@ static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp)
+ 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 +451,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,29 +472,29 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+       }
+       /* Now authenticate the intruder... */
+-      exp = auth_authenticate("mount", sin, p);
+-      if (!exp) {
+-              *error = NFSERR_ACCES;
++      exp = auth_authenticate("mount", sap, p);
++      if (exp == NULL) {
++              *error = MNT3ERR_ACCES;
+               return NULL;
+       }
+       if (stat(p, &stb) < 0) {
+               xlog(L_WARNING, "can't stat exported dir %s: %s",
+                               p, strerror(errno));
+               if (errno == ENOENT)
+-                      *error = NFSERR_NOENT;
++                      *error = MNT3ERR_NOENT;
+               else
+-                      *error = NFSERR_ACCES;
++                      *error = MNT3ERR_ACCES;
+               return NULL;
+       }
+       if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+               xlog(L_WARNING, "%s is not a directory or regular file", p);
+-              *error = NFSERR_NOTDIR;
++              *error = MNT3ERR_NOTDIR;
+               return NULL;
+       }
+       if (stat(exp->m_export.e_path, &estb) < 0) {
+               xlog(L_WARNING, "can't stat export point %s: %s",
+                    p, strerror(errno));
+-              *error = NFSERR_NOENT;
++              *error = MNT3ERR_NOENT;
+               return NULL;
+       }
+       if (estb.st_dev != stb.st_dev
+@@ -443,7 +502,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+                          || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT))) {
+               xlog(L_WARNING, "request to export directory %s below nearest filesystem %s",
+                    p, exp->m_export.e_path);
+-              *error = NFSERR_ACCES;
++              *error = MNT3ERR_ACCES;
+               return NULL;
+       }
+       if (exp->m_export.e_mountpoint &&
+@@ -452,7 +511,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+                                 exp->m_export.e_path)) {
+               xlog(L_WARNING, "request to export an unmounted filesystem: %s",
+                    p);
+-              *error = NFSERR_NOENT;
++              *error = MNT3ERR_NOENT;
+               return NULL;
+       }
+@@ -463,12 +522,12 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+                */
+               if (cache_export(exp, p)) {
+-                      *error = NFSERR_ACCES;
++                      *error = MNT3ERR_ACCES;
+                       return NULL;
+               }
+               fh = cache_get_filehandle(exp, v3?64:32, p);
+               if (fh == NULL) {
+-                      *error = NFSERR_ACCES;
++                      *error = MNT3ERR_ACCES;
+                       return NULL;
+               }
+       } else {
+@@ -482,13 +541,13 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+                       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) {
+@@ -498,12 +557,12 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+               if (fh == NULL) {
+                       xlog(L_WARNING, "getfh failed: %s", strerror(errno));
+-                      *error = NFSERR_ACCES;
++                      *error = MNT3ERR_ACCES;
+                       return NULL;
+               }
+       }
+-      *error = NFS_OK;
+-      mountlist_add(inet_ntoa(sin->sin_addr), p);
++      *error = MNT_OK;
++      mountlist_add(host_ntop(sap, buf, sizeof(buf)), p);
+       if (expret)
+               *expret = exp;
+       return fh;
+@@ -536,22 +595,21 @@ static void free_exportlist(exports *elist)
+ 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);
+       }
+@@ -634,13 +692,22 @@ main(int argc, char **argv)
+ {
+       char    *export_file = _PATH_EXPORTS;
+       char    *state_dir = NFS_STATEDIR;
++      char    *progname;
++      unsigned int listeners = 0;
+       int     foreground = 0;
+       int     port = 0;
+       int     descriptors = 0;
+       int     c;
++      int     vers;
+       struct sigaction sa;
+       struct rlimit rlim;
++      /* Set the basename */
++      if ((progname = strrchr(argv[0], '/')) != NULL)
++              progname++;
++      else
++              progname = argv[0];
++
+       /* Parse the command line options and arguments. */
+       opterr = 0;
+       while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF)
+@@ -652,8 +719,8 @@ main(int argc, char **argv)
+                       descriptors = atoi(optarg);
+                       if (descriptors <= 0) {
+                               fprintf(stderr, "%s: bad descriptors: %s\n",
+-                                      argv [0], optarg);
+-                              usage(argv [0], 1);
++                                      progname, optarg);
++                              usage(progname, 1);
+                       }
+                       break;
+               case 'F':
+@@ -669,19 +736,25 @@ main(int argc, char **argv)
+                       ha_callout_prog = optarg;
+                       break;
+               case 'h':
+-                      usage(argv [0], 0);
++                      usage(progname, 0);
+                       break;
+               case 'P':       /* XXX for nfs-server compatibility */
+               case 'p':
+                       port = atoi(optarg);
+                       if (port <= 0 || port > 65535) {
+                               fprintf(stderr, "%s: bad port number: %s\n",
+-                                      argv [0], optarg);
+-                              usage(argv [0], 1);
++                                      progname, optarg);
++                              usage(progname, 1);
+                       }
+                       break;
+               case 'N':
+-                      nfs_version &= ~(1 << (atoi (optarg) - 1));
++                      vers = atoi(optarg);
++                      if (vers < 2 || vers > 4) {
++                              fprintf(stderr, "%s: bad version number: %s\n",
++                                      argv[0], optarg);
++                              usage(argv[0], 1);
++                      }
++                      nfs_version &= ~NFSVERSBIT(vers);
+                       break;
+               case 'n':
+                       _rpcfdtype = SOCK_DGRAM;
+@@ -692,7 +765,7 @@ main(int argc, char **argv)
+               case 's':
+                       if ((state_dir = xstrdup(optarg)) == NULL) {
+                               fprintf(stderr, "%s: xstrdup(%s) failed!\n",
+-                                      argv[0], optarg);
++                                      progname, optarg);
+                               exit(1);
+                       }
+                       break;
+@@ -700,31 +773,37 @@ main(int argc, char **argv)
+                       num_threads = atoi (optarg);
+                       break;
+               case 'V':
+-                      nfs_version |= 1 << (atoi (optarg) - 1);
++                      vers = atoi(optarg);
++                      if (vers < 2 || vers > 4) {
++                              fprintf(stderr, "%s: bad version number: %s\n",
++                                      argv[0], optarg);
++                              usage(argv[0], 1);
++                      }
++                      nfs_version |= NFSVERSBIT(vers);
+                       break;
+               case 'v':
+-                      printf("kmountd %s\n", VERSION);
++                      printf("%s version " VERSION "\n", progname);
+                       exit(0);
+               case 0:
+                       break;
+               case '?':
+               default:
+-                      usage(argv [0], 1);
++                      usage(progname, 1);
+               }
+       /* No more arguments allowed. */
+-      if (optind != argc || !(nfs_version & 0x7))
+-              usage(argv [0], 1);
++      if (optind != argc || !version_any())
++              usage(progname, 1);
+       if (chdir(state_dir)) {
+               fprintf(stderr, "%s: chdir(%s) failed: %s\n",
+-                      argv [0], state_dir, strerror(errno));
++                      progname, state_dir, strerror(errno));
+               exit(1);
+       }
+       if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
+               fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
+-                              argv [0], strerror(errno));
++                              progname, strerror(errno));
+       else {
+               /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
+               if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) ||
+@@ -734,14 +813,14 @@ main(int argc, char **argv)
+                       rlim.rlim_cur = descriptors;
+                       if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
+                               fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
+-                                      argv [0], strerror(errno));
++                                      progname, strerror(errno));
+                               exit(1);
+                       }
+               }
+       }
+       /* Initialize logging. */
+       if (!foreground) xlog_stderr(0);
+-      xlog_open("mountd");
++      xlog_open(progname);
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = 0;
+@@ -761,15 +840,17 @@ main(int argc, char **argv)
+       if (new_cache)
+               cache_open();
+-      if (nfs_version & 0x1)
+-              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);
++      if (version2()) {
++              listeners += nfs_svc_create("mountd", MOUNTPROG,
++                                      MOUNTVERS, mount_dispatch, port);
++              listeners += nfs_svc_create("mountd", MOUNTPROG,
++                                      MOUNTVERS_POSIX, mount_dispatch, port);
++      }
++      if (version3())
++              listeners += nfs_svc_create("mountd", MOUNTPROG,
++                                      MOUNTVERS_NFSV3, mount_dispatch, port);
++      if (version23() && listeners == 0)
++              xlog(L_FATAL, "mountd: could not create listeners\n");
+       sa.sa_handler = killer;
+       sigaction(SIGINT, &sa, NULL);
+@@ -810,9 +891,11 @@ main(int argc, char **argv)
+       if (num_threads > 1)
+               fork_workers();
++      xlog(L_NOTICE, "Version " VERSION " starting");
+       my_svc_run();
+-      xlog(L_ERROR, "Ack! Gack! svc_run returned!\n");
++      xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n");
++      unregister_services();
+       exit(1);
+ }
+diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
+index 31bacb5..4c184d2 100644
+--- a/utils/mountd/mountd.h
++++ b/utils/mountd/mountd.h
+@@ -41,14 +41,19 @@ bool_t             mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *);
+ 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 --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
+index bfa06e0..4bb96e8 100644
+--- a/utils/mountd/mountd.man
++++ b/utils/mountd/mountd.man
+@@ -1,9 +1,9 @@
+-.\"
+-.\" mountd(8)
++.\"@(#)rpc.mountd.8"
+ .\"
+ .\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+ .\" Modified by Paul Clements, 2004.
+-.TH rpc.mountd 8 "31 Aug 2004"
++.\"
++.TH rpc.mountd 8 "31 Dec 2009"
+ .SH NAME
+ rpc.mountd \- NFS mount daemon
+ .SH SYNOPSIS
+@@ -11,48 +11,73 @@ rpc.mountd \- NFS mount daemon
+ .SH DESCRIPTION
+ The
+ .B rpc.mountd
+-program implements the NFS mount protocol. When receiving a MOUNT
+-request from an NFS client, it checks the request against the list of
+-currently exported file systems. If the client is permitted to mount
+-the file system,
+-.B rpc.mountd
+-obtains a file handle for requested directory and returns it to
+-the client.
+-.SS Exporting NFS File Systems
+-Making file systems available to NFS clients is called
+-.IR exporting .
+-.P
+-Usually, a file system and the hosts it should be made available to
+-are listed in the
+-.B /etc/exports
+-file, and invoking
+-.B exportfs -a
+-whenever the system is booted. The
++daemon implements the server side of the NFS MOUNT protocol,
++an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813].
++.PP
++An NFS server maintains a table of local physical file systems
++that are accessible to NFS clients.
++Each file system in this table is referred to as an
++.IR "exported file system" ,
++or
++.IR export ,
++for short.
++.PP
++Each file system in the export table has an access control list.
++.B rpc.mountd
++uses these access control lists to determine
++whether an NFS client is permitted to access a given file system.
++For details on how to manage your NFS server's export table, see the
++.BR exports (5)
++and
+ .BR exportfs (8)
+-command makes export information available to both the kernel NFS
+-server module and the
+-.B rpc.mountd
+-daemon.
+-.P
+-Alternatively, you can export individual directories temporarily 
+-using
+-.BR exportfs 's
+-.IB host : /directory
+-syntax.
++man pages.
++.SS Mounting exported NFS File Systems
++The NFS MOUNT protocol has several procedures.
++The most important of these are
++MNT (mount an export) and
++UMNT (unmount an export).
++.PP
++A MNT request has two arguments: an explicit argument that
++contains the pathname of the root directory of the export to be mounted,
++and an implicit argument that is the sender's IP address.
++.PP
++When receiving a MNT request from an NFS client,
++.B rpc.mountd
++checks both the pathname and the sender's IP address against its export table.
++If the sender is permitted to access the requested export,
++.B rpc.mountd
++returns an NFS file handle for the export's root directory to the client.
++The client can then use the root file handle and NFS LOOKUP requests
++to navigate the directory structure of the export.
+ .SS The rmtab File
+-For every mount request received from an NFS client,
+-.B rpc.mountd
+-adds an entry to the
+-.B /var/lib/nfs/rmtab
+-file. When receiving an unmount request, that entry is removed.
+-.P
+-However, this file is mostly ornamental. One, the client can continue
+-to use the file handle even after calling
+-.B rpc.mountd 's
+-UMOUNT procedure. And two, if a client reboots without notifying
+-.B rpc.mountd ,
+-a stale entry will remain in
+-.BR rmtab .
++The
++.B rpc.mountd
++daemon registers every successful MNT request by adding an entry to the
++.I /var/lib/nfs/rmtab
++file.
++When receivng a UMNT request from an NFS client,
++.B rpc.mountd
++simply removes the matching entry from
++.IR /var/lib/nfs/rmtab ,
++as long as the access control list for that export allows that sender
++to access the export.
++.PP
++Clients can discover the list of file systems an NFS server is
++currently exporting, or the list of other clients that have mounted
++its exports, by using the
++.BR showmount (8)
++command.
++.BR showmount (8)
++uses other procedures in the NFS MOUNT protocol to report information
++about the server's exported file systems.
++.PP
++Note, however, that there is little to guarantee that the contents of
++.I /var/lib/nfs/rmtab
++are accurate.
++A client may continue accessing an export even after invoking UMNT.
++If the client reboots without sending a UMNT request, stale entries
++remain for that client in
++.IR /var/lib/nfs/rmtab .
+ .SH OPTIONS
+ .TP
+ .B \-d kind " or " \-\-debug kind
+@@ -94,22 +119,25 @@ Don't advertise TCP for mount.
+ Ignored (compatibility with unfsd??).
+ .TP
+ .B \-p " or " \-\-port num
+-Force
++Specifies the port number used for RPC listener sockets.
++If this option is not specified,
+ .B rpc.mountd
+-to bind to the specified port num, instead of using the random port
+-number assigned by the portmapper.
++chooses a random ephemeral port for each listener socket.
++.IP
++This option can be used to fix the port value of
++.BR rpc.mountd 's
++listeners when NFS MOUNT requests must traverse a firewall
++between clients and servers.
+ .TP
+ .B \-H " or " \-\-ha-callout prog
+-Specify a high availability callout program, which will receive callouts
+-for all client mount and unmount requests. This allows 
+-.B rpc.mountd
+-to be used in a High Availability NFS (HA-NFS) environment. This callout is not
+-needed (and should not be used) with 2.6 and later kernels (instead,
+-mount the nfsd filesystem on
+-.B /proc/fs/nfsd
+-).
+-The program will be called with 4 arguments.
+-The first will be
++Specify a high availability callout program.
++This program receives callouts for all MOUNT and UNMOUNT requests.
++This allows
++.B rpc.mountd
++to be used in a High Availability NFS (HA-NFS) environment.
++.IP
++The callout program is run with 4 arguments.
++The first is
+ .B mount
+ or
+ .B unmount
+@@ -118,19 +146,30 @@ The second will be the name of the client performing the mount.
+ The third will be the path that the client is mounting.
+ The last is the number of concurrent mounts that we believe the client
+ has of that path.
++.IP
++This callout is not needed with 2.6 and later kernels.
++Instead, mount the nfsd filesystem on
++.IR /proc/fs/nfsd .
+ .TP
+ .BI "\-s," "" " \-\-state\-directory\-path "  directory
+-specify a directory in which to place statd state information.
++Specify a directory in which to place statd state information.
+ If this option is not specified the default of
+-.BR /var/lib/nfs
++.I /var/lib/nfs
+ is used.
+ .TP
+ .BI "\-r," "" " \-\-reverse\-lookup"
+-mountd tracks IP addresses in the rmtab, and when a DUMP request is made (by
+-someone running showmount -a, for instance), it returns IP addresses instead
+-of hostnames by default. This option causes mountd to do a reverse
+-lookup on each IP address and return that hostname instead. Enabling this can
+-have a substantial negative effect on performance in some situations.
++.B rpc.mountd
++tracks IP addresses in the
++.I rmtab
++file.  When a DUMP request is made (by
++someone running
++.BR "showmount -a" ,
++for instance), it returns IP addresses instead
++of hostnames by default. This option causes
++.B rpc.mountd
++to perform a reverse lookup on each IP address and return that hostname instead.
++Enabling this can have a substantial negative effect on performance
++in some situations.
+ .TP
+ .BR "\-t N" " or " "\-\-num\-threads=N"
+ This option specifies the number of worker threads that rpc.mountd
+@@ -162,41 +201,70 @@ If you use the
+ flag, then the list of group ids received from the client will be
+ replaced by a list of group ids determined by an appropriate lookup on
+ the server. Note that the 'primary' group id is not affected so a
+-.I newgroup
++.B newgroup
+ command on the client will still be effective.  This function requires
+ a Linux Kernel with version at least 2.6.21.
+-
+ .SH TCP_WRAPPERS SUPPORT
+-This
++You can protect your
+ .B rpc.mountd
+-version is protected by the
++listeners using the
++.B tcp_wrapper
++library or
++.BR iptables (8).
++.PP
++Note that the
+ .B tcp_wrapper
+-library. You have to give the clients access to
+-.B rpc.mountd
+-if they should be allowed to use it. To allow connects from clients of
+-the .bar.com domain you could use the following line in /etc/hosts.allow:
+-
+-mountd: .bar.com
+-
+-You have to use the daemon name 
++library supports only IPv4 networking.
++.PP
++Add the hostnames of NFS peers that are allowed to access
++.B rpc.mountd
++to
++.IR /etc/hosts.allow .
++Use the daemon name
+ .B mountd
+-for the daemon name (even if the binary has a different name).
+-.B Note:
+-hostnames used in either access file will be ignored when
++even if the
++.B rpc.mountd
++binary has a different name.
++.PP
++Hostnames used in either access file will be ignored when
+ they can not be resolved into IP addresses.
+-
+-For further information please have a look at the
++For further information see the
+ .BR tcpd (8)
+ and
+ .BR hosts_access (5)
+-manual pages.
++man pages.
++.SS IPv6 and TI-RPC support
++TI-RPC is a pre-requisite for supporting NFS on IPv6.
++If TI-RPC support is built into
++.BR rpc.mountd ,
++it attempts to start listeners on network transports marked 'visible' in
++.IR /etc/netconfig .
++As long as at least one network transport listener starts successfully,
++.B rpc.mountd
++will operate.
++.SH FILES
++.TP 2.5i
++.I /etc/exports
++input file for
++.BR exportfs ,
++listing exports, export options, and access control lists
++.TP 2.5i
++.I /var/lib/nfs/rmtab
++table of clients accessing server's exports
+ .SH SEE ALSO
+-.BR rpc.nfsd (8),
+ .BR exportfs (8),
+ .BR exports (5),
+-.BR rpc.rquotad (8).
+-.SH FILES
+-.BR /etc/exports ,
+-.BR /var/lib/nfs/xtab .
++.BR showmount (8),
++.BR rpc.nfsd (8),
++.BR rpc.rquotad (8),
++.BR nfs (5),
++.BR tcpd (8),
++.BR hosts_access (5),
++.BR iptables (8),
++.BR netconfig (5)
++.sp
++RFC 1094 - "NFS: Network File System Protocol Specification"
++.br
++RFC 1813 - "NFS Version 3 Protocol Specification"
+ .SH AUTHOR
+ Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
+diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
+index 19b22ee..d339296 100644
+--- a/utils/mountd/rmtab.c
++++ b/utils/mountd/rmtab.c
+@@ -16,7 +16,7 @@
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+-#include "xmalloc.h"
++
+ #include "misc.h"
+ #include "exportfs.h"
+ #include "xio.h"
+@@ -131,22 +131,22 @@ mountlist_del(char *hname, const char *path)
+ }
+ 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 *sin)
+               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 *sin)
+ 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 --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
+index 1cda1e5..650c593 100644
+--- a/utils/nfsd/nfsd.c
++++ b/utils/nfsd/nfsd.c
+@@ -27,15 +27,6 @@
+ #include "nfssvc.h"
+ #include "xlog.h"
+-/*
+- * IPv6 support for nfsd was finished before some of the other daemons (mountd
+- * and statd in particular). That could be a problem in the future if someone
+- * were to boot a kernel that supports IPv6 serving with  an older nfs-utils. For
+- * now, hardcode the IPv6 switch into the off position until the other daemons
+- * are functional.
+- */
+-#undef IPV6_SUPPORTED
+-
+ static void   usage(const char *);
+ static struct option longopts[] =
+diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
+index 60232b8..25cd459 100644
+--- a/utils/nfsd/nfssvc.c
++++ b/utils/nfsd/nfssvc.c
+@@ -22,15 +22,6 @@
+ #include "nfslib.h"
+ #include "xlog.h"
+-/*
+- * IPv6 support for nfsd was finished before some of the other daemons (mountd
+- * and statd in particular). That could be a problem in the future if someone
+- * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For
+- * now, hardcode the IPv6 switch into the off position until the other daemons
+- * are functional.
+- */
+-#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"
+@@ -181,7 +172,7 @@ nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
+               }
+               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 +242,7 @@ nfssvc_setvers(unsigned int ctlbits, int minorvers4)
+       }
+       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 +268,7 @@ nfssvc_threads(unsigned short port, const int nrservs)
+               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 --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
+index 99d77c9..740a803 100644
+--- a/utils/nfsstat/nfsstat.c
++++ b/utils/nfsstat/nfsstat.c
+@@ -791,7 +791,7 @@ print_callstats(const char *hdr, const char **names,
+ {
+       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 +816,7 @@ print_callstats_list(const char *hdr, const char **names,
+                       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 +1118,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 --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c
+index f567093..394f528 100644
+--- a/utils/showmount/showmount.c
++++ b/utils/showmount/showmount.c
+@@ -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 --git a/utils/statd/hostname.c b/utils/statd/hostname.c
+index 7d704cc..38f2265 100644
+--- a/utils/statd/hostname.c
++++ b/utils/statd/hostname.c
+@@ -212,7 +212,9 @@ statd_canonical_name(const char *hostname)
+                                       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 --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
+index 3259a3e..437e37a 100644
+--- a/utils/statd/sm-notify.c
++++ b/utils/statd/sm-notify.c
+@@ -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 *host)
+               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, struct nsm_host *host, XDR *xdr)
+ }
+ /*
+- * 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 --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
+index 163713e..7a1cbfa 100644
+--- a/utils/statd/sm-notify.man
++++ b/utils/statd/sm-notify.man
+@@ -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 requests.
+ 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 --git a/utils/statd/statd.man b/utils/statd/statd.man
+index ffc5e95..ca00e24 100644
+--- a/utils/statd/statd.man
++++ b/utils/statd/statd.man
+@@ -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.
index bcffa4e0b424174de93617a99c2092bec7e1b371..595c358b4c154ea5abcf36b518e56d7bc876159c 100644 (file)
@@ -1,5 +1,15 @@
 --- utils/gssd/krb5_util.c     2008-10-17 14:20:09.000000000 +0000
 +++ utils/gssd/krb5_util.c     2008-11-22 13:52:42.000000000 +0000
+@@ -115,9 +115,7 @@
+ #include <errno.h>
+ #include <time.h>
+ #include <gssapi/gssapi.h>
+-#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+ #include <gssapi/gssapi_krb5.h>
+-#endif
+ #include <krb5.h>
+ #include <rpc/auth_gss.h>
 @@ -927,9 +927,37 @@ 
  {
        krb5_error_code ret;
  
 +#ifdef HAVE_HEIMDAL
 +      krb5_creds pattern;
-+      krb5_realm *client_realm;
++      krb5_const_realm client_realm;
 +
 +      krb5_cc_clear_mcred(&pattern);
 +
-+      client_realm = krb5_princ_realm (context, principal);
++      client_realm = krb5_principal_get_realm (context, principal);
 +
 +      ret = krb5_make_principal (context, &pattern.server,
-+                                 *client_realm, KRB5_TGS_NAME, *client_realm,
++                                 client_realm, KRB5_TGS_NAME, client_realm,
 +                                 NULL);
 +      if (ret)
 +        krb5_err (context, 1, ret, "krb5_make_principal");
        krb5_cc_set_flags(context, ccache,  KRB5_TC_OPENCLOSE);
        krb5_cc_close(context, ccache);
  err_cache:
-@@ -1262,7 +1294,11 @@
-       if (context != NULL) {
-               origmsg = krb5_get_error_message(context, code);
-               msg = strdup(origmsg);
-+#ifdef HAVE_HEIMDAL
-+              krb5_free_error_string(context, origmsg);
-+#else
-               krb5_free_error_message(context, origmsg);
-+#endif
-       }
- #endif
-       if (msg != NULL)
index 51afd83890858e4347c92e392273d4372c03bf74..68a2b0e5f58f1aa861185399afa4c9ad2b42fe56 100644 (file)
@@ -16,7 +16,7 @@ Index: nfs-utils-1.1.0/utils/gssd/context_lucid.c
 @@ -171,10 +173,10 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
        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,
 +      maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx,
                                                1, &return_ctx);
@@ -42,13 +42,20 @@ Index: nfs-utils-1.1.0/utils/gssd/krb5_util.c
 ===================================================================
 --- nfs-utils-1.1.0.orig/utils/gssd/krb5_util.c
 +++ nfs-utils-1.1.0/utils/gssd/krb5_util.c
-@@ -294,10 +294,10 @@ limit_krb5_enctypes(struct rpc_gss_sec *
-               return -1;
-       }
+@@ -1317,13 +1317,13 @@ limit_krb5_enctypes(struct rpc_gss_sec *
+        * 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);
++              maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh,
++                                      num_enctypes, enctypes);
+       else
+-              maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+-                                      &krb5oid, num_krb5_enctypes, krb5_enctypes);
++              maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh,
++                                      num_krb5_enctypes, krb5_enctypes);
  
--      maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
-+      maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh,
-                                            num_enctypes, &enctypes);
        if (maj_stat != GSS_S_COMPLETE) {
 -              pgsserr("gss_set_allowable_enctypes",
 +              pgsserr("gss_krb5_set_allowable_enctypes",
index a7b5b3d42d466d1178b1d9eccf8afc1fc3c1a534..f97cad14a3a78f6c4bbc6ea54d50580fcd002c2b 100644 (file)
@@ -13,7 +13,7 @@ Summary(ru.UTF-8):    Утилиты для NFS и демоны поддержки
 Summary(uk.UTF-8):     Утиліти для NFS та демони підтримки для NFS-сервера ядра
 Name:          nfs-utils
 Version:       1.2.2
-Release:       2
+Release:       2.3
 License:       GPL v2
 Group:         Networking/Daemons
 Source0:       http://www.kernel.org/pub/linux/utils/nfs/%{name}-%{version}.tar.bz2
@@ -38,6 +38,7 @@ Patch4:               %{name}-kerberos-ac.patch
 Patch5:                %{name}-no_libgssapi.patch
 Patch6:                %{name}-pkgconfig_ac.patch
 Patch7:                %{name}-heimdal_functions.patch
+Patch100:      %{name}-git.patch
 URL:           http://nfs.sourceforge.net/
 BuildRequires: autoconf >= 2.59
 BuildRequires: automake
@@ -168,6 +169,7 @@ Wspólne programy do obsługi NFS.
 
 %prep
 %setup -q -a1
+%patch100 -p1
 %patch0 -p1
 %patch1 -p1
 %patch2 -p1
@@ -384,11 +386,15 @@ fi
 %attr(4755,root,root) /sbin/umount.nfs
 %attr(4755,root,root) /sbin/mount.nfs4
 %attr(4755,root,root) /sbin/umount.nfs4
+%attr(755,root,root) %{_sbindir}/mountstats
+%attr(755,root,root) %{_sbindir}/nfsiostat
 %attr(755,root,root) %{_sbindir}/showmount
 %attr(755,root,root) %{_sbindir}/rpc.gssd
 %attr(754,root,root) /etc/rc.d/init.d/gssd
 %{_mandir}/man8/gssd.8*
 %{_mandir}/man8/mount.nfs.8*
+%{_mandir}/man8/mountstats.8*
+%{_mandir}/man8/nfsiostat.8*
 %{_mandir}/man8/rpc.gssd.8*
 %{_mandir}/man8/showmount.8*
 %{_mandir}/man8/umount.nfs.8*
This page took 0.964936 seconds and 4 git commands to generate.