18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * lib/parser.c - simple parser for mount, etc. options.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/ctype.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/export.h>
98c2ecf20Sopenharmony_ci#include <linux/kstrtox.h>
108c2ecf20Sopenharmony_ci#include <linux/parser.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/**
158c2ecf20Sopenharmony_ci * match_one: - Determines if a string matches a simple pattern
168c2ecf20Sopenharmony_ci * @s: the string to examine for presence of the pattern
178c2ecf20Sopenharmony_ci * @p: the string containing the pattern
188c2ecf20Sopenharmony_ci * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
198c2ecf20Sopenharmony_ci * locations.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Description: Determines if the pattern @p is present in string @s. Can only
228c2ecf20Sopenharmony_ci * match extremely simple token=arg style patterns. If the pattern is found,
238c2ecf20Sopenharmony_ci * the location(s) of the arguments will be returned in the @args array.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_cistatic int match_one(char *s, const char *p, substring_t args[])
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	char *meta;
288c2ecf20Sopenharmony_ci	int argc = 0;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (!p)
318c2ecf20Sopenharmony_ci		return 1;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	while(1) {
348c2ecf20Sopenharmony_ci		int len = -1;
358c2ecf20Sopenharmony_ci		meta = strchr(p, '%');
368c2ecf20Sopenharmony_ci		if (!meta)
378c2ecf20Sopenharmony_ci			return strcmp(p, s) == 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		if (strncmp(p, s, meta-p))
408c2ecf20Sopenharmony_ci			return 0;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		s += meta - p;
438c2ecf20Sopenharmony_ci		p = meta + 1;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if (isdigit(*p))
468c2ecf20Sopenharmony_ci			len = simple_strtoul(p, (char **) &p, 10);
478c2ecf20Sopenharmony_ci		else if (*p == '%') {
488c2ecf20Sopenharmony_ci			if (*s++ != '%')
498c2ecf20Sopenharmony_ci				return 0;
508c2ecf20Sopenharmony_ci			p++;
518c2ecf20Sopenharmony_ci			continue;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		if (argc >= MAX_OPT_ARGS)
558c2ecf20Sopenharmony_ci			return 0;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		args[argc].from = s;
588c2ecf20Sopenharmony_ci		switch (*p++) {
598c2ecf20Sopenharmony_ci		case 's': {
608c2ecf20Sopenharmony_ci			size_t str_len = strlen(s);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci			if (str_len == 0)
638c2ecf20Sopenharmony_ci				return 0;
648c2ecf20Sopenharmony_ci			if (len == -1 || len > str_len)
658c2ecf20Sopenharmony_ci				len = str_len;
668c2ecf20Sopenharmony_ci			args[argc].to = s + len;
678c2ecf20Sopenharmony_ci			break;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci		case 'd':
708c2ecf20Sopenharmony_ci			simple_strtol(s, &args[argc].to, 0);
718c2ecf20Sopenharmony_ci			goto num;
728c2ecf20Sopenharmony_ci		case 'u':
738c2ecf20Sopenharmony_ci			simple_strtoul(s, &args[argc].to, 0);
748c2ecf20Sopenharmony_ci			goto num;
758c2ecf20Sopenharmony_ci		case 'o':
768c2ecf20Sopenharmony_ci			simple_strtoul(s, &args[argc].to, 8);
778c2ecf20Sopenharmony_ci			goto num;
788c2ecf20Sopenharmony_ci		case 'x':
798c2ecf20Sopenharmony_ci			simple_strtoul(s, &args[argc].to, 16);
808c2ecf20Sopenharmony_ci		num:
818c2ecf20Sopenharmony_ci			if (args[argc].to == args[argc].from)
828c2ecf20Sopenharmony_ci				return 0;
838c2ecf20Sopenharmony_ci			break;
848c2ecf20Sopenharmony_ci		default:
858c2ecf20Sopenharmony_ci			return 0;
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci		s = args[argc].to;
888c2ecf20Sopenharmony_ci		argc++;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/**
938c2ecf20Sopenharmony_ci * match_token: - Find a token (and optional args) in a string
948c2ecf20Sopenharmony_ci * @s: the string to examine for token/argument pairs
958c2ecf20Sopenharmony_ci * @table: match_table_t describing the set of allowed option tokens and the
968c2ecf20Sopenharmony_ci * arguments that may be associated with them. Must be terminated with a
978c2ecf20Sopenharmony_ci * &struct match_token whose pattern is set to the NULL pointer.
988c2ecf20Sopenharmony_ci * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
998c2ecf20Sopenharmony_ci * locations.
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * Description: Detects which if any of a set of token strings has been passed
1028c2ecf20Sopenharmony_ci * to it. Tokens can include up to MAX_OPT_ARGS instances of basic c-style
1038c2ecf20Sopenharmony_ci * format identifiers which will be taken into account when matching the
1048c2ecf20Sopenharmony_ci * tokens, and whose locations will be returned in the @args array.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ciint match_token(char *s, const match_table_t table, substring_t args[])
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	const struct match_token *p;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (p = table; !match_one(s, p->pattern, args) ; p++)
1118c2ecf20Sopenharmony_ci		;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return p->token;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_token);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/**
1188c2ecf20Sopenharmony_ci * match_number: scan a number in the given base from a substring_t
1198c2ecf20Sopenharmony_ci * @s: substring to be scanned
1208c2ecf20Sopenharmony_ci * @result: resulting integer on success
1218c2ecf20Sopenharmony_ci * @base: base to use when converting string
1228c2ecf20Sopenharmony_ci *
1238c2ecf20Sopenharmony_ci * Description: Given a &substring_t and a base, attempts to parse the substring
1248c2ecf20Sopenharmony_ci * as a number in that base. On success, sets @result to the integer represented
1258c2ecf20Sopenharmony_ci * by the string and returns 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_cistatic int match_number(substring_t *s, int *result, int base)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	char *endp;
1308c2ecf20Sopenharmony_ci	char *buf;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci	long val;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	buf = match_strdup(s);
1358c2ecf20Sopenharmony_ci	if (!buf)
1368c2ecf20Sopenharmony_ci		return -ENOMEM;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ret = 0;
1398c2ecf20Sopenharmony_ci	val = simple_strtol(buf, &endp, base);
1408c2ecf20Sopenharmony_ci	if (endp == buf)
1418c2ecf20Sopenharmony_ci		ret = -EINVAL;
1428c2ecf20Sopenharmony_ci	else if (val < (long)INT_MIN || val > (long)INT_MAX)
1438c2ecf20Sopenharmony_ci		ret = -ERANGE;
1448c2ecf20Sopenharmony_ci	else
1458c2ecf20Sopenharmony_ci		*result = (int) val;
1468c2ecf20Sopenharmony_ci	kfree(buf);
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/**
1518c2ecf20Sopenharmony_ci * match_u64int: scan a number in the given base from a substring_t
1528c2ecf20Sopenharmony_ci * @s: substring to be scanned
1538c2ecf20Sopenharmony_ci * @result: resulting u64 on success
1548c2ecf20Sopenharmony_ci * @base: base to use when converting string
1558c2ecf20Sopenharmony_ci *
1568c2ecf20Sopenharmony_ci * Description: Given a &substring_t and a base, attempts to parse the substring
1578c2ecf20Sopenharmony_ci * as a number in that base. On success, sets @result to the integer represented
1588c2ecf20Sopenharmony_ci * by the string and returns 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_cistatic int match_u64int(substring_t *s, u64 *result, int base)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	char *buf;
1638c2ecf20Sopenharmony_ci	int ret;
1648c2ecf20Sopenharmony_ci	u64 val;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	buf = match_strdup(s);
1678c2ecf20Sopenharmony_ci	if (!buf)
1688c2ecf20Sopenharmony_ci		return -ENOMEM;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	ret = kstrtoull(buf, base, &val);
1718c2ecf20Sopenharmony_ci	if (!ret)
1728c2ecf20Sopenharmony_ci		*result = val;
1738c2ecf20Sopenharmony_ci	kfree(buf);
1748c2ecf20Sopenharmony_ci	return ret;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci * match_int: - scan a decimal representation of an integer from a substring_t
1798c2ecf20Sopenharmony_ci * @s: substring_t to be scanned
1808c2ecf20Sopenharmony_ci * @result: resulting integer on success
1818c2ecf20Sopenharmony_ci *
1828c2ecf20Sopenharmony_ci * Description: Attempts to parse the &substring_t @s as a decimal integer. On
1838c2ecf20Sopenharmony_ci * success, sets @result to the integer represented by the string and returns 0.
1848c2ecf20Sopenharmony_ci * Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_ciint match_int(substring_t *s, int *result)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	return match_number(s, result, 0);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_int);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/**
1938c2ecf20Sopenharmony_ci * match_u64: - scan a decimal representation of a u64 from
1948c2ecf20Sopenharmony_ci *                  a substring_t
1958c2ecf20Sopenharmony_ci * @s: substring_t to be scanned
1968c2ecf20Sopenharmony_ci * @result: resulting unsigned long long on success
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * Description: Attempts to parse the &substring_t @s as a long decimal
1998c2ecf20Sopenharmony_ci * integer. On success, sets @result to the integer represented by the
2008c2ecf20Sopenharmony_ci * string and returns 0.
2018c2ecf20Sopenharmony_ci * Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_ciint match_u64(substring_t *s, u64 *result)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return match_u64int(s, result, 0);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_u64);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/**
2108c2ecf20Sopenharmony_ci * match_octal: - scan an octal representation of an integer from a substring_t
2118c2ecf20Sopenharmony_ci * @s: substring_t to be scanned
2128c2ecf20Sopenharmony_ci * @result: resulting integer on success
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * Description: Attempts to parse the &substring_t @s as an octal integer. On
2158c2ecf20Sopenharmony_ci * success, sets @result to the integer represented by the string and returns
2168c2ecf20Sopenharmony_ci * 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_ciint match_octal(substring_t *s, int *result)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	return match_number(s, result, 8);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_octal);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/**
2258c2ecf20Sopenharmony_ci * match_hex: - scan a hex representation of an integer from a substring_t
2268c2ecf20Sopenharmony_ci * @s: substring_t to be scanned
2278c2ecf20Sopenharmony_ci * @result: resulting integer on success
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci * Description: Attempts to parse the &substring_t @s as a hexadecimal integer.
2308c2ecf20Sopenharmony_ci * On success, sets @result to the integer represented by the string and
2318c2ecf20Sopenharmony_ci * returns 0. Returns -ENOMEM, -EINVAL, or -ERANGE on failure.
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_ciint match_hex(substring_t *s, int *result)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return match_number(s, result, 16);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_hex);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/**
2408c2ecf20Sopenharmony_ci * match_wildcard: - parse if a string matches given wildcard pattern
2418c2ecf20Sopenharmony_ci * @pattern: wildcard pattern
2428c2ecf20Sopenharmony_ci * @str: the string to be parsed
2438c2ecf20Sopenharmony_ci *
2448c2ecf20Sopenharmony_ci * Description: Parse the string @str to check if matches wildcard
2458c2ecf20Sopenharmony_ci * pattern @pattern. The pattern may contain two type wildcardes:
2468c2ecf20Sopenharmony_ci *   '*' - matches zero or more characters
2478c2ecf20Sopenharmony_ci *   '?' - matches one character
2488c2ecf20Sopenharmony_ci * If it's matched, return true, else return false.
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cibool match_wildcard(const char *pattern, const char *str)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	const char *s = str;
2538c2ecf20Sopenharmony_ci	const char *p = pattern;
2548c2ecf20Sopenharmony_ci	bool star = false;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	while (*s) {
2578c2ecf20Sopenharmony_ci		switch (*p) {
2588c2ecf20Sopenharmony_ci		case '?':
2598c2ecf20Sopenharmony_ci			s++;
2608c2ecf20Sopenharmony_ci			p++;
2618c2ecf20Sopenharmony_ci			break;
2628c2ecf20Sopenharmony_ci		case '*':
2638c2ecf20Sopenharmony_ci			star = true;
2648c2ecf20Sopenharmony_ci			str = s;
2658c2ecf20Sopenharmony_ci			if (!*++p)
2668c2ecf20Sopenharmony_ci				return true;
2678c2ecf20Sopenharmony_ci			pattern = p;
2688c2ecf20Sopenharmony_ci			break;
2698c2ecf20Sopenharmony_ci		default:
2708c2ecf20Sopenharmony_ci			if (*s == *p) {
2718c2ecf20Sopenharmony_ci				s++;
2728c2ecf20Sopenharmony_ci				p++;
2738c2ecf20Sopenharmony_ci			} else {
2748c2ecf20Sopenharmony_ci				if (!star)
2758c2ecf20Sopenharmony_ci					return false;
2768c2ecf20Sopenharmony_ci				str++;
2778c2ecf20Sopenharmony_ci				s = str;
2788c2ecf20Sopenharmony_ci				p = pattern;
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (*p == '*')
2858c2ecf20Sopenharmony_ci		++p;
2868c2ecf20Sopenharmony_ci	return !*p;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_wildcard);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/**
2918c2ecf20Sopenharmony_ci * match_strlcpy: - Copy the characters from a substring_t to a sized buffer
2928c2ecf20Sopenharmony_ci * @dest: where to copy to
2938c2ecf20Sopenharmony_ci * @src: &substring_t to copy
2948c2ecf20Sopenharmony_ci * @size: size of destination buffer
2958c2ecf20Sopenharmony_ci *
2968c2ecf20Sopenharmony_ci * Description: Copy the characters in &substring_t @src to the
2978c2ecf20Sopenharmony_ci * c-style string @dest.  Copy no more than @size - 1 characters, plus
2988c2ecf20Sopenharmony_ci * the terminating NUL.  Return length of @src.
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cisize_t match_strlcpy(char *dest, const substring_t *src, size_t size)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	size_t ret = src->to - src->from;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (size) {
3058c2ecf20Sopenharmony_ci		size_t len = ret >= size ? size - 1 : ret;
3068c2ecf20Sopenharmony_ci		memcpy(dest, src->from, len);
3078c2ecf20Sopenharmony_ci		dest[len] = '\0';
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	return ret;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_strlcpy);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/**
3148c2ecf20Sopenharmony_ci * match_strdup: - allocate a new string with the contents of a substring_t
3158c2ecf20Sopenharmony_ci * @s: &substring_t to copy
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * Description: Allocates and returns a string filled with the contents of
3188c2ecf20Sopenharmony_ci * the &substring_t @s. The caller is responsible for freeing the returned
3198c2ecf20Sopenharmony_ci * string with kfree().
3208c2ecf20Sopenharmony_ci */
3218c2ecf20Sopenharmony_cichar *match_strdup(const substring_t *s)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	return kmemdup_nul(s->from, s->to - s->from, GFP_KERNEL);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(match_strdup);
326