Skip to content

Commit

Permalink
btrfs: send: add support for fs-verity
Browse files Browse the repository at this point in the history
Preserve the fs-verity status of a btrfs file across send/recv.

There is no facility for installing the Merkle tree contents directly on
the receiving filesystem, so we package up the parameters used to enable
verity found in the verity descriptor. This gives the receive side
enough information to properly enable verity again. Note that this means
that receive will have to re-compute the whole Merkle tree, similar to
how compression worked before encoded_write.

Since the file becomes read-only after verity is enabled, it is
important that verity is added to the send stream after any file writes.
Therefore, when we process a verity item, merely note that it happened,
then actually create the command in the send stream during
'finish_inode_if_needed'.

This also creates V3 of the send stream format, without any format
changes besides adding the new commands and attributes.

Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Boris Burkov authored and David Sterba committed Sep 26, 2022
1 parent e5677f0 commit 3862201
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 7 deletions.
7 changes: 7 additions & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4103,6 +4103,7 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info)

extern const struct fsverity_operations btrfs_verityops;
int btrfs_drop_verity_items(struct btrfs_inode *inode);
int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size);

BTRFS_SETGET_FUNCS(verity_descriptor_encryption, struct btrfs_verity_descriptor_item,
encryption, 8);
Expand All @@ -4120,6 +4121,12 @@ static inline int btrfs_drop_verity_items(struct btrfs_inode *inode)
return 0;
}

static inline int btrfs_get_verity_descriptor(struct inode *inode, void *buf,
size_t buf_size)
{
return -EPERM;
}

#endif

/* Sanity test specific functions */
Expand Down
102 changes: 102 additions & 0 deletions fs/btrfs/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/string.h>
#include <linux/compat.h>
#include <linux/crc32c.h>
#include <linux/fsverity.h>

#include "send.h"
#include "ctree.h"
Expand Down Expand Up @@ -127,6 +128,8 @@ struct send_ctx {
bool cur_inode_new_gen;
bool cur_inode_deleted;
bool ignore_cur_inode;
bool cur_inode_needs_verity;
void *verity_descriptor;

u64 send_progress;

Expand Down Expand Up @@ -624,6 +627,7 @@ static int tlv_put(struct send_ctx *sctx, u16 attr, const void *data, int len)
return tlv_put(sctx, attr, &__tmp, sizeof(__tmp)); \
}

TLV_PUT_DEFINE_INT(8)
TLV_PUT_DEFINE_INT(32)
TLV_PUT_DEFINE_INT(64)

Expand Down Expand Up @@ -4886,6 +4890,84 @@ static int process_all_new_xattrs(struct send_ctx *sctx)
return ret;
}

static int send_verity(struct send_ctx *sctx, struct fs_path *path,
struct fsverity_descriptor *desc)
{
int ret;

ret = begin_cmd(sctx, BTRFS_SEND_C_ENABLE_VERITY);
if (ret < 0)
goto out;

TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, path);
TLV_PUT_U8(sctx, BTRFS_SEND_A_VERITY_ALGORITHM,
le8_to_cpu(desc->hash_algorithm));
TLV_PUT_U32(sctx, BTRFS_SEND_A_VERITY_BLOCK_SIZE,
1U << le8_to_cpu(desc->log_blocksize));
TLV_PUT(sctx, BTRFS_SEND_A_VERITY_SALT_DATA, desc->salt,
le8_to_cpu(desc->salt_size));
TLV_PUT(sctx, BTRFS_SEND_A_VERITY_SIG_DATA, desc->signature,
le32_to_cpu(desc->sig_size));

ret = send_cmd(sctx);

tlv_put_failure:
out:
return ret;
}

static int process_verity(struct send_ctx *sctx)
{
int ret = 0;
struct btrfs_fs_info *fs_info = sctx->send_root->fs_info;
struct inode *inode;
struct fs_path *p;

inode = btrfs_iget(fs_info->sb, sctx->cur_ino, sctx->send_root);
if (IS_ERR(inode))
return PTR_ERR(inode);

ret = btrfs_get_verity_descriptor(inode, NULL, 0);
if (ret < 0)
goto iput;

if (ret > FS_VERITY_MAX_DESCRIPTOR_SIZE) {
ret = -EMSGSIZE;
goto iput;
}
if (!sctx->verity_descriptor) {
sctx->verity_descriptor = kvmalloc(FS_VERITY_MAX_DESCRIPTOR_SIZE,
GFP_KERNEL);
if (!sctx->verity_descriptor) {
ret = -ENOMEM;
goto iput;
}
}

ret = btrfs_get_verity_descriptor(inode, sctx->verity_descriptor, ret);
if (ret < 0)
goto iput;

p = fs_path_alloc();
if (!p) {
ret = -ENOMEM;
goto iput;
}
ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p);
if (ret < 0)
goto free_path;

ret = send_verity(sctx, p, sctx->verity_descriptor);
if (ret < 0)
goto free_path;

free_path:
fs_path_free(p);
iput:
iput(inode);
return ret;
}

static inline u64 max_send_read_size(const struct send_ctx *sctx)
{
return sctx->send_max_size - SZ_16K;
Expand Down Expand Up @@ -6377,6 +6459,11 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)
if (ret < 0)
goto out;
}
if (sctx->cur_inode_needs_verity) {
ret = process_verity(sctx);
if (ret < 0)
goto out;
}

ret = send_capabilities(sctx);
if (ret < 0)
Expand Down Expand Up @@ -6785,6 +6872,17 @@ static int changed_extent(struct send_ctx *sctx,
return ret;
}

static int changed_verity(struct send_ctx *sctx, enum btrfs_compare_tree_result result)
{
int ret = 0;

if (!sctx->cur_inode_new_gen && !sctx->cur_inode_deleted) {
if (result == BTRFS_COMPARE_TREE_NEW)
sctx->cur_inode_needs_verity = true;
}
return ret;
}

static int dir_changed(struct send_ctx *sctx, u64 dir)
{
u64 orig_gen, new_gen;
Expand Down Expand Up @@ -6939,6 +7037,9 @@ static int changed_cb(struct btrfs_path *left_path,
ret = changed_xattr(sctx, result);
else if (key->type == BTRFS_EXTENT_DATA_KEY)
ret = changed_extent(sctx, result);
else if (key->type == BTRFS_VERITY_DESC_ITEM_KEY &&
key->offset == 0)
ret = changed_verity(sctx, result);
}

out:
Expand Down Expand Up @@ -8036,6 +8137,7 @@ long btrfs_ioctl_send(struct inode *inode, struct btrfs_ioctl_send_args *arg)
kvfree(sctx->clone_roots);
kfree(sctx->send_buf_pages);
kvfree(sctx->send_buf);
kvfree(sctx->verity_descriptor);

name_cache_free(sctx);

Expand Down
15 changes: 12 additions & 3 deletions fs/btrfs/send.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ enum btrfs_send_cmd {
BTRFS_SEND_C_ENCODED_WRITE = 25,
BTRFS_SEND_C_MAX_V2 = 25,

/* Version 3 */
BTRFS_SEND_C_ENABLE_VERITY = 26,
BTRFS_SEND_C_MAX_V3 = 26,
/* End */
BTRFS_SEND_C_MAX = 25,
BTRFS_SEND_C_MAX = 26,
};

/* attributes in send stream */
Expand Down Expand Up @@ -160,8 +163,14 @@ enum {
BTRFS_SEND_A_ENCRYPTION = 31,
BTRFS_SEND_A_MAX_V2 = 31,

/* End */
BTRFS_SEND_A_MAX = 31,
/* Version 3 */
BTRFS_SEND_A_VERITY_ALGORITHM = 32,
BTRFS_SEND_A_VERITY_BLOCK_SIZE = 33,
BTRFS_SEND_A_VERITY_SALT_DATA = 34,
BTRFS_SEND_A_VERITY_SIG_DATA = 35,
BTRFS_SEND_A_MAX_V3 = 35,

__BTRFS_SEND_A_MAX = 35,
};

long btrfs_ioctl_send(struct inode *inode, struct btrfs_ioctl_send_args *arg);
Expand Down
3 changes: 1 addition & 2 deletions fs/btrfs/verity.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,7 @@ static int btrfs_end_enable_verity(struct file *filp, const void *desc,
*
* Returns the size on success or a negative error code on failure.
*/
static int btrfs_get_verity_descriptor(struct inode *inode, void *buf,
size_t buf_size)
int btrfs_get_verity_descriptor(struct inode *inode, void *buf, size_t buf_size)
{
u64 true_size;
int ret = 0;
Expand Down
2 changes: 0 additions & 2 deletions fs/verity/fsverity_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ struct fsverity_info {
const struct inode *inode;
};

/* Arbitrary limit to bound the kmalloc() size. Can be changed. */
#define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384

#define FS_VERITY_MAX_SIGNATURE_SIZE (FS_VERITY_MAX_DESCRIPTOR_SIZE - \
sizeof(struct fsverity_descriptor))
Expand Down
3 changes: 3 additions & 0 deletions include/linux/fsverity.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
*/
#define FS_VERITY_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE

/* Arbitrary limit to bound the kmalloc() size. Can be changed. */
#define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384

/* Verity operations for filesystems */
struct fsverity_operations {

Expand Down

0 comments on commit 3862201

Please sign in to comment.