[cgl_discussion] Updated Forced umount

Dave Jiang djiang at mvista.com
Wed Apr 13 11:24:39 PDT 2005


Here is my update of the fumount patch that John Villalovos sent out on =

10/27/04. This is against the 2.6.11.6 kernel.
http://lists.osdl.org/pipermail/cgl_discussion/2004-October/001938.html

I cleaned up the walking of the prio tree for looking up mmaped files. =

Also merged in fixes from Mark Huth regarding the namespace races.

Comments and bug reports are greatly appreciated.

The userland mods to umount remains the same as what John had posted.

-- =

Dave

------------------------------------------------------
Dave Jiang
Software Engineer
MontaVista Software, Inc.
Web: www.mvista.com
------------------------------------------------------

-------------- next part --------------
diff -Naur linux-2.6.11.6/fs/coda/pioctl.c linux-2.6.11.6_mod/fs/coda/pioct=
l.c
--- linux-2.6.11.6/fs/coda/pioctl.c	2005-03-25 20:28:24.000000000 -0700
+++ linux-2.6.11.6_mod/fs/coda/pioctl.c	2005-04-05 16:15:11.752534088 -0700
@@ -14,6 +14,9 @@
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif
 #include <linux/namei.h>
 #include <linux/module.h>
 #include <asm/uaccess.h>
@@ -66,6 +69,9 @@
          * Look up the pathname. Note that the pathname is in =

          * user memory, and namei takes care of this
          */
+#ifdef CONFIG_FUMOUNT
+		down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
         if ( data.follow ) {
                 error =3D user_path_walk(data.path, &nd);
 	} else {
@@ -73,6 +79,9 @@
 	}
 		=

 	if ( error ) {
+#ifdef CONFIG_FUMOUNT
+		up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
         } else {
 	        target_inode =3D nd.dentry->d_inode;
@@ -81,6 +90,9 @@
 	/* return if it is not a Coda inode */
 	if ( target_inode->i_sb !=3D inode->i_sb ) {
 		path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+		up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	        return  -EINVAL;
 	}
 =

@@ -90,6 +102,9 @@
 	error =3D venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
 =

 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+		up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
         return error;
 }
 =

diff -Naur linux-2.6.11.6/fs/dcache.c linux-2.6.11.6_mod/fs/dcache.c
--- linux-2.6.11.6/fs/dcache.c	2005-03-25 20:28:15.000000000 -0700
+++ linux-2.6.11.6_mod/fs/dcache.c	2005-04-05 16:15:11.758533176 -0700
@@ -1027,10 +1027,17 @@
 	struct dentry * dentry =3D NULL;
 	unsigned long seq;
 =

-        do {
-                seq =3D read_seqbegin(&rename_lock);
-                dentry =3D __d_lookup(parent, name);
-                if (dentry)
+#ifdef CONFIG_FUMOUNT
+	if (unlikely(parent =3D=3D NULL)) {
+		DEBUG_FUMOUNT;
+		return dentry;
+	}
+#endif	=

+
+	do {
+		seq =3D read_seqbegin(&rename_lock);
+		dentry =3D __d_lookup(parent, name);
+		if (dentry)
 			break;
 	} while (read_seqretry(&rename_lock, seq));
 	return dentry;
@@ -1361,6 +1368,17 @@
 =

 	*--end =3D '\0';
 	buflen--;
+#ifdef CONFIG_FUMOUNT	=

+	if (unlikely(!dentry || !vfsmnt)) {
+		DEBUG_FUMOUNT;
+		buflen -=3D 6;
+		end -=3D 6;
+		memcpy(end, "(null)", 6);
+		retval =3D end;
+		return retval;
+	}
+#endif
+
 	if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
 		buflen -=3D 10;
 		end -=3D 10;
@@ -1469,6 +1487,21 @@
 =

 	read_lock(&current->fs->lock);
 	pwdmnt =3D mntget(current->fs->pwdmnt);
+	=

+#ifdef CONFIG_FUMOUNT	=

+	if (unlikely(pwdmnt =3D=3D NULL)) {
+		unsigned long len =3D 2;
+		char * root_dir =3D "/";
+		DEBUG_FUMOUNT;
+		if (copy_to_user(buf, root_dir, len))
+			error =3D -EFAULT;
+		else
+			error =3D len;
+		read_unlock(&current->fs->lock);
+		goto out_freepage;
+	}
+#endif
+
 	pwd =3D dget(current->fs->pwd);
 	rootmnt =3D mntget(current->fs->rootmnt);
 	root =3D dget(current->fs->root);
@@ -1503,6 +1536,9 @@
 	mntput(pwdmnt);
 	dput(root);
 	mntput(rootmnt);
+#ifdef CONFIG_FUMOUNT	=

+out_freepage:
+#endif
 	free_page((unsigned long) page);
 	return error;
 }
diff -Naur linux-2.6.11.6/fs/dnotify.c linux-2.6.11.6_mod/fs/dnotify.c
--- linux-2.6.11.6/fs/dnotify.c	2005-03-25 20:28:18.000000000 -0700
+++ linux-2.6.11.6_mod/fs/dnotify.c	2005-04-05 16:15:11.759533024 -0700
@@ -36,6 +36,37 @@
 	inode->i_dnotify_mask =3D new_mask;
 }
 =

+#ifdef CONFIG_FUMOUNT
+void fumount_dnotify_flush(struct file *filp)
+{
+	struct dnotify_struct *dn;
+	struct dnotify_struct **prev;
+	struct inode *inode;
+
+	DEBUG_FUMOUNT;
+
+	if (!filp || !filp->f_dentry || !filp->f_dentry->d_inode)
+		return;
+	=

+	inode =3D filp->f_dentry->d_inode;
+	if (!S_ISDIR(inode->i_mode))
+		return;
+	=

+	spin_lock(&inode->i_lock);
+	prev =3D &inode->i_dnotify;
+	while ((dn =3D *prev) !=3D NULL) {
+		if ( dn->dn_filp =3D=3D filp ) {
+			*prev =3D dn->dn_next;
+			redo_inode_mask(inode);
+			kmem_cache_free(dn_cache, dn);
+			break;
+		}
+		prev =3D &dn->dn_next;
+	}
+	spin_unlock(&inode->i_lock);
+}
+#endif
+
 void dnotify_flush(struct file *filp, fl_owner_t id)
 {
 	struct dnotify_struct *dn;
@@ -160,16 +191,19 @@
 	if (!dir_notify_enable)
 		return;
 =

-	spin_lock(&dentry->d_lock);
-	parent =3D 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 (dentry) {
+		spin_lock(&dentry->d_lock);
+		parent =3D 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);
+		}
+	} =

 }
 EXPORT_SYMBOL_GPL(dnotify_parent);
 =

diff -Naur linux-2.6.11.6/fs/exec.c linux-2.6.11.6_mod/fs/exec.c
--- linux-2.6.11.6/fs/exec.c	2005-03-25 20:28:19.000000000 -0700
+++ linux-2.6.11.6_mod/fs/exec.c	2005-04-05 16:15:11.755533632 -0700
@@ -39,6 +39,9 @@
 #include <linux/binfmts.h>
 #include <linux/swap.h>
 #include <linux/utsname.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif /* CONFIG_FUMOUNT */
 #include <linux/module.h>
 #include <linux/namei.h>
 #include <linux/proc_fs.h>
@@ -123,6 +126,9 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+	down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	nd.intent.open.flags =3D FMODE_READ;
 	error =3D __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
 	if (error)
@@ -162,6 +168,9 @@
 	}
 	fput(file);
 out:
+#ifdef CONFIG_FUMOUNT
+	up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
   	return error;
 exit:
 	path_release(&nd);
diff -Naur linux-2.6.11.6/fs/fcntl.c linux-2.6.11.6_mod/fs/fcntl.c
--- linux-2.6.11.6/fs/fcntl.c	2005-03-25 20:28:24.000000000 -0700
+++ linux-2.6.11.6_mod/fs/fcntl.c	2005-04-05 16:15:11.763532416 -0700
@@ -122,10 +122,29 @@
 	int err =3D -EBADF;
 	struct file * file, *tofree;
 	struct files_struct * files =3D current->files;
+#ifdef CONFIG_FUMOUNT
+	struct vfsmount *mnt =3D NULL;
+#endif
 =

 	spin_lock(&files->file_lock);
 	if (!(file =3D fcheck(oldfd)))
 		goto out_unlock;
+
+#ifdef CONFIG_FUMOUNT	=

+	/* this is a backdoor to close, so we need the close semaphore */
+	mnt =3D file->f_vfsmnt;
+	if(mnt) {
+		down_read(&mnt->mnt_close_sem);
+	}
+
+	if (unlikely(file->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+		/* allow no new references to this file */
+		err =3D -ENXIO;
+		goto out_unlock;
+	}
+#endif
+
 	err =3D newfd;
 	if (newfd =3D=3D oldfd)
 		goto out_unlock;
@@ -160,6 +179,10 @@
 		filp_close(tofree, files);
 	err =3D newfd;
 out:
+#ifdef CONFIG_FUMOUNT	=

+	if(mnt)
+		up_read(&mnt->mnt_close_sem);
+#endif		=

 	return err;
 out_unlock:
 	spin_unlock(&files->file_lock);
diff -Naur linux-2.6.11.6/fs/file_table.c linux-2.6.11.6_mod/fs/file_table.c
--- linux-2.6.11.6/fs/file_table.c	2005-03-25 20:28:14.000000000 -0700
+++ linux-2.6.11.6_mod/fs/file_table.c	2005-04-05 16:15:11.762532568 -0700
@@ -16,6 +16,15 @@
 #include <linux/eventpoll.h>
 #include <linux/mount.h>
 #include <linux/cdev.h>
+#include <linux/errno.h>
+
+#ifdef CONFIG_FUMOUNT
+static LIST_HEAD(defunct_list);
+
+extern int remove_file_mappings(struct file *);
+extern void remove_file_locks(struct file *);
+static struct file * clone_filp(struct file * source_file);
+#endif
 =

 /* sysctl tunables... */
 struct files_stat_struct files_stat =3D {
@@ -146,6 +155,19 @@
 	mntput(mnt);
 }
 =

+#ifdef CONFIG_FUMOUNT
+void fastcall fumount_fput(struct file * file)
+{
+	DEBUG_FUMOUNT;
+
+	/* fput has already been called on this file. */
+	if (atomic_dec_and_test(&file->f_count)) {
+		file_free(file);
+	}
+	return;
+}
+#endif
+
 struct file fastcall *fget(unsigned int fd)
 {
 	struct file *file;
@@ -153,14 +175,54 @@
 =

 	spin_lock(&files->file_lock);
 	file =3D fcheck_files(files, fd);
-	if (file)
+	if (file) {
+#ifdef CONFIG_FUMOUNT		=

+		if(!(file->f_mode & FMODE_FUMOUNT)) {
+			get_file(file);
+		}
+		else {
+			DEBUG_FUMOUNT;
+			file =3D NULL;
+		}
+#else			=

 		get_file(file);
+#endif		=

+	}
 	spin_unlock(&files->file_lock);
 	return file;
 }
 =

 EXPORT_SYMBOL(fget);
 =

+#ifdef CONFIG_FUMOUNT
+/* Find an unused file structure and clone the existing file.  Returns NUL=
L, 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;
+
+	new_file =3D get_empty_filp();
+	if (new_file) {
+		/* Copy all file stats, flags etc. */
+		new_file->f_version      =3D source_file->f_version;
+		new_file->f_dentry       =3D source_file->f_dentry;
+		new_file->f_vfsmnt       =3D source_file->f_vfsmnt;
+		new_file->f_op           =3D source_file->f_op;
+		new_file->f_flags        =3D source_file->f_flags;
+		new_file->f_mode         =3D source_file->f_mode;
+		new_file->f_pos          =3D source_file->f_pos;
+		memcpy(&new_file->f_ra, &source_file->f_ra, sizeof(struct file_ra_state)=
);
+		new_file->f_uid          =3D source_file->f_uid;
+		new_file->f_gid          =3D source_file->f_gid;
+		new_file->f_error        =3D source_file->f_error;
+		new_file->private_data   =3D source_file->private_data;
+		return new_file;
+	}
+	printk(KERN_WARNING "VFS FUMOUNT: filp allocation failed\n");
+	return NULL;
+}
+#endif
+
 /*
  * Lightweight file lookup - no refcnt increment if fd table isn't shared. =

  * You can use this only if it is guranteed that the current task already =

@@ -176,12 +238,31 @@
 	*fput_needed =3D 0;
 	if (likely((atomic_read(&files->count) =3D=3D 1))) {
 		file =3D fcheck_files(files, fd);
+
+#ifdef CONFIG_FUMOUNT		=

+		if (unlikely(file && (file->f_mode & FMODE_FUMOUNT))) {
+			DEBUG_FUMOUNT;
+			file =3D NULL;
+		}
+#endif
+
 	} else {
 		spin_lock(&files->file_lock);
 		file =3D fcheck_files(files, fd);
 		if (file) {
+#ifdef CONFIG_FUMOUNT
+			if(!(file->f_mode & FMODE_FUMOUNT)) {
+				get_file(file);
+				*fput_needed =3D 1;
+			}
+			else {
+				DEBUG_FUMOUNT;
+				file =3D NULL;
+			}
+#else
 			get_file(file);
 			*fput_needed =3D 1;
+#endif				=

 		}
 		spin_unlock(&files->file_lock);
 	}
@@ -207,6 +288,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 MNT_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 =3D &(sb->s_files);
+
+	if (list) {
+		if (!(file->f_vfsmnt->mnt_flags & MNT_FUMOUNT)) {
+			file_move(file, list);
+			return_code =3D 0;
+		} else {
+			DEBUG_FUMOUNT;
+			return_code =3D -ENXIO;
+		}
+	} else
+		return_code =3D 0;
+	return return_code;
+}
+#endif
+
 void file_kill(struct file *file)
 {
 	if (!list_empty(&file->f_list)) {
@@ -241,6 +346,173 @@
 	return 0;
 }
 =

+#ifdef CONFIG_FUMOUNT
+void fs_fumount_mark_files(struct vfsmount *mnt)
+{
+ 	struct list_head *p;
+	struct super_block *sb =3D mnt->mnt_sb;
+	struct file *file;
+
+	DEBUG_FUMOUNT;
+
+	/* get this lock - prevents problems with sys_flock */
+	lock_kernel();
+	/* Mark all files on the sb->s_files list for unmount if f_vfsmnt =3D=3D =
mnt */
+	list_for_each(p, &sb->s_files) {
+		file =3D list_entry(p, struct file, f_list);
+		if (file->f_vfsmnt =3D=3D mnt)
+			file->f_mode |=3D FMODE_FUMOUNT;
+
+	}
+	unlock_kernel();
+	return; =

+}
+
+/*  Forced Unmount code and comments originally from Monta Vista:
+I've added a semaphore 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 ca=
lls.
+
+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 t=
he
+file object reference count.  So find the inode and check for mappings bef=
ore
+the clone.  FMODE_FUMOUNT has made the mapping unalterable by the actual o=
wner,
+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 c=
lone
+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 vfsmount *mnt)
+{
+	struct list_head *p, *temp;
+	int return_code =3D 0;
+	struct file *cloned_file;
+	struct file *file;
+
+	DEBUG_FUMOUNT;
+
+	down_write(&mnt->mnt_close_sem);
+
+	file_list_lock();
+	/* go through all the open files for this superblock */
+	list_for_each_safe(p, temp, &sb->s_files) {
+
+		file =3D list_entry(p, struct file, f_list);
+
+		if (!((file->f_mode & FMODE_FUMOUNT)
+				&& (file->f_vfsmnt =3D=3D mnt)) ) {
+			continue;
+		}
+
+		file_list_unlock();
+
+		/* get reference count so file doesn't vanish */
+		get_file(file); =

+		/* drop lock to let sys_close progress
+				- I have the file reference to hold the
+				  object until I'm done removing the
+				  mmaps and locks */
+		up_write(&mnt->mnt_close_sem); =

+
+		/* check for mmappings and undo, if any */
+		return_code =3D remove_file_mappings(file);
+
+		/* Similarly, remove the file locks associated with this file
+		   object.
+		*/
+		remove_file_locks(file);
+
+		down_write(&mnt->mnt_close_sem);
+		if ( file_count(file) =3D=3D 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. */
+           cloned_file =3D file;
+           fput(file);
+           continue;
+		}
+		fput(file);
+		cloned_file =3D clone_filp(file); /* clone the file */
+		if (!cloned_file) {
+			return_code =3D -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 =3D NULL;
+        file->f_dentry =3D (struct dentry *)NULL;
+        file->f_vfsmnt =3D (struct vfsmount *)NULL;
+
+		/* Set defunct flag for cleanup with sys_close */
+		file->f_mode |=3D FMODE_DEFUNCT;
+
+		/* put the clone onto the sb list for further processing */
+		cloned_file->f_mode &=3D ~FMODE_FUMOUNT;
+		cloned_file->f_mode |=3D FMODE_DEFUNCT;
+
+		file_list_lock();
+		list_move(&cloned_file->f_list, &defunct_list);
+	}
+	file_list_unlock();
+	up_write(&mnt->mnt_close_sem);
+	return return_code;
+}
+
+void fs_fumount_close( void )
+{
+	struct list_head *p,*n;
+	struct file *file;
+
+	DEBUG_FUMOUNT;
+
+	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.
+	 * Otherwise, it is closed again, until it finally goes away.  We hold the
+	 * file list lock in case of the unlikely event of a concurrent fumount */
+
+	/* We are deleting entries underneath ourself, so list_for_each_safe */
+	list_for_each_safe(p, n, &defunct_list) {
+		file =3D list_entry(p, struct file, f_list);
+        /* drop the file list lock, since we have the
+		   only reachable reference(s) to this file object */
+		file_list_unlock();
+
+        if (!(file->f_mode & FMODE_DEFUNCT) ) {
+			/* have run into an invalid list */
+			BUG();
+		}
+
+		fumount_close( file );
+		file_list_lock();
+	}
+	file_list_unlock();
+	return;
+}
+#endif /* CONFIG_FUMOUNT */
+
 void __init files_init(unsigned long mempages)
 { =

 	int n; =

diff -Naur linux-2.6.11.6/fs/ioctl.c linux-2.6.11.6_mod/fs/ioctl.c
--- linux-2.6.11.6/fs/ioctl.c	2005-03-25 20:28:16.000000000 -0700
+++ linux-2.6.11.6_mod/fs/ioctl.c	2005-04-05 16:15:11.761532720 -0700
@@ -170,6 +170,15 @@
 	if (error)
 		goto out_fput;
 =

+#ifdef CONFIG_FUMOUNT
+	if (unlikely(filp->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+			/* allow no new references to this file */
+		error =3D -ENXIO;
+		goto out;
+	}
+#endif	=

+	=

 	error =3D vfs_ioctl(filp, fd, cmd, arg);
  out_fput:
 	fput_light(filp, fput_needed);
diff -Naur linux-2.6.11.6/fs/Kconfig linux-2.6.11.6_mod/fs/Kconfig
--- linux-2.6.11.6/fs/Kconfig	2005-03-25 20:28:22.000000000 -0700
+++ linux-2.6.11.6_mod/fs/Kconfig	2005-04-05 16:15:11.769531504 -0700
@@ -429,6 +429,14 @@
 	  local network, you probably do not need an automounter, and can say
 	  N here.
 =

+config FUMOUNT
+	bool "Forced Unmount support"
+	help
+      This option gives the ability to (really) force unmount a file
+	  system. It closes all the open files, 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 -Naur linux-2.6.11.6/fs/locks.c linux-2.6.11.6_mod/fs/locks.c
--- linux-2.6.11.6/fs/locks.c	2005-03-25 20:28:37.000000000 -0700
+++ linux-2.6.11.6_mod/fs/locks.c	2005-04-05 16:15:11.764532264 -0700
@@ -524,6 +524,19 @@
 	}
 }
 =

+#ifdef CONFIG_FUMOUNT
+/* count the waiters bound to file_ptr */ =

+static void =

+locks_waiters_count(struct file_lock *fl, int *count, struct file *file_pt=
r)
+{
+	struct file_lock *waiter;
+	list_for_each_entry(waiter, &fl->fl_block, fl_block) {
+		if (waiter->fl_file =3D=3D file_ptr)
+			(*count)++;
+	}
+}
+#endif
+
 /* Insert file lock fl into an inode's lock list at the position indicated
  * by pos. At the same time add the lock to the global file lock list.
  */
@@ -635,6 +648,12 @@
 	int result;
 	locks_insert_block(blocker, waiter);
 	result =3D interruptible_sleep_on_locked(&waiter->fl_wait, time);
+#ifdef CONFIG_FUMOUNT	=

+	if (unlikely(waiter->fl_file->f_mode & FMODE_FUMOUNT)) {
+		DEBUG_FUMOUNT;
+		result =3D -ENXIO;
+	}
+#endif	=

 	__locks_delete_block(waiter);
 	return result;
 }
@@ -754,6 +773,79 @@
 	return error;
 }
 =

+#ifdef CONFIG_FUMOUNT
+/*
+ * This function is called to chase out lock waiters attached
+ * to a given file object.  It waits to ensure the waiters =

+ * attached to file_ptr have exited the kernel before continuing.
+ * Locks associated with file_ptr are removed from the inode list.
+ * Hold BKL before calling
+ */
+static void =

+locks_unblock_remove_file(struct inode *inode_ptr, struct file *file_ptr)
+{
+	int count;
+	int starting_f_count;
+	struct file_lock *fl;
+	struct file_lock **before;
+	struct file_lock **next_before;
+	=

+	down_write(&file_ptr->f_vfsmnt->mnt_close_sem);
+
+	if (!inode_ptr->i_flock) {
+		up_write(&file_ptr->f_vfsmnt->mnt_close_sem);
+		return;
+	}
+
+	before =3D &inode_ptr->i_flock;
+	count =3D 0;
+	starting_f_count =3D file_count(file_ptr);
+
+	while ((fl =3D *before) !=3D NULL) {
+		locks_waiters_count(fl, &count, file_ptr);
+		next_before =3D &fl->fl_next;
+		if (fl->fl_file =3D=3D file_ptr) {
+			locks_delete_lock(before);
+		}
+		else {
+			locks_wake_up_blocks(fl);
+		}
+		before =3D next_before;
+	}
+	up_write(&file_ptr->f_vfsmnt->mnt_close_sem);
+}
+
+/* remove_file_locks is part of fumount.  This routine acquires the BKL, a=
nd
+ * 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 =
=3D
+ * TRUE.  This unblocks all of the waiters, causing them to check for fumo=
unt
+ * 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 po=
int,
+ * 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 =3D filp->f_dentry) ) {
+		if ( (inode_ptr =3D dentry_ptr->d_inode) ) {
+			locks_unblock_remove_file( inode_ptr, filp );
+		}
+	}
+
+	unlock_kernel();
+}
+#endif /* CONFIG_FUMOUNT */
+
 EXPORT_SYMBOL(posix_lock_file);
 =

 static int __posix_lock_file(struct inode *inode, struct file_lock *reques=
t)
@@ -1456,7 +1548,18 @@
 	int error;
 	might_sleep();
 	for (;;) {
+#ifdef CONFIG_FUMOUNT
+		if(unlikely(filp->f_mode & FMODE_FUMOUNT)) {
+			DEBUG_FUMOUNT;
+			error =3D -ENXIO;
+			break;
+		}
+		else {
+			error =3D flock_lock_file(filp, fl);
+		}
+#else
 		error =3D flock_lock_file(filp, fl);
+#endif		=

 		if ((error !=3D -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
 			break;
 		error =3D wait_event_interruptible(fl->fl_wait, !fl->fl_next);
diff -Naur linux-2.6.11.6/fs/namei.c linux-2.6.11.6_mod/fs/namei.c
--- linux-2.6.11.6/fs/namei.c	2005-03-25 20:28:18.000000000 -0700
+++ linux-2.6.11.6_mod/fs/namei.c	2005-04-05 16:15:11.752534088 -0700
@@ -28,6 +28,9 @@
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif /* CONFIG_FUMOUNT */
 #include <asm/namei.h>
 #include <asm/uaccess.h>
 =

@@ -510,6 +513,15 @@
 static inline int do_follow_link(struct dentry *dentry, struct nameidata *=
nd)
 {
 	int err =3D -ELOOP;
+
+#ifdef CONFIG_FUMOUNT
+	if (unlikely(!nd->mnt) ) {
+		DEBUG_FUMOUNT;
+		err =3D -ENXIO;
+		return err;
+	}
+#endif
+
 	if (current->link_count >=3D MAX_NESTED_LINKS)
 		goto loop;
 	if (current->total_link_count >=3D 40)
@@ -559,8 +571,20 @@
 	int res =3D 0;
 	while (d_mountpoint(*dentry)) {
 		struct vfsmount *mounted =3D lookup_mnt(*mnt, *dentry);
-		if (!mounted)
+		if (!mounted) {
+#ifdef CONFIG_FUMOUNT			=

+			DEBUG_FUMOUNT;
+			res =3D -ENXIO;
+#endif			=

+			break;		=

+		}
+#ifdef CONFIG_FUMOUNT		=

+		if (unlikely(mounted->mnt_flags & MNT_FUMOUNT)) {
+			DEBUG_FUMOUNT;
+			res =3D -ENXIO;
 			break;
+		}
+#endif		=

 		mntput(*mnt);
 		*mnt =3D mounted;
 		dput(*dentry);
@@ -579,11 +603,27 @@
 =

 	mounted =3D lookup_mnt(*mnt, *dentry);
 	if (mounted) {
+#ifdef CONFIG_FUMOUNT
+        struct dentry *old_dentry =3D *dentry;
+		DEBUG_FUMOUNT;
+        if( (*mnt =3D mntget(mounted)) )
+            *dentry =3D dget(mounted->mnt_root);
+        else
+            *dentry =3D (struct dentry *) NULL;
+        dput(old_dentry);
+        mntput(mounted->mnt_parent);
+        if ( *mnt )
+            return 1;
+        else
+            return 0;
+#else
 		mntput(*mnt);
 		*mnt =3D mounted;
 		dput(*dentry);
 		*dentry =3D dget(mounted->mnt_root);
 		return 1;
+
+#endif /* CONFIG_FUMOUNT */
 	}
 	return 0;
 }
@@ -645,6 +685,17 @@
 {
 	struct vfsmount *mnt =3D nd->mnt;
 	struct dentry *dentry =3D __d_lookup(nd->dentry, name);
+#ifdef CONFIG_FUMOUNT
+    int err;
+
+	/* Uh oh.  Walked into a pending FUMOUNT - follow_down
+	   has released parent mnt and dentry, so just bail */
+	if (unlikely(!nd->mnt)) {
+		DEBUG_FUMOUNT;
+		err =3D -ENXIO;
+		return err;
+	}
+#endif	=

 =

 	if (!dentry)
 		goto need_lookup;
@@ -680,6 +731,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)
 {
@@ -687,6 +750,13 @@
 	struct inode *inode;
 	int err;
 	unsigned int lookup_flags =3D nd->flags;
+
+#ifdef CONFIG_FUMOUNT
+	if (unlikely(!nd->mnt || name =3D=3D NULL)) {
+		DEBUG_FUMOUNT;
+		return -ENXIO;  /* outa' here if bad init_path */
+	}
+#endif
 	=

 	while (*name=3D=3D'/')
 		name++;
@@ -741,6 +811,13 @@
 				if (this.name[1] !=3D '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+#ifdef CONFIG_FUMOUNT				=

+				if (unlikely(!nd->mnt)) {
+					DEBUG_FUMOUNT;
+					err =3D -ENXIO;
+					goto return_err;
+				}
+#endif				=

 				inode =3D nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -761,8 +838,16 @@
 		if (err)
 			break;
 		/* Check mountpoints.. */
+#ifdef CONFIG_FUMOUNT
+		if (unlikely(follow_mount(&next.mnt, &next.dentry) < 0)) {
+			DEBUG_FUMOUNT;
+			err =3D -ENXIO;
+			break;
+		}
+#else
 		follow_mount(&next.mnt, &next.dentry);
-
+#endif
+		=

 		err =3D -ENOENT;
 		inode =3D next.dentry->d_inode;
 		if (!inode)
@@ -773,6 +858,13 @@
 =

 		if (inode->i_op->follow_link) {
 			mntget(next.mnt);
+#ifdef CONFIG_FUMOUNT			=

+			if (unlikely(next.mnt =3D=3D NULL)) {
+				DEBUG_FUMOUNT;
+				err =3D -ENXIO;
+				goto return_err;
+			}
+#endif			=

 			err =3D do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -809,6 +901,13 @@
 				if (this.name[1] !=3D '.')
 					break;
 				follow_dotdot(&nd->mnt, &nd->dentry);
+#ifdef CONFIG_FUMOUNT				=

+				if (unlikely(!nd->mnt) ) {
+					DEBUG_FUMOUNT;
+					err =3D -ENXIO;
+					goto return_err;
+				}
+#endif				=

 				inode =3D nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
@@ -822,11 +921,28 @@
 		err =3D do_lookup(nd, &this, &next);
 		if (err)
 			break;
+
+#ifdef CONFIG_FUMOUNT
+		if (unlikely(follow_mount(&next.mnt, &next.dentry) < 0)) {
+			DEBUG_FUMOUNT;
+			err =3D -ENXIO;
+			break;
+		}
+#else
 		follow_mount(&next.mnt, &next.dentry);
+#endif
+
 		inode =3D 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 (unlikely(next.mnt =3D=3D NULL)) {
+				DEBUG_FUMOUNT;
+				err =3D -ENXIO;
+				goto return_err;
+			}
+#endif			=

 			err =3D do_follow_link(next.dentry, nd);
 			dput(next.dentry);
 			mntput(next.mnt);
@@ -924,6 +1040,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 =3D __emul_prefix();
@@ -1437,6 +1555,15 @@
 		if (flag & O_NOFOLLOW)
 			goto exit_dput;
 		while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry));
+#ifdef CONFIG_FUMOUNT		=

+		/* Uh oh.  Walked into a pending FUMOUNT - follow_down
+		   has released parent mnt and dentry, so just bail */
+		if (unlikely(!nd->mnt)) {
+			DEBUG_FUMOUNT;
+			error =3D -ENXIO;
+			return error;
+		}
+#endif		=

 	}
 	error =3D -ENOENT;
 	if (!dentry->d_inode)
@@ -1575,7 +1702,10 @@
 	tmp =3D getname(filename);
 	if (IS_ERR(tmp))
 		return PTR_ERR(tmp);
-
+	=

+#ifdef CONFIG_FUMOUNT
+	down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D path_lookup(tmp, LOOKUP_PARENT, &nd);
 	if (error)
 		goto out;
@@ -1607,6 +1737,9 @@
 	up(&nd.dentry->d_inode->i_sem);
 	path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+	    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	putname(tmp);
 =

 	return error;
@@ -1647,6 +1780,10 @@
 		struct dentry *dentry;
 		struct nameidata nd;
 =

+#ifdef CONFIG_FUMOUNT
+        down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
+
 		error =3D path_lookup(tmp, LOOKUP_PARENT, &nd);
 		if (error)
 			goto out;
@@ -1661,6 +1798,9 @@
 		up(&nd.dentry->d_inode->i_sem);
 		path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		putname(tmp);
 	}
 =

@@ -1744,6 +1884,9 @@
 	if(IS_ERR(name))
 		return PTR_ERR(name);
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D path_lookup(name, LOOKUP_PARENT, &nd);
 	if (error)
 		goto exit;
@@ -1770,6 +1913,9 @@
 exit1:
 	path_release(&nd);
 exit:
+#ifdef CONFIG_FUMOUNT
+	up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	putname(name);
 	return error;
 }
@@ -1822,6 +1968,9 @@
 	if(IS_ERR(name))
 		return PTR_ERR(name);
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D path_lookup(name, LOOKUP_PARENT, &nd);
 	if (error)
 		goto exit;
@@ -1848,6 +1997,9 @@
 exit1:
 	path_release(&nd);
 exit:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	putname(name);
 	return error;
 =

@@ -1895,6 +2047,9 @@
 		struct dentry *dentry;
 		struct nameidata nd;
 =

+#ifdef CONFIG_FUMOUNT
+		down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		error =3D path_lookup(to, LOOKUP_PARENT, &nd);
 		if (error)
 			goto out;
@@ -1907,6 +2062,9 @@
 		up(&nd.dentry->d_inode->i_sem);
 		path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		putname(to);
 	}
 	putname(from);
@@ -1973,6 +2131,9 @@
 	if (IS_ERR(to))
 		return PTR_ERR(to);
 =

+#ifdef CONFIG_FUMOUNT
+	down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D __user_walk(oldname, 0, &old_nd);
 	if (error)
 		goto exit;
@@ -1994,6 +2155,9 @@
 out:
 	path_release(&old_nd);
 exit:
+#ifdef CONFIG_FUMOUNT
+	up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	putname(to);
 =

 	return error;
@@ -2155,6 +2319,9 @@
 	struct dentry * trap;
 	struct nameidata oldnd, newnd;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D path_lookup(oldname, LOOKUP_PARENT, &oldnd);
 	if (error)
 		goto exit;
@@ -2220,6 +2387,9 @@
 exit1:
 	path_release(&oldnd);
 exit:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

diff -Naur linux-2.6.11.6/fs/namespace.c linux-2.6.11.6_mod/fs/namespace.c
--- linux-2.6.11.6/fs/namespace.c	2005-03-25 20:28:23.000000000 -0700
+++ linux-2.6.11.6_mod/fs/namespace.c	2005-04-05 16:15:11.768531656 -0700
@@ -62,6 +62,9 @@
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
 		INIT_LIST_HEAD(&mnt->mnt_list);
 		INIT_LIST_HEAD(&mnt->mnt_fslink);
+#ifdef CONFIG_FUMOUNT			=

+		init_rwsem(&mnt->mnt_close_sem);
+#endif
 		if (name) {
 			int size =3D strlen(name)+1;
 			char *newname =3D kmalloc(size, GFP_KERNEL);
@@ -120,6 +123,9 @@
 	list_del_init(&mnt->mnt_child);
 	list_del_init(&mnt->mnt_hash);
 	old_nd->dentry->d_mounted--;
+#ifdef CONFIG_FUMOUNT			=

+	init_rwsem(&mnt->mnt_close_sem);
+#endif
 }
 =

 static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
@@ -175,6 +181,12 @@
 void __mntput(struct vfsmount *mnt)
 {
 	struct super_block *sb =3D mnt->mnt_sb;
+#ifdef CONFIG_FUMOUNT	=

+	if(unlikely(mnt =3D=3D NULL)) {
+		DEBUG_FUMOUNT;
+		return;
+	}
+#endif	=

 	dput(mnt->mnt_root);
 	free_vfsmnt(mnt);
 	deactivate_super(sb);
@@ -367,6 +379,9 @@
 static int do_umount(struct vfsmount *mnt, int flags)
 {
 	struct super_block * sb =3D mnt->mnt_sb;
+#ifdef CONFIG_FUMOUNT	=

+	int wait_count;
+#endif	=

 	int retval;
 =

 	retval =3D security_sb_umount(mnt, flags);
@@ -379,7 +394,7 @@
 	 *  (1) the mark is already set (the mark is cleared by mntput())
 	 *  (2) the usage count =3D=3D 1 [parent vfsmount] + 1 [sys_umount]
 	 */
-	if (flags & MNT_EXPIRE) {
+	if ((flags & MNT_EXPIRE) && !(flags & MNT_FFORCE) ) {
 		if (mnt =3D=3D current->fs->rootmnt ||
 		    flags & (MNT_FORCE | MNT_DETACH))
 			return -EINVAL;
@@ -402,8 +417,13 @@
 	 */
 =

 	lock_kernel();
-	if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
+#ifdef CONFIG_FUMOUNT	=

+	if (unlikely((flags & (MNT_FORCE | MNT_FFORCE)) && sb->s_op->umount_begin=
)) {
+#else	=

+	if ((flags & MNT_FORCE) && sb->s_op->umount_begin) {
+#endif
 		sb->s_op->umount_begin(sb);
+	}
 	unlock_kernel();
 =

 	/*
@@ -434,6 +454,9 @@
 	down_write(&current->namespace->sem);
 	spin_lock(&vfsmount_lock);
 =

+#ifdef CONFIG_FUMOUNT
+umount_retry:
+#endif	=

 	if (atomic_read(&sb->s_active) =3D=3D 1) {
 		/* last instance - try to be smart */
 		spin_unlock(&vfsmount_lock);
@@ -450,6 +473,145 @@
 			umount_tree(mnt);
 		retval =3D 0;
 	}
+
+#ifdef CONFIG_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 (unlikely(mnt !=3D current->fs->rootmnt =

+		    && (flags & MNT_FFORCE) =

+		    && (retval !=3D 0) =

+		    && !(mnt->mnt_flags & MNT_FUMOUNT) =

+		    && (list_empty(&mnt->mnt_mounts))) ) {
+
+			DEBUG_FUMOUNT;
+			/* stop additional references to the mount by setting
+			 * the MNT_FUMOUNT flag on the vfs mount context 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 -ENXIO.  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. Once the mount point has =

+			 * been marked, we can safely give up namespace semaphore until =

+			 * later, as any attempt to mount on or below the pending unmount
+			 * will fail on the attempt to walk the path.
+			 */
+			mnt->mnt_flags |=3D MNT_FUMOUNT;
+			=

+			/* 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 namespace semaphore - last
+			 * thing we need is for something to mount on the
+			 * subtree while trying to clean this up.  =

+			 */
+			fs_fumount_mark_files(mnt);
+			up_write(&current->namespace->sem);
+			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.  =

+			 */
+			printk(KERN_DEBUG "Mount reference count =3D %d\n", =

+					atomic_read(&mnt->mnt_count) );
+			current->state =3D TASK_UNINTERRUPTIBLE;
+			schedule_timeout(5*HZ);
+			  =

+			printk(KERN_DEBUG "Back from delay, looking for open files\n");
+			printk(KERN_DEBUG "Mount reference count =3D %d\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 fs_mount_close() releases them back to
+				 * the pool.  */
+				retval =3D fs_fumount_clone_list(sb, mnt); =

+				fs_fumount_close();
+			} while (retval);
+			printk(KERN_DEBUG "after clone mount reference count =3D %d\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 =3D=3D mnt ) {
+							set_fs_pwd( task_ptr->fs, =

+								(struct vfsmount *)NULL,
+								(struct dentry *)NULL );
+						}
+					}
+					if ( atomic_read(&mnt->mnt_count) =3D=3D 2 )
+						break;
+				} =

+				read_unlock( &tasklist_lock );
+			}
+			wait_count =3D 100;
+			printk(KERN_DEBUG "Mount reference count =3D %d\n", =

+					atomic_read(&mnt->mnt_count) );
+
+			while (atomic_read(&mnt->mnt_count) > 2 && wait_count >0 ) { =

+				current->state =3D TASK_UNINTERRUPTIBLE; =

+				schedule_timeout(5*HZ); =

+				wait_count--; =

+				printk(KERN_DEBUG "Mount reference count =3D %d wait counter =3D "
+						"%d\n", atomic_read(&mnt->mnt_count), wait_count );
+			}
+			down_write(&current->namespace->sem);
+
+			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 =3D %d\n", =

+					atomic_read(&mnt->mnt_count) );
+			spin_lock(&vfsmount_lock);
+			goto umount_retry;
+		}
+#endif /* CONFIG_FUMOUNT */	=

+
 	spin_unlock(&vfsmount_lock);
 	if (retval)
 		security_sb_umount_busy(mnt);
@@ -708,6 +870,11 @@
 	down_write(&current->namespace->sem);
 	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
 		;
+#ifdef CONFIG_FUMOUNT
+	err =3D -ENXIO;
+	if (unlikely(!nd->mnt))
+		goto out;
+#endif /*CONFIG_FUMOUNT*/
 	err =3D -EINVAL;
 	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
 		goto out;
@@ -792,6 +959,11 @@
 	/* Something was mounted here while we slept */
 	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
 		;
+#ifdef CONFIG_FUMOUNT
+	err =3D -ENXIO;
+	if (unlikely(!nd->mnt))
+		goto unlock;
+#endif /*CONFIG_FUMOUNT*/
 	err =3D -EINVAL;
 	if (!check_mnt(nd->mnt))
 		goto unlock;
@@ -887,7 +1059,9 @@
 			list_del_init(&mnt->mnt_child);
 			list_del_init(&mnt->mnt_hash);
 			mnt->mnt_mountpoint->d_mounted--;
-
+#ifdef CONFIG_FUMOUNT			=

+	init_rwsem(&mnt->mnt_close_sem);
+#endif
 			xdentry =3D mnt->mnt_mountpoint;
 			mnt->mnt_mountpoint =3D mnt->mnt_root;
 			xdmnt =3D mnt->mnt_parent;
@@ -1221,8 +1395,10 @@
 	write_lock(&fs->lock);
 	old_pwd =3D fs->pwd;
 	old_pwdmnt =3D fs->pwdmnt;
-	fs->pwdmnt =3D mntget(mnt);
-	fs->pwd =3D dget(dentry);
+	if ( (fs->pwdmnt =3D mntget(mnt)) )
+		fs->pwd =3D dget(dentry);
+	else
+		fs->pwd =3D (struct dentry *)NULL;
 	write_unlock(&fs->lock);
 =

 	if (old_pwd) {
diff -Naur linux-2.6.11.6/fs/open.c linux-2.6.11.6_mod/fs/open.c
--- linux-2.6.11.6/fs/open.c	2005-03-25 20:28:14.000000000 -0700
+++ linux-2.6.11.6_mod/fs/open.c	2005-04-05 16:17:54.097853848 -0700
@@ -15,6 +15,9 @@
 #include <linux/slab.h>
 #include <linux/tty.h>
 #include <linux/namei.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif /* CONFIG_FUMOUNT */
 #include <linux/backing-dev.h>
 #include <linux/security.h>
 #include <linux/mount.h>
@@ -122,6 +125,9 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
 	if (!error) {
 		struct statfs tmp;
@@ -130,6 +136,9 @@
 			error =3D -EFAULT;
 		path_release(&nd);
 	}
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -220,6 +229,9 @@
 	if (length < 0)	/* sorry, but loff_t says... */
 		goto out;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
 	if (error)
 		goto out;
@@ -267,6 +279,9 @@
 dput_and_out:
 	path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -357,6 +372,9 @@
 	struct inode * inode;
 	struct iattr newattrs;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(filename, &nd);
 	if (error)
 		goto out;
@@ -397,6 +415,9 @@
 dput_and_out:
 	path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -413,6 +434,9 @@
 	struct inode * inode;
 	struct iattr newattrs;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(filename, &nd);
 =

 	if (error)
@@ -450,6 +474,9 @@
 dput_and_out:
 	path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -498,6 +525,9 @@
 	else
 		current->cap_effective =3D current->cap_permitted;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	res =3D __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
 	if (!res) {
 		res =3D permission(nd.dentry->d_inode, mode, &nd);
@@ -508,6 +538,9 @@
 		path_release(&nd);
 	}
 =

+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	current->fsuid =3D old_fsuid;
 	current->fsgid =3D old_fsgid;
 	current->cap_effective =3D old_cap;
@@ -634,6 +667,9 @@
 	int error;
 	struct iattr newattrs;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(filename, &nd);
 	if (error)
 		goto out;
@@ -658,6 +694,9 @@
 dput_and_out:
 	path_release(&nd);
 out:
+#ifdef CONFIG_FUMOUNT
+	    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -701,11 +740,18 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(filename, &nd);
 	if (!error) {
 		error =3D chown_common(nd.dentry, user, group);
 		path_release(&nd);
 	}
+
+#ifdef CONFIG_FUMOUNT
+	    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -714,11 +760,17 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(filename, &nd);
 	if (!error) {
 		error =3D chown_common(nd.dentry, user, group);
 		path_release(&nd);
 	}
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -794,7 +846,19 @@
 	f->f_vfsmnt =3D mnt;
 	f->f_pos =3D 0;
 	f->f_op =3D fops_get(inode->i_fop);
+
+#ifdef CONFIG_FUMOUNT	=

+		error =3D file_move_test(f, inode->i_sb);
+		if (error) {
+			DEBUG_FUMOUNT;
+#if 0			=

+			printk(KERN_DEBUG "Disallowed file open due to pending unmount\n");
+#endif			=

+			goto cleanup_all;
+		}
+#else
 	file_move(f, &inode->i_sb->s_files);
+#endif
 =

 	if (f->f_op && f->f_op->open) {
 		error =3D f->f_op->open(inode,f);
@@ -944,10 +1008,16 @@
 		fd =3D get_unused_fd();
 		if (fd >=3D 0) {
 			struct file *f =3D filp_open(tmp, flags, mode);
+#ifdef CONFIG_FUMOUNT
+            down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 			error =3D PTR_ERR(f);
 			if (IS_ERR(f))
 				goto out_error;
 			fd_install(fd, f);
+#ifdef CONFIG_FUMOUNT
+			up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		}
 out:
 		putname(tmp);
@@ -955,6 +1025,9 @@
 	return fd;
 =

 out_error:
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	put_unused_fd(fd);
 	fd =3D error;
 	goto out;
@@ -998,14 +1071,56 @@
 			retval =3D err;
 	}
 =

+#ifdef CONFIG_FUMOUNT
+	if (!(filp->f_mode & FMODE_DEFUNCT) ) {
+		dnotify_flush(filp, id);
+		locks_remove_posix(filp, id);
+		fput(filp);
+	}
+	else
+		fumount_fput(filp);
+#else
 	dnotify_flush(filp, id);
 	locks_remove_posix(filp, id);
 	fput(filp);
+#endif
+		=

 	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;
+
+	DEBUG_FUMOUNT;
+
+	if (!file_count(filp)) {
+		printk(KERN_ERR "VFS: Close: file count is 0\n");
+		return;
+	}
+	retval =3D 0;
+	if (filp->f_op && filp->f_op->flush) {
+		lock_kernel();
+		retval =3D filp->f_op->flush(filp);
+		unlock_kernel();
+	}
+	fumount_dnotify_flush(filp);
+	fput(filp);
+}
+#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
@@ -1013,8 +1128,13 @@
  */
 asmlinkage long sys_close(unsigned int fd)
 {
-	struct file * filp;
+	struct file * filp =3D NULL;
 	struct files_struct *files =3D current->files;
+	int ret_code;
+#ifdef CONFIG_FUMOUNT	=

+	int semaphore_flag =3D 0;
+	struct vfsmount *mnt =3D NULL;
+#endif	=

 =

 	spin_lock(&files->file_lock);
 	if (fd >=3D files->max_fds)
@@ -1022,15 +1142,47 @@
 	filp =3D files->fd[fd];
 	if (!filp)
 		goto out_unlock;
+#ifdef CONFIG_FUMOUNT
+	mnt =3D filp->f_vfsmnt;
+	if(mnt) {
+		down_read(&filp->f_vfsmnt->mnt_close_sem);
+		semaphore_flag =3D 1;
+	}
+#endif
 	files->fd[fd] =3D NULL;
 	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
+    if (!(filp->f_mode & FMODE_DEFUNCT) =

+			&& !(S_ISFUMOUNTABLE(filp->f_dentry->d_inode->i_mode))) {
+	/* Release semaphore so system doesn't pause behind non-fumountable
+     * file types.  For example, a tape drive could take a long time
+	 * to close if it does a rewind before completing the close
+	 * action on the final fput
+	 */
+		if(semaphore_flag && mnt) {
+			semaphore_flag =3D 0;
+			up_read(&mnt->mnt_close_sem);
+		}
+	}
+#endif	=

+
+	ret_code =3D filp_close(filp, files);
+
+exit_sys_close:
+#ifdef CONFIG_FUMOUNT				   =

+	if (semaphore_flag && mnt) {
+		up_read(&mnt->mnt_close_sem);
+	}
+#endif			   =

+	return ret_code;
 =

 out_unlock:
 	spin_unlock(&files->file_lock);
-	return -EBADF;
+	ret_code =3D  -EBADF;
+	goto exit_sys_close;
 }
 =

 EXPORT_SYMBOL(sys_close);
diff -Naur linux-2.6.11.6/fs/stat.c linux-2.6.11.6_mod/fs/stat.c
--- linux-2.6.11.6/fs/stat.c	2005-03-25 20:28:16.000000000 -0700
+++ linux-2.6.11.6_mod/fs/stat.c	2005-04-05 16:15:11.753533936 -0700
@@ -13,6 +13,9 @@
 #include <linux/highuid.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif
 #include <linux/security.h>
 #include <linux/syscalls.h>
 =

@@ -68,11 +71,17 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(name, &nd);
 	if (!error) {
 		error =3D vfs_getattr(nd.mnt, nd.dentry, stat);
 		path_release(&nd);
 	}
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -83,11 +92,17 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(name, &nd);
 	if (!error) {
 		error =3D vfs_getattr(nd.mnt, nd.dentry, stat);
 		path_release(&nd);
 	}
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -268,6 +283,9 @@
 	if (bufsiz <=3D 0)
 		return -EINVAL;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(path, &nd);
 	if (!error) {
 		struct inode * inode =3D nd.dentry->d_inode;
@@ -282,6 +300,9 @@
 		}
 		path_release(&nd);
 	}
+#ifdef CONFIG_FUMOUNT
+	    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

diff -Naur linux-2.6.11.6/fs/xattr.c linux-2.6.11.6_mod/fs/xattr.c
--- linux-2.6.11.6/fs/xattr.c	2005-03-25 20:28:19.000000000 -0700
+++ linux-2.6.11.6_mod/fs/xattr.c	2005-04-05 16:15:11.761532720 -0700
@@ -13,6 +13,9 @@
 #include <linux/file.h>
 #include <linux/xattr.h>
 #include <linux/namei.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/namespace.h>
+#endif
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/module.h>
@@ -74,11 +77,17 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
 	if (error)
 		return error;
 	error =3D setxattr(nd.dentry, name, value, size, flags);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -89,11 +98,21 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+		up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}		=

 	error =3D setxattr(nd.dentry, name, value, size, flags);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -164,11 +183,21 @@
 	struct nameidata nd;
 	ssize_t error;
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D getxattr(nd.dentry, name, value, size);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -179,11 +208,21 @@
 	struct nameidata nd;
 	ssize_t error;
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D getxattr(nd.dentry, name, value, size);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -245,11 +284,21 @@
 	struct nameidata nd;
 	ssize_t error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D listxattr(nd.dentry, list, size);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -259,11 +308,21 @@
 	struct nameidata nd;
 	ssize_t error;
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+	up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D listxattr(nd.dentry, list, size);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -315,11 +374,21 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+        up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D removexattr(nd.dentry, name);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

@@ -329,11 +398,21 @@
 	struct nameidata nd;
 	int error;
 =

+#ifdef CONFIG_FUMOUNT
+	    down_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	error =3D user_path_walk_link(path, &nd);
-	if (error)
+	if (error) {
+#ifdef CONFIG_FUMOUNT
+		up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 		return error;
+	}
 	error =3D removexattr(nd.dentry, name);
 	path_release(&nd);
+#ifdef CONFIG_FUMOUNT
+    up_read(&current->namespace->sem);
+#endif /* CONFIG_FUMOUNT */
 	return error;
 }
 =

diff -Naur linux-2.6.11.6/include/linux/dnotify.h linux-2.6.11.6_mod/includ=
e/linux/dnotify.h
--- linux-2.6.11.6/include/linux/dnotify.h	2005-03-25 20:28:14.000000000 -0=
700
+++ linux-2.6.11.6_mod/include/linux/dnotify.h	2005-04-05 16:15:11.74853469=
6 -0700
@@ -22,6 +22,9 @@
 =

 #ifdef CONFIG_DNOTIFY
 =

+#ifdef CONFIG_FUMOUNT
+extern void fumount_dnotify_flush(struct file *filp);
+#endif
 extern void __inode_dir_notify(struct inode *, unsigned long);
 extern void dnotify_flush(struct file *, fl_owner_t);
 extern int fcntl_dirnotify(int, struct file *, unsigned long);
diff -Naur linux-2.6.11.6/include/linux/file.h linux-2.6.11.6_mod/include/l=
inux/file.h
--- linux-2.6.11.6/include/linux/file.h	2005-03-25 20:28:39.000000000 -0700
+++ linux-2.6.11.6_mod/include/linux/file.h	2005-04-05 16:15:11.754533784 -=
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
 static inline void fput_light(struct file *file, int fput_needed)
 {
 	if (unlikely(fput_needed))
diff -Naur linux-2.6.11.6/include/linux/fs.h linux-2.6.11.6_mod/include/lin=
ux/fs.h
--- linux-2.6.11.6/include/linux/fs.h	2005-03-25 20:28:17.000000000 -0700
+++ linux-2.6.11.6_mod/include/linux/fs.h	2005-04-05 16:15:11.756533480 -07=
00
@@ -10,6 +10,16 @@
 #include <linux/limits.h>
 #include <linux/ioctl.h>
 =

+#ifdef CONFIG_FUMOUNT
+#include <linux/mount.h>
+#undef FUDEBUG
+#ifdef FUDEBUG
+#define DEBUG_FUMOUNT printk("FUmount: (%s, %d), %s\n", __FILE__, __LINE__=
, __func__);
+#else
+#define DEBUG_FUMOUNT
+#endif /* FUDEBUG */
+#endif /* CONFIG_FUMOUNT */
+
 /*
  * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
  * the file limit at runtime and only root can increase the per-process
@@ -63,6 +73,11 @@
 #define FMODE_LSEEK	4
 #define FMODE_PREAD	8
 #define FMODE_PWRITE	FMODE_PREAD	/* These go hand in hand */
+/* next two mode flags are for forced umount */
+#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
@@ -608,6 +623,10 @@
 #define get_file(x)	atomic_inc(&(x)->f_count)
 #define file_count(x)	atomic_read(&(x)->f_count)
 =

+#ifdef CONFIG_FUMOUNT
+extern void fumount_close(struct file *);
+extern void fs_fumount_close( void );
+#endif
 #define	MAX_NON_LFS	((1UL<<31) - 1)
 =

 /* Page cache limit. The filesystems should put that into their s_maxbytes =

@@ -745,6 +764,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;
@@ -1329,6 +1349,10 @@
 =

 extern int fs_may_remount_ro(struct super_block *);
 =

+#ifdef CONFIG_FUMOUNT
+extern int fs_fumount_clone_list(struct super_block *, struct vfsmount *);
+#endif
+
 /*
  * return READ, READA, or WRITE
  */
@@ -1444,6 +1468,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
 extern void file_kill(struct file *f);
 struct bio;
 extern void submit_bio(int, struct bio *);
diff -Naur linux-2.6.11.6/include/linux/mount.h linux-2.6.11.6_mod/include/=
linux/mount.h
--- linux-2.6.11.6/include/linux/mount.h	2005-03-25 20:28:15.000000000 -0700
+++ linux-2.6.11.6_mod/include/linux/mount.h	2005-04-05 16:15:11.754533784 =
-0700
@@ -14,11 +14,15 @@
 =

 #include <linux/list.h>
 #include <linux/spinlock.h>
+#ifdef CONFIG_FUMOUNT
+#include <linux/rwsem.h>
+#endif
 #include <asm/atomic.h>
 =

 #define MNT_NOSUID	1
 #define MNT_NODEV	2
 #define MNT_NOEXEC	4
+#define MNT_FUMOUNT 1<<16 /* set when forcibly removing mount */
 =

 struct vfsmount
 {
@@ -36,14 +40,39 @@
 	struct list_head mnt_list;
 	struct list_head mnt_fslink;	/* link in fs-specific expiry list */
 	struct namespace *mnt_namespace; /* containing namespace */
+#ifdef CONFIG_FUMOUNT
+	struct rw_semaphore mnt_close_sem;
+#endif
 };
 =

+#ifdef CONFIG_FUMOUNT
+extern void fs_fumount_mark_files(struct vfsmount *);
+
+/* mntget checks that the parameter is not NULL, and now checks to
+   see that the mount 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_flags & MNT_FUMOUNT ) ){
+                atomic_inc(&mnt->mnt_count);
+        }
+        else {
+                mnt =3D (struct vfsmount *) NULL;
+        }
+        return mnt;
+}
+#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 -Naur linux-2.6.11.6/include/linux/stat.h linux-2.6.11.6_mod/include/l=
inux/stat.h
--- linux-2.6.11.6/include/linux/stat.h	2005-03-25 20:28:18.000000000 -0700
+++ linux-2.6.11.6_mod/include/linux/stat.h	2005-04-05 16:15:11.769531504 -=
0700
@@ -28,6 +28,10 @@
 #define S_ISBLK(m)	(((m) & S_IFMT) =3D=3D S_IFBLK)
 #define S_ISFIFO(m)	(((m) & S_IFMT) =3D=3D S_IFIFO)
 #define S_ISSOCK(m)	(((m) & S_IFMT) =3D=3D S_IFSOCK)
+#ifdef CONFIG_FUMOUNT
+#define S_ISFUMOUNTABLE(m) ( (S_ISLNK(m)) || (S_ISREG(m)) || (S_ISDIR(m))\
+		|| (S_ISFIFO(m)) )
+#endif
 =

 #define S_IRWXU 00700
 #define S_IRUSR 00400
diff -Naur linux-2.6.11.6/kernel/fork.c linux-2.6.11.6_mod/kernel/fork.c
--- linux-2.6.11.6/kernel/fork.c	2005-03-25 20:28:15.000000000 -0700
+++ linux-2.6.11.6_mod/kernel/fork.c	2005-04-05 16:15:11.760532872 -0700
@@ -505,8 +505,10 @@
 		read_lock(&old->lock);
 		fs->rootmnt =3D mntget(old->rootmnt);
 		fs->root =3D dget(old->root);
-		fs->pwdmnt =3D mntget(old->pwdmnt);
-		fs->pwd =3D dget(old->pwd);
+		if ( (fs->pwdmnt =3D mntget(old->pwdmnt)))
+			fs->pwd =3D dget(old->pwd);
+		else
+			fs->pwd =3D (struct dentry *)NULL;
 		if (old->altroot) {
 			fs->altrootmnt =3D mntget(old->altrootmnt);
 			fs->altroot =3D dget(old->altroot);
diff -Naur linux-2.6.11.6/mm/mmap.c linux-2.6.11.6_mod/mm/mmap.c
--- linux-2.6.11.6/mm/mmap.c	2005-03-25 20:28:23.000000000 -0700
+++ linux-2.6.11.6_mod/mm/mmap.c	2005-04-05 16:15:11.749534544 -0700
@@ -1878,6 +1878,204 @@
 #endif
 }
 =

+#ifdef CONFIG_FUMOUNT
+/* 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 we take the mmap_sem semaphore.  =

+ * We 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.
+ */
+static int remove_file_map(struct file *file, struct mm_struct *mm_ptr)
+{
+	int ret_code;
+
+	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 =3D mm_ptr->mmap; vma_ptr; vma_ptr =3D next_vma_ptr) {
+			next_vma_ptr =3D vma_ptr->vm_next;
+			if (vma_ptr->vm_file =3D=3D file) {
+				ret_code =3D 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);
+		mmdrop(mm_ptr);
+	}
+	return 0;
+}
+
+static int remove_shared_file_mappings(struct file *file,
+	struct address_space *addr_space_ptr)
+{
+	struct mm_struct *mm_ptr;
+	struct vm_area_struct *vma_ptr;
+	struct prio_tree_iter iter;
+	int ret_code =3D 0;
+
+	DEBUG_FUMOUNT;
+	if (prio_tree_empty(&addr_space_ptr->i_mmap))
+		return 0;
+	spin_lock(&addr_space_ptr->i_mmap_lock);
+
+	while(!prio_tree_empty(&addr_space_ptr->i_mmap)) {
+		/* =

+		* I think the r_index (aka begin) should be 0 and h_index =

+		* (aka end should be ULONG_MAX to search the entire tree. =

+		* Hopefully I'm right.... =

+		*/
+		vma_prio_tree_foreach(vma_ptr, &iter, =

+				&addr_space_ptr->i_mmap, 0, ULONG_MAX) {
+			if(vma_ptr->vm_file =3D=3D file)
+				break;
+		}
+
+		if(vma_ptr)
+		{
+			printk(KERN_DEBUG "found shared map\n");
+			mm_ptr =3D vma_ptr->vm_mm;
+			spin_unlock(&addr_space_ptr->i_mmap_lock);
+			ret_code =3D remove_file_map(file, mm_ptr);
+			if (ret_code) =

+				break;
+
+			spin_lock(&addr_space_ptr->i_mmap_lock);
+		}
+		else
+		{
+			printk(KERN_WARNING "%s - VM area found without mm\n", __func__);
+		}
+	}
+
+	spin_unlock(&addr_space_ptr->i_mmap_lock);
+	return ret_code;
+}
+
+/* =

+ */
+static int remove_nonlinear_mappings( struct file *file,
+	struct address_space *addr_space_ptr)
+{
+	struct list_head *ptr, *temp;
+	struct vm_area_struct *vma_ptr =3D NULL;
+	struct mm_struct *mm_ptr;
+	int ret_code;
+
+	DEBUG_FUMOUNT;
+
+	spin_lock(&addr_space_ptr->i_mmap_lock);
+	=

+	while (!list_empty(&addr_space_ptr->i_mmap_nonlinear)) {
+		list_for_each_safe(ptr, temp, &addr_space_ptr->i_mmap_nonlinear) {
+			vma_ptr =3D list_entry(ptr, struct vm_area_struct, anon_vma_node);
+			if((ptr =3D=3D &addr_space_ptr->i_mmap_nonlinear) ||
+					(vma_ptr->vm_file =3D=3D file))
+				break;
+		}
+
+		if (vma_ptr->vm_file !=3D file)
+			break;
+
+		printk(KERN_DEBUG "found anon map\n");
+		mm_ptr =3D vma_ptr->vm_mm;
+
+		spin_unlock(&addr_space_ptr->i_mmap_lock);
+		ret_code =3D remove_file_map(file, mm_ptr);
+		if (ret_code)
+			return ret_code;
+		spin_lock(&addr_space_ptr->i_mmap_lock);
+	}
+
+	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 cr=
eated
+ * 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;
+    struct dentry *dentry_ptr;
+    struct inode *inode_ptr;
+
+	int ret_code;
+
+	DEBUG_FUMOUNT;
+	printk(KERN_DEBUG "Remove_file_mappings called.\n");
+	if (!file ) =

+	      return -EBADF;
+
+	dentry_ptr =3D file->f_dentry;
+    if (!dentry_ptr) =

+		return -EBADF;
+
+	inode_ptr =3D dentry_ptr->d_inode;
+    if (!inode_ptr) =

+		return -EBADF;
+
+	addr_space_ptr =3D file->f_mapping;
+check_for_maps:
+	ret_code =3D 0;
+	if (!addr_space_ptr) =

+	      return -EBADF;
+	=

+	if (!prio_tree_empty(&addr_space_ptr->i_mmap)) {
+		ret_code =3D remove_shared_file_mappings(file, addr_space_ptr);
+		if (ret_code) {
+			printk(KERN_DEBUG "%s: incomplete shared map removal, retry\n", __func_=
_);
+			goto check_for_maps;
+		}
+	}
+
+	if (!list_empty(&addr_space_ptr->i_mmap_nonlinear) ) {
+		ret_code =3D remove_nonlinear_mappings(file, addr_space_ptr);
+		if (ret_code) {
+			printk(KERN_DEBUG "%s: incomplete nonlinear map removal, retry\n", __fu=
nc__);
+			goto check_for_maps;
+		}
+	}
+	=

+	addr_space_ptr =3D file->f_mapping;
+	if(!prio_tree_empty(&addr_space_ptr->i_mmap) ||
+			!list_empty(&addr_space_ptr->i_mmap_nonlinear))
+	{
+		printk("%s: there are still mappings\n", __func__);
+		goto check_for_maps;
+	}
+	=

+	printk(KERN_DEBUG "%s: no more mappings\n", __func__);
+	return ret_code;
+}
+
+#endif /* CONFIG_FUMOUNT */
+
 /*
  *  this is really a simplified "do_mmap".  it only handles
  *  anonymous maps.  eventually we may be able to do some


More information about the cgl_discussion mailing list