diff options
-rw-r--r-- | include/meta_reader.h | 5 | ||||
-rw-r--r-- | include/squashfs.h | 33 | ||||
-rw-r--r-- | lib/Makemodule.am | 2 | ||||
-rw-r--r-- | lib/sqfs/read_inode.c | 316 |
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; +} |