From nobody Sat Jul 25 22:33:16 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 188411FF5E3; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783866324; cv=none; b=rJ3QlAjTYqqtavtIHGG8pncwD64EdLGkr1z5V1Kc7PT7pgU4fCgUSEMlpULPXbPZaAPJ+sazUy/31OiIzwJ5EF1HMd4XTWCkV4z2FjkPNnIMd02x0bo0LJCbd07hmwvsBopb/RJD8FGu5cllH600+IB4xR067ikE2wqVn/qaMn4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783866324; c=relaxed/simple; bh=j1nEPAUOHUOESlYXXGEky5KoG/R24fIjV2AVRpvkEsc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mGp/I2YkaS4E94Z3By2YeMC552MVjNlJzNgRuRnILG3uQ5ecuFzbTy7icZh7BZizV3/RGMnt+pxOsMqHHPNLjGxUfVvkpM5Pf8CP55rcL71o7oAZNlFcs874C9AKV8gsEoKMzGFtOwc8noWbdfWDnI+otPDBgKe1j2z4hTaN74w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nVGgxm2n; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nVGgxm2n" Received: by smtp.kernel.org (Postfix) with ESMTPS id B5B7CC2BCF4; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1783866323; bh=j1nEPAUOHUOESlYXXGEky5KoG/R24fIjV2AVRpvkEsc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nVGgxm2n+T2S/VKKOgslCnMvE6mq6dC5JsQO4jbAfUHBJS+XU8bQr2jGAzYy/nZTV 1o9UOSMTglQoIjbKlqCpeVga8SKECRGCTd/rsS7Q2iXaCca9xY0Q3piDsZ+0HeITll Qu2yvGjroCEB9yM9F4PZUgepO7M5KTwjCtgi9Tb6kQJWI2ONKIMaw8A0shX2aasAU+ +hIxVmnnED50XezaePoShteE1OFzsABAE56kbKzkzyLVo/mO06JKibBR8DTobbMHQI KjU/cy2tyXg4uu47lwL4ytbLQWwmKlgv/Gdll6GeUU9M0wA8/2b1ih9A8QmP+gUuWc ECO0l51tAHTLQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97431C44501; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) From: Daan De Meyer via B4 Relay Date: Sun, 12 Jul 2026 16:25:13 +0200 Subject: [PATCH 1/2] btrfs: reject swapfile activation if any extent has checksums Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260712-reflink-into-nodatasum-v1-1-9f27ef836073@amutable.com> References: <20260712-reflink-into-nodatasum-v1-0-9f27ef836073@amutable.com> In-Reply-To: <20260712-reflink-into-nodatasum-v1-0-9f27ef836073@amutable.com> To: Chris Mason , David Sterba Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Daan De Meyer X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1783866322; l=3034; i=daan@amutable.com; s=20260712; h=from:subject:message-id; bh=u4OxLSoQvFJmB2D7ka2hrKLQE4cNzHXmvuQ5SAih+34=; b=YJca4M67OBNqWmBcy1w1pTcy5q4Y01HatTxtZHWH9eEAwx1grbC3uv9trLCSJ6pjIMkpJ0NxU Q2dHlJYmypUBQraxUs59Vv4cIHkEjBCpbt2P8xFy5Geo5UJ+QCCGt44 X-Developer-Key: i=daan@amutable.com; a=ed25519; pk=I1l+WwrtmzRgofA5SQ1wTuJi18fjh91w+f5uRkFeZEA= X-Endpoint-Received: by B4 Relay for daan@amutable.com/20260712 with auth_id=868 X-Original-From: Daan De Meyer Reply-To: daan@amutable.com From: Daan De Meyer btrfs_swap_activate() checks that the file is NODATACOW and NODATASUM and that none of its extents are shared, but it does not check that the extents are actually free of checksum items. Once an inode with NODATASUM set can reference extents that have checksums in the csum tree, which a subsequent patch allows by permitting reflinks from checksummed files into NODATASUM files, this becomes a corruption vector: 1. Reflink a checksummed file into a NODATASUM file. 2. Delete the source file, so the extents are no longer shared. 3. mkswap + swapon the NODATASUM file. The existing NODATACOW, NODATASUM and sharedness checks all pass. 4. Writes to the active swap file go to disk directly, bypassing the COW fallback in can_nocow_file_extent() that everywhere else prevents in-place writes over extents that have checksums. Such writes invalidate the checksums that remain in the csum tree, after which scrub and read repair report, and on profiles with redundancy attempt to repair, false corruption. Close the hole by rejecting swap file activation if checksums exist for any of the file's extents, mirroring the checksum lookup done by can_nocow_file_extent(). Signed-off-by: Daan De Meyer Reviewed-by: Neal Gompa --- fs/btrfs/inode.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 272598f6ae77..76a637f4263d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10216,6 +10216,7 @@ static int btrfs_swap_activate(struct swap_info_str= uct *sis, struct file *file, struct extent_buffer *leaf; struct btrfs_file_extent_item *ei; struct btrfs_block_group *bg; + struct btrfs_root *csum_root; u64 logical_block_start; u64 physical_block_start; u64 extent_gen; @@ -10300,6 +10301,34 @@ static int btrfs_swap_activate(struct swap_info_st= ruct *sis, struct file *file, goto out; } =20 + /* + * The extents may have csums despite the NODATASUM inode flag, + * from a reflink of a checksummed file into this file. Writes + * to an active swap file bypass the COW fallback that protects + * such extents everywhere else (see can_nocow_file_extent()), + * so they would invalidate the csums and make scrub and read + * repair report false corruption. + */ + csum_root =3D btrfs_csum_root(fs_info, logical_block_start); + if (unlikely(!csum_root)) { + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + logical_block_start); + ret =3D -EUCLEAN; + goto out; + } + ret =3D btrfs_lookup_csums_list(csum_root, logical_block_start, + logical_block_start + len - 1, + NULL, false); + if (ret < 0) + goto out; + if (ret > 0) { + btrfs_warn(fs_info, + "swapfile must not have checksummed extents"); + ret =3D -EINVAL; + goto out; + } + map =3D btrfs_get_chunk_map(fs_info, logical_block_start, len); if (IS_ERR(map)) { ret =3D PTR_ERR(map); --=20 2.54.0 From nobody Sat Jul 25 22:33:16 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 188A32264A8; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783866324; cv=none; b=PW6AQlUifQsL79LdjDyi/pncHamTtjJPxdfDGy3a9PMqbxM/p2ForgzkW4QAC/FLZdVJciixnm9tEayHNohtu4hhAEF0OCpWoYYip9+kv/OUBUHprixt+gRlP5cei2X2kNUlAeKSQ63LBK47v29Jmo199f+hl6plOLR6q43goNk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783866324; c=relaxed/simple; bh=vuzcIVzPjRqo7f5+zy8fi+bbRxHRjC33kq7tlm2vcbs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qEqGDP5/7o3MV2zeCKFAtj8aa/JEAOCKb69apRIoPGQlcQak3uBstPFdwyjvIYE1wLxUNis6V3NhwLfSH1kKMM6MEhSEbNQkDHRJ1eNyaGtBhaYZ723+fqFIkZBjm66t+MqhdHKZ3GfqUMGDxBuVfIwuL0AozG+87fE7bOSfTWU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Y34ILu+b; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Y34ILu+b" Received: by smtp.kernel.org (Postfix) with ESMTPS id C263DC2BCB9; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1783866323; bh=vuzcIVzPjRqo7f5+zy8fi+bbRxHRjC33kq7tlm2vcbs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Y34ILu+bh2NlUn0I8df29hgsLI6aRXxvoOiO+TPtNJOnTeu/c4I5UjgA8rp/adz8s AAmYZAyQarffX31sZ6ojCtmtQgfSXKkOOgZZQK9MmFkN0rwJjGCPSGR+uSqCvc1uKb kmWRI3Odl6g6tyaLREByozP6nKIf6RLFrH8VBA6vdLJA4r8v0aZA2145YO2crBb61l mFO8W6SP+b+QDrCeCxIrEj6gxGcw0nl5R5dlIw4F9kmy41bkRRCbPUfkUy/6S4U9rg rTSv8D+Bg2QToK2S1P2mm+uug5Yijbt7EddpNA8VVgxw2knCE46rrotRFm2am/v66o qoSX+f8TsarXg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A66B8C44506; Sun, 12 Jul 2026 14:25:23 +0000 (UTC) From: Daan De Meyer via B4 Relay Date: Sun, 12 Jul 2026 16:25:14 +0200 Subject: [PATCH 2/2] btrfs: allow reflinking from checksummed files into nodatasum files Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260712-reflink-into-nodatasum-v1-2-9f27ef836073@amutable.com> References: <20260712-reflink-into-nodatasum-v1-0-9f27ef836073@amutable.com> In-Reply-To: <20260712-reflink-into-nodatasum-v1-0-9f27ef836073@amutable.com> To: Chris Mason , David Sterba Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, Daan De Meyer X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1783866322; l=4259; i=daan@amutable.com; s=20260712; h=from:subject:message-id; bh=Hw0p3DRVBaGyXiZo3CL0kz3clGy7qMlOjFom3aktY3U=; b=9fuhcAk303kbpLIS3ldQ9CeEHlSms0au2XqPblElXbhfFBQyjkCW6MYLIsB1U4xiLjwBVwzfr sVOmcSM9WCHCqV4Io6bIuSAcCwTmLf9kn2IGyQps0YnpsTU2BenqhF5 X-Developer-Key: i=daan@amutable.com; a=ed25519; pk=I1l+WwrtmzRgofA5SQ1wTuJi18fjh91w+f5uRkFeZEA= X-Endpoint-Received: by B4 Relay for daan@amutable.com/20260712 with auth_id=868 X-Original-From: Daan De Meyer Reply-To: daan@amutable.com From: Daan De Meyer Cloning and deduplication have always been rejected when the source and destination inodes differ in their NODATASUM flag, making it impossible to reflink data from a regular, checksummed file into a NODATACOW file. This is a long-standing annoyance for use cases like copying VM images into chattr +C directories, where cp --reflink=3Dauto silently degrades to a full data copy, as previously reported. The restriction is only needed in one direction. Checksum items live in the csum tree keyed by disk bytenr and are shared by every inode that references an extent, and a clone operation copies no csum items at all. Therefore: - Reflinking from a NODATASUM file into a checksummed file would make the destination refer to extents that have no checksums, and reads of those extents would fail with -EIO. Creating the missing checksums at clone time is not an option either: besides making the clone read and checksum the full range, the checksums would be shared with the source file, whose NOCOW behavior would then silently break, as writes never go in place for extents that have checksums. This direction remains rejected. - Reflinking from a checksummed file into a NODATASUM file is safe. The destination inode has NODATASUM set, so reads through it never verify checksums, and the on-disk state it creates, a NODATASUM inode referencing extents that have checksums, is already handled correctly everywhere: * can_nocow_file_extent() forces COW for any range that has checksums, so in-place writes can never invalidate the checksums shared with the source, even after the source file is deleted and the extents are no longer shared. The first write to a cloned range COWs into a fresh extent without checksums, after which NOCOW behavior resumes. This is the same "COW once" behavior that NOCOW files already have after a snapshot. * fsync of the destination logs no csum items, and log replay only deletes csums that were found in the log, so replaying the destination cannot remove the checksums of the source's extents. * Scrub, balance and device replace operate on the csum tree per extent and do not consult inode flags. Relocation already tolerates per-extent mixed checksum state via EXTENT_NODATASUM. * Swap file activation rejects files with checksummed extents as of the previous patch. Relax the check in btrfs_remap_file_range_prep() to only reject the NODATASUM source -> checksummed destination direction. This applies to both clone and dedupe, which share the prep code. Link: https://lore.kernel.org/all/CA+H1V9zNSiJgXj6w8i2syhm_4qeaxkYPZHuxLgjm= fP-jjGMYBQ@mail.gmail.com/ Signed-off-by: Daan De Meyer Reviewed-by: Neal Gompa --- fs/btrfs/reflink.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index 9a49d2ecb949..9dab689ea388 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -858,11 +858,21 @@ static int btrfs_remap_file_range_prep(struct file *f= ile_in, loff_t pos_in, if (IS_ENCRYPTED(&inode_in->vfs_inode) !=3D IS_ENCRYPTED(&inode_out->vfs_= inode)) return -EINVAL; =20 - /* Don't make the dst file partly checksummed */ - if ((inode_in->flags & BTRFS_INODE_NODATASUM) !=3D - (inode_out->flags & BTRFS_INODE_NODATASUM)) { + /* + * Reflinking from a NODATASUM inode into a checksummed inode would + * make the destination refer to extents that have no csum items in + * the csum tree, and reads of those extents would then fail with + * -EIO. We can't create the missing csums here, so reject it. + * + * The other direction is safe: the destination inode has NODATASUM + * set, so reads through it never verify csums, and any write over a + * range whose extent has csums is forced to COW into a new extent + * (see can_nocow_file_extent()), so the csums shared with the source + * extents can never be invalidated by in-place writes. + */ + if ((inode_in->flags & BTRFS_INODE_NODATASUM) && + !(inode_out->flags & BTRFS_INODE_NODATASUM)) return -EINVAL; - } =20 /* * Now that the inodes are locked, we need to start writeback ourselves --=20 2.54.0