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