aboutsummaryrefslogtreecommitdiff
path: root/lib/sqfs/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfs/test')
-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
5 files changed, 952 insertions, 0 deletions
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;
+}