diff options
Diffstat (limited to 'unpack')
| -rw-r--r-- | unpack/Makemodule.am | 1 | ||||
| -rw-r--r-- | unpack/restore_fstree.c | 105 | ||||
| -rw-r--r-- | unpack/unsquashfs.c | 37 | ||||
| -rw-r--r-- | unpack/unsquashfs.h | 3 | 
4 files changed, 139 insertions, 7 deletions
| diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index a8fd2e7..7d71bf5 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -1,6 +1,7 @@  unsquashfs_SOURCES = unpack/unsquashfs.c unpack/tree_node_from_inode.c  unsquashfs_SOURCES += unpack/unsquashfs.h unpack/read_fstree.c  unsquashfs_SOURCES += unpack/list_files.c unpack/extract_file.c +unsquashfs_SOURCES += unpack/restore_fstree.c  unsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a  if WITH_LZMA diff --git a/unpack/restore_fstree.c b/unpack/restore_fstree.c new file mode 100644 index 0000000..56c3cc5 --- /dev/null +++ b/unpack/restore_fstree.c @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "unsquashfs.h" + +static int restore_directory(int dirfd, tree_node_t *n, compressor_t *cmp, +			     size_t block_size, frag_reader_t *frag, +			     int sqfsfd) +{ +	int fd; + +	for (n = n->data.dir->children; n != NULL; n = n->next) { +		switch (n->mode & S_IFMT) { +		case S_IFDIR: +			if (mkdirat(dirfd, n->name, n->mode) && +			    errno != EEXIST) { +				fprintf(stderr, "mkdir %s: %s\n", +					n->name, strerror(errno)); +				return -1; +			} + +			fd = openat(dirfd, n->name, O_RDONLY | O_DIRECTORY); +			if (fd < 0) { +				fprintf(stderr, "open dir %s: %s\n", +					n->name, strerror(errno)); +				return -1; +			} + +			if (restore_directory(fd, n, cmp, block_size, +					      frag, sqfsfd)) { +				close(fd); +				return -1; +			} + +			close(fd); +			break; +		case S_IFLNK: +			if (symlinkat(n->data.slink_target, dirfd, n->name)) { +				fprintf(stderr, "ln -s %s %s: %s\n", +					n->data.slink_target, n->name, +					strerror(errno)); +				return -1; +			} +			break; +		case S_IFSOCK: +		case S_IFIFO: +			if (mknodat(dirfd, n->name, n->mode, 0)) { +				fprintf(stderr, "creating %s: %s\n", +					n->name, strerror(errno)); +				return -1; +			} +			break; +		case S_IFBLK: +		case S_IFCHR: +			if (mknodat(dirfd, n->name, n->mode, n->data.devno)) { +				fprintf(stderr, "creating device %s: %s\n", +					n->name, strerror(errno)); +				return -1; +			} +			break; +		case S_IFREG: +			fd = openat(dirfd, n->name, +				    O_WRONLY | O_CREAT | O_EXCL, n->mode); +			if (fd < 0) { +				fprintf(stderr, "creating %s: %s\n", +					n->name, strerror(errno)); +				return -1; +			} + +			if (extract_file(n->data.file, cmp, block_size, +					 frag, sqfsfd, fd)) { +				close(fd); +				return -1; +			} + +			close(fd); +			break; +		default: +			break; +		} +	} + +	return 0; +} + +int restore_fstree(const char *rootdir, tree_node_t *root, compressor_t *cmp, +		   size_t block_size, frag_reader_t *frag, int sqfsfd) +{ +	int dirfd; + +	if (mkdir_p(rootdir)) +		return -1; + +	dirfd = open(rootdir, O_RDONLY | O_DIRECTORY); +	if (dirfd < 0) { +		perror(rootdir); +		return -1; +	} + +	if (restore_directory(dirfd, root, cmp, block_size, frag, sqfsfd)) { +		close(dirfd); +		return -1; +	} + +	close(dirfd); +	return 0; +} diff --git a/unpack/unsquashfs.c b/unpack/unsquashfs.c index 90a7583..178def4 100644 --- a/unpack/unsquashfs.c +++ b/unpack/unsquashfs.c @@ -5,16 +5,18 @@ enum {  	OP_NONE = 0,  	OP_LS,  	OP_CAT, +	OP_UNPACK,  };  static struct option long_opts[] = {  	{ "list", required_argument, NULL, 'l' },  	{ "cat", required_argument, NULL, 'c' }, +	{ "unpack-root", required_argument, NULL, 'u' },  	{ "help", no_argument, NULL, 'h' },  	{ "version", no_argument, NULL, 'V' },  }; -static const char *short_opts = "l:hV"; +static const char *short_opts = "l:c:u:hV";  static const char *help_string =  "Usage: %s [OPTIONS] <squashfs-file>\n" @@ -22,12 +24,14 @@ static const char *help_string =  "View or extract the contents of a squashfs image.\n"  "\n"  "Possible options:\n" -"  --list, -l <path>  Produce a directory listing for a given path in the\n" -"                     squashfs image.\n" -"  --cat, -c <path>   If the specified path is a regular file in the image,\n" -"                     dump its contents to stdout.\n" -"  --help, -h         Print help text and exit.\n" -"  --version, -V      Print version information and exit.\n" +"  --list, -l <path>     Produce a directory listing for a given path in the\n" +"                        squashfs image.\n" +"  --cat, -c <path>      If the specified path is a regular file in the,\n" +"                        image, dump its contents to stdout.\n" +"  --unpack-root <path>  Unpack the contents of the filesystem into the\n" +"                        specified path\n" +"  --help, -h            Print help text and exit.\n" +"  --version, -V         Print version information and exit.\n"  "\n";  extern const char *__progname; @@ -89,6 +93,7 @@ static char *get_path(char *old, const char *arg)  int main(int argc, char **argv)  {  	int i, fd, status = EXIT_FAILURE, op = OP_NONE; +	const char *unpack_root = NULL;  	frag_reader_t *frag = NULL;  	char *cmdpath = NULL;  	sqfs_super_t super; @@ -110,6 +115,10 @@ int main(int argc, char **argv)  			op = OP_LS;  			cmdpath = get_path(cmdpath, optarg);  			break; +		case 'u': +			op = OP_UNPACK; +			unpack_root = optarg; +			break;  		case 'h':  			printf(help_string, __progname);  			status = EXIT_SUCCESS; @@ -207,6 +216,20 @@ int main(int argc, char **argv)  			goto out_fs;  		}  		break; +	case OP_UNPACK: +		if (super.fragment_entry_count > 0 && +		    super.fragment_table_start < super.bytes_used && +		    !(super.flags & SQFS_FLAG_NO_FRAGMENTS)) { +			frag = frag_reader_create(&super, fd, cmp); +			if (frag == NULL) +				goto out_fs; +		} + +		if (restore_fstree(unpack_root, fs.root, cmp, super.block_size, +				   frag, fd)) { +			goto out_fs; +		} +		break;  	}  	status = EXIT_SUCCESS; diff --git a/unpack/unsquashfs.h b/unpack/unsquashfs.h index 0e8854b..5895fb8 100644 --- a/unpack/unsquashfs.h +++ b/unpack/unsquashfs.h @@ -31,4 +31,7 @@ void list_files(tree_node_t *node);  int extract_file(file_info_t *fi, compressor_t *cmp, size_t block_size,  		 frag_reader_t *frag, int sqfsfd, int outfd); +int restore_fstree(const char *rootdir, tree_node_t *root, compressor_t *cmp, +		   size_t block_size, frag_reader_t *frag, int sqfsfd); +  #endif /* UNSQUASHFS_H */ | 
