Print this page
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
SMB-50 User-mode SMB server
Includes work by these authors:
Thomas Keiser <thomas.keiser@nexenta.com>
Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-65 SMB server in non-global zones (data structure changes)
Many things move to the smb_server_t object, and
many functions gain an sv arg (which server).
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c
+++ new/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
23 + * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
24 24 */
25 25
26 26 #include <sys/types.h>
27 27 #include <sys/param.h>
28 28 #include <sys/sunddi.h>
29 29 #include <sys/errno.h>
30 30 #include <sys/extdirent.h>
31 31 #include <smbsrv/string.h>
32 32 #include <smbsrv/smb_vops.h>
33 33 #include <smbsrv/smb_kproto.h>
34 34 #include <smbsrv/smb_fsops.h>
35 35
36 36 /*
37 37 * Characters we don't allow in DOS file names.
38 38 * If a filename contains any of these chars, it should get mangled.
39 39 *
40 40 * '.' is also an invalid DOS char but since it's a special
41 41 * case it doesn't appear in the list.
42 42 */
43 43 static const char invalid_dos_chars[] =
44 44 "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
45 45 "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
46 46 " \"/\\:|<>*?";
47 47
48 48 /*
49 49 * According to MSKB article #142982, Windows deletes invalid chars and
50 50 * spaces from file name in mangling process; and invalid chars include:
51 51 * ."/\[]:;=,
52 52 *
53 53 * But some of these chars and some other chars (e.g. +) are replaced
54 54 * with underscore (_). They are introduced here as special chars.
55 55 */
56 56 static const char special_chars[] = "[];=,+";
57 57
58 58 #define isinvalid(c) (strchr(invalid_dos_chars, c) || (c & 0x80))
59 59
60 60 static int smb_generate_mangle(uint64_t, char *, size_t);
61 61 static char smb_mangle_char(char);
62 62
63 63 /*
64 64 * Return true if name contains characters that are invalid in a file
65 65 * name or it is a reserved DOS device name. Otherwise, returns false.
66 66 *
67 67 * Control characters (values 0 - 31) and the following characters are
68 68 * invalid:
69 69 * < > : " / \ | ? *
70 70 */
71 71 boolean_t
72 72 smb_is_invalid_filename(const char *name)
73 73 {
74 74 const char *p;
75 75
76 76 if ((p = strpbrk(name, invalid_dos_chars)) != NULL) {
77 77 if (*p != ' ')
78 78 return (B_TRUE);
79 79 }
80 80
81 81 return (smb_is_reserved_dos_name(name));
82 82 }
83 83
84 84 /*
85 85 * smb_is_reserved_dos_name
86 86 *
87 87 * This function checks if the name is a reserved DOS device name.
88 88 * The device name should not be followed immediately by an extension,
89 89 * for example, NUL.txt.
90 90 */
91 91 boolean_t
92 92 smb_is_reserved_dos_name(const char *name)
93 93 {
94 94 static char *cnames[] = { "CLOCK$", "COM1", "COM2", "COM3", "COM4",
95 95 "COM5", "COM6", "COM7", "COM8", "COM9", "CON" };
96 96 static char *lnames[] = { "LPT1", "LPT2", "LPT3", "LPT4", "LPT5",
97 97 "LPT6", "LPT7", "LPT8", "LPT9" };
98 98 static char *others[] = { "AUX", "NUL", "PRN" };
99 99 char **reserved;
100 100 char ch;
101 101 int n_reserved;
102 102 int len;
103 103 int i;
104 104
105 105 ch = smb_toupper(*name);
106 106
107 107 switch (ch) {
108 108 case 'A':
109 109 case 'N':
110 110 case 'P':
111 111 reserved = others;
112 112 n_reserved = sizeof (others) / sizeof (others[0]);
113 113 break;
114 114 case 'C':
115 115 reserved = cnames;
116 116 n_reserved = sizeof (cnames) / sizeof (cnames[0]);
117 117 break;
118 118 case 'L':
119 119 reserved = lnames;
120 120 n_reserved = sizeof (lnames) / sizeof (lnames[0]);
121 121 break;
122 122 default:
123 123 return (B_FALSE);
124 124 }
125 125
126 126 for (i = 0; i < n_reserved; ++i) {
127 127 len = strlen(reserved[i]);
128 128
129 129 if (smb_strcasecmp(reserved[i], name, len) == 0) {
130 130 ch = *(name + len);
131 131 if ((ch == '\0') || (ch == '.'))
132 132 return (B_TRUE);
133 133 }
134 134 }
135 135
136 136 return (B_FALSE);
137 137 }
138 138
139 139 /*
140 140 * smb_needs_mangled
141 141 *
142 142 * A name needs to be mangled if any of the following are true:
143 143 * - the first character is dot (.) and the name is not "." or ".."
144 144 * - the name contains illegal or special charsacter
145 145 * - the name name length > 12
146 146 * - the number of dots == 0 and length > 8
147 147 * - the number of dots > 1
148 148 * - the number of dots == 1 and name is not 8.3
149 149 */
150 150 boolean_t
151 151 smb_needs_mangled(const char *name)
152 152 {
153 153 int len, extlen, ndots;
154 154 const char *p;
155 155 const char *last_dot;
156 156
157 157 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
158 158 return (B_FALSE);
159 159
160 160 if (*name == '.')
161 161 return (B_TRUE);
162 162
163 163 len = 0;
164 164 ndots = 0;
165 165 last_dot = NULL;
166 166 for (p = name; *p != '\0'; ++p) {
167 167 if (smb_iscntrl(*p) ||
168 168 (strchr(special_chars, *p) != NULL) ||
169 169 (strchr(invalid_dos_chars, *p)) != NULL)
170 170 return (B_TRUE);
171 171
172 172 if (*p == '.') {
173 173 ++ndots;
174 174 last_dot = p;
175 175 }
176 176 ++len;
177 177 }
178 178
179 179 if ((len > SMB_NAME83_LEN) ||
180 180 (ndots == 0 && len > SMB_NAME83_BASELEN) ||
181 181 (ndots > 1)) {
182 182 return (B_TRUE);
183 183 }
184 184
185 185 if (last_dot != NULL) {
186 186 extlen = strlen(last_dot + 1);
187 187 if ((extlen == 0) || (extlen > SMB_NAME83_EXTLEN))
188 188 return (B_TRUE);
189 189
190 190 if ((len - extlen - 1) > SMB_NAME83_BASELEN)
191 191 return (B_TRUE);
192 192 }
193 193
194 194 return (B_FALSE);
195 195 }
196 196
197 197 /*
198 198 * smb_mangle_char
199 199 *
200 200 * If c is an invalid DOS character or non-ascii, it should
201 201 * not be used in the mangled name. We return -1 to indicate
202 202 * an invalid character.
203 203 *
204 204 * If c is a special chars, it should be replaced with '_'.
205 205 *
206 206 * Otherwise c is returned as uppercase.
207 207 */
208 208 static char
209 209 smb_mangle_char(char c)
210 210 {
211 211 if (isinvalid(c))
212 212 return (-1);
213 213
214 214 if (strchr(special_chars, c))
215 215 return ('_');
216 216
217 217 return (smb_toupper(c));
218 218 }
219 219
220 220 /*
221 221 * smb_generate_mangle
222 222 *
223 223 * Generate a mangle string containing at least 2 characters and
224 224 * at most (buflen - 1) characters.
225 225 *
226 226 * Returns the number of chars in the generated mangle.
227 227 */
228 228 static int
229 229 smb_generate_mangle(uint64_t fid, char *buf, size_t buflen)
230 230 {
231 231 static char *base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
232 232 char *p = buf;
233 233 int i;
234 234
235 235 if (fid == 0)
236 236 fid = (uint64_t)-1;
237 237
238 238 *p++ = '~';
239 239 for (i = 2; (i < buflen) && (fid > 0); fid /= 36, ++i)
240 240 *p++ = base36[fid % 36];
241 241 *p = '\0';
242 242
243 243 return (i - 1);
244 244 }
245 245
246 246 /*
247 247 * smb_maybe_mangled
248 248 *
249 249 * Mangled names should be valid DOS file names: less than 12 characters
250 250 * long, contain at least one tilde character and conform to an 8.3 name
251 251 * format.
252 252 *
253 253 * Returns true if the name looks like a mangled name.
254 254 */
255 255 boolean_t
256 256 smb_maybe_mangled(char *name)
257 257 {
258 258 const char *p;
259 259 boolean_t has_tilde = B_FALSE;
260 260 int ndots = 0;
261 261 int i;
262 262
263 263 for (p = name, i = 0; (*p != '\0') && (i < SMB_NAME83_LEN); i++, p++) {
264 264 if ((strchr(special_chars, *p) != NULL) ||
265 265 (strchr(invalid_dos_chars, *p) != NULL))
266 266 return (B_FALSE);
267 267
268 268 if (*p == '.') {
269 269 if ((++ndots) > 1)
270 270 return (B_FALSE);
271 271 }
272 272
273 273 if ((*p == '~') && (i < SMB_NAME83_BASELEN))
274 274 has_tilde = B_TRUE;
275 275
276 276 if (*p == '.' && !has_tilde)
277 277 return (B_FALSE);
278 278 }
279 279
280 280 return ((*p == '\0') && has_tilde);
281 281 }
282 282
283 283 /*
284 284 * smb_mangle
285 285 *
286 286 * Microsoft knowledge base article #142982 describes how Windows
287 287 * generates 8.3 filenames from long file names. Some other details
288 288 * can be found in article #114816.
289 289 *
290 290 * This function will mangle the name whether mangling is required
291 291 * or not. Callers should use smb_needs_mangled() to determine whether
292 292 * mangling is required.
293 293 *
294 294 * name original file name
295 295 * fid inode number to generate unique mangle
296 296 * buf output buffer (buflen bytes) to contain mangled name
297 297 */
298 298 void
299 299 smb_mangle(const char *name, ino64_t fid, char *buf, size_t buflen)
300 300 {
301 301 int i, avail;
302 302 const char *p;
303 303 char c;
304 304 char *pbuf;
305 305 char mangle_buf[SMB_NAME83_BASELEN];
306 306
307 307 ASSERT(name && buf && (buflen >= SMB_SHORTNAMELEN));
308 308
309 309 avail = SMB_NAME83_BASELEN -
310 310 smb_generate_mangle(fid, mangle_buf, SMB_NAME83_BASELEN);
311 311 name += strspn(name, ".");
312 312
313 313 /*
314 314 * Copy up to avail characters from the base part of name
315 315 * to buf then append the generated mangle string.
316 316 */
317 317 p = name;
318 318 pbuf = buf;
319 319 for (i = 0; (i < avail) && (*p != '\0') && (*p != '.'); ++i, ++p) {
320 320 if ((c = smb_mangle_char(*p)) == -1)
321 321 continue;
322 322 *pbuf++ = c;
323 323 }
324 324 *pbuf = '\0';
325 325 (void) strlcat(pbuf, mangle_buf, SMB_NAME83_BASELEN);
326 326 pbuf = strchr(pbuf, '\0');
327 327
328 328 /*
329 329 * Find the last dot in the name. If there is a dot and an
330 330 * extension, append '.' and up to SMB_NAME83_EXTLEN extension
331 331 * characters to the mangled name.
332 332 */
333 333 if (((p = strrchr(name, '.')) != NULL) && (*(++p) != '\0')) {
334 334 *pbuf++ = '.';
335 335 for (i = 0; (i < SMB_NAME83_EXTLEN) && (*p != '\0'); ++i, ++p) {
336 336 if ((c = smb_mangle_char(*p)) == -1)
337 337 continue;
338 338 *pbuf++ = c;
339 339 }
340 340 }
341 341
342 342 *pbuf = '\0';
343 343 }
|
↓ open down ↓ |
310 lines elided |
↑ open up ↑ |
344 344
345 345 /*
346 346 * smb_unmangle
347 347 *
348 348 * Given a mangled name, try to find the real file name as it appears
349 349 * in the directory entry.
350 350 *
351 351 * smb_unmangle should only be called on names for which
352 352 * smb_maybe_mangled() is true
353 353 *
354 - * File systems which support VFSFT_EDIRENT_FLAGS will return the
355 - * directory entries as a buffer of edirent_t structure. Others will
356 - * return a buffer of dirent64_t structures. A union is used for the
357 - * the pointer into the buffer (bufptr, edp and dp).
358 - * The ed_name/d_name is NULL terminated by the file system.
354 + * The flags arg is no longer used, but retained just to avoid
355 + * changing the many callers of this function.
359 356 *
360 357 * Returns:
361 358 * 0 - SUCCESS. Unmangled name is returned in namebuf.
362 359 * EINVAL - a parameter was invalid.
363 360 * ENOTDIR - dnode is not a directory node.
364 361 * ENOENT - an unmangled name could not be found.
365 362 */
366 363 #define SMB_UNMANGLE_BUFSIZE (4 * 1024)
367 364 int
368 365 smb_unmangle(smb_node_t *dnode, char *name, char *namebuf,
369 366 int buflen, uint32_t flags)
370 367 {
371 - int err, eof, bufsize, reclen;
368 + _NOTE(ARGUNUSED(flags)) // avoid changing all callers
369 + int err, eof, bufsize;
372 370 uint64_t offset;
373 371 ino64_t ino;
374 - boolean_t is_edp;
375 372 char *namep, *buf;
376 373 char shortname[SMB_SHORTNAMELEN];
377 374 vnode_t *vp;
378 - union {
379 - char *u_bufptr;
380 - edirent_t *u_edp;
381 - dirent64_t *u_dp;
382 - } u;
383 -#define bufptr u.u_bufptr
384 -#define edp u.u_edp
385 -#define dp u.u_dp
375 + char *bufptr;
376 + dirent64_t *dp;
377 + cred_t *cr = zone_kcred();
378 + int rc = ENOENT;
386 379
387 380 if (dnode == NULL || name == NULL || namebuf == NULL || buflen == 0)
388 381 return (EINVAL);
389 382
390 383 ASSERT(smb_maybe_mangled(name) == B_TRUE);
391 384
392 385 if (!smb_node_is_dir(dnode))
393 386 return (ENOTDIR);
394 387
395 388 vp = dnode->vp;
396 389 *namebuf = '\0';
397 - is_edp = vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS);
398 390
399 391 buf = kmem_alloc(SMB_UNMANGLE_BUFSIZE, KM_SLEEP);
400 - bufsize = SMB_UNMANGLE_BUFSIZE;
401 - offset = 0;
392 + bufptr = buf;
393 + bufsize = 0;
394 + offset = 0; // next entry offset
395 + eof = B_FALSE;
402 396
403 - while ((err = smb_vop_readdir(vp, offset, buf, &bufsize,
404 - &eof, flags, zone_kcred())) == 0) {
405 - if (bufsize == 0) {
406 - err = ENOENT;
407 - break;
397 + for (;;) {
398 + /*
399 + * Read some entries, if buffer empty or
400 + * we've scanned all of it. Flags zero
401 + * (no edirent, no ABE wanted here)
402 + */
403 + if (bufsize <= 0) {
404 + bufsize = SMB_UNMANGLE_BUFSIZE;
405 + rc = smb_vop_readdir(vp, offset, buf,
406 + &bufsize, &eof, 0, cr);
407 + if (rc != 0)
408 + break; /* error */
409 + if (bufsize == 0) {
410 + eof = B_TRUE;
411 + rc = ENOENT;
412 + break;
413 + }
414 + bufptr = buf;
408 415 }
416 + /* LINTED pointer alignment */
417 + dp = (dirent64_t *)bufptr;
409 418
410 - bufptr = buf;
411 - reclen = 0;
419 + /*
420 + * Partial records are not supposed to happen,
421 + * but let's be defensive. If this happens,
422 + * restart at the current offset.
423 + */
424 + bufptr += dp->d_reclen;
425 + bufsize -= dp->d_reclen;
426 + if (bufsize < 0)
427 + continue;
412 428
413 - while ((bufptr += reclen) < buf + bufsize) {
414 - if (is_edp) {
415 - reclen = edp->ed_reclen;
416 - offset = edp->ed_off;
417 - ino = edp->ed_ino;
418 - namep = edp->ed_name;
419 - } else {
420 - reclen = dp->d_reclen;
421 - offset = dp->d_off;
422 - ino = dp->d_ino;
423 - namep = dp->d_name;
424 - }
429 + offset = dp->d_off;
430 + ino = dp->d_ino;
431 + namep = dp->d_name;
425 432
426 - /* skip non utf8 filename */
427 - if (u8_validate(namep, strlen(namep), NULL,
428 - U8_VALIDATE_ENTIRE, &err) < 0)
429 - continue;
433 + /* skip non utf8 filename */
434 + if (u8_validate(namep, strlen(namep), NULL,
435 + U8_VALIDATE_ENTIRE, &err) < 0)
436 + continue;
430 437
431 - smb_mangle(namep, ino, shortname, SMB_SHORTNAMELEN);
432 -
433 - if (smb_strcasecmp(name, shortname, 0) == 0) {
434 - (void) strlcpy(namebuf, namep, buflen);
435 - kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
436 - return (0);
437 - }
438 - }
439 -
440 - if (eof) {
441 - err = ENOENT;
438 + smb_mangle(namep, ino, shortname, SMB_SHORTNAMELEN);
439 + if (smb_strcasecmp(name, shortname, 0) == 0) {
440 + (void) strlcpy(namebuf, namep, buflen);
441 + rc = 0;
442 442 break;
443 443 }
444 -
445 - bufsize = SMB_UNMANGLE_BUFSIZE;
446 444 }
447 445
448 446 kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
449 - return (err);
447 + return (rc);
450 448 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX