summaryrefslogtreecommitdiffstats
path: root/lib/util/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /lib/util/tests
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/util/tests')
-rw-r--r--lib/util/tests/README22
-rw-r--r--lib/util/tests/anonymous_shared.c70
-rw-r--r--lib/util/tests/asn1_tests.c383
-rw-r--r--lib/util/tests/binsearch.c173
-rw-r--r--lib/util/tests/data_blob.c172
-rw-r--r--lib/util/tests/dlinklist.c131
-rw-r--r--lib/util/tests/file.c291
-rw-r--r--lib/util/tests/genrand.c61
-rw-r--r--lib/util/tests/genrandperf.c39
-rw-r--r--lib/util/tests/idtree.c122
-rw-r--r--lib/util/tests/rfc1738.c411
-rw-r--r--lib/util/tests/str.c180
-rw-r--r--lib/util/tests/strlist.c558
-rw-r--r--lib/util/tests/strv.c201
-rw-r--r--lib/util/tests/strv_util.c150
-rw-r--r--lib/util/tests/test_bytearray.c435
-rw-r--r--lib/util/tests/test_byteorder.c435
-rw-r--r--lib/util/tests/test_byteorder_verify.c273
-rw-r--r--lib/util/tests/test_logging.c146
-rw-r--r--lib/util/tests/test_memcache.c161
-rw-r--r--lib/util/tests/test_ms_fnmatch.c114
-rw-r--r--lib/util/tests/test_sys_rw.c178
-rw-r--r--lib/util/tests/test_talloc_keep_secret.c94
-rw-r--r--lib/util/tests/test_util.c344
-rw-r--r--lib/util/tests/test_util_paths.c127
-rw-r--r--lib/util/tests/tfork-drd.supp14
-rw-r--r--lib/util/tests/tfork-helgrind.supp32
-rw-r--r--lib/util/tests/tfork.c855
-rw-r--r--lib/util/tests/time.c151
-rw-r--r--lib/util/tests/util.c652
-rw-r--r--lib/util/tests/util_str_escape.c90
31 files changed, 7065 insertions, 0 deletions
diff --git a/lib/util/tests/README b/lib/util/tests/README
new file mode 100644
index 0000000..c1337d5
--- /dev/null
+++ b/lib/util/tests/README
@@ -0,0 +1,22 @@
+tfork tests
+===========
+
+To run the tfork torture testsuite under valgrind with the helgrind or drd
+thread checkers, run valgrind with the --suppress option passing a suppressions
+file.
+
+For helgrind:
+
+$ valgrind \
+ --trace-children=yes \
+ --tool=helgrind \
+ --suppressions=lib/util/tests/tfork-helgrind.supp \
+ ./bin/smbtorture ncalrpc:localhost local.tfork.tfork_threads
+
+For drd:
+
+$ valgrind \
+ --trace-children=yes \
+ --tool=drd \
+ --suppressions=lib/util/tests/tfork-drd.supp \
+ ./bin/smbtorture ncalrpc:localhost local.tfork.tfork_threads
diff --git a/lib/util/tests/anonymous_shared.c b/lib/util/tests/anonymous_shared.c
new file mode 100644
index 0000000..512a53f
--- /dev/null
+++ b/lib/util/tests/anonymous_shared.c
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ anonymous_shared testing
+
+ Copyright (C) Stefan Metzmacher 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool test_anonymous_shared_simple(struct torture_context *tctx)
+{
+ void *ptr;
+ size_t len;
+
+ torture_comment(tctx, "anonymous_shared_free(NULL)\n");
+ anonymous_shared_free(NULL);
+
+ len = 500;
+ torture_comment(tctx, "anonymous_shared_allocate(%llu)\n",
+ (unsigned long long)len);
+ ptr = anonymous_shared_allocate(len);
+ torture_assert(tctx, ptr, "valid pointer");
+ memset(ptr, 0xfe, len);
+ torture_comment(tctx, "anonymous_shared_free(ptr)\n");
+ anonymous_shared_free(ptr);
+
+ len = 50000;
+ torture_comment(tctx, "anonymous_shared_allocate(%llu)\n",
+ (unsigned long long)len);
+ ptr = anonymous_shared_allocate(len);
+ torture_assert(tctx, ptr, "valid pointer");
+ memset(ptr, 0xfe, len);
+ torture_comment(tctx, "anonymous_shared_free(ptr)\n");
+ anonymous_shared_free(ptr);
+
+ memset(&len, 0xFF, sizeof(len));
+ torture_comment(tctx, "anonymous_shared_allocate(%llu)\n",
+ (unsigned long long)len);
+ ptr = anonymous_shared_allocate(len);
+ torture_assert(tctx, ptr == NULL, "null pointer");
+
+ return true;
+}
+
+/* local.anonymous_shared test suite creation */
+struct torture_suite *torture_local_util_anonymous_shared(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "anonymous_shared");
+
+ torture_suite_add_simple_test(suite, "simple",
+ test_anonymous_shared_simple);
+
+ return suite;
+}
diff --git a/lib/util/tests/asn1_tests.c b/lib/util/tests/asn1_tests.c
new file mode 100644
index 0000000..ab5262c
--- /dev/null
+++ b/lib/util/tests/asn1_tests.c
@@ -0,0 +1,383 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ util_asn1 testing
+
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "../asn1.h"
+
+struct oid_data {
+ const char *oid; /* String OID */
+ const char *bin_oid; /* Binary OID represented as string */
+};
+
+/* Data for successful OIDs conversions */
+static const struct oid_data oid_data_ok[] = {
+ {
+ .oid = "2.5.4.0",
+ .bin_oid = "550400"
+ },
+ {
+ .oid = "2.5.4.1",
+ .bin_oid = "550401"
+ },
+ {
+ .oid = "2.5.4.130",
+ .bin_oid = "55048102"
+ },
+ {
+ .oid = "2.5.130.4",
+ .bin_oid = "55810204"
+ },
+ {
+ .oid = "2.5.4.16387",
+ .bin_oid = "5504818003"
+ },
+ {
+ .oid = "2.5.16387.4",
+ .bin_oid = "5581800304"
+ },
+ {
+ .oid = "2.5.2097155.4",
+ .bin_oid = "558180800304"
+ },
+ {
+ .oid = "2.5.4.130.16387.2097155.268435459",
+ .bin_oid = "55048102818003818080038180808003"
+ },
+};
+
+/* Data for successful OIDs conversions */
+static const char *oid_data_err[] = {
+ "", /* empty OID */
+ ".2.5.4.130", /* first sub-identifier is empty */
+ "2.5.4.130.", /* last sub-identifier is empty */
+ "2..5.4.130", /* second sub-identifier is empty */
+ "2.5..4.130", /* third sub-identifier is empty */
+ "2.abc.4.130", /* invalid sub-identifier */
+ "2.5abc.4.130", /* invalid sub-identifier (alpha-numeric)*/
+};
+
+/* Data for successful Partial OIDs conversions */
+static const struct oid_data partial_oid_data_ok[] = {
+ {
+ .oid = "2.5.4.130:0x81",
+ .bin_oid = "5504810281"
+ },
+ {
+ .oid = "2.5.4.16387:0x8180",
+ .bin_oid = "55048180038180"
+ },
+ {
+ .oid = "2.5.4.16387:0x81",
+ .bin_oid = "550481800381"
+ },
+ {
+ .oid = "2.5.2097155.4:0x818080",
+ .bin_oid = "558180800304818080"
+ },
+ {
+ .oid = "2.5.2097155.4:0x8180",
+ .bin_oid = "5581808003048180"
+ },
+ {
+ .oid = "2.5.2097155.4:0x81",
+ .bin_oid = "55818080030481"
+ },
+};
+
+static const struct {
+ DATA_BLOB blob;
+ int value;
+} integer_tests[] = {
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x01\x00"), 3},
+ .value = 0
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x01\x7f"), 3},
+ .value = 127
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x02\x00\x80"), 4},
+ .value = 128
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x02\x01\x00"), 4},
+ .value = 256
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x01\x80"), 3},
+ .value = -128
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x02\xff\x7f"), 4},
+ .value = -129
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x01\xff"), 3},
+ .value = -1
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x02\xff\x01"), 4},
+ .value = -255
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x02\x00\xff"), 4},
+ .value = 255
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x04\x80\x00\x00\x00"), 6},
+ .value = 0x80000000
+ },
+ {
+ .blob = { discard_const_p(uint8_t, "\x02\x04\x7f\xff\xff\xff"), 6},
+ .value = 0x7fffffff
+ }
+};
+
+/* Testing ber_write_OID_String() function */
+static bool test_ber_write_OID_String(struct torture_context *tctx)
+{
+ int i;
+ char *hex_str;
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ const struct oid_data *data = oid_data_ok;
+
+ mem_ctx = talloc_new(tctx);
+
+ /* check for valid OIDs */
+ for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) {
+ torture_assert(tctx, ber_write_OID_String(mem_ctx, &blob, data[i].oid),
+ "ber_write_OID_String failed");
+
+ hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length);
+ torture_assert(tctx, hex_str, "No memory!");
+
+ torture_assert(tctx, strequal(data[i].bin_oid, hex_str),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ /* check for invalid OIDs */
+ for (i = 0; i < ARRAY_SIZE(oid_data_err); i++) {
+ torture_assert(tctx,
+ !ber_write_OID_String(mem_ctx, &blob, oid_data_err[i]),
+ talloc_asprintf(mem_ctx,
+ "Should fail for [%s] -> %s",
+ oid_data_err[i],
+ hex_encode_talloc(mem_ctx, blob.data, blob.length)));
+ }
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+/* Testing ber_read_OID_String() function */
+static bool test_ber_read_OID_String(struct torture_context *tctx)
+{
+ int i;
+ char *oid;
+ DATA_BLOB oid_blob;
+ TALLOC_CTX *mem_ctx;
+ const struct oid_data *data = oid_data_ok;
+
+ mem_ctx = talloc_new(tctx);
+
+ for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) {
+ oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid);
+
+ torture_assert(tctx, ber_read_OID_String(mem_ctx, oid_blob, &oid),
+ "ber_read_OID_String failed");
+
+ torture_assert(tctx, strequal(data[i].oid, oid),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+/* Testing ber_write_partial_OID_String() function */
+static bool test_ber_write_partial_OID_String(struct torture_context *tctx)
+{
+ int i;
+ char *hex_str;
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ const struct oid_data *data = oid_data_ok;
+
+ mem_ctx = talloc_new(tctx);
+
+ /* ber_write_partial_OID_String() should work with not partial OIDs also */
+ for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) {
+ torture_assert(tctx, ber_write_partial_OID_String(mem_ctx, &blob, data[i].oid),
+ "ber_write_partial_OID_String failed");
+
+ hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length);
+ torture_assert(tctx, hex_str, "No memory!");
+
+ torture_assert(tctx, strequal(data[i].bin_oid, hex_str),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ /* ber_write_partial_OID_String() test with partial OIDs */
+ data = partial_oid_data_ok;
+ for (i = 0; i < ARRAY_SIZE(partial_oid_data_ok); i++) {
+ torture_assert(tctx, ber_write_partial_OID_String(mem_ctx, &blob, data[i].oid),
+ "ber_write_partial_OID_String failed");
+
+ hex_str = hex_encode_talloc(mem_ctx, blob.data, blob.length);
+ torture_assert(tctx, hex_str, "No memory!");
+
+ torture_assert(tctx, strequal(data[i].bin_oid, hex_str),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+/* Testing ber_read_partial_OID_String() function */
+static bool test_ber_read_partial_OID_String(struct torture_context *tctx)
+{
+ int i;
+ char *oid;
+ DATA_BLOB oid_blob;
+ TALLOC_CTX *mem_ctx;
+ const struct oid_data *data = oid_data_ok;
+
+ mem_ctx = talloc_new(tctx);
+
+ /* ber_read_partial_OID_String() should work with not partial OIDs also */
+ for (i = 0; i < ARRAY_SIZE(oid_data_ok); i++) {
+ oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid);
+
+ torture_assert(tctx, ber_read_partial_OID_String(mem_ctx, oid_blob, &oid),
+ "ber_read_partial_OID_String failed");
+
+ torture_assert(tctx, strequal(data[i].oid, oid),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ /* ber_read_partial_OID_String() test with partial OIDs */
+ data = partial_oid_data_ok;
+ for (i = 0; i < ARRAY_SIZE(partial_oid_data_ok); i++) {
+ oid_blob = strhex_to_data_blob(mem_ctx, data[i].bin_oid);
+
+ torture_assert(tctx, ber_read_partial_OID_String(mem_ctx, oid_blob, &oid),
+ "ber_read_partial_OID_String failed");
+
+ torture_assert(tctx, strequal(data[i].oid, oid),
+ talloc_asprintf(mem_ctx,
+ "Failed: oid=%s, bin_oid:%s",
+ data[i].oid, data[i].bin_oid));
+ }
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+/*
+ * Testing asn1_read_Integer and asn1_write_Integer functions,
+ * inspired by Love Hornquist Astrand
+ */
+
+static bool test_asn1_Integer(struct torture_context *tctx)
+{
+ int i;
+ TALLOC_CTX *mem_ctx;
+ bool ret = false;
+
+ mem_ctx = talloc_new(tctx);
+
+ for (i = 0; i < ARRAY_SIZE(integer_tests); i++) {
+ ASN1_DATA *data;
+ DATA_BLOB blob;
+ int val;
+
+ data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ if (!data) {
+ goto err;
+ }
+
+ if (!asn1_write_Integer(data, integer_tests[i].value)) goto err;
+
+ if (!asn1_blob(data, &blob)) {
+ goto err;
+ }
+
+ torture_assert_data_blob_equal(tctx, blob, integer_tests[i].blob, "asn1_write_Integer gave incorrect result");
+
+ if (!asn1_load(data, blob)) goto err;
+ torture_assert(tctx, asn1_read_Integer(data, &val), "asn1_write_Integer output could not be read by asn1_read_Integer()");
+
+ torture_assert_int_equal(tctx, val, integer_tests[i].value,
+ "readback of asn1_write_Integer output by asn1_read_Integer() failed");
+ }
+
+ ret = true;
+
+ err:
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+/* LOCAL-ASN1 test suite creation */
+struct torture_suite *torture_local_util_asn1(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "asn1");
+
+ torture_suite_add_simple_test(suite, "ber_write_OID_String",
+ test_ber_write_OID_String);
+
+ torture_suite_add_simple_test(suite, "ber_read_OID_String",
+ test_ber_read_OID_String);
+
+ torture_suite_add_simple_test(suite, "ber_write_partial_OID_String",
+ test_ber_write_partial_OID_String);
+
+ torture_suite_add_simple_test(suite, "ber_read_partial_OID_String",
+ test_ber_read_partial_OID_String);
+
+ torture_suite_add_simple_test(suite, "asn1_Integer",
+ test_asn1_Integer);
+
+ return suite;
+}
diff --git a/lib/util/tests/binsearch.c b/lib/util/tests/binsearch.c
new file mode 100644
index 0000000..b3ecda1
--- /dev/null
+++ b/lib/util/tests/binsearch.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Tests for binsearch.h macros.
+
+ Copyright Catalyst IT 2016.
+
+ Written by Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/binsearch.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static int int_cmp(int a, int b)
+{
+ return a - b;
+}
+
+static int int_cmp_p(int a, int *b)
+{
+ return a - *b;
+}
+
+static bool test_binsearch_v(struct torture_context *tctx)
+{
+ int array[] = { -11, -7, 0, 1, 723, 1000000};
+ int misses[] = { -121, 17, -10, 10, -1, -723, 1000002};
+ int i;
+ int *result = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(misses); i++) {
+ BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array),
+ misses[i], int_cmp, result);
+ torture_comment(tctx, "looking for misses[%d] == %d\n", i, misses[i]);
+ torture_assert(tctx, result == NULL, "failed to miss");
+ }
+
+ for (i = 0; i < ARRAY_SIZE(array); i++) {
+ BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array),
+ array[i], int_cmp, result);
+ torture_comment(tctx, "looking for array[%d] == %d, %p; got %p\n",
+ i, array[i], &array[i], result);
+ torture_assert(tctx, result == &array[i],
+ "failed to find element");
+ }
+ return true;
+}
+
+static bool test_binsearch_gte(struct torture_context *tctx)
+{
+ int array[] = { -11, -7, -7, -7, -1, 0, 0, 1, 723, 723, 723,
+ 724, 724, 10000};
+ size_t a_len = ARRAY_SIZE(array);
+ int targets[] = { -121, -8, -7, -6, 17, -10, 10, -1, 723,
+ 724, 725, 10002, 10000, 0, -11, 1, 11};
+ int i, j, target;
+ int *result = NULL, *next = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(targets); i++) {
+ target = targets[i];
+ torture_comment(tctx, "looking for targets[%d] %d\n",
+ i, target);
+
+ BINARY_ARRAY_SEARCH_GTE(array, a_len, target,
+ int_cmp_p, result, next);
+
+ if (result == NULL) {
+ /* we think there is no exact match */
+ for (j = 0; j < a_len; j++) {
+ if (target == array[j]) {
+ torture_comment(tctx,
+ "failed to find %d\n",
+ targets[i]);
+ torture_fail(tctx,
+ "result is wrongly NULL");
+ }
+ }
+ if (next != NULL) {
+ torture_assert(tctx, (next >= array &&
+ next < array + a_len),
+ "next is out of bounds");
+
+ torture_assert(tctx, *next > target,
+ "next <= target");
+ if (target <= array[0]) {
+ torture_assert(tctx, next == array,
+ "search before start failed");
+ }
+ if (next != array) {
+ torture_assert(tctx, next[-1] < target,
+ "next[-1] >= target");
+ }
+ }
+ else {
+ torture_assert(tctx, array[a_len - 1] < target,
+ "next was not found\n");
+ }
+ } else {
+ /* we think we found an exact match */
+ torture_assert(tctx, *result == target,
+ "result has wrong value");
+
+ torture_assert(tctx, (result >= array &&
+ result < array + a_len),
+ "result is out of bounds!");
+
+ torture_assert(tctx, next == NULL,
+ "next should be NULL on exact match\n");
+ if (result != array) {
+ torture_assert(tctx, result[-1] != target,
+ "didn't find first target\n");
+ }
+ }
+ if (target >= array[a_len - 1]) {
+ torture_assert(tctx, next == NULL,
+ "next is not NULL at array end\n");
+ }
+ }
+
+ /* try again, with result and next the same pointer */
+ for (i = 0; i < ARRAY_SIZE(targets); i++) {
+ target = targets[i];
+ torture_comment(tctx, "looking for targets[%d] %d\n",
+ i, target);
+
+ BINARY_ARRAY_SEARCH_GTE(array, a_len, target,
+ int_cmp_p, result, result);
+
+ if (result == NULL) {
+ /* we think the target is greater than all elements */
+ torture_assert(tctx, array[a_len - 1] < target,
+ "element >= target not found\n");
+ } else {
+ /* we think an element is >= target */
+ torture_assert(tctx, *result >= target,
+ "result has wrong value");
+
+ torture_assert(tctx, (result >= array &&
+ result < array + a_len),
+ "result is out of bounds!");
+
+ if (result != array) {
+ torture_assert(tctx, result[-1] < target,
+ "didn't find first target\n");
+ }
+ }
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_local_util_binsearch(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "binsearch");
+ torture_suite_add_simple_test(suite, "binsearch_v", test_binsearch_v);
+ torture_suite_add_simple_test(suite, "binsearch_gte", test_binsearch_gte);
+ return suite;
+}
diff --git a/lib/util/tests/data_blob.c b/lib/util/tests/data_blob.c
new file mode 100644
index 0000000..e1e8129
--- /dev/null
+++ b/lib/util/tests/data_blob.c
@@ -0,0 +1,172 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ data blob testing
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool test_string(struct torture_context *tctx)
+{
+ DATA_BLOB blob = data_blob_string_const("bla");
+
+ torture_assert_int_equal(tctx, blob.length, 3, "blob length");
+ torture_assert_str_equal(tctx, (char *)blob.data, "bla", "blob data");
+
+ return true;
+}
+
+static bool test_string_null(struct torture_context *tctx)
+{
+ DATA_BLOB blob = data_blob_string_const_null("bla");
+
+ torture_assert_int_equal(tctx, blob.length, 4, "blob length");
+ torture_assert_str_equal(tctx, (char *)blob.data, "bla", "blob data");
+
+ return true;
+}
+
+static bool test_zero(struct torture_context *tctx)
+{
+ int i;
+ DATA_BLOB z = data_blob_talloc_zero(tctx, 4);
+ torture_assert_int_equal(tctx, z.length, 4, "length");
+ for (i = 0; i < z.length; i++)
+ torture_assert_int_equal(tctx, z.data[i], 0, "contents");
+ data_blob_free(&z);
+ return true;
+}
+
+
+static bool test_clear(struct torture_context *tctx)
+{
+ int i;
+ DATA_BLOB z = data_blob("lalala", 6);
+ torture_assert_int_equal(tctx, z.length, 6, "length");
+ data_blob_clear(&z);
+ for (i = 0; i < z.length; i++)
+ torture_assert_int_equal(tctx, z.data[i], 0, "contents");
+ data_blob_free(&z);
+ return true;
+}
+
+static bool test_cmp(struct torture_context *tctx)
+{
+ DATA_BLOB a = data_blob_string_const("bla");
+ DATA_BLOB b = data_blob_string_const("blae");
+ torture_assert(tctx, data_blob_cmp(&a, &b) != 0, "cmp different");
+ torture_assert(tctx, data_blob_cmp(&a, &a) == 0, "cmp self");
+ return true;
+}
+
+static bool test_equal_const_time(struct torture_context *tctx)
+{
+ const char *test_string = "foobarfoo";
+
+ DATA_BLOB null = data_blob_const(NULL, 0);
+ DATA_BLOB foobar = data_blob_const(test_string, 6);
+ DATA_BLOB bar = data_blob_const(test_string + 3, 3);
+
+ /* These data blobs both contain 'foo', but at different addresses. */
+ DATA_BLOB foo_same = data_blob_const(test_string, 3);
+ DATA_BLOB foo_other = data_blob_const(test_string + 6, 3);
+
+ /* Test all equality combinations behave as expected. */
+ torture_assert(tctx, data_blob_equal_const_time(&null, &null), "null == null");
+ torture_assert(tctx, !data_blob_equal_const_time(&null, &foobar), "null != 'foobar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&null, &bar), "null != 'bar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&null, &foo_same), "null != 'foo'");
+ torture_assert(tctx, !data_blob_equal_const_time(&null, &foo_other), "null != 'foo'");
+
+ torture_assert(tctx, !data_blob_equal_const_time(&foobar, &null), "'foobar' != null");
+ torture_assert(tctx, data_blob_equal_const_time(&foobar, &foobar), "'foobar' == 'foobar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&foobar, &bar), "'foobar' != 'bar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&foobar, &foo_same), "'foobar' != 'foo'");
+ torture_assert(tctx, !data_blob_equal_const_time(&foobar, &foo_other), "'foobar' != 'foo'");
+
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &null), "'foo' != null");
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &foobar), "'foo' != 'foobar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_same, &bar), "'foo' != 'bar'");
+ torture_assert(tctx, data_blob_equal_const_time(&foo_same, &foo_same), "'foo' == 'foo'");
+ torture_assert(tctx, data_blob_equal_const_time(&foo_same, &foo_other), "'foo' == 'foo'");
+
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &null), "'foo' != null");
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &foobar), "'foo' != 'foobar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&foo_other, &bar), "'foo' != 'bar'");
+ torture_assert(tctx, data_blob_equal_const_time(&foo_other, &foo_same), "'foo' == 'foo'");
+ torture_assert(tctx, data_blob_equal_const_time(&foo_other, &foo_other), "'foo' == 'foo'");
+
+ torture_assert(tctx, !data_blob_equal_const_time(&bar, &null), "'bar' != null");
+ torture_assert(tctx, !data_blob_equal_const_time(&bar, &foobar), "'bar' != 'foobar'");
+ torture_assert(tctx, data_blob_equal_const_time(&bar, &bar), "'bar' == 'bar'");
+ torture_assert(tctx, !data_blob_equal_const_time(&bar, &foo_same), "'bar' != 'foo'");
+ torture_assert(tctx, !data_blob_equal_const_time(&bar, &foo_other), "'bar' != 'foo'");
+
+ return true;
+}
+
+static bool test_hex_string(struct torture_context *tctx)
+{
+ DATA_BLOB a = data_blob_string_const("\xC\xA\xF\xE");
+ torture_assert_str_equal(tctx, data_blob_hex_string_lower(tctx, &a), "0c0a0f0e", "hex string");
+ torture_assert_str_equal(tctx, data_blob_hex_string_upper(tctx, &a), "0C0A0F0E", "hex string");
+ return true;
+}
+
+static bool test_append_NULL_0(struct torture_context *tctx)
+{
+ DATA_BLOB z = data_blob_talloc_zero(tctx, 0);
+ torture_assert_int_equal(tctx, z.length, 0, "length");
+ torture_assert(tctx, z.data == NULL, "data");
+ torture_assert(tctx, data_blob_append(NULL, &z, NULL, 0), "append NULL,0");
+ torture_assert(tctx, data_blob_append(NULL, &z, "", 0), "append '',0");
+ torture_assert_int_equal(tctx, z.length, 0, "length");
+ torture_assert(tctx, z.data == NULL, "data");
+ return true;
+}
+
+static bool test_append_empty_0(struct torture_context *tctx)
+{
+ DATA_BLOB e = data_blob_talloc(tctx, "", 0);
+ torture_assert_int_equal(tctx, e.length, 0, "length");
+ torture_assert(tctx, e.data != NULL, "data");
+ torture_assert(tctx, data_blob_append(NULL, &e, NULL, 0), "append NULL,0");
+ torture_assert(tctx, data_blob_append(NULL, &e, "", 0), "append '',0");
+ torture_assert_int_equal(tctx, e.length, 0, "length");
+ torture_assert(tctx, e.data != NULL, "data");
+ return true;
+}
+
+struct torture_suite *torture_local_util_data_blob(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "datablob");
+
+ torture_suite_add_simple_test(suite, "string", test_string);
+ torture_suite_add_simple_test(suite, "string_null", test_string_null);
+ torture_suite_add_simple_test(suite, "zero", test_zero);;
+ torture_suite_add_simple_test(suite, "clear", test_clear);
+ torture_suite_add_simple_test(suite, "cmp", test_cmp);
+ torture_suite_add_simple_test(suite, "equal_const_time", test_equal_const_time);
+ torture_suite_add_simple_test(suite, "hex string", test_hex_string);
+ torture_suite_add_simple_test(suite, "append_NULL_0", test_append_NULL_0);
+ torture_suite_add_simple_test(suite, "append_empty_0", test_append_empty_0);
+
+ return suite;
+}
diff --git a/lib/util/tests/dlinklist.c b/lib/util/tests/dlinklist.c
new file mode 100644
index 0000000..50adab3
--- /dev/null
+++ b/lib/util/tests/dlinklist.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of DLIST_*() macros
+
+ Copyright (C) Andrew Tridgell 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "lib/util/dlinklist.h"
+
+struct listel {
+ struct listel *next, *prev;
+};
+
+static bool torture_local_dlinklist_simple(struct torture_context *tctx)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ struct listel *l1 = NULL, *l2 = NULL, *el, *el2;
+ int i;
+
+ torture_comment(tctx, "add 5 elements at front\n");
+ for (i=0; i<5; i++) {
+ el = talloc(mem_ctx, struct listel);
+ DLIST_ADD(l1, el);
+ }
+
+ torture_comment(tctx, "add 5 elements at end\n");
+ for (i=0; i<5; i++) {
+ el = talloc(mem_ctx, struct listel);
+ DLIST_ADD_END(l1, el);
+ }
+
+ torture_comment(tctx, "delete 3 from front\n");
+ for (i=0; i < 3; i++) {
+ el = l1;
+ DLIST_REMOVE(l1, l1);
+ DLIST_ADD(l2, el);
+ }
+
+ torture_comment(tctx, "delete 3 from back\n");
+ for (i=0; i < 3; i++) {
+ el = DLIST_TAIL(l1);
+ DLIST_REMOVE(l1, el);
+ DLIST_ADD_END(l2, el);
+ }
+
+ torture_comment(tctx, "count forward\n");
+ for (i=0,el=l1; el; el=el->next) i++;
+ torture_assert_int_equal(tctx, i, 4, "should have 4 elements");
+
+ torture_comment(tctx, "count backwards\n");
+ for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++;
+ torture_assert_int_equal(tctx, i, 4, "should have 4 elements");
+
+ torture_comment(tctx, "check DLIST_HEAD\n");
+ el = DLIST_TAIL(l1);
+ DLIST_HEAD(el, el2);
+ torture_assert(tctx, el2 == l1, "should find head");
+
+ torture_comment(tctx, "check DLIST_ADD_AFTER\n");
+ el = talloc(mem_ctx, struct listel);
+ el2 = talloc(mem_ctx, struct listel);
+ DLIST_ADD_AFTER(l1, el, l1);
+ DLIST_ADD_AFTER(l1, el2, el);
+ torture_assert(tctx, l1->next == el, "2nd in list");
+ torture_assert(tctx, el->next == el2, "3rd in list");
+
+ torture_comment(tctx, "check DLIST_PROMOTE\n");
+ DLIST_PROMOTE(l1, el2);
+ torture_assert(tctx, el2==l1, "1st in list");
+ torture_assert(tctx, el2->next->next == el, "3rd in list");
+
+ torture_comment(tctx, "check DLIST_DEMOTE\n");
+ DLIST_DEMOTE(l1, el);
+ torture_assert(tctx, el->next == NULL, "last in list");
+ torture_assert(tctx, el2->prev == el, "backlink from head");
+
+ torture_comment(tctx, "count forward\n");
+ for (i=0,el=l1; el; el=el->next) i++;
+ torture_assert_int_equal(tctx, i, 6, "should have 6 elements");
+
+ torture_comment(tctx, "count backwards\n");
+ for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++;
+ torture_assert_int_equal(tctx, i, 6, "should have 6 elements");
+
+ torture_comment(tctx, "check DLIST_CONCATENATE\n");
+ DLIST_CONCATENATE(l1, l2);
+ torture_comment(tctx, "count forward\n");
+ for (i=0,el=l1; el; el=el->next) i++;
+ torture_assert_int_equal(tctx, i, 12, "should have 12 elements");
+
+ torture_comment(tctx, "count backwards\n");
+ for (i=0,el=DLIST_TAIL(l1); el; el=DLIST_PREV(el)) i++;
+ torture_assert_int_equal(tctx, i, 12, "should have 12 elements");
+
+ torture_comment(tctx, "free forwards\n");
+ for (el=l1; el; el=el2) {
+ el2 = el->next;
+ DLIST_REMOVE(l1, el);
+ talloc_free(el);
+ }
+
+ torture_assert(tctx, l1 == NULL, "list empty");
+ torture_assert_int_equal(tctx, talloc_total_blocks(mem_ctx), 1, "1 block");
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+struct torture_suite *torture_local_dlinklist(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "dlinklist");
+ torture_suite_add_simple_test(suite, "dlinklist", torture_local_dlinklist_simple);
+ return suite;
+}
diff --git a/lib/util/tests/file.c b/lib/util/tests/file.c
new file mode 100644
index 0000000..3501c7e
--- /dev/null
+++ b/lib/util/tests/file.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ util_file testing
+
+ Copyright (C) Jelmer Vernooij 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+#define TEST_FILENAME "utilfile.test"
+#define TEST_LINE1 "This is list line 1..."
+#define TEST_LINE2 ".. and this is line 2"
+#define TEST_LINE3 "and end of the file"
+
+#define TEST_DATA TEST_LINE1 "\n" TEST_LINE2 "\n" TEST_LINE3
+
+static bool test_file_load_save(struct torture_context *tctx)
+{
+ size_t len;
+ char *data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ torture_assert(tctx, file_save(TEST_FILENAME, TEST_DATA, strlen(TEST_DATA)),
+ "saving file");
+
+ torture_assert_file_contains_text(tctx, TEST_FILENAME, TEST_DATA,
+ "file contents");
+
+ data = file_load(TEST_FILENAME, &len, 0, mem_ctx);
+ torture_assert(tctx, data, "loading file");
+
+ torture_assert_int_equal(tctx, len, strlen(TEST_DATA), "Length");
+
+ torture_assert_mem_equal(tctx, data, TEST_DATA, len, "Contents");
+
+ data = file_load(TEST_FILENAME, &len, 5, mem_ctx);
+
+ torture_assert_int_equal(tctx, len, 5, "Length");
+
+ torture_assert_mem_equal(tctx, data, TEST_DATA, len, "Contents");
+
+ unlink(TEST_FILENAME);
+ return true;
+}
+
+#define TEST_DATA_WITH_NEWLINE TEST_DATA "\n"
+#define TEST_DATA_NO_NEWLINE TEST_DATA
+#define TEST_DATA_EMPTY ""
+#define TEST_DATA_BLANKS_ONLY "\n\n\n\n\n"
+#define TEST_DATA_WITH_TRAILING_BLANKS TEST_DATA TEST_DATA_BLANKS_ONLY
+
+static bool test_file_lines_load(struct torture_context *tctx)
+{
+ char **lines;
+ int numlines;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ /*
+ * Last line has trailing whitespace
+ */
+
+ torture_assert(tctx,
+ file_save(TEST_FILENAME,
+ TEST_DATA_WITH_NEWLINE,
+ strlen(TEST_DATA_WITH_NEWLINE)),
+ "saving file");
+
+ lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx);
+
+ torture_assert_int_equal(tctx, numlines, 3, "Lines");
+
+ torture_assert_mem_equal(tctx,
+ lines[0],
+ TEST_LINE1,
+ strlen(TEST_LINE1),
+ "Line 1");
+
+ torture_assert_mem_equal(tctx,
+ lines[1],
+ TEST_LINE2,
+ strlen(TEST_LINE2),
+ "Line 2");
+
+ torture_assert_mem_equal(tctx,
+ lines[2],
+ TEST_LINE3,
+ strlen(TEST_LINE3),
+ "Line 3");
+
+ unlink(TEST_FILENAME);
+
+ /*
+ * Last line has NO trailing whitespace
+ */
+
+ torture_assert(tctx,
+ file_save(TEST_FILENAME,
+ TEST_DATA_NO_NEWLINE,
+ strlen(TEST_DATA_NO_NEWLINE)),
+ "saving file");
+
+ lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx);
+
+ torture_assert_int_equal(tctx, numlines, 3, "Lines");
+
+ torture_assert_mem_equal(tctx,
+ lines[0],
+ TEST_LINE1,
+ strlen(TEST_LINE1),
+ "Line 1");
+
+ torture_assert_mem_equal(tctx,
+ lines[1],
+ TEST_LINE2,
+ strlen(TEST_LINE2),
+ "Line 2");
+
+ torture_assert_mem_equal(tctx,
+ lines[2],
+ TEST_LINE3,
+ strlen(TEST_LINE3),
+ "Line 3");
+
+ unlink(TEST_FILENAME);
+
+ /*
+ * Empty file
+ */
+
+ torture_assert(tctx,
+ file_save(TEST_FILENAME,
+ TEST_DATA_EMPTY,
+ strlen(TEST_DATA_EMPTY)),
+ "saving file");
+
+ (void)file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx);
+
+ torture_assert_int_equal(tctx, numlines, 0, "Lines");
+
+ unlink(TEST_FILENAME);
+
+ /*
+ * Just blank lines
+ */
+
+ torture_assert(tctx,
+ file_save(TEST_FILENAME,
+ TEST_DATA_BLANKS_ONLY,
+ strlen(TEST_DATA_BLANKS_ONLY)),
+ "saving file");
+
+ lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx);
+
+ torture_assert_int_equal(tctx, numlines, 0, "Lines");
+
+ unlink(TEST_FILENAME);
+
+ /*
+ * Several trailing blank lines
+ */
+
+ torture_assert(tctx,
+ file_save(TEST_FILENAME,
+ TEST_DATA_WITH_TRAILING_BLANKS,
+ strlen(TEST_DATA_WITH_TRAILING_BLANKS)),
+ "saving file");
+
+ lines = file_lines_load(TEST_FILENAME, &numlines, 0, mem_ctx);
+
+ torture_assert_int_equal(tctx, numlines, 3, "Lines");
+
+ torture_assert_mem_equal(tctx,
+ lines[0],
+ TEST_LINE1,
+ strlen(TEST_LINE1),
+ "Line 1");
+
+ torture_assert_mem_equal(tctx,
+ lines[1],
+ TEST_LINE2,
+ strlen(TEST_LINE2),
+ "Line 2");
+
+ torture_assert_mem_equal(tctx,
+ lines[2],
+ TEST_LINE3,
+ strlen(TEST_LINE3),
+ "Line 3");
+
+ unlink(TEST_FILENAME);
+
+ return true;
+}
+
+static bool test_afdgets(struct torture_context *tctx)
+{
+ int fd;
+ char *line;
+ TALLOC_CTX *mem_ctx = tctx;
+ bool ret = false;
+
+ torture_assert(tctx, file_save(TEST_FILENAME, (const void *)TEST_DATA,
+ strlen(TEST_DATA)),
+ "saving file");
+
+ fd = open(TEST_FILENAME, O_RDONLY);
+
+ torture_assert(tctx, fd != -1, "opening file");
+
+ line = afdgets(fd, mem_ctx, 8);
+ torture_assert_goto(tctx, strcmp(line, TEST_LINE1) == 0, ret, done,
+ "line 1 mismatch");
+
+ line = afdgets(fd, mem_ctx, 8);
+ torture_assert_goto(tctx, strcmp(line, TEST_LINE2) == 0, ret, done,
+ "line 2 mismatch");
+
+ line = afdgets(fd, mem_ctx, 8);
+ torture_assert_goto(tctx, strcmp(line, TEST_LINE3) == 0, ret, done,
+ "line 3 mismatch");
+ ret = true;
+done:
+ close(fd);
+
+ unlink(TEST_FILENAME);
+ return ret;
+}
+
+static bool test_file_lines_parse(struct torture_context *tctx)
+{
+ char **lines;
+ int numlines;
+ TALLOC_CTX *mem_ctx = tctx;
+ char *buf;
+ size_t size;
+
+ torture_assert(tctx, file_save(TEST_FILENAME,
+ (const void *)TEST_DATA,
+ strlen(TEST_DATA)),
+ "saving file");
+
+ buf = file_load(TEST_FILENAME, &size, 0, mem_ctx);
+ torture_assert(tctx, buf, "failed to load file");
+ unlink(TEST_FILENAME);
+
+ lines = file_lines_parse(buf,
+ size,
+ &numlines,
+ mem_ctx);
+ torture_assert(tctx, lines, "failed to parse lines");
+
+ TALLOC_FREE(lines);
+ TALLOC_FREE(buf);
+ return true;
+}
+
+struct torture_suite *torture_local_util_file(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "file");
+
+ torture_suite_add_simple_test(suite, "file_load_save",
+ test_file_load_save);
+
+ torture_suite_add_simple_test(suite,
+ "file_lines_load",
+ test_file_lines_load);
+
+ torture_suite_add_simple_test(suite, "afdgets", test_afdgets);
+
+ torture_suite_add_simple_test(suite, "file_lines_parse",
+ test_file_lines_parse);
+
+ return suite;
+}
diff --git a/lib/util/tests/genrand.c b/lib/util/tests/genrand.c
new file mode 100644
index 0000000..3987c33
--- /dev/null
+++ b/lib/util/tests/genrand.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of random data routines.
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool test_check_password_quality(struct torture_context *tctx)
+{
+ torture_assert(tctx, !check_password_quality(""), "empty password");
+ torture_assert(tctx, !check_password_quality("a"), "one char password");
+ torture_assert(tctx, !check_password_quality("aaaaaaaaaaaa"), "same char password");
+ torture_assert(tctx, !check_password_quality("BLA"), "multiple upcases password");
+ torture_assert(tctx, !check_password_quality("123"), "digits only");
+ torture_assert(tctx, !check_password_quality("matthiéu"), "not enough high symbols");
+ torture_assert(tctx, !check_password_quality("abcdééàçè"), "only lower case");
+ torture_assert(tctx, !check_password_quality("abcdééàçè+"), "only lower and symbols");
+ torture_assert(tctx, check_password_quality("abcdééàçè+ढ"), "valid");
+ torture_assert(tctx, check_password_quality("ç+ढ"), "valid");
+ torture_assert(tctx, check_password_quality("A2e"), "valid");
+ torture_assert(tctx, check_password_quality("BA2eLi443"), "valid");
+ return true;
+}
+
+static bool test_generate_random_str(struct torture_context *tctx)
+{
+ TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__);
+ char *r = generate_random_str(mem_ctx, 10);
+ torture_assert_int_equal(tctx, strlen(r), 10, "right length generated");
+ r = generate_random_str(mem_ctx, 5);
+ torture_assert_int_equal(tctx, strlen(r), 5, "right length generated");
+
+ TALLOC_FREE(mem_ctx);
+ return true;
+}
+
+struct torture_suite *torture_local_genrand(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "genrand");
+ torture_suite_add_simple_test(suite, "check_password_quality", test_check_password_quality);
+ torture_suite_add_simple_test(suite, "generate_random_str", test_generate_random_str);
+ return suite;
+}
diff --git a/lib/util/tests/genrandperf.c b/lib/util/tests/genrandperf.c
new file mode 100644
index 0000000..32d19ab
--- /dev/null
+++ b/lib/util/tests/genrandperf.c
@@ -0,0 +1,39 @@
+/*
+ Unix SMB/CIFS implementation.
+ local testing of random data routines.
+ Copyright (C) Volker Lendecke <vl@samba.org> 2015
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "lib/util/genrand.h"
+
+int main(int argc, const char *argv[])
+{
+ int i, num;
+ uint64_t val;
+
+ if (argc != 2) {
+ fprintf(stderr, "genrandperf <num>\n");
+ exit(1);
+ }
+ num = atoi(argv[1]);
+
+ for(i=0; i<num; i++) {
+ generate_random_buffer((uint8_t *)&val, sizeof(val));
+ }
+ printf("%"PRIu64"\n", val);
+ return 0;
+}
diff --git a/lib/util/tests/idtree.c b/lib/util/tests/idtree.c
new file mode 100644
index 0000000..f4f7b11
--- /dev/null
+++ b/lib/util/tests/idtree.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of idtree routines.
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool torture_local_idtree_simple(struct torture_context *tctx)
+{
+ struct idr_context *idr;
+ int i, ret;
+ int *ids;
+ int *present;
+ extern int torture_numops;
+ int n = torture_numops;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ idr = idr_init(mem_ctx);
+
+ ids = talloc_zero_array(mem_ctx, int, n);
+ present = talloc_zero_array(mem_ctx, int, n);
+
+ for (i=0;i<n;i++) {
+ ids[i] = -1;
+ }
+
+ for (i=0;i<n;i++) {
+ int ii = random() % n;
+ void *p = idr_find(idr, ids[ii]);
+ if (present[ii]) {
+ if (p != &ids[ii]) {
+ torture_fail(tctx, talloc_asprintf(tctx,
+ "wrong ptr at %d - %p should be %p",
+ ii, p, &ids[ii]));
+ }
+ if (random() % 7 == 0) {
+ if (idr_remove(idr, ids[ii]) != 0) {
+ torture_fail(tctx, talloc_asprintf(tctx,
+ "remove failed at %d (id=%d)",
+ i, ids[ii]));
+ }
+ present[ii] = 0;
+ ids[ii] = -1;
+ }
+ } else {
+ if (p != NULL) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "non-present at %d gave %p (would be %d)",
+ ii, p,
+ (int)((((char *)p) - (char *)(&ids[0])) / sizeof(int))));
+ }
+ if (random() % 5) {
+ ids[ii] = idr_get_new(idr, &ids[ii], n);
+ if (ids[ii] < 0) {
+ torture_fail(tctx, talloc_asprintf(tctx,
+ "alloc failure at %d (ret=%d)",
+ ii, ids[ii]));
+ } else {
+ present[ii] = 1;
+ }
+ }
+ }
+ }
+
+ torture_comment(tctx, "done %d random ops\n", i);
+
+ for (i=0;i<n;i++) {
+ if (present[i]) {
+ if (idr_remove(idr, ids[i]) != 0) {
+ torture_fail(tctx, talloc_asprintf(tctx,
+ "delete failed on cleanup at %d (id=%d)",
+ i, ids[i]));
+ }
+ }
+ }
+
+ /* now test some limits */
+ for (i=0;i<25000;i++) {
+ ret = idr_get_new_above(idr, &ids[0], random() % 25000, 0x10000-3);
+ torture_assert(tctx, ret != -1, "idr_get_new_above failed");
+ }
+
+ ret = idr_get_new_above(idr, &ids[0], 0x10000-2, 0x10000);
+ torture_assert_int_equal(tctx, ret, 0x10000-2, "idr_get_new_above failed");
+ ret = idr_get_new_above(idr, &ids[0], 0x10000-1, 0x10000);
+ torture_assert_int_equal(tctx, ret, 0x10000-1, "idr_get_new_above failed");
+ ret = idr_get_new_above(idr, &ids[0], 0x10000, 0x10000);
+ torture_assert_int_equal(tctx, ret, 0x10000, "idr_get_new_above failed");
+ ret = idr_get_new_above(idr, &ids[0], 0x10000+1, 0x10000);
+ torture_assert_int_equal(tctx, ret, -1, "idr_get_new_above succeeded above limit");
+ ret = idr_get_new_above(idr, &ids[0], 0x10000+2, 0x10000);
+ torture_assert_int_equal(tctx, ret, -1, "idr_get_new_above succeeded above limit");
+
+ torture_comment(tctx, "cleaned up\n");
+ return true;
+}
+
+struct torture_suite *torture_local_idtree(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "idtree");
+ torture_suite_add_simple_test(suite, "idtree", torture_local_idtree_simple);
+ return suite;
+}
diff --git a/lib/util/tests/rfc1738.c b/lib/util/tests/rfc1738.c
new file mode 100644
index 0000000..6e96037
--- /dev/null
+++ b/lib/util/tests/rfc1738.c
@@ -0,0 +1,411 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <cmocka.h>
+#include "lib/replace/replace.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <talloc.h>
+#include <ctype.h>
+#include <string.h>
+#include "lib/util/samba_util.h"
+
+/* These flags say what can be asserted about a relationship between a string
+ and its supposedly escaped equivalent.
+
+ The first part of the flag name indicates the direction of transformation;
+ tyhe second part is the expected result. For example, ESCAPE_EQ means the
+ escape is expected to succeed and result is expected to be equal to the
+ given answer. ESCAPE_EQ_CASECMP is only equal when compared
+ case-insensitively. UNESCAPE_ERR means unescaping the escaped string should
+ result in an error.
+*/
+#define UNESCAPE_ERR 1
+#define ESCAPE_ERR 2
+#define ESCAPE_EQ 4
+#define UNESCAPE_EQ 8
+#define ESCAPE_NE 16
+#define UNESCAPE_NE 32
+#define ESCAPE_EQ_CASECMP 64
+
+struct rfc1738_test {
+ const char *escaped; /* original for unescape; result for escape */
+ const char *unescaped; /* result in unescape; original for escape */
+ uint32_t flags; /* see above */
+ int unesc_len; /* end - start will be this */
+ int unesc_strlen; /* strlen() will say this */
+ int esc_len; /* escaped string length */
+};
+
+/* unreserved = ALPHA DIGIT - . _ ~ */
+
+char spectrum[255 + 1];
+char spectrum_escaped[255 * 3 + 1];
+
+struct rfc1738_test examples[] = {
+
+#define SIMPLE1 "this_is_a_simple-string._With_no_escapes~" /* maps to self */
+ {
+ SIMPLE1,
+ SIMPLE1,
+ ESCAPE_EQ | UNESCAPE_EQ, /* round trip should work */
+ sizeof(SIMPLE1) - 1,
+ sizeof(SIMPLE1) - 1,
+ sizeof(SIMPLE1) - 1,
+ },
+#define SIMPLE2 "no escapes, but\n non-printables \xc5\x8d\x99"
+#define SIMPLE2_ESC "no%20escapes%2C%20but%0A%20non-printables%20%C5%8D%99"
+ {
+ SIMPLE2_ESC,
+ SIMPLE2,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(SIMPLE2) - 1,
+ sizeof(SIMPLE2) - 1,
+ sizeof(SIMPLE2_ESC) - 1,
+ },
+#define SIMPLE3 "this @#$^&*()_+{}:;"
+#define SIMPLE3_ESC "this%20%40%23%24%5E%26%2A%28%29_%2B%7B%7D%3A%3B"
+ {
+ SIMPLE3_ESC,
+ SIMPLE3,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(SIMPLE3) - 1,
+ sizeof(SIMPLE3) - 1,
+ sizeof(SIMPLE3_ESC) - 1,
+ },
+
+#define ESCAPE1 "%/\x06this string has expected escapes"
+#define ESCAPE1_ESC "%25%2F%06this%20string%20has%20expected%20escapes"
+#define ESCAPE1_ESC_ESC "%2525%252F%2506this%2520string%2520has%2520expected"\
+ "%2520escapes"
+ {
+ ESCAPE1_ESC,
+ ESCAPE1,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(ESCAPE1) - 1,
+ sizeof(ESCAPE1) - 1,
+ sizeof(ESCAPE1_ESC) - 1,
+ },
+ {
+ ESCAPE1_ESC_ESC, /*re-escaping */
+ ESCAPE1_ESC,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(ESCAPE1_ESC) - 1,
+ sizeof(ESCAPE1_ESC) - 1,
+ sizeof(ESCAPE1_ESC_ESC) - 1,
+ },
+#define ESCAPE2 "%25%2f%06-this-string-has-expected-lowercase-escapes-%ab"
+#define ESCAPE2_UNESC "%/\x06-this-string-has-expected-lowercase-escapes-\xab"
+ {
+ ESCAPE2,
+ ESCAPE2_UNESC,
+ ESCAPE_EQ_CASECMP | UNESCAPE_EQ, /* escape won't match case */
+ sizeof(ESCAPE2_UNESC) - 1,
+ sizeof(ESCAPE2_UNESC) - 1,
+ sizeof(ESCAPE2) - 1,
+ },
+#define ESCAPE3 "%25%2f%06 %32 %44 %6a%AA THIS string h%61s random escapes %ab"
+#define ESCAPE3_UNESC "%/\x06 2 D j\xAA THIS string has random escapes \xab"
+ {
+ ESCAPE3,
+ ESCAPE3_UNESC,
+ ESCAPE_NE | UNESCAPE_EQ, /* escape will have escaped spaces */
+ sizeof(ESCAPE3_UNESC) - 1,
+ sizeof(ESCAPE3_UNESC) - 1,
+ sizeof(ESCAPE3) - 1,
+ },
+#define ESCAPE4 "%25%25%25" /* */
+#define ESCAPE4_UNESC "%%%" /* */
+#define ESCAPE4_ESC "%2525%2525%2525"
+ {
+ ESCAPE4,
+ ESCAPE4_UNESC,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(ESCAPE4_UNESC) - 1,
+ sizeof(ESCAPE4_UNESC) - 1,
+ sizeof(ESCAPE4) - 1,
+ },
+ {
+ ESCAPE4_ESC,
+ ESCAPE4,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ sizeof(ESCAPE4) - 1,
+ sizeof(ESCAPE4) - 1,
+ sizeof(ESCAPE4_ESC) - 1,
+ },
+#define BAD1 "trailing percent is bad %"
+#define BAD1_ESC "trailing%20percent%20is%20bad%20%25"
+ {
+ BAD1_ESC,
+ BAD1,
+ UNESCAPE_EQ |ESCAPE_EQ,
+ sizeof(BAD1) - 1,
+ sizeof(BAD1) - 1,
+ sizeof(BAD1_ESC) - 1,
+ },
+ {
+ BAD1,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD1) - 1,
+ },
+#define BAD2 "trailing percent is bad %1"
+#define BAD3 "bad characters %1 "
+ {
+ BAD2,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD2) - 1,
+ },
+ {
+ BAD3,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD3) - 1,
+ },
+#define BAD4 "bad characters %1 "
+ {
+ BAD4,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD4) - 1,
+ },
+#define BAD5 "bad characters %1- "
+ {
+ BAD5,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD5) - 1,
+ },
+#define BAD6 "bad characters %1G "
+ {
+ BAD6,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD6) - 1,
+ },
+#define BAD7 "bad characters %%1 "
+ {
+ BAD7,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD7) - 1,
+ },
+#define BAD8 "bad characters %sb "
+ {
+ BAD8,
+ NULL,
+ UNESCAPE_ERR,
+ 0,
+ 0,
+ sizeof(BAD8) - 1,
+ },
+#define BAD_SSCANF "sscanf would be happy with this\n"
+#define BAD_SSCANF_ESC "sscanf would be happy with this% a"
+ {
+ BAD_SSCANF_ESC,
+ BAD_SSCANF,
+ ESCAPE_NE | UNESCAPE_ERR,
+ sizeof(BAD_SSCANF) - 1,
+ sizeof(BAD_SSCANF) - 1,
+ sizeof(BAD_SSCANF_ESC) - 1,
+ },
+ /* now try some with zeros in. escaping can't see past zeros, and the result is truncated */
+#define ZERO "%00"
+#define ZERO_UNESC "\0"
+ {
+ ESCAPE4 ZERO ESCAPE4,
+ ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC,
+ ESCAPE_NE | UNESCAPE_EQ,
+ sizeof(ESCAPE4_UNESC ZERO_UNESC ESCAPE4_UNESC) - 1,
+ sizeof(ESCAPE4_UNESC) - 1,
+ sizeof(ESCAPE4 ZERO ESCAPE4) - 1,
+ },
+ {
+ ZERO ESCAPE4,
+ ZERO_UNESC ESCAPE4_UNESC,
+ ESCAPE_NE | UNESCAPE_EQ,
+ sizeof(ZERO_UNESC ESCAPE4_UNESC) - 1,
+ 0,
+ sizeof(ZERO ESCAPE4) - 1,
+ },
+ {
+ ZERO,
+ ZERO_UNESC,
+ ESCAPE_NE | UNESCAPE_EQ,
+ sizeof(ZERO_UNESC) - 1,
+ 0,
+ sizeof(ZERO) - 1,
+ },
+ {
+ spectrum_escaped,
+ spectrum,
+ ESCAPE_EQ | UNESCAPE_EQ,
+ 255,
+ 255,
+ 255 * 3,
+ },
+};
+
+static struct rfc1738_test * dup_test(struct rfc1738_test *src)
+{
+ struct rfc1738_test *dest = malloc(sizeof(*dest));
+ char *esc = NULL, *unesc = NULL;
+ if (dest == NULL) {
+ return NULL;
+ }
+ *dest = *src;
+ if (src->esc_len) {
+ esc = malloc(src->esc_len + 1);
+ if (esc == NULL) {
+ free(dest);
+ return NULL;
+ }
+ memcpy(esc, src->escaped, src->esc_len + 1);
+ dest->escaped = esc;
+ }
+
+ if (src->unesc_len) {
+ unesc = malloc(src->unesc_len + 1);
+ if (unesc == NULL) {
+ free(esc);
+ free(dest);
+ return NULL;
+ }
+ memcpy(unesc, src->unescaped, src->unesc_len + 1);
+ dest->unescaped = unesc;
+ }
+
+ return dest;
+}
+
+static void free_test(struct rfc1738_test *t)
+{
+ free(discard_const_p(char, t->escaped));
+ free(discard_const_p(char, t->unescaped));
+ free(t);
+}
+
+
+static void test_unescape(void **state)
+{
+ uint i;
+ char *s, *e;
+ struct rfc1738_test *test, *orig;
+ for (i = 0; i < ARRAY_SIZE(examples); i++) {
+ orig = &examples[i];
+ if ((orig->flags & (UNESCAPE_ERR |
+ UNESCAPE_EQ |
+ UNESCAPE_NE)) == 0) {
+ continue;
+ }
+ test = dup_test(&examples[i]);
+ s = discard_const_p(char, test->escaped);
+ e = rfc1738_unescape(s);
+ if (test->flags & UNESCAPE_ERR) {
+ assert_null(e);
+ free_test(test);
+ continue;
+ }
+ assert_non_null(e);
+ assert_int_equal(e - s, test->unesc_len);
+
+ if (test->flags & UNESCAPE_EQ) {
+ assert_memory_equal(s,
+ orig->unescaped,
+ orig->unesc_len);
+ assert_int_equal(strlen(s),
+ orig->unesc_strlen);
+ } else {
+ assert_memory_not_equal(s,
+ orig->unescaped,
+ orig->unesc_len);
+ assert_int_equal(strlen(s),
+ orig->unesc_strlen);
+ }
+ free_test(test);
+ }
+}
+
+static void test_escape(void **state)
+{
+ uint i;
+ char *s, *e;
+ struct rfc1738_test *test, *orig;
+ for (i = 0; i < ARRAY_SIZE(examples); i++) {
+ orig = &examples[i];
+ if ((orig->flags & (ESCAPE_EQ |
+ ESCAPE_EQ_CASECMP |
+ ESCAPE_NE)) == 0) {
+ continue;
+ }
+ test = dup_test(&examples[i]);
+ s = discard_const_p(char, test->unescaped);
+ e = rfc1738_escape_part(NULL, s);
+ if (test->flags & ESCAPE_EQ) {
+ assert_memory_equal(e, test->escaped,
+ test->esc_len + 1);
+ } else if (test->flags & ESCAPE_EQ_CASECMP) {
+ int cmp = strcasecmp(e, test->escaped);
+ assert_int_equal(cmp, 0);
+ assert_string_not_equal(e, test->escaped);
+ } else {
+ assert_string_not_equal(e, test->escaped);
+ }
+ free_test(test);
+ }
+}
+
+
+static void gen_spectrum(void)
+{
+ int i, j = 0;
+ const char *lut = "0123456789ABCDEF";
+ for (i = 1; i < 256; i++) {
+ spectrum[i - 1] = i;
+ if (isalnum(i) ||
+ i == '-' ||
+ i == '.' ||
+ i == '_' ||
+ i == '-' ||
+ i == '~') {
+ spectrum_escaped[j] = i;
+ j++;
+ } else {
+ spectrum_escaped[j] = '%';
+ spectrum_escaped[j + 1] = lut[i >> 4];
+ spectrum_escaped[j + 2] = lut[i & 15];
+ j += 3;
+ }
+ }
+ spectrum[i - 1] = '\0';
+ spectrum_escaped[j] = '\0';
+}
+
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_escape),
+ cmocka_unit_test(test_unescape),
+ };
+
+ gen_spectrum();
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/util/tests/str.c b/lib/util/tests/str.c
new file mode 100644
index 0000000..41a2836
--- /dev/null
+++ b/lib/util/tests/str.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ util_str testing
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool test_string_sub_simple(struct torture_context *tctx)
+{
+ char tmp[100];
+ strlcpy(tmp, "foobar", sizeof(tmp));
+ string_sub(tmp, "foo", "bar", sizeof(tmp));
+ torture_assert_str_equal(tctx, tmp, "barbar", "invalid sub");
+ return true;
+}
+
+static bool test_string_sub_multiple(struct torture_context *tctx)
+{
+ char tmp[100];
+ strlcpy(tmp, "fooblafoo", sizeof(tmp));
+ string_sub(tmp, "foo", "bar", sizeof(tmp));
+ torture_assert_str_equal(tctx, tmp, "barblabar", "invalid sub");
+ return true;
+}
+
+static bool test_string_sub_longer(struct torture_context *tctx)
+{
+ char tmp[100];
+ strlcpy(tmp, "foobla", sizeof(tmp));
+ string_sub(tmp, "foo", "blie", sizeof(tmp));
+ torture_assert_str_equal(tctx, tmp, "bliebla", "invalid sub");
+ return true;
+}
+
+static bool test_string_sub_shorter(struct torture_context *tctx)
+{
+ char tmp[100];
+ strlcpy(tmp, "foobla", sizeof(tmp));
+ string_sub(tmp, "foo", "bl", sizeof(tmp));
+ torture_assert_str_equal(tctx, tmp, "blbla", "invalid sub");
+ return true;
+}
+
+static bool test_string_sub_special_char(struct torture_context *tctx)
+{
+ char tmp[100];
+ strlcpy(tmp, "foobla", sizeof(tmp));
+ string_sub(tmp, "foo", "%b;l", sizeof(tmp));
+ torture_assert_str_equal(tctx, tmp, "_b_lbla", "invalid sub");
+ return true;
+}
+
+static bool test_talloc_string_sub_simple(struct torture_context *tctx)
+{
+ char *t;
+
+ t = talloc_string_sub(tctx, "foobla", "foo", "bl");
+
+ torture_assert_str_equal(tctx, t, "blbla", "invalid sub");
+
+ return true;
+}
+
+static bool test_talloc_string_sub_multiple(struct torture_context *tctx)
+{
+ char *t;
+
+ t = talloc_string_sub(tctx, "fooblafoo", "foo", "aapnootmies");
+
+ torture_assert_str_equal(tctx, t, "aapnootmiesblaaapnootmies",
+ "invalid sub");
+
+ return true;
+}
+
+/*
+ * with these next three tests, the failure is that the pattern looks like
+ * "+++" because the \x.. bytes encode a zero byte in UTF-8. If we are not
+ * careful with these strings we will see crashes instead of failures.
+ */
+
+static bool test_talloc_string_sub_tricky_utf8_4(struct torture_context *tctx)
+{
+ const char string[] = "++++--\xD8\xBB";
+ const char pattern[] = "+++\xF0\x80\x80\x80++";
+ const char replace[] = "...";
+
+ char *t = talloc_string_sub(tctx, string, pattern, replace);
+ torture_assert_str_equal(tctx, t, string,
+ "should reject 4 byte NUL char");
+ talloc_free(t);
+ return true;
+}
+
+static bool test_talloc_string_sub_tricky_utf8_3(struct torture_context *tctx)
+{
+ const char string[] = "++++--\xD8\xBB";
+ const char pattern[] = "+++\xE0\x80\x80++";
+ const char replace[] = "...";
+
+ char *t = talloc_string_sub(tctx, string, pattern, replace);
+ torture_assert_str_equal(tctx, t, string,
+ "should reject 3 byte NUL char");
+ talloc_free(t);
+ return true;
+}
+
+static bool test_talloc_string_sub_tricky_utf8_2(struct torture_context *tctx)
+{
+ const char string[] = "++++--\xD8\xBB";
+ const char pattern[] = "+++\xC0\x80++";
+ const char replace[] = "...";
+
+ char *t = talloc_string_sub(tctx, string, pattern, replace);
+ torture_assert_str_equal(tctx, t, string,
+ "should reject 2 byte NUL char");
+ talloc_free(t);
+ return true;
+}
+
+
+
+
+struct torture_suite *torture_local_util_str(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "str");
+
+ torture_suite_add_simple_test(suite, "string_sub_simple",
+ test_string_sub_simple);
+
+ torture_suite_add_simple_test(suite, "string_sub_multiple",
+ test_string_sub_multiple);
+
+ torture_suite_add_simple_test(suite, "string_sub_shorter",
+ test_string_sub_shorter);
+
+ torture_suite_add_simple_test(suite, "string_sub_longer",
+ test_string_sub_longer);
+
+ torture_suite_add_simple_test(suite, "string_sub_special_chars",
+ test_string_sub_special_char);
+
+ torture_suite_add_simple_test(suite, "talloc_string_sub_simple",
+ test_talloc_string_sub_simple);
+
+ torture_suite_add_simple_test(suite, "string_sub_talloc_multiple",
+ test_talloc_string_sub_multiple);
+
+ torture_suite_add_simple_test(suite,
+ "test_talloc_string_sub_tricky_utf8_4",
+ test_talloc_string_sub_tricky_utf8_4);
+
+ torture_suite_add_simple_test(suite,
+ "test_talloc_string_sub_tricky_utf8_3",
+ test_talloc_string_sub_tricky_utf8_3);
+
+ torture_suite_add_simple_test(suite,
+ "test_talloc_string_sub_tricky_utf8_2",
+ test_talloc_string_sub_tricky_utf8_2);
+
+ return suite;
+}
diff --git a/lib/util/tests/strlist.c b/lib/util/tests/strlist.c
new file mode 100644
index 0000000..a9306b8
--- /dev/null
+++ b/lib/util/tests/strlist.c
@@ -0,0 +1,558 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ util_strlist testing
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "param/param.h"
+
+struct test_list_element {
+ const char *list_as_string;
+ const char *separators;
+ const char *list[5];
+};
+
+const struct test_list_element test_lists_strings[] = {
+ {
+ .list_as_string = "",
+ .list = { NULL }
+ },
+ {
+ .list_as_string = "foo",
+ .list = { "foo", NULL }
+ },
+ {
+ .list_as_string = "foo bar",
+ .list = { "foo", "bar", NULL }
+ },
+ {
+ .list_as_string = "foo bar",
+ .list = { "foo bar", NULL },
+ .separators = ";"
+ },
+ {
+ .list_as_string = "\"foo bar\"",
+ .list = { "\"foo", "bar\"", NULL }
+ },
+ {
+ .list_as_string = "\"foo bar\",comma\ttab",
+ .list = { "\"foo", "bar\"", "comma", "tab", NULL }
+ },
+ {
+ .list_as_string = "\"foo bar\",comma;semicolon",
+ .list = { "\"foo bar\",comma", "semicolon", NULL },
+ .separators = ";"
+ }
+};
+
+const struct test_list_element test_lists_shell_strings[] = {
+ {
+ .list_as_string = "",
+ .list = { NULL }
+ },
+ {
+ .list_as_string = "foo",
+ .list = { "foo", NULL }
+ },
+ {
+ .list_as_string = "foo bar",
+ .list = { "foo", "bar", NULL }
+ },
+ {
+ .list_as_string = "foo bar",
+ .list = { "foo bar", NULL },
+ .separators = ";"
+ },
+ {
+ .list_as_string = "\"foo bar\"",
+ .list = { "foo bar", NULL }
+ },
+ {
+ .list_as_string = "foo bar \"bla \"",
+ .list = { "foo", "bar", "bla ", NULL }
+ },
+ {
+ .list_as_string = "foo \"\" bla",
+ .list = { "foo", "", "bla", NULL },
+ },
+ {
+ .list_as_string = "bla \"\"\"\" blie",
+ .list = { "bla", "", "", "blie", NULL },
+ }
+};
+
+static bool test_lists_shell(struct torture_context *tctx, const void *data)
+{
+ const struct test_list_element *element = data;
+
+ char **ret1, **ret2, *tmp;
+ bool match = true;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ ret1 = str_list_make_shell(mem_ctx, element->list_as_string, element->separators);
+
+ torture_assert(tctx, ret1, "str_list_make_shell() must not return NULL");
+ tmp = str_list_join_shell(mem_ctx, discard_const_p(const char *, ret1),
+ element->separators ? *element->separators : ' ');
+ ret2 = str_list_make_shell(mem_ctx, tmp, element->separators);
+
+ if ((ret1 == NULL || ret2 == NULL) && ret2 != ret1) {
+ match = false;
+ } else {
+ int j;
+ for (j = 0; ret1[j] && ret2[j]; j++) {
+ if (strcmp(ret1[j], ret2[j]) != 0) {
+ match = false;
+ break;
+ }
+ }
+
+ if (ret1[j] || ret2[j])
+ match = false;
+ }
+
+ torture_assert(tctx, match, talloc_asprintf(tctx,
+ "str_list_{make,join}_shell: Error double parsing, first run:\n%s\nSecond run: \n%s", element->list_as_string, tmp));
+ torture_assert(tctx, str_list_equal((const char * const *) ret1,
+ element->list),
+ talloc_asprintf(tctx,
+ "str_list_make_shell(%s) failed to create correct list",
+ element->list_as_string));
+
+ return true;
+}
+
+static bool test_list_make(struct torture_context *tctx, const void *data)
+{
+ const struct test_list_element *element = data;
+
+ char **result;
+ result = str_list_make(tctx, element->list_as_string, element->separators);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ torture_assert(tctx, str_list_equal((const char * const *) result,
+ element->list),
+ talloc_asprintf(tctx,
+ "str_list_make(%s) failed to create correct list",
+ element->list_as_string));
+ return true;
+}
+
+static bool test_list_copy(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = { "foo", "bar", NULL };
+ const char *empty_list[] = { NULL };
+ const char **null_list = NULL;
+ char **l;
+
+ l = str_list_copy(tctx, list);
+ result = discard_const_p(const char *, l);
+ torture_assert_int_equal(tctx, str_list_length(result), 2, "list length");
+ torture_assert_str_equal(tctx, result[0], "foo", "element 0");
+ torture_assert_str_equal(tctx, result[1], "bar", "element 1");
+ torture_assert_str_equal(tctx, result[2], NULL, "element 2");
+
+ l = str_list_copy(tctx, empty_list);
+ result = discard_const_p(const char *, l);
+ torture_assert_int_equal(tctx, str_list_length(result), 0, "list length");
+ torture_assert_str_equal(tctx, result[0], NULL, "element 0");
+
+ l = str_list_copy(tctx, null_list);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result == NULL, "result NULL");
+
+ return true;
+}
+
+static bool test_list_make_empty(struct torture_context *tctx)
+{
+ char **result;
+
+ result = str_list_make_empty(tctx);
+ torture_assert(tctx, result, "str_list_make_empty() must not return NULL");
+ torture_assert(tctx, result[0] == NULL, "first element in str_list_make_empty() result must be NULL");
+
+ result = str_list_make(tctx, NULL, NULL);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, NULL, NULL) result must be NULL");
+
+ result = str_list_make(tctx, "", NULL);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ torture_assert(tctx, result[0] == NULL, "first element in str_list_make(ctx, "", NULL) result must be NULL");
+
+ return true;
+}
+
+static bool test_list_make_single(struct torture_context *tctx)
+{
+ char **result;
+
+ result = str_list_make_single(tctx, "foo");
+
+ torture_assert(tctx, result, "str_list_make_single() must not return NULL");
+ torture_assert_str_equal(tctx, result[0], "foo", "element 0");
+ torture_assert(tctx, result[1] == NULL, "second element in result must be NULL");
+
+ return true;
+}
+
+static bool test_list_copy_const(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ NULL
+ };
+ result = str_list_copy_const(tctx, list);
+ torture_assert(tctx, result, "str_list_copy() must not return NULL");
+ torture_assert(tctx, str_list_equal(result, list),
+ "str_list_copy() failed");
+
+ return true;
+}
+
+static bool test_list_length(struct torture_context *tctx)
+{
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ NULL
+ };
+ const char *list2[] = {
+ NULL
+ };
+ torture_assert_int_equal(tctx, str_list_length(list), 4,
+ "str_list_length() failed");
+
+ torture_assert_int_equal(tctx, str_list_length(list2), 0,
+ "str_list_length() failed");
+
+ torture_assert_int_equal(tctx, str_list_length(NULL), 0,
+ "str_list_length() failed");
+
+ return true;
+}
+
+static bool test_list_add(struct torture_context *tctx)
+{
+ const char **result, **result2;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ NULL
+ };
+ char **l;
+
+ l = str_list_make(tctx, "element_0, element_1, element_2", NULL);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ result2 = str_list_add(result, "element_3");
+ torture_assert(tctx, result2, "str_list_add() must not return NULL");
+ torture_assert(tctx, str_list_equal(result2, list),
+ "str_list_add() failed");
+
+ return true;
+}
+
+static bool test_list_add_const(struct torture_context *tctx)
+{
+ const char **result, **result2;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ NULL
+ };
+ char **l;
+
+ l = str_list_make(tctx, "element_0, element_1, element_2", NULL);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ result2 = str_list_add_const(result, "element_3");
+ torture_assert(tctx, result2, "str_list_add_const() must not return NULL");
+ torture_assert(tctx, str_list_equal(result2, list),
+ "str_list_add() failed");
+
+ return true;
+}
+
+static bool test_list_remove(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_3",
+ NULL
+ };
+ char **l;
+
+ l = str_list_make(tctx, "element_0, element_1, element_2, element_3", NULL);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result, "str_list_make() must not return NULL");
+ str_list_remove(result, "element_2");
+ torture_assert(tctx, str_list_equal(result, list),
+ "str_list_remove() failed");
+
+ return true;
+}
+
+static bool test_list_check(struct torture_context *tctx)
+{
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ torture_assert(tctx, str_list_check(list, "element_1"),
+ "str_list_check() failed");
+
+ return true;
+}
+
+static bool test_list_check_ci(struct torture_context *tctx)
+{
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ torture_assert(tctx, str_list_check_ci(list, "ELEMENT_1"),
+ "str_list_check_ci() failed");
+
+ return true;
+}
+
+static bool test_list_unique(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ const char *list_dup[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_0",
+ "element_2",
+ "element_1",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ char **l;
+
+ l = str_list_copy(tctx, list_dup);
+ result = discard_const_p(const char *, l);
+ /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */
+ result = str_list_unique(result);
+ torture_assert(tctx, result, "str_list_unique() must not return NULL");
+
+ torture_assert(tctx, str_list_equal(list, result),
+ "str_list_unique() failed");
+
+ return true;
+}
+
+static bool test_list_unique_2(struct torture_context *tctx)
+{
+ int i;
+ int count, num_dups;
+ const char **result;
+ char **l1 = str_list_make_empty(tctx);
+ char **l2 = str_list_make_empty(tctx);
+ const char **list = discard_const_p(const char *, l1);
+ const char **list_dup = discard_const_p(const char *, l2);
+ char **l;
+
+ count = lpcfg_parm_int(tctx->lp_ctx, NULL, "list_unique", "count", 9);
+ num_dups = lpcfg_parm_int(tctx->lp_ctx, NULL, "list_unique", "dups", 7);
+ torture_comment(tctx, "test_list_unique_2() with %d elements and %d dups\n", count, num_dups);
+
+ for (i = 0; i < count; i++) {
+ list = str_list_add_const(list, (const char *)talloc_asprintf(tctx, "element_%03d", i));
+ }
+
+ for (i = 0; i < num_dups; i++) {
+ list_dup = str_list_append(list_dup, list);
+ }
+
+ l = str_list_copy(tctx, list_dup);
+ result = discard_const_p(const char *, l);
+ /* We must copy the list, as str_list_unique does a talloc_realloc() on it's parameter */
+ result = str_list_unique(result);
+ torture_assert(tctx, result, "str_list_unique() must not return NULL");
+
+ torture_assert(tctx, str_list_equal(list, result),
+ "str_list_unique() failed");
+
+ return true;
+}
+
+static bool test_list_append(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ const char *list2[] = {
+ "element_3",
+ "element_4",
+ "element_5",
+ NULL
+ };
+ const char *list_combined[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ "element_4",
+ "element_5",
+ NULL
+ };
+ char **l;
+ l = str_list_copy(tctx, list);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result, "str_list_copy() must not return NULL");
+ result = str_list_append(result, list2);
+ torture_assert(tctx, result, "str_list_append() must not return NULL");
+ torture_assert(tctx, str_list_equal(list_combined, result),
+ "str_list_unique() failed");
+
+ return true;
+}
+
+static bool test_list_append_const(struct torture_context *tctx)
+{
+ const char **result;
+ const char *list[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ NULL
+ };
+ const char *list2[] = {
+ "element_3",
+ "element_4",
+ "element_5",
+ NULL
+ };
+ const char *list_combined[] = {
+ "element_0",
+ "element_1",
+ "element_2",
+ "element_3",
+ "element_4",
+ "element_5",
+ NULL
+ };
+ char **l;
+ l = str_list_copy(tctx, list);
+ result = discard_const_p(const char *, l);
+ torture_assert(tctx, result, "str_list_copy() must not return NULL");
+ result = str_list_append_const(result, list2);
+ torture_assert(tctx, result, "str_list_append_const() must not return NULL");
+ torture_assert(tctx, str_list_equal(list_combined, result),
+ "str_list_unique() failed");
+
+ return true;
+}
+
+static bool test_list_add_printf_NULL(struct torture_context *tctx)
+{
+ char **list = NULL;
+ str_list_add_printf(&list, "x=%d", 1);
+ torture_assert(tctx, list==NULL, "str_list_add_printf must keep NULL");
+ return true;
+}
+
+static bool test_list_add_printf(struct torture_context *tctx)
+{
+ const char *list2[] = { "foo", "bar=baz", NULL };
+ char **list = str_list_make_empty(tctx);
+ str_list_add_printf(&list, "foo");
+ str_list_add_printf(&list, "bar=%s", "baz");
+ torture_assert(
+ tctx,
+ str_list_equal((const char * const *)list, list2),
+ "str_list_add_printf failed");
+ TALLOC_FREE(list);
+ return true;
+}
+
+struct torture_suite *torture_local_util_strlist(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "strlist");
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(test_lists_shell_strings); i++) {
+ char *name;
+ name = talloc_asprintf(suite, "lists_shell(%s)",
+ test_lists_shell_strings[i].list_as_string);
+ torture_suite_add_simple_tcase_const(suite, name,
+ test_lists_shell, &test_lists_shell_strings[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_lists_strings); i++) {
+ char *name;
+ name = talloc_asprintf(suite, "list_make(%s)",
+ test_lists_strings[i].list_as_string);
+ torture_suite_add_simple_tcase_const(suite, name,
+ test_list_make, &test_lists_strings[i]);
+ }
+
+ torture_suite_add_simple_test(suite, "list_copy", test_list_copy);
+ torture_suite_add_simple_test(suite, "make_empty", test_list_make_empty);
+ torture_suite_add_simple_test(suite, "make_single", test_list_make_single);
+ torture_suite_add_simple_test(suite, "list_copy_const", test_list_copy_const);
+ torture_suite_add_simple_test(suite, "list_length", test_list_length);
+ torture_suite_add_simple_test(suite, "list_add", test_list_add);
+ torture_suite_add_simple_test(suite, "list_add_const", test_list_add_const);
+ torture_suite_add_simple_test(suite, "list_remove", test_list_remove);
+ torture_suite_add_simple_test(suite, "list_check", test_list_check);
+ torture_suite_add_simple_test(suite, "list_check_ci", test_list_check_ci);
+ torture_suite_add_simple_test(suite, "list_unique", test_list_unique);
+ torture_suite_add_simple_test(suite, "list_unique_2", test_list_unique_2);
+ torture_suite_add_simple_test(suite, "list_append", test_list_append);
+ torture_suite_add_simple_test(suite, "list_append_const", test_list_append_const);
+ torture_suite_add_simple_test(
+ suite, "list_add_printf_NULL", test_list_add_printf_NULL);
+ torture_suite_add_simple_test(
+ suite, "list_add_printf", test_list_add_printf);
+ return suite;
+}
diff --git a/lib/util/tests/strv.c b/lib/util/tests/strv.c
new file mode 100644
index 0000000..c79ed5c
--- /dev/null
+++ b/lib/util/tests/strv.c
@@ -0,0 +1,201 @@
+/*
+ * Tests for strv
+ *
+ * Copyright Martin Schwenke <martin@meltin.net> 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <talloc.h>
+
+#include "replace.h"
+
+#include "libcli/util/ntstatus.h"
+#include "torture/torture.h"
+#include "lib/util/data_blob.h"
+#include "torture/local/proto.h"
+
+#include "lib/util/strv.h"
+
+static bool test_strv_empty(struct torture_context *tctx)
+{
+ /* NULL strv contains 0 entries */
+ torture_assert_int_equal(tctx,
+ strv_count(NULL),
+ 0,
+ "strv_count() on NULL failed");
+
+ /* NULL strv has no next entry */
+ torture_assert(tctx,
+ strv_next(NULL, NULL) == NULL,
+ "strv_next() on NULL failed");
+
+ return true;
+}
+
+static bool test_strv_single(struct torture_context *tctx)
+{
+ const char *data = "foo";
+ char *strv = NULL;
+ char *t;
+ int ret;
+
+ /* Add an item */
+ ret = strv_add(tctx, &strv, data);
+ torture_assert(tctx, ret == 0, "strv_add() failed");
+
+ /* Is there 1 item? */
+ torture_assert_int_equal(tctx,
+ strv_count(strv), 1,
+ "strv_count() failed");
+
+ /* Is the expected item the first one? */
+ t = strv_next(strv, NULL);
+ torture_assert(tctx,
+ strcmp(t, data) == 0,
+ "strv_next() failed");
+
+ /* Can the expected item be found? */
+ t = strv_find(strv, data);
+ torture_assert(tctx,
+ strcmp(t, data) == 0,
+ "strv_next() failed");
+
+ /* Delete it */
+ strv_delete(&strv, t);
+
+ /* Should have no items */
+ torture_assert_int_equal(tctx,
+ strv_count(strv), 0,
+ "strv_count() failed");
+ return true;
+}
+
+static bool test_strv_multi(struct torture_context *tctx)
+{
+ const char *data[] = { "foo", "bar", "", "samba", "x"};
+ char *strv = NULL;
+ char *t;
+ int i, ret;
+ const int num = ARRAY_SIZE(data);
+
+ /* Add items */
+ for (i = 0; i < num; i++) {
+ ret = strv_add(tctx, &strv, data[i]);
+ torture_assert(tctx, ret == 0, "strv_add() failed");
+ }
+
+ torture_assert_int_equal(tctx,
+ strv_count(strv), num,
+ "strv_count() failed");
+
+ /* Check that strv_next() finds the expected values */
+ t = NULL;
+ for (i = 0; i < num; i++) {
+ t = strv_next(strv, t);
+ torture_assert(tctx,
+ strcmp(t, data[i]) == 0,
+ "strv_next() failed");
+ }
+
+
+ /* Check that strv_next() finds the expected values */
+ t = NULL;
+ for (i = 0; i < num; i++) {
+ t = strv_next(strv, t);
+ torture_assert(tctx,
+ strcmp(t, data[i]) == 0,
+ "strv_next() failed");
+ }
+
+ /* Find each item, delete it, check count */
+ for (i = 0; i < num; i++) {
+ t = strv_find(strv, data[i]);
+ torture_assert(tctx,
+ strcmp(t, data[i]) == 0,
+ "strv_next() failed");
+ strv_delete(&strv, t);
+ torture_assert_int_equal(tctx,
+ strv_count(strv), num - i - 1,
+ "strv_delete() failed");
+ }
+
+ /* Add items */
+ for (i = 0; i < num; i++) {
+ ret = strv_add(tctx, &strv, data[i]);
+ torture_assert(tctx, ret == 0, "strv_add() failed");
+ }
+
+ torture_assert_int_equal(tctx,
+ strv_count(strv), num,
+ "strv_count() failed");
+
+ /* Find items in reverse, delete, check count */
+ for (i = num - 1; i >= 0; i--) {
+ t = strv_find(strv, data[i]);
+ torture_assert(tctx,
+ strcmp(t, data[i]) == 0,
+ "strv_next() failed");
+ strv_delete(&strv, t);
+ torture_assert_int_equal(tctx,
+ strv_count(strv), i,
+ "strv_delete() failed");
+ }
+
+ return true;
+}
+
+/* Similar to above but only add/check first 2 chars of each string */
+static bool test_strv_addn(struct torture_context *tctx)
+{
+ const char *data[] = { "foo", "bar", "samba" };
+ char *strv = NULL;
+ char *t;
+ int i, ret;
+ const int num = ARRAY_SIZE(data);
+
+ /* Add first 2 chars of each item */
+ for (i = 0; i < num; i++) {
+ ret = strv_addn(tctx, &strv, data[i], 2);
+ torture_assert(tctx, ret == 0, "strv_add() failed");
+ }
+
+ torture_assert_int_equal(tctx,
+ strv_count(strv), num,
+ "strv_count() failed");
+
+ /* Check that strv_next() finds the expected values */
+ t = NULL;
+ for (i = 0; i < num; i++) {
+ t = strv_next(strv, t);
+ torture_assert(tctx,
+ strncmp(t, data[i], 2) == 0,
+ "strv_next() failed");
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_local_util_strv(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "strv");
+
+ torture_suite_add_simple_test(suite, "strv_empty", test_strv_empty);
+ torture_suite_add_simple_test(suite, "strv_single", test_strv_single);
+ torture_suite_add_simple_test(suite, "strv_multi", test_strv_multi);
+ torture_suite_add_simple_test(suite, "strv_addn", test_strv_addn);
+
+ return suite;
+}
diff --git a/lib/util/tests/strv_util.c b/lib/util/tests/strv_util.c
new file mode 100644
index 0000000..b1496c7
--- /dev/null
+++ b/lib/util/tests/strv_util.c
@@ -0,0 +1,150 @@
+/*
+ * Tests for strv_util
+ *
+ * Copyright Martin Schwenke <martin@meltin.net> 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <talloc.h>
+
+#include "replace.h"
+
+#include "libcli/util/ntstatus.h"
+#include "torture/torture.h"
+#include "lib/util/data_blob.h"
+#include "torture/local/proto.h"
+
+#include "lib/util/strv.h"
+#include "lib/util/strv_util.h"
+
+static bool test_strv_split_none(struct torture_context *tctx)
+{
+ char *strv = NULL;
+ int ret;
+
+ /* NULL has 0 entries */
+ ret = strv_split(tctx, &strv, NULL, " ");
+ torture_assert(tctx, ret == 0, "strv_split() on NULL failed");
+ torture_assert_int_equal(tctx,
+ strv_count(strv),
+ 0,
+ "strv_split() on NULL failed");
+ TALLOC_FREE(strv);
+
+ /* Empty string has 0 entries */
+ ret = strv_split(tctx, &strv, "", " ");
+ torture_assert(tctx, ret == 0, "strv_split() on NULL failed");
+ torture_assert_int_equal(tctx,
+ strv_count(strv),
+ 0,
+ "strv_split() on \"\" failed");
+ TALLOC_FREE(strv);
+
+ /* String containing only separators has 0 entries */
+ ret = strv_split(tctx, &strv, "abcabcabc", "cba ");
+ torture_assert(tctx, ret == 0, "strv_split() on NULL failed");
+ torture_assert_int_equal(tctx,
+ strv_count(strv),
+ 0,
+ "strv_split() on seps-only failed");
+ TALLOC_FREE(strv);
+
+ return true;
+}
+
+struct test_str_split_data {
+ const char *in;
+ const char *sep;
+ const char *out[10]; /* Hardcoded maximum! */
+};
+
+static bool test_strv_split_some(struct torture_context *tctx)
+{
+ const struct test_str_split_data data[] = {
+ {
+ /* Single string */
+ .in = "foo",
+ .sep = " \t",
+ .out = { "foo" }
+ },
+ {
+ /* Single string, single leading separator */
+ .in = " foo",
+ .sep = " \t",
+ .out = { "foo" }
+ },
+ {
+ /* Single string, single trailing separator */
+ .in = " foo",
+ .sep = " \t",
+ .out = { "foo" }
+ },
+ {
+ /* Single string, lots of separators */
+ .in = " \t foo\t ",
+ .sep = " \t",
+ .out = { "foo" }
+ },
+ {
+ /* Multiple strings, many separators */
+ .in = " \t foo bar\t\tx\t samba\t ",
+ .sep = " \t",
+ .out = { "foo", "bar", "x", "samba" }
+ },
+ };
+ const char *t;
+ char *strv = NULL;
+ size_t j;
+
+ for (j = 0; j < ARRAY_SIZE(data); j++) {
+ size_t i, num;
+ int ret;
+ const struct test_str_split_data *d = &data[j];
+
+ num = 0;
+ while (num < ARRAY_SIZE(d->out) && d->out[num] != NULL) {
+ num++;
+ }
+ ret = strv_split(tctx, &strv, d->in, d->sep);
+ torture_assert(tctx, ret == 0, "strv_split() on NULL failed");
+ torture_assert_int_equal(tctx,
+ strv_count(strv),
+ num,
+ "strv_split() failed");
+ t = NULL;
+ for (i = 0; i < num; i++) {
+ t = strv_next(strv, t);
+ torture_assert(tctx,
+ strcmp(t, d->out[i]) == 0,
+ "strv_split() failed");
+ }
+ TALLOC_FREE(strv);
+ }
+ return true;
+}
+
+struct torture_suite *torture_local_util_strv_util(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite =
+ torture_suite_create(mem_ctx, "strv_util");
+
+ torture_suite_add_simple_test(suite,
+ "strv_split_none",
+ test_strv_split_none);
+ torture_suite_add_simple_test(suite,
+ "strv_split_some",
+ test_strv_split_some);
+ return suite;
+}
diff --git a/lib/util/tests/test_bytearray.c b/lib/util/tests/test_bytearray.c
new file mode 100644
index 0000000..fcf63d8
--- /dev/null
+++ b/lib/util/tests/test_bytearray.c
@@ -0,0 +1,435 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/bytearray.h"
+
+static void torture_pull_le_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ result = PULL_LE_U8(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ result = PULL_LE_U8(data, 0);
+ assert_int_equal(result, 42);
+
+
+ data[0] = 0xf;
+ result = PULL_LE_U8(data, 0);
+ assert_int_equal(result, 0xf);
+
+ data[0] = 0xff;
+ result = PULL_LE_U8(data, 0);
+ assert_int_equal(result, 0xff);
+
+ data[1] = 0x2a;
+ result = PULL_LE_U8(data, 1);
+ assert_int_equal(result, 42);
+}
+
+static void torture_pull_le_u16(void **state)
+{
+ uint8_t data[2] = {0, 0};
+ uint16_t result;
+
+ (void)state;
+
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ data[1] = 0x00;
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0xffff);
+}
+
+static void torture_pull_le_u32(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint32_t result;
+
+ (void)state;
+
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0xff;
+ data[3] = 0x00;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xff0000);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0xff;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ data[2] = 0xff;
+ data[3] = 0xff;
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xffffffff);
+}
+
+static void torture_push_le_u8(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {42, 42, 42, 42};
+
+ (void)state;
+
+ PUSH_LE_U8(data, 0, 42);
+ PUSH_LE_U8(data, 1, 42);
+ PUSH_LE_U8(data, 2, 42);
+ PUSH_LE_U8(data, 3, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+}
+
+static void torture_push_le_u16(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {0xa6, 0x7f, 0x2a, 0x00};
+ uint16_t result;
+
+ (void)state;
+
+ PUSH_LE_U16(data, 0, 32678);
+ PUSH_LE_U16(data, 2, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = PULL_LE_U16(data, 2);
+ assert_int_equal(result, 42);
+
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 32678);
+}
+
+static void torture_push_le_u32(void **state)
+{
+ uint8_t data[8] = {0};
+ uint8_t data2[8] = {0xa6, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00};
+ uint32_t result;
+
+ (void)state;
+
+ PUSH_LE_U32(data, 0, 32678);
+ PUSH_LE_U32(data, 4, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = PULL_LE_U32(data, 4);
+ assert_int_equal(result, 42);
+
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 32678);
+
+ PUSH_LE_U32(data, 0, 0xfffefffe);
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+static void torture_push_le_u64(void **state)
+{
+ uint8_t data[16] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ PUSH_LE_U64(data, 0, 32678);
+
+ result = PULL_LE_U64(data, 0);
+ assert_int_equal(result, 32678);
+
+ PUSH_LE_U64(data, 0, 0xfffefffefffefffeUL);
+
+ result = PULL_LE_U64(data, 0);
+ assert_int_equal(result, 0xfffefffefffefffeUL);
+}
+
+/****************** BIG ENDIAN ********************/
+
+static void torture_pull_be_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ result = PULL_BE_U8(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ result = PULL_BE_U8(data, 0);
+ assert_int_equal(result, 42);
+
+
+ data[0] = 0xf;
+ result = PULL_BE_U8(data, 0);
+ assert_int_equal(result, 0xf);
+
+ data[0] = 0xff;
+ result = PULL_BE_U8(data, 0);
+ assert_int_equal(result, 0xff);
+
+ data[1] = 0x2a;
+ result = PULL_BE_U8(data, 1);
+ assert_int_equal(result, 42);
+}
+
+static void torture_pull_be_u16(void **state)
+{
+ uint8_t data[2] = {0, 0};
+ uint16_t result;
+
+ (void)state;
+
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x00;
+ data[1] = 0x2a;
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0xffff);
+}
+
+static void torture_pull_be_u32(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint32_t result;
+
+ (void)state;
+
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x2a;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0xff;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0xff;
+ data[3] = 0x00;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xff0000);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ data[2] = 0xff;
+ data[3] = 0xff;
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xffffffff);
+}
+
+static void torture_push_be_u8(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {42, 42, 42, 42};
+
+ (void)state;
+
+ PUSH_BE_U8(data, 0, 42);
+ PUSH_BE_U8(data, 1, 42);
+ PUSH_BE_U8(data, 2, 42);
+ PUSH_BE_U8(data, 3, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+}
+
+static void torture_push_be_u16(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {0x7f, 0xa6, 0x00, 0x2a};
+ uint16_t result;
+
+ (void)state;
+
+ PUSH_BE_U16(data, 0, 32678);
+ PUSH_BE_U16(data, 2, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = PULL_BE_U16(data, 2);
+ assert_int_equal(result, 42);
+
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 32678);
+}
+
+static void torture_push_be_u32(void **state)
+{
+ uint8_t data[8] = {0};
+ uint8_t data2[8] = {0x00, 0x00, 0x7f, 0xa6, 0x00, 0x00, 0x00, 0x2a};
+ uint32_t result;
+
+ (void)state;
+
+ PUSH_BE_U32(data, 0, 32678);
+ PUSH_BE_U32(data, 4, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = PULL_BE_U32(data, 4);
+ assert_int_equal(result, 42);
+
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 32678);
+
+ PUSH_BE_U32(data, 0, 0xfffefffe);
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+static void torture_push_be_u64(void **state)
+{
+ uint8_t data[16] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ PUSH_BE_U64(data, 0, 32678);
+
+ result = PULL_BE_U64(data, 0);
+ assert_int_equal(result, 32678);
+
+ PUSH_LE_U64(data, 8, 0xfffefffe);
+
+ result = PULL_LE_U64(data, 8);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_pull_le_u8),
+ cmocka_unit_test(torture_pull_le_u16),
+ cmocka_unit_test(torture_pull_le_u32),
+
+ cmocka_unit_test(torture_push_le_u8),
+ cmocka_unit_test(torture_push_le_u16),
+ cmocka_unit_test(torture_push_le_u32),
+ cmocka_unit_test(torture_push_le_u64),
+
+ /* BIG ENDIAN */
+ cmocka_unit_test(torture_pull_be_u8),
+ cmocka_unit_test(torture_pull_be_u16),
+ cmocka_unit_test(torture_pull_be_u32),
+
+ cmocka_unit_test(torture_push_be_u8),
+ cmocka_unit_test(torture_push_be_u16),
+ cmocka_unit_test(torture_push_be_u32),
+ cmocka_unit_test(torture_push_be_u64),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/lib/util/tests/test_byteorder.c b/lib/util/tests/test_byteorder.c
new file mode 100644
index 0000000..9faa038
--- /dev/null
+++ b/lib/util/tests/test_byteorder.c
@@ -0,0 +1,435 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/byteorder.h"
+
+static void torture_pull_le_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 42);
+
+
+ data[0] = 0xf;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0xf);
+
+ data[0] = 0xff;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0xff);
+
+ data[1] = 0x2a;
+ result = CVAL(data, 1);
+ assert_int_equal(result, 42);
+}
+
+static void torture_pull_le_u16(void **state)
+{
+ uint8_t data[2] = {0, 0};
+ uint16_t result;
+
+ (void)state;
+
+ result = SVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ data[1] = 0x00;
+ result = SVAL(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ result = SVAL(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ result = SVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ result = SVAL(data, 0);
+ assert_int_equal(result, 0xffff);
+}
+
+static void torture_pull_le_u32(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint32_t result;
+
+ (void)state;
+
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0xff;
+ data[3] = 0x00;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xff0000);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0xff;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ data[2] = 0xff;
+ data[3] = 0xff;
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xffffffff);
+}
+
+static void torture_push_le_u8(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {42, 42, 42, 42};
+
+ (void)state;
+
+ SCVAL(data, 0, 42);
+ SCVAL(data, 1, 42);
+ SCVAL(data, 2, 42);
+ SCVAL(data, 3, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+}
+
+static void torture_push_le_u16(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {0xa6, 0x7f, 0x2a, 0x00};
+ uint16_t result;
+
+ (void)state;
+
+ SSVALX(data, 0, 32678);
+ SSVALX(data, 2, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = SVAL(data, 2);
+ assert_int_equal(result, 42);
+
+ result = SVAL(data, 0);
+ assert_int_equal(result, 32678);
+}
+
+static void torture_push_le_u32(void **state)
+{
+ uint8_t data[8] = {0};
+ uint8_t data2[8] = {0xa6, 0x7f, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00};
+ uint32_t result;
+
+ (void)state;
+
+ SIVALX(data, 0, 32678);
+ SIVALX(data, 4, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = IVAL(data, 4);
+ assert_int_equal(result, 42);
+
+ result = IVAL(data, 0);
+ assert_int_equal(result, 32678);
+
+ SIVALX(data, 0, 0xfffefffe);
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+static void torture_push_le_u64(void **state)
+{
+ uint8_t data[16] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ SBVAL(data, 0, 32678);
+
+ result = BVAL(data, 0);
+ assert_int_equal(result, 32678);
+
+ SBVAL(data, 0, 0xfffefffefffefffeUL);
+
+ result = BVAL(data, 0);
+ assert_int_equal(result, 0xfffefffefffefffeUL);
+}
+
+/****************** BIG ENDIAN ********************/
+
+static void torture_pull_be_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x2a;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 42);
+
+
+ data[0] = 0xf;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0xf);
+
+ data[0] = 0xff;
+ result = CVAL(data, 0);
+ assert_int_equal(result, 0xff);
+
+ data[1] = 0x2a;
+ result = CVAL(data, 1);
+ assert_int_equal(result, 42);
+}
+
+static void torture_pull_be_u16(void **state)
+{
+ uint8_t data[2] = {0, 0};
+ uint16_t result;
+
+ (void)state;
+
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x00;
+ data[1] = 0x2a;
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 0xffff);
+}
+
+static void torture_pull_be_u32(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint32_t result;
+
+ (void)state;
+
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x2a;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 42);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0xff;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ data[0] = 0x00;
+ data[1] = 0x00;
+ data[2] = 0xff;
+ data[3] = 0x00;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ data[0] = 0x00;
+ data[1] = 0xff;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xff0000);
+
+ data[0] = 0xff;
+ data[1] = 0x00;
+ data[2] = 0x00;
+ data[3] = 0x00;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ data[0] = 0xff;
+ data[1] = 0xff;
+ data[2] = 0xff;
+ data[3] = 0xff;
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xffffffff);
+}
+
+static void torture_push_be_u8(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {42, 42, 42, 42};
+
+ (void)state;
+
+ SCVAL(data, 0, 42);
+ SCVAL(data, 1, 42);
+ SCVAL(data, 2, 42);
+ SCVAL(data, 3, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+}
+
+static void torture_push_be_u16(void **state)
+{
+ uint8_t data[4] = {0, 0, 0, 0};
+ uint8_t data2[4] = {0x7f, 0xa6, 0x00, 0x2a};
+ uint16_t result;
+
+ (void)state;
+
+ RSSVALS(data, 0, 32678);
+ RSSVALS(data, 2, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = RSVAL(data, 2);
+ assert_int_equal(result, 42);
+
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 32678);
+}
+
+static void torture_push_be_u32(void **state)
+{
+ uint8_t data[8] = {0};
+ uint8_t data2[8] = {0x00, 0x00, 0x7f, 0xa6, 0x00, 0x00, 0x00, 0x2a};
+ uint32_t result;
+
+ (void)state;
+
+ RSIVALS(data, 0, 32678);
+ RSIVALS(data, 4, 42);
+ assert_memory_equal(data, data2, sizeof(data));
+
+ result = RIVAL(data, 4);
+ assert_int_equal(result, 42);
+
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 32678);
+
+ RSIVALS(data, 0, 0xfffefffe);
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+static void torture_push_be_u64(void **state)
+{
+ uint8_t data[16] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ RSBVALS(data, 0, 32678);
+
+ result = RBVAL(data, 0);
+ assert_int_equal(result, 32678);
+
+ SBVAL(data, 8, 0xfffefffe);
+
+ result = BVAL(data, 8);
+ assert_int_equal(result, 0xfffefffe);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_pull_le_u8),
+ cmocka_unit_test(torture_pull_le_u16),
+ cmocka_unit_test(torture_pull_le_u32),
+
+ cmocka_unit_test(torture_push_le_u8),
+ cmocka_unit_test(torture_push_le_u16),
+ cmocka_unit_test(torture_push_le_u32),
+ cmocka_unit_test(torture_push_le_u64),
+
+ /* BIG ENDIAN */
+ cmocka_unit_test(torture_pull_be_u8),
+ cmocka_unit_test(torture_pull_be_u16),
+ cmocka_unit_test(torture_pull_be_u32),
+
+ cmocka_unit_test(torture_push_be_u8),
+ cmocka_unit_test(torture_push_be_u16),
+ cmocka_unit_test(torture_push_be_u32),
+ cmocka_unit_test(torture_push_be_u64),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/lib/util/tests/test_byteorder_verify.c b/lib/util/tests/test_byteorder_verify.c
new file mode 100644
index 0000000..a18ddad
--- /dev/null
+++ b/lib/util/tests/test_byteorder_verify.c
@@ -0,0 +1,273 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/bytearray.h"
+#include "lib/util/byteorder.h"
+
+static void torture_le_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ /* Test CVAL and SCVAL */
+ PUSH_LE_U8(data, 0, 23);
+ PUSH_LE_U8(data, 1, 42);
+
+ result = CVAL(data, 0);
+ assert_int_equal(result, 23);
+
+ result = CVAL(data, 1);
+ assert_int_equal(result, 42);
+
+ /* Test CVAL_NC and PVAL */
+ PUSH_LE_U8(data, 0, 23);
+ PUSH_LE_U8(data, 1, 42);
+
+ result = CVAL_NC(data, 0);
+ assert_int_equal(result, 23);
+
+ result = PVAL(data, 1);
+ assert_int_equal(result, 42);
+
+ /* Test SCVAL */
+ SCVAL(data, 0, 42);
+ SCVAL(data, 1, 23);
+
+ result = PULL_LE_U8(data, 0);
+ assert_int_equal(result, 42);
+
+ result = PULL_LE_U8(data, 1);
+ assert_int_equal(result, 23);
+}
+
+static void torture_le_u16(void **state)
+{
+ uint8_t data[2] = {0};
+ uint16_t result;
+
+ (void)state;
+
+ /* Test SVAL */
+ PUSH_LE_U16(data, 0, 0xff00);
+ result = SVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ /* Test SSVAL */
+ SSVAL(data, 0, 0x00ff);
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ /* Test SSVALX */
+ SSVALX(data, 0, 0x00fa);
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0x00fa);
+
+ /* Test SSVALS */
+ SSVALS(data, 0, 0x00fb);
+ result = PULL_LE_U16(data, 0);
+ assert_int_equal(result, 0x00fb);
+}
+
+static void torture_le_u32(void **state)
+{
+ uint8_t data[4] = {0};
+ uint32_t result;
+
+ (void)state;
+
+ /* Test IVAL */
+ PUSH_LE_U32(data, 0, 0xff000000);
+ result = IVAL(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ /* Test SIVAL */
+ SIVAL(data, 0, 0xffaabbcc);
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xffaabbcc);
+
+ /* Test SIVALX */
+ SIVALX(data, 0, 0xffbbccdd);
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xffbbccdd);
+
+ /* Test SIVALS */
+ SIVALS(data, 0, 0xffccddee);
+ result = PULL_LE_U32(data, 0);
+ assert_int_equal(result, 0xffccddee);
+}
+
+static void torture_le_u64(void **state)
+{
+ uint8_t data[8] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ PUSH_LE_U64(data, 0, 0xfffefffefffefffeUL);
+ result = BVAL(data, 0);
+ assert_int_equal(result, 0xfffefffefffefffeUL);
+
+ SBVAL(data, 0, 0xfffafffafffafffaUL);
+ result = PULL_LE_U64(data, 0);
+ assert_int_equal(result, 0xfffafffafffafffaUL);
+}
+
+static void torture_be_u8(void **state)
+{
+ uint8_t data[2] = {0};
+ uint8_t result;
+
+ (void)state;
+
+ PUSH_BE_U8(data, 0, 23);
+ PUSH_BE_U8(data, 1, 42);
+
+ result = CVAL(data, 0);
+ assert_int_equal(result, 23);
+
+ result = CVAL(data, 1);
+ assert_int_equal(result, 42);
+
+ SCVAL(data, 0, 42);
+ SCVAL(data, 1, 23);
+
+ result = PULL_BE_U8(data, 0);
+ assert_int_equal(result, 42);
+
+ result = PULL_BE_U8(data, 1);
+ assert_int_equal(result, 23);
+}
+
+static void torture_be_u16(void **state)
+{
+ uint8_t data[2] = {0};
+ uint16_t result;
+
+ (void)state;
+
+ /* Test RSVAL */
+ PUSH_BE_U16(data, 0, 0xff00);
+ result = RSVAL(data, 0);
+ assert_int_equal(result, 0xff00);
+
+ /* Test RSVALS */
+ PUSH_BE_U16(data, 0, 0xffaa);
+ result = RSVALS(data, 0);
+ assert_int_equal(result, 0xffaa);
+
+ /* Test RSSVAL */
+ RSSVAL(data, 0, 0x00ff);
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0x00ff);
+
+ /* Test RSSVALS */
+ RSSVALS(data, 0, 0x00fa);
+ result = PULL_BE_U16(data, 0);
+ assert_int_equal(result, 0x00fa);
+}
+
+static void torture_be_u32(void **state)
+{
+ uint8_t data[4] = {0};
+ uint32_t result;
+
+ (void)state;
+
+ /* Test RIVAL */
+ PUSH_BE_U32(data, 0, 0xff000000);
+ result = RIVAL(data, 0);
+ assert_int_equal(result, 0xff000000);
+
+ /* Test RIVALS */
+ PUSH_BE_U32(data, 0, 0xff0000aa);
+ result = RIVALS(data, 0);
+ assert_int_equal(result, 0xff0000aa);
+
+ /* Test RSIVAL */
+ RSIVAL(data, 0, 0xffeeddcc);
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xffeeddcc);
+
+ /* Test RSIVALS */
+ RSIVALS(data, 0, 0xffaaddcc);
+ result = PULL_BE_U32(data, 0);
+ assert_int_equal(result, 0xffaaddcc);
+}
+
+static void torture_be_u64(void **state)
+{
+ uint8_t data[8] = {0};
+ uint64_t result;
+
+ (void)state;
+
+ /* Test RBVAL */
+ PUSH_BE_U64(data, 0, 0xfffefffefffefffeUL);
+ result = RBVAL(data, 0);
+ assert_int_equal(result, 0xfffefffefffefffeUL);
+
+ /* Test RBVALS */
+ PUSH_BE_U64(data, 0, 0xfffafffafffafffaUL);
+ result = RBVALS(data, 0);
+ assert_int_equal(result, 0xfffafffafffafffaUL);
+
+ /* Test RSBVAL */
+ RSBVAL(data, 0, 0xfffbfffbfffbfffbUL);
+ result = PULL_BE_U64(data, 0);
+ assert_int_equal(result, 0xfffbfffbfffbfffbUL);
+
+ /* Test RSBVALS */
+ RSBVALS(data, 0, 0xfffcfffcfffcfffcUL);
+ result = PULL_BE_U64(data, 0);
+ assert_int_equal(result, 0xfffcfffcfffcfffcUL);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_le_u8),
+ cmocka_unit_test(torture_le_u16),
+ cmocka_unit_test(torture_le_u32),
+ cmocka_unit_test(torture_le_u64),
+
+ cmocka_unit_test(torture_be_u8),
+ cmocka_unit_test(torture_be_u16),
+ cmocka_unit_test(torture_be_u32),
+ cmocka_unit_test(torture_be_u64),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/lib/util/tests/test_logging.c b/lib/util/tests/test_logging.c
new file mode 100644
index 0000000..9b13b05
--- /dev/null
+++ b/lib/util/tests/test_logging.c
@@ -0,0 +1,146 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A test server that only does logging.
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Douglas Bagnall 2022
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+
+#ifdef USING_CMDLINE_S3
+#include "lib/util/debug_s3.h"
+#endif
+
+#define BINARY_NAME "test_s4_logging"
+
+static int log_level = 1;
+
+
+#include "lib/util/debug-classes/debug-classname-table.c"
+
+static int log_all_classes(int level)
+{
+ size_t i;
+ const char *name = NULL;
+ for (i = 0; i < ARRAY_SIZE(default_classname_table); i++) {
+ name = default_classname_table[i];
+ DEBUGC(i, level,
+ ("logging for '%s' [%zu], at level %d\n",
+ name, i, level));
+
+ /*
+ * That's it for the tests *here*. The invoker of this
+ * process will have set up an smb.conf that directs the
+ * output in particular ways, and will be looking to see that
+ * happens correctly.
+ */
+ }
+ return 0;
+}
+
+
+static int init_daemon(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char *argv[],
+ const char **error)
+{
+ poptContext pc;
+ int opt;
+ bool ok;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "level",
+ .shortName = 'L',
+ .argInfo = POPT_ARG_INT,
+ .arg = &log_level,
+ .descrip = "log at this level",
+ .argDescrip = "LEVEL",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_DAEMON
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ setproctitle(BINARY_NAME);
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ *error = "Failed to init cmdline parser!\n";
+ return EINVAL;
+ }
+
+ pc = samba_popt_get_context(BINARY_NAME,
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ *error = "Failed to setup popt context!\n";
+ return ENOTRECOVERABLE;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+#ifdef USING_CMDLINE_S3
+ reopen_logs();
+#endif
+ return 0;
+}
+
+
+int main(int argc, const char *argv[])
+{
+ int rc;
+ const char *error = NULL;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ setproctitle_init(argc, discard_const(argv), environ);
+
+ rc = init_daemon(mem_ctx, argc, argv, &error);
+ if (rc != 0) {
+ fprintf(stderr, "error [%d]: %s\n", rc, error);
+ exit_daemon(error, rc);
+ }
+
+ rc = log_all_classes(log_level);
+ if (rc != 0) {
+ fprintf(stderr, "error in log_all_classes [%d]\n", rc);
+ exit_daemon("logging error", rc);
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return rc;
+}
diff --git a/lib/util/tests/test_memcache.c b/lib/util/tests/test_memcache.c
new file mode 100644
index 0000000..8a39978
--- /dev/null
+++ b/lib/util/tests/test_memcache.c
@@ -0,0 +1,161 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2021 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/memcache.h"
+
+static int setup_talloc_context(void **state)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ *state = frame;
+ return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+ TALLOC_CTX *frame = *state;
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+static void torture_memcache_init(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct memcache *cache = NULL;
+
+ cache = memcache_init(mem_ctx, 0);
+ assert_non_null(cache);
+
+ TALLOC_FREE(cache);
+
+ cache = memcache_init(mem_ctx, 10);
+ assert_non_null(cache);
+
+ TALLOC_FREE(cache);
+}
+
+static void torture_memcache_add_lookup_delete(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct memcache *cache = NULL;
+ DATA_BLOB key1, key2;
+ char *path1 = NULL, *path2 = NULL;
+
+ cache = memcache_init(mem_ctx, 0);
+ assert_non_null(cache);
+
+ key1 = data_blob_const("key1", 4);
+ path1 = talloc_strdup(mem_ctx, "/tmp/one");
+ assert_non_null(path1);
+
+ key2 = data_blob_const("key2", 4);
+ path2 = talloc_strdup(mem_ctx, "/tmp/two");
+ assert_non_null(path1);
+
+ memcache_add_talloc(cache, GETWD_CACHE, key1, &path1);
+ assert_null(path1);
+
+ memcache_add_talloc(cache, GETWD_CACHE, key2, &path2);
+ assert_null(path2);
+
+ path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+ assert_non_null(path1);
+ assert_string_equal(path1, "/tmp/one");
+
+ path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+ assert_non_null(path2);
+ assert_string_equal(path2, "/tmp/two");
+
+ memcache_delete(cache, GETWD_CACHE, key1);
+ path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+ assert_null(path1);
+
+ memcache_flush(cache, GETWD_CACHE);
+ path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+ assert_null(path2);
+
+ TALLOC_FREE(path1);
+ TALLOC_FREE(path2);
+ TALLOC_FREE(cache);
+}
+
+static void torture_memcache_add_oversize(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct memcache *cache = NULL;
+ DATA_BLOB key1, key2;
+ char *path1 = NULL, *path2 = NULL;
+
+ cache = memcache_init(mem_ctx, 10);
+ assert_non_null(cache);
+
+ key1 = data_blob_const("key1", 4);
+ path1 = talloc_strdup(mem_ctx, "/tmp/one");
+ assert_non_null(path1);
+
+ key2 = data_blob_const("key2", 4);
+ path2 = talloc_strdup(mem_ctx, "/tmp/two");
+ assert_non_null(path1);
+
+ memcache_add_talloc(cache, GETWD_CACHE, key1, &path1);
+ assert_null(path1);
+
+ memcache_add_talloc(cache, GETWD_CACHE, key2, &path2);
+ assert_null(path2);
+
+ path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+ assert_null(path1);
+
+ path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+ assert_non_null(path2);
+ assert_string_equal(path2, "/tmp/two");
+
+ TALLOC_FREE(path1);
+ TALLOC_FREE(path2);
+ TALLOC_FREE(cache);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_memcache_init),
+ cmocka_unit_test(torture_memcache_add_lookup_delete),
+ cmocka_unit_test(torture_memcache_add_oversize),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests,
+ setup_talloc_context,
+ teardown_talloc_context);
+
+ return rc;
+}
diff --git a/lib/util/tests/test_ms_fnmatch.c b/lib/util/tests/test_ms_fnmatch.c
new file mode 100644
index 0000000..d11c7be
--- /dev/null
+++ b/lib/util/tests/test_ms_fnmatch.c
@@ -0,0 +1,114 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018 David Disseldorp <ddiss@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <errno.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/samba_util.h"
+#include "libcli/smb/smb_constants.h"
+
+static void test_ms_fn_match_protocol_no_wildcard(void **state)
+{
+ int cmp;
+
+ /* no wildcards in pattern, a simple strcasecmp_m */
+ cmp = ms_fnmatch_protocol("pattern", "string", PROTOCOL_COREPLUS,
+ true); /* case sensitive */
+ assert_int_equal(cmp, -3);
+}
+
+static void test_ms_fn_match_protocol_pattern_upgraded(void **state)
+{
+ int cmp;
+
+ /* protocol < PROTOCOL_NT1 pattern is "upgraded" */
+ cmp = ms_fnmatch_protocol("??????", "string", PROTOCOL_COREPLUS,
+ false);
+ assert_int_equal(cmp, 0);
+}
+
+static void test_ms_fn_match_protocol_match_zero_or_more(void **state)
+{
+ int cmp;
+
+ /* '*' matches zero or more characters. handled via recursive calls */
+ cmp = ms_fnmatch_protocol("********", "string", PROTOCOL_COREPLUS,
+ true);
+ assert_int_equal(cmp, 0);
+}
+
+static void test_ms_fn_match_protocol_mapped_char(void **state)
+{
+ int cmp;
+
+ /* '?' is mapped to '>', which matches any char or a '\0' */
+ cmp = ms_fnmatch_protocol("???????", "string", PROTOCOL_COREPLUS,
+ false);
+ assert_int_equal(cmp, 0);
+}
+
+static void test_ms_fn_match_protocol_nt1_any_char(void **state)
+{
+ int cmp;
+
+ /* PROTOCOL_NT1 '?' matches any char, '\0' is not included */
+ cmp = ms_fnmatch_protocol("???????", "string", PROTOCOL_NT1,
+ false);
+ assert_int_equal(cmp, -1);
+}
+
+static void test_ms_fn_match_protocol_nt1_case_sensitive(void **state)
+{
+ int cmp;
+
+ cmp = ms_fnmatch_protocol("StRinG", "string", PROTOCOL_NT1,
+ true); /* case sensitive */
+ assert_int_equal(cmp, 0);
+
+ cmp = ms_fnmatch_protocol("StRin?", "string", PROTOCOL_NT1,
+ true); /* case sensitive */
+ assert_int_equal(cmp, -1);
+
+ cmp = ms_fnmatch_protocol("StRin?", "string", PROTOCOL_NT1,
+ false);
+ assert_int_equal(cmp, 0);
+ cmp = ms_fnmatch_protocol("strin?", "string", PROTOCOL_NT1,
+ true); /* case sensitive */
+ assert_int_equal(cmp, 0);
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_ms_fn_match_protocol_no_wildcard),
+ cmocka_unit_test(test_ms_fn_match_protocol_pattern_upgraded),
+ cmocka_unit_test(test_ms_fn_match_protocol_match_zero_or_more),
+ cmocka_unit_test(test_ms_fn_match_protocol_mapped_char),
+ cmocka_unit_test(test_ms_fn_match_protocol_nt1_any_char),
+ cmocka_unit_test(test_ms_fn_match_protocol_nt1_case_sensitive),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/util/tests/test_sys_rw.c b/lib/util/tests/test_sys_rw.c
new file mode 100644
index 0000000..22688fb
--- /dev/null
+++ b/lib/util/tests/test_sys_rw.c
@@ -0,0 +1,178 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Unit test for sys_rw.c
+ *
+ * Copyright (C) Ralph Böhme 2021
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "system/dir.h"
+
+#include "lib/util/sys_rw.c"
+
+static void test_sys_io_ranges_overlap(void **state)
+{
+ bool overlap;
+
+ /*
+ * sys_io_ranges_overlap() args are:
+ *
+ * src size, src offset, dst size, dst offset
+ */
+
+ /* src and dst size 0 => no overlap */
+ overlap = sys_io_ranges_overlap(0, 0, 0, 0);
+ assert_false(overlap);
+
+ /* dst size 0 => no overlap */
+ overlap = sys_io_ranges_overlap(1, 0, 0, 0);
+ assert_false(overlap);
+
+ /* src size 0 => no overlap */
+ overlap = sys_io_ranges_overlap(0, 0, 1, 0);
+ assert_false(overlap);
+
+ /* same range => overlap */
+ overlap = sys_io_ranges_overlap(1, 0, 1, 0);
+ assert_true(overlap);
+
+ /*
+ * |.|
+ * |.|
+ * src before dst => no overlap
+ */
+ overlap = sys_io_ranges_overlap(1, 0, 1, 1);
+ assert_false(overlap);
+
+ /*
+ * |..|
+ * |..|
+ * src into dst => overlap
+ */
+ overlap = sys_io_ranges_overlap(2, 0, 2, 1);
+ assert_true(overlap);
+
+ /*
+ * |....|
+ * |..|
+ * src encompasses dst => overlap
+ */
+ overlap = sys_io_ranges_overlap(4, 0, 1, 2);
+ assert_true(overlap);
+
+
+ /*
+ * |..|
+ * |..|
+ * dst into src => overlap
+ */
+ overlap = sys_io_ranges_overlap(2, 1, 2, 0);
+ assert_true(overlap);
+
+ /*
+ * |..|
+ * |....|
+ * dst encompasses src => overlap
+ */
+ overlap = sys_io_ranges_overlap(2, 1, 4, 0);
+ assert_true(overlap);
+}
+
+static void test_sys_block_align(void **state)
+{
+ int asize;
+
+ asize = sys_block_align(0, 2);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align(1, 2);
+ assert_int_equal(asize, 2);
+ asize = sys_block_align(2, 2);
+ assert_int_equal(asize, 2);
+ asize = sys_block_align(3, 2);
+ assert_int_equal(asize, 4);
+
+ asize = sys_block_align(0, 4);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align(1, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align(3, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align(4, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align(5, 4);
+ assert_int_equal(asize, 8);
+ asize = sys_block_align(7, 4);
+ assert_int_equal(asize, 8);
+ asize = sys_block_align(8, 4);
+ assert_int_equal(asize, 8);
+ asize = sys_block_align(9, 4);
+ assert_int_equal(asize, 12);
+}
+
+static void test_sys_block_align_truncate(void **state)
+{
+ int asize;
+
+ asize = sys_block_align_truncate(0, 2);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align_truncate(1, 2);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align_truncate(2, 2);
+ assert_int_equal(asize, 2);
+ asize = sys_block_align_truncate(3, 2);
+ assert_int_equal(asize, 2);
+ asize = sys_block_align_truncate(4, 2);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align_truncate(5, 2);
+ assert_int_equal(asize, 4);
+
+ asize = sys_block_align_truncate(0, 4);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align_truncate(1, 4);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align_truncate(3, 4);
+ assert_int_equal(asize, 0);
+ asize = sys_block_align_truncate(4, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align_truncate(5, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align_truncate(7, 4);
+ assert_int_equal(asize, 4);
+ asize = sys_block_align_truncate(8, 4);
+ assert_int_equal(asize, 8);
+ asize = sys_block_align_truncate(9, 4);
+ assert_int_equal(asize, 8);
+}
+
+int main(int argc, char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_sys_io_ranges_overlap),
+ cmocka_unit_test(test_sys_block_align),
+ cmocka_unit_test(test_sys_block_align_truncate),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/util/tests/test_talloc_keep_secret.c b/lib/util/tests/test_talloc_keep_secret.c
new file mode 100644
index 0000000..1462dab
--- /dev/null
+++ b/lib/util/tests/test_talloc_keep_secret.c
@@ -0,0 +1,94 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <string.h>
+#include <talloc.h>
+#include "lib/util/talloc_keep_secret.h"
+
+int rep_memset_s(void *dest, size_t destsz, int ch, size_t count);
+
+int rep_memset_s(void *dest, size_t destsz, int ch, size_t count)
+{
+ check_expected_ptr(dest);
+ check_expected(destsz);
+ check_expected(ch);
+ check_expected(count);
+
+ return 0;
+}
+
+static void test_talloc_keep_secret(void ** state)
+{
+ TALLOC_CTX *pool = NULL;
+ char *ptr1 = NULL;
+ char *ptr2 = NULL;
+ const char *ptr1_talloc_name = NULL;
+ size_t ptr1_size;
+ size_t i;
+
+ pool = talloc_pool(NULL, 256);
+ assert_non_null(pool);
+
+ ptr1 = talloc_strdup(pool, "secret");
+ assert_non_null(ptr1);
+ assert_string_equal(ptr1, "secret");
+
+ talloc_keep_secret(ptr1);
+
+ ptr1_talloc_name = talloc_get_name(ptr1);
+ assert_string_equal(ptr1_talloc_name, "ptr1");
+
+ ptr1_size = talloc_get_size(ptr1);
+ assert_int_equal(ptr1_size, strlen(ptr1) + 1);
+
+ expect_string(rep_memset_s, dest, "secret");
+ expect_value(rep_memset_s, destsz, strlen(ptr1) + 1);
+ expect_value(rep_memset_s, ch, (int)'\0');
+ expect_value(rep_memset_s, count, strlen(ptr1) + 1);
+
+ talloc_free(ptr1);
+
+ ptr2 = talloc_size(pool, ptr1_size);
+ assert_ptr_equal(ptr1, ptr2);
+
+ for (i = 1; i < ptr1_size; i++) {
+ assert_int_not_equal(ptr2[0], ptr2[i]);
+ }
+
+ talloc_free(pool);
+}
+
+static void test_talloc_keep_secret_validate_memset(void **state)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ char *password = NULL;
+
+ mem_ctx = talloc_new(NULL);
+ assert_non_null(mem_ctx);
+
+ password = talloc_strdup(mem_ctx, "secret");
+ assert_non_null(password);
+ talloc_keep_secret(password);
+
+ expect_string(rep_memset_s, dest, "secret");
+ expect_value(rep_memset_s, destsz, strlen(password) + 1);
+ expect_value(rep_memset_s, ch, (int)'\0');
+ expect_value(rep_memset_s, count, strlen(password) + 1);
+
+ talloc_free(mem_ctx);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_talloc_keep_secret),
+ cmocka_unit_test(test_talloc_keep_secret_validate_memset),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/util/tests/test_util.c b/lib/util/tests/test_util.c
new file mode 100644
index 0000000..62f10ed
--- /dev/null
+++ b/lib/util/tests/test_util.c
@@ -0,0 +1,344 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Unit test for util.c
+ *
+ * Copyright (C) Christof Schmitt 2020
+ * Copyright (C) Andreas Schneider 2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "system/dir.h"
+
+#include "lib/util/util.c"
+
+struct test_paths {
+ char testdir[PATH_MAX];
+ char none[PATH_MAX];
+ char dir[PATH_MAX];
+ char dir_recursive[PATH_MAX];
+ mode_t dir_mode;
+ char file[PATH_MAX];
+ mode_t file_mode;
+ char symlink_none[PATH_MAX];
+ char symlink_dir[PATH_MAX];
+ char symlink_file[PATH_MAX];
+};
+
+static int group_setup(void **state)
+{
+ struct test_paths *paths = NULL;
+ char *testdir = NULL;
+ int ret, fd;
+
+ umask(0);
+
+ paths = malloc(sizeof(struct test_paths));
+ assert_non_null(paths);
+
+ strlcpy(paths->testdir, tmpdir(), sizeof(paths->testdir));
+ strlcat(paths->testdir, "/test_util_XXXXXX", sizeof(paths->testdir));
+ testdir = mkdtemp(paths->testdir);
+ assert_non_null(testdir);
+
+ strlcpy(paths->none, testdir, sizeof(paths->none));
+ strlcat(paths->none, "/none", sizeof(paths->none));
+
+ strlcpy(paths->dir, testdir, sizeof(paths->dir));
+ strlcat(paths->dir, "/dir", sizeof(paths->dir));
+ paths->dir_mode = 0750;
+ ret = mkdir(paths->dir, paths->dir_mode);
+ assert_return_code(ret, errno);
+
+ strlcpy(paths->dir_recursive, testdir, sizeof(paths->dir));
+ strlcat(paths->dir_recursive, "/dir_recursive", sizeof(paths->dir));
+ paths->dir_mode = 0750;
+ ret = mkdir(paths->dir_recursive, paths->dir_mode);
+ assert_return_code(ret, errno);
+
+ strlcpy(paths->file, testdir, sizeof(paths->file));
+ strlcat(paths->file, "/file", sizeof(paths->file));
+ paths->file_mode = 0640;
+ fd = creat(paths->file, paths->file_mode);
+ assert_return_code(fd, errno);
+ ret = close(fd);
+ assert_return_code(ret, errno);
+
+ strlcpy(paths->symlink_none, testdir, sizeof(paths->symlink_none));
+ strlcat(paths->symlink_none, "/symlink_none",
+ sizeof(paths->symlink_none));
+ ret = symlink("/none", paths->symlink_none);
+ assert_return_code(ret, errno);
+
+ strlcpy(paths->symlink_dir, testdir, sizeof(paths->symlink_dir));
+ strlcat(paths->symlink_dir, "/symlink_dir", sizeof(paths->symlink_dir));
+ ret = symlink(paths->dir, paths->symlink_dir);
+ assert_return_code(ret, errno);
+
+ strlcpy(paths->symlink_file, testdir, sizeof(paths->symlink_file));
+ strlcat(paths->symlink_file, "/symlink_file",
+ sizeof(paths->symlink_file));
+ ret = symlink(paths->file, paths->symlink_file);
+ assert_return_code(ret, errno);
+
+ *state = paths;
+
+ return 0;
+}
+
+static int torture_rmdirs(const char *path)
+{
+ DIR *d;
+ struct dirent *dp;
+ struct stat sb;
+ char *fname;
+
+ if ((d = opendir(path)) != NULL) {
+ while(stat(path, &sb) == 0) {
+ /* if we can remove the directory we're done */
+ if (rmdir(path) == 0) {
+ break;
+ }
+ switch (errno) {
+ case ENOTEMPTY:
+ case EEXIST:
+ case EBADF:
+ break; /* continue */
+ default:
+ closedir(d);
+ return 0;
+ }
+
+ while ((dp = readdir(d)) != NULL) {
+ size_t len;
+ /* skip '.' and '..' */
+ if (dp->d_name[0] == '.' &&
+ (dp->d_name[1] == '\0' ||
+ (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) {
+ continue;
+ }
+
+ len = strlen(path) + strlen(dp->d_name) + 2;
+ fname = malloc(len);
+ if (fname == NULL) {
+ closedir(d);
+ return -1;
+ }
+ snprintf(fname, len, "%s/%s", path, dp->d_name);
+
+ /* stat the file */
+ if (lstat(fname, &sb) != -1) {
+ if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) {
+ if (rmdir(fname) < 0) { /* can't be deleted */
+ if (errno == EACCES) {
+ closedir(d);
+ SAFE_FREE(fname);
+ return -1;
+ }
+ torture_rmdirs(fname);
+ }
+ } else {
+ unlink(fname);
+ }
+ } /* lstat */
+ SAFE_FREE(fname);
+ } /* readdir */
+
+ rewinddir(d);
+ }
+ } else {
+ return -1;
+ }
+
+ closedir(d);
+ return 0;
+}
+
+static int group_teardown(void **state)
+{
+ struct test_paths *paths = *state;
+ int ret;
+
+ ret = unlink(paths->file);
+ assert_return_code(ret, errno);
+
+ ret = unlink(paths->symlink_none);
+ assert_return_code(ret, errno);
+
+ ret = unlink(paths->symlink_dir);
+ assert_return_code(ret, errno);
+
+ ret = unlink(paths->symlink_file);
+ assert_return_code(ret, errno);
+
+ ret = torture_rmdirs(paths->testdir);
+ assert_return_code(ret, errno);
+
+ free(paths);
+ return 0;
+}
+
+static void test_directory_create_or_exists_none(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->none, 0775);
+ assert_true(b);
+
+ ret = lstat(paths->none, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, 0775);
+ assert_true(S_ISDIR(sbuf.st_mode));
+}
+
+static int teardown_none_directory(void **state)
+{
+ struct test_paths *paths = *state;
+
+ rmdir(paths->none);
+ return 0;
+}
+
+static void test_directory_create_or_exists_dir(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->dir, 770);
+ assert_true(b);
+
+ ret = lstat(paths->dir, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, paths->dir_mode);
+ assert_true(S_ISDIR(sbuf.st_mode));
+}
+
+static void test_directory_create_or_exists_file(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->file, 770);
+ assert_false(b);
+
+ ret = lstat(paths->file, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, paths->file_mode);
+ assert_true(S_ISREG(sbuf.st_mode));
+}
+
+static void test_directory_create_or_exists_symlink_none(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->symlink_none, 770);
+ assert_false(b);
+
+ ret = lstat(paths->symlink_none, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, 0777);
+ assert_true(S_ISLNK(sbuf.st_mode));
+}
+
+static void test_directory_create_or_exists_symlink_dir(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->symlink_dir, 770);
+ assert_true(b);
+
+ ret = lstat(paths->symlink_dir, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, 0777);
+ assert_true(S_ISLNK(sbuf.st_mode));
+}
+
+static void test_directory_create_or_exists_symlink_file(void **state)
+{
+ struct test_paths *paths = *state;
+ bool b;
+ struct stat sbuf;
+ int ret;
+
+ b = directory_create_or_exist(paths->symlink_file, 770);
+ assert_false(b);
+
+ ret = lstat(paths->symlink_file, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, 0777);
+ assert_true(S_ISLNK(sbuf.st_mode));
+}
+
+static void test_directory_create_or_exists_recursive(void **state)
+{
+ struct test_paths *paths = *state;
+ char recursive_testdir[PATH_MAX] = {0};
+ struct stat sbuf = {0};
+ bool ok;
+ int ret;
+
+ ret = snprintf(recursive_testdir,
+ sizeof(recursive_testdir),
+ "%s/wurst/brot",
+ paths->dir_recursive);
+ assert_int_not_equal(ret, -1);
+
+ ok = directory_create_or_exists_recursive(recursive_testdir,
+ 0700);
+ assert_true(ok);
+
+ ret = lstat(recursive_testdir, &sbuf);
+ assert_return_code(ret, errno);
+ assert_int_equal(sbuf.st_mode & 0777, 0700);
+ assert_true(S_ISDIR(sbuf.st_mode));
+}
+
+int main(int argc, char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_teardown(test_directory_create_or_exists_none,
+ teardown_none_directory),
+ cmocka_unit_test(test_directory_create_or_exists_dir),
+ cmocka_unit_test(test_directory_create_or_exists_file),
+ cmocka_unit_test(test_directory_create_or_exists_symlink_none),
+ cmocka_unit_test(test_directory_create_or_exists_symlink_dir),
+ cmocka_unit_test(test_directory_create_or_exists_symlink_file),
+ cmocka_unit_test(test_directory_create_or_exists_recursive),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, group_setup, group_teardown);
+}
diff --git a/lib/util/tests/test_util_paths.c b/lib/util/tests/test_util_paths.c
new file mode 100644
index 0000000..4dfe11c
--- /dev/null
+++ b/lib/util/tests/test_util_paths.c
@@ -0,0 +1,127 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include <talloc.h>
+
+#include "lib/util/util_paths.c"
+
+static int setup(void **state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ assert_non_null(mem_ctx);
+ *state = mem_ctx;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+}
+
+static void test_get_user_home_dir(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct passwd *pwd = getpwuid(getuid());
+ char *user;
+
+ user = get_user_home_dir(mem_ctx);
+ assert_non_null(user);
+ assert_string_equal(user, pwd->pw_dir);
+
+ TALLOC_FREE(user);
+}
+
+static void test_path_expand_tilde(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ char h[256] = {0};
+ char *d = NULL;
+ const char *user = NULL;
+ char *home = NULL;
+
+ user = getenv("USER");
+ if (user == NULL){
+ user = getenv("LOGNAME");
+ }
+
+ /* In certain CIs there no such variables */
+ if (user == NULL) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw){
+ user = pw->pw_name;
+ }
+ }
+
+ home = getenv("HOME");
+ assert_non_null(home);
+ snprintf(h, sizeof(h), "%s/.cache", home);
+
+ d = path_expand_tilde(mem_ctx, "~/.cache");
+ assert_non_null(d);
+ assert_string_equal(d, h);
+ TALLOC_FREE(d);
+
+ snprintf(h, sizeof(h), "%s/.cache/X~", home);
+ d = path_expand_tilde(mem_ctx, "~/.cache/X~");
+ assert_string_equal(d, h);
+ TALLOC_FREE(d);
+
+ d = path_expand_tilde(mem_ctx, "/guru/meditation");
+ assert_non_null(d);
+ assert_string_equal(d, "/guru/meditation");
+ TALLOC_FREE(d);
+
+ snprintf(h, sizeof(h), "~%s/.cache", user);
+ d = path_expand_tilde(mem_ctx, h);
+ assert_non_null(d);
+
+ snprintf(h, sizeof(h), "%s/.cache", home);
+ assert_string_equal(d, h);
+ TALLOC_FREE(d);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_get_user_home_dir),
+ cmocka_unit_test(test_path_expand_tilde),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, setup, teardown);
+
+ return rc;
+}
diff --git a/lib/util/tests/tfork-drd.supp b/lib/util/tests/tfork-drd.supp
new file mode 100644
index 0000000..7d0544b
--- /dev/null
+++ b/lib/util/tests/tfork-drd.supp
@@ -0,0 +1,14 @@
+{
+ tfork_pthread_cond_init
+ drd:CondErr
+ fun:pthread_cond_init_intercept
+ fun:pthread_cond_init@*
+ fun:tfork_atfork_child
+ fun:fork
+ fun:tfork_start_waiter_and_worker
+ fun:tfork_create
+ fun:tfork_thread
+ fun:vgDrd_thread_wrapper
+ fun:start_thread
+ fun:clone
+}
diff --git a/lib/util/tests/tfork-helgrind.supp b/lib/util/tests/tfork-helgrind.supp
new file mode 100644
index 0000000..4b62b2a
--- /dev/null
+++ b/lib/util/tests/tfork-helgrind.supp
@@ -0,0 +1,32 @@
+{
+ tfork_atexit_unknown1
+ Helgrind:Misc
+ fun:mutex_destroy_WRK
+ fun:pthread_mutex_destroy
+ obj:/usr/lib64/libp11-kit.so.0.3.0
+ fun:_dl_fini
+ fun:__run_exit_handlers
+ fun:exit
+ fun:(below main)
+}
+
+{
+ tfork_atexit_unknown2
+ Helgrind:Misc
+ fun:mutex_destroy_WRK
+ fun:pthread_mutex_destroy
+ fun:_dl_fini
+ fun:__run_exit_handlers
+ fun:exit
+ fun:(below main)
+}
+{
+ tfork_pthread_get_specific
+ Helgrind:Race
+ fun:tfork_global_get
+ fun:tfork_create
+ fun:tfork_thread
+ fun:mythread_wrapper
+ fun:start_thread
+ fun:clone
+}
diff --git a/lib/util/tests/tfork.c b/lib/util/tests/tfork.c
new file mode 100644
index 0000000..70ae975
--- /dev/null
+++ b/lib/util/tests/tfork.c
@@ -0,0 +1,855 @@
+/*
+ * Tests for tfork
+ *
+ * Copyright Ralph Boehme <slow@samba.org> 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <talloc.h>
+#include <tevent.h>
+#include "system/filesys.h"
+#include "system/wait.h"
+#include "system/select.h"
+#include "libcli/util/ntstatus.h"
+#include "torture/torture.h"
+#include "lib/util/data_blob.h"
+#include "torture/local/proto.h"
+#include "lib/util/tfork.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/sys_rw.h"
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+static bool test_tfork_simple(struct torture_context *tctx)
+{
+ pid_t parent = getpid();
+ struct tfork *t = NULL;
+ pid_t child;
+ int ret;
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ torture_comment(tctx, "my parent pid is %d\n", parent);
+ torture_assert(tctx, getpid() != parent, "tfork failed\n");
+ _exit(0);
+ }
+
+ ret = tfork_destroy(&t);
+ torture_assert(tctx, ret == 0, "tfork_destroy failed\n");
+
+ return true;
+}
+
+static bool test_tfork_status(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ int status;
+ pid_t child;
+ bool ok = true;
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ _exit(123);
+ }
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ torture_fail(tctx, "tfork_status failed\n");
+ }
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
+ "tfork failed\n");
+
+ torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
+
+done:
+ return ok;
+}
+
+static bool test_tfork_sigign(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ struct sigaction act;
+ pid_t child;
+ int status;
+ bool ok = true;
+ int ret;
+
+ act = (struct sigaction) {
+ .sa_flags = SA_NOCLDWAIT,
+ .sa_handler = SIG_IGN,
+ };
+
+ ret = sigaction(SIGCHLD, &act, NULL);
+ torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n");
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ sleep(1);
+ _exit(123);
+ }
+
+ child = fork();
+ if (child == -1) {
+ torture_fail(tctx, "fork failed\n");
+ return false;
+ }
+ if (child == 0) {
+ _exit(0);
+ }
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ torture_fail(tctx, "tfork_status failed\n");
+ }
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
+ "tfork failed\n");
+ torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
+
+done:
+ return ok;
+}
+
+static void sigchld_handler1(int signum, siginfo_t *si, void *u)
+{
+ pid_t pid;
+ int status;
+
+ if (signum != SIGCHLD) {
+ abort();
+ }
+
+ pid = waitpid(si->si_pid, &status, 0);
+ if (pid != si->si_pid) {
+ abort();
+ }
+}
+
+static bool test_tfork_sighandler(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ struct sigaction act;
+ struct sigaction oldact;
+ pid_t child;
+ int status;
+ bool ok = true;
+ int ret;
+
+ act = (struct sigaction) {
+ .sa_flags = SA_SIGINFO,
+ .sa_sigaction = sigchld_handler1,
+ };
+
+ ret = sigaction(SIGCHLD, &act, &oldact);
+ torture_assert_goto(tctx, ret == 0, ok, done, "sigaction failed\n");
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ sleep(1);
+ _exit(123);
+ }
+
+ child = fork();
+ if (child == -1) {
+ torture_fail(tctx, "fork failed\n");
+ return false;
+ }
+ if (child == 0) {
+ _exit(0);
+ }
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ torture_fail(tctx, "tfork_status failed\n");
+ }
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 123, ok, done,
+ "tfork failed\n");
+ torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
+
+done:
+ sigaction(SIGCHLD, &oldact, NULL);
+
+ return ok;
+}
+
+static bool test_tfork_process_hierarchy(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ pid_t pid = getpid();
+ pid_t child;
+ pid_t pgid = getpgid(0);
+ pid_t sid = getsid(0);
+ char *procpath = NULL;
+ int status;
+ struct stat st;
+ int ret;
+ bool ok = true;
+
+ procpath = talloc_asprintf(tctx, "/proc/%d/status", getpid());
+ torture_assert_not_null(tctx, procpath, "talloc_asprintf failed\n");
+
+ ret = stat(procpath, &st);
+ TALLOC_FREE(procpath);
+ if (ret != 0) {
+ if (errno == ENOENT) {
+ torture_skip(tctx, "/proc missing\n");
+ }
+ torture_fail(tctx, "stat failed\n");
+ }
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ char *cmd = NULL;
+ FILE *fp = NULL;
+ char line[64];
+ char *p;
+ pid_t ppid;
+
+ torture_assert_goto(tctx, pgid == getpgid(0), ok, child_fail, "tfork failed\n");
+ torture_assert_goto(tctx, sid == getsid(0), ok, child_fail, "tfork failed\n");
+
+ cmd = talloc_asprintf(tctx, "cat /proc/%d/status | awk '/^PPid:/ {print $2}'", getppid());
+ torture_assert_goto(tctx, cmd != NULL, ok, child_fail, "talloc_asprintf failed\n");
+
+ fp = popen(cmd, "r");
+ torture_assert_goto(tctx, fp != NULL, ok, child_fail, "popen failed\n");
+
+ p = fgets(line, sizeof(line) - 1, fp);
+ pclose(fp);
+ torture_assert_goto(tctx, p != NULL, ok, child_fail, "popen failed\n");
+
+ ret = sscanf(line, "%d", &ppid);
+ torture_assert_goto(tctx, ret == 1, ok, child_fail, "sscanf failed\n");
+ torture_assert_goto(tctx, ppid == pid, ok, child_fail, "process hierarchy not rooted at caller\n");
+
+ _exit(0);
+
+ child_fail:
+ _exit(1);
+ }
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ torture_fail(tctx, "tfork_status failed\n");
+ }
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
+ "tfork failed\n");
+ torture_comment(tctx, "exit status [%d]\n", WEXITSTATUS(status));
+
+done:
+ return ok;
+}
+
+static bool test_tfork_pipe(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ int status;
+ pid_t child;
+ int up[2];
+ int down[2];
+ char c;
+ int ret;
+ bool ok = true;
+
+ ret = pipe(&up[0]);
+ torture_assert(tctx, ret == 0, "pipe failed\n");
+
+ ret = pipe(&down[0]);
+ torture_assert(tctx, ret == 0, "pipe failed\n");
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ close(up[0]);
+ close(down[1]);
+
+ ret = read(down[0], &c, 1);
+ torture_assert_goto(tctx, ret == 1, ok, child_fail, "read failed\n");
+ torture_assert_goto(tctx, c == 1, ok, child_fail, "read failed\n");
+
+ ret = write(up[1], &(char){2}, 1);
+ torture_assert_goto(tctx, ret == 1, ok, child_fail, "write failed\n");
+
+ _exit(0);
+
+ child_fail:
+ _exit(1);
+ }
+
+ close(up[1]);
+ close(down[0]);
+
+ ret = write(down[1], &(char){1}, 1);
+ torture_assert(tctx, ret == 1, "read failed\n");
+
+ ret = read(up[0], &c, 1);
+ torture_assert(tctx, ret == 1, "read failed\n");
+ torture_assert(tctx, c == 2, "read failed\n");
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ torture_fail(tctx, "tfork_status failed\n");
+ }
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
+ "tfork failed\n");
+done:
+ return ok;
+}
+
+static bool test_tfork_twice(struct torture_context *tctx)
+{
+ struct tfork *t = NULL;
+ int status;
+ pid_t child;
+ pid_t pid;
+ int up[2];
+ int ret;
+ bool ok = true;
+
+ ret = pipe(&up[0]);
+ torture_assert(tctx, ret == 0, "pipe failed\n");
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ close(up[0]);
+
+ t = tfork_create();
+ if (t == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ sleep(1);
+ pid = getpid();
+ ret = write(up[1], &pid, sizeof(pid_t));
+ torture_assert_goto(tctx, ret == sizeof(pid_t), ok, child_fail, "write failed\n");
+
+ _exit(0);
+
+ child_fail:
+ _exit(1);
+ }
+
+ _exit(0);
+ }
+
+ close(up[1]);
+
+ ret = read(up[0], &pid, sizeof(pid_t));
+ torture_assert(tctx, ret == sizeof(pid_t), "read failed\n");
+
+ status = tfork_status(&t, true);
+ torture_assert_goto(tctx, status != -1, ok, done, "tfork_status failed\n");
+
+ torture_assert_goto(tctx, WIFEXITED(status) == true, ok, done,
+ "tfork failed\n");
+ torture_assert_goto(tctx, WEXITSTATUS(status) == 0, ok, done,
+ "tfork failed\n");
+done:
+ return ok;
+}
+
+static void *tfork_thread(void *p)
+{
+ struct tfork *t = NULL;
+ int status;
+ pid_t child;
+ uint64_t tid = (uint64_t)pthread_self();
+ uint64_t *result = NULL;
+ int up[2];
+ ssize_t nread;
+ int ret;
+
+ ret = pipe(up);
+ if (ret != 0) {
+ pthread_exit(NULL);
+ }
+
+ t = tfork_create();
+ if (t == NULL) {
+ pthread_exit(NULL);
+ }
+ child = tfork_child_pid(t);
+ if (child == 0) {
+ ssize_t nwritten;
+
+ close(up[0]);
+ tid++;
+ nwritten = sys_write(up[1], &tid, sizeof(uint64_t));
+ if (nwritten != sizeof(uint64_t)) {
+ _exit(1);
+ }
+ _exit(0);
+ }
+ close(up[1]);
+
+ result = malloc(sizeof(uint64_t));
+ if (result == NULL) {
+ pthread_exit(NULL);
+ }
+
+ nread = sys_read(up[0], result, sizeof(uint64_t));
+ if (nread != sizeof(uint64_t)) {
+ pthread_exit(NULL);
+ }
+
+ status = tfork_status(&t, true);
+ if (status == -1) {
+ pthread_exit(NULL);
+ }
+
+ pthread_exit(result);
+}
+
+static bool test_tfork_threads(struct torture_context *tctx)
+{
+ int ret;
+ bool ok = true;
+ const int num_threads = 64;
+ pthread_t threads[num_threads];
+ sigset_t set;
+ int i;
+
+#ifndef HAVE_PTHREAD
+ torture_skip(tctx, "no pthread support\n");
+#endif
+
+ /*
+ * Be nasty and taste for the worst case: ensure all threads start with
+ * SIGCHLD unblocked so we have the most fun with SIGCHLD being
+ * delivered to a random thread. :)
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+#ifdef HAVE_PTHREAD
+ ret = pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+#else
+ ret = sigprocmask(SIG_UNBLOCK, &set, NULL);
+#endif
+ if (ret != 0) {
+ return false;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ ret = pthread_create(&threads[i], NULL, tfork_thread, NULL);
+ torture_assert_goto(tctx, ret == 0, ok, done,
+ "pthread_create failed\n");
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ void *p;
+ uint64_t *result;
+
+ ret = pthread_join(threads[i], &p);
+ torture_assert_goto(tctx, ret == 0, ok, done,
+ "pthread_join failed\n");
+ result = (uint64_t *)p;
+ torture_assert_goto(tctx, *result == (uint64_t)threads[i] + 1,
+ ok, done, "thread failed\n");
+ free(p);
+ }
+
+done:
+ return ok;
+}
+
+static bool test_tfork_cmd_send(struct torture_context *tctx)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ const char *cmd[2] = { NULL, NULL };
+ bool ok = true;
+
+ ev = tevent_context_init(tctx);
+ torture_assert_goto(tctx, ev != NULL, ok, done,
+ "tevent_context_init failed\n");
+
+ cmd[0] = talloc_asprintf(tctx, "%s/testprogs/blackbox/tfork.sh", SRCDIR);
+ torture_assert_goto(tctx, cmd[0] != NULL, ok, done,
+ "talloc_asprintf failed\n");
+
+ req = samba_runcmd_send(tctx, ev, timeval_zero(), 0, 0,
+ cmd, "foo", NULL);
+ torture_assert_goto(tctx, req != NULL, ok, done,
+ "samba_runcmd_send failed\n");
+
+ ok = tevent_req_poll(req, ev);
+ torture_assert_goto(tctx, ok, ok, done, "tevent_req_poll failed\n");
+
+ torture_comment(tctx, "samba_runcmd_send test finished\n");
+
+done:
+ TALLOC_FREE(ev);
+
+ return ok;
+}
+
+/*
+ * Test to ensure that the event_fd becomes readable after
+ * a tfork_process terminates.
+ */
+static bool test_tfork_event_file_handle(struct torture_context *tctx)
+{
+ bool ok = true;
+
+ struct tfork *t1 = NULL;
+ pid_t child1;
+ struct pollfd poll1[] = {
+ {
+ .fd = -1,
+ .events = POLLIN,
+ },
+ };
+
+ struct tfork *t2 = NULL;
+ pid_t child2;
+ struct pollfd poll2[] = {
+ {
+ .fd = -1,
+ .events = POLLIN,
+ },
+ };
+
+
+ t1 = tfork_create();
+ if (t1 == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+
+ child1 = tfork_child_pid(t1);
+ if (child1 == 0) {
+ /*
+ * Parent process will kill this with a SIGTERM
+ * so 10 seconds should be plenty
+ */
+ sleep(10);
+ exit(1);
+ }
+ poll1[0].fd = tfork_event_fd(t1);
+
+ t2 = tfork_create();
+ if (t2 == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child2 = tfork_child_pid(t2);
+ if (child2 == 0) {
+ /*
+ * Parent process will kill this with a SIGTERM
+ * so 10 seconds should be plenty
+ */
+ sleep(10);
+ exit(2);
+ }
+ poll2[0].fd = tfork_event_fd(t2);
+
+ /*
+ * Have forked two process and are in the master process
+ * Expect that both event_fds are unreadable
+ */
+ poll(poll1, 1, 0);
+ ok = !(poll1[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd readable\n");
+ poll(poll2, 1, 0);
+ ok = !(poll2[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd readable\n");
+
+ /* Kill the first child process */
+ kill(child1, SIGKILL);
+ sleep(1);
+
+ /*
+ * Have killed the first child, so expect it's event_fd to have gone
+ * readable.
+ *
+ */
+ poll(poll1, 1, 0);
+ ok = (poll1[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd not readable\n");
+ poll(poll2, 1, 0);
+ ok = !(poll2[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 2 event fd readable\n");
+
+ /* Kill the secind child process */
+ kill(child2, SIGKILL);
+ sleep(1);
+ /*
+ * Have killed the children, so expect their event_fd's to have gone
+ * readable.
+ *
+ */
+ poll(poll1, 1, 0);
+ ok = (poll1[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd not readable\n");
+ poll(poll2, 1, 0);
+ ok = (poll2[0].revents & POLLIN);
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 2 event fd not readable\n");
+
+done:
+ free(t1);
+ free(t2);
+
+ return ok;
+}
+
+/*
+ * Test to ensure that the status calls behave as expected after a process
+ * terminates.
+ *
+ * As the parent process owns the status fd's they get passed to all
+ * subsequent children after a tfork. So it's possible for another
+ * child process to hold the status pipe open.
+ *
+ * The event fd needs to be left open by tfork, as a close in the status
+ * code can cause issues in tevent code.
+ *
+ */
+static bool test_tfork_status_handle(struct torture_context *tctx)
+{
+ bool ok = true;
+
+ struct tfork *t1 = NULL;
+ pid_t child1;
+
+ struct tfork *t2 = NULL;
+ pid_t child2;
+
+ int status;
+ int fd;
+ int ev1_fd;
+ int ev2_fd;
+
+
+ t1 = tfork_create();
+ if (t1 == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+
+ child1 = tfork_child_pid(t1);
+ if (child1 == 0) {
+ /*
+ * Parent process will kill this with a SIGTERM
+ * so 10 seconds should be plenty
+ */
+ sleep(10);
+ exit(1);
+ }
+ ev1_fd = tfork_event_fd(t1);
+
+ t2 = tfork_create();
+ if (t2 == NULL) {
+ torture_fail(tctx, "tfork failed\n");
+ return false;
+ }
+ child2 = tfork_child_pid(t2);
+ if (child2 == 0) {
+ /*
+ * Parent process will kill this with a SIGTERM
+ * so 10 seconds should be plenty
+ */
+ sleep(10);
+ exit(2);
+ }
+ ev2_fd = tfork_event_fd(t2);
+
+ /*
+ * Have forked two process and are in the master process
+ * expect that the status call will block, and hence return -1
+ * as the processes are still running
+ * The event fd's should be open.
+ */
+ status = tfork_status(&t1, false);
+ ok = status == -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork status available for non terminated "
+ "process 1\n");
+ /* Is the event fd open? */
+ fd = dup(ev1_fd);
+ ok = fd != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd is not open");
+
+ status = tfork_status(&t2, false);
+ ok = status == -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork status available for non terminated "
+ "process 2\n");
+ /* Is the event fd open? */
+ fd = dup(ev2_fd);
+ ok = fd != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 2 event fd is not open");
+
+ /*
+ * Kill the first process, it's status should be readable
+ * and it's event_fd should be open
+ * The second process's status should be unreadable.
+ */
+ kill(child1, SIGTERM);
+ sleep(1);
+ status = tfork_status(&t1, false);
+ ok = status != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork status for child 1 not available after "
+ "termination\n");
+ /* Is the event fd open? */
+ fd = dup(ev2_fd);
+ ok = fd != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd is not open");
+
+ status = tfork_status(&t2, false);
+ ok = status == -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork status available for child 2 after "
+ "termination of child 1\n");
+
+ /*
+ * Kill the second process, it's status should be readable
+ */
+ kill(child2, SIGTERM);
+ sleep(1);
+ status = tfork_status(&t2, false);
+ ok = status != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork status for child 2 not available after "
+ "termination\n");
+
+ /* Check that the event fd's are still open */
+ /* Is the event fd open? */
+ fd = dup(ev1_fd);
+ ok = fd != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 1 event fd is not open");
+ /* Is the event fd open? */
+ fd = dup(ev2_fd);
+ ok = fd != -1;
+ torture_assert_goto(tctx, ok, ok, done,
+ "tfork process 2 event fd is not open");
+
+done:
+ return ok;
+}
+
+struct torture_suite *torture_local_tfork(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite =
+ torture_suite_create(mem_ctx, "tfork");
+
+ torture_suite_add_simple_test(suite,
+ "tfork_simple",
+ test_tfork_simple);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_status",
+ test_tfork_status);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_sigign",
+ test_tfork_sigign);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_sighandler",
+ test_tfork_sighandler);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_process_hierarchy",
+ test_tfork_process_hierarchy);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_pipe",
+ test_tfork_pipe);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_twice",
+ test_tfork_twice);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_threads",
+ test_tfork_threads);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_cmd_send",
+ test_tfork_cmd_send);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_event_file_handle",
+ test_tfork_event_file_handle);
+
+ torture_suite_add_simple_test(suite,
+ "tfork_status_handle",
+ test_tfork_status_handle);
+
+ return suite;
+}
diff --git a/lib/util/tests/time.c b/lib/util/tests/time.c
new file mode 100644
index 0000000..ec27f56
--- /dev/null
+++ b/lib/util/tests/time.c
@@ -0,0 +1,151 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ util time testing
+
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+
+static bool test_null_time(struct torture_context *tctx)
+{
+ torture_assert(tctx, null_time(0), "0");
+ torture_assert(tctx, null_time(0xFFFFFFFF), "0xFFFFFFFF");
+ torture_assert(tctx, null_time(-1), "-1");
+ torture_assert(tctx, !null_time(42), "42");
+ return true;
+}
+
+static bool test_null_nttime(struct torture_context *tctx)
+{
+ torture_assert(tctx, null_nttime(0), "0");
+ torture_assert(tctx, !null_nttime(NTTIME_FREEZE), "-1");
+ torture_assert(tctx, !null_nttime(NTTIME_THAW), "-2");
+ torture_assert(tctx, !null_nttime(42), "42");
+ return true;
+}
+
+
+static bool test_http_timestring(struct torture_context *tctx)
+{
+ const char *start = "Thu, 01 Jan 1970";
+ char *result;
+ /*
+ * Correct test for negative UTC offset. Without the correction, the
+ * test fails when run on hosts with negative UTC offsets, as the date
+ * returned is back in 1969 (pre-epoch).
+ */
+ time_t now = time(NULL);
+ struct tm local = *localtime(&now);
+ struct tm gmt = *gmtime(&now);
+ time_t utc_offset = mktime(&local) - mktime(&gmt);
+
+ result = http_timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0));
+ torture_assert(tctx, !strncmp(start, result,
+ strlen(start)), result);
+ torture_assert_str_equal(tctx, "never",
+ http_timestring(tctx, get_time_t_max()), "42");
+ return true;
+}
+
+static bool test_timestring(struct torture_context *tctx)
+{
+ const char *start = "Thu Jan 1";
+ char *result;
+ /*
+ * Correct test for negative UTC offset. Without the correction, the
+ * test fails when run on hosts with negative UTC offsets, as the date
+ * returned is back in 1969 (pre-epoch).
+ */
+ time_t now = time(NULL);
+ struct tm local = *localtime(&now);
+ struct tm gmt = *gmtime(&now);
+ time_t utc_offset = mktime(&local) - mktime(&gmt);
+
+ result = timestring(tctx, 42 - (utc_offset < 0 ? utc_offset : 0));
+ torture_assert(tctx, !strncmp(start, result, strlen(start)), result);
+ return true;
+}
+
+static bool test_normalize_timespec(struct torture_context *tctx)
+{
+ const struct {
+ time_t in_s; long in_ns;
+ time_t out_s; long out_ns;
+ } data [] = {
+ { 0, 0, 0, 0 }
+ , { 1, 0, 1, 0 }
+ , { -1, 0, -1, 0 }
+ , { 0, 1000000000, 1, 0 }
+ , { 0, 2000000000, 2, 0 }
+ , { 0, 1000000001, 1, 1 }
+ , { 0, 2000000001, 2, 1 }
+ , { 0, -1000000000, -1, 0 }
+ , { 0, -2000000000, -2, 0 }
+ , { 0, -1000000001, -2, 999999999 }
+ , { 0, -2000000001, -3, 999999999 }
+ , { 0, -1, -1, 999999999 }
+ , { 1, -1, 0, 999999999 }
+ , { -1, -1, -2, 999999999 }
+ , { 0, 999999999, 0, 999999999 }
+ , { 0, 1999999999, 1, 999999999 }
+ , { 0, 2999999999, 2, 999999999 }
+ , { 0, -999999999, -1, 1 }
+ , { 0, -1999999999, -2, 1 }
+ , { 0, -2999999999, -3, 1 }
+ , { LONG_MAX, 1000000001, LONG_MAX, 999999999 } /* overflow */
+ , { LONG_MAX, 999999999, LONG_MAX, 999999999 } /* harmless */
+ , { LONG_MAX, -1, LONG_MAX-1, 999999999 } /* -1 */
+ , { LONG_MIN, -1000000001, LONG_MIN, 0 } /* overflow */
+ , { LONG_MIN, 0, LONG_MIN, 0 } /* harmless */
+ , { LONG_MIN, 1000000000, LONG_MIN+1, 0 } /* +1 */
+ };
+ int i;
+
+ for (i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
+ struct timespec ts = (struct timespec)
+ { .tv_sec = data[i].in_s
+ , .tv_nsec = data[i].in_ns };
+
+ normalize_timespec(&ts);
+
+ torture_assert_int_equal(tctx, ts.tv_sec, data[i].out_s,
+ "mismatch in tv_sec");
+ torture_assert_int_equal(tctx, ts.tv_nsec, data[i].out_ns,
+ "mismatch in tv_nsec");
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_local_util_time(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "time");
+
+ torture_suite_add_simple_test(suite, "null_time", test_null_time);
+ torture_suite_add_simple_test(suite, "null_nttime", test_null_nttime);
+ torture_suite_add_simple_test(suite, "http_timestring",
+ test_http_timestring);
+ torture_suite_add_simple_test(suite, "timestring",
+ test_timestring);
+ torture_suite_add_simple_test(suite, "normalize_timespec",
+ test_normalize_timespec);
+
+ return suite;
+}
diff --git a/lib/util/tests/util.c b/lib/util/tests/util.c
new file mode 100644
index 0000000..0b69e21
--- /dev/null
+++ b/lib/util/tests/util.c
@@ -0,0 +1,652 @@
+/*
+ * Tests for strv_util
+ *
+ * Copyright Martin Schwenke <martin@meltin.net> 2016
+ * Copyright Christof Schmitt <cs@samba.org> 2018
+ * Copyright Swen Schillig <swen@linux.ibm.com> 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <talloc.h>
+
+#include "replace.h"
+#include "system/filesys.h"
+
+#include "libcli/util/ntstatus.h"
+#include "torture/torture.h"
+#include "lib/util/data_blob.h"
+#include "torture/local/proto.h"
+
+#include "lib/util/samba_util.h"
+#include "lib/util/smb_strtox.h"
+
+#include "limits.h"
+#include "string.h"
+
+struct test_trim_string_data {
+ const char *desc;
+ const char *in;
+ const char *front;
+ const char *back;
+ const char *out;
+ bool ret;
+};
+
+static const struct test_trim_string_data test_trim_string_data[] = {
+ {
+ .desc = "All NULL",
+ .in = NULL,
+ .front = NULL,
+ .back = NULL,
+ .out = NULL,
+ .ret = false,
+ },
+ {
+ .desc = "Input NULL",
+ .in = NULL,
+ .front = "abc",
+ .back = "123",
+ .out = NULL,
+ .ret = false,
+ },
+ {
+ .desc = "Trim NULL",
+ .in = "abc",
+ .front = NULL,
+ .back = NULL,
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim empty",
+ .in = "abc",
+ .front = "",
+ .back = "",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim front, non-matching",
+ .in = "abc",
+ .front = "x",
+ .back = "",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim front, matches back",
+ .in = "abc",
+ .front = "c",
+ .back = "",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim front, partial-match",
+ .in = "abc",
+ .front = "ac",
+ .back = "",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim front, too long",
+ .in = "aaa",
+ .front = "aaaa",
+ .back = "",
+ .out = "aaa",
+ .ret = false,
+ },
+ {
+ .desc = "Trim front, 1 char, 1x",
+ .in = "abc",
+ .front = "a",
+ .back = "",
+ .out = "bc",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 1 char, 2x",
+ .in = "aabc",
+ .front = "a",
+ .back = "",
+ .out = "bc",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 1 char, 3x",
+ .in = "aaabc",
+ .front = "a",
+ .back = "",
+ .out = "bc",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 1 char, matches all",
+ .in = "aaa",
+ .front = "a",
+ .back = "",
+ .out = "",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 2 chars, 1x",
+ .in = "abc",
+ .front = "ab",
+ .back = "",
+ .out = "c",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 2 chars, 2x",
+ .in = "ababc",
+ .front = "ab",
+ .back = "",
+ .out = "c",
+ .ret = true,
+ },
+ {
+ .desc = "Trim front, 3 chars, matches all",
+ .in = "abc",
+ .front = "abc",
+ .back = "",
+ .out = "",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, non-matching",
+ .in = "abc",
+ .front = "",
+ .back = "x",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim back, matches front",
+ .in = "abc",
+ .front = "",
+ .back = "a",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim back, partial-match",
+ .in = "abc",
+ .front = "",
+ .back = "xc",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim back, too long",
+ .in = "aaa",
+ .front = "",
+ .back = "aaaa",
+ .out = "aaa",
+ .ret = false,
+ },
+ {
+ .desc = "Trim back, 1 char, 1x",
+ .in = "abc",
+ .front = "",
+ .back = "c",
+ .out = "ab",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 1 char, 2x",
+ .in = "abcc",
+ .front = "",
+ .back = "c",
+ .out = "ab",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 1 char, 3x",
+ .in = "abccc",
+ .front = "",
+ .back = "c",
+ .out = "ab",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 1 char, matches all",
+ .in = "aaa",
+ .front = "",
+ .back = "a",
+ .out = "",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 2 chars, 1x",
+ .in = "abc",
+ .front = "",
+ .back = "bc",
+ .out = "a",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 2 chars, 2x",
+ .in = "abcbc",
+ .front = "",
+ .back = "bc",
+ .out = "a",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 3 chars, matches all",
+ .in = "abc",
+ .front = "",
+ .back = "abc",
+ .out = "",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, non-matching",
+ .in = "abc",
+ .front = "x",
+ .back = "y",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim both, reversed",
+ .in = "abc",
+ .front = "c",
+ .back = "a",
+ .out = "abc",
+ .ret = false,
+ },
+ {
+ .desc = "Trim both, 1 char, 1x",
+ .in = "abc",
+ .front = "a",
+ .back = "c",
+ .out = "b",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 1 char, 2x",
+ .in = "aabcc",
+ .front = "a",
+ .back = "c",
+ .out = "b",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 1 char, 3x",
+ .in = "aaabccc",
+ .front = "a",
+ .back = "c",
+ .out = "b",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 1 char, matches all",
+ .in = "aaabbb",
+ .front = "a",
+ .back = "b",
+ .out = "",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 2 chars, 1x",
+ .in = "abxbc",
+ .front = "ab",
+ .back = "bc",
+ .out = "x",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 2 chars, 2x",
+ .in = "ababxyzbcbc",
+ .front = "ab",
+ .back = "bc",
+ .out = "xyz",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 2 chars, front matches, back doesn't",
+ .in = "abcde",
+ .front = "ab",
+ .back = "xy",
+ .out = "cde",
+ .ret = true,
+ },
+ {
+ .desc = "Trim both, 2 chars, back matches, front doesn't",
+ .in = "abcde",
+ .front = "xy",
+ .back = "de",
+ .out = "abc",
+ .ret = true,
+ },
+ {
+ .desc = "Trim back, 3 chars, matches all",
+ .in = "abcxyz",
+ .front = "abc",
+ .back = "xyz",
+ .out = "",
+ .ret = true,
+ },
+};
+
+static bool test_trim_string(struct torture_context *tctx)
+{
+ int j;
+ for (j = 0; j < ARRAY_SIZE(test_trim_string_data); j++) {
+ bool ret;
+ const struct test_trim_string_data *d =
+ &test_trim_string_data[j];
+ char *str = talloc_strdup(tctx, d->in);
+ torture_assert(tctx, d->in == NULL || str != NULL,
+ "Out of memory");
+
+ torture_comment(tctx, "%s\n", d->desc);
+ ret = trim_string(str, d->front, d->back);
+ torture_assert(tctx, ret == d->ret,
+ "Incorrect return from trim_string()");
+ if (d->out == NULL) {
+ torture_assert(tctx, str == NULL, "Expected NULL");
+ } else {
+ torture_assert(tctx, str != NULL, "Expected non-NULL");
+ torture_assert_str_equal(tctx, str, d->out,
+ "Incorrect output");
+ }
+ TALLOC_FREE(str);
+ }
+
+ return true;
+}
+
+static bool test_directory_create_or_exist(struct torture_context *tctx)
+{
+ char *path = NULL, *new_path = NULL, *file_path = NULL;
+ bool ret = true, b = true;
+ int fd;
+ NTSTATUS status;
+ const mode_t perms = 0741;
+
+ status = torture_temp_dir(tctx, "util_dir", &path);;
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "Creating test directory failed.\n");
+
+ b = directory_create_or_exist(path, perms);
+ torture_assert_goto(tctx, b == true, ret, done,
+ "directory_create_or_exist on "
+ "existing directory failed.\n");
+
+ new_path = talloc_asprintf(tctx, "%s/%s", path, "dir");
+ torture_assert_goto(tctx, new_path != NULL, ret, done,
+ "Could not allocate memory for directory path\n");
+
+ b = directory_exist(new_path);
+ torture_assert_goto(tctx, b == false, ret, done,
+ "Check for non-existing directory failed.\n");
+
+ b = directory_create_or_exist(new_path, perms);
+ torture_assert_goto(tctx, b == true, ret, done,
+ "directory_create_or_exist for "
+ "new directory failed.\n");
+
+ b = directory_exist(new_path);
+ torture_assert_goto(tctx, b == true, ret, done,
+ "Check for existing directory failed.\n");
+
+ b = file_check_permissions(new_path, geteuid(), perms, NULL);
+ torture_assert_goto(tctx, b == true, ret, done,
+ "Permission check for directory failed.\n");
+
+ file_path = talloc_asprintf(tctx, "%s/%s", path, "file");
+ torture_assert_goto(tctx, file_path != NULL, ret, done,
+ "Could not allocate memory for file path\n");
+ fd = creat(file_path, perms);
+ torture_assert_goto(tctx, fd != -1, ret, done,
+ "Creating file failed.\n");
+ close(fd);
+
+ b = directory_create_or_exist(file_path, perms);
+ torture_assert_goto(tctx, b == false, ret, done,
+ "directory_create_or_exist for "
+ "existing file failed.\n");
+
+done:
+ return ret;
+}
+
+static bool test_mem_equal_const_time(struct torture_context *tctx)
+{
+ const char *test_string = "abcdabcd";
+
+ torture_assert(tctx, mem_equal_const_time("", "", 0),
+ "zero-length comparison failed");
+
+ torture_assert(tctx, mem_equal_const_time(test_string, test_string, 8),
+ "comparison with equal pointers failed");
+
+ torture_assert(tctx, mem_equal_const_time(test_string, test_string + 4, 4),
+ "comparison with non-equal pointers failed");
+
+ torture_assert(tctx, !mem_equal_const_time("abcd", "efgh", 4),
+ "comparison with different values failed");
+
+ return true;
+}
+
+static bool test_smb_strtoul_errno_check(struct torture_context *tctx)
+{
+ const char *number = "123";
+ unsigned long int val = 0;
+ unsigned long long int vall = 0;
+ int err;
+
+ /* select an error code which is not set by the smb_strtoul routines */
+ errno = EAGAIN;
+ err = EAGAIN;
+ val = smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, errno == EAGAIN, "smb_strtoul: Expected EAGAIN");
+ torture_assert(tctx, err == 0, "smb_strtoul: Expected err = 0");
+ torture_assert(tctx, val == 123, "smb_strtoul: Expected value 123");
+
+ /* set err to an impossible value again before continuing */
+ err = EAGAIN;
+ vall = smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, errno == EAGAIN, "smb_strtoull: Expected EAGAIN");
+ torture_assert(tctx, err == 0, "smb_strtoul: Expected err = 0");
+ torture_assert(tctx, vall == 123, "smb_strtoul: Expected value 123");
+
+ return true;
+}
+
+static bool test_smb_strtoul_negative(struct torture_context *tctx)
+{
+ const char *number = "-132";
+ const char *number2 = "132-";
+ unsigned long int val = 0;
+ unsigned long long int vall = 0;
+ int err;
+
+ err = 0;
+ smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL");
+
+ err = 0;
+ smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL");
+
+ /* it is allowed to have a "-" sign after a number,
+ * e.g. as part of a formular, however, it is not supposed to
+ * have an effect on the converted value.
+ */
+
+ err = 0;
+ val = smb_strtoul(number2, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == 0, "smb_strtoul: Expected no error");
+ torture_assert(tctx, val == 132, "smb_strtoul: Wrong value");
+
+ err = 0;
+ vall = smb_strtoull(number2, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == 0, "smb_strtoull: Expected no error");
+ torture_assert(tctx, vall == 132, "smb_strtoull: Wrong value");
+
+ return true;
+}
+
+static bool test_smb_strtoul_no_number(struct torture_context *tctx)
+{
+ const char *number = "ghijk";
+ const char *blank = "";
+ int err;
+
+ err = 0;
+ smb_strtoul(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL");
+
+ err = 0;
+ smb_strtoull(number, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL");
+
+ err = 0;
+ smb_strtoul(blank, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoul: Expected EINVAL");
+
+ err = 0;
+ smb_strtoull(blank, NULL, 0, &err, SMB_STR_STANDARD);
+ torture_assert(tctx, err == EINVAL, "smb_strtoull: Expected EINVAL");
+
+ return true;
+}
+
+static bool test_smb_strtoul_allow_negative(struct torture_context *tctx)
+{
+ const char *number = "-1";
+ const char *number2 = "-1-1";
+ unsigned long res = 0;
+ unsigned long long res2 = 0;
+ char *end_ptr = NULL;
+ int err;
+
+ err = 0;
+ res = smb_strtoul(number, NULL, 0, &err, SMB_STR_ALLOW_NEGATIVE);
+ torture_assert(tctx, err == 0, "strtoul_err: Unexpected error");
+ torture_assert(tctx, res == ULONG_MAX, "strtoul_err: Unexpected value");
+
+ err = 0;
+ res2 = smb_strtoull(number, NULL, 0, &err, SMB_STR_ALLOW_NEGATIVE);
+ torture_assert(tctx, err == 0, "strtoull_err: Unexpected error");
+ torture_assert(tctx, res2 == ULLONG_MAX, "strtoull_err: Unexpected value");
+
+ err = 0;
+ smb_strtoul(number2, &end_ptr, 0, &err, SMB_STR_ALLOW_NEGATIVE);
+ torture_assert(tctx, err == 0, "strtoul_err: Unexpected error");
+ torture_assert(tctx, end_ptr[0] == '-', "strtoul_err: Unexpected end pointer");
+
+ err = 0;
+ smb_strtoull(number2, &end_ptr, 0, &err, SMB_STR_ALLOW_NEGATIVE);
+ torture_assert(tctx, err == 0, "strtoull_err: Unexpected error");
+ torture_assert(tctx, end_ptr[0] == '-', "strtoull_err: Unexpected end pointer");
+
+ return true;
+}
+
+static bool test_smb_strtoul_full_string(struct torture_context *tctx)
+{
+ const char *number = "123 ";
+ const char *number2 = "123";
+ int err;
+
+ err = 0;
+ smb_strtoul(number, NULL, 0, &err, SMB_STR_FULL_STR_CONV);
+ torture_assert(tctx, err == EINVAL, "strtoul_err: Expected EINVAL");
+
+ err = 0;
+ smb_strtoull(number, NULL, 0, &err, SMB_STR_FULL_STR_CONV);
+ torture_assert(tctx, err == EINVAL, "strtoull_err: Expected EINVAL");
+
+ err = 0;
+ smb_strtoul(number2, NULL, 0, &err, SMB_STR_FULL_STR_CONV);
+ torture_assert(tctx, err == 0, "strtoul_err: Unexpected error");
+
+ err = 0;
+ smb_strtoull(number2, NULL, 0, &err, SMB_STR_FULL_STR_CONV);
+ torture_assert(tctx, err == 0, "strtoull_err: Unexpected error");
+
+ return true;
+}
+
+static bool test_smb_strtoul_allow_no_conversion(struct torture_context *tctx)
+{
+ const char *number = "";
+ const char *number2 = "xyz";
+ unsigned long int n1 = 0;
+ unsigned long long int n2 = 0;
+ int err;
+
+ err = 0;
+ smb_strtoul(number, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION);
+ torture_assert(tctx, err == 0, "strtoul_err: Unexpected error");
+ torture_assert(tctx, n1 == 0, "strtoul_err: Unexpected value");
+
+ err = 0;
+ smb_strtoull(number, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION);
+ torture_assert(tctx, err == 0, "strtoull_err: Unexpected error");
+ torture_assert(tctx, n2 == 0, "strtoull_err: Unexpected value");
+
+ err = 0;
+ smb_strtoul(number2, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION);
+ torture_assert(tctx, err == 0, "strtoul_err: Unexpected error");
+ torture_assert(tctx, n1 == 0, "strtoul_err: Unexpected value");
+
+ err = 0;
+ smb_strtoull(number2, NULL, 0, &err, SMB_STR_ALLOW_NO_CONVERSION);
+ torture_assert(tctx, err == 0, "strtoull_err: Unexpected error");
+ torture_assert(tctx, n2 == 0, "strtoull_err: Unexpected value");
+
+ return true;
+}
+struct torture_suite *torture_local_util(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite =
+ torture_suite_create(mem_ctx, "util");
+
+ torture_suite_add_simple_test(suite,
+ "trim_string",
+ test_trim_string);
+ torture_suite_add_simple_test(suite,
+ "directory_create_or_exist",
+ test_directory_create_or_exist);
+ torture_suite_add_simple_test(suite,
+ "mem_equal_const_time",
+ test_mem_equal_const_time);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) errno",
+ test_smb_strtoul_errno_check);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) negative",
+ test_smb_strtoul_negative);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) no number",
+ test_smb_strtoul_no_number);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) allow_negative",
+ test_smb_strtoul_allow_negative);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) full string conversion",
+ test_smb_strtoul_full_string);
+ torture_suite_add_simple_test(suite,
+ "smb_strtoul(l) allow no conversion",
+ test_smb_strtoul_allow_no_conversion);
+ return suite;
+}
diff --git a/lib/util/tests/util_str_escape.c b/lib/util/tests/util_str_escape.c
new file mode 100644
index 0000000..82e2209
--- /dev/null
+++ b/lib/util/tests/util_str_escape.c
@@ -0,0 +1,90 @@
+/*
+
+ util_str_escape testing
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/local/proto.h"
+#include "lib/util/util_str_escape.h"
+
+static bool test_log_escape_empty_string(struct torture_context *tctx)
+{
+ char *result = log_escape( tctx, "");
+ torture_assert_str_equal(tctx, result, "", "Empty string handling");
+ return true;
+}
+
+static bool test_log_escape_null_string(struct torture_context *tctx)
+{
+ char *result = log_escape( tctx, NULL);
+ torture_assert(tctx, (result == NULL), "Empty string handling");
+ return true;
+}
+
+static bool test_log_escape_plain_string(struct torture_context *tctx)
+{
+ const char *input = "a plain string with no escapable characters";
+ const char *expected = "a plain string with no escapable characters";
+
+ char *result = log_escape( tctx, input);
+ torture_assert_str_equal(tctx, result, expected,
+ "Plain string handling");
+ return true;
+}
+
+static bool test_log_escape_string(struct torture_context *tctx)
+{
+ const char *input = "\a\b\f\n\r\t\v\\\x01";
+ const char *expected = "\\a\\b\\f\\n\\r\\t\\v\\\\\\x01";
+
+ char *result = log_escape( tctx, input);
+ torture_assert_str_equal(tctx, result, expected,
+ "Escapable characters in string");
+ return true;
+}
+
+static bool test_log_escape_hex_string(struct torture_context *tctx)
+{
+ const char *input = "\x01\x1F ";
+ const char *expected = "\\x01\\x1F ";
+
+ char *result = log_escape( tctx, input);
+ torture_assert_str_equal(tctx, result, expected,
+ "hex escaping");
+ return true;
+}
+struct torture_suite *torture_local_util_str_escape(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "util_str_escape");
+
+ torture_suite_add_simple_test(suite, "log_escape_empty_string",
+ test_log_escape_empty_string);
+ torture_suite_add_simple_test(suite, "log_escape_null_string",
+ test_log_escape_null_string);
+ torture_suite_add_simple_test(suite, "log_escape_plain_string",
+ test_log_escape_plain_string);
+ torture_suite_add_simple_test(suite, "log_escape_string",
+ test_log_escape_string);
+ torture_suite_add_simple_test(suite, "log_escape_hex_string",
+ test_log_escape_hex_string);
+
+
+ return suite;
+}