Skip to content

Commit

Permalink
btrfs: store a fscrypt extent context per normal file extent
Browse files Browse the repository at this point in the history
In order to encrypt data, each file extent must have its own persistent
fscrypt_extent_context, which is then provided to fscrypt upon request.
This is only needed for encrypted extents and is of variable size on
disk, so file extents must additionally keep track of their actual
length.

This puts the long-preserved 1-byte encryption field to work. Right now we
don't anticipate very many encryption policies, so 2 bits should be
ample; similarly right now we can't imagine a extent context larger than
fscrypt's current inode contexts, which are 40 bytes, so 6 bits is ample
to store the extent context's size; and therefore we can pack these
together into the one-byte encryption field without touching other space
reserved for future use.

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
  • Loading branch information
sweettea authored and intel-lab-lkp committed Oct 25, 2022
1 parent 7da55cf commit 624387a
Show file tree
Hide file tree
Showing 15 changed files with 255 additions and 34 deletions.
29 changes: 29 additions & 0 deletions fs/btrfs/accessors.h
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,16 @@ BTRFS_SETGET_STACK_FUNCS(stack_file_extent_disk_num_bytes,
struct btrfs_file_extent_item, disk_num_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_compression,
struct btrfs_file_extent_item, compression, 8);
BTRFS_SETGET_STACK_FUNCS(stack_file_extent_encryption,
struct btrfs_file_extent_item, encryption, 8);
static inline u8 btrfs_stack_file_extent_encryption_ctxsize(
struct btrfs_file_extent_item *e)
{
u8 ctxsize;

btrfs_unpack_encryption(e->encryption, NULL, &ctxsize);
return ctxsize;
}

static inline unsigned long
btrfs_file_extent_inline_start(const struct btrfs_file_extent_item *e)
Expand Down Expand Up @@ -995,6 +1005,25 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);

static inline u8
btrfs_file_extent_encryption_ctxsize(const struct extent_buffer *eb,
struct btrfs_file_extent_item *e)
{
u8 ctxsize;

btrfs_unpack_encryption(btrfs_file_extent_encryption(eb, e),
NULL, &ctxsize);
return ctxsize;
}

static inline u8
btrfs_file_extent_ctxsize_from_item(const struct extent_buffer *leaf,
const struct btrfs_path *path)
{
return (btrfs_item_size(leaf, path->slots[0]) -
sizeof(struct btrfs_file_extent_item));
}

/*
* this returns the number of bytes used by the item on disk, minus the
* size of any extent headers. If a file is compressed on disk, this is
Expand Down
3 changes: 3 additions & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "block-rsv.h"
#include "locking.h"
#include "misc.h"
#include "fscrypt.h"

struct btrfs_trans_handle;
struct btrfs_transaction;
Expand Down Expand Up @@ -1088,6 +1089,8 @@ struct btrfs_replace_extent_info {
u64 file_offset;
/* Pointer to a file extent item of type regular or prealloc. */
char *extent_buf;
/* The length of @extent_buf */
u32 extent_buf_size;
/*
* Set to true when attempting to replace a file range with a new extent
* described by this structure, set to false when attempting to clone an
Expand Down
7 changes: 7 additions & 0 deletions fs/btrfs/extent_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
if (!list_empty(&prev->list) || !list_empty(&next->list))
return 0;

/*
* Don't merge adjacent maps with different fscrypt_contexts.
*/
if (!memcmp(&prev->fscrypt_context, &next->fscrypt_context,
sizeof(next->fscrypt_context)))
return 0;

ASSERT(next->block_start != EXTENT_MAP_DELALLOC &&
prev->block_start != EXTENT_MAP_DELALLOC);

Expand Down
4 changes: 4 additions & 0 deletions fs/btrfs/extent_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <linux/rbtree.h>
#include <linux/refcount.h>
#include "fscrypt.h"

#define EXTENT_MAP_LAST_BYTE ((u64)-4)
#define EXTENT_MAP_HOLE ((u64)-3)
Expand All @@ -27,6 +28,8 @@ enum {
EXTENT_FLAG_FS_MAPPING,
/* This em is merged from two or more physically adjacent ems */
EXTENT_FLAG_MERGED,
/* This em has a fscrypt extent context */
EXTENT_FLAG_ENCRYPTED,
};

struct extent_map {
Expand All @@ -50,6 +53,7 @@ struct extent_map {
*/
u64 generation;
unsigned long flags;
struct btrfs_fscrypt_extent_context fscrypt_context;
/* Used for chunk mappings, flag EXTENT_FLAG_FS_MAPPING must be set */
struct map_lookup *map_lookup;
refcount_t refs;
Expand Down
16 changes: 16 additions & 0 deletions fs/btrfs/file-item.c
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,7 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
em->generation = btrfs_file_extent_generation(leaf, fi);
if (type == BTRFS_FILE_EXTENT_REG ||
type == BTRFS_FILE_EXTENT_PREALLOC) {
u8 ctxsize;
em->start = extent_start;
em->len = extent_end - extent_start;
em->orig_start = extent_start -
Expand All @@ -1247,13 +1248,28 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode,
em->compress_type = compress_type;
em->block_start = bytenr;
em->block_len = em->orig_block_len;
} else if (btrfs_file_extent_encryption(leaf, fi)) {
set_bit(EXTENT_FLAG_ENCRYPTED, &em->flags);
em->block_start = bytenr;
em->block_len = em->orig_block_len;
} else {
bytenr += btrfs_file_extent_offset(leaf, fi);
em->block_start = bytenr;
em->block_len = em->len;
if (type == BTRFS_FILE_EXTENT_PREALLOC)
set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
}

ctxsize = btrfs_file_extent_ctxsize_from_item(leaf, path);
ASSERT(ctxsize == btrfs_file_extent_encryption_ctxsize(leaf, fi));

#ifdef CONFIG_FS_ENCRYPTION
em->fscrypt_context.len = ctxsize;

read_extent_buffer(leaf, em->fscrypt_context.buffer,
(unsigned long)fi->fscrypt_context,
ctxsize);
#endif /* CONFIG_FS_ENCRYPTION */
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
em->block_start = EXTENT_MAP_INLINE;
em->start = extent_start;
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2554,14 +2554,14 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = extent_info->file_offset;
ret = btrfs_insert_empty_item(trans, root, path, &key,
sizeof(struct btrfs_file_extent_item));
extent_info->extent_buf_size);
if (ret)
return ret;
leaf = path->nodes[0];
slot = path->slots[0];
write_extent_buffer(leaf, extent_info->extent_buf,
btrfs_item_ptr_offset(leaf, slot),
sizeof(struct btrfs_file_extent_item));
extent_info->extent_buf_size);
extent = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
ASSERT(btrfs_file_extent_type(leaf, extent) != BTRFS_FILE_EXTENT_INLINE);
btrfs_set_file_extent_offset(leaf, extent, extent_info->data_offset);
Expand Down
22 changes: 22 additions & 0 deletions fs/btrfs/fscrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "messages.h"
#include "transaction.h"
#include "xattr.h"
#include "fscrypt.h"

static int btrfs_fscrypt_get_context(struct inode *inode, void *ctx, size_t len)
{
Expand Down Expand Up @@ -181,9 +182,30 @@ static bool btrfs_fscrypt_empty_dir(struct inode *inode)
return inode->i_size == BTRFS_EMPTY_DIR_SIZE;
}

static int btrfs_fscrypt_get_extent_context(const struct inode *inode,
u64 lblk_num, void *ctx,
size_t len,
size_t *extent_offset,
size_t *extent_length)
{
return len;
}

static int btrfs_fscrypt_set_extent_context(void *extent, void *ctx,
size_t len)
{
struct btrfs_fscrypt_extent_context *extent_context = extent;

memcpy(extent_context->buffer, ctx, len);
extent_context->len = len;
return 0;
}

const struct fscrypt_operations btrfs_fscrypt_ops = {
.key_prefix = "btrfs:",
.get_context = btrfs_fscrypt_get_context,
.set_context = btrfs_fscrypt_set_context,
.empty_dir = btrfs_fscrypt_empty_dir,
.get_extent_context = btrfs_fscrypt_get_extent_context,
.set_extent_context = btrfs_fscrypt_set_extent_context,
};
35 changes: 35 additions & 0 deletions fs/btrfs/fscrypt.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,41 @@

#include <linux/fscrypt.h>

#define BTRFS_ENCRYPTION_POLICY_BITS 2
#define BTRFS_ENCRYPTION_CTXSIZE_BITS 6

#define BTRFS_ENCRYPTION_POLICY_MASK ((1 << BTRFS_ENCRYPTION_POLICY_BITS) - 1)
#define BTRFS_ENCRYPTION_CTXSIZE_MASK \
(((1 << BTRFS_ENCRYPTION_CTXSIZE_BITS) - 1) << \
BTRFS_ENCRYPTION_POLICY_BITS)

#ifdef CONFIG_FS_ENCRYPTION
struct btrfs_fscrypt_extent_context {
u8 buffer[FSCRYPT_EXTENT_CONTEXT_MAX_SIZE];
u8 len;
};
#else
struct btrfs_fscrypt_extent_context {
u8 len;
};
#endif

static inline void btrfs_unpack_encryption(u8 encryption,
u8 *policy,
u8 *ctxsize)
{
if (policy)
*policy = encryption & BTRFS_ENCRYPTION_POLICY_MASK;
if (ctxsize)
*ctxsize = ((encryption & BTRFS_ENCRYPTION_CTXSIZE_MASK) >>
BTRFS_ENCRYPTION_POLICY_BITS);
}

static inline u8 btrfs_pack_encryption(u8 policy, u8 ctxsize)
{
return policy | (ctxsize << BTRFS_ENCRYPTION_POLICY_BITS);
}

extern const struct fscrypt_operations btrfs_fscrypt_ops;

#endif /* BTRFS_FSCRYPT_H */

0 comments on commit 624387a

Please sign in to comment.