3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/sunddi.h>
29 #include <sys/errno.h>
30 #include <sys/extdirent.h>
31 #include <smbsrv/string.h>
32 #include <smbsrv/smb_vops.h>
33 #include <smbsrv/smb_kproto.h>
34 #include <smbsrv/smb_fsops.h>
35
36 /*
37 * Characters we don't allow in DOS file names.
38 * If a filename contains any of these chars, it should get mangled.
39 *
40 * '.' is also an invalid DOS char but since it's a special
41 * case it doesn't appear in the list.
42 */
43 static const char invalid_dos_chars[] =
334 *pbuf++ = '.';
335 for (i = 0; (i < SMB_NAME83_EXTLEN) && (*p != '\0'); ++i, ++p) {
336 if ((c = smb_mangle_char(*p)) == -1)
337 continue;
338 *pbuf++ = c;
339 }
340 }
341
342 *pbuf = '\0';
343 }
344
345 /*
346 * smb_unmangle
347 *
348 * Given a mangled name, try to find the real file name as it appears
349 * in the directory entry.
350 *
351 * smb_unmangle should only be called on names for which
352 * smb_maybe_mangled() is true
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.
359 *
360 * Returns:
361 * 0 - SUCCESS. Unmangled name is returned in namebuf.
362 * EINVAL - a parameter was invalid.
363 * ENOTDIR - dnode is not a directory node.
364 * ENOENT - an unmangled name could not be found.
365 */
366 #define SMB_UNMANGLE_BUFSIZE (4 * 1024)
367 int
368 smb_unmangle(smb_node_t *dnode, char *name, char *namebuf,
369 int buflen, uint32_t flags)
370 {
371 int err, eof, bufsize, reclen;
372 uint64_t offset;
373 ino64_t ino;
374 boolean_t is_edp;
375 char *namep, *buf;
376 char shortname[SMB_SHORTNAMELEN];
377 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
386
387 if (dnode == NULL || name == NULL || namebuf == NULL || buflen == 0)
388 return (EINVAL);
389
390 ASSERT(smb_maybe_mangled(name) == B_TRUE);
391
392 if (!smb_node_is_dir(dnode))
393 return (ENOTDIR);
394
395 vp = dnode->vp;
396 *namebuf = '\0';
397 is_edp = vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS);
398
399 buf = kmem_alloc(SMB_UNMANGLE_BUFSIZE, KM_SLEEP);
400 bufsize = SMB_UNMANGLE_BUFSIZE;
401 offset = 0;
402
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;
408 }
409
410 bufptr = buf;
411 reclen = 0;
412
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 }
425
426 /* skip non utf8 filename */
427 if (u8_validate(namep, strlen(namep), NULL,
428 U8_VALIDATE_ENTIRE, &err) < 0)
429 continue;
430
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;
442 break;
443 }
444
445 bufsize = SMB_UNMANGLE_BUFSIZE;
446 }
447
448 kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
449 return (err);
450 }
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/sunddi.h>
29 #include <sys/errno.h>
30 #include <sys/extdirent.h>
31 #include <smbsrv/string.h>
32 #include <smbsrv/smb_vops.h>
33 #include <smbsrv/smb_kproto.h>
34 #include <smbsrv/smb_fsops.h>
35
36 /*
37 * Characters we don't allow in DOS file names.
38 * If a filename contains any of these chars, it should get mangled.
39 *
40 * '.' is also an invalid DOS char but since it's a special
41 * case it doesn't appear in the list.
42 */
43 static const char invalid_dos_chars[] =
334 *pbuf++ = '.';
335 for (i = 0; (i < SMB_NAME83_EXTLEN) && (*p != '\0'); ++i, ++p) {
336 if ((c = smb_mangle_char(*p)) == -1)
337 continue;
338 *pbuf++ = c;
339 }
340 }
341
342 *pbuf = '\0';
343 }
344
345 /*
346 * smb_unmangle
347 *
348 * Given a mangled name, try to find the real file name as it appears
349 * in the directory entry.
350 *
351 * smb_unmangle should only be called on names for which
352 * smb_maybe_mangled() is true
353 *
354 * The flags arg is no longer used, but retained just to avoid
355 * changing the many callers of this function.
356 *
357 * Returns:
358 * 0 - SUCCESS. Unmangled name is returned in namebuf.
359 * EINVAL - a parameter was invalid.
360 * ENOTDIR - dnode is not a directory node.
361 * ENOENT - an unmangled name could not be found.
362 */
363 #define SMB_UNMANGLE_BUFSIZE (4 * 1024)
364 int
365 smb_unmangle(smb_node_t *dnode, char *name, char *namebuf,
366 int buflen, uint32_t flags)
367 {
368 _NOTE(ARGUNUSED(flags)) // avoid changing all callers
369 int err, eof, bufsize;
370 uint64_t offset;
371 ino64_t ino;
372 char *namep, *buf;
373 char shortname[SMB_SHORTNAMELEN];
374 vnode_t *vp;
375 char *bufptr;
376 dirent64_t *dp;
377 cred_t *cr = zone_kcred();
378 int rc = ENOENT;
379
380 if (dnode == NULL || name == NULL || namebuf == NULL || buflen == 0)
381 return (EINVAL);
382
383 ASSERT(smb_maybe_mangled(name) == B_TRUE);
384
385 if (!smb_node_is_dir(dnode))
386 return (ENOTDIR);
387
388 vp = dnode->vp;
389 *namebuf = '\0';
390
391 buf = kmem_alloc(SMB_UNMANGLE_BUFSIZE, KM_SLEEP);
392 bufptr = buf;
393 bufsize = 0;
394 offset = 0; // next entry offset
395 eof = B_FALSE;
396
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;
415 }
416 /* LINTED pointer alignment */
417 dp = (dirent64_t *)bufptr;
418
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;
428
429 offset = dp->d_off;
430 ino = dp->d_ino;
431 namep = dp->d_name;
432
433 /* skip non utf8 filename */
434 if (u8_validate(namep, strlen(namep), NULL,
435 U8_VALIDATE_ENTIRE, &err) < 0)
436 continue;
437
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 break;
443 }
444 }
445
446 kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
447 return (rc);
448 }
|