#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2022-2024 Oracle and/or its affiliates.  All Rights Reserved.
#
# Parent pointer common functions
#

#
# parse_parent_pointer parents parent_inode parent_pointer_name
#
# Given a list of parent pointers, find the record that matches
# the given inode and filename
#
# inputs:
# parents	: A list of parent pointers in the format of:
#		  inode/generation/name_length/name
# parent_inode	: The parent inode to search for
# parent_name	: The parent name to search for
#
# outputs:
# PPINO         : Parent pointer inode
# PPGEN         : Parent pointer generation
# PPNAME        : Parent pointer name
# PPNAME_LEN    : Parent pointer name length
#
_xfs_parse_parent_pointer()
{
	local parents=$1
	local pino=$2
	local parent_pointer_name=$3

	local found=0

	# Find the entry that has the same inode as the parent
	# and parse out the entry info
	while IFS=':' read PPINO PPGEN PPNAME_LEN PPNAME; do
		if [ "$PPINO" != "$pino" ]; then
			continue
		fi

		if [ "$PPNAME" != "$parent_pointer_name" ]; then
			continue
		fi

		found=1
		break
	done <<< $(echo "$parents")

	# Check to see if we found anything
	# We do not fail the test because we also use this
	# routine to verify when parent pointers should
	# be removed or updated  (ie a rename or a move
	# operation changes your parent pointer)
	if [ $found -eq "0" ]; then
		return 1
	fi

	# Verify the parent pointer name length is correct
	if [ "$PPNAME_LEN" -ne "${#parent_pointer_name}" ]
	then
		echo "*** Bad parent pointer:"\
			"name:$PPNAME, namelen:$PPNAME_LEN"
	fi

	#return sucess
	return 0
}

#
# _xfs_verify_parent parent_path parent_pointer_name child_path
#
# Verify that the given child path lists the given parent as a parent pointer
# and that the parent pointer name matches the given name
#
# Examples:
#
# #simple example
# mkdir testfolder1
# touch testfolder1/file1
# verify_parent testfolder1 file1 testfolder1/file1
#
# # In this above example, we want to verify that "testfolder1"
# # appears as a parent pointer of "testfolder1/file1".  Additionally
# # we verify that the name record of the parent pointer is "file1"
#
#
# #hardlink example
# mkdir testfolder1
# mkdir testfolder2
# touch testfolder1/file1
# ln testfolder1/file1 testfolder2/file1_ln
# verify_parent testfolder2 file1_ln testfolder1/file1
#
# # In this above example, we want to verify that "testfolder2"
# # appears as a parent pointer of "testfolder1/file1".  Additionally
# # we verify that the name record of the parent pointer is "file1_ln"
#
_xfs_verify_parent()
{
	local parent_path=$1
	local parent_pointer_name=$2
	local child_path=$3

	local parent_ppath="$parent_path/$parent_pointer_name"

	# Verify parent exists
	if [ ! -d $SCRATCH_MNT/$parent_path ]; then
		echo "$SCRATCH_MNT/$parent_path not found"
	else
		echo "*** $parent_path OK"
	fi

	# Verify child exists
	if [ ! -f $SCRATCH_MNT/$child_path ]; then
		echo "$SCRATCH_MNT/$child_path not found"
	else
		echo "*** $child_path OK"
	fi

	# Verify the parent pointer name exists as a child of the parent
	if [ ! -f $SCRATCH_MNT/$parent_ppath ]; then
		echo "$SCRATCH_MNT/$parent_ppath not found"
	else
		echo "*** $parent_ppath OK"
	fi

	# Get the inodes of both parent and child
	pino="$(stat -c '%i' $SCRATCH_MNT/$parent_path)"
	cino="$(stat -c '%i' $SCRATCH_MNT/$child_path)"

	# Get all the parent pointers of the child
	parents=($($XFS_IO_PROG -x -c \
	 "parent -s -i $pino -n $parent_pointer_name" $SCRATCH_MNT/$child_path))
	if [[ $? != 0 ]]; then
		 echo "No parent pointers found for $child_path"
	fi

	# Parse parent pointer output.
	# This sets PPINO PPGEN PPNAME PPNAME_LEN
	_xfs_parse_parent_pointer $parents $pino $parent_pointer_name

	# If we didnt find one, bail out
	if [ $? -ne 0 ]; then
		echo "No parent pointer record found for $parent_path"\
			"in $child_path"
	fi

	# Verify the inode generated by the parent pointer name is
	# the same as the child inode
	pppino="$(stat -c '%i' $SCRATCH_MNT/$parent_ppath)"
	if [ $cino -ne $pppino ]
	then
		echo "Bad parent pointer name value for $child_path."\
			"$SCRATCH_MNT/$parent_ppath belongs to inode $PPPINO,"\
			"but should be $cino"
	fi

	# Make sure path printing works by checking that the paths returned
	# all point to the same inode.
	local tgt="$SCRATCH_MNT/$child_path"
	$XFS_IO_PROG -x -c 'parent -p' "$tgt" | while read pptr_path; do
		test "$tgt" -ef "$pptr_path" || \
			echo "$tgt parent pointer $pptr_path should be the same file"
	done

	echo "*** Verified parent pointer:"\
			"name:$PPNAME, namelen:$PPNAME_LEN"
	echo "*** Parent pointer OK for child $child_path"
}

#
# _xfs_verify_parent parent_pointer_name pino child_path
#
# Verify that the given child path contains no parent pointer entry
# for the given inode and file name
#
_xfs_verify_no_parent()
{
	local parent_pname=$1
	local pino=$2
	local child_path=$3

	# Verify child exists
	if [ ! -f $SCRATCH_MNT/$child_path ]; then
		echo "$SCRATCH_MNT/$child_path not found"
	else
		echo "*** $child_path OK"
	fi

	# Get all the parent pointers of the child
	local parents=($($XFS_IO_PROG -x -c \
	 "parent -s -i $pino -n $parent_pname" $SCRATCH_MNT/$child_path))
	if [[ $? != 0 ]]; then
		return 0
	fi

	# Parse parent pointer output.
	# This sets PPINO PPGEN PPNAME PPNAME_LEN
	_xfs_parse_parent_pointer $parents $pino $parent_pname

	# If we didnt find one, return sucess
	if [ $? -ne 0 ]; then
		return 0
	fi

	echo "Parent pointer entry found where none should:"\
			"inode:$PPINO, gen:$PPGEN,"
			"name:$PPNAME, namelen:$PPNAME_LEN"
}
