From: Trond Myklebust <trond.myklebust@fys.uio.no>

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);

_