aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/data_reader.h33
-rw-r--r--lib/Makemodule.am4
-rw-r--r--lib/sqfs/data_reader.c157
-rw-r--r--unpack/Makemodule.am2
-rw-r--r--unpack/extract_file.c93
-rw-r--r--unpack/rdsquashfs.c61
-rw-r--r--unpack/rdsquashfs.h10
-rw-r--r--unpack/restore_fstree.c2
8 files changed, 205 insertions, 157 deletions
diff --git a/include/data_reader.h b/include/data_reader.h
new file mode 100644
index 0000000..68788b4
--- /dev/null
+++ b/include/data_reader.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#ifndef DATA_READER_H
+#define DATA_READER_H
+
+#include "squashfs.h"
+#include "compress.h"
+#include "fstree.h"
+
+typedef struct data_reader_t data_reader_t;
+
+/*
+ Create a data reader for accessing data blocks in a squashfs image.
+
+ Internally creates a fragment_reader_t (if applicable) to resolve
+ fragment indices.
+
+ Prints error messsages to stderr on failure.
+ */
+data_reader_t *data_reader_create(int fd, sqfs_super_t *super,
+ compressor_t *cmp);
+
+void data_reader_destroy(data_reader_t *data);
+
+/*
+ Use a file_info_t to locate and extract all blocks of the coresponding
+ file and its fragment, if it has one. The entire data is dumped to the
+ given file descriptor.
+
+ Returns 0 on success, prints error messages to stderr on failure.
+ */
+int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd);
+
+#endif /* DATA_READER_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 3c1ef12..ddc78f6 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -23,8 +23,8 @@ libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.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/frag_reader.h
+libsquashfs_a_SOURCES += include/data_writer.h include/frag_reader.h
+libsquashfs_a_SOURCES += include/data_reader.h lib/sqfs/data_reader.c
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/data_reader.c b/lib/sqfs/data_reader.c
new file mode 100644
index 0000000..22413f2
--- /dev/null
+++ b/lib/sqfs/data_reader.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "data_reader.h"
+#include "frag_reader.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+struct data_reader_t {
+ compressor_t *cmp;
+ size_t block_size;
+ frag_reader_t *frag;
+ int sqfsfd;
+
+ void *buffer;
+ void *scratch;
+};
+
+data_reader_t *data_reader_create(int fd, sqfs_super_t *super,
+ compressor_t *cmp)
+{
+ data_reader_t *data = calloc(1, sizeof(*data) + 2 * super->block_size);
+
+ if (data == NULL) {
+ perror("creating data reader");
+ return data;
+ }
+
+ if (super->fragment_entry_count > 0 &&
+ !(super->flags & SQFS_FLAG_NO_FRAGMENTS)) {
+ if (super->fragment_table_start >= super->bytes_used) {
+ fputs("Fragment table start is past end of file\n",
+ stderr);
+ free(data);
+ return NULL;
+ }
+
+ data->frag = frag_reader_create(super, fd, cmp);
+ if (data->frag == NULL) {
+ free(data);
+ return NULL;
+ }
+ }
+
+ data->buffer = (char *)data + sizeof(*data);
+ data->scratch = (char *)data->buffer + super->block_size;
+ data->sqfsfd = fd;
+ data->block_size = super->block_size;
+ data->cmp = cmp;
+ return data;
+}
+
+void data_reader_destroy(data_reader_t *data)
+{
+ if (data->frag != NULL)
+ frag_reader_destroy(data->frag);
+ free(data);
+}
+
+int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd)
+{
+ size_t i, count, fragsz;
+ bool compressed;
+ uint32_t bs;
+ ssize_t ret;
+ void *ptr;
+
+ count = fi->size / data->block_size;
+
+ if (count > 0) {
+ if (lseek(data->sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1)
+ goto fail_seek;
+
+ for (i = 0; i < count; ++i) {
+ bs = fi->blocksizes[i];
+
+ compressed = (bs & (1 << 24)) == 0;
+ bs &= (1 << 24) - 1;
+
+ if (bs > data->block_size)
+ goto fail_bs;
+
+ ret = read_retry(data->sqfsfd, data->buffer, bs);
+ if (ret < 0)
+ goto fail_rd;
+
+ if ((size_t)ret < bs)
+ goto fail_trunc;
+
+ if (compressed) {
+ ret = data->cmp->do_block(data->cmp,
+ data->buffer, bs,
+ data->scratch,
+ data->block_size);
+ if (ret <= 0)
+ return -1;
+
+ bs = ret;
+ ptr = data->scratch;
+ } else {
+ ptr = data->buffer;
+ }
+
+ ret = write_retry(outfd, ptr, bs);
+ if (ret < 0)
+ goto fail_wr;
+
+ if ((size_t)ret < bs)
+ goto fail_wr_trunc;
+ }
+ }
+
+ fragsz = fi->size % data->block_size;
+
+ if (fragsz > 0) {
+ if (data->frag == NULL)
+ goto fail_frag;
+
+ if (frag_reader_read(data->frag, fi->fragment,
+ fi->fragment_offset, data->buffer,
+ fragsz)) {
+ return -1;
+ }
+
+ ret = write_retry(outfd, data->buffer, fragsz);
+ if (ret < 0)
+ goto fail_wr;
+
+ if ((size_t)ret < fragsz)
+ goto fail_wr_trunc;
+ }
+
+ return 0;
+fail_seek:
+ perror("seek on squashfs");
+ return -1;
+fail_wr:
+ perror("writing uncompressed block");
+ return -1;
+fail_wr_trunc:
+ fputs("writing uncompressed block: truncated write\n", stderr);
+ return -1;
+fail_rd:
+ perror("reading from squashfs");
+ return -1;
+fail_trunc:
+ fputs("reading from squashfs: unexpected end of file\n", stderr);
+ return -1;
+fail_bs:
+ fputs("found compressed block larger than block size\n", stderr);
+ return -1;
+fail_frag:
+ fputs("file not a multiple of block size but no fragments available\n",
+ stderr);
+ return -1;
+}
diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am
index 5f4269e..c70026d 100644
--- a/unpack/Makemodule.am
+++ b/unpack/Makemodule.am
@@ -1,5 +1,5 @@
rdsquashfs_SOURCES = unpack/rdsquashfs.c unpack/rdsquashfs.h
-rdsquashfs_SOURCES += unpack/list_files.c unpack/extract_file.c
+rdsquashfs_SOURCES += unpack/list_files.c
rdsquashfs_SOURCES += unpack/restore_fstree.c unpack/describe.c
rdsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a
diff --git a/unpack/extract_file.c b/unpack/extract_file.c
deleted file mode 100644
index 62d1086..0000000
--- a/unpack/extract_file.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* SPDX-License-Identifier: GPL-3.0-or-later */
-#include "rdsquashfs.h"
-
-int extract_file(file_info_t *fi, unsqfs_info_t *info, int outfd)
-{
- size_t i, count, fragsz;
- bool compressed;
- uint32_t bs;
- ssize_t ret;
- void *ptr;
-
- count = fi->size / info->block_size;
-
- if (count > 0) {
- if (lseek(info->sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1)
- goto fail_seek;
-
- for (i = 0; i < count; ++i) {
- bs = fi->blocksizes[i];
-
- compressed = (bs & (1 << 24)) == 0;
- bs &= (1 << 24) - 1;
-
- if (bs > info->block_size)
- goto fail_bs;
-
- ret = read_retry(info->sqfsfd, info->buffer, bs);
- if (ret < 0)
- goto fail_rd;
-
- if ((size_t)ret < bs)
- goto fail_trunc;
-
- if (compressed) {
- ret = info->cmp->do_block(info->cmp,
- info->buffer, bs,
- info->scratch,
- info->block_size);
- if (ret <= 0)
- return -1;
-
- bs = ret;
- ptr = info->scratch;
- } else {
- ptr = info->buffer;
- }
-
- ret = write_retry(outfd, ptr, bs);
- if (ret < 0)
- goto fail_wr;
-
- if ((size_t)ret < bs)
- goto fail_wr_trunc;
- }
- }
-
- fragsz = fi->size % info->block_size;
-
- if (fragsz > 0) {
- if (frag_reader_read(info->frag, fi->fragment,
- fi->fragment_offset, info->buffer,
- fragsz)) {
- return -1;
- }
-
- ret = write_retry(outfd, info->buffer, fragsz);
- if (ret < 0)
- goto fail_wr;
-
- if ((size_t)ret < fragsz)
- goto fail_wr_trunc;
- }
-
- return 0;
-fail_seek:
- perror("seek on squashfs");
- return -1;
-fail_wr:
- perror("writing uncompressed block");
- return -1;
-fail_wr_trunc:
- fputs("writing uncompressed block: truncated write\n", stderr);
- return -1;
-fail_rd:
- perror("reading from squashfs");
- return -1;
-fail_trunc:
- fputs("reading from squashfs: unexpected end of file\n", stderr);
- return -1;
-fail_bs:
- fputs("found compressed block larger than block size\n", stderr);
- return -1;
-}
diff --git a/unpack/rdsquashfs.c b/unpack/rdsquashfs.c
index 81da226..7fd2947 100644
--- a/unpack/rdsquashfs.c
+++ b/unpack/rdsquashfs.c
@@ -121,43 +121,6 @@ static char *get_path(char *old, const char *arg)
return path;
}
-static int alloc_buffers(unsqfs_info_t *info, sqfs_super_t *super)
-{
- info->buffer = malloc(super->block_size);
- if (info->buffer == NULL) {
- perror("allocating block buffer");
- return -1;
- }
-
- info->scratch = malloc(super->block_size);
- if (info->scratch == NULL) {
- perror("allocating scrtach buffer for extracting blocks");
- return -1;
- }
-
- return 0;
-}
-
-static int load_fragment_table(unsqfs_info_t *info, sqfs_super_t *super)
-{
- if (super->fragment_entry_count == 0)
- return 0;
-
- if (super->flags & SQFS_FLAG_NO_FRAGMENTS)
- return 0;
-
- if (super->fragment_table_start >= super->bytes_used) {
- fputs("Fragment table start is past end of file\n", stderr);
- return -1;
- }
-
- info->frag = frag_reader_create(super, info->sqfsfd, info->cmp);
- if (info->frag == NULL)
- return -1;
-
- return 0;
-}
-
int main(int argc, char **argv)
{
int i, status = EXIT_FAILURE, op = OP_NONE;
@@ -281,8 +244,6 @@ int main(int argc, char **argv)
goto out_cmp;
}
- info.block_size = super.block_size;
-
switch (op) {
case OP_LS:
n = find_node(fs.root, cmdpath);
@@ -305,14 +266,14 @@ int main(int argc, char **argv)
goto out_fs;
}
- if (load_fragment_table(&info, &super))
+ info.data = data_reader_create(info.sqfsfd, &super, info.cmp);
+ if (info.data == NULL)
goto out_fs;
- if (alloc_buffers(&info, &super))
- goto out_fs;
-
- if (extract_file(n->data.file, &info, STDOUT_FILENO))
+ if (data_reader_dump_file(info.data, n->data.file,
+ STDOUT_FILENO)) {
goto out_fs;
+ }
break;
case OP_UNPACK:
n = find_node(fs.root, cmdpath);
@@ -321,10 +282,8 @@ int main(int argc, char **argv)
goto out_fs;
}
- if (load_fragment_table(&info, &super))
- goto out_fs;
-
- if (alloc_buffers(&info, &super))
+ info.data = data_reader_create(info.sqfsfd, &super, info.cmp);
+ if (info.data == NULL)
goto out_fs;
if (restore_fstree(unpack_root, n, &info))
@@ -337,8 +296,8 @@ int main(int argc, char **argv)
status = EXIT_SUCCESS;
out_fs:
- if (info.frag != NULL)
- frag_reader_destroy(info.frag);
+ if (info.data != NULL)
+ data_reader_destroy(info.data);
fstree_cleanup(&fs);
out_cmp:
info.cmp->destroy(info.cmp);
@@ -346,8 +305,6 @@ out_fd:
close(info.sqfsfd);
out_cmd:
free(cmdpath);
- free(info.buffer);
- free(info.scratch);
return status;
fail_arg:
fprintf(stderr, "Try `%s --help' for more information.\n", __progname);
diff --git a/unpack/rdsquashfs.h b/unpack/rdsquashfs.h
index a615bb5..b927bab 100644
--- a/unpack/rdsquashfs.h
+++ b/unpack/rdsquashfs.h
@@ -3,7 +3,7 @@
#define RDSQUASHFS_H
#include "meta_reader.h"
-#include "frag_reader.h"
+#include "data_reader.h"
#include "highlevel.h"
#include "squashfs.h"
#include "compress.h"
@@ -27,21 +27,15 @@ enum UNPACK_FLAGS {
};
typedef struct {
+ data_reader_t *data;
compressor_t *cmp;
- size_t block_size;
- frag_reader_t *frag;
int rdtree_flags;
int sqfsfd;
int flags;
-
- void *buffer;
- void *scratch;
} unsqfs_info_t;
void list_files(tree_node_t *node);
-int extract_file(file_info_t *fi, unsqfs_info_t *info, int outfd);
-
int restore_fstree(const char *rootdir, tree_node_t *root,
unsqfs_info_t *info);
diff --git a/unpack/restore_fstree.c b/unpack/restore_fstree.c
index 355bbb5..a39ec97 100644
--- a/unpack/restore_fstree.c
+++ b/unpack/restore_fstree.c
@@ -63,7 +63,7 @@ static int create_node(tree_node_t *n, unsqfs_info_t *info)
return -1;
}
- if (extract_file(n->data.file, info, fd)) {
+ if (data_reader_dump_file(info->data, n->data.file, fd)) {
close(fd);
return -1;
}