From 1f210bab667a540379243050b5ce3465086e1124 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sun, 7 Jun 2020 23:38:06 +0200 Subject: Cleanup: split libsquashfs xattr writer code This commit moves the libsquashfs xattr related code into a sub directory and splits the xattr writer code up into several files. No actual code is changed. Signed-off-by: David Oberhollenzer --- lib/sqfs/Makemodule.am | 11 +- lib/sqfs/xattr.c | 49 --- lib/sqfs/xattr/xattr.c | 49 +++ lib/sqfs/xattr/xattr_reader.c | 349 ++++++++++++++++++ lib/sqfs/xattr/xattr_writer.c | 110 ++++++ lib/sqfs/xattr/xattr_writer.h | 63 ++++ lib/sqfs/xattr/xattr_writer_flush.c | 342 ++++++++++++++++++ lib/sqfs/xattr/xattr_writer_record.c | 179 ++++++++++ lib/sqfs/xattr_reader.c | 349 ------------------ lib/sqfs/xattr_writer.c | 671 ----------------------------------- 10 files changed, 1099 insertions(+), 1073 deletions(-) delete mode 100644 lib/sqfs/xattr.c create mode 100644 lib/sqfs/xattr/xattr.c create mode 100644 lib/sqfs/xattr/xattr_reader.c create mode 100644 lib/sqfs/xattr/xattr_writer.c create mode 100644 lib/sqfs/xattr/xattr_writer.h create mode 100644 lib/sqfs/xattr/xattr_writer_flush.c create mode 100644 lib/sqfs/xattr/xattr_writer_record.c delete mode 100644 lib/sqfs/xattr_reader.c delete mode 100644 lib/sqfs/xattr_writer.c diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am index 61c9b09..85f4aad 100644 --- a/lib/sqfs/Makemodule.am +++ b/lib/sqfs/Makemodule.am @@ -11,15 +11,18 @@ LIBSQFS_HEARDS = include/sqfs/meta_writer.h \ include/sqfs/frag_table.h include/sqfs/block_writer.h libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/id_table.c lib/sqfs/super.c -libsquashfs_la_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr.c +libsquashfs_la_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr/xattr.c libsquashfs_la_SOURCES += lib/sqfs/write_table.c lib/sqfs/meta_writer.c libsquashfs_la_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c libsquashfs_la_SOURCES += lib/sqfs/read_inode.c lib/sqfs/write_inode.c -libsquashfs_la_SOURCES += lib/sqfs/dir_writer.c lib/sqfs/xattr_reader.c +libsquashfs_la_SOURCES += lib/sqfs/dir_writer.c lib/sqfs/xattr/xattr_reader.c libsquashfs_la_SOURCES += lib/sqfs/read_table.c lib/sqfs/comp/compressor.c -libsquashfs_la_SOURCES += lib/sqfs/comp/internal.h lib/sqfs/xattr_writer.c +libsquashfs_la_SOURCES += lib/sqfs/comp/internal.h libsquashfs_la_SOURCES += lib/sqfs/dir_reader.c lib/sqfs/read_tree.c -libsquashfs_la_SOURCES += lib/sqfs/inode.c +libsquashfs_la_SOURCES += lib/sqfs/inode.c lib/sqfs/xattr/xattr_writer.c +libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_flush.c +libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_record.c +libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer.h libsquashfs_la_SOURCES += lib/sqfs/write_super.c lib/sqfs/data_reader.c libsquashfs_la_SOURCES += lib/sqfs/block_processor/internal.h libsquashfs_la_SOURCES += lib/sqfs/block_processor/common.c diff --git a/lib/sqfs/xattr.c b/lib/sqfs/xattr.c deleted file mode 100644 index 29ecebf..0000000 --- a/lib/sqfs/xattr.c +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" - -#include - -static const struct { - const char *prefix; - SQFS_XATTR_TYPE type; -} xattr_types[] = { - { "user.", SQFS_XATTR_USER }, - { "trusted.", SQFS_XATTR_TRUSTED }, - { "security.", SQFS_XATTR_SECURITY }, -}; - -int sqfs_get_xattr_prefix_id(const char *key) -{ - size_t i, len; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - len = strlen(xattr_types[i].prefix); - - if (strncmp(key, xattr_types[i].prefix, len) == 0 && - strlen(key) > len) { - return xattr_types[i].type; - } - } - - return SQFS_ERROR_UNSUPPORTED; -} - -const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) -{ - size_t i; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - if (xattr_types[i].type == id) - return xattr_types[i].prefix; - } - - return NULL; -} diff --git a/lib/sqfs/xattr/xattr.c b/lib/sqfs/xattr/xattr.c new file mode 100644 index 0000000..29ecebf --- /dev/null +++ b/lib/sqfs/xattr/xattr.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" + +#include + +static const struct { + const char *prefix; + SQFS_XATTR_TYPE type; +} xattr_types[] = { + { "user.", SQFS_XATTR_USER }, + { "trusted.", SQFS_XATTR_TRUSTED }, + { "security.", SQFS_XATTR_SECURITY }, +}; + +int sqfs_get_xattr_prefix_id(const char *key) +{ + size_t i, len; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + len = strlen(xattr_types[i].prefix); + + if (strncmp(key, xattr_types[i].prefix, len) == 0 && + strlen(key) > len) { + return xattr_types[i].type; + } + } + + return SQFS_ERROR_UNSUPPORTED; +} + +const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) +{ + size_t i; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + if (xattr_types[i].type == id) + return xattr_types[i].prefix; + } + + return NULL; +} diff --git a/lib/sqfs/xattr/xattr_reader.c b/lib/sqfs/xattr/xattr_reader.c new file mode 100644 index 0000000..37f222a --- /dev/null +++ b/lib/sqfs/xattr/xattr_reader.c @@ -0,0 +1,349 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_reader.h" +#include "sqfs/meta_reader.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util.h" + +#include +#include +#include + +struct sqfs_xattr_reader_t { + sqfs_object_t base; + + sqfs_u64 xattr_start; + sqfs_u64 xattr_end; + + size_t num_id_blocks; + size_t num_ids; + + sqfs_u64 *id_block_starts; + + sqfs_meta_reader_t *idrd; + sqfs_meta_reader_t *kvrd; +}; + +static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; + sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); + + if (copy == NULL) + return NULL; + + memcpy(copy, xr, sizeof(*xr)); + + if (xr->kvrd != NULL) { + copy->kvrd = sqfs_copy(xr->kvrd); + if (copy->kvrd == NULL) + goto fail_kvrd; + } + + if (xr->idrd != NULL) { + copy->idrd = sqfs_copy(xr->idrd); + if (copy->idrd == NULL) + goto fail_idrd; + } + + if (xr->id_block_starts != NULL) { + copy->id_block_starts = alloc_array(sizeof(sqfs_u64), + xr->num_id_blocks); + if (copy->id_block_starts == NULL) + goto fail_idblk; + + memcpy(copy->id_block_starts, xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + } + + return (sqfs_object_t *)copy; +fail_idblk: + if (copy->idrd != NULL) + sqfs_destroy(copy->idrd); +fail_idrd: + if (copy->kvrd != NULL) + sqfs_destroy(copy->kvrd); +fail_kvrd: + free(copy); + return NULL; +} + +static void xattr_reader_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; + + if (xr->kvrd != NULL) + sqfs_destroy(xr->kvrd); + + if (xr->idrd != NULL) + sqfs_destroy(xr->idrd); + + free(xr->id_block_starts); + free(xr); +} + +int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, + sqfs_file_t *file, sqfs_compressor_t *cmp) +{ + sqfs_xattr_id_table_t idtbl; + size_t i; + int err; + + /* sanity check */ + if (super->flags & SQFS_FLAG_NO_XATTRS) + return 0; + + if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) + return 0; + + if (super->xattr_id_table_start >= super->bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + /* cleanup pre-existing data */ + if (xr->idrd != NULL) { + sqfs_destroy(xr->idrd); + xr->idrd = NULL; + } + + if (xr->kvrd != NULL) { + sqfs_destroy(xr->kvrd); + xr->kvrd = NULL; + } + + free(xr->id_block_starts); + xr->id_block_starts = NULL; + + /* read the locations table */ + err = file->read_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + xr->xattr_start = le64toh(idtbl.xattr_table_start); + xr->num_ids = le32toh(idtbl.xattr_ids); + xr->num_id_blocks = + (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; + + if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) + xr->num_id_blocks += 1; + + xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); + if (xr->id_block_starts == NULL) { + if (errno == EOVERFLOW) + return SQFS_ERROR_OVERFLOW; + return SQFS_ERROR_ALLOC; + } + + err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), + xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + if (err) + goto fail_blocks; + + for (i = 0; i < xr->num_id_blocks; ++i) { + xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); + + if (xr->id_block_starts[i] > super->bytes_used) { + err = SQFS_ERROR_OUT_OF_BOUNDS; + goto fail_blocks; + } + } + + /* create the meta data readers */ + xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->idrd == NULL) + goto fail_blocks; + + xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->kvrd == NULL) + goto fail_idrd; + + xr->xattr_end = super->bytes_used; + return 0; +fail_idrd: + sqfs_destroy(xr->idrd); + xr->idrd = NULL; +fail_blocks: + free(xr->id_block_starts); + xr->id_block_starts = NULL; + return err; +} + +int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, + sqfs_xattr_entry_t **key_out) +{ + sqfs_xattr_entry_t key, *out; + const char *prefix; + size_t plen, total; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); + if (ret) + return ret; + + key.type = le16toh(key.type); + key.size = le16toh(key.size); + + prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); + if (prefix == NULL) + return SQFS_ERROR_UNSUPPORTED; + + plen = strlen(prefix); + + if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || + SZ_ADD_OV(sizeof(*out), total, &total)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, total); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = key; + memcpy(out->key, prefix, plen); + + ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); + if (ret) { + free(out); + return ret; + } + + *key_out = out; + return 0; +} + +int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, + const sqfs_xattr_entry_t *key, + sqfs_xattr_value_t **val_out) +{ + size_t offset, new_offset, size; + sqfs_xattr_value_t value, *out; + sqfs_u64 ref, start, new_start; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (ret) + return ret; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); + if (ret) + return ret; + + sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); + + new_start = xr->xattr_start + (ref >> 16); + if (new_start >= xr->xattr_end) + return SQFS_ERROR_OUT_OF_BOUNDS; + + new_offset = ref & 0xFFFF; + if (new_offset >= SQFS_META_BLOCK_SIZE) + return SQFS_ERROR_OUT_OF_BOUNDS; + + ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); + if (ret) + return ret; + } + + value.size = le32toh(value.size); + + if (SZ_ADD_OV(sizeof(*out), value.size, &size) || + SZ_ADD_OV(size, 1, &size)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = value; + + ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); + if (ret) + goto fail; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); + if (ret) + goto fail; + } + + *val_out = out; + return 0; +fail: + free(out); + return ret; +} + +int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, + const sqfs_xattr_id_t *desc) +{ + sqfs_u32 offset = desc->xattr & 0xFFFF; + sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); + + return sqfs_meta_reader_seek(xr->kvrd, block, offset); +} + +int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, + sqfs_xattr_id_t *desc) +{ + size_t block, offset; + int ret; + + memset(desc, 0, sizeof(*desc)); + + if (idx == 0xFFFFFFFF) + return 0; + + if (xr->kvrd == NULL || xr->idrd == NULL) + return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; + + if (idx >= xr->num_ids) + return SQFS_ERROR_OUT_OF_BOUNDS; + + offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; + block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; + + ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], + offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); + if (ret) + return ret; + + desc->xattr = le64toh(desc->xattr); + desc->count = le32toh(desc->count); + desc->size = le32toh(desc->size); + return 0; +} + +sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) +{ + sqfs_xattr_reader_t *xr; + + if (flags != 0) + return NULL; + + xr = calloc(1, sizeof(*xr)); + if (xr == NULL) + return NULL; + + ((sqfs_object_t *)xr)->copy = xattr_reader_copy; + ((sqfs_object_t *)xr)->destroy = xattr_reader_destroy; + return xr; +} diff --git a/lib/sqfs/xattr/xattr_writer.c b/lib/sqfs/xattr/xattr_writer.c new file mode 100644 index 0000000..9de3823 --- /dev/null +++ b/lib/sqfs/xattr/xattr_writer.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; + kv_block_desc_t *blk, *it, **next; + sqfs_xattr_writer_t *copy; + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, xwr, sizeof(*xwr)); + + if (str_table_copy(©->keys, &xwr->keys)) + goto fail_keys; + + if (str_table_copy(©->values, &xwr->values)) + goto fail_values; + + copy->max_pairs = xwr->num_pairs; + copy->num_pairs = xwr->num_pairs; + + copy->kv_pairs = malloc(sizeof(copy->kv_pairs[0]) * xwr->num_pairs); + if (copy->kv_pairs == NULL) + goto fail_pairs; + + memcpy(copy->kv_pairs, xwr->kv_pairs, + sizeof(copy->kv_pairs[0]) * xwr->num_pairs); + + next = &(copy->kv_blocks); + + for (it = xwr->kv_blocks; it != NULL; it = it->next) { + blk = malloc(sizeof(*blk)); + if (blk == NULL) + goto fail_blk; + + memcpy(blk, it, sizeof(*it)); + blk->next = NULL; + + *next = blk; + next = &(blk->next); + } + + return (sqfs_object_t *)copy; +fail_blk: + while (copy->kv_blocks != NULL) { + blk = copy->kv_blocks; + copy->kv_blocks = copy->kv_blocks->next; + free(blk); + } +fail_pairs: + str_table_cleanup(©->values); +fail_values: + str_table_cleanup(©->keys); +fail_keys: + free(copy); + return NULL; +} + +static void xattr_writer_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; + kv_block_desc_t *blk; + + while (xwr->kv_blocks != NULL) { + blk = xwr->kv_blocks; + xwr->kv_blocks = xwr->kv_blocks->next; + free(blk); + } + + free(xwr->kv_pairs); + str_table_cleanup(&xwr->values); + str_table_cleanup(&xwr->keys); + free(xwr); +} + +sqfs_xattr_writer_t *sqfs_xattr_writer_create(void) +{ + sqfs_xattr_writer_t *xwr = calloc(1, sizeof(*xwr)); + + if (str_table_init(&xwr->keys, XATTR_KEY_BUCKETS)) + goto fail_keys; + + if (str_table_init(&xwr->values, XATTR_VALUE_BUCKETS)) + goto fail_values; + + xwr->max_pairs = XATTR_INITIAL_PAIR_CAP; + xwr->kv_pairs = alloc_array(sizeof(xwr->kv_pairs[0]), xwr->max_pairs); + + if (xwr->kv_pairs == NULL) + goto fail_pairs; + + ((sqfs_object_t *)xwr)->copy = xattr_writer_copy; + ((sqfs_object_t *)xwr)->destroy = xattr_writer_destroy; + return xwr; +fail_pairs: + str_table_cleanup(&xwr->values); +fail_values: + str_table_cleanup(&xwr->keys); +fail_keys: + free(xwr); + return NULL; +} diff --git a/lib/sqfs/xattr/xattr_writer.h b/lib/sqfs/xattr/xattr_writer.h new file mode 100644 index 0000000..826b6f6 --- /dev/null +++ b/lib/sqfs/xattr/xattr_writer.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef XATTR_WRITER_H +#define XATTR_WRITER_H + +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_writer.h" +#include "sqfs/meta_writer.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "str_table.h" +#include "util.h" + +#include +#include +#include + + +#define XATTR_KEY_BUCKETS 31 +#define XATTR_VALUE_BUCKETS 511 +#define XATTR_INITIAL_PAIR_CAP 128 + +#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) +#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) +#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) + + +typedef struct kv_block_desc_t { + struct kv_block_desc_t *next; + size_t start; + size_t count; + + sqfs_u64 start_ref; + size_t size_bytes; +} kv_block_desc_t; + +struct sqfs_xattr_writer_t { + sqfs_object_t base; + + str_table_t keys; + str_table_t values; + + sqfs_u64 *kv_pairs; + size_t max_pairs; + size_t num_pairs; + + size_t kv_start; + + kv_block_desc_t *kv_blocks; + size_t num_blocks; +}; + +#endif /* XATTR_WRITER_H */ diff --git a/lib/sqfs/xattr/xattr_writer_flush.c b/lib/sqfs/xattr/xattr_writer_flush.c new file mode 100644 index 0000000..9b3de30 --- /dev/null +++ b/lib/sqfs/xattr/xattr_writer_flush.c @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_flush.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static void *from_base32(const char *input, size_t *size_out) +{ + sqfs_u8 lo, hi, *out, *ptr; + size_t len; + + len = strlen(input); + *size_out = len / 2; + + out = malloc(*size_out); + if (out == NULL) + return NULL; + + ptr = out; + + while (*input != '\0') { + lo = strchr(hexmap, *(input++)) - hexmap; + hi = strchr(hexmap, *(input++)) - hexmap; + + *(ptr++) = lo | (hi << 4); + } + + return out; +} + +static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, + bool value_is_ool) +{ + sqfs_xattr_entry_t kent; + int type, err; + size_t len; + + type = sqfs_get_xattr_prefix_id(key); + assert(type >= 0); + + key = strchr(key, '.'); + assert(key != NULL); + ++key; + len = strlen(key); + + if (value_is_ool) + type |= SQFS_XATTR_FLAG_OOL; + + memset(&kent, 0, sizeof(kent)); + kent.type = htole16(type); + kent.size = htole16(len); + + err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, key, len); + if (err) + return err; + + return sizeof(kent) + len; +} + +static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, + sqfs_u64 *value_ref_out) +{ + sqfs_xattr_value_t vent; + sqfs_u32 offset; + sqfs_u64 block; + size_t size; + void *value; + int err; + + value = from_base32(value_str, &size); + if (value == NULL) + return SQFS_ERROR_ALLOC; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(size); + + sqfs_meta_writer_get_position(mw, &block, &offset); + *value_ref_out = (block << 16) | (offset & 0xFFFF); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + goto fail; + + err = sqfs_meta_writer_append(mw, value, size); + if (err) + goto fail; + + free(value); + return sizeof(vent) + size; +fail: + free(value); + return err; +} + +static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) +{ + sqfs_xattr_value_t vent; + sqfs_u64 ref; + int err; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(sizeof(location)); + ref = htole64(location); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); + if (err) + return err; + + return sizeof(vent) + sizeof(ref); +} + +static bool should_store_ool(const char *val_str, size_t refcount) +{ + if (refcount < 2) + return false; + + /* + Storing in line needs this many bytes: refcount * len + + Storing out-of-line needs this many: len + (refcount - 1) * 8 + + Out-of-line prefereable iff refcount > 1 and: + refcount * len > len + (refcount - 1) * 8 + => refcount * len - len > (refcount - 1) * 8 + => (refcount - 1) * len > (refcount - 1) * 8 + => len > 8 + */ + return (strlen(val_str) / 2) > sizeof(sqfs_u64); +} + +static int write_block_pairs(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, + kv_block_desc_t *blk, sqfs_u64 *ool_locations) +{ + sqfs_u32 key_idx, val_idx; + const char *key_str, *value_str; + sqfs_s32 diff, total = 0; + size_t i, refcount; + sqfs_u64 ref; + + for (i = 0; i < blk->count; ++i) { + key_idx = GET_KEY(xwr->kv_pairs[blk->start + i]); + val_idx = GET_VALUE(xwr->kv_pairs[blk->start + i]); + + key_str = str_table_get_string(&xwr->keys, key_idx); + value_str = str_table_get_string(&xwr->values, val_idx); + + if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { + diff = write_key(mw, key_str, false); + if (diff < 0) + return diff; + total += diff; + + diff = write_value(mw, value_str, &ref); + if (diff < 0) + return diff; + total += diff; + + refcount = str_table_get_ref_count(&xwr->values, + val_idx); + + if (should_store_ool(value_str, refcount)) + ool_locations[val_idx] = ref; + } else { + diff = write_key(mw, key_str, true); + if (diff < 0) + return diff; + total += diff; + + diff = write_value_ool(mw, ool_locations[val_idx]); + if (diff < 0) + return diff; + total += diff; + } + } + + return total; +} + +static int write_kv_pairs(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw) +{ + sqfs_u64 block, *ool_locations; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_s32 size; + size_t i; + + ool_locations = alloc_array(sizeof(ool_locations[0]), + xwr->values.num_strings); + if (ool_locations == NULL) + return SQFS_ERROR_ALLOC; + + for (i = 0; i < xwr->values.num_strings; ++i) + ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; + + for (blk = xwr->kv_blocks; blk != NULL; blk = blk->next) { + sqfs_meta_writer_get_position(mw, &block, &offset); + blk->start_ref = (block << 16) | (offset & 0xFFFF); + + size = write_block_pairs(xwr, mw, blk, ool_locations); + if (size < 0) { + free(ool_locations); + return size; + } + + blk->size_bytes = size; + } + + free(ool_locations); + return sqfs_meta_writer_flush(mw); +} + +static int write_id_table(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, + sqfs_u64 *locations) +{ + sqfs_xattr_id_t id_ent; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_u64 block; + size_t i = 0; + int err; + + locations[i++] = 0; + + for (blk = xwr->kv_blocks; blk != NULL; blk = blk->next) { + memset(&id_ent, 0, sizeof(id_ent)); + id_ent.xattr = htole64(blk->start_ref); + id_ent.count = htole32(blk->count); + id_ent.size = htole32(blk->size_bytes); + + err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); + if (err) + return err; + + sqfs_meta_writer_get_position(mw, &block, &offset); + if (block != locations[i - 1]) + locations[i++] = block; + } + + return sqfs_meta_writer_flush(mw); +} + +static int write_location_table(sqfs_xattr_writer_t *xwr, sqfs_u64 kv_start, + sqfs_file_t *file, const sqfs_super_t *super, + sqfs_u64 *locations, size_t loc_count) +{ + sqfs_xattr_id_table_t idtbl; + int err; + + memset(&idtbl, 0, sizeof(idtbl)); + idtbl.xattr_table_start = htole64(kv_start); + idtbl.xattr_ids = htole32(xwr->num_blocks); + + err = file->write_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), + locations, sizeof(locations[0]) * loc_count); +} + +static int alloc_location_table(sqfs_xattr_writer_t *xwr, sqfs_u64 **tbl_out, + size_t *szout) +{ + sqfs_u64 *locations; + size_t size, count; + + if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) + return SQFS_ERROR_OVERFLOW; + + count = size / SQFS_META_BLOCK_SIZE; + if (size % SQFS_META_BLOCK_SIZE) + ++count; + + locations = alloc_array(sizeof(sqfs_u64), count); + if (locations == NULL) + return SQFS_ERROR_ALLOC; + + *tbl_out = locations; + *szout = count; + return 0; +} + +int sqfs_xattr_writer_flush(sqfs_xattr_writer_t *xwr, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 *locations = NULL, kv_start, id_start; + sqfs_meta_writer_t *mw; + size_t i, count; + int err; + + if (xwr->num_pairs == 0 || xwr->num_blocks == 0) { + super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->flags |= SQFS_FLAG_NO_XATTRS; + return 0; + } + + mw = sqfs_meta_writer_create(file, cmp, 0); + if (mw == NULL) + return SQFS_ERROR_ALLOC; + + kv_start = file->get_size(file); + err = write_kv_pairs(xwr, mw); + if (err) + goto out; + + sqfs_meta_writer_reset(mw); + + id_start = file->get_size(file); + err = alloc_location_table(xwr, &locations, &count); + if (err) + goto out; + + err = write_id_table(xwr, mw, locations); + if (err) + goto out; + + super->xattr_id_table_start = file->get_size(file); + super->flags &= ~SQFS_FLAG_NO_XATTRS; + + for (i = 0; i < count; ++i) + locations[i] = htole64(locations[i] + id_start); + + err = write_location_table(xwr, kv_start, file, super, + locations, count); +out: + free(locations); + sqfs_destroy(mw); + return err; +} diff --git a/lib/sqfs/xattr/xattr_writer_record.c b/lib/sqfs/xattr/xattr_writer_record.c new file mode 100644 index 0000000..5e97868 --- /dev/null +++ b/lib/sqfs/xattr/xattr_writer_record.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_record.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static char *to_base32(const void *input, size_t size) +{ + const sqfs_u8 *in = input; + char *out, *ptr; + size_t i; + + out = malloc(2 * size + 1); + if (out == NULL) + return NULL; + + ptr = out; + + for (i = 0; i < size; ++i) { + *(ptr++) = hexmap[ in[i] & 0x0F]; + *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; + } + + *ptr = '\0'; + return out; +} + +static int compare_u64(const void *a, const void *b) +{ + sqfs_u64 lhs = *((const sqfs_u64 *)a); + sqfs_u64 rhs = *((const sqfs_u64 *)b); + + return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); +} + +int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr) +{ + xwr->kv_start = xwr->num_pairs; + return 0; +} + +int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, + const void *value, size_t size) +{ + size_t i, key_index, old_value_index, value_index, new_count; + sqfs_u64 kv_pair, *new; + char *value_str; + int err; + + if (sqfs_get_xattr_prefix_id(key) < 0) + return SQFS_ERROR_UNSUPPORTED; + + /* resolve key and value into unique, incremental IDs */ + err = str_table_get_index(&xwr->keys, key, &key_index); + if (err) + return err; + + value_str = to_base32(value, size); + if (value_str == NULL) + return SQFS_ERROR_ALLOC; + + err = str_table_get_index(&xwr->values, value_str, &value_index); + free(value_str); + if (err) + return err; + + str_table_add_ref(&xwr->values, value_index); + + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + /* bail if already have the pair, overwrite if we have the key */ + kv_pair = MK_PAIR(key_index, value_index); + + for (i = xwr->kv_start; i < xwr->num_pairs; ++i) { + if (xwr->kv_pairs[i] == kv_pair) + return 0; + + if (GET_KEY(xwr->kv_pairs[i]) == key_index) { + old_value_index = GET_VALUE(xwr->kv_pairs[i]); + + str_table_del_ref(&xwr->values, old_value_index); + + xwr->kv_pairs[i] = kv_pair; + return 0; + } + } + + /* append it to the list */ + if (xwr->max_pairs == xwr->num_pairs) { + new_count = xwr->max_pairs * 2; + new = realloc(xwr->kv_pairs, + sizeof(xwr->kv_pairs[0]) * new_count); + + if (new == NULL) + return SQFS_ERROR_ALLOC; + + xwr->kv_pairs = new; + xwr->max_pairs = new_count; + } + + xwr->kv_pairs[xwr->num_pairs++] = kv_pair; + return 0; +} + +int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) +{ + kv_block_desc_t *blk, *blk_prev; + size_t i, count, value_idx; + sqfs_u32 index; + int ret; + + count = xwr->num_pairs - xwr->kv_start; + if (count == 0) { + *out = 0xFFFFFFFF; + return 0; + } + + qsort(xwr->kv_pairs + xwr->kv_start, count, + sizeof(xwr->kv_pairs[0]), compare_u64); + + blk_prev = NULL; + blk = xwr->kv_blocks; + index = 0; + + while (blk != NULL) { + if (blk->count == count) { + ret = memcmp(xwr->kv_pairs + blk->start, + xwr->kv_pairs + xwr->kv_start, + sizeof(xwr->kv_pairs[0]) * count); + + if (ret == 0) + break; + } + + if (index == 0xFFFFFFFF) + return SQFS_ERROR_OVERFLOW; + + ++index; + blk_prev = blk; + blk = blk->next; + } + + if (blk != NULL) { + for (i = 0; i < count; ++i) { + value_idx = GET_VALUE(xwr->kv_pairs[xwr->kv_start + i]); + str_table_del_ref(&xwr->values, value_idx); + + value_idx = GET_VALUE(xwr->kv_pairs[blk->start + i]); + str_table_add_ref(&xwr->values, value_idx); + } + + xwr->num_pairs = xwr->kv_start; + } else { + blk = calloc(1, sizeof(*blk)); + if (blk == NULL) + return SQFS_ERROR_ALLOC; + + blk->start = xwr->kv_start; + blk->count = count; + + if (blk_prev == NULL) { + xwr->kv_blocks = blk; + } else { + blk_prev->next = blk; + } + + xwr->num_blocks += 1; + } + + *out = index; + return 0; +} diff --git a/lib/sqfs/xattr_reader.c b/lib/sqfs/xattr_reader.c deleted file mode 100644 index 37f222a..0000000 --- a/lib/sqfs/xattr_reader.c +++ /dev/null @@ -1,349 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_reader.h" -#include "sqfs/meta_reader.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util.h" - -#include -#include -#include - -struct sqfs_xattr_reader_t { - sqfs_object_t base; - - sqfs_u64 xattr_start; - sqfs_u64 xattr_end; - - size_t num_id_blocks; - size_t num_ids; - - sqfs_u64 *id_block_starts; - - sqfs_meta_reader_t *idrd; - sqfs_meta_reader_t *kvrd; -}; - -static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; - sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); - - if (copy == NULL) - return NULL; - - memcpy(copy, xr, sizeof(*xr)); - - if (xr->kvrd != NULL) { - copy->kvrd = sqfs_copy(xr->kvrd); - if (copy->kvrd == NULL) - goto fail_kvrd; - } - - if (xr->idrd != NULL) { - copy->idrd = sqfs_copy(xr->idrd); - if (copy->idrd == NULL) - goto fail_idrd; - } - - if (xr->id_block_starts != NULL) { - copy->id_block_starts = alloc_array(sizeof(sqfs_u64), - xr->num_id_blocks); - if (copy->id_block_starts == NULL) - goto fail_idblk; - - memcpy(copy->id_block_starts, xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - } - - return (sqfs_object_t *)copy; -fail_idblk: - if (copy->idrd != NULL) - sqfs_destroy(copy->idrd); -fail_idrd: - if (copy->kvrd != NULL) - sqfs_destroy(copy->kvrd); -fail_kvrd: - free(copy); - return NULL; -} - -static void xattr_reader_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; - - if (xr->kvrd != NULL) - sqfs_destroy(xr->kvrd); - - if (xr->idrd != NULL) - sqfs_destroy(xr->idrd); - - free(xr->id_block_starts); - free(xr); -} - -int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, - sqfs_file_t *file, sqfs_compressor_t *cmp) -{ - sqfs_xattr_id_table_t idtbl; - size_t i; - int err; - - /* sanity check */ - if (super->flags & SQFS_FLAG_NO_XATTRS) - return 0; - - if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) - return 0; - - if (super->xattr_id_table_start >= super->bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - /* cleanup pre-existing data */ - if (xr->idrd != NULL) { - sqfs_destroy(xr->idrd); - xr->idrd = NULL; - } - - if (xr->kvrd != NULL) { - sqfs_destroy(xr->kvrd); - xr->kvrd = NULL; - } - - free(xr->id_block_starts); - xr->id_block_starts = NULL; - - /* read the locations table */ - err = file->read_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - xr->xattr_start = le64toh(idtbl.xattr_table_start); - xr->num_ids = le32toh(idtbl.xattr_ids); - xr->num_id_blocks = - (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; - - if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) - xr->num_id_blocks += 1; - - xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); - if (xr->id_block_starts == NULL) { - if (errno == EOVERFLOW) - return SQFS_ERROR_OVERFLOW; - return SQFS_ERROR_ALLOC; - } - - err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), - xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - if (err) - goto fail_blocks; - - for (i = 0; i < xr->num_id_blocks; ++i) { - xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); - - if (xr->id_block_starts[i] > super->bytes_used) { - err = SQFS_ERROR_OUT_OF_BOUNDS; - goto fail_blocks; - } - } - - /* create the meta data readers */ - xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->idrd == NULL) - goto fail_blocks; - - xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->kvrd == NULL) - goto fail_idrd; - - xr->xattr_end = super->bytes_used; - return 0; -fail_idrd: - sqfs_destroy(xr->idrd); - xr->idrd = NULL; -fail_blocks: - free(xr->id_block_starts); - xr->id_block_starts = NULL; - return err; -} - -int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, - sqfs_xattr_entry_t **key_out) -{ - sqfs_xattr_entry_t key, *out; - const char *prefix; - size_t plen, total; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); - if (ret) - return ret; - - key.type = le16toh(key.type); - key.size = le16toh(key.size); - - prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); - if (prefix == NULL) - return SQFS_ERROR_UNSUPPORTED; - - plen = strlen(prefix); - - if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || - SZ_ADD_OV(sizeof(*out), total, &total)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, total); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = key; - memcpy(out->key, prefix, plen); - - ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); - if (ret) { - free(out); - return ret; - } - - *key_out = out; - return 0; -} - -int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, - const sqfs_xattr_entry_t *key, - sqfs_xattr_value_t **val_out) -{ - size_t offset, new_offset, size; - sqfs_xattr_value_t value, *out; - sqfs_u64 ref, start, new_start; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); - if (ret) - return ret; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); - if (ret) - return ret; - - sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); - - new_start = xr->xattr_start + (ref >> 16); - if (new_start >= xr->xattr_end) - return SQFS_ERROR_OUT_OF_BOUNDS; - - new_offset = ref & 0xFFFF; - if (new_offset >= SQFS_META_BLOCK_SIZE) - return SQFS_ERROR_OUT_OF_BOUNDS; - - ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); - if (ret) - return ret; - } - - value.size = le32toh(value.size); - - if (SZ_ADD_OV(sizeof(*out), value.size, &size) || - SZ_ADD_OV(size, 1, &size)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, size); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = value; - - ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); - if (ret) - goto fail; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); - if (ret) - goto fail; - } - - *val_out = out; - return 0; -fail: - free(out); - return ret; -} - -int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, - const sqfs_xattr_id_t *desc) -{ - sqfs_u32 offset = desc->xattr & 0xFFFF; - sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); - - return sqfs_meta_reader_seek(xr->kvrd, block, offset); -} - -int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, - sqfs_xattr_id_t *desc) -{ - size_t block, offset; - int ret; - - memset(desc, 0, sizeof(*desc)); - - if (idx == 0xFFFFFFFF) - return 0; - - if (xr->kvrd == NULL || xr->idrd == NULL) - return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; - - if (idx >= xr->num_ids) - return SQFS_ERROR_OUT_OF_BOUNDS; - - offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; - block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; - - ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], - offset); - if (ret) - return ret; - - ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); - if (ret) - return ret; - - desc->xattr = le64toh(desc->xattr); - desc->count = le32toh(desc->count); - desc->size = le32toh(desc->size); - return 0; -} - -sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) -{ - sqfs_xattr_reader_t *xr; - - if (flags != 0) - return NULL; - - xr = calloc(1, sizeof(*xr)); - if (xr == NULL) - return NULL; - - ((sqfs_object_t *)xr)->copy = xattr_reader_copy; - ((sqfs_object_t *)xr)->destroy = xattr_reader_destroy; - return xr; -} diff --git a/lib/sqfs/xattr_writer.c b/lib/sqfs/xattr_writer.c deleted file mode 100644 index 488fd43..0000000 --- a/lib/sqfs/xattr_writer.c +++ /dev/null @@ -1,671 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_writer.h" -#include "sqfs/meta_writer.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "str_table.h" -#include "util.h" - -#include -#include -#include - - -#define XATTR_KEY_BUCKETS 31 -#define XATTR_VALUE_BUCKETS 511 -#define XATTR_INITIAL_PAIR_CAP 128 - -#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) -#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) -#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) - - -static const char *hexmap = "0123456789ABCDEF"; - -static char *to_base32(const void *input, size_t size) -{ - const sqfs_u8 *in = input; - char *out, *ptr; - size_t i; - - out = malloc(2 * size + 1); - if (out == NULL) - return NULL; - - ptr = out; - - for (i = 0; i < size; ++i) { - *(ptr++) = hexmap[ in[i] & 0x0F]; - *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; - } - - *ptr = '\0'; - return out; -} - -static void *from_base32(const char *input, size_t *size_out) -{ - sqfs_u8 lo, hi, *out, *ptr; - size_t len; - - len = strlen(input); - *size_out = len / 2; - - out = malloc(*size_out); - if (out == NULL) - return NULL; - - ptr = out; - - while (*input != '\0') { - lo = strchr(hexmap, *(input++)) - hexmap; - hi = strchr(hexmap, *(input++)) - hexmap; - - *(ptr++) = lo | (hi << 4); - } - - return out; -} - -static int compare_u64(const void *a, const void *b) -{ - sqfs_u64 lhs = *((const sqfs_u64 *)a); - sqfs_u64 rhs = *((const sqfs_u64 *)b); - - return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); -} - - - -typedef struct kv_block_desc_t { - struct kv_block_desc_t *next; - size_t start; - size_t count; - - sqfs_u64 start_ref; - size_t size_bytes; -} kv_block_desc_t; - -struct sqfs_xattr_writer_t { - sqfs_object_t base; - - str_table_t keys; - str_table_t values; - - sqfs_u64 *kv_pairs; - size_t max_pairs; - size_t num_pairs; - - size_t kv_start; - - kv_block_desc_t *kv_blocks; - size_t num_blocks; -}; - - -static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; - kv_block_desc_t *blk, *it, **next; - sqfs_xattr_writer_t *copy; - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, xwr, sizeof(*xwr)); - - if (str_table_copy(©->keys, &xwr->keys)) - goto fail_keys; - - if (str_table_copy(©->values, &xwr->values)) - goto fail_values; - - copy->max_pairs = xwr->num_pairs; - copy->num_pairs = xwr->num_pairs; - - copy->kv_pairs = malloc(sizeof(copy->kv_pairs[0]) * xwr->num_pairs); - if (copy->kv_pairs == NULL) - goto fail_pairs; - - memcpy(copy->kv_pairs, xwr->kv_pairs, - sizeof(copy->kv_pairs[0]) * xwr->num_pairs); - - next = &(copy->kv_blocks); - - for (it = xwr->kv_blocks; it != NULL; it = it->next) { - blk = malloc(sizeof(*blk)); - if (blk == NULL) - goto fail_blk; - - memcpy(blk, it, sizeof(*it)); - blk->next = NULL; - - *next = blk; - next = &(blk->next); - } - - return (sqfs_object_t *)copy; -fail_blk: - while (copy->kv_blocks != NULL) { - blk = copy->kv_blocks; - copy->kv_blocks = copy->kv_blocks->next; - free(blk); - } -fail_pairs: - str_table_cleanup(©->values); -fail_values: - str_table_cleanup(©->keys); -fail_keys: - free(copy); - return NULL; -} - -static void xattr_writer_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; - kv_block_desc_t *blk; - - while (xwr->kv_blocks != NULL) { - blk = xwr->kv_blocks; - xwr->kv_blocks = xwr->kv_blocks->next; - free(blk); - } - - free(xwr->kv_pairs); - str_table_cleanup(&xwr->values); - str_table_cleanup(&xwr->keys); - free(xwr); -} - -sqfs_xattr_writer_t *sqfs_xattr_writer_create(void) -{ - sqfs_xattr_writer_t *xwr = calloc(1, sizeof(*xwr)); - - if (str_table_init(&xwr->keys, XATTR_KEY_BUCKETS)) - goto fail_keys; - - if (str_table_init(&xwr->values, XATTR_VALUE_BUCKETS)) - goto fail_values; - - xwr->max_pairs = XATTR_INITIAL_PAIR_CAP; - xwr->kv_pairs = alloc_array(sizeof(xwr->kv_pairs[0]), xwr->max_pairs); - - if (xwr->kv_pairs == NULL) - goto fail_pairs; - - ((sqfs_object_t *)xwr)->copy = xattr_writer_copy; - ((sqfs_object_t *)xwr)->destroy = xattr_writer_destroy; - return xwr; -fail_pairs: - str_table_cleanup(&xwr->values); -fail_values: - str_table_cleanup(&xwr->keys); -fail_keys: - free(xwr); - return NULL; -} - -int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr) -{ - xwr->kv_start = xwr->num_pairs; - return 0; -} - -int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, - const void *value, size_t size) -{ - size_t i, key_index, old_value_index, value_index, new_count; - sqfs_u64 kv_pair, *new; - char *value_str; - int err; - - if (sqfs_get_xattr_prefix_id(key) < 0) - return SQFS_ERROR_UNSUPPORTED; - - /* resolve key and value into unique, incremental IDs */ - err = str_table_get_index(&xwr->keys, key, &key_index); - if (err) - return err; - - value_str = to_base32(value, size); - if (value_str == NULL) - return SQFS_ERROR_ALLOC; - - err = str_table_get_index(&xwr->values, value_str, &value_index); - free(value_str); - if (err) - return err; - - str_table_add_ref(&xwr->values, value_index); - - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - /* bail if already have the pair, overwrite if we have the key */ - kv_pair = MK_PAIR(key_index, value_index); - - for (i = xwr->kv_start; i < xwr->num_pairs; ++i) { - if (xwr->kv_pairs[i] == kv_pair) - return 0; - - if (GET_KEY(xwr->kv_pairs[i]) == key_index) { - old_value_index = GET_VALUE(xwr->kv_pairs[i]); - - str_table_del_ref(&xwr->values, old_value_index); - - xwr->kv_pairs[i] = kv_pair; - return 0; - } - } - - /* append it to the list */ - if (xwr->max_pairs == xwr->num_pairs) { - new_count = xwr->max_pairs * 2; - new = realloc(xwr->kv_pairs, - sizeof(xwr->kv_pairs[0]) * new_count); - - if (new == NULL) - return SQFS_ERROR_ALLOC; - - xwr->kv_pairs = new; - xwr->max_pairs = new_count; - } - - xwr->kv_pairs[xwr->num_pairs++] = kv_pair; - return 0; -} - -int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) -{ - kv_block_desc_t *blk, *blk_prev; - size_t i, count, value_idx; - sqfs_u32 index; - int ret; - - count = xwr->num_pairs - xwr->kv_start; - if (count == 0) { - *out = 0xFFFFFFFF; - return 0; - } - - qsort(xwr->kv_pairs + xwr->kv_start, count, - sizeof(xwr->kv_pairs[0]), compare_u64); - - blk_prev = NULL; - blk = xwr->kv_blocks; - index = 0; - - while (blk != NULL) { - if (blk->count == count) { - ret = memcmp(xwr->kv_pairs + blk->start, - xwr->kv_pairs + xwr->kv_start, - sizeof(xwr->kv_pairs[0]) * count); - - if (ret == 0) - break; - } - - if (index == 0xFFFFFFFF) - return SQFS_ERROR_OVERFLOW; - - ++index; - blk_prev = blk; - blk = blk->next; - } - - if (blk != NULL) { - for (i = 0; i < count; ++i) { - value_idx = GET_VALUE(xwr->kv_pairs[xwr->kv_start + i]); - str_table_del_ref(&xwr->values, value_idx); - - value_idx = GET_VALUE(xwr->kv_pairs[blk->start + i]); - str_table_add_ref(&xwr->values, value_idx); - } - - xwr->num_pairs = xwr->kv_start; - } else { - blk = calloc(1, sizeof(*blk)); - if (blk == NULL) - return SQFS_ERROR_ALLOC; - - blk->start = xwr->kv_start; - blk->count = count; - - if (blk_prev == NULL) { - xwr->kv_blocks = blk; - } else { - blk_prev->next = blk; - } - - xwr->num_blocks += 1; - } - - *out = index; - return 0; -} - -/*****************************************************************************/ - -static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, - bool value_is_ool) -{ - sqfs_xattr_entry_t kent; - int type, err; - size_t len; - - type = sqfs_get_xattr_prefix_id(key); - assert(type >= 0); - - key = strchr(key, '.'); - assert(key != NULL); - ++key; - len = strlen(key); - - if (value_is_ool) - type |= SQFS_XATTR_FLAG_OOL; - - memset(&kent, 0, sizeof(kent)); - kent.type = htole16(type); - kent.size = htole16(len); - - err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, key, len); - if (err) - return err; - - return sizeof(kent) + len; -} - -static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, - sqfs_u64 *value_ref_out) -{ - sqfs_xattr_value_t vent; - sqfs_u32 offset; - sqfs_u64 block; - size_t size; - void *value; - int err; - - value = from_base32(value_str, &size); - if (value == NULL) - return SQFS_ERROR_ALLOC; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(size); - - sqfs_meta_writer_get_position(mw, &block, &offset); - *value_ref_out = (block << 16) | (offset & 0xFFFF); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - goto fail; - - err = sqfs_meta_writer_append(mw, value, size); - if (err) - goto fail; - - free(value); - return sizeof(vent) + size; -fail: - free(value); - return err; -} - -static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) -{ - sqfs_xattr_value_t vent; - sqfs_u64 ref; - int err; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(sizeof(location)); - ref = htole64(location); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); - if (err) - return err; - - return sizeof(vent) + sizeof(ref); -} - -static bool should_store_ool(const char *val_str, size_t refcount) -{ - if (refcount < 2) - return false; - - /* - Storing in line needs this many bytes: refcount * len - - Storing out-of-line needs this many: len + (refcount - 1) * 8 - - Out-of-line prefereable iff refcount > 1 and: - refcount * len > len + (refcount - 1) * 8 - => refcount * len - len > (refcount - 1) * 8 - => (refcount - 1) * len > (refcount - 1) * 8 - => len > 8 - */ - return (strlen(val_str) / 2) > sizeof(sqfs_u64); -} - -static int write_block_pairs(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, - kv_block_desc_t *blk, sqfs_u64 *ool_locations) -{ - sqfs_u32 key_idx, val_idx; - const char *key_str, *value_str; - sqfs_s32 diff, total = 0; - size_t i, refcount; - sqfs_u64 ref; - - for (i = 0; i < blk->count; ++i) { - key_idx = GET_KEY(xwr->kv_pairs[blk->start + i]); - val_idx = GET_VALUE(xwr->kv_pairs[blk->start + i]); - - key_str = str_table_get_string(&xwr->keys, key_idx); - value_str = str_table_get_string(&xwr->values, val_idx); - - if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { - diff = write_key(mw, key_str, false); - if (diff < 0) - return diff; - total += diff; - - diff = write_value(mw, value_str, &ref); - if (diff < 0) - return diff; - total += diff; - - refcount = str_table_get_ref_count(&xwr->values, - val_idx); - - if (should_store_ool(value_str, refcount)) - ool_locations[val_idx] = ref; - } else { - diff = write_key(mw, key_str, true); - if (diff < 0) - return diff; - total += diff; - - diff = write_value_ool(mw, ool_locations[val_idx]); - if (diff < 0) - return diff; - total += diff; - } - } - - return total; -} - -static int write_kv_pairs(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw) -{ - sqfs_u64 block, *ool_locations; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_s32 size; - size_t i; - - ool_locations = alloc_array(sizeof(ool_locations[0]), - xwr->values.num_strings); - if (ool_locations == NULL) - return SQFS_ERROR_ALLOC; - - for (i = 0; i < xwr->values.num_strings; ++i) - ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; - - for (blk = xwr->kv_blocks; blk != NULL; blk = blk->next) { - sqfs_meta_writer_get_position(mw, &block, &offset); - blk->start_ref = (block << 16) | (offset & 0xFFFF); - - size = write_block_pairs(xwr, mw, blk, ool_locations); - if (size < 0) { - free(ool_locations); - return size; - } - - blk->size_bytes = size; - } - - free(ool_locations); - return sqfs_meta_writer_flush(mw); -} - -static int write_id_table(sqfs_xattr_writer_t *xwr, sqfs_meta_writer_t *mw, - sqfs_u64 *locations) -{ - sqfs_xattr_id_t id_ent; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_u64 block; - size_t i = 0; - int err; - - locations[i++] = 0; - - for (blk = xwr->kv_blocks; blk != NULL; blk = blk->next) { - memset(&id_ent, 0, sizeof(id_ent)); - id_ent.xattr = htole64(blk->start_ref); - id_ent.count = htole32(blk->count); - id_ent.size = htole32(blk->size_bytes); - - err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); - if (err) - return err; - - sqfs_meta_writer_get_position(mw, &block, &offset); - if (block != locations[i - 1]) - locations[i++] = block; - } - - return sqfs_meta_writer_flush(mw); -} - -static int write_location_table(sqfs_xattr_writer_t *xwr, sqfs_u64 kv_start, - sqfs_file_t *file, const sqfs_super_t *super, - sqfs_u64 *locations, size_t loc_count) -{ - sqfs_xattr_id_table_t idtbl; - int err; - - memset(&idtbl, 0, sizeof(idtbl)); - idtbl.xattr_table_start = htole64(kv_start); - idtbl.xattr_ids = htole32(xwr->num_blocks); - - err = file->write_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), - locations, sizeof(locations[0]) * loc_count); -} - -static int alloc_location_table(sqfs_xattr_writer_t *xwr, sqfs_u64 **tbl_out, - size_t *szout) -{ - sqfs_u64 *locations; - size_t size, count; - - if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) - return SQFS_ERROR_OVERFLOW; - - count = size / SQFS_META_BLOCK_SIZE; - if (size % SQFS_META_BLOCK_SIZE) - ++count; - - locations = alloc_array(sizeof(sqfs_u64), count); - if (locations == NULL) - return SQFS_ERROR_ALLOC; - - *tbl_out = locations; - *szout = count; - return 0; -} - -int sqfs_xattr_writer_flush(sqfs_xattr_writer_t *xwr, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 *locations = NULL, kv_start, id_start; - sqfs_meta_writer_t *mw; - size_t i, count; - int err; - - if (xwr->num_pairs == 0 || xwr->num_blocks == 0) { - super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->flags |= SQFS_FLAG_NO_XATTRS; - return 0; - } - - mw = sqfs_meta_writer_create(file, cmp, 0); - if (mw == NULL) - return SQFS_ERROR_ALLOC; - - kv_start = file->get_size(file); - err = write_kv_pairs(xwr, mw); - if (err) - goto out; - - sqfs_meta_writer_reset(mw); - - id_start = file->get_size(file); - err = alloc_location_table(xwr, &locations, &count); - if (err) - goto out; - - err = write_id_table(xwr, mw, locations); - if (err) - goto out; - - super->xattr_id_table_start = file->get_size(file); - super->flags &= ~SQFS_FLAG_NO_XATTRS; - - for (i = 0; i < count; ++i) - locations[i] = htole64(locations[i] + id_start); - - err = write_location_table(xwr, kv_start, file, super, - locations, count); -out: - free(locations); - sqfs_destroy(mw); - return err; -} -- cgit v1.2.3