| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (C) 2021 SUSE Linux Products GmbH. All Rights Reserved. |
| # |
| # FSQA Test No. 640 |
| # |
| # Test that if we fsync a directory A, evict A's inode, move one file from |
| # directory A to a directory B, fsync some other inode that is not directory A, |
| # B or any inode inside these two directories, and then power fail, the file |
| # that was moved is not lost. |
| # |
| . ./common/preamble |
| _begin_fstest auto quick log |
| |
| # Override the default cleanup function. |
| _cleanup() |
| { |
| _cleanup_flakey |
| cd / |
| rm -f $tmp.* |
| } |
| |
| # Import common functions. |
| . ./common/filter |
| . ./common/dmflakey |
| |
| # real QA test starts here |
| _supported_fs generic |
| _require_scratch |
| _require_dm_target flakey |
| |
| _scratch_mkfs >>$seqres.full 2>&1 |
| _require_metadata_journaling $SCRATCH_DEV |
| _init_flakey |
| _mount_flakey |
| |
| # Create two test directories, one with a file we will rename later. |
| mkdir $SCRATCH_MNT/A |
| mkdir $SCRATCH_MNT/B |
| echo -n "hello world" > $SCRATCH_MNT/A/foo |
| |
| # Persist everything done so far. |
| sync |
| |
| # Add some new file to directory A and fsync the directory. |
| touch $SCRATCH_MNT/A/bar |
| $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/A |
| |
| # Now evict all inodes from memory. To trigger the original problem on btrfs we |
| # actually only needed to trigger eviction of A's inode, but there's no simple |
| # way to evict a single inode, so evict everything. |
| echo 2 > /proc/sys/vm/drop_caches |
| |
| # Now move file foo from directory A to directory B. |
| mv $SCRATCH_MNT/A/foo $SCRATCH_MNT/B/foo |
| |
| # Now make an fsync to anything except A, B or any file inside them, like for |
| # example create a file at the root directory and fsync this new file. |
| touch $SCRATCH_MNT/baz |
| $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/baz |
| |
| # Simulate a power failure and then check file foo still exists. |
| _flakey_drop_and_remount |
| |
| # Since file foo was not explicitly fsynced we can not guarantee that, for every |
| # filesystem, after replaying the journal/log we have file foo inside directory A |
| # or inside directory B. The file must exist however, and can only be found in |
| # one of the directories, not on both. |
| # |
| # At the moment of this writing, on f2fs file foo exists always at A/foo, |
| # regardless of the fsync-mode mount option ("-o fsync_mode=posix" or |
| # "-o fsync_mode=strict"). On ext4 and xfs it exists at B/foo. It is also |
| # supposed to exist at B/foo on btrfs (at the moment it doesn't exist in |
| # either directory due to a bug). |
| |
| foo_in_a=0 |
| foo_in_b=0 |
| |
| if [ -f $SCRATCH_MNT/A/foo ]; then |
| echo "File foo data: $(cat $SCRATCH_MNT/A/foo)" |
| foo_in_a=1 |
| fi |
| |
| if [ -f $SCRATCH_MNT/B/foo ]; then |
| echo "File foo data: $(cat $SCRATCH_MNT/B/foo)" |
| foo_in_b=1 |
| fi |
| |
| if [ $foo_in_a -eq 1 ] && [ $foo_in_b -eq 1 ]; then |
| echo "File foo found in A/ and B/" |
| elif [ $foo_in_a -eq 0 ] && [ $foo_in_b -eq 0 ]; then |
| echo "File foo is missing" |
| fi |
| |
| # While here, also check that files bar and baz exist. |
| [ -f $SCRATCH_MNT/A/bar ] || echo "File A/bar is missing" |
| [ -f $SCRATCH_MNT/baz ] || echo "File baz is missing" |
| |
| _unmount_flakey |
| status=0 |
| exit |