Commit eed7a0db authored by Joel Becker's avatar Joel Becker Committed by Mark Fasheh

configfs: configfs_mkdir() failed to cleanup linkage.

If configfs_mkdir() errored in certain ways after the parent<->child
linkage was already created, it would not undo the linkage.  Also,
comment the reference counting for clarity.
Signed-off-by: default avatarJoel Becker <joel.becker@oracle.com>
Signed-off-by: default avatarMark Fasheh <mark.fasheh@oracle.com>
parent 84efad1a
...@@ -505,13 +505,15 @@ static int populate_groups(struct config_group *group) ...@@ -505,13 +505,15 @@ static int populate_groups(struct config_group *group)
int i; int i;
if (group->default_groups) { if (group->default_groups) {
/* FYI, we're faking mkdir here /*
* FYI, we're faking mkdir here
* I'm not sure we need this semaphore, as we're called * I'm not sure we need this semaphore, as we're called
* from our parent's mkdir. That holds our parent's * from our parent's mkdir. That holds our parent's
* i_mutex, so afaik lookup cannot continue through our * i_mutex, so afaik lookup cannot continue through our
* parent to find us, let alone mess with our tree. * parent to find us, let alone mess with our tree.
* That said, taking our i_mutex is closer to mkdir * That said, taking our i_mutex is closer to mkdir
* emulation, and shouldn't hurt. */ * emulation, and shouldn't hurt.
*/
mutex_lock(&dentry->d_inode->i_mutex); mutex_lock(&dentry->d_inode->i_mutex);
for (i = 0; group->default_groups[i]; i++) { for (i = 0; group->default_groups[i]; i++) {
...@@ -546,20 +548,34 @@ static void unlink_obj(struct config_item *item) ...@@ -546,20 +548,34 @@ static void unlink_obj(struct config_item *item)
item->ci_group = NULL; item->ci_group = NULL;
item->ci_parent = NULL; item->ci_parent = NULL;
/* Drop the reference for ci_entry */
config_item_put(item); config_item_put(item);
/* Drop the reference for ci_parent */
config_group_put(group); config_group_put(group);
} }
} }
static void link_obj(struct config_item *parent_item, struct config_item *item) static void link_obj(struct config_item *parent_item, struct config_item *item)
{ {
/* Parent seems redundant with group, but it makes certain /*
* traversals much nicer. */ * Parent seems redundant with group, but it makes certain
* traversals much nicer.
*/
item->ci_parent = parent_item; item->ci_parent = parent_item;
/*
* We hold a reference on the parent for the child's ci_parent
* link.
*/
item->ci_group = config_group_get(to_config_group(parent_item)); item->ci_group = config_group_get(to_config_group(parent_item));
list_add_tail(&item->ci_entry, &item->ci_group->cg_children); list_add_tail(&item->ci_entry, &item->ci_group->cg_children);
/*
* We hold a reference on the child for ci_entry on the parent's
* cg_children
*/
config_item_get(item); config_item_get(item);
} }
...@@ -684,6 +700,10 @@ static void client_drop_item(struct config_item *parent_item, ...@@ -684,6 +700,10 @@ static void client_drop_item(struct config_item *parent_item,
type = parent_item->ci_type; type = parent_item->ci_type;
BUG_ON(!type); BUG_ON(!type);
/*
* If ->drop_item() exists, it is responsible for the
* config_item_put().
*/
if (type->ct_group_ops && type->ct_group_ops->drop_item) if (type->ct_group_ops && type->ct_group_ops->drop_item)
type->ct_group_ops->drop_item(to_config_group(parent_item), type->ct_group_ops->drop_item(to_config_group(parent_item),
item); item);
...@@ -694,14 +714,14 @@ static void client_drop_item(struct config_item *parent_item, ...@@ -694,14 +714,14 @@ static void client_drop_item(struct config_item *parent_item,
static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{ {
int ret; int ret, module_got = 0;
struct config_group *group; struct config_group *group;
struct config_item *item; struct config_item *item;
struct config_item *parent_item; struct config_item *parent_item;
struct configfs_subsystem *subsys; struct configfs_subsystem *subsys;
struct configfs_dirent *sd; struct configfs_dirent *sd;
struct config_item_type *type; struct config_item_type *type;
struct module *owner; struct module *owner = NULL;
char *name; char *name;
if (dentry->d_parent == configfs_sb->s_root) { if (dentry->d_parent == configfs_sb->s_root) {
...@@ -754,43 +774,63 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -754,43 +774,63 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
kfree(name); kfree(name);
if (!item) { if (!item) {
/*
* If item == NULL, then link_obj() was never called.
* There are no extra references to clean up.
*/
ret = -ENOMEM; ret = -ENOMEM;
goto out_put; goto out_put;
} }
ret = -EINVAL; /*
* link_obj() has been called (via link_group() for groups).
* From here on out, errors must clean that up.
*/
type = item->ci_type; type = item->ci_type;
if (type) { if (!type) {
owner = type->ct_owner; ret = -EINVAL;
if (try_module_get(owner)) { goto out_unlink;
if (group) { }
ret = configfs_attach_group(parent_item,
item,
dentry);
} else {
ret = configfs_attach_item(parent_item,
item,
dentry);
}
if (ret) { owner = type->ct_owner;
down(&subsys->su_sem); if (!try_module_get(owner)) {
if (group) ret = -EINVAL;
unlink_group(group); goto out_unlink;
else }
unlink_obj(item);
client_drop_item(parent_item, item);
up(&subsys->su_sem);
module_put(owner); /*
} * I hate doing it this way, but if there is
} * an error, module_put() probably should
* happen after any cleanup.
*/
module_got = 1;
if (group)
ret = configfs_attach_group(parent_item, item, dentry);
else
ret = configfs_attach_item(parent_item, item, dentry);
out_unlink:
if (ret) {
/* Tear down everything we built up */
down(&subsys->su_sem);
if (group)
unlink_group(group);
else
unlink_obj(item);
client_drop_item(parent_item, item);
up(&subsys->su_sem);
if (module_got)
module_put(owner);
} }
out_put: out_put:
/* /*
* link_obj()/link_group() took a reference from child->parent. * link_obj()/link_group() took a reference from child->parent,
* Drop our working ref * so the parent is safely pinned. We can drop our working
* reference.
*/ */
config_item_put(parent_item); config_item_put(parent_item);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment