aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>2014-10-13 18:55:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-13 20:18:26 -0400
commitc8250381c8272a9828fdd353171727b154fbd296 (patch)
tree050650d45d87d430334939f0552deafbe36561f9 /lib
parent45ff337a54c154680edf0c538e5c9eb4a2f862cc (diff)
lib / string_helpers: introduce string_escape_mem()
This is almost the opposite function to string_unescape(). Nevertheless it handles \0 and could be used for any byte buffer. The documentation is supplied together with the function prototype. The test cases covers most of the scenarios and would be expanded later on. [akpm@linux-foundation.org: avoid 1k stack consumption] Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: "John W . Linville" <linville@tuxdriver.com> Cc: Johannes Berg <johannes@sipsolutions.net> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Joe Perches <joe@perches.com> Cc: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/string_helpers.c274
-rw-r--r--lib/test-string_helpers.c240
2 files changed, 510 insertions, 4 deletions
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index 74ec60469640..58b78ba57439 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -8,6 +8,8 @@
8#include <linux/math64.h> 8#include <linux/math64.h>
9#include <linux/export.h> 9#include <linux/export.h>
10#include <linux/ctype.h> 10#include <linux/ctype.h>
11#include <linux/errno.h>
12#include <linux/string.h>
11#include <linux/string_helpers.h> 13#include <linux/string_helpers.h>
12 14
13/** 15/**
@@ -240,3 +242,275 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
240 return out - dst; 242 return out - dst;
241} 243}
242EXPORT_SYMBOL(string_unescape); 244EXPORT_SYMBOL(string_unescape);
245
246static int escape_passthrough(unsigned char c, char **dst, size_t *osz)
247{
248 char *out = *dst;
249
250 if (*osz < 1)
251 return -ENOMEM;
252
253 *out++ = c;
254
255 *dst = out;
256 *osz -= 1;
257
258 return 1;
259}
260
261static int escape_space(unsigned char c, char **dst, size_t *osz)
262{
263 char *out = *dst;
264 unsigned char to;
265
266 if (*osz < 2)
267 return -ENOMEM;
268
269 switch (c) {
270 case '\n':
271 to = 'n';
272 break;
273 case '\r':
274 to = 'r';
275 break;
276 case '\t':
277 to = 't';
278 break;
279 case '\v':
280 to = 'v';
281 break;
282 case '\f':
283 to = 'f';
284 break;
285 default:
286 return 0;
287 }
288
289 *out++ = '\\';
290 *out++ = to;
291
292 *dst = out;
293 *osz -= 2;
294
295 return 1;
296}
297
298static int escape_special(unsigned char c, char **dst, size_t *osz)
299{
300 char *out = *dst;
301 unsigned char to;
302
303 if (*osz < 2)
304 return -ENOMEM;
305
306 switch (c) {
307 case '\\':
308 to = '\\';
309 break;
310 case '\a':
311 to = 'a';
312 break;
313 case '\e':
314 to = 'e';
315 break;
316 default:
317 return 0;
318 }
319
320 *out++ = '\\';
321 *out++ = to;
322
323 *dst = out;
324 *osz -= 2;
325
326 return 1;
327}
328
329static int escape_null(unsigned char c, char **dst, size_t *osz)
330{
331 char *out = *dst;
332
333 if (*osz < 2)
334 return -ENOMEM;
335
336 if (c)
337 return 0;
338
339 *out++ = '\\';
340 *out++ = '0';
341
342 *dst = out;
343 *osz -= 2;
344
345 return 1;
346}
347
348static int escape_octal(unsigned char c, char **dst, size_t *osz)
349{
350 char *out = *dst;
351
352 if (*osz < 4)
353 return -ENOMEM;
354
355 *out++ = '\\';
356 *out++ = ((c >> 6) & 0x07) + '0';
357 *out++ = ((c >> 3) & 0x07) + '0';
358 *out++ = ((c >> 0) & 0x07) + '0';
359
360 *dst = out;
361 *osz -= 4;
362
363 return 1;
364}
365
366static int escape_hex(unsigned char c, char **dst, size_t *osz)
367{
368 char *out = *dst;
369
370 if (*osz < 4)
371 return -ENOMEM;
372
373 *out++ = '\\';
374 *out++ = 'x';
375 *out++ = hex_asc_hi(c);
376 *out++ = hex_asc_lo(c);
377
378 *dst = out;
379 *osz -= 4;
380
381 return 1;
382}
383
384/**
385 * string_escape_mem - quote characters in the given memory buffer
386 * @src: source buffer (unescaped)
387 * @isz: source buffer size
388 * @dst: destination buffer (escaped)
389 * @osz: destination buffer size
390 * @flags: combination of the flags (bitwise OR):
391 * %ESCAPE_SPACE:
392 * '\f' - form feed
393 * '\n' - new line
394 * '\r' - carriage return
395 * '\t' - horizontal tab
396 * '\v' - vertical tab
397 * %ESCAPE_SPECIAL:
398 * '\\' - backslash
399 * '\a' - alert (BEL)
400 * '\e' - escape
401 * %ESCAPE_NULL:
402 * '\0' - null
403 * %ESCAPE_OCTAL:
404 * '\NNN' - byte with octal value NNN (3 digits)
405 * %ESCAPE_ANY:
406 * all previous together
407 * %ESCAPE_NP:
408 * escape only non-printable characters (checked by isprint)
409 * %ESCAPE_ANY_NP:
410 * all previous together
411 * %ESCAPE_HEX:
412 * '\xHH' - byte with hexadecimal value HH (2 digits)
413 * @esc: NULL-terminated string of characters any of which, if found in
414 * the source, has to be escaped
415 *
416 * Description:
417 * The process of escaping byte buffer includes several parts. They are applied
418 * in the following sequence.
419 * 1. The character is matched to the printable class, if asked, and in
420 * case of match it passes through to the output.
421 * 2. The character is not matched to the one from @esc string and thus
422 * must go as is to the output.
423 * 3. The character is checked if it falls into the class given by @flags.
424 * %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
425 * character. Note that they actually can't go together, otherwise
426 * %ESCAPE_HEX will be ignored.
427 *
428 * Caller must provide valid source and destination pointers. Be aware that
429 * destination buffer will not be NULL-terminated, thus caller have to append
430 * it if needs.
431 *
432 * Return:
433 * The amount of the characters processed to the destination buffer, or
434 * %-ENOMEM if the size of buffer is not enough to put an escaped character is
435 * returned.
436 *
437 * Even in the case of error @dst pointer will be updated to point to the byte
438 * after the last processed character.
439 */
440int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,
441 unsigned int flags, const char *esc)
442{
443 char *out = *dst, *p = out;
444 bool is_dict = esc && *esc;
445 int ret = 0;
446
447 while (isz--) {
448 unsigned char c = *src++;
449
450 /*
451 * Apply rules in the following sequence:
452 * - the character is printable, when @flags has
453 * %ESCAPE_NP bit set
454 * - the @esc string is supplied and does not contain a
455 * character under question
456 * - the character doesn't fall into a class of symbols
457 * defined by given @flags
458 * In these cases we just pass through a character to the
459 * output buffer.
460 */
461 if ((flags & ESCAPE_NP && isprint(c)) ||
462 (is_dict && !strchr(esc, c))) {
463 /* do nothing */
464 } else {
465 if (flags & ESCAPE_SPACE) {
466 ret = escape_space(c, &p, &osz);
467 if (ret < 0)
468 break;
469 if (ret > 0)
470 continue;
471 }
472
473 if (flags & ESCAPE_SPECIAL) {
474 ret = escape_special(c, &p, &osz);
475 if (ret < 0)
476 break;
477 if (ret > 0)
478 continue;
479 }
480
481 if (flags & ESCAPE_NULL) {
482 ret = escape_null(c, &p, &osz);
483 if (ret < 0)
484 break;
485 if (ret > 0)
486 continue;
487 }
488
489 /* ESCAPE_OCTAL and ESCAPE_HEX always go last */
490 if (flags & ESCAPE_OCTAL) {
491 ret = escape_octal(c, &p, &osz);
492 if (ret < 0)
493 break;
494 continue;
495 }
496 if (flags & ESCAPE_HEX) {
497 ret = escape_hex(c, &p, &osz);
498 if (ret < 0)
499 break;
500 continue;
501 }
502 }
503
504 ret = escape_passthrough(c, &p, &osz);
505 if (ret < 0)
506 break;
507 }
508
509 *dst = p;
510
511 if (ret < 0)
512 return ret;
513
514 return p - out;
515}
516EXPORT_SYMBOL(string_escape_mem);
diff --git a/lib/test-string_helpers.c b/lib/test-string_helpers.c
index ac44c9245dcf..ab0d30e1e18f 100644
--- a/lib/test-string_helpers.c
+++ b/lib/test-string_helpers.c
@@ -5,6 +5,7 @@
5 5
6#include <linux/init.h> 6#include <linux/init.h>
7#include <linux/kernel.h> 7#include <linux/kernel.h>
8#include <linux/slab.h>
8#include <linux/module.h> 9#include <linux/module.h>
9#include <linux/random.h> 10#include <linux/random.h>
10#include <linux/string.h> 11#include <linux/string.h>
@@ -62,10 +63,14 @@ static const struct test_string strings[] __initconst = {
62static void __init test_string_unescape(const char *name, unsigned int flags, 63static void __init test_string_unescape(const char *name, unsigned int flags,
63 bool inplace) 64 bool inplace)
64{ 65{
65 char in[256]; 66 int q_real = 256;
66 char out_test[256]; 67 char *in = kmalloc(q_real, GFP_KERNEL);
67 char out_real[256]; 68 char *out_test = kmalloc(q_real, GFP_KERNEL);
68 int i, p = 0, q_test = 0, q_real = sizeof(out_real); 69 char *out_real = kmalloc(q_real, GFP_KERNEL);
70 int i, p = 0, q_test = 0;
71
72 if (!in || !out_test || !out_real)
73 goto out;
69 74
70 for (i = 0; i < ARRAY_SIZE(strings); i++) { 75 for (i = 0; i < ARRAY_SIZE(strings); i++) {
71 const char *s = strings[i].in; 76 const char *s = strings[i].in;
@@ -100,6 +105,223 @@ static void __init test_string_unescape(const char *name, unsigned int flags,
100 105
101 test_string_check_buf(name, flags, in, p - 1, out_real, q_real, 106 test_string_check_buf(name, flags, in, p - 1, out_real, q_real,
102 out_test, q_test); 107 out_test, q_test);
108out:
109 kfree(out_real);
110 kfree(out_test);
111 kfree(in);
112}
113
114struct test_string_1 {
115 const char *out;
116 unsigned int flags;
117};
118
119#define TEST_STRING_2_MAX_S1 32
120struct test_string_2 {
121 const char *in;
122 struct test_string_1 s1[TEST_STRING_2_MAX_S1];
123};
124
125#define TEST_STRING_2_DICT_0 NULL
126static const struct test_string_2 escape0[] __initconst = {{
127 .in = "\f\\ \n\r\t\v",
128 .s1 = {{
129 .out = "\\f\\ \\n\\r\\t\\v",
130 .flags = ESCAPE_SPACE,
131 },{
132 .out = "\\f\\134\\040\\n\\r\\t\\v",
133 .flags = ESCAPE_SPACE | ESCAPE_OCTAL,
134 },{
135 .out = "\\f\\x5c\\x20\\n\\r\\t\\v",
136 .flags = ESCAPE_SPACE | ESCAPE_HEX,
137 },{
138 /* terminator */
139 }},
140},{
141 .in = "\\h\\\"\a\e\\",
142 .s1 = {{
143 .out = "\\\\h\\\\\"\\a\\e\\\\",
144 .flags = ESCAPE_SPECIAL,
145 },{
146 .out = "\\\\\\150\\\\\\042\\a\\e\\\\",
147 .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,
148 },{
149 .out = "\\\\\\x68\\\\\\x22\\a\\e\\\\",
150 .flags = ESCAPE_SPECIAL | ESCAPE_HEX,
151 },{
152 /* terminator */
153 }},
154},{
155 .in = "\eb \\C\007\"\x90\r]",
156 .s1 = {{
157 .out = "\eb \\C\007\"\x90\\r]",
158 .flags = ESCAPE_SPACE,
159 },{
160 .out = "\\eb \\\\C\\a\"\x90\r]",
161 .flags = ESCAPE_SPECIAL,
162 },{
163 .out = "\\eb \\\\C\\a\"\x90\\r]",
164 .flags = ESCAPE_SPACE | ESCAPE_SPECIAL,
165 },{
166 .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\015\\135",
167 .flags = ESCAPE_OCTAL,
168 },{
169 .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\r\\135",
170 .flags = ESCAPE_SPACE | ESCAPE_OCTAL,
171 },{
172 .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\015\\135",
173 .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,
174 },{
175 .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\r\\135",
176 .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_OCTAL,
177 },{
178 .out = "\eb \\C\007\"\x90\r]",
179 .flags = ESCAPE_NP,
180 },{
181 .out = "\eb \\C\007\"\x90\\r]",
182 .flags = ESCAPE_SPACE | ESCAPE_NP,
183 },{
184 .out = "\\eb \\C\\a\"\x90\r]",
185 .flags = ESCAPE_SPECIAL | ESCAPE_NP,
186 },{
187 .out = "\\eb \\C\\a\"\x90\\r]",
188 .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NP,
189 },{
190 .out = "\\033b \\C\\007\"\\220\\015]",
191 .flags = ESCAPE_OCTAL | ESCAPE_NP,
192 },{
193 .out = "\\033b \\C\\007\"\\220\\r]",
194 .flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NP,
195 },{
196 .out = "\\eb \\C\\a\"\\220\\r]",
197 .flags = ESCAPE_SPECIAL | ESCAPE_SPACE | ESCAPE_OCTAL |
198 ESCAPE_NP,
199 },{
200 .out = "\\x1bb \\C\\x07\"\\x90\\x0d]",
201 .flags = ESCAPE_NP | ESCAPE_HEX,
202 },{
203 /* terminator */
204 }},
205},{
206 /* terminator */
207}};
208
209#define TEST_STRING_2_DICT_1 "b\\ \t\r"
210static const struct test_string_2 escape1[] __initconst = {{
211 .in = "\f\\ \n\r\t\v",
212 .s1 = {{
213 .out = "\f\\134\\040\n\\015\\011\v",
214 .flags = ESCAPE_OCTAL,
215 },{
216 .out = "\f\\x5c\\x20\n\\x0d\\x09\v",
217 .flags = ESCAPE_HEX,
218 },{
219 /* terminator */
220 }},
221},{
222 .in = "\\h\\\"\a\e\\",
223 .s1 = {{
224 .out = "\\134h\\134\"\a\e\\134",
225 .flags = ESCAPE_OCTAL,
226 },{
227 /* terminator */
228 }},
229},{
230 .in = "\eb \\C\007\"\x90\r]",
231 .s1 = {{
232 .out = "\e\\142\\040\\134C\007\"\x90\\015]",
233 .flags = ESCAPE_OCTAL,
234 },{
235 /* terminator */
236 }},
237},{
238 /* terminator */
239}};
240
241static __init const char *test_string_find_match(const struct test_string_2 *s2,
242 unsigned int flags)
243{
244 const struct test_string_1 *s1 = s2->s1;
245 unsigned int i;
246
247 if (!flags)
248 return s2->in;
249
250 /* Test cases are NULL-aware */
251 flags &= ~ESCAPE_NULL;
252
253 /* ESCAPE_OCTAL has a higher priority */
254 if (flags & ESCAPE_OCTAL)
255 flags &= ~ESCAPE_HEX;
256
257 for (i = 0; i < TEST_STRING_2_MAX_S1 && s1->out; i++, s1++)
258 if (s1->flags == flags)
259 return s1->out;
260 return NULL;
261}
262
263static __init void test_string_escape(const char *name,
264 const struct test_string_2 *s2,
265 unsigned int flags, const char *esc)
266{
267 int q_real = 512;
268 char *out_test = kmalloc(q_real, GFP_KERNEL);
269 char *out_real = kmalloc(q_real, GFP_KERNEL);
270 char *in = kmalloc(256, GFP_KERNEL);
271 char *buf = out_real;
272 int p = 0, q_test = 0;
273
274 if (!out_test || !out_real || !in)
275 goto out;
276
277 for (; s2->in; s2++) {
278 const char *out;
279 int len;
280
281 /* NULL injection */
282 if (flags & ESCAPE_NULL) {
283 in[p++] = '\0';
284 out_test[q_test++] = '\\';
285 out_test[q_test++] = '0';
286 }
287
288 /* Don't try strings that have no output */
289 out = test_string_find_match(s2, flags);
290 if (!out)
291 continue;
292
293 /* Copy string to in buffer */
294 len = strlen(s2->in);
295 memcpy(&in[p], s2->in, len);
296 p += len;
297
298 /* Copy expected result for given flags */
299 len = strlen(out);
300 memcpy(&out_test[q_test], out, len);
301 q_test += len;
302 }
303
304 q_real = string_escape_mem(in, p, &buf, q_real, flags, esc);
305
306 test_string_check_buf(name, flags, in, p, out_real, q_real, out_test,
307 q_test);
308out:
309 kfree(in);
310 kfree(out_real);
311 kfree(out_test);
312}
313
314static __init void test_string_escape_nomem(void)
315{
316 char *in = "\eb \\C\007\"\x90\r]";
317 char out[64], *buf = out;
318 int rc = -ENOMEM, ret;
319
320 ret = string_escape_str_any_np(in, &buf, strlen(in), NULL);
321 if (ret == rc)
322 return;
323
324 pr_err("Test 'escape nomem' failed: got %d instead of %d\n", ret, rc);
103} 325}
104 326
105static int __init test_string_helpers_init(void) 327static int __init test_string_helpers_init(void)
@@ -112,6 +334,16 @@ static int __init test_string_helpers_init(void)
112 test_string_unescape("unescape inplace", 334 test_string_unescape("unescape inplace",
113 get_random_int() % (UNESCAPE_ANY + 1), true); 335 get_random_int() % (UNESCAPE_ANY + 1), true);
114 336
337 /* Without dictionary */
338 for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++)
339 test_string_escape("escape 0", escape0, i, TEST_STRING_2_DICT_0);
340
341 /* With dictionary */
342 for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++)
343 test_string_escape("escape 1", escape1, i, TEST_STRING_2_DICT_1);
344
345 test_string_escape_nomem();
346
115 return -EINVAL; 347 return -EINVAL;
116} 348}
117module_init(test_string_helpers_init); 349module_init(test_string_helpers_init);