mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 23:51:02 +02:00
hfsplus: canonicalize decomposed catalog names like fsck_hfs expects
hfsplus_decompose_table (Apple Technote #1150) decomposes each source character entirely on its own. It never reorders the result, and it predates a handful of later corrections to the decomposition standard. Two concrete consequences, both of which macOS's own fsck_hfs treats as "Illegal name" (CatalogCheck.c: CheckCatalogName(), FixDecomps()): - When combining marks contributed by two different source characters end up adjacent, this driver stores them in input order rather than ascending Unicode combining-class order. - A small, fixed set of characters either aren't decomposed at all, or are decomposed into a sequence macOS corrected back in Mac OS X 10.2 ("Jaguar"). This is reliably reproducible: xfstests generic/339 exercises dirhash collisions by creating many files with randomized Unicode names, and the resulting HFS+ volume fails _check_generic_filesystem afterward with a long run of "Illegal name" reports from fsck.hfsplus. Fix hfsplus_asc2uni() to store names the way current macOS does, using the Unicode tables: - hfsplus_canonical_reorder() applies the Unicode Canonical Ordering Algorithm: each maximal run of nonzero-combining-class code units is stable-sorted into ascending class order. - decompose_unichar() falls back to hfsplus_legacy_decompose() for the characters missing from Apple Technote #1150's table. - hfsplus_fixup_legacy_sequences() substitutes any of fsck_hfs's known bad sequences with their corrected form. hfsplus_decompose_str() ties these together and is now shared by hfsplus_asc2uni(), hfsplus_hash_dentry() and hfsplus_compare_dentry(), so hashing and comparison always agree with what actually gets stored - otherwise two byte-for-byte different but canonically-equivalent spellings of a name could hash differently or fail to compare equal against the catalog entry a create() of either would produce. cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> cc: Yangtao Li <frank.li@vivo.com> cc: linux-fsdevel@vger.kernel.org Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Link: https://lore.kernel.org/r/20260919001607.2777138-3-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
This commit is contained in:
parent
7f33d2e5b0
commit
bbe5ebcbc0
|
|
@ -3641,3 +3641,102 @@ struct hfsplus_legacy_seq_fixup hfsplus_legacy_seq_fixups[] = {
|
|||
{ 3, { 0x0fb2, 0x0f80, 0x0f71 }, 1, { 0x0f77 } },
|
||||
{ 3, { 0x0fb3, 0x0f80, 0x0f71 }, 1, { 0x0f79 } },
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the Unicode canonical combining class of a BMP code point.
|
||||
* Returns 0 ("Not_Reordered") for any code point not listed in the table,
|
||||
* which includes every ordinary base character.
|
||||
*/
|
||||
u8 hfsplus_combining_class(u16 c)
|
||||
{
|
||||
int lo = 0, hi = ARRAY_SIZE(hfsplus_ccc_table) - 1;
|
||||
|
||||
while (lo <= hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
const struct hfsplus_ccc_range *r = &hfsplus_ccc_table[mid];
|
||||
|
||||
if (c < r->first)
|
||||
hi = mid - 1;
|
||||
else if (c > r->last)
|
||||
lo = mid + 1;
|
||||
else
|
||||
return r->combining_class;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the corrected canonical decomposition for a single BMP code
|
||||
* point that Apple Technote #1150's own decomposition table (above)
|
||||
* lacks. Returns NULL (and leaves *size alone) if @uc isn't one of these.
|
||||
*/
|
||||
const u16 *hfsplus_legacy_decompose(u16 uc, int *size)
|
||||
{
|
||||
int lo = 0, hi = ARRAY_SIZE(hfsplus_legacy_decomp_table) - 1;
|
||||
|
||||
while (lo <= hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
const struct hfsplus_legacy_decomp *e =
|
||||
&hfsplus_legacy_decomp_table[mid];
|
||||
|
||||
if (uc < e->uc)
|
||||
hi = mid - 1;
|
||||
else if (uc > e->uc)
|
||||
lo = mid + 1;
|
||||
else {
|
||||
*size = e->len;
|
||||
return e->repl;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan an already decomposed and canonically-reordered code unit buffer
|
||||
* for any of the short legacy sequences above and replace them in place
|
||||
* with their corrected form. @len is updated to the buffer's new length.
|
||||
*/
|
||||
void hfsplus_fixup_legacy_sequences(u16 *buf, int *len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < *len) {
|
||||
unsigned int j;
|
||||
bool matched = false;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(hfsplus_legacy_seq_fixups); j++) {
|
||||
struct hfsplus_legacy_seq_fixup *f =
|
||||
&hfsplus_legacy_seq_fixups[j];
|
||||
u16 *src, *dst;
|
||||
size_t copy_len;
|
||||
int k;
|
||||
|
||||
if (i + f->match_len > *len)
|
||||
continue;
|
||||
for (k = 0; k < f->match_len; k++)
|
||||
if (buf[i + k] != f->match[k])
|
||||
break;
|
||||
if (k != f->match_len)
|
||||
continue;
|
||||
|
||||
dst = &buf[i + f->repl_len];
|
||||
src = &buf[i + f->match_len];
|
||||
copy_len = (*len - i - f->match_len) * sizeof(*buf);
|
||||
memmove(dst, src, copy_len);
|
||||
|
||||
dst = &buf[i];
|
||||
src = f->repl;
|
||||
copy_len = f->repl_len * sizeof(*buf);
|
||||
memcpy(dst, src, copy_len);
|
||||
|
||||
*len += f->repl_len - f->match_len;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!matched)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -442,45 +442,264 @@ static u16 *decompose_unichar(wchar_t uc, int *size, u16 *hangul_buffer)
|
|||
*size = hfsplus_try_decompose_hangul(uc, result);
|
||||
if (*size == 0)
|
||||
result = hfsplus_decompose_nonhangul(uc, size);
|
||||
if (!result) {
|
||||
/*
|
||||
* Not every character with a canonical decomposition is in
|
||||
* Apple Technote #1150's own table above; a small, fixed
|
||||
* set was only added to the decomposition standard (or had
|
||||
* its decomposition corrected) after that table was
|
||||
* generated. hfsplus_legacy_decompose() covers those.
|
||||
*/
|
||||
const u16 *legacy = hfsplus_legacy_decompose(uc, size);
|
||||
|
||||
if (legacy)
|
||||
result = memcpy(hangul_buffer, legacy,
|
||||
*size * sizeof(*legacy));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply the Unicode Canonical Ordering Algorithm to a decomposed name held
|
||||
* as plain host-order code units: within each maximal run of characters
|
||||
* that have a nonzero combining class, stable-sort the run into ascending
|
||||
* combining-class order. A character with combining class 0 always starts
|
||||
* a new run and is never itself reordered.
|
||||
*
|
||||
* hfsplus_decompose_table decomposes each source character on its own; it
|
||||
* says nothing about how the decompositions of two different source
|
||||
* characters should be ordered relative to each other when both produce
|
||||
* combining marks that end up adjacent. Without this pass, such a
|
||||
* sequence can be stored in an order that macOS's own fsck_hfs
|
||||
* (FixDecomps() in CatalogCheck.c) considers illegal, even though every
|
||||
* individual character was decomposed correctly.
|
||||
*/
|
||||
static void hfsplus_canonical_reorder(u16 *ustr, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
u8 cls = hfsplus_combining_class(ustr[i]);
|
||||
int j = i;
|
||||
|
||||
if (!cls)
|
||||
continue;
|
||||
|
||||
while (j > 0) {
|
||||
u8 prev_cls = hfsplus_combining_class(ustr[j - 1]);
|
||||
u16 tmp;
|
||||
|
||||
if (!prev_cls || prev_cls <= cls)
|
||||
break;
|
||||
|
||||
tmp = ustr[j];
|
||||
ustr[j] = ustr[j - 1];
|
||||
ustr[j - 1] = tmp;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define HFSPLUS_HANGUL_MAX_JAMO (3) /* L + V + optional T */
|
||||
|
||||
static_assert(HFSPLUS_HANGUL_MAX_JAMO >= HFSPLUS_LEGACY_DECOMP_MAX_LEN,
|
||||
"hangul_buffer must fit the longest legacy decomposition too");
|
||||
|
||||
/*
|
||||
* Decompose and canonically reorder an entire Linux name, as plain
|
||||
* host-order code units. hfsplus_asc2uni(), hfsplus_hash_dentry() and
|
||||
* hfsplus_compare_dentry() all go through this so that storage, hashing
|
||||
* and comparison always agree on what a given name canonicalizes to.
|
||||
*
|
||||
* @out must hold at least @max_len entries, which must not exceed
|
||||
* HFSPLUS_MAX_STRLEN. Returns the number of code units written. If
|
||||
* @consumed is non-NULL, it is set to the number of input bytes actually
|
||||
* consumed, which is less than @len when @out fills up first.
|
||||
*/
|
||||
static int hfsplus_decompose_str(struct super_block *sb, const char *astr,
|
||||
int len, int max_len, int name_type,
|
||||
u16 *out, int *consumed)
|
||||
{
|
||||
int decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
const char *start = astr;
|
||||
int outlen = 0;
|
||||
|
||||
while (outlen < max_len && len > 0) {
|
||||
u16 *dstr;
|
||||
u16 dhangul[HFSPLUS_HANGUL_MAX_JAMO];
|
||||
int dsize, size;
|
||||
wchar_t c;
|
||||
|
||||
size = asc2unichar(sb, astr, len, &c, name_type);
|
||||
|
||||
dstr = decompose ? decompose_unichar(c, &dsize, dhangul) : NULL;
|
||||
if (dstr) {
|
||||
if (outlen + dsize > max_len)
|
||||
break;
|
||||
do {
|
||||
out[outlen++] = *dstr++;
|
||||
} while (--dsize > 0);
|
||||
} else {
|
||||
out[outlen++] = c;
|
||||
}
|
||||
|
||||
astr += size;
|
||||
len -= size;
|
||||
}
|
||||
|
||||
hfsplus_canonical_reorder(out, outlen);
|
||||
hfsplus_fixup_legacy_sequences(out, &outlen);
|
||||
if (consumed)
|
||||
*consumed = astr - start;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
int hfsplus_asc2uni(struct super_block *sb,
|
||||
struct hfsplus_unistr *ustr, int max_unistr_len,
|
||||
const char *astr, int len, int name_type)
|
||||
{
|
||||
int size, dsize, decompose;
|
||||
u16 *dstr, outlen = 0;
|
||||
wchar_t c;
|
||||
u16 dhangul[3];
|
||||
u16 buf[HFSPLUS_MAX_STRLEN];
|
||||
int outlen, i, consumed;
|
||||
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
while (outlen < max_unistr_len && len > 0) {
|
||||
size = asc2unichar(sb, astr, len, &c, name_type);
|
||||
if (max_unistr_len > HFSPLUS_MAX_STRLEN)
|
||||
max_unistr_len = HFSPLUS_MAX_STRLEN;
|
||||
|
||||
if (decompose)
|
||||
dstr = decompose_unichar(c, &dsize, dhangul);
|
||||
else
|
||||
dstr = NULL;
|
||||
if (dstr) {
|
||||
if (outlen + dsize > max_unistr_len)
|
||||
break;
|
||||
do {
|
||||
ustr->unicode[outlen++] = cpu_to_be16(*dstr++);
|
||||
} while (--dsize > 0);
|
||||
} else
|
||||
ustr->unicode[outlen++] = cpu_to_be16(c);
|
||||
|
||||
astr += size;
|
||||
len -= size;
|
||||
}
|
||||
outlen = hfsplus_decompose_str(sb, astr, len, max_unistr_len,
|
||||
name_type, buf, &consumed);
|
||||
for (i = 0; i < outlen; i++)
|
||||
ustr->unicode[i] = cpu_to_be16(buf[i]);
|
||||
ustr->length = cpu_to_be16(outlen);
|
||||
if (len > 0)
|
||||
|
||||
if (consumed < len)
|
||||
return -ENAMETOOLONG;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni);
|
||||
|
||||
/*
|
||||
* Maximum length of a single maximal run of nonzero-combining-class code
|
||||
* units that hfsplus_decompose_iter_next() below will canonically
|
||||
* reorder. Every code unit with combining class 0 starts a new run, so
|
||||
* this only bounds how many *consecutive* combining marks between two
|
||||
* base characters get sorted - real text, and even deliberately
|
||||
* adversarial "Zalgo" text, essentially never approaches this. Keeping
|
||||
* it small means hfsplus_hash_dentry() and hfsplus_compare_dentry() only
|
||||
* ever need a tiny amount of lookahead state, rather than buffering an
|
||||
* entire (up to 255-unit) name.
|
||||
*/
|
||||
#define HFSPLUS_CCC_RUN_MAX 32
|
||||
|
||||
/*
|
||||
* Iterator that produces the canonically-ordered, decomposed code units
|
||||
* of a Linux name one at a time, without materializing the whole name.
|
||||
* hfsplus_hash_dentry() and hfsplus_compare_dentry() use this so that
|
||||
* hashing and comparison always agree with what hfsplus_asc2uni() would
|
||||
* actually store, while still comparing lazily (stopping at the first
|
||||
* difference) the way this code did before canonical reordering existed.
|
||||
*/
|
||||
struct hfsplus_decompose_iter {
|
||||
struct super_block *sb;
|
||||
const char *astr;
|
||||
int len;
|
||||
int name_type;
|
||||
int decompose;
|
||||
|
||||
u16 run[HFSPLUS_CCC_RUN_MAX];
|
||||
int run_len;
|
||||
int run_pos;
|
||||
};
|
||||
|
||||
static void hfsplus_decompose_iter_init(struct hfsplus_decompose_iter *it,
|
||||
struct super_block *sb,
|
||||
const char *astr, int len,
|
||||
int name_type)
|
||||
{
|
||||
it->sb = sb;
|
||||
it->astr = astr;
|
||||
it->len = len;
|
||||
it->name_type = name_type;
|
||||
it->decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
it->run_len = 0;
|
||||
it->run_pos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather the next maximal run of nonzero-combining-class code units
|
||||
* (starting with whatever character comes next, base or not) and
|
||||
* canonically reorder just that run. A character is only included once
|
||||
* we know the class of the first unit it produces, so decoding it is
|
||||
* speculative until that's decided.
|
||||
*/
|
||||
static bool hfsplus_decompose_iter_refill(struct hfsplus_decompose_iter *it)
|
||||
{
|
||||
it->run_pos = 0;
|
||||
it->run_len = 0;
|
||||
|
||||
while (it->len > 0) {
|
||||
u16 *dstr;
|
||||
u16 dhangul[HFSPLUS_HANGUL_MAX_JAMO];
|
||||
int dsize, size;
|
||||
wchar_t c;
|
||||
|
||||
size = asc2unichar(it->sb, it->astr, it->len, &c,
|
||||
it->name_type);
|
||||
|
||||
dstr = it->decompose ?
|
||||
decompose_unichar(c, &dsize, dhangul) : NULL;
|
||||
if (!dstr) {
|
||||
dhangul[0] = c;
|
||||
dstr = dhangul;
|
||||
dsize = 1;
|
||||
}
|
||||
|
||||
if (it->run_len > 0 && !hfsplus_combining_class(dstr[0]))
|
||||
break;
|
||||
|
||||
if (it->run_len + dsize > HFSPLUS_CCC_RUN_MAX)
|
||||
break;
|
||||
|
||||
it->astr += size;
|
||||
it->len -= size;
|
||||
|
||||
do {
|
||||
it->run[it->run_len++] = *dstr++;
|
||||
} while (--dsize > 0);
|
||||
}
|
||||
|
||||
hfsplus_canonical_reorder(it->run, it->run_len);
|
||||
hfsplus_fixup_legacy_sequences(it->run, &it->run_len);
|
||||
return it->run_len > 0;
|
||||
}
|
||||
|
||||
/* Returns the next code unit, or a negative value once @it is exhausted. */
|
||||
static int hfsplus_decompose_iter_next(struct hfsplus_decompose_iter *it)
|
||||
{
|
||||
if (it->run_pos >= it->run_len && !hfsplus_decompose_iter_refill(it))
|
||||
return -1;
|
||||
return it->run[it->run_pos++];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the next code unit that matters for comparison/hashing: folded
|
||||
* if @casefold, with any character folding to 0 ("ignorable") skipped
|
||||
* entirely. Returns a negative value once @it is exhausted.
|
||||
*/
|
||||
static int hfsplus_decompose_iter_next_folded(struct hfsplus_decompose_iter *it,
|
||||
int casefold)
|
||||
{
|
||||
int c;
|
||||
|
||||
do {
|
||||
c = hfsplus_decompose_iter_next(it);
|
||||
if (c < 0)
|
||||
return -1;
|
||||
if (casefold)
|
||||
c = case_fold(c);
|
||||
} while (casefold && !c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash a string to an integer as appropriate for the HFS+ filesystem.
|
||||
* Composed unicode characters are decomposed and case-folding is performed
|
||||
|
|
@ -489,45 +708,17 @@ EXPORT_SYMBOL_IF_KUNIT(hfsplus_asc2uni);
|
|||
int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
const char *astr;
|
||||
const u16 *dstr;
|
||||
int casefold, decompose, size, len;
|
||||
int casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
struct hfsplus_decompose_iter it;
|
||||
unsigned long hash;
|
||||
wchar_t c;
|
||||
u16 c2;
|
||||
u16 dhangul[3];
|
||||
int c;
|
||||
|
||||
hfsplus_decompose_iter_init(&it, sb, str->name, str->len,
|
||||
HFS_REGULAR_NAME);
|
||||
|
||||
casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
hash = init_name_hash(dentry);
|
||||
astr = str->name;
|
||||
len = str->len;
|
||||
while (len > 0) {
|
||||
int dsize;
|
||||
size = asc2unichar(sb, astr, len, &c, HFS_REGULAR_NAME);
|
||||
astr += size;
|
||||
len -= size;
|
||||
|
||||
if (decompose)
|
||||
dstr = decompose_unichar(c, &dsize, dhangul);
|
||||
else
|
||||
dstr = NULL;
|
||||
if (dstr) {
|
||||
do {
|
||||
c2 = *dstr++;
|
||||
if (casefold)
|
||||
c2 = case_fold(c2);
|
||||
if (!casefold || c2)
|
||||
hash = partial_name_hash(c2, hash);
|
||||
} while (--dsize > 0);
|
||||
} else {
|
||||
c2 = c;
|
||||
if (casefold)
|
||||
c2 = case_fold(c2);
|
||||
if (!casefold || c2)
|
||||
hash = partial_name_hash(c2, hash);
|
||||
}
|
||||
}
|
||||
while ((c = hfsplus_decompose_iter_next_folded(&it, casefold)) >= 0)
|
||||
hash = partial_name_hash(c, hash);
|
||||
str->hash = end_name_hash(hash);
|
||||
|
||||
return 0;
|
||||
|
|
@ -543,87 +734,21 @@ int hfsplus_compare_dentry(const struct dentry *dentry,
|
|||
unsigned int len, const char *str, const struct qstr *name)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
int casefold, decompose, size;
|
||||
int dsize1, dsize2, len1, len2;
|
||||
const u16 *dstr1, *dstr2;
|
||||
const char *astr1, *astr2;
|
||||
u16 c1, c2;
|
||||
wchar_t c;
|
||||
u16 dhangul_1[3], dhangul_2[3];
|
||||
int casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
struct hfsplus_decompose_iter it1, it2;
|
||||
|
||||
casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
|
||||
decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
|
||||
astr1 = str;
|
||||
len1 = len;
|
||||
astr2 = name->name;
|
||||
len2 = name->len;
|
||||
dsize1 = dsize2 = 0;
|
||||
dstr1 = dstr2 = NULL;
|
||||
hfsplus_decompose_iter_init(&it1, sb, str, len, HFS_REGULAR_NAME);
|
||||
hfsplus_decompose_iter_init(&it2, sb, name->name, name->len,
|
||||
HFS_REGULAR_NAME);
|
||||
|
||||
while (len1 > 0 && len2 > 0) {
|
||||
if (!dsize1) {
|
||||
size = asc2unichar(sb, astr1, len1, &c,
|
||||
HFS_REGULAR_NAME);
|
||||
astr1 += size;
|
||||
len1 -= size;
|
||||
while (1) {
|
||||
int c1 = hfsplus_decompose_iter_next_folded(&it1, casefold);
|
||||
int c2 = hfsplus_decompose_iter_next_folded(&it2, casefold);
|
||||
|
||||
if (decompose)
|
||||
dstr1 = decompose_unichar(c, &dsize1,
|
||||
dhangul_1);
|
||||
if (!decompose || !dstr1) {
|
||||
c1 = c;
|
||||
dstr1 = &c1;
|
||||
dsize1 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dsize2) {
|
||||
size = asc2unichar(sb, astr2, len2, &c,
|
||||
HFS_REGULAR_NAME);
|
||||
astr2 += size;
|
||||
len2 -= size;
|
||||
|
||||
if (decompose)
|
||||
dstr2 = decompose_unichar(c, &dsize2,
|
||||
dhangul_2);
|
||||
if (!decompose || !dstr2) {
|
||||
c2 = c;
|
||||
dstr2 = &c2;
|
||||
dsize2 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
c1 = *dstr1;
|
||||
c2 = *dstr2;
|
||||
if (casefold) {
|
||||
c1 = case_fold(c1);
|
||||
if (!c1) {
|
||||
dstr1++;
|
||||
dsize1--;
|
||||
continue;
|
||||
}
|
||||
c2 = case_fold(c2);
|
||||
if (!c2) {
|
||||
dstr2++;
|
||||
dsize2--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
else if (c1 > c2)
|
||||
return 1;
|
||||
|
||||
dstr1++;
|
||||
dsize1--;
|
||||
dstr2++;
|
||||
dsize2--;
|
||||
if (c1 < 0 || c2 < 0)
|
||||
return c1 == c2 ? 0 : (c1 < 0 ? -1 : 1);
|
||||
if (c1 != c2)
|
||||
return c1 < c2 ? -1 : 1;
|
||||
}
|
||||
|
||||
if (len1 < len2)
|
||||
return -1;
|
||||
if (len1 > len2)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(hfsplus_compare_dentry);
|
||||
|
|
|
|||
|
|
@ -56,4 +56,8 @@ extern struct hfsplus_ccc_range hfsplus_ccc_table[];
|
|||
extern struct hfsplus_legacy_decomp hfsplus_legacy_decomp_table[];
|
||||
extern struct hfsplus_legacy_seq_fixup hfsplus_legacy_seq_fixups[];
|
||||
|
||||
u8 hfsplus_combining_class(u16 c);
|
||||
const u16 *hfsplus_legacy_decompose(u16 uc, int *size);
|
||||
void hfsplus_fixup_legacy_sequences(u16 *buf, int *len);
|
||||
|
||||
#endif /* _LINUX_HFSPLUS_UNICODE_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user