diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makemodule.am | 6 | ||||
| -rw-r--r-- | lib/sqfs/meta_reader.c | 7 | ||||
| -rw-r--r-- | lib/sqfs/write_xattr.c | 32 | ||||
| -rw-r--r-- | lib/sqfs/xattr.c | 52 | ||||
| -rw-r--r-- | lib/sqfs/xattr_reader.c | 372 | 
5 files changed, 435 insertions, 34 deletions
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; +}  | 
