FAILED: patch "[PATCH] btrfs: do not overwrite NODATASUM flag when removing" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 15f7c86215e8d5f14b24127fa88af6c79363d50e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090330-nappy-discharge-0af0@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 15f7c86215e8d5f14b24127fa88af6c79363d50e Mon Sep 17 00:00:00 2001
From: Qu Wenruo <[email protected]>
Date: Tue, 9 Jun 2026 08:43:34 +0930
Subject: [PATCH] btrfs: do not overwrite NODATASUM flag when removing
NODATACOW flag
[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to "-o nodatasum":
FSTYP -- btrfs PLATFORM -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026 MKFS_OPTIONS -- -O bgt -K /dev/mapper/test-scratch1
MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
--- tests/generic/628.out 2022-05-11 11:25:30.816666664 +0930
+++ /home/adam/xfstests/results//generic/628.out.bad 2026-06-08 18:56:49.878542927 +0930
@@ -8,8 +8,9 @@
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/d
test reflink flag not set iflag
+XFS_IOC_CLONE: Invalid argument
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/b
+d41d8cd98f00b204e9800998ecf8427e SCRATCH_MNT/b
...[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its NODATASUM flag inherited from the mount option. E.g.:
# mkfs.btrfs -f $dev
# mount $dev $mnt -o nodatasum
# touch $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x1(NODATASUM)
^^^^^^^^^ Proper NODATASUM flag # chattr +S $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 2 flags 0x20(SYNC)
^^^^ Only the new SYNC flagThis makes the inode drop the old NODATASUM flag, while the new reflink destination will still inherit the NODATASUM flag. The mismatching NODATASUM flags will cause the reflink to fail.
The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is set, we remove both NODATASUM and NODATACOW flag.
However we should not touch NODATASUM flag, as data COW doesn't require checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply DATASUM.
The deeper problems are:
- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option. So that why "chattr +S" implies "chattr -C", and is forcing us to change NODATACOW along with NODATASUM flag.
- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.
The deeper problems are much harder to attack.
[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove NODATASUM if "nodatasum" mount option is not set.
This allows the existing "chattr +C" then "chattr -C" to remove both NODATACOW and NODATASUM flags on a default mount.
But for a mount with "nodatasum" option, the NODATASUM inode flag will persist through either "chattr +C" and "chattr -C".
fs/btrfs/ioctl.c +15 -8
@@ -356,14 +356,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_flags |=BTRFS_INODE_NODATACOW ; } }else { -/* -* Revert back under same assumptions as above -*/ -if (S_ISREG (inode->vfs_inode.i_mode)) { -if (inode->vfs_inode.i_size ==0 ) - inode_flags &= ~(BTRFS_INODE_NODATACOW | -BTRFS_INODE_NODATASUM ); - }else { +/* We can only change NODATACOW for zero-sized regular file. */ +if (S_ISREG (inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size ==0 )) { + inode_flags &= ~BTRFS_INODE_NODATACOW ; +/* +* There is currently no way to change NODATASUM flag +* through fileattr API. If we unconditionally keep the +* current NODATASUM flag, chattr +C then chattr -C will +* keep the NODATASUM flag, and no way to remove that +* flag. +* +* So respect the current mount option for NODATASUM flag. +*/ +if (!btrfs_test_opt (fs_info,NODATASUM )) + inode_flags &= ~BTRFS_INODE_NODATASUM ; + }else if (!S_ISREG (inode->vfs_inode.i_mode)) { inode_flags &= ~BTRFS_INODE_NODATACOW ; } }
Sasha Levin
maintainerPATCH 1/8 btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c
From: Filipe Manana <[email protected]>
[ Upstream commit a5b3f117daead61c3c9c88cd1159d38fa4ad1362 ]
It's a generic helper not specific to ioctls and used in several places, so move it out from ioctl.c and into fs.c. While at it change its return type from int to bool and declare the loop variable in the loop itself.
This also slightly reduces the module's size.
Before this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781492 161037 16920 1959449 1de619 fs/btrfs/btrfs.koAfter this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781340 161037 16920 1959297 1de581 fs/btrfs/btrfs.koStable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/fs.c | 9 +++++ fs/btrfs/fs.h | 2 + fs/btrfs/ioctl.c | 96 +++++++++++++++++++++--------------------------- fs/btrfs/ioctl.h | 1 - 4 files changed, 53 insertions(+), 55 deletions(-)
fs/btrfs/fs.c +9 -0
@@ -5,6 +5,15 @@#include "fs.h" #include "accessors.h" +bool __pure btrfs_is_empty_uuid (const u8 *uuid) +{ +for (int i =0 ; i <BTRFS_UUID_SIZE ; i++) { +if (uuid[i] !=0 ) +return false; + } +return true; +} +void __btrfs_set_fs_incompat (struct btrfs_fs_info *fs_info,u64 flag,const char *name) {
fs/btrfs/fs.h +2 -0
@@ -994,6 +994,8 @@ void btrfs_exclop_balance(struct btrfs_fs_info *fs_info,int btrfs_check_ioctl_vol_args_path (const struct btrfs_ioctl_vol_args *vol_args); +bool __pure btrfs_is_empty_uuid (const u8 *uuid); +/* Compatibility and incompatibility defines */ void __btrfs_set_fs_incompat (struct btrfs_fs_info *fs_info,u64 flag,const char *name);
fs/btrfs/ioctl.c +42 -54
187 lines not on this page · raw message
fs/btrfs/ioctl.h +0 -1
@@ -19,7 +19,6 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);int btrfs_ioctl_get_supported_features (void __user *arg);void btrfs_sync_inode_flags_to_i_flags (struct inode *inode); -int __pure btrfs_is_empty_uuid (const u8 *uuid);void btrfs_update_ioctl_balance_args (struct btrfs_fs_info *fs_info,struct btrfs_ioctl_balance_args *bargs);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/8 btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h
From: Filipe Manana <[email protected]>
[ Upstream commit a4545b74e2de98989c1d14bc48c52c2f9fa734b6 ]
Currently BTRFS_BYTES_TO_BLKS() is defined in ctree.h but it's not related at all to the btree data structure, so move it into fs.h.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ctree.h | 3 --- fs/btrfs/fs.h | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-)
fs/btrfs/ctree.h +0 -3
@@ -506,9 +506,6 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)return BTRFS_MAX_ITEM_SIZE (info) -sizeof (struct btrfs_dir_item ); } -#define BTRFS_BYTES_TO_BLKS (fs_info, bytes) \ - ((bytes) >> (fs_info)->sectorsize_bits) -static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) {return mapping_gfp_constraint (mapping, ~__GFP_FS);
fs/btrfs/fs.h +2 -0
@@ -959,6 +959,8 @@ static inline u64 btrfs_calc_metadata_size(const struct btrfs_fs_info *fs_info,#define BTRFS_MAX_EXTENT_ITEM_SIZE (r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \ sizeof(struct btrfs_item)) +#define BTRFS_BYTES_TO_BLKS (fs_info, bytes) ((bytes) >> (fs_info)->sectorsize_bits) +static inline bool btrfs_is_zoned (const struct btrfs_fs_info *fs_info) {return IS_ENABLED (CONFIG_BLK_DEV_ZONED ) && fs_info->zone_size >0 ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/8 btrfs: move btrfs_alloc_write_mask() into fs.h
In reply to Sasha LevinFrom: Filipe Manana <[email protected]>
[ Upstream commit 378f25d3fc429ad001fa686f615df5c0f9cd47e1 ]
Currently btrfs_alloc_write_mask() is defined in ctree.h but it's not related at all to the btree data structure, so move it into fs.h.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ctree.h | 6 ------ fs/btrfs/fs.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-)
fs/btrfs/ctree.h +0 -6
@@ -7,7 +7,6 @@#define BTRFS_CTREE_H #include "linux/cleanup.h" -#include <linux/pagemap.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/mutex.h>
@@ -506,11 +505,6 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)return BTRFS_MAX_ITEM_SIZE (info) -sizeof (struct btrfs_dir_item ); } -static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) -{ -return mapping_gfp_constraint (mapping, ~__GFP_FS); -} -void btrfs_error_unpin_extent_range (struct btrfs_fs_info *fs_info,u64 start,u64 end);int btrfs_discard_extent (struct btrfs_fs_info *fs_info,u64 bytenr,u64 num_bytes,u64 *actual_bytes);
fs/btrfs/fs.h +6 -0
@@ -18,6 +18,7 @@#include <linux/rwsem.h> #include <linux/semaphore.h> #include <linux/list.h> +#include <linux/pagemap.h> #include <linux/radix-tree.h> #include <linux/workqueue.h> #include <linux/wait.h>
@@ -893,6 +894,11 @@ struct btrfs_fs_info {#define inode_to_fs_info (_inode) (BTRFS_I(_Generic((_inode), \ struct inode *: (_inode)))->root->fs_info) +static inline gfp_t btrfs_alloc_write_mask (struct address_space *mapping) +{ +return mapping_gfp_constraint (mapping, ~__GFP_FS); +} +static inline u64 btrfs_get_fs_generation (const struct btrfs_fs_info *fs_info) {return READ_ONCE (fs_info->generation);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/8 btrfs: expose per-inode stable writes flag
In reply to Sasha LevinFrom: Qu Wenruo <[email protected]>
[ Upstream commit ecde48a1a6b3256bd49db8780bf37556b157783c ]
The address space flag AS_STABLE_WRITES determine if FGP_STABLE for will wait for the folio to finish its writeback.
For btrfs, due to the default data checksum behavior, if we modify the folio while it's still under writeback, it will cause data checksum mismatch. Thus for quite some call sites we manually call folio_wait_writeback() to prevent such problem from happening.
Currently there is only one call site inside btrfs really utilizing FGP_STABLE, and in that case we also manually call folio_wait_writeback() to do the waiting.
But it's better to properly expose the stable writes flag to a per-inode basis, to allow call sites to fully benefit from FGP_STABLE flag. E.g. for inodes with NODATASUM allowing beginning dirtying the page without waiting for writeback.
This involves:
- Update the mapping's stable write flag when setting/clearing NODATASUM inode flag using ioctl
This only works for empty files, so it should be fine.
- Update the mapping's stable write flag when reading an inode from disk
- Remove the explicit folio_wait_writeback() for FGP_BEGINWRITE call site
[ sashal: Reduced backport -- upstream ecde48a1a6b32 touches 4 file(s), this
backport carries 3. Not backported here:
fs/btrfs/btrfs_inode.h
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/file.c | 3 +-- fs/btrfs/inode.c | 8 ++++++++ fs/btrfs/ioctl.c | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-)
fs/btrfs/file.c +1 -2
@@ -884,7 +884,7 @@ static int prepare_uptodate_page(struct inode *inode,static fgf_t get_prepare_fgp_flags (bool nowait) { -fgf_t fgp_flags =FGP_LOCK |FGP_ACCESSED |FGP_CREAT ; +fgf_t fgp_flags =FGP_LOCK |FGP_ACCESSED |FGP_CREAT |FGP_STABLE ;if (nowait) fgp_flags |=FGP_NOWAIT ;
@@ -954,7 +954,6 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages, faili = i -1 ; goto fail; } -wait_on_page_writeback (pages[i]); }return 0 ;
fs/btrfs/inode.c +8 -0
@@ -3851,6 +3851,10 @@ static int btrfs_read_locked_inode(struct inode *inode,btrfs_inode_split_flags (btrfs_inode_flags (leaf, inode_item), &BTRFS_I (inode)->flags, &BTRFS_I (inode)->ro_flags); +if (BTRFS_I (inode)->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->i_mapping); +else +mapping_set_stable_writes (inode->i_mapping); cache_index:/*
@@ -6310,6 +6314,10 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,if (btrfs_test_opt (fs_info,NODATACOW ))BTRFS_I (inode)->flags |=BTRFS_INODE_NODATACOW |BTRFS_INODE_NODATASUM ; +if (BTRFS_I (inode)->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->i_mapping); +else +mapping_set_stable_writes (inode->i_mapping); } ret =btrfs_insert_inode_locked (inode);
fs/btrfs/ioctl.c +4 -0
@@ -391,6 +391,10 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, update_flags: inode->flags = inode_flags; +if (inode->flags &BTRFS_INODE_NODATASUM ) +mapping_clear_stable_writes (inode->vfs_inode.i_mapping); +else +mapping_set_stable_writes (inode->vfs_inode.i_mapping);btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode);inode_inc_iversion (&inode->vfs_inode);inode_set_ctime_current (&inode->vfs_inode);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/8 btrfs: update include and forward declarations in headers
In reply to Sasha LevinFrom: David Sterba <[email protected]>
[ Upstream commit 6149c82bdaff63203e58a4910d9245468d281f39 ]
Pass over all header files and add missing forward declarations, includes or fix include types.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/accessors.h | 1 + fs/btrfs/acl.h | 2 ++ fs/btrfs/ctree.h | 2 +- fs/btrfs/delayed-ref.h | 2 ++ fs/btrfs/dir-item.h | 1 + fs/btrfs/direct-io.h | 2 ++ fs/btrfs/discard.h | 1 + fs/btrfs/extent-tree.h | 1 - fs/btrfs/file-item.h | 2 ++ fs/btrfs/file.h | 2 ++ fs/btrfs/fs.c | 1 - fs/btrfs/ioctl.h | 2 ++ fs/btrfs/locking.c | 1 - fs/btrfs/ordered-data.h | 1 + fs/btrfs/print-tree.h | 2 ++ fs/btrfs/props.h | 1 + fs/btrfs/qgroup.h | 3 +++ fs/btrfs/raid-stripe-tree.h | 1 + fs/btrfs/send.c | 1 - fs/btrfs/space-info.c | 2 +- fs/btrfs/subpage.c | 1 - fs/btrfs/sysfs.h | 1 + fs/btrfs/volumes.h | 4 ++++ fs/btrfs/xattr.h | 2 ++ 24 files changed, 32 insertions(+), 7 deletions(-)
fs/btrfs/accessors.h +1 -0
@@ -12,6 +12,7 @@#include <linux/string.h> #include <linux/mm.h> #include <uapi/linux/btrfs_tree.h> +#include "extent_io.h" struct extent_buffer ;
fs/btrfs/acl.h +2 -0
@@ -3,6 +3,8 @@#ifndef BTRFS_ACL_H #define BTRFS_ACL_H +#include <linux/types.h> +struct posix_acl ;struct inode ;struct btrfs_trans_handle ;
fs/btrfs/ctree.h +1 -1
@@ -6,7 +6,7 @@#ifndef BTRFS_CTREE_H #define BTRFS_CTREE_H -#include "linux/cleanup.h" +#include <linux/cleanup.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/mutex.h>
fs/btrfs/delayed-ref.h +2 -0
@@ -14,6 +14,8 @@#include <linux/spinlock.h> #include <linux/slab.h> #include <uapi/linux/btrfs_tree.h> +#include "fs.h" +#include "messages.h" struct btrfs_trans_handle ;struct btrfs_fs_info ;
fs/btrfs/dir-item.h +1 -0
@@ -10,6 +10,7 @@ struct fscrypt_str;struct btrfs_fs_info ;struct btrfs_key ;struct btrfs_path ; +struct btrfs_inode ;struct btrfs_root ;struct btrfs_trans_handle ;
fs/btrfs/direct-io.h +2 -0
@@ -5,6 +5,8 @@#include <linux/types.h> +struct kiocb ; +int __init btrfs_init_dio (void );void __cold btrfs_destroy_dio (void );
fs/btrfs/discard.h +1 -0
@@ -3,6 +3,7 @@#ifndef BTRFS_DISCARD_H #define BTRFS_DISCARD_H +#include <linux/types.h> #include <linux/sizes.h> struct btrfs_fs_info ;
fs/btrfs/extent-tree.h +0 -1
@@ -4,7 +4,6 @@#define BTRFS_EXTENT_TREE_H #include <linux/types.h> -#include "misc.h" #include "block-group.h" #include "locking.h"
fs/btrfs/file-item.h +2 -0
@@ -3,8 +3,10 @@#ifndef BTRFS_FILE_ITEM_H #define BTRFS_FILE_ITEM_H +#include <linux/blk_types.h> #include <linux/list.h> #include <uapi/linux/btrfs_tree.h> +#include "ctree.h" #include "accessors.h" struct extent_map ;
fs/btrfs/file.h +2 -0
@@ -9,6 +9,8 @@ struct file;struct extent_state ;struct kiocb ;struct iov_iter ; +struct inode ; +struct folio ;struct page ;struct btrfs_ioctl_encoded_io_args ;struct btrfs_drop_extents_args ;
fs/btrfs/fs.c +0 -1
@@ -1,7 +1,6 @@// SPDX-License-Identifier: GPL-2.0 #include "messages.h" -#include "ctree.h" #include "fs.h" #include "accessors.h"
fs/btrfs/ioctl.h +2 -0
@@ -9,6 +9,8 @@ struct file;struct dentry ;struct mnt_idmap ;struct fileattr ; +struct inode ; +struct io_uring_cmd ;struct btrfs_fs_info ;struct btrfs_ioctl_balance_args ;
fs/btrfs/locking.c +0 -1
@@ -9,7 +9,6 @@#include <linux/page-flags.h> #include <asm/bug.h> #include <trace/events/btrfs.h> -#include "misc.h" #include "ctree.h" #include "extent_io.h" #include "locking.h"
fs/btrfs/ordered-data.h +1 -0
@@ -17,6 +17,7 @@struct inode ;struct page ;struct extent_state ; +struct btrfs_block_group ;struct btrfs_inode ;struct btrfs_root ;struct btrfs_fs_info ;
fs/btrfs/print-tree.h +2 -0
@@ -6,6 +6,8 @@#ifndef BTRFS_PRINT_TREE_H #define BTRFS_PRINT_TREE_H +#include <linux/types.h> +/* Buffer size to contain tree name and possibly additional data (offset) */ #define BTRFS_ROOT_NAME_BUF_LEN 48
fs/btrfs/props.h +1 -0
@@ -6,6 +6,7 @@#ifndef BTRFS_PROPS_H #define BTRFS_PROPS_H +#include <linux/types.h> #include <linux/compiler_types.h> struct inode ;
fs/btrfs/qgroup.h +3 -0
@@ -22,6 +22,9 @@ struct btrfs_ioctl_quota_ctl_args;struct btrfs_trans_handle ;struct btrfs_delayed_ref_root ;struct btrfs_inode ; +struct btrfs_transaction ; +struct btrfs_block_group ; +struct btrfs_qgroup_swapped_blocks ;/* * Btrfs qgroup overview
fs/btrfs/raid-stripe-tree.h +1 -0
@@ -9,6 +9,7 @@#include <linux/types.h> #include <uapi/linux/btrfs_tree.h> #include "fs.h" +#include "accessors.h" #define BTRFS_RST_SUPP_BLOCK_GROUP_MASK (BTRFS_BLOCK_GROUP_DUP | \ BTRFS_BLOCK_GROUP_RAID1_MASK | \
fs/btrfs/send.c +0 -1
@@ -17,7 +17,6 @@#include <linux/compat.h> #include <linux/crc32c.h> #include <linux/fsverity.h> -#include "send.h" #include "ctree.h" #include "backref.h"
fs/btrfs/space-info.c +1 -1
@@ -1,6 +1,6 @@// SPDX-License-Identifier: GPL-2.0 -#include "linux/spinlock.h" +#include <linux/spinlock.h> #include <linux/minmax.h> #include "misc.h" #include "ctree.h"
fs/btrfs/subpage.c +0 -1
@@ -2,7 +2,6 @@#include <linux/slab.h> #include "messages.h" -#include "ctree.h" #include "subpage.h" #include "btrfs_inode.h"
fs/btrfs/sysfs.h +1 -0
@@ -7,6 +7,7 @@#include <linux/compiler_types.h> #include <linux/kobject.h> +struct block_device ;struct btrfs_fs_info ;struct btrfs_device ;struct btrfs_fs_devices ;
fs/btrfs/volumes.h +4 -0
@@ -7,6 +7,7 @@#define BTRFS_VOLUMES_H #include <linux/blk_types.h> +#include <linux/blkdev.h> #include <linux/sizes.h> #include <linux/atomic.h> #include <linux/sort.h>
@@ -18,14 +19,17 @@#include <linux/completion.h> #include <linux/rbtree.h> #include <uapi/linux/btrfs.h> +#include <uapi/linux/btrfs_tree.h> #include "messages.h" #include "rcu-string.h" +#include "extent-io-tree.h" struct block_device ;struct bdev_handle ;struct btrfs_fs_info ;struct btrfs_block_group ;struct btrfs_trans_handle ; +struct btrfs_transaction ;struct btrfs_zoned_device_info ;#define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G)
fs/btrfs/xattr.h +2 -0
@@ -6,6 +6,8 @@#ifndef BTRFS_XATTR_H #define BTRFS_XATTR_H +#include <linux/types.h> +struct dentry ;struct inode ;struct qstr ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 6/8 btrfs: parameter constification in ioctl.c
In reply to Sasha LevinFrom: David Sterba <[email protected]>
[ Upstream commit 5e54f9420fc9b8096d0a4b579a84806a3ef0bf88 ]
Add const to function parameters that are not changed.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/ioctl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
fs/btrfs/ioctl.c +14 -14
@@ -117,8 +117,8 @@ struct btrfs_ioctl_encoded_io_args_32 {#endif /* Mask out flags that are inappropriate for the given type of inode. */ -static unsigned int btrfs_mask_fsflags_for_type (struct inode *inode, -unsigned int flags) +static unsigned int btrfs_mask_fsflags_for_type (const struct inode *inode, +unsigned int flags) {if (S_ISDIR (inode->i_mode))return flags;
@@ -132,7 +132,7 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode,* Export internal inode flags to the format expected by the FS_IOC_GETFLAGS * ioctl. */ -static unsigned int btrfs_inode_flags_to_fsflags (struct btrfs_inode *binode) +static unsigned int btrfs_inode_flags_to_fsflags (const struct btrfs_inode *binode) {unsigned int iflags =0 ;u32 flags = binode->flags;
@@ -218,7 +218,7 @@ static int check_fsflags(unsigned int old_flags, unsigned int flags)return 0 ; } -static int check_fsflags_compatible (struct btrfs_fs_info *fs_info, +static int check_fsflags_compatible (const struct btrfs_fs_info *fs_info,unsigned int flags) {if (btrfs_is_zoned (fs_info) && (flags &FS_NOCOW_FL ))
@@ -247,7 +247,7 @@ static int btrfs_check_ioctl_vol_args2_subvol_name(const struct btrfs_ioctl_vol_*/ int btrfs_fileattr_get (struct dentry *dentry,struct fileattr *fa) { -struct btrfs_inode *binode =BTRFS_I (d_inode (dentry)); +const struct btrfs_inode *binode =BTRFS_I (d_inode (dentry));fileattr_fill_flags (fa,btrfs_inode_flags_to_fsflags (binode));return 0 ;
@@ -485,7 +485,7 @@ void btrfs_exclop_balance(struct btrfs_fs_info *fs_info, } } -static int btrfs_ioctl_getversion (struct inode *inode,int __user *arg) +static int btrfs_ioctl_getversion (const struct inode *inode,int __user *arg) {return put_user (inode->i_generation, arg); }
@@ -557,7 +557,7 @@ static noinline int btrfs_ioctl_fitrim(struct btrfs_fs_info *fs_info,* Calculate the number of transaction items to reserve for creating a subvolume * or snapshot, not including the inode, directory entries, or parent directory. */ -static unsigned int create_subvol_num_items (struct btrfs_qgroup_inherit *inherit) +static unsigned int create_subvol_num_items (const struct btrfs_qgroup_inherit *inherit) {/* * 1 to add root block
@@ -967,7 +967,7 @@ static int btrfs_may_delete(struct mnt_idmap *idmap,/* copy of may_create in fs/namei.c() */ static inline int btrfs_may_create (struct mnt_idmap *idmap, -struct inode *dir,struct dentry *child) +struct inode *dir,const struct dentry *child) {if (d_really_is_positive (child))return -EEXIST ;
@@ -1539,8 +1539,8 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,return ret; } -static noinline int key_in_sk(struct btrfs_key *key, -struct btrfs_ioctl_search_key *sk) +static noinline int key_in_sk(const struct btrfs_key *key, +const struct btrfs_ioctl_search_key *sk) {struct btrfs_key test;int ret;
@@ -1565,7 +1565,7 @@ static noinline int key_in_sk(struct btrfs_key *key, static noinline int copy_to_sk(struct btrfs_path *path, struct btrfs_key *key, - struct btrfs_ioctl_search_key *sk, + const struct btrfs_ioctl_search_key *sk, u64 *buf_size, char __user *ubuf, unsigned long *sk_offset,
@@ -2844,7 +2844,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)return ret; } -static long btrfs_ioctl_fs_info (struct btrfs_fs_info *fs_info, +static long btrfs_ioctl_fs_info (const struct btrfs_fs_info *fs_info,void __user *arg) {struct btrfs_ioctl_fs_info_args *fi_args;
@@ -2898,7 +2898,7 @@ static long btrfs_ioctl_fs_info(struct btrfs_fs_info *fs_info,return ret; } -static long btrfs_ioctl_dev_info (struct btrfs_fs_info *fs_info, +static long btrfs_ioctl_dev_info (const struct btrfs_fs_info *fs_info,void __user *arg) {BTRFS_DEV_LOOKUP_ARGS (args);
@@ -4356,7 +4356,7 @@ static int btrfs_ioctl_get_features(struct btrfs_fs_info *fs_info,return 0 ; } -static int check_feature_bits (struct btrfs_fs_info *fs_info, +static int check_feature_bits (const struct btrfs_fs_info *fs_info,enum btrfs_feature_set set,u64 change_mask,u64 flags,u64 supported_flags,u64 safe_set,u64 safe_clear)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 7/8 btrfs: pass struct btrfs_inode to btrfs_sync_inode_flags_to_i_flags()
In reply to Sasha LevinFrom: David Sterba <[email protected]>
[ Upstream commit 4f27a693940bcf313d17792ac4ec13a83f71cf25 ]
Pass a struct btrfs_inode to btrfs_sync_inode_flags_to_i_flags() as it's an internal interface.
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
--- fs/btrfs/inode.c | 4 ++-- fs/btrfs/ioctl.c | 19 +++++++++---------- fs/btrfs/ioctl.h | 4 ++-- fs/btrfs/verity.c | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-)
fs/btrfs/inode.c +2 -2
@@ -3972,7 +3972,7 @@ static int btrfs_read_locked_inode(struct inode *inode,break ; } -btrfs_sync_inode_flags_to_i_flags (inode); +btrfs_sync_inode_flags_to_i_flags (BTRFS_I (inode));return 0 ; }
@@ -6209,7 +6209,7 @@ static void btrfs_inherit_iflags(struct btrfs_inode *inode, struct btrfs_inode * inode->flags |=BTRFS_INODE_NODATASUM ; } -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); } int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
fs/btrfs/ioctl.c +9 -10
@@ -166,25 +166,24 @@ static unsigned int btrfs_inode_flags_to_fsflags(const struct btrfs_inode *binod/* * Update inode->i_flags based on the btrfs internal flags. */ -void btrfs_sync_inode_flags_to_i_flags (struct inode *inode) +void btrfs_sync_inode_flags_to_i_flags (struct btrfs_inode *inode) { -struct btrfs_inode *binode =BTRFS_I (inode);unsigned int new_fl =0 ; -if (binode->flags &BTRFS_INODE_SYNC ) +if (inode->flags &BTRFS_INODE_SYNC ) new_fl |=S_SYNC ; -if (binode->flags &BTRFS_INODE_IMMUTABLE ) +if (inode->flags &BTRFS_INODE_IMMUTABLE ) new_fl |=S_IMMUTABLE ; -if (binode->flags &BTRFS_INODE_APPEND ) +if (inode->flags &BTRFS_INODE_APPEND ) new_fl |=S_APPEND ; -if (binode->flags &BTRFS_INODE_NOATIME ) +if (inode->flags &BTRFS_INODE_NOATIME ) new_fl |=S_NOATIME ; -if (binode->flags &BTRFS_INODE_DIRSYNC ) +if (inode->flags &BTRFS_INODE_DIRSYNC ) new_fl |=S_DIRSYNC ; -if (binode->ro_flags &BTRFS_INODE_RO_VERITY ) +if (inode->ro_flags &BTRFS_INODE_RO_VERITY ) new_fl |=S_VERITY ; -set_mask_bits (&inode->i_flags, +set_mask_bits (&inode->vfs_inode.i_flags,S_SYNC |S_APPEND |S_IMMUTABLE |S_NOATIME |S_DIRSYNC |S_VERITY , new_fl); }
@@ -395,7 +394,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,mapping_clear_stable_writes (inode->vfs_inode.i_mapping); else mapping_set_stable_writes(inode->vfs_inode.i_mapping); -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode);inode_inc_iversion (&inode->vfs_inode);inode_set_ctime_current (&inode->vfs_inode); ret =btrfs_update_inode (trans, inode);
fs/btrfs/ioctl.h +2 -2
@@ -9,8 +9,8 @@ struct file;struct dentry ;struct mnt_idmap ;struct fileattr ; -struct inode ;struct io_uring_cmd ; +struct btrfs_inode ;struct btrfs_fs_info ;struct btrfs_ioctl_balance_args ;
@@ -20,7 +20,7 @@ int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);int btrfs_fileattr_set (struct mnt_idmap *idmap,struct dentry *dentry,struct fileattr *fa);int btrfs_ioctl_get_supported_features (void __user *arg); -void btrfs_sync_inode_flags_to_i_flags (struct inode *inode); +void btrfs_sync_inode_flags_to_i_flags (struct btrfs_inode *inode);void btrfs_update_ioctl_balance_args (struct btrfs_fs_info *fs_info,struct btrfs_ioctl_balance_args *bargs);
fs/btrfs/verity.c +2 -2
@@ -485,7 +485,7 @@ static int rollback_verity(struct btrfs_inode *inode) goto out; } inode->ro_flags &= ~BTRFS_INODE_RO_VERITY ; -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); ret =btrfs_update_inode (trans, inode);if (ret) {btrfs_abort_transaction (trans, ret);
@@ -552,7 +552,7 @@ static int finish_verity(struct btrfs_inode *inode, const void *desc, goto out; } inode->ro_flags |=BTRFS_INODE_RO_VERITY ; -btrfs_sync_inode_flags_to_i_flags (&inode->vfs_inode); +btrfs_sync_inode_flags_to_i_flags (inode); ret =btrfs_update_inode (trans, inode);if (ret) goto end_trans;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 8/8 btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag
In reply to Sasha LevinFrom: Qu Wenruo <[email protected]>
[ Upstream commit 15f7c86215e8d5f14b24127fa88af6c79363d50e ]
[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to "-o nodatasum":
FSTYP -- btrfs PLATFORM -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026 MKFS_OPTIONS -- -O bgt -K /dev/mapper/test-scratch1
MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
--- tests/generic/628.out 2022-05-11 11:25:30.816666664 +0930
+++ /home/adam/xfstests/results//generic/628.out.bad 2026-06-08 18:56:49.878542927 +0930
@@ -8,8 +8,9 @@
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/d
test reflink flag not set iflag
+XFS_IOC_CLONE: Invalid argument
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/b
+d41d8cd98f00b204e9800998ecf8427e SCRATCH_MNT/b
...[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its NODATASUM flag inherited from the mount option. E.g.:
# mkfs.btrfs -f $dev
# mount $dev $mnt -o nodatasum
# touch $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x1(NODATASUM)
^^^^^^^^^ Proper NODATASUM flag # chattr +S $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 2 flags 0x20(SYNC)
^^^^ Only the new SYNC flagThis makes the inode drop the old NODATASUM flag, while the new reflink destination will still inherit the NODATASUM flag. The mismatching NODATASUM flags will cause the reflink to fail.
The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is set, we remove both NODATASUM and NODATACOW flag.
However we should not touch NODATASUM flag, as data COW doesn't require checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply DATASUM.
The deeper problems are:
- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option. So that why "chattr +S" implies "chattr -C", and is forcing us to change NODATACOW along with NODATASUM flag.
- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.
The deeper problems are much harder to attack.
[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove NODATASUM if "nodatasum" mount option is not set.
This allows the existing "chattr +C" then "chattr -C" to remove both NODATACOW and NODATASUM flags on a default mount.
But for a mount with "nodatasum" option, the NODATASUM inode flag will persist through either "chattr +C" and "chattr -C".
--- fs/btrfs/ioctl.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
fs/btrfs/ioctl.c +15 -8
@@ -329,14 +329,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_flags |=BTRFS_INODE_NODATACOW ; } }else { -/* -* Revert back under same assumptions as above -*/ -if (S_ISREG (inode->vfs_inode.i_mode)) { -if (inode->vfs_inode.i_size ==0 ) - inode_flags &= ~(BTRFS_INODE_NODATACOW | -BTRFS_INODE_NODATASUM ); - }else { +/* We can only change NODATACOW for zero-sized regular file. */ +if (S_ISREG (inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size ==0 )) { + inode_flags &= ~BTRFS_INODE_NODATACOW ; +/* +* There is currently no way to change NODATASUM flag +* through fileattr API. If we unconditionally keep the +* current NODATASUM flag, chattr +C then chattr -C will +* keep the NODATASUM flag, and no way to remove that +* flag. +* +* So respect the current mount option for NODATASUM flag. +*/ +if (!btrfs_test_opt (fs_info,NODATASUM )) + inode_flags &= ~BTRFS_INODE_NODATASUM ; + }else if (!S_ISREG (inode->vfs_inode.i_mode)) { inode_flags &= ~BTRFS_INODE_NODATACOW ; } }
Signature · 1 line
2.53.0