xref: /openbmc/linux/fs/hfsplus/unicode.c (revision 3f649ab7)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/hfsplus/unicode.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001
61da177e4SLinus Torvalds  * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds  * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Handler routines for unicode strings
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/nls.h>
141da177e4SLinus Torvalds #include "hfsplus_fs.h"
151da177e4SLinus Torvalds #include "hfsplus_raw.h"
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* Fold the case of a unicode char, given the 16 bit value */
181da177e4SLinus Torvalds /* Returns folded char, or 0 if ignorable */
case_fold(u16 c)191da177e4SLinus Torvalds static inline u16 case_fold(u16 c)
201da177e4SLinus Torvalds {
211da177e4SLinus Torvalds 	u16 tmp;
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 	tmp = hfsplus_case_fold_table[c >> 8];
241da177e4SLinus Torvalds 	if (tmp)
251da177e4SLinus Torvalds 		tmp = hfsplus_case_fold_table[tmp + (c & 0xff)];
261da177e4SLinus Torvalds 	else
271da177e4SLinus Torvalds 		tmp = c;
281da177e4SLinus Torvalds 	return tmp;
291da177e4SLinus Torvalds }
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /* Compare unicode strings, return values like normal strcmp */
hfsplus_strcasecmp(const struct hfsplus_unistr * s1,const struct hfsplus_unistr * s2)322179d372SDavid Elliott int hfsplus_strcasecmp(const struct hfsplus_unistr *s1,
332179d372SDavid Elliott 		       const struct hfsplus_unistr *s2)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds 	u16 len1, len2, c1, c2;
361da177e4SLinus Torvalds 	const hfsplus_unichr *p1, *p2;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 	len1 = be16_to_cpu(s1->length);
391da177e4SLinus Torvalds 	len2 = be16_to_cpu(s2->length);
401da177e4SLinus Torvalds 	p1 = s1->unicode;
411da177e4SLinus Torvalds 	p2 = s2->unicode;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	while (1) {
441da177e4SLinus Torvalds 		c1 = c2 = 0;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 		while (len1 && !c1) {
471da177e4SLinus Torvalds 			c1 = case_fold(be16_to_cpu(*p1));
481da177e4SLinus Torvalds 			p1++;
491da177e4SLinus Torvalds 			len1--;
501da177e4SLinus Torvalds 		}
511da177e4SLinus Torvalds 		while (len2 && !c2) {
521da177e4SLinus Torvalds 			c2 = case_fold(be16_to_cpu(*p2));
531da177e4SLinus Torvalds 			p2++;
541da177e4SLinus Torvalds 			len2--;
551da177e4SLinus Torvalds 		}
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 		if (c1 != c2)
581da177e4SLinus Torvalds 			return (c1 < c2) ? -1 : 1;
591da177e4SLinus Torvalds 		if (!c1 && !c2)
601da177e4SLinus Torvalds 			return 0;
611da177e4SLinus Torvalds 	}
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
642179d372SDavid Elliott /* Compare names as a sequence of 16-bit unsigned integers */
hfsplus_strcmp(const struct hfsplus_unistr * s1,const struct hfsplus_unistr * s2)652179d372SDavid Elliott int hfsplus_strcmp(const struct hfsplus_unistr *s1,
662179d372SDavid Elliott 		   const struct hfsplus_unistr *s2)
672179d372SDavid Elliott {
682179d372SDavid Elliott 	u16 len1, len2, c1, c2;
692179d372SDavid Elliott 	const hfsplus_unichr *p1, *p2;
702179d372SDavid Elliott 	int len;
712179d372SDavid Elliott 
722179d372SDavid Elliott 	len1 = be16_to_cpu(s1->length);
732179d372SDavid Elliott 	len2 = be16_to_cpu(s2->length);
742179d372SDavid Elliott 	p1 = s1->unicode;
752179d372SDavid Elliott 	p2 = s2->unicode;
762179d372SDavid Elliott 
772179d372SDavid Elliott 	for (len = min(len1, len2); len > 0; len--) {
782179d372SDavid Elliott 		c1 = be16_to_cpu(*p1);
792179d372SDavid Elliott 		c2 = be16_to_cpu(*p2);
802179d372SDavid Elliott 		if (c1 != c2)
812179d372SDavid Elliott 			return c1 < c2 ? -1 : 1;
822179d372SDavid Elliott 		p1++;
832179d372SDavid Elliott 		p2++;
842179d372SDavid Elliott 	}
852179d372SDavid Elliott 
862179d372SDavid Elliott 	return len1 < len2 ? -1 :
872179d372SDavid Elliott 	       len1 > len2 ? 1 : 0;
882179d372SDavid Elliott }
892179d372SDavid Elliott 
902179d372SDavid Elliott 
911da177e4SLinus Torvalds #define Hangul_SBase	0xac00
921da177e4SLinus Torvalds #define Hangul_LBase	0x1100
931da177e4SLinus Torvalds #define Hangul_VBase	0x1161
941da177e4SLinus Torvalds #define Hangul_TBase	0x11a7
951da177e4SLinus Torvalds #define Hangul_SCount	11172
961da177e4SLinus Torvalds #define Hangul_LCount	19
971da177e4SLinus Torvalds #define Hangul_VCount	21
981da177e4SLinus Torvalds #define Hangul_TCount	28
991da177e4SLinus Torvalds #define Hangul_NCount	(Hangul_VCount * Hangul_TCount)
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 
hfsplus_compose_lookup(u16 * p,u16 cc)1021da177e4SLinus Torvalds static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
1031da177e4SLinus Torvalds {
1041da177e4SLinus Torvalds 	int i, s, e;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	s = 1;
1071da177e4SLinus Torvalds 	e = p[1];
1081da177e4SLinus Torvalds 	if (!e || cc < p[s * 2] || cc > p[e * 2])
1091da177e4SLinus Torvalds 		return NULL;
1101da177e4SLinus Torvalds 	do {
1111da177e4SLinus Torvalds 		i = (s + e) / 2;
1121da177e4SLinus Torvalds 		if (cc > p[i * 2])
1131da177e4SLinus Torvalds 			s = i + 1;
1141da177e4SLinus Torvalds 		else if (cc < p[i * 2])
1151da177e4SLinus Torvalds 			e = i - 1;
1161da177e4SLinus Torvalds 		else
1171da177e4SLinus Torvalds 			return hfsplus_compose_table + p[i * 2 + 1];
1181da177e4SLinus Torvalds 	} while (s <= e);
1191da177e4SLinus Torvalds 	return NULL;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
hfsplus_uni2asc(struct super_block * sb,const struct hfsplus_unistr * ustr,char * astr,int * len_p)1222753cc28SAnton Salikhmetov int hfsplus_uni2asc(struct super_block *sb,
1232753cc28SAnton Salikhmetov 		const struct hfsplus_unistr *ustr,
1242753cc28SAnton Salikhmetov 		char *astr, int *len_p)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	const hfsplus_unichr *ip;
127dd73a01aSChristoph Hellwig 	struct nls_table *nls = HFSPLUS_SB(sb)->nls;
1281da177e4SLinus Torvalds 	u8 *op;
1291da177e4SLinus Torvalds 	u16 cc, c0, c1;
1301da177e4SLinus Torvalds 	u16 *ce1, *ce2;
1311da177e4SLinus Torvalds 	int i, len, ustrlen, res, compose;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	op = astr;
1341da177e4SLinus Torvalds 	ip = ustr->unicode;
1351da177e4SLinus Torvalds 	ustrlen = be16_to_cpu(ustr->length);
1361da177e4SLinus Torvalds 	len = *len_p;
1371da177e4SLinus Torvalds 	ce1 = NULL;
13884adede3SChristoph Hellwig 	compose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	while (ustrlen > 0) {
1411da177e4SLinus Torvalds 		c0 = be16_to_cpu(*ip++);
1421da177e4SLinus Torvalds 		ustrlen--;
1431da177e4SLinus Torvalds 		/* search for single decomposed char */
1441da177e4SLinus Torvalds 		if (likely(compose))
1451da177e4SLinus Torvalds 			ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0);
1462b4f9ca8SAnton Salikhmetov 		if (ce1)
1472b4f9ca8SAnton Salikhmetov 			cc = ce1[0];
1482b4f9ca8SAnton Salikhmetov 		else
1492b4f9ca8SAnton Salikhmetov 			cc = 0;
1502b4f9ca8SAnton Salikhmetov 		if (cc) {
1511da177e4SLinus Torvalds 			/* start of a possibly decomposed Hangul char */
1521da177e4SLinus Torvalds 			if (cc != 0xffff)
1531da177e4SLinus Torvalds 				goto done;
1541da177e4SLinus Torvalds 			if (!ustrlen)
1551da177e4SLinus Torvalds 				goto same;
1561da177e4SLinus Torvalds 			c1 = be16_to_cpu(*ip) - Hangul_VBase;
1571da177e4SLinus Torvalds 			if (c1 < Hangul_VCount) {
1581da177e4SLinus Torvalds 				/* compose the Hangul char */
1591da177e4SLinus Torvalds 				cc = (c0 - Hangul_LBase) * Hangul_VCount;
1601da177e4SLinus Torvalds 				cc = (cc + c1) * Hangul_TCount;
1611da177e4SLinus Torvalds 				cc += Hangul_SBase;
1621da177e4SLinus Torvalds 				ip++;
1631da177e4SLinus Torvalds 				ustrlen--;
1641da177e4SLinus Torvalds 				if (!ustrlen)
1651da177e4SLinus Torvalds 					goto done;
1661da177e4SLinus Torvalds 				c1 = be16_to_cpu(*ip) - Hangul_TBase;
1671da177e4SLinus Torvalds 				if (c1 > 0 && c1 < Hangul_TCount) {
1681da177e4SLinus Torvalds 					cc += c1;
1691da177e4SLinus Torvalds 					ip++;
1701da177e4SLinus Torvalds 					ustrlen--;
1711da177e4SLinus Torvalds 				}
1721da177e4SLinus Torvalds 				goto done;
1731da177e4SLinus Torvalds 			}
1741da177e4SLinus Torvalds 		}
1751da177e4SLinus Torvalds 		while (1) {
1761da177e4SLinus Torvalds 			/* main loop for common case of not composed chars */
1771da177e4SLinus Torvalds 			if (!ustrlen)
1781da177e4SLinus Torvalds 				goto same;
1791da177e4SLinus Torvalds 			c1 = be16_to_cpu(*ip);
1801da177e4SLinus Torvalds 			if (likely(compose))
1812753cc28SAnton Salikhmetov 				ce1 = hfsplus_compose_lookup(
1822753cc28SAnton Salikhmetov 					hfsplus_compose_table, c1);
1831da177e4SLinus Torvalds 			if (ce1)
1841da177e4SLinus Torvalds 				break;
1851da177e4SLinus Torvalds 			switch (c0) {
1861da177e4SLinus Torvalds 			case 0:
1871da177e4SLinus Torvalds 				c0 = 0x2400;
1881da177e4SLinus Torvalds 				break;
1891da177e4SLinus Torvalds 			case '/':
1901da177e4SLinus Torvalds 				c0 = ':';
1911da177e4SLinus Torvalds 				break;
1921da177e4SLinus Torvalds 			}
1931da177e4SLinus Torvalds 			res = nls->uni2char(c0, op, len);
1941da177e4SLinus Torvalds 			if (res < 0) {
1951da177e4SLinus Torvalds 				if (res == -ENAMETOOLONG)
1961da177e4SLinus Torvalds 					goto out;
1971da177e4SLinus Torvalds 				*op = '?';
1981da177e4SLinus Torvalds 				res = 1;
1991da177e4SLinus Torvalds 			}
2001da177e4SLinus Torvalds 			op += res;
2011da177e4SLinus Torvalds 			len -= res;
2021da177e4SLinus Torvalds 			c0 = c1;
2031da177e4SLinus Torvalds 			ip++;
2041da177e4SLinus Torvalds 			ustrlen--;
2051da177e4SLinus Torvalds 		}
2061da177e4SLinus Torvalds 		ce2 = hfsplus_compose_lookup(ce1, c0);
2071da177e4SLinus Torvalds 		if (ce2) {
2081da177e4SLinus Torvalds 			i = 1;
2091da177e4SLinus Torvalds 			while (i < ustrlen) {
2102753cc28SAnton Salikhmetov 				ce1 = hfsplus_compose_lookup(ce2,
2112753cc28SAnton Salikhmetov 					be16_to_cpu(ip[i]));
2121da177e4SLinus Torvalds 				if (!ce1)
2131da177e4SLinus Torvalds 					break;
2141da177e4SLinus Torvalds 				i++;
2151da177e4SLinus Torvalds 				ce2 = ce1;
2161da177e4SLinus Torvalds 			}
2172b4f9ca8SAnton Salikhmetov 			cc = ce2[0];
2182b4f9ca8SAnton Salikhmetov 			if (cc) {
2191da177e4SLinus Torvalds 				ip += i;
2201da177e4SLinus Torvalds 				ustrlen -= i;
2211da177e4SLinus Torvalds 				goto done;
2221da177e4SLinus Torvalds 			}
2231da177e4SLinus Torvalds 		}
2241da177e4SLinus Torvalds same:
2251da177e4SLinus Torvalds 		switch (c0) {
2261da177e4SLinus Torvalds 		case 0:
2271da177e4SLinus Torvalds 			cc = 0x2400;
2281da177e4SLinus Torvalds 			break;
2291da177e4SLinus Torvalds 		case '/':
2301da177e4SLinus Torvalds 			cc = ':';
2311da177e4SLinus Torvalds 			break;
2321da177e4SLinus Torvalds 		default:
2331da177e4SLinus Torvalds 			cc = c0;
2341da177e4SLinus Torvalds 		}
2351da177e4SLinus Torvalds done:
2361da177e4SLinus Torvalds 		res = nls->uni2char(cc, op, len);
2371da177e4SLinus Torvalds 		if (res < 0) {
2381da177e4SLinus Torvalds 			if (res == -ENAMETOOLONG)
2391da177e4SLinus Torvalds 				goto out;
2401da177e4SLinus Torvalds 			*op = '?';
2411da177e4SLinus Torvalds 			res = 1;
2421da177e4SLinus Torvalds 		}
2431da177e4SLinus Torvalds 		op += res;
2441da177e4SLinus Torvalds 		len -= res;
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	res = 0;
2471da177e4SLinus Torvalds out:
2481da177e4SLinus Torvalds 	*len_p = (char *)op - astr;
2491da177e4SLinus Torvalds 	return res;
2501da177e4SLinus Torvalds }
2511da177e4SLinus Torvalds 
2521e96b7caSDuane Griffin /*
2531e96b7caSDuane Griffin  * Convert one or more ASCII characters into a single unicode character.
2541e96b7caSDuane Griffin  * Returns the number of ASCII characters corresponding to the unicode char.
2551e96b7caSDuane Griffin  */
asc2unichar(struct super_block * sb,const char * astr,int len,wchar_t * uc)2561e96b7caSDuane Griffin static inline int asc2unichar(struct super_block *sb, const char *astr, int len,
2571e96b7caSDuane Griffin 			      wchar_t *uc)
2581da177e4SLinus Torvalds {
259dd73a01aSChristoph Hellwig 	int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc);
2601da177e4SLinus Torvalds 	if (size <= 0) {
2611e96b7caSDuane Griffin 		*uc = '?';
2621da177e4SLinus Torvalds 		size = 1;
2631da177e4SLinus Torvalds 	}
2641e96b7caSDuane Griffin 	switch (*uc) {
2651da177e4SLinus Torvalds 	case 0x2400:
2661e96b7caSDuane Griffin 		*uc = 0;
2671da177e4SLinus Torvalds 		break;
2681da177e4SLinus Torvalds 	case ':':
2691e96b7caSDuane Griffin 		*uc = '/';
2701da177e4SLinus Torvalds 		break;
2711da177e4SLinus Torvalds 	}
2721e96b7caSDuane Griffin 	return size;
2731da177e4SLinus Torvalds }
2741e96b7caSDuane Griffin 
275afd6c9e1SErnesto A. Fernández /* Decomposes a non-Hangul unicode character. */
hfsplus_decompose_nonhangul(wchar_t uc,int * size)276afd6c9e1SErnesto A. Fernández static u16 *hfsplus_decompose_nonhangul(wchar_t uc, int *size)
2771e96b7caSDuane Griffin {
2781e96b7caSDuane Griffin 	int off;
2791e96b7caSDuane Griffin 
2801e96b7caSDuane Griffin 	off = hfsplus_decompose_table[(uc >> 12) & 0xf];
2811e96b7caSDuane Griffin 	if (off == 0 || off == 0xffff)
2821e96b7caSDuane Griffin 		return NULL;
2831e96b7caSDuane Griffin 
2841e96b7caSDuane Griffin 	off = hfsplus_decompose_table[off + ((uc >> 8) & 0xf)];
2851da177e4SLinus Torvalds 	if (!off)
2861e96b7caSDuane Griffin 		return NULL;
2871e96b7caSDuane Griffin 
2881e96b7caSDuane Griffin 	off = hfsplus_decompose_table[off + ((uc >> 4) & 0xf)];
2891da177e4SLinus Torvalds 	if (!off)
2901e96b7caSDuane Griffin 		return NULL;
2911e96b7caSDuane Griffin 
2921e96b7caSDuane Griffin 	off = hfsplus_decompose_table[off + (uc & 0xf)];
2931e96b7caSDuane Griffin 	*size = off & 3;
2941e96b7caSDuane Griffin 	if (*size == 0)
2951e96b7caSDuane Griffin 		return NULL;
2961e96b7caSDuane Griffin 	return hfsplus_decompose_table + (off / 4);
2971e96b7caSDuane Griffin }
2981e96b7caSDuane Griffin 
299afd6c9e1SErnesto A. Fernández /*
300afd6c9e1SErnesto A. Fernández  * Try to decompose a unicode character as Hangul. Return 0 if @uc is not
301afd6c9e1SErnesto A. Fernández  * precomposed Hangul, otherwise return the length of the decomposition.
302afd6c9e1SErnesto A. Fernández  *
303afd6c9e1SErnesto A. Fernández  * This function was adapted from sample code from the Unicode Standard
304afd6c9e1SErnesto A. Fernández  * Annex #15: Unicode Normalization Forms, version 3.2.0.
305afd6c9e1SErnesto A. Fernández  *
306afd6c9e1SErnesto A. Fernández  * Copyright (C) 1991-2018 Unicode, Inc.  All rights reserved.  Distributed
307afd6c9e1SErnesto A. Fernández  * under the Terms of Use in http://www.unicode.org/copyright.html.
308afd6c9e1SErnesto A. Fernández  */
hfsplus_try_decompose_hangul(wchar_t uc,u16 * result)309afd6c9e1SErnesto A. Fernández static int hfsplus_try_decompose_hangul(wchar_t uc, u16 *result)
310afd6c9e1SErnesto A. Fernández {
311afd6c9e1SErnesto A. Fernández 	int index;
312afd6c9e1SErnesto A. Fernández 	int l, v, t;
313afd6c9e1SErnesto A. Fernández 
314afd6c9e1SErnesto A. Fernández 	index = uc - Hangul_SBase;
315afd6c9e1SErnesto A. Fernández 	if (index < 0 || index >= Hangul_SCount)
316afd6c9e1SErnesto A. Fernández 		return 0;
317afd6c9e1SErnesto A. Fernández 
318afd6c9e1SErnesto A. Fernández 	l = Hangul_LBase + index / Hangul_NCount;
319afd6c9e1SErnesto A. Fernández 	v = Hangul_VBase + (index % Hangul_NCount) / Hangul_TCount;
320afd6c9e1SErnesto A. Fernández 	t = Hangul_TBase + index % Hangul_TCount;
321afd6c9e1SErnesto A. Fernández 
322afd6c9e1SErnesto A. Fernández 	result[0] = l;
323afd6c9e1SErnesto A. Fernández 	result[1] = v;
324afd6c9e1SErnesto A. Fernández 	if (t != Hangul_TBase) {
325afd6c9e1SErnesto A. Fernández 		result[2] = t;
326afd6c9e1SErnesto A. Fernández 		return 3;
327afd6c9e1SErnesto A. Fernández 	}
328afd6c9e1SErnesto A. Fernández 	return 2;
329afd6c9e1SErnesto A. Fernández }
330afd6c9e1SErnesto A. Fernández 
331afd6c9e1SErnesto A. Fernández /* Decomposes a single unicode character. */
decompose_unichar(wchar_t uc,int * size,u16 * hangul_buffer)332afd6c9e1SErnesto A. Fernández static u16 *decompose_unichar(wchar_t uc, int *size, u16 *hangul_buffer)
333afd6c9e1SErnesto A. Fernández {
334afd6c9e1SErnesto A. Fernández 	u16 *result;
335afd6c9e1SErnesto A. Fernández 
336afd6c9e1SErnesto A. Fernández 	/* Hangul is handled separately */
337afd6c9e1SErnesto A. Fernández 	result = hangul_buffer;
338afd6c9e1SErnesto A. Fernández 	*size = hfsplus_try_decompose_hangul(uc, result);
339afd6c9e1SErnesto A. Fernández 	if (*size == 0)
340afd6c9e1SErnesto A. Fernández 		result = hfsplus_decompose_nonhangul(uc, size);
341afd6c9e1SErnesto A. Fernández 	return result;
342afd6c9e1SErnesto A. Fernández }
343afd6c9e1SErnesto A. Fernández 
hfsplus_asc2uni(struct super_block * sb,struct hfsplus_unistr * ustr,int max_unistr_len,const char * astr,int len)344324ef39aSVyacheslav Dubeyko int hfsplus_asc2uni(struct super_block *sb,
345324ef39aSVyacheslav Dubeyko 		    struct hfsplus_unistr *ustr, int max_unistr_len,
3461e96b7caSDuane Griffin 		    const char *astr, int len)
3471e96b7caSDuane Griffin {
3481e96b7caSDuane Griffin 	int size, dsize, decompose;
3491e96b7caSDuane Griffin 	u16 *dstr, outlen = 0;
3501e96b7caSDuane Griffin 	wchar_t c;
351afd6c9e1SErnesto A. Fernández 	u16 dhangul[3];
3521e96b7caSDuane Griffin 
35384adede3SChristoph Hellwig 	decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
354324ef39aSVyacheslav Dubeyko 	while (outlen < max_unistr_len && len > 0) {
3551e96b7caSDuane Griffin 		size = asc2unichar(sb, astr, len, &c);
3561e96b7caSDuane Griffin 
3572b4f9ca8SAnton Salikhmetov 		if (decompose)
358afd6c9e1SErnesto A. Fernández 			dstr = decompose_unichar(c, &dsize, dhangul);
3592b4f9ca8SAnton Salikhmetov 		else
3602b4f9ca8SAnton Salikhmetov 			dstr = NULL;
3612b4f9ca8SAnton Salikhmetov 		if (dstr) {
362324ef39aSVyacheslav Dubeyko 			if (outlen + dsize > max_unistr_len)
3631da177e4SLinus Torvalds 				break;
3641da177e4SLinus Torvalds 			do {
3651e96b7caSDuane Griffin 				ustr->unicode[outlen++] = cpu_to_be16(*dstr++);
3661e96b7caSDuane Griffin 			} while (--dsize > 0);
3671e96b7caSDuane Griffin 		} else
3681da177e4SLinus Torvalds 			ustr->unicode[outlen++] = cpu_to_be16(c);
3691e96b7caSDuane Griffin 
3701e96b7caSDuane Griffin 		astr += size;
3711e96b7caSDuane Griffin 		len -= size;
3721da177e4SLinus Torvalds 	}
3731da177e4SLinus Torvalds 	ustr->length = cpu_to_be16(outlen);
3741da177e4SLinus Torvalds 	if (len > 0)
3751da177e4SLinus Torvalds 		return -ENAMETOOLONG;
3761da177e4SLinus Torvalds 	return 0;
3771da177e4SLinus Torvalds }
378d45bce8fSDuane Griffin 
379d45bce8fSDuane Griffin /*
380d45bce8fSDuane Griffin  * Hash a string to an integer as appropriate for the HFS+ filesystem.
381d45bce8fSDuane Griffin  * Composed unicode characters are decomposed and case-folding is performed
382d45bce8fSDuane Griffin  * if the appropriate bits are (un)set on the superblock.
383d45bce8fSDuane Griffin  */
hfsplus_hash_dentry(const struct dentry * dentry,struct qstr * str)384da53be12SLinus Torvalds int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
385d45bce8fSDuane Griffin {
386d45bce8fSDuane Griffin 	struct super_block *sb = dentry->d_sb;
387d45bce8fSDuane Griffin 	const char *astr;
388d45bce8fSDuane Griffin 	const u16 *dstr;
3898aa84ab9SAndrew Morton 	int casefold, decompose, size, len;
390d45bce8fSDuane Griffin 	unsigned long hash;
391d45bce8fSDuane Griffin 	wchar_t c;
392d45bce8fSDuane Griffin 	u16 c2;
393afd6c9e1SErnesto A. Fernández 	u16 dhangul[3];
394d45bce8fSDuane Griffin 
39584adede3SChristoph Hellwig 	casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
39684adede3SChristoph Hellwig 	decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
3978387ff25SLinus Torvalds 	hash = init_name_hash(dentry);
398d45bce8fSDuane Griffin 	astr = str->name;
399d45bce8fSDuane Griffin 	len = str->len;
400d45bce8fSDuane Griffin 	while (len > 0) {
4013f649ab7SKees Cook 		int dsize;
402d45bce8fSDuane Griffin 		size = asc2unichar(sb, astr, len, &c);
403d45bce8fSDuane Griffin 		astr += size;
404d45bce8fSDuane Griffin 		len -= size;
405d45bce8fSDuane Griffin 
4062b4f9ca8SAnton Salikhmetov 		if (decompose)
407afd6c9e1SErnesto A. Fernández 			dstr = decompose_unichar(c, &dsize, dhangul);
4082b4f9ca8SAnton Salikhmetov 		else
4092b4f9ca8SAnton Salikhmetov 			dstr = NULL;
4102b4f9ca8SAnton Salikhmetov 		if (dstr) {
411d45bce8fSDuane Griffin 			do {
412d45bce8fSDuane Griffin 				c2 = *dstr++;
4132b4f9ca8SAnton Salikhmetov 				if (casefold)
4142b4f9ca8SAnton Salikhmetov 					c2 = case_fold(c2);
4152b4f9ca8SAnton Salikhmetov 				if (!casefold || c2)
416d45bce8fSDuane Griffin 					hash = partial_name_hash(c2, hash);
417d45bce8fSDuane Griffin 			} while (--dsize > 0);
418d45bce8fSDuane Griffin 		} else {
419d45bce8fSDuane Griffin 			c2 = c;
4202b4f9ca8SAnton Salikhmetov 			if (casefold)
4212b4f9ca8SAnton Salikhmetov 				c2 = case_fold(c2);
4222b4f9ca8SAnton Salikhmetov 			if (!casefold || c2)
423d45bce8fSDuane Griffin 				hash = partial_name_hash(c2, hash);
424d45bce8fSDuane Griffin 		}
425d45bce8fSDuane Griffin 	}
426d45bce8fSDuane Griffin 	str->hash = end_name_hash(hash);
427d45bce8fSDuane Griffin 
428d45bce8fSDuane Griffin 	return 0;
429d45bce8fSDuane Griffin }
430d45bce8fSDuane Griffin 
431d45bce8fSDuane Griffin /*
432d45bce8fSDuane Griffin  * Compare strings with HFS+ filename ordering.
433d45bce8fSDuane Griffin  * Composed unicode characters are decomposed and case-folding is performed
434d45bce8fSDuane Griffin  * if the appropriate bits are (un)set on the superblock.
435d45bce8fSDuane Griffin  */
hfsplus_compare_dentry(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * name)4366fa67e70SAl Viro int hfsplus_compare_dentry(const struct dentry *dentry,
437621e155aSNick Piggin 		unsigned int len, const char *str, const struct qstr *name)
438d45bce8fSDuane Griffin {
439d3fe1985SAl Viro 	struct super_block *sb = dentry->d_sb;
440d45bce8fSDuane Griffin 	int casefold, decompose, size;
441d45bce8fSDuane Griffin 	int dsize1, dsize2, len1, len2;
442d45bce8fSDuane Griffin 	const u16 *dstr1, *dstr2;
443d45bce8fSDuane Griffin 	const char *astr1, *astr2;
444d45bce8fSDuane Griffin 	u16 c1, c2;
445d45bce8fSDuane Griffin 	wchar_t c;
446afd6c9e1SErnesto A. Fernández 	u16 dhangul_1[3], dhangul_2[3];
447d45bce8fSDuane Griffin 
44884adede3SChristoph Hellwig 	casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
44984adede3SChristoph Hellwig 	decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
450621e155aSNick Piggin 	astr1 = str;
451621e155aSNick Piggin 	len1 = len;
452621e155aSNick Piggin 	astr2 = name->name;
453621e155aSNick Piggin 	len2 = name->len;
454d45bce8fSDuane Griffin 	dsize1 = dsize2 = 0;
455d45bce8fSDuane Griffin 	dstr1 = dstr2 = NULL;
456d45bce8fSDuane Griffin 
457d45bce8fSDuane Griffin 	while (len1 > 0 && len2 > 0) {
458d45bce8fSDuane Griffin 		if (!dsize1) {
459d45bce8fSDuane Griffin 			size = asc2unichar(sb, astr1, len1, &c);
460d45bce8fSDuane Griffin 			astr1 += size;
461d45bce8fSDuane Griffin 			len1 -= size;
462d45bce8fSDuane Griffin 
4632753cc28SAnton Salikhmetov 			if (decompose)
464afd6c9e1SErnesto A. Fernández 				dstr1 = decompose_unichar(c, &dsize1,
465afd6c9e1SErnesto A. Fernández 							  dhangul_1);
4662753cc28SAnton Salikhmetov 			if (!decompose || !dstr1) {
467d45bce8fSDuane Griffin 				c1 = c;
468d45bce8fSDuane Griffin 				dstr1 = &c1;
469d45bce8fSDuane Griffin 				dsize1 = 1;
470d45bce8fSDuane Griffin 			}
471d45bce8fSDuane Griffin 		}
472d45bce8fSDuane Griffin 
473d45bce8fSDuane Griffin 		if (!dsize2) {
474d45bce8fSDuane Griffin 			size = asc2unichar(sb, astr2, len2, &c);
475d45bce8fSDuane Griffin 			astr2 += size;
476d45bce8fSDuane Griffin 			len2 -= size;
477d45bce8fSDuane Griffin 
4782753cc28SAnton Salikhmetov 			if (decompose)
479afd6c9e1SErnesto A. Fernández 				dstr2 = decompose_unichar(c, &dsize2,
480afd6c9e1SErnesto A. Fernández 							  dhangul_2);
4812753cc28SAnton Salikhmetov 			if (!decompose || !dstr2) {
482d45bce8fSDuane Griffin 				c2 = c;
483d45bce8fSDuane Griffin 				dstr2 = &c2;
484d45bce8fSDuane Griffin 				dsize2 = 1;
485d45bce8fSDuane Griffin 			}
486d45bce8fSDuane Griffin 		}
487d45bce8fSDuane Griffin 
488d45bce8fSDuane Griffin 		c1 = *dstr1;
489d45bce8fSDuane Griffin 		c2 = *dstr2;
490d45bce8fSDuane Griffin 		if (casefold) {
4912b4f9ca8SAnton Salikhmetov 			c1 = case_fold(c1);
4922b4f9ca8SAnton Salikhmetov 			if (!c1) {
493d45bce8fSDuane Griffin 				dstr1++;
494d45bce8fSDuane Griffin 				dsize1--;
495d45bce8fSDuane Griffin 				continue;
496d45bce8fSDuane Griffin 			}
4972b4f9ca8SAnton Salikhmetov 			c2 = case_fold(c2);
4982b4f9ca8SAnton Salikhmetov 			if (!c2) {
499d45bce8fSDuane Griffin 				dstr2++;
500d45bce8fSDuane Griffin 				dsize2--;
501d45bce8fSDuane Griffin 				continue;
502d45bce8fSDuane Griffin 			}
503d45bce8fSDuane Griffin 		}
504d45bce8fSDuane Griffin 		if (c1 < c2)
505d45bce8fSDuane Griffin 			return -1;
506d45bce8fSDuane Griffin 		else if (c1 > c2)
507d45bce8fSDuane Griffin 			return 1;
508d45bce8fSDuane Griffin 
509d45bce8fSDuane Griffin 		dstr1++;
510d45bce8fSDuane Griffin 		dsize1--;
511d45bce8fSDuane Griffin 		dstr2++;
512d45bce8fSDuane Griffin 		dsize2--;
513d45bce8fSDuane Griffin 	}
514d45bce8fSDuane Griffin 
515d45bce8fSDuane Griffin 	if (len1 < len2)
516d45bce8fSDuane Griffin 		return -1;
517d45bce8fSDuane Griffin 	if (len1 > len2)
518d45bce8fSDuane Griffin 		return 1;
519d45bce8fSDuane Griffin 	return 0;
520d45bce8fSDuane Griffin }
521