aboutsummaryrefslogtreecommitdiff
path: root/lib/sqfs/src/read_inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfs/src/read_inode.c')
-rw-r--r--lib/sqfs/src/read_inode.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/lib/sqfs/src/read_inode.c b/lib/sqfs/src/read_inode.c
new file mode 100644
index 0000000..12bef48
--- /dev/null
+++ b/lib/sqfs/src/read_inode.c
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+/*
+ * read_inode.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#define SQFS_BUILDING_DLL
+#include "config.h"
+
+#include "sqfs/meta_reader.h"
+#include "sqfs/error.h"
+#include "sqfs/super.h"
+#include "sqfs/inode.h"
+#include "sqfs/dir.h"
+#include "util/util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define SWAB16(x) x = le16toh(x)
+#define SWAB32(x) x = le32toh(x)
+#define SWAB64(x) x = le64toh(x)
+
+static int set_mode(sqfs_inode_t *inode)
+{
+ inode->mode &= ~S_IFMT;
+
+ switch (inode->type) {
+ case SQFS_INODE_SOCKET:
+ case SQFS_INODE_EXT_SOCKET:
+ inode->mode |= S_IFSOCK;
+ break;
+ case SQFS_INODE_SLINK:
+ case SQFS_INODE_EXT_SLINK:
+ inode->mode |= S_IFLNK;
+ break;
+ case SQFS_INODE_FILE:
+ case SQFS_INODE_EXT_FILE:
+ inode->mode |= S_IFREG;
+ break;
+ case SQFS_INODE_BDEV:
+ case SQFS_INODE_EXT_BDEV:
+ inode->mode |= S_IFBLK;
+ break;
+ case SQFS_INODE_DIR:
+ case SQFS_INODE_EXT_DIR:
+ inode->mode |= S_IFDIR;
+ break;
+ case SQFS_INODE_CDEV:
+ case SQFS_INODE_EXT_CDEV:
+ inode->mode |= S_IFCHR;
+ break;
+ case SQFS_INODE_FIFO:
+ case SQFS_INODE_EXT_FIFO:
+ inode->mode |= S_IFIFO;
+ break;
+ default:
+ return SQFS_ERROR_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size,
+ sqfs_u32 frag_index, sqfs_u32 frag_offset)
+{
+ sqfs_u64 count = size / block_size;
+
+ if ((size % block_size) != 0 &&
+ (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) {
+ ++count;
+ }
+
+ return count;
+}
+
+static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base,
+ size_t block_size, sqfs_inode_generic_t **result)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_file_t file;
+ sqfs_u64 i, count;
+ int err;
+
+ err = sqfs_meta_reader_read(ir, &file, sizeof(file));
+ if (err)
+ return err;
+
+ SWAB32(file.blocks_start);
+ SWAB32(file.fragment_index);
+ SWAB32(file.fragment_offset);
+ SWAB32(file.file_size);
+
+ count = get_block_count(file.file_size, block_size,
+ file.fragment_index, file.fragment_offset);
+
+ out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count);
+ if (out == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ out->base = *base;
+ out->data.file = file;
+ out->payload_bytes_available = count * sizeof(sqfs_u32);
+ out->payload_bytes_used = count * sizeof(sqfs_u32);
+
+ err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32));
+ if (err) {
+ free(out);
+ return err;
+ }
+
+ for (i = 0; i < count; ++i)
+ SWAB32(out->extra[i]);
+
+ *result = out;
+ return 0;
+}
+
+static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base,
+ size_t block_size, sqfs_inode_generic_t **result)
+{
+ sqfs_inode_file_ext_t file;
+ sqfs_inode_generic_t *out;
+ sqfs_u64 i, count;
+ int err;
+
+ err = sqfs_meta_reader_read(ir, &file, sizeof(file));
+ if (err)
+ return err;
+
+ 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 = get_block_count(file.file_size, block_size,
+ file.fragment_idx, file.fragment_offset);
+
+ out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count);
+ if (out == NULL) {
+ return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW :
+ SQFS_ERROR_ALLOC;
+ }
+
+ out->base = *base;
+ out->data.file_ext = file;
+ out->payload_bytes_available = count * sizeof(sqfs_u32);
+ out->payload_bytes_used = count * sizeof(sqfs_u32);
+
+ err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32));
+ if (err) {
+ free(out);
+ return err;
+ }
+
+ for (i = 0; i < count; ++i)
+ SWAB32(out->extra[i]);
+
+ *result = out;
+ return 0;
+}
+
+static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base,
+ sqfs_inode_generic_t **result)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_slink_t slink;
+ size_t size;
+ int err;
+
+ err = sqfs_meta_reader_read(ir, &slink, sizeof(slink));
+ if (err)
+ return err;
+
+ SWAB32(slink.nlink);
+ SWAB32(slink.target_size);
+
+ if (SZ_ADD_OV(slink.target_size, 1, &size) ||
+ SZ_ADD_OV(sizeof(*out), size, &size)) {
+ return SQFS_ERROR_OVERFLOW;
+ }
+
+ out = calloc(1, size);
+ if (out == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ out->payload_bytes_available = size - sizeof(*out);
+ out->payload_bytes_used = size - sizeof(*out) - 1;
+ out->base = *base;
+ out->data.slink = slink;
+
+ err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size);
+ if (err) {
+ free(out);
+ return err;
+ }
+
+ *result = out;
+ return 0;
+}
+
+static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base,
+ sqfs_inode_generic_t **result)
+{
+ sqfs_u32 xattr;
+ int err;
+
+ err = read_inode_slink(ir, base, result);
+ if (err)
+ return err;
+
+ err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr));
+ if (err) {
+ free(*result);
+ return err;
+ }
+
+ (*result)->data.slink_ext.xattr_idx = le32toh(xattr);
+ return 0;
+}
+
+static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base,
+ sqfs_inode_generic_t **result)
+{
+ size_t i, new_sz, index_max, index_used;
+ sqfs_inode_generic_t *out, *new;
+ sqfs_inode_dir_ext_t dir;
+ sqfs_dir_index_t ent;
+ int err;
+
+ err = sqfs_meta_reader_read(ir, &dir, sizeof(dir));
+ if (err)
+ return err;
+
+ SWAB32(dir.nlink);
+ SWAB32(dir.size);
+ SWAB32(dir.start_block);
+ SWAB32(dir.parent_inode);
+ SWAB16(dir.inodex_count);
+ SWAB16(dir.offset);
+ SWAB32(dir.xattr_idx);
+
+ index_max = dir.size ? 128 : 0;
+ index_used = 0;
+
+ out = alloc_flex(sizeof(*out), 1, index_max);
+ if (out == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ out->base = *base;
+ out->data.dir_ext = dir;
+
+ if (dir.size == 0) {
+ *result = out;
+ return 0;
+ }
+
+ for (i = 0; i < dir.inodex_count; ++i) {
+ err = sqfs_meta_reader_read(ir, &ent, sizeof(ent));
+ if (err) {
+ free(out);
+ return err;
+ }
+
+ SWAB32(ent.start_block);
+ SWAB32(ent.index);
+ SWAB32(ent.size);
+
+ new_sz = index_max;
+ while (sizeof(ent) + ent.size + 1 > new_sz - index_used) {
+ if (SZ_MUL_OV(new_sz, 2, &new_sz)) {
+ free(out);
+ return SQFS_ERROR_OVERFLOW;
+ }
+ }
+
+ if (new_sz > index_max) {
+ new = realloc(out, sizeof(*out) + new_sz);
+ if (new == NULL) {
+ free(out);
+ return SQFS_ERROR_ALLOC;
+ }
+ out = new;
+ index_max = new_sz;
+ }
+
+ memcpy((char *)out->extra + index_used, &ent, sizeof(ent));
+ index_used += sizeof(ent);
+
+ err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used,
+ ent.size + 1);
+ if (err) {
+ free(out);
+ return err;
+ }
+
+ index_used += ent.size + 1;
+ }
+
+ out->payload_bytes_used = index_used;
+ out->payload_bytes_available = index_used;
+ *result = out;
+ return 0;
+}
+
+int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir,
+ const sqfs_super_t *super,
+ sqfs_u64 block_start, size_t offset,
+ sqfs_inode_generic_t **result)
+{
+ sqfs_inode_generic_t *out;
+ sqfs_inode_t inode;
+ int err;
+
+ /* read base inode */
+ block_start += super->inode_table_start;
+
+ err = sqfs_meta_reader_seek(ir, block_start, offset);
+ if (err)
+ return err;
+
+ err = sqfs_meta_reader_read(ir, &inode, sizeof(inode));
+ if (err)
+ return err;
+
+ SWAB16(inode.type);
+ SWAB16(inode.mode);
+ SWAB16(inode.uid_idx);
+ SWAB16(inode.gid_idx);
+ SWAB32(inode.mod_time);
+ SWAB32(inode.inode_number);
+
+ err = set_mode(&inode);
+ if (err)
+ return err;
+
+ /* inode types where the size is variable */
+ switch (inode.type) {
+ case SQFS_INODE_FILE:
+ return read_inode_file(ir, &inode, super->block_size, result);
+ case SQFS_INODE_SLINK:
+ return read_inode_slink(ir, &inode, result);
+ case SQFS_INODE_EXT_FILE:
+ return read_inode_file_ext(ir, &inode, super->block_size,
+ result);
+ case SQFS_INODE_EXT_SLINK:
+ return read_inode_slink_ext(ir, &inode, result);
+ case SQFS_INODE_EXT_DIR:
+ return read_inode_dir_ext(ir, &inode, result);
+ default:
+ break;
+ }
+
+ /* everything else */
+ out = calloc(1, sizeof(*out));
+ if (out == NULL)
+ return SQFS_ERROR_ALLOC;
+
+ out->base = inode;
+
+ switch (inode.type) {
+ case SQFS_INODE_DIR:
+ err = sqfs_meta_reader_read(ir, &out->data.dir,
+ sizeof(out->data.dir));
+ if (err)
+ 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:
+ err = sqfs_meta_reader_read(ir, &out->data.dev,
+ sizeof(out->data.dev));
+ if (err)
+ goto fail_free;
+ SWAB32(out->data.dev.nlink);
+ SWAB32(out->data.dev.devno);
+ break;
+ case SQFS_INODE_FIFO:
+ case SQFS_INODE_SOCKET:
+ err = sqfs_meta_reader_read(ir, &out->data.ipc,
+ sizeof(out->data.ipc));
+ if (err)
+ goto fail_free;
+ SWAB32(out->data.ipc.nlink);
+ break;
+ case SQFS_INODE_EXT_BDEV:
+ case SQFS_INODE_EXT_CDEV:
+ err = sqfs_meta_reader_read(ir, &out->data.dev_ext,
+ sizeof(out->data.dev_ext));
+ if (err)
+ 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:
+ err = sqfs_meta_reader_read(ir, &out->data.ipc_ext,
+ sizeof(out->data.ipc_ext));
+ if (err)
+ goto fail_free;
+ SWAB32(out->data.ipc_ext.nlink);
+ SWAB32(out->data.ipc_ext.xattr_idx);
+ break;
+ default:
+ err = SQFS_ERROR_UNSUPPORTED;
+ goto fail_free;
+ }
+
+ *result = out;
+ return 0;
+fail_free:
+ free(out);
+ return err;
+}