Quelle dir.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dir . c - Operations for configfs directories .
*
* Based on sysfs :
* sysfs is Copyright ( C ) 2001 , 2002 , 2003 Patrick Mochel
*
* configfs Copyright ( C ) 2005 Oracle . All rights reserved .
*/
#undef DEBUG
#include <linux/fs.h>
#include <linux/fsnotify.h>
#include <linux/mount.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/configfs.h>
#include "configfs_internal.h"
/*
* Protects mutations of configfs_dirent linkage together with proper i_mutex
* Also protects mutations of symlinks linkage to target configfs_dirent
* Mutators of configfs_dirent linkage must * both * have the proper inode locked
* and configfs_dirent_lock locked , in that order .
* This allows one to safely traverse configfs_dirent trees and symlinks without
* having to lock inodes .
*
* Protects setting of CONFIGFS_USET_DROPPING : checking the flag
* unlocked is not reliable unless in detach_groups ( ) called from
* rmdir ( ) / unregister ( ) and from configfs_attach_group ( )
*/
DEFINE_SPINLOCK(configfs_dirent_lock);
/*
* All of link_obj / unlink_obj / link_group / unlink_group require that
* subsys - > su_mutex is held .
* But parent configfs_subsystem is NULL when config_item is root .
* Use this mutex when config_item is root .
*/
static DEFINE_MUTEX(configfs_subsystem_mutex);
static void configfs_d_iput(struct dentry * dentry,
struct inode * inode)
{
struct configfs_dirent *sd = dentry->d_fsdata;
if (sd) {
/* Coordinate with configfs_readdir */
spin_lock(&configfs_dirent_lock);
/*
* Set sd - > s_dentry to null only when this dentry is the one
* that is going to be killed . Otherwise configfs_d_iput may
* run just after configfs_lookup and set sd - > s_dentry to
* NULL even it ' s still in use .
*/
if (sd->s_dentry == dentry)
sd->s_dentry = NULL;
spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
}
iput(inode);
}
const struct dentry_operations configfs_dentry_ops = {
.d_iput = configfs_d_iput,
};
#ifdef CONFIG_LOCKDEP
/*
* Helpers to make lockdep happy with our recursive locking of default groups '
* inodes ( see configfs_attach_group ( ) and configfs_detach_group ( ) ) .
* We put default groups i_mutexes in separate classes according to their depth
* from the youngest non - default group ancestor .
*
* For a non - default group A having default groups A / B , A / C , and A / C / D , default
* groups A / B and A / C will have their inode ' s mutex in class
* default_group_class [ 0 ] , and default group A / C / D will be in
* default_group_class [ 1 ] .
*
* The lock classes are declared and assigned in inode . c , according to the
* s_depth value .
* The s_depth value is initialized to - 1 , adjusted to > = 0 when attaching
* default groups , and reset to - 1 when all default groups are attached . During
* attachment , if configfs_create ( ) sees s_depth > 0 , the lock class of the new
* inode ' s mutex is set to default_group_class [ s_depth - 1 ] .
*/
static void configfs_init_dirent_depth(struct configfs_dirent *sd)
{
sd->s_depth = -1 ;
}
static void configfs_set_dir_dirent_depth(struct configfs_dirent *parent_sd,
struct configfs_dirent *sd)
{
int parent_depth = parent_sd->s_depth;
if (parent_depth >= 0 )
sd->s_depth = parent_depth + 1 ;
}
static void
configfs_adjust_dir_dirent_depth_before_populate(struct configfs_dirent *sd)
{
/*
* item ' s i_mutex class is already setup , so s_depth is now only
* used to set new sub - directories s_depth , which is always done
* with item ' s i_mutex locked .
*/
/*
* sd - > s_depth = = - 1 iff we are a non default group .
* else ( we are a default group ) sd - > s_depth > 0 ( see
* create_dir ( ) ) .
*/
if (sd->s_depth == -1 )
/*
* We are a non default group and we are going to create
* default groups .
*/
sd->s_depth = 0 ;
}
static void
configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
{
/* We will not create default groups anymore. */
sd->s_depth = -1 ;
}
#else /* CONFIG_LOCKDEP */
static void configfs_init_dirent_depth(struct configfs_dirent *sd)
{
}
static void configfs_set_dir_dirent_depth(struct configfs_dirent *parent_sd,
struct configfs_dirent *sd)
{
}
static void
configfs_adjust_dir_dirent_depth_before_populate(struct configfs_dirent *sd)
{
}
static void
configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
{
}
#endif /* CONFIG_LOCKDEP */
static struct configfs_fragment *new_fragment(void )
{
struct configfs_fragment *p;
p = kmalloc(sizeof (struct configfs_fragment), GFP_KERNEL);
if (p) {
atomic_set(&p->frag_count, 1 );
init_rwsem(&p->frag_sem);
p->frag_dead = false ;
}
return p;
}
void put_fragment(struct configfs_fragment *frag)
{
if (frag && atomic_dec_and_test(&frag->frag_count))
kfree(frag);
}
struct configfs_fragment *get_fragment(struct configfs_fragment *frag)
{
if (likely(frag))
atomic_inc(&frag->frag_count);
return frag;
}
/*
* Allocates a new configfs_dirent and links it to the parent configfs_dirent
*/
static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd,
void *element, int type,
struct configfs_fragment *frag)
{
struct configfs_dirent * sd;
sd = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL);
if (!sd)
return ERR_PTR(-ENOMEM);
atomic_set(&sd->s_count, 1 );
INIT_LIST_HEAD(&sd->s_children);
sd->s_element = element;
sd->s_type = type;
configfs_init_dirent_depth(sd);
spin_lock(&configfs_dirent_lock);
if (parent_sd->s_type & CONFIGFS_USET_DROPPING) {
spin_unlock(&configfs_dirent_lock);
kmem_cache_free(configfs_dir_cachep, sd);
return ERR_PTR(-ENOENT);
}
sd->s_frag = get_fragment(frag);
/*
* configfs_lookup scans only for unpinned items . s_children is
* partitioned so that configfs_lookup can bail out early .
* CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are not symmetrical . readdir
* cursors still need to be inserted at the front of the list .
*/
if (sd->s_type & CONFIGFS_PINNED)
list_add_tail(&sd->s_sibling, &parent_sd->s_children);
else
list_add(&sd->s_sibling, &parent_sd->s_children);
spin_unlock(&configfs_dirent_lock);
return sd;
}
/*
*
* Return - EEXIST if there is already a configfs element with the same
* name for the same parent .
*
* called with parent inode ' s i_mutex held
*/
static int configfs_dirent_exists(struct dentry *dentry)
{
struct configfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
const unsigned char *new = dentry->d_name.name;
struct configfs_dirent *sd;
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
return -EEXIST;
}
}
return 0 ;
}
int configfs_make_dirent(struct configfs_dirent * parent_sd,
struct dentry * dentry, void * element,
umode_t mode, int type, struct configfs_fragment *frag)
{
struct configfs_dirent * sd;
sd = configfs_new_dirent(parent_sd, element, type, frag);
if (IS_ERR(sd))
return PTR_ERR(sd);
sd->s_mode = mode;
sd->s_dentry = dentry;
if (dentry)
dentry->d_fsdata = configfs_get(sd);
return 0 ;
}
static void configfs_remove_dirent(struct dentry *dentry)
{
struct configfs_dirent *sd = dentry->d_fsdata;
if (!sd)
return ;
spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
}
/**
* configfs_create_dir - create a directory for an config_item .
* @ item : config_itemwe ' re creating directory for .
* @ dentry : config_item ' s dentry .
* @ frag : config_item ' s fragment .
*
* Note : user - created entries won ' t be allowed under this new directory
* until it is validated by configfs_dir_set_ready ( )
*/
static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
struct configfs_fragment *frag)
{
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct dentry *p = dentry->d_parent;
struct inode *inode;
BUG_ON(!item);
error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
CONFIGFS_DIR | CONFIGFS_USET_CREATING,
frag);
if (unlikely(error))
return error;
configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
inode = configfs_create(dentry, mode);
if (IS_ERR(inode))
goto out_remove;
inode->i_op = &configfs_dir_inode_operations;
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));
item->ci_dentry = dentry;
return 0 ;
out_remove:
configfs_put(dentry->d_fsdata);
configfs_remove_dirent(dentry);
return PTR_ERR(inode);
}
/*
* Allow userspace to create new entries under a new directory created with
* configfs_create_dir ( ) , and under all of its chidlren directories recursively .
* @ sd configfs_dirent of the new directory to validate
*
* Caller must hold configfs_dirent_lock .
*/
static void configfs_dir_set_ready(struct configfs_dirent *sd)
{
struct configfs_dirent *child_sd;
sd->s_type &= ~CONFIGFS_USET_CREATING;
list_for_each_entry(child_sd, &sd->s_children, s_sibling)
if (child_sd->s_type & CONFIGFS_USET_CREATING)
configfs_dir_set_ready(child_sd);
}
/*
* Check that a directory does not belong to a directory hierarchy being
* attached and not validated yet .
* @ sd configfs_dirent of the directory to check
*
* @ return non - zero iff the directory was validated
*
* Note : takes configfs_dirent_lock , so the result may change from false to true
* in two consecutive calls , but never from true to false .
*/
int configfs_dirent_is_ready(struct configfs_dirent *sd)
{
int ret;
spin_lock(&configfs_dirent_lock);
ret = !(sd->s_type & CONFIGFS_USET_CREATING);
spin_unlock(&configfs_dirent_lock);
return ret;
}
int configfs_create_link(struct configfs_dirent *target, struct dentry *parent,
struct dentry *dentry, char *body)
{
int err = 0 ;
umode_t mode = S_IFLNK | S_IRWXUGO;
struct configfs_dirent *p = parent->d_fsdata;
struct inode *inode;
err = configfs_make_dirent(p, dentry, target, mode, CONFIGFS_ITEM_LINK,
p->s_frag);
if (err)
return err;
inode = configfs_create(dentry, mode);
if (IS_ERR(inode))
goto out_remove;
inode->i_link = body;
inode->i_op = &configfs_symlink_inode_operations;
d_instantiate(dentry, inode);
dget(dentry); /* pin link dentries in core */
return 0 ;
out_remove:
configfs_put(dentry->d_fsdata);
configfs_remove_dirent(dentry);
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))
simple_rmdir(d_inode(parent),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 .
*
* 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)
{
struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
struct configfs_dirent * sd;
struct inode *inode = NULL;
if (dentry->d_name.len > NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
/*
* Fake invisibility if dir belongs to a group / default groups hierarchy
* being attached
*
* This forbids userspace to read / write attributes of items which may
* not complete their initialization , since the dentries of the
* attributes won ' t be instantiated .
*/
if (!configfs_dirent_is_ready(parent_sd))
return ERR_PTR(-ENOENT);
spin_lock(&configfs_dirent_lock);
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
/*
* s_children is partitioned , see configfs_new_dirent . The first
* pinned item indicates we can stop scanning .
*/
if (sd->s_type & CONFIGFS_PINNED)
break ;
/*
* Note : CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are asymmetric .
* there may be a readdir cursor in this list
*/
if ((sd->s_type & CONFIGFS_NOT_PINNED) &&
!strcmp(configfs_get_name(sd), dentry->d_name.name)) {
struct configfs_attribute *attr = sd->s_element;
umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
dentry->d_fsdata = configfs_get(sd);
sd->s_dentry = dentry;
spin_unlock(&configfs_dirent_lock);
inode = configfs_create(dentry, mode);
if (IS_ERR(inode)) {
configfs_put(sd);
return ERR_CAST(inode);
}
if (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) {
inode->i_size = 0 ;
inode->i_fop = &configfs_bin_file_operations;
} else {
inode->i_size = PAGE_SIZE;
inode->i_fop = &configfs_file_operations;
}
goto done;
}
}
spin_unlock(&configfs_dirent_lock);
done:
d_add(dentry, inode);
return NULL;
}
/*
* Only subdirectories count here . Files ( CONFIGFS_NOT_PINNED ) are
* attributes and are removed by rmdir ( ) . We recurse , setting
* CONFIGFS_USET_DROPPING on all children that are candidates for
* default detach .
* 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)
{
struct configfs_dirent *parent_sd = dentry->d_fsdata;
struct configfs_dirent *sd;
int ret;
/* Mark that we're trying to drop the group */
parent_sd->s_type |= CONFIGFS_USET_DROPPING;
ret = -EBUSY;
if (parent_sd->s_links)
goto out;
ret = 0 ;
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (!sd->s_element ||
(sd->s_type & CONFIGFS_NOT_PINNED))
continue ;
if (sd->s_type & CONFIGFS_USET_DEFAULT) {
/* Abort if racing with mkdir() */
if (sd->s_type & CONFIGFS_USET_IN_MKDIR) {
if (wait)
*wait= dget(sd->s_dentry);
return -EAGAIN;
}
/*
* Yup , recursive . If there ' s a problem , blame
* deep nesting of default_groups
*/
ret = configfs_detach_prep(sd->s_dentry, wait);
if (!ret)
continue ;
} else
ret = -ENOTEMPTY;
break ;
}
out:
return ret;
}
/*
* Walk the tree , resetting CONFIGFS_USET_DROPPING wherever it was
* set .
*/
static void configfs_detach_rollback(struct dentry *dentry)
{
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);
}
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;
if (!dentry)
return ;
pr_debug("configfs %s: dropping attrs for dir\n" ,
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))
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);
}
/**
* Drop reference from dget ( ) on entrance .
*/
dput(dentry);
}
static int populate_attrs(struct config_item *item)
{
const struct config_item_type *t = item->ci_type;
struct configfs_group_operations *ops;
struct configfs_attribute *attr;
struct configfs_bin_attribute *bin_attr;
int error = 0 ;
int i;
if (!t)
return -EINVAL;
ops = t->ct_group_ops;
if (t->ct_attrs) {
for (i = 0 ; (attr = t->ct_attrs[i]) != NULL; i++) {
if (ops && ops->is_visible && !ops->is_visible(item, attr, i))
continue ;
if ((error = configfs_create_file(item, attr)))
break ;
}
}
if (!error && 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 ;
}
}
if (error)
detach_attrs(item);
return error;
}
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 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;
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))
continue ;
child = sd->s_dentry;
inode_lock(d_inode(child));
configfs_detach_group(sd->s_element);
d_inode(child)->i_flags |= S_DEAD;
dont_mount(child);
inode_unlock(d_inode(child));
d_delete(child);
dput(child);
}
/**
* Drop reference from dget ( ) on entrance .
*/
dput(dentry);
}
/*
* This fakes mkdir ( 2 ) on a default_groups [ ] entry . It
* creates a dentry , attachs it , and then does fixup
* on the sd - > s_type .
*
* 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,
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;
if (!group->cg_item.ci_name)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
ret = -ENOMEM;
child = d_alloc_name(parent, group->cg_item.ci_name);
if (child) {
d_add(child, NULL);
ret = configfs_attach_group(&parent_group->cg_item,
&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);
}
}
return ret;
}
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 ;
}
}
return ret;
}
void configfs_remove_default_groups(struct config_group *group)
{
struct config_group *g, *n;
list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
list_del(&g->group_entry);
config_item_put(&g->cg_item);
}
}
EXPORT_SYMBOL(configfs_remove_default_groups);
/*
* All of link_obj / unlink_obj / link_group / unlink_group require that
* subsys - > su_mutex is held .
*/
static void unlink_obj(struct config_item *item)
{
struct config_group *group;
group = item->ci_group;
if (group) {
list_del_init(&item->ci_entry);
item->ci_group = NULL;
item->ci_parent = NULL;
/* Drop the reference for ci_entry */
config_item_put(item);
/* Drop the reference for ci_parent */
config_group_put(group);
}
}
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 .
*/
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));
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);
}
static void unlink_group(struct config_group *group)
{
struct config_group *new_group;
list_for_each_entry(new_group, &group->default_groups, group_entry)
unlink_group(new_group);
group->cg_subsys = NULL;
unlink_obj(&group->cg_item);
}
static void link_group(struct config_group *parent_group, struct config_group *group)
{
struct config_group *new_group;
struct configfs_subsystem *subsys = NULL; /* gcc is a turd */
link_obj(&parent_group->cg_item, &group->cg_item);
if (parent_group->cg_subsys)
subsys = parent_group->cg_subsys;
else if (configfs_is_root(&parent_group->cg_item))
subsys = to_configfs_subsystem(group);
else
BUG();
group->cg_subsys = subsys;
list_for_each_entry(new_group, &group->default_groups, group_entry)
link_group(group, new_group);
}
/*
* The goal is that configfs_attach_item ( ) ( and
* configfs_attach_group ( ) ) can be called from either the VFS or this
* module . That is , they assume that the items have been created ,
* the dentry allocated , and the dcache is all ready to go .
*
* If they fail , they must clean up after themselves as if they
* had never been called . The caller ( VFS or local function ) will
* handle cleaning up the dcache bits .
*
* configfs_detach_group ( ) and configfs_detach_item ( ) behave similarly on
* the way out . They assume that the proper semaphores are held , they
* 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,
struct dentry *dentry,
struct configfs_fragment *frag)
{
int ret;
ret = configfs_create_dir(item, dentry, frag);
if (!ret) {
ret = populate_attrs(item);
if (ret) {
/*
* We are going to remove an inode and its dentry but
* the VFS may already have hit and used them . Thus ,
* we must lock them as rmdir ( ) would .
*/
inode_lock(d_inode(dentry));
configfs_remove_dir(item);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
inode_unlock(d_inode(dentry));
d_delete(dentry);
}
}
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);
}
static int configfs_attach_group(struct config_item *parent_item,
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);
if (!ret) {
sd = dentry->d_fsdata;
sd->s_type |= CONFIGFS_USET_DIR;
/*
* FYI , we ' re faking mkdir in populate_groups ( )
* We must lock the group ' s inode to avoid races with the VFS
* which can already hit the inode and try to add / remove entries
* under it .
*
* We must also lock the inode to remove it safely in case of
* error , as rmdir ( ) would .
*/
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
configfs_adjust_dir_dirent_depth_before_populate(sd);
ret = populate_groups(to_config_group(item), frag);
if (ret) {
configfs_detach_item(item);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
}
configfs_adjust_dir_dirent_depth_after_populate(sd);
inode_unlock(d_inode(dentry));
if (ret)
d_delete(dentry);
}
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
* we do that so they can perform any cleanup that requires
* navigating the hierarchy . A client does not need to provide this
* callback . The subsystem semaphore MUST be held by the caller , and
* references must be valid for both items . It also assumes the
* caller has validated ci_type .
*/
static void client_disconnect_notify(struct config_item *parent_item,
struct config_item *item)
{
const struct config_item_type *type;
type = parent_item->ci_type;
BUG_ON(!type);
if (type->ct_group_ops && type->ct_group_ops->disconnect_notify)
type->ct_group_ops->disconnect_notify(to_config_group(parent_item),
item);
}
/*
* Drop the initial reference from make_item ( ) / make_group ( )
* This function assumes that reference is held on item
* and that item holds a valid reference to the parent . Also , it
* assumes the caller has validated ci_type .
*/
static void client_drop_item(struct config_item *parent_item,
struct config_item *item)
{
const struct config_item_type *type;
type = parent_item->ci_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)
type->ct_group_ops->drop_item(to_config_group(parent_item),
item);
else
config_item_put(item);
}
#ifdef DEBUG
static void configfs_dump_one(struct configfs_dirent *sd, int level)
{
pr_info("%*s\" %s\":\n" , level, " " , configfs_get_name(sd));
#define type_print(_type) if (sd->s_type & _type) pr_info("%*s %s\n" , level, " " , #_ type)
type_print(CONFIGFS_ROOT);
type_print(CONFIGFS_DIR);
type_print(CONFIGFS_ITEM_ATTR);
type_print(CONFIGFS_ITEM_LINK);
type_print(CONFIGFS_USET_DIR);
type_print(CONFIGFS_USET_DEFAULT);
type_print(CONFIGFS_USET_DROPPING);
#undef type_print
}
static int configfs_dump(struct configfs_dirent *sd, int level)
{
struct configfs_dirent *child_sd;
int ret = 0 ;
configfs_dump_one(sd, level);
if (!(sd->s_type & (CONFIGFS_DIR|CONFIGFS_ROOT)))
return 0 ;
list_for_each_entry(child_sd, &sd->s_children, s_sibling) {
ret = configfs_dump(child_sd, level + 2 );
if (ret)
break ;
}
return ret;
}
#endif
/*
* configfs_depend_item ( ) and configfs_undepend_item ( )
*
* WARNING : Do not call these from a configfs callback !
*
* This describes these functions and their helpers .
*
* Allow another kernel system to depend on a config_item . If this
* happens , the item cannot go away until the dependent can live without
* it . The idea is to give client modules as simple an interface as
* possible . When a system asks them to depend on an item , they just
* call configfs_depend_item ( ) . If the item is live and the client
* driver is in good shape , we ' ll happily do the work for them .
*
* Why is the locking complex ? Because configfs uses the VFS to handle
* all locking , but this function is called outside the normal
* VFS - > configfs path . So it must take VFS locks to prevent the
* VFS - > configfs stuff ( configfs_mkdir ( ) , configfs_rmdir ( ) , etc ) . This is
* why you can ' t call these functions underneath configfs callbacks .
*
* Note , btw , that this can be called at * any * time , even when a configfs
* subsystem isn ' t registered , or when configfs is loading or unloading .
* Just like configfs_register_subsystem ( ) . So we take the same
* precautions . We pin the filesystem . We lock configfs_dirent_lock .
* If we can find the target item in the
* configfs tree , it must be part of the subsystem tree as well , so we
* do not need the subsystem semaphore . Holding configfs_dirent_lock helps
* locking out mkdir ( ) and rmdir ( ) , who might be racing us .
*/
/*
* configfs_depend_prep ( )
*
* Only subdirectories count here . Files ( CONFIGFS_NOT_PINNED ) are
* attributes . This is similar but not the same to configfs_detach_prep ( ) .
* Note that configfs_detach_prep ( ) expects the parent to be locked when it
* is called , but we lock the parent * inside * configfs_depend_prep ( ) . We
* do that so we can unlock it if we find nothing .
*
* Here we do a depth - first search of the dentry hierarchy looking for
* our object .
* We deliberately ignore items tagged as dropping since they are virtually
* dead , as well as items in the middle of attachment since they virtually
* do not exist yet . This completes the locking out of racing mkdir ( ) and
* rmdir ( ) .
* Note : subdirectories in the middle of attachment start with s_type =
* CONFIGFS_DIR | CONFIGFS_USET_CREATING set by create_dir ( ) . When
* CONFIGFS_USET_CREATING is set , we ignore the item . The actual set of
* s_type is in configfs_new_dirent ( ) , which has configfs_dirent_lock .
*
* If the target is not found , - ENOENT is bubbled up .
*
* This adds a requirement that all config_items be unique !
*
* This is recursive . There isn ' t
* 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,
struct config_item *target)
{
struct configfs_dirent *child_sd, *sd;
int ret = 0 ;
BUG_ON(!origin || !origin->d_fsdata);
sd = origin->d_fsdata;
if (sd->s_element == target) /* Boo-yah */
goto out;
list_for_each_entry(child_sd, &sd->s_children, s_sibling) {
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);
if (!ret)
goto out; /* Child path boo-yah */
}
}
/* We looped all our children and didn't find target */
ret = -ENOENT;
out:
return ret;
}
static int configfs_do_depend_item(struct dentry *subsys_dentry,
struct config_item *target)
{
struct configfs_dirent *p;
int ret;
spin_lock(&configfs_dirent_lock);
/* Scan the tree, return 0 if found */
ret = configfs_depend_prep(subsys_dentry, target);
if (ret)
goto out_unlock_dirent_lock;
/*
* We are sure that the item is not about to be removed by rmdir ( ) , and
* not in the middle of attachment by mkdir ( ) .
*/
p = target->ci_dentry->d_fsdata;
p->s_dependent_count += 1 ;
out_unlock_dirent_lock:
spin_unlock(&configfs_dirent_lock);
return ret;
}
static inline struct configfs_dirent *
configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
struct config_item *subsys_item)
{
struct configfs_dirent *p;
struct configfs_dirent *ret = NULL;
list_for_each_entry(p, &root_sd->s_children, s_sibling) {
if (p->s_type & CONFIGFS_DIR &&
p->s_element == subsys_item) {
ret = p;
break ;
}
}
return ret;
}
int configfs_depend_item(struct configfs_subsystem *subsys,
struct config_item *target)
{
int ret;
struct configfs_dirent *subsys_sd;
struct config_item *s_item = &subsys->su_group.cg_item;
struct dentry *root;
/*
* Pin the configfs filesystem . This means we can safely access
* the root of the configfs filesystem .
*/
root = configfs_pin_fs();
if (IS_ERR(root))
return PTR_ERR(root);
/*
* Next , lock the root directory . We ' re going to check that the
* subsystem is really registered , and so we need to lock out
* configfs_ [ un ] register_subsystem ( ) .
*/
inode_lock(d_inode(root));
subsys_sd = configfs_find_subsys_dentry(root->d_fsdata, s_item);
if (!subsys_sd) {
ret = -ENOENT;
goto out_unlock_fs;
}
/* Ok, now we can trust subsys/s_item */
ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
out_unlock_fs:
inode_unlock(d_inode(root));
/*
* If we succeeded , the fs is pinned via other methods . If not ,
* we ' re done with it anyway . So release_fs ( ) is always right .
*/
configfs_release_fs();
return ret;
}
EXPORT_SYMBOL(configfs_depend_item);
/*
* Release the dependent linkage . This is much simpler than
* configfs_depend_item ( ) because we know that the client driver is
* pinned , thus the subsystem is pinned , and therefore configfs is pinned .
*/
void configfs_undepend_item(struct config_item *target)
{
struct configfs_dirent *sd;
/*
* Since we can trust everything is pinned , we just need
* configfs_dirent_lock .
*/
spin_lock(&configfs_dirent_lock);
sd = target->ci_dentry->d_fsdata;
BUG_ON(sd->s_dependent_count < 1 );
sd->s_dependent_count -= 1 ;
/*
* After this unlock , we cannot trust the item to stay alive !
* DO NOT REFERENCE item after this unlock .
*/
spin_unlock(&configfs_dirent_lock);
}
EXPORT_SYMBOL(configfs_undepend_item);
/*
* caller_subsys is a caller ' s subsystem not target ' s . This is used to
* determine if we should lock root and check subsys or not . When we are
* in the same subsystem as our target there is no need to do locking as
* we know that subsys is valid and is not unregistered during this function
* as we are called from callback of one of his children and VFS holds a lock
* on some inode . Otherwise we have to lock our root to ensure that target ' s
* subsystem it is not unregistered during this function .
*/
int configfs_depend_item_unlocked(struct configfs_subsystem *caller_subsys,
struct config_item *target)
{
struct configfs_subsystem *target_subsys;
struct config_group *root, *parent;
struct configfs_dirent *subsys_sd;
int ret = -ENOENT;
/* Disallow this function for configfs root */
if (configfs_is_root(target))
return -EINVAL;
parent = target->ci_group;
/*
* This may happen when someone is trying to depend root
* directory of some subsystem
*/
if (configfs_is_root(&parent->cg_item)) {
target_subsys = to_configfs_subsystem(to_config_group(target));
root = parent;
} else {
target_subsys = parent->cg_subsys;
/* Find a cofnigfs root as we may need it for locking */
for (root = parent; !configfs_is_root(&root->cg_item);
root = root->cg_item.ci_group)
;
}
if (target_subsys != caller_subsys) {
/*
* We are in other configfs subsystem , so we have to do
* additional locking to prevent other subsystem from being
* unregistered
*/
inode_lock(d_inode(root->cg_item.ci_dentry));
/*
* As we are trying to depend item from other subsystem
* we have to check if this subsystem is still registered
*/
subsys_sd = configfs_find_subsys_dentry(
root->cg_item.ci_dentry->d_fsdata,
&target_subsys->su_group.cg_item);
if (!subsys_sd)
goto out_root_unlock;
} else {
subsys_sd = target_subsys->su_group.cg_item.ci_dentry->d_fsdata;
}
/* Now we can execute core of depend item */
ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
if (target_subsys != caller_subsys)
out_root_unlock:
/*
* We were called from subsystem other than our target so we
* took some locks so now it ' s time to release them
*/
inode_unlock(d_inode(root->cg_item.ci_dentry));
return ret;
}
EXPORT_SYMBOL(configfs_depend_item_unlocked);
static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
int ret = 0 ;
int module_got = 0 ;
struct config_group *group = NULL;
struct config_item *item = NULL;
struct config_item *parent_item;
struct configfs_subsystem *subsys;
struct configfs_dirent *sd;
const struct config_item_type *type;
struct module *subsys_owner = NULL, *new_item_owner = NULL;
struct configfs_fragment *frag;
char *name;
sd = dentry->d_parent->d_fsdata;
/*
* Fake invisibility if dir belongs to a group / default groups hierarchy
* being attached
*/
if (!configfs_dirent_is_ready(sd)) {
ret = -ENOENT;
goto out;
}
if (!(sd->s_type & CONFIGFS_USET_DIR)) {
ret = -EPERM;
goto out;
}
frag = new_fragment();
if (!frag) {
ret = -ENOMEM;
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;
BUG_ON(!subsys);
if (!type || !type->ct_group_ops ||
(!type->ct_group_ops->make_group &&
!type->ct_group_ops->make_item)) {
ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
goto out_put;
}
/*
* The subsystem may belong to a different module than the item
* being created . We don ' t want to safely pin the new item but
* fail to pin the subsystem it sits under .
*/
if (!subsys->su_group.cg_item.ci_type) {
ret = -EINVAL;
goto out_put;
}
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
if (!try_module_get(subsys_owner)) {
ret = -EINVAL;
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);
if (!group)
group = ERR_PTR(-ENOMEM);
if (!IS_ERR(group)) {
link_group(to_config_group(parent_item), group);
item = &group->cg_item;
} else
ret = PTR_ERR(group);
} else {
item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
if (!item)
item = ERR_PTR(-ENOMEM);
if (!IS_ERR(item))
link_obj(parent_item, item);
else
ret = PTR_ERR(item);
}
mutex_unlock(&subsys->su_mutex);
kfree(name);
if (ret) {
/*
* If ret ! = 0 , then link_obj ( ) was never called .
* There are no extra references to clean up .
*/
goto out_subsys_put;
}
/*
* link_obj ( ) has been called ( via link_group ( ) for groups ) .
* From here on out , errors must clean that up .
*/
type = item->ci_type;
if (!type) {
ret = -EINVAL;
goto out_unlink;
}
new_item_owner = type->ct_owner;
if (!try_module_get(new_item_owner)) {
ret = -EINVAL;
goto out_unlink;
}
/*
* I hate doing it this way , but if there is
* an error , module_put ( ) probably should
* happen after any cleanup .
*/
module_got = 1 ;
/*
* Make racing rmdir ( ) fail if it did not tag parent with
* CONFIGFS_USET_DROPPING
* Note : if CONFIGFS_USET_DROPPING is already set , attach_group ( ) will
* fail and let rmdir ( ) terminate correctly
*/
spin_lock(&configfs_dirent_lock);
/* This will make configfs_detach_prep() fail */
sd->s_type |= CONFIGFS_USET_IN_MKDIR;
spin_unlock(&configfs_dirent_lock);
if (group)
ret = configfs_attach_group(parent_item, item, dentry, frag);
else
ret = configfs_attach_item(parent_item, item, dentry, frag);
spin_lock(&configfs_dirent_lock);
sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
if (!ret)
configfs_dir_set_ready(dentry->d_fsdata);
spin_unlock(&configfs_dirent_lock);
out_unlink:
if (ret) {
/* Tear down everything we built up */
mutex_lock(&subsys->su_mutex);
client_disconnect_notify(parent_item, item);
if (group)
unlink_group(group);
else
unlink_obj(item);
client_drop_item(parent_item, item);
mutex_unlock(&subsys->su_mutex);
if (module_got)
module_put(new_item_owner);
}
out_subsys_put:
if (ret)
module_put(subsys_owner);
out_put:
/*
* link_obj ( ) / link_group ( ) took a reference from child - > parent ,
* so the parent is safely pinned . We can drop our working
* reference .
*/
config_item_put(parent_item);
put_fragment(frag);
out:
return ERR_PTR(ret);
}
static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct config_item *parent_item;
struct config_item *item;
struct configfs_subsystem *subsys;
struct configfs_dirent *sd;
struct configfs_fragment *frag;
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
int ret;
sd = dentry->d_fsdata;
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);
if (!parent_item->ci_type) {
config_item_put(parent_item);
return -EINVAL;
}
/* configfs_mkdir() shouldn't have allowed this */
BUG_ON(!subsys->su_group.cg_item.ci_type);
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
/*
* Ensure that no racing symlink ( ) will make detach_prep ( ) fail while
* the new link is temporarily attached
*/
do {
struct dentry *wait;
mutex_lock(&configfs_symlink_mutex);
spin_lock(&configfs_dirent_lock);
/*
* Here ' s where we check for dependents . We ' re protected by
* configfs_dirent_lock .
* If no dependent , atomically tag the item as dropping .
*/
ret = sd->s_dependent_count ? -EBUSY : 0 ;
if (!ret) {
ret = configfs_detach_prep(dentry, &wait);
if (ret)
configfs_detach_rollback(dentry);
}
spin_unlock(&configfs_dirent_lock);
mutex_unlock(&configfs_symlink_mutex);
if (ret) {
if (ret != -EAGAIN) {
config_item_put(parent_item);
return ret;
}
/* Wait until the racing operation terminates */
inode_lock(d_inode(wait));
inode_unlock(d_inode(wait));
dput(wait);
}
} while (ret == -EAGAIN);
frag = sd->s_frag;
if (down_write_killable(&frag->frag_sem)) {
spin_lock(&configfs_dirent_lock);
configfs_detach_rollback(dentry);
spin_unlock(&configfs_dirent_lock);
config_item_put(parent_item);
return -EINTR;
}
frag->frag_dead = true ;
up_write(&frag->frag_sem);
/* Get a working ref for the duration of this function */
item = configfs_get_config_item(dentry);
/* Drop reference from above, item already holds one. */
config_item_put(parent_item);
if (item->ci_type)
dead_item_owner = item->ci_type->ct_owner;
if (sd->s_type & CONFIGFS_USET_DIR) {
configfs_detach_group(item);
mutex_lock(&subsys->su_mutex);
client_disconnect_notify(parent_item, item);
unlink_group(to_config_group(item));
} else {
configfs_detach_item(item);
mutex_lock(&subsys->su_mutex);
client_disconnect_notify(parent_item, item);
unlink_obj(item);
}
client_drop_item(parent_item, item);
mutex_unlock(&subsys->su_mutex);
/* Drop our reference from above */
config_item_put(item);
module_put(dead_item_owner);
module_put(subsys_owner);
return 0 ;
}
const struct inode_operations configfs_dir_inode_operations = {
.mkdir = configfs_mkdir,
.rmdir = configfs_rmdir,
.symlink = configfs_symlink,
.unlink = configfs_unlink,
.lookup = configfs_lookup,
.setattr = configfs_setattr,
};
const struct inode_operations configfs_root_inode_operations = {
.lookup = configfs_lookup,
.setattr = configfs_setattr,
};
static int configfs_dir_open(struct inode *inode, struct file *file)
{
struct dentry * dentry = file->f_path.dentry;
struct configfs_dirent * parent_sd = dentry->d_fsdata;
int err;
inode_lock(d_inode(dentry));
/*
* Fake invisibility if dir belongs to a group / default groups hierarchy
* being attached
*/
err = -ENOENT;
if (configfs_dirent_is_ready(parent_sd)) {
file->private_data = configfs_new_dirent(parent_sd, NULL, 0 , NULL);
if (IS_ERR(file->private_data))
err = PTR_ERR(file->private_data);
else
err = 0 ;
}
inode_unlock(d_inode(dentry));
return err;
}
static int configfs_dir_close(struct inode *inode, struct file *file)
{
struct dentry * dentry = file->f_path.dentry;
struct configfs_dirent * cursor = file->private_data;
inode_lock(d_inode(dentry));
spin_lock(&configfs_dirent_lock);
list_del_init(&cursor->s_sibling);
spin_unlock(&configfs_dirent_lock);
inode_unlock(d_inode(dentry));
release_configfs_dirent(cursor);
return 0 ;
}
static int configfs_readdir(struct file *file, struct dir_context *ctx)
{
struct dentry *dentry = file->f_path.dentry;
struct super_block *sb = dentry->d_sb;
struct configfs_dirent * parent_sd = dentry->d_fsdata;
struct configfs_dirent *cursor = file->private_data;
struct list_head *p, *q = &cursor->s_sibling;
ino_t ino = 0 ;
if (!dir_emit_dots(file, ctx))
return 0 ;
spin_lock(&configfs_dirent_lock);
if (ctx->pos == 2 )
list_move(q, &parent_sd->s_children);
for (p = q->next; p != &parent_sd->s_children; p = p->next) {
struct configfs_dirent *next;
const char *name;
int len;
struct inode *inode = NULL;
next = list_entry(p, struct configfs_dirent, s_sibling);
if (!next->s_element)
continue ;
/*
* We ' ll have a dentry and an inode for
* PINNED items and for open attribute
* files . We lock here to prevent a race
* with configfs_d_iput ( ) clearing
* s_dentry before calling iput ( ) .
*
* Why do we go to the trouble ? If
* someone has an attribute file open ,
* the inode number should match until
* they close it . Beyond that , we don ' t
* care .
*/
dentry = next->s_dentry;
if (dentry)
inode = d_inode(dentry);
if (inode)
ino = inode->i_ino;
spin_unlock(&configfs_dirent_lock);
if (!inode)
ino = iunique(sb, 2 );
name = configfs_get_name(next);
len = strlen(name);
if (!dir_emit(ctx, name, len, ino,
fs_umode_to_dtype(next->s_mode)))
return 0 ;
spin_lock(&configfs_dirent_lock);
list_move(q, p);
p = q;
ctx->pos++;
}
spin_unlock(&configfs_dirent_lock);
return 0 ;
}
static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)
{
struct dentry * dentry = file->f_path.dentry;
switch (whence) {
case 1 :
offset += file->f_pos;
fallthrough;
case 0 :
if (offset >= 0 )
break ;
fallthrough;
default :
return -EINVAL;
}
if (offset != file->f_pos) {
file->f_pos = offset;
if (file->f_pos >= 2 ) {
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_dirent *cursor = file->private_data;
struct list_head *p;
loff_t n = file->f_pos - 2 ;
spin_lock(&configfs_dirent_lock);
list_del(&cursor->s_sibling);
p = sd->s_children.next;
while (n && p != &sd->s_children) {
struct configfs_dirent *next;
next = list_entry(p, struct configfs_dirent,
s_sibling);
if (next->s_element)
n--;
p = p->next;
}
list_add_tail(&cursor->s_sibling, p);
spin_unlock(&configfs_dirent_lock);
}
}
return offset;
}
const struct file_operations configfs_dir_operations = {
.open = configfs_dir_open,
.release = configfs_dir_close,
.llseek = configfs_dir_lseek,
.read = generic_read_dir,
.iterate_shared = configfs_readdir,
};
/**
* configfs_register_group - creates a parent - child relation between two groups
* @ parent_group : parent group
* @ group : child group
*
* link groups , creates dentry for the child and attaches it to the
* parent dentry .
*
* Return : 0 on success , negative errno code on error
*/
int configfs_register_group(struct config_group *parent_group,
struct config_group *group)
{
struct configfs_subsystem *subsys = parent_group->cg_subsys;
struct dentry *parent;
struct configfs_fragment *frag;
int ret;
frag = new_fragment();
if (!frag)
return -ENOMEM;
mutex_lock(&subsys->su_mutex);
link_group(parent_group, group);
mutex_unlock(&subsys->su_mutex);
parent = parent_group->cg_item.ci_dentry;
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
ret = create_default_group(parent_group, group, frag);
if (ret)
goto err_out;
spin_lock(&configfs_dirent_lock);
configfs_dir_set_ready(group->cg_item.ci_dentry->d_fsdata);
spin_unlock(&configfs_dirent_lock);
inode_unlock(d_inode(parent));
put_fragment(frag);
return 0 ;
err_out:
inode_unlock(d_inode(parent));
mutex_lock(&subsys->su_mutex);
unlink_group(group);
mutex_unlock(&subsys->su_mutex);
put_fragment(frag);
return ret;
}
EXPORT_SYMBOL(configfs_register_group);
/**
* configfs_unregister_group ( ) - unregisters a child group from its parent
* @ group : parent group to be unregistered
*
* Undoes 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 *parent = group->cg_item.ci_parent->ci_dentry;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
down_write(&frag->frag_sem);
frag->frag_dead = true ;
up_write(&frag->frag_sem);
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
spin_lock(&configfs_dirent_lock);
configfs_detach_prep(dentry, NULL);
spin_unlock(&configfs_dirent_lock);
configfs_detach_group(&group->cg_item);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
d_drop(dentry);
fsnotify_rmdir(d_inode(parent), dentry);
inode_unlock(d_inode(parent));
dput(dentry);
mutex_lock(&subsys->su_mutex);
unlink_group(group);
mutex_unlock(&subsys->su_mutex);
}
EXPORT_SYMBOL(configfs_unregister_group);
/**
* configfs_register_default_group ( ) - allocates and registers a child group
* @ parent_group : parent group
* @ name : child group name
* @ item_type : child item type description
*
* boilerplate to allocate and register a child group with its parent . We need
* kzalloc ' ed memory because child ' s default_group is initially empty .
*
* Return : allocated config group or ERR_PTR ( ) on error
*/
struct config_group *
configfs_register_default_group(struct config_group *parent_group,
const char *name,
const struct config_item_type *item_type)
{
int ret;
struct config_group *group;
group = kzalloc(sizeof (*group), GFP_KERNEL);
if (!group)
return ERR_PTR(-ENOMEM);
config_group_init_type_name(group, name, item_type);
ret = configfs_register_group(parent_group, group);
if (ret) {
kfree(group);
return ERR_PTR(ret);
}
return group;
}
EXPORT_SYMBOL(configfs_register_default_group);
/**
* configfs_unregister_default_group ( ) - unregisters and frees a child group
* @ group : the group to act on
*/
void configfs_unregister_default_group(struct config_group *group)
{
configfs_unregister_group(group);
kfree(group);
}
EXPORT_SYMBOL(configfs_unregister_default_group);
int configfs_register_subsystem(struct configfs_subsystem *subsys)
{
int err;
struct config_group *group = &subsys->su_group;
struct dentry *dentry;
struct dentry *root;
struct configfs_dirent *sd;
struct configfs_fragment *frag;
frag = new_fragment();
if (!frag)
return -ENOMEM;
root = configfs_pin_fs();
if (IS_ERR(root)) {
put_fragment(frag);
return PTR_ERR(root);
}
if (!group->cg_item.ci_name)
group->cg_item.ci_name = group->cg_item.ci_namebuf;
sd = root->d_fsdata;
mutex_lock(&configfs_subsystem_mutex);
link_group(to_config_group(sd->s_element), group);
mutex_unlock(&configfs_subsystem_mutex);
inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
err = -ENOMEM;
dentry = d_alloc_name(root, group->cg_item.ci_name);
if (dentry) {
d_add(dentry, NULL);
err = configfs_dirent_exists(dentry);
if (!err)
err = configfs_attach_group(sd->s_element,
&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);
}
}
inode_unlock(d_inode(root));
if (err) {
mutex_lock(&configfs_subsystem_mutex);
unlink_group(group);
mutex_unlock(&configfs_subsystem_mutex);
configfs_release_fs();
}
put_fragment(frag);
return err;
}
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 *root = dentry->d_sb->s_root;
struct configfs_dirent *sd = dentry->d_fsdata;
struct configfs_fragment *frag = sd->s_frag;
if (dentry->d_parent != root) {
pr_err("Tried to unregister non-subsystem!\n" );
return ;
}
down_write(&frag->frag_sem);
frag->frag_dead = true ;
up_write(&frag->frag_sem);
inode_lock_nested(d_inode(root),
I_MUTEX_PARENT);
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)) {
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);
d_inode(dentry)->i_flags |= S_DEAD;
dont_mount(dentry);
inode_unlock(d_inode(dentry));
d_drop(dentry);
fsnotify_rmdir(d_inode(root), dentry);
inode_unlock(d_inode(root));
dput(dentry);
mutex_lock(&configfs_subsystem_mutex);
unlink_group(group);
mutex_unlock(&configfs_subsystem_mutex);
configfs_release_fs();
}
EXPORT_SYMBOL(configfs_register_subsystem);
EXPORT_SYMBOL(configfs_unregister_subsystem);
Messung V0.5 in Prozent C=96 H=92 G=93
¤ Dauer der Verarbeitung: 0.65 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
letze Version des Elbe Quellennavigators
Jenseits des Üblichen ....
Besucher
Statistik
#Sources=1126864
#Domains=1897691