#!/bin/sh

# NOTE: The test device size is 16 GB

TEST_DEV="/dev/md0"
TEST_MP="/mnt"

remount_fs_expected_clean() # ${1} => OPTOPNAL_COMMENT
{
	echo "Unmount, check results: ${1}"
	umount ${TEST_DEV}
	e2fsck -f -n ${TEST_DEV}
	if [ "$?" -ne "0" ]
	then
		echo "remount_fs_expected_clean() ERROR: ${1}"
		exit 1
	fi

	mount -t ext2fs ${TEST_DEV} ${TEST_MP}
}

# Expeced e2fsck error example:

# e2fsck 1.43.4 (31-Jan-2017)
# Pass 1: Checking inodes, blocks, and sizes
# Pass 2: Checking directory structure
# Pass 3: Checking directory connectivity
# Pass 4: Checking reference counts
# Inode 16385 ref count is 1, should be 58893.  Fix? no

# Pass 5: Checking group summary information

# /dev/md0: ********** WARNING: Filesystem still has errors **********

# /dev/md0: 58903/1048576 files (0.0% non-contiguous), 169329/4194304 blocks
 

remount_fs_expexted_bad_refcount() # ${1} => OPTOPNAL_COMMENT
{
	echo "Unmount, check results: ${1}"
	umount ${TEST_DEV}
	E2FSCK_OUT=$(e2fsck -f -n ${TEST_DEV})

	echo $E2FSCK_OUT

	FIX_COUNT=$(echo ${E2FSCK_OUT} | grep 'Fix? no' | wc -l)
	REF_COUNT_ERR_COUNT=$(echo ${E2FSCK_OUT} | grep 'ref count is 1, should be' | wc -l)

	if [ "${FIX_COUNT}" -eq "0" ]
	then
		echo "remount_fs_expexted_bad_refcount() :We are expecting errors here => ERROR: ${1}"
		exit 1

	fi

	if [ "${FIX_COUNT}" -ne "${REF_COUNT_ERR_COUNT}" ]
	then
		echo "remount_fs_expexted_bad_refcount() ERROR: ${1}"
		exit 1
	fi

	mount -t ext2fs ${TEST_DEV} ${TEST_MP}
}



test_hardlinks_count_with_feature()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_nlink ${TEST_DEV}
	dumpe2fs ${TEST_DEV} | head -50
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Create only MAX number hardlinks to file"

	touch ${TEST_MP}/TEST_FILE
	i=0
	while [ 1 ]
	do
		ln ${TEST_MP}/TEST_FILE ${TEST_MP}/TEST_FILE_${i}
		if [ "$?" -ne "0" ]
		then
			break
		fi
		i=$((i+1))
	done

	if [ "${i}" -gt "65000" ];
	then
		echo "ERROR: i=${i}"
		exit 1
	fi

	echo "Unmount, check results"
	umount ${TEST_DEV}
	e2fsck -f -n ${TEST_DEV}
	if [ "$?" -ne "0" ]
	then
		echo "fsck ERROR"
		exit 1
	fi
}

mkdir_overfilled_no_dir_nlink() # ${1} DIR_PATH
{
	mkdir ${1}
	
	i=0
	while [ "${i}" -le "70000" ]
	do
		mkdir ${1}/TEST_DIR_${i}
		if [ "$?" -ne "0" ]
		then
			break
		fi
		i=$((i+1))
	done

	if [ "${i}" -gt "65000" ];
	then
		echo "ERROR: in mkdir_filled_no_dir_nlink(): i=${i}"
		exit 1
	fi
}

test_dirs_count_no_dir_nlink_option()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O ^dir_nlink -O ^dir_index ${TEST_DEV}
	dumpe2fs ${TEST_DEV} | head -50
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Check MAX number of links in directory without feature"

	mkdir_overfilled_no_dir_nlink "${TEST_MP}/TEST_DIR_0"

	echo "Unmount, check results"
	umount ${TEST_DEV}
	e2fsck -f -n ${TEST_DEV}
	if [ "$?" -ne "0" ]
	then
		echo "fsck ERROR"
		exit 1
	fi
}

mkdir_overfilled_with_dir_nlink() # ${1} DIR_PATH
{
	mkdir ${1}
	
	i=0
	while [ "${i}" -le "70000" ]
	do
		mkdir ${1}/TEST_DIR_${i}
		if [ "$?" -ne "0" ]
		then
			echo "ERROR: in mkdir_overfilled_with_dir_nlink(): i=${i}"
			exit 1
		fi
		i=$((i+1))
	done
}

rmdir_overfilled_partially() # ${1} DIR_PATH
{
	rm -r -f ${1}/TEST_DIR_60*
	rm -r -f ${1}/TEST_DIR_61*
	rm -r -f ${1}/TEST_DIR_62*
	rm -r -f ${1}/TEST_DIR_63*
	rm -r -f ${1}/TEST_DIR_64*
	rm -r -f ${1}/TEST_DIR_65*
	rm -r -f ${1}/TEST_DIR_66*
	rm -r -f ${1}/TEST_DIR_67*
	rm -r -f ${1}/TEST_DIR_68*
	rm -r -f ${1}/TEST_DIR_69*
}

test_dirs_count_with_dir_nlink_option()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_nlink ${TEST_DEV}
	dumpe2fs ${TEST_DEV} | head -50
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Create only MAX + some number of dirs in directory with feature"

	mkdir_overfilled_with_dir_nlink "${TEST_MP}/TEST_DIR_0"

	remount_fs_expected_clean

	echo "Delete part of dirs inside the dir, then verify again"
	rmdir_overfilled_partially "${TEST_MP}/TEST_DIR_0"

	remount_fs_expexted_bad_refcount

	umount ${TEST_DEV}
}

test_dirs_count_with_dir_index_no_dir_nlink_options()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_index -O ^dir_nlink ${TEST_DEV}
	dumpe2fs ${TEST_DEV} | head -50
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Create only MAX + some number of dirs in directory with dir_index bit without dir_nlink"

	mkdir_overfilled_with_dir_nlink "${TEST_MP}/TEST_DIR_0"

	remount_fs_expected_clean

	echo "Delete part of dirs inside the dir, then verify again"
	rmdir_overfilled_partially "${TEST_MP}/TEST_DIR_0"

	remount_fs_expexted_bad_refcount

	umount ${TEST_DEV}
}


test_different_mkdir_rmdir_cases_without_dir_nlink()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O ^dir_nlink -O ^dir_index ${TEST_DEV}
	dumpe2fs ${TEST_DEV} | head -50
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Check different mkdir/rmdir cases without dir_nlink"

	mkdir_overfilled_no_dir_nlink "${TEST_MP}/DIR0"
	mkdir_overfilled_no_dir_nlink "${TEST_MP}/DIR1"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_overfilled_no_dir_nlink "${TEST_MP}/DIR1/DIR2"

	remount_fs_expected_clean

	rm -r -f "${TEST_MP}/DIR1/DIR2/*"

	remount_fs_expected_clean

	mv "${TEST_MP}/DIR0" "${TEST_MP}/DIR1/DIR2"

	remount_fs_expected_clean

	rm -r -f "${TEST_MP}/DIR1"

	echo "Unmount, check results"
	umount ${TEST_DEV}
	e2fsck -f -n ${TEST_DEV}
	if [ "$?" -ne "0" ]
	then
		echo "fsck ERROR"
		exit 1
	fi
}

test_different_mkdir_rmdir_cases_with_dir_nlink()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_nlink ${TEST_DEV}
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Check different mkdir/rmdir cases with dir_nlink"

	mkdir_overfilled_with_dir_nlink "${TEST_MP}/DIR0"
	mkdir_overfilled_with_dir_nlink "${TEST_MP}/DIR1"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_overfilled_with_dir_nlink "${TEST_MP}/DIR1/DIR2"

	remount_fs_expexted_bad_refcount "mkdir/rmdir case 0"

	rm -r -f "${TEST_MP}/DIR1/DIR2/*"

	remount_fs_expexted_bad_refcount "mkdir/rmdir case 1"

	mv "${TEST_MP}/DIR0" "${TEST_MP}/DIR1/DIR2"

	remount_fs_expexted_bad_refcount "mkdir/rmdir case 2"

	rm -r -f "${TEST_MP}/DIR1"

	remount_fs_expected_clean "mkdir/rmdir case 3"

	echo "Unmount, check results"
	umount ${TEST_DEV}
}

mkdir_with_subdirs() # ${1} -> DIR_PATH, ${2} -> SUBDIR_NUM
{
	mkdir ${1}
	
	i=0
	while [ "${i}" -le "${2}" ]
	do
		mkdir ${1}/TEST_DIR_${i}
		if [ "$?" -ne "0" ]
		then
			echo "ERROR: in mkdir_with_subdirs(): i=${i}"
			exit 1
		fi
		i=$((i+1))
	done
}

test_different_rename_cases_with_dir_nlink_option()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_nlink ${TEST_DEV}
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Test rename cases with dir_nlink feature"

	# FPDPR, FDIR => TPDIR, TDIR

	mkdir_with_subdirs "${TEST_MP}/DIR1" "500"
	mv "${TEST_MP}/DIR1" "${TEST_MP}/DIR2"

	remount_fs_expected_clean "rename case 0"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mv "${TEST_MP}/DIR1" "${TEST_MP}/DIR2"

	remount_fs_expected_clean "rename case 1"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/"

	remount_fs_expected_clean "rename case 2"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/"

	remount_fs_expected_clean "rename case 3"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/"

	remount_fs_expected_clean "rename case 4"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/"

	remount_fs_expected_clean "rename case 5"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir "${TEST_MP}/DIR2"
	mv "${TEST_MP}/DIR1" "${TEST_MP}/DIR2"

	remount_fs_expected_clean "rename case 6"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR2" "70000"
	rm -r ${TEST_MP}/DIR2/*
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR2"

	remount_fs_expected_clean "rename case 7"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR2" "500"
	rm -r ${TEST_MP}/DIR2/*
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR2"

	remount_fs_expected_clean "rename case 8"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	mkdir "${TEST_MP}/DIR3/DIR4"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/DIR4"

	remount_fs_expected_clean "rename case 9"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR3/DIR4" "70000"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/DIR4"

	remount_fs_expected_clean "rename case 10"

	# ...

	umount ${TEST_DEV}
}

test_different_rename_cases_with_dir_nlink_option_and_exceeded_nlink_count()
{
	echo "mkfs and mount..."
	mkfs.ext3 -F -O dir_nlink ${TEST_DEV}
	mount -t ext2fs ${TEST_DEV} ${TEST_MP}

	echo "Test rename cases with dir_nlink feature and exceeded nlink"

	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mv "${TEST_MP}/DIR1" "${TEST_MP}/DIR2"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 1"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 2"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 3"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR3"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 4"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1/DIR2"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR3"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 5"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir "${TEST_MP}/DIR2"
	mv "${TEST_MP}/DIR1" "${TEST_MP}/DIR2"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 6"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR2" "70000"
	rm -r ${TEST_MP}/DIR2/*
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR2"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 7"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR2" "500"
	rm -r ${TEST_MP}/DIR2/*
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR2"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 8"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "500"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR3"
	mkdir "${TEST_MP}/DIR3/DIR4"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/DIR4"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 9"

	rm -r ${TEST_MP}/*
	mkdir_with_subdirs "${TEST_MP}/DIR1" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR1"
	mkdir_with_subdirs "${TEST_MP}/DIR1/DIR2" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR3" "70000"
	mkdir_with_subdirs "${TEST_MP}/DIR3/DIR4" "70000"
	rmdir_overfilled_partially "${TEST_MP}/DIR3/DIR4"
	mv "${TEST_MP}/DIR1/DIR2" "${TEST_MP}/DIR3/DIR4"

	remount_fs_expexted_bad_refcount "rename exceeded nlink case 10"

	# ...

	umount ${TEST_DEV}
}


#main()

echo "Test started, and will take near an one hour on my old laptop..."

test_hardlinks_count_with_feature

test_dirs_count_no_dir_nlink_option
test_dirs_count_with_dir_nlink_option
test_dirs_count_with_dir_index_no_dir_nlink_options

test_different_mkdir_rmdir_cases_without_dir_nlink
test_different_mkdir_rmdir_cases_with_dir_nlink

test_different_rename_cases_with_dir_nlink_option
test_different_rename_cases_with_dir_nlink_option_and_exceeded_nlink_count

echo "Test finished successfully!"
