From patchwork Tue Sep 24 08:11:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 11158249 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 823AB1599 for ; Tue, 24 Sep 2019 08:11:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 67E04214AF for ; Tue, 24 Sep 2019 08:11:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2394562AbfIXIL3 (ORCPT ); Tue, 24 Sep 2019 04:11:29 -0400 Received: from mx2.suse.de ([195.135.220.15]:38880 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2390364AbfIXIL3 (ORCPT ); Tue, 24 Sep 2019 04:11:29 -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 CEBEEB617; Tue, 24 Sep 2019 08:11:26 +0000 (UTC) From: Qu Wenruo To: linux-btrfs@vger.kernel.org Cc: Charles Wright Subject: [PATCH 1/3] btrfs-progs: check/lowmem: Add check and repair for invalid inode generation Date: Tue, 24 Sep 2019 16:11:18 +0800 Message-Id: <20190924081120.6283-2-wqu@suse.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190924081120.6283-1-wqu@suse.com> References: <20190924081120.6283-1-wqu@suse.com> MIME-Version: 1.0 Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org There are at least two bug reports of kernel tree-checker complaining about invalid inode generation. All offending inodes seem to be caused by old kernel around 2014, with inode generation overflow. So add such check and repair ability to lowmem mode check first. This involves: - Calculate the inode generation upper limit If it's an inode from log tree, then the upper limit is super_generation + 1, otherwise it's super_generation. - Check if the inode generation is larger than the upper limit - Repair by resetting inode generation to current transaction generation Reported-by: Charles Wright Signed-off-by: Qu Wenruo Tested-by: Nikolay Borisov Reviewed-by: Nikolay Borisov --- check/mode-lowmem.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c index 5f7f101d..7af29ba9 100644 --- a/check/mode-lowmem.c +++ b/check/mode-lowmem.c @@ -2472,6 +2472,59 @@ static bool has_orphan_item(struct btrfs_root *root, u64 ino) return false; } +static int repair_inode_gen_lowmem(struct btrfs_root *root, + struct btrfs_path *path) +{ + struct btrfs_trans_handle *trans; + struct btrfs_inode_item *ii; + struct btrfs_key key; + u64 transid; + int ret; + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + errno = -ret; + error("failed to start transaction for inode gen repair: %m"); + return ret; + } + transid = trans->transid; + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + ASSERT(key.type == BTRFS_INODE_ITEM_KEY); + + btrfs_release_path(path); + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret > 0) { + ret = -ENOENT; + error("no inode item found for ino %llu", key.objectid); + goto error; + } + if (ret < 0) { + errno = -ret; + error("failed to find inode item for ino %llu: %m", + key.objectid); + goto error; + } + ii = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + btrfs_set_inode_generation(path->nodes[0], ii, trans->transid); + btrfs_mark_buffer_dirty(path->nodes[0]); + ret = btrfs_commit_transaction(trans, root); + if (ret < 0) { + errno = -ret; + error("failed to commit transaction: %m"); + goto error; + } + printf("reseting inode generation to %llu for ino %llu\n", + transid, key.objectid); + return ret; + +error: + btrfs_abort_transaction(trans, ret); + return ret; +} + /* * Check INODE_ITEM and related ITEMs (the same inode number) * 1. check link count @@ -2487,6 +2540,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path) struct btrfs_inode_item *ii; struct btrfs_key key; struct btrfs_key last_key; + struct btrfs_super_block *super = root->fs_info->super_copy; u64 inode_id; u32 mode; u64 flags; @@ -2497,6 +2551,8 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path) u64 refs = 0; u64 extent_end = 0; u64 extent_size = 0; + u64 generation; + u64 gen_uplimit; unsigned int dir; unsigned int nodatasum; bool is_orphan = false; @@ -2527,6 +2583,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path) flags = btrfs_inode_flags(node, ii); dir = imode_to_type(mode) == BTRFS_FT_DIR; nlink = btrfs_inode_nlink(node, ii); + generation = btrfs_inode_generation(node, ii); nodatasum = btrfs_inode_flags(node, ii) & BTRFS_INODE_NODATASUM; if (!is_valid_imode(mode)) { @@ -2540,6 +2597,25 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path) } } + if (btrfs_super_log_root(super) != 0 && + root->objectid == BTRFS_TREE_LOG_OBJECTID) + gen_uplimit = btrfs_super_generation(super) + 1; + else + gen_uplimit = btrfs_super_generation(super); + + if (generation > gen_uplimit) { + error( + "invalid inode generation for ino %llu, have %llu expect [0, %llu)", + inode_id, generation, gen_uplimit); + if (repair) { + ret = repair_inode_gen_lowmem(root, path); + if (ret < 0) + err |= INVALID_GENERATION; + } else { + err |= INVALID_GENERATION; + } + + } if (S_ISLNK(mode) && flags & (BTRFS_INODE_IMMUTABLE | BTRFS_INODE_APPEND)) { err |= INODE_FLAGS_ERROR; From patchwork Tue Sep 24 08:11:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 11158251 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 E116E112B for ; Tue, 24 Sep 2019 08:11:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C5D54214AF for ; Tue, 24 Sep 2019 08:11:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2394728AbfIXILb (ORCPT ); Tue, 24 Sep 2019 04:11:31 -0400 Received: from mx2.suse.de ([195.135.220.15]:38894 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2392102AbfIXILa (ORCPT ); Tue, 24 Sep 2019 04:11:30 -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 954E5B618; Tue, 24 Sep 2019 08:11:28 +0000 (UTC) From: Qu Wenruo To: linux-btrfs@vger.kernel.org Cc: Charles Wright Subject: [PATCH 2/3] btrfs-progs: check/original: Add check and repair for invalid inode generation Date: Tue, 24 Sep 2019 16:11:19 +0800 Message-Id: <20190924081120.6283-3-wqu@suse.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190924081120.6283-1-wqu@suse.com> References: <20190924081120.6283-1-wqu@suse.com> MIME-Version: 1.0 Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org There are at least two bug reports of kernel tree-checker complaining about invalid inode generation. All offending inodes seem to be caused by old kernel around 2014, with inode generation overflow. So add such check and repair ability to lowmem mode check first. This involves: - Calculate the inode generation upper limit Unlike the original mode context, we don't have anyway to determine if this inode belongs to log tree. So we use super_generation + 1 as upper limit, just like what we did in kernel tree checker. - Check if the inode generation is larger than the upper limit - Repair by resetting inode generation to current transaction generation The difference is, in original mode, we have a common trans handle for all repair and reset path for each repair. Reported-by: Charles Wright Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Tested-by: Nikolay Borisov --- check/main.c | 50 ++++++++++++++++++++++++++++++++++++++++++- check/mode-original.h | 1 + 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/check/main.c b/check/main.c index c2d0f394..018707c8 100644 --- a/check/main.c +++ b/check/main.c @@ -604,6 +604,8 @@ static void print_inode_error(struct btrfs_root *root, struct inode_record *rec) if (errors & I_ERR_INVALID_IMODE) fprintf(stderr, ", invalid inode mode bit 0%o", rec->imode & ~07777); + if (errors & I_ERR_INVALID_GEN) + fprintf(stderr, ", invalid inode generation"); fprintf(stderr, "\n"); /* Print the holes if needed */ @@ -857,6 +859,7 @@ static int process_inode_item(struct extent_buffer *eb, { struct inode_record *rec; struct btrfs_inode_item *item; + u64 gen_uplimit; u64 flags; rec = active_node->current; @@ -879,6 +882,13 @@ static int process_inode_item(struct extent_buffer *eb, if (S_ISLNK(rec->imode) && flags & (BTRFS_INODE_IMMUTABLE | BTRFS_INODE_APPEND)) rec->errors |= I_ERR_ODD_INODE_FLAGS; + /* + * We don't have accurate root info to determine the correct + * inode generation uplimit, use super_generation + 1 anyway + */ + gen_uplimit = btrfs_super_generation(global_info->super_copy) + 1; + if (btrfs_inode_generation(eb, item) > gen_uplimit) + rec->errors |= I_ERR_INVALID_GEN; maybe_free_inode_rec(&active_node->inode_cache, rec); return 0; } @@ -2774,6 +2784,41 @@ static int repair_imode_original(struct btrfs_trans_handle *trans, return ret; } +static int repair_inode_gen_original(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + struct inode_record *rec) +{ + struct btrfs_inode_item *ii; + struct btrfs_key key; + int ret; + + key.objectid = rec->ino; + key.offset = 0; + key.type = BTRFS_INODE_ITEM_KEY; + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + if (ret > 0) { + ret = -ENOENT; + error("no inode item found for ino %llu", rec->ino); + return ret; + } + if (ret < 0) { + errno = -ret; + error("failed to search inode item for ino %llu: %m", rec->ino); + return ret; + } + ii = btrfs_item_ptr(path->nodes[0], path->slots[0], + struct btrfs_inode_item); + btrfs_set_inode_generation(path->nodes[0], ii, trans->transid); + btrfs_mark_buffer_dirty(path->nodes[0]); + btrfs_release_path(path); + printf("resetting inode generation to %llu for ino %llu\n", + trans->transid, rec->ino); + rec->errors &= ~I_ERR_INVALID_GEN; + return 0; +} + static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) { struct btrfs_trans_handle *trans; @@ -2794,7 +2839,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) I_ERR_FILE_NBYTES_WRONG | I_ERR_INLINE_RAM_BYTES_WRONG | I_ERR_MISMATCH_DIR_HASH | - I_ERR_UNALIGNED_EXTENT_REC))) + I_ERR_UNALIGNED_EXTENT_REC | + I_ERR_INVALID_GEN))) return rec->errors; /* @@ -2829,6 +2875,8 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec) ret = repair_unaligned_extent_recs(trans, root, &path, rec); if (!ret && rec->errors & I_ERR_INVALID_IMODE) ret = repair_imode_original(trans, root, &path, rec); + if (!ret && rec->errors & I_ERR_INVALID_GEN) + ret = repair_inode_gen_original(trans, root, &path, rec); btrfs_commit_transaction(trans, root); btrfs_release_path(&path); return ret; diff --git a/check/mode-original.h b/check/mode-original.h index 78d6bb30..b075a95c 100644 --- a/check/mode-original.h +++ b/check/mode-original.h @@ -185,6 +185,7 @@ struct unaligned_extent_rec_t { #define I_ERR_INLINE_RAM_BYTES_WRONG (1 << 17) #define I_ERR_MISMATCH_DIR_HASH (1 << 18) #define I_ERR_INVALID_IMODE (1 << 19) +#define I_ERR_INVALID_GEN (1 << 20) struct inode_record { struct list_head backrefs; From patchwork Tue Sep 24 08:11:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 11158253 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 6EF21912 for ; Tue, 24 Sep 2019 08:11:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 57E56214AF for ; Tue, 24 Sep 2019 08:11:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2409441AbfIXILc (ORCPT ); Tue, 24 Sep 2019 04:11:32 -0400 Received: from mx2.suse.de ([195.135.220.15]:38910 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S2390364AbfIXILb (ORCPT ); Tue, 24 Sep 2019 04:11:31 -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 A27C1B617 for ; Tue, 24 Sep 2019 08:11:29 +0000 (UTC) From: Qu Wenruo To: linux-btrfs@vger.kernel.org Subject: [PATCH 3/3] btrfs-progs: fsck-tests: Add test image for invalid inode generation repair Date: Tue, 24 Sep 2019 16:11:20 +0800 Message-Id: <20190924081120.6283-4-wqu@suse.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190924081120.6283-1-wqu@suse.com> References: <20190924081120.6283-1-wqu@suse.com> MIME-Version: 1.0 Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org The image contains one inode item with invalid generation. The image can be crafted by "btrfs-corrupt-block -i 257 -f generation". It should emulate the bad inode generation caused by older kernel around 2014. The image is repairable for both original and lowmem mode. Signed-off-by: Qu Wenruo --- .../043-bad-inode-generation/.lowmem_repairable | 0 .../bad_inode_geneartion.img.xz | Bin 0 -> 2012 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/fsck-tests/043-bad-inode-generation/.lowmem_repairable create mode 100644 tests/fsck-tests/043-bad-inode-generation/bad_inode_geneartion.img.xz GIT binary patch literal 2012 zcmV<22P63XH+ooF000E$*0e?f03iV!0000G&sfah3;ze3T>wRyj;C3^v%$$4d1r37 zhA1?^g@qU6X{4JoZYUD&m)J~cEzT$F8&JddXW->df79?XOzszXC^i=3jv5zdJ#gxs z9sQ15EP307fR>-UpptOL6$V1qyGH7H=dX29pcbb>H?Kn*GkWCd+> z4gz!Y>ckaFgrgJeuG6M51wqhmn{kbaDVJg3Ld?lgrBdfj3zsD?PK#AyrUSv9Tph@s zlWPn@3zTdm{~HMt@#SSDZQo9pza_F;Uft;~oZgvZAnors__Ju}&Qp3w_XQ)tsS2T| zq$FIL*2A$z^pWDZ*1|!M(O+5AX(EZJ$>dBH`%1hzGX?o=*9kma$y@8m;CS@I{;nWI z*j}3J7}iP+f7~@z9j`a%Vr;Y*M{DE&w$@uL;WPHEno3DACkV>>P=M{Ff3#!`V7d;rR>!$!8m zzr2L&o3Z*+cu zeHG)3b0=ql#0j_4)Gl@YHB@!`y9GEG1RvAk6KQ^YKcMMrE&8HfO=EEN1E(wLnK-l_6VQ zUEzt^?TcZ$wp0ul`k`yBMI3y4xfj}%wEPc%>_>?=JzO{|(p?yTYi(E~Ri(3d4PLj| z-q_V+l1HR|29?y>SZ#C;mEzg;X?sBSb`OI<^BrkUUO!z7;*qFFJEH3r(k z!AXLNe~a4|dsq;NVVL)uHr^WPWU_rXL-F}@@>i&G@F%st)hh9}CE+-jOR(h79xHk6 z^dF)hH#&Z+_Zm95ii!A=V@&q*g58Rx(*t2p*X(59-0l|_l8}Tc_=7*sX$P+QbM+cU zD9khoX3gK0U$`(inhSdoZ$DF>nS7fUp98+}-M;HFDb*iCz-L!tXG+#5T6AIEtKcQv zhvAhr@`FA8s`s@OM{J||gVAGFT&>`tX@G+Cg=Ei-+0E(NrO;(56yJzdmtUKI8O1Kf)!vjac5Mm>@t~X+|wv7i~&1yym3r0)M?og*@PZg_dS41n#{io?=L zciazGdOvd@Mjk}WI|g@>=qLkIwL0;W+Q`Jz%`|psoEMb<`|X2P4|$@YN5>%uq&X2^ zkyrwTpaXx3X0D2tvu9=WKrQiTKb>Q=rA#!_$5|X8@4P;=6i;P0gxgU}Ui371E42dz zn>c#uoIz?0HY94QQQ>8fu27(WWks^}FEXr`TwbDak}wd!J&V%r4bv4`30pmS+T9WT z=CP(|%4;_3elq$P8k7GWSdXE|_%te~?dW~>3V334JW;b6sM=7DecM0c4J54{58)H; z-uMbZ9#jCD;4(DYlCY|hSzb~klkcDdPB;a34yOjt&_BAjLZAMSiB5n_KF&COtxbes zEcszYE;#FVX-vDZ#vs(#R~@^8*^}Dym5@sYubU#`M6B)U4M!{M*{y(555${5Rbm4a z6JvM!4sOI~3}h^vXC`V4yqE^R&0-o@!R}HlW&R;T32p{en5w!+OZCEHJe^Fia)@}X zFP1X0u%e_f=pquM0wgP!<`;?+KaY6k(S!tV^G%Jl`BSY`lb3-&5!g^eQ6<>R-=RsPB-E z13E=;JDLif@t_INz&YKa!;Zw#THD=8~YwVLhT_7j2&I>Eq6 zaQA0+X>(drz2NQzo;VMNU>DoE_n5rZXC*Rc-adER=MD9Y1w21(wlG${buZ6hhdtQK utt-Hd+V!p60001@#^<1LEIQ)=0k;o;7ytk=B&NBs#Ao{g000001X)^iOycnX literal 0 HcmV?d00001 diff --git a/tests/fsck-tests/043-bad-inode-generation/.lowmem_repairable b/tests/fsck-tests/043-bad-inode-generation/.lowmem_repairable new file mode 100644 index 00000000..e69de29b diff --git a/tests/fsck-tests/043-bad-inode-generation/bad_inode_geneartion.img.xz b/tests/fsck-tests/043-bad-inode-generation/bad_inode_geneartion.img.xz new file mode 100644 index 0000000000000000000000000000000000000000..d7302fcbabc653885ae1fac438183d7a9908a2f1