| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Fri, 24 Jul 2020 18:44:55 +0000 |
| Subject: ANDROID: fscrypt: Add functions for direct I/O support |
| |
| Introduce fscrypt_dio_supported() to check whether a direct I/O request |
| is unsupported due to encryption constraints. |
| |
| Also introduce fscrypt_limit_io_blocks() to limit how many blocks can be |
| added to a bio being prepared for direct I/O. This is needed for |
| filesystems that use the iomap direct I/O implementation to avoid DUN |
| wraparound in the middle of a bio (which is possible with the |
| IV_INO_LBLK_32 IV generation method). Elsewhere fscrypt_mergeable_bio() |
| is used for this, but iomap operates on logical ranges directly, so |
| filesystems using iomap won't have a chance to call fscrypt_mergeable_bio() |
| on every block added to a bio. So we need this function which limits a |
| logical range in one go. |
| |
| [CPNOTE: 25/05/21] Lee: No update since v8 was posted in January - poked the MLs |
| [CPNOTE: 26/05/21] Lee: About to submit a patch to re-align with Mainline |
| [CPNOTE: 22/07/21] Lee: v9 was posted 7 weeks ago with no reviews - poked again |
| [CPNOTE: 04/08/21] Lee: Eric and Satya are still working on this [0] |
| |
| [0]https://lore.kernel.org/linux-xfs/YPmFSw4JbWnIozSZ@gmail.com/ |
| |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Co-developed-by: Satya Tangirala <satyat@google.com> |
| Signed-off-by: Satya Tangirala <satyat@google.com> |
| |
| Bug: 162255927 |
| Link: https://lore.kernel.org/r/20200724184501.1651378-2-satyat@google.com |
| Change-Id: I1dbd4f382d510d9b779d5e44a77fadf7040cf077 |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| --- |
| fs/crypto/crypto.c | 8 +++++ |
| fs/crypto/inline_crypt.c | 75 ++++++++++++++++++++++++++++++++++++++++ |
| include/linux/fscrypt.h | 18 ++++++++++ |
| 3 files changed, 101 insertions(+) |
| |
| diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c |
| --- a/fs/crypto/crypto.c |
| +++ b/fs/crypto/crypto.c |
| @@ -69,6 +69,14 @@ void fscrypt_free_bounce_page(struct page *bounce_page) |
| } |
| EXPORT_SYMBOL(fscrypt_free_bounce_page); |
| |
| +/* |
| + * Generate the IV for the given logical block number within the given file. |
| + * For filenames encryption, lblk_num == 0. |
| + * |
| + * Keep this in sync with fscrypt_limit_io_blocks(). fscrypt_limit_io_blocks() |
| + * needs to know about any IV generation methods where the low bits of IV don't |
| + * simply contain the lblk_num (e.g., IV_INO_LBLK_32). |
| + */ |
| void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num, |
| const struct fscrypt_info *ci) |
| { |
| diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c |
| --- a/fs/crypto/inline_crypt.c |
| +++ b/fs/crypto/inline_crypt.c |
| @@ -18,6 +18,7 @@ |
| #include <linux/buffer_head.h> |
| #include <linux/sched/mm.h> |
| #include <linux/slab.h> |
| +#include <linux/uio.h> |
| |
| #include "fscrypt_private.h" |
| |
| @@ -434,3 +435,77 @@ bool fscrypt_mergeable_bio_bh(struct bio *bio, |
| return fscrypt_mergeable_bio(bio, inode, next_lblk); |
| } |
| EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh); |
| + |
| +/** |
| + * fscrypt_dio_supported() - check whether a direct I/O request is unsupported |
| + * due to encryption constraints |
| + * @iocb: the file and position the I/O is targeting |
| + * @iter: the I/O data segment(s) |
| + * |
| + * Return: true if direct I/O is supported |
| + */ |
| +bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter) |
| +{ |
| + const struct inode *inode = file_inode(iocb->ki_filp); |
| + const unsigned int blocksize = i_blocksize(inode); |
| + |
| + /* If the file is unencrypted, no veto from us. */ |
| + if (!fscrypt_needs_contents_encryption(inode)) |
| + return true; |
| + |
| + /* We only support direct I/O with inline crypto, not fs-layer crypto */ |
| + if (!fscrypt_inode_uses_inline_crypto(inode)) |
| + return false; |
| + |
| + /* |
| + * Since the granularity of encryption is filesystem blocks, the I/O |
| + * must be block aligned -- not just disk sector aligned. |
| + */ |
| + if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), blocksize)) |
| + return false; |
| + |
| + return true; |
| +} |
| +EXPORT_SYMBOL_GPL(fscrypt_dio_supported); |
| + |
| +/** |
| + * fscrypt_limit_io_blocks() - limit I/O blocks to avoid discontiguous DUNs |
| + * @inode: the file on which I/O is being done |
| + * @lblk: the block at which the I/O is being started from |
| + * @nr_blocks: the number of blocks we want to submit starting at @pos |
| + * |
| + * Determine the limit to the number of blocks that can be submitted in the bio |
| + * targeting @pos without causing a data unit number (DUN) discontinuity. |
| + * |
| + * This is normally just @nr_blocks, as normally the DUNs just increment along |
| + * with the logical blocks. (Or the file is not encrypted.) |
| + * |
| + * In rare cases, fscrypt can be using an IV generation method that allows the |
| + * DUN to wrap around within logically continuous blocks, and that wraparound |
| + * will occur. If this happens, a value less than @nr_blocks will be returned |
| + * so that the wraparound doesn't occur in the middle of the bio. |
| + * |
| + * Return: the actual number of blocks that can be submitted |
| + */ |
| +u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks) |
| +{ |
| + const struct fscrypt_info *ci = inode->i_crypt_info; |
| + u32 dun; |
| + |
| + if (!fscrypt_inode_uses_inline_crypto(inode)) |
| + return nr_blocks; |
| + |
| + if (nr_blocks <= 1) |
| + return nr_blocks; |
| + |
| + if (!(fscrypt_policy_flags(&ci->ci_policy) & |
| + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) |
| + return nr_blocks; |
| + |
| + /* With IV_INO_LBLK_32, the DUN can wrap around from U32_MAX to 0. */ |
| + |
| + dun = ci->ci_hashed_ino + lblk; |
| + |
| + return min_t(u64, nr_blocks, (u64)U32_MAX + 1 - dun); |
| +} |
| +EXPORT_SYMBOL_GPL(fscrypt_limit_io_blocks); |
| diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h |
| --- a/include/linux/fscrypt.h |
| +++ b/include/linux/fscrypt.h |
| @@ -714,6 +714,10 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode, |
| bool fscrypt_mergeable_bio_bh(struct bio *bio, |
| const struct buffer_head *next_bh); |
| |
| +bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter); |
| + |
| +u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks); |
| + |
| #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ |
| |
| static inline bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) |
| @@ -742,6 +746,20 @@ static inline bool fscrypt_mergeable_bio_bh(struct bio *bio, |
| { |
| return true; |
| } |
| + |
| +static inline bool fscrypt_dio_supported(struct kiocb *iocb, |
| + struct iov_iter *iter) |
| +{ |
| + const struct inode *inode = file_inode(iocb->ki_filp); |
| + |
| + return !fscrypt_needs_contents_encryption(inode); |
| +} |
| + |
| +static inline u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, |
| + u64 nr_blocks) |
| +{ |
| + return nr_blocks; |
| +} |
| #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ |
| |
| #if IS_ENABLED(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_DM_DEFAULT_KEY) |