summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/data_reader.h10
-rw-r--r--lib/sqfs/data_reader.c81
2 files changed, 91 insertions, 0 deletions
diff --git a/include/data_reader.h b/include/data_reader.h
index c6d85f1..51bfff3 100644
--- a/include/data_reader.h
+++ b/include/data_reader.h
@@ -41,4 +41,14 @@ void data_reader_destroy(data_reader_t *data);
int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd,
bool allow_sparse);
+/*
+ Read a chunk of data from a file. Starting from 'offset' into the
+ uncompressed file, read 'size' bytes into 'buffer'.
+
+ Returns the number of bytes read, 0 if EOF, -1 on failure. Prints an
+ error message to stderr on failure.
+ */
+ssize_t data_reader_read(data_reader_t *data, file_info_t *fi,
+ uint64_t offset, void *buffer, size_t size);
+
#endif /* DATA_READER_H */
diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c
index e33ffbb..27a8302 100644
--- a/lib/sqfs/data_reader.c
+++ b/lib/sqfs/data_reader.c
@@ -225,3 +225,84 @@ fail_sparse:
perror("creating sparse output file");
return -1;
}
+
+ssize_t data_reader_read(data_reader_t *data, file_info_t *fi,
+ uint64_t offset, void *buffer, size_t size)
+{
+ size_t i, diff, fragsz, count, total = 0;
+ off_t off;
+ char *ptr;
+
+ /* work out block count and fragment size */
+ fragsz = fi->size % data->block_size;
+ count = fi->size / data->block_size;
+
+ if (fragsz != 0 && !(fi->flags & FILE_FLAG_HAS_FRAGMENT)) {
+ fragsz = 0;
+ ++count;
+ }
+
+ /* work out block index and on-disk location */
+ off = fi->startblock;
+ i = 0;
+
+ while (offset > data->block_size && i < count) {
+ off += SQFS_ON_DISK_BLOCK_SIZE(fi->blocks[i++].size);
+ offset -= data->block_size;
+ }
+
+ /* copy data from blocks */
+ while (i < count && size > 0) {
+ diff = data->block_size - offset;
+ if (size < diff)
+ size = diff;
+
+ if (SQFS_IS_SPARSE_BLOCK(fi->blocks[i].size)) {
+ memset(buffer, 0, diff);
+ } else {
+ if (precache_data_block(data, off, fi->blocks[i].size))
+ return -1;
+
+ memcpy(buffer, (char *)data->block + offset, diff);
+ off += SQFS_ON_DISK_BLOCK_SIZE(fi->blocks[i].size);
+ }
+
+ ++i;
+ offset = 0;
+ size -= diff;
+ total += diff;
+ buffer = (char *)buffer + diff;
+ }
+
+ /* copy from fragment */
+ if (i == count && size > 0 && fragsz > 0) {
+ if (precache_fragment_block(data, fi->fragment))
+ return -1;
+
+ if (fi->fragment_offset >= data->frag_used)
+ goto fail_range;
+
+ if ((fi->fragment_offset + fragsz - 1) >= data->frag_used)
+ goto fail_range;
+
+ ptr = (char *)data->frag_block + fi->fragment_offset;
+ ptr += offset;
+
+ if (offset >= fragsz) {
+ offset = 0;
+ size = 0;
+ }
+
+ if (offset + size > fragsz)
+ size = fragsz - offset;
+
+ if (size > 0) {
+ memcpy(buffer, ptr + offset, size);
+ total += size;
+ }
+ }
+ return total;
+fail_range:
+ fputs("attempted to read past fragment block limits\n", stderr);
+ return -1;
+}