From 9c9ef7cae619e95232f44be21d4648edb5f0777a Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Wed, 7 Jun 2023 23:10:41 +0200 Subject: libsquashfs: Add utility functions to read xattrs into list The common pattern is used in rdsquashfs and sqfs2tar, move the code to libsquashfs. Signed-off-by: David Oberhollenzer --- bin/rdsquashfs/src/dump_xattrs.c | 47 +++------ bin/rdsquashfs/src/rdsquashfs.h | 1 + bin/sqfs2tar/Makemodule.am | 3 +- bin/sqfs2tar/src/sqfs2tar.h | 4 - bin/sqfs2tar/src/write_tree.c | 11 +- bin/sqfs2tar/src/xattr.c | 84 --------------- include/sqfs/xattr_reader.h | 36 +++++++ lib/sqfs/src/xattr/xattr_reader.c | 216 +++++++++++++++++++++++++++++++------- 8 files changed, 236 insertions(+), 166 deletions(-) delete mode 100644 bin/sqfs2tar/src/xattr.c diff --git a/bin/rdsquashfs/src/dump_xattrs.c b/bin/rdsquashfs/src/dump_xattrs.c index 9dbe437..93f3635 100644 --- a/bin/rdsquashfs/src/dump_xattrs.c +++ b/bin/rdsquashfs/src/dump_xattrs.c @@ -63,58 +63,37 @@ static bool is_printable(const sqfs_u8 *value, size_t len) int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode) { - sqfs_xattr_value_t *value; - sqfs_xattr_entry_t *key; - sqfs_xattr_id_t desc; + sqfs_xattr_t *list; sqfs_u32 index; - size_t i; if (xattr == NULL) return 0; sqfs_inode_get_xattr_index(inode, &index); - if (index == 0xFFFFFFFF) - return 0; - - if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { - fputs("Error resolving xattr index\n", stderr); + if (sqfs_xattr_reader_read_all(xattr, index, &list)) { + fprintf(stderr, "Error loading xattr entries list #%08X\n", + index); return -1; } - if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { - fputs("Error locating xattr key-value pairs\n", stderr); - return -1; - } + for (const sqfs_xattr_t *ent = list; ent != NULL; ent = ent->next) { + size_t key_len = strlen(ent->key); - for (i = 0; i < desc.count; ++i) { - if (sqfs_xattr_reader_read_key(xattr, &key)) { - fputs("Error reading xattr key\n", stderr); - return -1; - } - - if (sqfs_xattr_reader_read_value(xattr, key, &value)) { - fputs("Error reading xattr value\n", stderr); - sqfs_free(key); - return -1; - } - - if (is_printable(key->key, key->size)) { - printf("%s=", key->key); + if (is_printable((const sqfs_u8 *)ent->key, key_len)) { + printf("%s=", ent->key); } else { - print_hex(key->key, key->size); + print_hex((const sqfs_u8 *)ent->key, key_len); } - if (is_printable(value->value, value->size)) { - printf("%s\n", value->value); + if (is_printable(ent->value, ent->value_len)) { + printf("%s\n", ent->value); } else { - print_hex(value->value, value->size); + print_hex(ent->value, ent->value_len); printf("\n"); } - - sqfs_free(key); - sqfs_free(value); } + sqfs_xattr_list_free(list); return 0; } diff --git a/bin/rdsquashfs/src/rdsquashfs.h b/bin/rdsquashfs/src/rdsquashfs.h index f9f75e9..364af14 100644 --- a/bin/rdsquashfs/src/rdsquashfs.h +++ b/bin/rdsquashfs/src/rdsquashfs.h @@ -10,6 +10,7 @@ #include "config.h" #include "common.h" #include "util/util.h" +#include "sqfs/xattr.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN diff --git a/bin/sqfs2tar/Makemodule.am b/bin/sqfs2tar/Makemodule.am index 2e6c411..9f7abf9 100644 --- a/bin/sqfs2tar/Makemodule.am +++ b/bin/sqfs2tar/Makemodule.am @@ -1,6 +1,5 @@ sqfs2tar_SOURCES = bin/sqfs2tar/src/sqfs2tar.c bin/sqfs2tar/src/sqfs2tar.h \ - bin/sqfs2tar/src/options.c bin/sqfs2tar/src/write_tree.c \ - bin/sqfs2tar/src/xattr.c + bin/sqfs2tar/src/options.c bin/sqfs2tar/src/write_tree.c sqfs2tar_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfs2tar_LDADD = libcommon.a libutil.a libsquashfs.la libtar.a sqfs2tar_LDADD += libio.a libxfrm.a libcompat.a libfstree.a diff --git a/bin/sqfs2tar/src/sqfs2tar.h b/bin/sqfs2tar/src/sqfs2tar.h index 3c7b3d1..e176008 100644 --- a/bin/sqfs2tar/src/sqfs2tar.h +++ b/bin/sqfs2tar/src/sqfs2tar.h @@ -46,10 +46,6 @@ extern ostream_t *out_file; char *assemble_tar_path(char *name, bool is_dir); -/* xattr.c */ -int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, - sqfs_xattr_t **out); - /* write_tree.c */ int write_tree(const sqfs_tree_node_t *n); diff --git a/bin/sqfs2tar/src/write_tree.c b/bin/sqfs2tar/src/write_tree.c index 71eb249..e578a9f 100644 --- a/bin/sqfs2tar/src/write_tree.c +++ b/bin/sqfs2tar/src/write_tree.c @@ -124,8 +124,15 @@ static int write_tree_dfs(const sqfs_tree_node_t *n) } } - if (!no_xattr) { - if (get_xattrs(name, n->inode, &xattr)) { + if (!no_xattr && xr != NULL) { + sqfs_u32 index; + int ret; + + sqfs_inode_get_xattr_index(n->inode, &index); + + ret = sqfs_xattr_reader_read_all(xr, index, &xattr); + if (ret) { + sqfs_perror(name, "resolving xattr index", ret); sqfs_free(name); return -1; } diff --git a/bin/sqfs2tar/src/xattr.c b/bin/sqfs2tar/src/xattr.c deleted file mode 100644 index 2af856d..0000000 --- a/bin/sqfs2tar/src/xattr.c +++ /dev/null @@ -1,84 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -static sqfs_xattr_t *mkxattr(const sqfs_xattr_entry_t *key, - const sqfs_xattr_value_t *value) -{ - sqfs_xattr_t *ent; - - ent = sqfs_xattr_create((const char *)key->key, - value->value, value->size); - if (ent == NULL) { - perror("creating xattr entry"); - return NULL; - } - - return ent; -} - -int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, - sqfs_xattr_t **out) -{ - sqfs_xattr_t *list = NULL, *ent; - sqfs_xattr_value_t *value; - sqfs_xattr_entry_t *key; - sqfs_xattr_id_t desc; - sqfs_u32 index; - size_t i; - int ret; - - if (xr == NULL) - return 0; - - sqfs_inode_get_xattr_index(inode, &index); - if (index == 0xFFFFFFFF) - return 0; - - ret = sqfs_xattr_reader_get_desc(xr, index, &desc); - if (ret) { - sqfs_perror(name, "resolving xattr index", ret); - return -1; - } - - ret = sqfs_xattr_reader_seek_kv(xr, &desc); - if (ret) { - sqfs_perror(name, "locating xattr key-value pairs", ret); - return -1; - } - - for (i = 0; i < desc.count; ++i) { - ret = sqfs_xattr_reader_read_key(xr, &key); - if (ret) { - sqfs_perror(name, "reading xattr key", ret); - goto fail; - } - - ret = sqfs_xattr_reader_read_value(xr, key, &value); - if (ret) { - sqfs_perror(name, "reading xattr value", ret); - sqfs_free(key); - goto fail; - } - - ent = mkxattr(key, value); - sqfs_free(key); - sqfs_free(value); - - if (ent == NULL) - goto fail; - - ent->next = list; - list = ent; - } - - *out = list; - return 0; -fail: - sqfs_xattr_list_free(list); - return -1; -} diff --git a/include/sqfs/xattr_reader.h b/include/sqfs/xattr_reader.h index a0242fb..2c65afa 100644 --- a/include/sqfs/xattr_reader.h +++ b/include/sqfs/xattr_reader.h @@ -190,6 +190,42 @@ int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, const sqfs_xattr_entry_t *key, sqfs_xattr_value_t **val_out); +/** + * @brief Read a combined key-value pair into an @ref sqfs_xattr_t struct + * + * @memberof sqfs_xattr_reader_t + * + * @param xr A pointer to an xattr reader instance. + * @param out Returns a pointer to a combined key-value struct. + * + * @return Zero on success, a negative @ref SQFS_ERROR value on failure. + */ +SQFS_API +int sqfs_xattr_reader_read(sqfs_xattr_reader_t *xr, sqfs_xattr_t **out); + +/** + * @brief Read all xattrs associated with an ID into memory + * + * @memberof sqfs_xattr_reader_t + * + * This is a convenience function that internally resolves the xattr index + * using @ref sqfs_xattr_reader_get_desc, seeks to the key-value list using + * @ref sqfs_xattr_reader_seek_kv and reads all the entries into a liked + * list of @ref sqfs_xattr_t structs. + * + * If the index is the special value 0xFFFFFFFF, the function successfully + * returns an empty list. + * + * @param xr A pointer to an xattr reader instance. + * @param idx An xattr index. + * @param out Returns a linked list to key-value pairs on success. + * + * @return Zero on success, a negative @ref SQFS_ERROR value on failure. + */ +SQFS_API int sqfs_xattr_reader_read_all(sqfs_xattr_reader_t *xr, sqfs_u32 idx, + sqfs_xattr_t **out); + + #ifdef __cplusplus } #endif diff --git a/lib/sqfs/src/xattr/xattr_reader.c b/lib/sqfs/src/xattr/xattr_reader.c index 9e3ea76..1c8ea37 100644 --- a/lib/sqfs/src/xattr/xattr_reader.c +++ b/lib/sqfs/src/xattr/xattr_reader.c @@ -166,6 +166,66 @@ fail_blocks: return err; } +static int read_key_hdr(sqfs_xattr_reader_t *xr, sqfs_xattr_entry_t *key, + const char **prefix) +{ + 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; + + return 0; +} + +static int read_value_hdr(sqfs_xattr_reader_t *xr, + const sqfs_xattr_entry_t *key, sqfs_u64 *start, + size_t *offset, sqfs_xattr_value_t *value) +{ + sqfs_u64 ref, new_start; + size_t new_offset; + 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; + + ref = le64toh(ref); + new_start = xr->xattr_start + (ref >> 16); + new_offset = ref & 0xFFFF; + + if (new_start >= xr->xattr_end || + new_offset >= SQFS_META_BLOCK_SIZE) { + return SQFS_ERROR_OUT_OF_BOUNDS; + } + + sqfs_meta_reader_get_position(xr->kvrd, start, offset); + + ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->kvrd, value, sizeof(*value)); + if (ret) + return ret; + } + + value->size = le32toh(value->size); + return 0; +} + int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, sqfs_xattr_entry_t **key_out) { @@ -174,17 +234,10 @@ int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, size_t plen, total; int ret; - ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); + ret = read_key_hdr(xr, &key, &prefix); 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) || @@ -213,66 +266,107 @@ 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; + sqfs_xattr_value_t value, *out = NULL; + size_t size, offset = 0; + sqfs_u64 start = 0; int ret; - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + ret = read_value_hdr(xr, key, &start, &offset, &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); + size = sizeof(*out) + 1; + if (SZ_ADD_OV(size, value.size, &size)) + return SQFS_ERROR_OVERFLOW; - new_start = xr->xattr_start + (ref >> 16); - if (new_start >= xr->xattr_end) - return SQFS_ERROR_OUT_OF_BOUNDS; + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; - new_offset = ref & 0xFFFF; - if (new_offset >= SQFS_META_BLOCK_SIZE) - return SQFS_ERROR_OUT_OF_BOUNDS; + *out = value; - ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); - if (ret) - return ret; + ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); + if (ret) + goto fail; - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); if (ret) - return ret; + goto fail; } - value.size = le32toh(value.size); + *val_out = out; + return 0; +fail: + free(out); + return ret; +} + +int sqfs_xattr_reader_read(sqfs_xattr_reader_t *xr, sqfs_xattr_t **out) +{ + sqfs_xattr_t *kv = NULL, *new = NULL; + size_t plen, total = 0, offset = 0; + sqfs_xattr_value_t value; + sqfs_xattr_entry_t key; + const char *prefix; + sqfs_u64 start = 0; + int ret; - if (SZ_ADD_OV(sizeof(*out), value.size, &size) || - SZ_ADD_OV(size, 1, &size)) { + /* read and decode the key */ + ret = read_key_hdr(xr, &key, &prefix); + if (ret) + return ret; + + plen = strlen(prefix); + total = sizeof(*kv) + plen + 1; + + if (SZ_ADD_OV(total, key.size, &total)) return SQFS_ERROR_OVERFLOW; - } - out = calloc(1, size); - if (out == NULL) + kv = calloc(1, total); + if (kv == NULL) return SQFS_ERROR_ALLOC; - *out = value; + memcpy(kv->data, prefix, plen); + ret = sqfs_meta_reader_read(xr->kvrd, kv->data + plen, key.size); + if (ret) + goto fail; - ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); + /* read and decode the value */ + ret = read_value_hdr(xr, &key, &start, &offset, &value); if (ret) goto fail; - if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = SQFS_ERROR_OVERFLOW; + if (SZ_ADD_OV(total, value.size, &total) || SZ_ADD_OV(total, 1, &total)) + goto fail; + + ret = SQFS_ERROR_ALLOC; + new = realloc(kv, total); + if (new == NULL) + goto fail; + kv = new; + + ret = sqfs_meta_reader_read(xr->kvrd, kv->data + plen + key.size + 1, + 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; + /* prefix with the kv struct */ + kv->value_len = value.size; + kv->key = (const char *)kv->data; + kv->value = kv->data + plen + key.size + 1; + kv->data[plen + key.size + 1 + value.size] = '\0'; + *out = kv; return 0; fail: - free(out); + free(kv); return ret; } @@ -320,6 +414,48 @@ int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, return 0; } +int sqfs_xattr_reader_read_all(sqfs_xattr_reader_t *xr, sqfs_u32 idx, + sqfs_xattr_t **out) +{ + sqfs_xattr_t *head = NULL, *tail = NULL; + sqfs_xattr_id_t desc; + int ret; + + *out = NULL; + if (idx == 0xFFFFFFFF) + return 0; + + ret = sqfs_xattr_reader_get_desc(xr, idx, &desc); + if (ret) + return ret; + + ret = sqfs_xattr_reader_seek_kv(xr, &desc); + if (ret) + return ret; + + for (size_t i = 0; i < desc.count; ++i) { + sqfs_xattr_t *ent; + + ret = sqfs_xattr_reader_read(xr, &ent); + if (ret) + goto fail; + + if (tail == NULL) { + head = ent; + tail = ent; + } else { + tail->next = ent; + tail = ent; + } + } + + *out = head; + return 0; +fail: + sqfs_xattr_list_free(head); + return ret; +} + sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) { sqfs_xattr_reader_t *xr; -- cgit v1.2.3