aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/data_reader.h3
-rw-r--r--lib/sqfshelper/data_reader.c75
2 files changed, 78 insertions, 0 deletions
diff --git a/include/data_reader.h b/include/data_reader.h
index 846ffd1..63bd539 100644
--- a/include/data_reader.h
+++ b/include/data_reader.h
@@ -28,6 +28,9 @@ 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_dump(data_reader_t *data, const sqfs_inode_generic_t *inode,
+ int outfd, bool allow_sparse);
+
/*
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
diff --git a/lib/sqfshelper/data_reader.c b/lib/sqfshelper/data_reader.c
index 42a351f..0c982d7 100644
--- a/lib/sqfshelper/data_reader.c
+++ b/lib/sqfshelper/data_reader.c
@@ -172,6 +172,81 @@ 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)
+{
+ uint32_t frag_idx, frag_off;
+ uint64_t filesz;
+ size_t i, diff;
+ off_t off;
+
+ 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;
+ } 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;
+ } else {
+ return -1;
+ }
+
+ if (allow_sparse && ftruncate(outfd, filesz))
+ goto fail_sparse;
+
+ for (i = 0; i < inode->num_file_blocks; ++i) {
+ diff = filesz > data->block_size ? data->block_size : filesz;
+ filesz -= diff;
+
+ 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]);
+ }
+
+ if (write_data("writing uncompressed block",
+ outfd, data->block, diff)) {
+ return -1;
+ }
+ }
+
+ if (filesz > 0 && frag_off != 0xFFFFFFFF) {
+ if (precache_fragment_block(data, frag_idx))
+ return -1;
+
+ if (frag_off >= data->frag_used)
+ goto fail_range;
+
+ if ((frag_off + filesz - 1) >= data->frag_used)
+ goto fail_range;
+
+ if (write_data("writing uncompressed fragment", outfd,
+ (char *)data->frag_block + frag_off,
+ filesz)) {
+ return -1;
+ }
+ }
+
+ 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;
+}
+
int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd,
bool allow_sparse)
{