diff mbox series

fscrypt: support decrypting data from large folios

Message ID 20230127224202.355629-1-ebiggers@kernel.org (mailing list archive)
State Accepted
Headers show
Series fscrypt: support decrypting data from large folios | expand

Commit Message

Eric Biggers Jan. 27, 2023, 10:42 p.m. UTC
From: Eric Biggers <ebiggers@google.com>

Try to make the filesystem-level decryption functions in fs/crypto/
aware of large folios.  This includes making fscrypt_decrypt_bio()
support the case where the bio contains large folios, and making
fscrypt_decrypt_pagecache_blocks() take a folio instead of a page.

There's no way to actually test this with large folios yet, but I've
tested that this doesn't cause any regressions.

Note that this patch just handles *decryption*, not encryption which
will be a little more difficult.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 Documentation/filesystems/fscrypt.rst |  4 ++--
 fs/buffer.c                           |  4 ++--
 fs/crypto/bio.c                       | 10 ++++------
 fs/crypto/crypto.c                    | 28 ++++++++++++++-------------
 fs/ext4/inode.c                       |  6 ++++--
 include/linux/fscrypt.h               |  9 ++++-----
 6 files changed, 31 insertions(+), 30 deletions(-)


base-commit: 5dc4c995db9eb45f6373a956eb1f69460e69e6d4

Comments

Matthew Wilcox Jan. 27, 2023, 11:33 p.m. UTC | #1
On Fri, Jan 27, 2023 at 02:42:02PM -0800, Eric Biggers wrote:
> +++ b/fs/buffer.c
> @@ -307,8 +307,8 @@ static void decrypt_bh(struct work_struct *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));

FYI, Andrew's been carrying "buffer: add b_folio as an alias of b_page"
in his tree since mid-December.  buffer_heads always point to the
head page, aka folio.

This all looks good to me.

Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Eric Biggers Jan. 27, 2023, 11:41 p.m. UTC | #2
On Fri, Jan 27, 2023 at 11:33:57PM +0000, Matthew Wilcox wrote:
> On Fri, Jan 27, 2023 at 02:42:02PM -0800, Eric Biggers wrote:
> > +++ b/fs/buffer.c
> > @@ -307,8 +307,8 @@ static void decrypt_bh(struct work_struct *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));
> 
> FYI, Andrew's been carrying "buffer: add b_folio as an alias of b_page"
> in his tree since mid-December.  buffer_heads always point to the
> head page, aka folio.
> 
> This all looks good to me.
> 
> Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>

Indeed, but it's not upstream yet, so I decided to use page_folio() for now to
avoid a cross-tree dependency.

- Eric
Eric Biggers Feb. 3, 2023, 9:57 p.m. UTC | #3
On Fri, Jan 27, 2023 at 02:42:02PM -0800, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> Try to make the filesystem-level decryption functions in fs/crypto/
> aware of large folios.  This includes making fscrypt_decrypt_bio()
> support the case where the bio contains large folios, and making
> fscrypt_decrypt_pagecache_blocks() take a folio instead of a page.
> 
> There's no way to actually test this with large folios yet, but I've
> tested that this doesn't cause any regressions.
> 
> Note that this patch just handles *decryption*, not encryption which
> will be a little more difficult.
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>
> ---
>  Documentation/filesystems/fscrypt.rst |  4 ++--
>  fs/buffer.c                           |  4 ++--
>  fs/crypto/bio.c                       | 10 ++++------
>  fs/crypto/crypto.c                    | 28 ++++++++++++++-------------
>  fs/ext4/inode.c                       |  6 ++++--
>  include/linux/fscrypt.h               |  9 ++++-----
>  6 files changed, 31 insertions(+), 30 deletions(-)

Applied to https://git.kernel.org/pub/scm/fs/fsverity/linux.git/log/?h=for-next

(I used the fsverity tree instead of the fscrypt tree, so that I could resolve
the conflict with "fs/buffer.c: support fsverity in block_read_full_folio()" in
the fsverity tree.)

- Eric
diff mbox series

Patch

diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index ef183387da208..eccd327e6df50 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -1277,8 +1277,8 @@  the file contents themselves, as described below:
 
 For the read path (->read_folio()) of regular files, filesystems can
 read the ciphertext into the page cache and decrypt it in-place.  The
-page lock must be held until decryption has finished, to prevent the
-page from becoming visible to userspace prematurely.
+folio lock must be held until decryption has finished, to prevent the
+folio from becoming visible to userspace prematurely.
 
 For the write path (->writepage()) of regular files, filesystems
 cannot encrypt data in-place in the page cache, since the cached
diff --git a/fs/buffer.c b/fs/buffer.c
index d9c6d1fbb6dde..4e7f169acb2ca 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -307,8 +307,8 @@  static void decrypt_bh(struct work_struct *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));
 	end_buffer_async_read(bh, err == 0);
 	kfree(ctx);
 }
diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
index 1b4403136d05c..d57d0a020f71c 100644
--- a/fs/crypto/bio.c
+++ b/fs/crypto/bio.c
@@ -30,13 +30,11 @@ 
  */
 bool fscrypt_decrypt_bio(struct bio *bio)
 {
-	struct bio_vec *bv;
-	struct bvec_iter_all iter_all;
+	struct folio_iter fi;
 
-	bio_for_each_segment_all(bv, bio, iter_all) {
-		struct page *page = bv->bv_page;
-		int err = fscrypt_decrypt_pagecache_blocks(page, bv->bv_len,
-							   bv->bv_offset);
+	bio_for_each_folio_all(fi, bio) {
+		int err = fscrypt_decrypt_pagecache_blocks(fi.folio, fi.length,
+							   fi.offset);
 
 		if (err) {
 			bio->bi_status = errno_to_blk_status(err);
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index e78be66bbf015..bf642479269a5 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -237,41 +237,43 @@  EXPORT_SYMBOL(fscrypt_encrypt_block_inplace);
 
 /**
  * fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a
- *					pagecache page
- * @page:      The locked pagecache page containing the block(s) to decrypt
+ *					pagecache folio
+ * @folio:     The locked pagecache folio containing the block(s) to decrypt
  * @len:       Total size of the block(s) to decrypt.  Must be a nonzero
  *		multiple of the filesystem's block size.
- * @offs:      Byte offset within @page of the first block to decrypt.  Must be
+ * @offs:      Byte offset within @folio of the first block to decrypt.  Must be
  *		a multiple of the filesystem's block size.
  *
- * The specified block(s) are decrypted in-place within the pagecache page,
- * which must still be locked and not uptodate.  Normally, blocksize ==
- * PAGE_SIZE and the whole page is decrypted at once.
+ * The specified block(s) are decrypted in-place within the pagecache folio,
+ * which must still be locked and not uptodate.
  *
  * This is for use by the filesystem's ->readahead() method.
  *
  * Return: 0 on success; -errno on failure
  */
-int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
-				     unsigned int offs)
+int fscrypt_decrypt_pagecache_blocks(struct folio *folio, size_t len,
+				     size_t offs)
 {
-	const struct inode *inode = page->mapping->host;
+	const struct inode *inode = folio->mapping->host;
 	const unsigned int blockbits = inode->i_blkbits;
 	const unsigned int blocksize = 1 << blockbits;
-	u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) +
+	u64 lblk_num = ((u64)folio->index << (PAGE_SHIFT - blockbits)) +
 		       (offs >> blockbits);
-	unsigned int i;
+	size_t i;
 	int err;
 
-	if (WARN_ON_ONCE(!PageLocked(page)))
+	if (WARN_ON_ONCE(!folio_test_locked(folio)))
 		return -EINVAL;
 
 	if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize)))
 		return -EINVAL;
 
 	for (i = offs; i < offs + len; i += blocksize, lblk_num++) {
+		struct page *page = folio_page(folio, i >> PAGE_SHIFT);
+
 		err = fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page,
-					  page, blocksize, i, GFP_NOFS);
+					  page, blocksize, i & ~PAGE_MASK,
+					  GFP_NOFS);
 		if (err)
 			return err;
 	}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9d9f414f99fec..0fe1b746fe864 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1136,7 +1136,8 @@  static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
 		for (i = 0; i < nr_wait; i++) {
 			int err2;
 
-			err2 = fscrypt_decrypt_pagecache_blocks(page, blocksize,
+			err2 = fscrypt_decrypt_pagecache_blocks(page_folio(page),
+								blocksize,
 								bh_offset(wait[i]));
 			if (err2) {
 				clear_buffer_uptodate(wait[i]);
@@ -3858,7 +3859,8 @@  static int __ext4_block_zero_page_range(handle_t *handle,
 		if (fscrypt_inode_uses_fs_layer_crypto(inode)) {
 			/* We expect the key to be set. */
 			BUG_ON(!fscrypt_has_encryption_key(inode));
-			err = fscrypt_decrypt_pagecache_blocks(page, blocksize,
+			err = fscrypt_decrypt_pagecache_blocks(page_folio(page),
+							       blocksize,
 							       bh_offset(bh));
 			if (err) {
 				clear_buffer_uptodate(bh);
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 4f5f8a6512132..433504422d02d 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -257,8 +257,8 @@  int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
 				  unsigned int len, unsigned int offs,
 				  u64 lblk_num, gfp_t gfp_flags);
 
-int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
-				     unsigned int offs);
+int fscrypt_decrypt_pagecache_blocks(struct folio *folio, size_t len,
+				     size_t offs);
 int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page,
 				  unsigned int len, unsigned int offs,
 				  u64 lblk_num);
@@ -422,9 +422,8 @@  static inline int fscrypt_encrypt_block_inplace(const struct inode *inode,
 	return -EOPNOTSUPP;
 }
 
-static inline int fscrypt_decrypt_pagecache_blocks(struct page *page,
-						   unsigned int len,
-						   unsigned int offs)
+static inline int fscrypt_decrypt_pagecache_blocks(struct folio *folio,
+						   size_t len, size_t offs)
 {
 	return -EOPNOTSUPP;
 }