Commit 2201a815 authored by Patrick Mochel's avatar Patrick Mochel

sysfs: fix oops in directory removal.

If a file that doesn't exist was looked up in a directory, and that
directory is later removed, sysfs would reap the negative dentrys along 
with the valid ones.

Fix is to manually remove the dentrys from the parent's list under the 
dcache_lock, then check if they're valid with dget_locked(). 

This ensures all the dentrys are removed, valid and invalid, and we don't 
reap anything we shouldn't.
parent ab6671e9
...@@ -98,10 +98,9 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t ...@@ -98,10 +98,9 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
if (!dentry->d_inode) { if (!dentry->d_inode) {
inode = sysfs_get_inode(dir->i_sb, mode, dev); inode = sysfs_get_inode(dir->i_sb, mode, dev);
if (inode) { if (inode)
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dget(dentry); else
} else
error = -ENOSPC; error = -ENOSPC;
} else } else
error = -EEXIST; error = -EEXIST;
...@@ -703,10 +702,6 @@ static void hash_and_remove(struct dentry * dir, const char * name) ...@@ -703,10 +702,6 @@ static void hash_and_remove(struct dentry * dir, const char * name)
pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name, pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name,
atomic_read(&victim->d_count)); atomic_read(&victim->d_count));
/**
* Drop reference from initial get_dentry().
*/
dput(victim);
} }
/** /**
...@@ -795,7 +790,7 @@ void sysfs_remove_link(struct kobject * kobj, char * name) ...@@ -795,7 +790,7 @@ void sysfs_remove_link(struct kobject * kobj, char * name)
void sysfs_remove_dir(struct kobject * kobj) void sysfs_remove_dir(struct kobject * kobj)
{ {
struct list_head * node, * next; struct list_head * node;
struct dentry * dentry = dget(kobj->dentry); struct dentry * dentry = dget(kobj->dentry);
struct dentry * parent; struct dentry * parent;
...@@ -807,32 +802,31 @@ void sysfs_remove_dir(struct kobject * kobj) ...@@ -807,32 +802,31 @@ void sysfs_remove_dir(struct kobject * kobj)
down(&parent->d_inode->i_sem); down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem); down(&dentry->d_inode->i_sem);
list_for_each_safe(node,next,&dentry->d_subdirs) { spin_lock(&dcache_lock);
struct dentry * d = dget(list_entry(node,struct dentry,d_child)); node = dentry->d_subdirs.next;
/** while (node != &dentry->d_subdirs) {
* Make sure dentry is still there struct dentry * d = list_entry(node,struct dentry,d_child);
*/ list_del_init(node);
pr_debug(" o %s: ",d->d_name.name);
if (d->d_inode) {
pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count));
if (d->d_inode) {
d = dget_locked(d);
pr_debug("removing"); pr_debug("removing");
/** /**
* Unlink and unhash. * Unlink and unhash.
*/ */
simple_unlink(dentry->d_inode,d); spin_unlock(&dcache_lock);
d_delete(d); d_delete(d);
simple_unlink(dentry->d_inode,d);
/**
* Drop reference from initial get_dentry().
*/
dput(d); dput(d);
spin_lock(&dcache_lock);
} }
pr_debug(" done (%d)\n",atomic_read(&d->d_count)); pr_debug(" done\n");
/**
* drop reference from dget() above. node = dentry->d_subdirs.next;
*/
dput(d);
} }
spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem); up(&dentry->d_inode->i_sem);
d_invalidate(dentry); d_invalidate(dentry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment