diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2020-08-10 16:16:39 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2020-08-12 00:25:34 +0200 | 
| commit | 1f2342c6e89280a21c2e1c665803aae0e42185cb (patch) | |
| tree | 44d218662b47322e1f9c099386067ea7455bab23 /bin | |
| parent | 8c4e1ac8a6eb77bdc65ae3bff2795a7755d12b0c (diff) | |
Add a "--stat" option to rdsquashfs
This commit adds a --stat option to rdsquashfs that dumps a lot of
information about and inode that tunred out to be usefull in
debugging.
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/rdsquashfs/Makemodule.am | 1 | ||||
| -rw-r--r-- | bin/rdsquashfs/options.c | 13 | ||||
| -rw-r--r-- | bin/rdsquashfs/rdsquashfs.1 | 5 | ||||
| -rw-r--r-- | bin/rdsquashfs/rdsquashfs.c | 4 | ||||
| -rw-r--r-- | bin/rdsquashfs/rdsquashfs.h | 4 | ||||
| -rw-r--r-- | bin/rdsquashfs/stat.c | 180 | 
6 files changed, 205 insertions, 2 deletions
| diff --git a/bin/rdsquashfs/Makemodule.am b/bin/rdsquashfs/Makemodule.am index d99d6ee..e4f3b96 100644 --- a/bin/rdsquashfs/Makemodule.am +++ b/bin/rdsquashfs/Makemodule.am @@ -2,6 +2,7 @@ rdsquashfs_SOURCES = bin/rdsquashfs/rdsquashfs.c bin/rdsquashfs/rdsquashfs.h  rdsquashfs_SOURCES += bin/rdsquashfs/list_files.c bin/rdsquashfs/options.c  rdsquashfs_SOURCES += bin/rdsquashfs/restore_fstree.c bin/rdsquashfs/describe.c  rdsquashfs_SOURCES += bin/rdsquashfs/fill_files.c bin/rdsquashfs/dump_xattrs.c +rdsquashfs_SOURCES += bin/rdsquashfs/stat.c  rdsquashfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS)  rdsquashfs_LDADD = libcommon.a libcompat.a libsquashfs.la  rdsquashfs_LDADD += libfstree.a $(LZO_LIBS) $(PTHREAD_LIBS) diff --git a/bin/rdsquashfs/options.c b/bin/rdsquashfs/options.c index cdd19e1..dbb5e40 100644 --- a/bin/rdsquashfs/options.c +++ b/bin/rdsquashfs/options.c @@ -10,6 +10,7 @@ static struct option long_opts[] = {  	{ "list", required_argument, NULL, 'l' },  	{ "cat", required_argument, NULL, 'c' },  	{ "xattr", required_argument, NULL, 'x' }, +	{ "stat", required_argument, NULL, 's' },  	{ "unpack-root", required_argument, NULL, 'p' },  	{ "unpack-path", required_argument, NULL, 'u' },  	{ "no-dev", no_argument, NULL, 'D' }, @@ -32,7 +33,7 @@ static struct option long_opts[] = {  };  static const char *short_opts = -	"l:c:u:p:x:DSFLCOEZTj:dqhV" +	"l:c:u:p:x:s:DSFLCOEZTj:dqhV"  #ifdef HAVE_SYS_XATTR_H  	"X"  #endif @@ -53,6 +54,9 @@ static const char *help_string =  "                            an inode that the given path resolves to.\n"  "  --unpack-path, -u <path>  Unpack this sub directory from the image. To\n"  "                            unpack everything, simply specify /.\n" +"  --stat, -s <path>         Dump all information that can be extracted from\n" +"                            the inode coresponding to a path, including\n" +"                            SquashFS specific internals.\n"  "  --describe, -d            Produce a file listing from the image.\n"  "\n"  "  --unpack-root, -p <path>  If used with --unpack-path, this is where the\n" @@ -167,6 +171,10 @@ void process_command_line(options_t *opt, int argc, char **argv)  			opt->op = OP_RDATTR;  			opt->cmdpath = get_path(opt->cmdpath, optarg);  			break; +		case 's': +			opt->op = OP_STAT; +			opt->cmdpath = get_path(opt->cmdpath, optarg); +			break;  		case 'l':  			opt->op = OP_LS;  			opt->cmdpath = get_path(opt->cmdpath, optarg); @@ -199,7 +207,8 @@ void process_command_line(options_t *opt, int argc, char **argv)  		goto fail_arg;  	} -	if (opt->op == OP_LS || opt->op == OP_CAT || opt->op == OP_RDATTR) { +	if (opt->op == OP_LS || opt->op == OP_CAT || opt->op == OP_RDATTR || +	    opt->op == OP_STAT) {  		opt->rdtree_flags |= SQFS_TREE_NO_RECURSE;  	} diff --git a/bin/rdsquashfs/rdsquashfs.1 b/bin/rdsquashfs/rdsquashfs.1 index a5e1d77..345a040 100644 --- a/bin/rdsquashfs/rdsquashfs.1 +++ b/bin/rdsquashfs/rdsquashfs.1 @@ -29,6 +29,11 @@ simply specify /.  \fB\-\-describe\fR, \fB\-d\fR  Produce a file listing from the image compatible with the format consumed by  gensquashfs. +.TP +\fB\-\-stat\fR, \fB\-s\fR <path> +Dump all available information about the inode that the path refers to, +including SquashFS specific internals such as the on-disk layout of a file +or the fast lookup index stored in an extended directory inode.  .PP  The following options can be used to control the behaviour of the specified  operation: diff --git a/bin/rdsquashfs/rdsquashfs.c b/bin/rdsquashfs/rdsquashfs.c index 78f54ba..1c11afa 100644 --- a/bin/rdsquashfs/rdsquashfs.c +++ b/bin/rdsquashfs/rdsquashfs.c @@ -111,6 +111,10 @@ int main(int argc, char **argv)  	case OP_LS:  		list_files(n);  		break; +	case OP_STAT: +		if (stat_file(n)) +			goto out; +		break;  	case OP_CAT:  		if (!S_ISREG(n->inode->base.mode)) {  			fprintf(stderr, "/%s: not a regular file\n", diff --git a/bin/rdsquashfs/rdsquashfs.h b/bin/rdsquashfs/rdsquashfs.h index 17c0a85..dd50f28 100644 --- a/bin/rdsquashfs/rdsquashfs.h +++ b/bin/rdsquashfs/rdsquashfs.h @@ -30,6 +30,7 @@  #include <ctype.h>  #include <errno.h>  #include <stdio.h> +#include <time.h>  enum UNPACK_FLAGS {  	UNPACK_CHMOD = 0x01, @@ -47,6 +48,7 @@ enum {  	OP_UNPACK,  	OP_DESCRIBE,  	OP_RDATTR, +	OP_STAT,  };  typedef struct { @@ -60,6 +62,8 @@ typedef struct {  void list_files(const sqfs_tree_node_t *node); +int stat_file(const sqfs_tree_node_t *node); +  int restore_fstree(sqfs_tree_node_t *root, int flags);  int update_tree_attribs(sqfs_xattr_reader_t *xattr, diff --git a/bin/rdsquashfs/stat.c b/bin/rdsquashfs/stat.c new file mode 100644 index 0000000..049baae --- /dev/null +++ b/bin/rdsquashfs/stat.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * stat.c + * + * Copyright (C) 2020 David Oberhollenzer <goliath@infraroot.at> + */ +#include "rdsquashfs.h" + +static const char *inode_types[] = { +	[SQFS_INODE_DIR] = "directory", +	[SQFS_INODE_FILE] = "file", +	[SQFS_INODE_SLINK] = "symbolic link", +	[SQFS_INODE_BDEV] = "block device", +	[SQFS_INODE_CDEV] = "character device", +	[SQFS_INODE_FIFO] = "named pipe", +	[SQFS_INODE_SOCKET] = "socket", +	[SQFS_INODE_EXT_DIR] = "extended directory", +	[SQFS_INODE_EXT_FILE] = "extended file", +	[SQFS_INODE_EXT_SLINK] = "extended symbolic link", +	[SQFS_INODE_EXT_BDEV] = "extended block device", +	[SQFS_INODE_EXT_CDEV] = "extended character device", +	[SQFS_INODE_EXT_FIFO] = "extended named pipe", +	[SQFS_INODE_EXT_SOCKET] = "extended socket", +}; + +int stat_file(const sqfs_tree_node_t *node) +{ +	sqfs_u32 xattr_idx = 0xFFFFFFFF, devno = 0, link_size = 0; +	const sqfs_inode_generic_t *inode = node->inode; +	const char *type = NULL, *link_target = NULL; +	sqfs_u32 frag_idx, frag_offset; +	bool have_devno = false; +	sqfs_u64 location, size; +	unsigned int nlinks = 0; +	sqfs_dir_index_t *idx; +	char buffer[64]; +	time_t timeval; +	struct tm *tm; +	size_t i; +	int ret; + +	/* decode */ +	if ((size_t)inode->base.type < +	    sizeof(inode_types) / sizeof(inode_types[0])) { +		type = inode_types[inode->base.type]; +	} + +	sqfs_inode_get_xattr_index(inode, &xattr_idx); + +	switch (inode->base.type) { +	case SQFS_INODE_DIR: +		nlinks = inode->data.dir.nlink; +		break; +	case SQFS_INODE_SLINK: +		nlinks = inode->data.slink.nlink; +		link_target = (const char *)inode->extra; +		link_size = inode->data.slink.target_size; +		break; +	case SQFS_INODE_BDEV: +	case SQFS_INODE_CDEV: +		nlinks = inode->data.dev.nlink; +		devno = inode->data.dev.devno; +		have_devno = true; +		break; +	case SQFS_INODE_FIFO: +	case SQFS_INODE_SOCKET: +		nlinks = inode->data.ipc.nlink; +		break; +	case SQFS_INODE_EXT_DIR: +		nlinks = inode->data.dir_ext.nlink; +		break; +	case SQFS_INODE_EXT_FILE: +		nlinks = inode->data.file_ext.nlink; +		break; +	case SQFS_INODE_EXT_SLINK: +		nlinks = inode->data.slink_ext.nlink; +		link_target = (const char *)inode->extra; +		link_size = inode->data.slink_ext.target_size; +		break; +	case SQFS_INODE_EXT_BDEV: +	case SQFS_INODE_EXT_CDEV: +		nlinks = inode->data.dev_ext.nlink; +		devno = inode->data.dev_ext.devno; +		have_devno = true; +		break; +	case SQFS_INODE_EXT_FIFO: +	case SQFS_INODE_EXT_SOCKET: +		nlinks = inode->data.ipc_ext.nlink; +		break; +	} + +	timeval = inode->base.mod_time; +	tm = gmtime(&timeval); +	strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", tm); + +	/* info dump */ +	printf("Name: %s\n", (const char *)node->name); +	printf("Inode type: %s\n", type == NULL ? "UNKNOWN" : type); +	printf("Inode number: %u\n", inode->base.inode_number); +	printf("Access: 0%o\n", inode->base.mode & ~SQFS_INODE_MODE_MASK); +	printf("UID: %u (index = %u)\n", node->uid, inode->base.uid_idx); +	printf("GID: %u (index = %u)\n", node->gid, inode->base.gid_idx); +	printf("Last modified: %s (%u)\n", buffer, inode->base.mod_time); + +	if (type != NULL && inode->base.type != SQFS_INODE_FILE) +		printf("Hard link count: %u\n", nlinks); + +	if (type != NULL && inode->base.type >= SQFS_INODE_EXT_DIR) +		printf("Xattr index: 0x%X\n", xattr_idx); + +	if (link_target != NULL) +		printf("Link target: %.*s\n", link_size, link_target); + +	if (have_devno) { +		printf("Device number: %u:%u (%u)\n", +		       major(devno), minor(devno), devno); +	} + +	switch (inode->base.type) { +	case SQFS_INODE_FILE: +	case SQFS_INODE_EXT_FILE: +		sqfs_inode_get_file_block_start(inode, &location); +		sqfs_inode_get_file_size(inode, &size); +		sqfs_inode_get_frag_location(inode, &frag_idx, &frag_offset); + +		printf("Fragment index: 0x%X\n", frag_idx); +		printf("Fragment offset: %u\n", frag_offset); +		printf("File size: %lu\n", (unsigned long)size); + +		if (inode->base.type == SQFS_INODE_EXT_FILE) +			printf("Sparse: %lu\n", inode->data.file_ext.sparse); + +		printf("Blocks start: %lu\n", (unsigned long)location); +		printf("Block count: %lu\n", +		       (unsigned long)sqfs_inode_get_file_block_count(inode)); + +		for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { +			printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, +			       SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), +			       SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? +			       "compressed" : "uncompressed"); +		} +		break; +	case SQFS_INODE_DIR: +		printf("Start block: %u\n", inode->data.dir.start_block); +		printf("Offset: %u\n", inode->data.dir.offset); +		printf("Listing size: %u\n", inode->data.dir.size); +		printf("Parent inode: %u\n", inode->data.dir.parent_inode); +		break; +	case SQFS_INODE_EXT_DIR: +		printf("Start block: %u\n", inode->data.dir_ext.start_block); +		printf("Offset: %u\n", inode->data.dir_ext.offset); +		printf("Listing size: %u\n", inode->data.dir_ext.size); +		printf("Parent inode: %u\n", inode->data.dir_ext.parent_inode); +		printf("Directory index entries: %u\n", +		       inode->data.dir_ext.inodex_count); + +		if (inode->data.dir_ext.size == 0) +			break; + +		for (i = 0; ; ++i) { +			ret = sqfs_inode_unpack_dir_index_entry(inode, &idx, i); +			if (ret == SQFS_ERROR_OUT_OF_BOUNDS) +				break; +			if (ret < 0) { +				sqfs_perror(NULL, "reading directory index", +					    ret); +				return -1; +			} + +			printf("\t'%.*s' -> block %u, header offset %u\n", +			       idx->size + 1, idx->name, +			       idx->start_block, idx->index); + +			free(idx); +		} +		break; +	} +	return 0; +} | 
