#!/bin/bash # # Copyright (c) International Business Machines Corp., 2005 # Authors: Avantika Mathur (mathurav@us.ibm.com) # Matt Helsley (matthltc@us.ibm.com) # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # if tst_kvcmp -lt "2.6.15"; then tst_resm TCONF "System kernel version is less than 2.6.15" tst_resm TCONF "Cannot execute test" exit 0 fi test_setup() { ####################################################################### ## Configure ####################################################################### dopts='-dEBb' ## Remove logged test state depending on results. 0 means do not remove, ## 1 means OK to remove. # rm saved state from tests that appear to have cleaned up properly? rm_ok=1 # rm saved state from tests that don't appear to have fully cleaned up? rm_err=0 ####################################################################### ## Initialize some variables ####################################################################### TCID="$0" TST_COUNT=0 test_dirs=( move bind rbind regression ) #cloneNS nfailed=0 nsucceeded=0 # set the LTPROOT directory cd `dirname $0` LTPROOT="${PWD}" echo "${LTPROOT}" | grep testscripts > /dev/null 2>&1 if [ $? -eq 0 ]; then cd .. LTPROOT="${PWD}" fi FS_BIND_ROOT="${LTPROOT}/testcases/bin/fs_bind" total=0 # total number of tests for dir in "${test_dirs[@]}" ; do ((total += `ls "${FS_BIND_ROOT}/${dir}/test"* | wc -l`)) done TST_TOTAL=${total} # set the PATH to include testcases/bin LTPBIN="${LTPROOT}/testcases/bin" PATH="${PATH}:/usr/sbin:${LTPBIN}:${FS_BIND_ROOT}/bin" # Results directory resdir="${LTPROOT}/results/fs_bind" if [ ! -d "${resdir}" ]; then mkdir -p "${resdir}" 2> /dev/null fi TMPDIR="${TMPDIR:-/tmp}" # A temporary directory where we can do stuff and that is # safe to remove sandbox="${TMPDIR}/sandbox" ERR_MSG="" export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL if [ ! -d "${resdir}" ]; then tst_brkm TBROK true "$0: failed to make results directory" exit 1 fi } test_prereqs() { # Must be root to run the containers testsuite if [ $UID != 0 ]; then tst_brkm TBROK true "FAILED: Must be root to execute this script" exit 1 fi mkdir "${sandbox}" >& /dev/null if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then tst_brkm TBROK true "$0: failed to make directory \"${sandbox}\"" exit -1 fi mount --bind "${sandbox}" "${sandbox}" >& /dev/null if [ $? -ne 0 ]; then tst_brkm TBROK true "$0: failed to perform bind mount on directory \"${sandbox}\"" exit 1 fi mount --make-private "${sandbox}" >& /dev/null if [ $? -ne 0 ]; then tst_brkm TBROK true "$0: failed to make private mountpoint on directory \"${sandbox}\"" exit 1 fi local mnt_bind=1 local mnt_move=1 pushd "${sandbox}" > /dev/null && { mkdir bind_test move_test && { mount --bind bind_test bind_test && { mnt_bind=0 mount --move bind_test move_test && { mnt_move=0 umount move_test } || { # bind mount succeeded but move mount # failed umount bind_test } } || { # mount failed -- check if it's because we # don't have privileges we need if [ $? -eq 32 ]; then tst_brkm TBROK true "$0 requires the privilege to use the mount command" exit 32 fi } rmdir bind_test move_test } popd > /dev/null } if [ ${mnt_bind} -eq 1 -o ${mnt_move} -eq 1 ]; then tst_brkm TBROK true "$0: requires that mount support the --bind and --move options" exit 1 fi if tst_kvcmp -lt "2.6.15"; then tst_resm TWARN "$0: the remaining tests require 2.6.15 or later" tst_exit 0 exit else tst_resm TINFO "$0: kernel >= 2.6.15 detected -- continuing" fi mount --make-shared "${sandbox}" > /dev/null 2>&1 || "${FS_BIND_ROOT}/bin/smount" "${sandbox}" shared umount "${sandbox}" || { tst_resm TFAIL "$0: failed to umount simplest shared subtree" exit 1 } tst_resm TPASS "$0: umounted simplest shared subtree" } # mounts we are concerned with in a well-defined order (helps diff) # returns grep return codes grep_proc_mounts() { local rc=0 # Save the pipefail shell option shopt -o -q pipefail local save=$? set -o pipefail # Grep /proc/mounts which is often more up-to-date than mounts # We use pipefail because if the grep fails we want to pass that along grep -F "${sandbox}" /proc/mounts | sort -b rc=$? # Restore the pipefail shell options [ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail return $rc } # Record the mount state save_proc_mounts() { touch "$2/proc_mounts.before" >& /dev/null if [ $? -ne 0 ]; then tst_brkm TBROK true "$1: failed to record proc mounts" return 1 fi grep_proc_mounts 2> /dev/null > "$2/proc_mounts.before" return 0 } # Compare mount list after the test with the list from before. # If there are no differences then remove the before list and silently # return 0. Else print the differences to stderr and return 1. check_proc_mounts() { local tname="$1" if [ ! -r "$2/proc_mounts.before" ]; then tst_brkm TBROK true "${tname}: Could not find pre-test proc mount list" return 1 fi grep_proc_mounts 2> /dev/null > "$2/proc_mounts.after" # If the mounts are the same then just return diff ${dopts} -q "$2/proc_mounts.before" "$2/proc_mounts.after" >& /dev/null if [ $? -eq 0 ]; then [ $rm_ok -eq 1 ] && rm -f "$2/proc_mounts."{before,after} return 0 fi tst_resm TWARN "${tname}: did not properly clean up its proc mounts" diff ${dopts} -U 0 "$2/proc_mounts.before" "$2/proc_mounts.after" | grep -vE '^\@\@' 1>&2 [ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} return 1 } # Undo leftover mounts restore_proc_mounts() { #local tname="$1" # do lazy umounts -- we're assuming that tests will only leave # new mounts around and will never remove mounts outside the test # directory ( while grep_proc_mounts ; do grep_proc_mounts | awk '{print $2}' | xargs -r -n 1 umount -l done ) >& /dev/null # mount list and exit with 0 [ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 1>&2 # >& /dev/null return 0 # if returning error do this: # tst_brkm TBROK true "${tname}: failed to restore mounts" } # mounts we are concerned with in a well-defined order (helps diff) # returns grep return codes grep_mounts() { local rc=0 # Save the pipefail shell option shopt -o -q pipefail local save=$? set -o pipefail # Grep mount command output (which tends to come from /etc/mtab) # We use pipefail because if the grep fails we want to pass that along mount | grep -F "${sandbox}" | sort -b rc=$? # Restore the pipefail shell options [ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail return $rc } # Record the mount state save_mounts() { touch "$2/mtab.before" >& /dev/null if [ $? -ne 0 ]; then tst_brkm TBROK true "$1: failed to record mtab mounts" return 1 fi grep_mounts 2> /dev/null > "$2/mtab.before" return 0 } # Compare mount list after the test with the list from before. # If there are no differences then remove the before list and silently # return 0. Else print the differences to stderr and return 1. check_mounts() { local tname="$1" if [ ! -r "$2/mtab.before" ]; then tst_brkm TBROK true "${tname}: Could not find pre-test mtab mount list" return 1 fi grep_mounts 2> /dev/null > "$2/mtab.after" # If the mounts are the same then just return diff ${dopts} -q "$2/mtab.before" "$2/mtab.after" >& /dev/null if [ $? -eq 0 ]; then [ $rm_ok -eq 1 ] && rm -f "$2/mtab."{before,after} return 0 fi tst_resm TWARN "${tname}: did not properly clean up its mtab mounts" diff ${dopts} -U 0 "$2/mtab.before" "$2/mtab.after" | grep -vE '^\@\@' 1>&2 [ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} return 1 } # Undo leftover mounts restore_mounts() { #local tname="$1" # do lazy umounts -- we're assuming that tests will only leave # new mounts around and will never remove mounts outside the test # directory ( while grep_mounts ; do grep_mounts | awk '{print $3}' | xargs -r -n 1 umount -l done ) >& /dev/null # mount list and exit with 0 [ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} 1>&2 # >& /dev/null return 0 # if returning error do this: # tst_brkm TBROK true "${tname}: failed to restore mounts" } # Record the sandbox state # We don't save full sandbox state -- just the names of files and dirs present save_sandbox() { local when="before" local tname="$1" if [ -e "$2/files.before" ]; then if [ -e "$2/files.after" ]; then tst_brkm TBROK true "${tname}: stale catalog of \"${sandbox}\"" return 1 fi when="after" fi ( find "${sandbox}" -type d -print | sort > "$2/dirs.$when" find "${sandbox}" -type f -print | sort | \ grep -vE '^'"$2"'/(dirs|files)\.(before|after)$' > "$2/files.$when" ) >& /dev/null return 0 } # Save sandbox after test and then compare. If the sandbox state is not # clean then print the differences to stderr and return 1. Else remove all # saved sandbox state and silently return 0 check_sandbox() { local tname="$1" if [ ! -r "$2/files.before" -o ! -r "$2/dirs.before" ]; then tst_brkm TBROK true "${tname} missing saved catalog of \"${sandbox}\"" return 1 fi save_sandbox "${tname} (check)" "$2" ( diff ${dopts} -q "$2/dirs.before" "$2/dirs.after" && \ diff ${dopts} -q "$2/files.before" "$2/files.after" ) >& /dev/null \ && { [ $rm_ok -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} return 0 } tst_resm TWARN "${tname} did not properly clean up \"${sandbox}\"" diff ${dopts} -U 0 "$2/dirs.before" "$2/dirs.after" 1>&2 diff ${dopts} -U 0 "$2/files.before" "$2/files.after" 1>&2 [ $rm_err -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} 1>&2 return 1 } # Robust sandbox cleanup clean_sandbox() { local tname="$1" { rm -rf "${sandbox}" ; mkdir "${sandbox}" ; } >& /dev/null if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then tst_brkm TBROK true "$tname: failed to make directory \"${sandbox}\"" return 1 fi return 0 } # Check file for non-whitespace chars is_file_empty() { awk '/^[[:space:]]*$/ { next } { exit 1; }' < "$1" } # # Run the specified test script. # # Return 1 if the test was broken but should not stop the remaining test # categories from being run. # Return 2 if the test was broken and no further tests should be run. # Return 0 otherwise (if the test was broken but it shouldn't affect other # test runs) # Note that this means the return status is not the success or failure of the # test itself. # run_test() { local t="$1" local tname="$(basename "$(dirname "$t")")/$(basename "$t")" local log="$resdir/$tname/log" local errlog="$resdir/$tname/err" local do_break=0 ERR_MSG="" # Pre-test mkdir -p "$resdir/$tname" if [ ! -d "$resdir/$tname" -o ! -x "$resdir/$tname" ]; then tst_brkm TBROK true "$0: can't make or use \"$resdir/$tname\" as a log directory" return 1 fi save_sandbox "$tname" "$resdir/$tname" || do_break=1 save_mounts "$tname" "$resdir/$tname" || do_break=1 save_proc_mounts "$tname" "$resdir/$tname" || do_break=1 mount --bind "${sandbox}" "${sandbox}" >& /dev/null || do_break=1 mount --make-private "${sandbox}" >& /dev/null || do_break=1 if [ $do_break -eq 1 ]; then umount -l "${sandbox}" >& /dev/null tst_brkm TBROK true "$tname: failed to save pre-test state of \"${sandbox}\"" return 2 fi pushd "${sandbox}" > /dev/null # Run the test ( TCID="$tname" declare -r TST_COUNT export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL "$t" #> "$log" 2> "$errlog" ) local rc=$? TCID="$0" # Post-test popd > /dev/null if [ $rc -ne 0 ]; then #echo "FAILED" ((nfailed++)) else #echo "SUCCEEDED" ((nsucceeded++)) fi umount -l "${sandbox}" >& /dev/null check_proc_mounts "$tname" "$resdir/$tname" || \ restore_proc_mounts "$tname" "$resdir/$tname" || do_break=1 check_mounts "$tname" "$resdir/$tname" || \ restore_mounts "$tname" "$resdir/$tname" || do_break=1 check_sandbox "$tname" "$resdir/$tname" clean_sandbox "$tname" || do_break=1 if [ $do_break -eq 1 ]; then tst_brkm TBROK true "$tname: failed to restore pre-test state of \"${sandbox}\"" return 2 fi # If we succeeded and the error log is empty remove it if [ $rc -eq 0 -a -w "$errlog" ] && is_file_empty "$errlog" ; then rm -f "$errlog" fi return 0 } main() { TST_COUNT=1 for dir in "${test_dirs[@]}" ; do tests=( $(find "${FS_BIND_ROOT}/${dir}" -type f -name 'test*') ) clean_sandbox "$0" || break for t in "${tests[@]}" ; do run_test "$t" local rc=$? if [ $rc -ne 0 ]; then break $rc fi ((TST_COUNT++)) done done rm -rf "${sandbox}" return 0 skipped=$((total - nsucceeded - nfailed)) if [ $nfailed -eq 0 -a $skipped -eq 0 ]; then # Use PASSED for the summary rather than SUCCEEDED to make it # easy to determine 100% success from a calling script summary="PASSED" else # Use FAILED to make it easy to find > 0% failure from a # calling script summary="FAILED" fi cat - <<-EOF ********************************* RESULTS SUMMARY: passed: $nsucceeded/$total failed: $nfailed/$total skipped: $skipped/$total summary: $summary ********************************* EOF } test_setup || exit 1 test_prereqs || exit 1 declare -r FS_BIND_ROOT declare -r TST_TOTAL main #2> "$resdir/errors" 1> "$resdir/summary"