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 }
 |