1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * fs/sharefs/config.c
4  *
5  * Copyright (c) 2023 Huawei Device Co., Ltd.
6  */
7 
8 #include <linux/configfs.h>
9 #include <linux/ctype.h>
10 #include <linux/dcache.h>
11 #include <linux/hashtable.h>
12 #include <linux/slab.h>
13 #include "sharefs.h"
14 
15 static struct kmem_cache *sharefs_bid_entry_cachep;
16 
17 struct sharefs_bid_entry {
18 	struct hlist_node node;
19 	struct qstr str;
20 	int id;
21 };
22 
23 struct sharefs_config_bitem {
24 	struct config_item item;
25 	struct qstr str;
26 };
27 
make_hash(const char *name, unsigned int len)28 static unsigned int make_hash(const char *name, unsigned int len)
29 {
30 	unsigned long hash;
31 
32 	hash = init_name_hash(0);
33 	while (len--)
34 		hash = partial_name_hash(tolower(*name++), hash);
35 
36 	return end_name_hash(hash);
37 }
38 
make_qstr(const char *name)39 static struct qstr make_qstr(const char *name)
40 {
41 	struct qstr str;
42 	str.name = name;
43 	str.len = strlen(name);
44 	str.hash = make_hash(str.name, str.len);
45 
46 	return str;
47 }
48 
alloc_bid_entry(const char *name, int id)49 static struct sharefs_bid_entry *alloc_bid_entry(const char *name, int id)
50 {
51 	struct sharefs_bid_entry *bid_entry;
52 	char *bid_entry_name;
53 
54 	bid_entry = kmem_cache_alloc(sharefs_bid_entry_cachep, GFP_KERNEL);
55 	if (!bid_entry) {
56 		bid_entry = ERR_PTR(-ENOMEM);
57 		goto out;
58 	}
59 
60 	bid_entry_name = kstrdup(name, GFP_KERNEL);
61 	if (!bid_entry_name) {
62 		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
63 		bid_entry = ERR_PTR(-ENOMEM);
64 		goto out;
65 	}
66 
67 	INIT_HLIST_NODE(&bid_entry->node);
68 	bid_entry->str = make_qstr(bid_entry_name);
69 	bid_entry->id = id;
70 out:
71 	return bid_entry;
72 }
73 
free_bid_entry(struct sharefs_bid_entry *bid_entry)74 static void free_bid_entry(struct sharefs_bid_entry *bid_entry)
75 {
76 	if (bid_entry == NULL)
77 		return;
78 
79 	kfree(bid_entry->str.name);
80 	kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);
81 }
82 
alloc_bitem(const char *name)83 static struct sharefs_config_bitem *alloc_bitem(const char *name)
84 {
85 	struct sharefs_config_bitem *bitem;
86 	char *bitem_name;
87 
88 	bitem = kzalloc(sizeof(*bitem), GFP_KERNEL);
89 	if (!bitem) {
90 		bitem = ERR_PTR(-ENOMEM);
91 		goto out;
92 	}
93 
94 	bitem_name = kstrdup(name, GFP_KERNEL);
95 	if (!bitem_name) {
96 		kfree(bitem);
97 		bitem = ERR_PTR(-ENOMEM);
98 		goto out;
99 	}
100 
101 	bitem->str = make_qstr(bitem_name);
102 out:
103 	return bitem;
104 }
105 
free_bitem(struct sharefs_config_bitem *bitem)106 static void free_bitem(struct sharefs_config_bitem *bitem)
107 {
108 	if (bitem == NULL)
109 		return;
110 
111 	kfree(bitem->str.name);
112 	kfree(bitem);
113 }
114 
115 #define SHAREFS_BUNDLE_ATTRIBUTE(_attr_)					\
116 									\
117 static DEFINE_HASHTABLE(sharefs_##_attr_##_hash_table, 4);		\
118 									\
119 static DEFINE_MUTEX(sharefs_##_attr_##_hash_mutex);			\
120 									\
121 static int query_##_attr_##_hash_entry(struct qstr *str)		\
122 {									\
123 	int id = 0;							\
124 	struct sharefs_bid_entry *bid_entry;				\
125 	struct hlist_node *hash_node;					\
126 									\
127 	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
128 	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
129 		bid_entry, hash_node, node, str->hash) {		\
130 		if (qstr_case_eq(str, &bid_entry->str)) {		\
131 			id = bid_entry->id;				\
132 			break;						\
133 		}							\
134 	}								\
135 	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
136 									\
137 	return id;							\
138 }									\
139 									\
140 static int insert_##_attr_##_hash_entry(struct qstr *str, int id)	\
141 {									\
142 	int err = 0;							\
143 	struct sharefs_bid_entry *bid_entry;				\
144 	struct hlist_node *hash_node;					\
145 									\
146 	sharefs_info("insert name = %s", str->name);			\
147 									\
148 	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
149 	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
150 		bid_entry, hash_node, node, str->hash) {		\
151 		if (qstr_case_eq(str, &bid_entry->str)) {		\
152 			bid_entry->id = id;				\
153 			mutex_unlock(&sharefs_##_attr_##_hash_mutex);	\
154 			goto out;					\
155 		}							\
156 	}								\
157 	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
158 									\
159 	bid_entry = alloc_bid_entry(str->name, id);			\
160 	if (IS_ERR(bid_entry)) {					\
161 		err = PTR_ERR(bid_entry);				\
162 		goto out;						\
163 	} 								\
164 									\
165 	hash_add_rcu(sharefs_##_attr_##_hash_table, &bid_entry->node,	\
166 		 bid_entry->str.hash);					\
167 out:									\
168 	return err;							\
169 }									\
170 									\
171 static void remove_##_attr_##_hash_entry(struct qstr *str)		\
172 {									\
173 	struct sharefs_bid_entry *bid_entry;				\
174 	struct hlist_node *hash_node;					\
175 									\
176 	sharefs_info("remove name = %s", str->name);			\
177 									\
178 	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
179 	hash_for_each_possible_safe(sharefs_##_attr_##_hash_table,	\
180 		bid_entry, hash_node, node, str->hash) {		\
181 		if (qstr_case_eq(str, &bid_entry->str)) {		\
182 			hash_del_rcu(&bid_entry->node);			\
183 			free_bid_entry(bid_entry);			\
184 			break;						\
185 		}							\
186 	}								\
187 	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
188 }									\
189 									\
190 static void clear_##_attr_##_hash_entry(void)				\
191 {									\
192 	int index;							\
193 	struct sharefs_bid_entry *bid_entry;				\
194 	struct hlist_node *hash_node;					\
195 									\
196 	sharefs_info("clear bid entry");					\
197 									\
198 	mutex_lock(&sharefs_##_attr_##_hash_mutex);			\
199 	hash_for_each_safe(sharefs_##_attr_##_hash_table, index,		\
200 		hash_node, bid_entry, node) {				\
201 		hash_del_rcu(&bid_entry->node);				\
202 		kfree(bid_entry->str.name);				\
203 		kmem_cache_free(sharefs_bid_entry_cachep, bid_entry);	\
204 	}								\
205 	mutex_unlock(&sharefs_##_attr_##_hash_mutex);			\
206 }									\
207 									\
208 static int sharefs_##_attr_##_get(const char *bname)			\
209 {									\
210 	struct qstr str;						\
211 									\
212 	str = make_qstr(bname);						\
213 	return query_##_attr_##_hash_entry(&str);			\
214 }									\
215 									\
216 static ssize_t sharefs_##_attr_##_show(struct config_item *item,		\
217 	char *page)							\
218 {									\
219 	int id;								\
220 	struct sharefs_config_bitem *bitem;				\
221 									\
222 	sharefs_info("show bundle id");					\
223 									\
224 	bitem = container_of(item, struct sharefs_config_bitem, item);	\
225 	id = query_##_attr_##_hash_entry(&bitem->str);			\
226 									\
227 	return scnprintf(page, PAGE_SIZE, "%u\n", id);			\
228 }									\
229 									\
230 static ssize_t sharefs_##_attr_##_store(struct config_item *item,	\
231 	const char *page, size_t count)					\
232 {									\
233 	int id;								\
234 	int err;							\
235 	size_t size;							\
236 	struct sharefs_config_bitem *bitem;				\
237 									\
238 	sharefs_info("store bundle id");					\
239 									\
240 	bitem = container_of(item, struct sharefs_config_bitem, item);	\
241 									\
242 	if (kstrtouint(page, 10, &id)) {				\
243 		size = -EINVAL;						\
244 		goto out; 						\
245 	}								\
246 									\
247 	err = insert_##_attr_##_hash_entry(&bitem->str, id);		\
248 	if (err) {							\
249 		size = err;						\
250 		goto out;						\
251 	}								\
252 									\
253 	size = count;							\
254 out:									\
255 	return size;							\
256 }									\
257 									\
258 static struct configfs_attribute sharefs_##_attr_##_attr = {		\
259 	.ca_name	= __stringify(_attr_),				\
260 	.ca_mode	= S_IRUGO | S_IWUGO,				\
261 	.ca_owner	= THIS_MODULE,					\
262 	.show		= sharefs_##_attr_##_show,			\
263 	.store		= sharefs_##_attr_##_store,			\
264 };
265 
266 SHAREFS_BUNDLE_ATTRIBUTE(appid)
267 
268 static struct configfs_attribute *sharefs_battrs[] = {
269 	&sharefs_appid_attr,
270 	NULL,
271 };
272 
sharefs_config_bitem_release(struct config_item *item)273 static void sharefs_config_bitem_release(struct config_item *item)
274 {
275 	struct sharefs_config_bitem *bitem;
276 
277 	sharefs_info("release bundle item");
278 
279 	bitem = container_of(item, struct sharefs_config_bitem, item);
280 	remove_appid_hash_entry(&bitem->str);
281 	remove_appid_hash_entry(&bitem->str);
282 	free_bitem(bitem);
283 }
284 
285 static struct configfs_item_operations sharefs_config_bitem_ops = {
286 	.release = sharefs_config_bitem_release,
287 };
288 
289 static struct config_item_type sharefs_config_bitem_type = {
290 	.ct_item_ops	= &sharefs_config_bitem_ops,
291 	.ct_attrs	= sharefs_battrs,
292 	.ct_owner	= THIS_MODULE,
293 };
294 
sharefs_make_bitem(struct config_group *group, const char *name)295 static struct config_item *sharefs_make_bitem(struct config_group *group,
296 					      const char *name)
297 {
298 	struct config_item *item;
299 	struct sharefs_config_bitem *bitem;
300 
301 	bitem = alloc_bitem(name);
302 	if (IS_ERR(bitem)) {
303 		item = ERR_PTR(-ENOMEM);
304 		goto out;
305 	}
306 
307 	config_item_init_type_name(&bitem->item, name,
308 		&sharefs_config_bitem_type);
309 	item = &bitem->item;
310 out:
311 	return item;
312 }
313 
314 static struct configfs_group_operations sharefs_group_ops = {
315 	.make_item = sharefs_make_bitem,
316 };
317 
318 static struct config_item_type sharefs_group_type = {
319 	.ct_group_ops     = &sharefs_group_ops,
320 	.ct_owner         = THIS_MODULE,
321 };
322 
323 static struct configfs_subsystem sharefs_subsystem = {
324 	.su_group = {
325 		.cg_item = {
326 			.ci_namebuf = "sharefs",
327 			.ci_type = &sharefs_group_type,
328 		},
329 	},
330 };
331 
get_bid_config(const char *bname)332 int get_bid_config(const char *bname)
333 {
334 	return sharefs_appid_get(bname);
335 }
336 
sharefs_init_configfs(void)337 int __init sharefs_init_configfs(void)
338 {
339 	int err;
340 	struct configfs_subsystem *subsys;
341 
342 	sharefs_info("init configfs");
343 
344 	sharefs_bid_entry_cachep = kmem_cache_create("sharefs_bid_entry_cachep",
345 		sizeof(struct sharefs_bid_entry), 0, 0, NULL);
346 	if (!sharefs_bid_entry_cachep) {
347 		sharefs_err("failed to create bid entry cachep");
348 		err = -ENOMEM;
349 		goto out;
350 	}
351 
352 	subsys = &sharefs_subsystem;
353 	config_group_init(&subsys->su_group);
354 	mutex_init(&subsys->su_mutex);
355 
356 	err = configfs_register_subsystem(subsys);
357 	if (err)
358 		sharefs_err("failed to register subsystem");
359 
360 out:
361 	return err;
362 }
363 
sharefs_exit_configfs(void)364 void sharefs_exit_configfs(void)
365 {
366 	sharefs_info("sharefs exit configfs");
367 
368 	configfs_unregister_subsystem(&sharefs_subsystem);
369 	clear_appid_hash_entry();
370 
371 	kmem_cache_destroy(sharefs_bid_entry_cachep);
372 }