new file mode 100644
@@ -0,0 +1,209 @@
+#! /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"
+}
@@ -2742,6 +2742,9 @@ _require_xfs_io_command()
echo $testio | grep -q "invalid option" && \
_notrun "xfs_io $command support is missing"
;;
+ "parent")
+ testio=`$XFS_IO_PROG -x -c "parent" $TEST_DIR 2>&1`
+ ;;
"pwrite")
# -N (RWF_NOWAIT) only works with direct vectored I/O writes
local pwrite_opts=" "
@@ -1863,3 +1863,15 @@ _xfs_force_no_pptrs()
MKFS_OPTIONS="$MKFS_OPTIONS -n parent=0"
}
+
+# this test requires the xfs parent pointers feature
+#
+_require_xfs_parent()
+{
+ _scratch_mkfs_xfs_supported -n parent > /dev/null 2>&1 \
+ || _notrun "mkfs.xfs does not support parent pointers"
+ _scratch_mkfs_xfs -n parent > /dev/null 2>&1
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "kernel does not support parent pointers"
+ _scratch_unmount
+}