From patchwork Sat Aug 26 14:33:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arsenii Skvortsov X-Patchwork-Id: 13366602 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E732AC83F11 for ; Sat, 26 Aug 2023 14:34:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229548AbjHZOda (ORCPT ); Sat, 26 Aug 2023 10:33:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50274 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230281AbjHZOd2 (ORCPT ); Sat, 26 Aug 2023 10:33:28 -0400 Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CE71D2109 for ; Sat, 26 Aug 2023 07:33:24 -0700 (PDT) Received: by mail-qk1-x736.google.com with SMTP id af79cd13be357-76de9c23e5cso125978685a.3 for ; Sat, 26 Aug 2023 07:33:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1693060403; x=1693665203; h=mime-version:user-agent:content-transfer-encoding:date:to:from :subject:message-id:from:to:cc:subject:date:message-id:reply-to; bh=PnRnphjHHwYmDVbbYGSjBUC/MIGuxwIO2Z+0Nnc8f4Y=; b=o0KPnm+mq5sFEa9p+dP43vBUmBYG5jMpSVyZbUHXQs4GZMWGz5bA3N+5+5cZZXz80h yuHn5slFjwWIqTDCrGkSpi93V0iH9ogDqwZlBeQFburWQnFXzPpO3xIX7fDOg9KFgSxF RbllQllInnzn8Y4sQ8xkvPcvW8eFYvoOmB1J10VaojBi0a4JChdo5cu4sGCx/+yfAeb0 BhQ8fqylSW7W/Bepa8KxEMLbqWWX/hioaRwCBlXPv0oTRbQ5xOuAramwxD2Zzhpecc3i yKnfN4fnRB0mOPAVqIrbSJkRmSU7PHkgEJ9ED88IkkCCgEyy5EBAwIFeIZGuFXFkG1li 3G9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1693060403; x=1693665203; h=mime-version:user-agent:content-transfer-encoding:date:to:from :subject:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=PnRnphjHHwYmDVbbYGSjBUC/MIGuxwIO2Z+0Nnc8f4Y=; b=fTeX6ZsL3OacAr+Zr3Cl0xQoecEW7UmtnBk2NdUtCXsZ5yL4mvBaXI5pKwoAiYbJFq LB5X/024xCuasT6DRECfinM3cwv2NctNGFGZHoKTTo6+kL2Yd6iJsN59W7mvx2THzwuv FgGyqEziu/8meQONCrs2aZOMYIcU+q1b896HCshwm7/an2kmh6YpuUR1GkIxjCNHy3q2 eS34kr3YTeJK0IFKZBoIm7//z5avcEKi18rvIY5pDMqHA62ffyq/rDYUhtNSFGbzc1FS r/NOUkE32oJ+E56H8ftetcpb9GlA00PXXEPBkNnooIx4XKGpWTsI/A8FifFU+vEVFhKH oDRw== X-Gm-Message-State: AOJu0YxrgPPAIBkAdwfgXegeRRzFH+ocOfc5yLjXJkvaKkXFQKJgBrs+ XY3e3/dzWKPKmZLiZBnOB7Ys5MidIKM= X-Google-Smtp-Source: AGHT+IF7ETJ9qmafSF+KcE3z4h5DP0NvIMlbjzzITOTkL41v7r9z3zNSMHCQaUnHpmyqI7J7UJd0Lw== X-Received: by 2002:a05:620a:4109:b0:76c:cd2e:de9c with SMTP id j9-20020a05620a410900b0076ccd2ede9cmr25396855qko.51.1693060403507; Sat, 26 Aug 2023 07:33:23 -0700 (PDT) Received: from [192.168.2.71] (bras-base-kntaon1614w-grc-44-70-49-32-245.dsl.bell.ca. [70.49.32.245]) by smtp.gmail.com with ESMTPSA id rg3-20020a05620a8ec300b0076c707f3bafsm1192742qkn.94.2023.08.26.07.33.22 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 26 Aug 2023 07:33:23 -0700 (PDT) Message-ID: <2fcd8ea94edb2a1d623525db80c67fcbca46aec8.camel@gmail.com> Subject: [PATCH] btrfs-progs: receive: cannot find clone source subvol when receiving in reverse direction From: Arsenii Skvortsov To: linux-btrfs@vger.kernel.org Date: Sat, 26 Aug 2023 10:33:22 -0400 User-Agent: Evolution 3.48.4 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org process_clone, unlike process_snapshot, only searched a subvolume matching by received uuid. However, when earlier "receiver" side sends, it mentions received uuid, which is for earlier "send" side (now "receiver" side) is just uuid. Fixes: https://github.com/kdave/btrfs-progs/issues/606 Signed-off-by: Arsenii Skvortsov ---  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..bdd4dee 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