#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2017 CTERA Networks. All Rights Reserved.
#
# FS QA Test 042
#
# Test creating lower hardlinks for copied up files.
#
# kernel v4.13 introduced the index=on feature for not breaking hardlinks
# on copy-up.  With the index feature enabled a regression was introduced -
# lower files that are hardlined while overlay is offline can result in
# lookup error after overlay in mounted.
#
# The regression was fixed by upstream commit
#   6eaf011144af ovl: fix EIO from lookup of non-indexed upper
# that was merged to v4.14-rc7.
#
# This test verifies that behavior is sane after creating lower hardlinks
# for copied up files while overlayfs is offline.
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"

tmp=/tmp/$$
status=1	# failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15

_cleanup()
{
	cd /
	rm -f $tmp.*
}

# get standard environment, filters and checks
. ./common/rc
. ./common/filter

# remove previous $seqres.full before test
rm -f $seqres.full

# real QA test starts here
_supported_fs overlay
_supported_os Linux
_require_scratch
# Without overlay index feature hardlinks are broken on copy up
_require_scratch_feature index

# Remove all files from previous tests
_scratch_mkfs

# Create lower file
lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
mkdir -p $lowerdir
touch $lowerdir/0

# Disable overlay index feature to copy up with no index
_scratch_mount -o index=off

# Copy up lower and create upper hardlink with no index
ln $SCRATCH_MNT/0 $SCRATCH_MNT/1

$UMOUNT_PROG $SCRATCH_MNT

# Add lower hardlinks while overlay is offline
ln $lowerdir/0 $lowerdir/2
ln $lowerdir/0 $lowerdir/3

# Enable overlay index feature to lookup copied up upper aliases
_scratch_mount -o index=on

# Stat upper hardlink that were created with no index and whose copy up
# origin is now an hardlink - expect same st_ino
ino0=$(stat -c '%i' $SCRATCH_MNT/0)
ino1=$(stat -c '%i' $SCRATCH_MNT/1)
[[ $ino0 == $ino1 ]] || \
	echo "Mismatch inode number for non-indexed upper hardlinks"

# Copy up lower and create new upper hardlink with index, because
# lower is a hardlink and index is enabled. The new hardlinks are not
# associated with the hardlinks that were created when lower was not
# a hardlink.
ln $SCRATCH_MNT/2 $SCRATCH_MNT/4

# Mount cycle to reset inode/dentry cache
_scratch_cycle_mount index=on

# Stat files that were copied up with index and whose copy up origin
# is now an hardlink - expect same st_ino as lower aliases and different
# st_ino from non-indexed upper aliases.
ino0=$(stat -c '%i' $SCRATCH_MNT/0)
ino1=$(stat -c '%i' $SCRATCH_MNT/1)
ino2=$(stat -c '%i' $SCRATCH_MNT/2)
ino3=$(stat -c '%i' $SCRATCH_MNT/3)
ino4=$(stat -c '%i' $SCRATCH_MNT/4)
[[ $ino0 == $ino1 ]] || \
	echo "Mismatch inode number for non-indexed upper hardlinks"
[[ $ino2 == $ino4 ]] || \
	echo "Mismatch inode number for indexed upper hardlinks"
[[ $ino2 == $ino3 ]] || \
	echo "Mismatch inode number for indexed upper and lower hardlinks"
[[ $ino2 != $ino0 ]] || \
	echo "Unexpected matching inode number for indexed and non-indexed upper hardlinks"

# Mount cycle to reset inode/dentry cache
_scratch_cycle_mount index=on

# Unlink all hardlinks - if overlay inode nlink is 0, this will trigger
# WARN_ON() in drop_nlink()
rm $SCRATCH_MNT/0
rm $SCRATCH_MNT/1
rm $SCRATCH_MNT/2
rm $SCRATCH_MNT/3
rm $SCRATCH_MNT/4

echo "Silence is golden"
status=0
exit
