| #! /bin/bash |
| # FS QA Test No. 065 |
| # |
| # Test fsync on directories that got new hardlinks added to them and that point |
| # to existing inodes. The goal is to verify that after the fsync log is replayed |
| # the new hardlinks exist and the inodes have a correct link count. |
| # Also test that new hardlinks pointing to new inodes are logged and exist as |
| # well after the fsync log is replayed. |
| # |
| # This test is motivated by an issue discovered in btrfs, where the inode link |
| # counts were incorrect after the fsync log was replayed and the hardlinks for |
| # new inodes were not logged. |
| # |
| #----------------------------------------------------------------------- |
| # Copyright (C) 2015 SUSE Linux Products GmbH. All Rights Reserved. |
| # Author: Filipe Manana <fdmanana@suse.com> |
| # |
| # This program is free software; you can redistribute it and/or |
| # modify it under the terms of the GNU General Public License as |
| # published by the Free Software Foundation. |
| # |
| # This program is distributed in the hope that it would be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write the Free Software Foundation, |
| # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| #----------------------------------------------------------------------- |
| # |
| |
| seq=`basename $0` |
| seqres=$RESULT_DIR/$seq |
| echo "QA output created by $seq" |
| |
| here=`pwd` |
| tmp=/tmp/$$ |
| status=1 # failure is the default! |
| |
| _cleanup() |
| { |
| _cleanup_flakey |
| rm -f $tmp.* |
| } |
| trap "_cleanup; exit \$status" 0 1 2 3 15 |
| |
| # get standard environment, filters and checks |
| . ./common/rc |
| . ./common/filter |
| . ./common/dmflakey |
| |
| # real QA test starts here |
| _supported_fs generic |
| _supported_os Linux |
| _require_scratch |
| _require_dm_target flakey |
| |
| rm -f $seqres.full |
| |
| _scratch_mkfs >> $seqres.full 2>&1 |
| _require_metadata_journaling $SCRATCH_DEV |
| _init_flakey |
| _mount_flakey |
| |
| # Create our main test file and directory. |
| $XFS_IO_PROG -f -c "pwrite -S 0xaa 0 8K" $SCRATCH_MNT/foo | _filter_xfs_io |
| mkdir $SCRATCH_MNT/mydir |
| |
| # Make sure all metadata and data are durably persisted. |
| sync |
| |
| # Add a hard link to 'foo' inside our test directory and fsync only the |
| # directory. The btrfs fsync implementation had a bug that caused the new |
| # directory entry to be visible after the fsync log replay but, the inode |
| # of our file remained with a link count of 1. |
| ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/foo_2 |
| |
| # Add a few more links and new files. |
| # This is just to verify nothing breaks or gives incorrect results after the |
| # fsync log is replayed. |
| ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/foo_3 |
| $XFS_IO_PROG -f -c "pwrite -S 0xff 0 64K" $SCRATCH_MNT/hello | _filter_xfs_io |
| ln $SCRATCH_MNT/hello $SCRATCH_MNT/mydir/hello_2 |
| |
| # Add some subdirectories and new files and links to them. This is to verify |
| # that after fsyncing our top level directory 'mydir', all the subdirectories |
| # and their files/links are registered in the fsync log and exist after the |
| # fsync log is replayed. |
| mkdir -p $SCRATCH_MNT/mydir/x/y/z |
| ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/x/y/foo_y_link |
| ln $SCRATCH_MNT/foo $SCRATCH_MNT/mydir/x/y/z/foo_z_link |
| touch $SCRATCH_MNT/mydir/x/y/z/qwerty |
| |
| # Now fsync only our top directory. |
| $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/mydir |
| |
| # And fsync now our new file named 'hello', just to verify later that it has |
| # the expected content and that the previous fsync on the directory 'mydir' had |
| # no bad influence on this fsync. |
| $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/hello |
| |
| _flakey_drop_and_remount |
| |
| # Verify the content of our file 'foo' remains the same as before, 8192 bytes, |
| # all with the value 0xaa. |
| echo "File 'foo' content after log replay:" |
| od -t x1 $SCRATCH_MNT/foo |
| |
| # Remove the first name of our inode. Because of the directory fsync bug, the |
| # inode's link count was 1 instead of 5, so removing the 'foo' name ended up |
| # deleting the inode and the other names became stale directory entries (still |
| # visible to applications). Attempting to remove or access the remaining |
| # dentries pointing to that inode resulted in stale file handle errors and |
| # made it impossible to remove the parent directories since it was impossible |
| # for them to become empty. |
| echo "file 'foo' link count after log replay: $(stat -c %h $SCRATCH_MNT/foo)" |
| rm -f $SCRATCH_MNT/foo |
| |
| # Now verify that all files, links and directories created before fsyncing our |
| # directory exist after the fsync log was replayed. |
| [ -f $SCRATCH_MNT/mydir/foo_2 ] || echo "Link mydir/foo_2 is missing" |
| [ -f $SCRATCH_MNT/mydir/foo_3 ] || echo "Link mydir/foo_3 is missing" |
| [ -f $SCRATCH_MNT/hello ] || echo "File hello is missing" |
| [ -f $SCRATCH_MNT/mydir/hello_2 ] || echo "Link mydir/hello_2 is missing" |
| [ -f $SCRATCH_MNT/mydir/x/y/foo_y_link ] || \ |
| echo "Link mydir/x/y/foo_y_link is missing" |
| [ -f $SCRATCH_MNT/mydir/x/y/z/foo_z_link ] || \ |
| echo "Link mydir/x/y/z/foo_z_link is missing" |
| [ -f $SCRATCH_MNT/mydir/x/y/z/qwerty ] || \ |
| echo "File mydir/x/y/z/qwerty is missing" |
| |
| # We expect our file here to have a size of 64Kb and all the bytes having the |
| # value 0xff. |
| echo "file 'hello' content after log replay:" |
| od -t x1 $SCRATCH_MNT/hello |
| |
| # Now remove all files/links, under our test directory 'mydir', and verify we |
| # can remove all the directories. |
| rm -f $SCRATCH_MNT/mydir/x/y/z/* |
| rmdir $SCRATCH_MNT/mydir/x/y/z |
| rm -f $SCRATCH_MNT/mydir/x/y/* |
| rmdir $SCRATCH_MNT/mydir/x/y |
| rmdir $SCRATCH_MNT/mydir/x |
| rm -f $SCRATCH_MNT/mydir/* |
| rmdir $SCRATCH_MNT/mydir |
| |
| # An fsck, run by the fstests framework everytime a test finishes, also detected |
| # the inconsistency and printed the following error message: |
| # |
| # root 5 inode 257 errors 2001, no inode item, link count wrong |
| # unresolved ref dir 258 index 2 namelen 5 name foo_2 filetype 1 errors 4, no inode ref |
| # unresolved ref dir 258 index 3 namelen 5 name foo_3 filetype 1 errors 4, no inode ref |
| |
| status=0 |
| exit |