diff options
Diffstat (limited to 'tar')
| -rw-r--r-- | tar/sqfs2tar.c | 99 | ||||
| -rw-r--r-- | tar/tar2sqfs.c | 105 | 
2 files changed, 171 insertions, 33 deletions
| diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c index 9a12a96..8cfad62 100644 --- a/tar/sqfs2tar.c +++ b/tar/sqfs2tar.c @@ -19,13 +19,14 @@  static struct option long_opts[] = {  	{ "subdir", required_argument, NULL, 'd' },  	{ "keep-as-dir", no_argument, NULL, 'k' }, +	{ "root-becomes", required_argument, NULL, 'r' },  	{ "no-skip", no_argument, NULL, 's' },  	{ "no-xattr", no_argument, NULL, 'X' },  	{ "help", no_argument, NULL, 'h' },  	{ "version", no_argument, NULL, 'V' },  }; -static const char *short_opts = "d:ksXhV"; +static const char *short_opts = "d:kr:sXhV";  static const char *usagestr =  "Usage: sqfs2tar [OPTIONS...] <sqfsfile>\n" @@ -41,6 +42,14 @@ static const char *usagestr =  "                            one is specified, it becomes the new root of\n"  "                            node of the archive file system tree.\n"  "\n" +"  --root-becomes, -r <dir>  Turn the root inode into a directory with the\n" +"                            specified name. Everything else will be stored\n" +"                            inside this directory. The special value '.' is\n" +"                            allowed to prefix all tar paths with './' and\n" +"                            add an entry named '.' for the root inode.\n" +"                            If this option isn't used, all meta data stored\n" +"                            in the root inode IS LOST!\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" @@ -68,6 +77,7 @@ static bool dont_skip = false;  static bool keep_as_dir = false;  static bool no_xattr = false; +static char *root_becomes = NULL;  static char **subdirs = NULL;  static size_t num_subdirs = 0;  static size_t max_subdirs = 0; @@ -114,6 +124,26 @@ static void process_args(int argc, char **argv)  			++num_subdirs;  			break; +		case 'r': +			free(root_becomes); +			root_becomes = strdup(optarg); +			if (root_becomes == NULL) +				goto fail_errno; + +			if (strcmp(root_becomes, "./") == 0) +				root_becomes[1] = '\0'; + +			if (strcmp(root_becomes, ".") == 0) +				break; + +			if (canonicalize_name(root_becomes) != 0 || +			    strlen(root_becomes) == 0) { +				fprintf(stderr, +					"Invalid root directory '%s'.\n", +					optarg); +				goto fail_arg; +			} +			break;  		case 'k':  			keep_as_dir = true;  			break; @@ -165,6 +195,7 @@ out_success:  out_exit:  	for (idx = 0; idx < num_subdirs; ++idx)  		free(subdirs[idx]); +	free(root_becomes);  	free(subdirs);  	exit(ret);  } @@ -260,31 +291,62 @@ fail:  static int write_tree_dfs(const sqfs_tree_node_t *n)  {  	tar_xattr_t *xattr = NULL, *xit; -	char *name, *target; +	char *name, *target, *temp;  	struct stat sb; +	size_t len;  	int ret; -	if (n->parent == NULL && S_ISDIR(n->inode->base.mode)) -		goto skip_hdr; +	if (n->parent == NULL) { +		if (root_becomes == NULL) +			goto skip_hdr; -	if (!is_filename_sane((const char *)n->name)) { -		fprintf(stderr, "Found a file named '%s', skipping.\n", -			n->name); -		if (dont_skip) { -			fputs("Not allowed to skip files, aborting!\n", stderr); +		len = strlen(root_becomes); +		name = malloc(len + 2); +		if (name == NULL) { +			perror("creating root directory");  			return -1;  		} -		return 0; -	} -	name = sqfs_tree_node_get_path(n); -	if (name == NULL) { -		perror("resolving tree node path"); -		return -1; -	} +		memcpy(name, root_becomes, len); +		name[len] = '/'; +		name[len + 1] = '\0'; +	} else { +		if (!is_filename_sane((const char *)n->name)) { +			fprintf(stderr, "Found a file named '%s', skipping.\n", +				n->name); +			if (dont_skip) { +				fputs("Not allowed to skip files, aborting!\n", +				      stderr); +				return -1; +			} +			return 0; +		} -	if (canonicalize_name(name)) -		goto out_skip; +		name = sqfs_tree_node_get_path(n); +		if (name == NULL) { +			perror("resolving tree node path"); +			return -1; +		} + +		if (canonicalize_name(name)) +			goto out_skip; + +		if (root_becomes != NULL) { +			len = strlen(root_becomes); +			temp = realloc(name, strlen(name) + len + 2); + +			if (temp == NULL) { +				perror("assembling tar entry filename"); +				free(name); +				return -1; +			} + +			name = temp; +			memmove(name + len + 1, name, strlen(name) + 1); +			memcpy(name, root_becomes, len); +			name[len] = '/'; +		} +	}  	inode_stat(n, &sb); @@ -565,5 +627,6 @@ out_dirs:  	for (i = 0; i < num_subdirs; ++i)  		free(subdirs[i]);  	free(subdirs); +	free(root_becomes);  	return status;  } 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...] <sqfsfile>\n" @@ -47,6 +48,13 @@ static const char *usagestr =  "\n"  "Possible options:\n"  "\n" +"  --root-becomes, -r <dir>    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 <name>     Select the compressor to use.\n"  "                              A list of available compressors is below.\n"  "  --comp-extra, -X <options>  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) { | 
