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