18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2001-2006 Anton Altaparmakov 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "types.h" 118c2ecf20Sopenharmony_ci#include "debug.h" 128c2ecf20Sopenharmony_ci#include "ntfs.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * IMPORTANT 168c2ecf20Sopenharmony_ci * ========= 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * All these routines assume that the Unicode characters are in little endian 198c2ecf20Sopenharmony_ci * encoding inside the strings!!! 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * This is used by the name collation functions to quickly determine what 248c2ecf20Sopenharmony_ci * characters are (in)valid. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cistatic const u8 legal_ansi_char_array[0x40] = { 278c2ecf20Sopenharmony_ci 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 288c2ecf20Sopenharmony_ci 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 318c2ecf20Sopenharmony_ci 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 348c2ecf20Sopenharmony_ci 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00, 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 378c2ecf20Sopenharmony_ci 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * ntfs_are_names_equal - compare two Unicode names for equality 428c2ecf20Sopenharmony_ci * @s1: name to compare to @s2 438c2ecf20Sopenharmony_ci * @s1_len: length in Unicode characters of @s1 448c2ecf20Sopenharmony_ci * @s2: name to compare to @s1 458c2ecf20Sopenharmony_ci * @s2_len: length in Unicode characters of @s2 468c2ecf20Sopenharmony_ci * @ic: ignore case bool 478c2ecf20Sopenharmony_ci * @upcase: upcase table (only if @ic == IGNORE_CASE) 488c2ecf20Sopenharmony_ci * @upcase_size: length in Unicode characters of @upcase (if present) 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Compare the names @s1 and @s2 and return 'true' (1) if the names are 518c2ecf20Sopenharmony_ci * identical, or 'false' (0) if they are not identical. If @ic is IGNORE_CASE, 528c2ecf20Sopenharmony_ci * the @upcase table is used to performa a case insensitive comparison. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cibool ntfs_are_names_equal(const ntfschar *s1, size_t s1_len, 558c2ecf20Sopenharmony_ci const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic, 568c2ecf20Sopenharmony_ci const ntfschar *upcase, const u32 upcase_size) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (s1_len != s2_len) 598c2ecf20Sopenharmony_ci return false; 608c2ecf20Sopenharmony_ci if (ic == CASE_SENSITIVE) 618c2ecf20Sopenharmony_ci return !ntfs_ucsncmp(s1, s2, s1_len); 628c2ecf20Sopenharmony_ci return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * ntfs_collate_names - collate two Unicode names 678c2ecf20Sopenharmony_ci * @name1: first Unicode name to compare 688c2ecf20Sopenharmony_ci * @name2: second Unicode name to compare 698c2ecf20Sopenharmony_ci * @err_val: if @name1 contains an invalid character return this value 708c2ecf20Sopenharmony_ci * @ic: either CASE_SENSITIVE or IGNORE_CASE 718c2ecf20Sopenharmony_ci * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) 728c2ecf20Sopenharmony_ci * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * ntfs_collate_names collates two Unicode names and returns: 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * -1 if the first name collates before the second one, 778c2ecf20Sopenharmony_ci * 0 if the names match, 788c2ecf20Sopenharmony_ci * 1 if the second name collates before the first one, or 798c2ecf20Sopenharmony_ci * @err_val if an invalid character is found in @name1 during the comparison. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * The following characters are considered invalid: '"', '*', '<', '>' and '?'. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ciint ntfs_collate_names(const ntfschar *name1, const u32 name1_len, 848c2ecf20Sopenharmony_ci const ntfschar *name2, const u32 name2_len, 858c2ecf20Sopenharmony_ci const int err_val, const IGNORE_CASE_BOOL ic, 868c2ecf20Sopenharmony_ci const ntfschar *upcase, const u32 upcase_len) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u32 cnt, min_len; 898c2ecf20Sopenharmony_ci u16 c1, c2; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci min_len = name1_len; 928c2ecf20Sopenharmony_ci if (name1_len > name2_len) 938c2ecf20Sopenharmony_ci min_len = name2_len; 948c2ecf20Sopenharmony_ci for (cnt = 0; cnt < min_len; ++cnt) { 958c2ecf20Sopenharmony_ci c1 = le16_to_cpu(*name1++); 968c2ecf20Sopenharmony_ci c2 = le16_to_cpu(*name2++); 978c2ecf20Sopenharmony_ci if (ic) { 988c2ecf20Sopenharmony_ci if (c1 < upcase_len) 998c2ecf20Sopenharmony_ci c1 = le16_to_cpu(upcase[c1]); 1008c2ecf20Sopenharmony_ci if (c2 < upcase_len) 1018c2ecf20Sopenharmony_ci c2 = le16_to_cpu(upcase[c2]); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci if (c1 < 64 && legal_ansi_char_array[c1] & 8) 1048c2ecf20Sopenharmony_ci return err_val; 1058c2ecf20Sopenharmony_ci if (c1 < c2) 1068c2ecf20Sopenharmony_ci return -1; 1078c2ecf20Sopenharmony_ci if (c1 > c2) 1088c2ecf20Sopenharmony_ci return 1; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci if (name1_len < name2_len) 1118c2ecf20Sopenharmony_ci return -1; 1128c2ecf20Sopenharmony_ci if (name1_len == name2_len) 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci /* name1_len > name2_len */ 1158c2ecf20Sopenharmony_ci c1 = le16_to_cpu(*name1); 1168c2ecf20Sopenharmony_ci if (c1 < 64 && legal_ansi_char_array[c1] & 8) 1178c2ecf20Sopenharmony_ci return err_val; 1188c2ecf20Sopenharmony_ci return 1; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * ntfs_ucsncmp - compare two little endian Unicode strings 1238c2ecf20Sopenharmony_ci * @s1: first string 1248c2ecf20Sopenharmony_ci * @s2: second string 1258c2ecf20Sopenharmony_ci * @n: maximum unicode characters to compare 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * Compare the first @n characters of the Unicode strings @s1 and @s2, 1288c2ecf20Sopenharmony_ci * The strings in little endian format and appropriate le16_to_cpu() 1298c2ecf20Sopenharmony_ci * conversion is performed on non-little endian machines. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * The function returns an integer less than, equal to, or greater than zero 1328c2ecf20Sopenharmony_ci * if @s1 (or the first @n Unicode characters thereof) is found, respectively, 1338c2ecf20Sopenharmony_ci * to be less than, to match, or be greater than @s2. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ciint ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci u16 c1, c2; 1388c2ecf20Sopenharmony_ci size_t i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) { 1418c2ecf20Sopenharmony_ci c1 = le16_to_cpu(s1[i]); 1428c2ecf20Sopenharmony_ci c2 = le16_to_cpu(s2[i]); 1438c2ecf20Sopenharmony_ci if (c1 < c2) 1448c2ecf20Sopenharmony_ci return -1; 1458c2ecf20Sopenharmony_ci if (c1 > c2) 1468c2ecf20Sopenharmony_ci return 1; 1478c2ecf20Sopenharmony_ci if (!c1) 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case 1558c2ecf20Sopenharmony_ci * @s1: first string 1568c2ecf20Sopenharmony_ci * @s2: second string 1578c2ecf20Sopenharmony_ci * @n: maximum unicode characters to compare 1588c2ecf20Sopenharmony_ci * @upcase: upcase table 1598c2ecf20Sopenharmony_ci * @upcase_size: upcase table size in Unicode characters 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * Compare the first @n characters of the Unicode strings @s1 and @s2, 1628c2ecf20Sopenharmony_ci * ignoring case. The strings in little endian format and appropriate 1638c2ecf20Sopenharmony_ci * le16_to_cpu() conversion is performed on non-little endian machines. 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Each character is uppercased using the @upcase table before the comparison. 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * The function returns an integer less than, equal to, or greater than zero 1688c2ecf20Sopenharmony_ci * if @s1 (or the first @n Unicode characters thereof) is found, respectively, 1698c2ecf20Sopenharmony_ci * to be less than, to match, or be greater than @s2. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ciint ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, 1728c2ecf20Sopenharmony_ci const ntfschar *upcase, const u32 upcase_size) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci size_t i; 1758c2ecf20Sopenharmony_ci u16 c1, c2; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) { 1788c2ecf20Sopenharmony_ci if ((c1 = le16_to_cpu(s1[i])) < upcase_size) 1798c2ecf20Sopenharmony_ci c1 = le16_to_cpu(upcase[c1]); 1808c2ecf20Sopenharmony_ci if ((c2 = le16_to_cpu(s2[i])) < upcase_size) 1818c2ecf20Sopenharmony_ci c2 = le16_to_cpu(upcase[c2]); 1828c2ecf20Sopenharmony_ci if (c1 < c2) 1838c2ecf20Sopenharmony_ci return -1; 1848c2ecf20Sopenharmony_ci if (c1 > c2) 1858c2ecf20Sopenharmony_ci return 1; 1868c2ecf20Sopenharmony_ci if (!c1) 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_civoid ntfs_upcase_name(ntfschar *name, u32 name_len, const ntfschar *upcase, 1938c2ecf20Sopenharmony_ci const u32 upcase_len) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci u32 i; 1968c2ecf20Sopenharmony_ci u16 u; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (i = 0; i < name_len; i++) 1998c2ecf20Sopenharmony_ci if ((u = le16_to_cpu(name[i])) < upcase_len) 2008c2ecf20Sopenharmony_ci name[i] = upcase[u]; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr, 2048c2ecf20Sopenharmony_ci const ntfschar *upcase, const u32 upcase_len) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci ntfs_upcase_name((ntfschar*)&file_name_attr->file_name, 2078c2ecf20Sopenharmony_ci file_name_attr->file_name_length, upcase, upcase_len); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciint ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1, 2118c2ecf20Sopenharmony_ci FILE_NAME_ATTR *file_name_attr2, 2128c2ecf20Sopenharmony_ci const int err_val, const IGNORE_CASE_BOOL ic, 2138c2ecf20Sopenharmony_ci const ntfschar *upcase, const u32 upcase_len) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return ntfs_collate_names((ntfschar*)&file_name_attr1->file_name, 2168c2ecf20Sopenharmony_ci file_name_attr1->file_name_length, 2178c2ecf20Sopenharmony_ci (ntfschar*)&file_name_attr2->file_name, 2188c2ecf20Sopenharmony_ci file_name_attr2->file_name_length, 2198c2ecf20Sopenharmony_ci err_val, ic, upcase, upcase_len); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/** 2238c2ecf20Sopenharmony_ci * ntfs_nlstoucs - convert NLS string to little endian Unicode string 2248c2ecf20Sopenharmony_ci * @vol: ntfs volume which we are working with 2258c2ecf20Sopenharmony_ci * @ins: input NLS string buffer 2268c2ecf20Sopenharmony_ci * @ins_len: length of input string in bytes 2278c2ecf20Sopenharmony_ci * @outs: on return contains the allocated output Unicode string buffer 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * Convert the input string @ins, which is in whatever format the loaded NLS 2308c2ecf20Sopenharmony_ci * map dictates, into a little endian, 2-byte Unicode string. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * This function allocates the string and the caller is responsible for 2338c2ecf20Sopenharmony_ci * calling kmem_cache_free(ntfs_name_cache, *@outs); when finished with it. 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * On success the function returns the number of Unicode characters written to 2368c2ecf20Sopenharmony_ci * the output string *@outs (>= 0), not counting the terminating Unicode NULL 2378c2ecf20Sopenharmony_ci * character. *@outs is set to the allocated output string buffer. 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * On error, a negative number corresponding to the error code is returned. In 2408c2ecf20Sopenharmony_ci * that case the output string is not allocated. Both *@outs and *@outs_len 2418c2ecf20Sopenharmony_ci * are then undefined. 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * This might look a bit odd due to fast path optimization... 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ciint ntfs_nlstoucs(const ntfs_volume *vol, const char *ins, 2468c2ecf20Sopenharmony_ci const int ins_len, ntfschar **outs) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct nls_table *nls = vol->nls_map; 2498c2ecf20Sopenharmony_ci ntfschar *ucs; 2508c2ecf20Sopenharmony_ci wchar_t wc; 2518c2ecf20Sopenharmony_ci int i, o, wc_len; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* We do not trust outside sources. */ 2548c2ecf20Sopenharmony_ci if (likely(ins)) { 2558c2ecf20Sopenharmony_ci ucs = kmem_cache_alloc(ntfs_name_cache, GFP_NOFS); 2568c2ecf20Sopenharmony_ci if (likely(ucs)) { 2578c2ecf20Sopenharmony_ci for (i = o = 0; i < ins_len; i += wc_len) { 2588c2ecf20Sopenharmony_ci wc_len = nls->char2uni(ins + i, ins_len - i, 2598c2ecf20Sopenharmony_ci &wc); 2608c2ecf20Sopenharmony_ci if (likely(wc_len >= 0 && 2618c2ecf20Sopenharmony_ci o < NTFS_MAX_NAME_LEN)) { 2628c2ecf20Sopenharmony_ci if (likely(wc)) { 2638c2ecf20Sopenharmony_ci ucs[o++] = cpu_to_le16(wc); 2648c2ecf20Sopenharmony_ci continue; 2658c2ecf20Sopenharmony_ci } /* else if (!wc) */ 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci } /* else if (wc_len < 0 || 2688c2ecf20Sopenharmony_ci o >= NTFS_MAX_NAME_LEN) */ 2698c2ecf20Sopenharmony_ci goto name_err; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci ucs[o] = 0; 2728c2ecf20Sopenharmony_ci *outs = ucs; 2738c2ecf20Sopenharmony_ci return o; 2748c2ecf20Sopenharmony_ci } /* else if (!ucs) */ 2758c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to allocate buffer for converted " 2768c2ecf20Sopenharmony_ci "name from ntfs_name_cache."); 2778c2ecf20Sopenharmony_ci return -ENOMEM; 2788c2ecf20Sopenharmony_ci } /* else if (!ins) */ 2798c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Received NULL pointer."); 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ciname_err: 2828c2ecf20Sopenharmony_ci kmem_cache_free(ntfs_name_cache, ucs); 2838c2ecf20Sopenharmony_ci if (wc_len < 0) { 2848c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Name using character set %s contains " 2858c2ecf20Sopenharmony_ci "characters that cannot be converted to " 2868c2ecf20Sopenharmony_ci "Unicode.", nls->charset); 2878c2ecf20Sopenharmony_ci i = -EILSEQ; 2888c2ecf20Sopenharmony_ci } else /* if (o >= NTFS_MAX_NAME_LEN) */ { 2898c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Name is too long (maximum length for a " 2908c2ecf20Sopenharmony_ci "name on NTFS is %d Unicode characters.", 2918c2ecf20Sopenharmony_ci NTFS_MAX_NAME_LEN); 2928c2ecf20Sopenharmony_ci i = -ENAMETOOLONG; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci return i; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/** 2988c2ecf20Sopenharmony_ci * ntfs_ucstonls - convert little endian Unicode string to NLS string 2998c2ecf20Sopenharmony_ci * @vol: ntfs volume which we are working with 3008c2ecf20Sopenharmony_ci * @ins: input Unicode string buffer 3018c2ecf20Sopenharmony_ci * @ins_len: length of input string in Unicode characters 3028c2ecf20Sopenharmony_ci * @outs: on return contains the (allocated) output NLS string buffer 3038c2ecf20Sopenharmony_ci * @outs_len: length of output string buffer in bytes 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * Convert the input little endian, 2-byte Unicode string @ins, of length 3068c2ecf20Sopenharmony_ci * @ins_len into the string format dictated by the loaded NLS. 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * If *@outs is NULL, this function allocates the string and the caller is 3098c2ecf20Sopenharmony_ci * responsible for calling kfree(*@outs); when finished with it. In this case 3108c2ecf20Sopenharmony_ci * @outs_len is ignored and can be 0. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * On success the function returns the number of bytes written to the output 3138c2ecf20Sopenharmony_ci * string *@outs (>= 0), not counting the terminating NULL byte. If the output 3148c2ecf20Sopenharmony_ci * string buffer was allocated, *@outs is set to it. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * On error, a negative number corresponding to the error code is returned. In 3178c2ecf20Sopenharmony_ci * that case the output string is not allocated. The contents of *@outs are 3188c2ecf20Sopenharmony_ci * then undefined. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * This might look a bit odd due to fast path optimization... 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ciint ntfs_ucstonls(const ntfs_volume *vol, const ntfschar *ins, 3238c2ecf20Sopenharmony_ci const int ins_len, unsigned char **outs, int outs_len) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct nls_table *nls = vol->nls_map; 3268c2ecf20Sopenharmony_ci unsigned char *ns; 3278c2ecf20Sopenharmony_ci int i, o, ns_len, wc; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* We don't trust outside sources. */ 3308c2ecf20Sopenharmony_ci if (ins) { 3318c2ecf20Sopenharmony_ci ns = *outs; 3328c2ecf20Sopenharmony_ci ns_len = outs_len; 3338c2ecf20Sopenharmony_ci if (ns && !ns_len) { 3348c2ecf20Sopenharmony_ci wc = -ENAMETOOLONG; 3358c2ecf20Sopenharmony_ci goto conversion_err; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci if (!ns) { 3388c2ecf20Sopenharmony_ci ns_len = ins_len * NLS_MAX_CHARSET_SIZE; 3398c2ecf20Sopenharmony_ci ns = kmalloc(ns_len + 1, GFP_NOFS); 3408c2ecf20Sopenharmony_ci if (!ns) 3418c2ecf20Sopenharmony_ci goto mem_err_out; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci for (i = o = 0; i < ins_len; i++) { 3448c2ecf20Sopenharmony_ciretry: wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o, 3458c2ecf20Sopenharmony_ci ns_len - o); 3468c2ecf20Sopenharmony_ci if (wc > 0) { 3478c2ecf20Sopenharmony_ci o += wc; 3488c2ecf20Sopenharmony_ci continue; 3498c2ecf20Sopenharmony_ci } else if (!wc) 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci else if (wc == -ENAMETOOLONG && ns != *outs) { 3528c2ecf20Sopenharmony_ci unsigned char *tc; 3538c2ecf20Sopenharmony_ci /* Grow in multiples of 64 bytes. */ 3548c2ecf20Sopenharmony_ci tc = kmalloc((ns_len + 64) & 3558c2ecf20Sopenharmony_ci ~63, GFP_NOFS); 3568c2ecf20Sopenharmony_ci if (tc) { 3578c2ecf20Sopenharmony_ci memcpy(tc, ns, ns_len); 3588c2ecf20Sopenharmony_ci ns_len = ((ns_len + 64) & ~63) - 1; 3598c2ecf20Sopenharmony_ci kfree(ns); 3608c2ecf20Sopenharmony_ci ns = tc; 3618c2ecf20Sopenharmony_ci goto retry; 3628c2ecf20Sopenharmony_ci } /* No memory so goto conversion_error; */ 3638c2ecf20Sopenharmony_ci } /* wc < 0, real error. */ 3648c2ecf20Sopenharmony_ci goto conversion_err; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci ns[o] = 0; 3678c2ecf20Sopenharmony_ci *outs = ns; 3688c2ecf20Sopenharmony_ci return o; 3698c2ecf20Sopenharmony_ci } /* else (!ins) */ 3708c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Received NULL pointer."); 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ciconversion_err: 3738c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Unicode name contains characters that cannot be " 3748c2ecf20Sopenharmony_ci "converted to character set %s. You might want to " 3758c2ecf20Sopenharmony_ci "try to use the mount option nls=utf8.", nls->charset); 3768c2ecf20Sopenharmony_ci if (ns != *outs) 3778c2ecf20Sopenharmony_ci kfree(ns); 3788c2ecf20Sopenharmony_ci if (wc != -ENAMETOOLONG) 3798c2ecf20Sopenharmony_ci wc = -EILSEQ; 3808c2ecf20Sopenharmony_ci return wc; 3818c2ecf20Sopenharmony_cimem_err_out: 3828c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to allocate name!"); 3838c2ecf20Sopenharmony_ci return -ENOMEM; 3848c2ecf20Sopenharmony_ci} 385