diff options
-rw-r--r-- | include/data_writer.h | 5 | ||||
-rw-r--r-- | include/sqfs/io.h | 29 | ||||
-rw-r--r-- | lib/sqfs/io.c | 66 | ||||
-rw-r--r-- | lib/sqfshelper/data_writer.c | 80 | ||||
-rw-r--r-- | tar/tar2sqfs.c | 22 |
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); } |