aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/data_writer.h5
-rw-r--r--include/sqfs/io.h29
-rw-r--r--lib/sqfs/io.c66
-rw-r--r--lib/sqfshelper/data_writer.c80
-rw-r--r--tar/tar2sqfs.c22
5 files changed, 123 insertions, 79 deletions
diff --git a/include/data_writer.h b/include/data_writer.h
index c9b416c..4d5fa12 100644
--- a/include/data_writer.h
+++ b/include/data_writer.h
@@ -97,8 +97,9 @@ int write_data_from_file(data_writer_t *data, file_info_t *fi,
Returns 0 on success, prints errors to stderr.
*/
-int write_data_from_fd_condensed(data_writer_t *data, file_info_t *fi,
- int infd, sqfs_sparse_map_t *map, int flags);
+int write_data_from_file_condensed(data_writer_t *data, sqfs_file_t *file,
+ file_info_t *fi,
+ const sqfs_sparse_map_t *map, int flags);
data_writer_stats_t *data_writer_get_stats(data_writer_t *data);
diff --git a/include/sqfs/io.h b/include/sqfs/io.h
index cf6f1fa..bbc3da9 100644
--- a/include/sqfs/io.h
+++ b/include/sqfs/io.h
@@ -189,6 +189,35 @@ SQFS_API int sqfs_file_create_block(sqfs_file_t *file, uint64_t offset,
size_t size, void *user, uint32_t flags,
sqfs_block_t **out);
+/**
+ * @brief Read a chunk from a condensed version of a sparse file and turn it
+ * into a block that can be fed to a block processor.
+ *
+ * @member sqfs_file_t
+ *
+ * This function works on condensed sparse files, i.e. a sparse file that had
+ * its holdes removed. The given mapping describes the original data region
+ * that are actually packed next to each other. The function emulates the
+ * orignal sparse file by zero-initializing the block data, then figuring
+ * out which regions overlap the block, working out their physical location and
+ * stitching the block together.
+ *
+ * @param file A pointer to a file implementation.
+ * @param offset A byte offset into the file.
+ * @param size The number of bytes to read, starting at the given offset.
+ * @param user A user pointer to set for the block.
+ * @param flags The flags to store in the newly created block.
+ * @param map Describes the data regions of the original sparse file.
+ * @param out Returns a pointer to a block on success.
+ *
+ * @return Zero on success, an @ref E_SQFS_ERROR identifier on failure.
+ */
+SQFS_API int sqfs_file_create_block_dense(sqfs_file_t *file, uint64_t offset,
+ size_t size, void *user,
+ uint32_t flags,
+ const sqfs_sparse_map_t *map,
+ sqfs_block_t **out);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/sqfs/io.c b/lib/sqfs/io.c
index 4afb489..b41fc4a 100644
--- a/lib/sqfs/io.c
+++ b/lib/sqfs/io.c
@@ -37,3 +37,69 @@ int sqfs_file_create_block(sqfs_file_t *file, uint64_t offset,
*out = blk;
return 0;
}
+
+int sqfs_file_create_block_dense(sqfs_file_t *file, uint64_t offset,
+ size_t size, void *user, uint32_t flags,
+ const sqfs_sparse_map_t *map,
+ sqfs_block_t **out)
+{
+ sqfs_block_t *blk = alloc_flex(sizeof(*blk), 1, size);
+ size_t dst_start, diff, count;
+ const sqfs_sparse_map_t *it;
+ uint64_t poffset, src_start;
+ int err;
+
+ if (blk == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ poffset = 0;
+
+ for (it = map; it != NULL; it = it->next) {
+ if (it->offset + it->count <= offset) {
+ poffset += it->count;
+ continue;
+ }
+
+ if (it->offset >= offset + size) {
+ poffset += it->count;
+ continue;
+ }
+
+ count = size;
+
+ if (offset + count >= it->offset + it->count)
+ count = it->offset + it->count - offset;
+
+ if (it->offset < offset) {
+ diff = offset - it->offset;
+
+ src_start = poffset + diff;
+ dst_start = 0;
+ count -= diff;
+ } else if (it->offset > offset) {
+ diff = it->offset - offset;
+
+ src_start = poffset;
+ dst_start = diff;
+ } else {
+ src_start = poffset;
+ dst_start = 0;
+ }
+
+ err = file->read_at(file, src_start,
+ blk->data + dst_start, count);
+ if (err) {
+ free(blk);
+ return err;
+ }
+
+ poffset += it->count;
+ }
+
+ blk->user = user;
+ blk->size = size;
+ blk->flags = flags;
+
+ *out = blk;
+ return 0;
+}
diff --git a/lib/sqfshelper/data_writer.c b/lib/sqfshelper/data_writer.c
index a6d0297..92592c9 100644
--- a/lib/sqfshelper/data_writer.c
+++ b/lib/sqfshelper/data_writer.c
@@ -390,77 +390,15 @@ int write_data_from_file(data_writer_t *data, file_info_t *fi,
return 0;
}
-static int check_map_valid(const sqfs_sparse_map_t *map, file_info_t *fi)
-{
- const sqfs_sparse_map_t *m;
- uint64_t offset;
-
- if (map != NULL) {
- offset = map->offset;
-
- for (m = map; m != NULL; m = m->next) {
- if (m->offset < offset)
- goto fail_map;
- offset = m->offset + m->count;
- }
-
- if (offset > fi->size)
- goto fail_map_size;
- }
-
- return 0;
-fail_map_size:
- fprintf(stderr, "%s: sparse file map spans beyond file size\n",
- fi->input_file);
- return -1;
-fail_map:
- fprintf(stderr,
- "%s: sparse file map is unordered or self overlapping\n",
- fi->input_file);
- return -1;
-}
-
-static int get_sparse_block(sqfs_block_t *blk, file_info_t *fi, int infd,
- sqfs_sparse_map_t **sparse_map, uint64_t offset,
- size_t diff)
-{
- sqfs_sparse_map_t *map = *sparse_map;
- size_t start, count;
-
- while (map != NULL && map->offset < offset + diff) {
- start = 0;
- count = map->count;
-
- if (map->offset < offset)
- count -= offset - map->offset;
-
- if (map->offset > offset)
- start = map->offset - offset;
-
- if (start + count > diff)
- count = diff - start;
-
- if (read_data(fi->input_file, infd, blk->data + start, count))
- return -1;
-
- map = map->next;
- }
-
- *sparse_map = map;
- return 0;
-}
-
-int write_data_from_fd_condensed(data_writer_t *data, file_info_t *fi,
- int infd, sqfs_sparse_map_t *map, int flags)
+int write_data_from_file_condensed(data_writer_t *data, sqfs_file_t *file,
+ file_info_t *fi,
+ const sqfs_sparse_map_t *map, int flags)
{
uint32_t blk_flags = BLK_FIRST_BLOCK;
size_t diff, i = 0;
sqfs_block_t *blk;
uint64_t offset;
- if (check_map_valid(map, fi))
- return -1;
-
if (flags & DW_DONT_COMPRESS)
blk_flags |= SQFS_BLK_DONT_COMPRESS;
@@ -474,17 +412,13 @@ int write_data_from_fd_condensed(data_writer_t *data, file_info_t *fi,
diff = fi->size - offset;
}
- blk = alloc_flex(sizeof(*blk), 1, diff);
- blk->size = diff;
- blk->index = i++;
- blk->user = fi;
- blk->flags = blk_flags;
-
- if (get_sparse_block(blk, fi, infd, &map, offset, diff)) {
- free(blk);
+ if (sqfs_file_create_block_dense(file, offset, diff, fi,
+ blk_flags, map, &blk)) {
return -1;
}
+ blk->index = i++;
+
if (is_zero_block(blk->data, blk->size)) {
data->stats.sparse_blocks += 1;
diff --git a/tar/tar2sqfs.c b/tar/tar2sqfs.c
index ca00579..84d56f4 100644
--- a/tar/tar2sqfs.c
+++ b/tar/tar2sqfs.c
@@ -225,12 +225,24 @@ fail_arg:
static int write_file(tar_header_decoded_t *hdr, file_info_t *fi,
data_writer_t *data)
{
+ const sqfs_sparse_map_t *it;
sqfs_file_t *file;
+ uint64_t sum;
int ret;
if (hdr->sparse != NULL) {
- ret = write_data_from_fd_condensed(data, fi, STDIN_FILENO,
- hdr->sparse, 0);
+ for (sum = 0, it = hdr->sparse; it != NULL; it = it->next)
+ sum += it->count;
+
+ file = sqfs_get_stdin_file(sum);
+ if (file == NULL) {
+ perror("packing files");
+ return -1;
+ }
+
+ ret = write_data_from_file_condensed(data, file, fi,
+ hdr->sparse, 0);
+ file->destroy(file);
if (ret)
return -1;
@@ -243,10 +255,12 @@ static int write_file(tar_header_decoded_t *hdr, file_info_t *fi,
return -1;
}
- if (write_data_from_file(data, fi, file, 0))
+ ret = write_data_from_file(data, fi, file, 0);
+ file->destroy(file);
+
+ if (ret)
return -1;
- file->destroy(file);
return skip_padding(STDIN_FILENO, fi->size);
}