]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
rpc.gssd: close upcall pipe on POLLHUP
authorChuck Lever <chuck.lever@oracle.com>
Thu, 19 Jul 2012 13:40:30 +0000 (09:40 -0400)
committerSteve Dickson <steved@redhat.com>
Thu, 19 Jul 2012 13:40:30 +0000 (09:40 -0400)
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 <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
utils/gssd/gssd.h
utils/gssd/gssd_main_loop.c
utils/gssd/gssd_proc.c

index 28a8206d95210190368e79a6acee70e8aa5a45d3..71a140bee534b10aac3fcec342a39cfc08c38184 100644 (file)
@@ -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;
 };
 
index cec09ea5b218fa6d586f5d5798e0280d4dde4125..c18e12c37c60cba220424c47ee1cafb0e8adb00c 100644 (file)
@@ -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;
index aa394359e27398fd3dfe25462ca2be177b7088d4..2861d060f0b97f35e714537f6f45dd80c879a274 100644 (file)
@@ -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);