aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-08-01 13:50:05 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-08-01 15:48:42 +0200
commit23a3f10ab62abadf5adcfb9540f2180275ab5b0c (patch)
treecfe537d4359de7dd3486898dad70c40761947680
parent7073f002b54c690ffa17d9f736a4417485d3c6d6 (diff)
Add xattr reader implementation to recover xattrs from squashfs
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--include/meta_reader.h3
-rw-r--r--include/squashfs.h6
-rw-r--r--include/xattr_reader.h26
-rw-r--r--lib/Makemodule.am6
-rw-r--r--lib/sqfs/meta_reader.c7
-rw-r--r--lib/sqfs/write_xattr.c32
-rw-r--r--lib/sqfs/xattr.c52
-rw-r--r--lib/sqfs/xattr_reader.c372
8 files changed, 470 insertions, 34 deletions
diff --git a/include/meta_reader.h b/include/meta_reader.h
index 2d790d8..8369ad3 100644
--- a/include/meta_reader.h
+++ b/include/meta_reader.h
@@ -24,6 +24,9 @@ void meta_reader_destroy(meta_reader_t *m);
int meta_reader_seek(meta_reader_t *m, uint64_t block_start,
size_t offset);
+void meta_reader_get_position(meta_reader_t *m, uint64_t *block_start,
+ size_t *offset);
+
/* Returns 0 on success. Internally prints to stderr on failure */
int meta_reader_read(meta_reader_t *m, void *data, size_t size);
diff --git a/include/squashfs.h b/include/squashfs.h
index bd3db7b..fb60049 100644
--- a/include/squashfs.h
+++ b/include/squashfs.h
@@ -256,6 +256,12 @@ int sqfs_super_write(sqfs_super_t *super, int fd);
/* Returns 0 on success. Prints error messages to stderr on failure. */
int sqfs_super_read(sqfs_super_t *super, int fd);
+/* Get id from xattr key prefix or -1 if not supported */
+int sqfs_get_xattr_prefix_id(const char *key);
+
+/* Get a prefix string from the ID or NULL if unknown */
+const char *sqfs_get_xattr_prefix(E_SQFS_XATTR_TYPE id);
+
/* Check if a given xattr key can be encoded in squashfs at all. */
bool sqfs_has_xattr(const char *key);
diff --git a/include/xattr_reader.h b/include/xattr_reader.h
new file mode 100644
index 0000000..a71f0f0
--- /dev/null
+++ b/include/xattr_reader.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xattr_reader.h
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#ifndef XATTR_READER_H
+#define XATTR_READER_H
+
+#include "config.h"
+
+#include "squashfs.h"
+#include "compress.h"
+#include "fstree.h"
+
+typedef struct xattr_reader_t xattr_reader_t;
+
+void xattr_reader_destroy(xattr_reader_t *xr);
+
+xattr_reader_t *xattr_reader_create(int sqfsfd, sqfs_super_t *super,
+ compressor_t *cmp);
+
+int xattr_reader_restore_node(xattr_reader_t *xr, fstree_t *fs,
+ tree_node_t *node, uint32_t xattr);
+
+#endif /* XATTR_READER_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 4288ef1..5a8ffaa 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -28,13 +28,13 @@ libsquashfs_a_SOURCES += lib/sqfs/write_table.c include/highlevel.h
libsquashfs_a_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c
libsquashfs_a_SOURCES += include/meta_reader.h lib/sqfs/id_table_write.c
libsquashfs_a_SOURCES += lib/sqfs/id_table_read.c lib/sqfs/read_inode.c
-libsquashfs_a_SOURCES += lib/sqfs/readdir.c
+libsquashfs_a_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr_reader.c
libsquashfs_a_SOURCES += lib/sqfs/write_dir.c lib/sqfs/write_inode.c
-libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.c
+libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.c lib/sqfs/xattr.c
libsquashfs_a_SOURCES += lib/sqfs/tree_node_from_inode.c
libsquashfs_a_SOURCES += lib/sqfs/deserialize_fstree.c
libsquashfs_a_SOURCES += lib/sqfs/data_writer.c lib/sqfs/write_xattr.c
-libsquashfs_a_SOURCES += include/data_writer.h
+libsquashfs_a_SOURCES += include/data_writer.h include/xattr_reader.h
libsquashfs_a_SOURCES += include/data_reader.h lib/sqfs/data_reader.c
libsquashfs_a_SOURCES += lib/sqfs/write_export_table.c
libsquashfs_a_SOURCES += lib/sqfs/read_table.c lib/sqfs/statistics.c
diff --git a/lib/sqfs/meta_reader.c b/lib/sqfs/meta_reader.c
index 6547440..711e8b2 100644
--- a/lib/sqfs/meta_reader.c
+++ b/lib/sqfs/meta_reader.c
@@ -117,6 +117,13 @@ int meta_reader_seek(meta_reader_t *m, uint64_t block_start, size_t offset)
return 0;
}
+void meta_reader_get_position(meta_reader_t *m, uint64_t *block_start,
+ size_t *offset)
+{
+ *block_start = m->block_offset;
+ *offset = m->offset;
+}
+
int meta_reader_read(meta_reader_t *m, void *data, size_t size)
{
size_t diff;
diff --git a/lib/sqfs/write_xattr.c b/lib/sqfs/write_xattr.c
index 98829e8..195de0d 100644
--- a/lib/sqfs/write_xattr.c
+++ b/lib/sqfs/write_xattr.c
@@ -15,43 +15,13 @@
#include <string.h>
#include <stdio.h>
-static const struct {
- const char *prefix;
- E_SQFS_XATTR_TYPE type;
-} xattr_types[] = {
- { "user.", SQUASHFS_XATTR_USER },
- { "trusted.", SQUASHFS_XATTR_TRUSTED },
- { "security.", SQUASHFS_XATTR_SECURITY },
-};
-
-static int get_prefix(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 -1;
-}
-
-bool sqfs_has_xattr(const char *key)
-{
- return get_prefix(key) >= 0;
-}
-
static int write_key(meta_writer_t *mw, const char *key, tree_xattr_t *xattr,
bool value_is_ool)
{
sqfs_xattr_entry_t kent;
int type;
- type = get_prefix(key);
+ type = sqfs_get_xattr_prefix_id(key);
if (type < 0) {
fprintf(stderr, "unsupported xattr key '%s'\n", key);
return -1;
diff --git a/lib/sqfs/xattr.c b/lib/sqfs/xattr.c
new file mode 100644
index 0000000..db8edf4
--- /dev/null
+++ b/lib/sqfs/xattr.c
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * write_xattr.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "squashfs.h"
+
+#include <string.h>
+
+static const struct {
+ const char *prefix;
+ E_SQFS_XATTR_TYPE type;
+} xattr_types[] = {
+ { "user.", SQUASHFS_XATTR_USER },
+ { "trusted.", SQUASHFS_XATTR_TRUSTED },
+ { "security.", SQUASHFS_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 -1;
+}
+
+const char *sqfs_get_xattr_prefix(E_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;
+}
+
+bool sqfs_has_xattr(const char *key)
+{
+ return sqfs_get_xattr_prefix_id(key) >= 0;
+}
diff --git a/lib/sqfs/xattr_reader.c b/lib/sqfs/xattr_reader.c
new file mode 100644
index 0000000..4383e89
--- /dev/null
+++ b/lib/sqfs/xattr_reader.c
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xattr_reader.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "xattr_reader.h"
+#include "meta_reader.h"
+#include "util.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+struct xattr_reader_t {
+ uint64_t xattr_start;
+
+ size_t num_id_blocks;
+ size_t num_ids;
+
+ uint64_t *id_block_starts;
+
+ meta_reader_t *idrd;
+ meta_reader_t *kvrd;
+ sqfs_super_t *super;
+};
+
+static int get_id_block_locations(xattr_reader_t *xr, int sqfsfd,
+ sqfs_super_t *super)
+{
+ sqfs_xattr_id_table_t idtbl;
+ size_t i;
+
+ if (super->xattr_id_table_start >= super->bytes_used) {
+ fputs("xattr ID location table is after end of filesystem\n",
+ stderr);
+ return -1;
+ }
+
+ if (read_data_at("reading xattr ID location table",
+ super->xattr_id_table_start,
+ sqfsfd, &idtbl, sizeof(idtbl))) {
+ return -1;
+ }
+
+ 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 = calloc(sizeof(uint64_t), xr->num_id_blocks);
+ if (xr->id_block_starts == NULL) {
+ perror("allocating xattr ID location table");
+ return -1;
+ }
+
+ if (read_data_at("reading xattr ID block locations",
+ super->xattr_id_table_start + sizeof(idtbl),
+ sqfsfd, xr->id_block_starts,
+ sizeof(xr->id_block_starts[0]) * xr->num_id_blocks)) {
+ free(xr->id_block_starts);
+ return -1;
+ }
+
+ 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) {
+ fputs("found xattr ID block that is past "
+ "end of filesystem\n", stderr);
+ free(xr->id_block_starts);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int get_xattr_desc(xattr_reader_t *xr, uint32_t idx,
+ sqfs_xattr_id_t *desc)
+{
+ size_t block, offset;
+
+ if (idx >= xr->num_ids) {
+ fputs("Tried to access out of bounds xattr index\n", stderr);
+ return -1;
+ }
+
+ offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE;
+ block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE;
+
+ if (meta_reader_seek(xr->idrd, xr->id_block_starts[block], offset))
+ return -1;
+
+ if (meta_reader_read(xr->idrd, desc, sizeof(*desc)))
+ return -1;
+
+ desc->xattr = le64toh(desc->xattr);
+ desc->count = le32toh(desc->count);
+ desc->size = le32toh(desc->size);
+
+ if ((desc->xattr & 0xFFFF) >= SQFS_META_BLOCK_SIZE) {
+ fputs("Found xattr ID record pointing outside "
+ "metadata block\n", stderr);
+ return -1;
+ }
+
+ if ((xr->xattr_start + (desc->xattr >> 16)) >= xr->super->bytes_used) {
+ fputs("Found xattr ID record pointing past "
+ "end of filesystem\n", stderr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static sqfs_xattr_entry_t *read_key(xattr_reader_t *xr)
+{
+ sqfs_xattr_entry_t key, *out;
+ const char *prefix;
+ size_t plen;
+
+ if (meta_reader_read(xr->kvrd, &key, sizeof(key)))
+ return NULL;
+
+ key.type = le16toh(key.type);
+ key.size = le16toh(key.size);
+
+ prefix = sqfs_get_xattr_prefix(key.type & SQUASHFS_XATTR_PREFIX_MASK);
+ if (prefix == NULL) {
+ fprintf(stderr, "found unknown xattr type %u\n",
+ key.type & SQUASHFS_XATTR_PREFIX_MASK);
+ return NULL;
+ }
+
+ plen = strlen(prefix);
+ out = calloc(1, sizeof(*out) + plen + key.size + 1);
+ if (out == NULL) {
+ perror("restoring xattr key");
+ return NULL;
+ }
+
+ *out = key;
+ memcpy(out->key, prefix, plen);
+
+ if (meta_reader_read(xr->kvrd, out->key + plen, key.size)) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+static sqfs_xattr_value_t *read_value(xattr_reader_t *xr,
+ const sqfs_xattr_entry_t *key)
+{
+ sqfs_xattr_value_t value, *out;
+ uint64_t ref, start, new_start;
+ size_t offset, new_offset;
+
+ if (meta_reader_read(xr->kvrd, &value, sizeof(value)))
+ return NULL;
+
+ if (key->type & SQUASHFS_XATTR_FLAG_OOL) {
+ if (meta_reader_read(xr->kvrd, &ref, sizeof(ref)))
+ return NULL;
+
+ meta_reader_get_position(xr->kvrd, &start, &offset);
+
+ new_start = xr->xattr_start + (ref >> 16);
+ new_offset = ref & 0xFFFF;
+
+ if (new_start > xr->super->bytes_used) {
+ fputs("OOL xattr reference points past end of "
+ "filesystem\n", stderr);
+ return NULL;
+ }
+
+ if (new_offset >= SQFS_META_BLOCK_SIZE) {
+ fputs("OOL xattr reference points outside "
+ "metadata block\n", stderr);
+ return NULL;
+ }
+
+ if (meta_reader_seek(xr->kvrd, new_start, new_offset))
+ return NULL;
+ }
+
+ value.size = le32toh(value.size);
+
+ out = calloc(1, sizeof(*out) + value.size);
+ if (out == NULL) {
+ perror("reading xattr value");
+ return NULL;
+ }
+
+ *out = value;
+
+ if (meta_reader_read(xr->kvrd, out->value, value.size))
+ goto fail;
+
+ if (key->type & SQUASHFS_XATTR_FLAG_OOL) {
+ if (meta_reader_seek(xr->kvrd, start, offset))
+ goto fail;
+ }
+
+ return out;
+fail:
+ free(out);
+ return NULL;
+}
+
+static int restore_kv_pairs(xattr_reader_t *xr, fstree_t *fs,
+ tree_node_t *node)
+{
+ size_t i, key_idx, val_idx;
+ sqfs_xattr_entry_t *key;
+ sqfs_xattr_value_t *val;
+ int ret;
+
+ if (meta_reader_seek(xr->kvrd, node->xattr->block,
+ node->xattr->offset)) {
+ return -1;
+ }
+
+ for (i = 0; i < node->xattr->num_attr; ++i) {
+ key = read_key(xr);
+ if (key == NULL)
+ return -1;
+
+ val = read_value(xr, key);
+ if (val == NULL) {
+ free(key);
+ return -1;
+ }
+
+ ret = str_table_get_index(&fs->xattr_keys,
+ (const char *)key->key, &key_idx);
+ if (ret) {
+ free(val);
+ free(key);
+ return -1;
+ }
+
+ ret = str_table_get_index(&fs->xattr_values,
+ (const char *)val->value, &val_idx);
+ if (ret) {
+ free(val);
+ free(key);
+ return -1;
+ }
+
+ if (sizeof(size_t) > sizeof(uint32_t)) {
+ if (key_idx > 0xFFFFFFFFUL) {
+ fputs("too many unique xattr keys\n", stderr);
+ return -1;
+ }
+
+ if (val_idx > 0xFFFFFFFFUL) {
+ fputs("too many unique xattr values\n", stderr);
+ return -1;
+ }
+ }
+
+ node->xattr->attr[i].key_index = key_idx;
+ node->xattr->attr[i].value_index = val_idx;
+
+ free(key);
+ free(val);
+ }
+
+ return 0;
+}
+
+int xattr_reader_restore_node(xattr_reader_t *xr, fstree_t *fs,
+ tree_node_t *node, uint32_t xattr)
+{
+ sqfs_xattr_id_t desc;
+ tree_xattr_t *it;
+ size_t size;
+
+ if (xr->kvrd == NULL || xr->idrd == NULL)
+ return 0;
+
+ for (it = fs->xattr; it != NULL; it = it->next) {
+ if (it->index == xattr) {
+ node->xattr = it;
+ return 0;
+ }
+ }
+
+ if (get_xattr_desc(xr, xattr, &desc))
+ return -1;
+
+ size = sizeof(*node->xattr);
+ size += sizeof(node->xattr->attr[0]) * desc.count;
+
+ node->xattr = calloc(1, size);
+ if (node->xattr == NULL) {
+ perror("creating xattr structure");
+ return -1;
+ }
+
+ node->xattr->num_attr = desc.count;
+ node->xattr->max_attr = desc.count;
+ node->xattr->block = xr->xattr_start + (desc.xattr >> 16);
+ node->xattr->offset = desc.xattr & 0xFFFF;
+ node->xattr->size = desc.size;
+ node->xattr->index = xattr;
+ node->xattr->owner = node;
+
+ if (restore_kv_pairs(xr, fs, node)) {
+ free(node->xattr);
+ return -1;
+ }
+
+ node->xattr->next = fs->xattr;
+ fs->xattr = node->xattr;
+ return 0;
+}
+
+void xattr_reader_destroy(xattr_reader_t *xr)
+{
+ if (xr->kvrd != NULL)
+ meta_reader_destroy(xr->kvrd);
+
+ if (xr->idrd != NULL)
+ meta_reader_destroy(xr->idrd);
+
+ free(xr->id_block_starts);
+ free(xr);
+}
+
+xattr_reader_t *xattr_reader_create(int sqfsfd, sqfs_super_t *super,
+ compressor_t *cmp)
+{
+ xattr_reader_t *xr = calloc(1, sizeof(*xr));
+
+ if (xr == NULL) {
+ perror("creating xattr reader");
+ return NULL;
+ }
+
+ if (super->flags & SQFS_FLAG_NO_XATTRS)
+ return xr;
+
+ if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF)
+ return xr;
+
+ if (get_id_block_locations(xr, sqfsfd, super))
+ goto fail;
+
+ xr->idrd = meta_reader_create(sqfsfd, cmp);
+ if (xr->idrd == NULL)
+ goto fail;
+
+ xr->kvrd = meta_reader_create(sqfsfd, cmp);
+ if (xr->kvrd == NULL)
+ goto fail;
+
+ xr->super = super;
+ return xr;
+fail:
+ xattr_reader_destroy(xr);
+ return NULL;
+}