mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
fsverity updates for 7.0
fsverity cleanups, speedup, and memory usage optimization from
Christoph Hellwig:
- Move some logic into common code
- Fix btrfs to reject truncates of fsverity files
- Improve the readahead implementation
- Store each inode's fsverity_info in a hash table instead of using a
pointer in the filesystem-specific part of the inode.
This optimizes for memory usage in the usual case where most files
don't have fsverity enabled.
- Look up the fsverity_info fewer times during verification, to
amortize the hash table overhead
-----BEGIN PGP SIGNATURE-----
iIoEABYIADIWIQSacvsUNc7UX4ntmEPzXCl4vpKOKwUCaY0nZhQcZWJpZ2dlcnNA
a2VybmVsLm9yZwAKCRDzXCl4vpKOK/AVAP9wSLEYsG3dqnNIHjIvLeK+9NC3Ni4d
m+fvT1JfuideOwEA9r2EfztusLU5iyqWJlHyxekibXItUDgYGltaYb7eXAU=
=a+To
-----END PGP SIGNATURE-----
Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux
Pull fsverity updates from Eric Biggers:
"fsverity cleanups, speedup, and memory usage optimization from
Christoph Hellwig:
- Move some logic into common code
- Fix btrfs to reject truncates of fsverity files
- Improve the readahead implementation
- Store each inode's fsverity_info in a hash table instead of using a
pointer in the filesystem-specific part of the inode.
This optimizes for memory usage in the usual case where most files
don't have fsverity enabled.
- Look up the fsverity_info fewer times during verification, to
amortize the hash table overhead"
* tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fsverity/linux:
fsverity: remove inode from fsverity_verification_ctx
fsverity: use a hashtable to find the fsverity_info
btrfs: consolidate fsverity_info lookup
f2fs: consolidate fsverity_info lookup
ext4: consolidate fsverity_info lookup
fs: consolidate fsverity_info lookup in buffer.c
fsverity: push out fsverity_info lookup
fsverity: deconstify the inode pointer in struct fsverity_info
fsverity: kick off hash readahead at data I/O submission time
ext4: move ->read_folio and ->readahead to readpage.c
readahead: push invalidate_lock out of page_cache_ra_unbounded
fsverity: don't issue readahead for non-ENOENT errors from __filemap_get_folio
fsverity: start consolidating pagecache code
fsverity: pass struct file to ->write_merkle_tree_block
f2fs: don't build the fsverity work handler for !CONFIG_FS_VERITY
ext4: don't build the fsverity work handler for !CONFIG_FS_VERITY
fs,fsverity: clear out fsverity_info from common code
fs,fsverity: reject size changes on fsverity files in setattr_prepare
This commit is contained in:
commit
997f9640c9
28 changed files with 519 additions and 437 deletions
12
fs/attr.c
12
fs/attr.c
|
|
@ -169,7 +169,17 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
* ATTR_FORCE.
|
||||
*/
|
||||
if (ia_valid & ATTR_SIZE) {
|
||||
int error = inode_newsize_ok(inode, attr->ia_size);
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Verity files are immutable, so deny truncates. This isn't
|
||||
* covered by the open-time check because sys_truncate() takes a
|
||||
* path, not an open file.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
|
||||
return -EPERM;
|
||||
|
||||
error = inode_newsize_ok(inode, attr->ia_size);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -339,10 +339,6 @@ struct btrfs_inode {
|
|||
|
||||
struct rw_semaphore i_mmap_lock;
|
||||
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
struct fsverity_info *i_verity_info;
|
||||
#endif
|
||||
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -475,25 +475,25 @@ void extent_clear_unlock_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
|
|||
end, page_ops);
|
||||
}
|
||||
|
||||
static bool btrfs_verify_folio(struct folio *folio, u64 start, u32 len)
|
||||
static bool btrfs_verify_folio(struct fsverity_info *vi, struct folio *folio,
|
||||
u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = folio_to_fs_info(folio);
|
||||
|
||||
if (!fsverity_active(folio->mapping->host) ||
|
||||
btrfs_folio_test_uptodate(fs_info, folio, start, len) ||
|
||||
start >= i_size_read(folio->mapping->host))
|
||||
if (!vi || btrfs_folio_test_uptodate(fs_info, folio, start, len))
|
||||
return true;
|
||||
return fsverity_verify_folio(folio);
|
||||
return fsverity_verify_folio(vi, folio);
|
||||
}
|
||||
|
||||
static void end_folio_read(struct folio *folio, bool uptodate, u64 start, u32 len)
|
||||
static void end_folio_read(struct fsverity_info *vi, struct folio *folio,
|
||||
bool uptodate, u64 start, u32 len)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = folio_to_fs_info(folio);
|
||||
|
||||
ASSERT(folio_pos(folio) <= start &&
|
||||
start + len <= folio_next_pos(folio));
|
||||
|
||||
if (uptodate && btrfs_verify_folio(folio, start, len))
|
||||
if (uptodate && btrfs_verify_folio(vi, folio, start, len))
|
||||
btrfs_folio_set_uptodate(fs_info, folio, start, len);
|
||||
else
|
||||
btrfs_folio_clear_uptodate(fs_info, folio, start, len);
|
||||
|
|
@ -573,14 +573,19 @@ static void begin_folio_read(struct btrfs_fs_info *fs_info, struct folio *folio)
|
|||
static void end_bbio_data_read(struct btrfs_bio *bbio)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = bbio->inode->root->fs_info;
|
||||
struct inode *inode = &bbio->inode->vfs_inode;
|
||||
struct bio *bio = &bbio->bio;
|
||||
struct fsverity_info *vi = NULL;
|
||||
struct folio_iter fi;
|
||||
|
||||
ASSERT(!bio_flagged(bio, BIO_CLONED));
|
||||
|
||||
if (bbio->file_offset < i_size_read(inode))
|
||||
vi = fsverity_get_info(inode);
|
||||
|
||||
bio_for_each_folio_all(fi, &bbio->bio) {
|
||||
bool uptodate = !bio->bi_status;
|
||||
struct folio *folio = fi.folio;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
u64 start = folio_pos(folio) + fi.offset;
|
||||
|
||||
btrfs_debug(fs_info,
|
||||
|
|
@ -615,7 +620,7 @@ static void end_bbio_data_read(struct btrfs_bio *bbio)
|
|||
}
|
||||
|
||||
/* Update page status and unlock. */
|
||||
end_folio_read(folio, uptodate, start, fi.length);
|
||||
end_folio_read(vi, folio, uptodate, start, fi.length);
|
||||
}
|
||||
bio_put(bio);
|
||||
}
|
||||
|
|
@ -990,7 +995,8 @@ static void btrfs_readahead_expand(struct readahead_control *ractl,
|
|||
* return 0 on success, otherwise return error
|
||||
*/
|
||||
static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached,
|
||||
struct btrfs_bio_ctrl *bio_ctrl)
|
||||
struct btrfs_bio_ctrl *bio_ctrl,
|
||||
struct fsverity_info *vi)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
|
||||
|
|
@ -1034,11 +1040,11 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached,
|
|||
ASSERT(IS_ALIGNED(cur, fs_info->sectorsize));
|
||||
if (cur >= last_byte) {
|
||||
folio_zero_range(folio, pg_offset, end - cur + 1);
|
||||
end_folio_read(folio, true, cur, end - cur + 1);
|
||||
end_folio_read(vi, folio, true, cur, end - cur + 1);
|
||||
break;
|
||||
}
|
||||
if (btrfs_folio_test_uptodate(fs_info, folio, cur, blocksize)) {
|
||||
end_folio_read(folio, true, cur, blocksize);
|
||||
end_folio_read(vi, folio, true, cur, blocksize);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
|
|
@ -1050,7 +1056,7 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached,
|
|||
*/
|
||||
em = get_extent_map(BTRFS_I(inode), folio, cur, locked_end - cur + 1, em_cached);
|
||||
if (IS_ERR(em)) {
|
||||
end_folio_read(folio, false, cur, end + 1 - cur);
|
||||
end_folio_read(vi, folio, false, cur, end + 1 - cur);
|
||||
return PTR_ERR(em);
|
||||
}
|
||||
extent_offset = cur - em->start;
|
||||
|
|
@ -1127,12 +1133,12 @@ static int btrfs_do_readpage(struct folio *folio, struct extent_map **em_cached,
|
|||
/* we've found a hole, just zero and go on */
|
||||
if (block_start == EXTENT_MAP_HOLE) {
|
||||
folio_zero_range(folio, pg_offset, blocksize);
|
||||
end_folio_read(folio, true, cur, blocksize);
|
||||
end_folio_read(vi, folio, true, cur, blocksize);
|
||||
continue;
|
||||
}
|
||||
/* the get_extent function already copied into the folio */
|
||||
if (block_start == EXTENT_MAP_INLINE) {
|
||||
end_folio_read(folio, true, cur, blocksize);
|
||||
end_folio_read(vi, folio, true, cur, blocksize);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1329,7 +1335,8 @@ again:
|
|||
|
||||
int btrfs_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
struct btrfs_inode *inode = folio_to_inode(folio);
|
||||
struct inode *vfs_inode = folio->mapping->host;
|
||||
struct btrfs_inode *inode = BTRFS_I(vfs_inode);
|
||||
const u64 start = folio_pos(folio);
|
||||
const u64 end = start + folio_size(folio) - 1;
|
||||
struct extent_state *cached_state = NULL;
|
||||
|
|
@ -1338,10 +1345,13 @@ int btrfs_read_folio(struct file *file, struct folio *folio)
|
|||
.last_em_start = U64_MAX,
|
||||
};
|
||||
struct extent_map *em_cached = NULL;
|
||||
struct fsverity_info *vi = NULL;
|
||||
int ret;
|
||||
|
||||
lock_extents_for_read(inode, start, end, &cached_state);
|
||||
ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl);
|
||||
if (folio_pos(folio) < i_size_read(vfs_inode))
|
||||
vi = fsverity_get_info(vfs_inode);
|
||||
ret = btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi);
|
||||
btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state);
|
||||
|
||||
btrfs_free_extent_map(em_cached);
|
||||
|
|
@ -2714,16 +2724,19 @@ void btrfs_readahead(struct readahead_control *rac)
|
|||
.last_em_start = U64_MAX,
|
||||
};
|
||||
struct folio *folio;
|
||||
struct btrfs_inode *inode = BTRFS_I(rac->mapping->host);
|
||||
struct inode *vfs_inode = rac->mapping->host;
|
||||
struct btrfs_inode *inode = BTRFS_I(vfs_inode);
|
||||
const u64 start = readahead_pos(rac);
|
||||
const u64 end = start + readahead_length(rac) - 1;
|
||||
struct extent_state *cached_state = NULL;
|
||||
struct extent_map *em_cached = NULL;
|
||||
struct fsverity_info *vi = NULL;
|
||||
|
||||
lock_extents_for_read(inode, start, end, &cached_state);
|
||||
|
||||
if (start < i_size_read(vfs_inode))
|
||||
vi = fsverity_get_info(vfs_inode);
|
||||
while ((folio = readahead_folio(rac)) != NULL)
|
||||
btrfs_do_readpage(folio, &em_cached, &bio_ctrl);
|
||||
btrfs_do_readpage(folio, &em_cached, &bio_ctrl, vi);
|
||||
|
||||
btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
#include <linux/sched/mm.h>
|
||||
#include <linux/iomap.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include "misc.h"
|
||||
#include "ctree.h"
|
||||
#include "disk-io.h"
|
||||
|
|
@ -5616,11 +5615,8 @@ void btrfs_evict_inode(struct inode *inode)
|
|||
|
||||
trace_btrfs_inode_evict(inode);
|
||||
|
||||
if (!root) {
|
||||
fsverity_cleanup_inode(inode);
|
||||
clear_inode(inode);
|
||||
return;
|
||||
}
|
||||
if (!root)
|
||||
goto clear_inode;
|
||||
|
||||
fs_info = inode_to_fs_info(inode);
|
||||
evict_inode_truncate_pages(inode);
|
||||
|
|
@ -5720,7 +5716,7 @@ out:
|
|||
* to retry these periodically in the future.
|
||||
*/
|
||||
btrfs_remove_delayed_node(BTRFS_I(inode));
|
||||
fsverity_cleanup_inode(inode);
|
||||
clear_inode:
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
|
@ -8151,9 +8147,6 @@ static void init_once(void *foo)
|
|||
struct btrfs_inode *ei = foo;
|
||||
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
ei->i_verity_info = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void __cold btrfs_destroy_cachep(void)
|
||||
|
|
|
|||
|
|
@ -694,7 +694,6 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size)
|
|||
*
|
||||
* @inode: inode to read a merkle tree page for
|
||||
* @index: page index relative to the start of the merkle tree
|
||||
* @num_ra_pages: number of pages to readahead. Optional, we ignore it
|
||||
*
|
||||
* The Merkle tree is stored in the filesystem btree, but its pages are cached
|
||||
* with a logical position past EOF in the inode's mapping.
|
||||
|
|
@ -702,8 +701,7 @@ int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size)
|
|||
* Returns the page we read, or an ERR_PTR on error.
|
||||
*/
|
||||
static struct page *btrfs_read_merkle_tree_page(struct inode *inode,
|
||||
pgoff_t index,
|
||||
unsigned long num_ra_pages)
|
||||
pgoff_t index)
|
||||
{
|
||||
struct folio *folio;
|
||||
u64 off = (u64)index << PAGE_SHIFT;
|
||||
|
|
@ -771,16 +769,17 @@ out:
|
|||
/*
|
||||
* fsverity op that writes a Merkle tree block into the btree.
|
||||
*
|
||||
* @inode: inode to write a Merkle tree block for
|
||||
* @file: file to write a Merkle tree block for
|
||||
* @buf: Merkle tree block to write
|
||||
* @pos: the position of the block in the Merkle tree (in bytes)
|
||||
* @size: the Merkle tree block size (in bytes)
|
||||
*
|
||||
* Returns 0 on success or negative error code on failure
|
||||
*/
|
||||
static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf,
|
||||
static int btrfs_write_merkle_tree_block(struct file *file, const void *buf,
|
||||
u64 pos, unsigned int size)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
loff_t merkle_pos = merkle_file_pos(inode);
|
||||
|
||||
if (merkle_pos < 0)
|
||||
|
|
@ -793,8 +792,6 @@ static int btrfs_write_merkle_tree_block(struct inode *inode, const void *buf,
|
|||
}
|
||||
|
||||
const struct fsverity_operations btrfs_verityops = {
|
||||
.inode_info_offs = (int)offsetof(struct btrfs_inode, i_verity_info) -
|
||||
(int)offsetof(struct btrfs_inode, vfs_inode),
|
||||
.begin_enable_verity = btrfs_begin_enable_verity,
|
||||
.end_enable_verity = btrfs_end_enable_verity,
|
||||
.get_verity_descriptor = btrfs_get_verity_descriptor,
|
||||
|
|
|
|||
25
fs/buffer.c
25
fs/buffer.c
|
|
@ -303,6 +303,7 @@ still_busy:
|
|||
struct postprocess_bh_ctx {
|
||||
struct work_struct work;
|
||||
struct buffer_head *bh;
|
||||
struct fsverity_info *vi;
|
||||
};
|
||||
|
||||
static void verify_bh(struct work_struct *work)
|
||||
|
|
@ -312,21 +313,12 @@ static void verify_bh(struct work_struct *work)
|
|||
struct buffer_head *bh = ctx->bh;
|
||||
bool valid;
|
||||
|
||||
valid = fsverity_verify_blocks(bh->b_folio, bh->b_size, bh_offset(bh));
|
||||
valid = fsverity_verify_blocks(ctx->vi, bh->b_folio, bh->b_size,
|
||||
bh_offset(bh));
|
||||
end_buffer_async_read(bh, valid);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
static bool need_fsverity(struct buffer_head *bh)
|
||||
{
|
||||
struct folio *folio = bh->b_folio;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
|
||||
return fsverity_active(inode) &&
|
||||
/* needed by ext4 */
|
||||
folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static void decrypt_bh(struct work_struct *work)
|
||||
{
|
||||
struct postprocess_bh_ctx *ctx =
|
||||
|
|
@ -336,7 +328,7 @@ static void decrypt_bh(struct work_struct *work)
|
|||
|
||||
err = fscrypt_decrypt_pagecache_blocks(bh->b_folio, bh->b_size,
|
||||
bh_offset(bh));
|
||||
if (err == 0 && need_fsverity(bh)) {
|
||||
if (err == 0 && ctx->vi) {
|
||||
/*
|
||||
* We use different work queues for decryption and for verity
|
||||
* because verity may require reading metadata pages that need
|
||||
|
|
@ -358,15 +350,20 @@ static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate)
|
|||
{
|
||||
struct inode *inode = bh->b_folio->mapping->host;
|
||||
bool decrypt = fscrypt_inode_uses_fs_layer_crypto(inode);
|
||||
bool verify = need_fsverity(bh);
|
||||
struct fsverity_info *vi = NULL;
|
||||
|
||||
/* needed by ext4 */
|
||||
if (bh->b_folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE))
|
||||
vi = fsverity_get_info(inode);
|
||||
|
||||
/* Decrypt (with fscrypt) and/or verify (with fsverity) if needed. */
|
||||
if (uptodate && (decrypt || verify)) {
|
||||
if (uptodate && (decrypt || vi)) {
|
||||
struct postprocess_bh_ctx *ctx =
|
||||
kmalloc(sizeof(*ctx), GFP_ATOMIC);
|
||||
|
||||
if (ctx) {
|
||||
ctx->bh = bh;
|
||||
ctx->vi = vi;
|
||||
if (decrypt) {
|
||||
INIT_WORK(&ctx->work, decrypt_bh);
|
||||
fscrypt_enqueue_decrypt_work(&ctx->work);
|
||||
|
|
|
|||
|
|
@ -1196,10 +1196,6 @@ struct ext4_inode_info {
|
|||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
struct fscrypt_inode_info *i_crypt_info;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
struct fsverity_info *i_verity_info;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -3744,8 +3740,8 @@ static inline void ext4_set_de_type(struct super_block *sb,
|
|||
}
|
||||
|
||||
/* readpages.c */
|
||||
extern int ext4_mpage_readpages(struct inode *inode,
|
||||
struct readahead_control *rac, struct folio *folio);
|
||||
int ext4_read_folio(struct file *file, struct folio *folio);
|
||||
void ext4_readahead(struct readahead_control *rac);
|
||||
extern int __init ext4_init_post_read_processing(void);
|
||||
extern void ext4_exit_post_read_processing(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -3373,33 +3373,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ext4_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
|
||||
trace_ext4_read_folio(inode, folio);
|
||||
|
||||
if (ext4_has_inline_data(inode))
|
||||
ret = ext4_readpage_inline(inode, folio);
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
return ext4_mpage_readpages(inode, NULL, folio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ext4_readahead(struct readahead_control *rac)
|
||||
{
|
||||
struct inode *inode = rac->mapping->host;
|
||||
|
||||
/* If the file has inline data, no need to do readahead. */
|
||||
if (ext4_has_inline_data(inode))
|
||||
return;
|
||||
|
||||
ext4_mpage_readpages(inode, rac, NULL);
|
||||
}
|
||||
|
||||
static void ext4_invalidate_folio(struct folio *folio, size_t offset,
|
||||
size_t length)
|
||||
{
|
||||
|
|
@ -5815,10 +5788,6 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = fsverity_prepare_setattr(dentry, attr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (is_quota_modification(idmap, inode, attr)) {
|
||||
error = dquot_initialize(inode);
|
||||
if (error)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include <linux/pagevec.h>
|
||||
|
||||
#include "ext4.h"
|
||||
#include <trace/events/ext4.h>
|
||||
|
||||
#define NUM_PREALLOC_POST_READ_CTXS 128
|
||||
|
||||
|
|
@ -62,6 +63,7 @@ enum bio_post_read_step {
|
|||
|
||||
struct bio_post_read_ctx {
|
||||
struct bio *bio;
|
||||
struct fsverity_info *vi;
|
||||
struct work_struct work;
|
||||
unsigned int cur_step;
|
||||
unsigned int enabled_steps;
|
||||
|
|
@ -97,6 +99,7 @@ static void verity_work(struct work_struct *work)
|
|||
struct bio_post_read_ctx *ctx =
|
||||
container_of(work, struct bio_post_read_ctx, work);
|
||||
struct bio *bio = ctx->bio;
|
||||
struct fsverity_info *vi = ctx->vi;
|
||||
|
||||
/*
|
||||
* fsverity_verify_bio() may call readahead() again, and although verity
|
||||
|
|
@ -109,7 +112,7 @@ static void verity_work(struct work_struct *work)
|
|||
mempool_free(ctx, bio_post_read_ctx_pool);
|
||||
bio->bi_private = NULL;
|
||||
|
||||
fsverity_verify_bio(bio);
|
||||
fsverity_verify_bio(vi, bio);
|
||||
|
||||
__read_end_io(bio);
|
||||
}
|
||||
|
|
@ -131,7 +134,8 @@ static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
|
|||
ctx->cur_step++;
|
||||
fallthrough;
|
||||
case STEP_VERITY:
|
||||
if (ctx->enabled_steps & (1 << STEP_VERITY)) {
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) &&
|
||||
ctx->enabled_steps & (1 << STEP_VERITY)) {
|
||||
INIT_WORK(&ctx->work, verity_work);
|
||||
fsverity_enqueue_verify_work(&ctx->work);
|
||||
return;
|
||||
|
|
@ -172,22 +176,16 @@ static void mpage_end_io(struct bio *bio)
|
|||
__read_end_io(bio);
|
||||
}
|
||||
|
||||
static inline bool ext4_need_verity(const struct inode *inode, pgoff_t idx)
|
||||
{
|
||||
return fsverity_active(inode) &&
|
||||
idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static void ext4_set_bio_post_read_ctx(struct bio *bio,
|
||||
const struct inode *inode,
|
||||
pgoff_t first_idx)
|
||||
struct fsverity_info *vi)
|
||||
{
|
||||
unsigned int post_read_steps = 0;
|
||||
|
||||
if (fscrypt_inode_uses_fs_layer_crypto(inode))
|
||||
post_read_steps |= 1 << STEP_DECRYPT;
|
||||
|
||||
if (ext4_need_verity(inode, first_idx))
|
||||
if (vi)
|
||||
post_read_steps |= 1 << STEP_VERITY;
|
||||
|
||||
if (post_read_steps) {
|
||||
|
|
@ -196,6 +194,7 @@ static void ext4_set_bio_post_read_ctx(struct bio *bio,
|
|||
mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
|
||||
|
||||
ctx->bio = bio;
|
||||
ctx->vi = vi;
|
||||
ctx->enabled_steps = post_read_steps;
|
||||
bio->bi_private = ctx;
|
||||
}
|
||||
|
|
@ -209,7 +208,7 @@ static inline loff_t ext4_readpage_limit(struct inode *inode)
|
|||
return i_size_read(inode);
|
||||
}
|
||||
|
||||
int ext4_mpage_readpages(struct inode *inode,
|
||||
static int ext4_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
|
||||
struct readahead_control *rac, struct folio *folio)
|
||||
{
|
||||
struct bio *bio = NULL;
|
||||
|
|
@ -329,8 +328,7 @@ int ext4_mpage_readpages(struct inode *inode,
|
|||
folio_zero_segment(folio, first_hole << blkbits,
|
||||
folio_size(folio));
|
||||
if (first_hole == 0) {
|
||||
if (ext4_need_verity(inode, folio->index) &&
|
||||
!fsverity_verify_folio(folio))
|
||||
if (vi && !fsverity_verify_folio(vi, folio))
|
||||
goto set_error_page;
|
||||
folio_end_read(folio, true);
|
||||
continue;
|
||||
|
|
@ -358,7 +356,7 @@ int ext4_mpage_readpages(struct inode *inode,
|
|||
REQ_OP_READ, GFP_KERNEL);
|
||||
fscrypt_set_bio_crypt_ctx(bio, inode, next_block,
|
||||
GFP_KERNEL);
|
||||
ext4_set_bio_post_read_ctx(bio, inode, folio->index);
|
||||
ext4_set_bio_post_read_ctx(bio, inode, vi);
|
||||
bio->bi_iter.bi_sector = first_block << (blkbits - 9);
|
||||
bio->bi_end_io = mpage_end_io;
|
||||
if (rac)
|
||||
|
|
@ -394,6 +392,44 @@ next_page:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ext4_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct fsverity_info *vi = NULL;
|
||||
int ret;
|
||||
|
||||
trace_ext4_read_folio(inode, folio);
|
||||
|
||||
if (ext4_has_inline_data(inode)) {
|
||||
ret = ext4_readpage_inline(inode, folio);
|
||||
if (ret != -EAGAIN)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (folio->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE))
|
||||
vi = fsverity_get_info(inode);
|
||||
if (vi)
|
||||
fsverity_readahead(vi, folio->index, folio_nr_pages(folio));
|
||||
return ext4_mpage_readpages(inode, vi, NULL, folio);
|
||||
}
|
||||
|
||||
void ext4_readahead(struct readahead_control *rac)
|
||||
{
|
||||
struct inode *inode = rac->mapping->host;
|
||||
struct fsverity_info *vi = NULL;
|
||||
|
||||
/* If the file has inline data, no need to do readahead. */
|
||||
if (ext4_has_inline_data(inode))
|
||||
return;
|
||||
|
||||
if (readahead_index(rac) < DIV_ROUND_UP(inode->i_size, PAGE_SIZE))
|
||||
vi = fsverity_get_info(inode);
|
||||
if (vi)
|
||||
fsverity_readahead(vi, readahead_index(rac),
|
||||
readahead_count(rac));
|
||||
ext4_mpage_readpages(inode, vi, rac, NULL);
|
||||
}
|
||||
|
||||
int __init ext4_init_post_read_processing(void)
|
||||
{
|
||||
bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, SLAB_RECLAIM_ACCOUNT);
|
||||
|
|
|
|||
|
|
@ -1489,9 +1489,6 @@ static void init_once(void *foo)
|
|||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
ei->i_crypt_info = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
ei->i_verity_info = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __init init_inodecache(void)
|
||||
|
|
@ -1539,7 +1536,6 @@ void ext4_clear_inode(struct inode *inode)
|
|||
EXT4_I(inode)->jinode = NULL;
|
||||
}
|
||||
fscrypt_put_encryption_info(inode);
|
||||
fsverity_cleanup_inode(inode);
|
||||
}
|
||||
|
||||
static struct inode *ext4_nfs_get_inode(struct super_block *sb,
|
||||
|
|
|
|||
|
|
@ -360,42 +360,32 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
|
|||
}
|
||||
|
||||
static struct page *ext4_read_merkle_tree_page(struct inode *inode,
|
||||
pgoff_t index,
|
||||
unsigned long num_ra_pages)
|
||||
pgoff_t index)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
|
||||
|
||||
folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
|
||||
if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
|
||||
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
|
||||
|
||||
if (!IS_ERR(folio))
|
||||
folio_put(folio);
|
||||
else if (num_ra_pages > 1)
|
||||
page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
|
||||
folio = read_mapping_folio(inode->i_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
return ERR_CAST(folio);
|
||||
}
|
||||
return folio_file_page(folio, index);
|
||||
return generic_read_merkle_tree_page(inode, index);
|
||||
}
|
||||
|
||||
static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf,
|
||||
static void ext4_readahead_merkle_tree(struct inode *inode, pgoff_t index,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT;
|
||||
generic_readahead_merkle_tree(inode, index, nr_pages);
|
||||
}
|
||||
|
||||
static int ext4_write_merkle_tree_block(struct file *file, const void *buf,
|
||||
u64 pos, unsigned int size)
|
||||
{
|
||||
pos += ext4_verity_metadata_pos(inode);
|
||||
pos += ext4_verity_metadata_pos(file_inode(file));
|
||||
|
||||
return pagecache_write(inode, buf, size, pos);
|
||||
return pagecache_write(file_inode(file), buf, size, pos);
|
||||
}
|
||||
|
||||
const struct fsverity_operations ext4_verityops = {
|
||||
.inode_info_offs = (int)offsetof(struct ext4_inode_info, i_verity_info) -
|
||||
(int)offsetof(struct ext4_inode_info, vfs_inode),
|
||||
.begin_enable_verity = ext4_begin_enable_verity,
|
||||
.end_enable_verity = ext4_end_enable_verity,
|
||||
.get_verity_descriptor = ext4_get_verity_descriptor,
|
||||
.read_merkle_tree_page = ext4_read_merkle_tree_page,
|
||||
.readahead_merkle_tree = ext4_readahead_merkle_tree,
|
||||
.write_merkle_tree_block = ext4_write_merkle_tree_block,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1181,6 +1181,7 @@ int f2fs_prepare_compress_overwrite(struct inode *inode,
|
|||
.cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size,
|
||||
.rpages = NULL,
|
||||
.nr_rpages = 0,
|
||||
.vi = NULL, /* can't write to fsverity files */
|
||||
};
|
||||
|
||||
return prepare_compress_overwrite(&cc, pagep, index, fsdata);
|
||||
|
|
@ -1716,7 +1717,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
|||
dic->nr_cpages = cc->nr_cpages;
|
||||
refcount_set(&dic->refcnt, 1);
|
||||
dic->failed = false;
|
||||
dic->need_verity = f2fs_need_verity(cc->inode, start_idx);
|
||||
dic->vi = cc->vi;
|
||||
|
||||
for (i = 0; i < dic->cluster_size; i++)
|
||||
dic->rpages[i] = cc->rpages[i];
|
||||
|
|
@ -1814,7 +1815,7 @@ static void f2fs_verify_cluster(struct work_struct *work)
|
|||
if (!rpage)
|
||||
continue;
|
||||
|
||||
if (fsverity_verify_page(rpage))
|
||||
if (fsverity_verify_page(dic->vi, rpage))
|
||||
SetPageUptodate(rpage);
|
||||
else
|
||||
ClearPageUptodate(rpage);
|
||||
|
|
@ -1833,7 +1834,7 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!failed && dic->need_verity) {
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) && !failed && dic->vi) {
|
||||
/*
|
||||
* Note that to avoid deadlocks, the verity work can't be done
|
||||
* on the decompression workqueue. This is because verifying
|
||||
|
|
|
|||
100
fs/f2fs/data.c
100
fs/f2fs/data.c
|
|
@ -109,6 +109,7 @@ enum bio_post_read_step {
|
|||
struct bio_post_read_ctx {
|
||||
struct bio *bio;
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct fsverity_info *vi;
|
||||
struct work_struct work;
|
||||
unsigned int enabled_steps;
|
||||
/*
|
||||
|
|
@ -165,6 +166,7 @@ static void f2fs_verify_bio(struct work_struct *work)
|
|||
container_of(work, struct bio_post_read_ctx, work);
|
||||
struct bio *bio = ctx->bio;
|
||||
bool may_have_compressed_pages = (ctx->enabled_steps & STEP_DECOMPRESS);
|
||||
struct fsverity_info *vi = ctx->vi;
|
||||
|
||||
/*
|
||||
* fsverity_verify_bio() may call readahead() again, and while verity
|
||||
|
|
@ -187,13 +189,13 @@ static void f2fs_verify_bio(struct work_struct *work)
|
|||
struct folio *folio = fi.folio;
|
||||
|
||||
if (!f2fs_is_compressed_page(folio) &&
|
||||
!fsverity_verify_page(&folio->page)) {
|
||||
!fsverity_verify_page(vi, &folio->page)) {
|
||||
bio->bi_status = BLK_STS_IOERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fsverity_verify_bio(bio);
|
||||
fsverity_verify_bio(vi, bio);
|
||||
}
|
||||
|
||||
f2fs_finish_read_bio(bio, true);
|
||||
|
|
@ -1036,7 +1038,8 @@ out:
|
|||
f2fs_up_write(&io->io_rwsem);
|
||||
}
|
||||
|
||||
static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
||||
static struct bio *f2fs_grab_read_bio(struct inode *inode,
|
||||
struct fsverity_info *vi, block_t blkaddr,
|
||||
unsigned nr_pages, blk_opf_t op_flag,
|
||||
pgoff_t first_idx, bool for_write)
|
||||
{
|
||||
|
|
@ -1057,7 +1060,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
|||
if (fscrypt_inode_uses_fs_layer_crypto(inode))
|
||||
post_read_steps |= STEP_DECRYPT;
|
||||
|
||||
if (f2fs_need_verity(inode, first_idx))
|
||||
if (vi)
|
||||
post_read_steps |= STEP_VERITY;
|
||||
|
||||
/*
|
||||
|
|
@ -1072,6 +1075,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
|||
ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
|
||||
ctx->bio = bio;
|
||||
ctx->sbi = sbi;
|
||||
ctx->vi = vi;
|
||||
ctx->enabled_steps = post_read_steps;
|
||||
ctx->fs_blkaddr = blkaddr;
|
||||
ctx->decompression_attempted = false;
|
||||
|
|
@ -1083,15 +1087,15 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
|||
}
|
||||
|
||||
/* This can handle encryption stuffs */
|
||||
static void f2fs_submit_page_read(struct inode *inode, struct folio *folio,
|
||||
block_t blkaddr, blk_opf_t op_flags,
|
||||
bool for_write)
|
||||
static void f2fs_submit_page_read(struct inode *inode, struct fsverity_info *vi,
|
||||
struct folio *folio, block_t blkaddr,
|
||||
blk_opf_t op_flags, bool for_write)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct bio *bio;
|
||||
|
||||
bio = f2fs_grab_read_bio(inode, blkaddr, 1, op_flags,
|
||||
folio->index, for_write);
|
||||
bio = f2fs_grab_read_bio(inode, vi, blkaddr, 1, op_flags, folio->index,
|
||||
for_write);
|
||||
|
||||
/* wait for GCed page writeback via META_MAPPING */
|
||||
f2fs_wait_on_block_writeback(inode, blkaddr);
|
||||
|
|
@ -1193,6 +1197,14 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index)
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline struct fsverity_info *f2fs_need_verity(const struct inode *inode,
|
||||
pgoff_t idx)
|
||||
{
|
||||
if (idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE))
|
||||
return fsverity_get_info(inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct folio *f2fs_get_read_data_folio(struct inode *inode, pgoff_t index,
|
||||
blk_opf_t op_flags, bool for_write, pgoff_t *next_pgofs)
|
||||
{
|
||||
|
|
@ -1258,8 +1270,8 @@ got_it:
|
|||
return folio;
|
||||
}
|
||||
|
||||
f2fs_submit_page_read(inode, folio, dn.data_blkaddr,
|
||||
op_flags, for_write);
|
||||
f2fs_submit_page_read(inode, f2fs_need_verity(inode, folio->index),
|
||||
folio, dn.data_blkaddr, op_flags, for_write);
|
||||
return folio;
|
||||
|
||||
put_err:
|
||||
|
|
@ -2063,12 +2075,12 @@ static inline blk_opf_t f2fs_ra_op_flags(struct readahead_control *rac)
|
|||
return rac ? REQ_RAHEAD : 0;
|
||||
}
|
||||
|
||||
static int f2fs_read_single_page(struct inode *inode, struct folio *folio,
|
||||
unsigned nr_pages,
|
||||
struct f2fs_map_blocks *map,
|
||||
struct bio **bio_ret,
|
||||
sector_t *last_block_in_bio,
|
||||
struct readahead_control *rac)
|
||||
static int f2fs_read_single_page(struct inode *inode, struct fsverity_info *vi,
|
||||
struct folio *folio, unsigned int nr_pages,
|
||||
struct f2fs_map_blocks *map,
|
||||
struct bio **bio_ret,
|
||||
sector_t *last_block_in_bio,
|
||||
struct readahead_control *rac)
|
||||
{
|
||||
struct bio *bio = *bio_ret;
|
||||
const unsigned int blocksize = F2FS_BLKSIZE;
|
||||
|
|
@ -2120,8 +2132,7 @@ got_it:
|
|||
} else {
|
||||
zero_out:
|
||||
folio_zero_segment(folio, 0, folio_size(folio));
|
||||
if (f2fs_need_verity(inode, index) &&
|
||||
!fsverity_verify_folio(folio)) {
|
||||
if (vi && !fsverity_verify_folio(vi, folio)) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -2143,9 +2154,8 @@ submit_and_realloc:
|
|||
bio = NULL;
|
||||
}
|
||||
if (bio == NULL)
|
||||
bio = f2fs_grab_read_bio(inode, block_nr, nr_pages,
|
||||
f2fs_ra_op_flags(rac), index,
|
||||
false);
|
||||
bio = f2fs_grab_read_bio(inode, vi, block_nr, nr_pages,
|
||||
f2fs_ra_op_flags(rac), index, false);
|
||||
|
||||
/*
|
||||
* If the page is under writeback, we need to wait for
|
||||
|
|
@ -2295,9 +2305,10 @@ submit_and_realloc:
|
|||
}
|
||||
|
||||
if (!bio)
|
||||
bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages - i,
|
||||
f2fs_ra_op_flags(rac),
|
||||
folio->index, for_write);
|
||||
bio = f2fs_grab_read_bio(inode, cc->vi, blkaddr,
|
||||
nr_pages - i,
|
||||
f2fs_ra_op_flags(rac),
|
||||
folio->index, for_write);
|
||||
|
||||
if (!bio_add_folio(bio, folio, blocksize, 0))
|
||||
goto submit_and_realloc;
|
||||
|
|
@ -2336,7 +2347,7 @@ out:
|
|||
* This function was originally taken from fs/mpage.c, and customized for f2fs.
|
||||
* Major change was from block_size == page_size in f2fs by default.
|
||||
*/
|
||||
static int f2fs_mpage_readpages(struct inode *inode,
|
||||
static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,
|
||||
struct readahead_control *rac, struct folio *folio)
|
||||
{
|
||||
struct bio *bio = NULL;
|
||||
|
|
@ -2391,6 +2402,7 @@ static int f2fs_mpage_readpages(struct inode *inode,
|
|||
|
||||
/* there are remained compressed pages, submit them */
|
||||
if (!f2fs_cluster_can_merge_page(&cc, index)) {
|
||||
cc.vi = vi;
|
||||
ret = f2fs_read_multi_pages(&cc, &bio,
|
||||
max_nr_pages,
|
||||
&last_block_in_bio,
|
||||
|
|
@ -2424,8 +2436,9 @@ static int f2fs_mpage_readpages(struct inode *inode,
|
|||
read_single_page:
|
||||
#endif
|
||||
|
||||
ret = f2fs_read_single_page(inode, folio, max_nr_pages, &map,
|
||||
&bio, &last_block_in_bio, rac);
|
||||
ret = f2fs_read_single_page(inode, vi, folio, max_nr_pages,
|
||||
&map, &bio, &last_block_in_bio,
|
||||
rac);
|
||||
if (ret) {
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
set_error_page:
|
||||
|
|
@ -2441,6 +2454,7 @@ next_page:
|
|||
if (f2fs_compressed_file(inode)) {
|
||||
/* last page */
|
||||
if (nr_pages == 1 && !f2fs_cluster_is_empty(&cc)) {
|
||||
cc.vi = vi;
|
||||
ret = f2fs_read_multi_pages(&cc, &bio,
|
||||
max_nr_pages,
|
||||
&last_block_in_bio,
|
||||
|
|
@ -2458,7 +2472,8 @@ next_page:
|
|||
static int f2fs_read_data_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
int ret = -EAGAIN;
|
||||
struct fsverity_info *vi = NULL;
|
||||
int ret;
|
||||
|
||||
trace_f2fs_readpage(folio, DATA);
|
||||
|
||||
|
|
@ -2468,16 +2483,22 @@ static int f2fs_read_data_folio(struct file *file, struct folio *folio)
|
|||
}
|
||||
|
||||
/* If the file has inline data, try to read it directly */
|
||||
if (f2fs_has_inline_data(inode))
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
ret = f2fs_read_inline_data(inode, folio);
|
||||
if (ret == -EAGAIN)
|
||||
ret = f2fs_mpage_readpages(inode, NULL, folio);
|
||||
return ret;
|
||||
if (ret != -EAGAIN)
|
||||
return ret;
|
||||
}
|
||||
|
||||
vi = f2fs_need_verity(inode, folio->index);
|
||||
if (vi)
|
||||
fsverity_readahead(vi, folio->index, folio_nr_pages(folio));
|
||||
return f2fs_mpage_readpages(inode, vi, NULL, folio);
|
||||
}
|
||||
|
||||
static void f2fs_readahead(struct readahead_control *rac)
|
||||
{
|
||||
struct inode *inode = rac->mapping->host;
|
||||
struct fsverity_info *vi = NULL;
|
||||
|
||||
trace_f2fs_readpages(inode, readahead_index(rac), readahead_count(rac));
|
||||
|
||||
|
|
@ -2488,7 +2509,11 @@ static void f2fs_readahead(struct readahead_control *rac)
|
|||
if (f2fs_has_inline_data(inode))
|
||||
return;
|
||||
|
||||
f2fs_mpage_readpages(inode, rac, NULL);
|
||||
vi = f2fs_need_verity(inode, readahead_index(rac));
|
||||
if (vi)
|
||||
fsverity_readahead(vi, readahead_index(rac),
|
||||
readahead_count(rac));
|
||||
f2fs_mpage_readpages(inode, vi, rac, NULL);
|
||||
}
|
||||
|
||||
int f2fs_encrypt_one_page(struct f2fs_io_info *fio)
|
||||
|
|
@ -3637,9 +3662,10 @@ repeat:
|
|||
err = -EFSCORRUPTED;
|
||||
goto put_folio;
|
||||
}
|
||||
f2fs_submit_page_read(use_cow ?
|
||||
F2FS_I(inode)->cow_inode : inode,
|
||||
folio, blkaddr, 0, true);
|
||||
f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode :
|
||||
inode,
|
||||
NULL, /* can't write to fsverity files */
|
||||
folio, blkaddr, 0, true);
|
||||
|
||||
folio_lock(folio);
|
||||
if (unlikely(folio->mapping != mapping)) {
|
||||
|
|
|
|||
|
|
@ -974,9 +974,6 @@ struct f2fs_inode_info {
|
|||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
struct fscrypt_inode_info *i_crypt_info; /* filesystem encryption info */
|
||||
#endif
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
struct fsverity_info *i_verity_info; /* filesystem verity info */
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void get_read_extent_info(struct extent_info *ext,
|
||||
|
|
@ -1603,6 +1600,7 @@ struct compress_ctx {
|
|||
size_t clen; /* valid data length in cbuf */
|
||||
void *private; /* payload buffer for specified compression algorithm */
|
||||
void *private2; /* extra payload buffer */
|
||||
struct fsverity_info *vi; /* verity info if needed */
|
||||
};
|
||||
|
||||
/* compress context for write IO path */
|
||||
|
|
@ -1658,7 +1656,7 @@ struct decompress_io_ctx {
|
|||
refcount_t refcnt;
|
||||
|
||||
bool failed; /* IO error occurred before decompression? */
|
||||
bool need_verity; /* need fs-verity verification after decompression? */
|
||||
struct fsverity_info *vi; /* fs-verity context if needed */
|
||||
unsigned char compress_algorithm; /* backup algorithm type */
|
||||
void *private; /* payload buffer for specified decompression algorithm */
|
||||
void *private2; /* extra payload buffer */
|
||||
|
|
@ -4886,12 +4884,6 @@ static inline bool f2fs_allow_multi_device_dio(struct f2fs_sb_info *sbi,
|
|||
return sbi->aligned_blksize;
|
||||
}
|
||||
|
||||
static inline bool f2fs_need_verity(const struct inode *inode, pgoff_t idx)
|
||||
{
|
||||
return fsverity_active(inode) &&
|
||||
idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||
extern int f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned long rate,
|
||||
unsigned long type, enum fault_option fo);
|
||||
|
|
|
|||
|
|
@ -1076,10 +1076,6 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = fsverity_prepare_setattr(dentry, attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (unlikely(IS_IMMUTABLE(inode)))
|
||||
return -EPERM;
|
||||
|
||||
|
|
@ -4424,7 +4420,9 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
|
|||
pgoff_t redirty_idx = page_idx;
|
||||
int page_len = 0, ret = 0;
|
||||
|
||||
filemap_invalidate_lock_shared(mapping);
|
||||
page_cache_ra_unbounded(&ractl, len, 0);
|
||||
filemap_invalidate_unlock_shared(mapping);
|
||||
|
||||
do {
|
||||
folio = read_cache_folio(mapping, page_idx, NULL, NULL);
|
||||
|
|
|
|||
|
|
@ -1000,7 +1000,6 @@ no_delete:
|
|||
}
|
||||
out_clear:
|
||||
fscrypt_put_encryption_info(inode);
|
||||
fsverity_cleanup_inode(inode);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -504,9 +504,6 @@ static void init_once(void *foo)
|
|||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
fi->i_crypt_info = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
fi->i_verity_info = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
|
|
|
|||
|
|
@ -256,42 +256,32 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
|
|||
}
|
||||
|
||||
static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
|
||||
pgoff_t index,
|
||||
unsigned long num_ra_pages)
|
||||
pgoff_t index)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT;
|
||||
|
||||
folio = f2fs_filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
|
||||
if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
|
||||
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
|
||||
|
||||
if (!IS_ERR(folio))
|
||||
folio_put(folio);
|
||||
else if (num_ra_pages > 1)
|
||||
page_cache_ra_unbounded(&ractl, num_ra_pages, 0);
|
||||
folio = read_mapping_folio(inode->i_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
return ERR_CAST(folio);
|
||||
}
|
||||
return folio_file_page(folio, index);
|
||||
return generic_read_merkle_tree_page(inode, index);
|
||||
}
|
||||
|
||||
static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf,
|
||||
static void f2fs_readahead_merkle_tree(struct inode *inode, pgoff_t index,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT;
|
||||
generic_readahead_merkle_tree(inode, index, nr_pages);
|
||||
}
|
||||
|
||||
static int f2fs_write_merkle_tree_block(struct file *file, const void *buf,
|
||||
u64 pos, unsigned int size)
|
||||
{
|
||||
pos += f2fs_verity_metadata_pos(inode);
|
||||
pos += f2fs_verity_metadata_pos(file_inode(file));
|
||||
|
||||
return pagecache_write(inode, buf, size, pos);
|
||||
return pagecache_write(file_inode(file), buf, size, pos);
|
||||
}
|
||||
|
||||
const struct fsverity_operations f2fs_verityops = {
|
||||
.inode_info_offs = (int)offsetof(struct f2fs_inode_info, i_verity_info) -
|
||||
(int)offsetof(struct f2fs_inode_info, vfs_inode),
|
||||
.begin_enable_verity = f2fs_begin_enable_verity,
|
||||
.end_enable_verity = f2fs_end_enable_verity,
|
||||
.get_verity_descriptor = f2fs_get_verity_descriptor,
|
||||
.read_merkle_tree_page = f2fs_read_merkle_tree_page,
|
||||
.readahead_merkle_tree = f2fs_readahead_merkle_tree,
|
||||
.write_merkle_tree_block = f2fs_write_merkle_tree_block,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/cdev.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/buffer_head.h> /* for inode_has_buffers */
|
||||
|
|
@ -773,6 +774,14 @@ void dump_mapping(const struct address_space *mapping)
|
|||
|
||||
void clear_inode(struct inode *inode)
|
||||
{
|
||||
/*
|
||||
* Only IS_VERITY() inodes can have verity info, so start by checking
|
||||
* for IS_VERITY() (which is faster than retrieving the pointer to the
|
||||
* verity info). This minimizes overhead for non-verity inodes.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
|
||||
fsverity_cleanup_inode(inode);
|
||||
|
||||
/*
|
||||
* We have to cycle the i_pages lock here because reclaim can be in the
|
||||
* process of removing the last page (in __filemap_remove_folio())
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ obj-$(CONFIG_FS_VERITY) += enable.o \
|
|||
init.o \
|
||||
measure.o \
|
||||
open.o \
|
||||
pagecache.o \
|
||||
read_metadata.o \
|
||||
verify.o
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,15 @@ static int hash_one_block(const struct merkle_tree_params *params,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int write_merkle_tree_block(struct inode *inode, const u8 *buf,
|
||||
static int write_merkle_tree_block(struct file *file, const u8 *buf,
|
||||
unsigned long index,
|
||||
const struct merkle_tree_params *params)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
u64 pos = (u64)index << params->log_blocksize;
|
||||
int err;
|
||||
|
||||
err = inode->i_sb->s_vop->write_merkle_tree_block(inode, buf, pos,
|
||||
err = inode->i_sb->s_vop->write_merkle_tree_block(file, buf, pos,
|
||||
params->block_size);
|
||||
if (err)
|
||||
fsverity_err(inode, "Error %d writing Merkle tree block %lu",
|
||||
|
|
@ -135,7 +136,7 @@ static int build_merkle_tree(struct file *filp,
|
|||
err = hash_one_block(params, &buffers[level]);
|
||||
if (err)
|
||||
goto out;
|
||||
err = write_merkle_tree_block(inode,
|
||||
err = write_merkle_tree_block(filp,
|
||||
buffers[level].data,
|
||||
level_offset[level],
|
||||
params);
|
||||
|
|
@ -155,7 +156,7 @@ static int build_merkle_tree(struct file *filp,
|
|||
err = hash_one_block(params, &buffers[level]);
|
||||
if (err)
|
||||
goto out;
|
||||
err = write_merkle_tree_block(inode,
|
||||
err = write_merkle_tree_block(filp,
|
||||
buffers[level].data,
|
||||
level_offset[level],
|
||||
params);
|
||||
|
|
@ -264,9 +265,26 @@ static int enable_verity(struct file *filp,
|
|||
goto rollback;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the fsverity_info into the hash table before finishing the
|
||||
* initialization so that we don't have to undo the enabling when memory
|
||||
* allocation for the hash table fails. This is safe because looking up
|
||||
* the fsverity_info always first checks the S_VERITY flag on the inode,
|
||||
* which will only be set at the very end of the ->end_enable_verity
|
||||
* method.
|
||||
*/
|
||||
err = fsverity_set_info(vi);
|
||||
if (err) {
|
||||
fsverity_free_info(vi);
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the filesystem to finish enabling verity on the file.
|
||||
* Serialized with ->begin_enable_verity() by the inode lock.
|
||||
* Serialized with ->begin_enable_verity() by the inode lock. The file
|
||||
* system needs to set the S_VERITY flag on the inode at the very end of
|
||||
* the method, at which point the fsverity information can be accessed
|
||||
* by other threads.
|
||||
*/
|
||||
inode_lock(inode);
|
||||
err = vops->end_enable_verity(filp, desc, desc_size, params.tree_size);
|
||||
|
|
@ -274,19 +292,10 @@ static int enable_verity(struct file *filp,
|
|||
if (err) {
|
||||
fsverity_err(inode, "%ps() failed with err %d",
|
||||
vops->end_enable_verity, err);
|
||||
fsverity_free_info(vi);
|
||||
fsverity_remove_info(vi);
|
||||
} else if (WARN_ON_ONCE(!IS_VERITY(inode))) {
|
||||
fsverity_remove_info(vi);
|
||||
err = -EINVAL;
|
||||
fsverity_free_info(vi);
|
||||
} else {
|
||||
/* Successfully enabled verity */
|
||||
|
||||
/*
|
||||
* Readers can start using the inode's verity info immediately,
|
||||
* so it can't be rolled back once set. So don't set it until
|
||||
* just after the filesystem has successfully enabled verity.
|
||||
*/
|
||||
fsverity_set_info(inode, vi);
|
||||
}
|
||||
out:
|
||||
kfree(params.hashstate);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#define pr_fmt(fmt) "fs-verity: " fmt
|
||||
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/rhashtable.h>
|
||||
|
||||
/*
|
||||
* Implementation limit: maximum depth of the Merkle tree. For now 8 is plenty;
|
||||
|
|
@ -63,17 +64,18 @@ struct merkle_tree_params {
|
|||
* fsverity_info - cached verity metadata for an inode
|
||||
*
|
||||
* When a verity file is first opened, an instance of this struct is allocated
|
||||
* and a pointer to it is stored in the file's in-memory inode. 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.
|
||||
* and a pointer to it is stored in the global hash table, indexed by the inode
|
||||
* pointer value. It remains alive 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.
|
||||
*/
|
||||
struct fsverity_info {
|
||||
struct rhash_head rhash_head;
|
||||
struct merkle_tree_params tree_params;
|
||||
u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE];
|
||||
u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE];
|
||||
const struct inode *inode;
|
||||
struct inode *inode;
|
||||
unsigned long *hash_block_verified;
|
||||
};
|
||||
|
||||
|
|
@ -124,12 +126,12 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
|
|||
unsigned int log_blocksize,
|
||||
const u8 *salt, size_t salt_size);
|
||||
|
||||
struct fsverity_info *fsverity_create_info(const struct inode *inode,
|
||||
struct fsverity_info *fsverity_create_info(struct inode *inode,
|
||||
struct fsverity_descriptor *desc);
|
||||
|
||||
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi);
|
||||
|
||||
int fsverity_set_info(struct fsverity_info *vi);
|
||||
void fsverity_free_info(struct fsverity_info *vi);
|
||||
void fsverity_remove_info(struct fsverity_info *vi);
|
||||
|
||||
int fsverity_get_descriptor(struct inode *inode,
|
||||
struct fsverity_descriptor **desc_ret);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@
|
|||
#include <linux/slab.h>
|
||||
|
||||
static struct kmem_cache *fsverity_info_cachep;
|
||||
static struct rhashtable fsverity_info_hash;
|
||||
|
||||
static const struct rhashtable_params fsverity_info_hash_params = {
|
||||
.key_len = sizeof_field(struct fsverity_info, inode),
|
||||
.key_offset = offsetof(struct fsverity_info, inode),
|
||||
.head_offset = offsetof(struct fsverity_info, rhash_head),
|
||||
.automatic_shrinking = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* fsverity_init_merkle_tree_params() - initialize Merkle tree parameters
|
||||
|
|
@ -175,7 +183,7 @@ static void compute_file_digest(const struct fsverity_hash_alg *hash_alg,
|
|||
* appended builtin signature), and check the signature if present. The
|
||||
* fsverity_descriptor must have already undergone basic validation.
|
||||
*/
|
||||
struct fsverity_info *fsverity_create_info(const struct inode *inode,
|
||||
struct fsverity_info *fsverity_create_info(struct inode *inode,
|
||||
struct fsverity_descriptor *desc)
|
||||
{
|
||||
struct fsverity_info *vi;
|
||||
|
|
@ -241,33 +249,19 @@ fail:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
void fsverity_set_info(struct inode *inode, struct fsverity_info *vi)
|
||||
int fsverity_set_info(struct fsverity_info *vi)
|
||||
{
|
||||
/*
|
||||
* Multiple tasks may race to set the inode's verity info pointer, so
|
||||
* use cmpxchg_release(). This pairs with the smp_load_acquire() in
|
||||
* fsverity_get_info(). I.e., publish the pointer with a RELEASE
|
||||
* barrier so that other tasks can ACQUIRE it.
|
||||
*/
|
||||
if (cmpxchg_release(fsverity_info_addr(inode), NULL, vi) != NULL) {
|
||||
/* Lost the race, so free the verity info we allocated. */
|
||||
fsverity_free_info(vi);
|
||||
/*
|
||||
* Afterwards, the caller may access the inode's verity info
|
||||
* directly, so make sure to ACQUIRE the winning verity info.
|
||||
*/
|
||||
(void)fsverity_get_info(inode);
|
||||
}
|
||||
return rhashtable_lookup_insert_fast(&fsverity_info_hash,
|
||||
&vi->rhash_head,
|
||||
fsverity_info_hash_params);
|
||||
}
|
||||
|
||||
void fsverity_free_info(struct fsverity_info *vi)
|
||||
struct fsverity_info *__fsverity_get_info(const struct inode *inode)
|
||||
{
|
||||
if (!vi)
|
||||
return;
|
||||
kfree(vi->tree_params.hashstate);
|
||||
kvfree(vi->hash_block_verified);
|
||||
kmem_cache_free(fsverity_info_cachep, vi);
|
||||
return rhashtable_lookup_fast(&fsverity_info_hash, &inode,
|
||||
fsverity_info_hash_params);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__fsverity_get_info);
|
||||
|
||||
static bool validate_fsverity_descriptor(struct inode *inode,
|
||||
const struct fsverity_descriptor *desc,
|
||||
|
|
@ -352,7 +346,7 @@ int fsverity_get_descriptor(struct inode *inode,
|
|||
|
||||
static int ensure_verity_info(struct inode *inode)
|
||||
{
|
||||
struct fsverity_info *vi = fsverity_get_info(inode);
|
||||
struct fsverity_info *vi = fsverity_get_info(inode), *found;
|
||||
struct fsverity_descriptor *desc;
|
||||
int err;
|
||||
|
||||
|
|
@ -369,8 +363,19 @@ static int ensure_verity_info(struct inode *inode)
|
|||
goto out_free_desc;
|
||||
}
|
||||
|
||||
fsverity_set_info(inode, vi);
|
||||
err = 0;
|
||||
/*
|
||||
* Multiple tasks may race to set the inode's verity info, in which case
|
||||
* we might find an existing fsverity_info in the hash table.
|
||||
*/
|
||||
found = rhashtable_lookup_get_insert_fast(&fsverity_info_hash,
|
||||
&vi->rhash_head,
|
||||
fsverity_info_hash_params);
|
||||
if (found) {
|
||||
fsverity_free_info(vi);
|
||||
if (IS_ERR(found))
|
||||
err = PTR_ERR(found);
|
||||
}
|
||||
|
||||
out_free_desc:
|
||||
kfree(desc);
|
||||
return err;
|
||||
|
|
@ -384,25 +389,32 @@ int __fsverity_file_open(struct inode *inode, struct file *filp)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__fsverity_file_open);
|
||||
|
||||
int __fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
void fsverity_free_info(struct fsverity_info *vi)
|
||||
{
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
return -EPERM;
|
||||
return 0;
|
||||
kfree(vi->tree_params.hashstate);
|
||||
kvfree(vi->hash_block_verified);
|
||||
kmem_cache_free(fsverity_info_cachep, vi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__fsverity_prepare_setattr);
|
||||
|
||||
void __fsverity_cleanup_inode(struct inode *inode)
|
||||
void fsverity_remove_info(struct fsverity_info *vi)
|
||||
{
|
||||
struct fsverity_info **vi_addr = fsverity_info_addr(inode);
|
||||
|
||||
fsverity_free_info(*vi_addr);
|
||||
*vi_addr = NULL;
|
||||
rhashtable_remove_fast(&fsverity_info_hash, &vi->rhash_head,
|
||||
fsverity_info_hash_params);
|
||||
fsverity_free_info(vi);
|
||||
}
|
||||
|
||||
void fsverity_cleanup_inode(struct inode *inode)
|
||||
{
|
||||
struct fsverity_info *vi = fsverity_get_info(inode);
|
||||
|
||||
if (vi)
|
||||
fsverity_remove_info(vi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__fsverity_cleanup_inode);
|
||||
|
||||
void __init fsverity_init_info_cache(void)
|
||||
{
|
||||
if (rhashtable_init(&fsverity_info_hash, &fsverity_info_hash_params))
|
||||
panic("failed to initialize fsverity hash\n");
|
||||
fsverity_info_cachep = KMEM_CACHE_USERCOPY(
|
||||
fsverity_info,
|
||||
SLAB_RECLAIM_ACCOUNT | SLAB_PANIC,
|
||||
|
|
|
|||
58
fs/verity/pagecache.c
Normal file
58
fs/verity/pagecache.c
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
/**
|
||||
* generic_read_merkle_tree_page - generic ->read_merkle_tree_page helper
|
||||
* @inode: inode containing the Merkle tree
|
||||
* @index: 0-based index of the Merkle tree page in the inode
|
||||
*
|
||||
* The caller needs to adjust @index from the Merkle-tree relative index passed
|
||||
* to ->read_merkle_tree_page to the actual index where the Merkle tree is
|
||||
* stored in the page cache for @inode.
|
||||
*/
|
||||
struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
folio = read_mapping_folio(inode->i_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
return ERR_CAST(folio);
|
||||
return folio_file_page(folio, index);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_read_merkle_tree_page);
|
||||
|
||||
/**
|
||||
* generic_readahead_merkle_tree() - generic ->readahead_merkle_tree helper
|
||||
* @inode: inode containing the Merkle tree
|
||||
* @index: 0-based index of the first Merkle tree page to read ahead in the
|
||||
* inode
|
||||
* @nr_pages: the number of Merkle tree pages that should be read ahead
|
||||
*
|
||||
* The caller needs to adjust @index from the Merkle-tree relative index passed
|
||||
* to ->read_merkle_tree_page to the actual index where the Merkle tree is
|
||||
* stored in the page cache for @inode.
|
||||
*/
|
||||
void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
lockdep_assert_held(&inode->i_mapping->invalidate_lock);
|
||||
|
||||
folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
|
||||
if (folio == ERR_PTR(-ENOENT) ||
|
||||
(!IS_ERR(folio) && !folio_test_uptodate(folio))) {
|
||||
DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);
|
||||
|
||||
page_cache_ra_unbounded(&ractl, nr_pages, 0);
|
||||
}
|
||||
if (!IS_ERR(folio))
|
||||
folio_put(folio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_readahead_merkle_tree);
|
||||
|
|
@ -28,24 +28,33 @@ static int fsverity_read_merkle_tree(struct inode *inode,
|
|||
if (offset >= end_offset)
|
||||
return 0;
|
||||
offs_in_page = offset_in_page(offset);
|
||||
index = offset >> PAGE_SHIFT;
|
||||
last_index = (end_offset - 1) >> PAGE_SHIFT;
|
||||
|
||||
/*
|
||||
* Kick off readahead for the range we are going to read to ensure a
|
||||
* single large sequential read instead of lots of small ones.
|
||||
*/
|
||||
if (inode->i_sb->s_vop->readahead_merkle_tree) {
|
||||
filemap_invalidate_lock_shared(inode->i_mapping);
|
||||
inode->i_sb->s_vop->readahead_merkle_tree(
|
||||
inode, index, last_index - index + 1);
|
||||
filemap_invalidate_unlock_shared(inode->i_mapping);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
for (; index <= last_index; index++) {
|
||||
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);
|
||||
page = vops->read_merkle_tree_page(inode, index);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
fsverity_err(inode,
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ struct fsverity_pending_block {
|
|||
};
|
||||
|
||||
struct fsverity_verification_context {
|
||||
struct inode *inode;
|
||||
struct fsverity_info *vi;
|
||||
unsigned long max_ra_pages;
|
||||
|
||||
/*
|
||||
* This is the queue of data blocks that are pending verification. When
|
||||
|
|
@ -37,6 +35,50 @@ struct fsverity_verification_context {
|
|||
|
||||
static struct workqueue_struct *fsverity_read_workqueue;
|
||||
|
||||
/**
|
||||
* fsverity_readahead() - kick off readahead on fsverity hashes
|
||||
* @vi: fsverity_info for the inode to be read
|
||||
* @index: first file data page index that is being read
|
||||
* @nr_pages: number of file data pages to be read
|
||||
*
|
||||
* Start readahead on the fsverity hashes that are needed to verify the file
|
||||
* data in the range from @index to @index + @nr_pages (exclusive upper bound).
|
||||
*
|
||||
* To be called from the file systems' ->read_folio and ->readahead methods to
|
||||
* ensure that the hashes are already cached on completion of the file data
|
||||
* read if possible.
|
||||
*/
|
||||
void fsverity_readahead(struct fsverity_info *vi, pgoff_t index,
|
||||
unsigned long nr_pages)
|
||||
{
|
||||
struct inode *inode = vi->inode;
|
||||
const struct merkle_tree_params *params = &vi->tree_params;
|
||||
u64 start_hidx = (u64)index << params->log_blocks_per_page;
|
||||
u64 end_hidx =
|
||||
(((u64)index + nr_pages) << params->log_blocks_per_page) - 1;
|
||||
int level;
|
||||
|
||||
if (!inode->i_sb->s_vop->readahead_merkle_tree)
|
||||
return;
|
||||
|
||||
for (level = 0; level < params->num_levels; level++) {
|
||||
unsigned long level_start = params->level_start[level];
|
||||
unsigned long next_start_hidx = start_hidx >> params->log_arity;
|
||||
unsigned long next_end_hidx = end_hidx >> params->log_arity;
|
||||
pgoff_t start_idx = (level_start + next_start_hidx) >>
|
||||
params->log_blocks_per_page;
|
||||
pgoff_t end_idx = (level_start + next_end_hidx) >>
|
||||
params->log_blocks_per_page;
|
||||
|
||||
inode->i_sb->s_vop->readahead_merkle_tree(
|
||||
inode, start_idx, end_idx - start_idx + 1);
|
||||
|
||||
start_hidx = next_start_hidx;
|
||||
end_hidx = next_end_hidx;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsverity_readahead);
|
||||
|
||||
/*
|
||||
* Returns true if the hash block with index @hblock_idx in the tree, located in
|
||||
* @hpage, has already been verified.
|
||||
|
|
@ -113,10 +155,10 @@ static bool is_hash_block_verified(struct fsverity_info *vi, struct page *hpage,
|
|||
*
|
||||
* Return: %true if the data block is valid, else %false.
|
||||
*/
|
||||
static bool verify_data_block(struct inode *inode, struct fsverity_info *vi,
|
||||
const struct fsverity_pending_block *dblock,
|
||||
unsigned long max_ra_pages)
|
||||
static bool verify_data_block(struct fsverity_info *vi,
|
||||
const struct fsverity_pending_block *dblock)
|
||||
{
|
||||
struct inode *inode = vi->inode;
|
||||
const u64 data_pos = dblock->pos;
|
||||
const struct merkle_tree_params *params = &vi->tree_params;
|
||||
const unsigned int hsize = params->digest_size;
|
||||
|
|
@ -200,8 +242,7 @@ static bool verify_data_block(struct inode *inode, struct fsverity_info *vi,
|
|||
(params->block_size - 1);
|
||||
|
||||
hpage = inode->i_sb->s_vop->read_merkle_tree_page(inode,
|
||||
hpage_idx, level == 0 ? min(max_ra_pages,
|
||||
params->tree_pages - hpage_idx) : 0);
|
||||
hpage_idx);
|
||||
if (IS_ERR(hpage)) {
|
||||
fsverity_err(inode,
|
||||
"Error %ld reading Merkle tree page %lu",
|
||||
|
|
@ -272,14 +313,9 @@ error:
|
|||
|
||||
static void
|
||||
fsverity_init_verification_context(struct fsverity_verification_context *ctx,
|
||||
struct inode *inode,
|
||||
unsigned long max_ra_pages)
|
||||
struct fsverity_info *vi)
|
||||
{
|
||||
struct fsverity_info *vi = *fsverity_info_addr(inode);
|
||||
|
||||
ctx->inode = inode;
|
||||
ctx->vi = vi;
|
||||
ctx->max_ra_pages = max_ra_pages;
|
||||
ctx->num_pending = 0;
|
||||
if (vi->tree_params.hash_alg->algo_id == HASH_ALGO_SHA256 &&
|
||||
sha256_finup_2x_is_optimized())
|
||||
|
|
@ -322,8 +358,7 @@ fsverity_verify_pending_blocks(struct fsverity_verification_context *ctx)
|
|||
}
|
||||
|
||||
for (i = 0; i < ctx->num_pending; i++) {
|
||||
if (!verify_data_block(ctx->inode, vi, &ctx->pending_blocks[i],
|
||||
ctx->max_ra_pages))
|
||||
if (!verify_data_block(vi, &ctx->pending_blocks[i]))
|
||||
return false;
|
||||
}
|
||||
fsverity_clear_pending_blocks(ctx);
|
||||
|
|
@ -359,6 +394,7 @@ static bool fsverity_add_data_blocks(struct fsverity_verification_context *ctx,
|
|||
|
||||
/**
|
||||
* fsverity_verify_blocks() - verify data in a folio
|
||||
* @vi: fsverity_info for the inode to be read
|
||||
* @folio: the folio containing the data to verify
|
||||
* @len: the length of the data to verify in the folio
|
||||
* @offset: the offset of the data to verify in the folio
|
||||
|
|
@ -369,11 +405,12 @@ static bool fsverity_add_data_blocks(struct fsverity_verification_context *ctx,
|
|||
*
|
||||
* Return: %true if the data is valid, else %false.
|
||||
*/
|
||||
bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset)
|
||||
bool fsverity_verify_blocks(struct fsverity_info *vi, struct folio *folio,
|
||||
size_t len, size_t offset)
|
||||
{
|
||||
struct fsverity_verification_context ctx;
|
||||
|
||||
fsverity_init_verification_context(&ctx, folio->mapping->host, 0);
|
||||
fsverity_init_verification_context(&ctx, vi);
|
||||
|
||||
if (fsverity_add_data_blocks(&ctx, folio, len, offset) &&
|
||||
fsverity_verify_pending_blocks(&ctx))
|
||||
|
|
@ -386,6 +423,7 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
|
|||
#ifdef CONFIG_BLOCK
|
||||
/**
|
||||
* fsverity_verify_bio() - verify a 'read' bio that has just completed
|
||||
* @vi: fsverity_info for the inode to be read
|
||||
* @bio: the bio to verify
|
||||
*
|
||||
* Verify the bio's data against the file's Merkle tree. All bio data segments
|
||||
|
|
@ -398,27 +436,12 @@ EXPORT_SYMBOL_GPL(fsverity_verify_blocks);
|
|||
* filesystems) must instead call fsverity_verify_page() directly on each page.
|
||||
* All filesystems must also call fsverity_verify_page() on holes.
|
||||
*/
|
||||
void fsverity_verify_bio(struct bio *bio)
|
||||
void fsverity_verify_bio(struct fsverity_info *vi, struct bio *bio)
|
||||
{
|
||||
struct inode *inode = bio_first_folio_all(bio)->mapping->host;
|
||||
struct fsverity_verification_context ctx;
|
||||
struct folio_iter fi;
|
||||
unsigned long max_ra_pages = 0;
|
||||
|
||||
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.
|
||||
*/
|
||||
max_ra_pages = bio->bi_iter.bi_size >> (PAGE_SHIFT + 2);
|
||||
}
|
||||
|
||||
fsverity_init_verification_context(&ctx, inode, max_ra_pages);
|
||||
fsverity_init_verification_context(&ctx, vi);
|
||||
|
||||
bio_for_each_folio_all(fi, bio) {
|
||||
if (!fsverity_add_data_blocks(&ctx, fi.folio, fi.length,
|
||||
|
|
|
|||
|
|
@ -30,13 +30,6 @@ struct fsverity_info;
|
|||
|
||||
/* Verity operations for filesystems */
|
||||
struct fsverity_operations {
|
||||
/**
|
||||
* The offset of the pointer to struct fsverity_info in the
|
||||
* filesystem-specific part of the inode, relative to the beginning of
|
||||
* the common part of the inode (the 'struct inode').
|
||||
*/
|
||||
ptrdiff_t inode_info_offs;
|
||||
|
||||
/**
|
||||
* Begin enabling verity on the given file.
|
||||
*
|
||||
|
|
@ -97,10 +90,6 @@ struct fsverity_operations {
|
|||
*
|
||||
* @inode: the inode
|
||||
* @index: 0-based index of the page within the Merkle tree
|
||||
* @num_ra_pages: The number of Merkle tree pages that should be
|
||||
* prefetched starting at @index if the page at @index
|
||||
* isn't already cached. Implementations may ignore this
|
||||
* argument; it's only a performance optimization.
|
||||
*
|
||||
* This can be called at any time on an open verity file. It may be
|
||||
* called by multiple processes concurrently, even with the same page.
|
||||
|
|
@ -110,13 +99,28 @@ struct fsverity_operations {
|
|||
* Return: the page on success, ERR_PTR() on failure
|
||||
*/
|
||||
struct page *(*read_merkle_tree_page)(struct inode *inode,
|
||||
pgoff_t index,
|
||||
unsigned long num_ra_pages);
|
||||
pgoff_t index);
|
||||
|
||||
/**
|
||||
* Write a Merkle tree block to the given inode.
|
||||
* Perform readahead of a Merkle tree for the given inode.
|
||||
*
|
||||
* @inode: the inode for which the Merkle tree is being built
|
||||
* @inode: the inode
|
||||
* @index: 0-based index of the first page within the Merkle tree
|
||||
* @nr_pages: number of pages to be read ahead.
|
||||
*
|
||||
* This can be called at any time on an open verity file. It may be
|
||||
* called by multiple processes concurrently, even with the same range.
|
||||
*
|
||||
* Optional method so that ->read_merkle_tree_page preferably finds
|
||||
* cached data instead of issuing dependent I/O.
|
||||
*/
|
||||
void (*readahead_merkle_tree)(struct inode *inode, pgoff_t index,
|
||||
unsigned long nr_pages);
|
||||
|
||||
/**
|
||||
* Write a Merkle tree block to the given file.
|
||||
*
|
||||
* @file: the file for which the Merkle tree is being built
|
||||
* @buf: the Merkle tree block to write
|
||||
* @pos: the position of the block in the Merkle tree (in bytes)
|
||||
* @size: the Merkle tree block size (in bytes)
|
||||
|
|
@ -126,43 +130,48 @@ struct fsverity_operations {
|
|||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int (*write_merkle_tree_block)(struct inode *inode, const void *buf,
|
||||
int (*write_merkle_tree_block)(struct file *file, const void *buf,
|
||||
u64 pos, unsigned int size);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
|
||||
/*
|
||||
* Returns the address of the verity info pointer within the filesystem-specific
|
||||
* part of the inode. (To save memory on filesystems that don't support
|
||||
* fsverity, a field in 'struct inode' itself is no longer used.)
|
||||
/**
|
||||
* fsverity_active() - do reads from the inode need to go through fs-verity?
|
||||
* @inode: inode to check
|
||||
*
|
||||
* This checks whether the inode's verity info has been set, and reads need
|
||||
* to verify the file data.
|
||||
*
|
||||
* Return: true if reads need to go through fs-verity, otherwise false
|
||||
*/
|
||||
static inline struct fsverity_info **
|
||||
fsverity_info_addr(const struct inode *inode)
|
||||
static inline bool fsverity_active(const struct inode *inode)
|
||||
{
|
||||
VFS_WARN_ON_ONCE(inode->i_sb->s_vop->inode_info_offs == 0);
|
||||
return (void *)inode + inode->i_sb->s_vop->inode_info_offs;
|
||||
if (IS_VERITY(inode)) {
|
||||
/*
|
||||
* This pairs with the try_cmpxchg in set_mask_bits()
|
||||
* used to set the S_VERITY bit in i_flags.
|
||||
*/
|
||||
smp_mb();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct fsverity_info *__fsverity_get_info(const struct inode *inode);
|
||||
/**
|
||||
* fsverity_get_info - get fsverity information for an inode
|
||||
* @inode: inode to operate on.
|
||||
*
|
||||
* This gets the fsverity_info for @inode if it exists. Safe to call without
|
||||
* knowin that a fsverity_info exist for @inode, including on file systems that
|
||||
* do not support fsverity.
|
||||
*/
|
||||
static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
|
||||
{
|
||||
/*
|
||||
* Since this function can be called on inodes belonging to filesystems
|
||||
* that don't support fsverity at all, and fsverity_info_addr() doesn't
|
||||
* work on such filesystems, we have to start with an IS_VERITY() check.
|
||||
* Checking IS_VERITY() here is also useful to minimize the overhead of
|
||||
* fsverity_active() on non-verity files.
|
||||
*/
|
||||
if (!IS_VERITY(inode))
|
||||
if (!fsverity_active(inode))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Pairs with the cmpxchg_release() in fsverity_set_info(). I.e.,
|
||||
* another task may publish the inode's verity info concurrently,
|
||||
* executing a RELEASE barrier. Use smp_load_acquire() here to safely
|
||||
* ACQUIRE the memory the other task published.
|
||||
*/
|
||||
return smp_load_acquire(fsverity_info_addr(inode));
|
||||
return __fsverity_get_info(inode);
|
||||
}
|
||||
|
||||
/* enable.c */
|
||||
|
|
@ -179,27 +188,6 @@ int fsverity_get_digest(struct inode *inode,
|
|||
/* open.c */
|
||||
|
||||
int __fsverity_file_open(struct inode *inode, struct file *filp);
|
||||
int __fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
void __fsverity_cleanup_inode(struct inode *inode);
|
||||
|
||||
/**
|
||||
* 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 the inode's verity info.
|
||||
*/
|
||||
static inline void fsverity_cleanup_inode(struct inode *inode)
|
||||
{
|
||||
/*
|
||||
* Only IS_VERITY() inodes can have verity info, so start by checking
|
||||
* for IS_VERITY() (which is faster than retrieving the pointer to the
|
||||
* verity info). This minimizes overhead for non-verity inodes.
|
||||
*/
|
||||
if (IS_VERITY(inode))
|
||||
__fsverity_cleanup_inode(inode);
|
||||
else
|
||||
VFS_WARN_ON_ONCE(*fsverity_info_addr(inode) != NULL);
|
||||
}
|
||||
|
||||
/* read_metadata.c */
|
||||
|
||||
|
|
@ -207,12 +195,18 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg);
|
|||
|
||||
/* verify.c */
|
||||
|
||||
bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset);
|
||||
void fsverity_verify_bio(struct bio *bio);
|
||||
bool fsverity_verify_blocks(struct fsverity_info *vi, struct folio *folio,
|
||||
size_t len, size_t offset);
|
||||
void fsverity_verify_bio(struct fsverity_info *vi, struct bio *bio);
|
||||
void fsverity_enqueue_verify_work(struct work_struct *work);
|
||||
|
||||
#else /* !CONFIG_FS_VERITY */
|
||||
|
||||
static inline bool fsverity_active(const struct inode *inode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -251,16 +245,6 @@ static inline int __fsverity_file_open(struct inode *inode, struct file *filp)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __fsverity_prepare_setattr(struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void fsverity_cleanup_inode(struct inode *inode)
|
||||
{
|
||||
}
|
||||
|
||||
/* read_metadata.c */
|
||||
|
||||
static inline int fsverity_ioctl_read_metadata(struct file *filp,
|
||||
|
|
@ -271,14 +255,16 @@ static inline int fsverity_ioctl_read_metadata(struct file *filp,
|
|||
|
||||
/* verify.c */
|
||||
|
||||
static inline bool fsverity_verify_blocks(struct folio *folio, size_t len,
|
||||
static inline bool fsverity_verify_blocks(struct fsverity_info *vi,
|
||||
struct folio *folio, size_t len,
|
||||
size_t offset)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void fsverity_verify_bio(struct bio *bio)
|
||||
static inline void fsverity_verify_bio(struct fsverity_info *vi,
|
||||
struct bio *bio)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
|
@ -290,32 +276,16 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
|
|||
|
||||
#endif /* !CONFIG_FS_VERITY */
|
||||
|
||||
static inline bool fsverity_verify_folio(struct folio *folio)
|
||||
static inline bool fsverity_verify_folio(struct fsverity_info *vi,
|
||||
struct folio *folio)
|
||||
{
|
||||
return fsverity_verify_blocks(folio, folio_size(folio), 0);
|
||||
return fsverity_verify_blocks(vi, folio, folio_size(folio), 0);
|
||||
}
|
||||
|
||||
static inline bool fsverity_verify_page(struct page *page)
|
||||
static inline bool fsverity_verify_page(struct fsverity_info *vi,
|
||||
struct page *page)
|
||||
{
|
||||
return fsverity_verify_blocks(page_folio(page), PAGE_SIZE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* fsverity_active() - do reads from the inode need to go through fs-verity?
|
||||
* @inode: inode to check
|
||||
*
|
||||
* This checks whether the inode's verity info has been set.
|
||||
*
|
||||
* Filesystems call this from ->readahead() to check whether the pages need to
|
||||
* be verified or not. Don't use IS_VERITY() for this purpose; it's subject to
|
||||
* a race condition where the file is being read concurrently with
|
||||
* FS_IOC_ENABLE_VERITY completing. (S_VERITY is set before the verity info.)
|
||||
*
|
||||
* Return: true if reads need to go through fs-verity, otherwise false
|
||||
*/
|
||||
static inline bool fsverity_active(const struct inode *inode)
|
||||
{
|
||||
return fsverity_get_info(inode) != NULL;
|
||||
return fsverity_verify_blocks(vi, page_folio(page), PAGE_SIZE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -338,22 +308,12 @@ static inline int fsverity_file_open(struct inode *inode, struct file *filp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fsverity_prepare_setattr() - prepare to change a verity inode's attributes
|
||||
* @dentry: dentry through which the inode is being changed
|
||||
* @attr: attributes to change
|
||||
*
|
||||
* Verity files are immutable, so deny truncates. This isn't covered by the
|
||||
* open-time check because sys_truncate() takes a path, not a file descriptor.
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static inline int fsverity_prepare_setattr(struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
{
|
||||
if (IS_VERITY(d_inode(dentry)))
|
||||
return __fsverity_prepare_setattr(dentry, attr);
|
||||
return 0;
|
||||
}
|
||||
void fsverity_cleanup_inode(struct inode *inode);
|
||||
void fsverity_readahead(struct fsverity_info *vi, pgoff_t index,
|
||||
unsigned long nr_pages);
|
||||
|
||||
struct page *generic_read_merkle_tree_page(struct inode *inode, pgoff_t index);
|
||||
void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,
|
||||
unsigned long nr_pages);
|
||||
|
||||
#endif /* _LINUX_FSVERITY_H */
|
||||
|
|
|
|||
|
|
@ -204,8 +204,9 @@ static struct folio *ractl_alloc_folio(struct readahead_control *ractl,
|
|||
* not the function you want to call. Use page_cache_async_readahead()
|
||||
* or page_cache_sync_readahead() instead.
|
||||
*
|
||||
* Context: File is referenced by caller. Mutexes may be held by caller.
|
||||
* May sleep, but will not reenter filesystem to reclaim memory.
|
||||
* Context: File is referenced by caller, and ractl->mapping->invalidate_lock
|
||||
* must be held by the caller at least in shared mode. Mutexes may be held by
|
||||
* caller. May sleep, but will not reenter filesystem to reclaim memory.
|
||||
*/
|
||||
void page_cache_ra_unbounded(struct readahead_control *ractl,
|
||||
unsigned long nr_to_read, unsigned long lookahead_size)
|
||||
|
|
@ -228,9 +229,10 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
|
|||
*/
|
||||
unsigned int nofs = memalloc_nofs_save();
|
||||
|
||||
lockdep_assert_held(&mapping->invalidate_lock);
|
||||
|
||||
trace_page_cache_ra_unbounded(mapping->host, index, nr_to_read,
|
||||
lookahead_size);
|
||||
filemap_invalidate_lock_shared(mapping);
|
||||
index = mapping_align_index(mapping, index);
|
||||
|
||||
/*
|
||||
|
|
@ -300,7 +302,6 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
|
|||
* will then handle the error.
|
||||
*/
|
||||
read_pages(ractl);
|
||||
filemap_invalidate_unlock_shared(mapping);
|
||||
memalloc_nofs_restore(nofs);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(page_cache_ra_unbounded);
|
||||
|
|
@ -314,9 +315,9 @@ EXPORT_SYMBOL_GPL(page_cache_ra_unbounded);
|
|||
static void do_page_cache_ra(struct readahead_control *ractl,
|
||||
unsigned long nr_to_read, unsigned long lookahead_size)
|
||||
{
|
||||
struct inode *inode = ractl->mapping->host;
|
||||
struct address_space *mapping = ractl->mapping;
|
||||
unsigned long index = readahead_index(ractl);
|
||||
loff_t isize = i_size_read(inode);
|
||||
loff_t isize = i_size_read(mapping->host);
|
||||
pgoff_t end_index; /* The last page we want to read */
|
||||
|
||||
if (isize == 0)
|
||||
|
|
@ -329,7 +330,9 @@ static void do_page_cache_ra(struct readahead_control *ractl,
|
|||
if (nr_to_read > end_index - index)
|
||||
nr_to_read = end_index - index + 1;
|
||||
|
||||
filemap_invalidate_lock_shared(mapping);
|
||||
page_cache_ra_unbounded(ractl, nr_to_read, lookahead_size);
|
||||
filemap_invalidate_unlock_shared(mapping);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue