Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp245546pxf; Wed, 24 Mar 2021 04:15:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwpBmzYuBtgu28JePtMc7y7rDDy2BvZgvJLtIXRgLTIUoUTr6kZSfXDdVPV7jYHxRMK/A9A X-Received: by 2002:a17:906:b846:: with SMTP id ga6mr3043085ejb.542.1616584521475; Wed, 24 Mar 2021 04:15:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1616584521; cv=none; d=google.com; s=arc-20160816; b=jpWpH5aQq/qc5o+BlCXalBVf2f0VTP23k1809vLBJhEUARqBXdXQbE+mi7yja+F7EW Mn8xGo32Nx0rAY5bT85EY6i9NvmxZQewtZhvq99FwBmnaRyyT12WSNAmFregUiCFW69G ppENAU1sGNaJPk/gvSZzCfhgAT2PT6SgraUhVsZovOj334czdFnvBWRTH39SXQvM9jqs zv38SoQZUMBVSGYixtFxxWB9HLUrhtxOZwH12juoBpoiV5UKbTMTlQIzfq5kWoGu44Xj aub6vjG7LpWLxzKsN4NyR2XrkloMM4z9nN5XUa5oTApc8PIc4cJr89Z97dZEJGO6qky+ HLNA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:message-id:date:subject:cc:to:from :dkim-signature; bh=zHZ27UNjC+j9Tu9MAozuFxZYQ2JDauhWQX3TNi2K4f4=; b=ZhODLvDpxUy1WN6zMNV5jf9ldve4x2Zpd2dJjk1/PQph9cWc5EyxFHMaBiN8CzN5t9 I3GLfm0xur1k6WVScGjwq06mtO3/HFk4eF0rzuHW9gJmMotHeBmvgpUyF31ij9VoZH2d Uy4ikvK+jbmn2kmn2mryxMpTWHQoF0RRMgNq2/IHug0fNKBbzgtu1q0iq3IMg87R7jTA t7UlVmS3N1ggYYSkZVRPROPcjL95fhHRqtLWEs+loDMyyD9XZjbxnVJ5qV1Baj2UfaZt c0rzBPpd9AF6A4PBwJidbi/ZyoNmZI60q1ERl/v8lhcBS0iu2pG4GezyZre2fVN5MHPZ +iAA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@synology.com header.s=123 header.b=eBNVQZAp; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=synology.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id f18si1520021eja.587.2021.03.24.04.14.58; Wed, 24 Mar 2021 04:15:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@synology.com header.s=123 header.b=eBNVQZAp; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=synology.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235000AbhCXJbz (ORCPT + 99 others); Wed, 24 Mar 2021 05:31:55 -0400 Received: from mail.synology.com ([211.23.38.101]:59258 "EHLO synology.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S235004AbhCXJbp (ORCPT ); Wed, 24 Mar 2021 05:31:45 -0400 Received: from localhost.localdomain (unknown [10.17.32.161]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by synology.com (Postfix) with ESMTPSA id 6B46CCE781EE; Wed, 24 Mar 2021 17:31:44 +0800 (CST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synology.com; s=123; t=1616578304; bh=ycjCGG+Ux6zuThGd3xuSA4cvVmyaodPS7cGN+rD28t0=; h=From:To:Cc:Subject:Date; b=eBNVQZApI1nVQ4+4osC1qG8/CpAYhaS/yColSxeB+GB4O+OhEYrcU4bjgYGMH4IRS XHWGDRpaSgNjDzT7mWjDiwSesW/XQe/TPPUepV4btR4UZPZVVmFLiiZG/YVvYM4npz eu1Xkqzt3JTzkO98516e6qi0QdqJ36j7NGSngzk8= From: bingjingc To: josef@toxicpanda.com, dsterba@suse.com, quwenruo@cn.fujitsu.com, clm@fb.com, linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org Cc: bingjingc@synology.com, cccheng@synology.com, robbieko@synology.com Subject: [PATCH] btrfs: fix a potential hole-punching failure Date: Wed, 24 Mar 2021 17:31:10 +0800 Message-Id: <1616578270-7365-1-git-send-email-bingjingc@synology.com> X-Mailer: git-send-email 2.7.4 X-Synology-MCP-Status: no X-Synology-Spam-Flag: no X-Synology-Spam-Status: score=0, required 6, WHITELIST_FROM_ADDRESS 0 X-Synology-Virus-Status: no Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: BingJing Chang In commit d77815461f04 ("btrfs: Avoid trucating page or punching hole in a already existed hole."), existed holes can be skipped by calling find_first_non_hole() to adjust *start and *len. However, if the given len is invalid and large, when an EXTENT_MAP_HOLE extent is found, the *len will not be set to zero because (em->start + em->len) is less than (*start + *len). Then the ret will be 1 but the *len will not be set to 0. The propagated non-zero ret will result in fallocate failure. In the while-loop of btrfs_replace_file_extents(), len is not updated every time before it calls find_first_non_hole(). That is, if the last file extent in the given hole-punching range has been dropped but btrfs_drop_extents() fails with -ENOSPC (btrfs_drop_extents() runs out of reserved space of the given transaction), the problem can happen. After it calls find_first_non_hole(), the cur_offset will be adjusted to be larger than or equal to end. However, since the len is not set to zero. The break-loop condition (ret && !len) will not meet. After it leaves the while-loop, uncleared ret will result in fallocate failure. We're not able to construct a reproducible way to let btrfs_drop_extents() fails with -ENOSPC after it drops the last file extent but with remaining holes. However, it's quite easy to fix. We just need to update and check the len every time before we call find_first_non_hole(). To make the while loop more readable, we also pull the variable updates to the bottom of loop like this: while (cur_offset < end) { ... // update cur_offset & len // advance cur_offset & len in hole-punching case if needed } Reported-by: Robbie Ko Fixes: d77815461f04 ("btrfs: Avoid trucating page or punching hole in a already existed hole.") Reviewed-by: Robbie Ko Reviewed-by: Chung-Chiang Cheng Signed-off-by: BingJing Chang --- fs/btrfs/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 0e155f0..dccb017 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2735,8 +2735,6 @@ int btrfs_replace_file_extents(struct inode *inode, struct btrfs_path *path, extent_info->file_offset += replace_len; } - cur_offset = drop_args.drop_end; - ret = btrfs_update_inode(trans, root, BTRFS_I(inode)); if (ret) break; @@ -2756,7 +2754,9 @@ int btrfs_replace_file_extents(struct inode *inode, struct btrfs_path *path, BUG_ON(ret); /* shouldn't happen */ trans->block_rsv = rsv; - if (!extent_info) { + cur_offset = drop_args.drop_end; + len = end - cur_offset; + if (!extent_info && len) { ret = find_first_non_hole(BTRFS_I(inode), &cur_offset, &len); if (unlikely(ret < 0)) -- 2.7.4