On Wed, Jul 27, 2022 at 04:46:22PM -0700, Boris Burkov wrote: > 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@xxxxxx> > --- > @@ -4886,6 +4889,80 @@ 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, desc->hash_algorithm); > + TLV_PUT_U32(sctx, BTRFS_SEND_A_VERITY_BLOCK_SIZE, 1 << desc->log_blocksize); bitshifts should be done on unsigned types 1U << desc->log_blocksize > + TLV_PUT(sctx, BTRFS_SEND_A_VERITY_SALT_DATA, desc->salt, desc->salt_size); > + TLV_PUT(sctx, BTRFS_SEND_A_VERITY_SIG_DATA, desc->signature, desc->sig_size); > + > + ret = send_cmd(sctx); > + > +tlv_put_failure: > +out: > + return ret; > +} > + > +static int process_new_verity(struct send_ctx *sctx) > +{ > + int ret = 0; > + struct btrfs_fs_info *fs_info = sctx->send_root->fs_info; > + struct inode *inode; > + struct fsverity_descriptor *desc; > + 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 = fs_info->sb->s_vop->get_verity_descriptor(inode, NULL, 0); > + if (ret < 0) > + goto iput; > + > + if (ret > FS_VERITY_MAX_DESCRIPTOR_SIZE) { > + ret = -EMSGSIZE; > + goto iput; > + } > + desc = kmalloc(ret, GFP_KERNEL); I think that once there's a file with verity record there will be more so it would make sense to cache the allocated memory, which means to allocate the full size. FS_VERITY_MAX_DESCRIPTOR_SIZE is 16K so this should be allocated by kvmalloc, like we have for other data during send. > + if (!desc) { > + ret = -ENOMEM; > + goto iput; > + } > + > + ret = fs_info->sb->s_vop->get_verity_descriptor(inode, desc, ret); > + if (ret < 0) > + goto free_desc; > + > + p = fs_path_alloc(); > + if (!p) { > + ret = -ENOMEM; > + goto free_desc; > + } > + ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen, p); > + if (ret < 0) > + goto free_path; > + > + ret = send_verity(sctx, p, desc); > + if (ret < 0) > + goto free_path; > + > +free_path: > + fs_path_free(p); > +free_desc: > + kfree(desc); > +iput: > + iput(inode); > + return ret; > +} > + > static inline u64 max_send_read_size(const struct send_ctx *sctx) > { > return sctx->send_max_size - SZ_16K; > --- a/fs/btrfs/send.h > +++ b/fs/btrfs/send.h > @@ -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, I find the name confusing, it sounds like enabling it for the whole filesystem, but it affects only one file. > + BTRFS_SEND_C_MAX_V3 = 26, > /* End */ > - BTRFS_SEND_C_MAX = 25, > + BTRFS_SEND_C_MAX = 26, > }; > > /* attributes in send stream */ > @@ -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, > };