configfs: Fix a reference leak in configfs_mkdir().
authorJoel Becker <joel.becker@oracle.com>
Tue, 28 Mar 2006 02:46:09 +0000 (18:46 -0800)
committerMark Fasheh <mark.fasheh@oracle.com>
Wed, 17 May 2006 21:38:50 +0000 (14:38 -0700)
configfs_mkdir() failed to release the working parent reference in most
exit paths.  Also changed the exit path for readability.

Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
fs/configfs/dir.c

index 5638c8f..810c139 100644 (file)
@@ -704,13 +704,18 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        struct module *owner;
        char *name;
 
-       if (dentry->d_parent == configfs_sb->s_root)
-               return -EPERM;
+       if (dentry->d_parent == configfs_sb->s_root) {
+               ret = -EPERM;
+               goto out;
+       }
 
        sd = dentry->d_parent->d_fsdata;
-       if (!(sd->s_type & CONFIGFS_USET_DIR))
-               return -EPERM;
+       if (!(sd->s_type & CONFIGFS_USET_DIR)) {
+               ret = -EPERM;
+               goto out;
+       }
 
+       /* Get a working ref for the duration of this function */
        parent_item = configfs_get_config_item(dentry->d_parent);
        type = parent_item->ci_type;
        subsys = to_config_group(parent_item)->cg_subsys;
@@ -719,15 +724,16 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (!type || !type->ct_group_ops ||
            (!type->ct_group_ops->make_group &&
             !type->ct_group_ops->make_item)) {
-               config_item_put(parent_item);
-               return -EPERM;  /* What lack-of-mkdir returns */
+               ret = -EPERM;  /* Lack-of-mkdir returns -EPERM */
+               goto out_put;
        }
 
        name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
        if (!name) {
-               config_item_put(parent_item);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_put;
        }
+
        snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
 
        down(&subsys->su_sem);
@@ -748,8 +754,8 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 
        kfree(name);
        if (!item) {
-               config_item_put(parent_item);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_put;
        }
 
        ret = -EINVAL;
@@ -776,12 +782,19 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
                                client_drop_item(parent_item, item);
                                up(&subsys->su_sem);
 
-                               config_item_put(parent_item);
                                module_put(owner);
                        }
                }
        }
 
+out_put:
+       /*
+        * link_obj()/link_group() took a reference from child->parent.
+        * Drop our working ref
+        */
+       config_item_put(parent_item);
+
+out:
        return ret;
 }
 
@@ -801,6 +814,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (sd->s_type & CONFIGFS_USET_DEFAULT)
                return -EPERM;
 
+       /* Get a working ref until we have the child */
        parent_item = configfs_get_config_item(dentry->d_parent);
        subsys = to_config_group(parent_item)->cg_subsys;
        BUG_ON(!subsys);
@@ -817,6 +831,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
                return ret;
        }
 
+       /* Get a working ref for the duration of this function */
        item = configfs_get_config_item(dentry);
 
        /* Drop reference from above, item already holds one. */