diff options
Diffstat (limited to 'unpack')
| -rw-r--r-- | unpack/Makemodule.am | 2 | ||||
| -rw-r--r-- | unpack/extract_file.c | 97 | ||||
| -rw-r--r-- | unpack/unsquashfs.c | 107 | ||||
| -rw-r--r-- | unpack/unsquashfs.h | 4 | 
4 files changed, 183 insertions, 27 deletions
| diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index a199375..a8fd2e7 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -1,6 +1,6 @@  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 +unsquashfs_SOURCES += unpack/list_files.c unpack/extract_file.c  unsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a  if WITH_LZMA diff --git a/unpack/extract_file.c b/unpack/extract_file.c new file mode 100644 index 0000000..c18ace8 --- /dev/null +++ b/unpack/extract_file.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "unsquashfs.h" + +int extract_file(file_info_t *fi, compressor_t *cmp, size_t block_size, +		 frag_reader_t *frag, int sqfsfd, int outfd) +{ +	size_t i, count, fragsz; +	bool compressed; +	void *buffer; +	uint32_t bs; +	ssize_t ret; + +	buffer = malloc(block_size); +	if (buffer == NULL) { +		perror("allocating scratch buffer"); +		return -1; +	} + +	count = fi->size / block_size; + +	if (count > 0) { +		if (lseek(sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1) +			goto fail_seek; + +		for (i = 0; i < count; ++i) { +			bs = fi->blocksizes[i]; + +			compressed = (bs & (1 << 24)) == 0; +			bs &= (1 << 24) - 1; + +			if (bs > block_size) +				goto fail_bs; + +			ret = read_retry(sqfsfd, buffer, bs); +			if (ret < 0) +				goto fail_rd; + +			if ((size_t)ret < bs) +				goto fail_trunc; + +			if (compressed) { +				ret = cmp->do_block(cmp, buffer, bs); +				if (ret <= 0) +					goto fail; + +				bs = ret; +			} + +			ret = write_retry(outfd, buffer, bs); +			if (ret < 0) +				goto fail_wr; + +			if ((size_t)ret < bs) +				goto fail_wr_trunc; +		} +	} + +	fragsz = fi->size % block_size; + +	if (fragsz > 0) { +		if (frag_reader_read(frag, fi->fragment, fi->fragment_offset, +				     buffer, fragsz)) { +			goto fail; +		} + +		ret = write_retry(outfd, buffer, fragsz); +		if (ret < 0) +			goto fail_wr; + +		if ((size_t)ret < fragsz) +			goto fail_wr_trunc; +	} + +	free(buffer); +	return 0; +fail_seek: +	perror("seek on squashfs"); +	goto fail; +fail_wr: +	perror("writing uncompressed block"); +	goto fail; +fail_wr_trunc: +	fputs("writing uncompressed block: truncated write\n", stderr); +	goto fail; +fail_rd: +	perror("reading from squashfs"); +	goto fail; +fail_trunc: +	fputs("reading from squashfs: unexpected end of file\n", stderr); +	goto fail; +fail_bs: +	fputs("found compressed block larger than block size\n", stderr); +	goto fail; +fail: +	free(buffer); +	return -1; +} diff --git a/unpack/unsquashfs.c b/unpack/unsquashfs.c index b253bae..90a7583 100644 --- a/unpack/unsquashfs.c +++ b/unpack/unsquashfs.c @@ -1,8 +1,15 @@  /* SPDX-License-Identifier: GPL-3.0-or-later */  #include "unsquashfs.h" +enum { +	OP_NONE = 0, +	OP_LS, +	OP_CAT, +}; +  static struct option long_opts[] = {  	{ "list", required_argument, NULL, 'l' }, +	{ "cat", required_argument, NULL, 'c' },  	{ "help", no_argument, NULL, 'h' },  	{ "version", no_argument, NULL, 'V' },  }; @@ -17,6 +24,8 @@ static const char *help_string =  "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"  "\n"; @@ -56,12 +65,35 @@ static tree_node_t *find_node(tree_node_t *n, const char *path)  	return n;  } +static char *get_path(char *old, const char *arg) +{ +	char *path; + +	free(old); + +	path = strdup(arg); +	if (path == NULL) { +		perror("processing arguments"); +		exit(EXIT_FAILURE); +	} + +	if (canonicalize_name(path)) { +		fprintf(stderr, "Invalid path: %s\n", arg); +		free(path); +		exit(EXIT_FAILURE); +	} + +	return path; +} +  int main(int argc, char **argv)  { -	int i, fd, status = EXIT_FAILURE; -	char *lspath = NULL; +	int i, fd, status = EXIT_FAILURE, op = OP_NONE; +	frag_reader_t *frag = NULL; +	char *cmdpath = NULL;  	sqfs_super_t super;  	compressor_t *cmp; +	tree_node_t *n;  	fstree_t fs;  	for (;;) { @@ -70,22 +102,13 @@ int main(int argc, char **argv)  			break;  		switch (i) { +		case 'c': +			op = OP_CAT; +			cmdpath = get_path(cmdpath, optarg); +			break;  		case 'l': -			if (optarg != NULL) { -				free(lspath); - -				lspath = strdup(optarg ? optarg : ""); -				if (lspath == NULL) { -					perror("processing arguments"); -					return EXIT_FAILURE; -				} - -				if (canonicalize_name(lspath)) { -					fprintf(stderr, "Invalid pth: %s\n", -						optarg); -					goto out_cmd; -				} -			} +			op = OP_LS; +			cmdpath = get_path(cmdpath, optarg);  			break;  		case 'h':  			printf(help_string, __progname); @@ -100,10 +123,14 @@ int main(int argc, char **argv)  		}  	} +	if (op == OP_NONE) { +		fputs("No opteration specified\n", stderr); +		goto fail_arg; +	} +  	if (optind >= argc) { -		fprintf(stderr, "Usage: %s [OPTIONS] <filename>\n", -			__progname); -		goto out_cmd; +		fputs("Missing image argument\n", stderr); +		goto fail_arg;  	}  	fd = open(argv[optind], O_RDONLY); @@ -145,29 +172,57 @@ int main(int argc, char **argv)  	if (read_fstree(&fs, fd, &super, cmp))  		goto out_cmp; -	if (lspath != NULL) { -		tree_node_t *n = find_node(fs.root, lspath); - +	switch (op) { +	case OP_LS: +		n = find_node(fs.root, cmdpath);  		if (n == NULL) { -			perror(lspath); +			perror(cmdpath);  			goto out_fs;  		}  		list_files(n); +		break; +	case OP_CAT: +		n = find_node(fs.root, cmdpath); +		if (n == NULL) { +			perror(cmdpath); +			goto out_fs; +		} + +		if (!S_ISREG(n->mode)) { +			fprintf(stderr, "/%s: not a regular file\n", cmdpath); +			goto out_fs; +		} + +		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 (extract_file(n->data.file, cmp, super.block_size, +				 frag, fd, STDOUT_FILENO)) { +			goto out_fs; +		} +		break;  	}  	status = EXIT_SUCCESS;  out_fs: +	if (frag != NULL) +		frag_reader_destroy(frag);  	fstree_cleanup(&fs);  out_cmp:  	cmp->destroy(cmp);  out_fd:  	close(fd);  out_cmd: -	free(lspath); +	free(cmdpath);  	return status;  fail_arg:  	fprintf(stderr, "Try `%s --help' for more information.\n", __progname); -	free(lspath); +	free(cmdpath);  	return EXIT_FAILURE;  } diff --git a/unpack/unsquashfs.h b/unpack/unsquashfs.h index 52b3889..0e8854b 100644 --- a/unpack/unsquashfs.h +++ b/unpack/unsquashfs.h @@ -3,6 +3,7 @@  #define UNSQUASHFS_H  #include "meta_reader.h" +#include "frag_reader.h"  #include "squashfs.h"  #include "compress.h"  #include "id_table.h" @@ -27,4 +28,7 @@ int read_fstree(fstree_t *out, int fd, sqfs_super_t *super, compressor_t *cmp);  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); +  #endif /* UNSQUASHFS_H */ | 
