Forced Unmount patch

"Villalovos, John L" <[email protected]> Mon, 18 Oct 2004 11:37:34 -0700
Newsgroups gmane.linux.kernel.carrier-grade,gmane.spam.detected
Message-ID <60C14C611F1DDD4198D53F2F43D8CA3B0252BDFE@orsmsx410>
Enclosed please find a patch for Forced Unmount.  This patch was
originally written by Monta Vista for the 2.4.x kernel.  It was then
ported to 2.6.8 by Atul Sabharwal at Intel Corporation.

This is Atul's patch.  I will be sending a clean up of the patch, that I
am working on, in the next couple of days.

John

_______________________________________________
cgl_discussion mailing list
[email protected]
http://lists.osdl.org/mailman/listinfo/cgl_discussion
ForcedUnmount.txt (text/plain, 3.7 KB)
Forced Unmount of a File System
By Atul Sabharwal
Aug/17/04
Forced Unmount as the name suggests gives an override to remove a file system
when a regular unmount operation would fail. It is a managed implementation for
"surprise removal" with no data loss. Currently, this can be achieved by
killing the applications which have open files, file maps, locks in the file
system. This brings us to the question as why do we need this feature. The
answer being that there are scenarios where the file maps, locks, files can be
released without having to kill the applications.  The applications can
gracefully recover from this operation by closing the file and polling for the
file system to get re-mounted.


Algorithm for Forced Unmount:
Forced unmount starts by marking the super block to be undergoing a forced
unmount operation. It then marks all the files open in the super block to be
undergoing a forced unmount.  These two operations prevent all system calls
except close to fail with an error –ENXIO. The typical operations are file
operations like read, write, seek, fcntl, ioctl etc. All file system lookup and
traversal operations also fail and return NULL. It then steps through the
address maps for the files and does an unmap operation thereby releasing the
page tables for the open files. Next, it removes the file locks and wakes up
all processes waiting on the lock. It also deletes the lock. After that, it
does a fput operation on the file.  After that, open files are cloned so that a
flush operation can be done on the cloned files. Once the cloned files are
closed, all data gets written to the storage media and the open files are
marked as detached. This is to note that we do a special close when the apps do
a close system call.  Also, the working directories of all processes inside the
forced unmounted file system are changed to NULL. The apps can recover from
this by doing the system call getcwd (). After that, a mntput operation is done
on the file system to bring the usage count to two.


Usage Scenarios:
Telecommunication devices like bladed system viz. ATCA need to hot swap blades
for reasons like blade overheat blade upgrade etc. On RAID systems, disks need
to be upgraded. For file system administration, file systems need to be
unmounted. For all these operations, a managed "surprise removal" is useful.

Advantages of Forced Unmount:
On hot plug enabled like ATCA systems, a user can pull a blade out without
having to reboot the system or kill applications. When the blade is plugged
back in, applications can proceed normally as before.  Also, on regular
systems, when a file system gets busy, the system administrator does not have
to reboot the system.  A forced unmount would suffice.

Disadvantages of Forced Unmount:
When a program has its binary in the forced unmounted file system, the
application would terminate with a segmentation fault as the code pages would
get removed from the virtual memory map. When the file system comes back alive,
these apps would have to be manually restarted.  Also, the root file system
cannot be force unmounted. Also, forced unmount works on a single file system
i.e. if there are child mounts inside a file system, forced unmount would fail.

File System Candidates for Forced Unmount:
On a typical UNIX system, /var, /tmp, /home, /opt would be good candidates to
perform a forced unmount operation.

Summary:
Forced unmount is a useful feature for system administrators. It implements
"surprise removal" which requires minimal system administration as well as does
not require a system reboot. Also, if the applications are designed to continue
without their data files, they need not be terminated.
ForcedUnmount.pdf (application/octet-stream, 27.3 KB) - not displayed
2.6.8.fumount.patch (application/octet-stream, 48.6 KB)
diff -Nur linux-2.6.8.1/fs/dcache.c linux-2.6.8.1.org/fs/dcache.c
--- linux-2.6.8.1/fs/dcache.c	2004-08-14 03:54:50.000000000 -0700
+++ linux-2.6.8.1.org/fs/dcache.c	2004-08-19 06:52:36.736223296 -0700
@@ -950,6 +950,10 @@
 	struct dentry * dentry = NULL;
 	unsigned long seq;
 
+        #ifdef CONFIG_FUMOUNT 
+        if ( parent == NULL )
+            return dentry;
+        #endif
         do {
                 seq = read_seqbegin(&rename_lock);
                 dentry = __d_lookup(parent, name);
@@ -1285,6 +1289,15 @@
 	char * retval;
 	int namelen;
 
+        #ifdef CONFIG_FUMOUNT
+	if ( !dentry || !vfsmnt ) {
+        	buflen -= 6;
+		end -= 6;
+		memcpy(end, "(null)", 6);
+		retval = end;
+		return retval;
+	}
+        #endif /*CONFIG_FUMOUNT*/
 	*--end = '\0';
 	buflen--;
 	if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
@@ -1397,6 +1410,19 @@
 	pwdmnt = mntget(current->fs->pwdmnt);
 	pwd = dget(current->fs->pwd);
 	rootmnt = mntget(current->fs->rootmnt);
+        #ifdef CONFIG_FUMOUNT
+        if(pwdmnt == NULL) {
+           char root_dir[] = "/\0";
+	   if (copy_to_user(buf, root_dir, 2))
+		error = -EFAULT;
+           else {
+                size = 2;
+                error = 0;
+           }
+	   read_unlock(&current->fs->lock);
+           goto out_fumount;
+        }
+        #endif
 	root = dget(current->fs->root);
 	read_unlock(&current->fs->lock);
 
@@ -1429,6 +1455,9 @@
 	mntput(pwdmnt);
 	dput(root);
 	mntput(rootmnt);
+#ifdef CONFIG_FUMOUNT
+out_fumount:
+#endif
 	free_page((unsigned long) page);
 	return error;
 }
diff -Nur linux-2.6.8.1/fs/dnotify.c linux-2.6.8.1.org/fs/dnotify.c
--- linux-2.6.8.1/fs/dnotify.c	2004-08-14 03:55:10.000000000 -0700
+++ linux-2.6.8.1.org/fs/dnotify.c	2004-08-19 06:52:43.391211584 -0700
@@ -36,6 +36,34 @@
 	inode->i_dnotify_mask = new_mask;
 }
 
+#ifdef CONFIG_FUMOUNT
+void fumount_dnotify_flush(struct file *filp)
+{
+	struct dnotify_struct *dn;
+	struct dnotify_struct **prev;
+	struct inode *inode;
+
+	if ( (filp) && (filp->f_dentry)
+	   && (( inode = filp->f_dentry->d_inode))
+	   ) {
+		if (!S_ISDIR(inode->i_mode))
+			return;
+	        spin_lock(&inode->i_lock);
+		prev = &inode->i_dnotify;
+		while ((dn = *prev) != NULL) {
+			if ( dn->dn_filp == filp ) {
+				*prev = dn->dn_next;
+				redo_inode_mask(inode);
+				kmem_cache_free(dn_cache, dn);
+				break;
+			}
+			prev = &dn->dn_next;
+		}
+	spin_unlock(&inode->i_lock);
+	}
+}
+#endif /*CONFIG_FUMOUNT*/
+
 void dnotify_flush(struct file *filp, fl_owner_t id)
 {
 	struct dnotify_struct *dn;
@@ -160,16 +188,22 @@
 	if (!dir_notify_enable)
 		return;
 
-	spin_lock(&dentry->d_lock);
-	parent = dentry->d_parent;
-	if (parent->d_inode->i_dnotify_mask & event) {
+        #ifdef CONFIG_FUMOUNT
+	if (dentry) {
+        #endif /*CONFIG_FUMOUNT*/
+	    spin_lock(&dentry->d_lock);
+	    parent = dentry->d_parent;
+	    if (parent->d_inode->i_dnotify_mask & event) {
 		dget(parent);
 		spin_unlock(&dentry->d_lock);
 		__inode_dir_notify(parent->d_inode, event);
 		dput(parent);
-	} else {
+	    } else {
 		spin_unlock(&dentry->d_lock);
+	    }
+        #ifdef CONFIG_FUMOUNT
 	}
+        #endif /*CONFIG_FUMOUNT*/
 }
 EXPORT_SYMBOL_GPL(dnotify_parent);
 
diff -Nur linux-2.6.8.1/fs/fcntl.c linux-2.6.8.1.org/fs/fcntl.c
--- linux-2.6.8.1/fs/fcntl.c	2004-08-14 03:55:35.000000000 -0700
+++ linux-2.6.8.1.org/fs/fcntl.c	2004-08-19 06:52:50.990056384 -0700
@@ -154,9 +154,20 @@
 	struct file * file, *tofree;
 	struct files_struct * files = current->files;
 
+        #ifdef CONFIG_FUMOUNT
+	/* this is a backdoor to close, so we need the close semaphore */
+	down(&close_sem);
+        #endif /*CONFIG_FUMOUNT*/
 	spin_lock(&files->file_lock);
 	if (!(file = fcheck(oldfd)))
 		goto out_unlock;
+        #ifdef CONFIG_FUMOUNT
+	if ( file->f_mode & FMODE_FUMOUNT ) {
+		/* allow no new references to this file */
+		err = -ENXIO;
+		goto out_unlock;
+	}
+        #endif /*CONFIG_FUMOUNT*/
 	err = newfd;
 	if (newfd == oldfd)
 		goto out_unlock;
@@ -191,6 +202,9 @@
 		filp_close(tofree, files);
 	err = newfd;
 out:
+        #ifdef CONFIG_FUMOUNT
+	up(&close_sem);
+        #endif /*CONFIG_FUMOUNT*/
 	return err;
 out_unlock:
 	spin_unlock(&files->file_lock);
diff -Nur linux-2.6.8.1/fs/file_table.c linux-2.6.8.1.org/fs/file_table.c
--- linux-2.6.8.1/fs/file_table.c	2004-08-14 03:54:48.000000000 -0700
+++ linux-2.6.8.1.org/fs/file_table.c	2004-08-19 06:52:59.474766512 -0700
@@ -17,11 +17,22 @@
 #include <linux/mount.h>
 #include <linux/cdev.h>
 
+#ifdef CONFIG_FUMOUNT
+#include <linux/errno.h>
+extern int remove_file_mappings(struct file *);
+extern void remove_file_locks(struct file *);
+static struct file * clone_filp(struct file * source_file);
+#endif /*CONFIG_FUMOUNT*/
+
 /* sysctl tunables... */
 struct files_stat_struct files_stat = {
 	.max_files = NR_FILE
 };
 
+#ifdef CONFIG_FUMOUNT
+/* ditto for the close semaphore */
+DECLARE_MUTEX(close_sem);
+#endif /*CONFIG_FUMOUNT*/
 EXPORT_SYMBOL(files_stat); /* Needed by unix.o */
 
 /* public *and* exported. Not pretty! */
@@ -106,6 +117,49 @@
 
 EXPORT_SYMBOL(get_empty_filp);
 
+#ifdef CONFIG_FUMOUNT
+/* Find an unused file structure and clone the existing file.
+ * Returns NULL, if there are no more free file structures or
+ * we run out of memory.
+ */
+static struct file * clone_filp(struct file * source_file)
+{
+	struct file * new_file;
+
+        if(files_stat.nr_free_files > NR_RESERVED_FILES) {
+        new_one :
+           new_file = get_empty_filp();
+           if( new_file) {
+                files_stat.nr_free_files--;
+                /* Copy all file stats, flags etc. */
+                new_file->f_version      = source_file->f_version;
+                new_file->f_dentry       = source_file->f_dentry;
+                new_file->f_vfsmnt       = source_file->f_vfsmnt;
+                new_file->f_op           = source_file->f_op;
+                new_file->f_flags        = source_file->f_flags;
+                new_file->f_mode         = source_file->f_mode;
+                new_file->f_pos          = source_file->f_pos;
+                memcpy( &new_file->f_ra, &source_file->f_ra, sizeof (struct file_ra_state));
+                new_file->f_uid          = source_file->f_uid;
+                new_file->f_gid          = source_file->f_gid;
+                new_file->f_error        = source_file->f_error;
+                new_file->private_data   = source_file->private_data;
+		return new_file;
+	   }
+        }
+        /* Use a reserved one if super user */
+        if(files_stat.nr_free_files && !current->euid )
+          goto new_one;
+
+        /* Allocate a new one if below limit */
+        if(files_stat.nr_free_files < files_stat.max_files ) 
+          goto new_one;
+        
+	printk(KERN_WARNING "VFS: filp allocation failed\n");
+	return NULL;
+}
+#endif /*CONFIG_FUMOUNT*/
+
 /*
  * Clear and initialize a (private) struct file for the given dentry,
  * allocate the security structure, and call the open function (if any).  
@@ -192,6 +246,17 @@
 	mntput(mnt);
 }
 
+#ifdef CONFIG_FUMOUNT
+void fumount_fput(struct file * file)
+{
+        /* fput has already been called on this file. */
+	if (atomic_dec_and_test(&file->f_count)) {
+	        files_stat.nr_files++;
+        }
+        return;
+}
+#endif /*CONFIG_FUMOUNT*/
+
 struct file fastcall *fget(unsigned int fd)
 {
 	struct file *file;
@@ -199,8 +264,20 @@
 
 	spin_lock(&files->file_lock);
 	file = fcheck_files(files, fd);
+        #ifdef CONFIG_FUMOUNT
 	if (file)
-		get_file(file);
+        {
+		if (file->f_mode & FMODE_FUMOUNT) {
+			file = (struct file *)NULL;
+		}
+		else {
+			get_file(file);
+		}
+        }
+        #else /*!CONFIG_FUMOUNT*/
+	if (file)
+ 		get_file(file);
+        #endif /*CONFIG_FUMOUNT */
 	spin_unlock(&files->file_lock);
 	return file;
 }
@@ -222,13 +299,26 @@
 	*fput_needed = 0;
 	if (likely((atomic_read(&files->count) == 1))) {
 		file = fcheck_files(files, fd);
+                #ifdef CONFIG_FUMOUNT
+	        if (file) {
+	        	if (file->f_mode & FMODE_FUMOUNT) {
+	         		file = (struct file *)NULL;
+		        }
+                }
+        	#endif /*CONFIG_FUMOUNT */
 	} else {
 		spin_lock(&files->file_lock);
 		file = fcheck_files(files, fd);
-		if (file) {
+	        if (file) {
+                #ifdef CONFIG_FUMOUNT
+	        	if (file->f_mode & FMODE_FUMOUNT) {
+	         		file = (struct file *)NULL;
+		        }
+                #else /*!CONFIG_FUMOUNT*/
 			get_file(file);
 			*fput_needed = 1;
-		}
+        	#endif /*CONFIG_FUMOUNT */
+                }
 		spin_unlock(&files->file_lock);
 	}
 	return file;
@@ -255,6 +345,30 @@
 	file_list_unlock();
 }
 
+#ifdef CONFIG_FUMOUNT
+/* file_move_test is same as file_move, but is used to complete open
+   operations under the lock only if MS_FUMOUNT is not set.
+   This makes sure that additional file objects are not placed on the
+   sb open file list when a FORCED umount is pending.  */
+
+int file_move_test(struct file *file, struct super_block *sb)
+{
+        int return_code = -ENXIO;
+        struct list_head *list = &(sb->s_files);
+
+	if (list) {
+		file_list_lock();
+		if(!(sb->s_flags & MS_FUMOUNT)) {
+	                list_move(&file->f_list, list);
+			return_code = 0;
+		}
+		file_list_unlock();
+	}
+	return return_code;
+}
+
+#endif /*CONFIG_FUMOUNT*/
+
 void file_kill(struct file *file)
 {
 	if (!list_empty(&file->f_list)) {
@@ -289,6 +403,159 @@
 	return 0;
 }
 
+#ifdef CONFIG_FUMOUNT
+
+void fs_fumount_mark_files(struct super_block *sb)
+{
+ 	struct list_head *p;
+	lock_kernel(); /* get this lock - prevents problems with sys_flock */
+	/* Mark all files on the sb->s_files list for unmount */
+	for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+		struct file *file = list_entry(p, struct file, f_list);
+		file->f_mode |= FMODE_FUMOUNT;
+	}
+	unlock_kernel();
+	return; 
+}
+
+/* I've added a lock that will prevent the fumount code
+from coliding with the normal syscall sys_close.  This
+seems necessary, as I'm about to clone the file object
+for open files and try
+to force a close - that can be tricky, as the
+close code wants to run in the context of the
+process that originally opened the file, and
+there may also be more than one owner of the file object
+at any given time, due to the fork and dup calls.
+
+Before cloning the file, it is necessary to unmap any areas
+that have been mmapped using this file descriptor.  Each
+mmap against a file increments the file obejct reference
+count.  So find the inode and check for mappings before the
+clone.  FMODE_FUMOUNT has made the mapping unalterable
+by the actual owner, as the sys calls have been walled off.
+
+The only syscall that is allowed to succeed following the
+setting of FMODE_FUMOUNT is the close call, and that is 
+protected by the new close_sem semaphore.
+
+In any
+event, I don't want to have a file object that I'm
+forcing close on, suddenly disappear when the real owner
+gets around to closing it.  So we clone under the lock,
+moving the file resources into a cloned file object, and
+leaving the previous owner with the husk only.
+Somewhere along the line, we need to find any locks
+associated with the file object, and release them.
+
+After cloning the file object, release the lock and then
+close the cloned file object however many times required
+to drive the use f_count to 0.  I can't use the syscall,
+but it looks like most of the routines are already there,
+just needing some tweaking to take my arguments.  The
+file locking seems to be the only thing requiring the
+process context of the original owner(s). 
+*/
+
+int fs_fumount_clone_list(struct super_block *sb)
+{
+	struct list_head *p;
+	struct file *file;
+	int return_code = 0;
+	struct file *cloned_file;
+
+	down(&close_sem);
+	for (p = sb->s_files.next; p != &sb->s_files; ) {
+		file = list_entry(p, struct file, f_list);
+		
+		/* check for mmappings and undo, if any */
+		get_file(file); /* get reference count so file doesn't vanish */
+		up(&close_sem); /* drop lock to let sys_close progress - I have
+				   the file reference to hold the object until
+				   I'm done removing the mmaps */
+		return_code = remove_file_mappings(file);
+
+		/* Similarly, remove the file locks associated with this file
+		   object.
+		*/
+		remove_file_locks(file);
+
+		down(&close_sem);
+		if ( file_count(file) == 1) {
+			/* okay, fumount holds last reference, so file will
+			   go away when we fput the file, removing it from the sb
+			   list.  We hold the close semaphore, so the next list
+			   item will still be valid if we get it before this 
+			   file object is released.  And, if we are terminating
+			   the use of this file object, then there is nothing else
+			   to do for this file, so no need to clone it.
+			*/
+			p = p->next;
+			fput(file);
+			continue;
+		}
+		fput(file);
+		if ( !(cloned_file = clone_filp(file)) ) {
+			return_code = -ENOMEM;
+			break;
+		}
+
+		/* we now have a duplicated file object - 
+		 * change some of the fields to reflect that we stole the
+		 * resources from the old file object - then remove the
+		 * object owned by the other process from the sb and
+		 * place it on the  anon_list, for lack of a better 
+		 * place - when the process finally closes it, it will 
+		 * go back to the free list.  Similarly, put the cloned
+		 * object onto the sb file list to deal with later.
+		*/
+
+		file->f_op = (struct file_operations *)NULL;
+                /* Set defunct flag for cleanup with sys_close */
+		file->f_mode |= FMODE_DEFUNCT;
+		p = p->next;
+
+		/* put the clone onto the sb list for further processing */
+		cloned_file->f_mode &= ~FMODE_FUMOUNT;
+
+                /* Add clone after the  head of the sb list */
+		list_del(&cloned_file->f_list);
+		list_add(&cloned_file->f_list, &sb->s_files);
+	}
+	up(&close_sem);
+	return return_code;
+}
+
+void fs_fumount_close( struct super_block *sb)
+{
+	struct list_head *p;
+	struct file *file;
+
+	
+        file_list_lock();
+	/* the for looks like it always starts over, but the first list entry
+	is removed by the body of the loop if it is a fumount closable item */
+
+	for (p = sb->s_files.next; p != &sb->s_files; p = sb->s_files.next ) {
+		file = list_entry(p, struct file, f_list);
+		if (!( file->f_mode & FMODE_FUMOUNT )) {
+
+                        /* fumount close grabs the list lock when required */
+                        file_list_unlock();
+			fumount_close( file );
+                        file_list_lock();
+		}
+                else
+                {
+                        /* running into fumountable files */
+                        break;
+                }
+	}
+        file_list_unlock();
+	return;
+}
+#endif /*CONFIG_FUMOUNT*/
+
 void __init files_init(unsigned long mempages)
 { 
 	int n; 
diff -Nur linux-2.6.8.1/fs/ioctl.c linux-2.6.8.1.org/fs/ioctl.c
--- linux-2.6.8.1/fs/ioctl.c	2004-08-14 03:54:51.000000000 -0700
+++ linux-2.6.8.1.org/fs/ioctl.c	2004-08-19 06:53:07.182594744 -0700
@@ -67,6 +67,15 @@
                 goto out;
         }
 
+        #ifdef CONFIG_FUMOUNT
+
+	if ( filp->f_mode & FMODE_FUMOUNT ) {
+   	 	/* allow no new references to this file */
+		error = -ENXIO;
+		goto out_fput;
+	}
+
+        #endif /*CONFIG_FUMOUNT*/
 	lock_kernel();
 	switch (cmd) {
 		case FIOCLEX:
@@ -132,6 +141,9 @@
 	unlock_kernel();
 	fput(filp);
 
+#ifdef CONFIG_FUMOUNT
+out_fput:
+#endif /*CONFIG_FUMOUNT*/
 out:
 	return error;
 }
diff -Nur linux-2.6.8.1/fs/Kconfig linux-2.6.8.1.org/fs/Kconfig
--- linux-2.6.8.1/fs/Kconfig	2004-08-14 03:55:33.000000000 -0700
+++ linux-2.6.8.1.org/fs/Kconfig	2004-08-19 06:53:45.983696080 -0700
@@ -481,6 +481,14 @@
 	  local network, you probably do not need an automounter, and can say
 	  N here.
 
+config FUMOUNT
+          bool 'Forced Unmount support (EXPERIMENTAL)' 
+          help
+            This options really force unmounts file system. Its useful in the
+            case of surprise removal. It closes the open file, flushes their
+            contents, releases file locks and tears down memory maps for the
+            files. If unsure, say N.
+
 menu "CD-ROM/DVD Filesystems"
 
 config ISO9660_FS
diff -Nur linux-2.6.8.1/fs/locks.c linux-2.6.8.1.org/fs/locks.c
--- linux-2.6.8.1/fs/locks.c	2004-08-14 03:56:22.000000000 -0700
+++ linux-2.6.8.1.org/fs/locks.c	2004-08-19 06:53:16.535172936 -0700
@@ -590,6 +590,10 @@
 	int result;
 	locks_insert_block(blocker, waiter);
 	result = interruptible_sleep_on_locked(&waiter->fl_wait, time);
+        #ifdef CONFIG_FUMOUNT
+	if (waiter->fl_file->f_mode & FMODE_FUMOUNT)
+		result = -ENXIO;
+        #endif /*CONFIG_FUMOUNT*/
 	__locks_delete_block(waiter);
 	return result;
 }
@@ -718,6 +722,83 @@
 	return error;
 }
 
+#ifdef CONFIG_FUMOUNT
+/*
+ * This function is called to unblock all waiters for an inode
+ * Hold BKL before calling
+ */
+void
+locks_unblock_all (struct inode *inode_ptr)
+{
+	struct file_lock *fl;
+	struct file_lock **before;
+
+	if (!inode_ptr->i_flock)
+		return;
+
+	before = &inode_ptr->i_flock;
+
+	while ((fl = *before) != NULL) {
+		locks_wake_up_blocks(fl);
+		before = &fl->fl_next;
+	}
+}
+
+
+
+/*
+ * This function is called to remove all locks for an inode
+ * Hold BKL before calling
+ */
+void
+locks_remove_all (struct inode *inode_ptr)
+{
+	struct file_lock *fl;
+	struct file_lock **before;
+
+	if (!inode_ptr->i_flock)
+		return;
+
+	before = &inode_ptr->i_flock;
+
+	while ((fl = *before) != NULL) {
+		locks_delete_lock(before);
+	}
+}
+
+
+/* remove_file_locks is part of fumount.  This routine takes the BKL, and
+ * examines the inode for the file structure passed as the argument.  For
+ * every fl_lock on the inode list, locks_wake_up_blocks is called with 
+ * a wait = TRUE.  This unblocks all of the waiters, causing them to check
+ * for fumount as they resume execution.  The fumount check causes the lock
+ * to fail, generally with -ENXIO.
+ *
+ * Once all of the waiters have been flushed from the syscalls, a version
+ * of locks_remove_* is called for all locks on the inode.  This removes
+ * all of the outstanding file locks resulting from all file objects.
+ * At that point, the file is safe to clone for fumount closing.
+*/
+
+void
+remove_file_locks( struct file *filp )
+{
+	struct inode *inode_ptr;
+	struct dentry *dentry_ptr;
+
+	lock_kernel();
+
+	if ( (dentry_ptr = filp->f_dentry) ) {
+		if ( (inode_ptr = dentry_ptr->d_inode) ) {
+			locks_unblock_all( inode_ptr );
+			locks_remove_all( inode_ptr );
+		}
+	}
+	
+	unlock_kernel();
+}
+#endif /*CONFIG_FUMOUNT*/
+
 EXPORT_SYMBOL(posix_lock_file);
 
 static int __posix_lock_file(struct inode *inode, struct file_lock *request)
@@ -1343,7 +1424,17 @@
 		goto out_free;
 
 	for (;;) {
-		error = flock_lock_file(filp, lock);
+                #ifdef CONFIG_FUMOUNT
+	        if ( !(filp->f_mode & FMODE_FUMOUNT) ) {
+                #endif /*CONFIG_FUMOUNT*/
+		     error = flock_lock_file(filp, lock);
+                #ifdef CONFIG_FUMOUNT
+	        }
+	        else {
+	        	error = -ENXIO;
+                        break;
+	        }
+                #endif /*CONFIG_FUMOUNT*/
 		if ((error != -EAGAIN) || !can_sleep)
 			break;
 		error = wait_event_interruptible(lock->fl_wait, !lock->fl_next);
diff -Nur linux-2.6.8.1/fs/namei.c linux-2.6.8.1.org/fs/namei.c
--- linux-2.6.8.1/fs/namei.c	2004-08-14 03:55:10.000000000 -0700
+++ linux-2.6.8.1.org/fs/namei.c	2004-08-19 06:53:23.533109088 -0700
@@ -474,6 +474,13 @@
 static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
 	int err = -ELOOP;
+
+        #ifdef CONFIG_FUMOUNT
+	if ( !nd->mnt ) {
+		err = -ENXIO;
+		return err;
+	}
+        #endif /* CONFIG_FUMOUNT */
 	if (current->link_count >= MAX_NESTED_LINKS)
 		goto loop;
 	if (current->total_link_count >= 40)
@@ -533,7 +540,16 @@
 	while (d_mountpoint(*dentry)) {
 		struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
 		if (!mounted)
+                        #ifdef CONFIG_FUMOUNT
+                        res = -1;
+                        #endif
 			break;
+                #ifdef CONFIG_FUMOUNT
+		if (mounted->mnt_sb->s_flags & MS_FUMOUNT) {
+                    res = -ENXIO;
+                    break;
+                }
+                #endif
 		mntput(*mnt);
 		*mnt = mounted;
 		dput(*dentry);
@@ -555,8 +571,14 @@
 		mntput(*mnt);
 		*mnt = mounted;
 		dput(*dentry);
-		*dentry = dget(mounted->mnt_root);
-		return 1;
+                #ifdef CONFIG_FUMOUNT
+		if (!(mounted->mnt_sb->s_flags & MS_FUMOUNT) ) {
+                #endif /*CONFIG_FUMOUNT*/
+		    *dentry = dget(mounted->mnt_root);
+		     return 1;
+                #ifdef CONFIG_FUMOUNT
+		}
+                #endif /*CONFIG_FUMOUNT*/
 	}
 	return 0;
 }
@@ -618,6 +640,19 @@
 {
 	struct vfsmount *mnt = nd->mnt;
 	struct dentry *dentry = __d_lookup(nd->dentry, name);
+        #ifdef CONFIG_FUMOUNT
+        int err;
+        #endif
+
+        #ifdef CONFIG_FUMOUNT
+	if (!nd->mnt) {
+	/* Oh oh.  Walked into a pending FUMOUNT - follow_down
+	   has released parent mnt and dentry, so just bail 
+	 */
+		err = -ENXIO;
+		return err;
+	}
+        #endif /*CONFIG_FUMOUNT*/
 
 	if (!dentry)
 		goto need_lookup;
@@ -652,6 +687,18 @@
  * This is the basic name resolution function, turning a pathname
  * into the final dentry.
  *
+#ifdef CONFIG_FUMOUNT
+ *  - bad expectation, since the error returns from mntget and
+ *  - path init are not always checked.  Add check up front to
+ *  - ensure that the main routine doesn't fall off of a NULL
+ *  - mount or dentry.  If nothing else, the FUMOUNT will cause
+ *  - NULL mount pointers.  The point is for FUMOUNT to not allow
+ *  - a path lookup into a pending FUMOUNT file system.  This
+ *  - barrier prevents the reference counts from incrementing when
+ *  - FUMOUNT is trying to clean everything up.  I will also add
+ *  - similar checks whenever this routine attempts to take another
+ *  - mount structure reference.
+ #endif 
  * We expect 'base' to be positive and a directory.
  */
 int fastcall link_path_walk(const char * name, struct nameidata *nd)
@@ -661,6 +708,11 @@
 	int err;
 	unsigned int lookup_flags = nd->flags;
 	
+        #ifdef CONFIG_FUMOUNT
+	if (!nd->mnt || name == NULL ) {
+		return -ENXIO;  /* outa' here if bad init_path */
+	}
+        #endif /*CONFIG_FUMOUNT*/
 	while (*name=='/')
 		name++;
 	if (!*name)
@@ -714,6 +766,12 @@
 				if (this.name[1] != '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+                                #ifdef CONFIG_FUMOUNT
+				if ( !nd->mnt ) {
+					err = -ENXIO;
+					goto return_err;
+				}
+                                #endif /* CONFIG_FUMOUNT */
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -734,7 +792,15 @@
 		if (err)
 			break;
 		/* Check mountpoints.. */
-		follow_mount(&next.mnt, &next.dentry);
+                #ifdef CONFIG_FUMOUNT
+		if(follow_mount(&next.mnt, &next.dentry) < 0 )
+                {
+                   err = -ENXIO;
+                   break;
+                }
+                #else
+		  follow_mount(&next.mnt, &next.dentry);
+                #endif
 
 		err = -ENOENT;
 		inode = next.dentry->d_inode;
@@ -746,6 +812,13 @@
 
 		if (inode->i_op->follow_link) {
 			mntget(next.mnt);
+                        #ifdef CONFIG_FUMOUNT
+                        if ( next.mnt == NULL)
+                        {
+                           err = -ENXIO;
+			   goto return_err;
+                        }
+                        #endif
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -782,6 +855,12 @@
 				if (this.name[1] != '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+                                #ifdef CONFIG_FUMOUNT
+				if ( !nd->mnt ) {
+					err = -ENXIO;
+					goto return_err;
+				}
+                                #endif /* CONFIG_FUMOUNT */
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -795,11 +874,26 @@
 		err = do_lookup(nd, &this, &next);
 		if (err)
 			break;
-		follow_mount(&next.mnt, &next.dentry);
+                #ifdef CONFIG_FUMOUNT
+		if(follow_mount(&next.mnt, &next.dentry) < 0 )
+                {
+                   err = -ENXIO;
+                   break;
+                }
+                #else
+		  follow_mount(&next.mnt, &next.dentry);
+                #endif
 		inode = next.dentry->d_inode;
 		if ((lookup_flags & LOOKUP_FOLLOW)
 		    && inode && inode->i_op && inode->i_op->follow_link) {
 			mntget(next.mnt);
+                        #ifdef CONFIG_FUMOUNT
+                        if ( next.mnt == NULL)
+                        {
+                           err = -ENXIO;
+			   goto return_err;
+                        }
+                        #endif
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -895,6 +989,11 @@
 	return 1;
 }
 
+#ifdef CONFIG_FUMOUNT
+/* Just release old altroot and associated mount and replace with new
+   values (NULL unless __emul_prefix is non-NULL)
+*/
+#endif /*CONFIG_FUMOUNT*/
 void set_fs_altroot(void)
 {
 	char *emul = __emul_prefix();
@@ -1405,6 +1504,15 @@
 		if (flag & O_NOFOLLOW)
 			goto exit_dput;
 		while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry));
+                #ifdef CONFIG_FUMOUNT
+		if (!nd->mnt) {
+			/* Oh oh.  Walked into a pending FUMOUNT - follow_down
+			   has released parent mnt and dentry, so just bail 
+			 */
+			error = -ENXIO;
+			return error;
+		}
+                #endif /*CONFIG_FUMOUNT*/
 	}
 	error = -ENOENT;
 	if (!dentry->d_inode)
diff -Nur linux-2.6.8.1/fs/namespace.c linux-2.6.8.1.org/fs/namespace.c
--- linux-2.6.8.1/fs/namespace.c	2004-08-14 03:55:35.000000000 -0700
+++ linux-2.6.8.1.org/fs/namespace.c	2004-08-19 06:53:30.190097072 -0700
@@ -176,6 +176,10 @@
 void __mntput(struct vfsmount *mnt)
 {
 	struct super_block *sb = mnt->mnt_sb;
+        #ifdef CONFIG_FUMOUNT
+        if ( mnt == NULL)
+           return;
+        #endif
 	dput(mnt->mnt_root);
 	free_vfsmnt(mnt);
 	deactivate_super(sb);
@@ -374,13 +378,20 @@
 	if (retval)
 		return retval;
 
+        #ifdef CONFIG_FUMOUNT
+        printk(KERN_DEBUG "do_umount entered for superblock %x\n", (unsigned int)sb);
+        #endif /*CONFIG_FUMOUNT*/
 	/*
 	 * Allow userspace to request a mountpoint be expired rather than
 	 * unmounting unconditionally. Unmount only happens if:
 	 *  (1) the mark is already set (the mark is cleared by mntput())
 	 *  (2) the usage count == 1 [parent vfsmount] + 1 [sys_umount]
 	 */
+        #ifdef CONFIG_FUMOUNT
+	if (flags & MNT_EXPIRE && !(flags & MNT_FFORCE )) {
+        #else
 	if (flags & MNT_EXPIRE) {
+        #endif
 		if (mnt == current->fs->rootmnt ||
 		    flags & (MNT_FORCE | MNT_DETACH))
 			return -EINVAL;
@@ -403,8 +414,16 @@
 	 */
 
 	lock_kernel();
-	if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
+        #ifdef CONFIG_FUMOUNT
+	if( (flags&(MNT_FORCE|MNT_FFORCE)) && sb->s_op->umount_begin){
+          printk(KERN_DEBUG "calling umount_begin for superblock %x\n", (unsigned int)sb);
+        #else
+ 	if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
+        #endif /*CONFIG_FUMOUNT*/
 		sb->s_op->umount_begin(sb);
+        #ifdef CONFIG_FUMOUNT
+	}
+        #endif /*CONFIG_FUMOUNT*/
 	unlock_kernel();
 
 	/*
@@ -434,6 +453,10 @@
 	down_write(&current->namespace->sem);
 	spin_lock(&vfsmount_lock);
 
+        #ifdef CONFIG_FUMOUNT
+        umount_retry:
+        #endif /*CONFIG_FUMOUNT*/
+
 	if (atomic_read(&sb->s_active) == 1) {
 		/* last instance - try to be smart */
 		spin_unlock(&vfsmount_lock);
@@ -450,6 +473,133 @@
 			umount_tree(mnt);
 		retval = 0;
 	}
+#ifdef CONFIG_FUMOUNT
+
+	/* now for the dred FORCE unmount.  The idea here is that
+	   if this isn't the root fs, and FUMOUNT is requested, and
+	   we aren't good to go with a normal unmount, and we haven't
+	   been through here before (you only go around once!), and 
+	   there are no child mounts (if there are children, we expect
+	   the administrator to clean those up first, rather than trying
+	   to force the umount recursively - why - because this is an
+	   ugly thing to do to a running system, and I choose to make the
+	   admin know what they are doing!)  then find the references that
+	   make the mount point busy and eliminate them.
+	*/
+	if (mnt != current->fs->rootmnt 
+	    && (flags & MNT_FFORCE) 
+	    && (retval != 0) 
+	    && !(sb->s_flags & MS_FUMOUNT) 
+	    && (list_empty(&mnt->mnt_mounts)) ) {
+
+            printk(KERN_DEBUG "List empty from mount %x is %x\n", (int)mnt, list_empty(&mnt->mnt_mounts));
+		
+	   /* stop additional references to the mount by setting
+	      the MS_FUMOUNT flag in the super block and modifying
+	      fget to fail if the flag is set.  The syscalls that
+	      attack the file system via a name string generally 
+	      end up returning -EBADF.  The alternative is to allow
+	      the mount reference count to fluctuate and check after
+	      the reference, but this was rejected, since the objective
+	      is to drive the ref count to the magic number to allow
+	      unmounting.
+           */
+	      sb->s_flags |= MS_FUMOUNT;
+              printk(KERN_DEBUG "Set MS_FUMOUNT in sb->s_flags = %lx\n", sb->s_flags);
+	      /* mark the files as subject to a fumount - this prevents
+		 further syscalls from starting with the file - instead
+		 causing the sys_calls to return -ENXIO.  Hopefully,
+		 the processes will get the message, and close the files
+		 after a brief wait - note that we hold onto the mount
+		 semaphore - last thing we need is for something to 
+		 mount on the subtree while trying to clean this up.
+		 Give up dcache lock, since fs_fumount_mark_files takes
+		 BKL.  Why???
+	      */
+	      fs_fumount_mark_files(sb);
+	      spin_unlock(&vfsmount_lock);
+
+	     /* wait a bit, in hopes that the processes will take
+		their errors, close out their files (and hope
+		against hope, satify any sleeps that have occurred
+		in the vfs - that is, bd reads will complete, and
+		locks will be released.)  It would also be nice if the
+		processes would get out of related working directories,
+		but I'm dreaming.  If all that happens, then the
+		forced cleanup is easy, and probably safe.
+		NB - the really proper way to do this is to compute the
+		correct magic number for each file object - that is, search
+		the process table to find the number of opens associated with
+		the file object and wait for the file object reference count
+		to fall below this number - then everything is back out of 
+		the kernel sys_calls, deterministically.  While I'm at it, I
+		should combine this patch with Tigrans to loop through in the
+		the process context after walling out the sys_calls.  Maybe
+		next year, if there is interest.
+	      */
+              printk(KERN_DEBUG "Mount reference count = %x\n", atomic_read(&mnt->mnt_count) );
+	      current->state = TASK_INTERRUPTIBLE;
+	      schedule_timeout(5*HZ);
+		
+              printk(KERN_DEBUG "Back from delay, looking for open files\n");
+              printk(KERN_DEBUG "Mount reference count = %x\n", atomic_read(&mnt->mnt_count) );
+	      do {
+			/* clone the open list - this is in a loop,
+			   since we may run out of file objects, and
+			   the fu_mount_close() releases them back to
+			   the pool.
+			 */
+			retval = fs_fumount_clone_list(sb); 
+			fs_fumount_close(sb);
+	      } while (retval);
+              printk(KERN_DEBUG "Mount reference count = %x\n", atomic_read(&mnt->mnt_count) );
+
+	      /* Having removed all the file objects from the mount, we can
+	         then, at our leisure, it seems, go through the task list and
+	 	 remove all cwdmnt references to the mount.  This will leave
+	 	 process without a relative working directory, but it can
+	 	 recover by cd to a rooted path not on the mount.  At that 
+		 point the mount count should be at the magic number, and we 
+		 will repeat the normal umount process.  
+	       */
+
+	       if ( atomic_read(&mnt->mnt_count) > 2 ) {
+			
+			struct task_struct *task_ptr;
+
+			read_lock( &tasklist_lock );
+			for_each_process(task_ptr) {
+				if ( task_ptr->fs ) {
+					if ( task_ptr->fs->pwdmnt == mnt ) {
+						lock_kernel();
+						set_fs_pwd( task_ptr->fs, 
+						    	(struct vfsmount *)NULL,
+						    	(struct dentry *)NULL );
+						unlock_kernel();
+					}
+				}
+				if ( atomic_read(&mnt->mnt_count) == 2 )
+					break;
+			} 
+			read_unlock( &tasklist_lock );
+	       }
+               printk(KERN_DEBUG "Mount reference count = %x\n", atomic_read(&mnt->mnt_count) );
+               if ( atomic_read(&mnt->mnt_count) > 2) printk(KERN_WARNING "Losing resources!\n");
+		   while ( atomic_read(&mnt->mnt_count) > 2 ) {
+			/* Okay, can't find all of the references - just
+			   drive the count down.  This may leave dangling
+			   resources, but too bad.  We are going to 
+			   fumount!
+			*/
+			mntput(mnt);
+	       }
+               printk(KERN_DEBUG "Mount reference count = %x\n", atomic_read(&mnt->mnt_count) );
+	       spin_lock(&vfsmount_lock);
+	       goto umount_retry;
+	}
+	sb->s_flags &= ~MS_FUMOUNT;
+#endif /*CONFIG_FUMOUNT*/
+
 	spin_unlock(&vfsmount_lock);
 	if (retval)
 		security_sb_umount_busy(mnt);
@@ -470,6 +620,10 @@
 	struct nameidata nd;
 	int retval;
 
+        #ifdef CONFIG_FUMOUNT
+	printk(KERN_DEBUG "Entered sys_umount, flags = %x\n", flags);
+        #endif/*CONFIG_FUMOUNT*/
+
 	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
 	if (retval)
 		goto out;
diff -Nur linux-2.6.8.1/fs/open.c linux-2.6.8.1.org/fs/open.c
--- linux-2.6.8.1/fs/open.c	2004-08-14 03:54:48.000000000 -0700
+++ linux-2.6.8.1.org/fs/open.c	2004-08-19 06:53:38.337858424 -0700
@@ -794,7 +794,15 @@
 	f->f_vfsmnt = mnt;
 	f->f_pos = 0;
 	f->f_op = fops_get(inode->i_fop);
-	file_move(f, &inode->i_sb->s_files);
+        #ifdef CONFIG_FUMOUNT
+	error = file_move_test(f, inode->i_sb);
+	if (error) {
+                printk(KERN_DEBUG "Disallowed file open due to pending unmount\n");
+		goto cleanup_file_ops;
+	}
+        #else
+ 	file_move(f, &inode->i_sb->s_files);
+        #endif /*CONFIG_FUMOUNT*/
 
 	if (f->f_op && f->f_op->open) {
 		error = f->f_op->open(inode,f);
@@ -815,6 +823,9 @@
 
 	return f;
 
+#ifdef CONFIG_FUMOUNT
+cleanup_file_ops:
+#endif /*CONFIG_FUMOUNT*/
 cleanup_all:
 	fops_put(f->f_op);
 	if (f->f_mode & FMODE_WRITE)
@@ -1006,14 +1017,60 @@
 			retval = err;
 	}
 
-	dnotify_flush(filp, id);
-	locks_remove_posix(filp, id);
-	fput(filp);
+        #ifdef CONFIG_FUMOUNT
+	if ( !(filp->f_mode & FMODE_DEFUNCT) ) {
+
+		/* if fumount has usurped the filp, then there is nothing left
+		   for the following to cleanup, and they don't check
+		   for NULL dentry - OOPs follows.
+		*/
+
+        #endif /*CONFIG_FUMOUNT*/
+	     dnotify_flush(filp, id);
+	     locks_remove_posix(filp, id);
+	     fput(filp);
+        #ifdef CONFIG_FUMOUNT
+	}
+	else {
+                /* We have already removed locks & done a fput */
+		fumount_fput(filp);
+	}
+	#endif /*CONFIG_FUMOUNT*/
 	return retval;
 }
 
 EXPORT_SYMBOL(filp_close);
 
+#ifdef CONFIG_FUMOUNT
+/*
+ * fumount_close is similar to filp_close.  However, we don't call
+ * locks_remove_posix, since we have lost the files id.  We have
+ * previously chased the locks out of the file object so 
+ * we assume that the locks are not in effect.  We also use a special
+ * version of dnotify_flush that doesn't care about matching the 
+ * id of the caller - it just flushes everything associated with
+ * the filp.
+ */
+void fumount_close(struct file *filp)
+{
+	int retval;
+
+	if (!file_count(filp)) {
+		printk(KERN_ERR "VFS: Close: file count is 0\n");
+		return;
+	}
+	retval = 0;
+	if (filp->f_op && filp->f_op->flush) {
+		lock_kernel();
+		retval = filp->f_op->flush(filp);
+		unlock_kernel();
+	}
+	fumount_dnotify_flush(filp);
+	fput(filp);
+	return;
+}
+#endif /*CONFIG_FUMOUNT*/
+
 /*
  * Careful here! We test whether the file pointer is NULL before
  * releasing the fd. This ensures that one clone task can't release
@@ -1023,7 +1080,13 @@
 {
 	struct file * filp;
 	struct files_struct *files = current->files;
-
+        #ifdef CONFIG_FUMOUNT
+        int ret_code;
+        #endif
+
+        #ifdef CONFIG_FUMOUNT
+	down(&close_sem);
+        #endif /*CONFIG_FUMOUNT*/
 	spin_lock(&files->file_lock);
 	if (fd >= files->max_fds)
 		goto out_unlock;
@@ -1034,11 +1097,25 @@
 	FD_CLR(fd, files->close_on_exec);
 	__put_unused_fd(files, fd);
 	spin_unlock(&files->file_lock);
-	return filp_close(filp, files);
-
+        #ifdef CONFIG_FUMOUNT
+	ret_code =  filp_close(filp, files);
+        #else
+	return  filp_close(filp, files);
+        #endif
+
+#ifdef CONFIG_FUMOUNT
+exit_sys_close:
+	up(&close_sem);
+        return ret_code;
+#endif /*CONFIG_FUMOUNT*/
 out_unlock:
 	spin_unlock(&files->file_lock);
+#ifndef CONFIG_FUMOUNT
 	return -EBADF;
+#else
+	ret_code =  -EBADF;
+	goto exit_sys_close;
+#endif
 }
 
 EXPORT_SYMBOL(sys_close);
diff -Nur linux-2.6.8.1/include/linux/dnotify.h linux-2.6.8.1.org/include/linux/dnotify.h
--- linux-2.6.8.1/include/linux/dnotify.h	2004-08-14 03:54:47.000000000 -0700
+++ linux-2.6.8.1.org/include/linux/dnotify.h	2004-08-19 06:54:19.294632048 -0700
@@ -15,6 +15,9 @@
 	fl_owner_t		dn_owner;
 };
 
+#ifdef CONFIG_FUMOUNT
+extern void fumount_dnotify_flush(struct file *filp);
+#endif /*CONFIG_FUMOUNT*/
 extern void __inode_dir_notify(struct inode *, unsigned long);
 extern void dnotify_flush(struct file *filp, fl_owner_t id);
 extern int fcntl_dirnotify(int, struct file *, unsigned long);
diff -Nur linux-2.6.8.1/include/linux/file.h linux-2.6.8.1.org/include/linux/file.h
--- linux-2.6.8.1/include/linux/file.h	2004-08-14 03:56:25.000000000 -0700
+++ linux-2.6.8.1.org/include/linux/file.h	2004-08-19 06:54:27.924320136 -0700
@@ -36,6 +36,9 @@
 extern void FASTCALL(__fput(struct file *));
 extern void FASTCALL(fput(struct file *));
 
+#ifdef CONFIG_FUMOUNT
+extern void FASTCALL(fumount_fput(struct file *));
+#endif /*CONFIG_FUMOUNT*/
 static inline void fput_light(struct file *file, int fput_needed)
 {
 	if (unlikely(fput_needed))
diff -Nur linux-2.6.8.1/include/linux/fs.h linux-2.6.8.1.org/include/linux/fs.h
--- linux-2.6.8.1/include/linux/fs.h	2004-08-14 03:55:09.000000000 -0700
+++ linux-2.6.8.1.org/include/linux/fs.h	2004-08-19 06:54:34.693291096 -0700
@@ -19,6 +19,7 @@
 #include <linux/prio_tree.h>
 #include <linux/kobject.h>
 #include <asm/atomic.h>
+#include <linux/mount.h>
 
 struct iovec;
 struct nameidata;
@@ -75,6 +76,11 @@
 #define FMODE_READ 1
 #define FMODE_WRITE 2
 
+#ifdef CONFIG_FUMOUNT
+/* next two mode flags are for fumount */
+#define FMODE_FUMOUNT 4	/* fumount is forcing this file to fail */
+#define FMODE_DEFUNCT 8	/* fumount has taken the resources away from this file */
+#endif /*CONFIG_FUMOUNT*/
 /* Internal kernel extensions */
 #define FMODE_LSEEK	4
 #define FMODE_PREAD	8
@@ -119,6 +125,9 @@
 #define MS_VERBOSE	32768
 #define MS_POSIXACL	(1<<16)	/* VFS does not apply the umask */
 #define MS_ONE_SECOND	(1<<17)	/* fs has 1 sec a/m/ctime resolution */
+#ifdef CONFIG_FUMOUNT
+#define MS_FUMOUNT	(1<<29) /* Start a FORCED unmount - no more opens */
+#endif /*CONFIG_FUMOUNT*/
 #define MS_ACTIVE	(1<<30)
 #define MS_NOUSER	(1<<31)
 
@@ -589,6 +598,9 @@
 	struct address_space	*f_mapping;
 };
 extern spinlock_t files_lock;
+#ifdef CONFIG_FUMOUNT
+extern struct semaphore close_sem;
+#endif /*CONFIG_FUMOUNT*/
 #define file_list_lock() spin_lock(&files_lock);
 #define file_list_unlock() spin_unlock(&files_lock);
 
@@ -600,6 +612,10 @@
 /* Release a private file and free its security structure. */
 extern void close_private_file(struct file *file);
 
+#ifdef CONFIG_FUMOUNT
+extern void fumount_close( struct file * );
+extern void fs_fumount_close( struct super_block * );
+#endif /*CONFIG_FUMOUNT*/
 #define	MAX_NON_LFS	((1UL<<31) - 1)
 
 /* Page cache limit. The filesystems should put that into their s_maxbytes 
@@ -720,7 +736,9 @@
 #define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
 #define MNT_DETACH	0x00000002	/* Just detach from the tree */
 #define MNT_EXPIRE	0x00000004	/* Mark for expiry */
-
+#ifdef CONFIG_FUMOUNT
+#define MNT_FFORCE	0x00000008	/* Really forcibily umount - no prisoners */
+#endif /*CONFIG_FUMOUNT*/
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
 
@@ -1279,6 +1297,10 @@
 
 extern int fs_may_remount_ro(struct super_block *);
 
+#ifdef CONFIG_FUMOUNT
+extern int fs_fumount_clone_list(struct super_block *);
+extern void fs_fumount_mark_files(struct super_block *);
+#endif /*CONFIG_FUMOUNT*/
 /*
  * return READ, READA, or WRITE
  */
@@ -1392,6 +1414,9 @@
 
 extern struct file * get_empty_filp(void);
 extern void file_move(struct file *f, struct list_head *list);
+#ifdef CONFIG_FUMOUNT
+extern int file_move_test(struct file *f,  struct super_block *sb);
+#endif /*CONFIG_FUMOUNT*/
 extern void file_kill(struct file *f);
 struct bio;
 extern void submit_bio(int, struct bio *);
@@ -1570,5 +1595,24 @@
 { }
 #endif	/* CONFIG_SECURITY */
 
+#ifdef CONFIG_FUMOUNT
+/* mntget checks that the parameter is not NULL, and now checks to
+   see that the mount structure's super block is not subject to a
+   pending forced unmount.  If both checks pass, then the reference
+   count for the mount structure is atomically incremented and the
+   mount structure pointer is returned.  Otherwise, NULL is returned.
+*/
+
+static inline struct vfsmount *mntget(struct vfsmount *mnt)
+{
+	if ( (mnt) && !( mnt->mnt_sb->s_flags & MS_FUMOUNT) ){
+		atomic_inc(&mnt->mnt_count);
+	}
+	else {
+		mnt = (struct vfsmount *) NULL;
+	}
+	return mnt;
+}
+#endif /*CONFIG_FUMOUNT*/
 #endif /* __KERNEL__ */
 #endif /* _LINUX_FS_H */
diff -Nur linux-2.6.8.1/include/linux/mount.h linux-2.6.8.1.org/include/linux/mount.h
--- linux-2.6.8.1/include/linux/mount.h	2004-08-14 03:54:48.000000000 -0700
+++ linux-2.6.8.1.org/include/linux/mount.h	2004-08-19 06:54:42.248142584 -0700
@@ -36,12 +36,18 @@
 	struct namespace *mnt_namespace; /* containing namespace */
 };
 
+#ifdef CONFIG_FUMOUNT
+/*
+mntget now found in fs.h due to ordering constraints
+*/
+#else
 static inline struct vfsmount *mntget(struct vfsmount *mnt)
 {
 	if (mnt)
 		atomic_inc(&mnt->mnt_count);
 	return mnt;
 }
+#endif /*CONFIG_FUMOUNT*/
 
 extern void __mntput(struct vfsmount *mnt);
 
diff -Nur linux-2.6.8.1/mm/mmap.c linux-2.6.8.1.org/mm/mmap.c
--- linux-2.6.8.1/mm/mmap.c	2004-08-14 03:55:35.000000000 -0700
+++ linux-2.6.8.1.org/mm/mmap.c	2004-08-19 06:54:00.224531144 -0700
@@ -1606,6 +1606,185 @@
 	return ret;
 }
 
+#ifdef CONFIG_FUMOUNT
+
+/*
+ * remove_file_mappings is a back door to do_munmap when the file
+ * object is known but the context may be different from the process
+ * context that created the mapping in the first place.  Used by
+ * fumount to remove the mappings and release the associated file
+ * reference prior to forcing the file object closed.
+ */
+int remove_file_mappings( struct file *file )
+{
+	struct mm_struct *mm_ptr;
+	struct vm_area_struct *vma_ptr;
+	struct address_space *addr_space_ptr;
+        struct  tagqueue
+        {
+          struct prio_tree_node * data;
+          struct tagqueue       *next;
+        } *queue, *tmpptr;
+        struct list_head *ptr;
+        struct prio_tree_node * tree_ptr;
+        int ret_code;
+
+        printk(KERN_DEBUG "Remove_file_mappings called.\n");
+	if (!file ) 
+              return -EBADF;
+	addr_space_ptr = file->f_mapping;
+	if (!addr_space_ptr) 
+              return -EBADF;
+	/*
+	 * Each time a mapping is found that matches the file object,
+	 * we get the mm_struct associated with the mapping, lock
+	 * the mm_struct by incrementing the mm_count.  Then drop the
+	 * inode address space spinlock and take the mmap_sem semaphore.
+	 * Then search the vma list for the mm space, and remove all 
+	 * mappings associated with the file.  This avoids having to
+	 * search all of the process mms for file matches, while still
+	 * appearing to be safe.  If the process terminates, then the 
+	 * vma list will be empty by the time I acquire the mm semaphore,
+	 * since I added code in exit_mmap to take the semaphore before
+	 * stealing all of the vmas.  It is held until all of the vmas
+         * are released, so finding an empty vma area means that the 
+	 * file references have been removed, which is the point of this
+	 * whole exercise.
+	 * Once done, we drop the mmap_sem and mm_count and restart our search.
+	 * We are only done with the
+	 * mappings for a given file when we traverse both the map lists
+	 * without working on a mapping for a particular file object.
+	 */
+	spin_lock(&addr_space_ptr->i_mmap_lock);
+	if ( prio_tree_empty(&addr_space_ptr->i_mmap) && 
+                !list_empty(&addr_space_ptr->i_mmap_nonlinear) ) {
+		spin_unlock(&addr_space_ptr->i_mmap_lock);
+                printk(KERN_DEBUG "no more mappings\n");
+		return 0;
+	}
+next_shared_mapping:
+        queue = kmalloc(sizeof(struct tagqueue), GFP_USER);
+        queue->data = addr_space_ptr->i_mmap.prio_tree_node;
+        queue->next = NULL;
+        tree_ptr = queue->data;
+	do 
+        {
+           vma_ptr =  prio_tree_entry(tree_ptr, struct vm_area_struct,  shared); 
+           if( ! prio_tree_left_empty(tree_ptr))
+           {
+               tmpptr = kmalloc(sizeof(struct tagqueue), GFP_USER);
+               tmpptr->data = tree_ptr->left;
+               tmpptr->next = NULL;
+               queue->next = tmpptr;
+           }
+           if( ! prio_tree_right_empty( tree_ptr ))
+           {
+               tmpptr = kmalloc(sizeof(struct tagqueue), GFP_USER);
+               tmpptr->data = tree_ptr->right;
+               tmpptr->next = NULL;
+               queue->next = tmpptr;
+           }
+           tmpptr = queue;
+           queue = queue->next;
+           if(queue != NULL)
+               tree_ptr = queue->data;
+           kfree(tmpptr);
+
+        }while(( queue != NULL ) && ( vma_ptr->vm_file != file ));
+
+        //empty the queue
+        while(queue!=NULL)
+        {
+             tmpptr= queue->next;
+             kfree(queue);
+             queue = tmpptr;
+        }
+
+	if ( vma_ptr->vm_file == file ) {
+		printk(KERN_DEBUG "found shared map\n");
+		mm_ptr = vma_ptr->vm_mm;
+		if ( mm_ptr ) {
+			struct vm_area_struct * next_vma_ptr;
+			atomic_inc(&mm_ptr->mm_count);
+			down_write(&mm_ptr->mmap_sem);
+	                spin_unlock(&addr_space_ptr->i_mmap_lock);
+
+			for( vma_ptr = mm_ptr->mmap; vma_ptr; vma_ptr = next_vma_ptr) {
+				next_vma_ptr = vma_ptr->vm_next;
+				if ( vma_ptr->vm_file == file ) {
+					if (( ret_code = do_munmap(mm_ptr, vma_ptr->vm_start,
+						       (size_t)(vma_ptr->vm_end - vma_ptr->vm_start)))) {
+                                                      /* Low memory condition. Retry built in *
+                                                       * caller                               */
+			                              atomic_dec(&mm_ptr->mm_count);
+			                              up_write(&mm_ptr->mmap_sem);
+                                                      //empty the queue
+                                                      while(queue!=NULL)
+                                                      {
+                                                          tmpptr = queue->next;
+                                                          queue->next = NULL;
+                                                          kfree(queue);
+                                                          queue = tmpptr;
+                                                      }
+                                                      return ret_code;
+
+					}
+				}
+			}	
+			
+	                spin_lock(&addr_space_ptr->i_mmap_lock);
+			up_write(&mm_ptr->mmap_sem);
+			mmput(mm_ptr);
+		}
+	        if ( !prio_tree_empty(&addr_space_ptr->i_mmap) )
+                      goto next_shared_mapping;
+	}
+
+next_nonlinear_mapping:
+        ptr = addr_space_ptr->i_mmap_nonlinear.next;
+	do 
+        {
+             vma_ptr = list_entry( ptr, struct vm_area_struct, anon_vma_node);
+             ptr = ptr->next;
+        }while( ptr!= &addr_space_ptr->i_mmap_nonlinear && 
+               (vma_ptr->vm_file != file ));
+
+	if ( vma_ptr->vm_file == file ) {
+		printk(KERN_DEBUG "found anon map\n");
+		mm_ptr = vma_ptr->vm_mm;
+		if ( mm_ptr ) {
+			struct vm_area_struct * next_vma_ptr;
+			atomic_inc(&mm_ptr->mm_count);
+			down_write(&mm_ptr->mmap_sem);
+	                spin_unlock(&addr_space_ptr->i_mmap_lock);
+
+			for( vma_ptr = mm_ptr->mmap; vma_ptr; vma_ptr = next_vma_ptr) {
+				next_vma_ptr = vma_ptr->vm_next;
+				if ( vma_ptr->vm_file == file ) {
+					if (( ret_code = do_munmap(mm_ptr, vma_ptr->vm_start,
+						       (size_t)(vma_ptr->vm_end - vma_ptr->vm_start)))) {
+                                                      /* Low memory condition. Retry built in *
+                                                       * caller                               */
+			                              atomic_dec(&mm_ptr->mm_count);
+			                              up_write(&mm_ptr->mmap_sem);
+                                                      return ret_code;
+					}
+				}
+			}	
+			
+	                spin_lock(&addr_space_ptr->i_mmap_lock);
+			up_write(&mm_ptr->mmap_sem);
+			mmput(mm_ptr);
+		}
+                if(!list_empty( &addr_space_ptr->i_mmap_nonlinear ))
+                     goto next_nonlinear_mapping;
+	}
+	spin_unlock(&addr_space_ptr->i_mmap_lock);
+
+	return 0;
+}
+#endif /*CONFIG_FUMOUNT*/
+
 /*
  *  this is really a simplified "do_mmap".  it only handles
  *  anonymous maps.  eventually we may be able to do some