Loading...
Note: File does not exist in v4.17.
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#include "../misc.h"
20
21#define SHARE_HASH_BITS 3
22static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
23static DECLARE_RWSEM(shares_table_lock);
24
25struct ksmbd_veto_pattern {
26 char *pattern;
27 struct list_head list;
28};
29
30static unsigned int share_name_hash(const char *name)
31{
32 return jhash(name, strlen(name), 0);
33}
34
35static 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
55void ksmbd_share_config_del(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
62void __ksmbd_share_config_put(struct ksmbd_share_config *share)
63{
64 ksmbd_share_config_del(share);
65 kill_share(share);
66}
67
68static struct ksmbd_share_config *
69__get_share_config(struct ksmbd_share_config *share)
70{
71 if (!atomic_inc_not_zero(&share->refcount))
72 return NULL;
73 return share;
74}
75
76static struct ksmbd_share_config *__share_lookup(const char *name)
77{
78 struct ksmbd_share_config *share;
79 unsigned int key = share_name_hash(name);
80
81 hash_for_each_possible(shares_table, share, hlist, key) {
82 if (!strcmp(name, share->name))
83 return share;
84 }
85 return NULL;
86}
87
88static int parse_veto_list(struct ksmbd_share_config *share,
89 char *veto_list,
90 int veto_list_sz)
91{
92 int sz = 0;
93
94 if (!veto_list_sz)
95 return 0;
96
97 while (veto_list_sz > 0) {
98 struct ksmbd_veto_pattern *p;
99
100 sz = strlen(veto_list);
101 if (!sz)
102 break;
103
104 p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
105 if (!p)
106 return -ENOMEM;
107
108 p->pattern = kstrdup(veto_list, GFP_KERNEL);
109 if (!p->pattern) {
110 kfree(p);
111 return -ENOMEM;
112 }
113
114 list_add(&p->list, &share->veto_list);
115
116 veto_list += sz + 1;
117 veto_list_sz -= (sz + 1);
118 }
119
120 return 0;
121}
122
123static struct ksmbd_share_config *share_config_request(struct unicode_map *um,
124 const char *name)
125{
126 struct ksmbd_share_config_response *resp;
127 struct ksmbd_share_config *share = NULL;
128 struct ksmbd_share_config *lookup;
129 int ret;
130
131 resp = ksmbd_ipc_share_config_request(name);
132 if (!resp)
133 return NULL;
134
135 if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
136 goto out;
137
138 if (*resp->share_name) {
139 char *cf_resp_name;
140 bool equal;
141
142 cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name);
143 if (IS_ERR(cf_resp_name))
144 goto out;
145 equal = !strcmp(cf_resp_name, name);
146 kfree(cf_resp_name);
147 if (!equal)
148 goto out;
149 }
150
151 share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
152 if (!share)
153 goto out;
154
155 share->flags = resp->flags;
156 atomic_set(&share->refcount, 1);
157 INIT_LIST_HEAD(&share->veto_list);
158 share->name = kstrdup(name, GFP_KERNEL);
159
160 if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
161 share->path = kstrdup(ksmbd_share_config_path(resp),
162 GFP_KERNEL);
163 if (share->path)
164 share->path_sz = strlen(share->path);
165 share->create_mask = resp->create_mask;
166 share->directory_mask = resp->directory_mask;
167 share->force_create_mode = resp->force_create_mode;
168 share->force_directory_mode = resp->force_directory_mode;
169 share->force_uid = resp->force_uid;
170 share->force_gid = resp->force_gid;
171 ret = parse_veto_list(share,
172 KSMBD_SHARE_CONFIG_VETO_LIST(resp),
173 resp->veto_list_sz);
174 if (!ret && share->path) {
175 ret = kern_path(share->path, 0, &share->vfs_path);
176 if (ret) {
177 ksmbd_debug(SMB, "failed to access '%s'\n",
178 share->path);
179 /* Avoid put_path() */
180 kfree(share->path);
181 share->path = NULL;
182 }
183 }
184 if (ret || !share->name) {
185 kill_share(share);
186 share = NULL;
187 goto out;
188 }
189 }
190
191 down_write(&shares_table_lock);
192 lookup = __share_lookup(name);
193 if (lookup)
194 lookup = __get_share_config(lookup);
195 if (!lookup) {
196 hash_add(shares_table, &share->hlist, share_name_hash(name));
197 } else {
198 kill_share(share);
199 share = lookup;
200 }
201 up_write(&shares_table_lock);
202
203out:
204 kvfree(resp);
205 return share;
206}
207
208struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um,
209 const char *name)
210{
211 struct ksmbd_share_config *share;
212
213 down_read(&shares_table_lock);
214 share = __share_lookup(name);
215 if (share)
216 share = __get_share_config(share);
217 up_read(&shares_table_lock);
218
219 if (share)
220 return share;
221 return share_config_request(um, name);
222}
223
224bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
225 const char *filename)
226{
227 struct ksmbd_veto_pattern *p;
228
229 list_for_each_entry(p, &share->veto_list, list) {
230 if (match_wildcard(p->pattern, filename))
231 return true;
232 }
233 return false;
234}