Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp1831597imm; Mon, 3 Sep 2018 10:33:45 -0700 (PDT) X-Google-Smtp-Source: ANB0VdaJujBoga7Neb3CJc+8QbQ2bTEyhHxFFnIujWBXvmk3FoLYs8Ta86lJtqGFX8xbH3Mc+NHh X-Received: by 2002:a62:174a:: with SMTP id 71-v6mr30974881pfx.217.1535996025687; Mon, 03 Sep 2018 10:33:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1535996025; cv=none; d=google.com; s=arc-20160816; b=cUtICjbaaq19zLHW/lC1XpyR8yAdGZVWejrEKkYo0g4/sBclM0EJzkH5ObVA5lt2Mn Tpg8YH1TENUlSHBYPhL1qBkMSAMReUuKJLTwwiPFSOfUHGDddtNZZJ+wjQbFDsk8NSDE WrZTWCknF1BXvsHUkO3F7Z0mc/xKu/Y2S2Je9DaIvLcmUpD5b7e8XwOEFR+RX4R2EPuq 4TomW4oFPYFSU7ovab18eA6shdSVfeay757K6EHmHOqL6xFeywbLcTErsXancxqU5CU3 BVrNXL4WZavAEUUfC0VpwkexdD1iq6Wvsyr6oIAgyaNpj5bngsw6F1iTGGAd2AEFbAOB UN4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:user-agent:references :in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=0hwVyMnx5ScWUwnjBRvTnbi9rvb3rddudhPBCNdngh8=; b=X3JphCWL5HFZN+POOvrioL4A8IU8yGjCMMH1i6A0ZJlmknR0680T4I3Z54cpeuB9ZY 6KybR8PVp7ywQ9gLIglAtdAA7wBH7BkGE3KHJwMXOseWNAjlK+WgqItSc/wza8OgyWgW sWGWLEKkfMLvD1ynXMp/rhoknUBFZexeUb2eP0xEt9ykOFY5uEZ7qH+EVCts1s4mBrRj pKSWkNQP6LbkjDJDZ1rRrPE/OObn2k8RYF+cFL6kQjcR/Yl488CYpO8XCQlRCSHL7hHs wFCKB4Fru+AYlSt0ejG8yQda9sjVsLXEE6CVxCTeXzuGLWrPQDKD8vWqkC5u1/XCyZMr kDdQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o81-v6si19561034pfj.350.2018.09.03.10.33.30; Mon, 03 Sep 2018 10:33:45 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731073AbeICVxF (ORCPT + 99 others); Mon, 3 Sep 2018 17:53:05 -0400 Received: from mail.linuxfoundation.org ([140.211.169.12]:47274 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728303AbeICVxE (ORCPT ); Mon, 3 Sep 2018 17:53:04 -0400 Received: from localhost (ip-213-127-74-90.ip.prioritytelecom.net [213.127.74.90]) by mail.linuxfoundation.org (Postfix) with ESMTPSA id C0E0AD18; Mon, 3 Sep 2018 17:31:54 +0000 (UTC) From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Filipe Manana , David Sterba Subject: [PATCH 4.18 017/123] Btrfs: fix mount failure after fsync due to hard link recreation Date: Mon, 3 Sep 2018 18:56:01 +0200 Message-Id: <20180903165720.219244018@linuxfoundation.org> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180903165719.499675257@linuxfoundation.org> References: <20180903165719.499675257@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Filipe Manana commit 0d836392cadd5535f4184d46d901a82eb276ed62 upstream. If we end up with logging an inode reference item which has the same name but different index from the one we have persisted, we end up failing when replaying the log with an errno value of -EEXIST. The error comes from btrfs_add_link(), which is called from add_inode_ref(), when we are replaying an inode reference item. Example scenario where this happens: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ touch /mnt/foo $ ln /mnt/foo /mnt/bar $ sync # Rename the first hard link (foo) to a new name and rename the second # hard link (bar) to the old name of the first hard link (foo). $ mv /mnt/foo /mnt/qwerty $ mv /mnt/bar /mnt/foo # Create a new file, in the same parent directory, with the old name of # the second hard link (bar) and fsync this new file. # We do this instead of calling fsync on foo/qwerty because if we did # that the fsync resulted in a full transaction commit, not triggering # the problem. $ touch /mnt/bar $ xfs_io -c "fsync" /mnt/bar $ mount /dev/sdb /mnt mount: mount /dev/sdb on /mnt failed: File exists So fix this by checking if a conflicting inode reference exists (same name, same parent but different index), removing it (and the associated dir index entries from the parent inode) if it exists, before attempting to add the new reference. A test case for fstests follows soon. CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/tree-log.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -1291,6 +1291,46 @@ again: return ret; } +static int btrfs_inode_ref_exists(struct inode *inode, struct inode *dir, + const u8 ref_type, const char *name, + const int namelen) +{ + struct btrfs_key key; + struct btrfs_path *path; + const u64 parent_id = btrfs_ino(BTRFS_I(dir)); + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + key.objectid = btrfs_ino(BTRFS_I(inode)); + key.type = ref_type; + if (key.type == BTRFS_INODE_REF_KEY) + key.offset = parent_id; + else + key.offset = btrfs_extref_hash(parent_id, name, namelen); + + ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &key, path, 0, 0); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + if (key.type == BTRFS_INODE_EXTREF_KEY) + ret = btrfs_find_name_in_ext_backref(path->nodes[0], + path->slots[0], parent_id, + name, namelen, NULL); + else + ret = btrfs_find_name_in_backref(path->nodes[0], path->slots[0], + name, namelen, NULL); + +out: + btrfs_free_path(path); + return ret; +} + /* * replay one inode back reference item found in the log tree. * eb, slot and key refer to the buffer and key found in the log tree. @@ -1400,6 +1440,32 @@ static noinline int add_inode_ref(struct } } + /* + * If a reference item already exists for this inode + * with the same parent and name, but different index, + * drop it and the corresponding directory index entries + * from the parent before adding the new reference item + * and dir index entries, otherwise we would fail with + * -EEXIST returned from btrfs_add_link() below. + */ + ret = btrfs_inode_ref_exists(inode, dir, key->type, + name, namelen); + if (ret > 0) { + ret = btrfs_unlink_inode(trans, root, + BTRFS_I(dir), + BTRFS_I(inode), + name, namelen); + /* + * If we dropped the link count to 0, bump it so + * that later the iput() on the inode will not + * free it. We will fixup the link count later. + */ + if (!ret && inode->i_nlink == 0) + inc_nlink(inode); + } + if (ret < 0) + goto out; + /* insert our name */ ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode),