162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci#include <linux/string.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci#include <linux/parser.h>
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/stringhash.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "utf8n.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciint utf8_validate(const struct unicode_map *um, const struct qstr *str)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	if (utf8nlen(um, UTF8_NFDI, str->name, str->len) < 0)
1562306a36Sopenharmony_ci		return -1;
1662306a36Sopenharmony_ci	return 0;
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_validate);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint utf8_strncmp(const struct unicode_map *um,
2162306a36Sopenharmony_ci		 const struct qstr *s1, const struct qstr *s2)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct utf8cursor cur1, cur2;
2462306a36Sopenharmony_ci	int c1, c2;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (utf8ncursor(&cur1, um, UTF8_NFDI, s1->name, s1->len) < 0)
2762306a36Sopenharmony_ci		return -EINVAL;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (utf8ncursor(&cur2, um, UTF8_NFDI, s2->name, s2->len) < 0)
3062306a36Sopenharmony_ci		return -EINVAL;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	do {
3362306a36Sopenharmony_ci		c1 = utf8byte(&cur1);
3462306a36Sopenharmony_ci		c2 = utf8byte(&cur2);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		if (c1 < 0 || c2 < 0)
3762306a36Sopenharmony_ci			return -EINVAL;
3862306a36Sopenharmony_ci		if (c1 != c2)
3962306a36Sopenharmony_ci			return 1;
4062306a36Sopenharmony_ci	} while (c1);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_strncmp);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint utf8_strncasecmp(const struct unicode_map *um,
4762306a36Sopenharmony_ci		     const struct qstr *s1, const struct qstr *s2)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct utf8cursor cur1, cur2;
5062306a36Sopenharmony_ci	int c1, c2;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
5362306a36Sopenharmony_ci		return -EINVAL;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (utf8ncursor(&cur2, um, UTF8_NFDICF, s2->name, s2->len) < 0)
5662306a36Sopenharmony_ci		return -EINVAL;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	do {
5962306a36Sopenharmony_ci		c1 = utf8byte(&cur1);
6062306a36Sopenharmony_ci		c2 = utf8byte(&cur2);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		if (c1 < 0 || c2 < 0)
6362306a36Sopenharmony_ci			return -EINVAL;
6462306a36Sopenharmony_ci		if (c1 != c2)
6562306a36Sopenharmony_ci			return 1;
6662306a36Sopenharmony_ci	} while (c1);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_strncasecmp);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* String cf is expected to be a valid UTF-8 casefolded
7362306a36Sopenharmony_ci * string.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ciint utf8_strncasecmp_folded(const struct unicode_map *um,
7662306a36Sopenharmony_ci			    const struct qstr *cf,
7762306a36Sopenharmony_ci			    const struct qstr *s1)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct utf8cursor cur1;
8062306a36Sopenharmony_ci	int c1, c2;
8162306a36Sopenharmony_ci	int i = 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (utf8ncursor(&cur1, um, UTF8_NFDICF, s1->name, s1->len) < 0)
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	do {
8762306a36Sopenharmony_ci		c1 = utf8byte(&cur1);
8862306a36Sopenharmony_ci		c2 = cf->name[i++];
8962306a36Sopenharmony_ci		if (c1 < 0)
9062306a36Sopenharmony_ci			return -EINVAL;
9162306a36Sopenharmony_ci		if (c1 != c2)
9262306a36Sopenharmony_ci			return 1;
9362306a36Sopenharmony_ci	} while (c1);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_strncasecmp_folded);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciint utf8_casefold(const struct unicode_map *um, const struct qstr *str,
10062306a36Sopenharmony_ci		  unsigned char *dest, size_t dlen)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct utf8cursor cur;
10362306a36Sopenharmony_ci	size_t nlen = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
10662306a36Sopenharmony_ci		return -EINVAL;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (nlen = 0; nlen < dlen; nlen++) {
10962306a36Sopenharmony_ci		int c = utf8byte(&cur);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		dest[nlen] = c;
11262306a36Sopenharmony_ci		if (!c)
11362306a36Sopenharmony_ci			return nlen;
11462306a36Sopenharmony_ci		if (c == -1)
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	return -EINVAL;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_casefold);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciint utf8_casefold_hash(const struct unicode_map *um, const void *salt,
12262306a36Sopenharmony_ci		       struct qstr *str)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct utf8cursor cur;
12562306a36Sopenharmony_ci	int c;
12662306a36Sopenharmony_ci	unsigned long hash = init_name_hash(salt);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (utf8ncursor(&cur, um, UTF8_NFDICF, str->name, str->len) < 0)
12962306a36Sopenharmony_ci		return -EINVAL;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	while ((c = utf8byte(&cur))) {
13262306a36Sopenharmony_ci		if (c < 0)
13362306a36Sopenharmony_ci			return -EINVAL;
13462306a36Sopenharmony_ci		hash = partial_name_hash((unsigned char)c, hash);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	str->hash = end_name_hash(hash);
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_casefold_hash);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciint utf8_normalize(const struct unicode_map *um, const struct qstr *str,
14262306a36Sopenharmony_ci		   unsigned char *dest, size_t dlen)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct utf8cursor cur;
14562306a36Sopenharmony_ci	ssize_t nlen = 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (utf8ncursor(&cur, um, UTF8_NFDI, str->name, str->len) < 0)
14862306a36Sopenharmony_ci		return -EINVAL;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	for (nlen = 0; nlen < dlen; nlen++) {
15162306a36Sopenharmony_ci		int c = utf8byte(&cur);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		dest[nlen] = c;
15462306a36Sopenharmony_ci		if (!c)
15562306a36Sopenharmony_ci			return nlen;
15662306a36Sopenharmony_ci		if (c == -1)
15762306a36Sopenharmony_ci			break;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	return -EINVAL;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_normalize);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const struct utf8data *find_table_version(const struct utf8data *table,
16462306a36Sopenharmony_ci		size_t nr_entries, unsigned int version)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	size_t i = nr_entries - 1;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	while (version < table[i].maxage)
16962306a36Sopenharmony_ci		i--;
17062306a36Sopenharmony_ci	if (version > table[i].maxage)
17162306a36Sopenharmony_ci		return NULL;
17262306a36Sopenharmony_ci	return &table[i];
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistruct unicode_map *utf8_load(unsigned int version)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct unicode_map *um;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	um = kzalloc(sizeof(struct unicode_map), GFP_KERNEL);
18062306a36Sopenharmony_ci	if (!um)
18162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
18262306a36Sopenharmony_ci	um->version = version;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	um->tables = symbol_request(utf8_data_table);
18562306a36Sopenharmony_ci	if (!um->tables)
18662306a36Sopenharmony_ci		goto out_free_um;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!utf8version_is_supported(um, version))
18962306a36Sopenharmony_ci		goto out_symbol_put;
19062306a36Sopenharmony_ci	um->ntab[UTF8_NFDI] = find_table_version(um->tables->utf8nfdidata,
19162306a36Sopenharmony_ci			um->tables->utf8nfdidata_size, um->version);
19262306a36Sopenharmony_ci	if (!um->ntab[UTF8_NFDI])
19362306a36Sopenharmony_ci		goto out_symbol_put;
19462306a36Sopenharmony_ci	um->ntab[UTF8_NFDICF] = find_table_version(um->tables->utf8nfdicfdata,
19562306a36Sopenharmony_ci			um->tables->utf8nfdicfdata_size, um->version);
19662306a36Sopenharmony_ci	if (!um->ntab[UTF8_NFDICF])
19762306a36Sopenharmony_ci		goto out_symbol_put;
19862306a36Sopenharmony_ci	return um;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciout_symbol_put:
20162306a36Sopenharmony_ci	symbol_put(um->tables);
20262306a36Sopenharmony_ciout_free_um:
20362306a36Sopenharmony_ci	kfree(um);
20462306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_load);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_civoid utf8_unload(struct unicode_map *um)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	if (um) {
21162306a36Sopenharmony_ci		symbol_put(utf8_data_table);
21262306a36Sopenharmony_ci		kfree(um);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ciEXPORT_SYMBOL(utf8_unload);
21662306a36Sopenharmony_ci
217