]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfstests-dev.git/commitdiff
generic: test fsync of file with 0 links and extents
authorFilipe Manana <fdmanana@suse.com>
Wed, 30 Jul 2025 19:21:41 +0000 (20:21 +0100)
committerZorro Lang <zlang@kernel.org>
Fri, 1 Aug 2025 19:51:14 +0000 (03:51 +0800)
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@suse.com>
Reviewed-by: Zorro Lang <zlang@redhat.com>
Signed-off-by: Zorro Lang <zlang@kernel.org>
.gitignore
src/Makefile
src/unlink-fsync.c [new file with mode: 0644]
tests/generic/771 [new file with mode: 0755]
tests/generic/771.out [new file with mode: 0644]

index 58dc2a63e0c122a430383e7d9d5eba46aca5750f..6948fd602f95878df4a9373472d0bec16728ca0c 100644 (file)
@@ -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
index 2cc1fb40d9f1aef5fdf0818a4a25cc67a9a11820..7080e34896c32720fcbf5bc62b6de89049eb03b1 100644 (file)
@@ -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 (file)
index 0000000..ce008c6
--- /dev/null
@@ -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 (executable)
index 0000000..ad30cc0
--- /dev/null
@@ -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 (file)
index 0000000..e40d709
--- /dev/null
@@ -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