summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-03 19:27:08 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-07-04 01:32:45 +0200
commit9899d4181084b0cb488fe6161ac3ffe9ccd55c29 (patch)
tree5966d87474fd41b6a6ae55413e7f8541af20e1cb
parentda7eb3445cab5936455b8a119cb656113ca3d402 (diff)
sqfs2tar: add option to extract subdirectories
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--doc/sqfs2tar.110
-rw-r--r--tar/sqfs2tar.c191
2 files changed, 161 insertions, 40 deletions
diff --git a/doc/sqfs2tar.1 b/doc/sqfs2tar.1
index c7389ed..2cf9f9c 100644
--- a/doc/sqfs2tar.1
+++ b/doc/sqfs2tar.1
@@ -13,6 +13,16 @@ work on tar archives.
.PP
Possible options:
.TP
+\fB\-\-subdir\fR, \fB\-d\fR <dir>
+Unpack the given sub directory instead of the filesystem root. Can be specified
+more than once to select multiple directories. If only one is specified, it
+becomes the new root of node of the archive file system tree.
+.TP
+\fB\-\-keep\-as\-dir\fR, \fB\-k\fR
+If \fB\-\-subdir\fR is used only once, don't make the subdir the archive root,
+instead keep it as prefix for all unpacked files. Using \fB\-\-subdir\fR more
+than once implies \fB\-\-keep\-as\-dir\fR.
+.TP
\fB\-\-no\-skip\fR, \fB\-s\fR
Abort if file cannot be stored in a tar record instead of skipping it.
.TP
diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c
index 76c0d74..110c8c4 100644
--- a/tar/sqfs2tar.c
+++ b/tar/sqfs2tar.c
@@ -17,12 +17,14 @@
#include <stdio.h>
static struct option long_opts[] = {
+ { "subdir", required_argument, NULL, 'd' },
+ { "keep-as-dir", no_argument, NULL, 'k' },
{ "no-skip", no_argument, NULL, 's' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
};
-static const char *short_opts = "shV";
+static const char *short_opts = "d:kshV";
static const char *usagestr =
"Usage: sqfs2tar [OPTIONS...] <sqfsfile>\n"
@@ -32,9 +34,21 @@ static const char *usagestr =
"\n"
"Possible options:\n"
"\n"
+" --subdir, -d <dir> Unpack the given sub directory instead of the\n"
+" filesystem root. Can be specified more than\n"
+" once to select multiple directories. If only\n"
+" one is specified, it becomes the new root of\n"
+" node of the archive file system tree.\n"
+"\n"
+" --keep-as-dir, -k If --subdir is used only once, don't make the\n"
+" subdir the archive root, instead keep it as\n"
+" prefix for all unpacked files.\n"
+" Using --subdir more than once implies\n"
+" --keep-as-dir.\n"
+"\n"
" --no-skip Abort if a file cannot be stored in a tar\n"
" archive. By default, it is simply skipped\n"
-" and a warning is written to stderr."
+" and a warning is written to stderr.\n"
"\n"
" --help, -h Print help text and exit.\n"
" --version, -V Print version information and exit.\n"
@@ -49,10 +63,19 @@ static const char *usagestr =
static const char *filename;
static unsigned int record_counter;
static bool dont_skip = false;
+static bool keep_as_dir = false;
+
+static const char *current_subdir = NULL;
+
+static char **subdirs = NULL;
+static size_t num_subdirs = 0;
+static size_t max_subdirs = 0;
static void process_args(int argc, char **argv)
{
- int i;
+ size_t idx, new_count;
+ int i, ret;
+ void *new;
for (;;) {
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
@@ -60,15 +83,41 @@ static void process_args(int argc, char **argv)
break;
switch (i) {
+ case 'd':
+ if (num_subdirs == max_subdirs) {
+ new_count = max_subdirs ? max_subdirs * 2 : 16;
+ new = realloc(subdirs,
+ new_count * sizeof(subdirs[0]));
+ if (new == NULL)
+ goto fail_errno;
+
+ max_subdirs = new_count;
+ subdirs = new;
+ }
+
+ subdirs[num_subdirs] = strdup(optarg);
+ if (subdirs[num_subdirs] == NULL)
+ goto fail_errno;
+
+ if (canonicalize_name(subdirs[num_subdirs])) {
+ perror(optarg);
+ goto fail;
+ }
+
+ ++num_subdirs;
+ break;
+ case 'k':
+ keep_as_dir = true;
+ break;
case 's':
dont_skip = true;
break;
case 'h':
fputs(usagestr, stdout);
- exit(EXIT_SUCCESS);
+ goto out_success;
case 'V':
print_version();
- exit(EXIT_SUCCESS);
+ goto out_success;
default:
goto fail_arg;
}
@@ -85,10 +134,28 @@ static void process_args(int argc, char **argv)
fputs("Unknown extra arguments\n", stderr);
goto fail_arg;
}
+
+ if (num_subdirs > 1)
+ keep_as_dir = true;
+
return;
+fail_errno:
+ perror("parsing options");
+ goto fail;
fail_arg:
fputs("Try `sqfs2tar --help' for more information.\n", stderr);
- exit(EXIT_FAILURE);
+ goto fail;
+fail:
+ ret = EXIT_FAILURE;
+ goto out_exit;
+out_success:
+ ret = EXIT_SUCCESS;
+ goto out_exit;
+out_exit:
+ for (idx = 0; idx < num_subdirs; ++idx)
+ free(subdirs[idx]);
+ free(subdirs);
+ exit(ret);
}
static int terminate_archive(void)
@@ -115,54 +182,71 @@ static int terminate_archive(void)
static int write_tree_dfs(fstree_t *fs, tree_node_t *n, data_reader_t *data)
{
+ size_t len, name_len;
char *name, *target;
struct stat sb;
int ret;
- if (n->parent != NULL || !S_ISDIR(n->mode)) {
- name = fstree_get_path(n);
- if (name == NULL) {
- perror("resolving tree node path");
- return -1;
- }
-
- assert(canonicalize_name(name) == 0);
+ if (n->parent == NULL && S_ISDIR(n->mode))
+ goto skip_hdr;
- fstree_node_stat(fs, n, &sb);
+ name = fstree_get_path(n);
+ if (name == NULL) {
+ perror("resolving tree node path");
+ return -1;
+ }
- target = S_ISLNK(sb.st_mode) ? n->data.slink_target : NULL;
- ret = write_tar_header(STDOUT_FILENO, &sb, name, target,
- record_counter++);
+ assert(canonicalize_name(name) == 0);
- if (ret > 0) {
- if (dont_skip) {
- fputs("Not allowed to skip files, aborting!\n",
- stderr);
- ret = -1;
- } else {
- fprintf(stderr, "Skipping %s\n", name);
- ret = 0;
- }
+ if (current_subdir != NULL && !keep_as_dir) {
+ if (strcmp(name, current_subdir) == 0) {
free(name);
- return ret;
+ goto skip_hdr;
}
- free(name);
+ len = strlen(current_subdir);
+ name_len = strlen(name);
- if (ret < 0)
- return -1;
+ assert(name_len > len);
+ assert(name[len] == '/');
- if (S_ISREG(n->mode)) {
- if (data_reader_dump_file(data, n->data.file,
- STDOUT_FILENO, false)) {
- return -1;
- }
+ memmove(name, name + len + 1, name_len - len);
+ }
- if (padd_file(STDOUT_FILENO, n->data.file->size, 512))
- return -1;
+ fstree_node_stat(fs, n, &sb);
+
+ target = S_ISLNK(sb.st_mode) ? n->data.slink_target : NULL;
+ ret = write_tar_header(STDOUT_FILENO, &sb, name, target,
+ record_counter++);
+
+ if (ret > 0) {
+ if (dont_skip) {
+ fputs("Not allowed to skip files, aborting!\n",
+ stderr);
+ ret = -1;
+ } else {
+ fprintf(stderr, "Skipping %s\n", name);
+ ret = 0;
}
+ free(name);
+ return ret;
}
+ free(name);
+
+ if (ret < 0)
+ return -1;
+
+ if (S_ISREG(n->mode)) {
+ if (data_reader_dump_file(data, n->data.file,
+ STDOUT_FILENO, false)) {
+ return -1;
+ }
+
+ if (padd_file(STDOUT_FILENO, n->data.file->size, 512))
+ return -1;
+ }
+skip_hdr:
if (S_ISDIR(n->mode)) {
for (n = n->data.dir->children; n != NULL; n = n->next) {
if (write_tree_dfs(fs, n, data))
@@ -179,8 +263,10 @@ int main(int argc, char **argv)
int status = EXIT_FAILURE;
sqfs_super_t super;
compressor_t *cmp;
+ tree_node_t *root;
fstree_t fs;
int sqfsfd;
+ size_t i;
process_args(argc, argv);
@@ -216,8 +302,30 @@ int main(int argc, char **argv)
if (data == NULL)
goto out_fs;
- if (write_tree_dfs(&fs, fs.root, data))
- goto out_data;
+ for (i = 0; i < num_subdirs; ++i) {
+ root = fstree_node_from_path(&fs, subdirs[i]);
+ if (root == NULL) {
+ perror(subdirs[i]);
+ goto out_data;
+ }
+
+ if (!S_ISDIR(root->mode)) {
+ fprintf(stderr, "%s is not a directory\n", subdirs[i]);
+ goto out_data;
+ }
+
+ current_subdir = subdirs[i];
+
+ if (write_tree_dfs(&fs, root, data))
+ goto out_data;
+ }
+
+ current_subdir = NULL;
+
+ if (num_subdirs == 0) {
+ if (write_tree_dfs(&fs, fs.root, data))
+ goto out_data;
+ }
if (terminate_archive())
goto out_data;
@@ -231,5 +339,8 @@ out_cmp:
cmp->destroy(cmp);
out_fd:
close(sqfsfd);
+ for (i = 0; i < num_subdirs; ++i)
+ free(subdirs[i]);
+ free(subdirs);
return status;
}