summaryrefslogtreecommitdiff
path: root/unpack/extract_file.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-05-03 15:59:35 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-05-03 21:45:46 +0200
commit4dc4c6bf50155550c0b83e433fef8c7c19462853 (patch)
treec46dabc7c757fac27b506194ce4d236c244ac938 /unpack/extract_file.c
parent12c8196ad46808dc9d0e84b3a798509dcf1a41e7 (diff)
unsquashfs: add file data extraction
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'unpack/extract_file.c')
-rw-r--r--unpack/extract_file.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/unpack/extract_file.c b/unpack/extract_file.c
new file mode 100644
index 0000000..c18ace8
--- /dev/null
+++ b/unpack/extract_file.c
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "unsquashfs.h"
+
+int extract_file(file_info_t *fi, compressor_t *cmp, size_t block_size,
+ frag_reader_t *frag, int sqfsfd, int outfd)
+{
+ size_t i, count, fragsz;
+ bool compressed;
+ void *buffer;
+ uint32_t bs;
+ ssize_t ret;
+
+ buffer = malloc(block_size);
+ if (buffer == NULL) {
+ perror("allocating scratch buffer");
+ return -1;
+ }
+
+ count = fi->size / block_size;
+
+ if (count > 0) {
+ if (lseek(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 > block_size)
+ goto fail_bs;
+
+ ret = read_retry(sqfsfd, buffer, bs);
+ if (ret < 0)
+ goto fail_rd;
+
+ if ((size_t)ret < bs)
+ goto fail_trunc;
+
+ if (compressed) {
+ ret = cmp->do_block(cmp, buffer, bs);
+ if (ret <= 0)
+ goto fail;
+
+ bs = ret;
+ }
+
+ ret = write_retry(outfd, buffer, bs);
+ if (ret < 0)
+ goto fail_wr;
+
+ if ((size_t)ret < bs)
+ goto fail_wr_trunc;
+ }
+ }
+
+ fragsz = fi->size % block_size;
+
+ if (fragsz > 0) {
+ if (frag_reader_read(frag, fi->fragment, fi->fragment_offset,
+ buffer, fragsz)) {
+ goto fail;
+ }
+
+ ret = write_retry(outfd, buffer, fragsz);
+ if (ret < 0)
+ goto fail_wr;
+
+ if ((size_t)ret < fragsz)
+ goto fail_wr_trunc;
+ }
+
+ free(buffer);
+ return 0;
+fail_seek:
+ perror("seek on squashfs");
+ goto fail;
+fail_wr:
+ perror("writing uncompressed block");
+ goto fail;
+fail_wr_trunc:
+ fputs("writing uncompressed block: truncated write\n", stderr);
+ goto fail;
+fail_rd:
+ perror("reading from squashfs");
+ goto fail;
+fail_trunc:
+ fputs("reading from squashfs: unexpected end of file\n", stderr);
+ goto fail;
+fail_bs:
+ fputs("found compressed block larger than block size\n", stderr);
+ goto fail;
+fail:
+ free(buffer);
+ return -1;
+}