From patchwork Tue Oct 15 15:42:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Borisov X-Patchwork-Id: 11190733 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B7A8C912 for ; Tue, 15 Oct 2019 15:42:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A1628205F4 for ; Tue, 15 Oct 2019 15:42:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731394AbfJOPmy (ORCPT ); Tue, 15 Oct 2019 11:42:54 -0400 Received: from mx2.suse.de ([195.135.220.15]:44486 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1731230AbfJOPmy (ORCPT ); Tue, 15 Oct 2019 11:42:54 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 41AC6B4C5; Tue, 15 Oct 2019 15:42:52 +0000 (UTC) From: Nikolay Borisov To: linux-btrfs@vger.kernel.org Cc: Nikolay Borisov Subject: [PATCH 1/2] btrfs-progs: corrupt-block: Refactor tree block corruption code Date: Tue, 15 Oct 2019 18:42:48 +0300 Message-Id: <20191015154249.21615-2-nborisov@suse.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191015154249.21615-1-nborisov@suse.com> References: <20191015154249.21615-1-nborisov@suse.com> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org As progs' transaction/CoW logic evolved over the years the metadata block corruption code failed to do so. It's currently impossible to corrupt the generation because the CoW logic will not only set it to the value of the currently running transaction (__btrfs_cow_block) but the current code will ASSERT due to the following check in __btrfs_cow_block: WARN_ON(!(buf->flags & EXTENT_BAD_TRANSID) && btrfs_header_generation(buf) > trans->transid); Fix this by making the generation corruption code directly write the modified block, outside of the transaction mechanism. At the same time move the old code into BTRFS_METADATA_BLOCK_SHIFT_ITEMS handling case, essentially leaving it unchanged. Signed-off-by: Nikolay Borisov --- btrfs-corrupt-block.c | 108 ++++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/btrfs-corrupt-block.c b/btrfs-corrupt-block.c index 716439a5ea7c..dc5b81ffca3a 100644 --- a/btrfs-corrupt-block.c +++ b/btrfs-corrupt-block.c @@ -746,15 +746,8 @@ static void shift_items(struct btrfs_root *root, struct extent_buffer *eb) static int corrupt_metadata_block(struct btrfs_fs_info *fs_info, u64 block, char *field) { - struct btrfs_trans_handle *trans; - struct btrfs_root *root; - struct btrfs_path *path; struct extent_buffer *eb; - struct btrfs_key key, root_key; enum btrfs_metadata_block_field corrupt_field; - u64 root_objectid; - u64 orig, bogus; - u8 level; int ret; corrupt_field = convert_metadata_block_field(field); @@ -768,63 +761,76 @@ static int corrupt_metadata_block(struct btrfs_fs_info *fs_info, u64 block, fprintf(stderr, "Couldn't read in tree block %s\n", field); return -EINVAL; } - root_objectid = btrfs_header_owner(eb); - level = btrfs_header_level(eb); - if (level) - btrfs_node_key_to_cpu(eb, &key, 0); - else - btrfs_item_key_to_cpu(eb, &key, 0); - free_extent_buffer(eb); - - root_key.objectid = root_objectid; - root_key.type = BTRFS_ROOT_ITEM_KEY; - root_key.offset = (u64)-1; - - root = btrfs_read_fs_root(fs_info, &root_key); - if (IS_ERR(root)) { - fprintf(stderr, "Couldn't find owner root %llu\n", - key.objectid); - return PTR_ERR(root); - } - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - btrfs_free_path(path); - fprintf(stderr, "Couldn't start transaction %ld\n", - PTR_ERR(trans)); - return PTR_ERR(trans); - } - - path->lowest_level = level; - ret = btrfs_search_slot(trans, root, &key, path, 0, 1); - if (ret < 0) { - fprintf(stderr, "Error searching to node %d\n", ret); - goto out; - } - eb = path->nodes[level]; ret = 0; switch (corrupt_field) { case BTRFS_METADATA_BLOCK_GENERATION: - orig = btrfs_header_generation(eb); - bogus = generate_u64(orig); + { + u64 orig = btrfs_header_generation(eb); + u64 bogus = generate_u64(orig); btrfs_set_header_generation(eb, bogus); + write_and_map_eb(fs_info, eb); + free_extent_buffer(eb); break; + } case BTRFS_METADATA_BLOCK_SHIFT_ITEMS: + { + struct btrfs_trans_handle *trans; + struct btrfs_root *root; + struct btrfs_path *path; + struct btrfs_key key, root_key; + u64 root_objectid; + u8 level; + root_objectid = btrfs_header_owner(eb); + level = btrfs_header_level(eb); + if (level) + btrfs_node_key_to_cpu(eb, &key, 0); + else + btrfs_item_key_to_cpu(eb, &key, 0); + free_extent_buffer(eb); + + root_key.objectid = root_objectid; + root_key.type = BTRFS_ROOT_ITEM_KEY; + root_key.offset = (u64)-1; + + root = btrfs_read_fs_root(fs_info, &root_key); + if (IS_ERR(root)) { + fprintf(stderr, "Couldn't find owner root %llu\n", + key.objectid); + return PTR_ERR(root); + } + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + btrfs_free_path(path); + fprintf(stderr, "Couldn't start transaction %ld\n", + PTR_ERR(trans)); + return PTR_ERR(trans); + } + + path->lowest_level = level; + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret < 0) { + fprintf(stderr, "Error searching to node %d\n", ret); + btrfs_free_path(path); + btrfs_abort_transaction(trans, ret); + return ret; + } + eb = path->nodes[level]; shift_items(root, path->nodes[level]); + btrfs_mark_buffer_dirty(path->nodes[level]); + btrfs_commit_transaction(trans, root); break; + } default: ret = -EINVAL; break; } - btrfs_mark_buffer_dirty(path->nodes[level]); -out: - btrfs_commit_transaction(trans, root); - btrfs_free_path(path); + return ret; } From patchwork Tue Oct 15 15:42:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikolay Borisov X-Patchwork-Id: 11190735 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F218E15AB for ; Tue, 15 Oct 2019 15:43:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DBD342086A for ; Tue, 15 Oct 2019 15:43:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731528AbfJOPnA (ORCPT ); Tue, 15 Oct 2019 11:43:00 -0400 Received: from mx2.suse.de ([195.135.220.15]:44504 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1731230AbfJOPnA (ORCPT ); Tue, 15 Oct 2019 11:43:00 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 9EF7BB551; Tue, 15 Oct 2019 15:42:52 +0000 (UTC) From: Nikolay Borisov To: linux-btrfs@vger.kernel.org Cc: Nikolay Borisov Subject: [PATCH 2/2] btrfs-progs: tests: Test backup root retention logic Date: Tue, 15 Oct 2019 18:42:49 +0300 Message-Id: <20191015154249.21615-3-nborisov@suse.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191015154249.21615-1-nborisov@suse.com> References: <20191015154249.21615-1-nborisov@suse.com> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org This tests ensures that the kernel correctly persists backup roots in case the filesystem has been mounted from a backup root. Signed-off-by: Nikolay Borisov --- .../misc-tests/038-backup-root-corruption/test.sh | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100755 tests/misc-tests/038-backup-root-corruption/test.sh diff --git a/tests/misc-tests/038-backup-root-corruption/test.sh b/tests/misc-tests/038-backup-root-corruption/test.sh new file mode 100755 index 000000000000..2fb117b3a928 --- /dev/null +++ b/tests/misc-tests/038-backup-root-corruption/test.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Test that a corrupted filesystem will correctly handle writing of +# backup root + +source "$TEST_TOP/common" + +check_prereq mkfs.btrfs +check_prereq btrfs +check_prereq btrfs-corrupt-block + +setup_loopdevs 1 +prepare_loopdevs +dev=${loopdevs[1]} + +run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f "$dev" + +# Create a file and unmount to commit some backup roots +run_check $SUDO_HELPER mount "$dev" "$TEST_MNT" +run_check touch "$TEST_MNT/file" && sync +run_check $SUDO_HELPER umount "$TEST_MNT" + +# Ensure currently active backup slot is the expected one (slot 3) +backup2_root_ptr=$($SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super -f "$dev" \ + | grep -A1 "backup 2" | grep backup_tree_root | awk '{print $2}') + +main_root_ptr=$($SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super -f "$dev" \ + | grep root | head -n1 | awk '{print $2}') + +[[ "$backup2_root_ptr" -eq "$main_root_ptr" ]] || _fail "Backup slot 2 is not in use" + +run_check "$TOP/btrfs-corrupt-block" -m $main_root_ptr -f generation "$dev" + +## should fail because the root is corrupted +run_mustfail "Unexpected successful mount" $SUDO_HELPER mount "$dev" "$TEST_MNT" + +# Cycle mount with the backup to force rewrite of slot 3 +run_check $SUDO_HELPER mount -ousebackuproot "$dev" "$TEST_MNT" +run_check $SUDO_HELPER umount "$TEST_MNT" + + +# Since we've used backup 1 as the usable root, then backup 2 should have been +# overwritten +main_root_ptr=$($SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super -f "$dev" \ + | grep root | head -n1 | awk '{print $2}') +backup2_new_root_ptr=$($SUDO_HELPER "$TOP/btrfs" inspect-internal dump-super -f "$dev" \ + | grep -A1 "backup 2" | grep backup_tree_root | awk '{print $2}') + +[[ "$backup2_root_ptr" -ne "$backup2_new_root_ptr" ]] || _fail "Backup 2 not overwritten" + +cleanup_loopdevs