From: Trond Myklebust RPCSEC_GSS: Make the upcalls detect if the userland daemon dies while processing a request. --- include/linux/sunrpc/rpc_pipe_fs.h | 4 +- net/sunrpc/auth_gss/auth_gss.c | 28 +++++++++++++++ net/sunrpc/rpc_pipe.c | 67 +++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 21 deletions(-) diff -puN include/linux/sunrpc/rpc_pipe_fs.h~nfs-03-pipe_close include/linux/sunrpc/rpc_pipe_fs.h --- 25/include/linux/sunrpc/rpc_pipe_fs.h~nfs-03-pipe_close 2004-01-09 22:16:10.000000000 -0800 +++ 25-akpm/include/linux/sunrpc/rpc_pipe_fs.h 2004-01-09 22:16:10.000000000 -0800 @@ -14,6 +14,7 @@ struct rpc_pipe_msg { struct rpc_pipe_ops { ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t); ssize_t (*downcall)(struct file *, const char __user *, size_t); + void (*release_pipe)(struct inode *); void (*destroy_msg)(struct rpc_pipe_msg *); }; @@ -21,8 +22,10 @@ struct rpc_inode { struct inode vfs_inode; void *private; struct list_head pipe; + struct list_head in_upcall; int pipelen; int nreaders; + int nwriters; wait_queue_head_t waitq; #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; @@ -36,7 +39,6 @@ RPC_I(struct inode *inode) return container_of(inode, struct rpc_inode, vfs_inode); } -extern void rpc_inode_setowner(struct inode *, void *); extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); diff -puN net/sunrpc/auth_gss/auth_gss.c~nfs-03-pipe_close net/sunrpc/auth_gss/auth_gss.c --- 25/net/sunrpc/auth_gss/auth_gss.c~nfs-03-pipe_close 2004-01-09 22:16:10.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/auth_gss.c 2004-01-09 22:16:10.000000000 -0800 @@ -482,6 +482,33 @@ err: return err; } +static void +gss_pipe_release(struct inode *inode) +{ + struct rpc_inode *rpci = RPC_I(inode); + struct rpc_clnt *clnt; + struct rpc_auth *auth; + struct gss_auth *gss_auth; + + clnt = rpci->private; + auth = clnt->cl_auth; + gss_auth = container_of(auth, struct gss_auth, rpc_auth); + spin_lock(&gss_auth->lock); + while (!list_empty(&gss_auth->upcalls)) { + struct gss_upcall_msg *gss_msg; + + gss_msg = list_entry(gss_auth->upcalls.next, + struct gss_upcall_msg, list); + gss_msg->msg.errno = -EPIPE; + atomic_inc(&gss_msg->count); + __gss_unhash_msg(gss_msg); + spin_unlock(&gss_auth->lock); + gss_release_msg(gss_msg); + spin_lock(&gss_auth->lock); + } + spin_unlock(&gss_auth->lock); +} + void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) { @@ -774,6 +801,7 @@ static struct rpc_pipe_ops gss_upcall_op .upcall = gss_pipe_upcall, .downcall = gss_pipe_downcall, .destroy_msg = gss_pipe_destroy_msg, + .release_pipe = gss_pipe_release, }; /* diff -puN net/sunrpc/rpc_pipe.c~nfs-03-pipe_close net/sunrpc/rpc_pipe.c --- 25/net/sunrpc/rpc_pipe.c~nfs-03-pipe_close 2004-01-09 22:16:10.000000000 -0800 +++ 25-akpm/net/sunrpc/rpc_pipe.c 2004-01-09 22:16:10.000000000 -0800 @@ -50,18 +50,16 @@ __rpc_purge_upcall(struct inode *inode, msg->errno = err; rpci->ops->destroy_msg(msg); } + while (!list_empty(&rpci->in_upcall)) { + msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); + list_del_init(&msg->list); + msg->errno = err; + rpci->ops->destroy_msg(msg); + } rpci->pipelen = 0; wake_up(&rpci->waitq); } -void -rpc_purge_upcall(struct inode *inode, int err) -{ - down(&inode->i_sem); - __rpc_purge_upcall(inode, err); - up(&inode->i_sem); -} - static void rpc_timeout_upcall_queue(void *data) { @@ -97,20 +95,31 @@ rpc_queue_upcall(struct inode *inode, st return res; } -void -rpc_inode_setowner(struct inode *inode, void *private) +static void +rpc_close_pipes(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); cancel_delayed_work(&rpci->queue_timeout); flush_scheduled_work(); down(&inode->i_sem); - rpci->private = private; - if (!private) + if (rpci->ops != NULL) { + rpci->nreaders = 0; __rpc_purge_upcall(inode, -EPIPE); + rpci->nwriters = 0; + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); + rpci->ops = NULL; + } up(&inode->i_sem); } +static inline void +rpc_inode_setowner(struct inode *inode, void *private) +{ + RPC_I(inode)->private = private; +} + static struct inode * rpc_alloc_inode(struct super_block *sb) { @@ -134,9 +143,11 @@ rpc_pipe_open(struct inode *inode, struc int res = -ENXIO; down(&inode->i_sem); - if (rpci->private != NULL) { + if (rpci->ops != NULL) { if (filp->f_mode & FMODE_READ) rpci->nreaders ++; + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters ++; res = 0; } up(&inode->i_sem); @@ -149,16 +160,24 @@ rpc_pipe_release(struct inode *inode, st struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); struct rpc_pipe_msg *msg; + down(&inode->i_sem); + if (rpci->ops == NULL) + goto out; msg = (struct rpc_pipe_msg *)filp->private_data; if (msg != NULL) { msg->errno = -EPIPE; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } - down(&inode->i_sem); + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters --; if (filp->f_mode & FMODE_READ) rpci->nreaders --; if (!rpci->nreaders) __rpc_purge_upcall(inode, -EPIPE); + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); +out: up(&inode->i_sem); return 0; } @@ -172,7 +191,7 @@ rpc_pipe_read(struct file *filp, char __ int res = 0; down(&inode->i_sem); - if (!rpci->private) { + if (rpci->ops == NULL) { res = -EPIPE; goto out_unlock; } @@ -182,7 +201,7 @@ rpc_pipe_read(struct file *filp, char __ msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); - list_del_init(&msg->list); + list_move(&msg->list, &rpci->in_upcall); rpci->pipelen -= msg->len; filp->private_data = msg; msg->copied = 0; @@ -194,6 +213,7 @@ rpc_pipe_read(struct file *filp, char __ res = rpci->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } out_unlock: @@ -210,7 +230,7 @@ rpc_pipe_write(struct file *filp, const down(&inode->i_sem); res = -EPIPE; - if (rpci->private != NULL) + if (rpci->ops != NULL) res = rpci->ops->downcall(filp, buf, len); up(&inode->i_sem); return res; @@ -226,7 +246,7 @@ rpc_pipe_poll(struct file *filp, struct poll_wait(filp, &rpci->waitq, wait); mask = POLLOUT | POLLWRNORM; - if (rpci->private == NULL) + if (rpci->ops == NULL) mask |= POLLERR | POLLHUP; if (!list_empty(&rpci->pipe)) mask |= POLLIN | POLLRDNORM; @@ -242,7 +262,7 @@ rpc_pipe_ioctl(struct inode *ino, struct switch (cmd) { case FIONREAD: - if (!rpci->private) + if (rpci->ops == NULL) return -EPIPE; len = rpci->pipelen; if (filp->private_data) { @@ -484,6 +504,7 @@ repeat: do { dentry = dvec[--n]; if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); simple_unlink(dir, dentry); } @@ -563,7 +584,10 @@ __rpc_rmdir(struct inode *dir, struct de int error; shrink_dcache_parent(dentry); - rpc_inode_setowner(dentry->d_inode, NULL); + if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); + rpc_inode_setowner(dentry->d_inode, NULL); + } if ((error = simple_rmdir(dir, dentry)) != 0) return error; if (!error) { @@ -715,6 +739,7 @@ rpc_unlink(char *path) } d_drop(dentry); if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); error = simple_unlink(dir, dentry); } @@ -790,6 +815,8 @@ init_once(void * foo, kmem_cache_t * cac inode_init_once(&rpci->vfs_inode); rpci->private = NULL; rpci->nreaders = 0; + rpci->nwriters = 0; + INIT_LIST_HEAD(&rpci->in_upcall); INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); _