/* 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, 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_kv(xwr, "user.foobar", "test", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add_kv(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_kv(xwr, "user.foobar", "bla", 3); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add_kv(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_kv(xwr, "security.selinux", "Xwhatever", 9); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add_kv(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_kv(xwr, "user.foobar", "mimimi", 6); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add_kv(xwr, "security.selinux", "blub", 4); TEST_EQUAL_I(ret, 0); ret = sqfs_xattr_writer_add_kv(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_kv(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; }