]> git.hungrycats.org Git - linux/commitdiff
sysfs: fix oops in directory removal.
authorPatrick Mochel <mochel@osdl.org>
Mon, 3 Mar 2003 03:43:37 +0000 (21:43 -0600)
committerPatrick Mochel <mochel@osdl.org>
Mon, 3 Mar 2003 03:43:37 +0000 (21:43 -0600)
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.

fs/sysfs/inode.c

index df09960cd1eafb16a8d17132eadd8c8554f09a78..6b3427f50039f842c0b2d148de79add96293336b 100644 (file)
@@ -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);