162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * item.c - library routines for handling generic config items 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on kobject: 662306a36Sopenharmony_ci * kobject is Copyright (c) 2002-2003 Patrick Mochel 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * configfs Copyright (C) 2005 Oracle. All rights reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Please see the file Documentation/filesystems/configfs.rst for 1162306a36Sopenharmony_ci * critical information about using the config_item interface. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/stat.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/configfs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic inline struct config_item *to_item(struct list_head *entry) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci return container_of(entry, struct config_item, ci_entry); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Evil kernel */ 2862306a36Sopenharmony_cistatic void config_item_release(struct kref *kref); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * config_item_init - initialize item. 3262306a36Sopenharmony_ci * @item: item in question. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic void config_item_init(struct config_item *item) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci kref_init(&item->ci_kref); 3762306a36Sopenharmony_ci INIT_LIST_HEAD(&item->ci_entry); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * config_item_set_name - Set the name of an item 4262306a36Sopenharmony_ci * @item: item. 4362306a36Sopenharmony_ci * @fmt: The vsnprintf()'s format string. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * If strlen(name) >= CONFIGFS_ITEM_NAME_LEN, then use a 4662306a36Sopenharmony_ci * dynamically allocated string that @item->ci_name points to. 4762306a36Sopenharmony_ci * Otherwise, use the static @item->ci_namebuf array. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ciint config_item_set_name(struct config_item *item, const char *fmt, ...) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci int limit = CONFIGFS_ITEM_NAME_LEN; 5262306a36Sopenharmony_ci int need; 5362306a36Sopenharmony_ci va_list args; 5462306a36Sopenharmony_ci char *name; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * First, try the static array 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci va_start(args, fmt); 6062306a36Sopenharmony_ci need = vsnprintf(item->ci_namebuf, limit, fmt, args); 6162306a36Sopenharmony_ci va_end(args); 6262306a36Sopenharmony_ci if (need < limit) 6362306a36Sopenharmony_ci name = item->ci_namebuf; 6462306a36Sopenharmony_ci else { 6562306a36Sopenharmony_ci va_start(args, fmt); 6662306a36Sopenharmony_ci name = kvasprintf(GFP_KERNEL, fmt, args); 6762306a36Sopenharmony_ci va_end(args); 6862306a36Sopenharmony_ci if (!name) 6962306a36Sopenharmony_ci return -EFAULT; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Free the old name, if necessary. */ 7362306a36Sopenharmony_ci if (item->ci_name && item->ci_name != item->ci_namebuf) 7462306a36Sopenharmony_ci kfree(item->ci_name); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Now, set the new name */ 7762306a36Sopenharmony_ci item->ci_name = name; 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciEXPORT_SYMBOL(config_item_set_name); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid config_item_init_type_name(struct config_item *item, 8362306a36Sopenharmony_ci const char *name, 8462306a36Sopenharmony_ci const struct config_item_type *type) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci config_item_set_name(item, "%s", name); 8762306a36Sopenharmony_ci item->ci_type = type; 8862306a36Sopenharmony_ci config_item_init(item); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL(config_item_init_type_name); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_civoid config_group_init_type_name(struct config_group *group, const char *name, 9362306a36Sopenharmony_ci const struct config_item_type *type) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci config_item_set_name(&group->cg_item, "%s", name); 9662306a36Sopenharmony_ci group->cg_item.ci_type = type; 9762306a36Sopenharmony_ci config_group_init(group); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ciEXPORT_SYMBOL(config_group_init_type_name); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct config_item *config_item_get(struct config_item *item) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (item) 10462306a36Sopenharmony_ci kref_get(&item->ci_kref); 10562306a36Sopenharmony_ci return item; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ciEXPORT_SYMBOL(config_item_get); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistruct config_item *config_item_get_unless_zero(struct config_item *item) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (item && kref_get_unless_zero(&item->ci_kref)) 11262306a36Sopenharmony_ci return item; 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL(config_item_get_unless_zero); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void config_item_cleanup(struct config_item *item) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci const struct config_item_type *t = item->ci_type; 12062306a36Sopenharmony_ci struct config_group *s = item->ci_group; 12162306a36Sopenharmony_ci struct config_item *parent = item->ci_parent; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci pr_debug("config_item %s: cleaning up\n", config_item_name(item)); 12462306a36Sopenharmony_ci if (item->ci_name != item->ci_namebuf) 12562306a36Sopenharmony_ci kfree(item->ci_name); 12662306a36Sopenharmony_ci item->ci_name = NULL; 12762306a36Sopenharmony_ci if (t && t->ct_item_ops && t->ct_item_ops->release) 12862306a36Sopenharmony_ci t->ct_item_ops->release(item); 12962306a36Sopenharmony_ci if (s) 13062306a36Sopenharmony_ci config_group_put(s); 13162306a36Sopenharmony_ci if (parent) 13262306a36Sopenharmony_ci config_item_put(parent); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void config_item_release(struct kref *kref) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci config_item_cleanup(container_of(kref, struct config_item, ci_kref)); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * config_item_put - decrement refcount for item. 14262306a36Sopenharmony_ci * @item: item. 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Decrement the refcount, and if 0, call config_item_cleanup(). 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_civoid config_item_put(struct config_item *item) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (item) 14962306a36Sopenharmony_ci kref_put(&item->ci_kref, config_item_release); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL(config_item_put); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * config_group_init - initialize a group for use 15562306a36Sopenharmony_ci * @group: config_group 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_civoid config_group_init(struct config_group *group) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci config_item_init(&group->cg_item); 16062306a36Sopenharmony_ci INIT_LIST_HEAD(&group->cg_children); 16162306a36Sopenharmony_ci INIT_LIST_HEAD(&group->default_groups); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ciEXPORT_SYMBOL(config_group_init); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * config_group_find_item - search for item in group. 16762306a36Sopenharmony_ci * @group: group we're looking in. 16862306a36Sopenharmony_ci * @name: item's name. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * Iterate over @group->cg_list, looking for a matching config_item. 17162306a36Sopenharmony_ci * If matching item is found take a reference and return the item. 17262306a36Sopenharmony_ci * Caller must have locked group via @group->cg_subsys->su_mtx. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistruct config_item *config_group_find_item(struct config_group *group, 17562306a36Sopenharmony_ci const char *name) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct list_head *entry; 17862306a36Sopenharmony_ci struct config_item *ret = NULL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci list_for_each(entry, &group->cg_children) { 18162306a36Sopenharmony_ci struct config_item *item = to_item(entry); 18262306a36Sopenharmony_ci if (config_item_name(item) && 18362306a36Sopenharmony_ci !strcmp(config_item_name(item), name)) { 18462306a36Sopenharmony_ci ret = config_item_get(item); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ciEXPORT_SYMBOL(config_group_find_item); 191