From: Liao Yuanhong <[email protected]>

Some filesystems store small data regions inside filesystem metadata rather
than submitting them through the normal bio path.  F2FS inline data is one
such case.  When an encrypted file uses blk-crypto, these regions still
need software fscrypt handling because no data bio is submitted for them.

Add fscrypt_encrypt_data_unit_inplace() and
fscrypt_decrypt_data_unit_inplace().  They use the same data-unit crypto
path as fscrypt_encrypt_block_inplace() and
fscrypt_decrypt_block_inplace(), but take a data-unit index instead of a
filesystem logical block number.

Also add fscrypt_inode_supports_data_unit_inplace() so filesystems can
check whether an inode has a software transform available for this path.

Factor the software skcipher setup into fscrypt_prepare_software_key().
The existing fscrypt_prepare_key() path now reuses it, and inline-crypto
key setup can use it in addition to preparing the blk-crypto key.  This
lets an inline-crypto inode keep its normal blk-crypto contents path while
also having a software transform for filesystem-managed data units.

Signed-off-by: Liao Yuanhong <[email protected]>

---
 fs/crypto/crypto.c          | 63 +++++++++++++++++++++++++++++++++++++
 fs/crypto/fscrypt_private.h |  3 +-
 fs/crypto/keysetup.c        | 59 +++++++++++++++++++++++++---------
 include/linux/fscrypt.h     | 28 +++++++++++++++++
 4 files changed, 138 insertions(+), 15 deletions(-)

diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 570a2231c945..c43acbc8b4ea 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -208,6 +208,44 @@ struct page *fscrypt_encrypt_pagecache_blocks(struct folio 
*folio,
 }
 EXPORT_SYMBOL(fscrypt_encrypt_pagecache_blocks);
 
+/**
+ * fscrypt_encrypt_data_unit_inplace() - Encrypt a data unit in-place
+ * @inode: The inode to which this data unit belongs
+ * @page:  The page containing the data unit to encrypt
+ * @len:   Size of data unit to encrypt.  This must be a multiple of
+ *        FSCRYPT_CONTENTS_ALIGNMENT.
+ * @offs:  Byte offset within @page at which the data unit begins
+ * @index: Fscrypt data unit index within the file
+ *
+ * Return: 0 on success; -errno on failure
+ */
+int fscrypt_encrypt_data_unit_inplace(const struct inode *inode,
+                                     struct page *page, unsigned int len,
+                                     unsigned int offs, u64 index)
+{
+       const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode);
+
+       if (!fscrypt_inode_supports_data_unit_inplace(inode))
+               return -EOPNOTSUPP;
+
+       return fscrypt_crypt_data_unit(ci, FS_ENCRYPT, index, page, page, len,
+                                      offs);
+}
+EXPORT_SYMBOL(fscrypt_encrypt_data_unit_inplace);
+
+bool fscrypt_inode_supports_data_unit_inplace(const struct inode *inode)
+{
+       const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode);
+
+       if (!IS_ENABLED(CONFIG_FS_ENCRYPTION_INLINE_CRYPT))
+               return false;
+       if (!ci)
+               return false;
+       /* pairs with smp_store_release() in fscrypt_prepare_software_key() */
+       return smp_load_acquire(&ci->ci_enc_key.tfm);
+}
+EXPORT_SYMBOL(fscrypt_inode_supports_data_unit_inplace);
+
 /**
  * fscrypt_encrypt_block_inplace() - Encrypt a filesystem block in-place
  * @inode:     The inode to which this block belongs
@@ -282,6 +320,31 @@ int fscrypt_decrypt_pagecache_blocks(struct folio *folio, 
size_t len,
 }
 EXPORT_SYMBOL(fscrypt_decrypt_pagecache_blocks);
 
+/**
+ * fscrypt_decrypt_data_unit_inplace() - Decrypt a data unit in-place
+ * @inode: The inode to which this data unit belongs
+ * @page:  The page containing the data unit to decrypt
+ * @len:   Size of data unit to decrypt.  This must be a multiple of
+ *        FSCRYPT_CONTENTS_ALIGNMENT.
+ * @offs:  Byte offset within @page at which the data unit begins
+ * @index: Fscrypt data unit index within the file
+ *
+ * Return: 0 on success; -errno on failure
+ */
+int fscrypt_decrypt_data_unit_inplace(const struct inode *inode,
+                                     struct page *page, unsigned int len,
+                                     unsigned int offs, u64 index)
+{
+       const struct fscrypt_inode_info *ci = fscrypt_get_inode_info_raw(inode);
+
+       if (!fscrypt_inode_supports_data_unit_inplace(inode))
+               return -EOPNOTSUPP;
+
+       return fscrypt_crypt_data_unit(ci, FS_DECRYPT, index, page, page, len,
+                                      offs);
+}
+EXPORT_SYMBOL(fscrypt_decrypt_data_unit_inplace);
+
 /**
  * fscrypt_decrypt_block_inplace() - Decrypt a filesystem block in-place
  * @inode:     The inode to which this block belongs
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 8d3c278a7591..b5c0b881fd4b 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -236,7 +236,8 @@ struct fscrypt_symlink_data {
  * @tfm: crypto API transform object
  * @blk_key: key for blk-crypto
  *
- * Normally only one of the fields will be non-NULL.
+ * Most users need only one prepared form.  Inline-crypto users that also need
+ * filesystem-layer software crypto for non-bio data regions may prepare both.
  */
 struct fscrypt_prepared_key {
        struct crypto_sync_skcipher *tfm;
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index ce327bfdada4..716911b97d8e 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -144,6 +144,23 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const 
u8 *raw_key,
        return ERR_PTR(err);
 }
 
+static int fscrypt_prepare_software_key(struct fscrypt_prepared_key *prep_key,
+                                       const u8 *raw_key,
+                                       const struct fscrypt_inode_info *ci)
+{
+       struct crypto_sync_skcipher *tfm;
+
+       /* pairs with smp_store_release() below */
+       if (smp_load_acquire(&prep_key->tfm))
+               return 0;
+       tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+       /* pairs with smp_load_acquire() above */
+       smp_store_release(&prep_key->tfm, tfm);
+       return 0;
+}
+
 /*
  * Prepare the crypto transform object or blk-crypto key in @prep_key, given 
the
  * raw key, encryption mode (@ci->ci_mode), flag indicating which encryption
@@ -153,24 +170,12 @@ fscrypt_allocate_skcipher(struct fscrypt_mode *mode, 
const u8 *raw_key,
 int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
                        const u8 *raw_key, const struct fscrypt_inode_info *ci)
 {
-       struct crypto_sync_skcipher *tfm;
-
        if (fscrypt_using_inline_encryption(ci))
                return fscrypt_prepare_inline_crypt_key(prep_key, raw_key,
                                                        ci->ci_mode->keysize,
                                                        false, ci);
 
-       tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode);
-       if (IS_ERR(tfm))
-               return PTR_ERR(tfm);
-       /*
-        * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
-        * I.e., here we publish ->tfm with a RELEASE barrier so that
-        * concurrent tasks can ACQUIRE it.  Note that this concurrency is only
-        * possible for per-mode keys, not for per-file keys.
-        */
-       smp_store_release(&prep_key->tfm, tfm);
-       return 0;
+       return fscrypt_prepare_software_key(prep_key, raw_key, ci);
 }
 
 /* Destroy a crypto transform object and/or blk-crypto key. */
@@ -190,6 +195,20 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info 
*ci,
        return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci);
 }
 
+static int
+fscrypt_prepare_inline_crypt_and_software_key(struct fscrypt_prepared_key 
*prep_key,
+                                             const u8 *raw_key,
+                                             const struct fscrypt_inode_info 
*ci)
+{
+       int err;
+
+       err = fscrypt_prepare_software_key(prep_key, raw_key, ci);
+       if (err)
+               return err;
+       return fscrypt_prepare_inline_crypt_key(prep_key, raw_key,
+                                              ci->ci_mode->keysize, false, ci);
+}
+
 static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci,
                                  struct fscrypt_master_key *mk,
                                  struct fscrypt_prepared_key *keys,
@@ -255,7 +274,16 @@ static int setup_per_mode_enc_key(struct 
fscrypt_inode_info *ci,
        }
        fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info,
                            hkdf_infolen, mode_key, mode->keysize);
-       err = fscrypt_prepare_key(prep_key, mode_key, ci);
+       if (!use_hw_wrapped_key && fscrypt_using_inline_encryption(ci)) {
+               /*
+                * Filesystem-managed regions such as F2FS inline_data need the
+                * same contents key as a software tfm.
+                */
+               err = fscrypt_prepare_inline_crypt_and_software_key(prep_key,
+                                                                   mode_key, 
ci);
+       } else {
+               err = fscrypt_prepare_key(prep_key, mode_key, ci);
+       }
        memzero_explicit(mode_key, mode->keysize);
        if (err)
                goto out_unlock;
@@ -381,6 +409,7 @@ static int fscrypt_setup_v2_file_key(struct 
fscrypt_inode_info *ci,
                   FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
                err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk);
        } else {
+               struct fscrypt_prepared_key *prep_key = &ci->ci_enc_key;
                u8 derived_key[FSCRYPT_MAX_RAW_KEY_SIZE];
 
                fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
@@ -388,6 +417,8 @@ static int fscrypt_setup_v2_file_key(struct 
fscrypt_inode_info *ci,
                                    ci->ci_nonce, FSCRYPT_FILE_NONCE_SIZE,
                                    derived_key, ci->ci_mode->keysize);
                err = fscrypt_set_per_file_enc_key(ci, derived_key);
+               if (!err && fscrypt_using_inline_encryption(ci))
+                       err = fscrypt_prepare_software_key(prep_key, 
derived_key, ci);
                memzero_explicit(derived_key, ci->ci_mode->keysize);
        }
        if (err)
diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 54712ec61ffb..3762a7526fcc 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -346,12 +346,20 @@ void fscrypt_enqueue_decrypt_work(struct work_struct *);
 
 struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
                size_t len, size_t offs, gfp_t gfp_flags);
+
+int fscrypt_encrypt_data_unit_inplace(const struct inode *inode,
+                                     struct page *page, unsigned int len,
+                                     unsigned int offs, u64 index);
+bool fscrypt_inode_supports_data_unit_inplace(const struct inode *inode);
 int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
                                  unsigned int len, unsigned int offs,
                                  u64 lblk_num);
 
 int fscrypt_decrypt_pagecache_blocks(struct folio *folio, size_t len,
                                     size_t offs);
+int fscrypt_decrypt_data_unit_inplace(const struct inode *inode,
+                                     struct page *page, unsigned int len,
+                                     unsigned int offs, u64 index);
 int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page,
                                  unsigned int len, unsigned int offs,
                                  u64 lblk_num);
@@ -519,6 +527,19 @@ static inline struct page 
*fscrypt_encrypt_pagecache_blocks(struct folio *folio,
        return ERR_PTR(-EOPNOTSUPP);
 }
 
+static inline int fscrypt_encrypt_data_unit_inplace(const struct inode *inode,
+                                                   struct page *page, unsigned 
int len,
+                                                   unsigned int offs, u64 
index)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline bool
+fscrypt_inode_supports_data_unit_inplace(const struct inode *inode)
+{
+       return false;
+}
+
 static inline int fscrypt_encrypt_block_inplace(const struct inode *inode,
                                                struct page *page,
                                                unsigned int len,
@@ -533,6 +554,13 @@ static inline int fscrypt_decrypt_pagecache_blocks(struct 
folio *folio,
        return -EOPNOTSUPP;
 }
 
+static inline int fscrypt_decrypt_data_unit_inplace(const struct inode *inode,
+                                                   struct page *page, unsigned 
int len,
+                                                   unsigned int offs, u64 
index)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline int fscrypt_decrypt_block_inplace(const struct inode *inode,
                                                struct page *page,
                                                unsigned int len,
-- 
2.34.1

Reply via email to