fscrypt: change fscrypt_dio_supported() to prepare for STATX_DIOALIGN
authorEric Biggers <ebiggers@google.com>
Sat, 27 Aug 2022 06:58:46 +0000 (23:58 -0700)
committerEric Biggers <ebiggers@google.com>
Mon, 12 Sep 2022 00:47:12 +0000 (19:47 -0500)
To prepare for STATX_DIOALIGN support, make two changes to
fscrypt_dio_supported().

First, remove the filesystem-block-alignment check and make the
filesystems handle it instead.  It previously made sense to have it in
fs/crypto/; however, to support STATX_DIOALIGN the alignment restriction
would have to be returned to filesystems.  It ends up being simpler if
filesystems handle this part themselves, especially for f2fs which only
allows fs-block-aligned DIO in the first place.

Second, make fscrypt_dio_supported() work on inodes whose encryption key
hasn't been set up yet, by making it set up the key if needed.  This is
required for statx(), since statx() doesn't require a file descriptor.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20220827065851.135710-4-ebiggers@kernel.org
fs/crypto/inline_crypt.c
fs/ext4/file.c
fs/f2fs/f2fs.h
include/linux/fscrypt.h

index 90f3e68..8d4bee5 100644 (file)
@@ -401,46 +401,45 @@ bool fscrypt_mergeable_bio_bh(struct bio *bio,
 EXPORT_SYMBOL_GPL(fscrypt_mergeable_bio_bh);
 
 /**
- * fscrypt_dio_supported() - check whether a DIO (direct I/O) request is
- *                          supported as far as encryption is concerned
- * @iocb: the file and position the I/O is targeting
- * @iter: the I/O data segment(s)
+ * fscrypt_dio_supported() - check whether DIO (direct I/O) is supported on an
+ *                          inode, as far as encryption is concerned
+ * @inode: the inode in question
  *
  * Return: %true if there are no encryption constraints that prevent DIO from
  *        being supported; %false if DIO is unsupported.  (Note that in the
  *        %true case, the filesystem might have other, non-encryption-related
- *        constraints that prevent DIO from actually being supported.)
+ *        constraints that prevent DIO from actually being supported.  Also, on
+ *        encrypted files the filesystem is still responsible for only allowing
+ *        DIO when requests are filesystem-block-aligned.)
  */
-bool fscrypt_dio_supported(struct kiocb *iocb, struct iov_iter *iter)
+bool fscrypt_dio_supported(struct inode *inode)
 {
-       const struct inode *inode = file_inode(iocb->ki_filp);
-       const unsigned int blocksize = i_blocksize(inode);
+       int err;
 
        /* If the file is unencrypted, no veto from us. */
        if (!fscrypt_needs_contents_encryption(inode))
                return true;
 
-       /* We only support DIO 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 file
-        * position and total I/O length must be aligned to the filesystem block
-        * size -- not just to the block device's logical block size as is
-        * traditionally the case for DIO on many filesystems.
+        * We only support DIO with inline crypto, not fs-layer crypto.
         *
-        * We require that the user-provided memory buffers be filesystem block
-        * aligned too.  It is simpler to have a single alignment value required
-        * for all properties of the I/O, as is normally the case for DIO.
-        * Also, allowing less aligned buffers would imply that data units could
-        * cross bvecs, which would greatly complicate the I/O stack, which
-        * assumes that bios can be split at any bvec boundary.
+        * To determine whether the inode is using inline crypto, we have to set
+        * up the key if it wasn't already done.  This is because in the current
+        * design of fscrypt, the decision of whether to use inline crypto or
+        * not isn't made until the inode's encryption key is being set up.  In
+        * the DIO read/write case, the key will always be set up already, since
+        * the file will be open.  But in the case of statx(), the key might not
+        * be set up yet, as the file might not have been opened yet.
         */
-       if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), blocksize))
+       err = fscrypt_require_key(inode);
+       if (err) {
+               /*
+                * Key unavailable or couldn't be set up.  This edge case isn't
+                * worth worrying about; just report that DIO is unsupported.
+                */
                return false;
-
-       return true;
+       }
+       return fscrypt_inode_uses_inline_crypto(inode);
 }
 EXPORT_SYMBOL_GPL(fscrypt_dio_supported);
 
index 109d076..26d7426 100644 (file)
@@ -40,8 +40,13 @@ static bool ext4_dio_supported(struct kiocb *iocb, struct iov_iter *iter)
 {
        struct inode *inode = file_inode(iocb->ki_filp);
 
-       if (!fscrypt_dio_supported(iocb, iter))
-               return false;
+       if (IS_ENCRYPTED(inode)) {
+               if (!fscrypt_dio_supported(inode))
+                       return false;
+               if (!IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter),
+                               i_blocksize(inode)))
+                       return false;
+       }
        if (fsverity_active(inode))
                return false;
        if (ext4_should_journal_data(inode))
index 3c7cdb7..0759da1 100644 (file)
@@ -4498,7 +4498,7 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        int rw = iov_iter_rw(iter);
 
-       if (!fscrypt_dio_supported(iocb, iter))
+       if (!fscrypt_dio_supported(inode))
                return true;
        if (fsverity_active(inode))
                return true;
index 7d2f1e0..1359885 100644 (file)
@@ -768,7 +768,7 @@ 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);
+bool fscrypt_dio_supported(struct inode *inode);
 
 u64 fscrypt_limit_io_blocks(const struct inode *inode, u64 lblk, u64 nr_blocks);
 
@@ -801,11 +801,8 @@ 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)
+static inline bool fscrypt_dio_supported(struct inode *inode)
 {
-       const struct inode *inode = file_inode(iocb->ki_filp);
-
        return !fscrypt_needs_contents_encryption(inode);
 }