cifs: mapchars mount option ignored
[sfrench/cifs-2.6.git] / fs / smb / server / 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 #include "../misc.h"
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(const 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_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
62 void __ksmbd_share_config_put(struct ksmbd_share_config *share)
63 {
64         ksmbd_share_config_del(share);
65         kill_share(share);
66 }
67
68 static 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
76 static 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
88 static 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
123 static 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
203 out:
204         kvfree(resp);
205         return share;
206 }
207
208 struct 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
224 bool 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 }