summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2003-03-02 21:43:37 -0600
committerPatrick Mochel <mochel@osdl.org>2003-03-02 21:43:37 -0600
commit2201a8156bb22abb8e64f8aa34e4dcb4406b8e1d (patch)
tree132b0d9ef2d04e17c2505be5e5e12af8062d30b5
parentab6671e98f80025ed95da3bfcc214b6808717460 (diff)
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.
-rw-r--r--fs/sysfs/inode.c44
1 files changed, 19 insertions, 25 deletions
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index df09960cd1ea..6b3427f50039 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -98,10 +98,9 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
if (!dentry->d_inode) {
inode = sysfs_get_inode(dir->i_sb, mode, dev);
- if (inode) {
+ if (inode)
d_instantiate(dentry, inode);
- dget(dentry);
- } else
+ else
error = -ENOSPC;
} else
error = -EEXIST;
@@ -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,
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)
void sysfs_remove_dir(struct kobject * kobj)
{
- struct list_head * node, * next;
+ struct list_head * node;
struct dentry * dentry = dget(kobj->dentry);
struct dentry * parent;
@@ -807,32 +802,31 @@ void sysfs_remove_dir(struct kobject * kobj)
down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem);
- list_for_each_safe(node,next,&dentry->d_subdirs) {
- struct dentry * d = dget(list_entry(node,struct dentry,d_child));
- /**
- * Make sure dentry is still there
- */
- pr_debug(" o %s: ",d->d_name.name);
- if (d->d_inode) {
+ spin_lock(&dcache_lock);
+ node = dentry->d_subdirs.next;
+ while (node != &dentry->d_subdirs) {
+ struct dentry * d = list_entry(node,struct dentry,d_child);
+ list_del_init(node);
+ 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");
+
/**
* Unlink and unhash.
*/
- simple_unlink(dentry->d_inode,d);
+ spin_unlock(&dcache_lock);
d_delete(d);
-
- /**
- * Drop reference from initial get_dentry().
- */
+ simple_unlink(dentry->d_inode,d);
dput(d);
+ spin_lock(&dcache_lock);
}
- pr_debug(" done (%d)\n",atomic_read(&d->d_count));
- /**
- * drop reference from dget() above.
- */
- dput(d);
+ pr_debug(" done\n");
+
+ node = dentry->d_subdirs.next;
}
+ spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);
d_invalidate(dentry);