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

Commit 0ee49d71 authored by Paul Lawrence's avatar Paul Lawrence Committed by Blagovest Kolenichev
Browse files

ANDROID: Incremental fs: Cache successful hash calculations



Bug: 155996534
Signed-off-by: default avatarPaul Lawrence <paullawrence@google.com>
Change-Id: Ic508e6fa07c90decb29e07647dd3b0fc4d243ce8
(cherry picked from commit 21e6d932da379416bab8ea8d78f14525e7bf564d)
Git-commit: 3c5daa41
Git-repo: https://android.googlesource.com/kernel/common/


Signed-off-by: default avatarSayali Lokhande <sayalil@codeaurora.org>
parent 20ebfcf5
Loading
Loading
Loading
Loading
+87 −48
Original line number Diff line number Diff line
@@ -2,15 +2,16 @@
/*
 * Copyright 2019 Google LLC
 */
#include <linux/gfp.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/crc32.h>
#include <linux/file.h>
#include <linux/gfp.h>
#include <linux/ktime.h>
#include <linux/lz4.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/lz4.h>
#include <linux/crc32.h>

#include "data_mgmt.h"
#include "format.h"
@@ -383,24 +384,25 @@ static void log_block_read(struct mount_info *mi, incfs_uuid_t *id,
	++head->current_record_no;

	spin_unlock(&log->rl_lock);
	if (schedule_delayed_work(&log->ml_wakeup_work, msecs_to_jiffies(16)))
		pr_debug("incfs: scheduled a log pollers wakeup");
	schedule_delayed_work(&log->ml_wakeup_work, msecs_to_jiffies(16));
}

static int validate_hash_tree(struct file *bf, struct data_file *df,
			      int block_index, struct mem_range data, u8 *buf)
static int validate_hash_tree(struct file *bf, struct file *f, int block_index,
			      struct mem_range data, u8 *buf)
{
	u8 digest[INCFS_MAX_HASH_SIZE] = {};
	struct data_file *df = get_incfs_data_file(f);
	u8 stored_digest[INCFS_MAX_HASH_SIZE] = {};
	u8 calculated_digest[INCFS_MAX_HASH_SIZE] = {};
	struct mtree *tree = NULL;
	struct incfs_df_signature *sig = NULL;
	struct mem_range calc_digest_rng;
	struct mem_range saved_digest_rng;
	struct mem_range root_hash_rng;
	int digest_size;
	int hash_block_index = block_index;
	int hash_per_block;
	int lvl = 0;
	int lvl;
	int res;
	loff_t hash_block_offset[INCFS_MAX_MTREE_LEVELS];
	size_t hash_offset_in_block[INCFS_MAX_MTREE_LEVELS];
	int hash_per_block;
	pgoff_t file_pages;

	tree = df->df_hash_tree;
	sig = df->df_signature;
@@ -409,38 +411,60 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,

	digest_size = tree->alg->digest_size;
	hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
	calc_digest_rng = range(digest, digest_size);
	res = incfs_calc_digest(tree->alg, data, calc_digest_rng);
	if (res)
		return res;

	for (lvl = 0; lvl < tree->depth; lvl++) {
		loff_t lvl_off =
			tree->hash_level_suboffset[lvl] + sig->hash_offset;
		loff_t hash_block_off = lvl_off +
			round_down(hash_block_index * digest_size,
				INCFS_DATA_FILE_BLOCK_SIZE);
		size_t hash_off_in_block = hash_block_index * digest_size
			% INCFS_DATA_FILE_BLOCK_SIZE;
		struct mem_range buf_range = range(buf,
		loff_t lvl_off = tree->hash_level_suboffset[lvl];

		hash_block_offset[lvl] =
			lvl_off + round_down(hash_block_index * digest_size,
					     INCFS_DATA_FILE_BLOCK_SIZE);
		ssize_t read_res = incfs_kread(bf, buf,
				INCFS_DATA_FILE_BLOCK_SIZE, hash_block_off);
		hash_offset_in_block[lvl] = hash_block_index * digest_size %
					    INCFS_DATA_FILE_BLOCK_SIZE;
		hash_block_index /= hash_per_block;
	}

		if (read_res < 0)
			return read_res;
		if (read_res != INCFS_DATA_FILE_BLOCK_SIZE)
	memcpy(stored_digest, tree->root_hash, digest_size);

	file_pages = DIV_ROUND_UP(df->df_size, INCFS_DATA_FILE_BLOCK_SIZE);
	for (lvl = tree->depth - 1; lvl >= 0; lvl--) {
		pgoff_t hash_page =
			file_pages +
			hash_block_offset[lvl] / INCFS_DATA_FILE_BLOCK_SIZE;
		struct page *page = find_get_page_flags(
			f->f_inode->i_mapping, hash_page, FGP_ACCESSED);

		if (page && PageChecked(page)) {
			u8 *addr = kmap_atomic(page);

			memcpy(stored_digest, addr + hash_offset_in_block[lvl],
			       digest_size);
			kunmap_atomic(addr);
			put_page(page);
			continue;
		}

		if (page)
			put_page(page);

		res = incfs_kread(bf, buf, INCFS_DATA_FILE_BLOCK_SIZE,
				  hash_block_offset[lvl] + sig->hash_offset);
		if (res < 0)
			return res;
		if (res != INCFS_DATA_FILE_BLOCK_SIZE)
			return -EIO;
		res = incfs_calc_digest(tree->alg,
					range(buf, INCFS_DATA_FILE_BLOCK_SIZE),
					range(calculated_digest, digest_size));
		if (res)
			return res;

		saved_digest_rng = range(buf + hash_off_in_block, digest_size);
		if (!incfs_equal_ranges(calc_digest_rng, saved_digest_rng)) {
		if (memcmp(stored_digest, calculated_digest, digest_size)) {
			int i;
			bool zero = true;

			pr_debug("incfs: Hash mismatch lvl:%d blk:%d\n",
				lvl, block_index);
			for (i = 0; i < saved_digest_rng.len; ++i)
				if (saved_digest_rng.data[i]) {
			for (i = 0; i < digest_size; i++)
				if (stored_digest[i]) {
					zero = false;
					break;
				}
@@ -450,17 +474,31 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
			return -EBADMSG;
		}

		res = incfs_calc_digest(tree->alg, buf_range, calc_digest_rng);
		memcpy(stored_digest, buf + hash_offset_in_block[lvl],
		       digest_size);

		page = grab_cache_page(f->f_inode->i_mapping, hash_page);
		if (page) {
			u8 *addr = kmap_atomic(page);

			memcpy(addr, buf, INCFS_DATA_FILE_BLOCK_SIZE);
			kunmap_atomic(addr);
			SetPageChecked(page);
			unlock_page(page);
			put_page(page);
		}
	}

	res = incfs_calc_digest(tree->alg, data,
				range(calculated_digest, digest_size));
	if (res)
		return res;
		hash_block_index /= hash_per_block;
	}

	root_hash_rng = range(tree->root_hash, digest_size);
	if (!incfs_equal_ranges(calc_digest_rng, root_hash_rng)) {
		pr_debug("incfs: Root hash mismatch blk:%d\n", block_index);
	if (memcmp(stored_digest, calculated_digest, digest_size)) {
		pr_debug("incfs: Leaf hash mismatch blk:%d\n", block_index);
		return -EBADMSG;
	}

	return 0;
}

@@ -872,7 +910,7 @@ static int wait_for_data_block(struct data_file *df, int block_index,
	return error;
}

ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
				   int index, int timeout_ms,
				   struct mem_range tmp)
{
@@ -882,6 +920,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
	struct mount_info *mi = NULL;
	struct file *bf = NULL;
	struct data_file_block block = {};
	struct data_file *df = get_incfs_data_file(f);

	if (!dst.data || !df)
		return -EFAULT;
@@ -924,7 +963,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
	}

	if (result > 0) {
		int err = validate_hash_tree(bf, df, index, dst, tmp.data);
		int err = validate_hash_tree(bf, f, index, dst, tmp.data);

		if (err < 0)
			result = err;
+1 −1
Original line number Diff line number Diff line
@@ -276,7 +276,7 @@ int incfs_scan_metadata_chain(struct data_file *df);
struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf);
void incfs_free_dir_file(struct dir_file *dir);

ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
				   int index, int timeout_ms,
				   struct mem_range tmp);

+1 −1
Original line number Diff line number Diff line
@@ -818,7 +818,7 @@ static int read_single_page(struct file *f, struct page *page)
		tmp.data = (u8 *)__get_free_pages(GFP_NOFS, get_order(tmp.len));
		bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE);
		read_result = incfs_read_data_file_block(
			range(page_start, bytes_to_read), df, block_index,
			range(page_start, bytes_to_read), f, block_index,
			timeout_ms, tmp);

		free_pages((unsigned long)tmp.data, get_order(tmp.len));