From 8f6cac0f6cf931eb08243447d48a71425e5ec3d4 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 2 Dec 2019 01:01:43 +0100 Subject: Add a --root-becomes option to tar2sqfs and sqfs2tar Signed-off-by: David Oberhollenzer --- tar/tar2sqfs.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 15 deletions(-) (limited to 'tar/tar2sqfs.c') diff --git a/tar/tar2sqfs.c b/tar/tar2sqfs.c index 56ae5dc..cc0ccfb 100644 --- a/tar/tar2sqfs.c +++ b/tar/tar2sqfs.c @@ -20,6 +20,7 @@ #endif static struct option long_opts[] = { + { "root-becomes", required_argument, NULL, 'r' }, { "compressor", required_argument, NULL, 'c' }, { "block-size", required_argument, NULL, 'b' }, { "dev-block-size", required_argument, NULL, 'B' }, @@ -37,7 +38,7 @@ static struct option long_opts[] = { { "version", no_argument, NULL, 'V' }, }; -static const char *short_opts = "c:b:B:d:X:j:Q:sxekfqhV"; +static const char *short_opts = "r:c:b:B:d:X:j:Q:sxekfqhV"; static const char *usagestr = "Usage: tar2sqfs [OPTIONS...] \n" @@ -47,6 +48,13 @@ static const char *usagestr = "\n" "Possible options:\n" "\n" +" --root-becomes, -r The specified directory becomes the root.\n" +" Only its children are packed into the image\n" +" and its attributes (ownership, permissions,\n" +" xattrs, ...) are stored in the root inode.\n" +" If not set and a tarbal has an entry for './'\n" +" or '/', it becomes the root instead.\n" +"\n" " --compressor, -c Select the compressor to use.\n" " A list of available compressors is below.\n" " --comp-extra, -X A comma seperated list of extra options for\n" @@ -93,6 +101,7 @@ static bool keep_time = true; static sqfs_writer_cfg_t cfg; static sqfs_writer_t sqfs; static FILE *input_file = NULL; +static char *root_becomes = NULL; static void process_args(int argc, char **argv) { @@ -156,6 +165,22 @@ static void process_args(int argc, char **argv) case 'k': keep_time = false; break; + case 'r': + free(root_becomes); + root_becomes = strdup(optarg); + if (root_becomes == NULL) { + perror("copying root directory name"); + exit(EXIT_FAILURE); + } + + if (canonicalize_name(root_becomes) != 0 || + strlen(root_becomes) == 0) { + fprintf(stderr, + "Invalid root directory '%s'.\n", + optarg); + goto fail_arg; + } + break; case 's': dont_skip = true; break; @@ -273,7 +298,7 @@ static int write_file(tar_header_decoded_t *hdr, file_info_t *fi, filesize : hdr->record_size); } -static int copy_xattr(tree_node_t *node, tar_header_decoded_t *hdr) +static int copy_xattr(tree_node_t *node, const tar_header_decoded_t *hdr) { tar_xattr_t *xattr; int ret; @@ -346,14 +371,39 @@ fail_errno: return -1; } +static int set_root_attribs(const tar_header_decoded_t *hdr) +{ + if (!S_ISDIR(hdr->sb.st_mode)) { + fprintf(stderr, "'%s' is not a directory!\n", hdr->name); + return -1; + } + + sqfs.fs.root->uid = hdr->sb.st_uid; + sqfs.fs.root->gid = hdr->sb.st_gid; + sqfs.fs.root->mode = hdr->sb.st_mode; + + if (keep_time) + sqfs.fs.root->mod_time = hdr->sb.st_mtime; + + if (!cfg.no_xattr) { + if (copy_xattr(sqfs.fs.root, hdr)) + return -1; + } + + return 0; +} + static int process_tar_ball(void) { + bool skip, is_root, is_prefixed; tar_header_decoded_t hdr; sqfs_u64 offset, count; sparse_map_t *m; - bool skip; + size_t rootlen; int ret; + rootlen = root_becomes == NULL ? 0 : strlen(root_becomes); + for (;;) { ret = read_header(input_file, &hdr); if (ret > 0) @@ -362,15 +412,8 @@ static int process_tar_ball(void) return -1; skip = false; - - if (hdr.name != NULL && strcmp(hdr.name, "./") == 0 && - S_ISDIR(hdr.sb.st_mode)) { - /* XXX: tar entries might be prefixed with ./ which is - stripped by cannonicalize_name, but the tar file may - contain a directory entry named './' */ - clear_header(&hdr); - continue; - } + is_root = false; + is_prefixed = true; if (hdr.name == NULL || canonicalize_name(hdr.name) != 0) { fprintf(stderr, "skipping '%s' (invalid name)\n", @@ -378,9 +421,41 @@ static int process_tar_ball(void) skip = true; } - if (hdr.name[0] == '\0') { - fputs("skipping entry with empty name\n", stderr); - skip = true; + if (root_becomes != NULL) { + if (strncmp(hdr.name, root_becomes, rootlen) == 0) { + if (hdr.name[rootlen] == '\0') { + is_root = true; + } else if (hdr.name[rootlen] != '/') { + is_prefixed = false; + } + } else { + is_prefixed = false; + } + + if (is_prefixed && !is_root) { + memmove(hdr.name, hdr.name + rootlen + 1, + strlen(hdr.name + rootlen + 1) + 1); + } + + if (is_prefixed && hdr.name[0] == '\0') { + fputs("skipping entry with empty name\n", + stderr); + skip = true; + } + } else if (hdr.name[0] == '\0') { + is_root = true; + } + + if (!is_prefixed) { + clear_header(&hdr); + continue; + } + + if (is_root) { + if (set_root_attribs(&hdr)) + goto fail; + clear_header(&hdr); + continue; } if (!skip && hdr.unknown_record) { -- cgit v1.2.3