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 \
--- /dev/null
+// 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;
+}
--- /dev/null
+#! /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