Skip to content

Commit 1ebdb7d

Browse files
ettavoltkdave
authored andcommitted
btrfs-progs: receive: cannot find clone source subvol when receiving in reverse direction
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: #606 Issue: #606 Pull-request: #643 Signed-off-by: Arsenii Skvortsov <ettavolt@gmail.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent b525d12 commit 1ebdb7d

File tree

2 files changed

+122
-11
lines changed

2 files changed

+122
-11
lines changed

cmds/receive.c

+23-11
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,25 @@ static int process_subvol(const char *path, const u8 *uuid, u64 ctransid,
221221
return ret;
222222
}
223223

224+
/*
225+
* Search for the @subvol_uuid, try received_uuid and subvolume as a fallback
226+
* if it's not found.
227+
*/
228+
static struct subvol_info *search_source_subvol(int mnt_fd, const u8 *subvol_uuid,
229+
u64 transid)
230+
{
231+
struct subvol_info *found;
232+
233+
found = subvol_uuid_search(mnt_fd, 0, subvol_uuid, transid, NULL,
234+
subvol_search_by_received_uuid);
235+
if (IS_ERR_OR_NULL(found)) {
236+
found = subvol_uuid_search(mnt_fd, 0, subvol_uuid, transid,
237+
NULL, subvol_search_by_uuid);
238+
}
239+
240+
return found;
241+
}
242+
224243
static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
225244
const u8 *parent_uuid, u64 parent_ctransid,
226245
void *user)
@@ -283,14 +302,8 @@ static int process_snapshot(const char *path, const u8 *uuid, u64 ctransid,
283302
memset(&args_v2, 0, sizeof(args_v2));
284303
strncpy_null(args_v2.name, path);
285304

286-
parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
287-
parent_ctransid, NULL,
288-
subvol_search_by_received_uuid);
289-
if (IS_ERR_OR_NULL(parent_subvol)) {
290-
parent_subvol = subvol_uuid_search(rctx->mnt_fd, 0, parent_uuid,
291-
parent_ctransid, NULL,
292-
subvol_search_by_uuid);
293-
}
305+
parent_subvol = search_source_subvol(rctx->mnt_fd, parent_uuid,
306+
parent_ctransid);
294307
if (IS_ERR_OR_NULL(parent_subvol)) {
295308
if (!parent_subvol)
296309
ret = -ENOENT;
@@ -745,9 +758,8 @@ static int process_clone(const char *path, u64 offset, u64 len,
745758
BTRFS_UUID_SIZE) == 0) {
746759
subvol_path = rctx->cur_subvol_path;
747760
} else {
748-
si = subvol_uuid_search(rctx->mnt_fd, 0, clone_uuid, clone_ctransid,
749-
NULL,
750-
subvol_search_by_received_uuid);
761+
si = search_source_subvol(rctx->mnt_fd, clone_uuid,
762+
clone_ctransid);
751763
if (IS_ERR_OR_NULL(si)) {
752764
char uuid_str[BTRFS_UUID_UNPARSED_SIZE];
753765

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/bin/bash
2+
#
3+
# Receive in reverse direction must not throw an error if it can find an
4+
# earlier "sent" parent. In general, shows a backup+sync setup between two (or
5+
# more) PCs with an external drive.
6+
7+
source "$TEST_TOP/common"
8+
9+
check_prereq mkfs.btrfs
10+
check_prereq btrfs
11+
check_global_prereq dd
12+
13+
declare -a roots
14+
i_pc1=1
15+
# An external drive used to backup and carry profile.
16+
i_ext=2
17+
i_pc2=3
18+
roots[$i_pc1]="$TEST_MNT/pc1"
19+
roots[$i_ext]="$TEST_MNT/external"
20+
roots[$i_pc2]="$TEST_MNT/pc2"
21+
22+
setup_root_helper
23+
mkdir -p "${roots[@]}"
24+
setup_loopdevs 3
25+
prepare_loopdevs
26+
for i in `seq 3`; do
27+
TEST_DEV="${loopdevs[$i]}"
28+
TEST_MNT="${roots[$i]}"
29+
run_check_mkfs_test_dev
30+
run_check_mount_test_dev
31+
run_check $SUDO_HELPER mkdir -p "$TEST_MNT/.snapshots"
32+
done
33+
34+
run_check_update_file()
35+
{
36+
run_check $SUDO_HELPER cp --reflink "${roots[$1]}/profile/$2" "${roots[$1]}/profile/staging"
37+
run_check $SUDO_HELPER dd if=/dev/urandom conv=notrunc bs=4K count=4 seek=$3 "of=${roots[$1]}/profile/staging"
38+
run_check $SUDO_HELPER mv "${roots[$1]}/profile/staging" "${roots[$1]}/profile/$2"
39+
}
40+
run_check_copy_snapshot_with_diff()
41+
{
42+
_mktemp_local send.data
43+
run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data -p "${roots[$1]}/.snapshots/$2" "${roots[$1]}/.snapshots/$3"
44+
run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$4]}/.snapshots"
45+
}
46+
run_check_backup_profile()
47+
{
48+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$1]}/profile" "${roots[$1]}/.snapshots/$3"
49+
run_check_copy_snapshot_with_diff "$1" "$2" "$3" "$i_ext"
50+
# Don't keep old snapshot in pc
51+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
52+
}
53+
run_check_restore_profile()
54+
{
55+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot "${roots[$1]}/.snapshots/$2" "${roots[$1]}/profile"
56+
}
57+
run_check_copy_fresh_backup_and_replace_profile()
58+
{
59+
run_check_copy_snapshot_with_diff "$i_ext" "$2" "$3" "$1"
60+
# IRL, it would be a nice idea to make a backup snapshot before deleting.
61+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/profile"
62+
run_check_restore_profile "$1" "$3"
63+
# Don't keep old snapshot in pc
64+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume delete "${roots[$1]}/.snapshots/$2"
65+
}
66+
67+
68+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume create "${roots[$i_pc1]}/profile"
69+
run_check $SUDO_HELPER dd if=/dev/urandom bs=4K count=16 "of=${roots[$i_pc1]}/profile/day1"
70+
run_check $SUDO_HELPER "$TOP/btrfs" subvolume snapshot -r "${roots[$i_pc1]}/profile" "${roots[$i_pc1]}/.snapshots/day1"
71+
_mktemp_local send.data
72+
run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_pc1]}/.snapshots/day1"
73+
run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_ext]}/.snapshots"
74+
75+
run_check_update_file "$i_pc1" day1 2
76+
run_check_backup_profile "$i_pc1" day1 day2
77+
78+
_mktemp_local send.data
79+
run_check $SUDO_HELPER "$TOP/btrfs" send -f send.data "${roots[$i_ext]}/.snapshots/day2"
80+
run_check $SUDO_HELPER "$TOP/btrfs" receive -f send.data "${roots[$i_pc2]}/.snapshots"
81+
run_check_restore_profile "$i_pc2" day2
82+
run_check_update_file "$i_pc2" day1 3
83+
run_check_backup_profile "$i_pc2" day2 day3
84+
85+
run_check_update_file "$i_pc2" day1 4
86+
run_check_backup_profile "$i_pc2" day3 day4
87+
88+
run_check_copy_fresh_backup_and_replace_profile "$i_pc1" day2 day4
89+
run_check_update_file "$i_pc1" day1 5
90+
run_check_backup_profile "$i_pc1" day4 day5
91+
92+
run_check_copy_fresh_backup_and_replace_profile "$i_pc2" day4 day5
93+
run_check_update_file "$i_pc2" day1 6
94+
run_check_backup_profile "$i_pc2" day5 day6
95+
96+
run_check_umount_test_dev "${loopdevs[@]}"
97+
rmdir "${roots[@]}"
98+
rm -f send.data
99+
cleanup_loopdevs

0 commit comments

Comments
 (0)