summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/meta_reader.h5
-rw-r--r--include/squashfs.h33
-rw-r--r--lib/Makemodule.am2
-rw-r--r--lib/sqfs/read_inode.c316
4 files changed, 348 insertions, 8 deletions
diff --git a/include/meta_reader.h b/include/meta_reader.h
index 4c03996..d7ab732 100644
--- a/include/meta_reader.h
+++ b/include/meta_reader.h
@@ -23,4 +23,9 @@ int meta_reader_seek(meta_reader_t *m, uint64_t block_start,
int meta_reader_read(meta_reader_t *m, void *data, size_t size);
+sqfs_inode_generic_t *meta_reader_read_inode(meta_reader_t *ir,
+ sqfs_super_t *super,
+ uint64_t block_start,
+ size_t offset);
+
#endif /* META_READER_H */
diff --git a/include/squashfs.h b/include/squashfs.h
index a690aad..eaa5a6e 100644
--- a/include/squashfs.h
+++ b/include/squashfs.h
@@ -57,26 +57,38 @@ typedef struct {
typedef struct {
uint32_t nlink;
+ uint32_t devno;
+ uint32_t xattr_idx;
+} sqfs_inode_dev_ext_t;
+
+typedef struct {
+ uint32_t nlink;
} sqfs_inode_ipc_t;
typedef struct {
uint32_t nlink;
- uint32_t devno;
uint32_t xattr_idx;
-} sqfs_inode_dev_ext_t;
+} sqfs_inode_ipc_ext_t;
typedef struct {
uint32_t nlink;
uint32_t target_size;
- uint8_t target[];
+ /*uint8_t target[];*/
} sqfs_inode_slink_t;
typedef struct {
+ uint32_t nlink;
+ uint32_t target_size;
+ /*uint8_t target[];*/
+ uint32_t xattr_idx;
+} sqfs_inode_slink_ext_t;
+
+typedef struct {
uint32_t blocks_start;
uint32_t fragment_index;
uint32_t fragment_offset;
uint32_t file_size;
- uint32_t block_sizes[];
+ /*uint32_t block_sizes[];*/
} sqfs_inode_file_t;
typedef struct {
@@ -87,7 +99,7 @@ typedef struct {
uint32_t fragment_idx;
uint32_t fragment_offset;
uint32_t xattr_idx;
- uint32_t block_sizes[];
+ /*uint32_t block_sizes[];*/
} sqfs_inode_file_ext_t;
typedef struct {
@@ -108,18 +120,25 @@ typedef struct {
uint32_t xattr_idx;
} sqfs_inode_dir_ext_t;
-typedef union {
- sqfs_inode_t inode;
+typedef struct {
+ sqfs_inode_t base;
+ char *slink_target;
+ uint32_t *block_sizes;
union {
sqfs_inode_dev_t dev;
sqfs_inode_dev_ext_t dev_ext;
+ sqfs_inode_ipc_t ipc;
+ sqfs_inode_ipc_ext_t ipc_ext;
sqfs_inode_slink_t slink;
+ sqfs_inode_slink_ext_t slink_ext;
sqfs_inode_file_t file;
sqfs_inode_file_ext_t file_ext;
sqfs_inode_dir_t dir;
sqfs_inode_dir_ext_t dir_ext;
} data;
+
+ uint8_t extra[];
} sqfs_inode_generic_t;
typedef struct {
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 08b53e1..b521a7e 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -13,7 +13,7 @@ libsquashfs_a_SOURCES += lib/sqfs/id_table.c include/id_table.h
libsquashfs_a_SOURCES += lib/sqfs/table.c include/table.h
libsquashfs_a_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c
libsquashfs_a_SOURCES += include/meta_reader.h lib/sqfs/id_table_write.c
-libsquashfs_a_SOURCES += lib/sqfs/id_table_read.c
+libsquashfs_a_SOURCES += lib/sqfs/id_table_read.c lib/sqfs/read_inode.c
libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c
libutil_a_SOURCES += lib/util/read_retry.c include/util.h
diff --git a/lib/sqfs/read_inode.c b/lib/sqfs/read_inode.c
new file mode 100644
index 0000000..71b3a54
--- /dev/null
+++ b/lib/sqfs/read_inode.c
@@ -0,0 +1,316 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "meta_reader.h"
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define SWAB16(x) x = le16toh(x)
+#define SWAB32(x) x = le32toh(x)
+#define SWAB64(x) x = le64toh(x)
+
+static int check_mode(sqfs_inode_t *inode)
+{
+ switch (inode->mode & S_IFMT) {
+ case S_IFSOCK:
+ if (inode->type != SQFS_INODE_SOCKET &&
+ inode->type != SQFS_INODE_EXT_SOCKET) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFLNK:
+ if (inode->type != SQFS_INODE_SLINK &&
+ inode->type != SQFS_INODE_EXT_SLINK) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFREG:
+ if (inode->type != SQFS_INODE_FILE &&
+ inode->type != SQFS_INODE_EXT_FILE) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFBLK:
+ if (inode->type != SQFS_INODE_BDEV &&
+ inode->type != SQFS_INODE_EXT_BDEV) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFDIR:
+ if (inode->type != SQFS_INODE_DIR &&
+ inode->type != SQFS_INODE_EXT_DIR) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFCHR:
+ if (inode->type != SQFS_INODE_CDEV &&
+ inode->type != SQFS_INODE_EXT_CDEV) {
+ goto fail_mismatch;
+ }
+ break;
+ case S_IFIFO:
+ if (inode->type != SQFS_INODE_FIFO &&
+ inode->type != SQFS_INODE_EXT_FIFO) {
+ goto fail_mismatch;
+ }
+ break;
+ default:
+ fputs("Found inode with unknown file mode\n", stderr);
+ return -1;
+ }
+
+ return 0;
+fail_mismatch:
+ fputs("Found inode where type does not match mode\n", stderr);
+ return -1;
+}
+
+static sqfs_inode_generic_t *read_inode_file(meta_reader_t *ir,
+ sqfs_inode_t *base,
+ size_t block_size)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_file_t file;
+ size_t i, count;
+
+ if (meta_reader_read(ir, &file, sizeof(file)))
+ return NULL;
+
+ SWAB32(file.blocks_start);
+ SWAB32(file.fragment_index);
+ SWAB32(file.fragment_offset);
+ SWAB32(file.file_size);
+
+ count = file.file_size / block_size;
+
+ out = calloc(1, sizeof(*out) + count * sizeof(uint32_t));
+ if (out == NULL) {
+ perror("reading extended file inode");
+ return NULL;
+ }
+
+ out->base = *base;
+ out->data.file = file;
+ out->block_sizes = (uint32_t *)out->extra;
+
+ if (meta_reader_read(ir, out->block_sizes, count * sizeof(uint32_t))) {
+ free(out);
+ return NULL;
+ }
+
+ for (i = 0; i < count; ++i)
+ SWAB32(out->block_sizes[i]);
+
+ return out;
+}
+
+static sqfs_inode_generic_t *read_inode_file_ext(meta_reader_t *ir,
+ sqfs_inode_t *base,
+ size_t block_size)
+{
+ sqfs_inode_file_ext_t file;
+ sqfs_inode_generic_t *out;
+ size_t i, count;
+
+ if (meta_reader_read(ir, &file, sizeof(file)))
+ return NULL;
+
+ SWAB64(file.blocks_start);
+ SWAB64(file.file_size);
+ SWAB64(file.sparse);
+ SWAB32(file.nlink);
+ SWAB32(file.fragment_idx);
+ SWAB32(file.fragment_offset);
+ SWAB32(file.xattr_idx);
+
+ count = file.file_size / block_size;
+
+ out = calloc(1, sizeof(*out) + count * sizeof(uint32_t));
+ if (out == NULL) {
+ perror("reading extended file inode");
+ return NULL;
+ }
+
+ out->base = *base;
+ out->data.file_ext = file;
+ out->block_sizes = (uint32_t *)out->extra;
+
+ if (meta_reader_read(ir, out->block_sizes, count * sizeof(uint32_t))) {
+ free(out);
+ return NULL;
+ }
+
+ for (i = 0; i < count; ++i)
+ SWAB32(out->block_sizes[i]);
+
+ return out;
+}
+
+static sqfs_inode_generic_t *read_inode_slink(meta_reader_t *ir,
+ sqfs_inode_t *base)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_slink_t slink;
+
+ if (meta_reader_read(ir, &slink, sizeof(slink)))
+ return NULL;
+
+ SWAB32(slink.nlink);
+ SWAB32(slink.target_size);
+
+ out = calloc(1, sizeof(*out) + slink.target_size + 1);
+ if (out == NULL) {
+ perror("reading symlink inode");
+ return NULL;
+ }
+
+ out->slink_target = (char *)out->extra;
+ out->base = *base;
+ out->data.slink = slink;
+
+ if (meta_reader_read(ir, out->slink_target, slink.target_size)) {
+ free(out);
+ return NULL;
+ }
+
+ return out;
+}
+
+static sqfs_inode_generic_t *read_inode_slink_ext(meta_reader_t *ir,
+ sqfs_inode_t *base)
+{
+ sqfs_inode_generic_t *out = read_inode_slink(ir, base);
+ uint32_t xattr;
+
+ if (out != NULL) {
+ if (meta_reader_read(ir, &xattr, sizeof(xattr))) {
+ free(out);
+ return NULL;
+ }
+
+ out->data.slink_ext.xattr_idx = le32toh(xattr);
+ }
+ return 0;
+}
+
+sqfs_inode_generic_t *meta_reader_read_inode(meta_reader_t *ir,
+ sqfs_super_t *super,
+ uint64_t block_start,
+ size_t offset)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_t inode;
+
+ /* read base inode */
+ block_start += super->inode_table_start;
+
+ if (meta_reader_seek(ir, block_start, offset))
+ return NULL;
+
+ if (meta_reader_read(ir, &inode, sizeof(inode)))
+ return NULL;
+
+ SWAB16(inode.type);
+ SWAB16(inode.mode);
+ SWAB16(inode.uid_idx);
+ SWAB16(inode.gid_idx);
+ SWAB32(inode.mod_time);
+ SWAB32(inode.inode_number);
+
+ if (check_mode(&inode))
+ return NULL;
+
+ /* inode types where the size is variable */
+ switch (inode.type) {
+ case SQFS_INODE_FILE:
+ return read_inode_file(ir, &inode, super->block_size);
+ case SQFS_INODE_SLINK:
+ return read_inode_slink(ir, &inode);
+ case SQFS_INODE_EXT_FILE:
+ return read_inode_file_ext(ir, &inode, super->block_size);
+ case SQFS_INODE_EXT_SLINK:
+ return read_inode_slink_ext(ir, &inode);
+ default:
+ break;
+ }
+
+ /* everything else */
+ out = calloc(1, sizeof(*out));
+ if (out == NULL) {
+ perror("reading symlink inode");
+ return NULL;
+ }
+
+ out->base = inode;
+
+ switch (inode.type) {
+ case SQFS_INODE_DIR:
+ if (meta_reader_read(ir, &out->data.dir,
+ sizeof(out->data.dir))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.dir.start_block);
+ SWAB32(out->data.dir.nlink);
+ SWAB16(out->data.dir.size);
+ SWAB16(out->data.dir.offset);
+ SWAB32(out->data.dir.parent_inode);
+ break;
+ case SQFS_INODE_BDEV:
+ case SQFS_INODE_CDEV:
+ if (meta_reader_read(ir, &out->data.dev,
+ sizeof(out->data.dev))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.dev.nlink);
+ SWAB32(out->data.dev.devno);
+ break;
+ case SQFS_INODE_FIFO:
+ case SQFS_INODE_SOCKET:
+ if (meta_reader_read(ir, &out->data.ipc,
+ sizeof(out->data.ipc))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.ipc.nlink);
+ break;
+ case SQFS_INODE_EXT_DIR:
+ if (meta_reader_read(ir, &out->data.dir_ext,
+ sizeof(out->data.dir_ext))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.dir_ext.nlink);
+ SWAB32(out->data.dir_ext.size);
+ SWAB32(out->data.dir_ext.start_block);
+ SWAB32(out->data.dir_ext.parent_inode);
+ SWAB16(out->data.dir_ext.inodex_count);
+ SWAB16(out->data.dir_ext.offset);
+ SWAB32(out->data.dir_ext.xattr_idx);
+ break;
+ case SQFS_INODE_EXT_BDEV:
+ case SQFS_INODE_EXT_CDEV:
+ if (meta_reader_read(ir, &out->data.dev_ext,
+ sizeof(out->data.dev_ext))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.dev_ext.nlink);
+ SWAB32(out->data.dev_ext.devno);
+ SWAB32(out->data.dev_ext.xattr_idx);
+ break;
+ case SQFS_INODE_EXT_FIFO:
+ case SQFS_INODE_EXT_SOCKET:
+ if (meta_reader_read(ir, &out->data.ipc_ext,
+ sizeof(out->data.ipc_ext))) {
+ goto fail_free;
+ }
+ SWAB32(out->data.ipc_ext.nlink);
+ SWAB32(out->data.ipc_ext.xattr_idx);
+ break;
+ default:
+ fputs("Unknown inode type found\n", stderr);
+ goto fail_free;
+ }
+
+ return out;
+fail_free:
+ free(out);
+ return NULL;
+}