diff mbox series

generic: test fsync of file with no more hard links

Message ID 3476019b76d6df3ce6eb364aeb1a2725b8fb4846.1742555101.git.fdmanana@suse.com (mailing list archive)
State New
Headers show
Series generic: test fsync of file with no more hard links | expand

Commit Message

Filipe Manana March 21, 2025, 11:09 a.m. UTC
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>
---
 tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
 tests/generic/764.out |  2 ++
 2 files changed, 80 insertions(+)
 create mode 100755 tests/generic/764
 create mode 100644 tests/generic/764.out

Comments

Darrick J. Wong March 21, 2025, 3:40 p.m. UTC | #1
On Fri, Mar 21, 2025 at 11:09:58AM +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>
> ---
>  tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/764.out |  2 ++
>  2 files changed, 80 insertions(+)
>  create mode 100755 tests/generic/764
>  create mode 100644 tests/generic/764.out
> 
> diff --git a/tests/generic/764 b/tests/generic/764
> new file mode 100755
> index 00000000..57d21095
> --- /dev/null
> +++ b/tests/generic/764
> @@ -0,0 +1,78 @@
> +#! /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()
> +{
> +	if [ ! -z $XFS_IO_PID ]; then
> +		kill $XFS_IO_PID > /dev/null 2>&1
> +		wait $XFS_IO_PID > /dev/null 2>&1
> +	fi
> +	_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_mknod
> +
> +_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
> +_require_metadata_journaling $SCRATCH_DEV
> +_init_flakey
> +_mount_flakey
> +
> +touch $SCRATCH_MNT/foo
> +
> +# Commit the current transaction and persist the file.
> +_scratch_sync
> +
> +# A fifo to communicate with a background xfs_io process that will fsync the
> +# file after we deleted its hard link while it's open by xfs_io.
> +mkfifo $SCRATCH_MNT/fifo
> +
> +tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &
> +XFS_IO_PID=$!
> +
> +# Give some time for the xfs_io process to open a file descriptor for the file.
> +sleep 1
> +
> +# Now while the file is open by the xfs_io process, delete its only hard link.
> +rm -f $SCRATCH_MNT/foo
> +
> +# Now that it has no more hard links, make the xfs_io process fsync it.
> +echo "fsync" > $SCRATCH_MNT/fifo
> +
> +# Terminate the xfs_io process so that we can unmount.
> +echo "quit" > $SCRATCH_MNT/fifo

Hee hee, what a way to drive xfs_io ;)

Seems reasonable to me
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> +wait $XFS_IO_PID
> +unset XFS_IO_PID
> +
> +# 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.
> +[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"
> +
> +_unmount_flakey
> +
> +# success, all done
> +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
> 
>
Daniel Vacek March 21, 2025, 11:21 p.m. UTC | #2
On Fri, 21 Mar 2025 at 12:10, <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>
> ---
>  tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/764.out |  2 ++
>  2 files changed, 80 insertions(+)
>  create mode 100755 tests/generic/764
>  create mode 100644 tests/generic/764.out
>
> diff --git a/tests/generic/764 b/tests/generic/764
> new file mode 100755
> index 00000000..57d21095
> --- /dev/null
> +++ b/tests/generic/764
> @@ -0,0 +1,78 @@
> +#! /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()
> +{
> +       if [ ! -z $XFS_IO_PID ]; then
> +               kill $XFS_IO_PID > /dev/null 2>&1
> +               wait $XFS_IO_PID > /dev/null 2>&1
> +       fi
> +       _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_mknod
> +
> +_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
> +_require_metadata_journaling $SCRATCH_DEV
> +_init_flakey
> +_mount_flakey
> +
> +touch $SCRATCH_MNT/foo
> +
> +# Commit the current transaction and persist the file.
> +_scratch_sync
> +
> +# A fifo to communicate with a background xfs_io process that will fsync the
> +# file after we deleted its hard link while it's open by xfs_io.
> +mkfifo $SCRATCH_MNT/fifo

After creating the pipe you can "exec 3<>$SCRATCH_MNT/fifo" (and
eventually unlink) ...

> +
> +tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &

... and then simply "$XFS_IO_PROG $SCRATCH_MNT/foo <&3 >>$seqres.full &"

> +XFS_IO_PID=$!
> +
> +# Give some time for the xfs_io process to open a file descriptor for the file.
> +sleep 1
> +
> +# Now while the file is open by the xfs_io process, delete its only hard link.
> +rm -f $SCRATCH_MNT/foo
> +
> +# Now that it has no more hard links, make the xfs_io process fsync it.
> +echo "fsync" > $SCRATCH_MNT/fifo

No need for the quotes. But won't hurt either if that's more readable for you.

Moreover with the above you can also "echo fsync >&3".

> +
> +# Terminate the xfs_io process so that we can unmount.
> +echo "quit" > $SCRATCH_MNT/fifo

...

> +wait $XFS_IO_PID
> +unset XFS_IO_PID
> +
> +# 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.
> +[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"
> +
> +_unmount_flakey
> +
> +# success, all done
> +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
>
>
Filipe Manana March 22, 2025, 8:40 p.m. UTC | #3
On Fri, Mar 21, 2025 at 11:22 PM Daniel Vacek <neelx@suse.com> wrote:
>
> On Fri, 21 Mar 2025 at 12:10, <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>
> > ---
> >  tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
> >  tests/generic/764.out |  2 ++
> >  2 files changed, 80 insertions(+)
> >  create mode 100755 tests/generic/764
> >  create mode 100644 tests/generic/764.out
> >
> > diff --git a/tests/generic/764 b/tests/generic/764
> > new file mode 100755
> > index 00000000..57d21095
> > --- /dev/null
> > +++ b/tests/generic/764
> > @@ -0,0 +1,78 @@
> > +#! /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()
> > +{
> > +       if [ ! -z $XFS_IO_PID ]; then
> > +               kill $XFS_IO_PID > /dev/null 2>&1
> > +               wait $XFS_IO_PID > /dev/null 2>&1
> > +       fi
> > +       _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_mknod
> > +
> > +_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
> > +_require_metadata_journaling $SCRATCH_DEV
> > +_init_flakey
> > +_mount_flakey
> > +
> > +touch $SCRATCH_MNT/foo
> > +
> > +# Commit the current transaction and persist the file.
> > +_scratch_sync
> > +
> > +# A fifo to communicate with a background xfs_io process that will fsync the
> > +# file after we deleted its hard link while it's open by xfs_io.
> > +mkfifo $SCRATCH_MNT/fifo
>
> After creating the pipe you can "exec 3<>$SCRATCH_MNT/fifo" (and
> eventually unlink) ...

Yes, I'm aware of exec.
Have used it in other tests written years ago, like btrfs/168,
btrfs/233, and others.

>
> > +
> > +tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &
>
> ... and then simply "$XFS_IO_PROG $SCRATCH_MNT/foo <&3 >>$seqres.full &"

That's arguably more cryptic and not so easy to read.

The approach I chose was exactly due to being a lot simpler to read
and less cryptic.

The goal is to make the test as easy to read as possible, and not to
show off technical skills with bash.

>
> > +XFS_IO_PID=$!
> > +
> > +# Give some time for the xfs_io process to open a file descriptor for the file.
> > +sleep 1
> > +
> > +# Now while the file is open by the xfs_io process, delete its only hard link.
> > +rm -f $SCRATCH_MNT/foo
> > +
> > +# Now that it has no more hard links, make the xfs_io process fsync it.
> > +echo "fsync" > $SCRATCH_MNT/fifo
>
> No need for the quotes. But won't hurt either if that's more readable for you.

Yes, I'm aware they're not needed. I like to use them however, for the
syntax highlighting I get with my editor.

>
> Moreover with the above you can also "echo fsync >&3".

Yes.
Again, I prefer the much more straightforward to read approach of
explicitly redirecting to the fifo.

Thanks.

>
> > +
> > +# Terminate the xfs_io process so that we can unmount.
> > +echo "quit" > $SCRATCH_MNT/fifo
>
> ...
>
> > +wait $XFS_IO_PID
> > +unset XFS_IO_PID
> > +
> > +# 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.
> > +[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"
> > +
> > +_unmount_flakey
> > +
> > +# success, all done
> > +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
> >
> >
Dave Chinner March 24, 2025, 9:38 p.m. UTC | #4
On Fri, Mar 21, 2025 at 11:09:58AM +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>
> ---
>  tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/764.out |  2 ++
>  2 files changed, 80 insertions(+)
>  create mode 100755 tests/generic/764
>  create mode 100644 tests/generic/764.out
> 
> diff --git a/tests/generic/764 b/tests/generic/764
> new file mode 100755
> index 00000000..57d21095
> --- /dev/null
> +++ b/tests/generic/764
> @@ -0,0 +1,78 @@
> +#! /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()
> +{
> +	if [ ! -z $XFS_IO_PID ]; then

	if [ -n "$XFS_IO_PID" ]; then

> +		kill $XFS_IO_PID > /dev/null 2>&1
> +		wait $XFS_IO_PID > /dev/null 2>&1
> +	fi
> +	_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_mknod
> +
> +_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
> +_require_metadata_journaling $SCRATCH_DEV
> +_init_flakey
> +_mount_flakey
> +
> +touch $SCRATCH_MNT/foo
> +
> +# Commit the current transaction and persist the file.
> +_scratch_sync
> +
> +# A fifo to communicate with a background xfs_io process that will fsync the
> +# file after we deleted its hard link while it's open by xfs_io.
> +mkfifo $SCRATCH_MNT/fifo
> +
> +tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &
> +XFS_IO_PID=$!
> +
> +# Give some time for the xfs_io process to open a file descriptor for the file.
> +sleep 1
> +
> +# Now while the file is open by the xfs_io process, delete its only hard link.
> +rm -f $SCRATCH_MNT/foo
> +
> +# Now that it has no more hard links, make the xfs_io process fsync it.
> +echo "fsync" > $SCRATCH_MNT/fifo
> +
> +# Terminate the xfs_io process so that we can unmount.
> +echo "quit" > $SCRATCH_MNT/fifo
> +wait $XFS_IO_PID
> +unset XFS_IO_PID

So this is effectively src/multi_open_unlink.c with an added fsync
after the unlink() call.

Wouldn't it be better to add a "-F" option to multi_open_unlink.c to
tell it to fsync after the unlink to src/multi_open_unlink.c, and
then the test becomes a lot simpler

> +# 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.
> +[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"

`ls $SCRATCH_MNT` and let the golden image match fail because
there's output when the file still exists. i.e. the ls command
should be silent if the test passes.

> +_unmount_flakey
> +
> +# success, all done
> +echo "Silence is golden"
> +status=0
> +exit

OK, so this seems to me that the test could be written as:

	src/multi_open_unlink -F -f $SCRATCH_MNT -s 0 -n 1
	_flakey_drop_and_remount

	ls $SCRATCH_MNT
	_unmount_flakey

	echo "Silence is golden"
	status=0
	exit

if we add a few lines of code to add fsync support to
src/multi_open_unlink.c...

-Dave.
Filipe Manana March 25, 2025, 11:52 a.m. UTC | #5
On Mon, Mar 24, 2025 at 9:38 PM Dave Chinner <david@fromorbit.com> wrote:
>
> On Fri, Mar 21, 2025 at 11:09:58AM +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>
> > ---
> >  tests/generic/764     | 78 +++++++++++++++++++++++++++++++++++++++++++
> >  tests/generic/764.out |  2 ++
> >  2 files changed, 80 insertions(+)
> >  create mode 100755 tests/generic/764
> >  create mode 100644 tests/generic/764.out
> >
> > diff --git a/tests/generic/764 b/tests/generic/764
> > new file mode 100755
> > index 00000000..57d21095
> > --- /dev/null
> > +++ b/tests/generic/764
> > @@ -0,0 +1,78 @@
> > +#! /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()
> > +{
> > +     if [ ! -z $XFS_IO_PID ]; then
>
>         if [ -n "$XFS_IO_PID" ]; then
>
> > +             kill $XFS_IO_PID > /dev/null 2>&1
> > +             wait $XFS_IO_PID > /dev/null 2>&1
> > +     fi
> > +     _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_mknod
> > +
> > +_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
> > +_require_metadata_journaling $SCRATCH_DEV
> > +_init_flakey
> > +_mount_flakey
> > +
> > +touch $SCRATCH_MNT/foo
> > +
> > +# Commit the current transaction and persist the file.
> > +_scratch_sync
> > +
> > +# A fifo to communicate with a background xfs_io process that will fsync the
> > +# file after we deleted its hard link while it's open by xfs_io.
> > +mkfifo $SCRATCH_MNT/fifo
> > +
> > +tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &
> > +XFS_IO_PID=$!
> > +
> > +# Give some time for the xfs_io process to open a file descriptor for the file.
> > +sleep 1
> > +
> > +# Now while the file is open by the xfs_io process, delete its only hard link.
> > +rm -f $SCRATCH_MNT/foo
> > +
> > +# Now that it has no more hard links, make the xfs_io process fsync it.
> > +echo "fsync" > $SCRATCH_MNT/fifo
> > +
> > +# Terminate the xfs_io process so that we can unmount.
> > +echo "quit" > $SCRATCH_MNT/fifo
> > +wait $XFS_IO_PID
> > +unset XFS_IO_PID
>
> So this is effectively src/multi_open_unlink.c with an added fsync
> after the unlink() call.

No, it's not just an extra fsync.

>
> Wouldn't it be better to add a "-F" option to multi_open_unlink.c to
> tell it to fsync after the unlink to src/multi_open_unlink.c, and
> then the test becomes a lot simpler

multi_open_unlink creates the files and does the unlink in the same
"transaction" - without any sync in between.

The goal here is to verify that a previously persisted file doesn't
exist after the unlink + fsync.

>
> > +# 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.
> > +[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"
>
> `ls $SCRATCH_MNT` and let the golden image match fail because
> there's output when the file still exists. i.e. the ls command
> should be silent if the test passes.

We've been through this in past tests.
The reason why not using ls and match against the golden output is
because on ext4 we'll get the 'lost+found' directory, so would have to
do something like:

ls $SCRATCH_MNT/ | grep -v 'lost+found'

And maybe other filesystems may have their own similar directory but
with other names (I'm not aware of any however).
That's why it's explicitly testing if the file exists.

>
> > +_unmount_flakey
> > +
> > +# success, all done
> > +echo "Silence is golden"
> > +status=0
> > +exit
>
> OK, so this seems to me that the test could be written as:
>
>         src/multi_open_unlink -F -f $SCRATCH_MNT -s 0 -n 1
>         _flakey_drop_and_remount
>
>         ls $SCRATCH_MNT
>         _unmount_flakey
>
>         echo "Silence is golden"
>         status=0
>         exit
>
> if we add a few lines of code to add fsync support to
> src/multi_open_unlink.c...

Nop, not without a 'sync' after it creates the files too.
But that may be way too specific and add yet another flag to the program.

Thanks.

>
> -Dave.
> --
> Dave Chinner
> david@fromorbit.com
diff mbox series

Patch

diff --git a/tests/generic/764 b/tests/generic/764
new file mode 100755
index 00000000..57d21095
--- /dev/null
+++ b/tests/generic/764
@@ -0,0 +1,78 @@ 
+#! /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()
+{
+	if [ ! -z $XFS_IO_PID ]; then
+		kill $XFS_IO_PID > /dev/null 2>&1
+		wait $XFS_IO_PID > /dev/null 2>&1
+	fi
+	_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_mknod
+
+_scratch_mkfs >>$seqres.full 2>&1 || _fail "mkfs failed"
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+touch $SCRATCH_MNT/foo
+
+# Commit the current transaction and persist the file.
+_scratch_sync
+
+# A fifo to communicate with a background xfs_io process that will fsync the
+# file after we deleted its hard link while it's open by xfs_io.
+mkfifo $SCRATCH_MNT/fifo
+
+tail -f $SCRATCH_MNT/fifo | $XFS_IO_PROG $SCRATCH_MNT/foo >>$seqres.full &
+XFS_IO_PID=$!
+
+# Give some time for the xfs_io process to open a file descriptor for the file.
+sleep 1
+
+# Now while the file is open by the xfs_io process, delete its only hard link.
+rm -f $SCRATCH_MNT/foo
+
+# Now that it has no more hard links, make the xfs_io process fsync it.
+echo "fsync" > $SCRATCH_MNT/fifo
+
+# Terminate the xfs_io process so that we can unmount.
+echo "quit" > $SCRATCH_MNT/fifo
+wait $XFS_IO_PID
+unset XFS_IO_PID
+
+# 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.
+[ -f $SCRATCH_MNT/foo ] && echo "file foo still exists"
+
+_unmount_flakey
+
+# success, all done
+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