diff options
Diffstat (limited to 'lib/argv_split.c')
| -rw-r--r-- | lib/argv_split.c | 89 | 
1 files changed, 41 insertions, 48 deletions
diff --git a/lib/argv_split.c b/lib/argv_split.c index 4b1b083f219..e927ed0e18a 100644 --- a/lib/argv_split.c +++ b/lib/argv_split.c @@ -6,25 +6,19 @@  #include <linux/ctype.h>  #include <linux/string.h>  #include <linux/slab.h> -#include <linux/module.h> - -static const char *skip_arg(const char *cp) -{ -	while (*cp && !isspace(*cp)) -		cp++; - -	return cp; -} +#include <linux/export.h>  static int count_argc(const char *str)  {  	int count = 0; +	bool was_space; -	while (*str) { -		str = skip_spaces(str); -		if (*str) { +	for (was_space = true; *str; str++) { +		if (isspace(*str)) { +			was_space = true; +		} else if (was_space) { +			was_space = false;  			count++; -			str = skip_arg(str);  		}  	} @@ -39,10 +33,8 @@ static int count_argc(const char *str)   */  void argv_free(char **argv)  { -	char **p; -	for (p = argv; *p; p++) -		kfree(*p); - +	argv--; +	kfree(argv[0]);  	kfree(argv);  }  EXPORT_SYMBOL(argv_free); @@ -59,43 +51,44 @@ EXPORT_SYMBOL(argv_free);   * considered to be a single argument separator.  The returned array   * is always NULL-terminated.  Returns NULL on memory allocation   * failure. + * + * The source string at `str' may be undergoing concurrent alteration via + * userspace sysctl activity (at least).  The argv_split() implementation + * attempts to handle this gracefully by taking a local copy to work on.   */  char **argv_split(gfp_t gfp, const char *str, int *argcp)  { -	int argc = count_argc(str); -	char **argv = kzalloc(sizeof(*argv) * (argc+1), gfp); -	char **argvp; - -	if (argv == NULL) -		goto out; - -	if (argcp) -		*argcp = argc; - -	argvp = argv; - -	while (*str) { -		str = skip_spaces(str); - -		if (*str) { -			const char *p = str; -			char *t; - -			str = skip_arg(str); +	char *argv_str; +	bool was_space; +	char **argv, **argv_ret; +	int argc; + +	argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp); +	if (!argv_str) +		return NULL; + +	argc = count_argc(argv_str); +	argv = kmalloc(sizeof(*argv) * (argc + 2), gfp); +	if (!argv) { +		kfree(argv_str); +		return NULL; +	} -			t = kstrndup(p, str-p, gfp); -			if (t == NULL) -				goto fail; -			*argvp++ = t; +	*argv = argv_str; +	argv_ret = ++argv; +	for (was_space = true; *argv_str; argv_str++) { +		if (isspace(*argv_str)) { +			was_space = true; +			*argv_str = 0; +		} else if (was_space) { +			was_space = false; +			*argv++ = argv_str;  		}  	} -	*argvp = NULL; - -  out: -	return argv; +	*argv = NULL; -  fail: -	argv_free(argv); -	return NULL; +	if (argcp) +		*argcp = argc; +	return argv_ret;  }  EXPORT_SYMBOL(argv_split);  | 
