From: Filipe Manana Date: Wed, 30 Jul 2025 19:21:41 +0000 (+0100) Subject: generic: test fsync of file with 0 links and extents X-Git-Tag: v2025.08.04~6 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d8b702f89e01fad5a67952833e2ef838a3436e62;p=xfstests-dev.git generic: test fsync of file with 0 links and extents 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 Reviewed-by: Zorro Lang Signed-off-by: Zorro Lang --- 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 +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd; + int ret; + + if (argc != 2) { + fprintf(stderr, "Use: %s \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