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