From 72c8155d9fc0eaeac72c053f46ebb7b231d4596a Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:30:46 +0100 Subject: Reintegrate test code with library code Signed-off-by: David Oberhollenzer --- lib/sqfs/test/abi.c | 174 ++++++++++++++++++++++ lib/sqfs/test/get_node_path.c | 132 ++++++++++++++++ lib/sqfs/test/table.c | 199 +++++++++++++++++++++++++ lib/sqfs/test/xattr_benchmark.c | 124 +++++++++++++++ lib/sqfs/test/xattr_writer.c | 323 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 952 insertions(+) create mode 100644 lib/sqfs/test/abi.c create mode 100644 lib/sqfs/test/get_node_path.c create mode 100644 lib/sqfs/test/table.c create mode 100644 lib/sqfs/test/xattr_benchmark.c create mode 100644 lib/sqfs/test/xattr_writer.c (limited to 'lib/sqfs/test') 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 + */ +#include "config.h" + +#include "sqfs/block_processor.h" +#include "sqfs/compressor.h" +#include "sqfs/block.h" +#include "util/test.h" + +#include + +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 + */ +#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 + */ +#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 **)©); + 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 + */ +#include "config.h" +#include "compat.h" +#include "common.h" + +#include "sqfs/xattr_writer.h" +#include "sqfs/xattr.h" + +#include +#include +#include +#include + +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 How many unique xattr blocks to generate.\n" +" --group-size, -g 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 + */ +#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; +} -- cgit v1.2.3