--- linux-2.6.19-gentoo-r5/include/linux/init_task.h	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/include/linux/init_task.h	2007-08-12 03:51:34.000000000 +0200
@@ -126,6 +126,7 @@
 	.thread		= INIT_THREAD,					\
 	.fs		= &init_fs,					\
 	.files		= &init_files,					\
+	.required_fds	= LIST_HEAD_INIT(tsk.required_fds),		\
 	.signal		= &init_signals,				\
 	.sighand	= &init_sighand,				\
 	.nsproxy	= &init_nsproxy,				\
--- linux-2.6.19-gentoo-r5/include/linux/sched.h	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/include/linux/sched.h	2007-08-12 13:47:44.000000000 +0200
@@ -907,6 +907,8 @@
 	struct fs_struct *fs;
 /* open file information */
 	struct files_struct *files;
+/* file descriptors required to complete current I/O operation successfully */
+	struct list_head required_fds;
 /* namespaces */
 	struct nsproxy *nsproxy;
 /* signal handlers */
@@ -930,7 +932,7 @@
 /* Thread group tracking */
    	u32 parent_exec_id;
    	u32 self_exec_id;
-/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
+/* Protection of (de-)allocation: mm, files, required_fds, fs, tty, keyrings */
 	spinlock_t alloc_lock;
 
 	/* Protection of the PI data structures: */
@@ -1025,6 +1027,13 @@
 #endif
 };
 
+#define REQUIRED_FD_INIT(fd) { .fd = fd }
+
+struct required_fd {
+	struct list_head list;
+	int fd;
+};
+
 static inline pid_t process_group(struct task_struct *tsk)
 {
 	return tsk->signal->pgrp;
@@ -1429,7 +1438,7 @@
 		(thread_group_leader(p) && !thread_group_empty(p))
 
 /*
- * Protects ->fs, ->files, ->mm, ->group_info, ->comm, keyring
+ * Protects ->fs, ->files, ->mm, ->group_info, ->comm, ->required_fds, keyring
  * subscriptions and synchronises with wait4().  Also used in procfs.  Also
  * pins the final release of task.io_context.  Also protects ->cpuset.
  *
--- linux-2.6.19-gentoo-r5/include/linux/fs.h	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/include/linux/fs.h	2007-08-12 13:27:02.000000000 +0200
@@ -1120,6 +1120,7 @@
 	int (*mmap) (struct file *, struct vm_area_struct *);
 	int (*open) (struct inode *, struct file *);
 	int (*flush) (struct file *, fl_owner_t id);
+	void (*closing_fd) (struct inode *, struct file *, struct files_struct *files, unsigned int fd);
 	int (*release) (struct inode *, struct file *);
 	int (*fsync) (struct file *, struct dentry *, int datasync);
 	int (*aio_fsync) (struct kiocb *, int datasync);
@@ -1497,6 +1498,10 @@
 			int mode);
 extern struct file *filp_open(const char *, int, int);
 extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
+extern int required_fds_are_bad(struct task_struct *task);
+extern void add_required_fd(struct task_struct *task,
+			    struct required_fd *req_fd);
+extern void del_required_fds(struct task_struct *task);
 extern int filp_close(struct file *, fl_owner_t id);
 extern char * getname(const char __user *);
 
--- linux-2.6.19-gentoo-r5/kernel/fork.c	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/kernel/fork.c	2007-08-12 13:12:59.000000000 +0200
@@ -1156,6 +1156,7 @@
 	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
 	if (retval)
 		goto bad_fork_cleanup_namespaces;
+	INIT_LIST_HEAD(&p->required_fds);
 
 	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
 	/*
--- linux-2.6.19-gentoo-r5/fs/read_write.c	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/fs/read_write.c	2007-08-12 13:19:59.000000000 +0200
@@ -355,10 +355,12 @@
 
 asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
 {
+	struct required_fd req_fd = REQUIRED_FD_INIT(fd);
 	struct file *file;
 	ssize_t ret = -EBADF;
 	int fput_needed;
 
+	add_required_fd(current, &req_fd);
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -366,6 +368,7 @@
 		file_pos_write(file, pos);
 		fput_light(file, fput_needed);
 	}
+	del_required_fds(current);
 
 	return ret;
 }
@@ -373,10 +376,12 @@
 
 asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
 {
+	struct required_fd req_fd = REQUIRED_FD_INIT(fd);
 	struct file *file;
 	ssize_t ret = -EBADF;
 	int fput_needed;
 
+	add_required_fd(current, &req_fd);
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
@@ -384,6 +389,7 @@
 		file_pos_write(file, pos);
 		fput_light(file, fput_needed);
 	}
+	del_required_fds(current);
 
 	return ret;
 }
--- linux-2.6.19-gentoo-r5/fs/open.c	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/fs/open.c	2007-08-12 16:30:03.000000000 +0200
@@ -2,6 +2,9 @@
  *  linux/fs/open.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  2007-08-12 Implemented required_fds functionality to close files
+ *             reliably with pthreads, by Fredrik Noring <noring@nocrew.org>.
  */
 
 #include <linux/string.h>
@@ -1015,6 +1018,53 @@
 
 #endif
 
+void mark_required_fd_bad(struct task_struct *task, unsigned int fd)
+{
+	struct required_fd *req_fd;
+
+	task_lock(task);
+	list_for_each_entry(req_fd, &task->required_fds, list)
+		if (req_fd->fd == fd)
+			req_fd->fd = -EBADF;
+	task_unlock(task);
+}
+
+int required_fds_are_bad(struct task_struct *task)
+{
+	struct required_fd *req_fd;
+	int ret = 0;
+
+	task_lock(task);
+	list_for_each_entry(req_fd, &task->required_fds, list)
+		if (req_fd->fd == -EBADF) {
+			ret = 1;
+			break;
+		}
+	task_unlock(task);
+	
+	return ret;
+}
+
+EXPORT_SYMBOL(required_fds_are_bad);
+
+void add_required_fd(struct task_struct *task, struct required_fd *req_fd)
+{
+	task_lock(task);
+	list_add(&req_fd->list, &task->required_fds);
+	task_unlock(task);
+}
+
+EXPORT_SYMBOL(add_required_fd);
+
+void del_required_fds(struct task_struct *task)
+{
+	task_lock(task);
+	INIT_LIST_HEAD(&task->required_fds);
+	task_unlock(task);
+}
+
+EXPORT_SYMBOL(del_required_fds);
+
 /*
  * "id" is the POSIX thread ID. We use the
  * files pointer for this..
@@ -1048,9 +1098,17 @@
 {
 	struct file * filp;
 	struct files_struct *files = current->files;
+	struct task_struct *task;
 	struct fdtable *fdt;
 	int retval;
 
+	/* FIXME: Proof of concept but obviously inefficient. Mark
+	 * within f_op->closing_fd instead, where we know the tasks
+	 * that are really interested in learning this? */
+	for_each_process(task)
+		if(task->files == files)
+			mark_required_fd_bad(task, fd);
+	
 	spin_lock(&files->file_lock);
 	fdt = files_fdtable(files);
 	if (fd >= fdt->max_fds)
@@ -1062,6 +1120,10 @@
 	FD_CLR(fd, fdt->close_on_exec);
 	__put_unused_fd(files, fd);
 	spin_unlock(&files->file_lock);
+	if (filp->f_op && filp->f_op->closing_fd) {
+		struct inode *inode = filp->f_dentry->d_inode;
+		filp->f_op->closing_fd(inode, filp, files, fd);
+	}
 	retval = filp_close(filp, files);
 
 	/* can't restart close syscall because file table entry was cleared */
--- linux-2.6.19-gentoo-r5/fs/pipe.c	2007-07-12 22:03:14.000000000 +0200
+++ linux-2.6.19-required-fds/fs/pipe.c	2007-08-12 13:43:13.000000000 +0200
@@ -316,6 +316,11 @@
 			wake_up_interruptible_sync(&pipe->wait);
  			kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
 		}
+		if (required_fds_are_bad(current)) {
+			if (!ret)
+				ret = -EBADF;
+			break;
+		}
 		pipe_wait(pipe);
 	}
 	mutex_unlock(&inode->i_mutex);
@@ -488,6 +493,11 @@
 			kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 			do_wakeup = 0;
 		}
+		if (required_fds_are_bad(current)) {
+			if (!ret)
+				ret = -EBADF;
+			break;
+		}
 		pipe->waiting_writers++;
 		pipe_wait(pipe);
 		pipe->waiting_writers--;
@@ -576,6 +586,18 @@
 	return mask;
 }
 
+static void
+pipe_closing_fd(struct inode *inode, struct file *file,
+		struct files_struct *files, unsigned int fd)
+{
+	struct pipe_inode_info *pipe;
+
+	mutex_lock(&inode->i_mutex);
+	pipe = inode->i_pipe;
+	wake_up_interruptible(&pipe->wait);
+	mutex_unlock(&inode->i_mutex);
+}
+
 static int
 pipe_release(struct inode *inode, int decr, int decw)
 {
@@ -727,6 +749,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_read_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_read_release,
 	.fasync		= pipe_read_fasync,
 };
@@ -739,6 +762,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_write_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_write_release,
 	.fasync		= pipe_write_fasync,
 };
@@ -752,6 +776,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_rdwr_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_rdwr_release,
 	.fasync		= pipe_rdwr_fasync,
 };
@@ -764,6 +789,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_read_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_read_release,
 	.fasync		= pipe_read_fasync,
 };
@@ -776,6 +802,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_write_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_write_release,
 	.fasync		= pipe_write_fasync,
 };
@@ -789,6 +816,7 @@
 	.poll		= pipe_poll,
 	.ioctl		= pipe_ioctl,
 	.open		= pipe_rdwr_open,
+	.closing_fd	= pipe_closing_fd,
 	.release	= pipe_rdwr_release,
 	.fasync		= pipe_rdwr_fasync,
 };
