On Wed, Jul 30, 2025 at 08:21:41PM +0100, fdmanana@xxxxxxxxxx wrote: > From: Filipe Manana <fdmanana@xxxxxxxx> > > Create two files, the first one with some data, and then fsync both > files. The first file is fsynced after removing its hardlink. After a > power failure we expect the fs to be mountable and for only the second > file to be present. > > This exercises an issue found in btrfs and fixed by the following patch: > > btrfs: fix log tree replay failure due to file with 0 links and extents > > Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx> > --- This test case looks good to me. And looks like I triggered the btrfs bug (kernel crash) [1] with this case. If it's the expected test result, then Reviewed-by: Zorro Lang <zlang@xxxxxxxxxx> [1] [2375125.078932] run fstests generic/771 at 2025-08-02 03:26:24 [2375125.442949] BTRFS: device fsid 9a850dcc-f313-4c8b-96c0-bb82000e8b68 devid 1 transid 8 /dev/mapper/flakey-test.771 (252:7) scanned by mount (127783) [2375125.456810] BTRFS info (device dm-7): first mount of filesystem 9a850dcc-f313-4c8b-96c0-bb82000e8b68 [2375125.466137] BTRFS info (device dm-7): using crc32c (crc32c-x86_64) checksum algorithm [2375125.474158] BTRFS info (device dm-7): using free-space-tree [2375125.482034] BTRFS info (device dm-7): checking UUID tree [2375125.531795] BTRFS info (device dm-7): last unmount of filesystem 9a850dcc-f313-4c8b-96c0-bb82000e8b68 [2375125.578357] BTRFS: device fsid 9a850dcc-f313-4c8b-96c0-bb82000e8b68 devid 1 transid 8 /dev/mapper/flakey-test.771 (252:7) scanned by mount (127827) [2375125.592066] BTRFS info (device dm-7): first mount of filesystem 9a850dcc-f313-4c8b-96c0-bb82000e8b68 [2375125.601381] BTRFS info (device dm-7): using crc32c (crc32c-x86_64) checksum algorithm [2375125.609391] BTRFS info (device dm-7): using free-space-tree [2375125.617546] BTRFS info (device dm-7): start tree-log replay [2375125.623731] BUG: kernel NULL pointer dereference, address: 0000000000000219 [2375125.630868] #PF: supervisor read access in kernel mode [2375125.636179] #PF: error_code(0x0000) - not-present page [2375125.641491] PGD 11315c067 P4D 0 [2375125.644898] Oops: Oops: 0000 [#1] SMP NOPTI [2375125.649259] CPU: 18 UID: 0 PID: 127827 Comm: mount Tainted: G S ------ --- 6.16.0-0.rc1.250613g27605c8c0f69.21.fc43.x86_64 #1 PREEMPT(lazy) [2375125.663583] Tainted: [S]=CPU_OUT_OF_SPEC [2375125.667682] Hardware name: Dell Inc. PowerEdge R750/0PJ80M, BIOS 1.5.4 12/17/2021 [2375125.675337] RIP: 0010:iput+0x20/0x230 [2375125.679174] Code: 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 48 85 ff 0f 84 86 01 00 00 41 54 55 48 8d af 50 01 00 00 53 48 89 fb <f6> 87 91 00 00 00 01 74 3a e9 68 01 00 00 8b 53 48 8b 83 90 00 00 [2375125.698093] RSP: 0018:ff2f85c0e8abb7c0 EFLAGS: 00010206 [2375125.703494] RAX: 0000000000000000 RBX: 0000000000000188 RCX: 00000000000b6012 [2375125.710799] RDX: 0000000000000000 RSI: ffffffff95fc5070 RDI: 0000000000000188 [2375125.718107] RBP: 00000000000002d8 R08: 0000000000000000 R09: ffffffff931aabd6 [2375125.725411] R10: ff153ec034a8f5b0 R11: ffbf667744d2a3c0 R12: ff2f85c0e8abb93f [2375125.732719] R13: 0000000000000000 R14: ff153ec0ff155770 R15: 00000000fffffffb [2375125.740025] FS: 00007f0bec606840(0000) GS:ff153ecfa8cff000(0000) knlGS:0000000000000000 [2375125.748284] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [2375125.754201] CR2: 0000000000000219 CR3: 00000002076a7006 CR4: 0000000000773ef0 [2375125.761510] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [2375125.768814] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [2375125.776121] PKRU: 55555554 [2375125.779008] Call Trace: [2375125.781631] <TASK> [2375125.783911] replay_one_extent+0xba/0x740 [2375125.788098] ? copy_extent_buffer+0x126/0x160 [2375125.792629] ? btrfs_release_path+0x2b/0x1b0 [2375125.797075] ? read_extent_buffer+0x102/0x140 [2375125.801609] replay_one_buffer+0x2ee/0x500 [2375125.805883] ? find_extent_buffer+0xab/0x110 [2375125.810326] walk_up_log_tree+0xe8/0x320 [2375125.814429] ? btrfs_root_node+0x2d/0x50 [2375125.818526] ? walk_log_tree+0x2e/0x280 [2375125.822538] walk_log_tree+0xc6/0x280 [2375125.826378] btrfs_recover_log_trees+0x1cc/0x5a0 [2375125.831171] ? __pfx_replay_one_buffer+0x10/0x10 [2375125.835964] open_ctree+0x916/0xb9c [2375125.839630] btrfs_get_tree_super.cold+0xb/0xbf [2375125.844336] vfs_get_tree+0x26/0xd0 [2375125.848003] fc_mount+0x12/0x50 [2375125.851320] btrfs_get_tree_subvol+0x10d/0x210 [2375125.855941] vfs_get_tree+0x26/0xd0 [2375125.859606] vfs_cmd_create+0x57/0xd0 [2375125.863446] __do_sys_fsconfig+0x4b6/0x650 [2375125.867718] do_syscall_64+0x82/0x2c0 [2375125.871558] ? __do_sys_getgid+0x27/0x30 [2375125.875657] ? do_syscall_64+0x82/0x2c0 [2375125.879669] ? __do_sys_newfstatat+0x4a/0x80 [2375125.884117] ? from_kgid_munged+0x17/0x30 [2375125.888301] ? __do_sys_getegid+0x27/0x30 [2375125.892487] ? do_syscall_64+0x82/0x2c0 [2375125.896499] ? do_syscall_64+0x82/0x2c0 [2375125.900511] ? do_syscall_64+0x82/0x2c0 [2375125.904525] ? __x64_sys_statx+0x9a/0xe0 [2375125.908626] ? do_syscall_64+0x82/0x2c0 [2375125.912639] ? clear_bhb_loop+0x50/0xa0 [2375125.916649] ? clear_bhb_loop+0x50/0xa0 [2375125.920664] ? clear_bhb_loop+0x50/0xa0 [2375125.924676] entry_SYSCALL_64_after_hwframe+0x76/0x7e [2375125.929902] RIP: 0033:0x7f0bec7e548e [2375125.933673] Code: 73 01 c3 48 8b 0d 72 19 0f 00 f7 d8 64 89 01 48 83 c8 ff c3 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 49 89 ca b8 af 01 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 42 19 0f 00 f7 d8 64 89 01 48 [2375125.952591] RSP: 002b:00007fff5b55e518 EFLAGS: 00000246 ORIG_RAX: 00000000000001af [2375125.960329] RAX: ffffffffffffffda RBX: 00005593c5cb8420 RCX: 00007f0bec7e548e [2375125.967637] RDX: 0000000000000000 RSI: 0000000000000006 RDI: 0000000000000003 [2375125.974943] RBP: 00007fff5b55e660 R08: 0000000000000000 R09: 0000000000000048 [2375125.982249] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 [2375125.989554] R13: 00005593c5cb96f0 R14: 00007f0bec960b00 R15: 00005593c5cb97b8 [2375125.996860] </TASK> [2375125.999224] Modules linked in: dm_flakey xfs joydev rfkill nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nft_chain_nat nf_nat mlx5_ib nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 ib_uverbs macsec ib_core mlx5_fwctl nf_tables intel_rapl_msr intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common i10nm_edac skx_edac_common nfit libnvdimm x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel mlx5_core kvm spi_nor dax_hmem cxl_acpi iTCO_wdt irqbypass ipmi_ssif cxl_port mtd intel_pmc_bxt rapl mlxfw iTCO_vendor_support cxl_core isst_if_mmio intel_cstate isst_if_mbox_pci psample mei_me intel_th_gth platform_profile dell_smbios fwctl tls i2c_i801 intel_th_pci spi_intel_pci tg3 mei einj dcdbas dell_wmi_descriptor wmi_bmof intel_uncore acpi_power_meter isst_if_common pci_hyperv_intf i2c_smbus spi_intel intel_th intel_pch_thermal intel_vsec ipmi_si acpi_ipmi ipmi_devintf ipmi_msghandler fuse loop nfnetlink zram lz4hc_compress lz4_compress [2375125.999290] polyval_clmulni ghash_clmulni_intel mgag200 sha512_ssse3 sha1_ssse3 megaraid_sas i2c_algo_bit wmi [2375126.098659] CR2: 0000000000000219 [2375126.102153] ---[ end trace 0000000000000000 ]--- [2375126.342787] RIP: 0010:iput+0x20/0x230 [2375126.346628] Code: 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 48 85 ff 0f 84 86 01 00 00 41 54 55 48 8d af 50 01 00 00 53 48 89 fb <f6> 87 91 00 00 00 01 74 3a e9 68 01 00 00 8b 53 48 8b 83 90 00 00 [2375126.365548] RSP: 0018:ff2f85c0e8abb7c0 EFLAGS: 00010206 [2375126.370949] RAX: 0000000000000000 RBX: 0000000000000188 RCX: 00000000000b6012 [2375126.378253] RDX: 0000000000000000 RSI: ffffffff95fc5070 RDI: 0000000000000188 [2375126.385560] RBP: 00000000000002d8 R08: 0000000000000000 R09: ffffffff931aabd6 [2375126.392865] R10: ff153ec034a8f5b0 R11: ffbf667744d2a3c0 R12: ff2f85c0e8abb93f [2375126.400171] R13: 0000000000000000 R14: ff153ec0ff155770 R15: 00000000fffffffb [2375126.407478] FS: 00007f0bec606840(0000) GS:ff153ecfa8cff000(0000) knlGS:0000000000000000 [2375126.415737] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [2375126.421657] CR2: 0000000000000219 CR3: 00000002076a7006 CR4: 0000000000773ef0 [2375126.428961] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [2375126.436268] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [2375126.443574] PKRU: 55555554 [2375126.446461] note: mount[127827] exited with irqs disabled [2375866.234285] sysrq: Show Blocked State [2375866.245781] task:btrfs-transacti state:D stack:0 pid:127842 tgid:127842 ppid:2 task_flags:0x208040 flags:0x00004000 [2375866.257188] Call Trace: [2375866.259830] <TASK> [2375866.262130] __schedule+0x2f9/0x7b0 [2375866.265821] schedule+0x27/0x80 [2375866.269154] btrfs_commit_transaction+0x912/0xd30 [2375866.274058] ? start_transaction+0x228/0x840 [2375866.278516] ? __pfx_autoremove_wake_function+0x10/0x10 [2375866.283935] transaction_kthread+0x157/0x1c0 [2375866.288395] ? __pfx_transaction_kthread+0x10/0x10 [2375866.293378] kthread+0xfc/0x240 [2375866.296713] ? __pfx_kthread+0x10/0x10 [2375866.300653] ret_from_fork+0x14f/0x180 [2375866.304590] ? __pfx_kthread+0x10/0x10 [2375866.308526] ret_from_fork_asm+0x1a/0x30 ... > .gitignore | 1 + > src/Makefile | 2 +- > src/unlink-fsync.c | 45 ++++++++++++++++++++++++++++++++ > tests/generic/771 | 60 +++++++++++++++++++++++++++++++++++++++++++ > tests/generic/771.out | 4 +++ > 5 files changed, 111 insertions(+), 1 deletion(-) > create mode 100644 src/unlink-fsync.c > create mode 100755 tests/generic/771 > create mode 100644 tests/generic/771.out > > diff --git a/.gitignore b/.gitignore > index 58dc2a63..6948fd60 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -210,6 +210,7 @@ tags > /src/fiemap-fault > /src/min_dio_alignment > /src/dio-writeback-race > +/src/unlink-fsync > > # Symlinked files > /tests/generic/035.out > diff --git a/src/Makefile b/src/Makefile > index 2cc1fb40..7080e348 100644 > --- a/src/Makefile > +++ b/src/Makefile > @@ -21,7 +21,7 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \ > t_mmap_writev_overlap checkpoint_journal mmap-rw-fault allocstale \ > t_mmap_cow_memory_failure fake-dump-rootino dio-buf-fault rewinddir-test \ > readdir-while-renames dio-append-buf-fault dio-write-fsync-same-fd \ > - dio-writeback-race > + dio-writeback-race unlink-fsync > > LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \ > preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \ > diff --git a/src/unlink-fsync.c b/src/unlink-fsync.c > new file mode 100644 > index 00000000..ce008c6b > --- /dev/null > +++ b/src/unlink-fsync.c > @@ -0,0 +1,45 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2025 SUSE Linux Products GmbH. All Rights Reserved. > + */ > + > +/* > + * Utility to open an existing file, unlink it while holding an open fd on it > + * and then fsync the file before closing the fd. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <fcntl.h> > + > +int main(int argc, char *argv[]) > +{ > + int fd; > + int ret; > + > + if (argc != 2) { > + fprintf(stderr, "Use: %s <file path>\n", argv[0]); > + return 1; > + } > + > + fd = open(argv[1], O_WRONLY, 0666); > + if (fd == -1) { > + perror("failed to open file"); > + exit(1); > + } > + > + ret = unlink(argv[1]); > + if (ret == -1) { > + perror("unlink failed"); > + exit(2); > + } > + > + ret = fsync(fd); > + if (ret == -1) { > + perror("fsync failed"); > + exit(3); > + } > + > + return 0; > +} > diff --git a/tests/generic/771 b/tests/generic/771 > new file mode 100755 > index 00000000..ad30cc0a > --- /dev/null > +++ b/tests/generic/771 > @@ -0,0 +1,60 @@ > +#! /bin/bash > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (c) 2025 SUSE Linux Products GmbH. All Rights Reserved. > +# > +# FS QA Test 771 > +# > +# Create two files, the first one with some data, and then fsync both files. > +# The first file is fsynced after removing its hardlink. After a power failure > +# we expect the fs to be mountable and for only the second file to be present. > +# > +. ./common/preamble > +_begin_fstest auto quick log > + > +_cleanup() > +{ > + _cleanup_flakey > + cd / > + rm -r -f $tmp.* > +} > + > +. ./common/filter > +. ./common/dmflakey > + > +_require_scratch > +_require_test_program unlink-fsync > +_require_dm_target flakey > + > +[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \ > + "btrfs: fix log tree replay failure due to file with 0 links and extents" > + > +_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed" > +_require_metadata_journaling $SCRATCH_DEV > +_init_flakey > +_mount_flakey > + > +# Create our first test file with some data. > +mkdir $SCRATCH_MNT/testdir > +$XFS_IO_PROG -f -c "pwrite 0K 64K" $SCRATCH_MNT/testdir/foo | _filter_xfs_io > + > +# fsync our first test file after unlinking it - we keep an fd open for the > +# file, unlink it and then fsync the file using that fd, so that we log/journal > +# a file with 0 hard links. > +$here/src/unlink-fsync $SCRATCH_MNT/testdir/foo > + > +# Create another test file and fsync it. > +touch $SCRATCH_MNT/testdir/bar > +$XFS_IO_PROG -c "fsync" $SCRATCH_MNT/testdir/bar > + > +# Simulate a power failure and replay the log/journal. > +# On btrfs we had a bug where the replay procedure failed, causing the fs mount > +# to fail, because the first test file has extents and the second one, which has > +# an higher inode number, has a non-zero (1) link count - the replay code got > +# confused and thought the extents belonged to the second file and then it > +# failed when trying to open a non-existing inode to replay the extents. > +_flakey_drop_and_remount > + > +# File foo should not exist and file bar should exist. > +ls -1 $SCRATCH_MNT/testdir > + > +_exit 0 > diff --git a/tests/generic/771.out b/tests/generic/771.out > new file mode 100644 > index 00000000..e40d7091 > --- /dev/null > +++ b/tests/generic/771.out > @@ -0,0 +1,4 @@ > +QA output created by 771 > +wrote 65536/65536 bytes at offset 0 > +XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) > +bar > -- > 2.47.2 > >