aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/meta_reader.h26
-rw-r--r--lib/Makemodule.am3
-rw-r--r--lib/sqfs/meta_reader.c123
3 files changed, 151 insertions, 1 deletions
diff --git a/include/meta_reader.h b/include/meta_reader.h
new file mode 100644
index 0000000..4c03996
--- /dev/null
+++ b/include/meta_reader.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#ifndef META_READER_H
+#define META_READER_H
+
+#include "compress.h"
+#include "squashfs.h"
+
+typedef struct {
+ uint64_t block_offset;
+ uint64_t next_block;
+ size_t offset;
+ int fd;
+ compressor_t *cmp;
+ uint8_t data[SQFS_META_BLOCK_SIZE];
+} meta_reader_t;
+
+meta_reader_t *meta_reader_create(int fd, compressor_t *cmp);
+
+void meta_reader_destroy(meta_reader_t *m);
+
+int meta_reader_seek(meta_reader_t *m, uint64_t block_start,
+ size_t offset);
+
+int meta_reader_read(meta_reader_t *m, void *data, size_t size);
+
+#endif /* META_READER_H */
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;
+}