From d92e4dc101bcb6f807accff8c8ecad4030f41afb Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 28 Jun 2019 13:01:17 +0200 Subject: Add support for unpacking sparse files as sparse files Signed-off-by: David Oberhollenzer --- include/data_reader.h | 6 +++++- lib/sqfs/data_reader.c | 20 +++++++++++++++++++- tar/sqfs2tar.c | 2 +- unpack/options.c | 8 +++++++- unpack/rdsquashfs.c | 3 ++- unpack/rdsquashfs.h | 1 + unpack/restore_fstree.c | 3 ++- 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/include/data_reader.h b/include/data_reader.h index 68788b4..7de8e1a 100644 --- a/include/data_reader.h +++ b/include/data_reader.h @@ -26,8 +26,12 @@ void data_reader_destroy(data_reader_t *data); file and its fragment, if it has one. The entire data is dumped to the given file descriptor. + If allow_sparse is true, try to truncate and seek forward on outfd if a + zero block is found. If false, always write blocks of zeros to outfd. + Returns 0 on success, prints error messages to stderr on failure. */ -int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd); +int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, + bool allow_sparse); #endif /* DATA_READER_H */ diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c index 421d914..f8c80af 100644 --- a/lib/sqfs/data_reader.c +++ b/lib/sqfs/data_reader.c @@ -59,7 +59,8 @@ void data_reader_destroy(data_reader_t *data) free(data); } -int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd) +int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, + bool allow_sparse) { size_t i, count, fragsz; bool compressed; @@ -82,6 +83,14 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd) if (bs > data->block_size) goto fail_bs; + if (bs == 0 && allow_sparse) { + if (ftruncate(outfd, i * data->block_size)) + goto fail_sparse; + if (lseek(outfd, 0, SEEK_END) == (off_t)-1) + goto fail_sparse; + continue; + } + if (bs == 0) { bs = data->block_size; memset(data->buffer, 0, bs); @@ -123,6 +132,12 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd) if (fragsz > 0) { if (fi->fragment == 0xFFFFFFFF || fi->fragment_offset == 0xFFFFFFFF) { + if (allow_sparse) { + if (ftruncate(outfd, fragsz)) + goto fail_sparse; + return 0; + } + memset(data->buffer, 0, fragsz); } else { if (data->frag == NULL) @@ -144,6 +159,9 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd) } return 0; +fail_sparse: + perror("creating sparse output file"); + return -1; fail_seek: perror("seek on squashfs"); return -1; diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c index 1be1b7e..e8b8736 100644 --- a/tar/sqfs2tar.c +++ b/tar/sqfs2tar.c @@ -131,7 +131,7 @@ static int write_tree_dfs(fstree_t *fs, tree_node_t *n, data_reader_t *data) if (S_ISREG(n->mode)) { if (data_reader_dump_file(data, n->data.file, - STDOUT_FILENO)) { + STDOUT_FILENO, false)) { return -1; } diff --git a/unpack/options.c b/unpack/options.c index 84c17cf..c42f16a 100644 --- a/unpack/options.c +++ b/unpack/options.c @@ -11,6 +11,7 @@ static struct option long_opts[] = { { "no-fifo", no_argument, NULL, 'F' }, { "no-slink", no_argument, NULL, 'L' }, { "no-empty-dir", no_argument, NULL, 'E' }, + { "no-sparse", no_argument, NULL, 'Z' }, { "describe", no_argument, NULL, 'd' }, { "chmod", no_argument, NULL, 'C' }, { "chown", no_argument, NULL, 'O' }, @@ -19,7 +20,7 @@ static struct option long_opts[] = { { "version", no_argument, NULL, 'V' }, }; -static const char *short_opts = "l:c:u:p:DSFLCOEdqhV"; +static const char *short_opts = "l:c:u:p:DSFLCOEZdqhV"; static const char *help_string = "Usage: %s [OPTIONS] \n" @@ -47,6 +48,8 @@ static const char *help_string = " --no-slink, -L Do not unpack symbolic links.\n" " --no-empty-dir, -E Do not unpack directories that would end up\n" " empty after applying the above rules.\n" +" --no-sparse, -Z Do not create sparse files, always write zero\n" +" blocks to disk.\n" " --chmod, -C Change permission flags of unpacked files to\n" " those store in the squashfs image.\n" " --chown, -O Change ownership of unpacked files to the\n" @@ -118,6 +121,9 @@ void process_command_line(options_t *opt, int argc, char **argv) case 'O': opt->flags |= UNPACK_CHOWN; break; + case 'Z': + opt->flags |= UNPACK_NO_SPARSE; + break; case 'c': opt->op = OP_CAT; opt->cmdpath = get_path(opt->cmdpath, optarg); diff --git a/unpack/rdsquashfs.c b/unpack/rdsquashfs.c index fcfc9c9..42bddff 100644 --- a/unpack/rdsquashfs.c +++ b/unpack/rdsquashfs.c @@ -100,7 +100,8 @@ int main(int argc, char **argv) if (data == NULL) goto out_fs; - if (data_reader_dump_file(data, n->data.file, STDOUT_FILENO)) + if (data_reader_dump_file(data, n->data.file, + STDOUT_FILENO, false)) goto out_fs; break; case OP_UNPACK: diff --git a/unpack/rdsquashfs.h b/unpack/rdsquashfs.h index 9d1ab4f..0ee48fa 100644 --- a/unpack/rdsquashfs.h +++ b/unpack/rdsquashfs.h @@ -24,6 +24,7 @@ enum UNPACK_FLAGS { UNPACK_CHMOD = 0x01, UNPACK_CHOWN = 0x02, UNPACK_QUIET = 0x04, + UNPACK_NO_SPARSE = 0x08, }; enum { diff --git a/unpack/restore_fstree.c b/unpack/restore_fstree.c index 976b15c..ab10f5d 100644 --- a/unpack/restore_fstree.c +++ b/unpack/restore_fstree.c @@ -63,7 +63,8 @@ static int create_node(tree_node_t *n, data_reader_t *data, int flags) return -1; } - if (data_reader_dump_file(data, n->data.file, fd)) { + if (data_reader_dump_file(data, n->data.file, fd, + (flags & UNPACK_NO_SPARSE) == 0)) { close(fd); return -1; } -- cgit v1.2.3