@@ -1299,6 +1299,13 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi));
+
+ if (ctxsize) {
+ unsigned long ptr = (unsigned long)fi->encryption_context;
+ int res = btrfs_fscrypt_load_extent_info(inode, leaf, ptr,
+ ctxsize, &em->fscrypt_info);
+ ASSERT(res == 0);
+ }
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
em->block_start = EXTENT_MAP_INLINE;
em->start = extent_start;
@@ -75,6 +75,65 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
return !memcmp(digest, nokey_name->sha256, sizeof(digest));
}
+int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer, size_t *context_len)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ int ret;
+
+ if (!IS_ENCRYPTED(&inode->vfs_inode))
+ return 0;
+
+
+ ret = fscrypt_set_extent_context(info, context_buffer + sizeof(u32),
+ FSCRYPT_SET_CONTEXT_MAX_SIZE);
+ if (ret < 0) {
+ btrfs_err(fs_info, "fscrypt context could not be saved");
+ return ret;
+ }
+
+ /* the return value, if nonnegative, is the fscrypt context size */
+ ret += sizeof(u32);
+
+ put_unaligned_le32(ret, context_buffer);
+
+ *context_len = ret;
+ return 0;
+}
+
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr,
+ u8 ctxsize,
+ struct fscrypt_info **info_ptr)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ int res;
+ unsigned int nofs_flags;
+ u32 len;
+
+ read_extent_buffer(leaf, context, ptr, ctxsize);
+
+ nofs_flags = memalloc_nofs_save();
+ res = fscrypt_load_extent_info(&inode->vfs_inode,
+ context + sizeof(u32),
+ ctxsize - sizeof(u32), info_ptr);
+ memalloc_nofs_restore(nofs_flags);
+
+ if (res)
+ btrfs_err(fs_info, "Unable to load fscrypt info: %d", res);
+
+ len = get_unaligned_le32(context);
+ if (len != ctxsize) {
+ res = -EINVAL;
+ btrfs_err(fs_info, "fscrypt info size mismatches");
+ }
+
+ return res;
+}
+
static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
struct btrfs_key key = {
@@ -138,11 +197,14 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
if (!trans)
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
- if (IS_ERR(trans))
+ if (IS_ERR(trans)) {
+ btrfs_free_path(path);
return PTR_ERR(trans);
+ }
ret = btrfs_search_slot(trans, BTRFS_I(inode)->root, &key, path, 0, 1);
if (ret < 0) {
+ btrfs_free_path(path);
btrfs_abort_transaction(trans, ret);
return ret;
}
@@ -151,12 +213,13 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
btrfs_release_path(path);
ret = btrfs_insert_empty_item(trans, BTRFS_I(inode)->root, path, &key, len);
if (ret) {
+ btrfs_free_path(path);
btrfs_abort_transaction(trans, ret);
return ret;
}
}
-
btrfs_fscrypt_update_context(path, ctx, len);
+ btrfs_free_path(path);
if (fs_data)
return ret;
@@ -166,6 +229,7 @@ static int btrfs_fscrypt_set_context(struct inode *inode, const void *ctx,
inode_inc_iversion(inode);
inode->i_ctime = current_time(inode);
ret = btrfs_update_inode(trans, BTRFS_I(inode)->root, BTRFS_I(inode));
+
if (!ret) {
btrfs_end_transaction(trans);
return ret;
@@ -181,6 +245,45 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
}
+int btrfs_fscrypt_get_extent_info(const struct inode *inode,
+ u64 lblk_num,
+ struct fscrypt_info **info_ptr,
+ u64 *extent_offset,
+ u64 *extent_length)
+{
+ u64 offset = lblk_num << inode->i_blkbits;
+ struct extent_map *em;
+
+ /* Since IO must be in progress on this extent, this must succeed */
+ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, PAGE_SIZE);
+ if (!em) {
+ btrfs_err(BTRFS_I(inode)->root->fs_info,
+ "extent context requested for block %llu of inode %lu without an extent",
+ lblk_num, inode->i_ino);
+ return -EINVAL;
+ }
+
+ if (em->block_start == EXTENT_MAP_HOLE) {
+ btrfs_info(BTRFS_I(inode)->root->fs_info,
+ "extent context requested for block %llu of inode %lu without an extent",
+ lblk_num, inode->i_ino);
+ free_extent_map(em);
+ return -ENOENT;
+ }
+
+ *info_ptr = em->fscrypt_info;
+
+ if (extent_offset)
+ *extent_offset
+ = (offset - em->orig_start) >> inode->i_blkbits;
+
+ if (extent_length)
+ *extent_length = em->len >> inode->i_blkbits;
+
+ free_extent_map(em);
+ return 0;
+}
+
static struct block_device **btrfs_fscrypt_get_devices(struct super_block *sb,
unsigned int *num_devs)
{
@@ -232,6 +335,7 @@ const struct fscrypt_operations btrfs_fscrypt_ops = {
.get_context = btrfs_fscrypt_get_context,
.set_context = btrfs_fscrypt_set_context,
.empty_dir = btrfs_fscrypt_empty_dir,
+ .get_extent_info = btrfs_fscrypt_get_extent_info,
.get_devices = btrfs_fscrypt_get_devices,
.key_prefix = "btrfs:"
};
@@ -9,6 +9,9 @@
#include "fs.h"
+#define BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE \
+ (sizeof(u32) + FSCRYPT_SET_CONTEXT_MAX_SIZE)
+
static inline u32
btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
struct btrfs_file_extent_item *e)
@@ -38,6 +41,15 @@ bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
struct extent_buffer *leaf,
unsigned long de_name, u32 de_name_len);
+int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer, size_t *context_len);
+
+int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr, u8 ctxsize,
+ struct fscrypt_info **info_ptr);
+
#else
static inline int btrfs_fscrypt_get_disk_name(struct extent_buffer *leaf,
struct btrfs_dir_item *di,
@@ -56,8 +68,31 @@ static inline bool btrfs_fscrypt_match_name(struct fscrypt_name *fname,
return !memcmp_extent_buffer(leaf, fname->disk_name.name, de_name,
de_name_len);
}
+
+static inline int btrfs_fscrypt_fill_extent_context(struct btrfs_inode *inode,
+ struct fscrypt_info *info,
+ u8 *context_buffer,
+ size_t *context_len)
+{
+ return -EINVAL;
+}
+
+static inline int btrfs_fscrypt_load_extent_info(struct btrfs_inode *inode,
+ struct extent_buffer *leaf,
+ unsigned long ptr,
+ u8 ctxsize,
+ struct fscrypt_info **info_ptr)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_FS_ENCRYPTION */
+int btrfs_fscrypt_get_extent_info(const struct inode *inode,
+ u64 lblk_num,
+ struct fscrypt_info **info_ptr,
+ u64 *extent_offset,
+ u64 *extent_length);
void btrfs_fscrypt_copy_fscrypt_info(struct btrfs_inode *inode,
struct fscrypt_info *from,
struct fscrypt_info **to_ptr);
@@ -2885,17 +2885,41 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
u64 num_bytes = btrfs_stack_file_extent_num_bytes(stack_fi);
u64 ram_bytes = btrfs_stack_file_extent_ram_bytes(stack_fi);
struct btrfs_drop_extents_args drop_args = { 0 };
- size_t fscrypt_context_size =
- btrfs_stack_file_extent_encryption(stack_fi) ?
- FSCRYPT_SET_CONTEXT_MAX_SIZE : 0;
+ size_t fscrypt_context_size = 0;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+ u8 encryption = 0;
+
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
+ if (IS_ENCRYPTED(&inode->vfs_inode)) {
+ struct fscrypt_info *fscrypt_info;
+ u64 lblk_num = file_pos >> root->fs_info->sectorsize_bits;
+
+ ret = btrfs_fscrypt_get_extent_info(&inode->vfs_inode,
+ lblk_num, &fscrypt_info,
+ NULL, NULL);
+ if (ret) {
+ btrfs_err(root->fs_info, "No fscrypt context found");
+ goto out;
+ }
+
+ ret = btrfs_fscrypt_fill_extent_context(inode, fscrypt_info,
+ context,
+ &fscrypt_context_size);
+ if (ret)
+ goto out;
+ encryption = BTRFS_ENCRYPTION_FSCRYPT;
+
+ }
+
+ btrfs_set_stack_file_extent_encryption(stack_fi, encryption);
+
/*
- * we may be replacing one extent in the tree with another.
+ * We may be replacing one extent in the tree with another.
* The new extent is pinned in the extent map, and we don't want
* to drop it from the cache until it is completely in the btree.
*
@@ -2928,6 +2952,11 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
btrfs_item_ptr_offset(leaf, path->slots[0]),
sizeof(struct btrfs_file_extent_item));
+ write_extent_buffer(leaf, context,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(struct btrfs_file_extent_item),
+ fscrypt_context_size);
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
@@ -22,6 +22,7 @@
#include "zoned.h"
#include "inode-item.h"
#include "fs.h"
+#include "fscrypt.h"
#include "accessors.h"
#include "extent-tree.h"
#include "root-tree.h"
@@ -4637,6 +4638,14 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
size_t fscrypt_context_size = 0;
u8 encryption = IS_ENCRYPTED(&inode->vfs_inode) ?
BTRFS_ENCRYPTION_FSCRYPT : 0;
+ u8 context[BTRFS_FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
+
+ ret = btrfs_fscrypt_fill_extent_context(inode, em->fscrypt_info,
+ context, &fscrypt_context_size);
+ if (ret)
+ return ret;
+
+ btrfs_set_stack_file_extent_encryption(&fi, encryption);
btrfs_set_stack_file_extent_generation(&fi, trans->transid);
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
@@ -4658,7 +4667,6 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
btrfs_set_stack_file_extent_num_bytes(&fi, em->len);
btrfs_set_stack_file_extent_ram_bytes(&fi, em->ram_bytes);
btrfs_set_stack_file_extent_compression(&fi, em->compress_type);
- btrfs_set_stack_file_extent_encryption(&fi, encryption);
ret = log_extent_csums(trans, inode, log, em, ctx);
if (ret)
@@ -4698,6 +4706,10 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
write_extent_buffer(leaf, &fi,
btrfs_item_ptr_offset(leaf, path->slots[0]),
sizeof(fi));
+ write_extent_buffer(leaf, context,
+ btrfs_item_ptr_offset(leaf, path->slots[0]) +
+ sizeof(fi), fscrypt_context_size);
+
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
This change actually saves and loads the extent contexts created and freed by the last change. Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> --- fs/btrfs/file-item.c | 7 +++ fs/btrfs/fscrypt.c | 108 ++++++++++++++++++++++++++++++++++++++++++- fs/btrfs/fscrypt.h | 35 ++++++++++++++ fs/btrfs/inode.c | 37 +++++++++++++-- fs/btrfs/tree-log.c | 14 +++++- 5 files changed, 194 insertions(+), 7 deletions(-)