diff options
Diffstat (limited to 'fs/buffer.c')
| -rw-r--r-- | fs/buffer.c | 72 | 
1 files changed, 60 insertions, 12 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index d9c6d1fbb6dd..623e77d6ef77 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -48,6 +48,7 @@  #include <linux/sched/mm.h>  #include <trace/events/block.h>  #include <linux/fscrypt.h> +#include <linux/fsverity.h>  #include "internal.h" @@ -295,20 +296,53 @@ still_busy:  	return;  } -struct decrypt_bh_ctx { +struct postprocess_bh_ctx {  	struct work_struct work;  	struct buffer_head *bh;  }; +static void verify_bh(struct work_struct *work) +{ +	struct postprocess_bh_ctx *ctx = +		container_of(work, struct postprocess_bh_ctx, work); +	struct buffer_head *bh = ctx->bh; +	bool valid; + +	valid = fsverity_verify_blocks(page_folio(bh->b_page), bh->b_size, +				       bh_offset(bh)); +	end_buffer_async_read(bh, valid); +	kfree(ctx); +} + +static bool need_fsverity(struct buffer_head *bh) +{ +	struct page *page = bh->b_page; +	struct inode *inode = page->mapping->host; + +	return fsverity_active(inode) && +		/* needed by ext4 */ +		page->index < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); +} +  static void decrypt_bh(struct work_struct *work)  { -	struct decrypt_bh_ctx *ctx = -		container_of(work, struct decrypt_bh_ctx, work); +	struct postprocess_bh_ctx *ctx = +		container_of(work, struct postprocess_bh_ctx, work);  	struct buffer_head *bh = ctx->bh;  	int err; -	err = fscrypt_decrypt_pagecache_blocks(bh->b_page, bh->b_size, -					       bh_offset(bh)); +	err = fscrypt_decrypt_pagecache_blocks(page_folio(bh->b_page), +					       bh->b_size, bh_offset(bh)); +	if (err == 0 && need_fsverity(bh)) { +		/* +		 * We use different work queues for decryption and for verity +		 * because verity may require reading metadata pages that need +		 * decryption, and we shouldn't recurse to the same workqueue. +		 */ +		INIT_WORK(&ctx->work, verify_bh); +		fsverity_enqueue_verify_work(&ctx->work); +		return; +	}  	end_buffer_async_read(bh, err == 0);  	kfree(ctx);  } @@ -319,15 +353,24 @@ static void decrypt_bh(struct work_struct *work)   */  static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate)  { -	/* Decrypt if needed */ -	if (uptodate && -	    fscrypt_inode_uses_fs_layer_crypto(bh->b_page->mapping->host)) { -		struct decrypt_bh_ctx *ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); +	struct inode *inode = bh->b_page->mapping->host; +	bool decrypt = fscrypt_inode_uses_fs_layer_crypto(inode); +	bool verify = need_fsverity(bh); + +	/* Decrypt (with fscrypt) and/or verify (with fsverity) if needed. */ +	if (uptodate && (decrypt || verify)) { +		struct postprocess_bh_ctx *ctx = +			kmalloc(sizeof(*ctx), GFP_ATOMIC);  		if (ctx) { -			INIT_WORK(&ctx->work, decrypt_bh);  			ctx->bh = bh; -			fscrypt_enqueue_decrypt_work(&ctx->work); +			if (decrypt) { +				INIT_WORK(&ctx->work, decrypt_bh); +				fscrypt_enqueue_decrypt_work(&ctx->work); +			} else { +				INIT_WORK(&ctx->work, verify_bh); +				fsverity_enqueue_verify_work(&ctx->work); +			}  			return;  		}  		uptodate = 0; @@ -2245,6 +2288,11 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)  	int nr, i;  	int fully_mapped = 1;  	bool page_error = false; +	loff_t limit = i_size_read(inode); + +	/* This is needed for ext4. */ +	if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode)) +		limit = inode->i_sb->s_maxbytes;  	VM_BUG_ON_FOLIO(folio_test_large(folio), folio); @@ -2253,7 +2301,7 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)  	bbits = block_size_bits(blocksize);  	iblock = (sector_t)folio->index << (PAGE_SHIFT - bbits); -	lblock = (i_size_read(inode)+blocksize-1) >> bbits; +	lblock = (limit+blocksize-1) >> bbits;  	bh = head;  	nr = 0;  	i = 0;  |