Project import generated by Copybara.
GitOrigin-RevId: 0acb173e07e155358594d3ce7b5f19e91bb9fec3
diff --git a/fs/verity/Kconfig b/fs/verity/Kconfig
index 24d1b54..88fb251 100644
--- a/fs/verity/Kconfig
+++ b/fs/verity/Kconfig
@@ -3,13 +3,9 @@
config FS_VERITY
bool "FS Verity (read-only file-based authenticity protection)"
select CRYPTO
- # SHA-256 is implied as it's intended to be the default hash algorithm.
+ # SHA-256 is selected as it's intended to be the default hash algorithm.
# To avoid bloat, other wanted algorithms must be selected explicitly.
- # Note that CRYPTO_SHA256 denotes the generic C implementation, but
- # some architectures provided optimized implementations of the same
- # algorithm that may be used instead. In this case, CRYPTO_SHA256 may
- # be omitted even if SHA-256 is being used.
- imply CRYPTO_SHA256
+ select CRYPTO_SHA256
help
This option enables fs-verity. fs-verity is the dm-verity
mechanism implemented at the file level. On supported
diff --git a/fs/verity/Makefile b/fs/verity/Makefile
index 435559a..570e913 100644
--- a/fs/verity/Makefile
+++ b/fs/verity/Makefile
@@ -5,7 +5,6 @@
init.o \
measure.o \
open.o \
- read_metadata.o \
verify.o
obj-$(CONFIG_FS_VERITY_BUILTIN_SIGNATURES) += signature.o
diff --git a/fs/verity/enable.c b/fs/verity/enable.c
index d988e8c..eabc6ac 100644
--- a/fs/verity/enable.c
+++ b/fs/verity/enable.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Ioctl to enable verity on a file
+ * fs/verity/enable.c: ioctl to enable verity on a file
*
* Copyright 2019 Google LLC
*/
@@ -8,48 +8,18 @@
#include "fsverity_private.h"
#include <crypto/hash.h>
-#include <linux/backing-dev.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>
-/*
- * Read a file data page for Merkle tree construction. Do aggressive readahead,
- * since we're sequentially reading the entire file.
- */
-static struct page *read_file_data_page(struct file *filp, pgoff_t index,
- struct file_ra_state *ra,
- unsigned long remaining_pages)
-{
- struct page *page;
-
- page = find_get_page_flags(filp->f_mapping, index, FGP_ACCESSED);
- if (!page || !PageUptodate(page)) {
- if (page)
- put_page(page);
- else
- page_cache_sync_readahead(filp->f_mapping, ra, filp,
- index, remaining_pages);
- page = read_mapping_page(filp->f_mapping, index, NULL);
- if (IS_ERR(page))
- return page;
- }
- if (PageReadahead(page))
- page_cache_async_readahead(filp->f_mapping, ra, filp, page,
- index, remaining_pages);
- return page;
-}
-
-static int build_merkle_tree_level(struct file *filp, unsigned int level,
+static int build_merkle_tree_level(struct inode *inode, unsigned int level,
u64 num_blocks_to_hash,
const struct merkle_tree_params *params,
u8 *pending_hashes,
struct ahash_request *req)
{
- struct inode *inode = file_inode(filp);
const struct fsverity_operations *vops = inode->i_sb->s_vop;
- struct file_ra_state ra = { 0 };
unsigned int pending_size = 0;
u64 dst_block_num;
u64 i;
@@ -66,8 +36,6 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level,
dst_block_num = 0; /* unused */
}
- file_ra_state_init(&ra, filp->f_mapping);
-
for (i = 0; i < num_blocks_to_hash; i++) {
struct page *src_page;
@@ -77,8 +45,7 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level,
if (level == 0) {
/* Leaf: hashing a data block */
- src_page = read_file_data_page(filp, i, &ra,
- num_blocks_to_hash - i);
+ src_page = read_mapping_page(inode->i_mapping, i, NULL);
if (IS_ERR(src_page)) {
err = PTR_ERR(src_page);
fsverity_err(inode,
@@ -87,14 +54,9 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level,
return err;
}
} else {
- unsigned long num_ra_pages =
- min_t(unsigned long, num_blocks_to_hash - i,
- inode->i_sb->s_bdi->io_pages);
-
/* Non-leaf: hashing hash block from level below */
src_page = vops->read_merkle_tree_page(inode,
- params->level_start[level - 1] + i,
- num_ra_pages);
+ params->level_start[level - 1] + i);
if (IS_ERR(src_page)) {
err = PTR_ERR(src_page);
fsverity_err(inode,
@@ -141,18 +103,17 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level,
}
/*
- * Build the Merkle tree for the given file using the given parameters, and
+ * Build the Merkle tree for the given inode using the given parameters, and
* return the root hash in @root_hash.
*
* The tree is written to a filesystem-specific location as determined by the
* ->write_merkle_tree_block() method. However, the blocks that comprise the
* tree are the same for all filesystems.
*/
-static int build_merkle_tree(struct file *filp,
+static int build_merkle_tree(struct inode *inode,
const struct merkle_tree_params *params,
u8 *root_hash)
{
- struct inode *inode = file_inode(filp);
u8 *pending_hashes;
struct ahash_request *req;
u64 blocks;
@@ -165,11 +126,9 @@ static int build_merkle_tree(struct file *filp,
return 0;
}
- /* This allocation never fails, since it's mempool-backed. */
- req = fsverity_alloc_hash_request(params->hash_alg, GFP_KERNEL);
-
pending_hashes = kmalloc(params->block_size, GFP_KERNEL);
- if (!pending_hashes)
+ req = ahash_request_alloc(params->hash_alg->tfm, GFP_KERNEL);
+ if (!pending_hashes || !req)
goto out;
/*
@@ -177,10 +136,10 @@ static int build_merkle_tree(struct file *filp,
* (level 0) and ascending to the root node (level 'num_levels - 1').
* Then at the end (level 'num_levels'), calculate the root hash.
*/
- blocks = ((u64)inode->i_size + params->block_size - 1) >>
+ blocks = (inode->i_size + params->block_size - 1) >>
params->log_blocksize;
for (level = 0; level <= params->num_levels; level++) {
- err = build_merkle_tree_level(filp, level, blocks, params,
+ err = build_merkle_tree_level(inode, level, blocks, params,
pending_hashes, req);
if (err)
goto out;
@@ -191,7 +150,7 @@ static int build_merkle_tree(struct file *filp,
err = 0;
out:
kfree(pending_hashes);
- fsverity_free_hash_request(params->hash_alg, req);
+ ahash_request_free(req);
return err;
}
@@ -216,7 +175,8 @@ static int enable_verity(struct file *filp,
/* Get the salt if the user provided one */
if (arg->salt_size &&
- copy_from_user(desc->salt, u64_to_user_ptr(arg->salt_ptr),
+ copy_from_user(desc->salt,
+ (const u8 __user *)(uintptr_t)arg->salt_ptr,
arg->salt_size)) {
err = -EFAULT;
goto out;
@@ -225,7 +185,8 @@ static int enable_verity(struct file *filp,
/* Get the signature if the user provided one */
if (arg->sig_size &&
- copy_from_user(desc->signature, u64_to_user_ptr(arg->sig_ptr),
+ copy_from_user(desc->signature,
+ (const u8 __user *)(uintptr_t)arg->sig_ptr,
arg->sig_size)) {
err = -EFAULT;
goto out;
@@ -266,7 +227,7 @@ static int enable_verity(struct file *filp,
*/
pr_debug("Building Merkle tree...\n");
BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE);
- err = build_merkle_tree(filp, ¶ms, desc->root_hash);
+ err = build_merkle_tree(inode, ¶ms, desc->root_hash);
if (err) {
fsverity_err(inode, "Error %d building Merkle tree", err);
goto rollback;
@@ -329,8 +290,6 @@ static int enable_verity(struct file *filp,
/**
* fsverity_ioctl_enable() - enable verity on a file
- * @filp: file to enable verity on
- * @uarg: user pointer to fsverity_enable_arg
*
* Enable fs-verity on a file. See the "FS_IOC_ENABLE_VERITY" section of
* Documentation/filesystems/fsverity.rst for the documentation.
@@ -398,9 +357,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg)
* Some pages of the file may have been evicted from pagecache after
* being used in the Merkle tree construction, then read into pagecache
* again by another process reading from the file concurrently. Since
- * these pages didn't undergo verification against the file digest which
- * fs-verity now claims to be enforcing, we have to wipe the pagecache
- * to ensure that all future reads are verified.
+ * these pages didn't undergo verification against the file measurement
+ * which fs-verity now claims to be enforcing, we have to wipe the
+ * pagecache to ensure that all future reads are verified.
*/
filemap_write_and_wait(inode->i_mapping);
invalidate_inode_pages2(inode->i_mapping);
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h
index a275cad..e74c79b 100644
--- a/fs/verity/fsverity_private.h
+++ b/fs/verity/fsverity_private.h
@@ -16,7 +16,6 @@
#include <crypto/sha.h>
#include <linux/fsverity.h>
-#include <linux/mempool.h>
struct ahash_request;
@@ -38,12 +37,11 @@ struct fsverity_hash_alg {
const char *name; /* crypto API name, e.g. sha256 */
unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */
unsigned int block_size; /* block size in bytes, e.g. 64 for SHA-256 */
- mempool_t req_pool; /* mempool with a preallocated hash request */
};
/* Merkle tree parameters: hash algorithm, initial hash state, and topology */
struct merkle_tree_params {
- struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
+ const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
const u8 *hashstate; /* initial hash state or NULL */
unsigned int digest_size; /* same as hash_alg->digest_size */
unsigned int block_size; /* size of data and tree blocks */
@@ -52,7 +50,6 @@ struct merkle_tree_params {
unsigned int log_arity; /* log2(hashes_per_block) */
unsigned int num_levels; /* number of levels in Merkle tree */
u64 tree_size; /* Merkle tree size in bytes */
- unsigned long level0_blocks; /* number of blocks in tree level 0 */
/*
* Starting block index for each tree level, ordered from leaf level (0)
@@ -61,50 +58,76 @@ struct merkle_tree_params {
u64 level_start[FS_VERITY_MAX_LEVELS];
};
-/*
+/**
* fsverity_info - cached verity metadata for an inode
*
* When a verity file is first opened, an instance of this struct is allocated
* and stored in ->i_verity_info; it remains until the inode is evicted. It
* caches information about the Merkle tree that's needed to efficiently verify
- * data read from the file. It also caches the file digest. The Merkle tree
- * pages themselves are not cached here, but the filesystem may cache them.
+ * data read from the file. It also caches the file measurement. The Merkle
+ * tree pages themselves are not cached here, but the filesystem may cache them.
*/
struct fsverity_info {
struct merkle_tree_params tree_params;
u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE];
- u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE];
+ u8 measurement[FS_VERITY_MAX_DIGEST_SIZE];
const struct inode *inode;
};
+/*
+ * Merkle tree properties. The file measurement is the hash of this structure
+ * excluding the signature and with the sig_size field set to 0.
+ */
+struct fsverity_descriptor {
+ __u8 version; /* must be 1 */
+ __u8 hash_algorithm; /* Merkle tree hash algorithm */
+ __u8 log_blocksize; /* log2 of size of data and tree blocks */
+ __u8 salt_size; /* size of salt in bytes; 0 if none */
+ __le32 sig_size; /* size of signature in bytes; 0 if none */
+ __le64 data_size; /* size of file the Merkle tree is built over */
+ __u8 root_hash[64]; /* Merkle tree root hash */
+ __u8 salt[32]; /* salt prepended to each hashed block */
+ __u8 __reserved[144]; /* must be 0's */
+ __u8 signature[]; /* optional PKCS#7 signature */
+};
+
/* Arbitrary limit to bound the kmalloc() size. Can be changed. */
#define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384
#define FS_VERITY_MAX_SIGNATURE_SIZE (FS_VERITY_MAX_DESCRIPTOR_SIZE - \
sizeof(struct fsverity_descriptor))
+/*
+ * Format in which verity file measurements are signed. This is the same as
+ * 'struct fsverity_digest', except here some magic bytes are prepended to
+ * provide some context about what is being signed in case the same key is used
+ * for non-fsverity purposes, and here the fields have fixed endianness.
+ */
+struct fsverity_signed_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
+
/* hash_algs.c */
extern struct fsverity_hash_alg fsverity_hash_algs[];
-struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
- unsigned int num);
-struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
- gfp_t gfp_flags);
-void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
- struct ahash_request *req);
-const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
+const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
+ unsigned int num);
+const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
const u8 *salt, size_t salt_size);
int fsverity_hash_page(const struct merkle_tree_params *params,
const struct inode *inode,
struct ahash_request *req, struct page *page, u8 *out);
-int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
+int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
const void *data, size_t size, u8 *out);
void __init fsverity_check_hash_algs(void);
/* init.c */
-void __printf(3, 4) __cold
+extern void __printf(3, 4) __cold
fsverity_msg(const struct inode *inode, const char *level,
const char *fmt, ...);
@@ -122,17 +145,12 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
const u8 *salt, size_t salt_size);
struct fsverity_info *fsverity_create_info(const struct inode *inode,
- struct fsverity_descriptor *desc,
- size_t desc_size);
+ void *desc, size_t desc_size);
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
void fsverity_free_info(struct fsverity_info *vi);
-int fsverity_get_descriptor(struct inode *inode,
- struct fsverity_descriptor **desc_ret,
- size_t *desc_size_ret);
-
int __init fsverity_init_info_cache(void);
void __init fsverity_exit_info_cache(void);
@@ -140,13 +158,15 @@ void __init fsverity_exit_info_cache(void);
#ifdef CONFIG_FS_VERITY_BUILTIN_SIGNATURES
int fsverity_verify_signature(const struct fsverity_info *vi,
- const u8 *signature, size_t sig_size);
+ const struct fsverity_descriptor *desc,
+ size_t desc_size);
int __init fsverity_init_signature(void);
#else /* !CONFIG_FS_VERITY_BUILTIN_SIGNATURES */
static inline int
fsverity_verify_signature(const struct fsverity_info *vi,
- const u8 *signature, size_t sig_size)
+ const struct fsverity_descriptor *desc,
+ size_t desc_size)
{
return 0;
}
diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c
index 71d0fcc..31e6d7d 100644
--- a/fs/verity/hash_algs.c
+++ b/fs/verity/hash_algs.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * fs-verity hash algorithms
+ * fs/verity/hash_algs.c: fs-verity hash algorithms
*
* Copyright 2019 Google LLC
*/
@@ -24,8 +24,6 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
},
};
-static DEFINE_MUTEX(fsverity_hash_alg_init_mutex);
-
/**
* fsverity_get_hash_alg() - validate and prepare a hash algorithm
* @inode: optional inode for logging purposes
@@ -38,8 +36,8 @@ static DEFINE_MUTEX(fsverity_hash_alg_init_mutex);
*
* Return: pointer to the hash alg on success, else an ERR_PTR()
*/
-struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
- unsigned int num)
+const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
+ unsigned int num)
{
struct fsverity_hash_alg *alg;
struct crypto_ahash *tfm;
@@ -52,15 +50,10 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
}
alg = &fsverity_hash_algs[num];
- /* pairs with smp_store_release() below */
- if (likely(smp_load_acquire(&alg->tfm) != NULL))
+ /* pairs with cmpxchg() below */
+ tfm = READ_ONCE(alg->tfm);
+ if (likely(tfm != NULL))
return alg;
-
- mutex_lock(&fsverity_hash_alg_init_mutex);
-
- if (alg->tfm != NULL)
- goto out_unlock;
-
/*
* Using the shash API would make things a bit simpler, but the ahash
* API is preferable as it allows the use of crypto accelerators.
@@ -71,14 +64,12 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
fsverity_warn(inode,
"Missing crypto API support for hash algorithm \"%s\"",
alg->name);
- alg = ERR_PTR(-ENOPKG);
- goto out_unlock;
+ return ERR_PTR(-ENOPKG);
}
fsverity_err(inode,
"Error allocating hash algorithm \"%s\": %ld",
alg->name, PTR_ERR(tfm));
- alg = ERR_CAST(tfm);
- goto out_unlock;
+ return ERR_CAST(tfm);
}
err = -EINVAL;
@@ -87,61 +78,18 @@ struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
if (WARN_ON(alg->block_size != crypto_ahash_blocksize(tfm)))
goto err_free_tfm;
- err = mempool_init_kmalloc_pool(&alg->req_pool, 1,
- sizeof(struct ahash_request) +
- crypto_ahash_reqsize(tfm));
- if (err)
- goto err_free_tfm;
-
pr_info("%s using implementation \"%s\"\n",
alg->name, crypto_ahash_driver_name(tfm));
- /* pairs with smp_load_acquire() above */
- smp_store_release(&alg->tfm, tfm);
- goto out_unlock;
+ /* pairs with READ_ONCE() above */
+ if (cmpxchg(&alg->tfm, NULL, tfm) != NULL)
+ crypto_free_ahash(tfm);
+
+ return alg;
err_free_tfm:
crypto_free_ahash(tfm);
- alg = ERR_PTR(err);
-out_unlock:
- mutex_unlock(&fsverity_hash_alg_init_mutex);
- return alg;
-}
-
-/**
- * fsverity_alloc_hash_request() - allocate a hash request object
- * @alg: the hash algorithm for which to allocate the request
- * @gfp_flags: memory allocation flags
- *
- * This is mempool-backed, so this never fails if __GFP_DIRECT_RECLAIM is set in
- * @gfp_flags. However, in that case this might need to wait for all
- * previously-allocated requests to be freed. So to avoid deadlocks, callers
- * must never need multiple requests at a time to make forward progress.
- *
- * Return: the request object on success; NULL on failure (but see above)
- */
-struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
- gfp_t gfp_flags)
-{
- struct ahash_request *req = mempool_alloc(&alg->req_pool, gfp_flags);
-
- if (req)
- ahash_request_set_tfm(req, alg->tfm);
- return req;
-}
-
-/**
- * fsverity_free_hash_request() - free a hash request object
- * @alg: the hash algorithm
- * @req: the hash request object to free
- */
-void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
- struct ahash_request *req)
-{
- if (req) {
- ahash_request_zero(req);
- mempool_free(req, &alg->req_pool);
- }
+ return ERR_PTR(err);
}
/**
@@ -153,7 +101,7 @@ void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
* Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed
* initial hash state on success or an ERR_PTR() on failure.
*/
-const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
+const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
const u8 *salt, size_t salt_size)
{
u8 *hashstate = NULL;
@@ -171,8 +119,11 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
if (!hashstate)
return ERR_PTR(-ENOMEM);
- /* This allocation never fails, since it's mempool-backed. */
- req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
+ req = ahash_request_alloc(alg->tfm, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ goto err_free;
+ }
/*
* Zero-pad the salt to the next multiple of the input size of the hash
@@ -207,7 +158,7 @@ const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
if (err)
goto err_free;
out:
- fsverity_free_hash_request(alg, req);
+ ahash_request_free(req);
kfree(padded_salt);
return hashstate;
@@ -278,7 +229,7 @@ int fsverity_hash_page(const struct merkle_tree_params *params,
*
* Return: 0 on success, -errno on failure
*/
-int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
+int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
const void *data, size_t size, u8 *out)
{
struct ahash_request *req;
@@ -286,8 +237,9 @@ int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
DECLARE_CRYPTO_WAIT(wait);
int err;
- /* This allocation never fails, since it's mempool-backed. */
- req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
+ req = ahash_request_alloc(alg->tfm, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
sg_init_one(&sg, data, size);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
@@ -297,7 +249,7 @@ int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
err = crypto_wait_req(crypto_ahash_digest(req), &wait);
- fsverity_free_hash_request(alg, req);
+ ahash_request_free(req);
return err;
}
diff --git a/fs/verity/init.c b/fs/verity/init.c
index c98b701..94c104e 100644
--- a/fs/verity/init.c
+++ b/fs/verity/init.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * fs-verity module initialization and logging
+ * fs/verity/init.c: fs-verity module initialization and logging
*
* Copyright 2019 Google LLC
*/
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index f0d7b30..05049b6 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Ioctl to get a verity file's digest
+ * fs/verity/measure.c: ioctl to get a verity file's measurement
*
* Copyright 2019 Google LLC
*/
@@ -10,12 +10,10 @@
#include <linux/uaccess.h>
/**
- * fsverity_ioctl_measure() - get a verity file's digest
- * @filp: file to get digest of
- * @_uarg: user pointer to fsverity_digest
+ * fsverity_ioctl_measure() - get a verity file's measurement
*
- * Retrieve the file digest that the kernel is enforcing for reads from a verity
- * file. See the "FS_IOC_MEASURE_VERITY" section of
+ * Retrieve the file measurement that the kernel is enforcing for reads from a
+ * verity file. See the "FS_IOC_MEASURE_VERITY" section of
* Documentation/filesystems/fsverity.rst for the documentation.
*
* Return: 0 on success, -errno on failure
@@ -51,7 +49,7 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg)
if (copy_to_user(uarg, &arg, sizeof(arg)))
return -EFAULT;
- if (copy_to_user(uarg->digest, vi->file_digest, hash_alg->digest_size))
+ if (copy_to_user(uarg->digest, vi->measurement, hash_alg->digest_size))
return -EFAULT;
return 0;
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 92df87f..63d1004 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Opening fs-verity files
+ * fs/verity/open.c: opening fs-verity files
*
* Copyright 2019 Google LLC
*/
@@ -31,7 +31,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
unsigned int log_blocksize,
const u8 *salt, size_t salt_size)
{
- struct fsverity_hash_alg *hash_alg;
+ const struct fsverity_hash_alg *hash_alg;
int err;
u64 blocks;
u64 offset;
@@ -89,7 +89,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
*/
/* Compute number of levels and the number of blocks in each level */
- blocks = ((u64)inode->i_size + params->block_size - 1) >> log_blocksize;
+ blocks = (inode->i_size + params->block_size - 1) >> log_blocksize;
pr_debug("Data is %lld bytes (%llu blocks)\n", inode->i_size, blocks);
while (blocks > 1) {
if (params->num_levels >= FS_VERITY_MAX_LEVELS) {
@@ -102,7 +102,6 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
/* temporarily using level_start[] to store blocks in level */
params->level_start[params->num_levels++] = blocks;
}
- params->level0_blocks = params->level_start[0];
/* Compute the starting block of each level */
offset = 0;
@@ -124,35 +123,63 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
}
/*
- * Compute the file digest by hashing the fsverity_descriptor excluding the
+ * Compute the file measurement by hashing the fsverity_descriptor excluding the
* signature and with the sig_size field set to 0.
*/
-static int compute_file_digest(struct fsverity_hash_alg *hash_alg,
- struct fsverity_descriptor *desc,
- u8 *file_digest)
+static int compute_file_measurement(const struct fsverity_hash_alg *hash_alg,
+ struct fsverity_descriptor *desc,
+ u8 *measurement)
{
__le32 sig_size = desc->sig_size;
int err;
desc->sig_size = 0;
- err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), file_digest);
+ err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), measurement);
desc->sig_size = sig_size;
return err;
}
/*
- * Create a new fsverity_info from the given fsverity_descriptor (with optional
- * appended signature), and check the signature if present. The
- * fsverity_descriptor must have already undergone basic validation.
+ * Validate the given fsverity_descriptor and create a new fsverity_info from
+ * it. The signature (if present) is also checked.
*/
struct fsverity_info *fsverity_create_info(const struct inode *inode,
- struct fsverity_descriptor *desc,
- size_t desc_size)
+ void *_desc, size_t desc_size)
{
+ struct fsverity_descriptor *desc = _desc;
struct fsverity_info *vi;
int err;
+ if (desc_size < sizeof(*desc)) {
+ fsverity_err(inode, "Unrecognized descriptor size: %zu bytes",
+ desc_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (desc->version != 1) {
+ fsverity_err(inode, "Unrecognized descriptor version: %u",
+ desc->version);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) {
+ fsverity_err(inode, "Reserved bits set in descriptor");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (desc->salt_size > sizeof(desc->salt)) {
+ fsverity_err(inode, "Invalid salt_size: %u", desc->salt_size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (le64_to_cpu(desc->data_size) != inode->i_size) {
+ fsverity_err(inode,
+ "Wrong data_size: %llu (desc) != %lld (inode)",
+ le64_to_cpu(desc->data_size), inode->i_size);
+ return ERR_PTR(-EINVAL);
+ }
+
vi = kmem_cache_zalloc(fsverity_info_cachep, GFP_KERNEL);
if (!vi)
return ERR_PTR(-ENOMEM);
@@ -171,18 +198,17 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
memcpy(vi->root_hash, desc->root_hash, vi->tree_params.digest_size);
- err = compute_file_digest(vi->tree_params.hash_alg, desc,
- vi->file_digest);
+ err = compute_file_measurement(vi->tree_params.hash_alg, desc,
+ vi->measurement);
if (err) {
- fsverity_err(inode, "Error %d computing file digest", err);
+ fsverity_err(inode, "Error %d computing file measurement", err);
goto out;
}
- pr_debug("Computed file digest: %s:%*phN\n",
+ pr_debug("Computed file measurement: %s:%*phN\n",
vi->tree_params.hash_alg->name,
- vi->tree_params.digest_size, vi->file_digest);
+ vi->tree_params.digest_size, vi->measurement);
- err = fsverity_verify_signature(vi, desc->signature,
- le32_to_cpu(desc->sig_size));
+ err = fsverity_verify_signature(vi, desc, desc_size);
out:
if (err) {
fsverity_free_info(vi);
@@ -194,20 +220,11 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode,
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi)
{
/*
- * Multiple tasks may race to set ->i_verity_info, so use
- * cmpxchg_release(). This pairs with the smp_load_acquire() in
- * fsverity_get_info(). I.e., here we publish ->i_verity_info with a
- * RELEASE barrier so that other tasks can ACQUIRE it.
+ * Multiple processes may race to set ->i_verity_info, so use cmpxchg.
+ * This pairs with the READ_ONCE() in fsverity_get_info().
*/
- if (cmpxchg_release(&inode->i_verity_info, NULL, vi) != NULL) {
- /* Lost the race, so free the fsverity_info we allocated. */
+ if (cmpxchg(&inode->i_verity_info, NULL, vi) != NULL)
fsverity_free_info(vi);
- /*
- * Afterwards, the caller may access ->i_verity_info directly,
- * so make sure to ACQUIRE the winning fsverity_info.
- */
- (void)fsverity_get_info(inode);
- }
}
void fsverity_free_info(struct fsverity_info *vi)
@@ -218,57 +235,15 @@ void fsverity_free_info(struct fsverity_info *vi)
kmem_cache_free(fsverity_info_cachep, vi);
}
-static bool validate_fsverity_descriptor(struct inode *inode,
- const struct fsverity_descriptor *desc,
- size_t desc_size)
+/* Ensure the inode has an ->i_verity_info */
+static int ensure_verity_info(struct inode *inode)
{
- if (desc_size < sizeof(*desc)) {
- fsverity_err(inode, "Unrecognized descriptor size: %zu bytes",
- desc_size);
- return false;
- }
-
- if (desc->version != 1) {
- fsverity_err(inode, "Unrecognized descriptor version: %u",
- desc->version);
- return false;
- }
-
- if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) {
- fsverity_err(inode, "Reserved bits set in descriptor");
- return false;
- }
-
- if (desc->salt_size > sizeof(desc->salt)) {
- fsverity_err(inode, "Invalid salt_size: %u", desc->salt_size);
- return false;
- }
-
- if (le64_to_cpu(desc->data_size) != inode->i_size) {
- fsverity_err(inode,
- "Wrong data_size: %llu (desc) != %lld (inode)",
- le64_to_cpu(desc->data_size), inode->i_size);
- return false;
- }
-
- if (le32_to_cpu(desc->sig_size) > desc_size - sizeof(*desc)) {
- fsverity_err(inode, "Signature overflows verity descriptor");
- return false;
- }
-
- return true;
-}
-
-/*
- * Read the inode's fsverity_descriptor (with optional appended signature) from
- * the filesystem, and do basic validation of it.
- */
-int fsverity_get_descriptor(struct inode *inode,
- struct fsverity_descriptor **desc_ret,
- size_t *desc_size_ret)
-{
- int res;
+ struct fsverity_info *vi = fsverity_get_info(inode);
struct fsverity_descriptor *desc;
+ int res;
+
+ if (vi)
+ return 0;
res = inode->i_sb->s_vop->get_verity_descriptor(inode, NULL, 0);
if (res < 0) {
@@ -287,46 +262,20 @@ int fsverity_get_descriptor(struct inode *inode,
res = inode->i_sb->s_vop->get_verity_descriptor(inode, desc, res);
if (res < 0) {
fsverity_err(inode, "Error %d reading verity descriptor", res);
- kfree(desc);
- return res;
+ goto out_free_desc;
}
- if (!validate_fsverity_descriptor(inode, desc, res)) {
- kfree(desc);
- return -EINVAL;
- }
-
- *desc_ret = desc;
- *desc_size_ret = res;
- return 0;
-}
-
-/* Ensure the inode has an ->i_verity_info */
-static int ensure_verity_info(struct inode *inode)
-{
- struct fsverity_info *vi = fsverity_get_info(inode);
- struct fsverity_descriptor *desc;
- size_t desc_size;
- int err;
-
- if (vi)
- return 0;
-
- err = fsverity_get_descriptor(inode, &desc, &desc_size);
- if (err)
- return err;
-
- vi = fsverity_create_info(inode, desc, desc_size);
+ vi = fsverity_create_info(inode, desc, res);
if (IS_ERR(vi)) {
- err = PTR_ERR(vi);
+ res = PTR_ERR(vi);
goto out_free_desc;
}
fsverity_set_info(inode, vi);
- err = 0;
+ res = 0;
out_free_desc:
kfree(desc);
- return err;
+ return res;
}
/**
@@ -380,7 +329,6 @@ EXPORT_SYMBOL_GPL(fsverity_prepare_setattr);
/**
* fsverity_cleanup_inode() - free the inode's verity info, if present
- * @inode: an inode being evicted
*
* Filesystems must call this on inode eviction to free ->i_verity_info.
*/
@@ -395,7 +343,7 @@ int __init fsverity_init_info_cache(void)
{
fsverity_info_cachep = KMEM_CACHE_USERCOPY(fsverity_info,
SLAB_RECLAIM_ACCOUNT,
- file_digest);
+ measurement);
if (!fsverity_info_cachep)
return -ENOMEM;
return 0;
diff --git a/fs/verity/read_metadata.c b/fs/verity/read_metadata.c
deleted file mode 100644
index 7e2d0c7..0000000
--- a/fs/verity/read_metadata.c
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Ioctl to read verity metadata
- *
- * Copyright 2021 Google LLC
- */
-
-#include "fsverity_private.h"
-
-#include <linux/backing-dev.h>
-#include <linux/highmem.h>
-#include <linux/sched/signal.h>
-#include <linux/uaccess.h>
-
-static int fsverity_read_merkle_tree(struct inode *inode,
- const struct fsverity_info *vi,
- void __user *buf, u64 offset, int length)
-{
- const struct fsverity_operations *vops = inode->i_sb->s_vop;
- u64 end_offset;
- unsigned int offs_in_page;
- pgoff_t index, last_index;
- int retval = 0;
- int err = 0;
-
- end_offset = min(offset + length, vi->tree_params.tree_size);
- if (offset >= end_offset)
- return 0;
- offs_in_page = offset_in_page(offset);
- last_index = (end_offset - 1) >> PAGE_SHIFT;
-
- /*
- * Iterate through each Merkle tree page in the requested range and copy
- * the requested portion to userspace. Note that the Merkle tree block
- * size isn't important here, as we are returning a byte stream; i.e.,
- * we can just work with pages even if the tree block size != PAGE_SIZE.
- */
- for (index = offset >> PAGE_SHIFT; index <= last_index; index++) {
- unsigned long num_ra_pages =
- min_t(unsigned long, last_index - index + 1,
- inode->i_sb->s_bdi->io_pages);
- unsigned int bytes_to_copy = min_t(u64, end_offset - offset,
- PAGE_SIZE - offs_in_page);
- struct page *page;
- const void *virt;
-
- page = vops->read_merkle_tree_page(inode, index, num_ra_pages);
- if (IS_ERR(page)) {
- err = PTR_ERR(page);
- fsverity_err(inode,
- "Error %d reading Merkle tree page %lu",
- err, index);
- break;
- }
-
- virt = kmap(page);
- if (copy_to_user(buf, virt + offs_in_page, bytes_to_copy)) {
- kunmap(page);
- put_page(page);
- err = -EFAULT;
- break;
- }
- kunmap(page);
- put_page(page);
-
- retval += bytes_to_copy;
- buf += bytes_to_copy;
- offset += bytes_to_copy;
-
- if (fatal_signal_pending(current)) {
- err = -EINTR;
- break;
- }
- cond_resched();
- offs_in_page = 0;
- }
- return retval ? retval : err;
-}
-
-/* Copy the requested portion of the buffer to userspace. */
-static int fsverity_read_buffer(void __user *dst, u64 offset, int length,
- const void *src, size_t src_length)
-{
- if (offset >= src_length)
- return 0;
- src += offset;
- src_length -= offset;
-
- length = min_t(size_t, length, src_length);
-
- if (copy_to_user(dst, src, length))
- return -EFAULT;
-
- return length;
-}
-
-static int fsverity_read_descriptor(struct inode *inode,
- void __user *buf, u64 offset, int length)
-{
- struct fsverity_descriptor *desc;
- size_t desc_size;
- int res;
-
- res = fsverity_get_descriptor(inode, &desc, &desc_size);
- if (res)
- return res;
-
- /* don't include the signature */
- desc_size = offsetof(struct fsverity_descriptor, signature);
- desc->sig_size = 0;
-
- res = fsverity_read_buffer(buf, offset, length, desc, desc_size);
-
- kfree(desc);
- return res;
-}
-
-static int fsverity_read_signature(struct inode *inode,
- void __user *buf, u64 offset, int length)
-{
- struct fsverity_descriptor *desc;
- size_t desc_size;
- int res;
-
- res = fsverity_get_descriptor(inode, &desc, &desc_size);
- if (res)
- return res;
-
- if (desc->sig_size == 0) {
- res = -ENODATA;
- goto out;
- }
-
- /*
- * Include only the signature. Note that fsverity_get_descriptor()
- * already verified that sig_size is in-bounds.
- */
- res = fsverity_read_buffer(buf, offset, length, desc->signature,
- le32_to_cpu(desc->sig_size));
-out:
- kfree(desc);
- return res;
-}
-
-/**
- * fsverity_ioctl_read_metadata() - read verity metadata from a file
- * @filp: file to read the metadata from
- * @uarg: user pointer to fsverity_read_metadata_arg
- *
- * Return: length read on success, 0 on EOF, -errno on failure
- */
-int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg)
-{
- struct inode *inode = file_inode(filp);
- const struct fsverity_info *vi;
- struct fsverity_read_metadata_arg arg;
- int length;
- void __user *buf;
-
- vi = fsverity_get_info(inode);
- if (!vi)
- return -ENODATA; /* not a verity file */
- /*
- * Note that we don't have to explicitly check that the file is open for
- * reading, since verity files can only be opened for reading.
- */
-
- if (copy_from_user(&arg, uarg, sizeof(arg)))
- return -EFAULT;
-
- if (arg.__reserved)
- return -EINVAL;
-
- /* offset + length must not overflow. */
- if (arg.offset + arg.length < arg.offset)
- return -EINVAL;
-
- /* Ensure that the return value will fit in INT_MAX. */
- length = min_t(u64, arg.length, INT_MAX);
-
- buf = u64_to_user_ptr(arg.buf_ptr);
-
- switch (arg.metadata_type) {
- case FS_VERITY_METADATA_TYPE_MERKLE_TREE:
- return fsverity_read_merkle_tree(inode, vi, buf, arg.offset,
- length);
- case FS_VERITY_METADATA_TYPE_DESCRIPTOR:
- return fsverity_read_descriptor(inode, buf, arg.offset, length);
- case FS_VERITY_METADATA_TYPE_SIGNATURE:
- return fsverity_read_signature(inode, buf, arg.offset, length);
- default:
- return -EINVAL;
- }
-}
-EXPORT_SYMBOL_GPL(fsverity_ioctl_read_metadata);
diff --git a/fs/verity/signature.c b/fs/verity/signature.c
index e33f6c4..c8b2552 100644
--- a/fs/verity/signature.c
+++ b/fs/verity/signature.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Verification of builtin signatures
+ * fs/verity/signature.c: verification of builtin signatures
*
* Copyright 2019 Google LLC
*/
@@ -28,50 +28,22 @@ static struct key *fsverity_keyring;
/**
* fsverity_verify_signature() - check a verity file's signature
- * @vi: the file's fsverity_info
- * @signature: the file's built-in signature
- * @sig_size: size of signature in bytes, or 0 if no signature
*
- * If the file includes a signature of its fs-verity file digest, verify it
- * against the certificates in the fs-verity keyring.
+ * If the file's fs-verity descriptor includes a signature of the file
+ * measurement, verify it against the certificates in the fs-verity keyring.
*
* Return: 0 on success (signature valid or not required); -errno on failure
*/
int fsverity_verify_signature(const struct fsverity_info *vi,
- const u8 *signature, size_t sig_size)
+ const struct fsverity_descriptor *desc,
+ size_t desc_size)
{
- unsigned int digest_algorithm =
- vi->tree_params.hash_alg - fsverity_hash_algs;
-
- return __fsverity_verify_signature(vi->inode, signature, sig_size,
- vi->file_digest, digest_algorithm);
-}
-
-/**
- * __fsverity_verify_signature() - check a verity file's signature
- * @inode: the file's inode
- * @signature: the file's signature
- * @sig_size: size of @signature. Can be 0 if there is no signature
- * @file_digest: the file's digest
- * @digest_algorithm: the digest algorithm used
- *
- * Takes the file's digest and optional signature and verifies the signature
- * against the digest and the fs-verity keyring if appropriate
- *
- * Return: 0 on success (signature valid or not required); -errno on failure
- */
-int __fsverity_verify_signature(const struct inode *inode, const u8 *signature,
- size_t sig_size, const u8 *file_digest,
- unsigned int digest_algorithm)
-{
- struct fsverity_formatted_digest *d;
- struct fsverity_hash_alg *hash_alg = fsverity_get_hash_alg(inode,
- digest_algorithm);
+ const struct inode *inode = vi->inode;
+ const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg;
+ const u32 sig_size = le32_to_cpu(desc->sig_size);
+ struct fsverity_signed_digest *d;
int err;
- if (IS_ERR(hash_alg))
- return PTR_ERR(hash_alg);
-
if (sig_size == 0) {
if (fsverity_require_signatures) {
fsverity_err(inode,
@@ -81,16 +53,22 @@ int __fsverity_verify_signature(const struct inode *inode, const u8 *signature,
return 0;
}
+ if (sig_size > desc_size - sizeof(*desc)) {
+ fsverity_err(inode, "Signature overflows verity descriptor");
+ return -EBADMSG;
+ }
+
d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
if (!d)
return -ENOMEM;
memcpy(d->magic, "FSVerity", 8);
d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
d->digest_size = cpu_to_le16(hash_alg->digest_size);
- memcpy(d->digest, file_digest, hash_alg->digest_size);
+ memcpy(d->digest, vi->measurement, hash_alg->digest_size);
err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size,
- signature, sig_size, fsverity_keyring,
+ desc->signature, sig_size,
+ fsverity_keyring,
VERIFYING_UNSPECIFIED_SIGNATURE,
NULL, NULL);
kfree(d);
@@ -109,11 +87,10 @@ int __fsverity_verify_signature(const struct inode *inode, const u8 *signature,
return err;
}
- pr_debug("Valid signature for file digest %s:%*phN\n",
- hash_alg->name, hash_alg->digest_size, file_digest);
+ pr_debug("Valid signature for file measurement %s:%*phN\n",
+ hash_alg->name, hash_alg->digest_size, vi->measurement);
return 0;
}
-EXPORT_SYMBOL_GPL(__fsverity_verify_signature);
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *fsverity_sysctl_header;
diff --git a/fs/verity/verify.c b/fs/verity/verify.c
index 0adb970..3e8f2de 100644
--- a/fs/verity/verify.c
+++ b/fs/verity/verify.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Data verification functions, i.e. hooks for ->readpages()
+ * fs/verity/verify.c: data verification functions, i.e. hooks for ->readpages()
*
* Copyright 2019 Google LLC
*/
@@ -84,8 +84,7 @@ static inline int cmp_hashes(const struct fsverity_info *vi,
* Return: true if the page is valid, else false.
*/
static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
- struct ahash_request *req, struct page *data_page,
- unsigned long level0_ra_pages)
+ struct ahash_request *req, struct page *data_page)
{
const struct merkle_tree_params *params = &vi->tree_params;
const unsigned int hsize = params->digest_size;
@@ -118,8 +117,8 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
pr_debug_ratelimited("Level %d: hindex=%lu, hoffset=%u\n",
level, hindex, hoffset);
- hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode, hindex,
- level == 0 ? level0_ra_pages : 0);
+ hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode,
+ hindex);
if (IS_ERR(hpage)) {
err = PTR_ERR(hpage);
fsverity_err(inode,
@@ -179,7 +178,6 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi,
/**
* fsverity_verify_page() - verify a data page
- * @page: the page to verity
*
* Verify a page that has just been read from a verity file. The page must be a
* pagecache page that is still locked and not yet uptodate.
@@ -193,12 +191,13 @@ bool fsverity_verify_page(struct page *page)
struct ahash_request *req;
bool valid;
- /* This allocation never fails, since it's mempool-backed. */
- req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
+ req = ahash_request_alloc(vi->tree_params.hash_alg->tfm, GFP_NOFS);
+ if (unlikely(!req))
+ return false;
- valid = verify_page(inode, vi, req, page, 0);
+ valid = verify_page(inode, vi, req, page);
- fsverity_free_hash_request(vi->tree_params.hash_alg, req);
+ ahash_request_free(req);
return valid;
}
@@ -207,7 +206,6 @@ EXPORT_SYMBOL_GPL(fsverity_verify_page);
#ifdef CONFIG_BLOCK
/**
* fsverity_verify_bio() - verify a 'read' bio that has just completed
- * @bio: the bio to verify
*
* Verify a set of pages that have just been read from a verity file. The pages
* must be pagecache pages that are still locked and not yet uptodate. Pages
@@ -224,49 +222,31 @@ void fsverity_verify_bio(struct bio *bio)
{
struct inode *inode = bio_first_page_all(bio)->mapping->host;
const struct fsverity_info *vi = inode->i_verity_info;
- const struct merkle_tree_params *params = &vi->tree_params;
struct ahash_request *req;
struct bio_vec *bv;
struct bvec_iter_all iter_all;
- unsigned long max_ra_pages = 0;
- /* This allocation never fails, since it's mempool-backed. */
- req = fsverity_alloc_hash_request(params->hash_alg, GFP_NOFS);
-
- if (bio->bi_opf & REQ_RAHEAD) {
- /*
- * If this bio is for data readahead, then we also do readahead
- * of the first (largest) level of the Merkle tree. Namely,
- * when a Merkle tree page is read, we also try to piggy-back on
- * some additional pages -- up to 1/4 the number of data pages.
- *
- * This improves sequential read performance, as it greatly
- * reduces the number of I/O requests made to the Merkle tree.
- */
+ req = ahash_request_alloc(vi->tree_params.hash_alg->tfm, GFP_NOFS);
+ if (unlikely(!req)) {
bio_for_each_segment_all(bv, bio, iter_all)
- max_ra_pages++;
- max_ra_pages /= 4;
+ SetPageError(bv->bv_page);
+ return;
}
bio_for_each_segment_all(bv, bio, iter_all) {
struct page *page = bv->bv_page;
- unsigned long level0_index = page->index >> params->log_arity;
- unsigned long level0_ra_pages =
- min(max_ra_pages, params->level0_blocks - level0_index);
- if (!PageError(page) &&
- !verify_page(inode, vi, req, page, level0_ra_pages))
+ if (!PageError(page) && !verify_page(inode, vi, req, page))
SetPageError(page);
}
- fsverity_free_hash_request(params->hash_alg, req);
+ ahash_request_free(req);
}
EXPORT_SYMBOL_GPL(fsverity_verify_bio);
#endif /* CONFIG_BLOCK */
/**
* fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue
- * @work: the work to enqueue
*
* Enqueue verification work for asynchronous processing.
*/