162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/unicode.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Handler routines for unicode strings 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/nls.h> 1462306a36Sopenharmony_ci#include "hfsplus_fs.h" 1562306a36Sopenharmony_ci#include "hfsplus_raw.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Fold the case of a unicode char, given the 16 bit value */ 1862306a36Sopenharmony_ci/* Returns folded char, or 0 if ignorable */ 1962306a36Sopenharmony_cistatic inline u16 case_fold(u16 c) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci u16 tmp; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci tmp = hfsplus_case_fold_table[c >> 8]; 2462306a36Sopenharmony_ci if (tmp) 2562306a36Sopenharmony_ci tmp = hfsplus_case_fold_table[tmp + (c & 0xff)]; 2662306a36Sopenharmony_ci else 2762306a36Sopenharmony_ci tmp = c; 2862306a36Sopenharmony_ci return tmp; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Compare unicode strings, return values like normal strcmp */ 3262306a36Sopenharmony_ciint hfsplus_strcasecmp(const struct hfsplus_unistr *s1, 3362306a36Sopenharmony_ci const struct hfsplus_unistr *s2) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u16 len1, len2, c1, c2; 3662306a36Sopenharmony_ci const hfsplus_unichr *p1, *p2; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci len1 = be16_to_cpu(s1->length); 3962306a36Sopenharmony_ci len2 = be16_to_cpu(s2->length); 4062306a36Sopenharmony_ci p1 = s1->unicode; 4162306a36Sopenharmony_ci p2 = s2->unicode; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci while (1) { 4462306a36Sopenharmony_ci c1 = c2 = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci while (len1 && !c1) { 4762306a36Sopenharmony_ci c1 = case_fold(be16_to_cpu(*p1)); 4862306a36Sopenharmony_ci p1++; 4962306a36Sopenharmony_ci len1--; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci while (len2 && !c2) { 5262306a36Sopenharmony_ci c2 = case_fold(be16_to_cpu(*p2)); 5362306a36Sopenharmony_ci p2++; 5462306a36Sopenharmony_ci len2--; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (c1 != c2) 5862306a36Sopenharmony_ci return (c1 < c2) ? -1 : 1; 5962306a36Sopenharmony_ci if (!c1 && !c2) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Compare names as a sequence of 16-bit unsigned integers */ 6562306a36Sopenharmony_ciint hfsplus_strcmp(const struct hfsplus_unistr *s1, 6662306a36Sopenharmony_ci const struct hfsplus_unistr *s2) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u16 len1, len2, c1, c2; 6962306a36Sopenharmony_ci const hfsplus_unichr *p1, *p2; 7062306a36Sopenharmony_ci int len; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci len1 = be16_to_cpu(s1->length); 7362306a36Sopenharmony_ci len2 = be16_to_cpu(s2->length); 7462306a36Sopenharmony_ci p1 = s1->unicode; 7562306a36Sopenharmony_ci p2 = s2->unicode; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci for (len = min(len1, len2); len > 0; len--) { 7862306a36Sopenharmony_ci c1 = be16_to_cpu(*p1); 7962306a36Sopenharmony_ci c2 = be16_to_cpu(*p2); 8062306a36Sopenharmony_ci if (c1 != c2) 8162306a36Sopenharmony_ci return c1 < c2 ? -1 : 1; 8262306a36Sopenharmony_ci p1++; 8362306a36Sopenharmony_ci p2++; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return len1 < len2 ? -1 : 8762306a36Sopenharmony_ci len1 > len2 ? 1 : 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define Hangul_SBase 0xac00 9262306a36Sopenharmony_ci#define Hangul_LBase 0x1100 9362306a36Sopenharmony_ci#define Hangul_VBase 0x1161 9462306a36Sopenharmony_ci#define Hangul_TBase 0x11a7 9562306a36Sopenharmony_ci#define Hangul_SCount 11172 9662306a36Sopenharmony_ci#define Hangul_LCount 19 9762306a36Sopenharmony_ci#define Hangul_VCount 21 9862306a36Sopenharmony_ci#define Hangul_TCount 28 9962306a36Sopenharmony_ci#define Hangul_NCount (Hangul_VCount * Hangul_TCount) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic u16 *hfsplus_compose_lookup(u16 *p, u16 cc) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int i, s, e; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci s = 1; 10762306a36Sopenharmony_ci e = p[1]; 10862306a36Sopenharmony_ci if (!e || cc < p[s * 2] || cc > p[e * 2]) 10962306a36Sopenharmony_ci return NULL; 11062306a36Sopenharmony_ci do { 11162306a36Sopenharmony_ci i = (s + e) / 2; 11262306a36Sopenharmony_ci if (cc > p[i * 2]) 11362306a36Sopenharmony_ci s = i + 1; 11462306a36Sopenharmony_ci else if (cc < p[i * 2]) 11562306a36Sopenharmony_ci e = i - 1; 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci return hfsplus_compose_table + p[i * 2 + 1]; 11862306a36Sopenharmony_ci } while (s <= e); 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint hfsplus_uni2asc(struct super_block *sb, 12362306a36Sopenharmony_ci const struct hfsplus_unistr *ustr, 12462306a36Sopenharmony_ci char *astr, int *len_p) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci const hfsplus_unichr *ip; 12762306a36Sopenharmony_ci struct nls_table *nls = HFSPLUS_SB(sb)->nls; 12862306a36Sopenharmony_ci u8 *op; 12962306a36Sopenharmony_ci u16 cc, c0, c1; 13062306a36Sopenharmony_ci u16 *ce1, *ce2; 13162306a36Sopenharmony_ci int i, len, ustrlen, res, compose; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci op = astr; 13462306a36Sopenharmony_ci ip = ustr->unicode; 13562306a36Sopenharmony_ci ustrlen = be16_to_cpu(ustr->length); 13662306a36Sopenharmony_ci len = *len_p; 13762306a36Sopenharmony_ci ce1 = NULL; 13862306a36Sopenharmony_ci compose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci while (ustrlen > 0) { 14162306a36Sopenharmony_ci c0 = be16_to_cpu(*ip++); 14262306a36Sopenharmony_ci ustrlen--; 14362306a36Sopenharmony_ci /* search for single decomposed char */ 14462306a36Sopenharmony_ci if (likely(compose)) 14562306a36Sopenharmony_ci ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0); 14662306a36Sopenharmony_ci if (ce1) 14762306a36Sopenharmony_ci cc = ce1[0]; 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci cc = 0; 15062306a36Sopenharmony_ci if (cc) { 15162306a36Sopenharmony_ci /* start of a possibly decomposed Hangul char */ 15262306a36Sopenharmony_ci if (cc != 0xffff) 15362306a36Sopenharmony_ci goto done; 15462306a36Sopenharmony_ci if (!ustrlen) 15562306a36Sopenharmony_ci goto same; 15662306a36Sopenharmony_ci c1 = be16_to_cpu(*ip) - Hangul_VBase; 15762306a36Sopenharmony_ci if (c1 < Hangul_VCount) { 15862306a36Sopenharmony_ci /* compose the Hangul char */ 15962306a36Sopenharmony_ci cc = (c0 - Hangul_LBase) * Hangul_VCount; 16062306a36Sopenharmony_ci cc = (cc + c1) * Hangul_TCount; 16162306a36Sopenharmony_ci cc += Hangul_SBase; 16262306a36Sopenharmony_ci ip++; 16362306a36Sopenharmony_ci ustrlen--; 16462306a36Sopenharmony_ci if (!ustrlen) 16562306a36Sopenharmony_ci goto done; 16662306a36Sopenharmony_ci c1 = be16_to_cpu(*ip) - Hangul_TBase; 16762306a36Sopenharmony_ci if (c1 > 0 && c1 < Hangul_TCount) { 16862306a36Sopenharmony_ci cc += c1; 16962306a36Sopenharmony_ci ip++; 17062306a36Sopenharmony_ci ustrlen--; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci goto done; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci while (1) { 17662306a36Sopenharmony_ci /* main loop for common case of not composed chars */ 17762306a36Sopenharmony_ci if (!ustrlen) 17862306a36Sopenharmony_ci goto same; 17962306a36Sopenharmony_ci c1 = be16_to_cpu(*ip); 18062306a36Sopenharmony_ci if (likely(compose)) 18162306a36Sopenharmony_ci ce1 = hfsplus_compose_lookup( 18262306a36Sopenharmony_ci hfsplus_compose_table, c1); 18362306a36Sopenharmony_ci if (ce1) 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci switch (c0) { 18662306a36Sopenharmony_ci case 0: 18762306a36Sopenharmony_ci c0 = 0x2400; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case '/': 19062306a36Sopenharmony_ci c0 = ':'; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci res = nls->uni2char(c0, op, len); 19462306a36Sopenharmony_ci if (res < 0) { 19562306a36Sopenharmony_ci if (res == -ENAMETOOLONG) 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci *op = '?'; 19862306a36Sopenharmony_ci res = 1; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci op += res; 20162306a36Sopenharmony_ci len -= res; 20262306a36Sopenharmony_ci c0 = c1; 20362306a36Sopenharmony_ci ip++; 20462306a36Sopenharmony_ci ustrlen--; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci ce2 = hfsplus_compose_lookup(ce1, c0); 20762306a36Sopenharmony_ci if (ce2) { 20862306a36Sopenharmony_ci i = 1; 20962306a36Sopenharmony_ci while (i < ustrlen) { 21062306a36Sopenharmony_ci ce1 = hfsplus_compose_lookup(ce2, 21162306a36Sopenharmony_ci be16_to_cpu(ip[i])); 21262306a36Sopenharmony_ci if (!ce1) 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci i++; 21562306a36Sopenharmony_ci ce2 = ce1; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci cc = ce2[0]; 21862306a36Sopenharmony_ci if (cc) { 21962306a36Sopenharmony_ci ip += i; 22062306a36Sopenharmony_ci ustrlen -= i; 22162306a36Sopenharmony_ci goto done; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_cisame: 22562306a36Sopenharmony_ci switch (c0) { 22662306a36Sopenharmony_ci case 0: 22762306a36Sopenharmony_ci cc = 0x2400; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci case '/': 23062306a36Sopenharmony_ci cc = ':'; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci default: 23362306a36Sopenharmony_ci cc = c0; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_cidone: 23662306a36Sopenharmony_ci res = nls->uni2char(cc, op, len); 23762306a36Sopenharmony_ci if (res < 0) { 23862306a36Sopenharmony_ci if (res == -ENAMETOOLONG) 23962306a36Sopenharmony_ci goto out; 24062306a36Sopenharmony_ci *op = '?'; 24162306a36Sopenharmony_ci res = 1; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci op += res; 24462306a36Sopenharmony_ci len -= res; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci res = 0; 24762306a36Sopenharmony_ciout: 24862306a36Sopenharmony_ci *len_p = (char *)op - astr; 24962306a36Sopenharmony_ci return res; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Convert one or more ASCII characters into a single unicode character. 25462306a36Sopenharmony_ci * Returns the number of ASCII characters corresponding to the unicode char. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic inline int asc2unichar(struct super_block *sb, const char *astr, int len, 25762306a36Sopenharmony_ci wchar_t *uc) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc); 26062306a36Sopenharmony_ci if (size <= 0) { 26162306a36Sopenharmony_ci *uc = '?'; 26262306a36Sopenharmony_ci size = 1; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci switch (*uc) { 26562306a36Sopenharmony_ci case 0x2400: 26662306a36Sopenharmony_ci *uc = 0; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case ':': 26962306a36Sopenharmony_ci *uc = '/'; 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci return size; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* Decomposes a non-Hangul unicode character. */ 27662306a36Sopenharmony_cistatic u16 *hfsplus_decompose_nonhangul(wchar_t uc, int *size) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int off; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci off = hfsplus_decompose_table[(uc >> 12) & 0xf]; 28162306a36Sopenharmony_ci if (off == 0 || off == 0xffff) 28262306a36Sopenharmony_ci return NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci off = hfsplus_decompose_table[off + ((uc >> 8) & 0xf)]; 28562306a36Sopenharmony_ci if (!off) 28662306a36Sopenharmony_ci return NULL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci off = hfsplus_decompose_table[off + ((uc >> 4) & 0xf)]; 28962306a36Sopenharmony_ci if (!off) 29062306a36Sopenharmony_ci return NULL; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci off = hfsplus_decompose_table[off + (uc & 0xf)]; 29362306a36Sopenharmony_ci *size = off & 3; 29462306a36Sopenharmony_ci if (*size == 0) 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci return hfsplus_decompose_table + (off / 4); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * Try to decompose a unicode character as Hangul. Return 0 if @uc is not 30162306a36Sopenharmony_ci * precomposed Hangul, otherwise return the length of the decomposition. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * This function was adapted from sample code from the Unicode Standard 30462306a36Sopenharmony_ci * Annex #15: Unicode Normalization Forms, version 3.2.0. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Copyright (C) 1991-2018 Unicode, Inc. All rights reserved. Distributed 30762306a36Sopenharmony_ci * under the Terms of Use in http://www.unicode.org/copyright.html. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic int hfsplus_try_decompose_hangul(wchar_t uc, u16 *result) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int index; 31262306a36Sopenharmony_ci int l, v, t; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci index = uc - Hangul_SBase; 31562306a36Sopenharmony_ci if (index < 0 || index >= Hangul_SCount) 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci l = Hangul_LBase + index / Hangul_NCount; 31962306a36Sopenharmony_ci v = Hangul_VBase + (index % Hangul_NCount) / Hangul_TCount; 32062306a36Sopenharmony_ci t = Hangul_TBase + index % Hangul_TCount; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci result[0] = l; 32362306a36Sopenharmony_ci result[1] = v; 32462306a36Sopenharmony_ci if (t != Hangul_TBase) { 32562306a36Sopenharmony_ci result[2] = t; 32662306a36Sopenharmony_ci return 3; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci return 2; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* Decomposes a single unicode character. */ 33262306a36Sopenharmony_cistatic u16 *decompose_unichar(wchar_t uc, int *size, u16 *hangul_buffer) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci u16 *result; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Hangul is handled separately */ 33762306a36Sopenharmony_ci result = hangul_buffer; 33862306a36Sopenharmony_ci *size = hfsplus_try_decompose_hangul(uc, result); 33962306a36Sopenharmony_ci if (*size == 0) 34062306a36Sopenharmony_ci result = hfsplus_decompose_nonhangul(uc, size); 34162306a36Sopenharmony_ci return result; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciint hfsplus_asc2uni(struct super_block *sb, 34562306a36Sopenharmony_ci struct hfsplus_unistr *ustr, int max_unistr_len, 34662306a36Sopenharmony_ci const char *astr, int len) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int size, dsize, decompose; 34962306a36Sopenharmony_ci u16 *dstr, outlen = 0; 35062306a36Sopenharmony_ci wchar_t c; 35162306a36Sopenharmony_ci u16 dhangul[3]; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 35462306a36Sopenharmony_ci while (outlen < max_unistr_len && len > 0) { 35562306a36Sopenharmony_ci size = asc2unichar(sb, astr, len, &c); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (decompose) 35862306a36Sopenharmony_ci dstr = decompose_unichar(c, &dsize, dhangul); 35962306a36Sopenharmony_ci else 36062306a36Sopenharmony_ci dstr = NULL; 36162306a36Sopenharmony_ci if (dstr) { 36262306a36Sopenharmony_ci if (outlen + dsize > max_unistr_len) 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci do { 36562306a36Sopenharmony_ci ustr->unicode[outlen++] = cpu_to_be16(*dstr++); 36662306a36Sopenharmony_ci } while (--dsize > 0); 36762306a36Sopenharmony_ci } else 36862306a36Sopenharmony_ci ustr->unicode[outlen++] = cpu_to_be16(c); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci astr += size; 37162306a36Sopenharmony_ci len -= size; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci ustr->length = cpu_to_be16(outlen); 37462306a36Sopenharmony_ci if (len > 0) 37562306a36Sopenharmony_ci return -ENAMETOOLONG; 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* 38062306a36Sopenharmony_ci * Hash a string to an integer as appropriate for the HFS+ filesystem. 38162306a36Sopenharmony_ci * Composed unicode characters are decomposed and case-folding is performed 38262306a36Sopenharmony_ci * if the appropriate bits are (un)set on the superblock. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ciint hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 38762306a36Sopenharmony_ci const char *astr; 38862306a36Sopenharmony_ci const u16 *dstr; 38962306a36Sopenharmony_ci int casefold, decompose, size, len; 39062306a36Sopenharmony_ci unsigned long hash; 39162306a36Sopenharmony_ci wchar_t c; 39262306a36Sopenharmony_ci u16 c2; 39362306a36Sopenharmony_ci u16 dhangul[3]; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); 39662306a36Sopenharmony_ci decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 39762306a36Sopenharmony_ci hash = init_name_hash(dentry); 39862306a36Sopenharmony_ci astr = str->name; 39962306a36Sopenharmony_ci len = str->len; 40062306a36Sopenharmony_ci while (len > 0) { 40162306a36Sopenharmony_ci int dsize; 40262306a36Sopenharmony_ci size = asc2unichar(sb, astr, len, &c); 40362306a36Sopenharmony_ci astr += size; 40462306a36Sopenharmony_ci len -= size; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (decompose) 40762306a36Sopenharmony_ci dstr = decompose_unichar(c, &dsize, dhangul); 40862306a36Sopenharmony_ci else 40962306a36Sopenharmony_ci dstr = NULL; 41062306a36Sopenharmony_ci if (dstr) { 41162306a36Sopenharmony_ci do { 41262306a36Sopenharmony_ci c2 = *dstr++; 41362306a36Sopenharmony_ci if (casefold) 41462306a36Sopenharmony_ci c2 = case_fold(c2); 41562306a36Sopenharmony_ci if (!casefold || c2) 41662306a36Sopenharmony_ci hash = partial_name_hash(c2, hash); 41762306a36Sopenharmony_ci } while (--dsize > 0); 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci c2 = c; 42062306a36Sopenharmony_ci if (casefold) 42162306a36Sopenharmony_ci c2 = case_fold(c2); 42262306a36Sopenharmony_ci if (!casefold || c2) 42362306a36Sopenharmony_ci hash = partial_name_hash(c2, hash); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci str->hash = end_name_hash(hash); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * Compare strings with HFS+ filename ordering. 43362306a36Sopenharmony_ci * Composed unicode characters are decomposed and case-folding is performed 43462306a36Sopenharmony_ci * if the appropriate bits are (un)set on the superblock. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ciint hfsplus_compare_dentry(const struct dentry *dentry, 43762306a36Sopenharmony_ci unsigned int len, const char *str, const struct qstr *name) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 44062306a36Sopenharmony_ci int casefold, decompose, size; 44162306a36Sopenharmony_ci int dsize1, dsize2, len1, len2; 44262306a36Sopenharmony_ci const u16 *dstr1, *dstr2; 44362306a36Sopenharmony_ci const char *astr1, *astr2; 44462306a36Sopenharmony_ci u16 c1, c2; 44562306a36Sopenharmony_ci wchar_t c; 44662306a36Sopenharmony_ci u16 dhangul_1[3], dhangul_2[3]; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); 44962306a36Sopenharmony_ci decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 45062306a36Sopenharmony_ci astr1 = str; 45162306a36Sopenharmony_ci len1 = len; 45262306a36Sopenharmony_ci astr2 = name->name; 45362306a36Sopenharmony_ci len2 = name->len; 45462306a36Sopenharmony_ci dsize1 = dsize2 = 0; 45562306a36Sopenharmony_ci dstr1 = dstr2 = NULL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci while (len1 > 0 && len2 > 0) { 45862306a36Sopenharmony_ci if (!dsize1) { 45962306a36Sopenharmony_ci size = asc2unichar(sb, astr1, len1, &c); 46062306a36Sopenharmony_ci astr1 += size; 46162306a36Sopenharmony_ci len1 -= size; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (decompose) 46462306a36Sopenharmony_ci dstr1 = decompose_unichar(c, &dsize1, 46562306a36Sopenharmony_ci dhangul_1); 46662306a36Sopenharmony_ci if (!decompose || !dstr1) { 46762306a36Sopenharmony_ci c1 = c; 46862306a36Sopenharmony_ci dstr1 = &c1; 46962306a36Sopenharmony_ci dsize1 = 1; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!dsize2) { 47462306a36Sopenharmony_ci size = asc2unichar(sb, astr2, len2, &c); 47562306a36Sopenharmony_ci astr2 += size; 47662306a36Sopenharmony_ci len2 -= size; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (decompose) 47962306a36Sopenharmony_ci dstr2 = decompose_unichar(c, &dsize2, 48062306a36Sopenharmony_ci dhangul_2); 48162306a36Sopenharmony_ci if (!decompose || !dstr2) { 48262306a36Sopenharmony_ci c2 = c; 48362306a36Sopenharmony_ci dstr2 = &c2; 48462306a36Sopenharmony_ci dsize2 = 1; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci c1 = *dstr1; 48962306a36Sopenharmony_ci c2 = *dstr2; 49062306a36Sopenharmony_ci if (casefold) { 49162306a36Sopenharmony_ci c1 = case_fold(c1); 49262306a36Sopenharmony_ci if (!c1) { 49362306a36Sopenharmony_ci dstr1++; 49462306a36Sopenharmony_ci dsize1--; 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci c2 = case_fold(c2); 49862306a36Sopenharmony_ci if (!c2) { 49962306a36Sopenharmony_ci dstr2++; 50062306a36Sopenharmony_ci dsize2--; 50162306a36Sopenharmony_ci continue; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (c1 < c2) 50562306a36Sopenharmony_ci return -1; 50662306a36Sopenharmony_ci else if (c1 > c2) 50762306a36Sopenharmony_ci return 1; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dstr1++; 51062306a36Sopenharmony_ci dsize1--; 51162306a36Sopenharmony_ci dstr2++; 51262306a36Sopenharmony_ci dsize2--; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (len1 < len2) 51662306a36Sopenharmony_ci return -1; 51762306a36Sopenharmony_ci if (len1 > len2) 51862306a36Sopenharmony_ci return 1; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 521