140b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * lib/parser.c - simple parser for mount, etc. options.
41da177e4SLinus Torvalds */
51da177e4SLinus Torvalds
61da177e4SLinus Torvalds #include <linux/ctype.h>
78bc3bcc9SPaul Gortmaker #include <linux/types.h>
88bc3bcc9SPaul Gortmaker #include <linux/export.h>
94c527293SAndy Shevchenko #include <linux/kstrtox.h>
101da177e4SLinus Torvalds #include <linux/parser.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/string.h>
131da177e4SLinus Torvalds
1467222c4bSLi Lingfeng /*
1567222c4bSLi Lingfeng * max size needed by different bases to express U64
1667222c4bSLi Lingfeng * HEX: "0xFFFFFFFFFFFFFFFF" --> 18
1767222c4bSLi Lingfeng * DEC: "18446744073709551615" --> 20
1867222c4bSLi Lingfeng * OCT: "01777777777777777777777" --> 23
1967222c4bSLi Lingfeng * pick the max one to define NUMBER_BUF_LEN
2067222c4bSLi Lingfeng */
2167222c4bSLi Lingfeng #define NUMBER_BUF_LEN 24
2267222c4bSLi Lingfeng
231da177e4SLinus Torvalds /**
24b9bffa10SBingJing Chang * match_one - Determines if a string matches a simple pattern
2525985edcSLucas De Marchi * @s: the string to examine for presence of the pattern
261da177e4SLinus Torvalds * @p: the string containing the pattern
271da177e4SLinus Torvalds * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
281da177e4SLinus Torvalds * locations.
291da177e4SLinus Torvalds *
301da177e4SLinus Torvalds * Description: Determines if the pattern @p is present in string @s. Can only
311da177e4SLinus Torvalds * match extremely simple token=arg style patterns. If the pattern is found,
321da177e4SLinus Torvalds * the location(s) of the arguments will be returned in the @args array.
331da177e4SLinus Torvalds */
match_one(char * s,const char * p,substring_t args[])34ef4533f8SDavid Howells static int match_one(char *s, const char *p, substring_t args[])
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds char *meta;
371da177e4SLinus Torvalds int argc = 0;
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds if (!p)
401da177e4SLinus Torvalds return 1;
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds while(1) {
431da177e4SLinus Torvalds int len = -1;
441da177e4SLinus Torvalds meta = strchr(p, '%');
451da177e4SLinus Torvalds if (!meta)
461da177e4SLinus Torvalds return strcmp(p, s) == 0;
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds if (strncmp(p, s, meta-p))
491da177e4SLinus Torvalds return 0;
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds s += meta - p;
521da177e4SLinus Torvalds p = meta + 1;
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds if (isdigit(*p))
55ef4533f8SDavid Howells len = simple_strtoul(p, (char **) &p, 10);
561da177e4SLinus Torvalds else if (*p == '%') {
571da177e4SLinus Torvalds if (*s++ != '%')
581da177e4SLinus Torvalds return 0;
591da177e4SLinus Torvalds p++;
601da177e4SLinus Torvalds continue;
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds if (argc >= MAX_OPT_ARGS)
641da177e4SLinus Torvalds return 0;
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds args[argc].from = s;
671da177e4SLinus Torvalds switch (*p++) {
68b5f54b07SAndré Goddard Rosa case 's': {
69b5f54b07SAndré Goddard Rosa size_t str_len = strlen(s);
70b5f54b07SAndré Goddard Rosa
71b5f54b07SAndré Goddard Rosa if (str_len == 0)
721da177e4SLinus Torvalds return 0;
73b5f54b07SAndré Goddard Rosa if (len == -1 || len > str_len)
74b5f54b07SAndré Goddard Rosa len = str_len;
751da177e4SLinus Torvalds args[argc].to = s + len;
761da177e4SLinus Torvalds break;
77b5f54b07SAndré Goddard Rosa }
781da177e4SLinus Torvalds case 'd':
791da177e4SLinus Torvalds simple_strtol(s, &args[argc].to, 0);
801da177e4SLinus Torvalds goto num;
811da177e4SLinus Torvalds case 'u':
821da177e4SLinus Torvalds simple_strtoul(s, &args[argc].to, 0);
831da177e4SLinus Torvalds goto num;
841da177e4SLinus Torvalds case 'o':
851da177e4SLinus Torvalds simple_strtoul(s, &args[argc].to, 8);
861da177e4SLinus Torvalds goto num;
871da177e4SLinus Torvalds case 'x':
881da177e4SLinus Torvalds simple_strtoul(s, &args[argc].to, 16);
891da177e4SLinus Torvalds num:
901da177e4SLinus Torvalds if (args[argc].to == args[argc].from)
911da177e4SLinus Torvalds return 0;
921da177e4SLinus Torvalds break;
931da177e4SLinus Torvalds default:
941da177e4SLinus Torvalds return 0;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds s = args[argc].to;
971da177e4SLinus Torvalds argc++;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds /**
102b9bffa10SBingJing Chang * match_token - Find a token (and optional args) in a string
1031da177e4SLinus Torvalds * @s: the string to examine for token/argument pairs
1041da177e4SLinus Torvalds * @table: match_table_t describing the set of allowed option tokens and the
1051da177e4SLinus Torvalds * arguments that may be associated with them. Must be terminated with a
1061da177e4SLinus Torvalds * &struct match_token whose pattern is set to the NULL pointer.
1071da177e4SLinus Torvalds * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match
1081da177e4SLinus Torvalds * locations.
1091da177e4SLinus Torvalds *
1101da177e4SLinus Torvalds * Description: Detects which if any of a set of token strings has been passed
111edd9334cSRandy Dunlap * to it. Tokens can include up to %MAX_OPT_ARGS instances of basic c-style
1121da177e4SLinus Torvalds * format identifiers which will be taken into account when matching the
1131da177e4SLinus Torvalds * tokens, and whose locations will be returned in the @args array.
1141da177e4SLinus Torvalds */
match_token(char * s,const match_table_t table,substring_t args[])115a447c093SSteven Whitehouse int match_token(char *s, const match_table_t table, substring_t args[])
1161da177e4SLinus Torvalds {
117ef4533f8SDavid Howells const struct match_token *p;
1181da177e4SLinus Torvalds
1191da177e4SLinus Torvalds for (p = table; !match_one(s, p->pattern, args) ; p++)
1201da177e4SLinus Torvalds ;
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds return p->token;
1231da177e4SLinus Torvalds }
124a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_token);
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds /**
127b9bffa10SBingJing Chang * match_number - scan a number in the given base from a substring_t
1281da177e4SLinus Torvalds * @s: substring to be scanned
1291da177e4SLinus Torvalds * @result: resulting integer on success
1301da177e4SLinus Torvalds * @base: base to use when converting string
1311da177e4SLinus Torvalds *
1321da177e4SLinus Torvalds * Description: Given a &substring_t and a base, attempts to parse the substring
133edd9334cSRandy Dunlap * as a number in that base.
134edd9334cSRandy Dunlap *
135edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the
136*359d6255SEric Biggers * string and returns 0. Returns -EINVAL or -ERANGE on failure.
1371da177e4SLinus Torvalds */
match_number(substring_t * s,int * result,int base)1381da177e4SLinus Torvalds static int match_number(substring_t *s, int *result, int base)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds char *endp;
14167222c4bSLi Lingfeng char buf[NUMBER_BUF_LEN];
1421da177e4SLinus Torvalds int ret;
14377dd3b0bSAlex Elder long val;
1441da177e4SLinus Torvalds
14567222c4bSLi Lingfeng if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
14667222c4bSLi Lingfeng return -ERANGE;
1471da177e4SLinus Torvalds ret = 0;
14877dd3b0bSAlex Elder val = simple_strtol(buf, &endp, base);
1491da177e4SLinus Torvalds if (endp == buf)
1501da177e4SLinus Torvalds ret = -EINVAL;
15177dd3b0bSAlex Elder else if (val < (long)INT_MIN || val > (long)INT_MAX)
15277dd3b0bSAlex Elder ret = -ERANGE;
15377dd3b0bSAlex Elder else
15477dd3b0bSAlex Elder *result = (int) val;
1551da177e4SLinus Torvalds return ret;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds /**
159b9bffa10SBingJing Chang * match_u64int - scan a number in the given base from a substring_t
160a317178eSJames Smart * @s: substring to be scanned
161a317178eSJames Smart * @result: resulting u64 on success
162a317178eSJames Smart * @base: base to use when converting string
163a317178eSJames Smart *
164a317178eSJames Smart * Description: Given a &substring_t and a base, attempts to parse the substring
165edd9334cSRandy Dunlap * as a number in that base.
166edd9334cSRandy Dunlap *
167edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the
168*359d6255SEric Biggers * string and returns 0. Returns -EINVAL or -ERANGE on failure.
169a317178eSJames Smart */
match_u64int(substring_t * s,u64 * result,int base)170a317178eSJames Smart static int match_u64int(substring_t *s, u64 *result, int base)
171a317178eSJames Smart {
17267222c4bSLi Lingfeng char buf[NUMBER_BUF_LEN];
173a317178eSJames Smart int ret;
174a317178eSJames Smart u64 val;
175a317178eSJames Smart
17667222c4bSLi Lingfeng if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
17767222c4bSLi Lingfeng return -ERANGE;
178a317178eSJames Smart ret = kstrtoull(buf, base, &val);
179a317178eSJames Smart if (!ret)
180a317178eSJames Smart *result = val;
181a317178eSJames Smart return ret;
182a317178eSJames Smart }
183a317178eSJames Smart
184a317178eSJames Smart /**
185b9bffa10SBingJing Chang * match_int - scan a decimal representation of an integer from a substring_t
1861da177e4SLinus Torvalds * @s: substring_t to be scanned
1871da177e4SLinus Torvalds * @result: resulting integer on success
1881da177e4SLinus Torvalds *
189edd9334cSRandy Dunlap * Description: Attempts to parse the &substring_t @s as a decimal integer.
190edd9334cSRandy Dunlap *
191edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the string
192*359d6255SEric Biggers * and returns 0. Returns -EINVAL or -ERANGE on failure.
1931da177e4SLinus Torvalds */
match_int(substring_t * s,int * result)1941da177e4SLinus Torvalds int match_int(substring_t *s, int *result)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds return match_number(s, result, 0);
1971da177e4SLinus Torvalds }
198a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_int);
1991da177e4SLinus Torvalds
200edd9334cSRandy Dunlap /**
20101531ac3SBingJing Chang * match_uint - scan a decimal representation of an integer from a substring_t
20201531ac3SBingJing Chang * @s: substring_t to be scanned
20301531ac3SBingJing Chang * @result: resulting integer on success
20401531ac3SBingJing Chang *
205edd9334cSRandy Dunlap * Description: Attempts to parse the &substring_t @s as a decimal integer.
206edd9334cSRandy Dunlap *
207edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the string
208*359d6255SEric Biggers * and returns 0. Returns -EINVAL or -ERANGE on failure.
20901531ac3SBingJing Chang */
match_uint(substring_t * s,unsigned int * result)21001531ac3SBingJing Chang int match_uint(substring_t *s, unsigned int *result)
21101531ac3SBingJing Chang {
21267222c4bSLi Lingfeng char buf[NUMBER_BUF_LEN];
21301531ac3SBingJing Chang
21467222c4bSLi Lingfeng if (match_strlcpy(buf, s, NUMBER_BUF_LEN) >= NUMBER_BUF_LEN)
21567222c4bSLi Lingfeng return -ERANGE;
21667222c4bSLi Lingfeng
21767222c4bSLi Lingfeng return kstrtouint(buf, 10, result);
21801531ac3SBingJing Chang }
21901531ac3SBingJing Chang EXPORT_SYMBOL(match_uint);
22001531ac3SBingJing Chang
2211da177e4SLinus Torvalds /**
222b9bffa10SBingJing Chang * match_u64 - scan a decimal representation of a u64 from
223a317178eSJames Smart * a substring_t
224a317178eSJames Smart * @s: substring_t to be scanned
225a317178eSJames Smart * @result: resulting unsigned long long on success
226a317178eSJames Smart *
227a317178eSJames Smart * Description: Attempts to parse the &substring_t @s as a long decimal
228edd9334cSRandy Dunlap * integer.
229edd9334cSRandy Dunlap *
230edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the string
231*359d6255SEric Biggers * and returns 0. Returns -EINVAL or -ERANGE on failure.
232a317178eSJames Smart */
match_u64(substring_t * s,u64 * result)233a317178eSJames Smart int match_u64(substring_t *s, u64 *result)
234a317178eSJames Smart {
235a317178eSJames Smart return match_u64int(s, result, 0);
236a317178eSJames Smart }
237a317178eSJames Smart EXPORT_SYMBOL(match_u64);
238a317178eSJames Smart
239a317178eSJames Smart /**
240b9bffa10SBingJing Chang * match_octal - scan an octal representation of an integer from a substring_t
2411da177e4SLinus Torvalds * @s: substring_t to be scanned
2421da177e4SLinus Torvalds * @result: resulting integer on success
2431da177e4SLinus Torvalds *
244edd9334cSRandy Dunlap * Description: Attempts to parse the &substring_t @s as an octal integer.
245edd9334cSRandy Dunlap *
246edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the string
247*359d6255SEric Biggers * and returns 0. Returns -EINVAL or -ERANGE on failure.
2481da177e4SLinus Torvalds */
match_octal(substring_t * s,int * result)2491da177e4SLinus Torvalds int match_octal(substring_t *s, int *result)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds return match_number(s, result, 8);
2521da177e4SLinus Torvalds }
253a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_octal);
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds /**
256b9bffa10SBingJing Chang * match_hex - scan a hex representation of an integer from a substring_t
2571da177e4SLinus Torvalds * @s: substring_t to be scanned
2581da177e4SLinus Torvalds * @result: resulting integer on success
2591da177e4SLinus Torvalds *
2601da177e4SLinus Torvalds * Description: Attempts to parse the &substring_t @s as a hexadecimal integer.
261edd9334cSRandy Dunlap *
262edd9334cSRandy Dunlap * Return: On success, sets @result to the integer represented by the string
263*359d6255SEric Biggers * and returns 0. Returns -EINVAL or -ERANGE on failure.
2641da177e4SLinus Torvalds */
match_hex(substring_t * s,int * result)2651da177e4SLinus Torvalds int match_hex(substring_t *s, int *result)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds return match_number(s, result, 16);
2681da177e4SLinus Torvalds }
269a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_hex);
2701da177e4SLinus Torvalds
2711da177e4SLinus Torvalds /**
272b9bffa10SBingJing Chang * match_wildcard - parse if a string matches given wildcard pattern
273aace0509SDu, Changbin * @pattern: wildcard pattern
274aace0509SDu, Changbin * @str: the string to be parsed
275aace0509SDu, Changbin *
276aace0509SDu, Changbin * Description: Parse the string @str to check if matches wildcard
277edd9334cSRandy Dunlap * pattern @pattern. The pattern may contain two types of wildcards:
278aace0509SDu, Changbin * '*' - matches zero or more characters
279aace0509SDu, Changbin * '?' - matches one character
280edd9334cSRandy Dunlap *
281edd9334cSRandy Dunlap * Return: If the @str matches the @pattern, return true, else return false.
282aace0509SDu, Changbin */
match_wildcard(const char * pattern,const char * str)283aace0509SDu, Changbin bool match_wildcard(const char *pattern, const char *str)
284aace0509SDu, Changbin {
285aace0509SDu, Changbin const char *s = str;
286aace0509SDu, Changbin const char *p = pattern;
287aace0509SDu, Changbin bool star = false;
288aace0509SDu, Changbin
289aace0509SDu, Changbin while (*s) {
290aace0509SDu, Changbin switch (*p) {
291aace0509SDu, Changbin case '?':
292aace0509SDu, Changbin s++;
293aace0509SDu, Changbin p++;
294aace0509SDu, Changbin break;
295aace0509SDu, Changbin case '*':
296aace0509SDu, Changbin star = true;
297aace0509SDu, Changbin str = s;
298aace0509SDu, Changbin if (!*++p)
299aace0509SDu, Changbin return true;
300aace0509SDu, Changbin pattern = p;
301aace0509SDu, Changbin break;
302aace0509SDu, Changbin default:
303aace0509SDu, Changbin if (*s == *p) {
304aace0509SDu, Changbin s++;
305aace0509SDu, Changbin p++;
306aace0509SDu, Changbin } else {
307aace0509SDu, Changbin if (!star)
308aace0509SDu, Changbin return false;
309aace0509SDu, Changbin str++;
310aace0509SDu, Changbin s = str;
311aace0509SDu, Changbin p = pattern;
312aace0509SDu, Changbin }
313aace0509SDu, Changbin break;
314aace0509SDu, Changbin }
315aace0509SDu, Changbin }
316aace0509SDu, Changbin
317aace0509SDu, Changbin if (*p == '*')
318aace0509SDu, Changbin ++p;
319aace0509SDu, Changbin return !*p;
320aace0509SDu, Changbin }
321a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_wildcard);
322aace0509SDu, Changbin
323aace0509SDu, Changbin /**
324b9bffa10SBingJing Chang * match_strlcpy - Copy the characters from a substring_t to a sized buffer
325b32a09dbSMarkus Armbruster * @dest: where to copy to
326b32a09dbSMarkus Armbruster * @src: &substring_t to copy
327b32a09dbSMarkus Armbruster * @size: size of destination buffer
3281da177e4SLinus Torvalds *
329b32a09dbSMarkus Armbruster * Description: Copy the characters in &substring_t @src to the
330b32a09dbSMarkus Armbruster * c-style string @dest. Copy no more than @size - 1 characters, plus
331edd9334cSRandy Dunlap * the terminating NUL.
332edd9334cSRandy Dunlap *
333edd9334cSRandy Dunlap * Return: length of @src.
3341da177e4SLinus Torvalds */
match_strlcpy(char * dest,const substring_t * src,size_t size)335b32a09dbSMarkus Armbruster size_t match_strlcpy(char *dest, const substring_t *src, size_t size)
3361da177e4SLinus Torvalds {
337b32a09dbSMarkus Armbruster size_t ret = src->to - src->from;
338b32a09dbSMarkus Armbruster
339b32a09dbSMarkus Armbruster if (size) {
340b32a09dbSMarkus Armbruster size_t len = ret >= size ? size - 1 : ret;
341b32a09dbSMarkus Armbruster memcpy(dest, src->from, len);
342b32a09dbSMarkus Armbruster dest[len] = '\0';
343b32a09dbSMarkus Armbruster }
344b32a09dbSMarkus Armbruster return ret;
3451da177e4SLinus Torvalds }
346a3d2cca4SAndrew Morton EXPORT_SYMBOL(match_strlcpy);
3471da177e4SLinus Torvalds
3481da177e4SLinus Torvalds /**
349b9bffa10SBingJing Chang * match_strdup - allocate a new string with the contents of a substring_t
3501da177e4SLinus Torvalds * @s: &substring_t to copy
3511da177e4SLinus Torvalds *
3521da177e4SLinus Torvalds * Description: Allocates and returns a string filled with the contents of
3531da177e4SLinus Torvalds * the &substring_t @s. The caller is responsible for freeing the returned
3541da177e4SLinus Torvalds * string with kfree().
355edd9334cSRandy Dunlap *
356edd9334cSRandy Dunlap * Return: the address of the newly allocated NUL-terminated string or
357edd9334cSRandy Dunlap * %NULL on error.
3581da177e4SLinus Torvalds */
match_strdup(const substring_t * s)359ef4533f8SDavid Howells char *match_strdup(const substring_t *s)
3601da177e4SLinus Torvalds {
36130f7bc99SEric Biggers return kmemdup_nul(s->from, s->to - s->from, GFP_KERNEL);
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds EXPORT_SYMBOL(match_strdup);
364