--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018 The University of Texas at Austin. All Rights Reserved.
+#
+# FS QA Test 520
+#
+# Test case created by CrashMonkey
+#
+# Test if we create a hard link to a file and persist either of the files, all
+# the names persist.
+#
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+ _cleanup_flakey
+ cd /
+ rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/dmflakey
+
+# 256MB in byte
+fssize=$((2**20 * 256))
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+_require_scratch_nocheck
+_require_dm_target flakey
+
+# initialize scratch device
+_scratch_mkfs_sized $fssize >> $seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+
+stat_opt='-c "blocks: %b size: %s inode: %i links: %h"'
+before=""
+after=""
+
+
+# Using _scratch_mkfs instead of cleaning up the working directory,
+# adds about 10 seconds of delay in total for the 37 tests.
+clean_dir()
+{
+ _mount_flakey
+ rm -rf $SCRATCH_MNT/*
+ sync
+ _unmount_flakey
+}
+
+check_consistency()
+{
+ _flakey_drop_and_remount
+
+ if [ -f $1 ]; then
+ after=`stat "$stat_opt" $1`
+ fi
+
+ if [ "$before" != "$after" ] && [ $2 -ne 1 ]; then
+ echo "Before: $before"
+ echo "After: $after"
+ fi
+
+ _unmount_flakey
+ _check_scratch_fs $FLAKEY_DEV
+}
+
+# create a hard link $2 to file $1, and fsync $3, followed by power-cut
+test_link_fsync()
+{
+ local sibling=0
+ local src=$SCRATCH_MNT/$1
+ local dest=$SCRATCH_MNT/$2
+ before=""
+ after=""
+
+ if [ "$3" == "./" ]; then
+ fsync=$SCRATCH_MNT
+ else
+ fsync=$SCRATCH_MNT/$3
+ fi
+
+ echo -ne "\n=== link $src $dest with fsync $fsync ===\n" | \
+ _filter_scratch
+ _mount_flakey
+
+ # Now execute the workload
+ # Create the directory in which the source and destination files
+ # will be created
+ mkdir -p "${src%/*}"
+ mkdir -p "${dest%/*}"
+ touch $src
+ ln $src $dest
+
+ # If the file being persisted is a sibling, create it first
+ if [ ! -f $fsync ]; then
+ sibling=1
+ touch $fsync
+ fi
+
+ $XFS_IO_PROG -c "fsync" $fsync
+
+ if [ $sibling -ne 1 ]; then
+ before=`stat "$stat_opt" $src`
+ fi
+
+ check_consistency $src $sibling
+ clean_dir
+}
+
+# create a hard link $2 to file $1, and sync, followed by power-cut
+test_link_sync()
+{
+ local src=$SCRATCH_MNT/$1
+ local dest=$SCRATCH_MNT/$2
+ before=""
+ after=""
+ echo -ne "\n=== link $src $dest with sync ===\n" | _filter_scratch
+ _mount_flakey
+
+ # now execute the workload
+ # Create the directory in which the source and destination files
+ # will be created
+ mkdir -p "${src%/*}"
+ mkdir -p "${dest%/*}"
+ touch $src
+ ln $src $dest
+ sync
+ before=`stat "$stat_opt" $src`
+
+ check_consistency $src 0
+ clean_dir
+}
+
+
+# Create different combinations to run the link test
+# Group 0: Both files within root directory
+file_names[0]="foo bar"
+fsync_names[0]="./ foo bar"
+
+# Group 1: Create hard link in a sub directory
+file_names[1]="foo A/bar"
+fsync_names[1]="./ foo bar A A/bar A/foo"
+
+# Group 2: Create hard link in parent directory
+file_names[2]="A/foo bar"
+fsync_names[2]="./ foo bar A A/bar A/foo"
+
+# Group 3: Both files within a directory other than root
+file_names[3]="A/foo A/bar"
+fsync_names[3]="./ A A/bar A/foo"
+
+#Group 4: Exercise name reuse : Link file in sub-directory
+file_names[4]="bar A/bar"
+fsync_names[4]="./ foo bar A A/bar A/foo"
+
+#Group 5: Exercise name reuse : Link file in parent directory
+file_names[5]="A/bar bar"
+fsync_names[5]="./ foo bar A A/bar A/foo"
+
+for ((test_group = 0; test_group < 6; test_group++)); do
+ for file in ${fsync_names[$test_group]}; do
+ test_link_fsync ${file_names[$test_group]} $file
+ done
+ test_link_sync ${file_names[$test_group]}
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 520
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/foo ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/bar ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/bar with sync ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/foo ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/bar ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/bar ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/foo ===
+
+=== link SCRATCH_MNT/foo SCRATCH_MNT/A/bar with sync ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/foo ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/bar ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/A ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/A/bar ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with fsync SCRATCH_MNT/A/foo ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/bar with sync ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/bar ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/foo ===
+
+=== link SCRATCH_MNT/A/foo SCRATCH_MNT/A/bar with sync ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/foo ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/bar ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/bar ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with fsync SCRATCH_MNT/A/foo ===
+
+=== link SCRATCH_MNT/bar SCRATCH_MNT/A/bar with sync ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT/foo ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT/bar ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT/A ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT/A/bar ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with fsync SCRATCH_MNT/A/foo ===
+
+=== link SCRATCH_MNT/A/bar SCRATCH_MNT/bar with sync ===