diff mbox series

[v2] btrfs-progs: receive: cannot find clone source subvol when receiving in reverse direction

Message ID ce4f7788f24442cd6f4779baee1992bb1978b85c.camel@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v2] btrfs-progs: receive: cannot find clone source subvol when receiving in reverse direction | expand

Commit Message

Arsenii Skvortsov Sept. 2, 2023, 2:57 a.m. UTC
process_clone only searches the received_uuid, but could exist in an earlier
uuid that isn't the received_uuid.  Mirror what process_snapshot does and search
both the received_uuid and if that fails look up by normal uuid.

Fixes: https://github.com/kdave/btrfs-progs/issues/606

Signed-off-by: Arsenii Skvortsov <ettavolt@gmail.com>
---
 cmds/receive.c                               | 28 +++---
 tests/misc-tests/058-reverse-receive/test.sh | 98 ++++++++++++++++++++
 2 files changed, 115 insertions(+), 11 deletions(-)
 create mode 100755 tests/misc-tests/058-reverse-receive/test.sh

Comments

Wang Yugui Oct. 3, 2023, 10:34 a.m. UTC | #1
Hi,

> process_clone only searches the received_uuid, but could exist in an earlier
> uuid that isn't the received_uuid.  Mirror what process_snapshot does and search
> both the received_uuid and if that fails look up by normal uuid.
> 
> Fixes: https://github.com/kdave/btrfs-progs/issues/606
> 
> Signed-off-by: Arsenii Skvortsov <ettavolt@gmail.com>

Is this patch yet not merged in btrfs-progs devel branch?

Best Regards
Wang Yugui (wangyugui@e16-tech.com)
2023/10/03


> ---
>  cmds/receive.c                               | 28 +++---
>  tests/misc-tests/058-reverse-receive/test.sh | 98 ++++++++++++++++++++
>  2 files changed, 115 insertions(+), 11 deletions(-)
>  create mode 100755 tests/misc-tests/058-reverse-receive/test.sh
> 
> diff --git a/cmds/receive.c b/cmds/receive.c
> index d16dc0a..763c2af 100644
> --- a/cmds/receive.c
> +++ b/cmds/receive.c
> @@ -222,6 +222,19 @@ out:
>  	return ret;
>  }
>  
> +static struct subvol_info *search_source_subvol(struct subvol_uuid_search *s,
> +			const u8 *subvol_uuid, u64 transid)
> +{
> +	struct subvol_info *found;
> +	found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
> +			subvol_search_by_received_uuid);
> +	if (IS_ERR_OR_NULL(found)) {
> +		found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
> +				subvol_search_by_uuid);
> +	}
> +	return found;
> +}
> +
>  static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
>  			    const u8 *parent_uuid, u64 parent_ctransid,
>  			    void *user)
> @@ -284,14 +297,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
>  	memset(&args_v2, 0, sizeof(args_v2));
>  	strncpy_null(args_v2.name, path);
>  
> -	parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
> -					   parent_ctransid, NULL,
> -					   subvol_search_by_received_uuid);
> -	if (IS_ERR_OR_NULL(parent_subvol)) {
> -		parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
> -						   parent_ctransid, NULL,
> -						   subvol_search_by_uuid);
> -	}
> +	parent_subvol = search_source_subvol(rctx->mnt_fd, parent_uuid,
> +			parent_ctransid);
>  	if (IS_ERR_OR_NULL(parent_subvol)) {
>  		if (!parent_subvol)
>  			ret = -ENOENT;
> @@ -746,9 +753,8 @@ static int process_clone(const char *path, u64 offset, u64 len,
>  		   BTRFS_UUID_SIZE) == 0) {
>  		subvol_path = rctx->cur_subvol_path;
>  	} else {
> -		si = subvol_uuid_search(rctx->mnt_fd, 0, clone_uuid, clone_ctransid,
> -					NULL,
> -					subvol_search_by_received_uuid);
> +		si = search_source_subvol(rctx->mnt_fd, clone_uuid,
> +				clone_ctransid);
>  		if (IS_ERR_OR_NULL(si)) {
>  			char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
>  
> diff --git a/tests/misc-tests/058-reverse-receive/test.sh b/tests/misc-tests/058-reverse-receive/test.sh
> new file mode 100755
> index 0000000..6eff560
> --- /dev/null
> +++ b/tests/misc-tests/058-reverse-receive/test.sh
> @@ -0,0 +1,98 @@
> +#!/bin/bash
> +#
> +# Receive in reverse direction must not throw an error if it can find an earlier "sent" parent.
> +# In general, shows a backup+sync setup between two (or more) PCs with an external drive.
> +
> +source "$TEST_TOP/common"
> +
> +check_prereq mkfs.btrfs
> +check_prereq btrfs
> +check_global_prereq dd
> +
> +declare -a roots
> +i_pc1=1
> +# An external drive used to backup and carry profile.
> +i_ext=2
> +i_pc2=3
> +roots[$i_pc1]="$TEST_MNT/pc1"
> +roots[$i_ext]="$TEST_MNT/external"
> +roots[$i_pc2]="$TEST_MNT/pc2"
> +
> +setup_root_helper
> +mkdir -p ${roots[@]}
> +setup_loopdevs 3
> +prepare_loopdevs
> +for i in `seq 3`; do
> +	TEST_DEV=${loopdevs[$i]}
> +    TEST_MNT="${roots[$i]}"
> +    run_check_mkfs_test_dev
> +    run_check_mount_test_dev
> +    run_check $SUDO_HELPER mkdir -p "$TEST_MNT/.snapshots"
> +done
> +
> +run_check_update_file()
> +{
> +    run_check $SUDO_HELPER cp --reflink ${roots[$1]}/profile/$2 ${roots[$1]}/profile/staging
> +    run_check $SUDO_HELPER dd if=/dev/urandom conv=notrunc bs=4K count=4 oseek=$3 "of=${roots[$1]}/profile/staging"
> +    run_check $SUDO_HELPER mv ${roots[$1]}/profile/staging ${roots[$1]}/profile/$2
> +}
> +run_check_copy_snapshot_with_diff()
> +{
> +    _mktemp_local send.data
> +    run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data -p "${roots[$1]}/.snapshots/$2" "${roots[$1]}/.snapshots/$3"
> +    run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$4]}/.snapshots"
> +}
> +run_check_backup_profile()
> +{
> +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$1]}/profile" "${roots[$1]}/.snapshots/$3"
> +    run_check_copy_snapshot_with_diff $1 $2 $3 $i_ext
> +    # Don't keep old snapshot in pc
> +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
> +}
> +run_check_restore_profile()
> +{
> +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot "${roots[$1]}/.snapshots/$2" "${roots[$1]}/profile"
> +}
> +run_check_copy_fresh_backup_and_replace_profile()
> +{
> +    run_check_copy_snapshot_with_diff $i_ext $2 $3 $1
> +    # IRL, it would be a nice idea to make a backup snapshot before deleting.
> +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/profile"
> +    run_check_restore_profile $1 $3
> +    # Don't keep old snapshot in pc
> +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
> +}
> +
> +
> +run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "${roots[$i_pc1]}/profile"
> +run_check $SUDO_HELPER dd if=/dev/urandom bs=4K count=16 "of=${roots[$i_pc1]}/profile/day1"
> +run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$i_pc1]}/profile" "${roots[$i_pc1]}/.snapshots/day1"
> +_mktemp_local send.data
> +run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_pc1]}/.snapshots/day1"
> +run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_ext]}/.snapshots"
> +
> +run_check_update_file $i_pc1 day1 2
> +run_check_backup_profile $i_pc1 day1 day2
> +
> +_mktemp_local send.data
> +run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_ext]}/.snapshots/day2"
> +run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_pc2]}/.snapshots"
> +run_check_restore_profile $i_pc2 day2
> +run_check_update_file $i_pc2 day1 3
> +run_check_backup_profile $i_pc2 day2 day3
> +
> +run_check_update_file $i_pc2 day1 4
> +run_check_backup_profile $i_pc2 day3 day4
> +
> +run_check_copy_fresh_backup_and_replace_profile $i_pc1 day2 day4
> +run_check_update_file $i_pc1 day1 5
> +run_check_backup_profile $i_pc1 day4 day5
> +
> +run_check_copy_fresh_backup_and_replace_profile $i_pc2 day4 day5
> +run_check_update_file $i_pc2 day1 6
> +run_check_backup_profile $i_pc2 day5 day6
> +
> +run_check_umount_test_dev ${loopdevs[@]}
> +rmdir ${roots[@]}
> +rm -f send.data
> +cleanup_loopdevs
> -- 
> 2.41.0
Arsenii Skvortsov Oct. 3, 2023, 11:38 a.m. UTC | #2
On Tue, 2023-10-03 at 18:34 +0800, Wang Yugui wrote:
> Hi,
> 
> > process_clone only searches the received_uuid, but could exist in an earlier
> > uuid that isn't the received_uuid.  Mirror what process_snapshot does and search
> > both the received_uuid and if that fails look up by normal uuid.
> > 
> > Fixes: https://github.com/kdave/btrfs-progs/issues/606
> > 
> > Signed-off-by: Arsenii Skvortsov <ettavolt@gmail.com>
> 
> Is this patch yet not merged in btrfs-progs devel branch?

AFAIK, it is not. There is no activity in the PR and no reply to this v2 message.

> Best Regards
> Wang Yugui (wangyugui@e16-tech.com)
> 2023/10/03
> 
> 
> > ---
> >  cmds/receive.c                               | 28 +++---
> >  tests/misc-tests/058-reverse-receive/test.sh | 98 ++++++++++++++++++++
> >  2 files changed, 115 insertions(+), 11 deletions(-)
> >  create mode 100755 tests/misc-tests/058-reverse-receive/test.sh
> > 
> > diff --git a/cmds/receive.c b/cmds/receive.c
> > index d16dc0a..763c2af 100644
> > --- a/cmds/receive.c
> > +++ b/cmds/receive.c
> > @@ -222,6 +222,19 @@ out:
> >         return ret;
> >  }
> >  
> > +static struct subvol_info *search_source_subvol(struct subvol_uuid_search *s,
> > +                       const u8 *subvol_uuid, u64 transid)
> > +{
> > +       struct subvol_info *found;
> > +       found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
> > +                       subvol_search_by_received_uuid);
> > +       if (IS_ERR_OR_NULL(found)) {
> > +               found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
> > +                               subvol_search_by_uuid);
> > +       }
> > +       return found;
> > +}
> > +
> >  static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
> >                             const u8 *parent_uuid, u64 parent_ctransid,
> >                             void *user)
> > @@ -284,14 +297,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
> >         memset(&args_v2, 0, sizeof(args_v2));
> >         strncpy_null(args_v2.name, path);
> >  
> > -       parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
> > -                                          parent_ctransid, NULL,
> > -                                          subvol_search_by_received_uuid);
> > -       if (IS_ERR_OR_NULL(parent_subvol)) {
> > -               parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
> > -                                                  parent_ctransid, NULL,
> > -                                                  subvol_search_by_uuid);
> > -       }
> > +       parent_subvol = search_source_subvol(rctx->mnt_fd, parent_uuid,
> > +                       parent_ctransid);
> >         if (IS_ERR_OR_NULL(parent_subvol)) {
> >                 if (!parent_subvol)
> >                         ret = -ENOENT;
> > @@ -746,9 +753,8 @@ static int process_clone(const char *path, u64 offset, u64 len,
> >                    BTRFS_UUID_SIZE) == 0) {
> >                 subvol_path = rctx->cur_subvol_path;
> >         } else {
> > -               si = subvol_uuid_search(rctx->mnt_fd, 0, clone_uuid, clone_ctransid,
> > -                                       NULL,
> > -                                       subvol_search_by_received_uuid);
> > +               si = search_source_subvol(rctx->mnt_fd, clone_uuid,
> > +                               clone_ctransid);
> >                 if (IS_ERR_OR_NULL(si)) {
> >                         char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
> >  
> > diff --git a/tests/misc-tests/058-reverse-receive/test.sh b/tests/misc-tests/058-reverse-receive/test.sh
> > new file mode 100755
> > index 0000000..6eff560
> > --- /dev/null
> > +++ b/tests/misc-tests/058-reverse-receive/test.sh
> > @@ -0,0 +1,98 @@
> > +#!/bin/bash
> > +#
> > +# Receive in reverse direction must not throw an error if it can find an earlier "sent" parent.
> > +# In general, shows a backup+sync setup between two (or more) PCs with an external drive.
> > +
> > +source "$TEST_TOP/common"
> > +
> > +check_prereq mkfs.btrfs
> > +check_prereq btrfs
> > +check_global_prereq dd
> > +
> > +declare -a roots
> > +i_pc1=1
> > +# An external drive used to backup and carry profile.
> > +i_ext=2
> > +i_pc2=3
> > +roots[$i_pc1]="$TEST_MNT/pc1"
> > +roots[$i_ext]="$TEST_MNT/external"
> > +roots[$i_pc2]="$TEST_MNT/pc2"
> > +
> > +setup_root_helper
> > +mkdir -p ${roots[@]}
> > +setup_loopdevs 3
> > +prepare_loopdevs
> > +for i in `seq 3`; do
> > +       TEST_DEV=${loopdevs[$i]}
> > +    TEST_MNT="${roots[$i]}"
> > +    run_check_mkfs_test_dev
> > +    run_check_mount_test_dev
> > +    run_check $SUDO_HELPER mkdir -p "$TEST_MNT/.snapshots"
> > +done
> > +
> > +run_check_update_file()
> > +{
> > +    run_check $SUDO_HELPER cp --reflink ${roots[$1]}/profile/$2 ${roots[$1]}/profile/staging
> > +    run_check $SUDO_HELPER dd if=/dev/urandom conv=notrunc bs=4K count=4 oseek=$3 "of=${roots[$1]}/profile/staging"
> > +    run_check $SUDO_HELPER mv ${roots[$1]}/profile/staging ${roots[$1]}/profile/$2
> > +}
> > +run_check_copy_snapshot_with_diff()
> > +{
> > +    _mktemp_local send.data
> > +    run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data -p "${roots[$1]}/.snapshots/$2"
> > "${roots[$1]}/.snapshots/$3"
> > +    run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$4]}/.snapshots"
> > +}
> > +run_check_backup_profile()
> > +{
> > +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$1]}/profile" "${roots[$1]}/.snapshots/$3"
> > +    run_check_copy_snapshot_with_diff $1 $2 $3 $i_ext
> > +    # Don't keep old snapshot in pc
> > +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
> > +}
> > +run_check_restore_profile()
> > +{
> > +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot "${roots[$1]}/.snapshots/$2" "${roots[$1]}/profile"
> > +}
> > +run_check_copy_fresh_backup_and_replace_profile()
> > +{
> > +    run_check_copy_snapshot_with_diff $i_ext $2 $3 $1
> > +    # IRL, it would be a nice idea to make a backup snapshot before deleting.
> > +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/profile"
> > +    run_check_restore_profile $1 $3
> > +    # Don't keep old snapshot in pc
> > +    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
> > +}
> > +
> > +
> > +run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "${roots[$i_pc1]}/profile"
> > +run_check $SUDO_HELPER dd if=/dev/urandom bs=4K count=16 "of=${roots[$i_pc1]}/profile/day1"
> > +run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$i_pc1]}/profile"
> > "${roots[$i_pc1]}/.snapshots/day1"
> > +_mktemp_local send.data
> > +run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_pc1]}/.snapshots/day1"
> > +run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_ext]}/.snapshots"
> > +
> > +run_check_update_file $i_pc1 day1 2
> > +run_check_backup_profile $i_pc1 day1 day2
> > +
> > +_mktemp_local send.data
> > +run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_ext]}/.snapshots/day2"
> > +run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_pc2]}/.snapshots"
> > +run_check_restore_profile $i_pc2 day2
> > +run_check_update_file $i_pc2 day1 3
> > +run_check_backup_profile $i_pc2 day2 day3
> > +
> > +run_check_update_file $i_pc2 day1 4
> > +run_check_backup_profile $i_pc2 day3 day4
> > +
> > +run_check_copy_fresh_backup_and_replace_profile $i_pc1 day2 day4
> > +run_check_update_file $i_pc1 day1 5
> > +run_check_backup_profile $i_pc1 day4 day5
> > +
> > +run_check_copy_fresh_backup_and_replace_profile $i_pc2 day4 day5
> > +run_check_update_file $i_pc2 day1 6
> > +run_check_backup_profile $i_pc2 day5 day6
> > +
> > +run_check_umount_test_dev ${loopdevs[@]}
> > +rmdir ${roots[@]}
> > +rm -f send.data
> > +cleanup_loopdevs
> > -- 
> > 2.41.0
> 
>
Joan Bruguera Micó Nov. 20, 2023, 9:11 p.m. UTC | #3
The first parameter of `search_source_subvol` should be changed from a
`struct subvol_uuid_search *` to an `int`, which I suggest naming `mnt_fd`,
otherwise this generates some integer <->pointer conversion warnings.

(Note that there are two different declarations of `subvol_uuid_search` in
 btrfs-progs, which probably caused the confusion with the typings).
diff mbox series

Patch

diff --git a/cmds/receive.c b/cmds/receive.c
index d16dc0a..763c2af 100644
--- a/cmds/receive.c
+++ b/cmds/receive.c
@@ -222,6 +222,19 @@  out:
 	return ret;
 }
 
+static struct subvol_info *search_source_subvol(struct subvol_uuid_search *s,
+			const u8 *subvol_uuid, u64 transid)
+{
+	struct subvol_info *found;
+	found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
+			subvol_search_by_received_uuid);
+	if (IS_ERR_OR_NULL(found)) {
+		found = subvol_uuid_search(s, 0, subvol_uuid, transid, NULL,
+				subvol_search_by_uuid);
+	}
+	return found;
+}
+
 static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 			    const u8 *parent_uuid, u64 parent_ctransid,
 			    void *user)
@@ -284,14 +297,8 @@  static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
 	memset(&args_v2, 0, sizeof(args_v2));
 	strncpy_null(args_v2.name, path);
 
-	parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
-					   parent_ctransid, NULL,
-					   subvol_search_by_received_uuid);
-	if (IS_ERR_OR_NULL(parent_subvol)) {
-		parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
-						   parent_ctransid, NULL,
-						   subvol_search_by_uuid);
-	}
+	parent_subvol = search_source_subvol(rctx->mnt_fd, parent_uuid,
+			parent_ctransid);
 	if (IS_ERR_OR_NULL(parent_subvol)) {
 		if (!parent_subvol)
 			ret = -ENOENT;
@@ -746,9 +753,8 @@  static int process_clone(const char *path, u64 offset, u64 len,
 		   BTRFS_UUID_SIZE) == 0) {
 		subvol_path = rctx->cur_subvol_path;
 	} else {
-		si = subvol_uuid_search(rctx->mnt_fd, 0, clone_uuid, clone_ctransid,
-					NULL,
-					subvol_search_by_received_uuid);
+		si = search_source_subvol(rctx->mnt_fd, clone_uuid,
+				clone_ctransid);
 		if (IS_ERR_OR_NULL(si)) {
 			char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
 
diff --git a/tests/misc-tests/058-reverse-receive/test.sh b/tests/misc-tests/058-reverse-receive/test.sh
new file mode 100755
index 0000000..6eff560
--- /dev/null
+++ b/tests/misc-tests/058-reverse-receive/test.sh
@@ -0,0 +1,98 @@ 
+#!/bin/bash
+#
+# Receive in reverse direction must not throw an error if it can find an earlier "sent" parent.
+# In general, shows a backup+sync setup between two (or more) PCs with an external drive.
+
+source "$TEST_TOP/common"
+
+check_prereq mkfs.btrfs
+check_prereq btrfs
+check_global_prereq dd
+
+declare -a roots
+i_pc1=1
+# An external drive used to backup and carry profile.
+i_ext=2
+i_pc2=3
+roots[$i_pc1]="$TEST_MNT/pc1"
+roots[$i_ext]="$TEST_MNT/external"
+roots[$i_pc2]="$TEST_MNT/pc2"
+
+setup_root_helper
+mkdir -p ${roots[@]}
+setup_loopdevs 3
+prepare_loopdevs
+for i in `seq 3`; do
+	TEST_DEV=${loopdevs[$i]}
+    TEST_MNT="${roots[$i]}"
+    run_check_mkfs_test_dev
+    run_check_mount_test_dev
+    run_check $SUDO_HELPER mkdir -p "$TEST_MNT/.snapshots"
+done
+
+run_check_update_file()
+{
+    run_check $SUDO_HELPER cp --reflink ${roots[$1]}/profile/$2 ${roots[$1]}/profile/staging
+    run_check $SUDO_HELPER dd if=/dev/urandom conv=notrunc bs=4K count=4 oseek=$3 "of=${roots[$1]}/profile/staging"
+    run_check $SUDO_HELPER mv ${roots[$1]}/profile/staging ${roots[$1]}/profile/$2
+}
+run_check_copy_snapshot_with_diff()
+{
+    _mktemp_local send.data
+    run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data -p "${roots[$1]}/.snapshots/$2" "${roots[$1]}/.snapshots/$3"
+    run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$4]}/.snapshots"
+}
+run_check_backup_profile()
+{
+    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$1]}/profile" "${roots[$1]}/.snapshots/$3"
+    run_check_copy_snapshot_with_diff $1 $2 $3 $i_ext
+    # Don't keep old snapshot in pc
+    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
+}
+run_check_restore_profile()
+{
+    run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot "${roots[$1]}/.snapshots/$2" "${roots[$1]}/profile"
+}
+run_check_copy_fresh_backup_and_replace_profile()
+{
+    run_check_copy_snapshot_with_diff $i_ext $2 $3 $1
+    # IRL, it would be a nice idea to make a backup snapshot before deleting.
+    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/profile"
+    run_check_restore_profile $1 $3
+    # Don't keep old snapshot in pc
+    run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
+}
+
+
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "${roots[$i_pc1]}/profile"
+run_check $SUDO_HELPER dd if=/dev/urandom bs=4K count=16 "of=${roots[$i_pc1]}/profile/day1"
+run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$i_pc1]}/profile" "${roots[$i_pc1]}/.snapshots/day1"
+_mktemp_local send.data
+run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_pc1]}/.snapshots/day1"
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_ext]}/.snapshots"
+
+run_check_update_file $i_pc1 day1 2
+run_check_backup_profile $i_pc1 day1 day2
+
+_mktemp_local send.data
+run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_ext]}/.snapshots/day2"
+run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_pc2]}/.snapshots"
+run_check_restore_profile $i_pc2 day2
+run_check_update_file $i_pc2 day1 3
+run_check_backup_profile $i_pc2 day2 day3
+
+run_check_update_file $i_pc2 day1 4
+run_check_backup_profile $i_pc2 day3 day4
+
+run_check_copy_fresh_backup_and_replace_profile $i_pc1 day2 day4
+run_check_update_file $i_pc1 day1 5
+run_check_backup_profile $i_pc1 day4 day5
+
+run_check_copy_fresh_backup_and_replace_profile $i_pc2 day4 day5
+run_check_update_file $i_pc2 day1 6
+run_check_backup_profile $i_pc2 day5 day6
+
+run_check_umount_test_dev ${loopdevs[@]}
+rmdir ${roots[@]}
+rm -f send.data
+cleanup_loopdevs