+++ /dev/null
-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.