162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "ntfs_fs.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic inline u16 upcase_unicode_char(const u16 *upcase, u16 chr)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	if (chr < 'a')
1662306a36Sopenharmony_ci		return chr;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	if (chr <= 'z')
1962306a36Sopenharmony_ci		return chr - ('a' - 'A');
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	return upcase[chr];
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * ntfs_cmp_names
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Thanks Kari Argillander <kari.argillander@gmail.com> for idea and implementation 'bothcase'
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Straight way to compare names:
3062306a36Sopenharmony_ci * - Case insensitive
3162306a36Sopenharmony_ci * - If name equals and 'bothcases' then
3262306a36Sopenharmony_ci * - Case sensitive
3362306a36Sopenharmony_ci * 'Straight way' code scans input names twice in worst case.
3462306a36Sopenharmony_ci * Optimized code scans input names only once.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ciint ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2,
3762306a36Sopenharmony_ci		   const u16 *upcase, bool bothcase)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	int diff1 = 0;
4062306a36Sopenharmony_ci	int diff2;
4162306a36Sopenharmony_ci	size_t len = min(l1, l2);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!bothcase && upcase)
4462306a36Sopenharmony_ci		goto case_insentive;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	for (; len; s1++, s2++, len--) {
4762306a36Sopenharmony_ci		diff1 = le16_to_cpu(*s1) - le16_to_cpu(*s2);
4862306a36Sopenharmony_ci		if (diff1) {
4962306a36Sopenharmony_ci			if (bothcase && upcase)
5062306a36Sopenharmony_ci				goto case_insentive;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci			return diff1;
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	return l1 - l2;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cicase_insentive:
5862306a36Sopenharmony_ci	for (; len; s1++, s2++, len--) {
5962306a36Sopenharmony_ci		diff2 = upcase_unicode_char(upcase, le16_to_cpu(*s1)) -
6062306a36Sopenharmony_ci			upcase_unicode_char(upcase, le16_to_cpu(*s2));
6162306a36Sopenharmony_ci		if (diff2)
6262306a36Sopenharmony_ci			return diff2;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	diff2 = l1 - l2;
6662306a36Sopenharmony_ci	return diff2 ? diff2 : diff1;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciint ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
7062306a36Sopenharmony_ci		       const u16 *upcase, bool bothcase)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	const u16 *s1 = uni1->name;
7362306a36Sopenharmony_ci	const __le16 *s2 = uni2->name;
7462306a36Sopenharmony_ci	size_t l1 = uni1->len;
7562306a36Sopenharmony_ci	size_t l2 = uni2->len;
7662306a36Sopenharmony_ci	size_t len = min(l1, l2);
7762306a36Sopenharmony_ci	int diff1 = 0;
7862306a36Sopenharmony_ci	int diff2;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!bothcase && upcase)
8162306a36Sopenharmony_ci		goto case_insentive;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	for (; len; s1++, s2++, len--) {
8462306a36Sopenharmony_ci		diff1 = *s1 - le16_to_cpu(*s2);
8562306a36Sopenharmony_ci		if (diff1) {
8662306a36Sopenharmony_ci			if (bothcase && upcase)
8762306a36Sopenharmony_ci				goto case_insentive;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci			return diff1;
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	return l1 - l2;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cicase_insentive:
9562306a36Sopenharmony_ci	for (; len; s1++, s2++, len--) {
9662306a36Sopenharmony_ci		diff2 = upcase_unicode_char(upcase, *s1) -
9762306a36Sopenharmony_ci			upcase_unicode_char(upcase, le16_to_cpu(*s2));
9862306a36Sopenharmony_ci		if (diff2)
9962306a36Sopenharmony_ci			return diff2;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	diff2 = l1 - l2;
10362306a36Sopenharmony_ci	return diff2 ? diff2 : diff1;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Helper function for ntfs_d_hash. */
10762306a36Sopenharmony_ciunsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase,
10862306a36Sopenharmony_ci			      unsigned long hash)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	while (len--) {
11162306a36Sopenharmony_ci		unsigned int c = upcase_unicode_char(upcase, *name++);
11262306a36Sopenharmony_ci		hash = partial_name_hash(c, hash);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return hash;
11662306a36Sopenharmony_ci}
117