From: Chuck Lever Date: Thu, 19 Jul 2012 13:40:30 +0000 (-0400) Subject: rpc.gssd: close upcall pipe on POLLHUP X-Git-Tag: debian/1%1.2.8-1~11^2^2~103 X-Git-Url: https://git.decadent.org.uk/gitweb/?a=commitdiff_plain;h=4dac21d1e8ff31c3b01f7f29e7cf877e89f09eaa;p=nfs-utils.git rpc.gssd: close upcall pipe on POLLHUP When a POLLHUP event is received on a pipe file descriptor, that means the other side has closed its end of the pipe. If the receiver does not close its end of the pipe, the pipe is left in an open-but-unlinked state. For a "gssd" upcall pipe, the kernel may close its end, removing the directory entry for it, and then later create a fresh pipe named "gssd" in the same directory. In this case, rpc.gssd continues to listen on the open-but-unlinked previous "gssd" pipe. Thus upcalls on the new "gssd" pipe are left unanswered. In addition, poll(2) continues to return POLLHUP on the old pipe. Since there is no logic to close the pipe in rpc.gssd, poll(2) always returns immediately, and rpc.gssd goes into a tight loop. Typically, the kernel closes upcall pipes and destroys their parent directory at the same time. When an RPC client's directory vanishes, rpc.gssd sees the change via dnotify and eventually invokes destroy_client() which closes the user-space end of the pipes. However, if the kernel wants to switch authentication flavors (say from AUTH_KRB5 to AUTH_UNIX) on an RPC client without destroying it, the upcall pipes go away, but the RPC client's directory remains. rpc.gssd invokes update_client_list(), but that logic never closes upcall pipes if the client directory is still in place. After a POLLHUP on a pipe, close it when rpc.gssd reconstructs its list of upcall clients. Signed-off-by: Chuck Lever Signed-off-by: Steve Dickson --- diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h index 28a8206..71a140b 100644 --- a/utils/gssd/gssd.h +++ b/utils/gssd/gssd.h @@ -81,8 +81,10 @@ struct clnt_info { char *protocol; int krb5_fd; int krb5_poll_index; + int krb5_close_me; int gssd_fd; int gssd_poll_index; + int gssd_close_me; struct sockaddr_storage addr; }; diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c index cec09ea..c18e12c 100644 --- a/utils/gssd/gssd_main_loop.c +++ b/utils/gssd/gssd_main_loop.c @@ -78,8 +78,10 @@ scan_poll_results(int ret) { i = clp->gssd_poll_index; if (i >= 0 && pollarray[i].revents) { - if (pollarray[i].revents & POLLHUP) + if (pollarray[i].revents & POLLHUP) { + clp->gssd_close_me = 1; dir_changed = 1; + } if (pollarray[i].revents & POLLIN) handle_gssd_upcall(clp); pollarray[clp->gssd_poll_index].revents = 0; @@ -89,8 +91,10 @@ scan_poll_results(int ret) } i = clp->krb5_poll_index; if (i >= 0 && pollarray[i].revents) { - if (pollarray[i].revents & POLLHUP) + if (pollarray[i].revents & POLLHUP) { + clp->krb5_close_me = 1; dir_changed = 1; + } if (pollarray[i].revents & POLLIN) handle_krb5_upcall(clp); pollarray[clp->krb5_poll_index].revents = 0; diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index aa39435..2861d06 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -340,6 +340,25 @@ process_clnt_dir_files(struct clnt_info * clp) char gname[PATH_MAX]; char info_file_name[PATH_MAX]; + if (clp->gssd_close_me) { + printerr(2, "Closing 'gssd' pipe for %s\n", clp->dirname); + close(clp->gssd_fd); + memset(&pollarray[clp->gssd_poll_index], 0, + sizeof(struct pollfd)); + clp->gssd_fd = -1; + clp->gssd_poll_index = -1; + clp->gssd_close_me = 0; + } + if (clp->krb5_close_me) { + printerr(2, "Closing 'krb5' pipe for %s\n", clp->dirname); + close(clp->krb5_fd); + memset(&pollarray[clp->krb5_poll_index], 0, + sizeof(struct pollfd)); + clp->krb5_fd = -1; + clp->krb5_poll_index = -1; + clp->krb5_close_me = 0; + } + if (clp->gssd_fd == -1) { snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); clp->gssd_fd = open(gname, O_RDWR);