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 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 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 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 */ 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 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 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 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 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 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 */ 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 */ 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