diff options
Diffstat (limited to 'fs/btrfs/send.c')
| -rw-r--r-- | fs/btrfs/send.c | 461 | 
1 files changed, 259 insertions, 202 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index e7671afcee4f..4ef4167072b8 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -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" @@ -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; @@ -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) @@ -842,17 +846,32 @@ out:  	return ret;  } +struct btrfs_inode_info { +	u64 size; +	u64 gen; +	u64 mode; +	u64 uid; +	u64 gid; +	u64 rdev; +	u64 fileattr; +	u64 nlink; +}; +  /*   * Helper function to retrieve some fields from an inode item.   */ -static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path, -			  u64 ino, u64 *size, u64 *gen, u64 *mode, u64 *uid, -			  u64 *gid, u64 *rdev, u64 *fileattr) +static int get_inode_info(struct btrfs_root *root, u64 ino, +			  struct btrfs_inode_info *info)  {  	int ret; +	struct btrfs_path *path;  	struct btrfs_inode_item *ii;  	struct btrfs_key key; +	path = alloc_path_for_send(); +	if (!path) +		return -ENOMEM; +  	key.objectid = ino;  	key.type = BTRFS_INODE_ITEM_KEY;  	key.offset = 0; @@ -860,47 +879,43 @@ static int __get_inode_info(struct btrfs_root *root, struct btrfs_path *path,  	if (ret) {  		if (ret > 0)  			ret = -ENOENT; -		return ret; +		goto out;  	} +	if (!info) +		goto out; +  	ii = btrfs_item_ptr(path->nodes[0], path->slots[0],  			struct btrfs_inode_item); -	if (size) -		*size = btrfs_inode_size(path->nodes[0], ii); -	if (gen) -		*gen = btrfs_inode_generation(path->nodes[0], ii); -	if (mode) -		*mode = btrfs_inode_mode(path->nodes[0], ii); -	if (uid) -		*uid = btrfs_inode_uid(path->nodes[0], ii); -	if (gid) -		*gid = btrfs_inode_gid(path->nodes[0], ii); -	if (rdev) -		*rdev = btrfs_inode_rdev(path->nodes[0], ii); +	info->size = btrfs_inode_size(path->nodes[0], ii); +	info->gen = btrfs_inode_generation(path->nodes[0], ii); +	info->mode = btrfs_inode_mode(path->nodes[0], ii); +	info->uid = btrfs_inode_uid(path->nodes[0], ii); +	info->gid = btrfs_inode_gid(path->nodes[0], ii); +	info->rdev = btrfs_inode_rdev(path->nodes[0], ii); +	info->nlink = btrfs_inode_nlink(path->nodes[0], ii);  	/*  	 * Transfer the unchanged u64 value of btrfs_inode_item::flags, that's  	 * otherwise logically split to 32/32 parts.  	 */ -	if (fileattr) -		*fileattr = btrfs_inode_flags(path->nodes[0], ii); +	info->fileattr = btrfs_inode_flags(path->nodes[0], ii); +out: +	btrfs_free_path(path);  	return ret;  } -static int get_inode_info(struct btrfs_root *root, -			  u64 ino, u64 *size, u64 *gen, -			  u64 *mode, u64 *uid, u64 *gid, -			  u64 *rdev, u64 *fileattr) +static int get_inode_gen(struct btrfs_root *root, u64 ino, u64 *gen)  { -	struct btrfs_path *path;  	int ret; +	struct btrfs_inode_info info; -	path = alloc_path_for_send(); -	if (!path) -		return -ENOMEM; -	ret = __get_inode_info(root, path, ino, size, gen, mode, uid, gid, -			       rdev, fileattr); -	btrfs_free_path(path); +	if (!gen) +		return -EPERM; + +	ret = get_inode_info(root, ino, &info); +	if (!ret) +		*gen = info.gen;  	return ret;  } @@ -1643,21 +1658,22 @@ static int get_cur_inode_state(struct send_ctx *sctx, u64 ino, u64 gen)  	int right_ret;  	u64 left_gen;  	u64 right_gen; +	struct btrfs_inode_info info; -	ret = get_inode_info(sctx->send_root, ino, NULL, &left_gen, NULL, NULL, -			NULL, NULL, NULL); +	ret = get_inode_info(sctx->send_root, ino, &info);  	if (ret < 0 && ret != -ENOENT)  		goto out; -	left_ret = ret; +	left_ret = (info.nlink == 0) ? -ENOENT : ret; +	left_gen = info.gen;  	if (!sctx->parent_root) {  		right_ret = -ENOENT;  	} else { -		ret = get_inode_info(sctx->parent_root, ino, NULL, &right_gen, -				NULL, NULL, NULL, NULL, NULL); +		ret = get_inode_info(sctx->parent_root, ino, &info);  		if (ret < 0 && ret != -ENOENT)  			goto out; -		right_ret = ret; +		right_ret = (info.nlink == 0) ? -ENOENT : ret; +		right_gen = info.gen;  	}  	if (!left_ret && !right_ret) { @@ -1816,8 +1832,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,  	btrfs_release_path(path);  	if (dir_gen) { -		ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL, -				     NULL, NULL, NULL, NULL); +		ret = get_inode_gen(root, parent_dir, dir_gen);  		if (ret < 0)  			goto out;  	} @@ -1874,6 +1889,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,  	int ret = 0;  	u64 gen;  	u64 other_inode = 0; +	struct btrfs_inode_info info;  	if (!sctx->parent_root)  		goto out; @@ -1888,8 +1904,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,  	 * and we can just unlink this entry.  	 */  	if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) { -		ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL, -				     NULL, NULL, NULL, NULL); +		ret = get_inode_gen(sctx->parent_root, dir, &gen);  		if (ret < 0 && ret != -ENOENT)  			goto out;  		if (ret) { @@ -1916,13 +1931,14 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,  	 */  	if (other_inode > sctx->send_progress ||  	    is_waiting_for_move(sctx, other_inode)) { -		ret = get_inode_info(sctx->parent_root, other_inode, NULL, -				who_gen, who_mode, NULL, NULL, NULL, NULL); +		ret = get_inode_info(sctx->parent_root, other_inode, &info);  		if (ret < 0)  			goto out;  		ret = 1;  		*who_ino = other_inode; +		*who_gen = info.gen; +		*who_mode = info.mode;  	} else {  		ret = 0;  	} @@ -1955,8 +1971,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,  		goto out;  	if (dir != BTRFS_FIRST_FREE_OBJECTID) { -		ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL, -				     NULL, NULL, NULL, NULL); +		ret = get_inode_gen(sctx->send_root, dir, &gen);  		if (ret < 0 && ret != -ENOENT)  			goto out;  		if (ret) { @@ -1978,8 +1993,7 @@ static int did_overwrite_ref(struct send_ctx *sctx,  		goto out;  	} -	ret = get_inode_info(sctx->send_root, ow_inode, NULL, &gen, NULL, NULL, -			NULL, NULL, NULL); +	ret = get_inode_gen(sctx->send_root, ow_inode, &gen);  	if (ret < 0)  		goto out; @@ -2645,6 +2659,7 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)  	int ret = 0;  	struct fs_path *p;  	int cmd; +	struct btrfs_inode_info info;  	u64 gen;  	u64 mode;  	u64 rdev; @@ -2656,10 +2671,12 @@ static int send_create_inode(struct send_ctx *sctx, u64 ino)  		return -ENOMEM;  	if (ino != sctx->cur_ino) { -		ret = get_inode_info(sctx->send_root, ino, NULL, &gen, &mode, -				     NULL, NULL, &rdev, NULL); +		ret = get_inode_info(sctx->send_root, ino, &info);  		if (ret < 0)  			goto out; +		gen = info.gen; +		mode = info.mode; +		rdev = info.rdev;  	} else {  		gen = sctx->cur_inode_gen;  		mode = sctx->cur_inode_mode; @@ -3359,8 +3376,7 @@ finish:  		/*  		 * The parent inode might have been deleted in the send snapshot  		 */ -		ret = get_inode_info(sctx->send_root, cur->dir, NULL, -				     NULL, NULL, NULL, NULL, NULL, NULL); +		ret = get_inode_info(sctx->send_root, cur->dir, NULL);  		if (ret == -ENOENT) {  			ret = 0;  			continue; @@ -3534,12 +3550,10 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,  		goto out;  	} -	ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL, -			     &left_gen, NULL, NULL, NULL, NULL, NULL); +	ret = get_inode_gen(sctx->parent_root, di_key.objectid, &left_gen);  	if (ret < 0)  		goto out; -	ret = get_inode_info(sctx->send_root, di_key.objectid, NULL, -			     &right_gen, NULL, NULL, NULL, NULL, NULL); +	ret = get_inode_gen(sctx->send_root, di_key.objectid, &right_gen);  	if (ret < 0) {  		if (ret == -ENOENT)  			ret = 0; @@ -3669,8 +3683,7 @@ static int is_ancestor(struct btrfs_root *root,  				cur_offset = item_size;  			} -			ret = get_inode_info(root, parent, NULL, &parent_gen, -					     NULL, NULL, NULL, NULL, NULL); +			ret = get_inode_gen(root, parent, &parent_gen);  			if (ret < 0)  				goto out;  			ret = check_ino_in_path(root, ino1, ino1_gen, @@ -3760,9 +3773,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,  		     memcmp(path_before->start, path_after->start, len1))) {  			u64 parent_ino_gen; -			ret = get_inode_info(sctx->parent_root, ino, NULL, -					     &parent_ino_gen, NULL, NULL, NULL, -					     NULL, NULL); +			ret = get_inode_gen(sctx->parent_root, ino, &parent_ino_gen);  			if (ret < 0)  				goto out;  			if (ino_gen == parent_ino_gen) { @@ -4441,8 +4452,7 @@ static int record_new_ref_if_needed(int num, u64 dir, int index,  	struct recorded_ref *ref;  	u64 dir_gen; -	ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL, -			     NULL, NULL, NULL, NULL); +	ret = get_inode_gen(sctx->send_root, dir, &dir_gen);  	if (ret < 0)  		goto out; @@ -4472,8 +4482,7 @@ static int record_deleted_ref_if_needed(int num, u64 dir, int index,  	struct recorded_ref *ref;  	u64 dir_gen; -	ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL, -			     NULL, NULL, NULL, NULL); +	ret = get_inode_gen(sctx->parent_root, dir, &dir_gen);  	if (ret < 0)  		goto out; @@ -4886,6 +4895,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; @@ -5056,8 +5143,7 @@ static int send_clone(struct send_ctx *sctx,  	TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH, p);  	if (clone_root->root == sctx->send_root) { -		ret = get_inode_info(sctx->send_root, clone_root->ino, NULL, -				&gen, NULL, NULL, NULL, NULL, NULL); +		ret = get_inode_gen(sctx->send_root, clone_root->ino, &gen);  		if (ret < 0)  			goto out;  		ret = get_cur_path(sctx, clone_root->ino, gen, p); @@ -5536,6 +5622,7 @@ static int clone_range(struct send_ctx *sctx, struct btrfs_path *dst_path,  	struct btrfs_path *path;  	struct btrfs_key key;  	int ret; +	struct btrfs_inode_info info;  	u64 clone_src_i_size = 0;  	/* @@ -5565,12 +5652,11 @@ static int clone_range(struct send_ctx *sctx, struct btrfs_path *dst_path,  	 * There are inodes that have extents that lie behind its i_size. Don't  	 * accept clones from these extents.  	 */ -	ret = __get_inode_info(clone_root->root, path, clone_root->ino, -			       &clone_src_i_size, NULL, NULL, NULL, NULL, NULL, -			       NULL); +	ret = get_inode_info(clone_root->root, clone_root->ino, &info);  	btrfs_release_path(path);  	if (ret < 0)  		goto out; +	clone_src_i_size = info.size;  	/*  	 * We can't send a clone operation for the entire range if we find @@ -6259,6 +6345,7 @@ out:  static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)  {  	int ret = 0; +	struct btrfs_inode_info info;  	u64 left_mode;  	u64 left_uid;  	u64 left_gid; @@ -6301,11 +6388,13 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)  		goto out;  	if (!at_end && sctx->cmp_key->objectid == sctx->cur_ino)  		goto out; - -	ret = get_inode_info(sctx->send_root, sctx->cur_ino, NULL, NULL, -			&left_mode, &left_uid, &left_gid, NULL, &left_fileattr); +	ret = get_inode_info(sctx->send_root, sctx->cur_ino, &info);  	if (ret < 0)  		goto out; +	left_mode = info.mode; +	left_uid = info.uid; +	left_gid = info.gid; +	left_fileattr = info.fileattr;  	if (!sctx->parent_root || sctx->cur_inode_new) {  		need_chown = 1; @@ -6316,11 +6405,14 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)  	} else {  		u64 old_size; -		ret = get_inode_info(sctx->parent_root, sctx->cur_ino, -				&old_size, NULL, &right_mode, &right_uid, -				&right_gid, NULL, &right_fileattr); +		ret = get_inode_info(sctx->parent_root, sctx->cur_ino, &info);  		if (ret < 0)  			goto out; +		old_size = info.size; +		right_mode = info.mode; +		right_uid = info.uid; +		right_gid = info.gid; +		right_fileattr = info.fileattr;  		if (left_uid != right_uid || left_gid != right_gid)  			need_chown = 1; @@ -6377,6 +6469,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) @@ -6407,86 +6504,6 @@ out:  	return ret;  } -struct parent_paths_ctx { -	struct list_head *refs; -	struct send_ctx *sctx; -}; - -static int record_parent_ref(int num, u64 dir, int index, struct fs_path *name, -			     void *ctx) -{ -	struct parent_paths_ctx *ppctx = ctx; - -	/* -	 * Pass 0 as the generation for the directory, we don't care about it -	 * here as we have no new references to add, we just want to delete all -	 * references for an inode. -	 */ -	return record_ref_in_tree(&ppctx->sctx->rbtree_deleted_refs, ppctx->refs, -				  name, dir, 0, ppctx->sctx); -} - -/* - * Issue unlink operations for all paths of the current inode found in the - * parent snapshot. - */ -static int btrfs_unlink_all_paths(struct send_ctx *sctx) -{ -	LIST_HEAD(deleted_refs); -	struct btrfs_path *path; -	struct btrfs_root *root = sctx->parent_root; -	struct btrfs_key key; -	struct btrfs_key found_key; -	struct parent_paths_ctx ctx; -	int iter_ret = 0; -	int ret; - -	path = alloc_path_for_send(); -	if (!path) -		return -ENOMEM; - -	key.objectid = sctx->cur_ino; -	key.type = BTRFS_INODE_REF_KEY; -	key.offset = 0; - -	ctx.refs = &deleted_refs; -	ctx.sctx = sctx; - -	btrfs_for_each_slot(root, &key, &found_key, path, iter_ret) { -		if (found_key.objectid != key.objectid) -			break; -		if (found_key.type != key.type && -		    found_key.type != BTRFS_INODE_EXTREF_KEY) -			break; - -		ret = iterate_inode_ref(root, path, &found_key, 1, -					record_parent_ref, &ctx); -		if (ret < 0) -			goto out; -	} -	/* Catch error found during iteration */ -	if (iter_ret < 0) { -		ret = iter_ret; -		goto out; -	} - -	while (!list_empty(&deleted_refs)) { -		struct recorded_ref *ref; - -		ref = list_first_entry(&deleted_refs, struct recorded_ref, list); -		ret = send_unlink(sctx, ref->full_path); -		if (ret < 0) -			goto out; -		recorded_ref_free(ref); -	} -	ret = 0; -out: -	btrfs_free_path(path); -	if (ret) -		__free_recorded_refs(&deleted_refs); -	return ret; -} -  static void close_current_inode(struct send_ctx *sctx)  {  	u64 i_size; @@ -6577,25 +6594,36 @@ static int changed_inode(struct send_ctx *sctx,  	 * file descriptor against it or turning a RO snapshot into RW mode,  	 * keep an open file descriptor against a file, delete it and then  	 * turn the snapshot back to RO mode before using it for a send -	 * operation. So if we find such cases, ignore the inode and all its -	 * items completely if it's a new inode, or if it's a changed inode -	 * make sure all its previous paths (from the parent snapshot) are all -	 * unlinked and all other the inode items are ignored. +	 * operation. The former is what the receiver operation does. +	 * Therefore, if we want to send these snapshots soon after they're +	 * received, we need to handle orphan inodes as well. Moreover, orphans +	 * can appear not only in the send snapshot but also in the parent +	 * snapshot. Here are several cases: +	 * +	 * Case 1: BTRFS_COMPARE_TREE_NEW +	 *       |  send snapshot  | action +	 * -------------------------------- +	 * nlink |        0        | ignore +	 * +	 * Case 2: BTRFS_COMPARE_TREE_DELETED +	 *       | parent snapshot | action +	 * ---------------------------------- +	 * nlink |        0        | as usual +	 * Note: No unlinks will be sent because there're no paths for it. +	 * +	 * Case 3: BTRFS_COMPARE_TREE_CHANGED +	 *           |       | parent snapshot | send snapshot | action +	 * ----------------------------------------------------------------------- +	 * subcase 1 | nlink |        0        |       0       | ignore +	 * subcase 2 | nlink |       >0        |       0       | new_gen(deletion) +	 * subcase 3 | nlink |        0        |      >0       | new_gen(creation) +	 *  	 */ -	if (result == BTRFS_COMPARE_TREE_NEW || -	    result == BTRFS_COMPARE_TREE_CHANGED) { -		u32 nlinks; - -		nlinks = btrfs_inode_nlink(sctx->left_path->nodes[0], left_ii); -		if (nlinks == 0) { +	if (result == BTRFS_COMPARE_TREE_NEW) { +		if (btrfs_inode_nlink(sctx->left_path->nodes[0], left_ii) == 0) {  			sctx->ignore_cur_inode = true; -			if (result == BTRFS_COMPARE_TREE_CHANGED) -				ret = btrfs_unlink_all_paths(sctx);  			goto out;  		} -	} - -	if (result == BTRFS_COMPARE_TREE_NEW) {  		sctx->cur_inode_gen = left_gen;  		sctx->cur_inode_new = true;  		sctx->cur_inode_deleted = false; @@ -6616,6 +6644,16 @@ static int changed_inode(struct send_ctx *sctx,  		sctx->cur_inode_mode = btrfs_inode_mode(  				sctx->right_path->nodes[0], right_ii);  	} else if (result == BTRFS_COMPARE_TREE_CHANGED) { +		u32 new_nlinks, old_nlinks; + +		new_nlinks = btrfs_inode_nlink(sctx->left_path->nodes[0], left_ii); +		old_nlinks = btrfs_inode_nlink(sctx->right_path->nodes[0], right_ii); +		if (new_nlinks == 0 && old_nlinks == 0) { +			sctx->ignore_cur_inode = true; +			goto out; +		} else if (new_nlinks == 0 || old_nlinks == 0) { +			sctx->cur_inode_new_gen = 1; +		}  		/*  		 * We need to do some special handling in case the inode was  		 * reported as changed with a changed generation number. This @@ -6642,38 +6680,44 @@ static int changed_inode(struct send_ctx *sctx,  			/*  			 * Now process the inode as if it was new.  			 */ -			sctx->cur_inode_gen = left_gen; -			sctx->cur_inode_new = true; -			sctx->cur_inode_deleted = false; -			sctx->cur_inode_size = btrfs_inode_size( -					sctx->left_path->nodes[0], left_ii); -			sctx->cur_inode_mode = btrfs_inode_mode( -					sctx->left_path->nodes[0], left_ii); -			sctx->cur_inode_rdev = btrfs_inode_rdev( -					sctx->left_path->nodes[0], left_ii); -			ret = send_create_inode_if_needed(sctx); -			if (ret < 0) -				goto out; +			if (new_nlinks > 0) { +				sctx->cur_inode_gen = left_gen; +				sctx->cur_inode_new = true; +				sctx->cur_inode_deleted = false; +				sctx->cur_inode_size = btrfs_inode_size( +						sctx->left_path->nodes[0], +						left_ii); +				sctx->cur_inode_mode = btrfs_inode_mode( +						sctx->left_path->nodes[0], +						left_ii); +				sctx->cur_inode_rdev = btrfs_inode_rdev( +						sctx->left_path->nodes[0], +						left_ii); +				ret = send_create_inode_if_needed(sctx); +				if (ret < 0) +					goto out; -			ret = process_all_refs(sctx, BTRFS_COMPARE_TREE_NEW); -			if (ret < 0) -				goto out; -			/* -			 * Advance send_progress now as we did not get into -			 * process_recorded_refs_if_needed in the new_gen case. -			 */ -			sctx->send_progress = sctx->cur_ino + 1; +				ret = process_all_refs(sctx, BTRFS_COMPARE_TREE_NEW); +				if (ret < 0) +					goto out; +				/* +				 * Advance send_progress now as we did not get +				 * into process_recorded_refs_if_needed in the +				 * new_gen case. +				 */ +				sctx->send_progress = sctx->cur_ino + 1; -			/* -			 * Now process all extents and xattrs of the inode as if -			 * they were all new. -			 */ -			ret = process_all_extents(sctx); -			if (ret < 0) -				goto out; -			ret = process_all_new_xattrs(sctx); -			if (ret < 0) -				goto out; +				/* +				 * Now process all extents and xattrs of the +				 * inode as if they were all new. +				 */ +				ret = process_all_extents(sctx); +				if (ret < 0) +					goto out; +				ret = process_all_new_xattrs(sctx); +				if (ret < 0) +					goto out; +			}  		} else {  			sctx->cur_inode_gen = left_gen;  			sctx->cur_inode_new = false; @@ -6785,18 +6829,27 @@ 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;  	int ret; -	ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL, -			     NULL, NULL, NULL); +	ret = get_inode_gen(sctx->send_root, dir, &new_gen);  	if (ret)  		return ret; -	ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL, -			     NULL, NULL, NULL, NULL); +	ret = get_inode_gen(sctx->parent_root, dir, &orig_gen);  	if (ret)  		return ret; @@ -6939,6 +6992,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: @@ -8036,6 +8092,7 @@ out:  		kvfree(sctx->clone_roots);  		kfree(sctx->send_buf_pages);  		kvfree(sctx->send_buf); +		kvfree(sctx->verity_descriptor);  		name_cache_free(sctx);  |