History

string: provide strscpy()

The strscpy() API is intended to be used instead of strlcpy(), and instead of most uses of strncpy(). - Unlike strlcpy(), it doesn't read from memory beyond (src + size). - Unlike strlcpy() or strncpy(), the API provides an easy way to check for destination buffer overflow: an -E2BIG error return value. - The provided implementation is robust in the face of the source buffer being asynchronously changed during the copy, unlike the current implementation of strlcpy(). - Unlike strncpy(), the destination buffer will be NUL-terminated if the string in the source buffer is too long. - Also unlike strncpy(), the destination buffer will not be updated beyond the NUL termination, avoiding strncpy's behavior of zeroing the entire tail end of the destination buffer. (A memset() after the strscpy() can be used if this behavior is desired.) - The implementation should be reasonably performant on all platforms since it uses the asm/word-at-a-time.h API rather than simple byte copy. Kernel-to-kernel string copy is not considered to be performance critical in any case.

Chris Metcalf committed 11 years ago

Commit 30035e41 parent a6e2f022 files · +91 −0Browse files

Showing the changes to lib/string.c (1 of 2 files). Show all

Unified · Split

lib/string.c+88−0
@@ -27,6 +27,10 @@ · 26 unmodified lines
2727 #include <linux/bug.h>
2828 #include <linux/errno.h>
2929
30+#include <asm/byteorder.h>
31+#include <asm/word-at-a-time.h>
32+#include <asm/page.h>
33+
3034 #ifndef __HAVE_ARCH_STRNCASECMP
3135 /**
3236 * strncasecmp - Case insensitive, length-limited string comparison
@@ -146,6 +150,90 @@ size_t strlcpy(char *dest, const char *src, size_t size) · 113 unmodified lines
146150 EXPORT_SYMBOL(strlcpy);
147151 #endif
148152
153+#ifndef __HAVE_ARCH_STRSCPY
154+/**
155+ * strscpy - Copy a C-string into a sized buffer
156+ * @dest: Where to copy the string to
157+ * @src: Where to copy the string from
158+ * @count: Size of destination buffer
159+ *
160+ * Copy the string, or as much of it as fits, into the dest buffer.
161+ * The routine returns the number of characters copied (not including
162+ * the trailing NUL) or -E2BIG if the destination buffer wasn't big enough.
163+ * The behavior is undefined if the string buffers overlap.
164+ * The destination buffer is always NUL terminated, unless it's zero-sized.
165+ *
166+ * Preferred to strlcpy() since the API doesn't require reading memory
167+ * from the src string beyond the specified "count" bytes, and since
168+ * the return value is easier to error-check than strlcpy()'s.
169+ * In addition, the implementation is robust to the string changing out
170+ * from underneath it, unlike the current strlcpy() implementation.
171+ *
172+ * Preferred to strncpy() since it always returns a valid string, and
173+ * doesn't unnecessarily force the tail of the destination buffer to be
174+ * zeroed. If the zeroing is desired, it's likely cleaner to use strscpy()
175+ * with an overflow test, then just memset() the tail of the dest buffer.
176+ */
177+ssize_t strscpy(char *dest, const char *src, size_t count)
178+{
179+ const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
180+ size_t max = count;
181+ long res = 0;
182+
183+ if (count == 0)
184+ return -E2BIG;
185+
186+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
187+ /*
188+ * If src is unaligned, don't cross a page boundary,
189+ * since we don't know if the next page is mapped.
190+ */
191+ if ((long)src & (sizeof(long) - 1)) {
192+ size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1));
193+ if (limit < max)
194+ max = limit;
195+ }
196+#else
197+ /* If src or dest is unaligned, don't do word-at-a-time. */
198+ if (((long) dest | (long) src) & (sizeof(long) - 1))
199+ max = 0;
200+#endif
201+
202+ while (max >= sizeof(unsigned long)) {
203+ unsigned long c, data;
204+
205+ c = *(unsigned long *)(src+res);
206+ *(unsigned long *)(dest+res) = c;
207+ if (has_zero(c, &data, &constants)) {
208+ data = prep_zero_mask(c, data, &constants);
209+ data = create_zero_mask(data);
210+ return res + find_zero(data);
211+ }
212+ res += sizeof(unsigned long);
213+ count -= sizeof(unsigned long);
214+ max -= sizeof(unsigned long);
215+ }
216+
217+ while (count) {
218+ char c;
219+
220+ c = src[res];
221+ dest[res] = c;
222+ if (!c)
223+ return res;
224+ res++;
225+ count--;
226+ }
227+
228+ /* Hit buffer length without finding a NUL; force NUL-termination. */
229+ if (res)
230+ dest[res-1] = '\0';
231+
232+ return -E2BIG;
233+}
234+EXPORT_SYMBOL(strscpy);
235+#endif
236+
149237 #ifndef __HAVE_ARCH_STRCAT
150238 /**
151239 * strcat - Append one %NUL-terminated string to another

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t