ksmbd: move fs/cifsd to fs/ksmbd
[linux-2.6-microblaze.git] / fs / ksmbd / mgmt / share_config.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
4  */
5
6 #include <linux/list.h>
7 #include <linux/jhash.h>
8 #include <linux/slab.h>
9 #include <linux/rwsem.h>
10 #include <linux/parser.h>
11 #include <linux/namei.h>
12 #include <linux/sched.h>
13 #include <linux/mm.h>
14
15 #include "share_config.h"
16 #include "user_config.h"
17 #include "user_session.h"
18 #include "../transport_ipc.h"
19
20 #define SHARE_HASH_BITS         3
21 static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
22 static DECLARE_RWSEM(shares_table_lock);
23
24 struct ksmbd_veto_pattern {
25         char                    *pattern;
26         struct list_head        list;
27 };
28
29 static unsigned int share_name_hash(char *name)
30 {
31         return jhash(name, strlen(name), 0);
32 }
33
34 static void kill_share(struct ksmbd_share_config *share)
35 {
36         while (!list_empty(&share->veto_list)) {
37                 struct ksmbd_veto_pattern *p;
38
39                 p = list_entry(share->veto_list.next,
40                                struct ksmbd_veto_pattern,
41                                list);
42                 list_del(&p->list);
43                 kfree(p->pattern);
44                 kfree(p);
45         }
46
47         if (share->path)
48                 path_put(&share->vfs_path);
49         kfree(share->name);
50         kfree(share->path);
51         kfree(share);
52 }
53
54 void __ksmbd_share_config_put(struct ksmbd_share_config *share)
55 {
56         down_write(&shares_table_lock);
57         hash_del(&share->hlist);
58         up_write(&shares_table_lock);
59
60         kill_share(share);
61 }
62
63 static struct ksmbd_share_config *
64 __get_share_config(struct ksmbd_share_config *share)
65 {
66         if (!atomic_inc_not_zero(&share->refcount))
67                 return NULL;
68         return share;
69 }
70
71 static struct ksmbd_share_config *__share_lookup(char *name)
72 {
73         struct ksmbd_share_config *share;
74         unsigned int key = share_name_hash(name);
75
76         hash_for_each_possible(shares_table, share, hlist, key) {
77                 if (!strcmp(name, share->name))
78                         return share;
79         }
80         return NULL;
81 }
82
83 static int parse_veto_list(struct ksmbd_share_config *share,
84                            char *veto_list,
85                            int veto_list_sz)
86 {
87         int sz = 0;
88
89         if (!veto_list_sz)
90                 return 0;
91
92         while (veto_list_sz > 0) {
93                 struct ksmbd_veto_pattern *p;
94
95                 sz = strlen(veto_list);
96                 if (!sz)
97                         break;
98
99                 p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
100                 if (!p)
101                         return -ENOMEM;
102
103                 p->pattern = kstrdup(veto_list, GFP_KERNEL);
104                 if (!p->pattern) {
105                         kfree(p);
106                         return -ENOMEM;
107                 }
108
109                 list_add(&p->list, &share->veto_list);
110
111                 veto_list += sz + 1;
112                 veto_list_sz -= (sz + 1);
113         }
114
115         return 0;
116 }
117
118 static struct ksmbd_share_config *share_config_request(char *name)
119 {
120         struct ksmbd_share_config_response *resp;
121         struct ksmbd_share_config *share = NULL;
122         struct ksmbd_share_config *lookup;
123         int ret;
124
125         resp = ksmbd_ipc_share_config_request(name);
126         if (!resp)
127                 return NULL;
128
129         if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
130                 goto out;
131
132         share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
133         if (!share)
134                 goto out;
135
136         share->flags = resp->flags;
137         atomic_set(&share->refcount, 1);
138         INIT_LIST_HEAD(&share->veto_list);
139         share->name = kstrdup(name, GFP_KERNEL);
140
141         if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
142                 share->path = kstrdup(ksmbd_share_config_path(resp),
143                                       GFP_KERNEL);
144                 if (share->path)
145                         share->path_sz = strlen(share->path);
146                 share->create_mask = resp->create_mask;
147                 share->directory_mask = resp->directory_mask;
148                 share->force_create_mode = resp->force_create_mode;
149                 share->force_directory_mode = resp->force_directory_mode;
150                 share->force_uid = resp->force_uid;
151                 share->force_gid = resp->force_gid;
152                 ret = parse_veto_list(share,
153                                       KSMBD_SHARE_CONFIG_VETO_LIST(resp),
154                                       resp->veto_list_sz);
155                 if (!ret && share->path) {
156                         ret = kern_path(share->path, 0, &share->vfs_path);
157                         if (ret) {
158                                 ksmbd_debug(SMB, "failed to access '%s'\n",
159                                             share->path);
160                                 /* Avoid put_path() */
161                                 kfree(share->path);
162                                 share->path = NULL;
163                         }
164                 }
165                 if (ret || !share->name) {
166                         kill_share(share);
167                         share = NULL;
168                         goto out;
169                 }
170         }
171
172         down_write(&shares_table_lock);
173         lookup = __share_lookup(name);
174         if (lookup)
175                 lookup = __get_share_config(lookup);
176         if (!lookup) {
177                 hash_add(shares_table, &share->hlist, share_name_hash(name));
178         } else {
179                 kill_share(share);
180                 share = lookup;
181         }
182         up_write(&shares_table_lock);
183
184 out:
185         kvfree(resp);
186         return share;
187 }
188
189 static void strtolower(char *share_name)
190 {
191         while (*share_name) {
192                 *share_name = tolower(*share_name);
193                 share_name++;
194         }
195 }
196
197 struct ksmbd_share_config *ksmbd_share_config_get(char *name)
198 {
199         struct ksmbd_share_config *share;
200
201         strtolower(name);
202
203         down_read(&shares_table_lock);
204         share = __share_lookup(name);
205         if (share)
206                 share = __get_share_config(share);
207         up_read(&shares_table_lock);
208
209         if (share)
210                 return share;
211         return share_config_request(name);
212 }
213
214 bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
215                                const char *filename)
216 {
217         struct ksmbd_veto_pattern *p;
218
219         list_for_each_entry(p, &share->veto_list, list) {
220                 if (match_wildcard(p->pattern, filename))
221                         return true;
222         }
223         return false;
224 }
225
226 void ksmbd_share_configs_cleanup(void)
227 {
228         struct ksmbd_share_config *share;
229         struct hlist_node *tmp;
230         int i;
231
232         down_write(&shares_table_lock);
233         hash_for_each_safe(shares_table, i, tmp, share, hlist) {
234                 hash_del(&share->hlist);
235                 kill_share(share);
236         }
237         up_write(&shares_table_lock);
238 }