xref: /openbmc/linux/fs/ntfs/unistr.c (revision a1d312de)
1a1d312deSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project.
41da177e4SLinus Torvalds  *
5d4faf636SAnton Altaparmakov  * Copyright (c) 2001-2006 Anton Altaparmakov
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
8d4faf636SAnton Altaparmakov #include <linux/slab.h>
9d4faf636SAnton Altaparmakov 
101da177e4SLinus Torvalds #include "types.h"
111da177e4SLinus Torvalds #include "debug.h"
121da177e4SLinus Torvalds #include "ntfs.h"
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds /*
151da177e4SLinus Torvalds  * IMPORTANT
161da177e4SLinus Torvalds  * =========
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  * All these routines assume that the Unicode characters are in little endian
191da177e4SLinus Torvalds  * encoding inside the strings!!!
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds /*
231da177e4SLinus Torvalds  * This is used by the name collation functions to quickly determine what
241da177e4SLinus Torvalds  * characters are (in)valid.
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds static const u8 legal_ansi_char_array[0x40] = {
271da177e4SLinus Torvalds 	0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
281da177e4SLinus Torvalds 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
311da177e4SLinus Torvalds 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 	0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17,
341da177e4SLinus Torvalds 	0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00,
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds 	0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
371da177e4SLinus Torvalds 	0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18,
381da177e4SLinus Torvalds };
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /**
411da177e4SLinus Torvalds  * ntfs_are_names_equal - compare two Unicode names for equality
421da177e4SLinus Torvalds  * @s1:			name to compare to @s2
431da177e4SLinus Torvalds  * @s1_len:		length in Unicode characters of @s1
441da177e4SLinus Torvalds  * @s2:			name to compare to @s1
451da177e4SLinus Torvalds  * @s2_len:		length in Unicode characters of @s2
461da177e4SLinus Torvalds  * @ic:			ignore case bool
471da177e4SLinus Torvalds  * @upcase:		upcase table (only if @ic == IGNORE_CASE)
481da177e4SLinus Torvalds  * @upcase_size:	length in Unicode characters of @upcase (if present)
491da177e4SLinus Torvalds  *
50c49c3111SRichard Knutsson  * Compare the names @s1 and @s2 and return 'true' (1) if the names are
51c49c3111SRichard Knutsson  * identical, or 'false' (0) if they are not identical. If @ic is IGNORE_CASE,
521da177e4SLinus Torvalds  * the @upcase table is used to performa a case insensitive comparison.
531da177e4SLinus Torvalds  */
ntfs_are_names_equal(const ntfschar * s1,size_t s1_len,const ntfschar * s2,size_t s2_len,const IGNORE_CASE_BOOL ic,const ntfschar * upcase,const u32 upcase_size)54c49c3111SRichard Knutsson bool ntfs_are_names_equal(const ntfschar *s1, size_t s1_len,
551da177e4SLinus Torvalds 		const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
561da177e4SLinus Torvalds 		const ntfschar *upcase, const u32 upcase_size)
571da177e4SLinus Torvalds {
581da177e4SLinus Torvalds 	if (s1_len != s2_len)
59c49c3111SRichard Knutsson 		return false;
601da177e4SLinus Torvalds 	if (ic == CASE_SENSITIVE)
611da177e4SLinus Torvalds 		return !ntfs_ucsncmp(s1, s2, s1_len);
621da177e4SLinus Torvalds 	return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /**
661da177e4SLinus Torvalds  * ntfs_collate_names - collate two Unicode names
671da177e4SLinus Torvalds  * @name1:	first Unicode name to compare
681da177e4SLinus Torvalds  * @name2:	second Unicode name to compare
691da177e4SLinus Torvalds  * @err_val:	if @name1 contains an invalid character return this value
701da177e4SLinus Torvalds  * @ic:		either CASE_SENSITIVE or IGNORE_CASE
711da177e4SLinus Torvalds  * @upcase:	upcase table (ignored if @ic is CASE_SENSITIVE)
721da177e4SLinus Torvalds  * @upcase_len:	upcase table size (ignored if @ic is CASE_SENSITIVE)
731da177e4SLinus Torvalds  *
741da177e4SLinus Torvalds  * ntfs_collate_names collates two Unicode names and returns:
751da177e4SLinus Torvalds  *
761da177e4SLinus Torvalds  *  -1 if the first name collates before the second one,
771da177e4SLinus Torvalds  *   0 if the names match,
781da177e4SLinus Torvalds  *   1 if the second name collates before the first one, or
791da177e4SLinus Torvalds  * @err_val if an invalid character is found in @name1 during the comparison.
801da177e4SLinus Torvalds  *
811da177e4SLinus Torvalds  * The following characters are considered invalid: '"', '*', '<', '>' and '?'.
821da177e4SLinus Torvalds  */
ntfs_collate_names(const ntfschar * name1,const u32 name1_len,const ntfschar * name2,const u32 name2_len,const int err_val,const IGNORE_CASE_BOOL ic,const ntfschar * upcase,const u32 upcase_len)831da177e4SLinus Torvalds int ntfs_collate_names(const ntfschar *name1, const u32 name1_len,
841da177e4SLinus Torvalds 		const ntfschar *name2, const u32 name2_len,
851da177e4SLinus Torvalds 		const int err_val, const IGNORE_CASE_BOOL ic,
861da177e4SLinus Torvalds 		const ntfschar *upcase, const u32 upcase_len)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds 	u32 cnt, min_len;
891da177e4SLinus Torvalds 	u16 c1, c2;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	min_len = name1_len;
921da177e4SLinus Torvalds 	if (name1_len > name2_len)
931da177e4SLinus Torvalds 		min_len = name2_len;
941da177e4SLinus Torvalds 	for (cnt = 0; cnt < min_len; ++cnt) {
951da177e4SLinus Torvalds 		c1 = le16_to_cpu(*name1++);
961da177e4SLinus Torvalds 		c2 = le16_to_cpu(*name2++);
971da177e4SLinus Torvalds 		if (ic) {
981da177e4SLinus Torvalds 			if (c1 < upcase_len)
991da177e4SLinus Torvalds 				c1 = le16_to_cpu(upcase[c1]);
1001da177e4SLinus Torvalds 			if (c2 < upcase_len)
1011da177e4SLinus Torvalds 				c2 = le16_to_cpu(upcase[c2]);
1021da177e4SLinus Torvalds 		}
1031da177e4SLinus Torvalds 		if (c1 < 64 && legal_ansi_char_array[c1] & 8)
1041da177e4SLinus Torvalds 			return err_val;
1051da177e4SLinus Torvalds 		if (c1 < c2)
1061da177e4SLinus Torvalds 			return -1;
1071da177e4SLinus Torvalds 		if (c1 > c2)
1081da177e4SLinus Torvalds 			return 1;
1091da177e4SLinus Torvalds 	}
1101da177e4SLinus Torvalds 	if (name1_len < name2_len)
1111da177e4SLinus Torvalds 		return -1;
1121da177e4SLinus Torvalds 	if (name1_len == name2_len)
1131da177e4SLinus Torvalds 		return 0;
1141da177e4SLinus Torvalds 	/* name1_len > name2_len */
1151da177e4SLinus Torvalds 	c1 = le16_to_cpu(*name1);
1161da177e4SLinus Torvalds 	if (c1 < 64 && legal_ansi_char_array[c1] & 8)
1171da177e4SLinus Torvalds 		return err_val;
1181da177e4SLinus Torvalds 	return 1;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds /**
1221da177e4SLinus Torvalds  * ntfs_ucsncmp - compare two little endian Unicode strings
1231da177e4SLinus Torvalds  * @s1:		first string
1241da177e4SLinus Torvalds  * @s2:		second string
1251da177e4SLinus Torvalds  * @n:		maximum unicode characters to compare
1261da177e4SLinus Torvalds  *
1271da177e4SLinus Torvalds  * Compare the first @n characters of the Unicode strings @s1 and @s2,
1281da177e4SLinus Torvalds  * The strings in little endian format and appropriate le16_to_cpu()
1291da177e4SLinus Torvalds  * conversion is performed on non-little endian machines.
1301da177e4SLinus Torvalds  *
1311da177e4SLinus Torvalds  * The function returns an integer less than, equal to, or greater than zero
1321da177e4SLinus Torvalds  * if @s1 (or the first @n Unicode characters thereof) is found, respectively,
1331da177e4SLinus Torvalds  * to be less than, to match, or be greater than @s2.
1341da177e4SLinus Torvalds  */
ntfs_ucsncmp(const ntfschar * s1,const ntfschar * s2,size_t n)1351da177e4SLinus Torvalds int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds 	u16 c1, c2;
1381da177e4SLinus Torvalds 	size_t i;
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	for (i = 0; i < n; ++i) {
1411da177e4SLinus Torvalds 		c1 = le16_to_cpu(s1[i]);
1421da177e4SLinus Torvalds 		c2 = le16_to_cpu(s2[i]);
1431da177e4SLinus Torvalds 		if (c1 < c2)
1441da177e4SLinus Torvalds 			return -1;
1451da177e4SLinus Torvalds 		if (c1 > c2)
1461da177e4SLinus Torvalds 			return 1;
1471da177e4SLinus Torvalds 		if (!c1)
1481da177e4SLinus Torvalds 			break;
1491da177e4SLinus Torvalds 	}
1501da177e4SLinus Torvalds 	return 0;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds /**
1541da177e4SLinus Torvalds  * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case
1551da177e4SLinus Torvalds  * @s1:			first string
1561da177e4SLinus Torvalds  * @s2:			second string
1571da177e4SLinus Torvalds  * @n:			maximum unicode characters to compare
1581da177e4SLinus Torvalds  * @upcase:		upcase table
1591da177e4SLinus Torvalds  * @upcase_size:	upcase table size in Unicode characters
1601da177e4SLinus Torvalds  *
1611da177e4SLinus Torvalds  * Compare the first @n characters of the Unicode strings @s1 and @s2,
1621da177e4SLinus Torvalds  * ignoring case. The strings in little endian format and appropriate
1631da177e4SLinus Torvalds  * le16_to_cpu() conversion is performed on non-little endian machines.
1641da177e4SLinus Torvalds  *
1651da177e4SLinus Torvalds  * Each character is uppercased using the @upcase table before the comparison.
1661da177e4SLinus Torvalds  *
1671da177e4SLinus Torvalds  * The function returns an integer less than, equal to, or greater than zero
1681da177e4SLinus Torvalds  * if @s1 (or the first @n Unicode characters thereof) is found, respectively,
1691da177e4SLinus Torvalds  * to be less than, to match, or be greater than @s2.
1701da177e4SLinus Torvalds  */
ntfs_ucsncasecmp(const ntfschar * s1,const ntfschar * s2,size_t n,const ntfschar * upcase,const u32 upcase_size)1711da177e4SLinus Torvalds int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
1721da177e4SLinus Torvalds 		const ntfschar *upcase, const u32 upcase_size)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	size_t i;
1751da177e4SLinus Torvalds 	u16 c1, c2;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	for (i = 0; i < n; ++i) {
1781da177e4SLinus Torvalds 		if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
1791da177e4SLinus Torvalds 			c1 = le16_to_cpu(upcase[c1]);
1801da177e4SLinus Torvalds 		if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
1811da177e4SLinus Torvalds 			c2 = le16_to_cpu(upcase[c2]);
1821da177e4SLinus Torvalds 		if (c1 < c2)
1831da177e4SLinus Torvalds 			return -1;
1841da177e4SLinus Torvalds 		if (c1 > c2)
1851da177e4SLinus Torvalds 			return 1;
1861da177e4SLinus Torvalds 		if (!c1)
1871da177e4SLinus Torvalds 			break;
1881da177e4SLinus Torvalds 	}
1891da177e4SLinus Torvalds 	return 0;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds 
ntfs_upcase_name(ntfschar * name,u32 name_len,const ntfschar * upcase,const u32 upcase_len)1921da177e4SLinus Torvalds void ntfs_upcase_name(ntfschar *name, u32 name_len, const ntfschar *upcase,
1931da177e4SLinus Torvalds 		const u32 upcase_len)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds 	u32 i;
1961da177e4SLinus Torvalds 	u16 u;
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	for (i = 0; i < name_len; i++)
1991da177e4SLinus Torvalds 		if ((u = le16_to_cpu(name[i])) < upcase_len)
2001da177e4SLinus Torvalds 			name[i] = upcase[u];
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
ntfs_file_upcase_value(FILE_NAME_ATTR * file_name_attr,const ntfschar * upcase,const u32 upcase_len)2031da177e4SLinus Torvalds void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr,
2041da177e4SLinus Torvalds 		const ntfschar *upcase, const u32 upcase_len)
2051da177e4SLinus Torvalds {
2061da177e4SLinus Torvalds 	ntfs_upcase_name((ntfschar*)&file_name_attr->file_name,
2071da177e4SLinus Torvalds 			file_name_attr->file_name_length, upcase, upcase_len);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
ntfs_file_compare_values(FILE_NAME_ATTR * file_name_attr1,FILE_NAME_ATTR * file_name_attr2,const int err_val,const IGNORE_CASE_BOOL ic,const ntfschar * upcase,const u32 upcase_len)2101da177e4SLinus Torvalds int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1,
2111da177e4SLinus Torvalds 		FILE_NAME_ATTR *file_name_attr2,
2121da177e4SLinus Torvalds 		const int err_val, const IGNORE_CASE_BOOL ic,
2131da177e4SLinus Torvalds 		const ntfschar *upcase, const u32 upcase_len)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds 	return ntfs_collate_names((ntfschar*)&file_name_attr1->file_name,
2161da177e4SLinus Torvalds 			file_name_attr1->file_name_length,
2171da177e4SLinus Torvalds 			(ntfschar*)&file_name_attr2->file_name,
2181da177e4SLinus Torvalds 			file_name_attr2->file_name_length,
2191da177e4SLinus Torvalds 			err_val, ic, upcase, upcase_len);
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds /**
2231da177e4SLinus Torvalds  * ntfs_nlstoucs - convert NLS string to little endian Unicode string
2241da177e4SLinus Torvalds  * @vol:	ntfs volume which we are working with
2251da177e4SLinus Torvalds  * @ins:	input NLS string buffer
2261da177e4SLinus Torvalds  * @ins_len:	length of input string in bytes
2271da177e4SLinus Torvalds  * @outs:	on return contains the allocated output Unicode string buffer
2281da177e4SLinus Torvalds  *
2291da177e4SLinus Torvalds  * Convert the input string @ins, which is in whatever format the loaded NLS
2301da177e4SLinus Torvalds  * map dictates, into a little endian, 2-byte Unicode string.
2311da177e4SLinus Torvalds  *
2321da177e4SLinus Torvalds  * This function allocates the string and the caller is responsible for
233d4faf636SAnton Altaparmakov  * calling kmem_cache_free(ntfs_name_cache, *@outs); when finished with it.
2341da177e4SLinus Torvalds  *
2351da177e4SLinus Torvalds  * On success the function returns the number of Unicode characters written to
2361da177e4SLinus Torvalds  * the output string *@outs (>= 0), not counting the terminating Unicode NULL
2371da177e4SLinus Torvalds  * character. *@outs is set to the allocated output string buffer.
2381da177e4SLinus Torvalds  *
2391da177e4SLinus Torvalds  * On error, a negative number corresponding to the error code is returned. In
2401da177e4SLinus Torvalds  * that case the output string is not allocated. Both *@outs and *@outs_len
2411da177e4SLinus Torvalds  * are then undefined.
2421da177e4SLinus Torvalds  *
2431da177e4SLinus Torvalds  * This might look a bit odd due to fast path optimization...
2441da177e4SLinus Torvalds  */
ntfs_nlstoucs(const ntfs_volume * vol,const char * ins,const int ins_len,ntfschar ** outs)2451da177e4SLinus Torvalds int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
2461da177e4SLinus Torvalds 		const int ins_len, ntfschar **outs)
2471da177e4SLinus Torvalds {
2481da177e4SLinus Torvalds 	struct nls_table *nls = vol->nls_map;
2491da177e4SLinus Torvalds 	ntfschar *ucs;
2501da177e4SLinus Torvalds 	wchar_t wc;
2511da177e4SLinus Torvalds 	int i, o, wc_len;
2521da177e4SLinus Torvalds 
253d4faf636SAnton Altaparmakov 	/* We do not trust outside sources. */
254d4faf636SAnton Altaparmakov 	if (likely(ins)) {
255e6b4f8daSChristoph Lameter 		ucs = kmem_cache_alloc(ntfs_name_cache, GFP_NOFS);
256d4faf636SAnton Altaparmakov 		if (likely(ucs)) {
2571da177e4SLinus Torvalds 			for (i = o = 0; i < ins_len; i += wc_len) {
2581da177e4SLinus Torvalds 				wc_len = nls->char2uni(ins + i, ins_len - i,
2591da177e4SLinus Torvalds 						&wc);
260d4faf636SAnton Altaparmakov 				if (likely(wc_len >= 0 &&
261d4faf636SAnton Altaparmakov 						o < NTFS_MAX_NAME_LEN)) {
262d4faf636SAnton Altaparmakov 					if (likely(wc)) {
2631da177e4SLinus Torvalds 						ucs[o++] = cpu_to_le16(wc);
2641da177e4SLinus Torvalds 						continue;
265d4faf636SAnton Altaparmakov 					} /* else if (!wc) */
2661da177e4SLinus Torvalds 					break;
267d4faf636SAnton Altaparmakov 				} /* else if (wc_len < 0 ||
268d4faf636SAnton Altaparmakov 						o >= NTFS_MAX_NAME_LEN) */
269d4faf636SAnton Altaparmakov 				goto name_err;
2701da177e4SLinus Torvalds 			}
2711da177e4SLinus Torvalds 			ucs[o] = 0;
2721da177e4SLinus Torvalds 			*outs = ucs;
2731da177e4SLinus Torvalds 			return o;
274d4faf636SAnton Altaparmakov 		} /* else if (!ucs) */
275d4faf636SAnton Altaparmakov 		ntfs_error(vol->sb, "Failed to allocate buffer for converted "
276d4faf636SAnton Altaparmakov 				"name from ntfs_name_cache.");
2771da177e4SLinus Torvalds 		return -ENOMEM;
278d4faf636SAnton Altaparmakov 	} /* else if (!ins) */
279d4faf636SAnton Altaparmakov 	ntfs_error(vol->sb, "Received NULL pointer.");
2801da177e4SLinus Torvalds 	return -EINVAL;
281d4faf636SAnton Altaparmakov name_err:
2821da177e4SLinus Torvalds 	kmem_cache_free(ntfs_name_cache, ucs);
283d4faf636SAnton Altaparmakov 	if (wc_len < 0) {
284d4faf636SAnton Altaparmakov 		ntfs_error(vol->sb, "Name using character set %s contains "
285d4faf636SAnton Altaparmakov 				"characters that cannot be converted to "
286d4faf636SAnton Altaparmakov 				"Unicode.", nls->charset);
287d4faf636SAnton Altaparmakov 		i = -EILSEQ;
288d4faf636SAnton Altaparmakov 	} else /* if (o >= NTFS_MAX_NAME_LEN) */ {
289d4faf636SAnton Altaparmakov 		ntfs_error(vol->sb, "Name is too long (maximum length for a "
290d4faf636SAnton Altaparmakov 				"name on NTFS is %d Unicode characters.",
291d4faf636SAnton Altaparmakov 				NTFS_MAX_NAME_LEN);
292d4faf636SAnton Altaparmakov 		i = -ENAMETOOLONG;
293d4faf636SAnton Altaparmakov 	}
294d4faf636SAnton Altaparmakov 	return i;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds /**
2981da177e4SLinus Torvalds  * ntfs_ucstonls - convert little endian Unicode string to NLS string
2991da177e4SLinus Torvalds  * @vol:	ntfs volume which we are working with
3001da177e4SLinus Torvalds  * @ins:	input Unicode string buffer
3011da177e4SLinus Torvalds  * @ins_len:	length of input string in Unicode characters
3021da177e4SLinus Torvalds  * @outs:	on return contains the (allocated) output NLS string buffer
3031da177e4SLinus Torvalds  * @outs_len:	length of output string buffer in bytes
3041da177e4SLinus Torvalds  *
3051da177e4SLinus Torvalds  * Convert the input little endian, 2-byte Unicode string @ins, of length
3061da177e4SLinus Torvalds  * @ins_len into the string format dictated by the loaded NLS.
3071da177e4SLinus Torvalds  *
3081da177e4SLinus Torvalds  * If *@outs is NULL, this function allocates the string and the caller is
3091da177e4SLinus Torvalds  * responsible for calling kfree(*@outs); when finished with it. In this case
3101da177e4SLinus Torvalds  * @outs_len is ignored and can be 0.
3111da177e4SLinus Torvalds  *
3121da177e4SLinus Torvalds  * On success the function returns the number of bytes written to the output
3131da177e4SLinus Torvalds  * string *@outs (>= 0), not counting the terminating NULL byte. If the output
3141da177e4SLinus Torvalds  * string buffer was allocated, *@outs is set to it.
3151da177e4SLinus Torvalds  *
3161da177e4SLinus Torvalds  * On error, a negative number corresponding to the error code is returned. In
3171da177e4SLinus Torvalds  * that case the output string is not allocated. The contents of *@outs are
3181da177e4SLinus Torvalds  * then undefined.
3191da177e4SLinus Torvalds  *
3201da177e4SLinus Torvalds  * This might look a bit odd due to fast path optimization...
3211da177e4SLinus Torvalds  */
ntfs_ucstonls(const ntfs_volume * vol,const ntfschar * ins,const int ins_len,unsigned char ** outs,int outs_len)3221da177e4SLinus Torvalds int ntfs_ucstonls(const ntfs_volume *vol, const ntfschar *ins,
3231da177e4SLinus Torvalds 		const int ins_len, unsigned char **outs, int outs_len)
3241da177e4SLinus Torvalds {
3251da177e4SLinus Torvalds 	struct nls_table *nls = vol->nls_map;
3261da177e4SLinus Torvalds 	unsigned char *ns;
3271da177e4SLinus Torvalds 	int i, o, ns_len, wc;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	/* We don't trust outside sources. */
3301da177e4SLinus Torvalds 	if (ins) {
3311da177e4SLinus Torvalds 		ns = *outs;
3321da177e4SLinus Torvalds 		ns_len = outs_len;
3331da177e4SLinus Torvalds 		if (ns && !ns_len) {
3341da177e4SLinus Torvalds 			wc = -ENAMETOOLONG;
3351da177e4SLinus Torvalds 			goto conversion_err;
3361da177e4SLinus Torvalds 		}
3371da177e4SLinus Torvalds 		if (!ns) {
3381da177e4SLinus Torvalds 			ns_len = ins_len * NLS_MAX_CHARSET_SIZE;
339f52720caSPanagiotis Issaris 			ns = kmalloc(ns_len + 1, GFP_NOFS);
3401da177e4SLinus Torvalds 			if (!ns)
3411da177e4SLinus Torvalds 				goto mem_err_out;
3421da177e4SLinus Torvalds 		}
3431da177e4SLinus Torvalds 		for (i = o = 0; i < ins_len; i++) {
3441da177e4SLinus Torvalds retry:			wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o,
3451da177e4SLinus Torvalds 					ns_len - o);
3461da177e4SLinus Torvalds 			if (wc > 0) {
3471da177e4SLinus Torvalds 				o += wc;
3481da177e4SLinus Torvalds 				continue;
3491da177e4SLinus Torvalds 			} else if (!wc)
3501da177e4SLinus Torvalds 				break;
3511da177e4SLinus Torvalds 			else if (wc == -ENAMETOOLONG && ns != *outs) {
3521da177e4SLinus Torvalds 				unsigned char *tc;
3531da177e4SLinus Torvalds 				/* Grow in multiples of 64 bytes. */
354f52720caSPanagiotis Issaris 				tc = kmalloc((ns_len + 64) &
3551da177e4SLinus Torvalds 						~63, GFP_NOFS);
3561da177e4SLinus Torvalds 				if (tc) {
3571da177e4SLinus Torvalds 					memcpy(tc, ns, ns_len);
3581da177e4SLinus Torvalds 					ns_len = ((ns_len + 64) & ~63) - 1;
3591da177e4SLinus Torvalds 					kfree(ns);
3601da177e4SLinus Torvalds 					ns = tc;
3611da177e4SLinus Torvalds 					goto retry;
3621da177e4SLinus Torvalds 				} /* No memory so goto conversion_error; */
3631da177e4SLinus Torvalds 			} /* wc < 0, real error. */
3641da177e4SLinus Torvalds 			goto conversion_err;
3651da177e4SLinus Torvalds 		}
3661da177e4SLinus Torvalds 		ns[o] = 0;
3671da177e4SLinus Torvalds 		*outs = ns;
3681da177e4SLinus Torvalds 		return o;
3691da177e4SLinus Torvalds 	} /* else (!ins) */
3701da177e4SLinus Torvalds 	ntfs_error(vol->sb, "Received NULL pointer.");
3711da177e4SLinus Torvalds 	return -EINVAL;
3721da177e4SLinus Torvalds conversion_err:
3731da177e4SLinus Torvalds 	ntfs_error(vol->sb, "Unicode name contains characters that cannot be "
374f94ad38eSAnton Altaparmakov 			"converted to character set %s.  You might want to "
375f94ad38eSAnton Altaparmakov 			"try to use the mount option nls=utf8.", nls->charset);
3761da177e4SLinus Torvalds 	if (ns != *outs)
3771da177e4SLinus Torvalds 		kfree(ns);
3781da177e4SLinus Torvalds 	if (wc != -ENAMETOOLONG)
3791da177e4SLinus Torvalds 		wc = -EILSEQ;
3801da177e4SLinus Torvalds 	return wc;
3811da177e4SLinus Torvalds mem_err_out:
3821da177e4SLinus Torvalds 	ntfs_error(vol->sb, "Failed to allocate name!");
3831da177e4SLinus Torvalds 	return -ENOMEM;
3841da177e4SLinus Torvalds }
385