Updated Forced unmount

"Villalovos, John L" <[email protected]> Wed, 27 Oct 2004 16:37:58 -0700
Newsgroups gmane.linux.kernel.carrier-grade,gmane.spam.detected
Message-ID <60C14C611F1DDD4198D53F2F43D8CA3B026EB61A@orsmsx410>
Here is my update of Atul's port to the 2.6.8 kernel.

This is working on my system but I would love for people to look at it
and give me feedback.

I realize that it has sections which are big and ugly and I am hoping to
fix that.

Comments on its actual operation would be appreciated.  Bug reports and
the like.

I have debug output enabled in this patch so it will spew out a little
bit of information.  Not all that much though.

Also included is a patch to the umount userspace utils.  This patch
works against the util-linux package that comes with Fedora Core 2.

John

_______________________________________________
cgl_discussion mailing list
[email protected]
http://lists.osdl.org/mailman/listinfo/cgl_discussion
forced-umount-2.6.8.1-2004-10-27.patch (application/octet-stream, 46.9 KB)
Index: fs/Kconfig
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/Kconfig,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- fs/Kconfig	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/Kconfig	27 Sep 2004 19:53:54 -0000	1.2
@@ -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
Index: fs/dcache.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/dcache.c,v
retrieving revision 1.1.1.1
retrieving revision 1.12
diff -u -r1.1.1.1 -r1.12
--- fs/dcache.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/dcache.c	20 Oct 2004 00:48:43 -0000	1.12
@@ -950,6 +950,11 @@
 	struct dentry * dentry = NULL;
 	unsigned long seq;
 
+	if (FUMOUNT && parent == NULL)
+		{
+			DEBUG_FUMOUNT;
+			return dentry;
+		}
         do {
                 seq = read_seqbegin(&rename_lock);
                 dentry = __d_lookup(parent, name);
@@ -1287,6 +1292,14 @@
 
 	*--end = '\0';
 	buflen--;
+	if (FUMOUNT && (!dentry || !vfsmnt)) {
+		DEBUG_FUMOUNT;
+		buflen -= 6;
+		end -= 6;
+		memcpy(end, "(null)", 6);
+		retval = end;
+		return retval;
+	}
 	if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
 		buflen -= 10;
 		end -= 10;
@@ -1395,6 +1408,17 @@
 
 	read_lock(&current->fs->lock);
 	pwdmnt = mntget(current->fs->pwdmnt);
+	if (FUMOUNT && pwdmnt == NULL) {
+		DEBUG_FUMOUNT;
+		unsigned long len = 2;
+		char * root_dir = "/";
+		if (copy_to_user(buf, root_dir, len))
+			error = -EFAULT;
+		else
+			error = len;
+		read_unlock(&current->fs->lock);
+		goto out_freepage;
+	}
 	pwd = dget(current->fs->pwd);
 	rootmnt = mntget(current->fs->rootmnt);
 	root = dget(current->fs->root);
@@ -1429,6 +1453,7 @@
 	mntput(pwdmnt);
 	dput(root);
 	mntput(rootmnt);
+out_freepage:
 	free_page((unsigned long) page);
 	return error;
 }
Index: fs/dnotify.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/dnotify.c,v
retrieving revision 1.1.1.1
retrieving revision 1.16
diff -u -r1.1.1.1 -r1.16
--- fs/dnotify.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/dnotify.c	20 Oct 2004 01:21:26 -0000	1.16
@@ -36,6 +36,39 @@
 	inode->i_dnotify_mask = new_mask;
 }
 
+void fumount_dnotify_flush(struct file *filp)
+{
+	struct dnotify_struct *dn;
+	struct dnotify_struct **prev;
+	struct inode *inode;
+
+	if (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	if (!filp || !filp->f_dentry || !filp->f_dentry->d_inode)
+		return;
+
+	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);
+}
+
 void dnotify_flush(struct file *filp, fl_owner_t id)
 {
 	struct dnotify_struct *dn;
@@ -160,16 +193,20 @@
 	if (!dir_notify_enable)
 		return;
 
-	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 {
-		spin_unlock(&dentry->d_lock);
-	}
+	/* Skip this if compiled with forced unmount and the dentry is NULL */
+	if (!FUMOUNT || dentry) {
+		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 {
+			spin_unlock(&dentry->d_lock);
+		}
+	} else
+		DEBUG_FUMOUNT;
 }
 EXPORT_SYMBOL_GPL(dnotify_parent);
 
Index: fs/fcntl.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/fcntl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.8
diff -u -r1.1.1.1 -r1.8
--- fs/fcntl.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/fcntl.c	20 Oct 2004 02:44:44 -0000	1.8
@@ -154,9 +154,18 @@
 	struct file * file, *tofree;
 	struct files_struct * files = current->files;
 
+	/* this is a backdoor to close, so we need the close semaphore */
+	if (FUMOUNT)
+		down(&close_sem);
 	spin_lock(&files->file_lock);
 	if (!(file = fcheck(oldfd)))
 		goto out_unlock;
+	if (FUMOUNT && (file->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+		/* allow no new references to this file */
+		err = -ENXIO;
+		goto out_unlock;
+	}
 	err = newfd;
 	if (newfd == oldfd)
 		goto out_unlock;
@@ -191,6 +200,8 @@
 		filp_close(tofree, files);
 	err = newfd;
 out:
+	if (FUMOUNT)
+		up(&close_sem);
 	return err;
 out_unlock:
 	spin_unlock(&files->file_lock);
Index: fs/file_table.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/file_table.c,v
retrieving revision 1.1.1.1
retrieving revision 1.49
diff -u -r1.1.1.1 -r1.49
--- fs/file_table.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/file_table.c	27 Oct 2004 19:48:21 -0000	1.49
@@ -16,12 +16,19 @@
 #include <linux/eventpoll.h>
 #include <linux/mount.h>
 #include <linux/cdev.h>
+#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);
 
 /* sysctl tunables... */
 struct files_stat_struct files_stat = {
 	.max_files = NR_FILE
 };
 
+/* ditto for the close semaphore */
+DECLARE_MUTEX(close_sem);
 EXPORT_SYMBOL(files_stat); /* Needed by unix.o */
 
 /* public *and* exported. Not pretty! */
@@ -106,6 +113,39 @@
 
 EXPORT_SYMBOL(get_empty_filp);
 
+/* 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 (!FUMOUNT) {
+		BUG();
+		return NULL;
+	}
+	DEBUG_FUMOUNT;
+
+	new_file = get_empty_filp();
+	if (new_file) {
+		/* 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;
+	}
+	printk(KERN_WARNING "VFS FUMOUNT: filp allocation failed\n");
+	return NULL;
+}
+
 /*
  * Clear and initialize a (private) struct file for the given dentry,
  * allocate the security structure, and call the open function (if any).  
@@ -192,6 +232,22 @@
 	mntput(mnt);
 }
 
+void fumount_fput(struct file * file)
+{
+	if (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	/* fput has already been called on this file. */
+	if (atomic_dec_and_test(&file->f_count)) {
+		// FIXME:  Are we supposed to do this?? JLV
+		files_stat.nr_files++;
+	}
+	return;
+}
+
 struct file fastcall *fget(unsigned int fd)
 {
 	struct file *file;
@@ -199,8 +255,13 @@
 
 	spin_lock(&files->file_lock);
 	file = fcheck_files(files, fd);
-	if (file)
-		get_file(file);
+	if (file) {
+		if (FUMOUNT && (file->f_mode & FMODE_FUMOUNT)) {
+			DEBUG_FUMOUNT;
+			file = NULL;
+		} else
+			get_file(file);
+	}
 	spin_unlock(&files->file_lock);
 	return file;
 }
@@ -222,12 +283,21 @@
 	*fput_needed = 0;
 	if (likely((atomic_read(&files->count) == 1))) {
 		file = fcheck_files(files, fd);
+		if (FUMOUNT && file && (file->f_mode & FMODE_FUMOUNT)) {
+			DEBUG_FUMOUNT;
+			file = NULL;
+		}
 	} else {
 		spin_lock(&files->file_lock);
 		file = fcheck_files(files, fd);
 		if (file) {
-			get_file(file);
-			*fput_needed = 1;
+			if (!FUMOUNT) {
+				get_file(file);
+				*fput_needed = 1;
+			} else if (file->f_mode & FMODE_FUMOUNT) {
+				DEBUG_FUMOUNT;
+				file = NULL;
+			}
 		}
 		spin_unlock(&files->file_lock);
 	}
@@ -255,6 +325,35 @@
 	file_list_unlock();
 }
 
+/* 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;
+        struct list_head *list = &(sb->s_files);
+
+	if (!FUMOUNT) {
+		BUG();
+		return -ENXIO;
+	}
+
+	if (list) {
+		if (!(sb->s_flags & MS_FUMOUNT)) {
+			file_move(file, list);
+			return_code = 0;
+		} else {
+			DEBUG_FUMOUNT;
+			return_code = -ENXIO;
+		}
+	} else
+		return_code = 0;
+	return return_code;
+}
+
+
 void file_kill(struct file *file)
 {
 	if (!list_empty(&file->f_list)) {
@@ -287,6 +386,163 @@
 too_bad:
 	file_list_unlock();
 	return 0;
+}
+
+
+void fs_fumount_mark_files(struct super_block *sb)
+{
+ 	struct list_head *p;
+	struct file *file;
+
+	if (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	/* get this lock - prevents problems with sys_flock */
+	lock_kernel();
+	/* Mark all files on the sb->s_files list for unmount */
+	list_for_each(p, &sb->s_files) {
+		file = list_entry(p, struct file, f_list);
+		file->f_mode |= FMODE_FUMOUNT;
+	}
+	unlock_kernel();
+	return; 
+}
+
+/*  Forced Unmount code and comments originally from Monta Vista:
+
+I've added a lock that will prevent the fumount code from colliding 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 object 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 to
+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;
+	int return_code = 0;
+
+	if (!FUMOUNT) {
+		BUG();
+		return 0;
+	}
+	DEBUG_FUMOUNT;
+
+	down(&close_sem);
+
+	/* go through all the open files for this superblock */
+	list_for_each(p, &sb->s_files) {
+		struct file *cloned_file;
+
+		struct file *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. */
+			fput(file);
+			continue;
+		}
+		fput(file);
+		cloned_file = clone_filp(file); /* clone the file */
+		if (!cloned_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 */
+		file->f_op = NULL;
+		/* Set defunct flag for cleanup with sys_close */
+		file->f_mode |= FMODE_DEFUNCT;
+		/* FIXME: Is this comment true?
+		 * 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.  */
+
+		/* 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_move(&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,*n;
+	struct file *file;
+
+	if (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	file_list_lock();
+	/* We are deleting entries underneath ourself, so list_for_each_safe */
+	list_for_each_safe(p, n, &sb->s_files) {
+		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();
+			/* remove file from list and close it */
+			fumount_close(file);
+			file_list_lock();
+		} else {
+			/* running into fumountable files */
+			break;
+		}
+	}
+	file_list_unlock();
+	return;
 }
 
 void __init files_init(unsigned long mempages)
Index: fs/ioctl.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/ioctl.c,v
retrieving revision 1.1.1.1
retrieving revision 1.9
diff -u -r1.1.1.1 -r1.9
--- fs/ioctl.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/ioctl.c	20 Oct 2004 02:06:21 -0000	1.9
@@ -67,6 +67,13 @@
                 goto out;
         }
 
+	if (FUMOUNT && (filp->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+   	 	/* allow no new references to this file */
+		error = -ENXIO;
+		goto out;
+	}
+
 	lock_kernel();
 	switch (cmd) {
 		case FIOCLEX:
Index: fs/locks.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/locks.c,v
retrieving revision 1.1.1.1
retrieving revision 1.13
diff -u -r1.1.1.1 -r1.13
--- fs/locks.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/locks.c	27 Oct 2004 19:27:30 -0000	1.13
@@ -590,6 +590,10 @@
 	int result;
 	locks_insert_block(blocker, waiter);
 	result = interruptible_sleep_on_locked(&waiter->fl_wait, time);
+	if (FUMOUNT && (waiter->fl_file->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+		result = -ENXIO;
+	}
 	__locks_delete_block(waiter);
 	return result;
 }
@@ -718,6 +722,100 @@
 	return error;
 }
 
+/*
+ * 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 (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	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 (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	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 acquires 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;
+
+	if (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	lock_kernel();
+
+	dentry_ptr = filp->f_dentry;
+	if (dentry_ptr) {
+		if ( (inode_ptr = dentry_ptr->d_inode) ) {
+			locks_unblock_all( inode_ptr );
+			locks_remove_all( inode_ptr );
+		}
+	}
+	
+	unlock_kernel();
+}
+
 EXPORT_SYMBOL(posix_lock_file);
 
 static int __posix_lock_file(struct inode *inode, struct file_lock *request)
@@ -1343,7 +1441,14 @@
 		goto out_free;
 
 	for (;;) {
-		error = flock_lock_file(filp, lock);
+		if (!FUMOUNT || !(filp->f_mode & FMODE_FUMOUNT)) {
+			error = flock_lock_file(filp, lock);
+		}
+		else {
+			DEBUG_FUMOUNT;
+			error = -ENXIO;
+			break;
+		}
 		if ((error != -EAGAIN) || !can_sleep)
 			break;
 		error = wait_event_interruptible(lock->fl_wait, !lock->fl_next);
Index: fs/namei.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/namei.c,v
retrieving revision 1.1.1.1
retrieving revision 1.13
diff -u -r1.1.1.1 -r1.13
--- fs/namei.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/namei.c	20 Oct 2004 00:48:43 -0000	1.13
@@ -474,6 +474,12 @@
 static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
 	int err = -ELOOP;
+
+	if (FUMOUNT && !nd->mnt ) {
+		DEBUG_FUMOUNT;
+		err = -ENXIO;
+		return err;
+	}
 	if (current->link_count >= MAX_NESTED_LINKS)
 		goto loop;
 	if (current->total_link_count >= 40)
@@ -532,8 +538,18 @@
 	int res = 0;
 	while (d_mountpoint(*dentry)) {
 		struct vfsmount *mounted = lookup_mnt(*mnt, *dentry);
-		if (!mounted)
+		if (!mounted) {
+			if (FUMOUNT) {
+				DEBUG_FUMOUNT;
+				res = -1;
+			}
 			break;
+		}
+		if (FUMOUNT && (mounted->mnt_sb->s_flags & MS_FUMOUNT)) {
+			DEBUG_FUMOUNT;
+			res = -ENXIO;
+			break;
+                }
 		mntput(*mnt);
 		*mnt = mounted;
 		dput(*dentry);
@@ -555,8 +571,11 @@
 		mntput(*mnt);
 		*mnt = mounted;
 		dput(*dentry);
-		*dentry = dget(mounted->mnt_root);
-		return 1;
+		if (!FUMOUNT || !(mounted->mnt_sb->s_flags & MS_FUMOUNT) ) {
+			*dentry = dget(mounted->mnt_root);
+			return 1;
+		} else
+			DEBUG_FUMOUNT;
 	}
 	return 0;
 }
@@ -618,6 +637,15 @@
 {
 	struct vfsmount *mnt = nd->mnt;
 	struct dentry *dentry = __d_lookup(nd->dentry, name);
+        int err;
+
+	/* Uh oh.  Walked into a pending FUMOUNT - follow_down
+	   has released parent mnt and dentry, so just bail */
+	if (FUMOUNT && !nd->mnt) {
+		DEBUG_FUMOUNT;
+		err = -ENXIO;
+		return err;
+	}
 
 	if (!dentry)
 		goto need_lookup;
@@ -653,6 +681,18 @@
  * into the final dentry.
  *
  * We expect 'base' to be positive and a directory.
+ *  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.
+ *
  */
 int fastcall link_path_walk(const char * name, struct nameidata *nd)
 {
@@ -661,6 +701,10 @@
 	int err;
 	unsigned int lookup_flags = nd->flags;
 	
+	if (FUMOUNT && (!nd->mnt || name == NULL)) {
+		DEBUG_FUMOUNT;
+		return -ENXIO;  /* outa' here if bad init_path */
+	}
 	while (*name=='/')
 		name++;
 	if (!*name)
@@ -714,6 +758,11 @@
 				if (this.name[1] != '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+				if (FUMOUNT && !nd->mnt) {
+					DEBUG_FUMOUNT;
+					err = -ENXIO;
+					goto return_err;
+				}
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -734,7 +783,13 @@
 		if (err)
 			break;
 		/* Check mountpoints.. */
-		follow_mount(&next.mnt, &next.dentry);
+		if (!FUMOUNT)
+			follow_mount(&next.mnt, &next.dentry);
+		else if (follow_mount(&next.mnt, &next.dentry) < 0) {
+				DEBUG_FUMOUNT;
+				err = -ENXIO;
+				break;
+		}
 
 		err = -ENOENT;
 		inode = next.dentry->d_inode;
@@ -746,6 +801,11 @@
 
 		if (inode->i_op->follow_link) {
 			mntget(next.mnt);
+                        if (FUMOUNT && next.mnt == NULL) {
+				DEBUG_FUMOUNT;
+				err = -ENXIO;
+				goto return_err;
+                        }
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -782,6 +842,11 @@
 				if (this.name[1] != '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+				if (FUMOUNT && !nd->mnt ) {
+					DEBUG_FUMOUNT;
+					err = -ENXIO;
+					goto return_err;
+				}
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -795,11 +860,22 @@
 		err = do_lookup(nd, &this, &next);
 		if (err)
 			break;
-		follow_mount(&next.mnt, &next.dentry);
+		if (!FUMOUNT)
+			follow_mount(&next.mnt, &next.dentry);
+		else if (follow_mount(&next.mnt, &next.dentry) < 0) {
+				DEBUG_FUMOUNT;
+				err = -ENXIO;
+				break;
+		}
 		inode = next.dentry->d_inode;
 		if ((lookup_flags & LOOKUP_FOLLOW)
 		    && inode && inode->i_op && inode->i_op->follow_link) {
 			mntget(next.mnt);
+			if (FUMOUNT && next.mnt == NULL) {
+				DEBUG_FUMOUNT;
+				err = -ENXIO;
+				goto return_err;
+			}
 			err = do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -895,6 +971,8 @@
 	return 1;
 }
 
+/* Just release old altroot and associated mount and replace with new
+   values (NULL unless __emul_prefix is non-NULL) */
 void set_fs_altroot(void)
 {
 	char *emul = __emul_prefix();
@@ -1405,6 +1483,13 @@
 		if (flag & O_NOFOLLOW)
 			goto exit_dput;
 		while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry));
+		/* Uh oh.  Walked into a pending FUMOUNT - follow_down
+		   has released parent mnt and dentry, so just bail */
+		if (FUMOUNT && !nd->mnt) {
+			DEBUG_FUMOUNT;
+			error = -ENXIO;
+			return error;
+		}
 	}
 	error = -ENOENT;
 	if (!dentry->d_inode)
Index: fs/namespace.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/namespace.c,v
retrieving revision 1.1.1.1
retrieving revision 1.16
diff -u -r1.1.1.1 -r1.16
--- fs/namespace.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/namespace.c	27 Oct 2004 18:10:57 -0000	1.16
@@ -176,6 +176,10 @@
 void __mntput(struct vfsmount *mnt)
 {
 	struct super_block *sb = mnt->mnt_sb;
+	if (FUMOUNT && mnt == NULL) {
+		DEBUG_FUMOUNT;
+		return;
+	}
 	dput(mnt->mnt_root);
 	free_vfsmnt(mnt);
 	deactivate_super(sb);
@@ -374,13 +378,15 @@
 	if (retval)
 		return retval;
 
+	if (FUMOUNT)
+		printk(KERN_DEBUG "do_umount entered for superblock %x\n", (unsigned int)sb);
 	/*
 	 * 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]
 	 */
-	if (flags & MNT_EXPIRE) {
+	if ((flags & MNT_EXPIRE) && (!FUMOUNT || !(flags & MNT_FFORCE)) ) {
 		if (mnt == current->fs->rootmnt ||
 		    flags & (MNT_FORCE | MNT_DETACH))
 			return -EINVAL;
@@ -403,7 +409,18 @@
 	 */
 
 	lock_kernel();
-	if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
+	if (FUMOUNT){
+		if ((flags & (MNT_FORCE|MNT_FFORCE)) && sb->s_op->umount_begin) {
+			/* FIXME: Redo this when debug info not needed */
+			if (flags & MNT_FFORCE) {
+				/* Doing a "real" force unmount */
+				DEBUG_FUMOUNT;
+				printk(KERN_DEBUG "calling umount_begin for superblock %x\n"
+					, (unsigned int)sb);
+			}
+			sb->s_op->umount_begin(sb);
+		}
+	} else if ((flags&MNT_FORCE) && sb->s_op->umount_begin)
 		sb->s_op->umount_begin(sb);
 	unlock_kernel();
 
@@ -434,6 +451,7 @@
 	down_write(&current->namespace->sem);
 	spin_lock(&vfsmount_lock);
 
+umount_retry:
 	if (atomic_read(&sb->s_active) == 1) {
 		/* last instance - try to be smart */
 		spin_unlock(&vfsmount_lock);
@@ -450,6 +468,133 @@
 			umount_tree(mnt);
 		retval = 0;
 	}
+
+	if (FUMOUNT)
+	{
+		/*  Code and comments originally written by Monta Vista for the
+		 *  2.4.x kernel.
+		
+		 * Now for the dreaded 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)) ) {
+
+			DEBUG_FUMOUNT;
+			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;
+	}
+
 	spin_unlock(&vfsmount_lock);
 	if (retval)
 		security_sb_umount_busy(mnt);
@@ -458,17 +603,22 @@
 }
 
 /*
- * Now umount can handle mount points as well as block devices.
- * This is important for filesystems which use unnamed block devices.
+ * Now umount can handle mount points as well as block devices.  This is
+ * important for filesystems which use unnamed block devices.
  *
- * We now support a flag for forced unmount like the other 'big iron'
- * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
+ * We now support a flag for forced unmount like the other 'big iron' unixes.
+ * Our API is identical to OSF/1 to avoid making a mess of AMD
  */
 
 asmlinkage long sys_umount(char __user * name, int flags)
 {
 	struct nameidata nd;
 	int retval;
+
+	if (FUMOUNT && (flags & MNT_FFORCE)) {
+		DEBUG_FUMOUNT;
+		printk(KERN_DEBUG "Entered sys_umount, flags = %x\n", flags);
+	}
 
 	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
 	if (retval)
Index: fs/open.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/fs/open.c,v
retrieving revision 1.1.1.1
retrieving revision 1.26
diff -u -r1.1.1.1 -r1.26
--- fs/open.c	27 Sep 2004 19:33:40 -0000	1.1.1.1
+++ fs/open.c	27 Oct 2004 00:53:40 -0000	1.26
@@ -794,7 +794,16 @@
 	f->f_vfsmnt = mnt;
 	f->f_pos = 0;
 	f->f_op = fops_get(inode->i_fop);
-	file_move(f, &inode->i_sb->s_files);
+	if (!FUMOUNT)
+		file_move(f, &inode->i_sb->s_files);
+	else {
+		error = file_move_test(f, inode->i_sb);
+		if (error) {
+			DEBUG_FUMOUNT;
+			printk(KERN_DEBUG "Disallowed file open due to pending unmount\n");
+			goto cleanup_all;
+		}
+	}
 
 	if (f->f_op && f->f_op->open) {
 		error = f->f_op->open(inode,f);
@@ -1006,15 +1015,59 @@
 			retval = err;
 	}
 
-	dnotify_flush(filp, id);
-	locks_remove_posix(filp, id);
-	fput(filp);
+	if (!FUMOUNT || !(filp->f_mode & FMODE_DEFUNCT) ) {
+
+		dnotify_flush(filp, id);
+		locks_remove_posix(filp, id);
+		fput(filp);
+	} else {
+		DEBUG_FUMOUNT;
+		/* 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.  */
+                /* We have already removed locks & done an fput */
+		dump_stack();
+		fumount_fput(filp);
+	}
 	return retval;
 }
 
 EXPORT_SYMBOL(filp_close);
 
 /*
+ * 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 (!FUMOUNT) {
+		BUG();
+		return;
+	}
+	DEBUG_FUMOUNT;
+
+	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);
+}
+
+/*
  * Careful here! We test whether the file pointer is NULL before
  * releasing the fd. This ensures that one clone task can't release
  * an fd while another clone is opening it.
@@ -1023,7 +1076,11 @@
 {
 	struct file * filp;
 	struct files_struct *files = current->files;
+        int ret_code;
 
+	if (FUMOUNT) {
+		down(&close_sem);
+	}
 	spin_lock(&files->file_lock);
 	if (fd >= files->max_fds)
 		goto out_unlock;
@@ -1034,11 +1091,18 @@
 	FD_CLR(fd, files->close_on_exec);
 	__put_unused_fd(files, fd);
 	spin_unlock(&files->file_lock);
-	return filp_close(filp, files);
+	ret_code = filp_close(filp, files);
+
+exit_sys_close:
+	if (FUMOUNT) {
+		up(&close_sem);
+	}
+        return ret_code;
 
 out_unlock:
 	spin_unlock(&files->file_lock);
-	return -EBADF;
+	ret_code =  -EBADF;
+	goto exit_sys_close;
 }
 
 EXPORT_SYMBOL(sys_close);
Index: include/linux/dnotify.h
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/include/linux/dnotify.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- include/linux/dnotify.h	27 Sep 2004 19:32:31 -0000	1.1.1.1
+++ include/linux/dnotify.h	8 Oct 2004 23:47:11 -0000	1.3
@@ -15,6 +15,7 @@
 	fl_owner_t		dn_owner;
 };
 
+extern void fumount_dnotify_flush(struct file *filp);
 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);
Index: include/linux/file.h
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/include/linux/file.h,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- include/linux/file.h	27 Sep 2004 19:32:32 -0000	1.1.1.1
+++ include/linux/file.h	8 Oct 2004 23:47:38 -0000	1.3
@@ -36,6 +36,7 @@
 extern void FASTCALL(__fput(struct file *));
 extern void FASTCALL(fput(struct file *));
 
+extern void FASTCALL(fumount_fput(struct file *));
 static inline void fput_light(struct file *file, int fput_needed)
 {
 	if (unlikely(fput_needed))
Index: include/linux/fs.h
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/include/linux/fs.h,v
retrieving revision 1.1.1.1
retrieving revision 1.15
diff -u -r1.1.1.1 -r1.15
--- include/linux/fs.h	27 Sep 2004 19:32:31 -0000	1.1.1.1
+++ include/linux/fs.h	20 Oct 2004 20:52:54 -0000	1.15
@@ -19,6 +19,16 @@
 #include <linux/prio_tree.h>
 #include <linux/kobject.h>
 #include <asm/atomic.h>
+#include <linux/mount.h>
+
+
+#ifdef CONFIG_FUMOUNT
+enum {FUMOUNT = 1};
+#define DEBUG_FUMOUNT do { printk("Forced Unmount: (%s, %d), %s\n", __FILE__, __LINE__, __FUNCTION__); } while (0)
+#else
+enum {FUMOUNT = 0};
+#define DEBUG_FUMOUNT
+#endif
 
 struct iovec;
 struct nameidata;
@@ -79,6 +89,9 @@
 #define FMODE_LSEEK	4
 #define FMODE_PREAD	8
 #define FMODE_PWRITE	FMODE_PREAD	/* These go hand in hand */
+/* next two mode flags are for fumount */
+#define FMODE_FUMOUNT 16	/* fumount is forcing this file to fail */
+#define FMODE_DEFUNCT 32	/* fumount has taken the resources away from this file */
 
 #define RW_MASK		1
 #define RWA_MASK	2
@@ -119,6 +132,7 @@
 #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 */
+#define MS_FUMOUNT	(1<<29) /* Start a FORCED unmount - no more opens */
 #define MS_ACTIVE	(1<<30)
 #define MS_NOUSER	(1<<31)
 
@@ -589,6 +603,7 @@
 	struct address_space	*f_mapping;
 };
 extern spinlock_t files_lock;
+extern struct semaphore close_sem;
 #define file_list_lock() spin_lock(&files_lock);
 #define file_list_unlock() spin_unlock(&files_lock);
 
@@ -600,6 +615,8 @@
 /* Release a private file and free its security structure. */
 extern void close_private_file(struct file *file);
 
+extern void fumount_close( struct file * );
+extern void fs_fumount_close( struct super_block * );
 #define	MAX_NON_LFS	((1UL<<31) - 1)
 
 /* Page cache limit. The filesystems should put that into their s_maxbytes 
@@ -720,7 +737,7 @@
 #define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
 #define MNT_DETACH	0x00000002	/* Just detach from the tree */
 #define MNT_EXPIRE	0x00000004	/* Mark for expiry */
-
+#define MNT_FFORCE	0x00000008	/* Really forcibily umount - no prisoners */
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
 
@@ -1279,6 +1296,8 @@
 
 extern int fs_may_remount_ro(struct super_block *);
 
+extern int fs_fumount_clone_list(struct super_block *);
+extern void fs_fumount_mark_files(struct super_block *);
 /*
  * return READ, READA, or WRITE
  */
@@ -1392,6 +1411,7 @@
 
 extern struct file * get_empty_filp(void);
 extern void file_move(struct file *f, struct list_head *list);
+extern int file_move_test(struct file *f,  struct super_block *sb);
 extern void file_kill(struct file *f);
 struct bio;
 extern void submit_bio(int, struct bio *);
@@ -1570,5 +1590,26 @@
 { }
 #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) {
+		if (!(mnt->mnt_sb->s_flags & MS_FUMOUNT)) {
+			atomic_inc(&mnt->mnt_count);
+		} else {
+			DEBUG_FUMOUNT;
+			mnt = NULL;
+		}
+	}
+	return mnt;
+}
+#endif /*CONFIG_FUMOUNT*/
 #endif /* __KERNEL__ */
 #endif /* _LINUX_FS_H */
Index: include/linux/mount.h
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/include/linux/mount.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- include/linux/mount.h	27 Sep 2004 19:32:31 -0000	1.1.1.1
+++ include/linux/mount.h	27 Sep 2004 19:53:56 -0000	1.2
@@ -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);
 
Index: mm/mmap.c
===================================================================
RCS file: /home/cvs/components/forced_unmount/linux-2.6.8.1/mm/mmap.c,v
retrieving revision 1.1.1.1
retrieving revision 1.58
diff -u -r1.1.1.1 -r1.58
--- mm/mmap.c	27 Sep 2004 19:33:58 -0000	1.1.1.1
+++ mm/mmap.c	27 Oct 2004 22:32:44 -0000	1.58
@@ -1606,6 +1606,229 @@
 	return ret;
 }
 
+int remove_file_map(struct file *file, struct mm_struct *mm_ptr)
+{
+	/* 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.
+	 */
+	int ret_code;
+
+	if (!FUMOUNT) {
+		BUG();
+		return 0;
+	}
+
+	if (mm_ptr) {
+		struct vm_area_struct *next_vma_ptr;
+		struct vm_area_struct *vma_ptr;
+
+		atomic_inc(&mm_ptr->mm_count);
+		down_write(&mm_ptr->mmap_sem);
+
+		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) {
+				ret_code = do_munmap(mm_ptr, vma_ptr->vm_start,
+					(size_t)(vma_ptr->vm_end
+					- vma_ptr->vm_start));
+				if (ret_code) {
+					/* Low memory condition. Retry built into
+					 * the caller */
+					up_write(&mm_ptr->mmap_sem);
+					atomic_dec(&mm_ptr->mm_count);
+					return ret_code;
+				}
+			}
+		}
+		up_write(&mm_ptr->mmap_sem);
+		mmput(mm_ptr);
+	}
+	return 0;
+}
+
+int remove_shared_file_mappings(struct file *file,
+	struct address_space *addr_space_ptr)
+{
+	struct mm_struct *mm_ptr;
+        struct  tagqueue
+        {
+          struct prio_tree_node * data;
+          struct tagqueue       *next;
+        } *queue, *tmpptr;
+        struct prio_tree_node * tree_ptr;
+	struct vm_area_struct *vma_ptr;
+        int ret_code;
+
+	if (!FUMOUNT) {
+		BUG();
+		return 0;
+	}
+	DEBUG_FUMOUNT;
+	if (prio_tree_empty(&addr_space_ptr->i_mmap))
+		return 0;
+	spin_lock(&addr_space_ptr->i_mmap_lock);
+
+next_shared_mapping:
+	DEBUG_FUMOUNT;
+	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)
+		goto out_shared_mapping;
+
+	printk(KERN_DEBUG "found shared map\n");
+	mm_ptr = vma_ptr->vm_mm;
+
+	spin_unlock(&addr_space_ptr->i_mmap_lock);
+	ret_code = remove_file_map(file, mm_ptr);
+	if (ret_code) {
+		// 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);
+
+	if ( !prio_tree_empty(&addr_space_ptr->i_mmap) )
+	      goto next_shared_mapping;
+
+out_shared_mapping:
+	spin_unlock(&addr_space_ptr->i_mmap_lock);
+	return 0;
+}
+
+
+int remove_nonlinear_mappings( struct file *file,
+	struct address_space *addr_space_ptr)
+{
+        struct list_head *ptr;
+	struct vm_area_struct *vma_ptr;
+	struct mm_struct *mm_ptr;
+        int ret_code;
+
+	if (!FUMOUNT) {
+		BUG();
+		return 0;
+	}
+	DEBUG_FUMOUNT;
+
+	if (list_empty(&addr_space_ptr->i_mmap_nonlinear))
+		return 0;
+
+	spin_lock(&addr_space_ptr->i_mmap_lock);
+	while (!list_empty(&addr_space_ptr->i_mmap_nonlinear)) {
+		DEBUG_FUMOUNT;
+		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)
+			goto out_nonlinear_mapping;
+
+		printk(KERN_DEBUG "found anon map\n");
+		mm_ptr = vma_ptr->vm_mm;
+
+		spin_unlock(&addr_space_ptr->i_mmap_lock);
+		ret_code = remove_file_map(file, mm_ptr);
+		if (ret_code)
+			return ret_code;
+		spin_lock(&addr_space_ptr->i_mmap_lock);
+	}
+
+out_nonlinear_mapping:
+	spin_unlock(&addr_space_ptr->i_mmap_lock);
+	return 0;
+}
+
+/* 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 address_space *addr_space_ptr;
+        int ret_code = 0;
+
+	if (!FUMOUNT) {
+		BUG();
+		return 0;
+	}
+	DEBUG_FUMOUNT;
+	printk(KERN_DEBUG "Remove_file_mappings called.\n");
+	if (!file ) 
+	      return -EBADF;
+	addr_space_ptr = file->f_mapping;
+	if (!addr_space_ptr) 
+	      return -EBADF;
+	
+	DEBUG_FUMOUNT;
+	if (!prio_tree_empty(&addr_space_ptr->i_mmap)) {
+		ret_code = remove_shared_file_mappings(file, addr_space_ptr);
+		if (ret_code)
+			return ret_code;
+	}
+	DEBUG_FUMOUNT;
+	if (!list_empty(&addr_space_ptr->i_mmap_nonlinear) )
+		ret_code = remove_nonlinear_mappings(file, addr_space_ptr);
+	return ret_code;
+}
+
 /*
  *  this is really a simplified "do_mmap".  it only handles
  *  anonymous maps.  eventually we may be able to do some
umount-userspace.patch (application/octet-stream, 3.5 KB)
diff -Nur util-linux-2.12pre/configure util-linux-2.12pre.org/configure
--- util-linux-2.12pre/configure.fumount	2003-07-05 13:06:55.000000000 -0700
+++ util-linux-2.12pre/configure	2004-10-20 15:17:31.914627387 -0700
@@ -70,6 +70,13 @@
 
 rm -f conftest.c conftest
 
+for arg in "$@"
+do
+    if [ "$arg" = "--with-fumount" ]; then
+	echo "#define HAVE_FUMOUNT" >> defines.h
+    fi
+done
+
 #
 # Old gcc wants options like -m486, but gcc 3.0 says
 # `-m486' is deprecated. Use `-march=i486' or `-mcpu=i486' instead.
diff -Nur util-linux-2.12pre/mount/umount.c util-linux-2.12pre.org/mount/umount.c
--- util-linux-2.12pre/mount/umount.c	2002-10-31 17:00:50.000000000 -0800
+++ util-linux-2.12pre.org/mount/umount.c	2004-08-12 22:59:38.967943624 -0700
@@ -45,6 +45,10 @@
 #include "fstab.h"
 #include "env.h"
 #include "nls.h"
+/* for vforce unmount */
+#ifdef HAVE_FUMOUNT
+#define MNT_FFORCE      0x00000008      /* Really forcibily umount - no prisoners */
+#endif
 
 #ifdef HAVE_NFS
 #include <sys/socket.h>
@@ -94,6 +98,11 @@
 /* Nonzero for force umount (-f).  There is kernel support since 2.1.116.  */
 int force = 0;
 
+#ifdef HAVE_FUMOUNT
+/* Nonzero for vforce umount (-F). */
+int vforce = 0;
+#endif
+
 /* Nonzero for lazy umount (-l).  There is kernel support since 2.4.11.  */
 int lazy = 0;
 
@@ -275,8 +284,22 @@
 			umnt_err = errno;
 		goto writemtab;
 	}
-
-	if (force) {		/* only supported for NFS */
+ 
+        #ifdef HAVE_FUMOUNT
+        if (vforce) {
+		res = umount2 (node, MNT_FFORCE);
+		if (res == -1) {
+			perror("umount2");
+			if (errno == ENOSYS) {
+				if (verbose)
+					printf(_("no umount2, trying umount...\n"));
+				res = umount (node);
+			}
+		}
+        }
+        else 
+        #endif
+        if (force) {		/* only supported for NFS */
 		res = umount2 (node, MNT_FORCE);
 		if (res == -1) {
 			perror("umount2");
@@ -441,6 +464,9 @@
 {
   { "all", 0, 0, 'a' },
   { "force", 0, 0, 'f' },
+#ifdef HAVE_FUMOUNT
+  { "vforce", 0, 0, 'F' },
+#endif
   { "help", 0, 0, 'h' },
   { "no-mtab", 0, 0, 'n' },
   { "test-opts", 1, 0, 'O' },
@@ -454,9 +480,15 @@
 static void
 usage (FILE *fp, int n)
 {
+  #ifdef HAVE_FUMOUNT
+  fprintf (fp, _("Usage: umount [-hV]\n"
+	       "       umount -a [-f] [-F] [-r] [-n] [-v] [-t vfstypes] [-O opts]\n"
+	       "       umount [-f] [-F] [-r] [-n] [-v] special | node...\n"));
+  #else
   fprintf (fp, _("Usage: umount [-hV]\n"
 	       "       umount -a [-f] [-r] [-n] [-v] [-t vfstypes] [-O opts]\n"
 	       "       umount [-f] [-r] [-n] [-v] special | node...\n"));
+  #endif
   exit (n);
 }
 
@@ -619,8 +651,13 @@
 
 	umask(033);
 
+        #ifdef HAVE_FUMOUNT
+	while ((c = getopt_long (argc, argv, "adfFhlnrt:O:vV",
+				 longopts, NULL)) != -1)
+        #else
 	while ((c = getopt_long (argc, argv, "adfhlnrt:O:vV",
 				 longopts, NULL)) != -1)
+        #endif
 		switch (c) {
 		case 'a':		/* umount everything */
 			++all;
@@ -632,6 +669,11 @@
 		case 'f':		/* force umount */
 			++force;
 			break;
+                #ifdef HAVE_FUMOUNT
+		case 'F':		/* VFS level force umount */
+			++vforce;
+			break;
+                #endif
 		case 'h':		/* help */
 			usage (stdout, 0);
 			break;
@@ -665,8 +707,13 @@
 
 	if (getuid () != geteuid ()) {
 		suid = 1;
-		if (all || types || nomtab || force)
+                #ifdef HAVE_FUMOUNT
+		if (all || types || nomtab || force || vforce)
+			die (2, _("umount: only root can do that"));
+                #else
+		if (all || types || nomtab || force )
 			die (2, _("umount: only root can do that"));
+                #endif
 	}
 
 	argc -= optind;