@@ -113,6 +113,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
struct crypto_skcipher *tfm = ci->ci_enc_key->tfm;
int res = 0;
+ if (!ci)
+ return -EINVAL;
if (WARN_ON_ONCE(len <= 0))
return -EINVAL;
if (WARN_ON_ONCE(len % FSCRYPT_CONTENTS_ALIGNMENT != 0))
@@ -287,31 +287,17 @@ typedef enum {
} fscrypt_direction_t;
/**
- * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block
+ * fscrypt_fs_uses_extent_encryption() -- whether a filesystem uses per-extent
+ * encryption
*
- * @inode: the inode to which the block belongs
- * @lblk: the offset of the block within the file which the inode
- * references
- * @offset: a pointer to return the offset of the block from the first block
- * that the info covers. For inode-based encryption, this will
- * always be @lblk; for extent-based encryption, this will be in
- * the range [0, lblk]. Can be NULL
- * @extent_len: a pointer to return the minimum number of lblks starting at
- * this offset which also belong to the same fscrypt_info. Can be
- * NULL
+ * @sb: the superblock of the filesystem in question
*
- * Return: the appropriate fscrypt_info if there is one, else NULL.
+ * Return: true if the fs uses per-extent fscrypt_infos, false otherwise
*/
-static inline struct fscrypt_info *
-fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
- u64 *extent_len)
+static inline bool
+fscrypt_fs_uses_extent_encryption(const struct super_block *sb)
{
- if (offset)
- *offset = lblk;
- if (extent_len)
- *extent_len = U64_MAX;
-
- return inode->i_crypt_info;
+ return !!sb->s_cop->get_extent_info;
}
/**
@@ -324,27 +310,51 @@ fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
*/
static inline bool fscrypt_uses_extent_encryption(const struct inode *inode)
{
- /* Non-regular files don't have extents */
+ /* Non-regular files don't have extents. */
if (!S_ISREG(inode->i_mode))
return false;
- /* No filesystems currently use per-extent infos */
- return false;
+ return fscrypt_fs_uses_extent_encryption(inode->i_sb);
}
+
/**
- * fscrypt_fs_uses_extent_encryption() -- whether a filesystem uses per-extent
- * encryption
+ * fscrypt_get_lblk_info() - get the fscrypt_info to crypt a particular block
*
- * @sb: the superblock of the filesystem in question
+ * @inode: the inode to which the block belongs
+ * @lblk: the offset of the block within the file which the inode
+ * references
+ * @offset: a pointer to return the offset of the block from the first block
+ * that the info covers. For inode-based encryption, this will
+ * always be @lblk; for extent-based encryption, this will be in
+ * the range [0, lblk]. Can be NULL
+ * @extent_len: a pointer to return the minimum number of lblks starting at
+ * this offset which also belong to the same fscrypt_info. Can be
+ * NULL
*
- * Return: true if the fs uses per-extent fscrypt_infos, false otherwise
+ * Return: the appropriate fscrypt_info if there is one, else NULL.
*/
-static inline bool
-fscrypt_fs_uses_extent_encryption(const struct super_block *sb)
+static inline struct fscrypt_info *
+fscrypt_get_lblk_info(const struct inode *inode, u64 lblk, u64 *offset,
+ u64 *extent_len)
{
- // No filesystems currently use per-extent infos
- return false;
+ if (fscrypt_uses_extent_encryption(inode)) {
+ struct fscrypt_info *info;
+ int res;
+
+ res = inode->i_sb->s_cop->get_extent_info(inode, lblk, &info,
+ offset, extent_len);
+ if (res == 0)
+ return info;
+ return NULL;
+ }
+
+ if (offset)
+ *offset = lblk;
+ if (extent_len)
+ *extent_len = U64_MAX;
+
+ return inode->i_crypt_info;
}
/**
@@ -271,7 +271,10 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
if (!fscrypt_inode_uses_inline_crypto(inode))
return;
+
ci = fscrypt_get_lblk_info(inode, first_lblk, &ci_offset, NULL);
+ if (!ci)
+ return;
fscrypt_generate_dun(ci, ci_offset, dun);
bio_crypt_set_ctx(bio, ci->ci_enc_key->blk_key, dun, gfp_mask);
@@ -364,7 +367,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
* uses the same pointer. I.e., there's currently no need to support
* merging requests where the keys are the same but the pointers differ.
*/
- if (bc->bc_key != ci->ci_enc_key->blk_key)
+ if (!ci || bc->bc_key != ci->ci_enc_key->blk_key)
return false;
fscrypt_generate_dun(ci, ci_offset, next_dun);
@@ -929,6 +929,60 @@ int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode,
}
EXPORT_SYMBOL_GPL(fscrypt_prepare_new_inode);
+/**
+ * fscrypt_prepare_new_extent() - set up the fscrypt_info for a new extent
+ * @inode: the inode to which the extent belongs
+ * @info_ptr: a pointer to return the extent's fscrypt_info into. Should be
+ * a pointer to a member of the extent struct, as it will be passed
+ * back to the filesystem if key removal demands removal of the
+ * info from the extent
+ * @encrypt_ret: (output) set to %true if the new inode will be encrypted
+ *
+ * If the extent is part of an encrypted inode, set up its fscrypt_info in
+ * preparation for encrypting data and set *encrypt_ret=true.
+ *
+ * This isn't %GFP_NOFS-safe, and therefore it should be called before starting
+ * any filesystem transaction to create the inode.
+ *
+ * This doesn't persist the new inode's encryption context. That still needs to
+ * be done later by calling fscrypt_set_context().
+ *
+ * Return: 0 on success, -ENOKEY if the encryption key is missing, or another
+ * -errno code
+ */
+int fscrypt_prepare_new_extent(struct inode *inode,
+ struct fscrypt_info **info_ptr)
+{
+ const union fscrypt_policy *policy;
+ u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
+
+ policy = fscrypt_policy_to_inherit(inode);
+ if (policy == NULL)
+ return 0;
+ if (IS_ERR(policy))
+ return PTR_ERR(policy);
+
+ /* Only regular files can have extents. */
+ if (WARN_ON_ONCE(!S_ISREG(inode->i_mode)))
+ return -EINVAL;
+
+ get_random_bytes(nonce, FSCRYPT_FILE_NONCE_SIZE);
+ return fscrypt_setup_encryption_info(inode, policy, nonce,
+ false, info_ptr);
+}
+EXPORT_SYMBOL_GPL(fscrypt_prepare_new_extent);
+
+/**
+ * fscrypt_free_extent_info() - free an extent's fscrypt_info
+ * @info_ptr: a pointer containing the extent's fscrypt_info pointer.
+ */
+void fscrypt_free_extent_info(struct fscrypt_info **info_ptr)
+{
+ put_crypt_info(*info_ptr);
+ *info_ptr = NULL;
+}
+EXPORT_SYMBOL_GPL(fscrypt_free_extent_info);
+
/**
* fscrypt_put_encryption_info() - free most of an inode's fscrypt data
* @inode: an inode being evicted
@@ -113,6 +113,29 @@ struct fscrypt_operations {
int (*set_context)(struct inode *inode, const void *ctx, size_t len,
void *fs_data);
+ /*
+ * Get the fscrypt_info for the given inode at the given block, for
+ * extent-based encryption only.
+ *
+ * @inode: the inode in question
+ * @lblk: the logical block number in question
+ * @ci: a pointer to return the fscrypt_info
+ * @offset: a pointer to return the offset of @lblk into the extent,
+ * in blocks (may be NULL)
+ * @extent_len: a pointer to return the number of blocks in this extent
+ * starting at this point (may be NULL)
+ *
+ * May cause the filesystem to allocate memory, which the filesystem
+ * must do with %GFP_NOFS, including calls into fscrypt to create or
+ * load an fscrypt_info.
+ *
+ * Return: 0 if an extent is found with an info, -ENODATA if the key is
+ * unavailable, or another -errno.
+ */
+ int (*get_extent_info)(const struct inode *inode, u64 lblk,
+ struct fscrypt_info **ci, u64 *offset,
+ u64 *extent_len);
+
/*
* Get the dummy fscrypt policy in use on the filesystem (if any).
*
@@ -339,6 +362,10 @@ void fscrypt_put_encryption_info(struct inode *inode);
void fscrypt_free_inode(struct inode *inode);
int fscrypt_drop_inode(struct inode *inode);
+int fscrypt_prepare_new_extent(struct inode *inode,
+ struct fscrypt_info **info_ptr);
+void fscrypt_free_extent_info(struct fscrypt_info **info_ptr);
+
/* fname.c */
int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
u8 *out, unsigned int olen);
@@ -600,6 +627,18 @@ static inline int fscrypt_drop_inode(struct inode *inode)
return 0;
}
+static inline int fscrypt_prepare_new_extent(struct inode *inode,
+ struct fscrypt_info **info_ptr)
+{
+ if (IS_ENCRYPTED(inode))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static inline void fscrypt_free_extent_info(struct fscrypt_info **info_ptr)
+{
+}
+
/* fname.c */
static inline int fscrypt_setup_filename(struct inode *dir,
const struct qstr *iname,
This change adds the superblock function pointer to get the info corresponding to a specific block in an inode for a filesystem using per-extent infos. It allows creating a info for a new extent and freeing that info, and uses the extent's info if appropriate in encrypting blocks of data. It also makes sure that the return value of fscrypt_get_lblk_info() is non-NULL before using it, since there's no longer a mechanical guarantee that we'll never call fscrypt_get_lblk_info() without having the relevant info loaded. We *oughtn't*, but we're not explicitly checking that it's loaded before these points. This change does not deal with saving and loading an extent's info, but introduces the mechanics necessary therefore. Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> --- fs/crypto/crypto.c | 2 + fs/crypto/fscrypt_private.h | 74 +++++++++++++++++++++---------------- fs/crypto/inline_crypt.c | 5 ++- fs/crypto/keysetup.c | 54 +++++++++++++++++++++++++++ include/linux/fscrypt.h | 39 +++++++++++++++++++ 5 files changed, 141 insertions(+), 33 deletions(-)