summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-15 04:28:20 +0530
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-15 04:28:20 +0530
commitde02909ae81aa4fda213d16915adb5e1b088a7db (patch)
treea49a44264dc37e943786d6e074105ad1b458f2b7
parent8ab34af09f292ca9620dad8df253fec766729b29 (diff)
parentd53ac61b6120fa596d758ff6e22b5dcb6db21ce8 (diff)
Merge tag 'pull-configfs-fixed' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull configfs updates from Al Viro: "A couple of fixes (UAF in configfs_lookup() and really old races introduced when lseek() on configfs directories stopped locking those directories; impact up to and including UAF). Fixes aside, the main result is that configfs is finally switched to tree-in-dcache machinery. It's *not* making use of recursive removal helpers yet, and it still does the bloody awful "build subtree in full sight of userland, with possibility of failure halfway through and need to unroll" that forces the locking model from hell; dealing with that is a separate patch series, once this one is out of the way. However, it is using DCACHE_PERSISTENT properly now. And apparmorfs is the sole remaining user of __simple_{unlink,rmdir}() at that point" * tag 'pull-configfs-fixed' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: create_default_group(): pass parent's dentry instead of config_group configfs_attach_group(): drop the unused parent_item argument configs_attach_item(): drop unused parent_item argument configfs_create(): lift parent timestamp updates into callers kill configfs_drop_dentry() configfs: mark pinned dentries persistent configfs: dentry refcount needs to be pinned only once switch configfs_detach_{group,item}() to passing dentry configfs_remove_dir(), detach_attrs(): switch to passing dentry populate_attrs(): move cleanup to the sole caller populate_group(): move cleanup on failure to the sole caller configfs_detach_rollback(): pass configfs_dirent instead of dentry configfs_do_depend_item(): pass configfs_dirent instead of dentry configfs_depend_prep(): pass configfs_dirent instead of dentry configfs_detach_prep(): pass configfs_dirent instead of dentry configfs_mkdir(): use take_dentry_name_snapshot() configfs: fix lockless traversals of ->s_children configfs_lookup(): don't leave ->s_dentry dangling on failure
-rw-r--r--fs/configfs/configfs_internal.h1
-rw-r--r--fs/configfs/dir.c315
-rw-r--r--fs/configfs/inode.c25
-rw-r--r--fs/configfs/symlink.c3
4 files changed, 157 insertions, 187 deletions
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index 0b969d0eb8ff..acdeea8e2d69 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -76,7 +76,6 @@ extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *,
extern int configfs_dirent_is_ready(struct configfs_dirent *);
extern const unsigned char * configfs_get_name(struct configfs_dirent *sd);
-extern void configfs_drop_dentry(struct configfs_dirent *sd, struct dentry *parent);
extern int configfs_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *iattr);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index cd65a69765ea..3c88f13f1ca2 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -235,15 +235,16 @@ static int configfs_dirent_exists(struct dentry *dentry)
const unsigned char *new = dentry->d_name.name;
struct configfs_dirent *sd;
+ spin_lock(&configfs_dirent_lock);
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (sd->s_element) {
- const unsigned char *existing = configfs_get_name(sd);
- if (strcmp(existing, new))
- continue;
- else
+ if (strcmp(configfs_get_name(sd), new) == 0) {
+ spin_unlock(&configfs_dirent_lock);
return -EEXIST;
+ }
}
}
+ spin_unlock(&configfs_dirent_lock);
return 0;
}
@@ -295,6 +296,7 @@ static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct dentry *p = dentry->d_parent;
+ struct inode *p_inode = d_inode(p);
struct inode *inode;
BUG_ON(!item);
@@ -314,10 +316,9 @@ static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
inode->i_fop = &configfs_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
- d_instantiate(dentry, inode);
- /* already hashed */
- dget(dentry); /* pin directory dentries in core */
- inc_nlink(d_inode(p));
+ d_make_persistent(dentry, inode);
+ inc_nlink(p_inode);
+ inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode));
item->ci_dentry = dentry;
return 0;
@@ -371,6 +372,7 @@ int configfs_create_link(struct configfs_dirent *target, struct dentry *parent,
int err = 0;
umode_t mode = S_IFLNK | S_IRWXUGO;
struct configfs_dirent *p = parent->d_fsdata;
+ struct inode *p_inode = d_inode(parent);
struct inode *inode;
err = configfs_make_dirent(p, dentry, target, mode, CONFIGFS_ITEM_LINK,
@@ -384,8 +386,8 @@ int configfs_create_link(struct configfs_dirent *target, struct dentry *parent,
inode->i_link = body;
inode->i_op = &configfs_symlink_inode_operations;
- d_instantiate(dentry, inode);
- dget(dentry); /* pin link dentries in core */
+ d_make_persistent(dentry, inode);
+ inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode));
return 0;
out_remove:
@@ -394,29 +396,9 @@ out_remove:
return PTR_ERR(inode);
}
-static void remove_dir(struct dentry * d)
-{
- struct dentry * parent = dget(d->d_parent);
-
- configfs_remove_dirent(d);
-
- if (d_really_is_positive(d)) {
- if (likely(simple_empty(d))) {
- __simple_rmdir(d_inode(parent),d);
- dput(d);
- } else {
- pr_warn("remove_dir (%pd): attributes remain", d);
- }
- }
-
- pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
-
- dput(parent);
-}
-
/**
* configfs_remove_dir - remove an config_item's directory.
- * @item: config_item we're removing.
+ * @d: dentry we're removing.
*
* The only thing special about this is that we remove any files in
* the directory before we remove the directory, and we've inlined
@@ -425,18 +407,20 @@ static void remove_dir(struct dentry * d)
* Caller holds the mutex of the item's inode
*/
-static void configfs_remove_dir(struct config_item * item)
+static void configfs_remove_dir(struct dentry *d)
{
- struct dentry * dentry = dget(item->ci_dentry);
+ struct dentry * parent = dget(d->d_parent);
- if (!dentry)
- return;
+ configfs_remove_dirent(d);
- remove_dir(dentry);
- /**
- * Drop reference from dget() on entrance.
- */
- dput(dentry);
+ if (d_really_is_positive(d)) {
+ if (unlikely(simple_rmdir(d_inode(parent), d)))
+ pr_warn("remove_dir (%pd): attributes remain", d);
+ }
+
+ pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
+
+ dput(parent);
}
static struct dentry * configfs_lookup(struct inode *dir,
@@ -486,6 +470,9 @@ static struct dentry * configfs_lookup(struct inode *dir,
inode = configfs_create(dentry, mode);
if (IS_ERR(inode)) {
+ spin_lock(&configfs_dirent_lock);
+ sd->s_dentry = NULL;
+ spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
return ERR_CAST(inode);
}
@@ -512,9 +499,8 @@ done:
* If there is an error, the caller will reset the flags via
* configfs_detach_rollback().
*/
-static int configfs_detach_prep(struct dentry *dentry, struct dentry **wait)
+static int configfs_detach_prep(struct configfs_dirent *parent_sd, struct dentry **wait)
{
- struct configfs_dirent *parent_sd = dentry->d_fsdata;
struct configfs_dirent *sd;
int ret;
@@ -542,7 +528,7 @@ static int configfs_detach_prep(struct dentry *dentry, struct dentry **wait)
* Yup, recursive. If there's a problem, blame
* deep nesting of default_groups
*/
- ret = configfs_detach_prep(sd->s_dentry, wait);
+ ret = configfs_detach_prep(sd, wait);
if (!ret)
continue;
} else
@@ -559,45 +545,77 @@ out:
* Walk the tree, resetting CONFIGFS_USET_DROPPING wherever it was
* set.
*/
-static void configfs_detach_rollback(struct dentry *dentry)
+static void configfs_detach_rollback(struct configfs_dirent *parent_sd)
{
- struct configfs_dirent *parent_sd = dentry->d_fsdata;
struct configfs_dirent *sd;
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);
+ configfs_detach_rollback(sd);
}
-static void detach_attrs(struct config_item * item)
+/*
+ * Find the next non-cursor. configfs_dirent_lock held by caller.
+ */
+static struct configfs_dirent *next_dirent(struct configfs_dirent *parent,
+ struct configfs_dirent *last)
{
- struct dentry * dentry = dget(item->ci_dentry);
- struct configfs_dirent * parent_sd;
- struct configfs_dirent * sd, * tmp;
+ struct configfs_dirent *s;
- if (!dentry)
- return;
+ s = list_prepare_entry(last, &parent->s_children, s_sibling);
+
+ list_for_each_entry_continue(s, &parent->s_children, s_sibling) {
+ if (s->s_element)
+ return s;
+ }
+ return NULL;
+}
+
+static void detach_attrs(struct dentry *dentry)
+{
+ struct configfs_dirent *parent_sd;
+ struct configfs_dirent *sd, *next;
- pr_debug("configfs %s: dropping attrs for dir\n",
- dentry->d_name.name);
+ pr_debug("configfs %pd: dropping attrs for dir\n", dentry);
parent_sd = dentry->d_fsdata;
- list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
- if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED))
+
+ spin_lock(&configfs_dirent_lock);
+ for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
+ struct dentry *child;
+
+ next = next_dirent(parent_sd, sd);
+ if (!(sd->s_type & CONFIGFS_NOT_PINNED))
continue;
- spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
+ child = sd->s_dentry;
+ if (child) {
+ spin_lock(&child->d_lock);
+ if (simple_positive(child)) {
+ dget_dlock(child);
+ __d_drop(child);
+ spin_unlock(&child->d_lock);
+ } else {
+ spin_unlock(&child->d_lock);
+ child = NULL;
+ }
+ }
spin_unlock(&configfs_dirent_lock);
- configfs_drop_dentry(sd, dentry);
+ if (child) {
+ struct inode *inode = child->d_inode;
+
+ inode_lock_nested(inode, I_MUTEX_NONDIR2);
+ inode_set_ctime_current(inode);
+ drop_nlink(inode);
+ inode_unlock(inode);
+ dput(child);
+ }
configfs_put(sd);
+ spin_lock(&configfs_dirent_lock);
}
-
- /**
- * Drop reference from dget() on entrance.
- */
- dput(dentry);
+ spin_unlock(&configfs_dirent_lock);
}
static int populate_attrs(struct config_item *item)
@@ -619,54 +637,49 @@ static int populate_attrs(struct config_item *item)
if (ops && ops->is_visible && !ops->is_visible(item, attr, i))
continue;
- if ((error = configfs_create_file(item, attr)))
- break;
+ error = configfs_create_file(item, attr);
+ if (error)
+ return error;
}
}
- if (!error && t->ct_bin_attrs) {
+ if (t->ct_bin_attrs) {
for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) {
if (ops && ops->is_bin_visible && !ops->is_bin_visible(item, bin_attr, i))
continue;
error = configfs_create_bin_file(item, bin_attr);
if (error)
- break;
+ return error;
}
}
- if (error)
- detach_attrs(item);
-
- return error;
+ return 0;
}
-static int configfs_attach_group(struct config_item *parent_item,
- struct config_item *item,
+static int configfs_attach_group(struct config_item *item,
struct dentry *dentry,
struct configfs_fragment *frag);
-static void configfs_detach_group(struct config_item *item);
+static void configfs_detach_group(struct dentry *dentry);
-static void detach_groups(struct config_group *group)
+static void detach_groups(struct dentry *dentry)
{
- struct dentry * dentry = dget(group->cg_item.ci_dentry);
struct dentry *child;
struct configfs_dirent *parent_sd;
- struct configfs_dirent *sd, *tmp;
-
- if (!dentry)
- return;
+ struct configfs_dirent *sd, *next;
parent_sd = dentry->d_fsdata;
- list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
- if (!sd->s_element ||
- !(sd->s_type & CONFIGFS_USET_DEFAULT))
+ spin_lock(&configfs_dirent_lock);
+ for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
+ next = next_dirent(parent_sd, sd);
+ if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
continue;
- child = sd->s_dentry;
+ child = dget(sd->s_dentry);
+ spin_unlock(&configfs_dirent_lock);
inode_lock(d_inode(child));
- configfs_detach_group(sd->s_element);
+ configfs_detach_group(child);
d_inode(child)->i_flags |= S_DEAD;
dont_mount(child);
@@ -674,12 +687,9 @@ static void detach_groups(struct config_group *group)
d_delete(child);
dput(child);
+ spin_lock(&configfs_dirent_lock);
}
-
- /**
- * Drop reference from dget() on entrance.
- */
- dput(dentry);
+ spin_unlock(&configfs_dirent_lock);
}
/*
@@ -690,14 +700,14 @@ static void detach_groups(struct config_group *group)
* We could, perhaps, tweak our parent's ->mkdir for a minute and
* try using vfs_mkdir. Just a thought.
*/
-static int create_default_group(struct config_group *parent_group,
+static int create_default_group(struct dentry *parent,
struct config_group *group,
struct configfs_fragment *frag)
{
int ret;
struct configfs_dirent *sd;
/* We trust the caller holds a reference to parent */
- struct dentry *child, *parent = parent_group->cg_item.ci_dentry;
+ struct dentry *child;
if (!group->cg_item.ci_name)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
@@ -707,16 +717,15 @@ static int create_default_group(struct config_group *parent_group,
if (child) {
d_add(child, NULL);
- ret = configfs_attach_group(&parent_group->cg_item,
- &group->cg_item, child, frag);
+ ret = configfs_attach_group(&group->cg_item, child, frag);
if (!ret) {
sd = child->d_fsdata;
sd->s_type |= CONFIGFS_USET_DEFAULT;
} else {
BUG_ON(d_inode(child));
d_drop(child);
- dput(child);
}
+ dput(child);
}
return ret;
@@ -725,18 +734,15 @@ static int create_default_group(struct config_group *parent_group,
static int populate_groups(struct config_group *group,
struct configfs_fragment *frag)
{
+ struct dentry *parent = group->cg_item.ci_dentry;
struct config_group *new_group;
- int ret = 0;
list_for_each_entry(new_group, &group->default_groups, group_entry) {
- ret = create_default_group(group, new_group, frag);
- if (ret) {
- detach_groups(group);
- break;
- }
+ int ret = create_default_group(parent, new_group, frag);
+ if (ret)
+ return ret;
}
-
- return ret;
+ return 0;
}
void configfs_remove_default_groups(struct config_group *group)
@@ -826,6 +832,13 @@ static void link_group(struct config_group *parent_group, struct config_group *g
link_group(group, new_group);
}
+/* Caller holds the mutex of the item's inode */
+static void configfs_detach_item(struct dentry *dentry)
+{
+ detach_attrs(dentry);
+ configfs_remove_dir(dentry);
+}
+
/*
* The goal is that configfs_attach_item() (and
* configfs_attach_group()) can be called from either the VFS or this
@@ -841,8 +854,7 @@ static void link_group(struct config_group *parent_group, struct config_group *g
* clean up the configfs items, and they expect their callers will
* handle the dcache bits.
*/
-static int configfs_attach_item(struct config_item *parent_item,
- struct config_item *item,
+static int configfs_attach_item(struct config_item *item,
struct dentry *dentry,
struct configfs_fragment *frag)
{
@@ -858,7 +870,7 @@ static int configfs_attach_item(struct config_item *parent_item,
* we must lock them as rmdir() would.
*/
inode_lock(d_inode(dentry));
- configfs_remove_dir(item);
+ configfs_detach_item(dentry);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
inode_unlock(d_inode(dentry));
@@ -869,22 +881,21 @@ static int configfs_attach_item(struct config_item *parent_item,
return ret;
}
-/* Caller holds the mutex of the item's inode */
-static void configfs_detach_item(struct config_item *item)
+/* Caller holds the mutex of the group's inode */
+static void configfs_detach_group(struct dentry *dentry)
{
- detach_attrs(item);
- configfs_remove_dir(item);
+ detach_groups(dentry);
+ configfs_detach_item(dentry);
}
-static int configfs_attach_group(struct config_item *parent_item,
- struct config_item *item,
+static int configfs_attach_group(struct config_item *item,
struct dentry *dentry,
struct configfs_fragment *frag)
{
int ret;
struct configfs_dirent *sd;
- ret = configfs_attach_item(parent_item, item, dentry, frag);
+ ret = configfs_attach_item(item, dentry, frag);
if (!ret) {
sd = dentry->d_fsdata;
sd->s_type |= CONFIGFS_USET_DIR;
@@ -902,7 +913,7 @@ static int configfs_attach_group(struct config_item *parent_item,
configfs_adjust_dir_dirent_depth_before_populate(sd);
ret = populate_groups(to_config_group(item), frag);
if (ret) {
- configfs_detach_item(item);
+ configfs_detach_group(dentry);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
}
@@ -915,13 +926,6 @@ static int configfs_attach_group(struct config_item *parent_item,
return ret;
}
-/* Caller holds the mutex of the group's inode */
-static void configfs_detach_group(struct config_item *item)
-{
- detach_groups(to_config_group(item));
- configfs_detach_item(item);
-}
-
/*
* After the item has been detached from the filesystem view, we are
* ready to tear it out of the hierarchy. Notify the client before
@@ -1064,15 +1068,12 @@ static int configfs_dump(struct configfs_dirent *sd, int level)
* much on the stack, though, so folks that need this function - be careful
* about your stack! Patches will be accepted to make it iterative.
*/
-static int configfs_depend_prep(struct dentry *origin,
+static int configfs_depend_prep(struct configfs_dirent *sd,
struct config_item *target)
{
- struct configfs_dirent *child_sd, *sd;
+ struct configfs_dirent *child_sd;
int ret = 0;
- BUG_ON(!origin || !origin->d_fsdata);
- sd = origin->d_fsdata;
-
if (sd->s_element == target) /* Boo-yah */
goto out;
@@ -1080,8 +1081,7 @@ static int configfs_depend_prep(struct dentry *origin,
if ((child_sd->s_type & CONFIGFS_DIR) &&
!(child_sd->s_type & CONFIGFS_USET_DROPPING) &&
!(child_sd->s_type & CONFIGFS_USET_CREATING)) {
- ret = configfs_depend_prep(child_sd->s_dentry,
- target);
+ ret = configfs_depend_prep(child_sd, target);
if (!ret)
goto out; /* Child path boo-yah */
}
@@ -1094,7 +1094,7 @@ out:
return ret;
}
-static int configfs_do_depend_item(struct dentry *subsys_dentry,
+static int configfs_do_depend_item(struct configfs_dirent *subsys_sd,
struct config_item *target)
{
struct configfs_dirent *p;
@@ -1102,7 +1102,7 @@ static int configfs_do_depend_item(struct dentry *subsys_dentry,
spin_lock(&configfs_dirent_lock);
/* Scan the tree, return 0 if found */
- ret = configfs_depend_prep(subsys_dentry, target);
+ ret = configfs_depend_prep(subsys_sd, target);
if (ret)
goto out_unlock_dirent_lock;
@@ -1126,6 +1126,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
struct configfs_dirent *p;
struct configfs_dirent *ret = NULL;
+ spin_lock(&configfs_dirent_lock);
list_for_each_entry(p, &root_sd->s_children, s_sibling) {
if (p->s_type & CONFIGFS_DIR &&
p->s_element == subsys_item) {
@@ -1133,6 +1134,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
break;
}
}
+ spin_unlock(&configfs_dirent_lock);
return ret;
}
@@ -1168,7 +1170,7 @@ int configfs_depend_item(struct configfs_subsystem *subsys,
}
/* Ok, now we can trust subsys/s_item */
- ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
+ ret = configfs_do_depend_item(subsys_sd, target);
out_unlock_fs:
inode_unlock(d_inode(root));
@@ -1270,7 +1272,7 @@ int configfs_depend_item_unlocked(struct configfs_subsystem *caller_subsys,
}
/* Now we can execute core of depend item */
- ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
+ ret = configfs_do_depend_item(subsys_sd, target);
if (target_subsys != caller_subsys)
out_root_unlock:
@@ -1297,7 +1299,11 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
const struct config_item_type *type;
struct module *subsys_owner = NULL, *new_item_owner = NULL;
struct configfs_fragment *frag;
- char *name;
+ struct name_snapshot n;
+ const char *name;
+
+ take_dentry_name_snapshot(&n, dentry);
+ name = n.name.name;
sd = dentry->d_parent->d_fsdata;
@@ -1349,14 +1355,6 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
goto out_put;
}
- name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
- if (!name) {
- ret = -ENOMEM;
- goto out_subsys_put;
- }
-
- snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
-
mutex_lock(&subsys->su_mutex);
if (type->ct_group_ops->make_group) {
group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
@@ -1378,7 +1376,6 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
}
mutex_unlock(&subsys->su_mutex);
- kfree(name);
if (ret) {
/*
* If ret != 0, then link_obj() was never called.
@@ -1423,9 +1420,9 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
spin_unlock(&configfs_dirent_lock);
if (group)
- ret = configfs_attach_group(parent_item, item, dentry, frag);
+ ret = configfs_attach_group(item, dentry, frag);
else
- ret = configfs_attach_item(parent_item, item, dentry, frag);
+ ret = configfs_attach_item(item, dentry, frag);
spin_lock(&configfs_dirent_lock);
sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
@@ -1465,6 +1462,7 @@ out_put:
put_fragment(frag);
out:
+ release_dentry_name_snapshot(&n);
return ERR_PTR(ret);
}
@@ -1512,9 +1510,9 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
*/
ret = sd->s_dependent_count ? -EBUSY : 0;
if (!ret) {
- ret = configfs_detach_prep(dentry, &wait);
+ ret = configfs_detach_prep(sd, &wait);
if (ret)
- configfs_detach_rollback(dentry);
+ configfs_detach_rollback(sd);
}
spin_unlock(&configfs_dirent_lock);
mutex_unlock(&configfs_symlink_mutex);
@@ -1535,7 +1533,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
frag = sd->s_frag;
if (down_write_killable(&frag->frag_sem)) {
spin_lock(&configfs_dirent_lock);
- configfs_detach_rollback(dentry);
+ configfs_detach_rollback(sd);
spin_unlock(&configfs_dirent_lock);
config_item_put(parent_item);
return -EINTR;
@@ -1553,13 +1551,13 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
dead_item_owner = item->ci_type->ct_owner;
if (sd->s_type & CONFIGFS_USET_DIR) {
- configfs_detach_group(item);
+ configfs_detach_group(dentry);
mutex_lock(&subsys->su_mutex);
client_disconnect_notify(parent_item, item);
unlink_group(to_config_group(item));
} else {
- configfs_detach_item(item);
+ configfs_detach_item(dentry);
mutex_lock(&subsys->su_mutex);
client_disconnect_notify(parent_item, item);
@@ -1769,7 +1767,7 @@ int configfs_register_group(struct config_group *parent_group,
parent = parent_group->cg_item.ci_dentry;
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- ret = create_default_group(parent_group, group, frag);
+ ret = create_default_group(parent, group, frag);
if (ret)
goto err_out;
@@ -1798,7 +1796,7 @@ EXPORT_SYMBOL(configfs_register_group);
void configfs_unregister_group(struct config_group *group)
{
struct configfs_subsystem *subsys = group->cg_subsys;
- struct dentry *dentry = group->cg_item.ci_dentry;
+ struct dentry *dentry = dget(group->cg_item.ci_dentry);
struct dentry *parent = group->cg_item.ci_parent->ci_dentry;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
@@ -1809,10 +1807,10 @@ void configfs_unregister_group(struct config_group *group)
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
spin_lock(&configfs_dirent_lock);
- configfs_detach_prep(dentry, NULL);
+ configfs_detach_prep(sd, NULL);
spin_unlock(&configfs_dirent_lock);
- configfs_detach_group(&group->cg_item);
+ configfs_detach_group(dentry);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
d_drop(dentry);
@@ -1907,18 +1905,17 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
err = configfs_dirent_exists(dentry);
if (!err)
- err = configfs_attach_group(sd->s_element,
- &group->cg_item,
+ err = configfs_attach_group(&group->cg_item,
dentry, frag);
if (err) {
BUG_ON(d_inode(dentry));
d_drop(dentry);
- dput(dentry);
} else {
spin_lock(&configfs_dirent_lock);
configfs_dir_set_ready(dentry->d_fsdata);
spin_unlock(&configfs_dirent_lock);
}
+ dput(dentry);
}
inode_unlock(d_inode(root));
@@ -1937,7 +1934,7 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys)
void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
{
struct config_group *group = &subsys->su_group;
- struct dentry *dentry = group->cg_item.ci_dentry;
+ struct dentry *dentry = dget(group->cg_item.ci_dentry);
struct dentry *root = dentry->d_sb->s_root;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
@@ -1956,12 +1953,12 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
mutex_lock(&configfs_symlink_mutex);
spin_lock(&configfs_dirent_lock);
- if (configfs_detach_prep(dentry, NULL)) {
+ if (configfs_detach_prep(sd, NULL)) {
pr_err("Tried to unregister non-empty subsystem!\n");
}
spin_unlock(&configfs_dirent_lock);
mutex_unlock(&configfs_symlink_mutex);
- configfs_detach_group(&group->cg_item);
+ configfs_detach_group(dentry);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
inode_unlock(d_inode(dentry));
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c
index bc507b720a01..68290fe0e374 100644
--- a/fs/configfs/inode.c
+++ b/fs/configfs/inode.c
@@ -157,7 +157,6 @@ struct inode *configfs_create(struct dentry *dentry, umode_t mode)
{
struct inode *inode = NULL;
struct configfs_dirent *sd;
- struct inode *p_inode;
if (!dentry)
return ERR_PTR(-ENOENT);
@@ -170,8 +169,6 @@ struct inode *configfs_create(struct dentry *dentry, umode_t mode)
if (!inode)
return ERR_PTR(-ENOMEM);
- p_inode = d_inode(dentry->d_parent);
- inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode));
configfs_set_inode_lock_class(sd, inode);
return inode;
}
@@ -195,25 +192,3 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd)
}
return NULL;
}
-
-
-/*
- * Unhashes the dentry corresponding to given configfs_dirent
- * Called with parent inode's i_mutex held.
- */
-void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
-{
- struct dentry * dentry = sd->s_dentry;
-
- if (dentry) {
- spin_lock(&dentry->d_lock);
- if (simple_positive(dentry)) {
- dget_dlock(dentry);
- __d_drop(dentry);
- spin_unlock(&dentry->d_lock);
- __simple_unlink(d_inode(parent), dentry);
- dput(dentry);
- } else
- spin_unlock(&dentry->d_lock);
- }
-}
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index f3f79c67add5..31eb28b27309 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -229,9 +229,8 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry)
spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
- configfs_drop_dentry(sd, dentry->d_parent);
- dput(dentry);
configfs_put(sd);
+ simple_unlink(dir, dentry);
/*
* drop_link() must be called before