diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-05-03 17:35:37 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-05-03 21:45:46 +0200 | 
| commit | 12c8196ad46808dc9d0e84b3a798509dcf1a41e7 (patch) | |
| tree | 9069df66c14ca427540290ca1fb88688c859bb15 /lib | |
| parent | 0903622b468136ef4354f33ebb24b12df0f549bb (diff) | |
Add utility functions to read the fragment data from an image
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makemodule.am | 3 | ||||
| -rw-r--r-- | lib/sqfs/frag_reader.c | 179 | 
2 files changed, 181 insertions, 1 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 3b171c6..f23d9b7 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -14,7 +14,8 @@ libsquashfs_a_SOURCES += lib/sqfs/table.c include/table.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/frag_reader.c +libsquashfs_a_SOURCES += include/frag_reader.h  libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c  libutil_a_SOURCES += lib/util/read_retry.c include/util.h diff --git a/lib/sqfs/frag_reader.c b/lib/sqfs/frag_reader.c new file mode 100644 index 0000000..1906376 --- /dev/null +++ b/lib/sqfs/frag_reader.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "meta_reader.h" +#include "frag_reader.h" +#include "util.h" + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +static int precache_block(frag_reader_t *f, size_t i) +{ +	bool compressed; +	size_t size; +	ssize_t ret; + +	if (i == f->current_index) +		return 0; + +	if (lseek(f->fd, f->tbl[i].start_offset, SEEK_SET) == (off_t)-1) { +		perror("seeking to fragment location"); +		return -1; +	} + +	compressed = (f->tbl[i].size & (1 << 24)) == 0; +	size = f->tbl[i].size & ((1 << 24) - 1); + +	if (size > f->block_size) { +		fputs("found fragment block larger than block size\n", stderr); +		return -1; +	} + +	ret = read_retry(f->fd, f->buffer, size); +	if (ret < 0) { +		perror("reading fragment"); +		return -1; +	} + +	if ((size_t)ret < size) { +		fputs("reading fragment: unexpected end of file\n", stderr); +		return -1; +	} + +	if (compressed) { +		ret = f->cmp->do_block(f->cmp, f->buffer, size); + +		if (ret <= 0) { +			fputs("extracting fragment failed\n", stderr); +			return -1; +		} +	} + +	f->current_index = i; +	f->used = ret; +	return 0; +} + + +frag_reader_t *frag_reader_create(sqfs_super_t *super, int fd, +				  compressor_t *cmp) +{ +	size_t i, blockcount, j, diff, count; +	sqfs_fragment_t *tbl = NULL; +	uint64_t *locations = NULL; +	meta_reader_t *m = NULL; +	frag_reader_t *f = NULL; +	ssize_t ret; + +	count = super->fragment_entry_count; +	blockcount = count / (SQFS_META_BLOCK_SIZE / sizeof(tbl[0])); + +	if (count % (SQFS_META_BLOCK_SIZE / sizeof(tbl[0]))) +		++blockcount; + +	/* pre allocate all the stuff */ +	f = calloc(1, sizeof(*f) + super->block_size); +	if (f == NULL) +		goto fail_rd; + +	tbl = malloc(count * sizeof(tbl[0])); +	if (tbl == NULL) +		goto fail_rd; + +	locations = malloc(blockcount * sizeof(locations[0])); +	if (locations == NULL) +		goto fail_rd; + +	/* read the meta block offset table */ +	if (lseek(fd, super->fragment_table_start, SEEK_SET) == (off_t)-1) +		goto fail_seek; + +	ret = read_retry(fd, locations, blockcount * sizeof(locations[0])); +	if (ret < 0) +		goto fail_rd; + +	if ((size_t)ret < (blockcount * sizeof(locations[0]))) +		goto fail_trunc; + +	for (i = 0; i < blockcount; ++i) +		locations[i] = le64toh(locations[i]); + +	/* read the meta blocks */ +	m = meta_reader_create(fd, cmp); +	if (m == NULL) +		goto fail; + +	for (i = 0, j = 0; i < blockcount && j < count; ++i, j += diff) { +		if (meta_reader_seek(m, locations[i], 0)) +			goto fail; + +		diff = SQFS_META_BLOCK_SIZE / sizeof(tbl[0]); +		if (diff > count) +			diff = count; + +		if (meta_reader_read(m, tbl + j, diff * sizeof(tbl[0]))) +			goto fail; +	} + +	for (i = 0; i < count; ++i) { +		tbl[i].start_offset = le64toh(tbl[i].start_offset); +		tbl[i].size = le32toh(tbl[i].size); +	} + +	/* cleanup and ship it */ +	meta_reader_destroy(m); +	free(locations); + +	f->tbl = tbl; +	f->num_fragments = count; +	f->cmp = cmp; +	f->fd = fd; +	f->block_size = super->block_size; +	f->current_index = count; +	return f; +fail_seek: +	perror("seek to fragment table"); +	goto fail; +fail_trunc: +	fputs("reading fragment table: unexpected end of file\n", stderr); +	goto fail; +fail_rd: +	perror("reading fragment table"); +	goto fail; +fail: +	if (m != NULL) +		meta_reader_destroy(m); +	free(tbl); +	free(locations); +	free(f); +	return NULL; +} + +void frag_reader_destroy(frag_reader_t *f) +{ +	free(f->tbl); +	free(f); +} + +int frag_reader_read(frag_reader_t *f, size_t index, size_t offset, +		     void *buffer, size_t size) +{ +	if (precache_block(f, index)) +		return -1; + +	if (offset >= f->used) +		goto fail_range; + +	if (size == 0) +		return 0; + +	if ((offset + size - 1) >= f->used) +		goto fail_range; + +	memcpy(buffer, f->buffer + offset, size); +	return 0; +fail_range: +	fputs("attempted to read past fragment block limits\n", stderr); +	return -1; +}  | 
