xref: /openbmc/linux/fs/unicode/utf8-core.c (revision 94d964e5)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4 #include <linux/string.h>
5 #include <linux/slab.h>
6 #include <linux/parser.h>
7 #include <linux/errno.h>
8 #include <linux/stringhash.h>
9 
10 #include "utf8n.h"
11 
12 int utf8_validate(const struct unicode_map *um, const struct qstr *str)
13 {
14 	if (utf8nlen(um, UTF8_NFDI, str->name, str->len) < 0)
15 		return -1;
16 	return 0;
17 }
18 EXPORT_SYMBOL(utf8_validate);
19 
20 int utf8_strncmp(const struct unicode_map *um,
21 		 const struct qstr *s1, const struct qstr *s2)
22 {
23 	struct utf8cursor cur1, cur2;
24 	int c1, c2;
25 
26 	if (utf8ncursor(&cur1, um, UTF8_NFDI, s1->name, s1->len) < 0)
27 		return -EINVAL;
28 
29 	if (utf8ncursor(&cur2, um, UTF8_NFDI, s2->name, s2->len) < 0)
30 		return -EINVAL;
31 
32 	do {
33 		c1 = utf8byte(&cur1);
34 		c2 = utf8byte(&cur2);
35 
36 		if (c1 < 0 || c2 < 0)
37 			return -EINVAL;
38 		if (c1 != c2)
39 			return 1;
40 	} while (c1);
41 
42 	return 0;
43 }
44 EXPORT_SYMBOL(utf8_strncmp);
45 
46 int utf8_strncasecmp(const struct unicode_map *um,
47 		     const struct qstr *s1, const struct qstr *s2)
48 {
49 	struct utf8cursor cur1, cur2;
50 	int c1, c2;
51 
52 	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
53 		return -EINVAL;
54 
55 	if (utf8ncursor(&cur2, um, UTF8_NFDICF, s2->name, s2->len) < 0)
56 		return -EINVAL;
57 
58 	do {
59 		c1 = utf8byte(&cur1);
60 		c2 = utf8byte(&cur2);
61 
62 		if (c1 < 0 || c2 < 0)
63 			return -EINVAL;
64 		if (c1 != c2)
65 			return 1;
66 	} while (c1);
67 
68 	return 0;
69 }
70 EXPORT_SYMBOL(utf8_strncasecmp);
71 
72 /* String cf is expected to be a valid UTF-8 casefolded
73  * string.
74  */
75 int utf8_strncasecmp_folded(const struct unicode_map *um,
76 			    const struct qstr *cf,
77 			    const struct qstr *s1)
78 {
79 	struct utf8cursor cur1;
80 	int c1, c2;
81 	int i = 0;
82 
83 	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
84 		return -EINVAL;
85 
86 	do {
87 		c1 = utf8byte(&cur1);
88 		c2 = cf->name[i++];
89 		if (c1 < 0)
90 			return -EINVAL;
91 		if (c1 != c2)
92 			return 1;
93 	} while (c1);
94 
95 	return 0;
96 }
97 EXPORT_SYMBOL(utf8_strncasecmp_folded);
98 
99 int utf8_casefold(const struct unicode_map *um, const struct qstr *str,
100 		  unsigned char *dest, size_t dlen)
101 {
102 	struct utf8cursor cur;
103 	size_t nlen = 0;
104 
105 	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
106 		return -EINVAL;
107 
108 	for (nlen = 0; nlen < dlen; nlen++) {
109 		int c = utf8byte(&cur);
110 
111 		dest[nlen] = c;
112 		if (!c)
113 			return nlen;
114 		if (c == -1)
115 			break;
116 	}
117 	return -EINVAL;
118 }
119 EXPORT_SYMBOL(utf8_casefold);
120 
121 int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
122 		       struct qstr *str)
123 {
124 	struct utf8cursor cur;
125 	int c;
126 	unsigned long hash = init_name_hash(salt);
127 
128 	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
129 		return -EINVAL;
130 
131 	while ((c = utf8byte(&cur))) {
132 		if (c < 0)
133 			return -EINVAL;
134 		hash = partial_name_hash((unsigned char)c, hash);
135 	}
136 	str->hash = end_name_hash(hash);
137 	return 0;
138 }
139 EXPORT_SYMBOL(utf8_casefold_hash);
140 
141 int utf8_normalize(const struct unicode_map *um, const struct qstr *str,
142 		   unsigned char *dest, size_t dlen)
143 {
144 	struct utf8cursor cur;
145 	ssize_t nlen = 0;
146 
147 	if (utf8ncursor(&cur, um, UTF8_NFDI, str->name, str->len) < 0)
148 		return -EINVAL;
149 
150 	for (nlen = 0; nlen < dlen; nlen++) {
151 		int c = utf8byte(&cur);
152 
153 		dest[nlen] = c;
154 		if (!c)
155 			return nlen;
156 		if (c == -1)
157 			break;
158 	}
159 	return -EINVAL;
160 }
161 EXPORT_SYMBOL(utf8_normalize);
162 
163 static const struct utf8data *find_table_version(const struct utf8data *table,
164 		size_t nr_entries, unsigned int version)
165 {
166 	size_t i = nr_entries - 1;
167 
168 	while (version < table[i].maxage)
169 		i--;
170 	if (version > table[i].maxage)
171 		return NULL;
172 	return &table[i];
173 }
174 
175 struct unicode_map *utf8_load(unsigned int version)
176 {
177 	struct unicode_map *um;
178 
179 	um = kzalloc(sizeof(struct unicode_map), GFP_KERNEL);
180 	if (!um)
181 		return ERR_PTR(-ENOMEM);
182 	um->version = version;
183 
184 	um->tables = symbol_request(utf8_data_table);
185 	if (!um->tables)
186 		goto out_free_um;
187 
188 	if (!utf8version_is_supported(um, version))
189 		goto out_symbol_put;
190 	um->ntab[UTF8_NFDI] = find_table_version(um->tables->utf8nfdidata,
191 			um->tables->utf8nfdidata_size, um->version);
192 	if (!um->ntab[UTF8_NFDI])
193 		goto out_symbol_put;
194 	um->ntab[UTF8_NFDICF] = find_table_version(um->tables->utf8nfdicfdata,
195 			um->tables->utf8nfdicfdata_size, um->version);
196 	if (!um->ntab[UTF8_NFDICF])
197 		goto out_symbol_put;
198 	return um;
199 
200 out_symbol_put:
201 	symbol_put(um->tables);
202 out_free_um:
203 	kfree(um);
204 	return ERR_PTR(-EINVAL);
205 }
206 EXPORT_SYMBOL(utf8_load);
207 
208 void utf8_unload(struct unicode_map *um)
209 {
210 	if (um) {
211 		symbol_put(utf8_data_table);
212 		kfree(um);
213 	}
214 }
215 EXPORT_SYMBOL(utf8_unload);
216 
217 MODULE_LICENSE("GPL v2");
218