configfs: Fix a reference leak in configfs_mkdir().
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>
This commit is contained in:
@@ -704,13 +704,18 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
struct module *owner;
|
struct module *owner;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
if (dentry->d_parent == configfs_sb->s_root)
|
if (dentry->d_parent == configfs_sb->s_root) {
|
||||||
return -EPERM;
|
ret = -EPERM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
sd = dentry->d_parent->d_fsdata;
|
sd = dentry->d_parent->d_fsdata;
|
||||||
if (!(sd->s_type & CONFIGFS_USET_DIR))
|
if (!(sd->s_type & CONFIGFS_USET_DIR)) {
|
||||||
return -EPERM;
|
ret = -EPERM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a working ref for the duration of this function */
|
||||||
parent_item = configfs_get_config_item(dentry->d_parent);
|
parent_item = configfs_get_config_item(dentry->d_parent);
|
||||||
type = parent_item->ci_type;
|
type = parent_item->ci_type;
|
||||||
subsys = to_config_group(parent_item)->cg_subsys;
|
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 ||
|
if (!type || !type->ct_group_ops ||
|
||||||
(!type->ct_group_ops->make_group &&
|
(!type->ct_group_ops->make_group &&
|
||||||
!type->ct_group_ops->make_item)) {
|
!type->ct_group_ops->make_item)) {
|
||||||
config_item_put(parent_item);
|
ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
|
||||||
return -EPERM; /* What lack-of-mkdir returns */
|
goto out_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
|
name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
config_item_put(parent_item);
|
ret = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto out_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
|
snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
|
||||||
|
|
||||||
down(&subsys->su_sem);
|
down(&subsys->su_sem);
|
||||||
@@ -748,8 +754,8 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
|
|
||||||
kfree(name);
|
kfree(name);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
config_item_put(parent_item);
|
ret = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto out_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
@@ -776,12 +782,19 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||||||
client_drop_item(parent_item, item);
|
client_drop_item(parent_item, item);
|
||||||
up(&subsys->su_sem);
|
up(&subsys->su_sem);
|
||||||
|
|
||||||
config_item_put(parent_item);
|
|
||||||
module_put(owner);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,6 +814,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||||||
if (sd->s_type & CONFIGFS_USET_DEFAULT)
|
if (sd->s_type & CONFIGFS_USET_DEFAULT)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
|
/* Get a working ref until we have the child */
|
||||||
parent_item = configfs_get_config_item(dentry->d_parent);
|
parent_item = configfs_get_config_item(dentry->d_parent);
|
||||||
subsys = to_config_group(parent_item)->cg_subsys;
|
subsys = to_config_group(parent_item)->cg_subsys;
|
||||||
BUG_ON(!subsys);
|
BUG_ON(!subsys);
|
||||||
@@ -817,6 +831,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a working ref for the duration of this function */
|
||||||
item = configfs_get_config_item(dentry);
|
item = configfs_get_config_item(dentry);
|
||||||
|
|
||||||
/* Drop reference from above, item already holds one. */
|
/* Drop reference from above, item already holds one. */
|
||||||
|
Reference in New Issue
Block a user