1#!/bin/bash 2 3# 4# Copyright (c) International Business Machines Corp., 2005 5# Authors: Avantika Mathur (mathurav@us.ibm.com) 6# Matt Helsley (matthltc@us.ibm.com) 7# 8# This library is free software; you can redistribute it and/or 9# modify it under the terms of the GNU Lesser General Public 10# License as published by the Free Software Foundation; either 11# version 2.1 of the License, or (at your option) any later version. 12# 13# This library is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16# Lesser General Public License for more details. 17# 18# You should have received a copy of the GNU Lesser General Public 19# License along with this library; if not, write to the Free Software 20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21# 22 23if tst_kvercmp 2 6 15 ; then 24 tst_resm TCONF "System kernel version is less than 2.6.15" 25 tst_resm TCONF "Cannot execute test" 26 exit 0 27fi 28 29 30test_setup() 31{ 32 ####################################################################### 33 ## Configure 34 ####################################################################### 35 dopts='-dEBb' 36 37 ## Remove logged test state depending on results. 0 means do not remove, 38 ## 1 means OK to remove. 39 # rm saved state from tests that appear to have cleaned up properly? 40 rm_ok=1 41 # rm saved state from tests that don't appear to have fully cleaned up? 42 rm_err=0 43 44 ####################################################################### 45 ## Initialize some variables 46 ####################################################################### 47 TCID="$0" 48 TST_COUNT=0 49 50 test_dirs=( move bind rbind regression ) #cloneNS 51 nfailed=0 52 nsucceeded=0 53 54 # set the LTPROOT directory 55 cd `dirname $0` 56 LTPROOT="${PWD}" 57 echo "${LTPROOT}" | grep testscripts > /dev/null 2>&1 58 if [ $? -eq 0 ]; then 59 cd .. 60 LTPROOT="${PWD}" 61 fi 62 63 FS_BIND_ROOT="${LTPROOT}/testcases/bin/fs_bind" 64 65 total=0 # total number of tests 66 for dir in "${test_dirs[@]}" ; do 67 ((total += `ls "${FS_BIND_ROOT}/${dir}/test"* | wc -l`)) 68 done 69 TST_TOTAL=${total} 70 71 # set the PATH to include testcases/bin 72 LTPBIN="${LTPROOT}/testcases/bin" 73 PATH="${PATH}:/usr/sbin:${LTPBIN}:${FS_BIND_ROOT}/bin" 74 75 # Results directory 76 resdir="${LTPROOT}/results/fs_bind" 77 if [ ! -d "${resdir}" ]; then 78 mkdir -p "${resdir}" 2> /dev/null 79 fi 80 81 TMPDIR="${TMPDIR:-/tmp}" 82 # A temporary directory where we can do stuff and that is 83 # safe to remove 84 sandbox="${TMPDIR}/sandbox" 85 86 ERR_MSG="" 87 88 export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL 89 90 if [ ! -d "${resdir}" ]; then 91 tst_brkm TBROK true "$0: failed to make results directory" 92 exit 1 93 fi 94} 95 96test_prereqs() 97{ 98 # Must be root to run the containers testsuite 99 if [ $UID != 0 ]; then 100 tst_brkm TBROK true "FAILED: Must be root to execute this script" 101 exit 1 102 fi 103 104 mkdir "${sandbox}" >& /dev/null 105 if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then 106 tst_brkm TBROK true "$0: failed to make directory \"${sandbox}\"" 107 exit -1 108 fi 109 110 mount --bind "${sandbox}" "${sandbox}" >& /dev/null 111 if [ $? -ne 0 ]; then 112 tst_brkm TBROK true "$0: failed to perform bind mount on directory \"${sandbox}\"" 113 exit 1 114 fi 115 116 mount --make-private "${sandbox}" >& /dev/null 117 if [ $? -ne 0 ]; then 118 tst_brkm TBROK true "$0: failed to make private mountpoint on directory \"${sandbox}\"" 119 exit 1 120 fi 121 122 local mnt_bind=1 123 local mnt_move=1 124 125 pushd "${sandbox}" > /dev/null && { 126 mkdir bind_test move_test && { 127 mount --bind bind_test bind_test && { 128 mnt_bind=0 129 mount --move bind_test move_test && { 130 mnt_move=0 131 umount move_test 132 } || { 133 # bind mount succeeded but move mount 134 # failed 135 umount bind_test 136 } 137 } || { 138 # mount failed -- check if it's because we 139 # don't have privileges we need 140 if [ $? -eq 32 ]; then 141 tst_brkm TBROK true "$0 requires the privilege to use the mount command" 142 exit 32 143 fi 144 } 145 rmdir bind_test move_test 146 } 147 popd > /dev/null 148 } 149 150 if [ ${mnt_bind} -eq 1 -o ${mnt_move} -eq 1 ]; then 151 tst_brkm TBROK true "$0: requires that mount support the --bind and --move options" 152 exit 1 153 fi 154 155 tst_kvercmp 2 6 15 156 X=$? 157 if [ $X -lt 0 ]; then 158 tst_brkm TBROK "$0: failed to get the running kernel version" 159 exit 1 160 elif [ $X -lt 1 ]; then 161 tst_resm TWARN "$0: the remaining tests require 2.6.15 or later" 162 tst_exit 0 163 exit 164 else 165 tst_resm TINFO "$0: kernel >= 2.6.15 detected -- continuing" 166 fi 167 168 mount --make-shared "${sandbox}" > /dev/null 2>&1 || "${FS_BIND_ROOT}/bin/smount" "${sandbox}" shared 169 umount "${sandbox}" || { 170 tst_resm TFAIL "$0: failed to umount simplest shared subtree" 171 exit 1 172 } 173 tst_resm TPASS "$0: umounted simplest shared subtree" 174 175} 176 177# mounts we are concerned with in a well-defined order (helps diff) 178# returns grep return codes 179grep_proc_mounts() 180{ 181 local rc=0 182 183 # Save the pipefail shell option 184 shopt -o -q pipefail 185 local save=$? 186 set -o pipefail 187 188 # Grep /proc/mounts which is often more up-to-date than mounts 189 # We use pipefail because if the grep fails we want to pass that along 190 grep -F "${sandbox}" /proc/mounts | sort -b 191 rc=$? 192 193 # Restore the pipefail shell options 194 [ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail 195 196 return $rc 197} 198 199# Record the mount state 200save_proc_mounts() 201{ 202 touch "$2/proc_mounts.before" >& /dev/null 203 if [ $? -ne 0 ]; then 204 tst_brkm TBROK true "$1: failed to record proc mounts" 205 return 1 206 fi 207 208 grep_proc_mounts 2> /dev/null > "$2/proc_mounts.before" 209 return 0 210} 211 212# Compare mount list after the test with the list from before. 213# If there are no differences then remove the before list and silently 214# return 0. Else print the differences to stderr and return 1. 215check_proc_mounts() 216{ 217 local tname="$1" 218 219 if [ ! -r "$2/proc_mounts.before" ]; then 220 tst_brkm TBROK true "${tname}: Could not find pre-test proc mount list" 221 return 1 222 fi 223 224 grep_proc_mounts 2> /dev/null > "$2/proc_mounts.after" 225 # If the mounts are the same then just return 226 diff ${dopts} -q "$2/proc_mounts.before" "$2/proc_mounts.after" >& /dev/null 227 if [ $? -eq 0 ]; then 228 [ $rm_ok -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 229 return 0 230 fi 231 232 tst_resm TWARN "${tname}: did not properly clean up its proc mounts" 233 diff ${dopts} -U 0 "$2/proc_mounts.before" "$2/proc_mounts.after" | grep -vE '^\@\@' 1>&2 234 [ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 235 return 1 236} 237 238# Undo leftover mounts 239restore_proc_mounts() 240{ 241 #local tname="$1" 242 243 # do lazy umounts -- we're assuming that tests will only leave 244 # new mounts around and will never remove mounts outside the test 245 # directory 246 ( while grep_proc_mounts ; do 247 grep_proc_mounts | awk '{print $2}' | xargs -r -n 1 umount -l 248 done ) >& /dev/null 249 250 # mount list and exit with 0 251 [ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 1>&2 # >& /dev/null 252 return 0 253 # if returning error do this: 254 # tst_brkm TBROK true "${tname}: failed to restore mounts" 255} 256 257# mounts we are concerned with in a well-defined order (helps diff) 258# returns grep return codes 259grep_mounts() 260{ 261 local rc=0 262 263 # Save the pipefail shell option 264 shopt -o -q pipefail 265 local save=$? 266 set -o pipefail 267 268 # Grep mount command output (which tends to come from /etc/mtab) 269 # We use pipefail because if the grep fails we want to pass that along 270 mount | grep -F "${sandbox}" | sort -b 271 rc=$? 272 273 # Restore the pipefail shell options 274 [ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail 275 276 return $rc 277} 278 279# Record the mount state 280save_mounts() 281{ 282 touch "$2/mtab.before" >& /dev/null 283 if [ $? -ne 0 ]; then 284 tst_brkm TBROK true "$1: failed to record mtab mounts" 285 return 1 286 fi 287 288 grep_mounts 2> /dev/null > "$2/mtab.before" 289 return 0 290} 291 292# Compare mount list after the test with the list from before. 293# If there are no differences then remove the before list and silently 294# return 0. Else print the differences to stderr and return 1. 295check_mounts() 296{ 297 local tname="$1" 298 299 if [ ! -r "$2/mtab.before" ]; then 300 tst_brkm TBROK true "${tname}: Could not find pre-test mtab mount list" 301 return 1 302 fi 303 304 grep_mounts 2> /dev/null > "$2/mtab.after" 305 # If the mounts are the same then just return 306 diff ${dopts} -q "$2/mtab.before" "$2/mtab.after" >& /dev/null 307 if [ $? -eq 0 ]; then 308 [ $rm_ok -eq 1 ] && rm -f "$2/mtab."{before,after} 309 return 0 310 fi 311 312 tst_resm TWARN "${tname}: did not properly clean up its mtab mounts" 313 diff ${dopts} -U 0 "$2/mtab.before" "$2/mtab.after" | grep -vE '^\@\@' 1>&2 314 [ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} 315 return 1 316} 317 318# Undo leftover mounts 319restore_mounts() 320{ 321 #local tname="$1" 322 323 # do lazy umounts -- we're assuming that tests will only leave 324 # new mounts around and will never remove mounts outside the test 325 # directory 326 ( while grep_mounts ; do 327 grep_mounts | awk '{print $3}' | xargs -r -n 1 umount -l 328 done ) >& /dev/null 329 330 # mount list and exit with 0 331 [ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} 1>&2 # >& /dev/null 332 return 0 333 # if returning error do this: 334 # tst_brkm TBROK true "${tname}: failed to restore mounts" 335} 336 337# Record the sandbox state 338# We don't save full sandbox state -- just the names of files and dirs present 339save_sandbox() 340{ 341 local when="before" 342 local tname="$1" 343 344 if [ -e "$2/files.before" ]; then 345 if [ -e "$2/files.after" ]; then 346 tst_brkm TBROK true "${tname}: stale catalog of \"${sandbox}\"" 347 return 1 348 fi 349 when="after" 350 fi 351 352 ( find "${sandbox}" -type d -print | sort > "$2/dirs.$when" 353 find "${sandbox}" -type f -print | sort | \ 354 grep -vE '^'"$2"'/(dirs|files)\.(before|after)$' > "$2/files.$when" ) >& /dev/null 355 return 0 356} 357 358# Save sandbox after test and then compare. If the sandbox state is not 359# clean then print the differences to stderr and return 1. Else remove all 360# saved sandbox state and silently return 0 361check_sandbox() 362{ 363 local tname="$1" 364 365 if [ ! -r "$2/files.before" -o ! -r "$2/dirs.before" ]; then 366 tst_brkm TBROK true "${tname} missing saved catalog of \"${sandbox}\"" 367 return 1 368 fi 369 370 save_sandbox "${tname} (check)" "$2" 371 372 ( diff ${dopts} -q "$2/dirs.before" "$2/dirs.after" && \ 373 diff ${dopts} -q "$2/files.before" "$2/files.after" ) >& /dev/null \ 374 && { 375 [ $rm_ok -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} 376 return 0 377 } 378 379 tst_resm TWARN "${tname} did not properly clean up \"${sandbox}\"" 380 diff ${dopts} -U 0 "$2/dirs.before" "$2/dirs.after" 1>&2 381 diff ${dopts} -U 0 "$2/files.before" "$2/files.after" 1>&2 382 [ $rm_err -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} 1>&2 383 return 1 384} 385 386# Robust sandbox cleanup 387clean_sandbox() 388{ 389 local tname="$1" 390 391 { rm -rf "${sandbox}" ; mkdir "${sandbox}" ; } >& /dev/null 392 if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then 393 tst_brkm TBROK true "$tname: failed to make directory \"${sandbox}\"" 394 return 1 395 fi 396 return 0 397} 398 399# Check file for non-whitespace chars 400is_file_empty() 401{ 402 awk '/^[[:space:]]*$/ { next } 403 { exit 1; }' < "$1" 404} 405 406# 407# Run the specified test script. 408# 409# Return 1 if the test was broken but should not stop the remaining test 410# categories from being run. 411# Return 2 if the test was broken and no further tests should be run. 412# Return 0 otherwise (if the test was broken but it shouldn't affect other 413# test runs) 414# Note that this means the return status is not the success or failure of the 415# test itself. 416# 417run_test() 418{ 419 local t="$1" 420 local tname="$(basename "$(dirname "$t")")/$(basename "$t")" 421 local log="$resdir/$tname/log" 422 local errlog="$resdir/$tname/err" 423 local do_break=0 424 425 ERR_MSG="" 426 427 # Pre-test 428 mkdir -p "$resdir/$tname" 429 if [ ! -d "$resdir/$tname" -o ! -x "$resdir/$tname" ]; then 430 tst_brkm TBROK true "$0: can't make or use \"$resdir/$tname\" as a log directory" 431 return 1 432 fi 433 434 save_sandbox "$tname" "$resdir/$tname" || do_break=1 435 save_mounts "$tname" "$resdir/$tname" || do_break=1 436 save_proc_mounts "$tname" "$resdir/$tname" || do_break=1 437 mount --bind "${sandbox}" "${sandbox}" >& /dev/null || do_break=1 438 mount --make-private "${sandbox}" >& /dev/null || do_break=1 439 440 if [ $do_break -eq 1 ]; then 441 tst_brkm TBROK true "$tname: failed to save pre-test state of \"${sandbox}\"" 442 return 2 443 fi 444 pushd "${sandbox}" > /dev/null 445 446 # Run the test 447 ( 448 TCID="$tname" 449 declare -r TST_COUNT 450 export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL 451 "$t" #> "$log" 2> "$errlog" 452 ) 453 local rc=$? 454 TCID="$0" 455 456 # Post-test 457 popd > /dev/null 458 if [ $rc -ne 0 ]; then 459 #echo "FAILED" 460 ((nfailed++)) 461 else 462 #echo "SUCCEEDED" 463 ((nsucceeded++)) 464 fi 465 umount -l "${sandbox}" >& /dev/null 466 check_proc_mounts "$tname" "$resdir/$tname" || \ 467 restore_proc_mounts "$tname" "$resdir/$tname" || do_break=1 468 check_mounts "$tname" "$resdir/$tname" || \ 469 restore_mounts "$tname" "$resdir/$tname" || do_break=1 470 check_sandbox "$tname" "$resdir/$tname" 471 clean_sandbox "$tname" || do_break=1 472 if [ $do_break -eq 1 ]; then 473 tst_brkm TBROK true "$tname: failed to restore pre-test state of \"${sandbox}\"" 474 return 2 475 fi 476 477 # If we succeeded and the error log is empty remove it 478 if [ $rc -eq 0 -a -w "$errlog" ] && is_file_empty "$errlog" ; then 479 rm -f "$errlog" 480 fi 481 return 0 482} 483 484main() 485{ 486 TST_COUNT=1 487 for dir in "${test_dirs[@]}" ; do 488 tests=( $(find "${FS_BIND_ROOT}/${dir}" -type f -name 'test*') ) 489 clean_sandbox "$0" || break 490 for t in "${tests[@]}" ; do 491 run_test "$t" 492 local rc=$? 493 494 if [ $rc -ne 0 ]; then 495 break $rc 496 fi 497 498 ((TST_COUNT++)) 499 done 500 done 501 rm -rf "${sandbox}" 502 return 0 503 504 skipped=$((total - nsucceeded - nfailed)) 505 if [ $nfailed -eq 0 -a $skipped -eq 0 ]; then 506 # Use PASSED for the summary rather than SUCCEEDED to make it 507 # easy to determine 100% success from a calling script 508 summary="PASSED" 509 else 510 # Use FAILED to make it easy to find > 0% failure from a 511 # calling script 512 summary="FAILED" 513 fi 514 cat - <<-EOF 515 ********************************* 516 RESULTS SUMMARY: 517 518 passed: $nsucceeded/$total 519 failed: $nfailed/$total 520 skipped: $skipped/$total 521 summary: $summary 522 523 ********************************* 524 EOF 525} 526 527test_setup || exit 1 528test_prereqs || exit 1 529declare -r FS_BIND_ROOT 530declare -r TST_TOTAL 531main #2> "$resdir/errors" 1> "$resdir/summary" 532