Message ID | 9a9c54e662293fb01591c256dd32243b1a149fe2.1742905343.git.fdmanana@suse.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [v2] generic: test fsync of file with no more hard links | expand |
On Tue, Mar 25, 2025 at 12:24:40PM +0000, fdmanana@kernel.org wrote: > From: Filipe Manana <fdmanana@suse.com> > > 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> > --- > > V2: Use the src/multi_open_unlink.c program with added options to sync > filesystem after creating files and fsync files after the unlink. This version is good to me, Reviewed-by: Zorro Lang <zlang@redhat.com> If no more review points this week, I'll merge it this week. Thanks, Zorro > > src/multi_open_unlink.c | 24 ++++++++++++++++++-- > tests/generic/764 | 50 +++++++++++++++++++++++++++++++++++++++++ > tests/generic/764.out | 2 ++ > 3 files changed, 74 insertions(+), 2 deletions(-) > create mode 100755 tests/generic/764 > create mode 100644 tests/generic/764.out > > diff --git a/src/multi_open_unlink.c b/src/multi_open_unlink.c > index c221d39e..fb054e87 100644 > --- a/src/multi_open_unlink.c > +++ b/src/multi_open_unlink.c > @@ -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 > index 00000000..1b21bc02 > --- /dev/null > +++ b/tests/generic/764 > @@ -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 > index 00000000..bb58e5b8 > --- /dev/null > +++ b/tests/generic/764.out > @@ -0,0 +1,2 @@ > +QA output created by 764 > +Silence is golden > -- > 2.45.2 > >
diff --git a/src/multi_open_unlink.c b/src/multi_open_unlink.c index c221d39e..fb054e87 100644 --- a/src/multi_open_unlink.c +++ b/src/multi_open_unlink.c @@ -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 index 00000000..1b21bc02 --- /dev/null +++ b/tests/generic/764 @@ -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 index 00000000..bb58e5b8 --- /dev/null +++ b/tests/generic/764.out @@ -0,0 +1,2 @@ +QA output created by 764 +Silence is golden