diff options
Diffstat (limited to 'tar')
| -rw-r--r-- | tar/sqfs2tar.c | 191 | 
1 files changed, 151 insertions, 40 deletions
| 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;  } | 
