xref: /linux/fs/ntfs3/upcase.c (revision a266ef69b890f099069cf51bb40572611c435a54)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *
4  * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
5  *
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/types.h>
10 
11 #include "ntfs_fs.h"
12 
13 static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr)
14 {
15 	if (chr < 'a')
16 		return chr;
17 
18 	if (chr <= 'z')
19 		return chr - ('a' - 'A');
20 
21 	return upcase[chr];
22 }
23 
24 /*
25  * ntfs_cmp_names
26  *
27  * Thanks Kari Argillander <kari.argillander@gmail.com> for idea and implementation 'bothcase'
28  *
29  * Straight way to compare names:
30  * - Case insensitive
31  * - If name equals and 'bothcases' then
32  * - Case sensitive
33  * 'Straight way' code scans input names twice in worst case.
34  * Optimized code scans input names only once.
35  */
36 int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2,
37 		   const u16 *upcase, bool bothcase)
38 {
39 	int diff1 = 0;
40 	int diff2;
41 	size_t len = min(l1, l2);
42 
43 	if (!bothcase && upcase)
44 		goto case_insentive;
45 
46 	for (; len; s1++, s2++, len--) {
47 		diff1 = le16_to_cpu(*s1) - le16_to_cpu(*s2);
48 		if (diff1) {
49 			if (bothcase && upcase)
50 				goto case_insentive;
51 
52 			return diff1;
53 		}
54 	}
55 	return l1 - l2;
56 
57 case_insentive:
58 	for (; len; s1++, s2++, len--) {
59 		diff2 = upcase_unicode_char(upcase, le16_to_cpu(*s1)) -
60 			upcase_unicode_char(upcase, le16_to_cpu(*s2));
61 		if (diff2)
62 			return diff2;
63 	}
64 
65 	diff2 = l1 - l2;
66 	return diff2 ? diff2 : diff1;
67 }
68 
69 int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
70 		       const u16 *upcase, bool bothcase)
71 {
72 	const u16 *s1 = uni1->name;
73 	const __le16 *s2 = uni2->name;
74 	size_t l1 = uni1->len;
75 	size_t l2 = uni2->len;
76 	size_t len = min(l1, l2);
77 	int diff1 = 0;
78 	int diff2;
79 
80 	if (!bothcase && upcase)
81 		goto case_insentive;
82 
83 	for (; len; s1++, s2++, len--) {
84 		diff1 = *s1 - le16_to_cpu(*s2);
85 		if (diff1) {
86 			if (bothcase && upcase)
87 				goto case_insentive;
88 
89 			return diff1;
90 		}
91 	}
92 	return l1 - l2;
93 
94 case_insentive:
95 	for (; len; s1++, s2++, len--) {
96 		diff2 = upcase_unicode_char(upcase, *s1) -
97 			upcase_unicode_char(upcase, le16_to_cpu(*s2));
98 		if (diff2)
99 			return diff2;
100 	}
101 
102 	diff2 = l1 - l2;
103 	return diff2 ? diff2 : diff1;
104 }
105 
106 /* Helper function for ntfs_d_hash. */
107 unsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase,
108 			      unsigned long hash)
109 {
110 	while (len--) {
111 		unsigned int c = upcase_unicode_char(upcase, *name++);
112 		hash = partial_name_hash(c, hash);
113 	}
114 
115 	return hash;
116 }
117