diff options
Diffstat (limited to 'lib/sqfs/data_writer.c')
-rw-r--r-- | lib/sqfs/data_writer.c | 150 |
1 files changed, 118 insertions, 32 deletions
diff --git a/lib/sqfs/data_writer.c b/lib/sqfs/data_writer.c index 70fe884..8492c98 100644 --- a/lib/sqfs/data_writer.c +++ b/lib/sqfs/data_writer.c @@ -18,6 +18,8 @@ struct data_writer_t { size_t max_fragments; size_t frag_offset; + int block_idx; + sqfs_super_t *super; compressor_t *cmp; int outfd; @@ -111,18 +113,55 @@ int data_writer_flush_fragments(data_writer_t *data) return 0; } -int write_data_from_fd(data_writer_t *data, file_info_t *fi, int infd) +static int flush_data_block(data_writer_t *data, size_t size, file_info_t *fi) { - uint64_t count = fi->size; - int blk_idx = 0; uint32_t out; + + if (is_zero_block(data->block, size)) { + if (size < data->super->block_size) { + fi->fragment_offset = 0xFFFFFFFF; + fi->fragment = 0xFFFFFFFF; + } else { + fi->blocksizes[data->block_idx++] = 0; + } + + fi->sparse += size; + return 0; + } + + if (size < data->super->block_size) { + if (data->frag_offset + size > data->super->block_size) { + if (data_writer_flush_fragments(data)) + return -1; + } + + fi->fragment_offset = data->frag_offset; + fi->fragment = data->num_fragments; + + memcpy((char *)data->fragment + data->frag_offset, + data->block, size); + data->frag_offset += size; + } else { + if (write_compressed(data, data->block, size, &out)) + return -1; + + fi->blocksizes[data->block_idx++] = out; + } + + return 0; +} + +int write_data_from_fd(data_writer_t *data, file_info_t *fi, int infd) +{ + uint64_t count; ssize_t ret; size_t diff; fi->startblock = data->super->bytes_used; fi->sparse = 0; + data->block_idx = 0; - while (count != 0) { + for (count = fi->size; count != 0; count -= diff) { diff = count > (uint64_t)data->super->block_size ? data->super->block_size : count; @@ -132,45 +171,92 @@ int write_data_from_fd(data_writer_t *data, file_info_t *fi, int infd) if ((size_t)ret < diff) goto fail_trunc; - if (is_zero_block(data->block, diff)) { - if (diff < data->super->block_size) { - fi->fragment_offset = 0xFFFFFFFF; - fi->fragment = 0xFFFFFFFF; - } else { - fi->blocksizes[blk_idx++] = 0; - } - fi->sparse += diff; - count -= diff; - continue; - } + if (flush_data_block(data, diff, fi)) + return -1; + } - if (diff < data->super->block_size) { - if (data->frag_offset + diff > data->super->block_size) { - if (data_writer_flush_fragments(data)) - return -1; - } + return 0; +fail_read: + perror(fi->input_file); + return -1; +fail_trunc: + fprintf(stderr, "%s: truncated read\n", fi->input_file); + return -1; +} + +int write_data_from_fd_condensed(data_writer_t *data, file_info_t *fi, + int infd, sparse_map_t *map) +{ + size_t start, count, diff; + sparse_map_t *m; + uint64_t offset; + ssize_t ret; - fi->fragment_offset = data->frag_offset; - fi->fragment = data->num_fragments; + fi->startblock = data->super->bytes_used; + fi->sparse = 0; + data->block_idx = 0; + + 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; + } - memcpy((char *)data->fragment + data->frag_offset, - data->block, diff); - data->frag_offset += diff; + if (offset > fi->size) + goto fail_map_size; + } + + for (offset = 0; offset < fi->size; offset += diff) { + if (fi->size - offset >= (uint64_t)data->super->block_size) { + diff = data->super->block_size; } else { - if (write_compressed(data, data->block, - data->super->block_size, &out)) { - return -1; - } + diff = fi->size - offset; + } + + memset(data->block, 0, diff); + + while (map != NULL && map->offset < offset + diff) { + start = 0; + count = map->count; + + if (map->offset < offset) + count -= offset - map->offset; - fi->blocksizes[blk_idx++] = out; + if (map->offset > offset) + start = map->offset - offset; + + if (start + count > diff) + count = diff - start; + + ret = read_retry(infd, (char *)data->block + start, + count); + if (ret < 0) + goto fail_read; + if ((size_t)ret < count) + goto fail_trunc; + + map = map->next; } - count -= diff; + if (flush_data_block(data, diff, fi)) + return -1; } 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; fail_read: - fprintf(stderr, "read from %s: %s\n", fi->input_file, strerror(errno)); + perror(fi->input_file); return -1; fail_trunc: fprintf(stderr, "%s: truncated read\n", fi->input_file); |