]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/configfs/dir.c
[PATCH] configfs: Fix failing symlink() making rmdir() fail
[linux-2.6-omap-h63xx.git] / fs / configfs / dir.c
index 0e64312a084ccfad43cce1ea9bbbbab8dde991b8..4e228c80fe9fa02446ccc2ea4d742d2f7067fcd0 100644 (file)
@@ -370,6 +370,9 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex
        struct configfs_dirent *sd;
        int ret;
 
+       /* Mark that we're trying to drop the group */
+       parent_sd->s_type |= CONFIGFS_USET_DROPPING;
+
        ret = -EBUSY;
        if (!list_empty(&parent_sd->s_links))
                goto out;
@@ -385,8 +388,6 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex
                                        *wait_mutex = &sd->s_dentry->d_inode->i_mutex;
                                return -EAGAIN;
                        }
-                       /* Mark that we're trying to drop the group */
-                       sd->s_type |= CONFIGFS_USET_DROPPING;
 
                        /*
                         * Yup, recursive.  If there's a problem, blame
@@ -414,12 +415,11 @@ static void configfs_detach_rollback(struct dentry *dentry)
        struct configfs_dirent *parent_sd = dentry->d_fsdata;
        struct configfs_dirent *sd;
 
-       list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
-               if (sd->s_type & CONFIGFS_USET_DEFAULT) {
+       parent_sd->s_type &= ~CONFIGFS_USET_DROPPING;
+
+       list_for_each_entry(sd, &parent_sd->s_children, s_sibling)
+               if (sd->s_type & CONFIGFS_USET_DEFAULT)
                        configfs_detach_rollback(sd->s_dentry);
-                       sd->s_type &= ~CONFIGFS_USET_DROPPING;
-               }
-       }
 }
 
 static void detach_attrs(struct config_item * item)
@@ -1027,9 +1027,10 @@ EXPORT_SYMBOL(configfs_undepend_item);
 
 static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
-       int ret, module_got = 0;
-       struct config_group *group;
-       struct config_item *item;
+       int ret = 0;
+       int module_got = 0;
+       struct config_group *group = NULL;
+       struct config_item *item = NULL;
        struct config_item *parent_item;
        struct configfs_subsystem *subsys;
        struct configfs_dirent *sd;
@@ -1070,18 +1071,23 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
 
        mutex_lock(&subsys->su_mutex);
-       group = NULL;
-       item = NULL;
        if (type->ct_group_ops->make_group) {
-               ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group);
-               if (!ret) {
+               group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
+               if (!group)
+                       group = ERR_PTR(-ENOMEM);
+               if (!IS_ERR(group)) {
                        link_group(to_config_group(parent_item), group);
                        item = &group->cg_item;
-               }
+               } else
+                       ret = PTR_ERR(group);
        } else {
-               ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item);
-               if (!ret)
+               item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
+               if (!item)
+                       item = ERR_PTR(-ENOMEM);
+               if (!IS_ERR(item))
                        link_obj(parent_item, item);
+               else
+                       ret = PTR_ERR(item);
        }
        mutex_unlock(&subsys->su_mutex);
 
@@ -1201,6 +1207,11 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                return -EINVAL;
        }
 
+       /*
+        * Ensure that no racing symlink() will make detach_prep() fail while
+        * the new link is temporarily attached
+        */
+       mutex_lock(&configfs_symlink_mutex);
        spin_lock(&configfs_dirent_lock);
        do {
                struct mutex *wait_mutex;
@@ -1209,6 +1220,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                if (ret) {
                        configfs_detach_rollback(dentry);
                        spin_unlock(&configfs_dirent_lock);
+                       mutex_unlock(&configfs_symlink_mutex);
                        if (ret != -EAGAIN) {
                                config_item_put(parent_item);
                                return ret;
@@ -1218,10 +1230,12 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                        mutex_lock(wait_mutex);
                        mutex_unlock(wait_mutex);
 
+                       mutex_lock(&configfs_symlink_mutex);
                        spin_lock(&configfs_dirent_lock);
                }
        } while (ret == -EAGAIN);
        spin_unlock(&configfs_dirent_lock);
+       mutex_unlock(&configfs_symlink_mutex);
 
        /* Get a working ref for the duration of this function */
        item = configfs_get_config_item(dentry);
@@ -1511,11 +1525,13 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
        mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex,
                          I_MUTEX_PARENT);
        mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD);
+       mutex_lock(&configfs_symlink_mutex);
        spin_lock(&configfs_dirent_lock);
        if (configfs_detach_prep(dentry, NULL)) {
                printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n");
        }
        spin_unlock(&configfs_dirent_lock);
+       mutex_unlock(&configfs_symlink_mutex);
        configfs_detach_group(&group->cg_item);
        dentry->d_inode->i_flags |= S_DEAD;
        mutex_unlock(&dentry->d_inode->i_mutex);