diff options
| author | Patrick Mochel <mochel@osdl.org> | 2003-03-02 21:43:37 -0600 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2003-03-02 21:43:37 -0600 |
| commit | 2201a8156bb22abb8e64f8aa34e4dcb4406b8e1d (patch) | |
| tree | 132b0d9ef2d04e17c2505be5e5e12af8062d30b5 | |
| parent | ab6671e98f80025ed95da3bfcc214b6808717460 (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.c | 44 |
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); |
