summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--difftool/extract.c10
-rw-r--r--include/data_reader.h10
-rw-r--r--include/sqfs/error.h2
-rw-r--r--lib/sqfshelper/Makemodule.am1
-rw-r--r--lib/sqfshelper/data_reader.c160
-rw-r--r--lib/sqfshelper/data_reader_dump.c87
-rw-r--r--tar/sqfs2tar.c3
-rw-r--r--unpack/fill_files.c2
-rw-r--r--unpack/rdsquashfs.c3
9 files changed, 216 insertions, 62 deletions
diff --git a/difftool/extract.c b/difftool/extract.c
index 45c5560..94477b1 100644
--- a/difftool/extract.c
+++ b/difftool/extract.c
@@ -7,7 +7,7 @@
#include "sqfsdiff.h"
static int extract(data_reader_t *data, const sqfs_inode_generic_t *inode,
- const char *prefix, const char *path)
+ const char *prefix, const char *path, size_t block_size)
{
char *ptr, *temp;
int fd;
@@ -27,7 +27,7 @@ static int extract(data_reader_t *data, const sqfs_inode_generic_t *inode,
return -1;
}
- if (data_reader_dump(data, inode, fd, true)) {
+ if (data_reader_dump(data, inode, fd, block_size, true)) {
close(fd);
return -1;
}
@@ -41,12 +41,14 @@ int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old,
const char *path)
{
if (old != NULL) {
- if (extract(sd->sqfs_old.data, old, "old", path))
+ if (extract(sd->sqfs_old.data, old, "old",
+ path, sd->sqfs_old.super.block_size))
return -1;
}
if (new != NULL) {
- if (extract(sd->sqfs_new.data, new, "new", path))
+ if (extract(sd->sqfs_new.data, new, "new",
+ path, sd->sqfs_new.super.block_size))
return -1;
}
diff --git a/include/data_reader.h b/include/data_reader.h
index 77bdd82..324cdc8 100644
--- a/include/data_reader.h
+++ b/include/data_reader.h
@@ -28,8 +28,16 @@ data_reader_t *data_reader_create(sqfs_file_t *file, sqfs_super_t *super,
void data_reader_destroy(data_reader_t *data);
+int data_reader_get_fragment(data_reader_t *data,
+ const sqfs_inode_generic_t *inode,
+ sqfs_block_t **out);
+
+int data_reader_get_block(data_reader_t *data,
+ const sqfs_inode_generic_t *inode,
+ size_t index, sqfs_block_t **out);
+
int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode,
- int outfd, bool allow_sparse);
+ int outfd, size_t block_size, bool allow_sparse);
/*
Read a chunk of data from a file. Starting from 'offset' into the
diff --git a/include/sqfs/error.h b/include/sqfs/error.h
index 31ebb47..7277a3d 100644
--- a/include/sqfs/error.h
+++ b/include/sqfs/error.h
@@ -109,6 +109,8 @@ typedef enum {
SQFS_ERROR_NO_ENTRY = -13,
SQFS_ERROR_LINK_LOOP = -14,
+
+ SQFS_ERROR_NOT_FILE = -15,
} E_SQFS_ERROR;
#endif /* SQFS_ERROR_H */
diff --git a/lib/sqfshelper/Makemodule.am b/lib/sqfshelper/Makemodule.am
index e155fa6..d711cf2 100644
--- a/lib/sqfshelper/Makemodule.am
+++ b/lib/sqfshelper/Makemodule.am
@@ -4,6 +4,7 @@ libsqfshelper_a_SOURCES += lib/sqfshelper/tree_node_to_inode.c
libsqfshelper_a_SOURCES += lib/sqfshelper/write_export_table.c
libsqfshelper_a_SOURCES += lib/sqfshelper/print_version.c
libsqfshelper_a_SOURCES += lib/sqfshelper/inode_stat.c
+libsqfshelper_a_SOURCES += lib/sqfshelper/data_reader_dump.c
libsqfshelper_a_SOURCES += lib/sqfshelper/compress.c lib/sqfshelper/comp_opt.c
libsqfshelper_a_SOURCES += include/data_reader.h lib/sqfshelper/data_reader.c
libsqfshelper_a_SOURCES += include/data_writer.h lib/sqfshelper/data_writer.c
diff --git a/lib/sqfshelper/data_reader.c b/lib/sqfshelper/data_reader.c
index 0780da0..929b7ee 100644
--- a/lib/sqfshelper/data_reader.c
+++ b/lib/sqfshelper/data_reader.c
@@ -6,6 +6,9 @@
*/
#include "config.h"
+#include "sqfs/block_processor.h"
+#include "sqfs/error.h"
+
#include "data_reader.h"
#include "highlevel.h"
#include "util.h"
@@ -32,6 +35,55 @@ struct data_reader_t {
void *frag_block;
};
+static int get_block(data_reader_t *data, uint64_t off, uint32_t size,
+ size_t unpacked_size, sqfs_block_t **out)
+{
+ sqfs_block_t *blk = alloc_flex(sizeof(*blk), 1, unpacked_size);
+ size_t on_disk_size;
+ ssize_t ret;
+ int err;
+
+ if (blk == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ blk->size = unpacked_size;
+
+ if (SQFS_IS_SPARSE_BLOCK(size)) {
+ *out = blk;
+ return 0;
+ }
+
+ on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size);
+
+ if (on_disk_size > unpacked_size)
+ return SQFS_ERROR_OVERFLOW;
+
+ if (SQFS_IS_BLOCK_COMPRESSED(size)) {
+ err = data->file->read_at(data->file, off,
+ data->scratch, on_disk_size);
+ if (err) {
+ free(blk);
+ return err;
+ }
+
+ ret = data->cmp->do_block(data->cmp, data->scratch,
+ on_disk_size, blk->data, blk->size);
+ if (ret <= 0)
+ err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW;
+ } else {
+ err = data->file->read_at(data->file, off,
+ blk->data, on_disk_size);
+ }
+
+ if (err) {
+ free(blk);
+ return err;
+ }
+
+ *out = blk;
+ return 0;
+}
+
static ssize_t read_block(data_reader_t *data, off_t offset, uint32_t size,
void *dst)
{
@@ -172,79 +224,79 @@ void data_reader_destroy(data_reader_t *data)
free(data);
}
-int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode,
- int outfd, bool allow_sparse)
+int data_reader_get_block(data_reader_t *data,
+ const sqfs_inode_generic_t *inode,
+ size_t index, sqfs_block_t **out)
{
- uint32_t frag_idx, frag_off;
- uint64_t filesz;
- size_t i, diff;
- off_t off;
+ size_t i, unpacked_size;
+ uint64_t off, filesz;
if (inode->base.type == SQFS_INODE_FILE) {
- filesz = inode->data.file.file_size;
off = inode->data.file.blocks_start;
- frag_idx = inode->data.file.fragment_index;
- frag_off = inode->data.file.fragment_offset;
+ filesz = inode->data.file.file_size;
} else if (inode->base.type == SQFS_INODE_EXT_FILE) {
- filesz = inode->data.file_ext.file_size;
off = inode->data.file_ext.blocks_start;
- frag_idx = inode->data.file_ext.fragment_idx;
- frag_off = inode->data.file_ext.fragment_offset;
+ filesz = inode->data.file_ext.file_size;
} else {
- return -1;
+ return SQFS_ERROR_NOT_FILE;
}
- if (allow_sparse && ftruncate(outfd, filesz))
- goto fail_sparse;
+ if (index >= inode->num_file_blocks)
+ return SQFS_ERROR_OUT_OF_BOUNDS;
- for (i = 0; i < inode->num_file_blocks; ++i) {
- diff = filesz > data->block_size ? data->block_size : filesz;
- filesz -= diff;
+ for (i = 0; i < index; ++i) {
+ off += SQFS_ON_DISK_BLOCK_SIZE(inode->block_sizes[i]);
+ filesz -= data->block_size;
+ }
- if (SQFS_IS_SPARSE_BLOCK(inode->block_sizes[i])) {
- if (allow_sparse) {
- if (lseek(outfd, diff, SEEK_CUR) == (off_t)-1)
- goto fail_sparse;
- continue;
- }
- memset(data->block, 0, diff);
- } else {
- if (precache_data_block(data, off,
- inode->block_sizes[i]))
- return -1;
- off += SQFS_ON_DISK_BLOCK_SIZE(inode->block_sizes[i]);
- }
+ unpacked_size = filesz < data->block_size ? filesz : data->block_size;
- if (write_data("writing uncompressed block",
- outfd, data->block, diff)) {
- return -1;
- }
+ return get_block(data, off, inode->block_sizes[index],
+ unpacked_size, out);
+}
+
+int data_reader_get_fragment(data_reader_t *data,
+ const sqfs_inode_generic_t *inode,
+ sqfs_block_t **out)
+{
+ uint32_t frag_idx, frag_off, frag_sz;
+ sqfs_block_t *blk;
+ uint64_t filesz;
+
+ if (inode->base.type == SQFS_INODE_EXT_FILE) {
+ filesz = inode->data.file_ext.file_size;
+ frag_idx = inode->data.file_ext.fragment_idx;
+ frag_off = inode->data.file_ext.fragment_offset;
+ } else if (inode->base.type == SQFS_INODE_FILE) {
+ filesz = inode->data.file.file_size;
+ frag_idx = inode->data.file.fragment_index;
+ frag_off = inode->data.file.fragment_offset;
+ } else {
+ return -1;
}
- if (filesz > 0 && frag_off != 0xFFFFFFFF) {
- if (precache_fragment_block(data, frag_idx))
- return -1;
+ if (inode->num_file_blocks * data->block_size >= filesz) {
+ *out = NULL;
+ return 0;
+ }
- if (frag_off >= data->frag_used)
- goto fail_range;
+ frag_sz = filesz % data->block_size;
- if ((frag_off + filesz - 1) >= data->frag_used)
- goto fail_range;
+ if (precache_fragment_block(data, frag_idx))
+ return -1;
- if (write_data("writing uncompressed fragment", outfd,
- (char *)data->frag_block + frag_off,
- filesz)) {
- return -1;
- }
- }
+ if (frag_off + frag_sz > data->frag_used)
+ return -1;
+
+ blk = alloc_flex(sizeof(*blk), 1, frag_sz);
+ if (blk == NULL)
+ return -1;
+
+ blk->size = frag_sz;
+ memcpy(blk->data, (char *)data->frag_block + frag_off, frag_sz);
+ *out = blk;
return 0;
-fail_range:
- fputs("attempted to read past fragment block limits\n", stderr);
- return -1;
-fail_sparse:
- perror("creating sparse output file");
- return -1;
}
ssize_t data_reader_read(data_reader_t *data,
diff --git a/lib/sqfshelper/data_reader_dump.c b/lib/sqfshelper/data_reader_dump.c
new file mode 100644
index 0000000..23cd482
--- /dev/null
+++ b/lib/sqfshelper/data_reader_dump.c
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * data_reader_dump.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "sqfs/block_processor.h"
+#include "data_reader.h"
+#include "highlevel.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode,
+ int outfd, size_t block_size, bool allow_sparse)
+{
+ sqfs_block_t *blk;
+ uint64_t filesz;
+ size_t i, diff;
+ int err;
+
+ if (inode->base.type == SQFS_INODE_EXT_FILE) {
+ filesz = inode->data.file_ext.file_size;
+ } else {
+ filesz = inode->data.file.file_size;
+ }
+
+ if (allow_sparse && ftruncate(outfd, filesz))
+ goto fail_sparse;
+
+ for (i = 0; i < inode->num_file_blocks; ++i) {
+ if (SQFS_IS_SPARSE_BLOCK(inode->block_sizes[i]) &&
+ allow_sparse) {
+ if (filesz < block_size) {
+ diff = filesz;
+ filesz = 0;
+ } else {
+ diff = block_size;
+ filesz -= block_size;
+ }
+
+ if (lseek(outfd, diff, SEEK_CUR) == (off_t)-1)
+ goto fail_sparse;
+ } else {
+ err = data_reader_get_block(data, inode, i, &blk);
+ if (err) {
+ fprintf(stderr, "error reading "
+ "data block: %d\n", err);
+ return -1;
+ }
+
+ if (write_data("writing uncompressed block",
+ outfd, blk->data, blk->size)) {
+ free(blk);
+ return -1;
+ }
+
+ free(blk);
+ filesz -= blk->size;
+ }
+ }
+
+ if (filesz > 0) {
+ if (data_reader_get_fragment(data, inode, &blk)) {
+ fputs("error reading fragment block", stderr);
+ return -1;
+ }
+
+ if (write_data("writing uncompressed fragment", outfd,
+ blk->data, blk->size)) {
+ free(blk);
+ return -1;
+ }
+
+ free(blk);
+ }
+
+ return 0;
+fail_sparse:
+ perror("creating sparse output file");
+ return -1;
+}
diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c
index 43df40b..c624934 100644
--- a/tar/sqfs2tar.c
+++ b/tar/sqfs2tar.c
@@ -317,7 +317,8 @@ static int write_tree_dfs(const sqfs_tree_node_t *n)
return -1;
if (S_ISREG(sb.st_mode)) {
- if (data_reader_dump(data, n->inode, STDOUT_FILENO, false))
+ if (data_reader_dump(data, n->inode, STDOUT_FILENO,
+ super.block_size, false))
return -1;
if (padd_file(STDOUT_FILENO, sb.st_size, 512))
diff --git a/unpack/fill_files.c b/unpack/fill_files.c
index de0b676..f98a801 100644
--- a/unpack/fill_files.c
+++ b/unpack/fill_files.c
@@ -172,7 +172,7 @@ static int fill_files(data_reader_t *data, int flags)
if (!(flags & UNPACK_QUIET))
printf("unpacking %s\n", files[i].path);
- if (data_reader_dump(data, files[i].inode, fd,
+ if (data_reader_dump(data, files[i].inode, fd, block_size,
(flags & UNPACK_NO_SPARSE) == 0)) {
close(fd);
return -1;
diff --git a/unpack/rdsquashfs.c b/unpack/rdsquashfs.c
index 60bfd19..e8ec3ec 100644
--- a/unpack/rdsquashfs.c
+++ b/unpack/rdsquashfs.c
@@ -102,7 +102,8 @@ int main(int argc, char **argv)
goto out;
}
- if (data_reader_dump(data, n->inode, STDOUT_FILENO, false))
+ if (data_reader_dump(data, n->inode, STDOUT_FILENO,
+ super.block_size, false))
goto out;
break;
case OP_UNPACK: