|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * | 
|  | * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  |  | 
|  | #include "ntfs_fs.h" | 
|  |  | 
|  | static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) | 
|  | { | 
|  | if (chr < 'a') | 
|  | return chr; | 
|  |  | 
|  | if (chr <= 'z') | 
|  | return chr - ('a' - 'A'); | 
|  |  | 
|  | return upcase[chr]; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * ntfs_cmp_names | 
|  | * | 
|  | * Thanks Kari Argillander <kari.argillander@gmail.com> for idea and implementation 'bothcase' | 
|  | * | 
|  | * Straight way to compare names: | 
|  | * - Case insensitive | 
|  | * - If name equals and 'bothcases' then | 
|  | * - Case sensitive | 
|  | * 'Straight way' code scans input names twice in worst case. | 
|  | * Optimized code scans input names only once. | 
|  | */ | 
|  | int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, | 
|  | const u16 *upcase, bool bothcase) | 
|  | { | 
|  | int diff1 = 0; | 
|  | int diff2; | 
|  | size_t len = min(l1, l2); | 
|  |  | 
|  | if (!bothcase && upcase) | 
|  | goto case_insentive; | 
|  |  | 
|  | for (; len; s1++, s2++, len--) { | 
|  | diff1 = le16_to_cpu(*s1) - le16_to_cpu(*s2); | 
|  | if (diff1) { | 
|  | if (bothcase && upcase) | 
|  | goto case_insentive; | 
|  |  | 
|  | return diff1; | 
|  | } | 
|  | } | 
|  | return l1 - l2; | 
|  |  | 
|  | case_insentive: | 
|  | for (; len; s1++, s2++, len--) { | 
|  | diff2 = upcase_unicode_char(upcase, le16_to_cpu(*s1)) - | 
|  | upcase_unicode_char(upcase, le16_to_cpu(*s2)); | 
|  | if (diff2) | 
|  | return diff2; | 
|  | } | 
|  |  | 
|  | diff2 = l1 - l2; | 
|  | return diff2 ? diff2 : diff1; | 
|  | } | 
|  |  | 
|  | int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, | 
|  | const u16 *upcase, bool bothcase) | 
|  | { | 
|  | const u16 *s1 = uni1->name; | 
|  | const __le16 *s2 = uni2->name; | 
|  | size_t l1 = uni1->len; | 
|  | size_t l2 = uni2->len; | 
|  | size_t len = min(l1, l2); | 
|  | int diff1 = 0; | 
|  | int diff2; | 
|  |  | 
|  | if (!bothcase && upcase) | 
|  | goto case_insentive; | 
|  |  | 
|  | for (; len; s1++, s2++, len--) { | 
|  | diff1 = *s1 - le16_to_cpu(*s2); | 
|  | if (diff1) { | 
|  | if (bothcase && upcase) | 
|  | goto case_insentive; | 
|  |  | 
|  | return diff1; | 
|  | } | 
|  | } | 
|  | return l1 - l2; | 
|  |  | 
|  | case_insentive: | 
|  | for (; len; s1++, s2++, len--) { | 
|  | diff2 = upcase_unicode_char(upcase, *s1) - | 
|  | upcase_unicode_char(upcase, le16_to_cpu(*s2)); | 
|  | if (diff2) | 
|  | return diff2; | 
|  | } | 
|  |  | 
|  | diff2 = l1 - l2; | 
|  | return diff2 ? diff2 : diff1; | 
|  | } | 
|  |  | 
|  | /* Helper function for ntfs_d_hash. */ | 
|  | unsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase, | 
|  | unsigned long hash) | 
|  | { | 
|  | while (len--) { | 
|  | unsigned int c = upcase_unicode_char(upcase, *name++); | 
|  | hash = partial_name_hash(c, hash); | 
|  | } | 
|  |  | 
|  | return hash; | 
|  | } |