| // 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; |
| } |