Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[safe/jmp/linux-2.6] / fs / configfs / dir.c
index 4d11479..8e93341 100644 (file)
@@ -435,7 +435,8 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex
 
        ret = 0;
        list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
-               if (sd->s_type & CONFIGFS_NOT_PINNED)
+               if (!sd->s_element ||
+                   (sd->s_type & CONFIGFS_NOT_PINNED))
                        continue;
                if (sd->s_type & CONFIGFS_USET_DEFAULT) {
                        /* Abort if racing with mkdir() */
@@ -1099,7 +1100,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        struct configfs_subsystem *subsys;
        struct configfs_dirent *sd;
        struct config_item_type *type;
-       struct module *owner = NULL;
+       struct module *subsys_owner = NULL, *new_item_owner = NULL;
        char *name;
 
        if (dentry->d_parent == configfs_sb->s_root) {
@@ -1136,10 +1137,25 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
                goto out_put;
        }
 
+       /*
+        * The subsystem may belong to a different module than the item
+        * being created.  We don't want to safely pin the new item but
+        * fail to pin the subsystem it sits under.
+        */
+       if (!subsys->su_group.cg_item.ci_type) {
+               ret = -EINVAL;
+               goto out_put;
+       }
+       subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
+       if (!try_module_get(subsys_owner)) {
+               ret = -EINVAL;
+               goto out_put;
+       }
+
        name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
        if (!name) {
                ret = -ENOMEM;
-               goto out_put;
+               goto out_subsys_put;
        }
 
        snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
@@ -1171,7 +1187,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
                 * If ret != 0, then link_obj() was never called.
                 * There are no extra references to clean up.
                 */
-               goto out_put;
+               goto out_subsys_put;
        }
 
        /*
@@ -1185,8 +1201,8 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
                goto out_unlink;
        }
 
-       owner = type->ct_owner;
-       if (!try_module_get(owner)) {
+       new_item_owner = type->ct_owner;
+       if (!try_module_get(new_item_owner)) {
                ret = -EINVAL;
                goto out_unlink;
        }
@@ -1235,9 +1251,13 @@ out_unlink:
                mutex_unlock(&subsys->su_mutex);
 
                if (module_got)
-                       module_put(owner);
+                       module_put(new_item_owner);
        }
 
+out_subsys_put:
+       if (ret)
+               module_put(subsys_owner);
+
 out_put:
        /*
         * link_obj()/link_group() took a reference from child->parent,
@@ -1256,7 +1276,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
        struct config_item *item;
        struct configfs_subsystem *subsys;
        struct configfs_dirent *sd;
-       struct module *owner = NULL;
+       struct module *subsys_owner = NULL, *dead_item_owner = NULL;
        int ret;
 
        if (dentry->d_parent == configfs_sb->s_root)
@@ -1283,20 +1303,26 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                return -EINVAL;
        }
 
+       /* configfs_mkdir() shouldn't have allowed this */
+       BUG_ON(!subsys->su_group.cg_item.ci_type);
+       subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
+
        /*
         * 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;
 
+               mutex_lock(&configfs_symlink_mutex);
+               spin_lock(&configfs_dirent_lock);
                ret = configfs_detach_prep(dentry, &wait_mutex);
-               if (ret) {
+               if (ret)
                        configfs_detach_rollback(dentry);
-                       spin_unlock(&configfs_dirent_lock);
-                       mutex_unlock(&configfs_symlink_mutex);
+               spin_unlock(&configfs_dirent_lock);
+               mutex_unlock(&configfs_symlink_mutex);
+
+               if (ret) {
                        if (ret != -EAGAIN) {
                                config_item_put(parent_item);
                                return ret;
@@ -1305,13 +1331,8 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                        /* Wait until the racing operation terminates */
                        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);
@@ -1320,7 +1341,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
        config_item_put(parent_item);
 
        if (item->ci_type)
-               owner = item->ci_type->ct_owner;
+               dead_item_owner = item->ci_type->ct_owner;
 
        if (sd->s_type & CONFIGFS_USET_DIR) {
                configfs_detach_group(item);
@@ -1342,7 +1363,8 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
        /* Drop our reference from above */
        config_item_put(item);
 
-       module_put(owner);
+       module_put(dead_item_owner);
+       module_put(subsys_owner);
 
        return 0;
 }