[PATCH 10/13] fsverity: pass the zero-hash value to the implementation

Darrick J. Wong
2024-03-30T00:35:17Z

From: Darrick J. Wong <djwong@kernel.org>

Compute the hash of a data block full of zeros, and then supply this to
the merkle tree read and write methods. A subsequent xfs patch will use
this to reduce the size of the merkle tree when dealing with sparse gold
master disk images and the like.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/verity/enable.c | 2 ++
fs/verity/fsverity_private.h | 2 ++
fs/verity/open.c | 7 +++++++
fs/verity/verify.c | 2 ++
include/linux/fsverity.h | 8 ++++++++
5 files changed, 21 insertions(+)

diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index 233b20fb12ff5..8c6fe4b72b14e 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -52,6 +52,8 @@ static int write_merkle_tree_block(struct inode *inode, const u8 *buf,
{
struct fsverity_writemerkle req = {
.inode = inode,
+ .zero_digest = params->zero_digest,
+ .digest_size = params->digest_size,
};
u64 pos = (u64)index << params->log_blocksize;
int err;
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index de8798f141d4a..195a92f203bba 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -47,6 +47,8 @@ struct merkle_tree_params {
u64 tree_size; /* Merkle tree size in bytes */
unsigned long tree_pages; /* Merkle tree size in pages */

+ u8 zero_digest[FS_VERITY_MAX_DIGEST_SIZE]; /* hash of zeroed data block */
+
/*
* Starting block index for each tree level, ordered from leaf level (0)
* to root level ('num_levels - 1')
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 7a86407732c41..cdf694a261605 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -144,6 +144,13 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
goto out_err;
}

+ err = fsverity_hash_block(params, inode, page_address(ZERO_PAGE(0)),
+ params->zero_digest);
+ if (err) {
+ fsverity_err(inode, "Error %d computing zero digest", err);
+ goto out_err;
+ }
+
params->tree_size = offset << log_blocksize;
params->tree_pages = PAGE_ALIGN(params->tree_size) >> PAGE_SHIFT;
return 0;
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index c4ebf85ba2c79..99b1529bbb50b 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -432,6 +432,8 @@ int fsverity_read_merkle_tree_block(struct inode *inode,
.num_levels = params->num_levels,
.log_blocksize = params->log_blocksize,
.ra_bytes = ra_bytes,
+ .zero_digest = params->zero_digest,
+ .digest_size = params->digest_size,
};

block->verified = false;
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index 5dacd30d65353..761a0b76eefec 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -66,6 +66,8 @@ struct fsverity_blockbuf {
* if the page at @block->offset isn't already cached.
* Implementations may ignore this argument; it's only a
* performance optimization.
+ * @zero_digest: the hash for a data block of zeroes
+ * @digest_size: size of zero_digest
*/
struct fsverity_readmerkle {
struct inode *inode;
@@ -73,6 +75,8 @@ struct fsverity_readmerkle {
int level;
int num_levels;
u8 log_blocksize;
+ const u8 *zero_digest;
+ unsigned int digest_size;
};

/**
@@ -81,12 +85,16 @@ struct fsverity_readmerkle {
* @level: level of the block; level 0 are the leaves
* @num_levels: number of levels in the tree total
* @log_blocksize: log2 of the size of the block
+ * @zero_digest: the hash for a data block of zeroes
+ * @digest_size: size of zero_digest
*/
struct fsverity_writemerkle {
struct inode *inode;
int level;
int num_levels;
u8 log_blocksize;
+ const u8 *zero_digest;
+ unsigned int digest_size;
};

/* Verity operations for filesystems */