btrfs-progs: tests: fix source path for testsuite
[platform/upstream/btrfs-progs.git] / tests / common
index e653d4c..4b266c5 100644 (file)
@@ -21,6 +21,27 @@ _assert_path()
        exit 1
 }
 
+# $1: this string gets matched to files, absolute or relative path, or a
+# systemwide command available via $PATH
+_is_file_or_command()
+{
+       local msg
+
+       msg="$1"
+       if [ -z "$msg" ]; then
+               return 1
+       fi
+
+       if [ -f "$msg" -o -d "$msg" -o -b "$msg" ]; then
+               return 0
+       fi
+       msg=$(type -p -- "$msg")
+       if [ -f "$msg" ]; then
+               return 0
+       fi
+       return 1
+}
+
 _fail()
 {
        echo "$*" | tee -a "$RESULTS"
@@ -33,14 +54,88 @@ _log()
        echo "$*" | tee -a "$RESULTS"
 }
 
+# copy stdout to log and pass to stdout, eg. another stdout consumer, commands
+# should redirect stderr to stdout if this is consmed by further commands
+_log_stdout()
+{
+       tee -a "$RESULTS"
+}
+
 _not_run()
 {
        echo "    [NOTRUN] $*"
        exit 0
 }
 
+# debugging helper
+_dump_args()
+{
+       local i
+
+       i=1
+       echo "DUMP args for ${FUNCNAME[1]}:"
+       while [ $# -gt 0 ]; do
+               echo "ARG[$i]: $1"
+               i=$(($i+1))
+               shift
+       done
+}
+
+# read arguments, look if we're calling btrfs and if there's a known
+# subcommand, return argument index to insert, taking root helper into
+# consideration, returns 2 for unknown subcommand
+_get_spec_ins()
+{
+       if [ "$1" = 'root_helper' ]; then
+               if [[ $2 =~ /btrfs$ ]]; then
+                       echo -n 4
+                       return
+               fi
+       else
+               if [[ $1 =~ /btrfs$ ]]; then
+                       echo -n 3
+                       return
+               fi
+       fi
+       echo -n 2
+}
+
+# return command-specific arguments if enabled
+_cmd_spec()
+{
+       if [ "$TEST_ENABLE_OVERRIDE" = 'true' ]; then
+               # if defined via common.local, use it, otherwise pass make
+               # arguments
+               if [ "$(type -t _skip_spec)" = 'function' ]; then
+                       if _skip_spec "$@"; then
+                               return
+                       fi
+               fi
+               case "$1" in
+                       check) echo -n "$TEST_ARGS_CHECK" ;;
+               esac
+       fi
+}
+
+# Argument passing magic:
+# the command passed to run_* helpers is inspected, if there's 'btrfs command'
+# found and there are defined additional arguments, they're inserted just after
+# the command name, ie. any arguments in the test could override them.
+#
+# The root helper is recognized.  Unrecognized subcommands or external tools
+# are not affected.
+
 run_check()
 {
+       local spec
+       local ins
+       local cmd
+
+       ins=$(_get_spec_ins "$@")
+       spec=$(($ins-1))
+       cmd=$(eval echo "\${$spec}")
+       spec=$(_cmd_spec "${@:$spec}")
+       set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
        echo "############### $@" >> "$RESULTS" 2>&1
        if [[ $TEST_LOG =~ tty ]]; then echo "CMD: $@" > /dev/tty; fi
        if [ "$1" = 'root_helper' ]; then
@@ -54,12 +149,24 @@ run_check()
 # can be processed further
 run_check_stdout()
 {
+       local spec
+       local ins
+       local cmd
+
+       ins=$(_get_spec_ins "$@")
+       spec=$(($ins-1))
+       cmd=$(eval echo "\${$spec}")
+       spec=$(_cmd_spec "${@:$spec}")
+       set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
        echo "############### $@" >> "$RESULTS" 2>&1
        if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): $@" > /dev/tty; fi
        if [ "$1" = 'root_helper' ]; then
-               "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
+               "$@" 2>&1 | tee -a "$RESULTS"
        else
-               $INSTRUMENT "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
+               $INSTRUMENT "$@" 2>&1 | tee -a "$RESULTS"
+       fi
+       if [ ${PIPESTATUS[0]} -ne 0 ]; then
+               _fail "failed: $@"
        fi
 }
 
@@ -68,12 +175,20 @@ run_check_stdout()
 # output is logged
 run_mayfail()
 {
+       local spec
+       local ins
+       local cmd
        local ret
 
+       ins=$(_get_spec_ins "$@")
+       spec=$(($ins-1))
+       cmd=$(eval echo "\${$spec}")
+       spec=$(_cmd_spec "${@:$spec}")
+       set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
        echo "############### $@" >> "$RESULTS" 2>&1
        if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): $@" > /dev/tty; fi
        if [ "$1" = 'root_helper' ]; then
-               "$@" >> $RESULTS 2>&1
+               "$@" >> "$RESULTS" 2>&1
        else
                $INSTRUMENT "$@" >> "$RESULTS" 2>&1
        fi
@@ -93,11 +208,24 @@ run_mayfail()
 # same as run_check but expects the command to fail, output is logged
 run_mustfail()
 {
+       local spec
+       local ins
+       local cmd
        local msg
 
        msg="$1"
        shift
 
+       if _is_file_or_command "$msg"; then
+               echo "ASSERTION FAIL: 1st argument of run_mustfail must be a message"
+               exit 1
+       fi
+
+       ins=$(_get_spec_ins "$@")
+       spec=$(($ins-1))
+       cmd=$(eval echo "\${$spec}")
+       spec=$(_cmd_spec "${@:$spec}")
+       set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
        echo "############### $@" >> "$RESULTS" 2>&1
        if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): $@" > /dev/tty; fi
        if [ "$1" = 'root_helper' ]; then
@@ -115,10 +243,66 @@ run_mustfail()
        fi
 }
 
+# The first parameter is error message to print if it fails, just like
+# run_must_fail().
+# NOTE: we don't use pipefail to avoid disturbing other script, so here we
+# use a temporary output file.
+# So it doesn't support pipeline in the @cmd
+run_mustfail_stdout()
+{
+       local spec
+       local ins
+       local cmd
+       local msg
+       local ret
+       local tmp_output
+
+       tmp_output=$(mktemp --tmpdir btrfs-progs-test--mustfail-stdtout.XXXXXX)
+
+       msg="$1"
+       shift
+
+       if _is_file_or_command "$msg"; then
+               echo "ASSERTION FAIL: 1st argument of run_mustfail_stdout must be a message"
+               exit 1
+       fi
+
+       ins=$(_get_spec_ins "$@")
+       spec=$(($ins-1))
+       cmd=$(eval echo "\${$spec}")
+       spec=$(_cmd_spec "${@:$spec}")
+       set -- "${@:1:$(($ins-1))}" $spec "${@: $ins}"
+       echo "############### $@" >> "$RESULTS" 2>&1
+       if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): $@" > /dev/tty; fi
+       if [ "$1" = 'root_helper' ]; then
+               "$@" 2>&1 > "$tmp_output"
+       else
+               $INSTRUMENT "$@" 2>&1 > "$tmp_output"
+       fi
+       ret=$?
+
+       cat "$tmp_output" >> "$RESULTS"
+       cat "$tmp_output"
+       rm "$tmp_output"
+
+       if [ "$ret" != 0 ]; then
+               echo "failed (expected): $@" >> "$RESULTS"
+               return 0
+       else
+               echo "succeeded (unexpected!): $@" >> "$RESULTS"
+               _fail "unexpected success: $msg"
+               return 1
+       fi
+}
+
 check_prereq()
 {
-       if ! [ -f "$TOP/$1" ]; then
-               _fail "Failed prerequisites: $1";
+       if [ "$1" = "btrfs-corrupt-block" -o "$1" = "fssum" ]; then
+               if ! [ -f "$INTERNAL_BIN/$1" ]; then
+                       _fail "Failed prerequisites: $INTERNAL_BIN/$1";
+               fi
+       elif ! [ -f "$TOP/$1" ]; then
+               _fail "Failed prerequisites: $TOP/$1";
        fi
 }
 
@@ -136,11 +320,11 @@ check_image()
 
        image=$1
        echo "testing image $(basename $image)" >> "$RESULTS"
-       $TOP/btrfs check "$image" >> "$RESULTS" 2>&1
+       "$TOP/btrfs" check "$image" >> "$RESULTS" 2>&1
        [ $? -eq 0 ] && _fail "btrfs check should have detected corruption"
 
-       run_check $TOP/btrfs check --repair "$image"
-       run_check $TOP/btrfs check "$image"
+       run_check "$TOP/btrfs" check --repair "$image"
+       run_check "$TOP/btrfs" check "$image"
 }
 
 # Extract a usable image from packed formats
@@ -158,7 +342,7 @@ extract_image()
        case "$image" in
        *.img)
                rm -f "$image.restored"
-               ;;
+               ;;
        *.img.xz)
                xz --decompress --keep "$image" || \
                        _fail "failed to decompress image $image" >&2
@@ -202,6 +386,10 @@ check_all_images()
        local extracted
 
        dir="$1"
+       if [ -z "$dir" ]; then
+               dir=.
+       fi
+       _assert_path "$dir"
        for image in $(find "$dir" \( -iname '*.img' -o \
                                -iname '*.img.xz' -o    \
                                -iname '*.raw' -o       \
@@ -264,13 +452,19 @@ prepare_test_dev()
        # num[K/M/G/T...]
        local size="$1"
 
-       [[ "$TEST_DEV" ]] && return
        [[ "$size" ]] || size='2G'
+       # Still truncate it to new size
+       if [ -n "$TEST_DEV" ]; then
+               truncate -s 0 "$TEST_DEV"
+               truncate -s "$size" "$TEST_DEV"
+               return;
+       fi
 
-       echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
+       echo "\$TEST_DEV not given, using $TEST_TOP/test.img as fallback" >> \
                "$RESULTS"
-       TEST_DEV="$TOP/tests/test.img"
+       TEST_DEV="$TEST_TOP/test.img"
 
+       truncate -s 0 "$TEST_DEV"
        truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
 }
 
@@ -291,29 +485,173 @@ run_check_mount_test_dev()
                _fail "Invalid \$TEST_MNT: $TEST_MNT"
        }
 
-       run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
+       run_check $SUDO_HELPER mount -t btrfs $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
 }
 
+# $1-$n: optional paths to unmount, otherwise fallback to TEST_DEV
 run_check_umount_test_dev()
 {
        setup_root_helper
-       run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
+       if [ "$#" = 0 ]; then
+               set -- "$TEST_DEV"
+       fi
+       run_check $SUDO_HELPER umount "$@"
 }
 
 check_kernel_support()
 {
        if ! grep -iq 'btrfs' /proc/filesystems; then
-               echo "WARNING: btrfs filesystem not listed in /proc/filesystems, some tests might fail"
-               return 1
+               run_check $SUDO_HELPER modprobe btrfs
+                       if ! grep -iq 'btrfs' /proc/filesystems; then
+                               echo \
+"WARNING: btrfs filesystem not found in /proc/filesystems, some tests might fail"
+                               return 1
+                       fi
        fi
        return 0
 }
 
+# how many files to create.
+DATASET_SIZE=50
+
+generate_dataset() {
+
+       dataset_type="$1"
+       dirpath=$TEST_MNT/$dataset_type
+       run_check $SUDO_HELPER mkdir -p "$dirpath"
+
+       case "$dataset_type" in
+               small)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+                               count=1 >/dev/null 2>&1
+                       done
+                       ;;
+
+               hardlink)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+                               run_check $SUDO_HELPER ln "$dirpath/$dataset_type.$num" "$dirpath/hlink.$num"
+                       done
+                       ;;
+
+               fast_symlink)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+                               run_check cd "$dirpath" && \
+                                       $SUDO_HELPER ln -s "$dataset_type.$num" "$dirpath/slink.$num" && \
+                                       cd /
+                       done
+                       ;;
+
+               brokenlink)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER ln -s "$dirpath/$dataset_type.$num" "$dirpath/blink.$num"
+                       done
+                       ;;
+
+               perm)
+                       for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000            \
+                               1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000   \
+                               2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000   \
+                               4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
+                               run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$modes"
+                               run_check $SUDO_HELPER chmod "$modes" "$dirpath/$dataset_type.$modes"
+                       done
+                       ;;
+
+               sparse)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+                               count=1 >/dev/null 2>&1
+                               run_check $SUDO_HELPER truncate -s 500K "$dirpath/$dataset_type.$num"
+                               run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
+                               oflag=append conv=notrunc count=1 >/dev/null 2>&1
+                               run_check $SUDO_HELPER truncate -s 800K "$dirpath/$dataset_type.$num"
+                       done
+                       ;;
+
+               acls)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
+                               run_check $SUDO_HELPER setfacl -m "u:root:x" "$dirpath/$dataset_type.$num"
+                               run_check $SUDO_HELPER setfattr -n user.foo -v "bar$num" "$dirpath/$dataset_type.$num"
+                       done
+                       ;;
+
+               fifo)
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER mkfifo "$dirpath/$dataset_type.$num"
+                       done
+                       ;;
+
+               slow_symlink)
+                       long_filename=`date +%s | sha256sum | cut -f1 -d ' '`
+                       run_check $SUDO_HELPER touch "$dirpath/$long_filename"
+                       for num in $(seq 1 "$DATASET_SIZE"); do
+                               run_check $SUDO_HELPER ln -s "$dirpath/$long_filename" "$dirpath/slow_slink.$num"
+                       done
+                       ;;
+               large)
+                       run_check $SUDO_HELPER dd if=/dev/urandom bs=32M count=1 \
+                               of="$dirpath/$dataset_type" >/dev/null 2>&1
+                       ;;
+       esac
+}
+
+# prepare environment for loop devices, set up the following variables
+# - nloopdevs -- number of desired devices
+# - loopdevs  -- array containing paths to all devices (after prepare is called)
+# - loopdev_prefix -- file backed images starting with this string, 'img' by default
+#
+# $1: number of loop devices to be set up
+setup_loopdevs()
+{
+       if [ -z "$1" ]; then
+               _fail "setup_loopdevs needs a number"
+       fi
+       nloopdevs="$1"
+       loopdev_prefix=img
+       declare -a loopdevs
+
+}
+
+# create all loop devices from a given loopdev environment
+prepare_loopdevs()
+{
+       for i in `seq $nloopdevs`; do
+               touch $loopdev_prefix$i
+               chmod a+rw $loopdev_prefix$i
+               truncate -s0 $loopdev_prefix$i
+               truncate -s2g $loopdev_prefix$i
+               loopdevs[$i]=`run_check_stdout $SUDO_HELPER losetup --find --show $loopdev_prefix$i`
+       done
+}
+
+# detach loop devices and reset their size to 0, delete the files afterwards
+cleanup_loopdevs()
+{
+       for dev in ${loopdevs[@]}; do
+               run_check $SUDO_HELPER losetup -d $dev
+       done
+       for i in `seq $nloopdevs`; do
+               truncate -s0 $loopdev_prefix$i
+               rm -- "$loopdev_prefix$i"
+       done
+       run_check $SUDO_HELPER losetup --all
+}
+
 init_env()
 {
-       TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
+       TEST_MNT="${TEST_MNT:-$TEST_TOP/mnt}"
        export TEST_MNT
        mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
 
+       source $TEST_TOP/common.local
+
+       if [ "$TEST_ENABLE_OVERRIDE" = 'true' -a -n "$RESULTS" ]; then
+               echo "INCLUDE common.local" >> "$RESULTS"
+               echo "  check: $TEST_ARGS_CHECK" >> "$RESULTS"
+       fi
 }
 init_env