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(¤t->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(¤t->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(¤t->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;