diff options
Diffstat (limited to 'lib/string.c')
| -rw-r--r-- | lib/string.c | 89 | 
1 files changed, 89 insertions, 0 deletions
diff --git a/lib/string.c b/lib/string.c index 13d1e84ddb80..84775ba873b9 100644 --- a/lib/string.c +++ b/lib/string.c @@ -27,6 +27,10 @@  #include <linux/bug.h>  #include <linux/errno.h> +#include <asm/byteorder.h> +#include <asm/word-at-a-time.h> +#include <asm/page.h> +  #ifndef __HAVE_ARCH_STRNCASECMP  /**   * strncasecmp - Case insensitive, length-limited string comparison @@ -146,6 +150,91 @@ size_t strlcpy(char *dest, const char *src, size_t size)  EXPORT_SYMBOL(strlcpy);  #endif +#ifndef __HAVE_ARCH_STRSCPY +/** + * strscpy - Copy a C-string into a sized buffer + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @count: Size of destination buffer + * + * Copy the string, or as much of it as fits, into the dest buffer. + * The routine returns the number of characters copied (not including + * the trailing NUL) or -E2BIG if the destination buffer wasn't big enough. + * The behavior is undefined if the string buffers overlap. + * The destination buffer is always NUL terminated, unless it's zero-sized. + * + * Preferred to strlcpy() since the API doesn't require reading memory + * from the src string beyond the specified "count" bytes, and since + * the return value is easier to error-check than strlcpy()'s. + * In addition, the implementation is robust to the string changing out + * from underneath it, unlike the current strlcpy() implementation. + * + * Preferred to strncpy() since it always returns a valid string, and + * doesn't unnecessarily force the tail of the destination buffer to be + * zeroed.  If the zeroing is desired, it's likely cleaner to use strscpy() + * with an overflow test, then just memset() the tail of the dest buffer. + */ +ssize_t strscpy(char *dest, const char *src, size_t count) +{ +	const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; +	size_t max = count; +	long res = 0; + +	if (count == 0) +		return -E2BIG; + +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +	/* +	 * If src is unaligned, don't cross a page boundary, +	 * since we don't know if the next page is mapped. +	 */ +	if ((long)src & (sizeof(long) - 1)) { +		size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)); +		if (limit < max) +			max = limit; +	} +#else +	/* If src or dest is unaligned, don't do word-at-a-time. */ +	if (((long) dest | (long) src) & (sizeof(long) - 1)) +		max = 0; +#endif + +	while (max >= sizeof(unsigned long)) { +		unsigned long c, data; + +		c = *(unsigned long *)(src+res); +		if (has_zero(c, &data, &constants)) { +			data = prep_zero_mask(c, data, &constants); +			data = create_zero_mask(data); +			*(unsigned long *)(dest+res) = c & zero_bytemask(data); +			return res + find_zero(data); +		} +		*(unsigned long *)(dest+res) = c; +		res += sizeof(unsigned long); +		count -= sizeof(unsigned long); +		max -= sizeof(unsigned long); +	} + +	while (count) { +		char c; + +		c = src[res]; +		dest[res] = c; +		if (!c) +			return res; +		res++; +		count--; +	} + +	/* Hit buffer length without finding a NUL; force NUL-termination. */ +	if (res) +		dest[res-1] = '\0'; + +	return -E2BIG; +} +EXPORT_SYMBOL(strscpy); +#endif +  #ifndef __HAVE_ARCH_STRCAT  /**   * strcat - Append one %NUL-terminated string to another  | 
