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