aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 11:30:46 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 18:04:25 +0100
commit72c8155d9fc0eaeac72c053f46ebb7b231d4596a (patch)
tree5758865289c52fa93f56e3fe743bb40c283c5233 /lib
parentcdccc69c62579b0c13b35fad0728079652b8f3c9 (diff)
Reintegrate test code with library code
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
-rw-r--r--lib/fstree/Makemodule.am36
-rw-r--r--lib/fstree/test/add_by_path.c134
-rw-r--r--lib/fstree/test/fstree_init.c50
-rw-r--r--lib/fstree/test/fstree_sort.c88
-rw-r--r--lib/fstree/test/gen_inode_numbers.c90
-rw-r--r--lib/fstree/test/get_path.c59
-rw-r--r--lib/fstree/test/mknode_dir.c63
-rw-r--r--lib/fstree/test/mknode_reg.c42
-rw-r--r--lib/fstree/test/mknode_simple.c98
-rw-r--r--lib/fstree/test/mknode_slink.c54
-rw-r--r--lib/io/Makemodule.am13
-rw-r--r--lib/io/test/get_line.c166
-rw-r--r--lib/io/test/get_line.txt11
-rw-r--r--lib/io/test/sparse_fb.c66
-rw-r--r--lib/sqfs/Makemodule.am23
-rw-r--r--lib/sqfs/test/abi.c174
-rw-r--r--lib/sqfs/test/get_node_path.c132
-rw-r--r--lib/sqfs/test/table.c199
-rw-r--r--lib/sqfs/test/xattr_benchmark.c124
-rw-r--r--lib/sqfs/test/xattr_writer.c323
-rw-r--r--lib/tar/Makemodule.am177
-rw-r--r--lib/tar/test/data/CREDITS35
-rw-r--r--lib/tar/test/data/file-size/12-digit.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/file-size/gnu.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/file-size/pax.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/gnu-g.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/gnu.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/link_filled.tarbin0 -> 10240 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/pax.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/ustar-pre-posix.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/ustar.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/format-acceptance/v7.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/large-mtime/12-digit.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/large-mtime/gnu.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/large-mtime/pax.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/long-paths/gnu.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/long-paths/pax.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/long-paths/ustar.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/negative-mtime/gnu.tarbin0 -> 10240 bytes
-rw-r--r--lib/tar/test/data/negative-mtime/pax.tarbin0 -> 10240 bytes
-rw-r--r--lib/tar/test/data/sparse-files/gnu-small.tarbin0 -> 9728 bytes
-rw-r--r--lib/tar/test/data/sparse-files/gnu.tarbin0 -> 34816 bytes
-rw-r--r--lib/tar/test/data/sparse-files/pax-gnu0-0.tarbin0 -> 35840 bytes
-rw-r--r--lib/tar/test/data/sparse-files/pax-gnu0-1.tarbin0 -> 35328 bytes
-rw-r--r--lib/tar/test/data/sparse-files/pax-gnu1-0.tarbin0 -> 35840 bytes
-rw-r--r--lib/tar/test/data/user-group-largenum/8-digit.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/user-group-largenum/gnu.tarbin0 -> 2048 bytes
-rw-r--r--lib/tar/test/data/user-group-largenum/pax.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/xattr/acl.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/xattr/xattr-libarchive.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/xattr/xattr-schily-binary.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/data/xattr/xattr-schily.tarbin0 -> 3072 bytes
-rw-r--r--lib/tar/test/tar_big_file.c31
-rw-r--r--lib/tar/test/tar_fuzz.c49
-rw-r--r--lib/tar/test/tar_simple.c64
-rw-r--r--lib/tar/test/tar_sparse.c86
-rw-r--r--lib/tar/test/tar_sparse_gnu.c52
-rw-r--r--lib/tar/test/tar_target_filled.c109
-rw-r--r--lib/tar/test/tar_xattr.c42
-rw-r--r--lib/tar/test/tar_xattr_bin.c50
-rw-r--r--lib/util/Makemodule.am52
-rw-r--r--lib/util/test/base64_decode.c74
-rw-r--r--lib/util/test/canonicalize_name.c78
-rw-r--r--lib/util/test/epoch.c67
-rw-r--r--lib/util/test/filename_sane.c94
-rw-r--r--lib/util/test/hex_decode.c66
-rw-r--r--lib/util/test/is_memory_zero.c33
-rw-r--r--lib/util/test/rbtree.c233
-rw-r--r--lib/util/test/str_table.c85
-rw-r--r--lib/util/test/threadpool.c168
-rw-r--r--lib/util/test/words.txt1000
-rw-r--r--lib/util/test/xxhash.c66
-rw-r--r--lib/xfrm/Makemodule.am46
-rw-r--r--lib/xfrm/test/pack.c165
-rw-r--r--lib/xfrm/test/unpack.c428
75 files changed, 5295 insertions, 0 deletions
diff --git a/lib/fstree/Makemodule.am b/lib/fstree/Makemodule.am
index f740527..e2d2ae7 100644
--- a/lib/fstree/Makemodule.am
+++ b/lib/fstree/Makemodule.am
@@ -4,3 +4,39 @@ libfstree_a_SOURCES = include/fstree.h lib/fstree/src/fstree.c \
lib/fstree/src/add_by_path.c lib/fstree/src/get_by_path.c
noinst_LIBRARIES += libfstree.a
+
+test_mknode_simple_SOURCES = lib/fstree/test/mknode_simple.c
+test_mknode_simple_LDADD = libfstree.a libcompat.a
+
+test_mknode_slink_SOURCES = lib/fstree/test/mknode_slink.c
+test_mknode_slink_LDADD = libfstree.a libcompat.a
+
+test_mknode_reg_SOURCES = lib/fstree/test/mknode_reg.c
+test_mknode_reg_LDADD = libfstree.a libcompat.a
+
+test_mknode_dir_SOURCES = lib/fstree/test/mknode_dir.c
+test_mknode_dir_LDADD = libfstree.a libcompat.a
+
+test_gen_inode_numbers_SOURCES = lib/fstree/test/gen_inode_numbers.c
+test_gen_inode_numbers_LDADD = libfstree.a libutil.a libcompat.a
+
+test_add_by_path_SOURCES = lib/fstree/test/add_by_path.c
+test_add_by_path_LDADD = libfstree.a libutil.a libcompat.a
+
+test_get_path_SOURCES = lib/fstree/test/get_path.c
+test_get_path_LDADD = libfstree.a libutil.a libcompat.a
+
+test_fstree_sort_SOURCES = lib/fstree/test/fstree_sort.c
+test_fstree_sort_LDADD = libfstree.a libio.a libutil.a libcompat.a
+
+test_fstree_init_SOURCES = lib/fstree/test/fstree_init.c
+test_fstree_init_LDADD = libfstree.a libio.a libutil.a libcompat.a
+
+FSTREE_TESTS = \
+ test_mknode_simple test_mknode_slink \
+ test_mknode_reg test_mknode_dir test_gen_inode_numbers \
+ test_add_by_path test_get_path test_fstree_sort \
+ test_fstree_init
+
+check_PROGRAMS += $(FSTREE_TESTS)
+TESTS += $(FSTREE_TESTS)
diff --git a/lib/fstree/test/add_by_path.c b/lib/fstree/test/add_by_path.c
new file mode 100644
index 0000000..97e5a60
--- /dev/null
+++ b/lib/fstree/test/add_by_path.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * add_by_path.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *a, *b;
+ struct stat sb;
+ fstree_t fs;
+ char *opts;
+ (void)argc; (void)argv;
+
+ opts = strdup("mode=0755,uid=21,gid=42");
+ TEST_ASSERT(fstree_init(&fs, opts) == 0);
+ free(opts);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0750;
+ sb.st_uid = 1000;
+ sb.st_gid = 100;
+
+ TEST_EQUAL_UI(fs.root->link_count, 2);
+
+ a = fstree_add_generic(&fs, "dir", &sb, NULL);
+ TEST_NOT_NULL(a);
+ TEST_STR_EQUAL(a->name, "dir");
+ TEST_EQUAL_UI(a->mode, sb.st_mode);
+ TEST_EQUAL_UI(a->uid, sb.st_uid);
+ TEST_EQUAL_UI(a->gid, sb.st_gid);
+ TEST_ASSERT(a->parent == fs.root);
+ TEST_EQUAL_UI(a->link_count, 2);
+ TEST_NULL(a->next);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_EQUAL_UI(fs.root->link_count, 3);
+ TEST_ASSERT(!a->data.dir.created_implicitly);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFBLK | 0640;
+ sb.st_rdev = 1234;
+
+ b = fstree_add_generic(&fs, "blkdev", &sb, NULL);
+ TEST_NOT_NULL(b);
+ TEST_ASSERT(b != a);
+ TEST_STR_EQUAL(b->name, "blkdev");
+ TEST_EQUAL_UI(b->mode, sb.st_mode);
+ TEST_EQUAL_UI(b->uid, sb.st_uid);
+ TEST_EQUAL_UI(b->gid, sb.st_gid);
+ TEST_ASSERT(b->parent == fs.root);
+ TEST_EQUAL_UI(b->link_count, 1);
+ TEST_EQUAL_UI(b->data.devno, sb.st_rdev);
+ TEST_ASSERT(b->next == a);
+ TEST_EQUAL_UI(fs.root->link_count, 4);
+ TEST_ASSERT(fs.root->data.dir.children == b);
+
+ TEST_NULL(fstree_add_generic(&fs, "blkdev/foo", &sb, NULL));
+ TEST_EQUAL_UI(errno, ENOTDIR);
+
+ TEST_NULL(fstree_add_generic(&fs, "dir", &sb, NULL));
+ TEST_EQUAL_UI(errno, EEXIST);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0755;
+ TEST_NULL(fstree_add_generic(&fs, "dir", &sb, NULL));
+ TEST_EQUAL_UI(errno, EEXIST);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFCHR | 0444;
+ b = fstree_add_generic(&fs, "dir/chrdev", &sb, NULL);
+ TEST_NOT_NULL(b);
+ TEST_EQUAL_UI(b->mode, sb.st_mode);
+ TEST_EQUAL_UI(b->uid, sb.st_uid);
+ TEST_EQUAL_UI(b->gid, sb.st_gid);
+ TEST_EQUAL_UI(b->link_count, 1);
+ TEST_ASSERT(b->parent == a);
+ TEST_EQUAL_UI(b->data.devno, sb.st_rdev);
+ TEST_NULL(b->next);
+ TEST_ASSERT(a->data.dir.children == b);
+
+ TEST_EQUAL_UI(a->link_count, 3);
+ TEST_EQUAL_UI(fs.root->link_count, 4);
+
+ b = fstree_add_generic(&fs, "dir/foo/chrdev", &sb, NULL);
+ TEST_NOT_NULL(b);
+ TEST_NULL(b->next);
+ TEST_EQUAL_UI(b->mode, sb.st_mode);
+ TEST_EQUAL_UI(b->uid, sb.st_uid);
+ TEST_EQUAL_UI(b->gid, sb.st_gid);
+ TEST_EQUAL_UI(b->link_count, 1);
+ TEST_ASSERT(b->parent != a);
+ TEST_ASSERT(b->parent->parent == a);
+ TEST_EQUAL_UI(b->data.devno, sb.st_rdev);
+ TEST_NULL(b->next);
+
+ TEST_EQUAL_UI(a->link_count, 4);
+ TEST_EQUAL_UI(fs.root->link_count, 4);
+ TEST_ASSERT(a->data.dir.children != b);
+
+ b = b->parent;
+ TEST_ASSERT(b->data.dir.created_implicitly);
+ TEST_EQUAL_UI(b->mode, S_IFDIR | 0755);
+ TEST_EQUAL_UI(b->uid, 21);
+ TEST_EQUAL_UI(b->gid, 42);
+ TEST_EQUAL_UI(b->link_count, 3);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0750;
+ sb.st_uid = 1000;
+ sb.st_gid = 100;
+
+ a = fstree_add_generic(&fs, "dir/foo", &sb, NULL);
+ TEST_NOT_NULL(a);
+ TEST_ASSERT(a == b);
+ TEST_ASSERT(!a->data.dir.created_implicitly);
+ TEST_EQUAL_UI(a->mode, sb.st_mode);
+ TEST_EQUAL_UI(a->uid, sb.st_uid);
+ TEST_EQUAL_UI(a->gid, sb.st_gid);
+ TEST_EQUAL_UI(a->link_count, 3);
+
+ TEST_EQUAL_UI(a->parent->link_count, 4);
+ TEST_EQUAL_UI(fs.root->link_count, 4);
+
+ TEST_NULL(fstree_add_generic(&fs, "dir/foo", &sb, NULL));
+ TEST_EQUAL_UI(errno, EEXIST);
+
+ fstree_cleanup(&fs);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/fstree_init.c b/lib/fstree/test/fstree_init.c
new file mode 100644
index 0000000..186f25b
--- /dev/null
+++ b/lib/fstree/test/fstree_init.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * fstree_init.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+#include "util/util.h"
+
+int main(int argc, char **argv)
+{
+ fstree_t fs;
+ char *str;
+ (void)argc; (void)argv;
+
+ str = strdup("mtime=1337,uid=1000,gid=100,mode=0321");
+ TEST_NOT_NULL(str);
+ TEST_ASSERT(fstree_init(&fs, str) == 0);
+ free(str);
+ TEST_EQUAL_UI(fs.defaults.st_mtime, 1337);
+ TEST_EQUAL_UI(fs.defaults.st_uid, 1000);
+ TEST_EQUAL_UI(fs.defaults.st_gid, 100);
+ TEST_EQUAL_UI(fs.defaults.st_mode, S_IFDIR | 0321);
+ fstree_cleanup(&fs);
+
+ TEST_ASSERT(fstree_init(&fs, NULL) == 0);
+ if (fs.defaults.st_mtime != 0) {
+ TEST_EQUAL_UI(fs.defaults.st_mtime, get_source_date_epoch());
+ }
+ TEST_EQUAL_UI(fs.defaults.st_uid, 0);
+ TEST_EQUAL_UI(fs.defaults.st_gid, 0);
+ TEST_EQUAL_UI(fs.defaults.st_mode, S_IFDIR | 0755);
+ fstree_cleanup(&fs);
+
+ str = strdup("mode=07777");
+ TEST_NOT_NULL(str);
+ TEST_ASSERT(fstree_init(&fs, str) == 0);
+ free(str);
+ fstree_cleanup(&fs);
+
+ str = strdup("mode=017777");
+ TEST_NOT_NULL(str);
+ TEST_ASSERT(fstree_init(&fs, str) != 0);
+ free(str);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/fstree_sort.c b/lib/fstree/test/fstree_sort.c
new file mode 100644
index 0000000..7df85f5
--- /dev/null
+++ b/lib/fstree/test/fstree_sort.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * fstree_sort.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *a, *b, *c, *d;
+ struct stat sb;
+ fstree_t fs;
+ int ret;
+ (void)argc; (void)argv;
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFBLK | 0600;
+ sb.st_rdev = 1337;
+
+ /* in order */
+ ret = fstree_init(&fs, NULL);
+ TEST_EQUAL_I(ret, 0);
+
+ a = fstree_mknode(fs.root, "a", 1, NULL, &sb);
+ TEST_NOT_NULL(a);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_NULL(a->next);
+
+ b = fstree_mknode(fs.root, "b", 1, NULL, &sb);
+ TEST_NOT_NULL(a);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_ASSERT(a->next == b);
+ TEST_NULL(b->next);
+
+ c = fstree_mknode(fs.root, "c", 1, NULL, &sb);
+ TEST_NOT_NULL(c);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_ASSERT(a->next == b);
+ TEST_ASSERT(b->next == c);
+ TEST_NULL(c->next);
+
+ d = fstree_mknode(fs.root, "d", 1, NULL, &sb);
+ TEST_NOT_NULL(d);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_ASSERT(a->next == b);
+ TEST_ASSERT(b->next == c);
+ TEST_ASSERT(c->next == d);
+ TEST_NULL(d->next);
+
+ fstree_cleanup(&fs);
+
+ /* out-of-order */
+ ret = fstree_init(&fs, NULL);
+ TEST_EQUAL_I(ret, 0);
+
+ d = fstree_mknode(fs.root, "d", 1, NULL, &sb);
+ TEST_NOT_NULL(d);
+ TEST_ASSERT(fs.root->data.dir.children == d);
+ TEST_NULL(d->next);
+
+ c = fstree_mknode(fs.root, "c", 1, NULL, &sb);
+ TEST_NOT_NULL(c);
+ TEST_ASSERT(fs.root->data.dir.children == c);
+ TEST_ASSERT(c->next == d);
+ TEST_NULL(d->next);
+
+ b = fstree_mknode(fs.root, "b", 1, NULL, &sb);
+ TEST_NOT_NULL(b);
+ TEST_ASSERT(fs.root->data.dir.children == b);
+ TEST_ASSERT(b->next == c);
+ TEST_ASSERT(c->next == d);
+ TEST_NULL(d->next);
+
+ a = fstree_mknode(fs.root, "a", 1, NULL, &sb);
+ TEST_NOT_NULL(a);
+ TEST_ASSERT(fs.root->data.dir.children == a);
+ TEST_ASSERT(a->next == b);
+ TEST_ASSERT(b->next == c);
+ TEST_ASSERT(c->next == d);
+ TEST_NULL(d->next);
+
+ fstree_cleanup(&fs);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/gen_inode_numbers.c b/lib/fstree/test/gen_inode_numbers.c
new file mode 100644
index 0000000..5403580
--- /dev/null
+++ b/lib/fstree/test/gen_inode_numbers.c
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * gen_inode_table.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+static tree_node_t *gen_node(tree_node_t *parent, const char *name)
+{
+ struct stat sb;
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0755;
+
+ return fstree_mknode(parent, name, strlen(name), NULL, &sb);
+}
+
+static void check_children_before_root(tree_node_t *root)
+{
+ tree_node_t *n;
+
+ for (n = root->data.dir.children; n != NULL; n = n->next)
+ TEST_LESS_THAN_UI(n->inode_num, root->inode_num);
+
+ for (n = root->data.dir.children; n != NULL; n = n->next)
+ check_children_before_root(n);
+}
+
+static void check_children_continuous(tree_node_t *root)
+{
+ tree_node_t *n;
+
+ for (n = root->data.dir.children; n != NULL; n = n->next) {
+ if (n->next != NULL) {
+ TEST_EQUAL_UI(n->next->inode_num, (n->inode_num + 1));
+ }
+ }
+
+ for (n = root->data.dir.children; n != NULL; n = n->next)
+ check_children_continuous(n);
+}
+
+int main(int argc, char **argv)
+{
+ tree_node_t *a, *b, *c;
+ fstree_t fs;
+ (void)argc; (void)argv;
+
+ // inode table for the empty tree
+ TEST_ASSERT(fstree_init(&fs, NULL) == 0);
+ fstree_post_process(&fs);
+ TEST_EQUAL_UI(fs.unique_inode_count, 1);
+ TEST_EQUAL_UI(fs.root->inode_num, 1);
+ fstree_cleanup(&fs);
+
+ // tree with 2 levels under root, fan out 3
+ TEST_ASSERT(fstree_init(&fs, NULL) == 0);
+
+ a = gen_node(fs.root, "a");
+ b = gen_node(fs.root, "b");
+ c = gen_node(fs.root, "c");
+ TEST_NOT_NULL(a);
+ TEST_NOT_NULL(b);
+ TEST_NOT_NULL(c);
+
+ TEST_NOT_NULL(gen_node(a, "a_a"));
+ TEST_NOT_NULL(gen_node(a, "a_b"));
+ TEST_NOT_NULL(gen_node(a, "a_c"));
+
+ TEST_NOT_NULL(gen_node(b, "b_a"));
+ TEST_NOT_NULL(gen_node(b, "b_b"));
+ TEST_NOT_NULL(gen_node(b, "b_c"));
+
+ TEST_NOT_NULL(gen_node(c, "c_a"));
+ TEST_NOT_NULL(gen_node(c, "c_b"));
+ TEST_NOT_NULL(gen_node(c, "c_c"));
+
+ fstree_post_process(&fs);
+ TEST_EQUAL_UI(fs.unique_inode_count, 13);
+
+ check_children_before_root(fs.root);
+ check_children_continuous(fs.root);
+
+ fstree_cleanup(&fs);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/get_path.c b/lib/fstree/test/get_path.c
new file mode 100644
index 0000000..61001e9
--- /dev/null
+++ b/lib/fstree/test/get_path.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * get_path.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *a, *b, *c, *d;
+ struct stat sb;
+ fstree_t fs;
+ char *str;
+ (void)argc; (void)argv;
+
+ TEST_ASSERT(fstree_init(&fs, NULL) == 0);
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0750;
+ sb.st_uid = 1000;
+ sb.st_gid = 100;
+
+ a = fstree_add_generic(&fs, "foo", &sb, NULL);
+ b = fstree_add_generic(&fs, "foo/bar", &sb, NULL);
+ c = fstree_add_generic(&fs, "foo/bar/baz", &sb, NULL);
+ d = fstree_add_generic(&fs, "foo/bar/baz/dir", &sb, NULL);
+
+ str = fstree_get_path(fs.root);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/");
+ free(str);
+
+ str = fstree_get_path(a);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/foo");
+ free(str);
+
+ str = fstree_get_path(b);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/foo/bar");
+ free(str);
+
+ str = fstree_get_path(c);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/foo/bar/baz");
+ free(str);
+
+ str = fstree_get_path(d);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/foo/bar/baz/dir");
+ free(str);
+
+ fstree_cleanup(&fs);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/mknode_dir.c b/lib/fstree/test/mknode_dir.c
new file mode 100644
index 0000000..dd7eba7
--- /dev/null
+++ b/lib/fstree/test/mknode_dir.c
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * mknode_dir.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *root, *a, *b;
+ struct stat sb;
+ fstree_t fs;
+ (void)argc; (void)argv;
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFDIR | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 4096;
+
+ root = fstree_mknode(NULL, "rootdir", 7, NULL, &sb);
+ TEST_EQUAL_UI(root->uid, sb.st_uid);
+ TEST_EQUAL_UI(root->gid, sb.st_gid);
+ TEST_EQUAL_UI(root->mode, sb.st_mode);
+ TEST_EQUAL_UI(root->link_count, 2);
+ TEST_ASSERT(root->name >= (char *)root->payload);
+ TEST_STR_EQUAL(root->name, "rootdir");
+ TEST_NULL(root->data.dir.children);
+ TEST_NULL(root->parent);
+ TEST_NULL(root->next);
+
+ a = fstree_mknode(root, "adir", 4, NULL, &sb);
+ TEST_ASSERT(a->parent == root);
+ TEST_NULL(a->next);
+ TEST_EQUAL_UI(a->link_count, 2);
+ TEST_EQUAL_UI(root->link_count, 3);
+ TEST_ASSERT(root->data.dir.children == a);
+ TEST_NULL(root->parent);
+ TEST_NULL(root->next);
+
+ b = fstree_mknode(root, "bdir", 4, NULL, &sb);
+ TEST_ASSERT(a->parent == root);
+ TEST_ASSERT(b->parent == root);
+ TEST_EQUAL_UI(b->link_count, 2);
+ TEST_ASSERT(root->data.dir.children == a);
+ TEST_ASSERT(a->next == b);
+ TEST_EQUAL_UI(root->link_count, 4);
+ TEST_NULL(b->next);
+ TEST_NULL(root->parent);
+ TEST_NULL(root->next);
+
+ free(root);
+ free(a);
+ free(b);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/mknode_reg.c b/lib/fstree/test/mknode_reg.c
new file mode 100644
index 0000000..368720f
--- /dev/null
+++ b/lib/fstree/test/mknode_reg.c
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * mknode_reg.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *node;
+ struct stat sb;
+ fstree_t fs;
+ (void)argc; (void)argv;
+
+ memset(&fs, 0, sizeof(fs));
+
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFREG | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 4096;
+
+ node = fstree_mknode(NULL, "filename", 8, "input", &sb);
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, sb.st_mode);
+ TEST_NULL(node->parent);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_ASSERT(node->data.file.input_file >= (char *)node->payload);
+ TEST_ASSERT(node->data.file.input_file >= node->name + 8);
+ TEST_STR_EQUAL(node->name, "filename");
+ TEST_STR_EQUAL(node->data.file.input_file, "input");
+ free(node);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/mknode_simple.c b/lib/fstree/test/mknode_simple.c
new file mode 100644
index 0000000..c7efb49
--- /dev/null
+++ b/lib/fstree/test/mknode_simple.c
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * mknode_simple.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *node;
+ struct stat sb;
+ fstree_t fs;
+ (void)argc; (void)argv;
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFSOCK | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 1337;
+
+ node = fstree_mknode(NULL, "sockfile", 8, NULL, &sb);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_STR_EQUAL(node->name, "sockfile");
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, sb.st_mode);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_NULL(node->parent);
+ TEST_NULL(node->data.target);
+ TEST_EQUAL_UI(node->data.devno, 0);
+ free(node);
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFIFO | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 1337;
+
+ node = fstree_mknode(NULL, "fifo", 4, NULL, &sb);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_STR_EQUAL(node->name, "fifo");
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, sb.st_mode);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_NULL(node->parent);
+ TEST_NULL(node->data.target);
+ TEST_EQUAL_UI(node->data.devno, 0);
+ free(node);
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFBLK | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 1337;
+
+ node = fstree_mknode(NULL, "blkdev", 6, NULL, &sb);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_STR_EQUAL(node->name, "blkdev");
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, sb.st_mode);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_EQUAL_UI(node->data.devno, sb.st_rdev);
+ TEST_NULL(node->parent);
+ free(node);
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFCHR | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 1337;
+
+ node = fstree_mknode(NULL, "chardev", 7, NULL, &sb);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_STR_EQUAL(node->name, "chardev");
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, sb.st_mode);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_EQUAL_UI(node->data.devno, sb.st_rdev);
+ TEST_NULL(node->parent);
+ free(node);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/fstree/test/mknode_slink.c b/lib/fstree/test/mknode_slink.c
new file mode 100644
index 0000000..c50a0ba
--- /dev/null
+++ b/lib/fstree/test/mknode_slink.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * mknode_slink.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "fstree.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tree_node_t *node;
+ struct stat sb;
+ fstree_t fs;
+ (void)argc; (void)argv;
+
+ memset(&fs, 0, sizeof(fs));
+ memset(&sb, 0, sizeof(sb));
+ sb.st_mode = S_IFLNK | 0654;
+ sb.st_uid = 123;
+ sb.st_gid = 456;
+ sb.st_rdev = 789;
+ sb.st_size = 1337;
+
+ node = fstree_mknode(NULL, "symlink", 7, "target", &sb);
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, S_IFLNK | 0777);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_NULL(node->parent);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_ASSERT(node->data.target >= (char *)node->payload);
+ TEST_ASSERT(node->data.target >= node->name + 8);
+ TEST_STR_EQUAL(node->name, "symlink");
+ TEST_STR_EQUAL(node->data.target, "target");
+ free(node);
+
+ node = fstree_mknode(NULL, "symlink", 7, "", &sb);
+ TEST_EQUAL_UI(node->uid, sb.st_uid);
+ TEST_EQUAL_UI(node->gid, sb.st_gid);
+ TEST_EQUAL_UI(node->mode, S_IFLNK | 0777);
+ TEST_EQUAL_UI(node->link_count, 1);
+ TEST_NULL(node->parent);
+ TEST_ASSERT((char *)node->name >= (char *)node->payload);
+ TEST_ASSERT(node->data.target >= (char *)node->payload);
+ TEST_ASSERT(node->data.target >= node->name + 8);
+ TEST_STR_EQUAL(node->name, "symlink");
+ TEST_STR_EQUAL(node->data.target, "");
+ free(node);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/io/Makemodule.am b/lib/io/Makemodule.am
index c331124..17952f4 100644
--- a/lib/io/Makemodule.am
+++ b/lib/io/Makemodule.am
@@ -14,3 +14,16 @@ libio_a_SOURCES += lib/io/src/unix/ostream.c lib/io/src/unix/istream.c
endif
noinst_LIBRARIES += libio.a
+
+test_get_line_SOURCES = lib/io/test/get_line.c
+test_get_line_LDADD = libio.a libcompat.a
+test_get_line_CPPFLAGS = $(AM_CPPFLAGS)
+test_get_line_CPPFLAGS += -DTESTFILE=$(top_srcdir)/lib/io/test/get_line.txt
+
+test_sparse_fb_SOURCES = lib/io/test/sparse_fb.c
+test_sparse_fb_LDADD = libio.a libutil.a libcompat.a
+
+check_PROGRAMS += test_get_line test_sparse_fb
+TESTS += test_get_line test_sparse_fb
+
+EXTRA_DIST += $(top_srcdir)/lib/io/test/get_line.txt
diff --git a/lib/io/test/get_line.c b/lib/io/test/get_line.c
new file mode 100644
index 0000000..2d0f9b7
--- /dev/null
+++ b/lib/io/test/get_line.c
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * get_line.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "io/file.h"
+#include "util/test.h"
+
+typedef struct {
+ size_t line_num;
+ const char *str;
+} line_t;
+
+static void run_test_case(const line_t *lines, size_t count,
+ int flags)
+{
+ size_t i, line_num, old_line_num;
+ istream_t *fp;
+ char *line;
+ int ret;
+
+ fp = istream_open_file(STRVALUE(TESTFILE));
+ TEST_NOT_NULL(fp);
+
+ line_num = 1;
+ line = NULL;
+
+ for (i = 0; i < count; ++i) {
+ old_line_num = line_num;
+ ret = istream_get_line(fp, &line, &line_num, flags);
+
+ TEST_ASSERT(line_num >= old_line_num);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(line);
+
+ TEST_EQUAL_UI(line_num, lines[i].line_num);
+ TEST_STR_EQUAL(line, lines[i].str);
+
+ free(line);
+ line = NULL;
+ line_num += 1;
+ }
+
+ ret = istream_get_line(fp, &line, &line_num, flags);
+ TEST_ASSERT(ret > 0);
+
+ sqfs_drop(fp);
+}
+
+static const line_t lines_raw[] = {
+ { 1, "" },
+ { 2, "The quick" },
+ { 3, " " },
+ { 4, " brown fox " },
+ { 5, "" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 9, "" },
+ { 10, "dog" },
+ { 11, "" },
+};
+
+static const line_t lines_ltrim[] = {
+ { 1, "" },
+ { 2, "The quick" },
+ { 3, "" },
+ { 4, "brown fox " },
+ { 5, "" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 9, "" },
+ { 10, "dog" },
+ { 11, "" },
+};
+
+static const line_t lines_rtrim[] = {
+ { 1, "" },
+ { 2, "The quick" },
+ { 3, "" },
+ { 4, " brown fox" },
+ { 5, "" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 9, "" },
+ { 10, "dog" },
+ { 11, "" },
+};
+
+static const line_t lines_trim[] = {
+ { 1, "" },
+ { 2, "The quick" },
+ { 3, "" },
+ { 4, "brown fox" },
+ { 5, "" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 9, "" },
+ { 10, "dog" },
+ { 11, "" },
+};
+
+static const line_t lines_no_empty[] = {
+ { 2, "The quick" },
+ { 3, " " },
+ { 4, " brown fox " },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 10, "dog" },
+};
+
+static const line_t lines_no_empty_ltrim[] = {
+ { 2, "The quick" },
+ { 4, "brown fox " },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 10, "dog" },
+};
+
+static const line_t lines_no_empty_rtrim[] = {
+ { 2, "The quick" },
+ { 4, " brown fox" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 10, "dog" },
+};
+
+static const line_t lines_no_empty_trim[] = {
+ { 2, "The quick" },
+ { 4, "brown fox" },
+ { 6, "jumps over" },
+ { 7, "the" },
+ { 8, "lazy" },
+ { 10, "dog" },
+};
+
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+
+ run_test_case(lines_raw, 11, 0);
+ run_test_case(lines_ltrim, 11, ISTREAM_LINE_LTRIM);
+ run_test_case(lines_rtrim, 11, ISTREAM_LINE_RTRIM);
+ run_test_case(lines_trim, 11,
+ ISTREAM_LINE_LTRIM | ISTREAM_LINE_RTRIM);
+
+ run_test_case(lines_no_empty, 7, ISTREAM_LINE_SKIP_EMPTY);
+ run_test_case(lines_no_empty_ltrim, 6,
+ ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM);
+ run_test_case(lines_no_empty_rtrim, 6,
+ ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_RTRIM);
+ run_test_case(lines_no_empty_trim, 6,
+ ISTREAM_LINE_SKIP_EMPTY | ISTREAM_LINE_LTRIM |
+ ISTREAM_LINE_RTRIM);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/io/test/get_line.txt b/lib/io/test/get_line.txt
new file mode 100644
index 0000000..a1994f0
--- /dev/null
+++ b/lib/io/test/get_line.txt
@@ -0,0 +1,11 @@
+
+The quick
+
+ brown fox
+
+jumps over
+the
+lazy
+
+dog
+
diff --git a/lib/io/test/sparse_fb.c b/lib/io/test/sparse_fb.c
new file mode 100644
index 0000000..fa4b840
--- /dev/null
+++ b/lib/io/test/sparse_fb.c
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * get_line.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "io/ostream.h"
+#include "util/test.h"
+#include "util/util.h"
+
+static ostream_t dummy;
+static size_t total = 0;
+
+static int dummy_append(ostream_t *strm, const void *data, size_t size)
+{
+ bool bret;
+
+ TEST_ASSERT(strm == &dummy);
+ TEST_NOT_NULL(data);
+ TEST_ASSERT(size > 0);
+
+ bret = is_memory_zero(data, size);
+ TEST_ASSERT(bret);
+
+ bret = SZ_ADD_OV(total, size, &total);
+ TEST_ASSERT(!bret);
+ return 0;
+}
+
+static int dummy_flush(ostream_t *strm)
+{
+ TEST_ASSERT(strm == &dummy);
+ return 0;
+}
+
+static ostream_t dummy = {
+ {
+ 1,
+ NULL,
+ NULL,
+ },
+ dummy_append,
+ NULL,
+ dummy_flush,
+ NULL,
+};
+
+int main(int argc, char **argv)
+{
+ size_t ref;
+ int ret;
+ (void)argc; (void)argv;
+
+ ref = 131072 + 1337;
+
+ ret = ostream_append_sparse(&dummy, ref);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = ostream_flush(&dummy);
+ TEST_EQUAL_I(ret, 0);
+
+ TEST_EQUAL_UI(ref, total);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am
index dfd88c9..3500aad 100644
--- a/lib/sqfs/Makemodule.am
+++ b/lib/sqfs/Makemodule.am
@@ -103,3 +103,26 @@ sqfsinclude_HEADERS = $(LIBSQFS_HEARDS)
lib_LTLIBRARIES += libsquashfs.la
pkgconfig_DATA += lib/sqfs/libsquashfs1.pc
+
+test_abi_SOURCES = lib/sqfs/test/abi.c
+test_abi_LDADD = libsquashfs.la libcompat.a
+
+test_table_SOURCES = lib/sqfs/test/table.c
+test_table_LDADD = libsquashfs.la libcompat.a
+
+test_xattr_writer_SOURCES = lib/sqfs/test/xattr_writer.c
+test_xattr_writer_LDADD = libsquashfs.la libcompat.a
+
+xattr_benchmark_SOURCES = lib/sqfs/test/xattr_benchmark.c
+xattr_benchmark_LDADD = libcommon.a libsquashfs.la libcompat.a
+
+test_get_node_path_SOURCES = lib/sqfs/test/get_node_path.c
+test_get_node_path_LDADD = libcommon.a libsquashfs.la libcompat.a
+
+LIBSQFS_TESTS = \
+ test_abi test_table test_xattr_writer test_get_node_path
+
+noinst_PROGRAMS += xattr_benchmark
+
+check_PROGRAMS += $(LIBSQFS_TESTS)
+TESTS += $(LIBSQFS_TESTS)
diff --git a/lib/sqfs/test/abi.c b/lib/sqfs/test/abi.c
new file mode 100644
index 0000000..0eaf5df
--- /dev/null
+++ b/lib/sqfs/test/abi.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * abi.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "sqfs/block_processor.h"
+#include "sqfs/compressor.h"
+#include "sqfs/block.h"
+#include "util/test.h"
+
+#include <stddef.h>
+
+static void test_compressor_opt_struct(void)
+{
+ sqfs_compressor_config_t cfg;
+
+ TEST_EQUAL_UI(sizeof(cfg.id), sizeof(sqfs_u16));
+ TEST_EQUAL_UI(sizeof(cfg.flags), sizeof(sqfs_u16));
+ TEST_EQUAL_UI(sizeof(cfg.block_size), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(cfg.level), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(cfg.opt), (2 * sizeof(sqfs_u64)));
+
+ TEST_EQUAL_UI(sizeof(cfg.opt.gzip), sizeof(cfg.opt));
+ TEST_EQUAL_UI(sizeof(cfg.opt.lzo), sizeof(cfg.opt));
+ TEST_EQUAL_UI(sizeof(cfg.opt.xz), sizeof(cfg.opt));
+ TEST_EQUAL_UI(sizeof(cfg.opt.padd0), sizeof(cfg.opt));
+
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, id), 0);
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, flags),
+ sizeof(sqfs_u16));
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, block_size),
+ sizeof(sqfs_u32));
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, level),
+ (2 * sizeof(sqfs_u32)));
+
+ if (__alignof__(sqfs_compressor_config_t) == __alignof__(sqfs_u32)) {
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, opt),
+ (3 * sizeof(sqfs_u32)));
+ } else if (__alignof__(sqfs_compressor_config_t) == __alignof__(sqfs_u64)) {
+ TEST_EQUAL_UI(offsetof(sqfs_compressor_config_t, opt),
+ (4 * sizeof(sqfs_u32)));
+ }
+}
+
+static const char *names[] = {
+ [SQFS_COMP_GZIP] = "gzip",
+ [SQFS_COMP_LZMA] = "lzma",
+ [SQFS_COMP_LZO] = "lzo",
+ [SQFS_COMP_XZ] = "xz",
+ [SQFS_COMP_LZ4] = "lz4",
+ [SQFS_COMP_ZSTD] = "zstd",
+};
+
+static void test_compressor_names(void)
+{
+ const char *str;
+ int i, id;
+
+ for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) {
+ str = sqfs_compressor_name_from_id(i);
+ TEST_STR_EQUAL(str, names[i]);
+
+ id = sqfs_compressor_id_from_name(str);
+ TEST_EQUAL_I(id, i);
+ }
+}
+
+static void test_blockproc_stats(void)
+{
+ sqfs_block_processor_stats_t stats;
+ size_t off;
+
+ TEST_EQUAL_UI(sizeof(stats.size), sizeof(size_t));
+ TEST_EQUAL_UI(sizeof(stats.input_bytes_read), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.output_bytes_generated), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.data_block_count), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.frag_block_count), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.sparse_block_count), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.total_frag_count), sizeof(sqfs_u64));
+ TEST_EQUAL_UI(sizeof(stats.actual_frag_count), sizeof(sqfs_u64));
+
+ if (__alignof__(stats) == __alignof__(sqfs_u32)) {
+ TEST_ASSERT(sizeof(stats) >=
+ (sizeof(sqfs_u32) + 7 * sizeof(sqfs_u64)));
+ } else if (__alignof__(stats) == __alignof__(sqfs_u64)) {
+ TEST_ASSERT(sizeof(stats) >= (8 * sizeof(sqfs_u64)));
+ }
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t, size), 0);
+
+ if (sizeof(size_t) < sizeof(sqfs_u64) &&
+ (__alignof__(sqfs_block_processor_stats_t) ==
+ __alignof__(sqfs_u64))) {
+ off = sizeof(sqfs_u64);
+ } else {
+ off = sizeof(stats.size);
+ }
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ input_bytes_read), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ output_bytes_generated), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ data_block_count), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ frag_block_count), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ sparse_block_count), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ total_frag_count), off);
+ off += sizeof(sqfs_u64);
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_stats_t,
+ actual_frag_count), off);
+}
+
+static void test_blockproc_desc(void)
+{
+ sqfs_block_processor_desc_t desc;
+
+ TEST_ASSERT(sizeof(desc) >= (4 * sizeof(sqfs_u32) +
+ 5 * sizeof(void *)));
+
+ TEST_EQUAL_UI(sizeof(desc.size), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(desc.max_block_size), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(desc.num_workers), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(desc.max_backlog), sizeof(sqfs_u32));
+ TEST_EQUAL_UI(sizeof(desc.cmp), sizeof(void *));
+ TEST_EQUAL_UI(sizeof(desc.wr), sizeof(void *));
+ TEST_EQUAL_UI(sizeof(desc.tbl), sizeof(void *));
+ TEST_EQUAL_UI(sizeof(desc.file), sizeof(void *));
+ TEST_EQUAL_UI(sizeof(desc.uncmp), sizeof(void *));
+
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, size), 0);
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, max_block_size),
+ sizeof(sqfs_u32));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, num_workers),
+ (2 * sizeof(sqfs_u32)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, max_backlog),
+ (3 * sizeof(sqfs_u32)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, cmp),
+ (4 * sizeof(sqfs_u32)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, wr),
+ (4 * sizeof(sqfs_u32) + sizeof(void *)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, tbl),
+ (4 * sizeof(sqfs_u32) + 2 * sizeof(void *)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, file),
+ (4 * sizeof(sqfs_u32) + 3 * sizeof(void *)));
+ TEST_EQUAL_UI(offsetof(sqfs_block_processor_desc_t, uncmp),
+ (4 * sizeof(sqfs_u32) + 4 * sizeof(void *)));
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+ test_compressor_opt_struct();
+ test_compressor_names();
+ test_blockproc_stats();
+ test_blockproc_desc();
+ return EXIT_SUCCESS;
+}
diff --git a/lib/sqfs/test/get_node_path.c b/lib/sqfs/test/get_node_path.c
new file mode 100644
index 0000000..c76cc1c
--- /dev/null
+++ b/lib/sqfs/test/get_node_path.c
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * get_node_path.c
+ *
+ * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+#include "util/test.h"
+
+#include "sqfs/dir_reader.h"
+#include "sqfs/error.h"
+
+int main(int argc, char **argv)
+{
+ sqfs_tree_node_t *n0, *n1, *n2;
+ char *str;
+ int ret;
+ (void)argc;
+ (void)argv;
+
+ n0 = calloc(1, sizeof(*n0) + 16);
+ TEST_NOT_NULL(n0);
+
+ n1 = calloc(1, sizeof(*n1) + 16);
+ TEST_NOT_NULL(n1);
+
+ n2 = calloc(1, sizeof(*n2) + 16);
+ TEST_NOT_NULL(n2);
+
+ /* no parent -> must return "/" */
+ ret = sqfs_tree_node_get_path(n0, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/");
+ sqfs_free(str);
+
+ /* hiearchy levels */
+ n1->parent = n0;
+ n0->children = n1;
+ strcpy((char *)n1->name, "bar");
+
+ n2->parent = n1;
+ n1->children = n2;
+ strcpy((char *)n2->name, "baz");
+
+ ret = sqfs_tree_node_get_path(n1, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar");
+ sqfs_free(str);
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar/baz");
+ sqfs_free(str);
+
+ /* root node must not have a name */
+ strcpy((char *)n0->name, "foo");
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_ARG_INVALID);
+ TEST_NULL(str);
+ n0->name[0] = '\0';
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar/baz");
+ sqfs_free(str);
+
+ /* non-root nodes must have names */
+ n1->name[0] = '\0';
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED);
+ TEST_NULL(str);
+ n1->name[0] = 'b';
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar/baz");
+ sqfs_free(str);
+
+ /* some names are illegal */
+ strcpy((char *)n1->name, "..");
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED);
+ TEST_NULL(str);
+
+ strcpy((char *)n1->name, ".");
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED);
+ TEST_NULL(str);
+
+ strcpy((char *)n1->name, "a/b");
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_CORRUPTED);
+ TEST_NULL(str);
+
+ strcpy((char *)n1->name, "bar");
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar/baz");
+ sqfs_free(str);
+
+ /* link loops must be detected */
+ n0->parent = n2;
+ strcpy((char *)n0->name, "foo");
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, SQFS_ERROR_LINK_LOOP);
+ TEST_NULL(str);
+
+ n0->parent = NULL;
+ n0->name[0] = '\0';
+
+ ret = sqfs_tree_node_get_path(n2, &str);
+ TEST_EQUAL_I(ret, 0);
+ TEST_NOT_NULL(str);
+ TEST_STR_EQUAL(str, "/bar/baz");
+ sqfs_free(str);
+
+ /* cleanup */
+ free(n0);
+ free(n1);
+ free(n2);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/sqfs/test/table.c b/lib/sqfs/test/table.c
new file mode 100644
index 0000000..3e44fa3
--- /dev/null
+++ b/lib/sqfs/test/table.c
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * table.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+#include "util/test.h"
+
+#include "sqfs/compressor.h"
+#include "sqfs/error.h"
+#include "sqfs/table.h"
+#include "sqfs/io.h"
+
+static sqfs_u8 file_data[32768];
+static size_t file_used = 0;
+
+static int dummy_read_at(sqfs_file_t *file, sqfs_u64 offset,
+ void *buffer, size_t size)
+{
+ (void)file;
+
+ if (offset >= sizeof(file_data))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (size > (sizeof(file_data) - offset))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ memset(buffer, 0, size);
+
+ if (offset < file_used) {
+ if (size > (file_used - offset))
+ size = file_used - offset;
+
+ memcpy(buffer, file_data + offset, size);
+ }
+ return 0;
+}
+
+static int dummy_write_at(sqfs_file_t *file, sqfs_u64 offset,
+ const void *buffer, size_t size)
+{
+ (void)file;
+
+ if (offset >= sizeof(file_data))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (size > (sizeof(file_data) - offset))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (offset > file_used)
+ memset(file_data + file_used, 0, offset - file_used);
+
+ if ((offset + size) > file_used)
+ file_used = offset + size;
+
+ memcpy(file_data + offset, buffer, size);
+ return 0;
+}
+
+static sqfs_u64 dummy_get_size(const sqfs_file_t *file)
+{
+ (void)file;
+ return file_used;
+}
+
+static sqfs_s32 dummy_compress(sqfs_compressor_t *cmp, const sqfs_u8 *in,
+ sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize)
+{
+ (void)cmp;
+ memcpy(out, in, outsize < size ? outsize : size);
+ return 0;
+}
+
+static sqfs_s32 dummy_uncompress(sqfs_compressor_t *cmp, const sqfs_u8 *in,
+ sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize)
+{
+ (void)cmp;
+ if (outsize < size)
+ return 0;
+ memcpy(out, in, size);
+ return size;
+}
+
+static sqfs_file_t dummy_file = {
+ { 1, NULL, NULL },
+ dummy_read_at,
+ dummy_write_at,
+ dummy_get_size,
+ NULL,
+};
+
+static sqfs_compressor_t dummy_compressor = {
+ { 1, NULL, NULL },
+ NULL,
+ NULL,
+ NULL,
+ dummy_compress,
+};
+
+static sqfs_compressor_t dummy_uncompressor = {
+ { 1, NULL, NULL },
+ NULL,
+ NULL,
+ NULL,
+ dummy_uncompress,
+};
+
+/*****************************************************************************/
+
+static sqfs_u64 table[4000];
+
+int main(int argc, char **argv)
+{
+ sqfs_u64 start, value, locations[4], *copy;
+ sqfs_u16 hdr;
+ size_t i;
+ int ret;
+ (void)argc; (void)argv;
+
+ /* fill the table with data */
+ for (i = 0; i < sizeof(table) / sizeof(table[0]); ++i)
+ table[i] = i;
+
+ /* serialize the table */
+ ret = sqfs_write_table(&dummy_file, &dummy_compressor,
+ table, sizeof(table), &start);
+ TEST_EQUAL_I(ret, 0);
+
+ TEST_EQUAL_UI(file_used, (3 * (8192 + 2) + (7424 + 2) + 4 * sizeof(sqfs_u64)));
+ TEST_EQUAL_UI(start, (3 * (8192 + 2) + (7424 + 2)));
+
+ /* check the location list */
+ memcpy(locations, file_data + start, sizeof(locations));
+ for (i = 0; i < 4; ++i)
+ locations[i] = le64toh(locations[i]);
+
+ TEST_EQUAL_UI(locations[0], 0);
+ TEST_EQUAL_UI(locations[1], (1 * (8192 + 2)));
+ TEST_EQUAL_UI(locations[2], (2 * (8192 + 2)));
+ TEST_EQUAL_UI(locations[3], (3 * (8192 + 2)));
+
+ /* check the individual blocks */
+ memcpy(&hdr, file_data + locations[0], sizeof(hdr));
+ hdr = le16toh(hdr);
+ TEST_EQUAL_UI(hdr, (0x8000 | 8192));
+
+ for (i = 0; i < 8192; i += sizeof(sqfs_u64)) {
+ memcpy(&value, (file_data + locations[0] + 2) + i,
+ sizeof(value));
+
+ TEST_EQUAL_UI(value, i / sizeof(sqfs_u64));
+ }
+
+ memcpy(&hdr, file_data + locations[1], sizeof(hdr));
+ hdr = le16toh(hdr);
+ TEST_EQUAL_UI(hdr, (0x8000 | 8192));
+
+ for (i = 0; i < 8192; i += sizeof(sqfs_u64)) {
+ memcpy(&value, (file_data + locations[1] + 2) + i,
+ sizeof(value));
+
+ TEST_EQUAL_UI(value, (1024 + i / sizeof(sqfs_u64)));
+ }
+
+ memcpy(&hdr, file_data + locations[2], sizeof(hdr));
+ hdr = le16toh(hdr);
+ TEST_EQUAL_UI(hdr, (0x8000 | 8192));
+
+ for (i = 0; i < 8192; i += sizeof(sqfs_u64)) {
+ memcpy(&value, (file_data + locations[2] + 2) + i,
+ sizeof(value));
+
+ TEST_EQUAL_UI(value, (2048 + i / sizeof(sqfs_u64)));
+ }
+
+ memcpy(&hdr, file_data + locations[3], sizeof(hdr));
+ hdr = le16toh(hdr);
+ TEST_EQUAL_UI(hdr, (0x8000 | 7424));
+
+ for (i = 0; i < 7424; i += sizeof(sqfs_u64)) {
+ memcpy(&value, (file_data + locations[3] + 2) + i,
+ sizeof(value));
+
+ TEST_EQUAL_UI(value, (3072 + i / sizeof(sqfs_u64)));
+ }
+
+ /* read the table back */
+ ret = sqfs_read_table(&dummy_file, &dummy_uncompressor,
+ sizeof(table), start, 0, start, (void **)&copy);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = memcmp(copy, table, sizeof(table));
+ TEST_EQUAL_I(ret, 0);
+
+ free(copy);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/sqfs/test/xattr_benchmark.c b/lib/sqfs/test/xattr_benchmark.c
new file mode 100644
index 0000000..072dd06
--- /dev/null
+++ b/lib/sqfs/test/xattr_benchmark.c
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xattr_benchmark.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+#include "common.h"
+
+#include "sqfs/xattr_writer.h"
+#include "sqfs/xattr.h"
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+
+static struct option long_opts[] = {
+ { "block-count", required_argument, NULL, 'b' },
+ { "groups-size", required_argument, NULL, 'g' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+static const char *short_opts = "g:b:hV";
+
+static const char *help_string =
+"Usage: xattr_benchmark [OPTIONS...]\n"
+"\n"
+"Possible options:\n"
+"\n"
+" --block-count, -b <count> How many unique xattr blocks to generate.\n"
+" --group-size, -g <count> Number of key-value pairs to generate for each\n"
+" xattr block.\n"
+"\n";
+
+int main(int argc, char **argv)
+{
+ long blkidx, grpidx, block_count = 0, group_size = 0;
+ sqfs_xattr_writer_t *xwr;
+ sqfs_u32 id;
+ int ret;
+
+ for (;;) {
+ int i = getopt_long(argc, argv, short_opts, long_opts, NULL);
+ if (i == -1)
+ break;
+
+ switch (i) {
+ case 'b':
+ block_count = strtol(optarg, NULL, 0);
+ break;
+ case 'g':
+ group_size = strtol(optarg, NULL, 0);
+ break;
+ case 'h':
+ fputs(help_string, stdout);
+ return EXIT_SUCCESS;
+ case 'V':
+ print_version("xattr_benchmark");
+ return EXIT_SUCCESS;
+ default:
+ goto fail_arg;
+ }
+ }
+
+ if (block_count <= 0) {
+ fputs("A block count > 0 must be specified.\n", stderr);
+ goto fail_arg;
+ }
+
+ if (group_size <= 0) {
+ fputs("A group size > 0 must be specified.\n", stderr);
+ goto fail_arg;
+ }
+
+ /* setup writer */
+ xwr = sqfs_xattr_writer_create(0);
+
+ /* generate blocks */
+ for (blkidx = 0; blkidx < block_count; ++blkidx) {
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ if (ret < 0) {
+ sqfs_perror(NULL, "begin xattr block", ret);
+ goto fail;
+ }
+
+ for (grpidx = 0; grpidx < group_size; ++grpidx) {
+ char key[64], value[64];
+
+ snprintf(key, sizeof(key), "user.group%ld.key%ld",
+ blkidx, grpidx);
+
+ snprintf(value, sizeof(value), "group%ld/value%ld",
+ blkidx, grpidx);
+
+ ret = sqfs_xattr_writer_add(xwr, key, value,
+ strlen(value));
+
+ if (ret < 0) {
+ sqfs_perror(NULL, "add to xattr block", ret);
+ goto fail;
+ }
+ }
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ if (ret < 0) {
+ sqfs_perror(NULL, "end xattr block", ret);
+ goto fail;
+ }
+ }
+
+ /* cleanup */
+ sqfs_drop(xwr);
+ return EXIT_SUCCESS;
+fail:
+ sqfs_drop(xwr);
+ return EXIT_FAILURE;
+fail_arg:
+ fputs("Try `xattr_benchmark --help' for more information.\n", stderr);
+ return EXIT_FAILURE;
+}
diff --git a/lib/sqfs/test/xattr_writer.c b/lib/sqfs/test/xattr_writer.c
new file mode 100644
index 0000000..f7d0734
--- /dev/null
+++ b/lib/sqfs/test/xattr_writer.c
@@ -0,0 +1,323 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xattr_writer.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+#include "util/test.h"
+
+#include "sqfs/xattr_writer.h"
+#include "sqfs/compressor.h"
+#include "sqfs/xattr.h"
+#include "sqfs/error.h"
+#include "sqfs/super.h"
+#include "sqfs/io.h"
+
+static sqfs_u8 file_data[1024];
+static size_t file_used = 0;
+
+static int dummy_write_at(sqfs_file_t *file, sqfs_u64 offset,
+ const void *buffer, size_t size)
+{
+ (void)file;
+
+ if (offset >= sizeof(file_data))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (size > (sizeof(file_data) - offset))
+ return SQFS_ERROR_OUT_OF_BOUNDS;
+
+ if (offset > file_used)
+ memset(file_data + file_used, 0, offset - file_used);
+
+ if ((offset + size) > file_used)
+ file_used = offset + size;
+
+ memcpy(file_data + offset, buffer, size);
+ return 0;
+}
+
+static sqfs_u64 dummy_get_size(const sqfs_file_t *file)
+{
+ (void)file;
+ return file_used;
+}
+
+static sqfs_s32 dummy_compress(sqfs_compressor_t *cmp, const sqfs_u8 *in,
+ sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize)
+{
+ (void)cmp;
+ memcpy(out, in, outsize < size ? outsize : size);
+ return 0;
+}
+
+static sqfs_file_t dummy_file = {
+ { 1, NULL, NULL },
+ NULL,
+ dummy_write_at,
+ dummy_get_size,
+ NULL,
+};
+
+static sqfs_compressor_t dummy_compressor = {
+ { 1, NULL, NULL },
+ NULL,
+ NULL,
+ NULL,
+ dummy_compress,
+};
+
+/*****************************************************************************/
+
+int main(int argc, char **argv)
+{
+ size_t offset, ool_value_offset, id_offset;
+ sqfs_xattr_id_table_t idtbl;
+ sqfs_xattr_writer_t *xwr;
+ sqfs_xattr_value_t value;
+ sqfs_xattr_entry_t key;
+ sqfs_xattr_id_t desc;
+ sqfs_super_t super;
+ char strbuf[32];
+ sqfs_u16 hdr;
+ sqfs_u64 ref;
+ sqfs_u32 id;
+ int ret;
+ (void)argc; (void)argv;
+
+ /* setup */
+ xwr = sqfs_xattr_writer_create(0);
+ TEST_NOT_NULL(xwr);
+
+ /* record a block of key/value pairs */
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "user.foobar", "test", 4);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ TEST_EQUAL_I(ret, 0);
+ TEST_EQUAL_UI(id, 0);
+
+ /* record a second, different block */
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "user.foobar", "bla", 3);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "security.selinux", "blub", 4);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ TEST_EQUAL_I(ret, 0);
+ TEST_EQUAL_UI(id, 1);
+
+ /* same as first block after sorting and gets the same ID */
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "user.foobar", "test", 4);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ TEST_EQUAL_I(ret, 0);
+ TEST_EQUAL_UI(id, 0);
+
+ /* the third assignment overwrites the first, making
+ the block identical to the second one */
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "user.foobar", "mimimi", 6);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "security.selinux", "blub", 4);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "user.foobar", "bla", 3);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ TEST_EQUAL_I(ret, 0);
+ TEST_EQUAL_UI(id, 1);
+
+ /* add another block with the same value, so it gets stored OOL */
+ ret = sqfs_xattr_writer_begin(xwr, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_add(xwr, "security.selinux", "Xwhatever", 9);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = sqfs_xattr_writer_end(xwr, &id);
+ TEST_EQUAL_I(ret, 0);
+ TEST_EQUAL_UI(id, 2);
+
+ /* serialize */
+ sqfs_super_init(&super, 131072, 0, SQFS_COMP_GZIP);
+ ret = sqfs_xattr_writer_flush(xwr, &dummy_file, &super,
+ &dummy_compressor);
+ TEST_EQUAL_I(ret, 0);
+
+ TEST_EQUAL_UI(file_used, 177);
+
+ /* meta data block holding the key-value-pairs */
+ memcpy(&hdr, file_data, sizeof(hdr));
+ hdr = le16toh(hdr);
+ TEST_EQUAL_UI(hdr, (0x8000 | 101));
+ offset = 2;
+
+ memcpy(&key, file_data + offset, sizeof(key));
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+ offset += sizeof(key);
+ TEST_EQUAL_UI(key.type, SQFS_XATTR_USER);
+ TEST_EQUAL_UI(key.size, 6);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, key.size);
+ TEST_STR_EQUAL(strbuf, "foobar");
+ offset += key.size;
+
+ memcpy(&value, file_data + offset, sizeof(value));
+ value.size = le32toh(value.size);
+ offset += sizeof(value);
+ TEST_EQUAL_UI(value.size, 4);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, value.size);
+ TEST_STR_EQUAL(strbuf, "test");
+ offset += value.size;
+
+ memcpy(&key, file_data + offset, sizeof(key));
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+ offset += sizeof(key);
+ TEST_EQUAL_UI(key.type, SQFS_XATTR_SECURITY);
+ TEST_EQUAL_UI(key.size, 7);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, key.size);
+ TEST_STR_EQUAL(strbuf, "selinux");
+ offset += key.size;
+
+ ool_value_offset = offset;
+
+ memcpy(&value, file_data + offset, sizeof(value));
+ value.size = le32toh(value.size);
+ offset += sizeof(value);
+ TEST_EQUAL_UI(value.size, 9);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, value.size);
+ TEST_STR_EQUAL(strbuf, "Xwhatever");
+ offset += value.size;
+
+ memcpy(&key, file_data + offset, sizeof(key));
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+ offset += sizeof(key);
+ TEST_EQUAL_UI(key.type, SQFS_XATTR_USER);
+ TEST_EQUAL_UI(key.size, 6);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, key.size);
+ TEST_STR_EQUAL(strbuf, "foobar");
+ offset += key.size;
+
+ memcpy(&value, file_data + offset, sizeof(value));
+ value.size = le32toh(value.size);
+ offset += sizeof(value);
+ TEST_EQUAL_UI(value.size, 3);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, value.size);
+ TEST_STR_EQUAL(strbuf, "bla");
+ offset += value.size;
+
+ memcpy(&key, file_data + offset, sizeof(key));
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+ offset += sizeof(key);
+ TEST_EQUAL_UI(key.type, SQFS_XATTR_SECURITY);
+ TEST_EQUAL_UI(key.size, 7);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, key.size);
+ TEST_STR_EQUAL(strbuf, "selinux");
+ offset += key.size;
+
+ memcpy(&value, file_data + offset, sizeof(value));
+ value.size = le32toh(value.size);
+ offset += sizeof(value);
+ TEST_EQUAL_UI(value.size, 4);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, value.size);
+ TEST_STR_EQUAL(strbuf, "blub");
+ offset += value.size;
+
+ memcpy(&key, file_data + offset, sizeof(key));
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+ offset += sizeof(key);
+ TEST_EQUAL_UI(key.type, (SQFS_XATTR_SECURITY | SQFS_XATTR_FLAG_OOL));
+ TEST_EQUAL_UI(key.size, 7);
+ memset(strbuf, '\0', sizeof(strbuf));
+ memcpy(strbuf, file_data + offset, key.size);
+ TEST_STR_EQUAL(strbuf, "selinux");
+ offset += key.size;
+
+ memcpy(&value, file_data + offset, sizeof(value));
+ value.size = le32toh(value.size);
+ offset += sizeof(value);
+ TEST_EQUAL_UI(value.size, 8);
+ memcpy(&ref, file_data + offset, sizeof(ref));
+ ref = le64toh(ref);
+ TEST_EQUAL_UI(ref, (ool_value_offset - 2));
+ offset += value.size;
+
+ /* meta data block holding the ID descriptions */
+ id_offset = offset;
+
+ memcpy(&hdr, file_data + offset, sizeof(hdr));
+ TEST_EQUAL_UI(le16toh(hdr), (0x8000 | (16 * 3)));
+ offset += sizeof(hdr);
+
+ memcpy(&desc, file_data + offset, sizeof(desc));
+ TEST_EQUAL_UI(le64toh(desc.xattr), 0);
+ TEST_EQUAL_UI(le32toh(desc.count), 2);
+ TEST_EQUAL_UI(le32toh(desc.size), 42);
+ offset += sizeof(desc);
+
+ memcpy(&desc, file_data + offset, sizeof(desc));
+ TEST_EQUAL_UI(le64toh(desc.xattr), 42);
+ TEST_EQUAL_UI(le32toh(desc.count), 2);
+ TEST_EQUAL_UI(le32toh(desc.size), 36);
+ offset += sizeof(desc);
+
+ memcpy(&desc, file_data + offset, sizeof(desc));
+ TEST_EQUAL_UI(le64toh(desc.xattr), 78);
+ TEST_EQUAL_UI(le32toh(desc.count), 1);
+ TEST_EQUAL_UI(le32toh(desc.size), 23);
+ offset += sizeof(desc);
+
+ /* the xattr table itself */
+ TEST_EQUAL_UI(super.xattr_id_table_start, offset);
+
+ memcpy(&idtbl, file_data + offset, sizeof(idtbl));
+ TEST_EQUAL_UI(le64toh(idtbl.xattr_table_start), 0);
+ TEST_EQUAL_UI(le32toh(idtbl.xattr_ids), 3);
+ offset += sizeof(idtbl);
+
+ memcpy(&ref, file_data + offset, sizeof(ref));
+ TEST_EQUAL_UI(le64toh(ref), id_offset);
+ offset += sizeof(ref);
+
+ TEST_EQUAL_UI(offset, file_used);
+
+ /* cleanup */
+ sqfs_drop(xwr);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/Makemodule.am b/lib/tar/Makemodule.am
index 1db02f1..896ce0f 100644
--- a/lib/tar/Makemodule.am
+++ b/lib/tar/Makemodule.am
@@ -6,3 +6,180 @@ libtar_a_SOURCES = lib/tar/src/read_header.c lib/tar/src/write_header.c \
include/tar/tar.h include/tar/format.h
noinst_LIBRARIES += libtar.a
+
+TARDATADIR=$(top_srcdir)/lib/tar/test/data
+
+test_tar_gnu0_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu0_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu0_CPPFLAGS += -DTESTFILE=format-acceptance/gnu.tar
+
+test_tar_gnu1_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu1_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu1_CPPFLAGS += -DTESTFILE=format-acceptance/gnu-g.tar
+
+test_tar_gnu2_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu2_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu2_CPPFLAGS += -DTESTFILE=user-group-largenum/gnu.tar
+test_tar_gnu2_CPPFLAGS += -DTESTUID=0x80000000 -DTESTGID=0x80000000
+test_tar_gnu2_CPPFLAGS += -DTESTTS=1542995392
+
+test_tar_gnu3_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu3_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu3_CPPFLAGS += -DTESTFILE=negative-mtime/gnu.tar -DTESTTS=-315622800
+
+test_tar_gnu4_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu4_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu4_CPPFLAGS += -DTESTFILE=long-paths/gnu.tar -DLONG_NAME_TEST
+test_tar_gnu4_CPPFLAGS += -DTESTTS=1542909670
+
+test_tar_gnu5_SOURCES = lib/tar/test/tar_simple.c
+test_tar_gnu5_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu5_CPPFLAGS += -DTESTFILE=large-mtime/gnu.tar -DTESTTS=8589934592L
+
+test_tar_gnu6_SOURCES = lib/tar/test/tar_big_file.c
+test_tar_gnu6_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_gnu6_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_gnu6_CPPFLAGS += -DTESTFILE=file-size/gnu.tar
+
+test_tar_pax0_SOURCES = lib/tar/test/tar_simple.c
+test_tar_pax0_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax0_CPPFLAGS += -DTESTFILE=format-acceptance/pax.tar
+
+test_tar_pax1_SOURCES = lib/tar/test/tar_simple.c
+test_tar_pax1_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax1_CPPFLAGS += -DTESTFILE=user-group-largenum/pax.tar
+test_tar_pax1_CPPFLAGS += -DTESTUID=2147483648UL -DTESTGID=2147483648UL
+test_tar_pax1_CPPFLAGS += -DTESTTS=1542995392
+
+test_tar_pax2_SOURCES = lib/tar/test/tar_simple.c
+test_tar_pax2_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax2_CPPFLAGS += -DTESTFILE=large-mtime/pax.tar -DTESTTS=8589934592L
+
+test_tar_pax3_SOURCES = lib/tar/test/tar_simple.c
+test_tar_pax3_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax3_CPPFLAGS += -DTESTFILE=negative-mtime/pax.tar -DTESTTS=-315622800
+
+test_tar_pax4_SOURCES = lib/tar/test/tar_simple.c
+test_tar_pax4_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax4_CPPFLAGS += -DTESTFILE=long-paths/pax.tar
+test_tar_pax4_CPPFLAGS += -DLONG_NAME_TEST -DTESTTS=1542909670
+
+test_tar_pax5_SOURCES = lib/tar/test/tar_big_file.c
+test_tar_pax5_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_pax5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_pax5_CPPFLAGS += -DTESTFILE=file-size/pax.tar
+
+test_tar_ustar0_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar0_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar0_CPPFLAGS += -DTESTFILE=format-acceptance/ustar.tar
+
+test_tar_ustar1_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar1_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar1_CPPFLAGS += -DTESTFILE=format-acceptance/ustar-pre-posix.tar
+
+test_tar_ustar2_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar2_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar2_CPPFLAGS += -DTESTFILE=format-acceptance/v7.tar
+
+test_tar_ustar3_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar3_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar3_CPPFLAGS += -DTESTFILE=user-group-largenum/8-digit.tar
+test_tar_ustar3_CPPFLAGS += -DTESTUID=8388608 -DTESTGID=8388608
+test_tar_ustar3_CPPFLAGS += -DTESTTS=1542995392
+
+test_tar_ustar4_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar4_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar4_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar4_CPPFLAGS += -DTESTFILE=large-mtime/12-digit.tar
+test_tar_ustar4_CPPFLAGS += -DTESTTS=8589934592L
+
+test_tar_ustar5_SOURCES = lib/tar/test/tar_simple.c
+test_tar_ustar5_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar5_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar5_CPPFLAGS += -DTESTFILE=long-paths/ustar.tar
+test_tar_ustar5_CPPFLAGS += -DLONG_NAME_TEST -DTESTTS=1542909670
+
+test_tar_ustar6_SOURCES = lib/tar/test/tar_big_file.c
+test_tar_ustar6_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_ustar6_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_ustar6_CPPFLAGS += -DTESTFILE=file-size/12-digit.tar
+
+test_tar_target_filled_SOURCES = lib/tar/test/tar_target_filled.c
+test_tar_target_filled_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_target_filled_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+
+test_tar_sparse_gnu_SOURCES = lib/tar/test/tar_sparse_gnu.c
+test_tar_sparse_gnu_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_sparse_gnu_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+
+test_tar_sparse_gnu0_SOURCES = lib/tar/test/tar_sparse.c
+test_tar_sparse_gnu0_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_sparse_gnu0_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_sparse_gnu0_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu0-0.tar
+
+test_tar_sparse_gnu1_SOURCES = lib/tar/test/tar_sparse.c
+test_tar_sparse_gnu1_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_sparse_gnu1_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_sparse_gnu1_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu0-1.tar
+
+test_tar_sparse_gnu2_SOURCES = lib/tar/test/tar_sparse.c
+test_tar_sparse_gnu2_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_sparse_gnu2_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_sparse_gnu2_CPPFLAGS += -DTESTFILE=sparse-files/pax-gnu1-0.tar
+
+test_tar_sparse_gnu3_SOURCES = lib/tar/test/tar_sparse.c
+test_tar_sparse_gnu3_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_sparse_gnu3_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_sparse_gnu3_CPPFLAGS += -DTESTFILE=sparse-files/gnu.tar
+
+test_tar_xattr_bsd_SOURCES = lib/tar/test/tar_xattr.c
+test_tar_xattr_bsd_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_xattr_bsd_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_xattr_bsd_CPPFLAGS += -DTESTFILE=xattr/xattr-libarchive.tar
+
+test_tar_xattr_schily_SOURCES = lib/tar/test/tar_xattr.c
+test_tar_xattr_schily_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_xattr_schily_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_xattr_schily_CPPFLAGS += -DTESTFILE=xattr/xattr-schily.tar
+
+test_tar_xattr_schily_bin_SOURCES = lib/tar/test/tar_xattr_bin.c
+test_tar_xattr_schily_bin_LDADD = libtar.a libio.a libutil.a libcompat.a
+test_tar_xattr_schily_bin_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(TARDATADIR)
+test_tar_xattr_schily_bin_CPPFLAGS += -DTESTFILE=xattr/xattr-schily-binary.tar
+
+tar_fuzz_SOURCES = lib/tar/test/tar_fuzz.c
+tar_fuzz_LDADD = libtar.a libio.a libutil.a libcompat.a
+
+LIBTAR_TESTS = \
+ test_tar_ustar0 test_tar_ustar1 test_tar_ustar2 test_tar_ustar3 \
+ test_tar_ustar4 test_tar_ustar5 test_tar_ustar6 \
+ test_tar_pax0 test_tar_pax1 test_tar_pax2 test_tar_pax3 test_tar_pax4 \
+ test_tar_pax5 \
+ test_tar_gnu0 test_tar_gnu1 test_tar_gnu2 test_tar_gnu3 test_tar_gnu4 \
+ test_tar_gnu5 test_tar_gnu6 \
+ test_tar_sparse_gnu test_tar_sparse_gnu0 test_tar_sparse_gnu1 \
+ test_tar_sparse_gnu2 test_tar_sparse_gnu3 \
+ test_tar_xattr_bsd test_tar_xattr_schily test_tar_xattr_schily_bin \
+ test_tar_target_filled
+
+check_PROGRAMS += $(LIBTAR_TESTS)
+TESTS += $(LIBTAR_TESTS)
+
+noinst_PROGRAMS += tar_fuzz
+
+EXTRA_DIST += $(TARDATADIR)
diff --git a/lib/tar/test/data/CREDITS b/lib/tar/test/data/CREDITS
new file mode 100644
index 0000000..7a2738f
--- /dev/null
+++ b/lib/tar/test/data/CREDITS
@@ -0,0 +1,35 @@
+The tar archives in this directory have been obtained from here:
+
+ https://github.com/mgorny/tar-test-inputs
+
+ git commit hash a2110a6
+
+This repository was linked in the following article on interoperability of
+various different tar programs:
+
+ https://dev.gentoo.org/~mgorny/articles/portability-of-tar-features.html
+
+The original intention of the example archives was to test various tar programs
+for interoperability with each others extensions and format quirks.
+
+The following have been removed since there is no intention in adding support
+for those features:
+
+ - volume-label tests
+ - multi-volume tests
+ - longe user + group names
+ - sun tar samples
+ - star samples
+ - file flags tests
+
+In addition to that, the files in "file-size" are truncated, since we are only
+interested in parsing the header.
+
+The following addtional files have been added:
+ - xattr/xattr-shily-binary.tar
+ Created from xattr/xattr-shily.tar by manually patching in a capability
+ xattr key/value pair.
+ - tar/format-acceptance/link_filled.tar
+ Contributed in GitHub issue #64. A tar ball that contains a hard link
+ where the 100 byte target field is completely filled without containing
+ a null-terminator.
diff --git a/lib/tar/test/data/file-size/12-digit.tar b/lib/tar/test/data/file-size/12-digit.tar
new file mode 100644
index 0000000..a6b3553
--- /dev/null
+++ b/lib/tar/test/data/file-size/12-digit.tar
Binary files differ
diff --git a/lib/tar/test/data/file-size/gnu.tar b/lib/tar/test/data/file-size/gnu.tar
new file mode 100644
index 0000000..aad726f
--- /dev/null
+++ b/lib/tar/test/data/file-size/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/file-size/pax.tar b/lib/tar/test/data/file-size/pax.tar
new file mode 100644
index 0000000..e391fee
--- /dev/null
+++ b/lib/tar/test/data/file-size/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/gnu-g.tar b/lib/tar/test/data/format-acceptance/gnu-g.tar
new file mode 100644
index 0000000..a72f60d
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/gnu-g.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/gnu.tar b/lib/tar/test/data/format-acceptance/gnu.tar
new file mode 100644
index 0000000..98e8157
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/link_filled.tar b/lib/tar/test/data/format-acceptance/link_filled.tar
new file mode 100644
index 0000000..9b80f89
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/link_filled.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/pax.tar b/lib/tar/test/data/format-acceptance/pax.tar
new file mode 100644
index 0000000..69a029f
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/ustar-pre-posix.tar b/lib/tar/test/data/format-acceptance/ustar-pre-posix.tar
new file mode 100644
index 0000000..4230f5f
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/ustar-pre-posix.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/ustar.tar b/lib/tar/test/data/format-acceptance/ustar.tar
new file mode 100644
index 0000000..9a6d9b3
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/ustar.tar
Binary files differ
diff --git a/lib/tar/test/data/format-acceptance/v7.tar b/lib/tar/test/data/format-acceptance/v7.tar
new file mode 100644
index 0000000..5483b4b
--- /dev/null
+++ b/lib/tar/test/data/format-acceptance/v7.tar
Binary files differ
diff --git a/lib/tar/test/data/large-mtime/12-digit.tar b/lib/tar/test/data/large-mtime/12-digit.tar
new file mode 100644
index 0000000..8202b28
--- /dev/null
+++ b/lib/tar/test/data/large-mtime/12-digit.tar
Binary files differ
diff --git a/lib/tar/test/data/large-mtime/gnu.tar b/lib/tar/test/data/large-mtime/gnu.tar
new file mode 100644
index 0000000..4e3dda2
--- /dev/null
+++ b/lib/tar/test/data/large-mtime/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/large-mtime/pax.tar b/lib/tar/test/data/large-mtime/pax.tar
new file mode 100644
index 0000000..8d32530
--- /dev/null
+++ b/lib/tar/test/data/large-mtime/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/long-paths/gnu.tar b/lib/tar/test/data/long-paths/gnu.tar
new file mode 100644
index 0000000..4eee731
--- /dev/null
+++ b/lib/tar/test/data/long-paths/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/long-paths/pax.tar b/lib/tar/test/data/long-paths/pax.tar
new file mode 100644
index 0000000..1e63e9c
--- /dev/null
+++ b/lib/tar/test/data/long-paths/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/long-paths/ustar.tar b/lib/tar/test/data/long-paths/ustar.tar
new file mode 100644
index 0000000..812255c
--- /dev/null
+++ b/lib/tar/test/data/long-paths/ustar.tar
Binary files differ
diff --git a/lib/tar/test/data/negative-mtime/gnu.tar b/lib/tar/test/data/negative-mtime/gnu.tar
new file mode 100644
index 0000000..a9b1272
--- /dev/null
+++ b/lib/tar/test/data/negative-mtime/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/negative-mtime/pax.tar b/lib/tar/test/data/negative-mtime/pax.tar
new file mode 100644
index 0000000..d0b3e6c
--- /dev/null
+++ b/lib/tar/test/data/negative-mtime/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/sparse-files/gnu-small.tar b/lib/tar/test/data/sparse-files/gnu-small.tar
new file mode 100644
index 0000000..7a1b222
--- /dev/null
+++ b/lib/tar/test/data/sparse-files/gnu-small.tar
Binary files differ
diff --git a/lib/tar/test/data/sparse-files/gnu.tar b/lib/tar/test/data/sparse-files/gnu.tar
new file mode 100644
index 0000000..3d641a2
--- /dev/null
+++ b/lib/tar/test/data/sparse-files/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/sparse-files/pax-gnu0-0.tar b/lib/tar/test/data/sparse-files/pax-gnu0-0.tar
new file mode 100644
index 0000000..628f09e
--- /dev/null
+++ b/lib/tar/test/data/sparse-files/pax-gnu0-0.tar
Binary files differ
diff --git a/lib/tar/test/data/sparse-files/pax-gnu0-1.tar b/lib/tar/test/data/sparse-files/pax-gnu0-1.tar
new file mode 100644
index 0000000..87e362e
--- /dev/null
+++ b/lib/tar/test/data/sparse-files/pax-gnu0-1.tar
Binary files differ
diff --git a/lib/tar/test/data/sparse-files/pax-gnu1-0.tar b/lib/tar/test/data/sparse-files/pax-gnu1-0.tar
new file mode 100644
index 0000000..edb0ccf
--- /dev/null
+++ b/lib/tar/test/data/sparse-files/pax-gnu1-0.tar
Binary files differ
diff --git a/lib/tar/test/data/user-group-largenum/8-digit.tar b/lib/tar/test/data/user-group-largenum/8-digit.tar
new file mode 100644
index 0000000..a48ea5d
--- /dev/null
+++ b/lib/tar/test/data/user-group-largenum/8-digit.tar
Binary files differ
diff --git a/lib/tar/test/data/user-group-largenum/gnu.tar b/lib/tar/test/data/user-group-largenum/gnu.tar
new file mode 100644
index 0000000..4c30f57
--- /dev/null
+++ b/lib/tar/test/data/user-group-largenum/gnu.tar
Binary files differ
diff --git a/lib/tar/test/data/user-group-largenum/pax.tar b/lib/tar/test/data/user-group-largenum/pax.tar
new file mode 100644
index 0000000..a9fe54c
--- /dev/null
+++ b/lib/tar/test/data/user-group-largenum/pax.tar
Binary files differ
diff --git a/lib/tar/test/data/xattr/acl.tar b/lib/tar/test/data/xattr/acl.tar
new file mode 100644
index 0000000..a65c0af
--- /dev/null
+++ b/lib/tar/test/data/xattr/acl.tar
Binary files differ
diff --git a/lib/tar/test/data/xattr/xattr-libarchive.tar b/lib/tar/test/data/xattr/xattr-libarchive.tar
new file mode 100644
index 0000000..3bd6125
--- /dev/null
+++ b/lib/tar/test/data/xattr/xattr-libarchive.tar
Binary files differ
diff --git a/lib/tar/test/data/xattr/xattr-schily-binary.tar b/lib/tar/test/data/xattr/xattr-schily-binary.tar
new file mode 100644
index 0000000..0312807
--- /dev/null
+++ b/lib/tar/test/data/xattr/xattr-schily-binary.tar
Binary files differ
diff --git a/lib/tar/test/data/xattr/xattr-schily.tar b/lib/tar/test/data/xattr/xattr-schily.tar
new file mode 100644
index 0000000..1cf525c
--- /dev/null
+++ b/lib/tar/test/data/xattr/xattr-schily.tar
Binary files differ
diff --git a/lib/tar/test/tar_big_file.c b/lib/tar/test/tar_big_file.c
new file mode 100644
index 0000000..deb41f4
--- /dev/null
+++ b/lib/tar/test/tar_big_file.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_big_file.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "tar/tar.h"
+#include "io/file.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE));
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, 01750);
+ TEST_EQUAL_UI(hdr.gid, 01750);
+ TEST_EQUAL_UI(hdr.actual_size, 8589934592);
+ TEST_EQUAL_UI(hdr.mtime, 1542959190);
+ TEST_STR_EQUAL(hdr.name, "big-file.bin");
+ TEST_ASSERT(!hdr.unknown_record);
+ clear_header(&hdr);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_fuzz.c b/lib/tar/test/tar_fuzz.c
new file mode 100644
index 0000000..21e6978
--- /dev/null
+++ b/lib/tar/test/tar_fuzz.c
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_fuzz.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "io/file.h"
+#include "tar/tar.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ istream_t *fp;
+ int ret;
+
+ if (argc != 2) {
+ fputs("usage: tar_fuzz <tarball>\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ fp = istream_open_file(argv[1]);
+ if (fp == NULL)
+ return EXIT_FAILURE;
+
+ for (;;) {
+ ret = read_header(fp, &hdr);
+ if (ret > 0)
+ break;
+ if (ret < 0)
+ goto fail;
+
+ ret = istream_skip(fp, hdr.record_size);
+
+ clear_header(&hdr);
+ if (ret < 0)
+ goto fail;
+ }
+
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+fail:
+ sqfs_drop(fp);
+ return EXIT_FAILURE;
+}
diff --git a/lib/tar/test/tar_simple.c b/lib/tar/test/tar_simple.c
new file mode 100644
index 0000000..cb38abb
--- /dev/null
+++ b/lib/tar/test/tar_simple.c
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_simple.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+#ifndef TESTUID
+#define TESTUID 1000
+#endif
+
+#ifndef TESTGID
+#define TESTGID TESTUID
+#endif
+
+#ifndef TESTFNAME
+#define TESTFNAME input.txt
+#endif
+
+#ifndef TESTTS
+#define TESTTS 1542905892
+#endif
+
+#ifdef LONG_NAME_TEST
+static const char *fname =
+"012345678901234567890123456789/012345678901234567890123456789/"
+"012345678901234567890123456789/012345678901234567890123456789/"
+"012345678901234567890123456789/input.txt";
+#else
+static const char *fname = STRVALUE(TESTFNAME);
+#endif
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ char buffer[6];
+ sqfs_s64 ts;
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE));
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, TESTUID);
+ TEST_EQUAL_UI(hdr.gid, TESTGID);
+ TEST_EQUAL_UI(hdr.actual_size, 5);
+
+ ts = TESTTS;
+ TEST_EQUAL_UI(hdr.mtime, ts);
+ TEST_STR_EQUAL(hdr.name, fname);
+ TEST_ASSERT(!hdr.unknown_record);
+
+ TEST_ASSERT(istream_read(fp, buffer, 5) == 5);
+ buffer[5] = '\0';
+ TEST_STR_EQUAL(buffer, "test\n");
+ clear_header(&hdr);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_sparse.c b/lib/tar/test/tar_sparse.c
new file mode 100644
index 0000000..27ce053
--- /dev/null
+++ b/lib/tar/test/tar_sparse.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_sparse.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+static void test_case_sparse(const char *path)
+{
+ tar_header_decoded_t hdr;
+ sparse_map_t *sparse;
+ istream_t *fp;
+
+ fp = istream_open_file(path);
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, 01750);
+ TEST_EQUAL_UI(hdr.gid, 01750);
+ TEST_EQUAL_UI(hdr.actual_size, 2097152);
+ TEST_EQUAL_UI(hdr.record_size, 32768);
+ TEST_STR_EQUAL(hdr.name, "input.bin");
+ TEST_ASSERT(!hdr.unknown_record);
+
+ sparse = hdr.sparse;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 0);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 262144);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 524288);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 786432);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 1048576);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 1310720);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 1572864);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 1835008);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 2097152);
+ TEST_EQUAL_UI(sparse->count, 0);
+
+ sparse = sparse->next;
+ TEST_NULL(sparse);
+
+ clear_header(&hdr);
+ sqfs_drop(fp);
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc; (void)argv;
+ test_case_sparse( STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE) );
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_sparse_gnu.c b/lib/tar/test/tar_sparse_gnu.c
new file mode 100644
index 0000000..19ddd0a
--- /dev/null
+++ b/lib/tar/test/tar_sparse_gnu.c
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_sparse_gnu.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ sparse_map_t *sparse;
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ TEST_ASSERT(chdir(TEST_PATH) == 0);
+
+ fp = istream_open_file("sparse-files/gnu-small.tar");
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, 01750);
+ TEST_EQUAL_UI(hdr.gid, 01750);
+ TEST_EQUAL_UI(hdr.actual_size, 524288);
+ TEST_EQUAL_UI(hdr.record_size, 8192);
+ TEST_STR_EQUAL(hdr.name, "input.bin");
+ TEST_ASSERT(!hdr.unknown_record);
+
+ sparse = hdr.sparse;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 0);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 262144);
+ TEST_EQUAL_UI(sparse->count, 4096);
+
+ sparse = sparse->next;
+ TEST_NOT_NULL(sparse);
+ TEST_EQUAL_UI(sparse->offset, 524288);
+ TEST_EQUAL_UI(sparse->count, 0);
+
+ TEST_NULL(sparse->next);
+
+ clear_header(&hdr);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_target_filled.c b/lib/tar/test/tar_target_filled.c
new file mode 100644
index 0000000..abc6a47
--- /dev/null
+++ b/lib/tar/test/tar_target_filled.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_target_filled.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ char buffer[16];
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ TEST_ASSERT(chdir(TEST_PATH) == 0);
+
+ fp = istream_open_file("format-acceptance/link_filled.tar");
+ TEST_NOT_NULL(fp);
+
+ /* "deep" directory hierarchy containg 2 files */
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/"
+ "20_characters_here03/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/"
+ "20_characters_here03/20_characters_here04/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/"
+ "20_characters_here03/20_characters_here04/"
+ "errored_file_tst");
+ TEST_EQUAL_UI(hdr.actual_size, 5);
+ TEST_ASSERT(istream_read(fp, buffer, 5) == 5);
+ buffer[5] = '\0';
+ TEST_STR_EQUAL(buffer, "test\n");
+ TEST_ASSERT(skip_padding(fp, 5) == 0);
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0777);
+ TEST_STR_EQUAL(hdr.name, "20_characters_here01/20_characters_here02/"
+ "20_characters_here03/20_characters_here04/"
+ "some_test_file");
+ TEST_EQUAL_UI(hdr.actual_size, 5);
+ TEST_ASSERT(istream_read(fp, buffer, 5) == 5);
+ buffer[5] = '\0';
+ TEST_STR_EQUAL(buffer, "test\n");
+ TEST_ASSERT(skip_padding(fp, 5) == 0);
+ clear_header(&hdr);
+
+ /* "deep" directory hierarchy containg a hard link */
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/"
+ "20CharsForLnkTest003/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFDIR | 0777);
+ TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/"
+ "20CharsForLnkTest003/20CharsForLnkTest004/");
+ clear_header(&hdr);
+
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_STR_EQUAL(hdr.name, "20CharsForLnkTest001/20CharsForLnkTest002/"
+ "20CharsForLnkTest003/20CharsForLnkTest004/"
+ "01234567890123456789");
+ TEST_ASSERT(hdr.is_hard_link);
+
+ TEST_STR_EQUAL(hdr.link_target, "20_characters_here01/"
+ "20_characters_here02/20_characters_here03/"
+ "20_characters_here04/errored_file_tst");
+ clear_header(&hdr);
+
+ /* end of file */
+ TEST_ASSERT(read_header(fp, &hdr) > 0);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_xattr.c b/lib/tar/test/tar_xattr.c
new file mode 100644
index 0000000..122d1db
--- /dev/null
+++ b/lib/tar/test/tar_xattr.c
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_xattr.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ char buffer[6];
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE));
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, 01750);
+ TEST_EQUAL_UI(hdr.gid, 01750);
+ TEST_EQUAL_UI(hdr.actual_size, 5);
+ TEST_EQUAL_UI(hdr.mtime, 1543094477);
+ TEST_STR_EQUAL(hdr.name, "input.txt");
+ TEST_ASSERT(!hdr.unknown_record);
+ TEST_ASSERT(istream_read(fp, buffer, 5) == 5);
+ buffer[5] = '\0';
+ TEST_STR_EQUAL(buffer, "test\n");
+
+ TEST_NOT_NULL(hdr.xattr);
+ TEST_STR_EQUAL(hdr.xattr->key, "user.mime_type");
+ TEST_STR_EQUAL((const char *)hdr.xattr->value, "text/plain");
+ TEST_EQUAL_UI(hdr.xattr->value_len, 10);
+ TEST_NULL(hdr.xattr->next);
+
+ clear_header(&hdr);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/tar/test/tar_xattr_bin.c b/lib/tar/test/tar_xattr_bin.c
new file mode 100644
index 0000000..90443a1
--- /dev/null
+++ b/lib/tar/test/tar_xattr_bin.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * tar_xattr_bin.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "io/file.h"
+#include "tar/tar.h"
+#include "util/test.h"
+
+static const uint8_t value[] = {
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+int main(int argc, char **argv)
+{
+ tar_header_decoded_t hdr;
+ char buffer[6];
+ istream_t *fp;
+ (void)argc; (void)argv;
+
+ fp = istream_open_file(STRVALUE(TESTPATH) "/" STRVALUE(TESTFILE));
+ TEST_NOT_NULL(fp);
+ TEST_ASSERT(read_header(fp, &hdr) == 0);
+ TEST_EQUAL_UI(hdr.mode, S_IFREG | 0644);
+ TEST_EQUAL_UI(hdr.uid, 01750);
+ TEST_EQUAL_UI(hdr.gid, 01750);
+ TEST_EQUAL_UI(hdr.actual_size, 5);
+ TEST_EQUAL_UI(hdr.mtime, 1543094477);
+ TEST_STR_EQUAL(hdr.name, "input.txt");
+ TEST_ASSERT(!hdr.unknown_record);
+ TEST_ASSERT(istream_read(fp, buffer, 5) == 5);
+ buffer[5] = '\0';
+ TEST_STR_EQUAL(buffer, "test\n");
+
+ TEST_NOT_NULL(hdr.xattr);
+ TEST_STR_EQUAL(hdr.xattr->key, "security.capability");
+ TEST_EQUAL_UI(hdr.xattr->value_len, sizeof(value));
+ TEST_ASSERT(memcmp(hdr.xattr->value, value, sizeof(value)) == 0);
+ TEST_NULL(hdr.xattr->next);
+
+ clear_header(&hdr);
+ sqfs_drop(fp);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am
index 35e8078..0a0e50e 100644
--- a/lib/util/Makemodule.am
+++ b/lib/util/Makemodule.am
@@ -32,3 +32,55 @@ libutil_a_SOURCES += lib/util/src/mempool.c
endif
noinst_LIBRARIES += libutil.a
+
+test_str_table_SOURCES = lib/util/test/str_table.c
+test_str_table_LDADD = libutil.a libio.a libcompat.a
+test_str_table_CPPFLAGS = $(AM_CPPFLAGS) -DTESTPATH=$(top_srcdir)/lib/util/test
+
+test_rbtree_SOURCES = lib/util/test/rbtree.c
+test_rbtree_LDADD = libutil.a libcompat.a
+
+test_xxhash_SOURCES = lib/util/test/xxhash.c
+test_xxhash_LDADD = libutil.a libcompat.a
+
+test_threadpool_SOURCES = lib/util/test/threadpool.c
+test_threadpool_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS)
+test_threadpool_CPPFLAGS = $(AM_CPPFLAGS)
+test_threadpool_LDADD = libutil.a libcompat.a $(PTHREAD_LIBS)
+
+if HAVE_PTHREAD
+test_threadpool_CPPFLAGS += -DHAVE_PTHREAD
+endif
+
+test_ismemzero_SOURCES = lib/util/test/is_memory_zero.c
+test_ismemzero_LDADD = libutil.a libcompat.a
+
+test_canonicalize_name_SOURCES = lib/util/test/canonicalize_name.c
+test_canonicalize_name_LDADD = libutil.a libcompat.a
+
+test_filename_sane_SOURCES = lib/util/test/filename_sane.c
+test_filename_sane_SOURCES += lib/util/src/filename_sane.c
+test_filename_sane_LDADD = libcompat.a libutil.a
+
+test_filename_sane_w32_SOURCES = lib/util/test/filename_sane.c
+test_filename_sane_w32_SOURCES += lib/util/src/filename_sane.c
+test_filename_sane_w32_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_WIN32=1
+test_filename_sane_w32_LDADD = libcompat.a
+
+test_sdate_epoch_SOURCES = lib/util/test/epoch.c
+test_sdate_epoch_LDADD = libutil.a libcompat.a
+
+test_hex_decode_SOURCES = lib/util/test/hex_decode.c
+test_hex_decode_LDADD = libutil.a libcompat.a
+
+test_base64_decode_SOURCES = lib/util/test/base64_decode.c
+test_base64_decode_LDADD = libutil.a libcompat.a
+
+LIBUTIL_TESTS = \
+ test_str_table test_rbtree test_xxhash test_threadpool test_ismemzero \
+ test_canonicalize_name test_filename_sane test_filename_sane_w32 \
+ test_sdate_epoch test_hex_decode test_base64_decode
+
+check_PROGRAMS += $(LIBUTIL_TESTS)
+TESTS += $(LIBUTIL_TESTS)
+EXTRA_DIST += $(top_srcdir)/lib/util/test/words.txt
diff --git a/lib/util/test/base64_decode.c b/lib/util/test/base64_decode.c
new file mode 100644
index 0000000..8f22a86
--- /dev/null
+++ b/lib/util/test/base64_decode.c
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * base64_decode.c
+ *
+ * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "util/util.h"
+#include "util/test.h"
+
+static const struct {
+ int result;
+ const char *in;
+ const char *out;
+} test_vec[] = {
+ { 0, "", "" },
+ { 0, "Zg", "f" },
+ { 0, "Zg==", "f" },
+ { 0, "Zm8=", "fo" },
+ { 0, "Zm9v", "foo" },
+ { 0, "Zm9vYg==", "foob" },
+ { 0, "Zm9vYmE=", "fooba" },
+ { 0, "Zm9vYmFy", "foobar" },
+ { 0, "TGV0J3MgYWxsIGxvdmUgTGFpbiEK", "Let's all love Lain!\n" },
+ { -1, "Zg==X", "XX" },
+};
+
+int main(int argc, char **argv)
+{
+ sqfs_u8 buffer[256];
+ size_t i, j;
+ (void)argc; (void)argv;
+
+ for (i = 0; i < sizeof(test_vec) / sizeof(test_vec[0]); ++i) {
+ const size_t in_len = strlen(test_vec[i].in);
+ const size_t out_len = strlen(test_vec[i].out);
+ size_t real_out;
+ int ret;
+
+ /* initialize the buffer */
+ for (j = 0; j < sizeof(buffer); ++j) {
+ buffer[j] = (j % 2) ? 0xAA : 0x55;
+ }
+
+ /* convert */
+ real_out = sizeof(buffer);
+ ret = base64_decode(test_vec[i].in, in_len, buffer, &real_out);
+
+ /* make sure pattern is un-touched after expected offset */
+ j = (in_len / 4) * 3;
+ if (in_len % 4)
+ j += 3;
+
+ for (; j < sizeof(buffer); ++j) {
+ TEST_ASSERT(buffer[j] == ((j % 2) ? 0xAA : 0x55));
+ }
+
+ /* check result */
+ if (test_vec[i].result == 0) {
+ TEST_ASSERT(ret == 0);
+ TEST_EQUAL_UI(real_out, out_len);
+ ret = memcmp(buffer, test_vec[i].out, out_len);
+ TEST_ASSERT(ret == 0);
+ } else {
+ TEST_ASSERT(ret != 0);
+ TEST_EQUAL_UI(real_out, 0);
+ }
+
+ fprintf(stderr, "CASE %lu OK\n", (unsigned long)i);
+ }
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/lib/util/test/canonicalize_name.c b/lib/util/test/canonicalize_name.c
new file mode 100644
index 0000000..9f81b04
--- /dev/null
+++ b/lib/util/test/canonicalize_name.c
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * canonicalize_name.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "util/util.h"
+#include "util/test.h"
+
+static const struct {
+ const char *in;
+ const char *out;
+} must_work[] = {
+ { "", "" },
+ { "/", "" },
+ { "\\", "\\" },
+ { "///", "" },
+ { "\\\\\\", "\\\\\\" },
+ { "/\\//\\\\/", "\\/\\\\" },
+ { "foo/bar/test", "foo/bar/test" },
+ { "foo\\bar\\test", "foo\\bar\\test" },
+ { "/foo/bar/test/", "foo/bar/test" },
+ { "\\foo\\bar\\test\\", "\\foo\\bar\\test\\" },
+ { "///foo//bar//test///", "foo/bar/test" },
+ { "./foo/././bar/test/./.", "foo/bar/test" },
+ { "./foo/././", "foo" },
+ { ".", "" },
+ { "./", "" },
+ { "./.", "" },
+ { "foo/.../bar", "foo/.../bar" },
+ { "foo/.test/bar", "foo/.test/bar" },
+};
+
+static const char *must_not_work[] = {
+ "..",
+ "foo/../bar",
+ "../foo/bar",
+ "foo/bar/..",
+ "foo/bar/../",
+};
+
+int main(int argc, char **argv)
+{
+ char buffer[512];
+ size_t i;
+ (void)argc; (void)argv;
+
+ for (i = 0; i < sizeof(must_work) / sizeof(must_work[0]); ++i) {
+ strcpy(buffer, must_work[i].in);
+
+ if (canonicalize_name(buffer)) {
+ fprintf(stderr, "Test case rejected: '%s'\n",
+ must_work[i].in);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(buffer, must_work[i].out) != 0) {
+ fprintf(stderr, "Expected result: %s\n",
+ must_work[i].out);
+ fprintf(stderr, "Actual result: %s\n", buffer);
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = 0; i < sizeof(must_not_work) / sizeof(must_not_work[0]); ++i) {
+ strcpy(buffer, must_not_work[i]);
+
+ if (canonicalize_name(buffer) == 0) {
+ fprintf(stderr, "Test case accepted: '%s'\n",
+ must_not_work[i]);
+ fprintf(stderr, "Transformed into: '%s'\n", buffer);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/epoch.c b/lib/util/test/epoch.c
new file mode 100644
index 0000000..a04942e
--- /dev/null
+++ b/lib/util/test/epoch.c
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * epoch.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "util/util.h"
+#include "util/test.h"
+
+#if defined(_WIN32) || defined(__WINDOWS__)
+static void setenv(const char *key, const char *value, int overwrite)
+{
+ char buffer[128];
+ (void)overwrite;
+
+ snprintf(buffer, sizeof(buffer) - 1, "%s=%s", key, value);
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ _putenv(buffer);
+}
+
+static void unsetenv(const char *key)
+{
+ setenv(key, "", 0);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ sqfs_u32 ts;
+ (void)argc; (void)argv;
+
+ unsetenv("SOURCE_DATE_EPOCH");
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0);
+
+ setenv("SOURCE_DATE_EPOCH", "1337", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 1337);
+
+ setenv("SOURCE_DATE_EPOCH", "0xCAFE", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0);
+
+ setenv("SOURCE_DATE_EPOCH", "foobar", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0);
+
+ setenv("SOURCE_DATE_EPOCH", "-12", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0);
+
+ setenv("SOURCE_DATE_EPOCH", "12", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 12);
+
+ setenv("SOURCE_DATE_EPOCH", "4294967295", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0xFFFFFFFF);
+
+ setenv("SOURCE_DATE_EPOCH", "4294967296", 1);
+ ts = get_source_date_epoch();
+ TEST_EQUAL_UI(ts, 0);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/filename_sane.c b/lib/util/test/filename_sane.c
new file mode 100644
index 0000000..9c9930d
--- /dev/null
+++ b/lib/util/test/filename_sane.c
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * filename_sane.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "util/util.h"
+#include "util/test.h"
+
+static const char *must_work[] = {
+ "foobar",
+ "test.txt",
+#if !defined(_WIN32) && !defined(__WINDOWS__) && !defined(TEST_WIN32)
+ "\\foo", "foo\\", "foo\\bar",
+#endif
+ NULL,
+};
+
+static const char *must_not_work[] = {
+ ".",
+ "..",
+ "/foo",
+ "foo/",
+ "foo/bar",
+ NULL,
+};
+
+static const char *must_not_work_here[] = {
+#if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32)
+ "\\foo", "foo\\", "foo\\bar",
+ "fo<o", "fo>o", "fo:o", "fo\"o",
+ "fo|o", "fo?o", "fo*o", "fo\ro",
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "LPT1", "LPT2",
+ "con", "prn", "aux", "nul",
+ "com1", "com2", "lpt1", "lpt2",
+ "AUX.txt", "aux.txt", "NUL.txt", "nul.txt",
+#endif
+ NULL,
+};
+
+int main(int argc, char **argv)
+{
+ size_t i;
+ (void)argc; (void)argv;
+
+ for (i = 0; must_work[i] != NULL; ++i) {
+ if (!is_filename_sane(must_work[i], false)) {
+ fprintf(stderr, "%s was rejected!\n", must_work[i]);
+ return EXIT_FAILURE;
+ }
+
+ if (!is_filename_sane(must_work[i], true)) {
+ fprintf(stderr,
+ "%s was rejected when testing for "
+ "OS specific stuff!\n", must_work[i]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = 0; must_not_work[i] != NULL; ++i) {
+ if (is_filename_sane(must_not_work[i], false)) {
+ fprintf(stderr, "%s was accepted!\n",
+ must_not_work[i]);
+ return EXIT_FAILURE;
+ }
+
+ if (is_filename_sane(must_not_work[i], true)) {
+ fprintf(stderr,
+ "%s was accepted when testing for "
+ "OS specific stuff!\n", must_not_work[i]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (i = 0; must_not_work_here[i] != NULL; ++i) {
+ if (!is_filename_sane(must_not_work_here[i], false)) {
+ fprintf(stderr,
+ "%s was rejected in the generic test!\n",
+ must_not_work_here[i]);
+ return EXIT_FAILURE;
+ }
+
+ if (is_filename_sane(must_not_work_here[i], true)) {
+ fprintf(stderr,
+ "%s was accepted when testing for "
+ "OS specific stuff!\n", must_not_work_here[i]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/hex_decode.c b/lib/util/test/hex_decode.c
new file mode 100644
index 0000000..21ac4e7
--- /dev/null
+++ b/lib/util/test/hex_decode.c
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * hex_decode.c
+ *
+ * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "util/util.h"
+#include "util/test.h"
+
+static const struct {
+ int result;
+ const char *in;
+ const char *out;
+} test_vec[] = {
+ { 0, "", NULL },
+ { -1, "A", NULL },
+ { 0, "AA", "\xAA" },
+ { 0, "0A", "\x0A" },
+ { 0, "A0", "\xA0" },
+ { -1, "A0B", NULL },
+ { 0, "A0BC", "\xA0\xBC" },
+ { 0, "0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF" },
+ { 0, "0123456789abcdef", "\x01\x23\x45\x67\x89\xAB\xCD\xEF" },
+ { -1, "0123456789ABCDEFGH", NULL },
+ { -1, "0123456789abcdefgh", NULL },
+};
+
+int main(int argc, char **argv)
+{
+ sqfs_u8 buffer[256];
+ size_t i, j;
+ (void)argc; (void)argv;
+
+ for (i = 0; i < sizeof(test_vec) / sizeof(test_vec[0]); ++i) {
+ size_t in_len = strlen(test_vec[i].in);
+ size_t out_len = in_len / 2;
+ int ret;
+
+ /* initialize the buffer */
+ for (j = 0; j < sizeof(buffer); ++j) {
+ buffer[j] = (j % 2) ? 0xAA : 0x55;
+ }
+
+ /* convert */
+ ret = hex_decode(test_vec[i].in, in_len,
+ buffer, sizeof(buffer));
+
+ /* make sure pattern is un-touched after expected offset */
+ for (j = out_len; j < sizeof(buffer); ++j) {
+ TEST_ASSERT(buffer[j] == ((j % 2) ? 0xAA : 0x55));
+ }
+
+ /* check result */
+ if (test_vec[i].result == 0) {
+ TEST_ASSERT(ret == 0);
+ ret = memcmp(buffer, test_vec[i].out, out_len);
+ TEST_ASSERT(ret == 0);
+ } else {
+ TEST_ASSERT(ret != 0);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/lib/util/test/is_memory_zero.c b/lib/util/test/is_memory_zero.c
new file mode 100644
index 0000000..f62b0bb
--- /dev/null
+++ b/lib/util/test/is_memory_zero.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * is_memory_zero.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/test.h"
+#include "util/util.h"
+
+int main(int argc, char **argv)
+{
+ unsigned char temp[1024];
+ size_t i, j;
+ (void)argc; (void)argv;
+
+ memset(temp, 0, sizeof(temp));
+
+ for (i = 0; i < sizeof(temp); ++i) {
+ TEST_ASSERT(is_memory_zero(temp, i));
+
+ for (j = 0; j < i; ++j) {
+ TEST_ASSERT(is_memory_zero(temp, i));
+ temp[j] = 42;
+ TEST_ASSERT(!is_memory_zero(temp, i));
+ temp[j] = 0;
+ TEST_ASSERT(is_memory_zero(temp, i));
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/rbtree.c b/lib/util/test/rbtree.c
new file mode 100644
index 0000000..ca01c0d
--- /dev/null
+++ b/lib/util/test/rbtree.c
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * rbtree.c
+ *
+ * Copyright (C) 2020 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/rbtree.h"
+#include "util/test.h"
+
+static int key_compare(const void *ctx, const void *a, const void *b)
+{
+ (void)ctx;
+ return *((const sqfs_s32 *)a) - *((const sqfs_s32 *)b);
+}
+
+static size_t count_nodes_dfs(rbtree_node_t *n)
+{
+ return 1 + (n->left == NULL ? 0 : count_nodes_dfs(n->left))
+ + (n->right == NULL ? 0 : count_nodes_dfs(n->right));
+}
+
+static size_t min_depth(rbtree_node_t *n)
+{
+ size_t lhs, rhs;
+
+ if (n == NULL)
+ return 0;
+
+ lhs = min_depth(n->left) + 1;
+ rhs = min_depth(n->right) + 1;
+
+ return lhs < rhs ? lhs : rhs;
+}
+
+static size_t max_depth(rbtree_node_t *n)
+{
+ size_t lhs, rhs;
+
+ if (n == NULL)
+ return 0;
+
+ lhs = min_depth(n->left) + 1;
+ rhs = min_depth(n->right) + 1;
+
+ return lhs > rhs ? lhs : rhs;
+}
+
+static size_t get_ref_black_depth(rbtree_t *rb)
+{
+ rbtree_node_t *n;
+ size_t count = 0;
+
+ for (n = rb->root; n != NULL; n = n->left) {
+ if (!n->is_red)
+ count += 1;
+ }
+
+ return count;
+}
+
+static void check_binary_tree_dfs(rbtree_node_t *n)
+{
+ const void *key = rbtree_node_key(n);
+ const void *cmp;
+
+ if (n->left != NULL) {
+ cmp = rbtree_node_key(n->left);
+ TEST_ASSERT(key_compare(NULL, cmp, key) < 0);
+
+ check_binary_tree_dfs(n->left);
+ }
+
+ if (n->right != NULL) {
+ cmp = rbtree_node_key(n->right);
+ TEST_ASSERT(key_compare(NULL, cmp, key) > 0);
+
+ check_binary_tree_dfs(n->right);
+ }
+}
+
+static void check_colors_dfs(rbtree_node_t *n)
+{
+ if (n->is_red) {
+ TEST_ASSERT(n->left == NULL || !n->left->is_red);
+ TEST_ASSERT(n->right == NULL || !n->right->is_red);
+ }
+
+ if (n->left != NULL)
+ check_colors_dfs(n->left);
+
+ if (n->right != NULL)
+ check_colors_dfs(n->right);
+}
+
+static void check_black_depth_dfs(rbtree_node_t *n, size_t ref,
+ size_t counter)
+{
+ if (!n->is_red)
+ counter += 1;
+
+ if (n->left == NULL || n->right == NULL)
+ TEST_EQUAL_UI(counter, ref);
+
+ if (n->left != NULL)
+ check_black_depth_dfs(n->left, ref, counter);
+
+ if (n->right != NULL)
+ check_black_depth_dfs(n->right, ref, counter);
+}
+
+static int check_subtrees_equal(const rbtree_node_t *lhs,
+ const rbtree_node_t *rhs,
+ size_t datasize)
+{
+ if (lhs == rhs)
+ return -1;
+
+ if (lhs->value_offset != rhs->value_offset)
+ return -1;
+
+ if ((lhs->is_red && !rhs->is_red) || (!lhs->is_red && rhs->is_red))
+ return -1;
+
+ if (memcmp(lhs->data, rhs->data, datasize) != 0)
+ return -1;
+
+ if (lhs->left == NULL) {
+ if (rhs->left != NULL)
+ return -1;
+ } else {
+ if (rhs->left == NULL)
+ return -1;
+
+ if (check_subtrees_equal(lhs->left, rhs->left, datasize))
+ return -1;
+ }
+
+ if (lhs->right == NULL) {
+ if (rhs->right != NULL)
+ return -1;
+ } else {
+ if (rhs->right == NULL)
+ return -1;
+
+ if (check_subtrees_equal(lhs->right, rhs->right, datasize))
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ size_t count, blkdepth, mind, maxd;
+ sqfs_s32 key, key2;
+ rbtree_t rb, copy;
+ rbtree_node_t *n;
+ sqfs_u64 value;
+ int ret;
+ (void)argc; (void)argv;
+
+ TEST_ASSERT(rbtree_init(&rb, sizeof(sqfs_s32),
+ sizeof(sqfs_u64), key_compare) == 0);
+
+ count = 0;
+
+ for (key = -1000; key < 1000; ++key) {
+ /* lookup of current key must fail prior to insert */
+ TEST_NULL(rbtree_lookup(&rb, &key));
+
+ /* previous key/value pairs must still be there */
+ for (key2 = -1000; key2 < key; ++key2) {
+ n = rbtree_lookup(&rb, &key2);
+ TEST_NOT_NULL(n);
+ value = *((sqfs_u64 *)rbtree_node_value(n));
+ TEST_EQUAL_UI((sqfs_u64)(key2 + 10000), value);
+ }
+
+ /* insert key value pair */
+ value = key + 10000;
+ TEST_ASSERT(rbtree_insert(&rb, &key, &value) == 0);
+ count += 1;
+
+ /* check if the tree has the right number of nodes */
+ TEST_EQUAL_UI(count_nodes_dfs(rb.root), count);
+
+ /* check if it is still a binary tree */
+ check_binary_tree_dfs(rb.root);
+
+ /* root node must be black. Every red node
+ must have black children. */
+ TEST_ASSERT(!rb.root->is_red);
+ check_colors_dfs(rb.root);
+
+ /* every path from the root to a leave must have
+ the same number of black nodes. */
+ blkdepth = get_ref_black_depth(&rb);
+ check_black_depth_dfs(rb.root, blkdepth, 0);
+
+ /* longest root to leaf path must be at most
+ twice as long as the shortest. */
+ mind = min_depth(rb.root);
+ maxd = max_depth(rb.root);
+ TEST_ASSERT(maxd <= mind * 2);
+
+ /* lookup of current key must work after insert */
+ n = rbtree_lookup(&rb, &key);
+ TEST_NOT_NULL(n);
+ value = *((sqfs_u64 *)rbtree_node_value(n));
+ TEST_EQUAL_UI((sqfs_u64)(key + 10000), value);
+ }
+
+ /* test if copy works */
+ ret = rbtree_copy(&rb, &copy);
+ TEST_EQUAL_I(ret, 0);
+
+ TEST_EQUAL_UI(rb.key_size, copy.key_size);
+ TEST_EQUAL_UI(rb.key_size_padded, copy.key_size_padded);
+ TEST_EQUAL_UI(rb.value_size, copy.value_size);
+ TEST_ASSERT(rb.key_compare == copy.key_compare);
+ TEST_ASSERT(rb.root != copy.root);
+
+ ret = check_subtrees_equal(rb.root, copy.root,
+ rb.key_size_padded + rb.value_size);
+ TEST_EQUAL_I(ret, 0);
+
+ /* cleanup */
+ rbtree_cleanup(&rb);
+ rbtree_cleanup(&copy);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/str_table.c b/lib/util/test/str_table.c
new file mode 100644
index 0000000..d4160eb
--- /dev/null
+++ b/lib/util/test/str_table.c
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * str_table.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/str_table.h"
+#include "io/file.h"
+#include "compat.h"
+#include "util/test.h"
+
+static char *strings[1000];
+
+static int read_strings(void)
+{
+ istream_t *fp;
+ ssize_t ret;
+ char *line;
+ int i;
+
+ fp = istream_open_file("words.txt");
+ TEST_NOT_NULL(fp);
+
+ for (i = 0; i < 1000; ++i) {
+ ret = istream_get_line(fp, &line, NULL, 0);
+ TEST_EQUAL_I(ret, 0);
+
+ strings[i] = line;
+ }
+
+ sqfs_drop(fp);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ str_table_t table;
+ size_t i, j, idx;
+ const char *str;
+ (void)argc; (void)argv;
+
+ TEST_ASSERT(chdir(TEST_PATH) == 0);
+
+ if (read_strings())
+ return EXIT_FAILURE;
+
+ TEST_ASSERT(str_table_init(&table) == 0);
+
+ for (i = 0; i < 1000; ++i) {
+ TEST_ASSERT(str_table_get_index(&table, strings[i], &idx) == 0);
+
+ TEST_EQUAL_UI(idx, i);
+
+ for (j = 0; j <= i; ++j) {
+ str = str_table_get_string(&table, j);
+
+ TEST_NOT_NULL(str);
+ TEST_ASSERT(str != strings[i]);
+ TEST_STR_EQUAL(str, strings[j]);
+ }
+
+ for (; j < 1000; ++j)
+ TEST_NULL(str_table_get_string(&table, j));
+ }
+
+ for (i = 0; i < 1000; ++i) {
+ TEST_ASSERT(str_table_get_index(&table, strings[i], &idx) == 0);
+ TEST_EQUAL_UI(idx, i);
+
+ str = str_table_get_string(&table, i);
+
+ TEST_NOT_NULL(str);
+ TEST_ASSERT(str != strings[i]);
+ TEST_STR_EQUAL(str, strings[i]);
+ }
+
+ str_table_cleanup(&table);
+
+ for (i = 0; i < 1000; ++i)
+ free(strings[i]);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/threadpool.c b/lib/util/test/threadpool.c
new file mode 100644
index 0000000..cf54484
--- /dev/null
+++ b/lib/util/test/threadpool.c
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+/*
+ * threadpool.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/threadpool.h"
+#include "util/test.h"
+
+#if defined(_WIN32) || defined(__WINDOWS__)
+#define WIN32_LEAN_AND_MEAN
+#define VC_EXTRALEAN
+#include <windows.h>
+
+static CRITICAL_SECTION mutex;
+static unsigned int ticket;
+
+static void ticket_init(void)
+{
+ InitializeCriticalSection(&mutex);
+ ticket = 0;
+}
+
+static void ticket_cleanup(void)
+{
+ DeleteCriticalSection(&mutex);
+ ticket = 0;
+}
+
+static void ticket_wait(unsigned int value)
+{
+ for (;;) {
+ EnterCriticalSection(&mutex);
+
+ if (value == ticket) {
+ ticket += 1;
+ LeaveCriticalSection(&mutex);
+ break;
+ }
+
+ LeaveCriticalSection(&mutex);
+ SwitchToThread();
+ }
+}
+#elif defined(HAVE_PTHREAD)
+#include <pthread.h>
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int ticket;
+
+static void ticket_init(void)
+{
+ ticket = 0;
+}
+
+static void ticket_cleanup(void)
+{
+}
+
+static void ticket_wait(unsigned int value)
+{
+ for (;;) {
+ pthread_mutex_lock(&mutex);
+
+ if (value == ticket) {
+ ticket += 1;
+ pthread_mutex_unlock(&mutex);
+ break;
+ }
+
+ pthread_mutex_unlock(&mutex);
+ sched_yield();
+ }
+}
+#else
+static void ticket_init(void)
+{
+}
+
+static void ticket_cleanup(void)
+{
+}
+
+static void ticket_wait(unsigned int value)
+{
+ (void)value;
+}
+#endif
+
+static int worker(void *user, void *work_item)
+{
+ unsigned int value = *((unsigned int *)work_item);
+ (void)user;
+
+ ticket_wait(value);
+
+ *((unsigned int *)work_item) = 42;
+ return 0;
+}
+
+static int worker_serial(void *user, void *work_item)
+{
+ (void)user;
+ *((unsigned int *)work_item) = 42;
+ return 0;
+}
+
+static void test_case(thread_pool_t *pool)
+{
+ unsigned int values[10];
+ unsigned int *ptr;
+ size_t i, count;
+ int ret;
+
+ /* must return a sane value */
+ count = pool->get_worker_count(pool);
+ TEST_ASSERT(count >= 1);
+
+ /* dequeue on empty pool MUST NOT lock up */
+ ptr = pool->dequeue(pool);
+ TEST_NULL(ptr);
+
+ /* submit work items in reverse order */
+ ticket_init();
+
+ for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
+ values[i] = (sizeof(values) / sizeof(values[0]) - 1) - i;
+
+ ret = pool->submit(pool, values + i);
+ TEST_EQUAL_I(ret, 0);
+ }
+
+ /* items must dequeue in the same order */
+ for (i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
+ ptr = pool->dequeue(pool);
+
+ TEST_NOT_NULL(ptr);
+ TEST_ASSERT(ptr == (values + i));
+ TEST_EQUAL_UI(*ptr, 42);
+ }
+
+ ticket_cleanup();
+
+ /* queue now empty */
+ ptr = pool->dequeue(pool);
+ TEST_NULL(ptr);
+}
+
+int main(int argc, char **argv)
+{
+ thread_pool_t *pool;
+ (void)argc; (void)argv;
+
+ /* test the actual parallel implementation */
+ pool = thread_pool_create(10, worker);
+ TEST_NOT_NULL(pool);
+ test_case(pool);
+ pool->destroy(pool);
+
+ /* repeate the test with the serial reference implementation */
+ pool = thread_pool_create_serial(worker_serial);
+ TEST_NOT_NULL(pool);
+ test_case(pool);
+ pool->destroy(pool);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/util/test/words.txt b/lib/util/test/words.txt
new file mode 100644
index 0000000..9496e14
--- /dev/null
+++ b/lib/util/test/words.txt
@@ -0,0 +1,1000 @@
+a
+ability
+able
+about
+above
+accept
+according
+account
+across
+act
+action
+activity
+actually
+add
+address
+administration
+admit
+adult
+affect
+after
+again
+against
+age
+agency
+agent
+ago
+agree
+agreement
+ahead
+air
+all
+allow
+almost
+alone
+along
+already
+also
+although
+always
+American
+among
+amount
+analysis
+and
+animal
+another
+answer
+any
+anyone
+anything
+appear
+apply
+approach
+area
+argue
+arm
+around
+arrive
+art
+article
+artist
+as
+ask
+assume
+at
+attack
+attention
+attorney
+audience
+author
+authority
+available
+avoid
+away
+baby
+back
+bad
+bag
+ball
+bank
+bar
+base
+be
+beat
+beautiful
+because
+become
+bed
+before
+begin
+behavior
+behind
+believe
+benefit
+best
+better
+between
+beyond
+big
+bill
+billion
+bit
+black
+blood
+blue
+board
+body
+book
+born
+both
+box
+boy
+break
+bring
+brother
+budget
+build
+building
+business
+but
+buy
+by
+call
+camera
+campaign
+can
+cancer
+candidate
+capital
+car
+card
+care
+career
+carry
+case
+catch
+cause
+cell
+center
+central
+century
+certain
+certainly
+chair
+challenge
+chance
+change
+character
+charge
+check
+child
+choice
+choose
+church
+citizen
+city
+civil
+claim
+class
+clear
+clearly
+close
+coach
+cold
+collection
+college
+color
+come
+commercial
+common
+community
+company
+compare
+computer
+concern
+condition
+conference
+Congress
+consider
+consumer
+contain
+continue
+control
+cost
+could
+country
+couple
+course
+court
+cover
+create
+crime
+cultural
+culture
+cup
+current
+customer
+cut
+dark
+data
+daughter
+day
+dead
+deal
+death
+debate
+decade
+decide
+decision
+deep
+defense
+degree
+Democrat
+democratic
+describe
+design
+despite
+detail
+determine
+develop
+development
+die
+difference
+different
+difficult
+dinner
+direction
+director
+discover
+discuss
+discussion
+disease
+do
+doctor
+dog
+door
+down
+draw
+dream
+drive
+drop
+drug
+during
+each
+early
+east
+easy
+eat
+economic
+economy
+edge
+education
+effect
+effort
+eight
+either
+election
+else
+employee
+end
+energy
+enjoy
+enough
+enter
+entire
+environment
+environmental
+especially
+establish
+even
+evening
+event
+ever
+every
+everybody
+everyone
+everything
+evidence
+exactly
+example
+executive
+exist
+expect
+experience
+expert
+explain
+eye
+face
+fact
+factor
+fail
+fall
+family
+far
+fast
+father
+fear
+federal
+feel
+feeling
+few
+field
+fight
+figure
+fill
+film
+final
+finally
+financial
+find
+fine
+finger
+finish
+fire
+firm
+first
+fish
+five
+floor
+fly
+focus
+follow
+food
+foot
+for
+force
+foreign
+forget
+form
+former
+forward
+four
+free
+friend
+from
+front
+full
+fund
+future
+game
+garden
+gas
+general
+generation
+get
+girl
+give
+glass
+go
+goal
+good
+government
+great
+green
+ground
+group
+grow
+growth
+guess
+gun
+guy
+hair
+half
+hand
+hang
+happen
+happy
+hard
+have
+he
+head
+health
+hear
+heart
+heat
+heavy
+help
+her
+here
+herself
+high
+him
+himself
+his
+history
+hit
+hold
+home
+hope
+hospital
+hot
+hotel
+hour
+house
+how
+however
+huge
+human
+hundred
+husband
+I
+idea
+identify
+if
+image
+imagine
+impact
+important
+improve
+in
+include
+including
+increase
+indeed
+indicate
+individual
+industry
+information
+inside
+instead
+institution
+interest
+interesting
+international
+interview
+into
+investment
+involve
+issue
+it
+item
+its
+itself
+job
+join
+just
+keep
+key
+kid
+kill
+kind
+kitchen
+know
+knowledge
+land
+language
+large
+last
+late
+later
+laugh
+law
+lawyer
+lay
+lead
+leader
+learn
+least
+leave
+left
+leg
+legal
+less
+let
+letter
+level
+lie
+life
+light
+like
+likely
+line
+list
+listen
+little
+live
+local
+long
+look
+lose
+loss
+lot
+love
+low
+machine
+magazine
+main
+maintain
+major
+majority
+make
+man
+manage
+management
+manager
+many
+market
+marriage
+material
+matter
+may
+maybe
+me
+mean
+measure
+media
+medical
+meet
+meeting
+member
+memory
+mention
+message
+method
+middle
+might
+military
+million
+mind
+minute
+miss
+mission
+model
+modern
+moment
+money
+month
+more
+morning
+most
+mother
+mouth
+move
+movement
+movie
+Mr
+Mrs
+much
+music
+must
+my
+myself
+name
+nation
+national
+natural
+nature
+near
+nearly
+necessary
+need
+network
+never
+new
+news
+newspaper
+next
+nice
+night
+no
+none
+nor
+north
+not
+note
+nothing
+notice
+now
+n't
+number
+occur
+of
+off
+offer
+office
+officer
+official
+often
+oh
+oil
+ok
+old
+on
+once
+one
+only
+onto
+open
+operation
+opportunity
+option
+or
+order
+organization
+other
+others
+our
+out
+outside
+over
+own
+owner
+page
+pain
+painting
+paper
+parent
+part
+participant
+particular
+particularly
+partner
+party
+pass
+past
+patient
+pattern
+pay
+peace
+people
+per
+perform
+performance
+perhaps
+period
+person
+personal
+phone
+physical
+pick
+picture
+piece
+place
+plan
+plant
+play
+player
+PM
+point
+police
+policy
+political
+politics
+poor
+popular
+population
+position
+positive
+possible
+power
+practice
+prepare
+present
+president
+pressure
+pretty
+prevent
+price
+private
+probably
+problem
+process
+produce
+product
+production
+professional
+professor
+program
+project
+property
+protect
+prove
+provide
+public
+pull
+purpose
+push
+put
+quality
+question
+quickly
+quite
+race
+radio
+raise
+range
+rate
+rather
+reach
+read
+ready
+real
+reality
+realize
+really
+reason
+receive
+recent
+recently
+recognize
+record
+red
+reduce
+reflect
+region
+relate
+relationship
+religious
+remain
+remember
+remove
+report
+represent
+Republican
+require
+research
+resource
+respond
+response
+responsibility
+rest
+result
+return
+reveal
+rich
+right
+rise
+risk
+road
+rock
+role
+room
+rule
+run
+safe
+same
+save
+say
+scene
+school
+science
+scientist
+score
+sea
+season
+seat
+second
+section
+security
+see
+seek
+seem
+sell
+send
+senior
+sense
+series
+serious
+serve
+service
+set
+seven
+several
+sex
+sexual
+shake
+share
+she
+shoot
+short
+shot
+should
+shoulder
+show
+side
+sign
+significant
+similar
+simple
+simply
+since
+sing
+single
+sister
+sit
+site
+situation
+six
+size
+skill
+skin
+small
+smile
+so
+social
+society
+soldier
+some
+somebody
+someone
+something
+sometimes
+son
+song
+soon
+sort
+sound
+source
+south
+southern
+space
+speak
+special
+specific
+speech
+spend
+sport
+spring
+staff
+stage
+stand
+standard
+star
+start
+state
+statement
+station
+stay
+step
+still
+stock
+stop
+store
+story
+strategy
+street
+strong
+structure
+student
+study
+stuff
+style
+subject
+success
+successful
+such
+suddenly
+suffer
+suggest
+summer
+support
+sure
+surface
+system
+table
+take
+talk
+task
+tax
+teach
+teacher
+team
+technology
+television
+tell
+ten
+tend
+term
+test
+than
+thank
+that
+the
+their
+them
+themselves
+then
+theory
+there
+these
+they
+thing
+think
+third
+this
+those
+though
+thought
+thousand
+threat
+three
+through
+throughout
+throw
+thus
+time
+to
+today
+together
+tonight
+too
+top
+total
+tough
+toward
+town
+trade
+traditional
+training
+travel
+treat
+treatment
+tree
+trial
+trip
+trouble
+true
+truth
+try
+turn
+TV
+two
+type
+under
+understand
+unit
+until
+up
+upon
+us
+use
+usually
+value
+various
+very
+victim
+view
+violence
+visit
+voice
+vote
+wait
+walk
+wall
+want
+war
+watch
+water
+way
+we
+weapon
+wear
+week
+weight
+well
+west
+western
+what
+whatever
+when
+where
+whether
+which
+while
+white
+who
+whole
+whom
+whose
+why
+wide
+wife
+will
+win
+wind
+window
+wish
+with
+within
+without
+woman
+wonder
+word
+work
+worker
+world
+worry
+would
+write
+writer
+wrong
+yard
+yeah
+year
+yes
+yet
+you
+young
+your
+yourself
diff --git a/lib/util/test/xxhash.c b/lib/util/test/xxhash.c
new file mode 100644
index 0000000..17374fd
--- /dev/null
+++ b/lib/util/test/xxhash.c
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xxhash.c
+ *
+ * Copyright (C) 2020 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "util/util.h"
+#include "util/test.h"
+
+static const struct {
+ const char *plaintext;
+ size_t psize;
+ sqfs_u32 digest;
+} test_vectors[] = {
+ {
+ .plaintext = "\x9e",
+ .psize = 1,
+ .digest = 0xB85CBEE5,
+ },
+ {
+ .plaintext = "\x9e\xff\x1f\x4b\x5e\x53\x2f\xdd"
+ "\xb5\x54\x4d\x2a\x95\x2b",
+ .psize = 14,
+ .digest = 0xE5AA0AB4,
+ },
+ {
+ .plaintext = "\x9e\xff\x1f\x4b\x5e\x53\x2f\xdd"
+ "\xb5\x54\x4d\x2a\x95\x2b\x57\xae"
+ "\x5d\xba\x74\xe9\xd3\xa6\x4c\x98"
+ "\x30\x60\xc0\x80\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00",
+ .psize = 101,
+ .digest = 0x018F52BC,
+ },
+};
+
+int main(int argc, char **argv)
+{
+ sqfs_u32 hash;
+ size_t i;
+ (void)argc; (void)argv;
+
+ for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); ++i) {
+ hash = xxh32(test_vectors[i].plaintext, test_vectors[i].psize);
+
+ if (hash != test_vectors[i].digest) {
+ fprintf(stderr, "Test case " PRI_SZ " failed!\n", i);
+ fprintf(stderr, "Expected result: 0x%08X\n",
+ test_vectors[i].digest);
+ fprintf(stderr, "Actual result: 0x%08X\n", hash);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/lib/xfrm/Makemodule.am b/lib/xfrm/Makemodule.am
index ad446f6..a517df7 100644
--- a/lib/xfrm/Makemodule.am
+++ b/lib/xfrm/Makemodule.am
@@ -1,3 +1,6 @@
+LIBXFRM_LIBS = $(ZLIB_LIBS) $(XZ_LIBS) $(BZIP2_LIBS) $(ZSTD_LIBS)
+LIBXFRM_TESTS =
+
libxfrm_a_SOURCES = include/xfrm/stream.h include/xfrm/compress.h \
lib/xfrm/src/compress.c
libxfrm_a_CFLAGS = $(AM_CFLAGS)
@@ -5,23 +8,66 @@ libxfrm_a_CFLAGS = $(AM_CFLAGS)
if WITH_XZ
libxfrm_a_SOURCES += lib/xfrm/src/xz.c
libxfrm_a_CFLAGS += $(XZ_CFLAGS) -DWITH_XZ
+
+test_unpack_xz_SOURCES = lib/xfrm/test/unpack.c
+test_unpack_xz_LDADD = libxfrm.a $(XZ_LIBS)
+test_unpack_xz_CPPFLAGS = $(AM_CPPFLAGS) -DDO_XZ=1
+
+test_pack_xz_SOURCES = lib/xfrm/test/pack.c
+test_pack_xz_LDADD = libxfrm.a $(LIBXFRM_LIBS)
+test_pack_xz_CPPFLAGS = $(AM_CPPFLAGS) -DDO_XZ=1
+
+LIBXFRM_TESTS += test_pack_xz test_unpack_xz
endif
if WITH_BZIP2
libxfrm_a_SOURCES += lib/xfrm/src/bzip2.c
libxfrm_a_CFLAGS += $(BZIP2_CFLAGS) -DWITH_BZIP2
+
+test_unpack_bzip2_SOURCES = lib/xfrm/test/unpack.c
+test_unpack_bzip2_LDADD = libxfrm.a $(BZIP2_LIBS)
+test_unpack_bzip2_CPPFLAGS = $(AM_CPPFLAGS) -DDO_BZIP2=1
+
+test_pack_bzip2_SOURCES = lib/xfrm/test/pack.c
+test_pack_bzip2_LDADD = libxfrm.a $(LIBXFRM_LIBS)
+test_pack_bzip2_CPPFLAGS = $(AM_CPPFLAGS) -DDO_BZIP2=1
+
+LIBXFRM_TESTS += test_unpack_bzip2 test_pack_bzip2
endif
if WITH_GZIP
libxfrm_a_SOURCES += lib/xfrm/src/gzip.c
libxfrm_a_CFLAGS += $(ZLIB_CFLAGS) -DWITH_GZIP
+
+test_unpack_gzip_SOURCES = lib/xfrm/test/unpack.c
+test_unpack_gzip_LDADD = libxfrm.a $(ZLIB_LIBS)
+test_unpack_gzip_CPPFLAGS = $(AM_CPPFLAGS) -DDO_GZIP=1
+
+test_pack_gzip_SOURCES = lib/xfrm/test/pack.c
+test_pack_gzip_LDADD = libxfrm.a $(LIBXFRM_LIBS)
+test_pack_gzip_CPPFLAGS = $(AM_CPPFLAGS) -DDO_GZIP=1
+
+LIBXFRM_TESTS += test_pack_gzip test_unpack_gzip
endif
if WITH_ZSTD
if HAVE_ZSTD_STREAM
libxfrm_a_SOURCES += lib/xfrm/src/zstd.c
libxfrm_a_CFLAGS += $(ZSTD_CFLAGS) -DWITH_ZSTD
+
+test_pack_zstd_SOURCES = lib/xfrm/test/unpack.c
+test_pack_zstd_LDADD = libxfrm.a $(ZSTD_LIBS)
+test_pack_zstd_CPPFLAGS = $(AM_CPPFLAGS) -DDO_ZSTD=1
+
+test_unpack_zstd_SOURCES = lib/xfrm/test/pack.c
+test_unpack_zstd_LDADD = libxfrm.a $(LIBXFRM_LIBS)
+test_unpack_zstd_CPPFLAGS = $(AM_CPPFLAGS) -DDO_ZSTD=1
+
+LIBXFRM_TESTS += test_pack_zstd test_unpack_zstd
endif
endif
noinst_LIBRARIES += libxfrm.a
+
+check_PROGRAMS += $(LIBXFRM_TESTS)
+TESTS += $(LIBXFRM_TESTS)
diff --git a/lib/xfrm/test/pack.c b/lib/xfrm/test/pack.c
new file mode 100644
index 0000000..c297005
--- /dev/null
+++ b/lib/xfrm/test/pack.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * compress.c
+ *
+ * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "xfrm/compress.h"
+#include "xfrm/stream.h"
+#include "util/test.h"
+
+static const char text[] =
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n"
+"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"
+"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n"
+"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n"
+"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n"
+"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
+
+static sqfs_u8 buffer_cmp[1024];
+static sqfs_u8 buffer_plain[1024];
+static sqfs_u8 ref_cmp[1024];
+static size_t ref_size;
+
+#if defined(DO_BZIP2)
+#define mkdecompressor decompressor_stream_bzip2_create
+#define mkcompressor compressor_stream_bzip2_create
+
+static sqfs_u8 magic[3] = "BZh";
+static int comp_id = XFRM_COMPRESSOR_BZIP2;
+static const char *comp_name = "bzip2";
+#elif defined(DO_XZ)
+#define mkdecompressor decompressor_stream_xz_create
+#define mkcompressor compressor_stream_xz_create
+
+static sqfs_u8 magic[6] = "\xFD" "7zXZ";
+static int comp_id = XFRM_COMPRESSOR_XZ;
+static const char *comp_name = "xz";
+#elif defined(DO_GZIP)
+#define mkdecompressor decompressor_stream_gzip_create
+#define mkcompressor compressor_stream_gzip_create
+
+static sqfs_u8 magic[3] = "\x1F\x8B\x08";
+static int comp_id = XFRM_COMPRESSOR_GZIP;
+static const char *comp_name = "gzip";
+#elif defined(DO_ZSTD)
+#define mkdecompressor decompressor_stream_zstd_create
+#define mkcompressor compressor_stream_zstd_create
+
+static sqfs_u8 magic[4] = "\x28\xB5\x2F\xFD";
+static int comp_id = XFRM_COMPRESSOR_ZSTD;
+static const char *comp_name = "zstd";
+#else
+#error build configuration broken
+#endif
+
+int main(int argc, char **argv)
+{
+ sqfs_u32 in_diff = 0, out_diff = 0;
+ xfrm_stream_t *xfrm;
+ const char *str;
+ size_t size;
+ int ret;
+ (void)argc; (void)argv;
+
+ /* generic name/ID API */
+ ret = xfrm_compressor_id_from_name(comp_name);
+ TEST_EQUAL_I(ret, comp_id);
+
+ str = xfrm_compressor_name_from_id(ret);
+ TEST_STR_EQUAL(str, comp_name);
+
+ /* compress the original text */
+ xfrm = mkcompressor(NULL);
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ ret = xfrm->process_data(xfrm, text, sizeof(text),
+ buffer_cmp, sizeof(buffer_cmp),
+ &in_diff, &out_diff, XFRM_STREAM_FLUSH_FULL);
+ TEST_EQUAL_I(ret, XFRM_STREAM_END);
+
+ TEST_EQUAL_UI(in_diff, sizeof(text));
+ TEST_ASSERT(out_diff > 0);
+ TEST_ASSERT(out_diff < sizeof(text));
+
+ sqfs_drop(xfrm);
+
+ size = out_diff;
+ in_diff = out_diff = 0;
+
+ memcpy(ref_cmp, buffer_cmp, size);
+ ref_size = size;
+
+ /* check if it has the expected magic number */
+ TEST_ASSERT(size >= sizeof(magic));
+ ret = memcmp(buffer_cmp, magic, sizeof(magic));
+ TEST_EQUAL_I(ret, 0);
+
+ /* check if the auto detection correctly identifies it */
+ ret = xfrm_compressor_id_from_magic(buffer_cmp, size);
+ TEST_EQUAL_I(ret, comp_id);
+
+ ret = xfrm_compressor_id_from_magic(text, sizeof(text));
+ TEST_EQUAL_I(ret, -1);
+
+ /* unpack the compressed text and compare to the original */
+ xfrm = mkdecompressor();
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ ret = xfrm->process_data(xfrm, buffer_cmp, size,
+ buffer_plain, sizeof(buffer_plain),
+ &in_diff, &out_diff, 0);
+ TEST_ASSERT(ret == XFRM_STREAM_END || ret == XFRM_STREAM_OK);
+
+ TEST_EQUAL_UI(in_diff, size);
+ TEST_EQUAL_UI(out_diff, sizeof(text));
+ ret = memcmp(buffer_plain, text, out_diff);
+ TEST_EQUAL_I(ret, 0);
+
+ sqfs_drop(xfrm);
+ in_diff = out_diff = 0;
+
+ /* retry packing but create the compressor using the ID */
+ memset(buffer_cmp, 0, sizeof(buffer_cmp));
+ memset(buffer_plain, 0, sizeof(buffer_plain));
+
+ xfrm = compressor_stream_create(comp_id, NULL);
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ ret = xfrm->process_data(xfrm, text, sizeof(text),
+ buffer_cmp, sizeof(buffer_cmp),
+ &in_diff, &out_diff, XFRM_STREAM_FLUSH_FULL);
+ TEST_EQUAL_I(ret, XFRM_STREAM_END);
+
+ TEST_EQUAL_UI(in_diff, sizeof(text));
+ TEST_EQUAL_UI(out_diff, ref_size);
+
+ sqfs_drop(xfrm);
+ size = out_diff;
+ in_diff = out_diff = 0;
+
+ /* make sure we got an identical result */
+ ret = memcmp(ref_cmp, buffer_cmp, size);
+ TEST_EQUAL_I(ret, 0);
+
+ /* decompress it using ID */
+ xfrm = decompressor_stream_create(comp_id);
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ ret = xfrm->process_data(xfrm, buffer_cmp, size,
+ buffer_plain, sizeof(buffer_plain),
+ &in_diff, &out_diff, 0);
+ TEST_ASSERT(ret == XFRM_STREAM_END || ret == XFRM_STREAM_OK);
+
+ TEST_EQUAL_UI(in_diff, size);
+ TEST_EQUAL_UI(out_diff, sizeof(text));
+ ret = memcmp(buffer_plain, text, out_diff);
+ TEST_EQUAL_I(ret, 0);
+
+ sqfs_drop(xfrm);
+ return EXIT_SUCCESS;
+}
diff --git a/lib/xfrm/test/unpack.c b/lib/xfrm/test/unpack.c
new file mode 100644
index 0000000..ca51f10
--- /dev/null
+++ b/lib/xfrm/test/unpack.c
@@ -0,0 +1,428 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * unpack.c
+ *
+ * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "xfrm/compress.h"
+#include "xfrm/stream.h"
+#include "util/test.h"
+
+static const sqfs_u8 blob_in[] = {
+#if defined(DO_XZ)
+ 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00,
+ 0xff, 0x12, 0xd9, 0x41, 0x02, 0x00, 0x21, 0x01,
+ 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
+ 0xe0, 0x01, 0xbd, 0x01, 0x43, 0x5d, 0x00, 0x26,
+ 0x1b, 0xca, 0x46, 0x67, 0x5a, 0xf2, 0x77, 0xb8,
+ 0x7d, 0x86, 0xd8, 0x41, 0xdb, 0x05, 0x35, 0xcd,
+ 0x83, 0xa5, 0x7c, 0x12, 0xa5, 0x05, 0xdb, 0x90,
+ 0xbd, 0x2f, 0x14, 0xd3, 0x71, 0x72, 0x96, 0xa8,
+ 0x8a, 0x7d, 0x84, 0x56, 0x71, 0x8d, 0x6a, 0x22,
+ 0x98, 0xab, 0x9e, 0x3d, 0xc3, 0x55, 0xef, 0xcc,
+ 0xa5, 0xc3, 0xdd, 0x5b, 0x8e, 0xbf, 0x03, 0x81,
+ 0x21, 0x40, 0xd6, 0x26, 0x91, 0x02, 0x45, 0x4e,
+ 0x20, 0x91, 0xcf, 0x8c, 0x51, 0x22, 0x02, 0x70,
+ 0xba, 0x05, 0x6b, 0x83, 0xef, 0x3f, 0x8e, 0x09,
+ 0xef, 0x88, 0xf5, 0x37, 0x1b, 0x89, 0x8d, 0xff,
+ 0x1e, 0xee, 0xe8, 0xb0, 0xac, 0xf2, 0x6e, 0xd4,
+ 0x3e, 0x25, 0xaf, 0xa0, 0x6d, 0x2e, 0xc0, 0x7f,
+ 0xb5, 0xa0, 0xcb, 0x90, 0x1f, 0x08, 0x1a, 0xe2,
+ 0x90, 0x20, 0x19, 0x71, 0x0c, 0xe8, 0x3f, 0xe5,
+ 0x39, 0xeb, 0x9a, 0x62, 0x4f, 0x06, 0xda, 0x3c,
+ 0x32, 0x59, 0xcc, 0x83, 0xe3, 0x83, 0x0f, 0x38,
+ 0x7d, 0x43, 0x37, 0x6c, 0x0b, 0x05, 0x65, 0x98,
+ 0x25, 0xdb, 0xf2, 0xc0, 0x2d, 0x39, 0x36, 0x5d,
+ 0xd4, 0xb6, 0xc2, 0x79, 0x73, 0x3e, 0xc2, 0x6e,
+ 0x54, 0xec, 0x78, 0x2b, 0x5d, 0xf1, 0xd1, 0xb4,
+ 0xb3, 0xcd, 0xf3, 0x89, 0xf5, 0x81, 0x3e, 0x2c,
+ 0x65, 0xd6, 0x73, 0xd3, 0x1b, 0x20, 0x68, 0x0c,
+ 0x93, 0xd4, 0xfc, 0x9f, 0xf8, 0xa7, 0xd4, 0xfa,
+ 0x3a, 0xb1, 0x13, 0x93, 0x4b, 0xec, 0x78, 0x7d,
+ 0x5c, 0x81, 0x80, 0xe5, 0x14, 0x78, 0xfe, 0x7e,
+ 0xde, 0xf7, 0xad, 0x9e, 0x84, 0xba, 0xf1, 0x00,
+ 0xe9, 0xbd, 0x2c, 0xf4, 0x70, 0x7d, 0xbe, 0x29,
+ 0xb9, 0xf0, 0x10, 0xb9, 0x01, 0xf1, 0x76, 0x8a,
+ 0x5a, 0xad, 0x02, 0xa1, 0x32, 0xc8, 0x53, 0x59,
+ 0x11, 0x4c, 0xe2, 0x98, 0x34, 0xd9, 0x23, 0x51,
+ 0x4a, 0x40, 0x2b, 0x87, 0x41, 0xdd, 0x50, 0xcd,
+ 0x98, 0x1e, 0x29, 0x86, 0x23, 0x93, 0x3e, 0x9b,
+ 0x6b, 0x16, 0xa1, 0x40, 0xac, 0xe7, 0x40, 0xfe,
+ 0xa9, 0x87, 0x48, 0x25, 0x52, 0x02, 0x8b, 0xc4,
+ 0x68, 0x08, 0x5a, 0x62, 0xc1, 0xb2, 0x07, 0x3b,
+ 0x26, 0x1e, 0x59, 0x5c, 0x47, 0x24, 0xae, 0x8e,
+ 0xe5, 0xf7, 0xe6, 0x4b, 0x13, 0xb4, 0x6d, 0x46,
+ 0x65, 0x4f, 0xd0, 0x48, 0xcc, 0x51, 0x4b, 0x80,
+ 0xcb, 0xf1, 0xd4, 0x6c, 0x45, 0x98, 0x92, 0x47,
+ 0xeb, 0x60, 0x00, 0x00, 0x00, 0x01, 0xd7, 0x02,
+ 0xbe, 0x03, 0x00, 0x00, 0xda, 0x2c, 0x45, 0x49,
+ 0xa8, 0x00, 0x0a, 0xfc, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x59, 0x5a
+#elif defined(DO_BZIP2)
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26,
+ 0x53, 0x59, 0x05, 0x24, 0x28, 0x04, 0x00, 0x00,
+ 0x27, 0xd7, 0x80, 0x00, 0x10, 0x40, 0x05, 0x06,
+ 0x04, 0x02, 0x00, 0x3f, 0xe7, 0xff, 0x40, 0x30,
+ 0x01, 0x2d, 0x23, 0x62, 0x26, 0x05, 0x3d, 0x03,
+ 0x54, 0xfd, 0x53, 0x4c, 0x86, 0x9e, 0x90, 0x6a,
+ 0x9e, 0x9e, 0x85, 0x3c, 0xa0, 0x00, 0x00, 0x1a,
+ 0x9e, 0x41, 0x13, 0x13, 0x28, 0x69, 0x03, 0xd4,
+ 0x0f, 0x1c, 0x70, 0xd0, 0xb4, 0xe3, 0xe4, 0x75,
+ 0x4e, 0x8b, 0x67, 0x43, 0x7b, 0x38, 0x27, 0x77,
+ 0xe4, 0xc1, 0x98, 0x3a, 0x2d, 0x3a, 0xe4, 0x44,
+ 0x98, 0xdc, 0x49, 0x8b, 0x22, 0x48, 0xfc, 0xc8,
+ 0xe7, 0x57, 0x05, 0x3c, 0x5a, 0xee, 0x5a, 0x84,
+ 0xcd, 0x7c, 0x8f, 0x26, 0x6b, 0x6e, 0xf7, 0xb5,
+ 0x49, 0x1f, 0x79, 0x42, 0x5d, 0x09, 0x8c, 0xc6,
+ 0xde, 0x0c, 0x0d, 0xb1, 0x46, 0xb4, 0xee, 0xd9,
+ 0x8f, 0x33, 0x37, 0x04, 0xa9, 0x05, 0x49, 0xe3,
+ 0x04, 0x16, 0x62, 0x36, 0x3a, 0x01, 0xda, 0xd4,
+ 0xc8, 0x8a, 0x32, 0x02, 0x1f, 0x62, 0x4b, 0xa4,
+ 0x49, 0x59, 0xda, 0x50, 0x85, 0x69, 0x35, 0x21,
+ 0x10, 0xc6, 0x8a, 0x3c, 0x44, 0x95, 0xb0, 0xbc,
+ 0xc5, 0x6b, 0xea, 0xfb, 0x40, 0xbd, 0x14, 0x01,
+ 0x6a, 0xfa, 0xcd, 0x67, 0xd8, 0x2d, 0x93, 0x8b,
+ 0xda, 0x44, 0x1b, 0xe9, 0x5a, 0x87, 0x60, 0xb0,
+ 0xe0, 0x73, 0xd1, 0x01, 0x3a, 0x66, 0x05, 0xcc,
+ 0x34, 0xa0, 0x63, 0x8d, 0x35, 0x5e, 0xa0, 0x9f,
+ 0x05, 0x89, 0x15, 0x51, 0x48, 0x16, 0x0c, 0x61,
+ 0xf4, 0x30, 0xb8, 0x07, 0x29, 0xc0, 0xf5, 0x1a,
+ 0xe1, 0x0d, 0x6c, 0xfe, 0x91, 0xda, 0x13, 0x2f,
+ 0x8e, 0x5b, 0x1c, 0xfc, 0xb3, 0xb2, 0x30, 0x9d,
+ 0xf6, 0x09, 0x30, 0x55, 0x30, 0x67, 0xc2, 0x87,
+ 0xe9, 0x9a, 0xd4, 0x1d, 0x66, 0x11, 0x54, 0x89,
+ 0x21, 0xe1, 0x55, 0x84, 0xbf, 0xa6, 0x11, 0xa4,
+ 0xb8, 0x40, 0xed, 0x42, 0x20, 0xb9, 0xb7, 0x26,
+ 0x31, 0x14, 0x4f, 0x86, 0xdc, 0x50, 0x34, 0x38,
+ 0x8b, 0x57, 0x77, 0x21, 0xf6, 0x89, 0xbd, 0xc5,
+ 0x65, 0xc3, 0x23, 0x45, 0xec, 0x7f, 0x8b, 0xb9,
+ 0x22, 0x9c, 0x28, 0x48, 0x02, 0x92, 0x14, 0x02,
+ 0x00,
+#elif defined(DO_GZIP)
+ 0x1f, 0x8b, 0x08, 0x08, 0xdb, 0xa1, 0x97, 0x63,
+ 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
+ 0x78, 0x74, 0x00, 0x35, 0x90, 0xc1, 0x71, 0x43,
+ 0x31, 0x08, 0x44, 0xef, 0xbf, 0x8a, 0x2d, 0x20,
+ 0xf3, 0xab, 0x48, 0x6e, 0xb9, 0xa6, 0x00, 0x82,
+ 0xb0, 0xc3, 0x8c, 0x24, 0x64, 0x09, 0x3c, 0x2e,
+ 0x3f, 0xc8, 0x4e, 0x6e, 0x42, 0xc0, 0xb2, 0xfb,
+ 0x3e, 0x6d, 0x4a, 0x83, 0x8e, 0x15, 0x0d, 0xc5,
+ 0xaa, 0x4d, 0x2c, 0x75, 0x50, 0x13, 0x7f, 0x03,
+ 0x5b, 0x5f, 0xc2, 0x2e, 0x1e, 0x13, 0x54, 0x74,
+ 0xe8, 0x62, 0xed, 0x57, 0x48, 0xd5, 0x6c, 0x2e,
+ 0x29, 0xb9, 0x00, 0xd1, 0x58, 0xcd, 0xca, 0xe1,
+ 0xd2, 0x46, 0x2e, 0x6b, 0x67, 0x2d, 0x5a, 0xa2,
+ 0x3b, 0xc2, 0x51, 0xe9, 0x3b, 0xe5, 0x21, 0xfe,
+ 0x92, 0x16, 0x34, 0xba, 0x76, 0x02, 0x55, 0xbd,
+ 0x05, 0x9d, 0xf8, 0x72, 0x48, 0xd7, 0x96, 0xda,
+ 0x68, 0xba, 0x1f, 0xf7, 0x2c, 0xa9, 0xbd, 0x1d,
+ 0xb7, 0xd0, 0x85, 0x6e, 0xcb, 0x67, 0x14, 0xc8,
+ 0x43, 0x26, 0xab, 0x93, 0xab, 0x75, 0x44, 0xad,
+ 0xd4, 0xd8, 0x5e, 0xca, 0x7b, 0x48, 0x97, 0xee,
+ 0x4b, 0x4f, 0x49, 0x1d, 0x39, 0x0c, 0xa1, 0x34,
+ 0xde, 0xd2, 0x93, 0x1d, 0xcf, 0x00, 0x79, 0xca,
+ 0x4f, 0xbc, 0x6f, 0x49, 0x0a, 0x17, 0xe8, 0x8c,
+ 0x74, 0xf2, 0xca, 0xaa, 0x1d, 0x53, 0xc6, 0x94,
+ 0x1f, 0xe9, 0x45, 0x66, 0x06, 0xcf, 0x8f, 0xbb,
+ 0xd5, 0x18, 0x79, 0x4e, 0xd2, 0x4e, 0x26, 0x85,
+ 0xac, 0x25, 0x07, 0x6b, 0xad, 0xff, 0x84, 0x32,
+ 0x50, 0xe0, 0x12, 0x57, 0x25, 0x47, 0xdf, 0x86,
+ 0x30, 0x68, 0x66, 0x11, 0xf3, 0xc4, 0xc7, 0x83,
+ 0x65, 0xb8, 0xc4, 0xc6, 0x98, 0x0c, 0x8c, 0x99,
+ 0x84, 0x73, 0x8e, 0x63, 0x68, 0x21, 0xdf, 0x1b,
+ 0xd6, 0x8f, 0x31, 0x4d, 0x8b, 0xf4, 0x4d, 0x71,
+ 0x93, 0xca, 0xa3, 0x1c, 0x75, 0x10, 0x32, 0x02,
+ 0xec, 0x72, 0x51, 0x56, 0x42, 0x91, 0x25, 0x73,
+ 0x77, 0x9b, 0xd5, 0x6d, 0x83, 0x36, 0x20, 0x4d,
+ 0x1c, 0xeb, 0x8f, 0x6b, 0xb4, 0xf3, 0xf8, 0x05,
+ 0x6b, 0x8b, 0x8b, 0x20, 0xbe, 0x01, 0x00, 0x00,
+#elif defined(DO_ZSTD)
+ 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x88, 0xa5, 0x08,
+ 0x00, 0x46, 0x97, 0x3a, 0x1a, 0x80, 0x37, 0xcd,
+ 0x01, 0xc0, 0x8a, 0xec, 0xfe, 0x2d, 0xf2, 0xb9,
+ 0x44, 0x6b, 0xb9, 0x24, 0x77, 0x56, 0x5a, 0x33,
+ 0x17, 0x0b, 0x67, 0x83, 0x2e, 0x47, 0x07, 0x31,
+ 0x00, 0x32, 0x00, 0x33, 0x00, 0xc5, 0x2c, 0x5a,
+ 0x92, 0x93, 0x0f, 0x7b, 0xd1, 0x1d, 0x63, 0x2c,
+ 0xc8, 0x99, 0x94, 0x77, 0x8f, 0x94, 0x38, 0x75,
+ 0x80, 0x2f, 0xae, 0xc1, 0x3e, 0xd2, 0xcf, 0x49,
+ 0x15, 0x25, 0x1a, 0x87, 0x93, 0xdd, 0xe8, 0x00,
+ 0x6d, 0xaa, 0xf8, 0x54, 0x74, 0xe5, 0x48, 0x4d,
+ 0xa6, 0xf3, 0x1a, 0xa3, 0x13, 0x08, 0xe5, 0x26,
+ 0xdc, 0x73, 0xcc, 0x3e, 0xfd, 0x86, 0xa9, 0x52,
+ 0xb2, 0x76, 0xc7, 0xc2, 0x0f, 0xe4, 0x84, 0x4b,
+ 0x12, 0x61, 0x3a, 0x6b, 0x7a, 0x1e, 0x8a, 0x81,
+ 0xa9, 0x9b, 0x11, 0x37, 0x25, 0x55, 0x73, 0x73,
+ 0x71, 0xa0, 0x84, 0xca, 0xc3, 0x4b, 0xb5, 0xcc,
+ 0x50, 0xa6, 0x46, 0xd7, 0xe8, 0x08, 0xaa, 0x04,
+ 0x28, 0xb1, 0x8e, 0xea, 0xb4, 0x4a, 0x49, 0x2b,
+ 0xd6, 0x0d, 0x59, 0x68, 0xda, 0x64, 0x29, 0x1f,
+ 0x85, 0x53, 0x72, 0xf1, 0xc5, 0x88, 0x1a, 0x0b,
+ 0x4f, 0x96, 0x43, 0xe0, 0x91, 0x89, 0xb9, 0xc0,
+ 0xe8, 0x18, 0xd5, 0x6e, 0x94, 0xe8, 0x35, 0x66,
+ 0x01, 0x94, 0x80, 0x95, 0x87, 0xe2, 0xc8, 0x19,
+ 0x73, 0xa3, 0x01, 0x05, 0xc1, 0x64, 0x72, 0xc9,
+ 0x6b, 0x6e, 0x55, 0x7c, 0x29, 0x67, 0x90, 0x93,
+ 0x49, 0xeb, 0xe3, 0x85, 0xc2, 0xf5, 0x79, 0x68,
+ 0x9d, 0x92, 0xc3, 0x32, 0x75, 0x80, 0x66, 0xf2,
+ 0x43, 0xa7, 0xb0, 0xc3, 0x22, 0x3f, 0x39, 0x8a,
+ 0x35, 0x5c, 0x63, 0x5c, 0xd1, 0x9e, 0x8a, 0xd2,
+ 0x78, 0x3c, 0x12, 0x01, 0x25, 0x04, 0x0e, 0x08,
+ 0x10, 0x88, 0xb6, 0x1b, 0xb7, 0x96, 0x35, 0xa8,
+ 0x0d, 0x1e, 0xae, 0xac, 0x4a, 0x70, 0xa5, 0x31,
+ 0xd0, 0x0c, 0x78, 0xbf, 0xdd, 0xc5, 0x24, 0x3e,
+ 0xcb, 0x0a, 0x0a, 0x69, 0x40, 0xba, 0xb0, 0xc4,
+ 0x2a, 0x9b, 0x1e, 0x0a, 0x51, 0xa6, 0x16, 0x98,
+ 0x76,
+#endif
+};
+
+static const sqfs_u8 blob_in_concat[] = {
+#if defined(DO_XZ)
+ 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x04,
+ 0xe6, 0xd6, 0xb4, 0x46, 0x02, 0x00, 0x21, 0x01,
+ 0x16, 0x00, 0x00, 0x00, 0x74, 0x2f, 0xe5, 0xa3,
+ 0xe0, 0x00, 0xdc, 0x00, 0xb3, 0x5d, 0x00, 0x26,
+ 0x1b, 0xca, 0x46, 0x67, 0x5a, 0xf2, 0x77, 0xb8,
+ 0x7d, 0x86, 0xd8, 0x41, 0xdb, 0x05, 0x35, 0xcd,
+ 0x83, 0xa5, 0x7c, 0x12, 0xa5, 0x05, 0xdb, 0x90,
+ 0xbd, 0x2f, 0x14, 0xd3, 0x71, 0x72, 0x96, 0xa8,
+ 0x8a, 0x7d, 0x84, 0x56, 0x71, 0x8d, 0x6a, 0x22,
+ 0x98, 0xab, 0x9e, 0x3d, 0xc3, 0x55, 0xef, 0xcc,
+ 0xa5, 0xc3, 0xdd, 0x5b, 0x8e, 0xbf, 0x03, 0x81,
+ 0x21, 0x40, 0xd6, 0x26, 0x91, 0x02, 0x45, 0x4e,
+ 0x20, 0x91, 0xcf, 0x8c, 0x51, 0x22, 0x02, 0x70,
+ 0xba, 0x05, 0x6b, 0x83, 0xef, 0x3f, 0x8e, 0x09,
+ 0xef, 0x88, 0xf5, 0x37, 0x1b, 0x89, 0x8d, 0xff,
+ 0x1e, 0xee, 0xe8, 0xb0, 0xac, 0xf2, 0x6e, 0xd4,
+ 0x3e, 0x25, 0xaf, 0xa0, 0x6d, 0x2e, 0xc0, 0x7f,
+ 0xb5, 0xa0, 0xcb, 0x90, 0x1f, 0x08, 0x1a, 0xe2,
+ 0x90, 0x20, 0x19, 0x71, 0x0c, 0xe8, 0x3f, 0xe5,
+ 0x39, 0xeb, 0x9a, 0x62, 0x4f, 0x06, 0xda, 0x3c,
+ 0x32, 0x59, 0xcc, 0x83, 0xe3, 0x83, 0x0f, 0x38,
+ 0x7d, 0x43, 0x37, 0x6c, 0x0b, 0x05, 0x65, 0x98,
+ 0x25, 0xdb, 0xf2, 0xc0, 0x2d, 0x39, 0x36, 0x5d,
+ 0xd4, 0xb6, 0xc2, 0x79, 0x73, 0x3e, 0xc2, 0x6e,
+ 0x54, 0xec, 0x78, 0x2b, 0x5d, 0xf1, 0xd1, 0xb4,
+ 0xb3, 0xcd, 0xf3, 0x89, 0xf5, 0x80, 0x79, 0x46,
+ 0xc0, 0x00, 0x00, 0x00, 0xc4, 0xf5, 0x1d, 0x08,
+ 0xf0, 0x34, 0x3a, 0x59, 0x00, 0x01, 0xcf, 0x01,
+ 0xdd, 0x01, 0x00, 0x00, 0x7f, 0x5a, 0x77, 0xcb,
+ 0xb1, 0xc4, 0x67, 0xfb, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x59, 0x5a,
+ 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x04,
+ 0xe6, 0xd6, 0xb4, 0x46, 0x02, 0x00, 0x21, 0x01,
+ 0x16, 0x00, 0x00, 0x00, 0x74, 0x2f, 0xe5, 0xa3,
+ 0xe0, 0x00, 0xe0, 0x00, 0xb7, 0x5d, 0x00, 0x31,
+ 0x9b, 0xca, 0x19, 0xc5, 0x54, 0xec, 0xb6, 0x54,
+ 0xe7, 0xb1, 0x7d, 0xc4, 0x57, 0x9e, 0x6c, 0x89,
+ 0xad, 0x4a, 0x6d, 0x16, 0xd8, 0x3c, 0x05, 0x94,
+ 0x10, 0x16, 0x99, 0x38, 0x21, 0xa3, 0xb9, 0xc5,
+ 0x80, 0xff, 0xfc, 0xee, 0xd4, 0xd5, 0x3f, 0xdd,
+ 0x8c, 0xd7, 0x3d, 0x8f, 0x76, 0xec, 0x96, 0x9d,
+ 0x20, 0xac, 0xcb, 0x18, 0xf5, 0xb2, 0x9c, 0x12,
+ 0xf6, 0x7c, 0x33, 0xdc, 0x4f, 0x9a, 0xe5, 0x2d,
+ 0x63, 0x68, 0xa4, 0x2b, 0x1d, 0x0a, 0x1e, 0xf0,
+ 0xfe, 0x73, 0xf2, 0x5f, 0x7b, 0xb4, 0xea, 0x54,
+ 0xad, 0x27, 0xd1, 0xff, 0xb6, 0x50, 0x06, 0x7b,
+ 0x51, 0x3f, 0x25, 0x8a, 0xcf, 0x4c, 0x03, 0x3e,
+ 0xc3, 0xad, 0x47, 0x34, 0xcf, 0xba, 0x45, 0x79,
+ 0xd0, 0x7b, 0xf6, 0x66, 0x63, 0xc0, 0xc6, 0x69,
+ 0xa7, 0x51, 0x84, 0xa8, 0xa0, 0x0b, 0xbc, 0x6f,
+ 0x13, 0x89, 0xd6, 0x5e, 0xac, 0xca, 0x2f, 0xd2,
+ 0xe7, 0xe1, 0x1e, 0x78, 0x22, 0x3a, 0x59, 0x6c,
+ 0x9c, 0x8c, 0x65, 0xf1, 0x5b, 0xf4, 0xbf, 0xd5,
+ 0xdc, 0x05, 0xeb, 0x70, 0x10, 0xb8, 0x6c, 0xf2,
+ 0x13, 0x20, 0xb0, 0xdd, 0x3e, 0xb2, 0x92, 0x5b,
+ 0xa3, 0xf7, 0x94, 0xa1, 0xa1, 0x74, 0x36, 0x9a,
+ 0xf1, 0xd8, 0xc2, 0xf0, 0xc6, 0x29, 0x7e, 0x85,
+ 0x28, 0xf5, 0xf2, 0x21, 0x00, 0x00, 0x00, 0x00,
+ 0xc8, 0x80, 0x67, 0x40, 0xc3, 0xaa, 0x17, 0x57,
+ 0x00, 0x01, 0xd3, 0x01, 0xe1, 0x01, 0x00, 0x00,
+ 0x86, 0xdf, 0x9e, 0x05, 0xb1, 0xc4, 0x67, 0xfb,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x59, 0x5a
+#elif defined(DO_BZIP2)
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26,
+ 0x53, 0x59, 0x5d, 0x09, 0x24, 0x1d, 0x00, 0x00,
+ 0x13, 0xd7, 0x80, 0x00, 0x10, 0x40, 0x05, 0x00,
+ 0x04, 0x02, 0x00, 0x3e, 0xa7, 0xff, 0x40, 0x30,
+ 0x00, 0xac, 0x43, 0x54, 0xf5, 0x36, 0x4c, 0xa7,
+ 0xa8, 0xd3, 0x6a, 0x60, 0x81, 0x40, 0x00, 0xd0,
+ 0x32, 0x64, 0x0d, 0x53, 0xda, 0x02, 0x09, 0xa2,
+ 0x68, 0x34, 0xd1, 0x27, 0x4a, 0xdd, 0xf2, 0x0a,
+ 0x73, 0x43, 0xf9, 0xa2, 0x51, 0x85, 0x76, 0x45,
+ 0x9a, 0x68, 0x3a, 0xe7, 0x0d, 0xc0, 0x21, 0x4a,
+ 0xc4, 0xf9, 0xf7, 0x40, 0xc3, 0x10, 0xb2, 0x9b,
+ 0x58, 0x56, 0x71, 0x50, 0x2f, 0xa4, 0xc5, 0x61,
+ 0x19, 0xf6, 0x59, 0x06, 0x82, 0x03, 0x7f, 0xeb,
+ 0xd2, 0x61, 0x88, 0xcd, 0xe8, 0xf7, 0xe8, 0x87,
+ 0x59, 0x9d, 0xe1, 0xf8, 0x19, 0x6e, 0xad, 0x77,
+ 0xbf, 0x34, 0x17, 0x21, 0x6b, 0x91, 0xc9, 0x52,
+ 0xd0, 0x81, 0x1e, 0xb5, 0x0b, 0xee, 0x42, 0x84,
+ 0x80, 0xd5, 0xa1, 0x8a, 0x04, 0x18, 0x4d, 0xf3,
+ 0xda, 0x7e, 0x3c, 0x40, 0xa4, 0xdb, 0xe5, 0xf0,
+ 0x37, 0x40, 0x3a, 0x7d, 0xa7, 0x45, 0x21, 0xf2,
+ 0x5a, 0x7b, 0x59, 0x56, 0x16, 0xd5, 0xac, 0x9f,
+ 0x60, 0x85, 0x0e, 0xf5, 0x73, 0xd9, 0x47, 0xe2,
+ 0xee, 0x48, 0xa7, 0x0a, 0x12, 0x0b, 0xa1, 0x24,
+ 0x83, 0xa0,
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26,
+ 0x53, 0x59, 0x2c, 0x24, 0x39, 0xa0, 0x00, 0x00,
+ 0x1f, 0x55, 0x80, 0x00, 0x10, 0x40, 0x05, 0x06,
+ 0x00, 0x3f, 0xe7, 0xff, 0x40, 0x30, 0x00, 0xb5,
+ 0x91, 0x13, 0x4f, 0x54, 0x7a, 0x6a, 0x6d, 0x4d,
+ 0xa2, 0x68, 0x0c, 0x84, 0x53, 0xf5, 0x30, 0x89,
+ 0xa3, 0xd4, 0x0d, 0x0f, 0x49, 0xa0, 0xd4, 0xf4,
+ 0xd1, 0x53, 0xf4, 0x93, 0x69, 0x3c, 0x81, 0x1a,
+ 0x65, 0x53, 0x90, 0x51, 0x07, 0x2a, 0xad, 0x8f,
+ 0x63, 0xba, 0x25, 0xc2, 0x0c, 0x8b, 0xb9, 0x95,
+ 0x15, 0xd8, 0xda, 0x61, 0x5c, 0xa9, 0xe4, 0x0b,
+ 0x21, 0xc9, 0x97, 0x57, 0x01, 0x28, 0x9b, 0xfb,
+ 0x94, 0xb9, 0x48, 0xa3, 0x0a, 0xc6, 0x1c, 0x54,
+ 0x98, 0x9a, 0x39, 0xc3, 0x87, 0x90, 0x33, 0x58,
+ 0x2d, 0x3e, 0x16, 0xb1, 0xae, 0x26, 0x89, 0x75,
+ 0xf5, 0x77, 0xa5, 0x8e, 0x5b, 0x8c, 0x8a, 0x39,
+ 0xbd, 0x75, 0x21, 0x9d, 0x99, 0x18, 0x4a, 0x91,
+ 0xab, 0xbc, 0x08, 0x87, 0xa4, 0xf1, 0x81, 0xb5,
+ 0xb4, 0xb0, 0xfe, 0x6b, 0x9f, 0xbe, 0x19, 0x82,
+ 0xd1, 0x50, 0xe1, 0x5e, 0x13, 0xb5, 0xc6, 0x2c,
+ 0xa4, 0x82, 0xf2, 0x5c, 0xc3, 0x20, 0x41, 0x13,
+ 0x56, 0x63, 0x3d, 0xec, 0x71, 0x2a, 0xbf, 0x2c,
+ 0x60, 0x2f, 0x7a, 0x4d, 0xcb, 0x3f, 0x8b, 0xb9,
+ 0x22, 0x9c, 0x28, 0x48, 0x16, 0x12, 0x1c, 0xd0,
+ 0x00,
+#elif defined(DO_ZSTD)
+ 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x58, 0x75, 0x04,
+ 0x00, 0xb2, 0x4c, 0x20, 0x17, 0xa0, 0x25, 0x69,
+ 0x03, 0xf0, 0xb2, 0x37, 0xb1, 0x5e, 0xb9, 0x24,
+ 0x56, 0x5b, 0x52, 0x22, 0x39, 0x01, 0x44, 0x2b,
+ 0x03, 0x55, 0xe3, 0x47, 0x03, 0x12, 0x9a, 0xe1,
+ 0xf0, 0x94, 0x0b, 0xe5, 0xe2, 0xba, 0x7e, 0xfe,
+ 0x9c, 0xc7, 0x61, 0x43, 0xc8, 0xfa, 0xf0, 0x3a,
+ 0xfa, 0x51, 0xaa, 0x50, 0xa6, 0x2d, 0x9a, 0x78,
+ 0xce, 0x2f, 0x61, 0x20, 0x6c, 0x7e, 0x35, 0x60,
+ 0xfb, 0xdd, 0x4c, 0x63, 0xfb, 0x95, 0x35, 0xc0,
+ 0x82, 0x59, 0xc2, 0xc9, 0x78, 0x6e, 0x30, 0xe6,
+ 0xd2, 0x72, 0x15, 0x14, 0x18, 0x62, 0x5d, 0xeb,
+ 0x2d, 0x9d, 0x3e, 0xee, 0x2e, 0x58, 0x58, 0xe9,
+ 0x40, 0x68, 0xb9, 0x2f, 0x23, 0x99, 0x2a, 0x4d,
+ 0xe8, 0x49, 0x79, 0x70, 0x1f, 0xf9, 0xe2, 0x34,
+ 0x2e, 0xab, 0xa5, 0xa3, 0xf2, 0x70, 0x98, 0xd0,
+ 0xb2, 0xb1, 0x3e, 0x5d, 0x90, 0x20, 0xd9, 0x36,
+ 0x8b, 0xdb, 0xaa, 0x20, 0x40, 0x03, 0x14, 0x06,
+ 0x03, 0x16, 0x2a, 0x9d, 0x31, 0xbd, 0x28, 0x3b,
+ 0x0c, 0xac, 0x41,
+ 0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x58, 0xbd, 0x04,
+ 0x00, 0x62, 0xcd, 0x22, 0x19, 0xa0, 0x25, 0x69,
+ 0x03, 0x60, 0x72, 0xc9, 0x36, 0xda, 0xd2, 0x8b,
+ 0xfc, 0xbf, 0x25, 0x42, 0xa9, 0x82, 0x38, 0x70,
+ 0x1a, 0x2e, 0x54, 0x95, 0x33, 0x02, 0x03, 0x51,
+ 0x36, 0x51, 0x80, 0xcc, 0x7a, 0x6e, 0x52, 0x2e,
+ 0x75, 0x64, 0x2d, 0x33, 0x2c, 0xd6, 0xdb, 0xfc,
+ 0x39, 0x31, 0xd5, 0xa8, 0xa2, 0x40, 0xd7, 0x12,
+ 0x4c, 0xc6, 0x76, 0xdc, 0x1e, 0x0f, 0xf4, 0x4e,
+ 0x0a, 0xd3, 0x0c, 0x87, 0x67, 0x25, 0x25, 0x52,
+ 0x66, 0x87, 0x95, 0xc6, 0x69, 0x0c, 0xb4, 0x5e,
+ 0x1d, 0xe7, 0x5e, 0xcd, 0x47, 0x41, 0x80, 0x89,
+ 0x5c, 0xa5, 0x4a, 0x32, 0x26, 0xb3, 0x3d, 0x2b,
+ 0xd5, 0xc0, 0x16, 0xde, 0xfb, 0x65, 0xcd, 0x6a,
+ 0x0c, 0x3f, 0xe7, 0xd6, 0xb2, 0x17, 0x7c, 0x25,
+ 0x35, 0x6b, 0x58, 0xf0, 0x95, 0xb5, 0xf2, 0xe4,
+ 0x4e, 0xf0, 0x34, 0x4f, 0x5f, 0x39, 0xd1, 0x90,
+ 0xf8, 0xb9, 0x59, 0xbe, 0x2e, 0xf9, 0xd4, 0x02,
+ 0x98, 0x50, 0x5a, 0xc2, 0xcf, 0xe1, 0x08, 0x02,
+ 0x00, 0x0f, 0x1e, 0x44, 0x40, 0x79, 0x50, 0x67,
+ 0x3d, 0xd3, 0x35, 0x8f,
+#elif defined(DO_GZIP)
+ 0,
+#endif
+};
+
+#if defined(DO_XZ)
+static size_t in_stop = 244;
+static size_t out_stop = 221;
+
+#define mkdecompressor decompressor_stream_xz_create
+#elif defined(DO_BZIP2)
+static size_t in_stop = 186;
+static size_t out_stop = 221;
+
+#define mkdecompressor decompressor_stream_bzip2_create
+#elif defined(DO_ZSTD)
+static size_t in_stop = 319;
+static size_t out_stop = 446;
+
+#define mkdecompressor decompressor_stream_zstd_create
+#elif defined(DO_GZIP)
+#define mkdecompressor decompressor_stream_gzip_create
+#endif
+
+static const char orig[] =
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n"
+"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"
+"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n"
+"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n"
+"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n"
+"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
+
+int main(int argc, char **argv)
+{
+ sqfs_u32 in_diff = 0, out_diff = 0;
+ xfrm_stream_t *xfrm;
+ char buffer[1024];
+ int ret;
+ (void)argc; (void)argv;
+
+ /* normal stream */
+ xfrm = mkdecompressor();
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ ret = xfrm->process_data(xfrm, blob_in, sizeof(blob_in),
+ buffer, sizeof(buffer),
+ &in_diff, &out_diff, XFRM_STREAM_FLUSH_FULL);
+ TEST_EQUAL_I(ret, XFRM_STREAM_END);
+
+ TEST_EQUAL_UI(in_diff, sizeof(blob_in));
+ TEST_EQUAL_UI(out_diff, sizeof(orig) - 1);
+ ret = memcmp(buffer, orig, out_diff);
+ TEST_EQUAL_I(ret, 0);
+
+ sqfs_drop(xfrm);
+
+ /* concatenated streams */
+#if !defined(DO_GZIP)
+ xfrm = mkdecompressor();
+ TEST_NOT_NULL(xfrm);
+ TEST_EQUAL_UI(((sqfs_object_t *)xfrm)->refcount, 1);
+
+ in_diff = 0;
+ out_diff = 0;
+
+ ret = xfrm->process_data(xfrm, blob_in_concat, sizeof(blob_in_concat),
+ buffer, sizeof(buffer),
+ &in_diff, &out_diff, XFRM_STREAM_FLUSH_FULL);
+ TEST_EQUAL_I(ret, XFRM_STREAM_END);
+
+ TEST_EQUAL_UI(in_diff, in_stop);
+ TEST_EQUAL_UI(out_diff, out_stop);
+ ret = memcmp(buffer, orig, out_diff);
+ TEST_EQUAL_I(ret, 0);
+
+ ret = xfrm->process_data(xfrm, blob_in_concat + in_diff,
+ sizeof(blob_in_concat) - in_diff,
+ buffer + out_diff, sizeof(buffer) - out_diff,
+ &in_diff, &out_diff, XFRM_STREAM_FLUSH_FULL);
+ TEST_EQUAL_I(ret, XFRM_STREAM_END);
+
+ TEST_EQUAL_UI(in_diff, sizeof(blob_in_concat));
+ TEST_EQUAL_UI(out_diff, sizeof(orig) - 1);
+ ret = memcmp(buffer, orig, out_diff);
+ TEST_EQUAL_I(ret, 0);
+
+ sqfs_drop(xfrm);
+#else
+ (void)blob_in_concat;
+#endif
+ return EXIT_SUCCESS;
+}