aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/rdsquashfs/Makemodule.am1
-rw-r--r--bin/rdsquashfs/options.c13
-rw-r--r--bin/rdsquashfs/rdsquashfs.15
-rw-r--r--bin/rdsquashfs/rdsquashfs.c4
-rw-r--r--bin/rdsquashfs/rdsquashfs.h4
-rw-r--r--bin/rdsquashfs/stat.c180
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;
+}