]> git.apps.os.sepia.ceph.com Git - xfstests-dev.git/commitdiff
generic: test fsync of file with no more hard links
authorFilipe Manana <fdmanana@suse.com>
Tue, 25 Mar 2025 12:24:40 +0000 (12:24 +0000)
committerZorro Lang <zlang@kernel.org>
Fri, 28 Mar 2025 01:05:05 +0000 (09:05 +0800)
Test that if we fsync a file that has no more hard links, power fail and
then mount the filesystem, after the journal/log is replayed, the file
doesn't exists anymore.

This exercises a bug recently found and fixed by the following patch for
the linux kernel:

   btrfs: fix fsync of files with no hard links not persisting deletion

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Zorro Lang <zlang@redhat.com>
Signed-off-by: Zorro Lang <zlang@kernel.org>
src/multi_open_unlink.c
tests/generic/764 [new file with mode: 0755]
tests/generic/764.out [new file with mode: 0644]

index c221d39e4022120447e6dfdf5256ca4616bb199b..fb054e872ca015922e256d75074a61b04ad99139 100644 (file)
@@ -28,7 +28,7 @@
 void
 usage(char *prog)
 {
-       fprintf(stderr, "Usage: %s [-e num-eas] [-f path_prefix] [-n num_files] [-s sleep_time] [-v ea-valuesize] \n", prog);
+       fprintf(stderr, "Usage: %s [-e num-eas] [-f path_prefix] [-F] [-n num_files] [-s sleep_time] [-S] [-v ea-valuesize] \n", prog);
        exit(1);
 }
 
@@ -44,8 +44,10 @@ main(int argc, char *argv[])
        int value_size = MAX_VALUELEN;
        int fd = -1;
        int i,j,c;
+       int fsync_files = 0;
+       int sync_fs = 0;
 
-       while ((c = getopt(argc, argv, "e:f:n:s:v:")) != EOF) {
+       while ((c = getopt(argc, argv, "e:f:Fn:s:Sv:")) != EOF) {
                switch (c) {
                        case 'e':   /* create eas */
                                num_eas = atoi(optarg);
@@ -53,12 +55,18 @@ main(int argc, char *argv[])
                        case 'f':   /* file prefix */
                                given_path = optarg;
                                break;
+                       case 'F':   /* fsync files after unlink */
+                               fsync_files = 1;
+                               break;
                        case 'n':   /* number of files */
                                num_files = atoi(optarg);
                                break;
                        case 's':   /* sleep time */
                                sleep_time = atoi(optarg);
                                break;
+                       case 'S':   /* sync fs after creating files */
+                               sync_fs = 1;
+                               break;
                        case 'v':  /* value size on eas */
                                value_size = atoi(optarg);
                                break;
@@ -83,6 +91,12 @@ main(int argc, char *argv[])
                        return 1;
                }
 
+               if (sync_fs && syncfs(fd) == -1) {
+                       fprintf(stderr, "%s: failed to sync filesystem: %s\n",
+                               prog, strerror(errno));
+                       return 1;
+               }
+
                /* set the EAs */
                for (j = 0; j < num_eas; j++) {
                        int sts;
@@ -111,6 +125,12 @@ main(int argc, char *argv[])
                                prog, path, strerror(errno));
                        return 1;
                }
+
+               if (fsync_files && fsync(fd) == -1) {
+                       fprintf(stderr, "%s: failed to fsync \"%s\": %s\n",
+                               prog, path, strerror(errno));
+                       return 1;
+               }
        }
 
        sleep(sleep_time);
diff --git a/tests/generic/764 b/tests/generic/764
new file mode 100755 (executable)
index 0000000..1b21bc0
--- /dev/null
@@ -0,0 +1,50 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 SUSE Linux Products GmbH.  All Rights Reserved.
+#
+# FS QA Test 764
+#
+# Test that if we fsync a file that has no more hard links, power fail and then
+# mount the filesystem, after the journal/log is replayed, the file doesn't
+# exists anymore.
+#
+. ./common/preamble
+_begin_fstest auto quick log
+
+_cleanup()
+{
+       _cleanup_flakey
+       cd /
+       rm -r -f $tmp.*
+}
+
+. ./common/dmflakey
+
+[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit xxxxxxxxxxxx \
+       "btrfs: fix fsync of files with no hard links not persisting deletion"
+
+_require_scratch
+_require_dm_target flakey
+_require_test_program "multi_open_unlink"
+
+_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+mkdir $SCRATCH_MNT/testdir
+$here/src/multi_open_unlink -f $SCRATCH_MNT/testdir/foo -F -S -n 1 -s 0
+
+# Simulate a power failure and then mount again the filesystem to replay the
+# journal/log.
+_flakey_drop_and_remount
+
+# We don't expect the file to exist anymore, since it was fsynced when it had no
+# more hard links.
+ls $SCRATCH_MNT/testdir
+
+_unmount_flakey
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/764.out b/tests/generic/764.out
new file mode 100644 (file)
index 0000000..bb58e5b
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 764
+Silence is golden