Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <sys/param.h>
     28 #include <sys/sunddi.h>
     29 #include <sys/errno.h>
     30 #include <smbsrv/string.h>
     31 #include <smbsrv/smb_vops.h>
     32 #include <smbsrv/smb_kproto.h>
     33 #include <smbsrv/smb_fsops.h>
     34 
     35 /*
     36  * Characters we don't allow in DOS file names.
     37  * If a filename contains any of these chars, it should get mangled.
     38  *
     39  * '.' is also an invalid DOS char but since it's a special
     40  * case it doesn't appear in the list.
     41  */
     42 static char *invalid_dos_chars =
     43 	"\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
     44 	"\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
     45 	" \"/\\:|<>*?";
     46 
     47 /*
     48  * According to MSKB article #142982, Windows deletes invalid chars and
     49  * spaces from file name in mangling process; and invalid chars include:
     50  * ."/\[]:;=,
     51  *
     52  * But some of these chars and some other chars (e.g. +) are replaced
     53  * with underscore (_). They are introduced here as special chars.
     54  */
     55 static char *special_chars = "[];=,+";
     56 
     57 #define	isinvalid(c)	(strchr(invalid_dos_chars, c) || (c & 0x80))
     58 
     59 static int smb_match_unknown(char *name, char *pattern);
     60 static boolean_t smb_is_reserved_dos_name(const char *name);
     61 
     62 /*
     63  * smb_match_name
     64  *
     65  * This function will mangle the "name" field and save the resulted
     66  * shortname to the "shortname" field and 8.3 name to "name83" field.
     67  * The three fields, "name", "shortname" and "name83" will then be
     68  * sent for pattern match with "pattern" field.
     69  *
     70  * The 0 is returned when the name is a reserved dos name, no match
     71  * for the pattern or any type of failure. The 1 is returned when
     72  * there is a match.
     73  */
     74 int
     75 smb_match_name(ino64_t fileid, char *name, char *pattern, boolean_t ignore_case)
     76 {
     77 	int rc = 0;
     78 	int force;
     79 	char name83[SMB_SHORTNAMELEN];
     80 	char shortname[SMB_SHORTNAMELEN];
     81 
     82 	/* Leading or trailing dots are disallowed */
     83 	if (smb_is_reserved_dos_name(name))
     84 		return (0);
     85 
     86 	for (force = 0; (force < 2 && rc == 0); force++) {
     87 		(void) smb_mangle_name(fileid, name, shortname, name83, force);
     88 
     89 		rc = smb_match_ci(pattern, name);
     90 
     91 		/* If no match, check for shortname (if any) */
     92 
     93 		if (rc == 0 && strchr(pattern, '~'))
     94 			if (*shortname != 0)
     95 				rc = smb_match_ci(pattern, shortname);
     96 
     97 		/*
     98 		 * Sigh... DOS Shells use short name
     99 		 * interchangeably with long case sensitive
    100 		 * names. So check that too...
    101 		 */
    102 		if ((rc == 0) && !ignore_case)
    103 			rc = smb_match83(pattern, name83);
    104 
    105 		/*
    106 		 * Still not found and potentially a premangled name...
    107 		 * Check to see if the butt-head programmer is
    108 		 * assuming that we mangle names in the same manner
    109 		 * as NT...
    110 		 */
    111 		if (rc == 0)
    112 			rc = smb_match_unknown(name, pattern);
    113 	}
    114 
    115 	return (rc);
    116 }
    117 
    118 /*
    119  * smb_match_unknown
    120  *
    121  * I couldn't figure out what the assumptions of this peice of
    122  * code about the format of pattern and name are and so how
    123  * it's trying to match them.  I just cleaned it up a little bit!
    124  *
    125  * If anybody could figure out what this is doing, please put
    126  * comment here and change the function's name!
    127  */
    128 static int
    129 smb_match_unknown(char *name, char *pattern)
    130 {
    131 	int rc;
    132 	char nc, pc;
    133 	char *np, *pp;
    134 
    135 	rc = 0;
    136 	if (smb_isstrupr(pattern) <= 0)
    137 		return (rc);
    138 
    139 	np = name;
    140 	pp = pattern;
    141 
    142 	pc = *pattern;
    143 	while ((nc = *np++) != 0) {
    144 		if (nc == ' ')
    145 			continue;
    146 
    147 		nc = smb_toupper(nc);
    148 		if ((pc = *pp++) != nc)
    149 			break;
    150 	}
    151 
    152 	if ((pc == '~') &&
    153 	    (pp != (pattern + 1)) &&
    154 	    ((pc = *pp++) != 0)) {
    155 		while (smb_isdigit(pc))
    156 			pc = *pp++;
    157 
    158 		if (pc == '.') {
    159 			while ((nc = *np++) != 0) {
    160 				if (nc == '.')
    161 					break;
    162 			}
    163 
    164 			while ((nc = *np++) != 0) {
    165 				nc = smb_toupper(nc);
    166 				if ((pc = *pp++) != nc)
    167 					break;
    168 			}
    169 		}
    170 
    171 		if (pc == 0)
    172 			rc = 1;
    173 	}
    174 
    175 	return (rc);
    176 }
    177 
    178 /*
    179  * Return true if name contains characters that are invalid in a file
    180  * name or it is a reserved DOS device name.  Otherwise, returns false.
    181  *
    182  * Control characters (values 0 - 31) and the following characters are
    183  * invalid:
    184  *	< > : " / \ | ? *
    185  */
    186 boolean_t
    187 smb_is_invalid_filename(const char *name)
    188 {
    189 	const char *p;
    190 
    191 	if ((p = strpbrk(name, invalid_dos_chars)) != NULL) {
    192 		if (*p != ' ')
    193 			return (B_TRUE);
    194 	}
    195 
    196 	return (smb_is_reserved_dos_name(name));
    197 }
    198 
    199 /*
    200  * smb_is_reserved_dos_name
    201  *
    202  * This function checks if the name is a reserved DOS device name.
    203  * The device name should not be followed immediately by an extension,
    204  * for example, NUL.txt.
    205  */
    206 static boolean_t
    207 smb_is_reserved_dos_name(const char *name)
    208 {
    209 	static char *cnames[] = { "CLOCK$", "COM1", "COM2", "COM3", "COM4",
    210 		"COM5", "COM6", "COM7", "COM8", "COM9", "CON" };
    211 	static char *lnames[] = { "LPT1", "LPT2", "LPT3", "LPT4", "LPT5",
    212 		"LPT6", "LPT7", "LPT8", "LPT9" };
    213 	static char *others[] = { "AUX", "NUL", "PRN" };
    214 	char	**reserved;
    215 	char	ch;
    216 	int	n_reserved;
    217 	int	len;
    218 	int	i;
    219 
    220 	ch = smb_toupper(*name);
    221 
    222 	switch (ch) {
    223 	case 'A':
    224 	case 'N':
    225 	case 'P':
    226 		reserved = others;
    227 		n_reserved = sizeof (others) / sizeof (others[0]);
    228 		break;
    229 	case 'C':
    230 		reserved = cnames;
    231 		n_reserved = sizeof (cnames) / sizeof (cnames[0]);
    232 		break;
    233 	case 'L':
    234 		reserved = lnames;
    235 		n_reserved = sizeof (lnames) / sizeof (lnames[0]);
    236 		break;
    237 	default:
    238 		return (B_FALSE);
    239 	}
    240 
    241 	for (i  = 0; i < n_reserved; ++i) {
    242 		len = strlen(reserved[i]);
    243 
    244 		if (smb_strcasecmp(reserved[i], name, len) == 0) {
    245 			ch = *(name + len);
    246 			if ((ch == '\0') || (ch == '.'))
    247 				return (B_TRUE);
    248 		}
    249 	}
    250 
    251 	return (B_FALSE);
    252 }
    253 
    254 /*
    255  * smb_needs_mangle
    256  *
    257  * Determines whether the given name needs to get mangled.
    258  *
    259  * Here are the (known) rules:
    260  *
    261  *	1st char is dot (.)
    262  *	name length > 12 chars
    263  *	# dots > 1
    264  *	# dots == 0 and length > 8
    265  *	# dots == 1 and name isn't 8.3
    266  *	contains illegal chars
    267  */
    268 int
    269 smb_needs_mangle(char *name, char **dot_pos)
    270 {
    271 	int len, ndots;
    272 	char *namep;
    273 	char *last_dot;
    274 
    275 	/*
    276 	 * Returning (1) for these cases forces consistency with how
    277 	 * these names are treated (smb_mangle_name() will produce an 8.3 name
    278 	 * for these)
    279 	 */
    280 	if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
    281 		return (1);
    282 
    283 	/* skip the leading dots (if any) */
    284 	for (namep = name; *namep == '.'; namep++)
    285 		;
    286 
    287 	len = ndots = 0;
    288 	last_dot = 0;
    289 	for (; *namep; namep++) {
    290 		len++;
    291 		if (*namep == '.') {
    292 			/* keep the position of last dot */
    293 			last_dot = namep;
    294 			ndots++;
    295 		}
    296 	}
    297 	*dot_pos = last_dot;
    298 
    299 	/* Windows mangles names like .a, .abc, or .abcd */
    300 	if (*name == '.')
    301 		return (1);
    302 
    303 	if (len > 12)
    304 		return (1);
    305 
    306 	switch (ndots) {
    307 	case 0:
    308 		/* no dot */
    309 		if (len > 8)
    310 			return (1);
    311 		break;
    312 
    313 	case 1:
    314 		/* just one dot */
    315 		/*LINTED E_PTR_DIFF_OVERFLOW*/
    316 		if (((last_dot - name) > 8) ||		/* name length > 8 */
    317 		    (strlen(last_dot + 1) > 3))		/* extention > 3 */
    318 			return (1);
    319 		break;
    320 
    321 	default:
    322 		/* more than one dot */
    323 		return (1);
    324 	}
    325 
    326 	for (namep = name; *namep; namep++) {
    327 		if (!smb_isascii(*namep) ||
    328 		    strchr(special_chars, *namep) ||
    329 		    strchr(invalid_dos_chars, *namep))
    330 			return (1);
    331 	}
    332 
    333 	return (0);
    334 }
    335 
    336 /*
    337  * smb_needs_shortname
    338  *
    339  * Determine whether a shortname should be generated for a file name that is
    340  * already in 8.3 format.
    341  *
    342  * Paramters:
    343  *   name - original file name
    344  *
    345  * Return:
    346  *   1 - Shortname is required to be generated.
    347  *   0 - No shortname needs to be generated.
    348  *
    349  * Note
    350  * =======
    351  * Windows NT server:       shortname is created only if either
    352  *                          the filename or extension portion of
    353  *                          a file is made up of mixed case.
    354  * Windows 2000 server:     shortname is not created regardless
    355  *                          of the case.
    356  * Windows 2003 server:     [Same as Windows NT server.]
    357  *
    358  * StorEdge will conform to the rule used by Windows NT/2003 server.
    359  *
    360  * For instance:
    361  *    File      | Create shortname?
    362  * ================================
    363  *  nf.txt      | N
    364  *  NF.TXT      | N
    365  *  NF.txt      | N
    366  *  nf          | N
    367  *  NF          | N
    368  *  nF.txt      | Y
    369  *  nf.TxT      | Y
    370  *  Nf          | Y
    371  *  nF          | Y
    372  *
    373  */
    374 static int
    375 smb_needs_shortname(char *name)
    376 {
    377 	char buf[9];
    378 	int len;
    379 	int create = 0;
    380 	const char *dot_pos = 0;
    381 
    382 	dot_pos = strrchr(name, '.');
    383 	/*LINTED E_PTRDIFF_OVERFLOW*/
    384 	len = (!dot_pos) ? strlen(name) : (dot_pos - name);
    385 	/* First, examine the name portion of the file */
    386 	if (len) {
    387 		(void) snprintf(buf, len + 1, "%s", name);
    388 		/* if the name contains both lower and upper cases */
    389 		if (smb_isstrupr(buf) == 0 && smb_isstrlwr(buf) == 0) {
    390 			/* create shortname */
    391 			create = 1;
    392 		} else 	if (dot_pos) {
    393 			/* Next, examine the extension portion of the file */
    394 			(void) snprintf(buf, sizeof (buf), "%s", dot_pos + 1);
    395 			/*
    396 			 * if the extension contains both lower and upper
    397 			 * cases
    398 			 */
    399 			if (smb_isstrupr(buf) == 0 && smb_isstrlwr(buf) == 0)
    400 				/* create shortname */
    401 				create = 1;
    402 		}
    403 	}
    404 
    405 	return (create);
    406 }
    407 
    408 /*
    409  * smb_mangle_char
    410  *
    411  * If given char is an invalid DOS character or it's not an
    412  * ascii char, it should be deleted from mangled and 8.3 name.
    413  *
    414  * If given char is one of special chars, it should be replaced
    415  * with '_'.
    416  *
    417  * Otherwise just make it upper case.
    418  */
    419 static unsigned char
    420 smb_mangle_char(unsigned char ch)
    421 {
    422 	if (isinvalid(ch))
    423 		return (0);
    424 
    425 	if (strchr(special_chars, ch))
    426 		return ('_');
    427 
    428 	return (smb_toupper(ch));
    429 }
    430 
    431 /*
    432  * smb_generate_mangle
    433  *
    434  * Generate a mangle string containing at least 2 characters and at most
    435  * (buflen - 1) characters.  Note: fid cannot be 0.
    436  *
    437  * Returns the number of chars in the generated mangle.
    438  */
    439 static int
    440 smb_generate_mangle(uint64_t fid, unsigned char *buf, size_t buflen)
    441 {
    442 	static char *base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    443 	unsigned char *p = buf;
    444 	int i;
    445 
    446 	if (fid == 0)
    447 		fid = (uint64_t)-1;
    448 
    449 	*p++ = '~';
    450 	for (i = 2; (i < buflen) && (fid > 0); fid /= 36, ++i)
    451 		*p++ = base36[fid % 36];
    452 	*p = '\0';
    453 
    454 	return (i - 1);
    455 }
    456 
    457 /*
    458  * smb_maybe_mangled_name
    459  *
    460  * Mangled names should be valid DOS file names: less than 12 characters
    461  * long, contain at least one tilde character and conform to an 8.3 name
    462  * format.
    463  *
    464  * Returns true if the name looks like a mangled name.
    465  */
    466 int
    467 smb_maybe_mangled_name(char *name)
    468 {
    469 	const char *p;
    470 	boolean_t has_tilde = B_FALSE;
    471 	int ndots = 0;
    472 	int i;
    473 
    474 	for (p = name, i = 0; (*p != '\0') && (i < SMB_NAME83_LEN); i++, p++) {
    475 		if ((strchr(special_chars, *p) != NULL) ||
    476 		    (strchr(invalid_dos_chars, *p) != NULL))
    477 			return (B_FALSE);
    478 
    479 		if (*p == '.') {
    480 			if ((++ndots) > 1)
    481 				return (B_FALSE);
    482 		}
    483 
    484 		if ((*p == '~') && (i < SMB_NAME83_BASELEN))
    485 			has_tilde = B_TRUE;
    486 
    487 		if (*p == '.' && !has_tilde)
    488 			return (B_FALSE);
    489 	}
    490 
    491 	return ((*p == 0) && has_tilde);
    492 }
    493 
    494 /*
    495  * smb_mangle_name
    496  *
    497  * Microsoft knowledge base article #142982 describes how Windows
    498  * generates 8.3 filenames from long file names. Some other details
    499  * can be found in article #114816.
    500  *
    501  * The function first checks to see whether the given name needs mangling.
    502  * If not, and the force parameter is not set, then no mangling is done,
    503  * but both the shortname (if needed) and the 8.3 name are produced and
    504  * returned.
    505  *
    506  * If the "force" parameter is set (as will be the case for case-insensitive
    507  * collisions), then the name will be mangled.
    508  *
    509  * Whenever mangling is needed, both the shortname and the 8.3 names are
    510  * produced and returned.
    511  *
    512  * For example, the xxx.xy in 8.3 format will be "xxx     .xy ".
    513  */
    514 
    515 int smb_mangle_name(
    516 	ino64_t fileid,		/* inode number to generate unique mangle */
    517 	char *name,		/* original file name */
    518 	char *shortname,	/* mangled name (if applicable) */
    519 	char *name83,		/* (mangled) name in 8.3 format */
    520 	int force)		/* force mangling even if mangling is not */
    521 				/* needed according to standard algorithm */
    522 {
    523 	int avail, len;
    524 	unsigned char ch;
    525 	unsigned char mangle_buf[SMB_NAME83_BASELEN];
    526 	unsigned char *namep;
    527 	unsigned char *manglep;
    528 	unsigned char *out_short;
    529 	unsigned char *out_83;
    530 	char *dot_pos = NULL;
    531 
    532 	/*
    533 	 * NOTE:
    534 	 * This function used to consider filename case
    535 	 * in order to mangle. I removed those checks.
    536 	 */
    537 
    538 	*shortname = *name83 = 0;
    539 
    540 	/* Allow dot and dot dot up front */
    541 	if (strcmp(name, ".") == 0) {
    542 		/* no shortname */
    543 		(void) strcpy(name83, ".       .   ");
    544 		return (1);
    545 	}
    546 
    547 	if (strcmp(name, "..") == 0) {
    548 		/* no shortname */
    549 		(void) strcpy(name83, "..      .   ");
    550 		return (1);
    551 	}
    552 
    553 	out_short = (unsigned char *)shortname;
    554 	out_83 = (unsigned char *)name83;
    555 
    556 	if ((smb_needs_mangle(name, &dot_pos) == 0) && (force == 0)) {
    557 		/* no mangle */
    558 
    559 		/* check if shortname is required or not */
    560 		if (smb_needs_shortname(name)) {
    561 			namep = (unsigned char *)name;
    562 			while (*namep)
    563 				*out_short++ = smb_toupper(*namep++);
    564 			*out_short = '\0';
    565 		}
    566 
    567 		out_83 = (unsigned char *)name83;
    568 		(void) strcpy((char *)out_83, "        .   ");
    569 		while (*name && *name != '.')
    570 			*out_83++ = smb_toupper(*name++);
    571 
    572 		if (*name == '.') {
    573 			/* copy extension */
    574 			name++;
    575 			out_83 = (unsigned char *)name83 + 9;
    576 			while (*name)
    577 				*out_83++ = smb_toupper(*name++);
    578 		}
    579 		return (1);
    580 	}
    581 
    582 	len = smb_generate_mangle(fileid, mangle_buf, SMB_NAME83_BASELEN);
    583 	avail = SMB_NAME83_BASELEN - len;
    584 
    585 	/*
    586 	 * generated mangle part has always less than 8 chars, so
    587 	 * use the chars before the first dot in filename
    588 	 * and try to generate a full 8 char name.
    589 	 */
    590 
    591 	/* skip the leading dots (if any) */
    592 	for (namep = (unsigned char *)name; *namep == '.'; namep++)
    593 		;
    594 
    595 	for (; avail && *namep && (*namep != '.'); namep++) {
    596 		ch = smb_mangle_char(*namep);
    597 		if (ch == 0)
    598 			continue;
    599 		*out_short++ = *out_83++ = ch;
    600 		avail--;
    601 	}
    602 
    603 	/* Copy in mangled part */
    604 	manglep = mangle_buf;
    605 
    606 	while (*manglep)
    607 		*out_short++ = *out_83++ = *(manglep++);
    608 
    609 	/* Pad any leftover in 8.3 name with spaces */
    610 	while (avail--)
    611 		*out_83++ = ' ';
    612 
    613 	/* Work on extension now */
    614 	avail = 3;
    615 	*out_83++ = '.';
    616 	if (dot_pos) {
    617 		namep = (unsigned char *)dot_pos + 1;
    618 		if (*namep != 0) {
    619 			*out_short++ = '.';
    620 			for (; avail && *namep; namep++) {
    621 				ch = smb_mangle_char(*namep);
    622 				if (ch == 0)
    623 					continue;
    624 
    625 				*out_short++ = *out_83++ = ch;
    626 				avail--;
    627 			}
    628 		}
    629 	}
    630 
    631 	while (avail--)
    632 		*out_83++ = ' ';
    633 
    634 	*out_short = *out_83 = '\0';
    635 
    636 	return (1);
    637 }
    638 
    639 /*
    640  * smb_unmangle_name
    641  *
    642  * Given a mangled name, try to find the real file name as it appears
    643  * in the directory entry.
    644  *
    645  * smb_unmangle_name should only be called on names for which
    646  * smb_maybe_mangled_name() is true
    647  *
    648  * File systems which support VFSFT_EDIRENT_FLAGS will return the
    649  * directory entries as a buffer of edirent_t structure. Others will
    650  * return a buffer of dirent64_t structures. A union is used for the
    651  * the pointer into the buffer (bufptr, edp and dp).
    652  * The ed_name/d_name is NULL terminated by the file system.
    653  *
    654  * Returns:
    655  *   0       - SUCCESS. Unmangled name is returned in namebuf.
    656  *   EINVAL  - a parameter was invalid.
    657  *   ENOTDIR - dnode is not a directory node.
    658  *   ENOENT  - an unmangled name could not be found.
    659  */
    660 #define	SMB_UNMANGLE_BUFSIZE	(4 * 1024)
    661 int
    662 smb_unmangle_name(smb_node_t *dnode, char *name, char *namebuf,
    663     int buflen, uint32_t flags)
    664 {
    665 	int		err, eof, bufsize, reclen;
    666 	uint64_t	offset;
    667 	ino64_t		ino;
    668 	boolean_t	is_edp;
    669 	char		*namep, *buf;
    670 	char		shortname[SMB_SHORTNAMELEN];
    671 	char		name83[SMB_SHORTNAMELEN];
    672 	vnode_t		*vp;
    673 	union {
    674 		char		*bufptr;
    675 		edirent_t	*edp;
    676 		dirent64_t	*dp;
    677 	} u;
    678 #define	bufptr	u.bufptr
    679 #define	edp		u.edp
    680 #define	dp		u.dp
    681 
    682 	if (dnode == NULL || name == NULL || namebuf == NULL || buflen == 0)
    683 		return (EINVAL);
    684 
    685 	ASSERT(smb_maybe_mangled_name(name) != 0);
    686 
    687 	vp = dnode->vp;
    688 	if (vp->v_type != VDIR)
    689 		return (ENOTDIR);
    690 
    691 	*namebuf = '\0';
    692 	is_edp = vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS);
    693 
    694 	buf = kmem_alloc(SMB_UNMANGLE_BUFSIZE, KM_SLEEP);
    695 	bufsize = SMB_UNMANGLE_BUFSIZE;
    696 	offset = 0;
    697 
    698 	while ((err = smb_vop_readdir(vp, offset, buf, &bufsize,
    699 	    &eof, flags, kcred)) == 0) {
    700 		if (bufsize == 0) {
    701 			err = ENOENT;
    702 			break;
    703 		}
    704 
    705 		bufptr = buf;
    706 		reclen = 0;
    707 
    708 		while ((bufptr += reclen) < buf + bufsize) {
    709 			if (is_edp) {
    710 				reclen = edp->ed_reclen;
    711 				offset = edp->ed_off;
    712 				ino = edp->ed_ino;
    713 				namep = edp->ed_name;
    714 			} else {
    715 				reclen = dp->d_reclen;
    716 				offset = dp->d_off;
    717 				ino = dp->d_ino;
    718 				namep = dp->d_name;
    719 			}
    720 
    721 			/* skip non utf8 filename */
    722 			if (u8_validate(namep, strlen(namep), NULL,
    723 			    U8_VALIDATE_ENTIRE, &err) < 0)
    724 				continue;
    725 
    726 			(void) smb_mangle_name(ino, namep,
    727 			    shortname, name83, 1);
    728 
    729 			if (smb_strcasecmp(name, shortname, 0) == 0) {
    730 				(void) strlcpy(namebuf, namep, buflen);
    731 				kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
    732 				return (0);
    733 			}
    734 		}
    735 
    736 		if (eof) {
    737 			err = ENOENT;
    738 			break;
    739 		}
    740 
    741 		bufsize = SMB_UNMANGLE_BUFSIZE;
    742 	}
    743 
    744 	kmem_free(buf, SMB_UNMANGLE_BUFSIZE);
    745 	return (err);
    746 }
    747