#! /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.
#
. ./common/preamble
_begin_fstest auto quick copyup hardlink

# Import common functions.
. ./common/filter

# real QA test starts here
_supported_fs overlay
_fixed_by_kernel_commit 6eaf011144af \
	"ovl: fix EIO from lookup of non-indexed upper"

_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
