mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-06-24 10:08:00 +03:00

Add the ZFS Test Suite and test-runner framework from illumos. This is a continuation of the work done by Turbo Fredriksson to port the ZFS Test Suite to Linux. While this work was originally conceived as a stand alone project integrating it directly with the ZoL source tree has several advantages: * Allows the ZFS Test Suite to be packaged in zfs-test package. * Facilitates easy integration with the CI testing. * Users can locally run the ZFS Test Suite to validate ZFS. This testing should ONLY be done on a dedicated test system because the ZFS Test Suite in its current form is destructive. * Allows the ZFS Test Suite to be run directly in the ZoL source tree enabled developers to iterate quickly during development. * Developers can easily add/modify tests in the framework as features are added or functionality is changed. The tests will then always be in sync with the implementation. Full documentation for how to run the ZFS Test Suite is available in the tests/README.md file. Warning: This test suite is designed to be run on a dedicated test system. It will make modifications to the system including, but not limited to, the following. * Adding new users * Adding new groups * Modifying the following /proc files: * /proc/sys/kernel/core_pattern * /proc/sys/kernel/core_uses_pid * Creating directories under / Notes: * Not all of the test cases are expected to pass and by default these test cases are disabled. The failures are primarily due to assumption made for illumos which are invalid under Linux. * When updating these test cases it should be done in as generic a way as possible so the patch can be submitted back upstream. Most existing library functions have been updated to be Linux aware, and the following functions and variables have been added. * Functions: * is_linux - Used to wrap a Linux specific section. * block_device_wait - Waits for block devices to be added to /dev/. * Variables: Linux Illumos * ZVOL_DEVDIR "/dev/zvol" "/dev/zvol/dsk" * ZVOL_RDEVDIR "/dev/zvol" "/dev/zvol/rdsk" * DEV_DSKDIR "/dev" "/dev/dsk" * DEV_RDSKDIR "/dev" "/dev/rdsk" * NEWFS_DEFAULT_FS "ext2" "ufs" * Many of the disabled test cases fail because 'zfs/zpool destroy' returns EBUSY. This is largely causes by the asynchronous nature of device handling on Linux and is expected, the impacted test cases will need to be updated to handle this. * There are several test cases which have been disabled because they can trigger a deadlock. A primary example of this is to recursively create zpools within zpools. These tests have been disabled until the root issue can be addressed. * Illumos specific utilities such as (mkfile) should be added to the tests/zfs-tests/cmd/ directory. Custom programs required by the test scripts can also be added here. * SELinux should be either is permissive mode or disabled when running the tests. The test cases should be updated to conform to a standard policy. * Redundant test functionality has been removed (zfault.sh). * Existing test scripts (zconfig.sh) should be migrated to use the framework for consistency and ease of testing. * The DISKS environment variable currently only supports loopback devices because of how the ZFS Test Suite expects partitions to be named (p1, p2, etc). Support must be added to generate the correct partition name based on the device location and name. * The ZFS Test Suite is part of the illumos code base at: https://github.com/illumos/illumos-gate/tree/master/usr/src/test Original-patch-by: Turbo Fredriksson <turbo@bayour.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Olaf Faaland <faaland1@llnl.gov> Closes #6 Closes #1534
627 lines
12 KiB
Plaintext
627 lines
12 KiB
Plaintext
#
|
|
# CDDL HEADER START
|
|
#
|
|
# The contents of this file are subject to the terms of the
|
|
# Common Development and Distribution License (the "License").
|
|
# You may not use this file except in compliance with the License.
|
|
#
|
|
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
# or http://www.opensolaris.org/os/licensing.
|
|
# See the License for the specific language governing permissions
|
|
# and limitations under the License.
|
|
#
|
|
# When distributing Covered Code, include this CDDL HEADER in each
|
|
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
# If applicable, add the following below this CDDL HEADER, with the
|
|
# fields enclosed by brackets "[]" replaced with your own identifying
|
|
# information: Portions Copyright [yyyy] [name of copyright owner]
|
|
#
|
|
# CDDL HEADER END
|
|
#
|
|
|
|
#
|
|
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
|
# Use is subject to license terms.
|
|
#
|
|
|
|
. $STF_SUITE/tests/functional/acl/acl.cfg
|
|
. $STF_SUITE/include/libtest.shlib
|
|
|
|
#
|
|
# Get the given file/directory access mode
|
|
#
|
|
# $1 object -- file or directroy
|
|
#
|
|
function get_mode #<obj>
|
|
{
|
|
typeset obj=$1
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
$LS -ld $obj | $AWK '{print $1}'
|
|
}
|
|
|
|
#
|
|
# Get the given file/directory ACL
|
|
#
|
|
# $1 object -- file or directroy
|
|
#
|
|
function get_acl #<obj>
|
|
{
|
|
typeset obj=$1
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
$LS -vd $obj | $NAWK '(NR != 1) {print $0}'
|
|
}
|
|
|
|
#
|
|
# Get the given file/directory ACL
|
|
#
|
|
# $1 object -- file or directroy
|
|
#
|
|
function get_compact_acl #<obj>
|
|
{
|
|
typeset obj=$1
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
$LS -Vd $obj | $NAWK '(NR != 1) {print $0}'
|
|
}
|
|
|
|
#
|
|
# Check the given two files/directories have the same ACLs
|
|
#
|
|
# Return 0, if source object acl is equal to target object acl.
|
|
#
|
|
# $1 source object
|
|
# $2 target object
|
|
#
|
|
function compare_acls #<src> <tgt>
|
|
{
|
|
typeset src=$1
|
|
typeset tgt=$2
|
|
|
|
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
|
|
[[ $src == $tgt ]] && return 0
|
|
|
|
typeset tmpsrc=/tmp/compare_acls.src.$$
|
|
typeset tmptgt=/tmp/compare_acls.tgt.$$
|
|
|
|
get_acl $src > $tmpsrc
|
|
get_acl $tgt > $tmptgt
|
|
typeset -i ret=0
|
|
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
|
|
ret=$?
|
|
$RM -f $tmpsrc $tmptgt
|
|
|
|
if (( ret != 0 )); then
|
|
return $ret
|
|
fi
|
|
|
|
get_compact_acl $src > $tmpsrc
|
|
get_compact_acl $tgt > $tmptgt
|
|
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
|
|
ret=$?
|
|
$RM -f $tmpsrc $tmptgt
|
|
|
|
return $ret
|
|
}
|
|
|
|
#
|
|
# Check that the given two objects have the same modes.
|
|
# Return 0, if their modes are equal with each other. Otherwise, return 1.
|
|
#
|
|
# $1 source object
|
|
# $2 target object
|
|
#
|
|
function compare_modes #<src> <tgt>
|
|
{
|
|
typeset src=$1
|
|
typeset tgt=$2
|
|
typeset -i i=0
|
|
set -A mode
|
|
|
|
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
|
|
[[ $src == $tgt ]] && return 0
|
|
|
|
typeset obj
|
|
for obj in $src $tgt
|
|
do
|
|
mode[i]=$(get_mode $obj)
|
|
|
|
(( i = i + 1 ))
|
|
done
|
|
|
|
[[ ${mode[0]} != ${mode[1]} ]] && return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Check that the given two objects have the same xattrs.
|
|
# Return 0, if their xattrs are equal with each other. Otherwise, return 1.
|
|
#
|
|
# $1 source object
|
|
# $2 target object
|
|
#
|
|
function compare_xattrs #<src> <tgt>
|
|
{
|
|
typeset src=$1
|
|
typeset tgt=$2
|
|
|
|
(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
|
|
[[ $src == $tgt ]] && return 0
|
|
|
|
typeset tmpsrc=/tmp/compare_xattrs.src.$$
|
|
typeset tmptgt=/tmp/compare_xattrs.tgt.$$
|
|
|
|
get_xattr $src > $tmpsrc
|
|
get_xattr $tgt > $tmptgt
|
|
typeset -i ret=0
|
|
$DIFF $tmpsrc $tmptgt > /dev/null 2>&1
|
|
ret=$?
|
|
$RM -f $tmpsrc $tmptgt
|
|
|
|
return $ret
|
|
}
|
|
|
|
#
|
|
# Check '+' is set for a given file/directory with 'ls [-l]' command
|
|
#
|
|
# $1 object -- file or directory.
|
|
#
|
|
function plus_sign_check_l #<obj>
|
|
{
|
|
typeset obj=$1
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
$LS -ld $obj | $AWK '{print $1}' | $GREP "+\>" > /dev/null
|
|
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# Check '+' is set for a given file/directory with 'ls [-v]' command
|
|
#
|
|
# $1 object -- file or directory.
|
|
#
|
|
function plus_sign_check_v #<obj>
|
|
{
|
|
typeset obj=$1
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
$LS -vd $obj | $NAWK '(NR == 1) {print $1}' | $GREP "+\>" > /dev/null
|
|
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# A wrapper function of c program
|
|
#
|
|
# $1 legal login name
|
|
# $2-n commands and options
|
|
#
|
|
function chgusr_exec #<login_name> <commands> [...]
|
|
{
|
|
$CHG_USR_EXEC $@
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# Export the current user for the following usr_exec operating.
|
|
#
|
|
# $1 legal login name
|
|
#
|
|
function set_cur_usr #<login_name>
|
|
{
|
|
export ZFS_ACL_CUR_USER=$1
|
|
}
|
|
|
|
#
|
|
# Run commands by $ZFS_ACL_CUR_USER
|
|
#
|
|
# $1-n commands and options
|
|
#
|
|
function usr_exec #<commands> [...]
|
|
{
|
|
$CHG_USR_EXEC "$ZFS_ACL_CUR_USER" $@
|
|
return $?
|
|
}
|
|
|
|
#
|
|
# Count how many ACEs for the speficied file or directory.
|
|
#
|
|
# $1 file or directroy name
|
|
#
|
|
function count_ACE #<file or dir name>
|
|
{
|
|
if [[ ! -e $1 ]]; then
|
|
log_note "Need input file or directroy name."
|
|
return 1
|
|
fi
|
|
|
|
$LS -vd $1 | $NAWK 'BEGIN {count=0}
|
|
(NR != 1)&&(/[0-9]:/) {count++}
|
|
END {print count}'
|
|
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Get specified number ACE content of specified file or directory.
|
|
#
|
|
# $1 file or directory name
|
|
# $2 specified number
|
|
#
|
|
function get_ACE #<file or dir name> <specified number> <verbose|compact>
|
|
{
|
|
if [[ ! -e $1 || $2 -ge $(count_ACE $1) ]]; then
|
|
return 1
|
|
fi
|
|
|
|
typeset file=$1
|
|
typeset -i num=$2
|
|
typeset format=${3:-verbose}
|
|
typeset -i next_num=-1
|
|
|
|
typeset tmpfile=/tmp/tmp_get_ACE.$$
|
|
typeset line=""
|
|
typeset args
|
|
|
|
case $format in
|
|
verbose) args="-vd"
|
|
;;
|
|
compact) args="-Vd"
|
|
;;
|
|
*) log_fail "Invalid parameter as ($format), " \
|
|
"only verbose|compact is supported."
|
|
;;
|
|
esac
|
|
|
|
$LS $args $file > $tmpfile
|
|
(( $? != 0 )) && log_fail "FAIL: $LS $args $file > $tmpfile"
|
|
while read line; do
|
|
[[ -z $line ]] && continue
|
|
if [[ $args == -vd ]]; then
|
|
if [[ $line == "$num":* ]]; then
|
|
(( next_num = num + 1 ))
|
|
fi
|
|
if [[ $line == "$next_num":* ]]; then
|
|
break
|
|
fi
|
|
if (( next_num != -1 )); then
|
|
print -n $line
|
|
fi
|
|
else
|
|
if (( next_num == num )); then
|
|
print -n $line
|
|
fi
|
|
(( next_num += 1 ))
|
|
fi
|
|
done < $tmpfile
|
|
|
|
$RM -f $tmpfile
|
|
(( $? != 0 )) && log_fail "FAIL: $RM -f $tmpfile"
|
|
}
|
|
|
|
#
|
|
# Cleanup exist user/group.
|
|
#
|
|
function cleanup_user_group
|
|
{
|
|
del_user $ZFS_ACL_ADMIN
|
|
|
|
del_user $ZFS_ACL_STAFF1
|
|
del_user $ZFS_ACL_STAFF2
|
|
del_group $ZFS_ACL_STAFF_GROUP
|
|
|
|
del_user $ZFS_ACL_OTHER1
|
|
del_user $ZFS_ACL_OTHER2
|
|
del_group $ZFS_ACL_OTHER_GROUP
|
|
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Clean up testfile and test directory
|
|
#
|
|
function cleanup
|
|
{
|
|
if [[ -d $TESTDIR ]]; then
|
|
cd $TESTDIR
|
|
$RM -rf $TESTDIR/*
|
|
fi
|
|
}
|
|
|
|
#
|
|
# According to specified access or acl_spec, do relevant operating by using the
|
|
# specified user.
|
|
#
|
|
# $1 specified user
|
|
# $2 node
|
|
# $3 acl_spec or access
|
|
#
|
|
function rwx_node #user node acl_spec|access
|
|
{
|
|
typeset user=$1
|
|
typeset node=$2
|
|
typeset acl_spec=$3
|
|
|
|
if [[ $user == "" || $node == "" || $acl_spec == "" ]]; then
|
|
log_note "node or acl_spec are not defined."
|
|
return 1
|
|
fi
|
|
|
|
if [[ -d $node ]]; then
|
|
case $acl_spec in
|
|
*:read_data:*|read_data)
|
|
chgusr_exec $user $LS -l $node > /dev/null 2>&1
|
|
return $? ;;
|
|
*:write_data:*|write_data)
|
|
if [[ -f ${node}/tmpfile ]]; then
|
|
log_must $RM -f ${node}/tmpfile
|
|
fi
|
|
chgusr_exec $user $TOUCH ${node}/tmpfile > \
|
|
/dev/null 2>&1
|
|
return $? ;;
|
|
*"execute:"*|execute)
|
|
chgusr_exec $user $FIND $node > /dev/null 2>&1
|
|
return $? ;;
|
|
esac
|
|
else
|
|
case $acl_spec in
|
|
*:read_data:*|read_data)
|
|
chgusr_exec $user $CAT $node > /dev/null 2>&1
|
|
return $? ;;
|
|
*:write_data:*|write_data)
|
|
chgusr_exec $user $DD if=/usr/bin/ls of=$node > \
|
|
/dev/null 2>&1
|
|
return $? ;;
|
|
*"execute:"*|execute)
|
|
ZFS_ACL_ERR_STR=$(chgusr_exec $user $node 2>&1)
|
|
return $? ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get the given file/directory xattr
|
|
#
|
|
# $1 object -- file or directroy
|
|
#
|
|
function get_xattr #<obj>
|
|
{
|
|
typeset obj=$1
|
|
typeset xattr
|
|
if (( ${#obj} == 0 )); then
|
|
return 1
|
|
fi
|
|
|
|
for xattr in `$RUNAT $obj $LS | \
|
|
/usr/xpg4/bin/egrep -v -e SUNWattr_ro -e SUNWattr_rw` ; do
|
|
$RUNAT $obj $SUM $xattr
|
|
done
|
|
}
|
|
|
|
#
|
|
# Get the owner of a file/directory
|
|
#
|
|
function get_owner #node
|
|
{
|
|
typeset node=$1
|
|
typeset value
|
|
|
|
if [[ -z $node ]]; then
|
|
log_fail "node are not defined."
|
|
fi
|
|
|
|
if [[ -d $node ]]; then
|
|
value=$($LS -dl $node | $AWK '{print $3}')
|
|
elif [[ -e $node ]]; then
|
|
value=$($LS -l $node | $AWK '{print $3}')
|
|
fi
|
|
|
|
$ECHO $value
|
|
}
|
|
|
|
#
|
|
# Get the group of a file/directory
|
|
#
|
|
function get_group #node
|
|
{
|
|
typeset node=$1
|
|
typeset value
|
|
|
|
if [[ -z $node ]]; then
|
|
log_fail "node are not defined."
|
|
fi
|
|
|
|
if [[ -d $node ]]; then
|
|
value=$($LS -dl $node | $AWK '{print $4}')
|
|
elif [[ -e $node ]]; then
|
|
value=$($LS -l $node | $AWK '{print $4}')
|
|
fi
|
|
|
|
$ECHO $value
|
|
}
|
|
|
|
|
|
#
|
|
# Get the group name that a UID belongs to
|
|
#
|
|
function get_user_group #uid
|
|
{
|
|
typeset uid=$1
|
|
typeset value
|
|
|
|
if [[ -z $uid ]]; then
|
|
log_fail "UID not defined."
|
|
fi
|
|
|
|
value=$(id $uid)
|
|
|
|
if [[ $? -eq 0 ]]; then
|
|
value=${value##*\(}
|
|
value=${value%%\)*}
|
|
$ECHO $value
|
|
else
|
|
log_fail "Invalid UID (uid)."
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Get the specified item of the specified string
|
|
#
|
|
# $1: Item number, count from 0.
|
|
# $2-n: strings
|
|
#
|
|
function getitem
|
|
{
|
|
typeset -i n=$1
|
|
shift
|
|
|
|
(( n += 1 ))
|
|
eval echo \${$n}
|
|
}
|
|
|
|
#
|
|
# This function calculate the specified directory files checksum and write
|
|
# to the specified array.
|
|
#
|
|
# $1 directory in which the files will be cksum.
|
|
# $2 file array name which was used to store file cksum information.
|
|
# $3 attribute array name which was used to store attribute information.
|
|
#
|
|
function cksum_files #<dir> <file_array_name> <attribute_array_name>
|
|
{
|
|
typeset dir=$1
|
|
typeset farr_name=$2
|
|
typeset aarr_name=$3
|
|
|
|
[[ ! -d $dir ]] && return
|
|
typeset oldpwd=$PWD
|
|
cd $dir
|
|
typeset files=$($LS file*)
|
|
|
|
typeset -i i=0
|
|
typeset -i n=0
|
|
while (( i < NUM_FILE )); do
|
|
typeset f=$(getitem $i $files)
|
|
eval $farr_name[$i]=\$\(\$CKSUM $f\)
|
|
|
|
typeset -i j=0
|
|
while (( j < NUM_ATTR )); do
|
|
eval $aarr_name[$n]=\$\(\$RUNAT \$f \$CKSUM \
|
|
attribute.$j\)
|
|
|
|
(( j += 1 ))
|
|
(( n += 1 ))
|
|
done
|
|
|
|
(( i += 1 ))
|
|
done
|
|
|
|
cd $oldpwd
|
|
}
|
|
|
|
#
|
|
# This function compare two cksum results array.
|
|
#
|
|
# $1 The array name which stored the cksum before operation.
|
|
# $2 The array name which stored the cksum after operation.
|
|
#
|
|
function compare_cksum #<array1> <array2>
|
|
{
|
|
typeset before=$1
|
|
typeset after=$2
|
|
eval typeset -i count=\${#$before[@]}
|
|
|
|
typeset -i i=0
|
|
while (( i < count )); do
|
|
eval typeset var1=\${$before[$i]}
|
|
eval typeset var2=\${$after[$i]}
|
|
|
|
if [[ $var1 != $var2 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
(( i += 1 ))
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# This function calculate all the files cksum information in current directory
|
|
# and output them to the specified file.
|
|
#
|
|
# $1 directory from which the files will be cksum.
|
|
# $2 cksum output file
|
|
#
|
|
function record_cksum #<outfile>
|
|
{
|
|
typeset dir=$1
|
|
typeset outfile=$2
|
|
|
|
[[ ! -d ${outfile%/*} ]] && usr_exec $MKDIR -p ${outfile%/*}
|
|
|
|
usr_exec cd $dir ; $FIND . -depth -type f -exec cksum {} \\\; | \
|
|
$SORT > $outfile
|
|
usr_exec cd $dir ; $FIND . -depth -type f -xattr -exec runat {} \
|
|
cksum attribute* \\\; | $SORT >> $outfile
|
|
}
|
|
|
|
#
|
|
# The function create_files creates the directories and files that the script
|
|
# will operate on to test extended attribute functionality.
|
|
#
|
|
# $1 The base directory in which to create directories and files.
|
|
#
|
|
function create_files #<directory>
|
|
{
|
|
typeset basedir=$1
|
|
|
|
[[ ! -d $basedir ]] && usr_exec $MKDIR -m 777 $basedir
|
|
[[ ! -d $RES_DIR ]] && usr_exec $MKDIR -m 777 $RES_DIR
|
|
[[ ! -d $INI_DIR ]] && usr_exec $MKDIR -m 777 $INI_DIR
|
|
[[ ! -d $TST_DIR ]] && usr_exec $MKDIR -m 777 $TST_DIR
|
|
[[ ! -d $TMP_DIR ]] && usr_exec $MKDIR -m 777 $TMP_DIR
|
|
|
|
#
|
|
# Create the original file and its attribute files.
|
|
#
|
|
[[ ! -a $RES_DIR/file ]] && \
|
|
usr_exec $FILE_WRITE -o create -f $RES_DIR/file \
|
|
-b 1024 -d 0 -c 1
|
|
[[ ! -a $RES_DIR/attribute ]] && \
|
|
usr_exec $CP $RES_DIR/file $RES_DIR/attribute
|
|
|
|
typeset oldpwd=$PWD
|
|
cd $INI_DIR
|
|
|
|
typeset -i i=0
|
|
while (( i < NUM_FILE )); do
|
|
typeset dstfile=$INI_DIR/file.$$.$i
|
|
usr_exec $CP $RES_DIR/file $dstfile
|
|
|
|
typeset -i j=0
|
|
while (( j < NUM_ATTR )); do
|
|
usr_exec $RUNAT $dstfile \
|
|
$CP $RES_DIR/attribute ./attribute.$j
|
|
(( j += 1 ))
|
|
done
|
|
|
|
(( i += 1 ))
|
|
done
|
|
|
|
cd $oldpwd
|
|
}
|