xref: /linux/fs/unicode/utf8-core.c (revision 1dbb7422053fa0f4fb5f9acc3931617fc0091939)
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/rcupdate.h>
7 #include <linux/parser.h>
8 #include <linux/errno.h>
9 #include <linux/stringhash.h>
10 
11 #include "utf8n.h"
12 
utf8_validate(const struct unicode_map * um,const struct qstr * str)13 int utf8_validate(const struct unicode_map *um, const struct qstr *str)
14 {
15 	if (utf8nlen(um, UTF8_NFDI, str->name, str->len) < 0)
16 		return -1;
17 	return 0;
18 }
19 EXPORT_SYMBOL(utf8_validate);
20 
utf8_strncmp(const struct unicode_map * um,const struct qstr * s1,const struct qstr * s2)21 int utf8_strncmp(const struct unicode_map *um,
22 		 const struct qstr *s1, const struct qstr *s2)
23 {
24 	struct utf8cursor cur1, cur2;
25 	int c1, c2;
26 
27 	if (utf8ncursor(&cur1, um, UTF8_NFDI, s1->name, s1->len) < 0)
28 		return -EINVAL;
29 
30 	if (utf8ncursor(&cur2, um, UTF8_NFDI, s2->name, s2->len) < 0)
31 		return -EINVAL;
32 
33 	do {
34 		c1 = utf8byte(&cur1);
35 		c2 = utf8byte(&cur2);
36 
37 		if (c1 < 0 || c2 < 0)
38 			return -EINVAL;
39 		if (c1 != c2)
40 			return 1;
41 	} while (c1);
42 
43 	return 0;
44 }
45 EXPORT_SYMBOL(utf8_strncmp);
46 
utf8_strncasecmp(const struct unicode_map * um,const struct qstr * s1,const struct qstr * s2)47 int utf8_strncasecmp(const struct unicode_map *um,
48 		     const struct qstr *s1, const struct qstr *s2)
49 {
50 	struct utf8cursor cur1, cur2;
51 	int c1, c2;
52 
53 	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
54 		return -EINVAL;
55 
56 	if (utf8ncursor(&cur2, um, UTF8_NFDICF, s2->name, s2->len) < 0)
57 		return -EINVAL;
58 
59 	do {
60 		c1 = utf8byte(&cur1);
61 		c2 = utf8byte(&cur2);
62 
63 		if (c1 < 0 || c2 < 0)
64 			return -EINVAL;
65 		if (c1 != c2)
66 			return 1;
67 	} while (c1);
68 
69 	return 0;
70 }
71 EXPORT_SYMBOL(utf8_strncasecmp);
72 
73 /* String cf is expected to be a valid UTF-8 casefolded
74  * string.
75  */
utf8_strncasecmp_folded(const struct unicode_map * um,const struct qstr * cf,const struct qstr * s1)76 int utf8_strncasecmp_folded(const struct unicode_map *um,
77 			    const struct qstr *cf,
78 			    const struct qstr *s1)
79 {
80 	struct utf8cursor cur1;
81 	int c1, c2;
82 	int i = 0;
83 
84 	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
85 		return -EINVAL;
86 
87 	do {
88 		c1 = utf8byte(&cur1);
89 		c2 = cf->name[i++];
90 		if (c1 < 0)
91 			return -EINVAL;
92 		if (c1 != c2)
93 			return 1;
94 	} while (c1);
95 
96 	return 0;
97 }
98 EXPORT_SYMBOL(utf8_strncasecmp_folded);
99 
utf8_casefold(const struct unicode_map * um,const struct qstr * str,unsigned char * dest,size_t dlen)100 int utf8_casefold(const struct unicode_map *um, const struct qstr *str,
101 		  unsigned char *dest, size_t dlen)
102 {
103 	struct utf8cursor cur;
104 	size_t nlen = 0;
105 
106 	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
107 		return -EINVAL;
108 
109 	for (nlen = 0; nlen < dlen; nlen++) {
110 		int c = utf8byte(&cur);
111 
112 		dest[nlen] = c;
113 		if (!c)
114 			return nlen;
115 		if (c == -1)
116 			break;
117 	}
118 	return -EINVAL;
119 }
120 EXPORT_SYMBOL(utf8_casefold);
121 
utf8_casefold_hash(const struct unicode_map * um,const void * salt,struct qstr * str)122 int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
123 		       struct qstr *str)
124 {
125 	struct utf8cursor cur;
126 	int c;
127 	unsigned long hash = init_name_hash(salt);
128 
129 	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
130 		return -EINVAL;
131 
132 	while ((c = utf8byte(&cur))) {
133 		if (c < 0)
134 			return -EINVAL;
135 		hash = partial_name_hash((unsigned char)c, hash);
136 	}
137 	str->hash = end_name_hash(hash);
138 	return 0;
139 }
140 EXPORT_SYMBOL(utf8_casefold_hash);
141 
find_table_version(const struct utf8data * table,size_t nr_entries,unsigned int version)142 static const struct utf8data *find_table_version(const struct utf8data *table,
143 		size_t nr_entries, unsigned int version)
144 {
145 	size_t i = nr_entries - 1;
146 
147 	while (version < table[i].maxage)
148 		i--;
149 	if (version > table[i].maxage)
150 		return NULL;
151 	return &table[i];
152 }
153 
utf8_load(unsigned int version)154 struct unicode_map *utf8_load(unsigned int version)
155 {
156 	struct unicode_map *um;
157 
158 	um = kzalloc_obj(struct unicode_map);
159 	if (!um)
160 		return ERR_PTR(-ENOMEM);
161 	um->version = version;
162 
163 	um->tables = symbol_request(utf8_data_table);
164 	if (!um->tables)
165 		goto out_free_um;
166 
167 	if (!utf8version_is_supported(um, version))
168 		goto out_symbol_put;
169 	um->ntab[UTF8_NFDI] = find_table_version(um->tables->utf8nfdidata,
170 			um->tables->utf8nfdidata_size, um->version);
171 	if (!um->ntab[UTF8_NFDI])
172 		goto out_symbol_put;
173 	um->ntab[UTF8_NFDICF] = find_table_version(um->tables->utf8nfdicfdata,
174 			um->tables->utf8nfdicfdata_size, um->version);
175 	if (!um->ntab[UTF8_NFDICF])
176 		goto out_symbol_put;
177 	return um;
178 
179 out_symbol_put:
180 	symbol_put(utf8_data_table);
181 out_free_um:
182 	kfree(um);
183 	return ERR_PTR(-EINVAL);
184 }
185 EXPORT_SYMBOL(utf8_load);
186 
utf8_unload_rcu(struct rcu_head * head)187 static void utf8_unload_rcu(struct rcu_head *head)
188 {
189 	struct unicode_map *um = container_of(head, struct unicode_map, rcu);
190 
191 	symbol_put(utf8_data_table);
192 	kfree(um);
193 }
194 
195 /*
196  * A pathwalk in rcu mode may still be in ->d_hash() or ->d_compare() of a
197  * filesystem that is being torn down and read the map through its
198  * superblock, so the map and the tables it points to have to outlive the
199  * grace period.
200  */
utf8_unload(struct unicode_map * um)201 void utf8_unload(struct unicode_map *um)
202 {
203 	if (um)
204 		call_rcu(&um->rcu, utf8_unload_rcu);
205 }
206 EXPORT_SYMBOL(utf8_unload);
207 
208 /**
209  * utf8_parse_version - Parse a UTF-8 version number from a string
210  *
211  * @version: input string
212  *
213  * Returns the parsed version on success, negative code on error
214  */
utf8_parse_version(char * version)215 int utf8_parse_version(char *version)
216 {
217 	substring_t args[3];
218 	unsigned int maj, min, rev;
219 	static const struct match_token token[] = {
220 		{1, "%u.%u.%u"},
221 		{0, NULL}
222 	};
223 
224 	if (match_token(version, token, args) != 1)
225 		return -EINVAL;
226 
227 	if (match_uint(&args[0], &maj) || match_uint(&args[1], &min) ||
228 	    match_uint(&args[2], &rev))
229 		return -EINVAL;
230 
231 	if (maj > U8_MAX || min > U8_MAX || rev > U8_MAX)
232 		return -EINVAL;
233 	return UNICODE_AGE(maj, min, rev);
234 }
235 EXPORT_SYMBOL(utf8_parse_version);
236