diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-05-01 23:54:48 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-05-03 21:45:46 +0200 | 
| commit | affbcaf1deab75cf83e95512ff539d73474909e2 (patch) | |
| tree | 60a0f173b0fb8b5c5c86c4d097790cb99f1da762 /lib/sqfs/read_inode.c | |
| parent | 31d327c3780aecdb78304263f676ff89a8ea93d1 (diff) | |
Add function to read generic inode from meta data reader
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/sqfs/read_inode.c')
| -rw-r--r-- | lib/sqfs/read_inode.c | 316 | 
1 files changed, 316 insertions, 0 deletions
| 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; +} | 
