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