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