diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index f9111ffa12ff..61da06fda45c 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -33,8 +33,7 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done) bio_for_each_segment_all(bv, bio, iter_all) { struct page *page = bv->bv_page; - int ret = fscrypt_decrypt_page(page->mapping->host, page, - PAGE_SIZE, 0, page->index); + int ret = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); if (ret) SetPageError(page); diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index f82c45ac285a..45c3d0427fb2 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -283,29 +283,47 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page, EXPORT_SYMBOL(fscrypt_encrypt_block_inplace); /** - * fscrypt_decrypt_page() - Decrypts a page in-place - * @inode: The corresponding inode for the page to decrypt. - * @page: The page to decrypt. Must be locked. - * @len: Number of bytes in @page to be decrypted. - * @offs: Start of data in @page. - * @lblk_num: Logical block number. + * fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a pagecache page + * @page: The locked pagecache page 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 + * a multiple of the filesystem's block size. * - * Decrypts page in-place using the ctx encryption context. + * 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. * - * Called from the read completion callback. + * This is for use by the filesystem's ->readpages() method. * - * Return: Zero on success, non-zero otherwise. + * Return: 0 on success; -errno on failure */ -int fscrypt_decrypt_page(const struct inode *inode, struct page *page, - unsigned int len, unsigned int offs, u64 lblk_num) +int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs) { + const struct inode *inode = page->mapping->host; + const unsigned int blockbits = inode->i_blkbits; + const unsigned int blocksize = 1 << blockbits; + u64 lblk_num = ((u64)page->index << (PAGE_SHIFT - blockbits)) + + (offs >> blockbits); + unsigned int i; + int err; + if (WARN_ON_ONCE(!PageLocked(page))) return -EINVAL; - return fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, page, - len, offs, GFP_NOFS); + if (WARN_ON_ONCE(len <= 0 || !IS_ALIGNED(len | offs, blocksize))) + return -EINVAL; + + for (i = offs; i < offs + len; i += blocksize, lblk_num++) { + err = fscrypt_crypt_block(inode, FS_DECRYPT, lblk_num, page, + page, blocksize, i, GFP_NOFS); + if (err) + return err; + } + return 0; } -EXPORT_SYMBOL(fscrypt_decrypt_page); +EXPORT_SYMBOL(fscrypt_decrypt_pagecache_blocks); /** * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c7f77c643008..8bfd8941f5ff 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1232,8 +1232,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, if (unlikely(err)) page_zero_new_buffers(page, from, to); else if (decrypt) - err = fscrypt_decrypt_page(page->mapping->host, page, - PAGE_SIZE, 0, page->index); + err = fscrypt_decrypt_pagecache_blocks(page, PAGE_SIZE, 0); return err; } #endif @@ -4066,8 +4065,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, /* We expect the key to be set. */ BUG_ON(!fscrypt_has_encryption_key(inode)); BUG_ON(blocksize != PAGE_SIZE); - WARN_ON_ONCE(fscrypt_decrypt_page(page->mapping->host, - page, PAGE_SIZE, 0, page->index)); + WARN_ON_ONCE(fscrypt_decrypt_pagecache_blocks( + page, PAGE_SIZE, 0)); } } if (ext4_should_journal_data(inode)) { diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 315affc99b05..bd8f207a2fb6 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -112,8 +112,9 @@ extern 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); -extern int fscrypt_decrypt_page(const struct inode *, struct page *, unsigned int, - unsigned int, u64); + +extern int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len, + unsigned int offs); extern int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page, unsigned int len, unsigned int offs, u64 lblk_num); @@ -310,10 +311,9 @@ static inline int fscrypt_encrypt_block_inplace(const struct inode *inode, return -EOPNOTSUPP; } -static inline int fscrypt_decrypt_page(const struct inode *inode, - struct page *page, - unsigned int len, unsigned int offs, - u64 lblk_num) +static inline int fscrypt_decrypt_pagecache_blocks(struct page *page, + unsigned int len, + unsigned int offs) { return -EOPNOTSUPP; }