162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/sharefs/config.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/configfs.h>
962306a36Sopenharmony_ci#include <linux/ctype.h>
1062306a36Sopenharmony_ci#include <linux/dcache.h>
1162306a36Sopenharmony_ci#include <linux/hashtable.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include "sharefs.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic struct kmem_cache *sharefs_bid_entry_cachep;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct sharefs_bid_entry {
1862306a36Sopenharmony_ci	struct hlist_node node;
1962306a36Sopenharmony_ci	struct qstr str;
2062306a36Sopenharmony_ci	int id;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct sharefs_config_bitem {
2462306a36Sopenharmony_ci	struct config_item item;
2562306a36Sopenharmony_ci	struct qstr str;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic unsigned int make_hash(const char *name, unsigned int len)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned long hash;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	hash = init_name_hash(0);
3362306a36Sopenharmony_ci	while (len--)
3462306a36Sopenharmony_ci		hash = partial_name_hash(tolower(*name++), hash);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return end_name_hash(hash);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic struct qstr make_qstr(const char *name)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct qstr str;
4262306a36Sopenharmony_ci	str.name = name;
4362306a36Sopenharmony_ci	str.len = strlen(name);
4462306a36Sopenharmony_ci	str.hash = make_hash(str.name, str.len);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return str;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct sharefs_bid_entry *alloc_bid_entry(const char *name, int id)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct sharefs_bid_entry *bid_entry;
5262306a36Sopenharmony_ci	char *bid_entry_name;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	bid_entry = kmem_cache_alloc(sharefs_bid_entry_cachep, GFP_KERNEL);
5562306a36Sopenharmony_ci	if (!bid_entry) {
5662306a36Sopenharmony_ci		bid_entry = ERR_PTR(-ENOMEM);
5762306a36Sopenharmony_ci		goto out;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	bid_entry_name = kstrdup(name, GFP_KERNEL);
6162306a36Sopenharmony_ci	if (!bid_entry_name) {
6262306a36Sopenharmony_ci		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
6362306a36Sopenharmony_ci		bid_entry = ERR_PTR(-ENOMEM);
6462306a36Sopenharmony_ci		goto out;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	INIT_HLIST_NODE(&bid_entry->node);
6862306a36Sopenharmony_ci	bid_entry->str = make_qstr(bid_entry_name);
6962306a36Sopenharmony_ci	bid_entry->id = id;
7062306a36Sopenharmony_ciout:
7162306a36Sopenharmony_ci	return bid_entry;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void free_bid_entry(struct sharefs_bid_entry *bid_entry)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	if (bid_entry == NULL)
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	kfree(bid_entry->str.name);
8062306a36Sopenharmony_ci	kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct sharefs_config_bitem *alloc_bitem(const char *name)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct sharefs_config_bitem *bitem;
8662306a36Sopenharmony_ci	char *bitem_name;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	bitem = kzalloc(sizeof(*bitem), GFP_KERNEL);
8962306a36Sopenharmony_ci	if (!bitem) {
9062306a36Sopenharmony_ci		bitem = ERR_PTR(-ENOMEM);
9162306a36Sopenharmony_ci		goto out;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	bitem_name = kstrdup(name, GFP_KERNEL);
9562306a36Sopenharmony_ci	if (!bitem_name) {
9662306a36Sopenharmony_ci		kfree(bitem);
9762306a36Sopenharmony_ci		bitem = ERR_PTR(-ENOMEM);
9862306a36Sopenharmony_ci		goto out;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	bitem->str = make_qstr(bitem_name);
10262306a36Sopenharmony_ciout:
10362306a36Sopenharmony_ci	return bitem;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void free_bitem(struct sharefs_config_bitem *bitem)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if (bitem == NULL)
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	kfree(bitem->str.name);
11262306a36Sopenharmony_ci	kfree(bitem);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define SHAREFS_BUNDLE_ATTRIBUTE(_attr_)					\
11662306a36Sopenharmony_ci									\
11762306a36Sopenharmony_cistatic DEFINE_HASHTABLE(sharefs_##_attr_##_hash_table, 4);		\
11862306a36Sopenharmony_ci									\
11962306a36Sopenharmony_cistatic DEFINE_MUTEX(sharefs_##_attr_##_hash_mutex);			\
12062306a36Sopenharmony_ci									\
12162306a36Sopenharmony_cistatic int query_##_attr_##_hash_entry(struct qstr *str)		\
12262306a36Sopenharmony_ci{									\
12362306a36Sopenharmony_ci	int id = 0;							\
12462306a36Sopenharmony_ci	struct sharefs_bid_entry *bid_entry;				\
12562306a36Sopenharmony_ci	struct hlist_node *hash_node;					\
12662306a36Sopenharmony_ci									\
12762306a36Sopenharmony_ci	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
12862306a36Sopenharmony_ci	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
12962306a36Sopenharmony_ci		bid_entry, hash_node, node, str->hash) {		\
13062306a36Sopenharmony_ci		if (qstr_case_eq(str, &bid_entry->str)) {		\
13162306a36Sopenharmony_ci			id = bid_entry->id;				\
13262306a36Sopenharmony_ci			break;						\
13362306a36Sopenharmony_ci		}							\
13462306a36Sopenharmony_ci	}								\
13562306a36Sopenharmony_ci	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
13662306a36Sopenharmony_ci									\
13762306a36Sopenharmony_ci	return id;							\
13862306a36Sopenharmony_ci}									\
13962306a36Sopenharmony_ci									\
14062306a36Sopenharmony_cistatic int insert_##_attr_##_hash_entry(struct qstr *str, int id)	\
14162306a36Sopenharmony_ci{									\
14262306a36Sopenharmony_ci	int err = 0;							\
14362306a36Sopenharmony_ci	struct sharefs_bid_entry *bid_entry;				\
14462306a36Sopenharmony_ci	struct hlist_node *hash_node;					\
14562306a36Sopenharmony_ci									\
14662306a36Sopenharmony_ci	sharefs_info("insert name = %s", str->name);			\
14762306a36Sopenharmony_ci									\
14862306a36Sopenharmony_ci	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
14962306a36Sopenharmony_ci	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
15062306a36Sopenharmony_ci		bid_entry, hash_node, node, str->hash) {		\
15162306a36Sopenharmony_ci		if (qstr_case_eq(str, &bid_entry->str)) {		\
15262306a36Sopenharmony_ci			bid_entry->id = id;				\
15362306a36Sopenharmony_ci			mutex_unlock(&sharefs_##_attr_##_hash_mutex);	\
15462306a36Sopenharmony_ci			goto out;					\
15562306a36Sopenharmony_ci		}							\
15662306a36Sopenharmony_ci	}								\
15762306a36Sopenharmony_ci	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
15862306a36Sopenharmony_ci									\
15962306a36Sopenharmony_ci	bid_entry = alloc_bid_entry(str->name, id);			\
16062306a36Sopenharmony_ci	if (IS_ERR(bid_entry)) {					\
16162306a36Sopenharmony_ci		err = PTR_ERR(bid_entry);				\
16262306a36Sopenharmony_ci		goto out;						\
16362306a36Sopenharmony_ci	} 								\
16462306a36Sopenharmony_ci									\
16562306a36Sopenharmony_ci	hash_add_rcu(sharefs_##_attr_##_hash_table, &bid_entry->node,	\
16662306a36Sopenharmony_ci		 bid_entry->str.hash);					\
16762306a36Sopenharmony_ciout:									\
16862306a36Sopenharmony_ci	return err;							\
16962306a36Sopenharmony_ci}									\
17062306a36Sopenharmony_ci									\
17162306a36Sopenharmony_cistatic void remove_##_attr_##_hash_entry(struct qstr *str)		\
17262306a36Sopenharmony_ci{									\
17362306a36Sopenharmony_ci	struct sharefs_bid_entry *bid_entry;				\
17462306a36Sopenharmony_ci	struct hlist_node *hash_node;					\
17562306a36Sopenharmony_ci									\
17662306a36Sopenharmony_ci	sharefs_info("remove name = %s", str->name);			\
17762306a36Sopenharmony_ci									\
17862306a36Sopenharmony_ci	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
17962306a36Sopenharmony_ci	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
18062306a36Sopenharmony_ci		bid_entry, hash_node, node, str->hash) {		\
18162306a36Sopenharmony_ci		if (qstr_case_eq(str, &bid_entry->str)) {		\
18262306a36Sopenharmony_ci			hash_del_rcu(&bid_entry->node);			\
18362306a36Sopenharmony_ci			free_bid_entry(bid_entry);			\
18462306a36Sopenharmony_ci			break;						\
18562306a36Sopenharmony_ci		}							\
18662306a36Sopenharmony_ci	}								\
18762306a36Sopenharmony_ci	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
18862306a36Sopenharmony_ci}									\
18962306a36Sopenharmony_ci									\
19062306a36Sopenharmony_cistatic void clear_##_attr_##_hash_entry(void)				\
19162306a36Sopenharmony_ci{									\
19262306a36Sopenharmony_ci	int index;							\
19362306a36Sopenharmony_ci	struct sharefs_bid_entry *bid_entry;				\
19462306a36Sopenharmony_ci	struct hlist_node *hash_node;					\
19562306a36Sopenharmony_ci									\
19662306a36Sopenharmony_ci	sharefs_info("clear bid entry");					\
19762306a36Sopenharmony_ci									\
19862306a36Sopenharmony_ci	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
19962306a36Sopenharmony_ci	hash_for_each_safe(sharefs_##_attr_##_hash_table, index,		\
20062306a36Sopenharmony_ci		hash_node, bid_entry, node) {				\
20162306a36Sopenharmony_ci		hash_del_rcu(&bid_entry->node);				\
20262306a36Sopenharmony_ci		kfree(bid_entry->str.name);				\
20362306a36Sopenharmony_ci		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);	\
20462306a36Sopenharmony_ci	}								\
20562306a36Sopenharmony_ci	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
20662306a36Sopenharmony_ci}									\
20762306a36Sopenharmony_ci									\
20862306a36Sopenharmony_cistatic int sharefs_##_attr_##_get(const char *bname)			\
20962306a36Sopenharmony_ci{									\
21062306a36Sopenharmony_ci	struct qstr str;						\
21162306a36Sopenharmony_ci									\
21262306a36Sopenharmony_ci	str = make_qstr(bname);						\
21362306a36Sopenharmony_ci	return query_##_attr_##_hash_entry(&str);			\
21462306a36Sopenharmony_ci}									\
21562306a36Sopenharmony_ci									\
21662306a36Sopenharmony_cistatic ssize_t sharefs_##_attr_##_show(struct config_item *item,		\
21762306a36Sopenharmony_ci	char *page)							\
21862306a36Sopenharmony_ci{									\
21962306a36Sopenharmony_ci	int id;								\
22062306a36Sopenharmony_ci	struct sharefs_config_bitem *bitem;				\
22162306a36Sopenharmony_ci									\
22262306a36Sopenharmony_ci	sharefs_info("show bundle id");					\
22362306a36Sopenharmony_ci									\
22462306a36Sopenharmony_ci	bitem = container_of(item, struct sharefs_config_bitem, item);	\
22562306a36Sopenharmony_ci	id = query_##_attr_##_hash_entry(&bitem->str);			\
22662306a36Sopenharmony_ci									\
22762306a36Sopenharmony_ci	return scnprintf(page, PAGE_SIZE, "%u\n", id);			\
22862306a36Sopenharmony_ci}									\
22962306a36Sopenharmony_ci									\
23062306a36Sopenharmony_cistatic ssize_t sharefs_##_attr_##_store(struct config_item *item,	\
23162306a36Sopenharmony_ci	const char *page, size_t count)					\
23262306a36Sopenharmony_ci{									\
23362306a36Sopenharmony_ci	int id;								\
23462306a36Sopenharmony_ci	int err;							\
23562306a36Sopenharmony_ci	size_t size;							\
23662306a36Sopenharmony_ci	struct sharefs_config_bitem *bitem;				\
23762306a36Sopenharmony_ci									\
23862306a36Sopenharmony_ci	sharefs_info("store bundle id");					\
23962306a36Sopenharmony_ci									\
24062306a36Sopenharmony_ci	bitem = container_of(item, struct sharefs_config_bitem, item);	\
24162306a36Sopenharmony_ci									\
24262306a36Sopenharmony_ci	if (kstrtouint(page, 10, &id)) {				\
24362306a36Sopenharmony_ci		size = -EINVAL;						\
24462306a36Sopenharmony_ci		goto out; 						\
24562306a36Sopenharmony_ci	}								\
24662306a36Sopenharmony_ci									\
24762306a36Sopenharmony_ci	err = insert_##_attr_##_hash_entry(&bitem->str, id);		\
24862306a36Sopenharmony_ci	if (err) {							\
24962306a36Sopenharmony_ci		size = err;						\
25062306a36Sopenharmony_ci		goto out;						\
25162306a36Sopenharmony_ci	}								\
25262306a36Sopenharmony_ci									\
25362306a36Sopenharmony_ci	size = count;							\
25462306a36Sopenharmony_ciout:									\
25562306a36Sopenharmony_ci	return size;							\
25662306a36Sopenharmony_ci}									\
25762306a36Sopenharmony_ci									\
25862306a36Sopenharmony_cistatic struct configfs_attribute sharefs_##_attr_##_attr = {		\
25962306a36Sopenharmony_ci	.ca_name	= __stringify(_attr_),				\
26062306a36Sopenharmony_ci	.ca_mode	= S_IRUGO | S_IWUGO,				\
26162306a36Sopenharmony_ci	.ca_owner	= THIS_MODULE,					\
26262306a36Sopenharmony_ci	.show		= sharefs_##_attr_##_show,			\
26362306a36Sopenharmony_ci	.store		= sharefs_##_attr_##_store,			\
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ciSHAREFS_BUNDLE_ATTRIBUTE(appid)
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic struct configfs_attribute *sharefs_battrs[] = {
26962306a36Sopenharmony_ci	&sharefs_appid_attr,
27062306a36Sopenharmony_ci	NULL,
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void sharefs_config_bitem_release(struct config_item *item)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct sharefs_config_bitem *bitem;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	sharefs_info("release bundle item");
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	bitem = container_of(item, struct sharefs_config_bitem, item);
28062306a36Sopenharmony_ci	remove_appid_hash_entry(&bitem->str);
28162306a36Sopenharmony_ci	remove_appid_hash_entry(&bitem->str);
28262306a36Sopenharmony_ci	free_bitem(bitem);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic struct configfs_item_operations sharefs_config_bitem_ops = {
28662306a36Sopenharmony_ci	.release = sharefs_config_bitem_release,
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic struct config_item_type sharefs_config_bitem_type = {
29062306a36Sopenharmony_ci	.ct_item_ops	= &sharefs_config_bitem_ops,
29162306a36Sopenharmony_ci	.ct_attrs	= sharefs_battrs,
29262306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic struct config_item *sharefs_make_bitem(struct config_group *group,
29662306a36Sopenharmony_ci					      const char *name)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct config_item *item;
29962306a36Sopenharmony_ci	struct sharefs_config_bitem *bitem;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	bitem = alloc_bitem(name);
30262306a36Sopenharmony_ci	if (IS_ERR(bitem)) {
30362306a36Sopenharmony_ci		item = ERR_PTR(-ENOMEM);
30462306a36Sopenharmony_ci		goto out;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	config_item_init_type_name(&bitem->item, name,
30862306a36Sopenharmony_ci		&sharefs_config_bitem_type);
30962306a36Sopenharmony_ci	item = &bitem->item;
31062306a36Sopenharmony_ciout:
31162306a36Sopenharmony_ci	return item;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic struct configfs_group_operations sharefs_group_ops = {
31562306a36Sopenharmony_ci	.make_item = sharefs_make_bitem,
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic struct config_item_type sharefs_group_type = {
31962306a36Sopenharmony_ci	.ct_group_ops     = &sharefs_group_ops,
32062306a36Sopenharmony_ci	.ct_owner         = THIS_MODULE,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic struct configfs_subsystem sharefs_subsystem = {
32462306a36Sopenharmony_ci	.su_group = {
32562306a36Sopenharmony_ci		.cg_item = {
32662306a36Sopenharmony_ci			.ci_namebuf = "sharefs",
32762306a36Sopenharmony_ci			.ci_type = &sharefs_group_type,
32862306a36Sopenharmony_ci		},
32962306a36Sopenharmony_ci	},
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciint get_bid_config(const char *bname)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return sharefs_appid_get(bname);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ciint __init sharefs_init_configfs(void)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	int err;
34062306a36Sopenharmony_ci	struct configfs_subsystem *subsys;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	sharefs_info("init configfs");
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	sharefs_bid_entry_cachep = kmem_cache_create("sharefs_bid_entry_cachep",
34562306a36Sopenharmony_ci		sizeof(struct sharefs_bid_entry), 0, 0, NULL);
34662306a36Sopenharmony_ci	if (!sharefs_bid_entry_cachep) {
34762306a36Sopenharmony_ci		sharefs_err("failed to create bid entry cachep");
34862306a36Sopenharmony_ci		err = -ENOMEM;
34962306a36Sopenharmony_ci		goto out;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	subsys = &sharefs_subsystem;
35362306a36Sopenharmony_ci	config_group_init(&subsys->su_group);
35462306a36Sopenharmony_ci	mutex_init(&subsys->su_mutex);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	err = configfs_register_subsystem(subsys);
35762306a36Sopenharmony_ci	if (err)
35862306a36Sopenharmony_ci		sharefs_err("failed to register subsystem");
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciout:
36162306a36Sopenharmony_ci	return err;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_civoid sharefs_exit_configfs(void)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	sharefs_info("sharefs exit configfs");
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	configfs_unregister_subsystem(&sharefs_subsystem);
36962306a36Sopenharmony_ci	clear_appid_hash_entry();
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	kmem_cache_destroy(sharefs_bid_entry_cachep);
37262306a36Sopenharmony_ci}