Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 975fd062 authored by Srinivasarao P's avatar Srinivasarao P
Browse files

Reverting incremental fs changes



e2821247 ANDROID: Incremental fs: Fix issues with very large files
b564958e ANDROID: Incremental fs: Add setattr call
fdd560ec ANDROID: Incremental fs: Use simple compression in log buffer
4f81903f ANDROID: Incremental fs: Fix create_file performance
3c76e8df ANDROID: Incremental fs: Fix compound page usercopy crash
3aee2b94 ANDROID: Incremental fs: Clean up incfs_test build process
e158e207 ANDROID: Incremental fs: make remount log buffer change atomic
467d1f63 ANDROID: Incremental fs: Optimize get_filled_block
91ef6b61 ANDROID: Incremental fs: Fix mislabeled __user ptrs
1b7e2d07 ANDROID: Incremental fs: Use 64-bit int for file_size when writing hash blocks
df76f38e Revert "ANDROID: Incremental fs: Fix initialization, use of bitfields"
d73d0b4d ANDROID: Incremental fs: Fix remount
ecd6f86b ANDROID: Incremental fs: Protect get_fill_block, and add a field
1a000625 ANDROID: Incremental fs: Fix crash polling 0 size read_log
df5824ee ANDROID: Incremental fs: get_filled_blocks: better index_out
a4cad4eb ANDROID: Incremental fs: Fix four resource bugs
76f5f1ca ANDROID: Incremental fs: Add INCFS_IOC_GET_FILLED_BLOCKS
2d41ac81 ANDROID: Incremental fs: Fix two typos
cb94ec7a ANDROID: Incremental fs: Add INCFS_IOC_PERMIT_FILL
758073be ANDROID: Incremental fs: Remove signature checks from kernel
8118f34d ANDROID: Incremental fs: Pad hash blocks
dd3909c4 ANDROID: Incremental fs: Make fill block an ioctl
89e09054 ANDROID: Incremental fs: Remove all access_ok checks
ee1d24d6 ANDROID: Incremental fs: Support xattrs

Change-Id: Ib455db0ab788d08e968dcc665e2c9bd98c701b91
Signed-off-by: default avatarSrinivasarao P <spathi@codeaurora.org>
parent 816f245a
Loading
Loading
Loading
Loading
+233 −457

File changed.

Preview size limit exceeded, changes collapsed.

+52 −78
Original line number Diff line number Diff line
@@ -20,74 +20,63 @@

#define SEGMENTS_PER_FILE 3

enum LOG_RECORD_TYPE {
	FULL,
	SAME_FILE,
	SAME_FILE_NEXT_BLOCK,
	SAME_FILE_NEXT_BLOCK_SHORT,
};
struct read_log_record {
	u32 bitfield;

	u64 timestamp_us;

struct full_record {
	enum LOG_RECORD_TYPE type : 2; /* FULL */
	u32 block_index : 30;
	incfs_uuid_t file_id;
	u64 absolute_ts_us;
} __packed; /* 28 bytes */

struct same_file_record {
	enum LOG_RECORD_TYPE type : 2; /* SAME_FILE */
	u32 block_index : 30;
	u32 relative_ts_us; /* max 2^32 us ~= 1 hour (1:11:30) */
} __packed; /* 12 bytes */

struct same_file_next_block {
	enum LOG_RECORD_TYPE type : 2; /* SAME_FILE_NEXT_BLOCK */
	u32 relative_ts_us : 30; /* max 2^30 us ~= 15 min (17:50) */
} __packed; /* 4 bytes */

struct same_file_next_block_short {
	enum LOG_RECORD_TYPE type : 2; /* SAME_FILE_NEXT_BLOCK_SHORT */
	u16 relative_ts_us : 14; /* max 2^14 us ~= 16 ms */
} __packed; /* 2 bytes */

union log_record {
	struct full_record full_record;
	struct same_file_record same_file_record;
	struct same_file_next_block same_file_next_block;
	struct same_file_next_block_short same_file_next_block_short;
};
} __packed;

struct read_log_state {
	/* Log buffer generation id, incremented on configuration changes */
	u32 generation_id;
#define RLR_BLOCK_INDEX_MASK 0x7fff
#define RLR_TIMED_OUT_MASK 0x8000

	/* Offset in rl_ring_buf to write into. */
	u32 next_offset;
static inline u32 get_block_index(const struct read_log_record *rlr)
{
	return rlr->bitfield & RLR_BLOCK_INDEX_MASK;
}

	/* Current number of writer passes over rl_ring_buf */
	u32 current_pass_no;
static inline void set_block_index(struct read_log_record *rlr,
				  u32 block_index)
{
	rlr->bitfield = (rlr->bitfield & ~RLR_BLOCK_INDEX_MASK)
			| (block_index & RLR_BLOCK_INDEX_MASK);
}

static inline bool get_timed_out(const struct read_log_record *rlr)
{
	return (rlr->bitfield & RLR_TIMED_OUT_MASK) == RLR_TIMED_OUT_MASK;
}

static inline void set_timed_out(struct read_log_record *rlr, bool timed_out)
{
	if (timed_out)
		rlr->bitfield |= RLR_TIMED_OUT_MASK;
	else
		rlr->bitfield &= ~RLR_TIMED_OUT_MASK;
}

	/* Current full_record to diff against */
	struct full_record base_record;
struct read_log_state {
	/* Next slot in rl_ring_buf to write to. */
	u32 next_index;

	/* Current record number counting from configuration change */
	u64 current_record_no;
	/* Current number of writer pass over rl_ring_buf */
	u32 current_pass_no;
};

/* A ring buffer to save records about data blocks which were recently read. */
struct read_log {
	void *rl_ring_buf;

	int rl_size;
	struct read_log_record *rl_ring_buf;

	struct read_log_state rl_head;
	struct read_log_state rl_state;

	struct read_log_state rl_tail;
	spinlock_t rl_writer_lock;

	/* A lock to protect the above fields */
	spinlock_t rl_lock;
	int rl_size;

	/* A queue of waiters who want to be notified about reads */
	/*
	 * A queue of waiters who want to be notified about reads.
	 */
	wait_queue_head_t ml_notif_wq;
};

@@ -142,12 +131,6 @@ struct mount_info {

	/* Temporary buffer for read logger. */
	struct read_log mi_log;

	void *log_xattr;
	size_t log_xattr_size;

	void *pending_read_xattr;
	size_t pending_read_xattr_size;
};

struct data_file_block {
@@ -220,20 +203,16 @@ struct data_file {
	/* File size in bytes */
	loff_t df_size;

	/* File header flags */
	u32 df_header_flags;

	/* File size in DATA_FILE_BLOCK_SIZE blocks */
	int df_data_block_count;

	/* Total number of blocks, data + hash */
	int df_total_block_count;
	int df_block_count; /* File size in DATA_FILE_BLOCK_SIZE blocks */

	struct file_attr n_attr;

	struct mtree *df_hash_tree;

	struct incfs_df_signature *df_signature;
	struct ondisk_signature *df_signature;

	/* True, if file signature has already been validated. */
	bool df_signature_validated;
};

struct dir_file {
@@ -260,9 +239,6 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb,
					  struct mount_options *options,
					  struct path *backing_dir_path);

int incfs_realloc_mount_info(struct mount_info *mi,
			     struct mount_options *options);

void incfs_free_mount_info(struct mount_info *mi);

struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf);
@@ -277,16 +253,14 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
				   int index, int timeout_ms,
				   struct mem_range tmp);

int incfs_get_filled_blocks(struct data_file *df,
			    struct incfs_get_filled_blocks_args *arg);

int incfs_read_file_signature(struct data_file *df, struct mem_range dst);

int incfs_process_new_data_block(struct data_file *df,
				 struct incfs_fill_block *block, u8 *data);
				 struct incfs_new_data_block *block, u8 *data);

int incfs_process_new_hash_block(struct data_file *df,
				 struct incfs_fill_block *block, u8 *data);
				 struct incfs_new_data_block *block, u8 *data);


bool incfs_fresh_pending_reads_exist(struct mount_info *mi, int last_number);

@@ -305,7 +279,7 @@ int incfs_collect_logged_reads(struct mount_info *mi,
			       int reads_size);
struct read_log_state incfs_get_log_state(struct mount_info *mi);
int incfs_get_uncollected_logs_count(struct mount_info *mi,
				     const struct read_log_state *state);
				     struct read_log_state state);

static inline struct inode_info *get_incfs_node(struct inode *inode)
{
@@ -323,7 +297,7 @@ static inline struct inode_info *get_incfs_node(struct inode *inode)

static inline struct data_file *get_incfs_data_file(struct file *f)
{
	struct inode_info *node = NULL;
	struct inode_info *node;

	if (!f)
		return NULL;
+61 −65
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@
#include <linux/kernel.h>

#include "format.h"
#include "data_mgmt.h"

struct backing_file_context *incfs_alloc_bfc(struct file *backing_file)
{
@@ -94,6 +93,7 @@ static int append_zeros(struct backing_file_context *bfc, size_t len)
{
	loff_t file_size = 0;
	loff_t new_last_byte_offset = 0;
	int res = 0;

	if (!bfc)
		return -EFAULT;
@@ -110,18 +110,28 @@ static int append_zeros(struct backing_file_context *bfc, size_t len)
	 */
	file_size = incfs_get_end_offset(bfc->bc_file);
	new_last_byte_offset = file_size + len - 1;
	return vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1);
	res = vfs_fallocate(bfc->bc_file, 0, new_last_byte_offset, 1);
	if (res)
		return res;

	res = vfs_fsync_range(bfc->bc_file, file_size, file_size + len, 1);
	return res;
}

static int write_to_bf(struct backing_file_context *bfc, const void *buf,
			size_t count, loff_t pos)
			size_t count, loff_t pos, bool sync)
{
	ssize_t res = incfs_kwrite(bfc->bc_file, buf, count, pos);
	ssize_t res = 0;

	res = incfs_kwrite(bfc->bc_file, buf, count, pos);
	if (res < 0)
		return res;
	if (res != count)
		return -EIO;

	if (sync)
		return vfs_fsync_range(bfc->bc_file, pos, pos + count, 1);

	return 0;
}

@@ -175,7 +185,7 @@ static int append_md_to_backing_file(struct backing_file_context *bfc,
	/* Write the metadata record to the end of the backing file */
	record_offset = file_pos;
	new_md_offset = cpu_to_le64(record_offset);
	result = write_to_bf(bfc, record, record_size, file_pos);
	result = write_to_bf(bfc, record, record_size, file_pos, true);
	if (result)
		return result;

@@ -196,7 +206,7 @@ static int append_md_to_backing_file(struct backing_file_context *bfc,
				    fh_first_md_offset);
	}
	result = write_to_bf(bfc, &new_md_offset, sizeof(new_md_offset),
			     file_pos);
				file_pos, true);
	if (result)
		return result;

@@ -204,22 +214,12 @@ static int append_md_to_backing_file(struct backing_file_context *bfc,
	return result;
}

int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags)
{
	if (!bfc)
		return -EFAULT;

	return write_to_bf(bfc, &flags, sizeof(flags),
			   offsetof(struct incfs_file_header,
				    fh_file_header_flags));
}

/*
 * Reserve 0-filled space for the blockmap body, and append
 * incfs_blockmap metadata record pointing to it.
 */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
					 u32 block_count)
				u32 block_count, loff_t *map_base_off)
{
	struct incfs_blockmap blockmap = {};
	int result = 0;
@@ -245,9 +245,12 @@ int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
	/* Write blockmap metadata record pointing to the body written above. */
	blockmap.m_base_offset = cpu_to_le64(file_end);
	result = append_md_to_backing_file(bfc, &blockmap.m_header);
	if (result)
	if (result) {
		/* Error, rollback file changes */
		truncate_backing_file(bfc, file_end);
	} else if (map_base_off) {
		*map_base_off = file_end;
	}

	return result;
}
@@ -280,7 +283,7 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
	file_attr.fa_offset = cpu_to_le64(value_offset);
	file_attr.fa_crc = cpu_to_le32(crc);

	result = write_to_bf(bfc, value.data, value.len, value_offset);
	result = write_to_bf(bfc, value.data, value.len, value_offset, true);
	if (result)
		return result;

@@ -296,7 +299,9 @@ int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
}

int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
					  struct mem_range sig, u32 tree_size)
		u8 hash_alg, u32 tree_size,
		struct mem_range root_hash, struct mem_range add_data,
		struct mem_range sig)
{
	struct incfs_file_signature sg = {};
	int result = 0;
@@ -306,6 +311,8 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,

	if (!bfc)
		return -EFAULT;
	if (root_hash.len > sizeof(sg.sg_root_hash))
		return -E2BIG;

	LOCK_REQUIRED(bfc->bc_mutex);

@@ -314,19 +321,32 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
	sg.sg_header.h_md_entry_type = INCFS_MD_SIGNATURE;
	sg.sg_header.h_record_size = cpu_to_le16(sizeof(sg));
	sg.sg_header.h_next_md_offset = cpu_to_le64(0);
	sg.sg_hash_alg = hash_alg;
	if (sig.data != NULL && sig.len > 0) {
		loff_t pos = incfs_get_end_offset(bfc->bc_file);

		sg.sg_sig_size = cpu_to_le32(sig.len);
		sg.sg_sig_offset = cpu_to_le64(pos);

		result = write_to_bf(bfc, sig.data, sig.len, pos);
		result = write_to_bf(bfc, sig.data, sig.len, pos, false);
		if (result)
			goto err;
	}

	if (add_data.len > 0) {
		loff_t pos = incfs_get_end_offset(bfc->bc_file);

		sg.sg_add_data_size = cpu_to_le32(add_data.len);
		sg.sg_add_data_offset = cpu_to_le64(pos);

		result = write_to_bf(bfc, add_data.data,
			add_data.len, pos, false);
		if (result)
			goto err;
	}

	tree_area_pos = incfs_get_end_offset(bfc->bc_file);
	if (tree_size > 0) {
	if (hash_alg && tree_size > 0) {
		if (tree_size > 5 * INCFS_DATA_FILE_BLOCK_SIZE) {
			/*
			 * If hash tree is big enough, it makes sense to
@@ -349,13 +369,15 @@ int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
		sg.sg_hash_tree_size = cpu_to_le32(tree_size);
		sg.sg_hash_tree_offset = cpu_to_le64(tree_area_pos);
	}
	memcpy(sg.sg_root_hash, root_hash.data, root_hash.len);

	/* Write a hash tree metadata record pointing to the hash tree above. */
	result = append_md_to_backing_file(bfc, &sg.sg_header);
err:
	if (result)
	if (result) {
		/* Error, rollback file changes */
		truncate_backing_file(bfc, rollback_pos);
	}
	return result;
}

@@ -389,7 +411,7 @@ int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
	if (file_pos != 0)
		return -EEXIST;

	return write_to_bf(bfc, &fh, sizeof(fh), file_pos);
	return write_to_bf(bfc, &fh, sizeof(fh), file_pos, true);
}

/* Write a given data block and update file's blockmap to point it. */
@@ -418,7 +440,7 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,
	}

	/* Write the block data at the end of the backing file. */
	result = write_to_bf(bfc, block.data, block.len, data_offset);
	result = write_to_bf(bfc, block.data, block.len, data_offset, false);
	if (result)
		return result;

@@ -428,25 +450,18 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,
	bm_entry.me_data_size = cpu_to_le16((u16)block.len);
	bm_entry.me_flags = cpu_to_le16(flags);

	return write_to_bf(bfc, &bm_entry, sizeof(bm_entry),
				bm_entry_off);
	result = write_to_bf(bfc, &bm_entry, sizeof(bm_entry),
				bm_entry_off, false);
	return result;
}

int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
					struct mem_range block,
					   int block_index,
					   loff_t hash_area_off,
					   loff_t bm_base_off,
					   loff_t file_size)
					int block_index, loff_t hash_area_off)
{
	struct incfs_blockmap_entry bm_entry = {};
	int result;
	loff_t data_offset = 0;
	loff_t file_end = 0;
	loff_t bm_entry_off =
		bm_base_off +
		sizeof(struct incfs_blockmap_entry) *
			(block_index + get_blocks_count_for_size(file_size));


	if (!bfc)
		return -EFAULT;
@@ -460,16 +475,7 @@ int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
		return -EINVAL;
	}

	result = write_to_bf(bfc, block.data, block.len, data_offset);
	if (result)
		return result;

	bm_entry.me_data_offset_lo = cpu_to_le32((u32)data_offset);
	bm_entry.me_data_offset_hi = cpu_to_le16((u16)(data_offset >> 32));
	bm_entry.me_data_size = cpu_to_le16(INCFS_DATA_FILE_BLOCK_SIZE);
	bm_entry.me_flags = cpu_to_le16(INCFS_BLOCK_HASH);

	return write_to_bf(bfc, &bm_entry, sizeof(bm_entry), bm_entry_off);
	return write_to_bf(bfc, block.data, block.len, data_offset, false);
}

/* Initialize a new image in a given backing file. */
@@ -499,19 +505,8 @@ int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
			loff_t bm_base_off,
			struct incfs_blockmap_entry *bm_entry)
{
	int error = incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1,
	return incfs_read_blockmap_entries(bfc, bm_entry, block_index, 1,
		bm_base_off);

	if (error < 0)
		return error;

	if (error == 0)
		return -EIO;

	if (error != 1)
		return -EFAULT;

	return 0;
}

int incfs_read_blockmap_entries(struct backing_file_context *bfc,
@@ -535,12 +530,15 @@ int incfs_read_blockmap_entries(struct backing_file_context *bfc,
			     bm_entry_off);
	if (result < 0)
		return result;
	return result / sizeof(*entries);
	if (result < bytes_to_read)
		return -EIO;
	return 0;
}


int incfs_read_file_header(struct backing_file_context *bfc,
			   loff_t *first_md_off, incfs_uuid_t *uuid,
			   u64 *file_size, u32 *flags)
			   u64 *file_size)
{
	ssize_t bytes_read = 0;
	struct incfs_file_header fh = {};
@@ -574,8 +572,6 @@ int incfs_read_file_header(struct backing_file_context *bfc,
		*uuid = fh.fh_uuid;
	if (file_size)
		*file_size = le64_to_cpu(fh.fh_file_size);
	if (flags)
		*flags = le32_to_cpu(fh.fh_file_header_flags);
	return 0;
}

+38 −29
Original line number Diff line number Diff line
@@ -121,10 +121,6 @@ enum incfs_metadata_type {
	INCFS_MD_SIGNATURE = 3
};

enum incfs_file_header_flags {
	INCFS_FILE_COMPLETE = 1 << 0,
};

/* Header included at the beginning of all metadata records on the disk. */
struct incfs_md_header {
	__u8 h_md_entry_type;
@@ -163,8 +159,8 @@ struct incfs_file_header {
	/* INCFS_DATA_FILE_BLOCK_SIZE */
	__le16 fh_data_block_size;

	/* File flags, from incfs_file_header_flags */
	__le32 fh_file_header_flags;
	/* Padding, also reserved for future use. */
	__le32 fh_dummy;

	/* Offset of the first metadata record */
	__le64 fh_first_md_offset;
@@ -182,7 +178,6 @@ struct incfs_file_header {

enum incfs_block_map_entry_flags {
	INCFS_BLOCK_COMPRESSED_LZ4 = (1 << 0),
	INCFS_BLOCK_HASH = (1 << 1),
};

/* Block map entry pointing to an actual location of the data block. */
@@ -222,26 +217,26 @@ struct incfs_file_attr {
	__le32 fa_crc;
} __packed;

/* Metadata record for file signature. Type = INCFS_MD_SIGNATURE */
/* Metadata record for file attribute. Type = INCFS_MD_SIGNATURE */
struct incfs_file_signature {
	struct incfs_md_header sg_header;

	__le32 sg_sig_size; /* The size of the signature. */

	__le64 sg_sig_offset; /* Signature's offset in the backing file */
	__u8 sg_hash_alg; /* Value from incfs_hash_tree_algorithm */

	__le32 sg_hash_tree_size; /* The size of the hash tree. */

	__le64 sg_hash_tree_offset; /* Hash tree offset in the backing file */
} __packed;

/* In memory version of above */
struct incfs_df_signature {
	u32 sig_size;
	u64 sig_offset;
	u32 hash_size;
	u64 hash_offset;
};
	__u8 sg_root_hash[INCFS_MAX_HASH_SIZE];

	__le32 sg_sig_size; /* The size of the pkcs7 signature. */

	__le64 sg_sig_offset; /* pkcs7 signature's offset in the backing file */

	__le32 sg_add_data_size; /* The size of the additional data. */

	__le64 sg_add_data_offset; /* Additional data's offset */
} __packed;

/* State of the backing file. */
struct backing_file_context {
@@ -258,6 +253,23 @@ struct backing_file_context {
	loff_t bc_last_md_record_offset;
};


/* Backing file locations of things required for signature validation. */
struct ondisk_signature {

	loff_t add_data_offset; /* Additional data's offset */

	loff_t sig_offset; /* pkcs7 signature's offset in the backing file */

	loff_t mtree_offset; /* Backing file offset of the hash tree. */

	u32 add_data_size; /* The size of the additional data. */

	u32 sig_size; /* The size of the pkcs7 signature. */

	u32 mtree_size; /* The size of the hash tree. */
};

struct metadata_handler {
	loff_t md_record_offset;
	loff_t md_prev_record_offset;
@@ -289,7 +301,7 @@ void incfs_free_bfc(struct backing_file_context *bfc);

/* Writing stuff */
int incfs_write_blockmap_to_backing_file(struct backing_file_context *bfc,
					 u32 block_count);
					 u32 block_count, loff_t *map_base_off);

int incfs_write_fh_to_backing_file(struct backing_file_context *bfc,
				   incfs_uuid_t *uuid, u64 file_size);
@@ -301,18 +313,15 @@ int incfs_write_data_block_to_backing_file(struct backing_file_context *bfc,

int incfs_write_hash_block_to_backing_file(struct backing_file_context *bfc,
					struct mem_range block,
					   int block_index,
					   loff_t hash_area_off,
					   loff_t bm_base_off,
					   loff_t file_size);
					int block_index, loff_t hash_area_off);

int incfs_write_file_attr_to_backing_file(struct backing_file_context *bfc,
		struct mem_range value, struct incfs_file_attr *attr);

int incfs_write_signature_to_backing_file(struct backing_file_context *bfc,
					  struct mem_range sig, u32 tree_size);

int incfs_write_file_header_flags(struct backing_file_context *bfc, u32 flags);
		u8 hash_alg, u32 tree_size,
		struct mem_range root_hash, struct mem_range add_data,
		struct mem_range sig);

int incfs_make_empty_backing_file(struct backing_file_context *bfc,
				  incfs_uuid_t *uuid, u64 file_size);
@@ -320,7 +329,7 @@ int incfs_make_empty_backing_file(struct backing_file_context *bfc,
/* Reading stuff */
int incfs_read_file_header(struct backing_file_context *bfc,
			   loff_t *first_md_off, incfs_uuid_t *uuid,
			   u64 *file_size, u32 *flags);
			   u64 *file_size);

int incfs_read_blockmap_entry(struct backing_file_context *bfc, int block_index,
			      loff_t bm_base_off,
+80 −103
Original line number Diff line number Diff line
@@ -10,6 +10,70 @@

#include "integrity.h"

int incfs_validate_pkcs7_signature(struct mem_range pkcs7_blob,
	struct mem_range root_hash, struct mem_range add_data)
{
	struct pkcs7_message *pkcs7 = NULL;
	const void *data = NULL;
	size_t data_len = 0;
	const char *p;
	int err;

	pkcs7 = pkcs7_parse_message(pkcs7_blob.data, pkcs7_blob.len);
	if (IS_ERR(pkcs7)) {
		pr_debug("PKCS#7 parsing error. ptr=%p size=%ld err=%ld\n",
			pkcs7_blob.data, pkcs7_blob.len, -PTR_ERR(pkcs7));
		return PTR_ERR(pkcs7);
	}

	err = pkcs7_get_content_data(pkcs7, &data, &data_len, NULL);
	if (err || data_len == 0 || data == NULL) {
		pr_debug("PKCS#7 message does not contain data\n");
		err = -EBADMSG;
		goto out;
	}

	if (root_hash.len == 0) {
		pr_debug("Root hash is empty.\n");
		err = -EBADMSG;
		goto out;
	}

	if (data_len != root_hash.len + add_data.len) {
		pr_debug("PKCS#7 data size doesn't match arguments.\n");
		err = -EKEYREJECTED;
		goto out;
	}

	p = data;
	if (memcmp(p, root_hash.data, root_hash.len) != 0) {
		pr_debug("Root hash mismatch.\n");
		err = -EKEYREJECTED;
		goto out;
	}
	p += root_hash.len;
	if (memcmp(p, add_data.data, add_data.len) != 0) {
		pr_debug("Additional data mismatch.\n");
		err = -EKEYREJECTED;
		goto out;
	}

	err = pkcs7_verify(pkcs7, VERIFYING_UNSPECIFIED_SIGNATURE);
	if (err)
		pr_debug("PKCS#7 signature verification error: %d\n", -err);

	/*
	 * RSA signature verification sometimes returns unexpected error codes
	 * when signature doesn't match.
	 */
	if (err == -ERANGE || err == -EINVAL)
		err = -EBADMSG;

out:
	pkcs7_free_message(pkcs7);
	return err;
}

struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
{
	static struct incfs_hash_alg sha256 = {
@@ -49,90 +113,11 @@ struct incfs_hash_alg *incfs_get_hash_alg(enum incfs_hash_tree_algorithm id)
	return result;
}

struct signature_info {
	u32 version;
	enum incfs_hash_tree_algorithm hash_algorithm;
	u8 log2_blocksize;
	struct mem_range salt;
	struct mem_range root_hash;
};

static bool read_u32(u8 **p, u8 *top, u32 *result)
{
	if (*p + sizeof(u32) > top)
		return false;

	*result = le32_to_cpu(*(__le32 *)*p);
	*p += sizeof(u32);
	return true;
}

static bool read_u8(u8 **p, u8 *top, u8 *result)
{
	if (*p + sizeof(u8) > top)
		return false;

	*result = *(u8 *)*p;
	*p += sizeof(u8);
	return true;
}

static bool read_mem_range(u8 **p, u8 *top, struct mem_range *range)
struct mtree *incfs_alloc_mtree(enum incfs_hash_tree_algorithm id,
				int data_block_count,
				struct mem_range root_hash)
{
	u32 len;

	if (!read_u32(p, top, &len) || *p + len > top)
		return false;

	range->len = len;
	range->data = *p;
	*p += len;
	return true;
}

static int incfs_parse_signature(struct mem_range signature,
				 struct signature_info *si)
{
	u8 *p = signature.data;
	u8 *top = signature.data + signature.len;
	u32 hash_section_size;

	if (signature.len > INCFS_MAX_SIGNATURE_SIZE)
		return -EINVAL;

	if (!read_u32(&p, top, &si->version) ||
	    si->version != INCFS_SIGNATURE_VERSION)
		return -EINVAL;

	if (!read_u32(&p, top, &hash_section_size) ||
	    p + hash_section_size > top)
		return -EINVAL;
	top = p + hash_section_size;

	if (!read_u32(&p, top, &si->hash_algorithm) ||
	    si->hash_algorithm != INCFS_HASH_TREE_SHA256)
		return -EINVAL;

	if (!read_u8(&p, top, &si->log2_blocksize) || si->log2_blocksize != 12)
		return -EINVAL;

	if (!read_mem_range(&p, top, &si->salt))
		return -EINVAL;

	if (!read_mem_range(&p, top, &si->root_hash))
		return -EINVAL;

	if (p != top)
		return -EINVAL;

	return 0;
}

struct mtree *incfs_alloc_mtree(struct mem_range signature,
				int data_block_count)
{
	int error;
	struct signature_info si;
	struct mtree *result = NULL;
	struct incfs_hash_alg *hash_alg = NULL;
	int hash_per_block;
@@ -144,15 +129,11 @@ struct mtree *incfs_alloc_mtree(struct mem_range signature,
	if (data_block_count <= 0)
		return ERR_PTR(-EINVAL);

	error = incfs_parse_signature(signature, &si);
	if (error)
		return ERR_PTR(error);

	hash_alg = incfs_get_hash_alg(si.hash_algorithm);
	hash_alg = incfs_get_hash_alg(id);
	if (IS_ERR(hash_alg))
		return ERR_PTR(PTR_ERR(hash_alg));

	if (si.root_hash.len < hash_alg->digest_size)
	if (root_hash.len < hash_alg->digest_size)
		return ERR_PTR(-EINVAL);

	result = kzalloc(sizeof(*result), GFP_NOFS);
@@ -192,7 +173,7 @@ struct mtree *incfs_alloc_mtree(struct mem_range signature,
	}

	/* Root hash is stored separately from the rest of the tree. */
	memcpy(result->root_hash, si.root_hash.data, hash_alg->digest_size);
	memcpy(result->root_hash, root_hash.data, hash_alg->digest_size);
	return result;

err:
@@ -217,20 +198,16 @@ int incfs_calc_digest(struct incfs_hash_alg *alg, struct mem_range data,
		return -EINVAL;

	desc->tfm = alg->shash;

	if (data.len < INCFS_DATA_FILE_BLOCK_SIZE) {
		int err;
		void *buf = kzalloc(INCFS_DATA_FILE_BLOCK_SIZE, GFP_NOFS);

		if (!buf)
			return -ENOMEM;

		memcpy(buf, data.data, data.len);
		err = crypto_shash_digest(desc, buf, INCFS_DATA_FILE_BLOCK_SIZE,
					  digest.data);
		kfree(buf);
		return err;
	}
	return crypto_shash_digest(desc, data.data, data.len, digest.data);
}

void incfs_free_signature_info(struct signature_info *si)
{
	if (!si)
		return;
	kfree(si->root_hash.data);
	kfree(si->additional_data.data);
	kfree(si->signature.data);
	kfree(si);
}
Loading