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