]>
Commit | Line | Data |
---|---|---|
e2f34481 NJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> | |
4 | * Copyright (C) 2018 Samsung Electronics Co., Ltd. | |
5 | */ | |
6 | ||
7 | #include <linux/kernel.h> | |
8 | #include <linux/version.h> | |
9 | #include <linux/xattr.h> | |
10 | #include <linux/fs.h> | |
11 | ||
12 | #include "misc.h" | |
13 | #include "smb_common.h" | |
14 | #include "connection.h" | |
15 | #include "vfs.h" | |
16 | ||
17 | #include "mgmt/share_config.h" | |
18 | ||
19 | /** | |
20 | * match_pattern() - compare a string with a pattern which might include | |
21 | * wildcard '*' and '?' | |
22 | * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR | |
23 | * | |
24 | * @string: string to compare with a pattern | |
b24c9335 | 25 | * @len: string length |
e2f34481 NJ |
26 | * @pattern: pattern string which might include wildcard '*' and '?' |
27 | * | |
28 | * Return: 0 if pattern matched with the string, otherwise non zero value | |
29 | */ | |
b24c9335 | 30 | int match_pattern(const char *str, size_t len, const char *pattern) |
e2f34481 NJ |
31 | { |
32 | const char *s = str; | |
33 | const char *p = pattern; | |
34 | bool star = false; | |
35 | ||
b24c9335 | 36 | while (*s && len) { |
e2f34481 NJ |
37 | switch (*p) { |
38 | case '?': | |
39 | s++; | |
b24c9335 | 40 | len--; |
e2f34481 NJ |
41 | p++; |
42 | break; | |
43 | case '*': | |
44 | star = true; | |
45 | str = s; | |
46 | if (!*++p) | |
47 | return true; | |
48 | pattern = p; | |
49 | break; | |
50 | default: | |
51 | if (tolower(*s) == tolower(*p)) { | |
52 | s++; | |
b24c9335 | 53 | len--; |
e2f34481 NJ |
54 | p++; |
55 | } else { | |
56 | if (!star) | |
57 | return false; | |
58 | str++; | |
59 | s = str; | |
60 | p = pattern; | |
61 | } | |
62 | break; | |
63 | } | |
64 | } | |
65 | ||
66 | if (*p == '*') | |
67 | ++p; | |
68 | return !*p; | |
69 | } | |
70 | ||
71 | /* | |
72 | * is_char_allowed() - check for valid character | |
73 | * @ch: input character to be checked | |
74 | * | |
75 | * Return: 1 if char is allowed, otherwise 0 | |
76 | */ | |
77 | static inline int is_char_allowed(char ch) | |
78 | { | |
79 | /* check for control chars, wildcards etc. */ | |
80 | if (!(ch & 0x80) && | |
64b39f4a NJ |
81 | (ch <= 0x1f || |
82 | ch == '?' || ch == '"' || ch == '<' || | |
83 | ch == '>' || ch == '|' || ch == '*')) | |
e2f34481 NJ |
84 | return 0; |
85 | ||
86 | return 1; | |
87 | } | |
88 | ||
89 | int ksmbd_validate_filename(char *filename) | |
90 | { | |
91 | while (*filename) { | |
92 | char c = *filename; | |
93 | ||
94 | filename++; | |
95 | if (!is_char_allowed(c)) { | |
96 | ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); | |
97 | return -ENOENT; | |
98 | } | |
99 | } | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static int ksmbd_validate_stream_name(char *stream_name) | |
105 | { | |
106 | while (*stream_name) { | |
107 | char c = *stream_name; | |
108 | ||
109 | stream_name++; | |
110 | if (c == '/' || c == ':' || c == '\\') { | |
111 | ksmbd_err("Stream name validation failed: %c\n", c); | |
112 | return -ENOENT; | |
113 | } | |
114 | } | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | int parse_stream_name(char *filename, char **stream_name, int *s_type) | |
120 | { | |
121 | char *stream_type; | |
122 | char *s_name; | |
123 | int rc = 0; | |
124 | ||
125 | s_name = filename; | |
126 | filename = strsep(&s_name, ":"); | |
127 | ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); | |
128 | if (strchr(s_name, ':')) { | |
129 | stream_type = s_name; | |
130 | s_name = strsep(&stream_type, ":"); | |
131 | ||
132 | rc = ksmbd_validate_stream_name(s_name); | |
133 | if (rc < 0) { | |
134 | rc = -ENOENT; | |
135 | goto out; | |
136 | } | |
137 | ||
138 | ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, | |
139 | stream_type); | |
140 | if (!strncasecmp("$data", stream_type, 5)) | |
141 | *s_type = DATA_STREAM; | |
142 | else if (!strncasecmp("$index_allocation", stream_type, 17)) | |
143 | *s_type = DIR_STREAM; | |
144 | else | |
145 | rc = -ENOENT; | |
146 | } | |
147 | ||
148 | *stream_name = s_name; | |
149 | out: | |
150 | return rc; | |
151 | } | |
152 | ||
153 | /** | |
154 | * convert_to_nt_pathname() - extract and return windows path string | |
155 | * whose share directory prefix was removed from file path | |
156 | * @filename : unix filename | |
157 | * @sharepath: share path string | |
158 | * | |
159 | * Return : windows path string or error | |
160 | */ | |
161 | ||
162 | char *convert_to_nt_pathname(char *filename, char *sharepath) | |
163 | { | |
164 | char *ab_pathname; | |
165 | int len, name_len; | |
166 | ||
167 | name_len = strlen(filename); | |
168 | ab_pathname = kmalloc(name_len, GFP_KERNEL); | |
169 | if (!ab_pathname) | |
170 | return NULL; | |
171 | ||
172 | ab_pathname[0] = '\\'; | |
173 | ab_pathname[1] = '\0'; | |
174 | ||
175 | len = strlen(sharepath); | |
176 | if (!strncmp(filename, sharepath, len) && name_len != len) { | |
177 | strscpy(ab_pathname, &filename[len], name_len); | |
178 | ksmbd_conv_path_to_windows(ab_pathname); | |
179 | } | |
180 | ||
181 | return ab_pathname; | |
182 | } | |
183 | ||
184 | int get_nlink(struct kstat *st) | |
185 | { | |
186 | int nlink; | |
187 | ||
188 | nlink = st->nlink; | |
189 | if (S_ISDIR(st->mode)) | |
190 | nlink--; | |
191 | ||
192 | return nlink; | |
193 | } | |
194 | ||
195 | void ksmbd_conv_path_to_unix(char *path) | |
196 | { | |
197 | strreplace(path, '\\', '/'); | |
198 | } | |
199 | ||
200 | void ksmbd_strip_last_slash(char *path) | |
201 | { | |
202 | int len = strlen(path); | |
203 | ||
204 | while (len && path[len - 1] == '/') { | |
205 | path[len - 1] = '\0'; | |
206 | len--; | |
207 | } | |
208 | } | |
209 | ||
210 | void ksmbd_conv_path_to_windows(char *path) | |
211 | { | |
212 | strreplace(path, '/', '\\'); | |
213 | } | |
214 | ||
215 | /** | |
36ba3866 | 216 | * ksmbd_extract_sharename() - get share name from tree connect request |
e2f34481 NJ |
217 | * @treename: buffer containing tree name and share name |
218 | * | |
219 | * Return: share name on success, otherwise error | |
220 | */ | |
36ba3866 | 221 | char *ksmbd_extract_sharename(char *treename) |
e2f34481 NJ |
222 | { |
223 | char *name = treename; | |
224 | char *dst; | |
225 | char *pos = strrchr(name, '\\'); | |
226 | ||
227 | if (pos) | |
228 | name = (pos + 1); | |
229 | ||
230 | /* caller has to free the memory */ | |
231 | dst = kstrdup(name, GFP_KERNEL); | |
232 | if (!dst) | |
233 | return ERR_PTR(-ENOMEM); | |
234 | return dst; | |
235 | } | |
236 | ||
237 | /** | |
238 | * convert_to_unix_name() - convert windows name to unix format | |
239 | * @path: name to be converted | |
240 | * @tid: tree id of mathing share | |
241 | * | |
242 | * Return: converted name on success, otherwise NULL | |
243 | */ | |
244 | char *convert_to_unix_name(struct ksmbd_share_config *share, char *name) | |
245 | { | |
246 | int no_slash = 0, name_len, path_len; | |
247 | char *new_name; | |
248 | ||
249 | if (name[0] == '/') | |
250 | name++; | |
251 | ||
252 | path_len = share->path_sz; | |
253 | name_len = strlen(name); | |
254 | new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); | |
255 | if (!new_name) | |
256 | return new_name; | |
257 | ||
258 | memcpy(new_name, share->path, path_len); | |
259 | if (new_name[path_len - 1] != '/') { | |
260 | new_name[path_len] = '/'; | |
261 | no_slash = 1; | |
262 | } | |
263 | ||
264 | memcpy(new_name + path_len + no_slash, name, name_len); | |
265 | path_len += name_len + no_slash; | |
266 | new_name[path_len] = 0x00; | |
267 | return new_name; | |
268 | } | |
269 | ||
270 | char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, | |
64b39f4a | 271 | const struct nls_table *local_nls, int *conv_len) |
e2f34481 NJ |
272 | { |
273 | char *conv; | |
274 | int sz = min(4 * d_info->name_len, PATH_MAX); | |
275 | ||
276 | if (!sz) | |
277 | return NULL; | |
278 | ||
279 | conv = kmalloc(sz, GFP_KERNEL); | |
280 | if (!conv) | |
281 | return NULL; | |
282 | ||
283 | /* XXX */ | |
284 | *conv_len = smbConvertToUTF16((__le16 *)conv, | |
285 | d_info->name, | |
286 | d_info->name_len, | |
287 | local_nls, | |
288 | 0); | |
289 | *conv_len *= 2; | |
290 | ||
291 | /* We allocate buffer twice bigger than needed. */ | |
292 | conv[*conv_len] = 0x00; | |
293 | conv[*conv_len + 1] = 0x00; | |
294 | return conv; | |
295 | } |