diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makemodule.am | 3 | ||||
-rw-r--r-- | lib/sqfs/meta_reader.c | 123 |
2 files changed, 125 insertions, 1 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 026e3f1..5cadf58 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -11,7 +11,8 @@ libsquashfs_a_SOURCES = include/meta_writer.h include/squashfs.h libsquashfs_a_SOURCES += lib/sqfs/meta_writer.c lib/sqfs/super.c libsquashfs_a_SOURCES += lib/sqfs/id_table.c include/id_table.h libsquashfs_a_SOURCES += lib/sqfs/table.c include/table.h -libsquashfs_a_SOURCES += lib/sqfs/read_super.c +libsquashfs_a_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c +libsquashfs_a_SOURCES += include/meta_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/meta_reader.c b/lib/sqfs/meta_reader.c new file mode 100644 index 0000000..8cd7c8f --- /dev/null +++ b/lib/sqfs/meta_reader.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "meta_reader.h" +#include "util.h" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +meta_reader_t *meta_reader_create(int fd, compressor_t *cmp) +{ + meta_reader_t *m = calloc(1, sizeof(*m)); + + if (m == NULL) { + perror("creating meta data reader"); + return NULL; + } + + m->fd = fd; + m->cmp = cmp; + return m; +} + +void meta_reader_destroy(meta_reader_t *m) +{ + free(m); +} + +int meta_reader_seek(meta_reader_t *m, uint64_t block_start, size_t offset) +{ + bool compressed; + uint16_t header; + ssize_t ret; + size_t size; + + if (offset >= sizeof(m->data)) { + fputs("Tried to seek past end of meta data block.\n", stderr); + return -1; + } + + if (block_start == m->block_offset) { + m->offset = offset; + return 0; + } + + if (lseek(m->fd, block_start, SEEK_SET) == (off_t)-1) { + perror("seek on image file"); + return -1; + } + + ret = read_retry(m->fd, &header, 2); + if (ret < 0) + goto fail_rd; + + if ((size_t)ret < 2) + goto fail_trunc; + + header = le16toh(header); + compressed = (header & 0x8000) == 0; + size = header & 0x7FFF; + + if (size > sizeof(m->data)) { + fputs("found meta data block larger than maximum size\n", + stderr); + return -1; + } + + m->block_offset = block_start; + m->offset = offset; + m->next_block = block_start + size + 2; + + memset(m->data, 0, sizeof(m->data)); + + ret = read_retry(m->fd, m->data, size); + if (ret < 0) + goto fail_rd; + + if ((size_t)ret < size) + goto fail_trunc; + + if (compressed) { + ret = m->cmp->do_block(m->cmp, m->data, size); + + if (ret <= 0) { + fputs("error uncompressing meta data block\n", stderr); + return -1; + } + } + + return 0; +fail_trunc: + fputs("reading meta data: unexpected end of file\n", stderr); + return -1; +fail_rd: + perror("reading meta data"); + return -1; +} + +int meta_reader_read(meta_reader_t *m, void *data, size_t size) +{ + size_t diff; + + while (size != 0) { + diff = sizeof(m->data) - m->offset; + + if (diff == 0) { + if (meta_reader_seek(m, m->next_block, 0)) + return -1; + diff = sizeof(m->data); + } + + if (diff > size) + diff = size; + + memcpy(data, m->data + m->offset, diff); + + m->offset += diff; + data = (char *)data + diff; + size -= diff; + } + + return 0; +} |