diff --git a/nfs-utils-1-2-3-rc1.patch b/nfs-utils-1-2-3-rc1.patch deleted file mode 100644 index dfafe7d..0000000 --- a/nfs-utils-1-2-3-rc1.patch +++ /dev/null @@ -1,593 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index b7520d8..518b6d8 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"]) -diff --git a/support/export/client.c b/support/export/client.c -index 6236561..5e937b0 100644 ---- a/support/export/client.c -+++ b/support/export/client.c -@@ -28,6 +28,8 @@ - #if !defined(__GLIBC__) || __GLIBC__ < 2 - extern int innetgr(char *netgr, char *host, char *, char *); - #endif -+ -+static char *add_name(char *old, const char *add); - static void client_init(nfs_client *clp, const char *hname, - struct hostent *hp); - static int client_checkaddr(nfs_client *clp, struct in_addr addr); -@@ -211,28 +213,6 @@ client_freeall(void) - } - } - --nfs_client * --client_find(struct hostent *hp) --{ -- nfs_client *clp; -- int i; -- -- for (i = 0; i < MCL_MAXTYPES; i++) { -- for (clp = clientlist[i]; clp; clp = clp->m_next) { -- if (!client_check(clp, hp)) -- continue; --#ifdef notdef -- if (clp->m_type == MCL_FQDN) -- return clp; -- return client_dup(clp, hp); --#else -- return clp; --#endif -- } -- } -- return NULL; --} -- - struct hostent * - client_resolve(struct in_addr addr) - { -@@ -246,14 +226,18 @@ client_resolve(struct in_addr addr) - return he; - } - --/* -- * 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 -+ * @he: pointer to hostent 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) - { -@@ -271,13 +255,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 +280,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 +294,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); -diff --git a/support/export/export.c b/support/export/export.c -index 2943466..42e78f6 100644 ---- a/support/export/export.c -+++ b/support/export/export.c -@@ -128,6 +128,7 @@ export_dup(nfs_export *exp, struct hostent *hp) - - return new; - } -+ - /* - * Add export entry to hash table - */ -@@ -276,15 +277,15 @@ export_freeall(void) - if (exp->m_export.e_mountpoint) - free(exp->m_export.e_mountpoint); - if (exp->m_export.e_fslocdata) -- xfree(exp->m_export.e_fslocdata); -+ free(exp->m_export.e_fslocdata); - xfree(exp->m_export.e_hostname); - xfree(exp); - } -- 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; -+ 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; - } - client_freeall(); - } -diff --git a/support/include/exportfs.h b/support/include/exportfs.h -index 470b2ec..9a19cbb 100644 ---- a/support/include/exportfs.h -+++ b/support/include/exportfs.h -@@ -69,17 +69,16 @@ 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 *); - int client_gettype(char *hname); - int client_check(nfs_client *, struct hostent *); --int client_match(nfs_client *, char *hname); - 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); -+int client_member(const char *client, -+ const char *name); - - int export_read(char *fname); - void export_add(nfs_export *); -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/nfs/cacheio.c b/support/nfs/cacheio.c -index bdf5d84..0587ecb 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) -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/utils/mount/network.c b/utils/mount/network.c -index 8dc183a..c541257 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -857,7 +857,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 +964,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; - } -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index 9b8c38f..98557d2 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -799,6 +799,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/cache.c b/utils/mountd/cache.c -index d63e10a..6343325 100644 ---- a/utils/mountd/cache.c -+++ b/utils/mountd/cache.c -@@ -125,7 +125,7 @@ void auth_unix_gid(FILE *f) - * 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 +136,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 +153,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; icl_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/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-1-2-3-rc2.patch b/nfs-utils-1-2-3-rc2.patch deleted file mode 100644 index 73a7233..0000000 --- a/nfs-utils-1-2-3-rc2.patch +++ /dev/null @@ -1,1216 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index 518b6d8..d90a88f 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -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 5e937b0..6a25928 100644 ---- a/support/export/client.c -+++ b/support/export/client.c -@@ -32,11 +32,26 @@ extern int innetgr(char *netgr, char *host, char *, char *); - static char *add_name(char *old, const char *add); - static void client_init(nfs_client *clp, const char *hname, - struct hostent *hp); --static int client_checkaddr(nfs_client *clp, struct in_addr addr); - - nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; - - -+static void -+init_addrlist(nfs_client *clp, const struct hostent *hp) -+{ -+ char **ap; -+ int i; -+ -+ if (hp == NULL) -+ return; -+ -+ ap = hp->h_addr_list; -+ for (i = 0; *ap != NULL && i < NFSCLNT_ADDRMAX; i++, ap++) -+ clp->m_addrlist[i] = *(struct in_addr *)*ap; -+ -+ clp->m_naddr = i; -+} -+ - /* 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 -@@ -97,14 +112,8 @@ client_lookup(char *hname, int canonical) - 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 (htype == MCL_FQDN && clp->m_naddr == 0) -+ init_addrlist(clp, hp); - - if (hp) - free (hp); -@@ -138,6 +147,7 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp) - - clp->m_exported = 0; - clp->m_count = 0; -+ clp->m_naddr = 0; - - if (clp->m_type == MCL_SUBNETWORK) { - char *cp = strchr(clp->m_hostname, '/'); -@@ -161,18 +171,10 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp) - } - } - *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; -+ return; - } -+ -+ init_addrlist(clp, hp); - } - - void -@@ -329,101 +331,158 @@ add_name(char *old, const char *add) - } - - /* -- * Match a host (given its hostent record) to a client record. This -- * is usually called from mountd. -+ * Check each address listed in @hp 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 hostent *hp) - { -- char *hname = (char *) hp->h_name; -- char *cname = clp->m_hostname; -- char **ap; -+ struct in_addr addr; -+ 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)) -- return 1; -- } -- return 0; -- case MCL_WILDCARD: -- if (wildmat(hname, cname)) -- 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)) -+ for (ap = hp->h_addr_list; *ap; ap++) { -+ addr = *(struct in_addr *)*ap; -+ -+ for (i = 0; i < clp->m_naddr; i++) -+ if (clp->m_addrlist[i].s_addr == addr.s_addr) - 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; -- } -+/* -+ * Check each address listed in @hp 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 hostent *hp) -+{ -+ struct in_addr addr; -+ char **ap; - -- /* 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; -- } -+ for (ap = hp->h_addr_list; *ap; ap++) { -+ addr = *(struct in_addr *)*ap; - -- /* Okay, strip off the domain (if we have one) */ -- if ((dot = strchr(hname, '.')) == NULL) -- return 0; -+ if (!((clp->m_addrlist[0].s_addr ^ addr.s_addr) & -+ clp->m_addrlist[1].s_addr)) -+ return 1; -+ } -+ 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 hostent *hp) -+{ -+ char *cname = clp->m_hostname; -+ char *hname = hp->h_name; -+ 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 */ -+ for (ap = hp->h_aliases; *ap; ap++) { -+ if (wildmat(*ap, cname)) -+ return 1; - } - - return 0; - } - -+/* -+ * Check if @hp's hostname or aliases fall in a given netgroup. -+ * Return 1 if @hp represents a host in the netgroup, otherwise zero. -+ */ -+#ifdef HAVE_INNETGR -+static int -+check_netgroup(const nfs_client *clp, const struct hostent *hp) -+{ -+ const char *netgroup = clp->m_hostname + 1; -+ const char *hname = hp->h_name; -+ struct hostent *nhp = NULL; -+ struct sockaddr_in addr; -+ int match, i; -+ char *dot; -+ -+ /* First, try to match the hostname without -+ * splitting off the domain */ -+ if (innetgr(netgroup, hname, NULL, NULL)) -+ return 1; -+ -+ /* See if hname aliases listed in /etc/hosts or nis[+] -+ * match the requested netgroup */ -+ for (i = 0; hp->h_aliases[i]; i++) { -+ if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) -+ return 1; -+ } -+ -+ /* 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 = nhp->h_name; -+ if (innetgr(netgroup, hname, NULL, NULL)) -+ return 1; -+ } -+ -+ /* Okay, strip off the domain (if we have one) */ -+ dot = strchr(hname, '.'); -+ if (dot == NULL) -+ return 0; -+ -+ *dot = '\0'; -+ match = innetgr(netgroup, hname, NULL, NULL); -+ *dot = '.'; -+ -+ 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 hostent *hp) - { -- 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 -+ * @hp: pointer to hostent containing host IP information -+ * -+ * Returns 1 if the address information matches the cached nfs_client, -+ * otherwise zero. -+ */ -+int -+client_check(nfs_client *clp, struct hostent *hp) -+{ - 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, hp); - case MCL_SUBNETWORK: -- return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) -- & clp->m_addrlist[1].s_addr); -+ return check_subnetwork(clp, hp); -+ case MCL_WILDCARD: -+ return check_wildcard(clp, hp); -+ case MCL_NETGROUP: -+ return check_netgroup(clp, hp); -+ 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; - } - -diff --git a/support/export/export.c b/support/export/export.c -index 42e78f6..3e4da69 100644 ---- a/support/export/export.c -+++ b/support/export/export.c -@@ -28,6 +28,18 @@ static int export_check(nfs_export *, struct hostent *, char *); - static nfs_export * - export_allowed_internal(struct hostent *hp, char *path); - -+static void -+export_free(nfs_export *exp) -+{ -+ xfree(exp->m_export.e_squids); -+ xfree(exp->m_export.e_sqgids); -+ free(exp->m_export.e_mountpoint); -+ free(exp->m_export.e_fslocdata); -+ -+ xfree(exp->m_export.e_hostname); -+ xfree(exp); -+} -+ - static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) - { - if (exp->m_export.e_flags != eep->e_flags) { -@@ -117,6 +129,10 @@ export_dup(nfs_export *exp, struct hostent *hp) - if (exp->m_export.e_hostname) - new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); - clp = client_dup(exp->m_client, hp); -+ if (clp == NULL) { -+ export_free(new); -+ return NULL; -+ } - clp->m_count++; - new->m_client = clp; - new->m_mayexport = exp->m_mayexport; -@@ -260,6 +276,10 @@ export_check(nfs_export *exp, struct hostent *hp, char *path) - return client_check(exp->m_client, hp); - } - -+/** -+ * export_freeall - deallocate all nfs_export records -+ * -+ */ - void - export_freeall(void) - { -@@ -270,16 +290,7 @@ 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) -- free(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; -diff --git a/support/include/nfslib.h b/support/include/nfslib.h -index 537a31e..e44cf8f 100644 ---- a/support/include/nfslib.h -+++ b/support/include/nfslib.h -@@ -152,6 +152,8 @@ 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); - -diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c -index 0587ecb..55fa45d 100644 ---- a/support/nfs/cacheio.c -+++ b/support/nfs/cacheio.c -@@ -241,7 +241,7 @@ int qword_get_int(char **bpp, int *anint) - return 0; - } - --int qword_get_uint(char *bpp, unsigned int *anint) -+int qword_get_uint(char **bpp, unsigned int *anint) - { - char buf[50]; - char *ep; -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/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man -new file mode 100644 -index 0000000..7b1e0a8 ---- /dev/null -+++ b/tools/nfs-iostat/nfsiostat.man -@@ -0,0 +1,70 @@ -+.\" -+.\" 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 -+.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/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..b87bf76 100644 ---- a/utils/gssd/context_lucid.c -+++ b/utils/gssd/context_lucid.c -@@ -42,6 +42,7 @@ - #include - #include - #include -+#include - - #include - -@@ -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,100 @@ 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 (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 +264,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 +286,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 +309,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 +320,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..f9cbb02 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,120 @@ 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; -+ 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_proc.c b/utils/gssd/gssd_proc.c -index be4fb11..a55418b 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) -@@ -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..dccbeb6 100644 ---- a/utils/gssd/krb5_util.c -+++ b/utils/gssd/krb5_util.c -@@ -292,61 +292,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. -@@ -1304,3 +1249,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, 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]); -+ 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/nfs-utils-1-2-3-rc3.patch b/nfs-utils-1-2-3-rc3.patch new file mode 100644 index 0000000..ae27e6b --- /dev/null +++ b/nfs-utils-1-2-3-rc3.patch @@ -0,0 +1,2109 @@ +diff -up nfs-utils-1.2.2/aclocal/libcap.m4.orig nfs-utils-1.2.2/aclocal/libcap.m4 +--- nfs-utils-1.2.2/aclocal/libcap.m4.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/aclocal/libcap.m4 2010-05-06 07:16:37.851057000 -0400 +@@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ + dnl look for prctl + AC_CHECK_FUNC([prctl], , ) + +- dnl look for the library; do not add to LIBS if found +- AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) +- AC_SUBST(LIBCAP) ++ AC_ARG_ENABLE([caps], ++ [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) ++ ++ LIBCAP= ++ ++ if test "x$enable_caps" != "xno" ; then ++ dnl look for the library; do not add to LIBS if found ++ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) + +- AC_CHECK_HEADERS([sys/capability.h], , +- [AC_MSG_ERROR([libcap headers not found.])]) ++ AC_CHECK_HEADERS([sys/capability.h], , ++ [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) ++ fi ++ ++ AC_SUBST(LIBCAP) + + ])dnl +diff -up nfs-utils-1.2.2/configure.ac.orig nfs-utils-1.2.2/configure.ac +--- nfs-utils-1.2.2/configure.ac.orig 2010-05-06 07:15:49.881176000 -0400 ++++ nfs-utils-1.2.2/configure.ac 2010-05-06 07:16:37.856060000 -0400 +@@ -89,7 +89,7 @@ AC_ARG_ENABLE(nfsv41, + if test "$enable_nfsv41" = yes; then + AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + else +- enable_nfsv4= ++ enable_nfsv41= + fi + AC_SUBST(enable_nfsv41) + AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) +@@ -436,6 +436,8 @@ AC_CONFIG_FILES([ + tools/nlmtest/Makefile + tools/rpcdebug/Makefile + tools/rpcgen/Makefile ++ tools/mountstats/Makefile ++ tools/nfs-iostat/Makefile + utils/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile +diff -up nfs-utils-1.2.2/support/export/client.c.orig nfs-utils-1.2.2/support/export/client.c +--- nfs-utils-1.2.2/support/export/client.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/client.c 2010-05-06 07:16:37.862057000 -0400 +@@ -17,7 +17,7 @@ + #include + #include + #include +-#include "xmalloc.h" ++ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +@@ -28,13 +28,116 @@ + #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, }; + + ++static void ++init_addrlist(nfs_client *clp, const struct hostent *hp) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ char **ap; ++ int i; ++ ++ if (hp == NULL) ++ return; ++ ++ ap = hp->h_addr_list; ++ for (i = 0; *ap != NULL && i < NFSCLNT_ADDRMAX; i++, ap++) { ++ sin.sin_addr = *(struct in_addr *)*ap; ++ set_addrlist_in(clp, i, &sin); ++ } ++ 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) ++{ ++ struct sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ ++ if (strchr(slash + 1, '.') != NULL) ++ sin.sin_addr.s_addr = inet_addr(slash + 1); ++ else { ++ int prefixlen = atoi(slash + 1); ++ if (0 < prefixlen && prefixlen <= 32) ++ sin.sin_addr.s_addr = ++ htonl((uint32_t)~0 << (32 - prefixlen)); ++ else ++ goto out_badprefix; ++ } ++ ++ set_addrlist_in(clp, 1, &sin); ++ return 1; ++ ++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 sockaddr_in sin = { ++ .sin_family = AF_INET, ++ }; ++ static char slash32[] = "/32"; ++ char *cp; ++ ++ cp = strchr(clp->m_hostname, '/'); ++ if (cp == NULL) ++ cp = slash32; ++ ++ *cp = '\0'; ++ sin.sin_addr.s_addr = inet_addr(clp->m_hostname); ++ set_addrlist_in(clp, 0, &sin); ++ *cp = '/'; ++ ++ return init_netmask(clp, cp); ++} ++ ++static int ++client_init(nfs_client *clp, const char *hname, const struct hostent *hp) ++{ ++ 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, hp); ++ 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; ++} ++ + /* 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 +@@ -87,23 +190,23 @@ 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 (htype == MCL_FQDN && clp->m_naddr == 0) ++ init_addrlist(clp, hp); + ++out: + if (hp) + free (hp); + +@@ -115,78 +218,21 @@ client_dup(nfs_client *clp, struct hoste + { + 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, hp->h_name, hp)) { ++ 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; +-} +- + void + client_release(nfs_client *clp) + { +@@ -205,34 +251,11 @@ client_freeall(void) + head = clientlist + i; + while (*head) { + *head = (clp = *head)->m_next; +- xfree(clp->m_hostname); +- xfree(clp); ++ client_free(clp); + } + } + } + +-nfs_client * +-client_find(struct hostent *hp) +-{ +- nfs_client *clp; +- int i; +- +- for (i = 0; i < MCL_MAXTYPES; i++) { +- for (clp = clientlist[i]; clp; clp = clp->m_next) { +- if (!client_check(clp, hp)) +- continue; +-#ifdef notdef +- if (clp->m_type == MCL_FQDN) +- return clp; +- return client_dup(clp, hp); +-#else +- return clp; +-#endif +- } +- } +- return NULL; +-} +- + struct hostent * + client_resolve(struct in_addr addr) + { +@@ -246,14 +269,18 @@ client_resolve(struct in_addr addr) + return he; + } + +-/* +- * 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 ++ * @he: pointer to hostent 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) + { +@@ -271,13 +298,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 +323,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 +337,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,101 +372,164 @@ 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 @hp 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 hostent *hp) + { +- char *hname = (char *) hp->h_name; +- char *cname = clp->m_hostname; +- char **ap; ++ const struct sockaddr_in *sin; ++ struct in_addr addr; ++ 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 (ap = hp->h_addr_list; *ap; ap++) { ++ addr = *(struct in_addr *)*ap; ++ ++ for (i = 0; i < clp->m_naddr; i++) { ++ sin = get_addrlist_in(clp, i); ++ if (sin->sin_addr.s_addr == addr.s_addr) + return 1; + } +- return 0; +- case MCL_WILDCARD: +- if (wildmat(hname, cname)) +- 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; +- } ++/* ++ * Check each address listed in @hp 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 hostent *hp) ++{ ++ const struct sockaddr_in *address, *mask; ++ struct in_addr addr; ++ char **ap; ++ ++ for (ap = hp->h_addr_list; *ap; ap++) { ++ address = get_addrlist_in(clp, 0); ++ mask = get_addrlist_in(clp, 1); ++ addr = *(struct in_addr *)*ap; + +- /* If hname is ip address convert to FQDN */ +- if (inet_aton(hname, &addr.sin_addr) && +- (nhp = gethostbyaddr((const char *)&(addr.sin_addr), +- sizeof(addr.sin_addr), AF_INET))) { +- hname = (char *)nhp->h_name; +- if (innetgr(cname+1, hname, NULL, NULL)) +- return 1; +- } ++ if (!((address->sin_addr.s_addr ^ addr.s_addr) & ++ mask->sin_addr.s_addr)) ++ return 1; ++ } ++ return 0; ++} + +- /* Okay, strip off the domain (if we have one) */ +- if ((dot = strchr(hname, '.')) == NULL) +- return 0; +- +- *dot = '\0'; +- match = innetgr(cname+1, hname, NULL, NULL); +- *dot = '.'; ++/* ++ * 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 hostent *hp) ++{ ++ char *cname = clp->m_hostname; ++ char *hname = hp->h_name; ++ 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 */ ++ for (ap = hp->h_aliases; *ap; ap++) { ++ if (wildmat(*ap, cname)) ++ return 1; + } + + return 0; + } + ++/* ++ * Check if @hp's hostname or aliases fall in a given netgroup. ++ * Return 1 if @hp represents a host in the netgroup, otherwise zero. ++ */ ++#ifdef HAVE_INNETGR ++static int ++check_netgroup(const nfs_client *clp, const struct hostent *hp) ++{ ++ const char *netgroup = clp->m_hostname + 1; ++ const char *hname = hp->h_name; ++ struct hostent *nhp = NULL; ++ struct sockaddr_in addr; ++ int match, i; ++ char *dot; ++ ++ /* First, try to match the hostname without ++ * splitting off the domain */ ++ if (innetgr(netgroup, hname, NULL, NULL)) ++ return 1; ++ ++ /* See if hname aliases listed in /etc/hosts or nis[+] ++ * match the requested netgroup */ ++ for (i = 0; hp->h_aliases[i]; i++) { ++ if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) ++ return 1; ++ } ++ ++ /* 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 = nhp->h_name; ++ if (innetgr(netgroup, hname, NULL, NULL)) ++ return 1; ++ } ++ ++ /* Okay, strip off the domain (if we have one) */ ++ dot = strchr(hname, '.'); ++ if (dot == NULL) ++ return 0; ++ ++ *dot = '\0'; ++ match = innetgr(netgroup, hname, NULL, NULL); ++ *dot = '.'; ++ ++ 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 hostent *hp) + { +- 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 ++ * @hp: pointer to hostent containing host IP information ++ * ++ * Returns 1 if the address information matches the cached nfs_client, ++ * otherwise zero. ++ */ ++int ++client_check(nfs_client *clp, struct hostent *hp) ++{ + 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, hp); + case MCL_SUBNETWORK: +- return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) +- & clp->m_addrlist[1].s_addr); ++ return check_subnetwork(clp, hp); ++ case MCL_WILDCARD: ++ return check_wildcard(clp, hp); ++ case MCL_NETGROUP: ++ return check_netgroup(clp, hp); ++ 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; + } + +diff -up nfs-utils-1.2.2/support/export/export.c.orig nfs-utils-1.2.2/support/export/export.c +--- nfs-utils-1.2.2/support/export/export.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/export.c 2010-05-06 07:16:37.867057000 -0400 +@@ -28,6 +28,18 @@ static int export_check(nfs_export *, st + static nfs_export * + export_allowed_internal(struct hostent *hp, char *path); + ++static void ++export_free(nfs_export *exp) ++{ ++ xfree(exp->m_export.e_squids); ++ xfree(exp->m_export.e_sqgids); ++ free(exp->m_export.e_mountpoint); ++ free(exp->m_export.e_fslocdata); ++ ++ xfree(exp->m_export.e_hostname); ++ xfree(exp); ++} ++ + static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) + { + if (exp->m_export.e_flags != eep->e_flags) { +@@ -117,6 +129,10 @@ export_dup(nfs_export *exp, struct hoste + if (exp->m_export.e_hostname) + new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); + clp = client_dup(exp->m_client, hp); ++ if (clp == NULL) { ++ export_free(new); ++ return NULL; ++ } + clp->m_count++; + new->m_client = clp; + new->m_mayexport = exp->m_mayexport; +@@ -128,6 +144,7 @@ export_dup(nfs_export *exp, struct hoste + + return new; + } ++ + /* + * Add export entry to hash table + */ +@@ -259,6 +276,10 @@ export_check(nfs_export *exp, struct hos + return client_check(exp->m_client, hp); + } + ++/** ++ * export_freeall - deallocate all nfs_export records ++ * ++ */ + void + export_freeall(void) + { +@@ -269,22 +290,13 @@ export_freeall(void) + for (exp = exportlist[i].p_head; exp; exp = nxt) { + nxt = exp->m_next; + client_release(exp->m_client); +- if (exp->m_export.e_squids) +- xfree(exp->m_export.e_squids); +- if (exp->m_export.e_sqgids) +- xfree(exp->m_export.e_sqgids); +- if (exp->m_export.e_mountpoint) +- free(exp->m_export.e_mountpoint); +- if (exp->m_export.e_fslocdata) +- xfree(exp->m_export.e_fslocdata); +- xfree(exp->m_export.e_hostname); +- xfree(exp); ++ export_free(exp); ++ } ++ for (j = 0; j < HASH_TABLE_SIZE; j++) { ++ exportlist[i].entries[j].p_first = NULL; ++ exportlist[i].entries[j].p_last = NULL; + } +- for(j = 0; j < HASH_TABLE_SIZE; j++) { +- exportlist[i].entries[j].p_first = NULL; +- exportlist[i].entries[j].p_last = NULL; +- } +- exportlist[i].p_head = NULL; ++ exportlist[i].p_head = NULL; + } + client_freeall(); + } +diff -up nfs-utils-1.2.2/support/export/nfsctl.c.orig nfs-utils-1.2.2/support/export/nfsctl.c +--- nfs-utils-1.2.2/support/export/nfsctl.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/export/nfsctl.c 2010-05-06 07:16:37.872057000 -0400 +@@ -66,7 +66,7 @@ str_tolower(char *s) + static int + cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) + { +- int i; ++ int i, j; + + if (clp->m_type != MCL_FQDN) { + xlog(L_ERROR, "internal: can't export non-FQDN host"); +@@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, n + strncpy(cltarg->cl_ident, clp->m_hostname, + sizeof (cltarg->cl_ident) - 1); + str_tolower(cltarg->cl_ident); +- cltarg->cl_naddr = clp->m_naddr; +- for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) +- cltarg->cl_addrlist[i] = clp->m_addrlist[i]; + ++ j = 0; ++ for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { ++ 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; + } + +diff -up nfs-utils-1.2.2/support/include/exportfs.h.orig nfs-utils-1.2.2/support/include/exportfs.h +--- nfs-utils-1.2.2/support/include/exportfs.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/exportfs.h 2010-05-06 07:16:37.877057000 -0400 +@@ -10,6 +10,8 @@ + #define EXPORTFS_H + + #include ++ ++#include "sockaddr.h" + #include "nfslib.h" + + enum { +@@ -35,11 +37,56 @@ typedef struct mclient { + char * m_hostname; + int m_type; + int m_naddr; +- struct in_addr m_addrlist[NFSCLNT_ADDRMAX]; ++ union nfs_sockaddr m_addrlist[NFSCLNT_ADDRMAX]; + int m_exported; /* exported to nfsd */ + int m_count; + } nfs_client; + ++static inline const struct sockaddr * ++get_addrlist(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].sa; ++} ++ ++static inline const struct sockaddr_in * ++get_addrlist_in(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s4; ++} ++ ++static inline const struct sockaddr_in6 * ++get_addrlist_in6(const nfs_client *clp, const int i) ++{ ++ return &clp->m_addrlist[i].s6; ++} ++ ++static inline void ++set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin) ++{ ++ memcpy(&clp->m_addrlist[i].s4, sin, sizeof(*sin)); ++} ++ ++static inline void ++set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6) ++{ ++ memcpy(&clp->m_addrlist[i].s6, sin6, sizeof(*sin6)); ++} ++ ++static inline void ++set_addrlist(nfs_client *clp, const int i, const struct sockaddr *sap) ++{ ++ switch (sap->sa_family) { ++ case AF_INET: ++ memcpy(&clp->m_addrlist[i].s4, sap, sizeof(struct sockaddr_in)); ++ break; ++#ifdef IPV6_SUPPORTED ++ case AF_INET6: ++ memcpy(&clp->m_addrlist[i].s6, sap, sizeof(struct sockaddr_in6)); ++ break; ++#endif ++ } ++} ++ + typedef struct mexport { + struct mexport * m_next; + struct mclient * m_client; +@@ -69,17 +116,15 @@ extern exp_hash_table exportlist[MCL_MAX + extern nfs_client * clientlist[MCL_MAXTYPES]; + + nfs_client * client_lookup(char *hname, int canonical); +-nfs_client * client_find(struct hostent *); +-void client_add(nfs_client *); + nfs_client * client_dup(nfs_client *, struct hostent *); + int client_gettype(char *hname); + int client_check(nfs_client *, struct hostent *); +-int client_match(nfs_client *, char *hname); + 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); ++int client_member(const char *client, ++ const char *name); + + int export_read(char *fname); + void export_add(nfs_export *); +diff -up nfs-utils-1.2.2/support/include/nfslib.h.orig nfs-utils-1.2.2/support/include/nfslib.h +--- nfs-utils-1.2.2/support/include/nfslib.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/nfslib.h 2010-05-06 07:16:37.882059000 -0400 +@@ -152,6 +152,8 @@ void qword_addhex(char **bpp, int *lp, c + void qword_addint(char **bpp, int *lp, int n); + void qword_adduint(char **bpp, int *lp, unsigned int n); + void qword_addeol(char **bpp, int *lp); ++int qword_get_uint(char **bpp, unsigned int *anint); ++void qword_printuint(FILE *f, unsigned int num); + + void closeall(int min); + +diff -up nfs-utils-1.2.2/support/include/nfsrpc.h.orig nfs-utils-1.2.2/support/include/nfsrpc.h +--- nfs-utils-1.2.2/support/include/nfsrpc.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/include/nfsrpc.h 2010-05-06 07:16:37.888057000 -0400 +@@ -160,4 +160,7 @@ extern int nfs_rpc_ping(const struct so + const unsigned short protocol, + const struct timeval *timeout); + ++/* create AUTH_SYS handle with no supplemental groups */ ++extern AUTH * nfs_authsys_create(void); ++ + #endif /* !__NFS_UTILS_NFSRPC_H */ +diff -up nfs-utils-1.2.2/support/nfs/cacheio.c.orig nfs-utils-1.2.2/support/nfs/cacheio.c +--- nfs-utils-1.2.2/support/nfs/cacheio.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/cacheio.c 2010-05-06 07:16:37.893057000 -0400 +@@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num) + fprintf(f, "%d ", num); + } + ++void qword_printuint(FILE *f, unsigned int num) ++{ ++ fprintf(f, "%u ", num); ++} ++ + int qword_eol(FILE *f) + { + int err; +@@ -236,6 +241,20 @@ int qword_get_int(char **bpp, int *anint + return 0; + } + ++int qword_get_uint(char **bpp, unsigned int *anint) ++{ ++ char buf[50]; ++ char *ep; ++ unsigned int rv; ++ int len = qword_get(bpp, buf, 50); ++ if (len < 0) return -1; ++ if (len ==0) return -1; ++ rv = strtoul(buf, &ep, 0); ++ if (*ep) return -1; ++ *anint = rv; ++ return 0; ++} ++ + #define READLINE_BUFFER_INCREMENT 2048 + + int readline(int fd, char **buf, int *lenp) +diff -up nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig nfs-utils-1.2.2/support/nfs/rpc_socket.c +--- nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/support/nfs/rpc_socket.c 2010-05-06 07:16:37.898057000 -0400 +@@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog + + return program; + } ++ ++/* ++ * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. ++ * If there are more than that, trying to determine which ones to include ++ * in the list is problematic. This function creates an auth handle that ++ * only has the primary gid in the supplemental gids list. It's intended to ++ * be used for protocols where credentials really don't matter much (the MNT ++ * protocol, for instance). ++ */ ++AUTH * ++nfs_authsys_create(void) ++{ ++ char machname[MAXHOSTNAMELEN + 1]; ++ uid_t uid = geteuid(); ++ gid_t gid = getegid(); ++ ++ if (gethostname(machname, sizeof(machname)) == -1) ++ return NULL; ++ ++ return authunix_create(machname, uid, gid, 1, &gid); ++} +diff -up nfs-utils-1.2.2/support/nsm/file.c.orig nfs-utils-1.2.2/support/nsm/file.c +--- nfs-utils-1.2.2/support/nsm/file.c.orig 2010-05-06 07:15:49.888166000 -0400 ++++ nfs-utils-1.2.2/support/nsm/file.c 2010-05-06 07:16:37.917057000 -0400 +@@ -67,7 +67,9 @@ + #endif + + #include ++#ifdef HAVE_SYS_CAPABILITY_H + #include ++#endif + #include + #include + +@@ -348,6 +350,7 @@ nsm_is_default_parentdir(void) + static _Bool + nsm_clear_capabilities(void) + { ++#ifdef HAVE_SYS_CAPABILITY_H + cap_t caps; + + caps = cap_from_text("cap_net_bind_service=ep"); +@@ -363,6 +366,7 @@ nsm_clear_capabilities(void) + } + + (void)cap_free(caps); ++#endif + return true; + } + +diff -up nfs-utils-1.2.2/tools/Makefile.am.orig nfs-utils-1.2.2/tools/Makefile.am +--- nfs-utils-1.2.2/tools/Makefile.am.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/tools/Makefile.am 2010-05-06 07:16:37.923062000 -0400 +@@ -6,6 +6,6 @@ if CONFIG_RPCGEN + OPTDIRS += rpcgen + endif + +-SUBDIRS = locktest rpcdebug nlmtest $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff -up nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig nfs-utils-1.2.2/tools/mountstats/Makefile.am +--- nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig 2010-05-06 07:16:37.939040000 -0400 ++++ nfs-utils-1.2.2/tools/mountstats/Makefile.am 2010-05-06 07:16:37.941038000 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = mountstats.py ++ ++man8_MANS = mountstats.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 mountstats.py $(DESTDIR)$(sbindir)/mountstats ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig nfs-utils-1.2.2/tools/mountstats/mountstats.man +--- nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig 2010-05-06 07:16:37.944036000 -0400 ++++ nfs-utils-1.2.2/tools/mountstats/mountstats.man 2010-05-06 07:16:37.945043000 -0400 +@@ -0,0 +1,32 @@ ++.\" ++.\" mountstats(8) ++.\" ++.TH mountstats 8 "15 Apr 2010" ++.SH NAME ++mountstats \- Displays NFS client per-mount statistics ++.SH SYNOPSIS ++.BI "mountstats [" "] " " [ " "]" ++.SH DESCRIPTION ++The ++.B mountstats ++command displays NFS client statisitics on each given ++.I ++.SH OPTIONS ++.TP ++.B " \-\-nfs ++display only the NFS statistics ++.TP ++.B " \-\-rpc ++display only the RPC statistics ++.TP ++.B " \-\-version ++display the version of this command ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR nfsiostat (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff -up nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am +--- nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig 2010-05-06 07:16:37.958021000 -0400 ++++ nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am 2010-05-06 07:16:37.960024000 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = nfs-iostat.py ++ ++man8_MANS = nfsiostat.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) --mode 755 nfs-iostat.py $(DESTDIR)$(sbindir)/nfsiostat ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man +--- nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig 2010-05-06 07:16:37.963021000 -0400 ++++ nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man 2010-05-06 07:16:37.965018000 -0400 +@@ -0,0 +1,70 @@ ++.\" ++.\" 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 ++.B \-\-version ++show program's version number and exit ++.SH FILES ++.TP ++.B /proc/self/mountstats ++.SH SEE ALSO ++.BR iostat (8), ++.BR mountstats (8), ++.BR nfsstat(8) ++.SH AUTHOR ++Chuck Lever +diff -up nfs-utils-1.2.2/utils/gssd/context.h.orig nfs-utils-1.2.2/utils/gssd/context.h +--- nfs-utils-1.2.2/utils/gssd/context.h.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context.h 2010-05-06 07:16:37.970009000 -0400 +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004,2008 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -36,6 +36,10 @@ + /* Hopefully big enough to hold any serialized context */ + #define MAX_CTX_LEN 4096 + ++/* New context format flag values */ ++#define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++#define KRB5_CTX_FLAG_CFX 0x00000002 ++#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + + int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, + gss_OID mech, int32_t *endtime); +diff -up nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig nfs-utils-1.2.2/utils/gssd/context_lucid.c +--- nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context_lucid.c 2010-05-06 07:16:37.975007000 -0400 +@@ -42,6 +42,7 @@ + #include + #include + #include ++#include + + #include + +@@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc + * Note that the rfc1964 version only supports DES enctypes. + */ + if (lctx->rfc1964_kd.ctx_key.type != 4) { +- printerr(1, "prepare_krb5_rfc1964_buffer: " +- "overriding heimdal keytype (%d => %d)\n", +- lctx->rfc1964_kd.ctx_key.type, 4); ++ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); + lctx->rfc1964_kd.ctx_key.type = 4; + } + #endif +- printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " +- "enctype %d and length %d\n", +- lctx->rfc1964_kd.ctx_key.type, ++ printerr(2, "%s: serializing keys with enctype %d and length %d\n", ++ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, + lctx->rfc1964_kd.ctx_key.length); + + /* derive the encryption key and copy it into buffer */ +@@ -158,11 +157,100 @@ 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 (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 +264,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + gss_krb5_lucid_context_v1_t *lctx = 0; + int retcode = 0; + +- printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); ++ printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); + maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, + 1, &return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -198,11 +286,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + break; + } + +- /* Now lctx points to a lucid context that we can send down to kernel */ +- if (lctx->protocol == 0) ++ /* ++ * Now lctx points to a lucid context that we can send down to kernel ++ * ++ * Note: we send down different information to the kernel depending ++ * on the protocol version and the enctyption type. ++ * For protocol version 0 with all enctypes besides DES3, we use ++ * the original format. For protocol version != 0 or DES3, we ++ * send down the new style information. ++ */ ++ ++ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) + retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); + else +- retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf, endtime); ++ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime); + + maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +@@ -212,8 +309,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + } + + if (retcode) { +- printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " +- "failed (retcode = %d)\n", retcode); ++ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", ++ __FUNCTION__, retcode); + goto out_err; + } + +@@ -223,4 +320,7 @@ out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); + return -1; + } ++ ++ ++ + #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ +diff -up nfs-utils-1.2.2/utils/gssd/context_mit.c.orig nfs-utils-1.2.2/utils/gssd/context_mit.c +--- nfs-utils-1.2.2/utils/gssd/context_mit.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/context_mit.c 2010-05-06 07:16:37.980998000 -0400 +@@ -1,5 +1,5 @@ + /* +- Copyright (c) 2004 The Regents of the University of Michigan. ++ Copyright (c) 2004-2006 The Regents of the University of Michigan. + All rights reserved. + + Redistribution and use in source and binary forms, with or without +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -52,8 +53,7 @@ + /* XXX argggg, there's gotta be a better way than just duplicating this + * whole struct. Unfortunately, this is in a "private" header file, + * so this is our best choice at this point :-/ +- * +- * XXX Does this match the Heimdal definition? */ ++ */ + + typedef struct _krb5_gss_ctx_id_rec { + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ +@@ -156,50 +156,120 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss + { + krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; + char *p, *end; +- static int constant_one = 1; + static int constant_zero = 0; ++ static int constant_one = 1; ++ static int constant_two = 2; + uint32_t word_seq_send; ++ u_int64_t seq_send_64bit; ++ uint32_t v2_flags = 0; + + if (!(buf->value = calloc(1, MAX_CTX_LEN))) + goto out_err; + p = buf->value; + end = buf->value + MAX_CTX_LEN; + +- if (kctx->initiate) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (kctx->seed_init) { +- if (WRITE_BYTES(&p, end, constant_one)) goto out_err; +- } +- else { +- if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; +- } +- if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ switch (kctx->enc->enctype) { ++ case ENCTYPE_DES_CBC_CRC: ++ case ENCTYPE_DES_CBC_MD4: ++ case ENCTYPE_DES_CBC_MD5: ++ case ENCTYPE_DES_CBC_RAW: ++ /* Old format of context to the kernel */ ++ if (kctx->initiate) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (kctx->seed_init) { ++ if (WRITE_BYTES(&p, end, constant_one)) goto out_err; ++ } ++ else { ++ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; ++ } ++ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) ++ goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ word_seq_send = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; ++ if (write_oid(&p, end, kctx->mech_used)) goto out_err; ++ ++ printerr(2, "serialize_krb5_ctx: serializing keys with " ++ "enctype %d and length %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_keyblock(&p, end, kctx->enc)) goto out_err; ++ if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ break; ++ case ENCTYPE_DES3_CBC_RAW: ++ case ENCTYPE_DES3_CBC_SHA1: ++ case ENCTYPE_ARCFOUR_HMAC: ++ case ENCTYPE_ARCFOUR_HMAC_EXP: ++ case ENCTYPE_AES128_CTS_HMAC_SHA1_96: ++ case ENCTYPE_AES256_CTS_HMAC_SHA1_96: ++ /* New format of context to the kernel */ ++ /* u32 flags; ++ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 ++ * #define KRB5_CTX_FLAG_CFX 0x00000002 ++ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 ++ * s32 endtime; ++ * u64 seq_send; ++ * u32 enctype; ++ * rawkey data ++ */ ++ ++ if (kctx->initiate) ++ v2_flags |= KRB5_CTX_FLAG_INITIATOR; ++ if (kctx->proto == 1) ++ v2_flags |= KRB5_CTX_FLAG_CFX; ++ if (kctx->have_acceptor_subkey) ++ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; ++ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; ++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; ++ ++ seq_send_64bit = kctx->seq_send; ++ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; ++ ++ if (kctx->have_acceptor_subkey) { ++ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing subkey " ++ "with enctype %d and size %d\n", ++ kctx->acceptor_subkey->enctype, ++ kctx->acceptor_subkey->length); ++ ++ if (write_bytes(&p, end, ++ kctx->acceptor_subkey->contents, ++ kctx->acceptor_subkey->length)) ++ goto out_err; ++ } else { ++ if (WRITE_BYTES(&p, end, kctx->enc->enctype)) ++ goto out_err; ++ printerr(2, "serialize_krb5_ctx: serializing key " ++ "with enctype %d and size %d\n", ++ kctx->enc->enctype, kctx->enc->length); ++ ++ if (write_bytes(&p, end, kctx->enc->contents, ++ kctx->enc->length)) ++ goto out_err; ++ } ++ break; ++ default: ++ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption " ++ "algorithm %d\n", kctx->enc->enctype); + goto out_err; +- if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; +- if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; +- if (endtime) +- *endtime = kctx->endtime; +- word_seq_send = kctx->seq_send; +- if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; +- if (write_oid(&p, end, kctx->mech_used)) goto out_err; +- +- printerr(2, "serialize_krb5_ctx: serializing keys with " +- "enctype %d and length %d\n", +- kctx->enc->enctype, kctx->enc->length); +- +- if (write_keyblock(&p, end, kctx->enc)) goto out_err; +- if (write_keyblock(&p, end, kctx->seq)) goto out_err; ++ } + + buf->length = p - (char *)buf->value; + return 0; ++ + out_err: + printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); +- if (buf->value) free(buf->value); ++ if (buf->value) { ++ free(buf->value); ++ } ++ buf->value = NULL; + buf->length = 0; + return -1; + } +diff -up nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.2/utils/gssd/gssd_proc.c +--- nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/gssd_proc.c 2010-05-06 07:16:37.986992000 -0400 +@@ -600,6 +600,67 @@ update_client_list(void) + return retval; + } + ++/* Encryption types supported by the kernel rpcsec_gss code */ ++int num_krb5_enctypes = 0; ++krb5_enctype *krb5_enctypes = NULL; ++ ++/* ++ * Parse the supported encryption type information ++ */ ++static int ++parse_enctypes(char *enctypes) ++{ ++ int n = 0; ++ char *curr, *comma; ++ int i; ++ static char *cached_types; ++ ++ if (cached_types && strcmp(cached_types, enctypes) == 0) ++ return 0; ++ free(cached_types); ++ ++ if (krb5_enctypes != NULL) { ++ free(krb5_enctypes); ++ krb5_enctypes = NULL; ++ num_krb5_enctypes = 0; ++ } ++ ++ /* count the number of commas */ ++ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { ++ comma = strchr(curr, ','); ++ if (comma != NULL) ++ n++; ++ else ++ break; ++ } ++ /* If no more commas and we're not at the end, there's one more value */ ++ if (*curr != '\0') ++ n++; ++ ++ /* Empty string, return an error */ ++ if (n == 0) ++ return ENOENT; ++ ++ /* Allocate space for enctypes array */ ++ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { ++ return ENOMEM; ++ } ++ ++ /* Now parse each value into the array */ ++ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { ++ krb5_enctypes[i++] = atoi(curr); ++ comma = strchr(curr, ','); ++ if (comma == NULL) ++ break; ++ } ++ ++ num_krb5_enctypes = n; ++ if ((cached_types = malloc(strlen(enctypes)+1))) ++ strcpy(cached_types, enctypes); ++ ++ return 0; ++} ++ + static int + do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + gss_buffer_desc *context_token) +@@ -1133,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp + char *mech = NULL; + char *target = NULL; + char *service = NULL; ++ char *enctypes = NULL; + + printerr(1, "handling gssd upcall (%s)\n", clp->dirname); + +@@ -1176,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp + goto out; + } + ++ /* read supported encryption types if supplied */ ++ if ((p = strstr(lbuf, "enctypes=")) != NULL) { ++ enctypes = malloc(lbuflen); ++ if (!enctypes) ++ goto out; ++ if (sscanf(p, "enctypes=%s", enctypes) != 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse target name " ++ "in upcall string '%s'\n", lbuf); ++ goto out; ++ } ++ if (parse_enctypes(enctypes) != 0) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "parsing encryption types failed: errno %d\n", errno); ++ } ++ } ++ + /* read target name */ + if ((p = strstr(lbuf, "target=")) != NULL) { + target = malloc(lbuflen); +@@ -1222,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp + out: + free(lbuf); + free(mech); ++ free(enctypes); + free(target); + free(service); + return; +diff -up nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig nfs-utils-1.2.2/utils/gssd/krb5_util.c +--- nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/gssd/krb5_util.c 2010-05-06 07:16:37.992992000 -0400 +@@ -292,61 +292,6 @@ gssd_find_existing_krb5_ccache(uid_t uid + return err; + } + +- +-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +-/* +- * this routine obtains a credentials handle via gss_acquire_cred() +- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption +- * types negotiated. +- * +- * XXX Should call some function to determine the enctypes supported +- * by the kernel. (Only need to do that once!) +- * +- * Returns: +- * 0 => all went well +- * -1 => there was an error +- */ +- +-int +-limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) +-{ +- u_int maj_stat, min_stat; +- gss_cred_id_t credh; +- gss_OID_set_desc desired_mechs; +- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, +- ENCTYPE_DES_CBC_MD5, +- ENCTYPE_DES_CBC_MD4 }; +- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); +- +- /* We only care about getting a krb5 cred */ +- desired_mechs.count = 1; +- desired_mechs.elements = &krb5oid; +- +- maj_stat = gss_acquire_cred(&min_stat, NULL, 0, +- &desired_mechs, GSS_C_INITIATE, +- &credh, NULL, NULL); +- +- if (maj_stat != GSS_S_COMPLETE) { +- if (get_verbosity() > 0) +- pgsserr("gss_acquire_cred", +- maj_stat, min_stat, &krb5oid); +- return -1; +- } +- +- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, +- num_enctypes, &enctypes); +- if (maj_stat != GSS_S_COMPLETE) { +- pgsserr("gss_set_allowable_enctypes", +- maj_stat, min_stat, &krb5oid); +- gss_release_cred(&min_stat, &credh); +- return -1; +- } +- sec->cred = credh; +- +- return 0; +-} +-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +- + /* + * Obtain credentials via a key in the keytab given + * a keytab handle and a gssd_k5_kt_princ structure. +@@ -1304,3 +1249,68 @@ gssd_k5_get_default_realm(char **def_rea + + krb5_free_context(context); + } ++ ++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES ++/* ++ * this routine obtains a credentials handle via gss_acquire_cred() ++ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption ++ * types negotiated. ++ * ++ * XXX Should call some function to determine the enctypes supported ++ * by the kernel. (Only need to do that once!) ++ * ++ * Returns: ++ * 0 => all went well ++ * -1 => there was an error ++ */ ++ ++int ++limit_krb5_enctypes(struct rpc_gss_sec *sec, 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]); ++ extern int num_krb5_enctypes; ++ extern krb5_enctype *krb5_enctypes; ++ ++ /* We only care about getting a krb5 cred */ ++ desired_mechs.count = 1; ++ desired_mechs.elements = &krb5oid; ++ ++ maj_stat = gss_acquire_cred(&min_stat, NULL, 0, ++ &desired_mechs, GSS_C_INITIATE, ++ &credh, NULL, NULL); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ if (get_verbosity() > 0) ++ pgsserr("gss_acquire_cred", ++ maj_stat, min_stat, &krb5oid); ++ return -1; ++ } ++ ++ /* ++ * If we failed for any reason to produce global ++ * list of supported enctypes, use local default here. ++ */ ++ if (krb5_enctypes == NULL) ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_enctypes, enctypes); ++ else ++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh, ++ &krb5oid, num_krb5_enctypes, krb5_enctypes); ++ ++ if (maj_stat != GSS_S_COMPLETE) { ++ pgsserr("gss_set_allowable_enctypes", ++ maj_stat, min_stat, &krb5oid); ++ gss_release_cred(&min_stat, &credh); ++ return -1; ++ } ++ sec->cred = credh; ++ ++ return 0; ++} ++#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ +diff -up nfs-utils-1.2.2/utils/mountd/auth.c.orig nfs-utils-1.2.2/utils/mountd/auth.c +--- nfs-utils-1.2.2/utils/mountd/auth.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/auth.c 2010-05-06 07:16:38.009992000 -0400 +@@ -142,7 +142,7 @@ auth_authenticate_newcache(char *what, s + return NULL; + + my_client.m_naddr = 1; +- my_client.m_addrlist[0] = caller->sin_addr; ++ set_addrlist_in(&my_client, 0, caller); + my_exp.m_client = &my_client; + + exp = NULL; +diff -up nfs-utils-1.2.2/utils/mountd/cache.c.orig nfs-utils-1.2.2/utils/mountd/cache.c +--- nfs-utils-1.2.2/utils/mountd/cache.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mountd/cache.c 2010-05-06 07:16:38.014995000 -0400 +@@ -125,7 +125,7 @@ void auth_unix_gid(FILE *f) + * 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 +136,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 +153,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; im_client, 0); + int err; + FILE *f; + +@@ -871,7 +872,7 @@ int cache_export(nfs_export *exp, char * + return -1; + + qword_print(f, "nfsd"); +- qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); ++ qword_print(f, inet_ntoa(sin->sin_addr)); + qword_printint(f, time(0)+30*60); + qword_print(f, exp->m_client->m_hostname); + err = qword_eol(f); +diff -up nfs-utils-1.2.2/utils/mount/network.c.orig nfs-utils-1.2.2/utils/mount/network.c +--- nfs-utils-1.2.2/utils/mount/network.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/network.c 2010-05-06 07:16:37.998992000 -0400 +@@ -857,7 +857,14 @@ int nfs_advise_umount(const struct socka + return 0; + } + +- client->cl_auth = authunix_create_default(); ++ client->cl_auth = nfs_authsys_create(); ++ if (client->cl_auth == NULL) { ++ if (verbose) ++ nfs_error(_("%s: Failed to create RPC auth handle"), ++ progname); ++ CLNT_DESTROY(client); ++ return 0; ++ } + + res = CLNT_CALL(client, MOUNTPROC_UMNT, + (xdrproc_t)xdr_dirpath, (caddr_t)argp, +@@ -957,8 +964,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_se + } + if (clnt) { + /* try to mount hostname:dirname */ +- clnt->cl_auth = authunix_create_default(); +- return clnt; ++ clnt->cl_auth = nfs_authsys_create(); ++ if (clnt->cl_auth) ++ return clnt; ++ CLNT_DESTROY(clnt); + } + return NULL; + } +diff -up nfs-utils-1.2.2/utils/mount/stropts.c.orig nfs-utils-1.2.2/utils/mount/stropts.c +--- nfs-utils-1.2.2/utils/mount/stropts.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/mount/stropts.c 2010-05-06 07:16:38.004992000 -0400 +@@ -799,6 +799,7 @@ static int nfs_is_permanent_error(int er + case ESTALE: + case ETIMEDOUT: + case ECONNREFUSED: ++ case EHOSTUNREACH: + return 0; /* temporary */ + default: + return 1; /* permanent */ +diff -up nfs-utils-1.2.2/utils/showmount/showmount.c.orig nfs-utils-1.2.2/utils/showmount/showmount.c +--- nfs-utils-1.2.2/utils/showmount/showmount.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/showmount/showmount.c 2010-05-06 07:16:38.019995000 -0400 +@@ -194,7 +194,13 @@ int main(int argc, char **argv) + } + + mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); +- mclient->cl_auth = authunix_create_default(); ++ mclient->cl_auth = nfs_authsys_create(); ++ if (mclient->cl_auth == NULL) { ++ fprintf(stderr, "%s: unable to create RPC auth handle.\n", ++ program_name); ++ clnt_destroy(mclient); ++ exit(1); ++ } + total_timeout.tv_sec = TOTAL_TIMEOUT; + total_timeout.tv_usec = 0; + +diff -up nfs-utils-1.2.2/utils/statd/sm-notify.c.orig nfs-utils-1.2.2/utils/statd/sm-notify.c +--- nfs-utils-1.2.2/utils/statd/sm-notify.c.orig 2010-02-18 07:35:00.000000000 -0500 ++++ nfs-utils-1.2.2/utils/statd/sm-notify.c 2010-05-06 07:16:38.025992000 -0400 +@@ -54,7 +54,7 @@ struct nsm_host { + uint32_t xid; + }; + +-static char nsm_hostname[256]; ++static char nsm_hostname[SM_MAXSTRLEN + 1]; + static int nsm_state; + static int nsm_family = AF_INET; + static int opt_debug = 0; +@@ -412,12 +412,33 @@ usage: fprintf(stderr, + } + } + +- if (opt_srcaddr) { +- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); +- } else +- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { +- xlog(L_ERROR, "Failed to obtain name of local host: %m"); +- exit(1); ++ if (opt_srcaddr != NULL) { ++ struct addrinfo *ai = NULL; ++ struct addrinfo hint = { ++ .ai_family = AF_UNSPEC, ++ .ai_flags = AI_NUMERICHOST, ++ }; ++ ++ if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) ++ /* not a presentation address - use it */ ++ strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); ++ else { ++ /* was a presentation address - look it up in ++ * /etc/hosts, so it can be used for my_name */ ++ int error; ++ ++ freeaddrinfo(ai); ++ hint.ai_flags = AI_CANONNAME; ++ error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); ++ if (error != 0) { ++ xlog(L_ERROR, "Bind address %s is unusable: %s", ++ opt_srcaddr, gai_strerror(error)); ++ exit(1); ++ } ++ strncpy(nsm_hostname, ai->ai_canonname, ++ sizeof(nsm_hostname)); ++ freeaddrinfo(ai); ++ } + } + + (void)nsm_retire_monitored_hosts(); +@@ -535,6 +556,8 @@ notify(const int sock) + static int + notify_host(int sock, struct nsm_host *host) + { ++ const char *my_name = (opt_srcaddr != NULL ? ++ nsm_hostname : host->my_name); + struct sockaddr *sap; + socklen_t salen; + +@@ -580,8 +603,8 @@ notify_host(int sock, struct nsm_host *h + host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); + else + host->xid = nsm_xmit_notify(sock, sap, salen, +- SM_PROG, nsm_hostname, nsm_state); +- ++ SM_PROG, my_name, nsm_state); ++ + return 0; + } + +@@ -611,15 +634,28 @@ recv_rpcbind_reply(struct sockaddr *sap, + } + + /* +- * Successful NOTIFY call. Server returns void, so nothing +- * we need to do here. ++ * Successful NOTIFY call. Server returns void. ++ * ++ * Try sending another SM_NOTIFY with an unqualified "my_name" ++ * argument. Reuse the port number. If "my_name" is already ++ * unqualified, we're done. + */ + static void + recv_notify_reply(struct nsm_host *host) + { +- xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ char *dot = strchr(host->my_name, '.'); + +- smn_forget_host(host); ++ if (dot != NULL) { ++ *dot = '\0'; ++ host->send_next = time(NULL); ++ host->xid = 0; ++ if (host->timeout >= NSM_MAX_TIMEOUT / 4) ++ host->timeout = NSM_MAX_TIMEOUT / 4; ++ insert_host(host); ++ } else { ++ xlog(D_GENERAL, "Host %s notified successfully", host->name); ++ smn_forget_host(host); ++ } + } + + /* +diff -up nfs-utils-1.2.2/utils/statd/sm-notify.man.orig nfs-utils-1.2.2/utils/statd/sm-notify.man +--- nfs-utils-1.2.2/utils/statd/sm-notify.man.orig 2010-05-06 07:15:49.867180000 -0400 ++++ nfs-utils-1.2.2/utils/statd/sm-notify.man 2010-05-06 07:16:38.030995000 -0400 +@@ -97,11 +97,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command normally sends + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY r + If this option is not specified, + .B sm-notify + uses a wildcard address as the transport bind address, +-and uses the results of +-.BR gethostname (3) +-as the ++and uses the ++.I my_name ++recorded when the remote was monitored as the + .I mon_name +-argument. ++argument when sending SM_NOTIFY requests. + .IP + The + .I ipaddr + form can be expressed as either an IPv4 or an IPv6 presentation address. ++If the ++.I ipaddr ++form is used, the ++.B sm-notify ++command converts this address to a hostname for use as the ++.I mon_name ++argument when sending SM_NOTIFY requests. + .IP + This option can be useful in multi-homed configurations where + the remote requires notification from a specific network address. +@@ -252,13 +257,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. +diff -up nfs-utils-1.2.2/utils/statd/statd.man.orig nfs-utils-1.2.2/utils/statd/statd.man +--- nfs-utils-1.2.2/utils/statd/statd.man.orig 2010-05-06 07:15:49.874177000 -0400 ++++ nfs-utils-1.2.2/utils/statd/statd.man 2010-05-06 07:16:38.036992000 -0400 +@@ -100,11 +100,9 @@ It uses the + string as the destination. + To identify which host has rebooted, the + .B sm-notify +-command normally sends the results of +-.BR gethostname (3) +-as the ++command sends the + .I my_name +-string. ++string recorded when that remote was monitored. + The remote + .B rpc.statd + matches incoming SM_NOTIFY requests using this string, +@@ -292,7 +290,6 @@ man pages. + .SH ADDITIONAL NOTES + Lock recovery after a reboot is critical to maintaining data integrity + and preventing unnecessary application hangs. +-.PP + To help + .B rpc.statd + match SM_NOTIFY requests to NLM requests, a number of best practices +@@ -309,13 +306,6 @@ consistent + The hostname the client uses to mount the server should match the server's + .I mon_name + in SM_NOTIFY requests it sends +-.IP +-The use of network addresses as a +-.I mon_name +-or a +-.I my_name +-string should be avoided when +-interoperating with non-Linux NFS implementations. + .PP + Unmounting an NFS file system does not necessarily stop + either the NFS client or server from monitoring each other. diff --git a/nfs-utils.spec b/nfs-utils.spec index 0a87513..6b1e642 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://sourceforge.net/projects/nfs Version: 1.2.2 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 1 # group all 32bit related archs @@ -18,8 +18,7 @@ Source13: rpcgssd.init Source14: rpcsvcgssd.init Source15: nfs.sysconfig -Patch001: nfs-utils-1-2-3-rc1.patch -Patch002: nfs-utils-1-2-3-rc2.patch +Patch001: nfs-utils-1-2-3-rc3.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.2-statdpath.patch @@ -73,7 +72,6 @@ This package also contains the mount.nfs and umount.nfs program. %setup -q %patch001 -p1 -%patch002 -p1 %patch100 -p1 %patch101 -p1 @@ -253,6 +251,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Thu May 6 2010 Steve Dickson 1.2.2-4 +- Update to upstream RC release: nfs-utils-1-2-3-rc3 + * Fri Apr 16 2010 Steve Dickson 1.2.2-3 - Update to upstream RC release: nfs-utils-1-2-3-rc2