From 1f2342c6e89280a21c2e1c665803aae0e42185cb Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 10 Aug 2020 16:16:39 +0200 Subject: 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 --- bin/rdsquashfs/Makemodule.am | 1 + bin/rdsquashfs/options.c | 13 +++- bin/rdsquashfs/rdsquashfs.1 | 5 ++ bin/rdsquashfs/rdsquashfs.c | 4 + bin/rdsquashfs/rdsquashfs.h | 4 + bin/rdsquashfs/stat.c | 180 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 bin/rdsquashfs/stat.c (limited to 'bin') 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 Unpack this sub directory from the image. To\n" " unpack everything, simply specify /.\n" +" --stat, -s 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 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 +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 #include #include +#include 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 + */ +#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; +} -- cgit v1.2.3