ANDROID: Incremental fs: Cache successful hash calculations

Bug: 155996534
Signed-off-by: Paul Lawrence <paullawrence@google.com>
Change-Id: Ic508e6fa07c90decb29e07647dd3b0fc4d243ce8
(cherry picked from commit 21e6d932da379416bab8ea8d78f14525e7bf564d)
This commit is contained in:
Paul Lawrence 2020-05-28 08:25:02 -07:00
parent 6a5003146f
commit bff7633337
3 changed files with 91 additions and 52 deletions

View File

@ -2,15 +2,16 @@
/*
* Copyright 2019 Google LLC
*/
#include <linux/gfp.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/ktime.h>
#include <linux/mm.h>
#include <linux/workqueue.h>
#include <linux/lz4.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 "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,
INCFS_DATA_FILE_BLOCK_SIZE);
ssize_t read_res = incfs_kread(bf, buf,
INCFS_DATA_FILE_BLOCK_SIZE, hash_block_off);
loff_t lvl_off = tree->hash_level_suboffset[lvl];
if (read_res < 0)
return read_res;
if (read_res != INCFS_DATA_FILE_BLOCK_SIZE)
hash_block_offset[lvl] =
lvl_off + round_down(hash_block_index * digest_size,
INCFS_DATA_FILE_BLOCK_SIZE);
hash_offset_in_block[lvl] = hash_block_index * digest_size %
INCFS_DATA_FILE_BLOCK_SIZE;
hash_block_index /= hash_per_block;
}
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);
if (res)
return res;
hash_block_index /= hash_per_block;
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);
}
}
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);
res = incfs_calc_digest(tree->alg, data,
range(calculated_digest, digest_size));
if (res)
return res;
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;

View File

@ -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);

View File

@ -816,7 +816,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));