From: Jan Rękorajski Date: Mon, 30 Aug 2010 12:15:32 +0000 (+0000) Subject: - git patch with nfs-utils-1-2-3-rc4 changes X-Git-Tag: auto/th/nfs-utils-1_2_2-3~1 X-Git-Url: https://git.pld-linux.org/?a=commitdiff_plain;h=747453cc8d5bf4cd037379ec6a67328c5cd5ee62;p=packages%2Fnfs-utils.git - git patch with nfs-utils-1-2-3-rc4 changes - real ipv6 support in nfsd and mountd - support for more krb5 enctypes (AES, ARCFOUR, etc., req kernel 2.6.35+), at last no more DES! Changed files: nfs-utils-git.patch -> 1.1 nfs-utils-heimdal_functions.patch -> 1.4 nfs-utils-no_libgssapi.patch -> 1.3 nfs-utils.spec -> 1.187 --- diff --git a/nfs-utils-git.patch b/nfs-utils-git.patch new file mode 100644 index 0000000..c1e0288 --- /dev/null +++ b/nfs-utils-git.patch @@ -0,0 +1,7262 @@ +diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4 +index eabe507..68a624c 100644 +--- a/aclocal/libcap.m4 ++++ b/aclocal/libcap.m4 +@@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ + dnl look for prctl + AC_CHECK_FUNC([prctl], , ) + +- dnl look for the library; do not add to LIBS if found +- AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) +- AC_SUBST(LIBCAP) ++ AC_ARG_ENABLE([caps], ++ [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) ++ ++ LIBCAP= ++ ++ if test "x$enable_caps" != "xno" ; then ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) + +- AC_CHECK_HEADERS([sys/capability.h], , +- [AC_MSG_ERROR([libcap headers not found.])]) ++ AC_CHECK_HEADERS([sys/capability.h], , ++ [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) ++ fi ++ ++ AC_SUBST(LIBCAP) + + ])dnl +diff --git a/autogen.sh b/autogen.sh +old mode 100644 +new mode 100755 +diff --git a/configure.ac b/configure.ac +index b7520d8..7c9e61a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -81,7 +81,7 @@ AC_ARG_ENABLE(nfsv41, + if test "$enable_nfsv41" = yes; then + AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + else +- enable_nfsv4= ++ enable_nfsv41= + fi + AC_SUBST(enable_nfsv41) + AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) +@@ -400,7 +400,7 @@ case $host in + ARCHFLAGS="" ;; + esac + +-my_am_cflags="-Wall -Wstrict-prototypes $ARCHFLAGS -pipe" ++my_am_cflags="-Wall -Wextra -Wstrict-prototypes $ARCHFLAGS -pipe" + + AC_SUBST([AM_CFLAGS], ["$my_am_cflags"]) + +@@ -425,6 +425,8 @@ AC_CONFIG_FILES([ + tools/nlmtest/Makefile + tools/rpcdebug/Makefile + tools/rpcgen/Makefile ++ tools/mountstats/Makefile ++ tools/nfs-iostat/Makefile + utils/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile +diff --git a/support/export/client.c b/support/export/client.c +index 6236561..c74961e 100644 +--- a/support/export/client.c ++++ b/support/export/client.c +@@ -17,7 +17,9 @@ + #include + #include + #include +-#include "xmalloc.h" ++#include ++ ++#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 . + */ + + #ifdef HAVE_CONFIG_H + #include + #endif + +-/* +-#define TEST +-*/ +- + #include +-#include +-#include +-#include + #include +-#include +-#ifdef TEST +-#define xmalloc malloc +-#else +-#include "xmalloc.h" +-#include "misc.h" +-#endif ++#include ++#include ++#include + +-#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 ++ ++#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 + #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 + #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 ++#ifdef HAVE_SYS_CAPABILITY_H + #include ++#endif + #include + #include + +@@ -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 [" "] " " [ " "]" ++.SH DESCRIPTION ++The ++.B mountstats ++command displays NFS client statisitics on each given ++.I ++.SH OPTIONS ++.TP ++.B " \-\-nfs ++display only the NFS statistics ++.TP ++.B " \-\-rpc ++display only the RPC statistics ++.TP ++.B " \-\-version ++display the version of this command ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR nfsiostat (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff --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 [[" "] [" "]] [" "][" "] ++.SH DESCRIPTION ++The ++.B nfsiostat ++command displays NFS client per-mount statisitics. ++.TP ++ ++specifies the amount of time in seconds between each report. ++The first report contains statistics for the time since each file ++system was mounted. Each subsequent report contains statistics collected ++during the interval since the previous report. ++.TP ++ ++If the ++.I ++parameter is ++specified, the value of ++.I ++determines the number of reports generated at ++. ++seconds apart. if the interval parameter is ++specified without the ++.I ++parameter, the command generates reports continuously. ++.TP ++ ++Define below ++.TP ++ ++If one or more ++.I ++names are specified, statistics for only these mount points will ++be displayed. Otherwise, all NFS mount points on the client are listed. ++.SH OPTIONS ++.TP ++.B \-a " or " \-\-attr ++displays statistics related to the attribute cache ++.TP ++.B \-d " or " \-\-dir ++displays statistics related to directory operations ++.TP ++.B \-h " or " \-\-help ++shows help message and exit ++.TP ++.B \-l LIST or " \-\-list=LIST ++only print stats for first LIST mount points ++.TP ++.B \-p " or " \-\-page ++displays statistics related to the page cache ++.TP ++.B \-s " or " \-\-sort ++Sort NFS mount points by ops/second ++.TP ++.B \-\-version ++show program's version number and exit ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR mountstats (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff --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 + #endif + ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include +-#include "xmalloc.h" ++ ++#include "sockaddr.h" + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "xmalloc.h" + #include "xlog.h" + + static void export_all(int verbose); +@@ -34,13 +38,15 @@ static void unexportfs(char *arg, int 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 + .\" Modifications 1999-2003 Neil Brown +-.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, ++Olaf Kirch + .br +-Neil Brown, +- ++Neil Brown +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 + #include + #include ++#include + + #include + +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -52,8 +53,7 @@ + /* XXX argggg, there's gotta be a better way than just duplicating this + * whole struct. Unfortunately, this is in a "private" header file, + * so this is our best choice at this point :-/ +- * +- * XXX Does this match the Heimdal definition? */ ++ */ + + typedef struct _krb5_gss_ctx_id_rec { + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ +@@ -156,50 +156,122 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_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 + #include + #include ++ ++#include "sockaddr.h" + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +@@ -110,13 +112,16 @@ auth_reload() + return counter; + } + +-static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error) ++static char * ++get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, ++ enum auth_error *error) + { ++ char buf[INET6_ADDRSTRLEN]; + char *n; + + if (use_ipaddr) +- return strdup(inet_ntoa(caller->sin_addr)); +- n = client_compose(hp); ++ return strdup(host_ntop(caller, buf, sizeof(buf))); ++ n = client_compose(ai); + *error = unknown_host; + if (!n) + return NULL; +@@ -128,8 +133,8 @@ static char *get_client_hostname(struct 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; imnt_dir; + } + +-void nfsd_fh(FILE *f) ++static void nfsd_fh(FILE *f) + { + /* request are: + * domain fsidtype fsid +@@ -294,8 +345,7 @@ void nfsd_fh(FILE *f) + unsigned int fsidnum=0; + char fsid[32]; + struct exportent *found = NULL; +- struct hostent *he = NULL; +- struct in_addr addr; ++ struct addrinfo *ai = NULL; + char *found_path = NULL; + nfs_export *exp; + int i; +@@ -398,6 +448,7 @@ void nfsd_fh(FILE *f) + struct stat stb; + char u[16]; + char *path; ++ int type; + + if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) { + static nfs_export *prev = NULL; +@@ -461,22 +512,29 @@ void nfsd_fh(FILE *f) + continue; + check_uuid: + if (exp->m_export.e_uuid) +- get_uuid(NULL, exp->m_export.e_uuid, ++ get_uuid(exp->m_export.e_uuid, + uuidlen, u); +- else if (get_uuid(path, NULL, uuidlen, u) == 0) +- continue; ++ else ++ for (type = 0; ++ uuid_by_path(path, type, uuidlen, u); ++ type++) ++ if (memcmp(u, fhuuid, uuidlen) == 0) ++ break; + + if (memcmp(u, fhuuid, uuidlen) != 0) + continue; + break; + } + if (use_ipaddr) { +- if (he == NULL) { +- if (!inet_aton(dom, &addr)) ++ if (ai == NULL) { ++ struct addrinfo *tmp; ++ tmp = host_pton(dom); ++ if (tmp == NULL) + goto out; +- he = client_resolve(addr); ++ ai = client_resolve(tmp->ai_addr); ++ freeaddrinfo(tmp); + } +- if (!client_check(exp->m_client, he)) ++ if (!client_check(exp->m_client, ai)) + continue; + } + /* It's a match !! */ +@@ -534,21 +592,20 @@ void nfsd_fh(FILE *f) + out: + if (found_path) + free(found_path); +- if (he) +- free(he); ++ freeaddrinfo(ai); + free(dom); + xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); + return; + } + +-static void write_fsloc(FILE *f, struct exportent *ep, char *path) ++static void write_fsloc(FILE *f, struct exportent *ep) + { + struct servers *servers; + + if (ep->e_fslocmethod == FSLOC_NONE) + return; + +- servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path); ++ servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata); + if (!servers) + return; + qword_print(f, "fsloc"); +@@ -596,17 +653,17 @@ static int dump_to_cache(FILE *f, char *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 + .\" 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 + #include + #include +-#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. diff --git a/nfs-utils-heimdal_functions.patch b/nfs-utils-heimdal_functions.patch index bcffa4e..595c358 100644 --- a/nfs-utils-heimdal_functions.patch +++ b/nfs-utils-heimdal_functions.patch @@ -1,5 +1,15 @@ --- utils/gssd/krb5_util.c 2008-10-17 14:20:09.000000000 +0000 +++ utils/gssd/krb5_util.c 2008-11-22 13:52:42.000000000 +0000 +@@ -115,9 +115,7 @@ + #include + #include + #include +-#ifdef USE_PRIVATE_KRB5_FUNCTIONS + #include +-#endif + #include + #include + @@ -927,9 +927,37 @@ { krb5_error_code ret; @@ -9,14 +19,14 @@ +#ifdef HAVE_HEIMDAL + krb5_creds pattern; -+ krb5_realm *client_realm; ++ krb5_const_realm client_realm; + + krb5_cc_clear_mcred(&pattern); + -+ client_realm = krb5_princ_realm (context, principal); ++ client_realm = krb5_principal_get_realm (context, principal); + + ret = krb5_make_principal (context, &pattern.server, -+ *client_realm, KRB5_TGS_NAME, *client_realm, ++ client_realm, KRB5_TGS_NAME, client_realm, + NULL); + if (ret) + krb5_err (context, 1, ret, "krb5_make_principal"); @@ -57,15 +67,3 @@ krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); krb5_cc_close(context, ccache); err_cache: -@@ -1262,7 +1294,11 @@ - if (context != NULL) { - origmsg = krb5_get_error_message(context, code); - msg = strdup(origmsg); -+#ifdef HAVE_HEIMDAL -+ krb5_free_error_string(context, origmsg); -+#else - krb5_free_error_message(context, origmsg); -+#endif - } - #endif - if (msg != NULL) diff --git a/nfs-utils-no_libgssapi.patch b/nfs-utils-no_libgssapi.patch index 51afd83..68a2b0e 100644 --- a/nfs-utils-no_libgssapi.patch +++ b/nfs-utils-no_libgssapi.patch @@ -16,7 +16,7 @@ Index: nfs-utils-1.1.0/utils/gssd/context_lucid.c @@ -171,10 +173,10 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss int retcode = 0; - printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); + printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); - maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, + maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &ctx, 1, &return_ctx); @@ -42,13 +42,20 @@ Index: nfs-utils-1.1.0/utils/gssd/krb5_util.c =================================================================== --- nfs-utils-1.1.0.orig/utils/gssd/krb5_util.c +++ nfs-utils-1.1.0/utils/gssd/krb5_util.c -@@ -294,10 +294,10 @@ limit_krb5_enctypes(struct rpc_gss_sec * - return -1; - } +@@ -1317,13 +1317,13 @@ limit_krb5_enctypes(struct rpc_gss_sec * + * list of supported enctypes, use local default here. + */ + if (krb5_enctypes == NULL) +- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, +- &krb5oid, num_enctypes, enctypes); ++ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh, ++ num_enctypes, enctypes); + else +- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, +- &krb5oid, num_krb5_enctypes, krb5_enctypes); ++ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh, ++ num_krb5_enctypes, krb5_enctypes); -- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, -+ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, credh, - num_enctypes, &enctypes); if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_set_allowable_enctypes", + pgsserr("gss_krb5_set_allowable_enctypes", diff --git a/nfs-utils.spec b/nfs-utils.spec index a7b5b3d..f97cad1 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -13,7 +13,7 @@ Summary(ru.UTF-8): Утилиты для NFS и демоны поддержки Summary(uk.UTF-8): Утиліти для NFS та демони підтримки для NFS-сервера ядра Name: nfs-utils Version: 1.2.2 -Release: 2 +Release: 2.3 License: GPL v2 Group: Networking/Daemons Source0: http://www.kernel.org/pub/linux/utils/nfs/%{name}-%{version}.tar.bz2 @@ -38,6 +38,7 @@ Patch4: %{name}-kerberos-ac.patch Patch5: %{name}-no_libgssapi.patch Patch6: %{name}-pkgconfig_ac.patch Patch7: %{name}-heimdal_functions.patch +Patch100: %{name}-git.patch URL: http://nfs.sourceforge.net/ BuildRequires: autoconf >= 2.59 BuildRequires: automake @@ -168,6 +169,7 @@ Wspólne programy do obsługi NFS. %prep %setup -q -a1 +%patch100 -p1 %patch0 -p1 %patch1 -p1 %patch2 -p1 @@ -384,11 +386,15 @@ fi %attr(4755,root,root) /sbin/umount.nfs %attr(4755,root,root) /sbin/mount.nfs4 %attr(4755,root,root) /sbin/umount.nfs4 +%attr(755,root,root) %{_sbindir}/mountstats +%attr(755,root,root) %{_sbindir}/nfsiostat %attr(755,root,root) %{_sbindir}/showmount %attr(755,root,root) %{_sbindir}/rpc.gssd %attr(754,root,root) /etc/rc.d/init.d/gssd %{_mandir}/man8/gssd.8* %{_mandir}/man8/mount.nfs.8* +%{_mandir}/man8/mountstats.8* +%{_mandir}/man8/nfsiostat.8* %{_mandir}/man8/rpc.gssd.8* %{_mandir}/man8/showmount.8* %{_mandir}/man8/umount.nfs.8*