lib / string_helpers: introduce string_escape_mem()
[cascardo/linux.git] / lib / test-string_helpers.c
index ac44c92..ab0d30e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/string.h>
@@ -62,10 +63,14 @@ static const struct test_string strings[] __initconst = {
 static void __init test_string_unescape(const char *name, unsigned int flags,
                                        bool inplace)
 {
-       char in[256];
-       char out_test[256];
-       char out_real[256];
-       int i, p = 0, q_test = 0, q_real = sizeof(out_real);
+       int q_real = 256;
+       char *in = kmalloc(q_real, GFP_KERNEL);
+       char *out_test = kmalloc(q_real, GFP_KERNEL);
+       char *out_real = kmalloc(q_real, GFP_KERNEL);
+       int i, p = 0, q_test = 0;
+
+       if (!in || !out_test || !out_real)
+               goto out;
 
        for (i = 0; i < ARRAY_SIZE(strings); i++) {
                const char *s = strings[i].in;
@@ -100,6 +105,223 @@ static void __init test_string_unescape(const char *name, unsigned int flags,
 
        test_string_check_buf(name, flags, in, p - 1, out_real, q_real,
                              out_test, q_test);
+out:
+       kfree(out_real);
+       kfree(out_test);
+       kfree(in);
+}
+
+struct test_string_1 {
+       const char *out;
+       unsigned int flags;
+};
+
+#define        TEST_STRING_2_MAX_S1            32
+struct test_string_2 {
+       const char *in;
+       struct test_string_1 s1[TEST_STRING_2_MAX_S1];
+};
+
+#define        TEST_STRING_2_DICT_0            NULL
+static const struct test_string_2 escape0[] __initconst = {{
+       .in = "\f\\ \n\r\t\v",
+       .s1 = {{
+               .out = "\\f\\ \\n\\r\\t\\v",
+               .flags = ESCAPE_SPACE,
+       },{
+               .out = "\\f\\134\\040\\n\\r\\t\\v",
+               .flags = ESCAPE_SPACE | ESCAPE_OCTAL,
+       },{
+               .out = "\\f\\x5c\\x20\\n\\r\\t\\v",
+               .flags = ESCAPE_SPACE | ESCAPE_HEX,
+       },{
+               /* terminator */
+       }},
+},{
+       .in = "\\h\\\"\a\e\\",
+       .s1 = {{
+               .out = "\\\\h\\\\\"\\a\\e\\\\",
+               .flags = ESCAPE_SPECIAL,
+       },{
+               .out = "\\\\\\150\\\\\\042\\a\\e\\\\",
+               .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,
+       },{
+               .out = "\\\\\\x68\\\\\\x22\\a\\e\\\\",
+               .flags = ESCAPE_SPECIAL | ESCAPE_HEX,
+       },{
+               /* terminator */
+       }},
+},{
+       .in = "\eb \\C\007\"\x90\r]",
+       .s1 = {{
+               .out = "\eb \\C\007\"\x90\\r]",
+               .flags = ESCAPE_SPACE,
+       },{
+               .out = "\\eb \\\\C\\a\"\x90\r]",
+               .flags = ESCAPE_SPECIAL,
+       },{
+               .out = "\\eb \\\\C\\a\"\x90\\r]",
+               .flags = ESCAPE_SPACE | ESCAPE_SPECIAL,
+       },{
+               .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\015\\135",
+               .flags = ESCAPE_OCTAL,
+       },{
+               .out = "\\033\\142\\040\\134\\103\\007\\042\\220\\r\\135",
+               .flags = ESCAPE_SPACE | ESCAPE_OCTAL,
+       },{
+               .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\015\\135",
+               .flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,
+       },{
+               .out = "\\e\\142\\040\\\\\\103\\a\\042\\220\\r\\135",
+               .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_OCTAL,
+       },{
+               .out = "\eb \\C\007\"\x90\r]",
+               .flags = ESCAPE_NP,
+       },{
+               .out = "\eb \\C\007\"\x90\\r]",
+               .flags = ESCAPE_SPACE | ESCAPE_NP,
+       },{
+               .out = "\\eb \\C\\a\"\x90\r]",
+               .flags = ESCAPE_SPECIAL | ESCAPE_NP,
+       },{
+               .out = "\\eb \\C\\a\"\x90\\r]",
+               .flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NP,
+       },{
+               .out = "\\033b \\C\\007\"\\220\\015]",
+               .flags = ESCAPE_OCTAL | ESCAPE_NP,
+       },{
+               .out = "\\033b \\C\\007\"\\220\\r]",
+               .flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NP,
+       },{
+               .out = "\\eb \\C\\a\"\\220\\r]",
+               .flags = ESCAPE_SPECIAL | ESCAPE_SPACE | ESCAPE_OCTAL |
+                        ESCAPE_NP,
+       },{
+               .out = "\\x1bb \\C\\x07\"\\x90\\x0d]",
+               .flags = ESCAPE_NP | ESCAPE_HEX,
+       },{
+               /* terminator */
+       }},
+},{
+       /* terminator */
+}};
+
+#define        TEST_STRING_2_DICT_1            "b\\ \t\r"
+static const struct test_string_2 escape1[] __initconst = {{
+       .in = "\f\\ \n\r\t\v",
+       .s1 = {{
+               .out = "\f\\134\\040\n\\015\\011\v",
+               .flags = ESCAPE_OCTAL,
+       },{
+               .out = "\f\\x5c\\x20\n\\x0d\\x09\v",
+               .flags = ESCAPE_HEX,
+       },{
+               /* terminator */
+       }},
+},{
+       .in = "\\h\\\"\a\e\\",
+       .s1 = {{
+               .out = "\\134h\\134\"\a\e\\134",
+               .flags = ESCAPE_OCTAL,
+       },{
+               /* terminator */
+       }},
+},{
+       .in = "\eb \\C\007\"\x90\r]",
+       .s1 = {{
+               .out = "\e\\142\\040\\134C\007\"\x90\\015]",
+               .flags = ESCAPE_OCTAL,
+       },{
+               /* terminator */
+       }},
+},{
+       /* terminator */
+}};
+
+static __init const char *test_string_find_match(const struct test_string_2 *s2,
+                                                unsigned int flags)
+{
+       const struct test_string_1 *s1 = s2->s1;
+       unsigned int i;
+
+       if (!flags)
+               return s2->in;
+
+       /* Test cases are NULL-aware */
+       flags &= ~ESCAPE_NULL;
+
+       /* ESCAPE_OCTAL has a higher priority */
+       if (flags & ESCAPE_OCTAL)
+               flags &= ~ESCAPE_HEX;
+
+       for (i = 0; i < TEST_STRING_2_MAX_S1 && s1->out; i++, s1++)
+               if (s1->flags == flags)
+                       return s1->out;
+       return NULL;
+}
+
+static __init void test_string_escape(const char *name,
+                                     const struct test_string_2 *s2,
+                                     unsigned int flags, const char *esc)
+{
+       int q_real = 512;
+       char *out_test = kmalloc(q_real, GFP_KERNEL);
+       char *out_real = kmalloc(q_real, GFP_KERNEL);
+       char *in = kmalloc(256, GFP_KERNEL);
+       char *buf = out_real;
+       int p = 0, q_test = 0;
+
+       if (!out_test || !out_real || !in)
+               goto out;
+
+       for (; s2->in; s2++) {
+               const char *out;
+               int len;
+
+               /* NULL injection */
+               if (flags & ESCAPE_NULL) {
+                       in[p++] = '\0';
+                       out_test[q_test++] = '\\';
+                       out_test[q_test++] = '0';
+               }
+
+               /* Don't try strings that have no output */
+               out = test_string_find_match(s2, flags);
+               if (!out)
+                       continue;
+
+               /* Copy string to in buffer */
+               len = strlen(s2->in);
+               memcpy(&in[p], s2->in, len);
+               p += len;
+
+               /* Copy expected result for given flags */
+               len = strlen(out);
+               memcpy(&out_test[q_test], out, len);
+               q_test += len;
+       }
+
+       q_real = string_escape_mem(in, p, &buf, q_real, flags, esc);
+
+       test_string_check_buf(name, flags, in, p, out_real, q_real, out_test,
+                             q_test);
+out:
+       kfree(in);
+       kfree(out_real);
+       kfree(out_test);
+}
+
+static __init void test_string_escape_nomem(void)
+{
+       char *in = "\eb \\C\007\"\x90\r]";
+       char out[64], *buf = out;
+       int rc = -ENOMEM, ret;
+
+       ret = string_escape_str_any_np(in, &buf, strlen(in), NULL);
+       if (ret == rc)
+               return;
+
+       pr_err("Test 'escape nomem' failed: got %d instead of %d\n", ret, rc);
 }
 
 static int __init test_string_helpers_init(void)
@@ -112,6 +334,16 @@ static int __init test_string_helpers_init(void)
        test_string_unescape("unescape inplace",
                             get_random_int() % (UNESCAPE_ANY + 1), true);
 
+       /* Without dictionary */
+       for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++)
+               test_string_escape("escape 0", escape0, i, TEST_STRING_2_DICT_0);
+
+       /* With dictionary */
+       for (i = 0; i < (ESCAPE_ANY_NP | ESCAPE_HEX) + 1; i++)
+               test_string_escape("escape 1", escape1, i, TEST_STRING_2_DICT_1);
+
+       test_string_escape_nomem();
+
        return -EINVAL;
 }
 module_init(test_string_helpers_init);