From 10da12d352b7b2bb330a8609fdda9a58bf0e9856 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 00:29:37 -0400 Subject: [PATCH 01/18] configfs_lookup(): don't leave ->s_dentry dangling on failure Normally ->s_dentry is cleared when dentry it's pointing to becomes negative (on eviction, realistically). However, that only happens if dentry gets to be positive in the first place; in case of inode allocation failure dentry never becomes positive, so ->d_iput() is not called at all. We do part of what normally would've been done by configfs_d_iput() (dropping the reference to configfs_dirent) manually, but we do not clear ->s_dentry there. Sloppy as it is, it does not matter in case of configfs_create_{dir,link}() - there configfs_dirent does not survive dropping the sole reference to it. However, for configfs_lookup() it *does* survive, with a dangling pointer to soon to be freed dentry sitting it its ->s_dentry. Subsequent getdents(2) in that directory will end up dereferencing that pointer in order to pick the inode number. Use after free... This is the minimal fix; the right approach is to set the linkage between dentry and configfs_dirent only after we know that we have an inode, but that takes more surgery and the bug had been there since 2006, so... Fixes: 3d0f89bb1694 ("configfs: Add permission and ownership to configfs objects") # 2.6.16-rc3 Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro --- fs/configfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 362b6ff9b908..e84483a0836d 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -486,6 +486,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); } From 9b9e8bb81c41fd27e7b57a1c936fde140548535f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 30 May 2026 03:48:34 -0400 Subject: [PATCH 02/18] configfs: fix lockless traversals of ->s_children Having the parent directory locked protects entries from removal by another thread, but it does *not* protect cursors from being moved around by lseek() - or freed, for that matter. Fixes: 6f6107640625 ("configfs: Introduce configfs_dirent_lock") Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index e84483a0836d..eb991b2a9c34 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; } @@ -575,11 +576,28 @@ static void configfs_detach_rollback(struct dentry *dentry) configfs_detach_rollback(sd->s_dentry); } +/* + * 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 configfs_dirent *s; + + 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 config_item * item) { struct dentry * dentry = dget(item->ci_dentry); - struct configfs_dirent * parent_sd; - struct configfs_dirent * sd, * tmp; + struct configfs_dirent *parent_sd; + struct configfs_dirent *sd, *next; if (!dentry) return; @@ -588,15 +606,19 @@ static void detach_attrs(struct config_item * item) dentry->d_name.name); 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) { + 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); spin_unlock(&configfs_dirent_lock); configfs_drop_dentry(sd, dentry); configfs_put(sd); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -655,18 +677,20 @@ static void detach_groups(struct config_group *group) struct dentry * dentry = dget(group->cg_item.ci_dentry); struct dentry *child; struct configfs_dirent *parent_sd; - struct configfs_dirent *sd, *tmp; + struct configfs_dirent *sd, *next; if (!dentry) return; 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; + spin_unlock(&configfs_dirent_lock); inode_lock(d_inode(child)); @@ -678,7 +702,9 @@ static void detach_groups(struct config_group *group) d_delete(child); dput(child); + spin_lock(&configfs_dirent_lock); } + spin_unlock(&configfs_dirent_lock); /** * Drop reference from dget() on entrance. @@ -1130,6 +1156,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) { @@ -1137,6 +1164,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd, break; } } + spin_unlock(&configfs_dirent_lock); return ret; } From 5cefe23f63742881229ea80a55b5f9e5021966bc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2026 12:41:46 -0400 Subject: [PATCH 03/18] configfs_mkdir(): use take_dentry_name_snapshot() Note that neither ->make_group() nor ->make_item() are allowed to modify the string passed to them - the argument is const char *. Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro --- fs/configfs/dir.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index eb991b2a9c34..8181771c8a58 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1329,7 +1329,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; @@ -1381,14 +1385,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); @@ -1410,7 +1406,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. @@ -1497,6 +1492,7 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, put_fragment(frag); out: + release_dentry_name_snapshot(&n); return ERR_PTR(ret); } From 96551d7f9f7b59c87da1a6d1b072ab9219a2263e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 01:17:13 -0400 Subject: [PATCH 04/18] configfs_detach_prep(): pass configfs_dirent instead of dentry The only thing it uses the argument for is its ->d_fsdata and all callers have that already available. Note that in the recursive call we are dealing with a (sub)directory configfs_dirent, and for those ->s_dentry->d_fsdata points back to configfs_dirent itself. Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro --- fs/configfs/dir.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 8181771c8a58..b456e4de25ab 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -517,9 +517,8 @@ static struct dentry * configfs_lookup(struct inode *dir, * 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; @@ -547,7 +546,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 @@ -1540,7 +1539,7 @@ 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); } @@ -1837,7 +1836,7 @@ 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); @@ -1984,7 +1983,7 @@ 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); From 764682e0118432260191d194edbdaff208260483 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 01:23:29 -0400 Subject: [PATCH 05/18] configfs_depend_prep(): pass configfs_dirent instead of dentry Again, the only thing it uses dentry for is dentry->d_fsdata; for the recursive call the situation is the same as with configfs_detach_prep() and the same observation about ->s_dentry->d_fsdata applies. Reviewed-by: Jan Kara Reviewed-by: Breno Leitao Signed-off-by: Al Viro --- fs/configfs/dir.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index b456e4de25ab..a6b99bcbddbc 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1093,15 +1093,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; @@ -1109,8 +1106,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 */ } @@ -1131,7 +1127,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_dentry->d_fsdata, target); if (ret) goto out_unlock_dirent_lock; From cbd6bbd48732987de51e8a6d4917875daa636079 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 01:25:48 -0400 Subject: [PATCH 06/18] configfs_do_depend_item(): pass configfs_dirent instead of dentry Again, the only thing it uses the argument for is its ->d_fsdata and callers already have that - as the matter of fact, they are passing ->s_dentry of that configfs_dirent, so that the function could get it back as ->d_fsdata of that. With nothing else in dentry even looked at... configfs_dirent in question is a directory one - in this case those are subdirectories of root (aka roots of "subsystem" trees). Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a6b99bcbddbc..c6055a626bef 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1119,7 +1119,7 @@ static int configfs_depend_prep(struct configfs_dirent *sd, 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; @@ -1127,7 +1127,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->d_fsdata, target); + ret = configfs_depend_prep(subsys_sd, target); if (ret) goto out_unlock_dirent_lock; @@ -1195,7 +1195,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)); @@ -1297,7 +1297,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: From acef053618d190c3ac418eda3f678d1043e9362c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 01:29:00 -0400 Subject: [PATCH 07/18] configfs_detach_rollback(): pass configfs_dirent instead of dentry same story as with configfs_detach_prep() this function is undoing. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index c6055a626bef..c420191610c3 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -563,16 +563,15 @@ static int configfs_detach_prep(struct configfs_dirent *parent_sd, struct dentry * 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); } /* @@ -1537,7 +1536,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) if (!ret) { 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); @@ -1558,7 +1557,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; From 17bee4d214a4dd6f062edf5eb71fbe681304bc32 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 02:10:35 -0400 Subject: [PATCH 08/18] populate_group(): move cleanup on failure to the sole caller ... where it folds with configfs_detach_item() into a call of configfs_detach_group(). Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index c420191610c3..01de8ef5fbe6 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -754,17 +754,13 @@ static int populate_groups(struct config_group *group, struct configfs_fragment *frag) { 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(group, new_group, frag); + if (ret) + return ret; } - - return ret; + return 0; } void configfs_remove_default_groups(struct config_group *group) @@ -904,6 +900,13 @@ static void configfs_detach_item(struct config_item *item) configfs_remove_dir(item); } +/* 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); +} + static int configfs_attach_group(struct config_item *parent_item, struct config_item *item, struct dentry *dentry, @@ -930,7 +933,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(item); d_inode(dentry)->i_flags |= S_DEAD; dont_mount(dentry); } @@ -943,13 +946,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 From 34a62943762cffb1d40b5aa2e09371408b291b5f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 02:18:38 -0400 Subject: [PATCH 09/18] populate_attrs(): move cleanup to the sole caller ... where it folds with configfs_remove_dir() into a call of configfs_detach_item(). Note that at the early failure exit (before we'd added any children) we were not calling detach_attrs() only because there it would've been a no-op - nothing added, nothing there to be removed. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 01de8ef5fbe6..6580c919ee5a 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -643,25 +643,23 @@ 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, @@ -850,6 +848,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 config_item *item) +{ + detach_attrs(item); + configfs_remove_dir(item); +} + /* * The goal is that configfs_attach_item() (and * configfs_attach_group()) can be called from either the VFS or this @@ -882,7 +887,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(item); d_inode(dentry)->i_flags |= S_DEAD; dont_mount(dentry); inode_unlock(d_inode(dentry)); @@ -893,13 +898,6 @@ 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) -{ - detach_attrs(item); - configfs_remove_dir(item); -} - /* Caller holds the mutex of the group's inode */ static void configfs_detach_group(struct config_item *item) { From 05a4d3d1b676c42ea676e39a8b459eadf81d8d37 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 02:26:41 -0400 Subject: [PATCH 10/18] configfs_remove_dir(), detach_attrs(): switch to passing dentry ... and deal with grabbing/dropping it in the sole caller. After that configfs_remove_dir() becomes an unconditional call of remove_dir(), so we can fold them together. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 60 ++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 6580c919ee5a..11e2597fb1e2 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -395,7 +395,18 @@ int configfs_create_link(struct configfs_dirent *target, struct dentry *parent, return PTR_ERR(inode); } -static void remove_dir(struct dentry * d) +/** + * configfs_remove_dir - remove an config_item's directory. + * @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 + * what used to be configfs_rmdir() below, instead of calling separately. + * + * Caller holds the mutex of the item's inode + */ + +static void configfs_remove_dir(struct dentry *d) { struct dentry * parent = dget(d->d_parent); @@ -415,31 +426,6 @@ static void remove_dir(struct dentry * d) dput(parent); } -/** - * configfs_remove_dir - remove an config_item's directory. - * @item: config_item 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 - * what used to be configfs_rmdir() below, instead of calling separately. - * - * Caller holds the mutex of the item's inode - */ - -static void configfs_remove_dir(struct config_item * item) -{ - struct dentry * dentry = dget(item->ci_dentry); - - if (!dentry) - return; - - remove_dir(dentry); - /** - * Drop reference from dget() on entrance. - */ - dput(dentry); -} - static struct dentry * configfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) @@ -591,17 +577,12 @@ static struct configfs_dirent *next_dirent(struct configfs_dirent *parent, return NULL; } -static void detach_attrs(struct config_item * item) +static void detach_attrs(struct dentry *dentry) { - struct dentry * dentry = dget(item->ci_dentry); struct configfs_dirent *parent_sd; struct configfs_dirent *sd, *next; - if (!dentry) - return; - - 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; @@ -617,11 +598,6 @@ static void detach_attrs(struct config_item * item) spin_lock(&configfs_dirent_lock); } spin_unlock(&configfs_dirent_lock); - - /** - * Drop reference from dget() on entrance. - */ - dput(dentry); } static int populate_attrs(struct config_item *item) @@ -851,8 +827,12 @@ static void link_group(struct config_group *parent_group, struct config_group *g /* Caller holds the mutex of the item's inode */ static void configfs_detach_item(struct config_item *item) { - detach_attrs(item); - configfs_remove_dir(item); + struct dentry *dentry = dget(item->ci_dentry); + if (dentry) { + detach_attrs(dentry); + configfs_remove_dir(dentry); + dput(dentry); + } } /* From abe91a2aa9c4a704e290e0e164228ff20a123c0a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 03:13:37 -0400 Subject: [PATCH 11/18] switch configfs_detach_{group,item}() to passing dentry ... and there's no need to grab/drop it, or check for NULL - none of the callers would even get there with NULL dentry and all of them have the sucker pinned Note that if sd is a directory configfs_dirent, we have sd->s_element pointing to config_item with item->ci_dentry equal to sd->s_dentry. Which is the only reason why detach_groups() gets away with using the latter for locking the inode and the former for removal. Aren't redundant data structures wonderful, for obfuscation if nothing else? Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 11e2597fb1e2..87a0847d88b9 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -642,18 +642,14 @@ static int configfs_attach_group(struct config_item *parent_item, 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, *next; - if (!dentry) - return; - parent_sd = dentry->d_fsdata; spin_lock(&configfs_dirent_lock); for (sd = next_dirent(parent_sd, NULL); sd; sd = next) { @@ -666,7 +662,7 @@ static void detach_groups(struct config_group *group) 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); @@ -677,11 +673,6 @@ static void detach_groups(struct config_group *group) spin_lock(&configfs_dirent_lock); } spin_unlock(&configfs_dirent_lock); - - /** - * Drop reference from dget() on entrance. - */ - dput(dentry); } /* @@ -825,14 +816,10 @@ static void link_group(struct config_group *parent_group, struct config_group *g } /* Caller holds the mutex of the item's inode */ -static void configfs_detach_item(struct config_item *item) +static void configfs_detach_item(struct dentry *dentry) { - struct dentry *dentry = dget(item->ci_dentry); - if (dentry) { - detach_attrs(dentry); - configfs_remove_dir(dentry); - dput(dentry); - } + detach_attrs(dentry); + configfs_remove_dir(dentry); } /* @@ -867,7 +854,7 @@ static int configfs_attach_item(struct config_item *parent_item, * we must lock them as rmdir() would. */ inode_lock(d_inode(dentry)); - configfs_detach_item(item); + configfs_detach_item(dentry); d_inode(dentry)->i_flags |= S_DEAD; dont_mount(dentry); inode_unlock(d_inode(dentry)); @@ -879,10 +866,10 @@ static int configfs_attach_item(struct config_item *parent_item, } /* Caller holds the mutex of the group's inode */ -static void configfs_detach_group(struct config_item *item) +static void configfs_detach_group(struct dentry *dentry) { - detach_groups(to_config_group(item)); - configfs_detach_item(item); + detach_groups(dentry); + configfs_detach_item(dentry); } static int configfs_attach_group(struct config_item *parent_item, @@ -911,7 +898,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_group(item); + configfs_detach_group(dentry); d_inode(dentry)->i_flags |= S_DEAD; dont_mount(dentry); } @@ -1549,13 +1536,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); @@ -1808,7 +1795,7 @@ void configfs_unregister_group(struct config_group *group) 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); @@ -1957,7 +1944,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys) } 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)); From 1a29e25799bcd7c3b2285f46e2c630b62d969b25 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 03:19:41 -0400 Subject: [PATCH 12/18] configfs: dentry refcount needs to be pinned only once currently we have a weird situation where * symlinks and roots of subtrees created by mkdir are pinned once * subdirectories of subtrees created by mkdir are pinned twice * roots of subtrees created by register_{group,subsystem} are pinned twice. It makes things harder to follow for no good reason. The goal is to encapsulate the unbalanced dget/dput into d_{make,discard}_persisitent() and, preferably, allow a use of simple_recursive_removal() or analogue thereof. So let's regularize that and pin things only once. create_default_group() and configfs_register_subsystem() don't need to keep their reference around on success - configfs_create_dir() has pinned the sucker already. So we can drop the reference passed to configfs_create_dir() (via configfs_attach_group(), etc.) both on success and on failure. On the removal side we no longer have the double references, so we need an explicit dget() to compensate. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 87a0847d88b9..4836ffb3b70f 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -657,7 +657,7 @@ static void detach_groups(struct dentry *dentry) 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)); @@ -708,8 +708,8 @@ static int create_default_group(struct config_group *parent_group, } else { BUG_ON(d_inode(child)); d_drop(child); - dput(child); } + dput(child); } return ret; @@ -1781,7 +1781,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; @@ -1896,12 +1896,12 @@ int configfs_register_subsystem(struct configfs_subsystem *subsys) 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)); @@ -1920,7 +1920,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; From 1387aedb391ac6e790fa941f29570d6f72113dd9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 12:18:21 -0400 Subject: [PATCH 13/18] configfs: mark pinned dentries persistent on the removal side we can (finally) get rid of __simple_unlink() and __simple_rmdir() kludges now that dentries in question are properly marked persistent - simple_unlink() and simple_rmdir() will do the right thing for those. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 13 +++---------- fs/configfs/symlink.c | 3 +-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 4836ffb3b70f..479d37b8d806 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -315,9 +315,7 @@ 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 */ + d_make_persistent(dentry, inode); inc_nlink(d_inode(p)); item->ci_dentry = dentry; return 0; @@ -385,8 +383,7 @@ 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); return 0; out_remove: @@ -413,12 +410,8 @@ static void configfs_remove_dir(struct dentry *d) configfs_remove_dirent(d); if (d_really_is_positive(d)) { - if (likely(simple_empty(d))) { - __simple_rmdir(d_inode(parent),d); - dput(d); - } else { + 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)); 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 From 43b45755e80e5eee9d3f6271d301940af7dbc29b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2026 12:53:35 -0400 Subject: [PATCH 14/18] kill configfs_drop_dentry() Fold into the only remaining user, don't bother with the timestamps of parent - we are going to rmdir it shortly anyway, which will override those. Fix the locking of inode, while we are at it - updating the link count and timestamps ought to be done with the inode locked. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/configfs_internal.h | 1 - fs/configfs/dir.c | 24 +++++++++++++++++++++++- fs/configfs/inode.c | 22 ---------------------- 3 files changed, 23 insertions(+), 24 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 479d37b8d806..4a4bad1741cf 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -581,12 +581,34 @@ static void detach_attrs(struct dentry *dentry) 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; 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); } diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index bc507b720a01..3c26b6c443be 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -195,25 +195,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); - } -} From 967c898d9defb5b6855ea9325eb2dda3b7f06bab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 18 May 2026 23:48:50 -0400 Subject: [PATCH 15/18] configfs_create(): lift parent timestamp updates into callers ... and do *not* do it in ->lookup() case. stat foo/bar should not update mtime of foo, TYVM... Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 6 +++++- fs/configfs/inode.c | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 4a4bad1741cf..b382d60e7ebc 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -296,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); @@ -316,7 +317,8 @@ static int configfs_create_dir(struct config_item *item, struct dentry *dentry, /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); d_make_persistent(dentry, inode); - inc_nlink(d_inode(p)); + inc_nlink(p_inode); + inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode)); item->ci_dentry = dentry; return 0; @@ -370,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,6 +387,7 @@ int configfs_create_link(struct configfs_dirent *target, struct dentry *parent, inode->i_link = body; inode->i_op = &configfs_symlink_inode_operations; d_make_persistent(dentry, inode); + inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode)); return 0; out_remove: diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index 3c26b6c443be..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; } From d4152c7562a8759ef98f882385121b8dee04606d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 26 May 2026 19:16:24 -0400 Subject: [PATCH 16/18] configs_attach_item(): drop unused parent_item argument That argument has been unused since the initial merge in 2005. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index b382d60e7ebc..2f3f3f504e2c 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -856,8 +856,7 @@ static void configfs_detach_item(struct dentry *dentry) * 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) { @@ -899,7 +898,7 @@ static int configfs_attach_group(struct config_item *parent_item, 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; @@ -1426,7 +1425,7 @@ static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (group) ret = configfs_attach_group(parent_item, 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; From ad235a65dfff7e2e559fbcfe0f600d58b7df2396 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 26 May 2026 19:19:45 -0400 Subject: [PATCH 17/18] configfs_attach_group(): drop the unused parent_item argument This one *was* used - for passing it to configfs_attach_item(), which didn't use the value passed to it. Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 2f3f3f504e2c..8fc05fe69992 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -657,8 +657,7 @@ static int populate_attrs(struct config_item *item) 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 dentry *dentry); @@ -719,8 +718,7 @@ 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; @@ -890,8 +888,7 @@ static void configfs_detach_group(struct dentry *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) { @@ -1423,7 +1420,7 @@ 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(item, dentry, frag); @@ -1908,8 +1905,7 @@ 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)); From d53ac61b6120fa596d758ff6e22b5dcb6db21ce8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 26 May 2026 19:23:56 -0400 Subject: [PATCH 18/18] create_default_group(): pass parent's dentry instead of config_group the only way parent_group is used there... Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/configfs/dir.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 8fc05fe69992..4d82375b0cfd 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -701,14 +701,14 @@ static void detach_groups(struct dentry *dentry) * 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; @@ -735,10 +735,11 @@ 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; list_for_each_entry(new_group, &group->default_groups, group_entry) { - int ret = create_default_group(group, new_group, frag); + int ret = create_default_group(parent, new_group, frag); if (ret) return ret; } @@ -1767,7 +1768,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;