diff options
author | Nicholas D Steeves <nsteeves@gmail.com> | 2016-04-23 00:41:30 +0100 |
---|---|---|
committer | Nicholas D Steeves <nsteeves@gmail.com> | 2016-04-23 00:41:30 +0100 |
commit | cec572daccafa1e912cbed363df6f84687778c6f (patch) | |
tree | 7d99ab9f73d25c1ed8eaf6393f6374edf5316b03 /tests |
btrfs-progs (4.4.1-1.1) unstable; urgency=medium
* Non-maintainer upload.
* New upstream release.
* Rename package to btrfs-progs (Closes: #780081)
* Update standards version to 3.9.7 (no changes needed).
* debian/control: Add "Breaks" per Gianfranco Costamagna's suggestion
* Change lintian override to reflect package rename
* Switch from using postinst and postrm to using triggers
per Christian Seiler's recommendation.
# imported from the archive
Diffstat (limited to 'tests')
70 files changed, 2322 insertions, 0 deletions
diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..be3bda82 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,136 @@ +# Btrfs-progs tests + +Run the tests from the top directory: + +```shell +$ make test +$ make test-fsck +$ make test-convert +``` + +or selectively from the `tests/` directory: + +```shell +$ ./fsck-tests.sh +$ ./misc-tests.sh +``` + +The verbose output of the tests is logged into a file named after the test +category, eg. `fsck-tests-results.txt`. + +## Selective testing + +The test are prefixed by a number for ordering and uniquenes. To run a +particular test use: + +```shell +$ make TEST=MASK test +``` + +where `MASK` is a glob expression that will execute only tests +that match the MASK. Here the test number comes handy: + +```shell +$ make TEST=001\* test-fsck +$ TEST=001\* ./fsck-tests.sh +``` + +will run the first test in fsck-tests subdirectory. + + +## Test structure + +*tests/fsck-tests/:* + + * tests targeted at bugs that are fixable by fsck + +*tests/convert-tests/:* + + * coverage tests of ext2/3/4 and btrfs-convert options + +*tests/fuzz-tests/:* + + * collection of fuzzed or crafted images + * tests that are supposed to run various utilities on the images and not + crash + +*tests/misc-tests/:* + + * anything that does not fit to the above, the test driver script will only + execute `./test.sh` in the test directory + +*tests/common:* + + * script with helpers + +*tests/test.img:* + + * default testing image, the file is never deleted by the scripts but + truncated to 0 bytes, so it keeps it's permissions. It's eg. possible to + host it on NFS, make it `chmod a+w` for root. + + +## Other tuning, environment variables + +### Instrumentation + +It's possible to wrap the tested commands to utilities that might do more +checking or catch failures at runtime. This can be done by setting the +`INSTRUMENT` environment variable: + +```shell +INSTRUMENT=valgrind ./fuzz-tests.sh # in tests/ +make INSTRUMENT=valgrind test-fuzz # in the top directory +``` + +The variable is prepended to the command *unquoted*, all sorts of shell tricks +are possible. + +Note: instrumentation is not applied to privileged commands (anything that uses +the root helper). + +### Verbosity + +Setting the variable `TEST_LOG=tty` will print all commands executed by some of +the wrappers (`run_check` etc), other commands are silent. + +### Permissions + +Some commands require root privileges (to mount/umount, access loop devices). +It is assumed that `sudo` will work in some way (no password, password asked +and cached). Note that instrumentation is not applied in this case, for safety +reasons. You need to modify the test script instead. + +### Cleanup + +The tests are supposed to cleanup after themselves if they pass. In case of +failure, the rest of the tests are skipped and intermediate files, mounts and +loop devices are kept. This should help to investigate the test failure but at +least the mounts and loop devices need to be cleaned before the next run. + +This is partially done by the script `clean-tests.sh`, you may want to check +the loop devices as they are managed on a per-test basis. + +## New test + +1. Pick the category for the new test or fallback to `misc-tests` if not sure. For +an easy start copy an existing `test.sh` script from some test that might be +close to the purpose of your new test. + +* Use the highest unused number in the sequence, write a short descriptive title +and join by dashes `-`. + +* Write a short description of the bug and how it's teste to the comment at the +begining of `test.sh`. + +* Write the test commands, comment anything that's not obvious. + +* Test your test. Use the `TEST` variable to jump right to your test: +```shell +$ make TEST=012\* tests-misc # from top directory +$ TEST=012\* ./misc-tests.sh # from tests/ +``` + +* The commit changelog should reference a commit that either introduced or + fixed the bug (or both). Subject line of the shall mention the name of the + new directory for ease of search, eg. `btrfs-progs: tests: add 012-subvolume-sync-must-wait` diff --git a/tests/clean-tests.sh b/tests/clean-tests.sh new file mode 100755 index 00000000..f7fefdda --- /dev/null +++ b/tests/clean-tests.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# remove all intermediate files from tests + +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +source $TOP/tests/common + +setup_root_helper + +if [ "$BUILD_VERBOSE" = 1 ]; then + verbose=-print +fi + +$SUDO_HELPER umount "$TEST_MNT" &>/dev/null + +if ! cd $TOP/tests; then + echo "ERROR: cannot cd to $TOP/tests" + exit 1 +fi + +find fsck-tests -type f -name '*.restored' $verbose -delete + +# do not remove, the file could have special permissions set +echo -n > test.img diff --git a/tests/common b/tests/common new file mode 100644 index 00000000..61780486 --- /dev/null +++ b/tests/common @@ -0,0 +1,240 @@ +#!/bin/bash +# +# Common routines for all tests +# + +_fail() +{ + echo "$*" | tee -a $RESULTS + exit 1 +} + +# log a message to the results file +_log() +{ + echo "$*" | tee -a $RESULTS +} + +_not_run() +{ + echo " [NOTRUN] $*" + exit 0 +} + +run_check() +{ + echo "############### $@" >> $RESULTS 2>&1 + if [ "$TEST_LOG" = 'tty' ]; then echo "CMD: $@" > /dev/tty; fi + if [ "$1" = 'root_helper' ]; then + "$@" >> $RESULTS 2>&1 || _fail "failed: $@" + else + $INSTRUMENT "$@" >> $RESULTS 2>&1 || _fail "failed: $@" + fi +} + +# same as run_check but the stderr+stdout output is duplicated on stdout and +# can be processed further +run_check_stdout() +{ + 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: $@" + else + $INSTRUMENT "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@" + fi +} + +# same as run_check but does not fail the test, output is logged +run_mayfail() +{ + echo "############### $@" >> $RESULTS 2>&1 + if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mayfail): $@" > /dev/tty; fi + if [ "$1" = 'root_helper' ]; then + "$@" >> $RESULTS 2>&1 + else + $INSTRUMENT "$@" >> $RESULTS 2>&1 + fi + if [ $? != 0 ]; then + echo "failed (ignored): $@" >> $RESULTS + return 1 + fi +} + +check_prereq() +{ + if ! [ -f $TOP/$1 ]; then + _fail "Failed prerequisities: $1"; + fi +} + +check_image() +{ + local image + + image=$1 + echo "testing image $(basename $image)" >> $RESULTS + $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 +} + +# Extract a usable image from packed formats +# - raw btrfs filesystem images, suffix .raw +# - dtto compressed by XZ, suffix .raw.xz +# - meta-dump images with suffix .img +# - dtto compressed by XZ, suffix .img.xz +extract_image() +{ + local image + local cleanme + + image="$1" + case "$image" in + *.img) + rm -f $image.restored + : ;; + *.img.xz) + xz --decompress --keep "$image" || \ + _fail "failed to decompress image $image" >&2 + image=${image%%.xz} + rm -f $image.restored + cleanme=$image + ;; + *.raw) + cp --sparse=auto $image $image.restored + ;; + *.raw.xz) + xz --decompress --keep "$image" || \ + _fail "failed to decompress image $image" >&2 + image=${image%%.xz} + mv "$image" "$image".restored + ;; + esac + + if ! [ -f $image.restored ]; then + echo "restoring image $(basename $image)" >> $RESULTS + $TOP/btrfs-image -r $image $image.restored \ + &>> $RESULTS \ + || _fail "failed to restore image $image" >&2 + fi + + [ -f "$cleanme" ] && rm -f "$cleanme" + + echo "$image.restored" +} + +# Process all image dumps in a given directory +check_all_images() +{ + local dir + local extracted + + dir="$1" + for image in $(find $dir \( -iname '*.img' -o \ + -iname '*.img.xz' -o \ + -iname '*.raw' -o \ + -iname '*.raw.xz' \) | sort) + do + extracted=$(extract_image "$image") + check_image "$extracted" + rm -f "$extracted" + done +} + +# some tests need to mount the recovered image and do verifications call +# 'setup_root_helper' and then check for have_root_helper == 1 if the test +# needs to fail otherwise; using sudo by default for now +SUDO_HELPER= +NEED_SUDO_VALIDATE=unknown +export SUDO_HELPER +export NEED_SUDO_VALIDATE +root_helper() +{ + if [ $UID -eq 0 ]; then + "$@" + else + if [ "$NEED_SUDO_VALIDATE" = 'yes' ]; then + sudo -v -n &>/dev/null || \ + _not_run "Need to validate sudo credentials" + sudo -n "$@" + elif [ "$NEED_SUDO_VALIDATE" = 'no' ]; then + sudo -n /bin/true &> /dev/null || \ + _not_run "Need to validate sudo user settings" + sudo -n "$@" + else + # should not happen + _not_run "Need to validate root privileges" + fi + fi +} + +setup_root_helper() +{ + if [ $UID -eq 0 -o -n "$SUDO_HELPER" ]; then + return + fi + + # Test for old sudo or special settings, which make sudo -v fail even + # if user setting is NOPASSWD + sudo -n /bin/true &>/dev/null && NEED_SUDO_VALIDATE=no + + # Newer sudo or default sudo setting + sudo -v -n &>/dev/null && NEED_SUDO_VALIDATE=yes + + if [ "$NEED_SUDO_VALIDATE" = 'unknown' ]; then + _not_run "Need to validate root privileges" + fi + SUDO_HELPER=root_helper +} + +prepare_test_dev() +{ + # num[K/M/G/T...] + local size="$1" + + [[ "$TEST_DEV" ]] && return + [[ "$size" ]] || size='2G' + + echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \ + $RESULTS + TEST_DEV="$TOP/tests/test.img" + + truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed" +} + +run_check_mount_test_dev() +{ + setup_root_helper + + local loop_opt + if [[ -b "$TEST_DEV" ]]; then + loop_opt="" + elif [[ -f "$TEST_DEV" ]]; then + loop_opt="-o loop" + else + _fail "Invalid \$TEST_DEV: $TEST_DEV" + fi + + [[ -d "$TEST_MNT" ]] || { + _fail "Invalid \$TEST_MNT: $TEST_MNT" + } + + run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT" +} + +run_check_umount_test_dev() +{ + setup_root_helper + run_check $SUDO_HELPER umount "$@" "$TEST_DEV" +} + +init_env() +{ + TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}" + export TEST_MNT + mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; } +} +init_env diff --git a/tests/convert-tests.sh b/tests/convert-tests.sh new file mode 100644 index 00000000..0bfb41f8 --- /dev/null +++ b/tests/convert-tests.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# +# convert ext2/3/4 images to btrfs images, and make sure the results are +# clean. +# + +unset TOP +unset LANG +LANG=C +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +RESULTS="$TOP/tests/convert-tests-results.txt" + +source $TOP/tests/common + +rm -f $RESULTS + +setup_root_helper +prepare_test_dev 256M + +CHECKSUMTMP=$(mktemp --tmpdir btrfs-progs-convert.XXXXXXXXXX) + +convert_test() { + local features + local nodesize + + features="$1" + shift + + if [ -z "$features" ]; then + echo " [TEST/conv] $1, btrfs defaults" + else + echo " [TEST/conv] $1, btrfs $features" + fi + nodesize=$2 + shift 2 + echo "creating ext image with: $*" >> $RESULTS + # TEST_DEV not removed as the file might have special permissions, eg. + # when test image is on NFS and would not be writable for root + run_check truncate -s 0 $TEST_DEV + # 256MB is the smallest acceptable btrfs image. + run_check truncate -s 256M $TEST_DEV + run_check $* -F $TEST_DEV + + # create a file to check btrfs-convert can convert regular file + # correct + run_check_mount_test_dev + run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/test bs=$nodesize \ + count=1 1>/dev/null 2>&1 + run_check_stdout md5sum $TEST_MNT/test > $CHECKSUMTMP + run_check_umount_test_dev + + run_check $TOP/btrfs-convert ${features:+-O "$features"} -N "$nodesize" $TEST_DEV + run_check $TOP/btrfs check $TEST_DEV + run_check $TOP/btrfs-show-super $TEST_DEV + + run_check_mount_test_dev + run_check_stdout md5sum -c $CHECKSUMTMP | + grep -q 'OK' || _fail "file validation failed." + run_check_umount_test_dev +} + +if ! [ -z "$TEST" ]; then + echo " [TEST/conv] skipped all convert tests, TEST=$TEST" + exit 0 +fi + +for feature in '' 'extref' 'skinny-metadata' 'no-holes'; do + convert_test "$feature" "ext2 4k nodesize" 4096 mke2fs -b 4096 + convert_test "$feature" "ext3 4k nodesize" 4096 mke2fs -j -b 4096 + convert_test "$feature" "ext4 4k nodesize" 4096 mke2fs -t ext4 -b 4096 + convert_test "$feature" "ext2 8k nodesize" 8192 mke2fs -b 4096 + convert_test "$feature" "ext3 8k nodesize" 8192 mke2fs -j -b 4096 + convert_test "$feature" "ext4 8k nodesize" 8192 mke2fs -t ext4 -b 4096 + convert_test "$feature" "ext2 16k nodesize" 16384 mke2fs -b 4096 + convert_test "$feature" "ext3 16k nodesize" 16384 mke2fs -j -b 4096 + convert_test "$feature" "ext4 16k nodesize" 16384 mke2fs -t ext4 -b 4096 + convert_test "$feature" "ext2 32k nodesize" 32768 mke2fs -b 4096 + convert_test "$feature" "ext3 32k nodesize" 32768 mke2fs -j -b 4096 + convert_test "$feature" "ext4 32k nodesize" 32768 mke2fs -t ext4 -b 4096 + convert_test "$feature" "ext2 64k nodesize" 65536 mke2fs -b 4096 + convert_test "$feature" "ext3 64k nodesize" 65536 mke2fs -j -b 4096 + convert_test "$feature" "ext4 64k nodesize" 65536 mke2fs -t ext4 -b 4096 +done + +rm $CHECKSUMTMP diff --git a/tests/fsck-tests.sh b/tests/fsck-tests.sh new file mode 100755 index 00000000..2aab4ff2 --- /dev/null +++ b/tests/fsck-tests.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# +# loop through all of our bad images and make sure fsck repairs them properly +# +# It's GPL, same as everything else in this tree. +# + +unset TOP +unset LANG +LANG=C +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +TEST_DEV=${TEST_DEV:-} +RESULTS="$TOP/tests/fsck-tests-results.txt" + +source $TOP/tests/common + +# Allow child test to use $TOP and $RESULTS +export TOP +export RESULTS +# For custom script needs to verfiy recovery +export LANG + +rm -f $RESULTS + +# test rely on corrupting blocks tool +check_prereq btrfs-corrupt-block +check_prereq btrfs-image +check_prereq btrfs + +run_one_test() { + local testname + + testname="$1" + echo " [TEST/fsck] $(basename $testname)" + cd $testname + echo "=== Entering $testname" >> $RESULTS + if [ -x test.sh ]; then + # Type 2 + ./test.sh + if [ $? -ne 0 ]; then + _fail "test failed for case $(basename $testname)" + fi + else + # Type 1 + check_all_images `pwd` + fi + cd $TOP +} + +# Each dir contains one type of error for btrfsck test. +# Each dir must be one of the following 2 types: +# 1) Only btrfs-image dump +# Only contains one or several btrfs-image dumps (.img) +# Each image will be tested by generic test routine +# (btrfsck --repair and btrfsck). +# This is for case that btree-healthy images. +# 2) Custom test script +# This dir contains test.sh which will do custom image +# generation/check/verification. +# This is for case btrfs-image can't dump or case needs extra +# check/verify + +for i in $(find $TOP/tests/fsck-tests -maxdepth 1 -mindepth 1 -type d \ + ${TEST:+-name "$TEST"} | sort) +do + run_one_test "$i" +done diff --git a/tests/fsck-tests/001-bad-file-extent-bytenr/default_case.img b/tests/fsck-tests/001-bad-file-extent-bytenr/default_case.img Binary files differnew file mode 100644 index 00000000..d2a05bb8 --- /dev/null +++ b/tests/fsck-tests/001-bad-file-extent-bytenr/default_case.img diff --git a/tests/fsck-tests/002-bad-transid/default_case.img b/tests/fsck-tests/002-bad-transid/default_case.img Binary files differnew file mode 100644 index 00000000..85bd87cd --- /dev/null +++ b/tests/fsck-tests/002-bad-transid/default_case.img diff --git a/tests/fsck-tests/003-shift-offsets/default_case.img b/tests/fsck-tests/003-shift-offsets/default_case.img Binary files differnew file mode 100644 index 00000000..ce23f673 --- /dev/null +++ b/tests/fsck-tests/003-shift-offsets/default_case.img diff --git a/tests/fsck-tests/004-no-dir-index/default_case.img b/tests/fsck-tests/004-no-dir-index/default_case.img Binary files differnew file mode 100644 index 00000000..6f2483e6 --- /dev/null +++ b/tests/fsck-tests/004-no-dir-index/default_case.img diff --git a/tests/fsck-tests/005-bad-item-offset/default_case.img b/tests/fsck-tests/005-bad-item-offset/default_case.img Binary files differnew file mode 100644 index 00000000..e11e1e32 --- /dev/null +++ b/tests/fsck-tests/005-bad-item-offset/default_case.img diff --git a/tests/fsck-tests/006-bad-root-items/default_case.tar.xz b/tests/fsck-tests/006-bad-root-items/default_case.tar.xz Binary files differnew file mode 100644 index 00000000..125d8e7d --- /dev/null +++ b/tests/fsck-tests/006-bad-root-items/default_case.tar.xz diff --git a/tests/fsck-tests/006-bad-root-items/skinny_case.tar.xz b/tests/fsck-tests/006-bad-root-items/skinny_case.tar.xz Binary files differnew file mode 100644 index 00000000..ed99dc4d --- /dev/null +++ b/tests/fsck-tests/006-bad-root-items/skinny_case.tar.xz diff --git a/tests/fsck-tests/006-bad-root-items/test.sh b/tests/fsck-tests/006-bad-root-items/test.sh new file mode 100755 index 00000000..84332348 --- /dev/null +++ b/tests/fsck-tests/006-bad-root-items/test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +source $TOP/tests/common + +check_prereq btrfs + +echo "extracting image default_case.tar.xz" >> $RESULTS +tar --no-same-owner -xJf default_case.tar.xz || \ + _fail "failed to extract default_case.tar.xz" +check_image test.img + +echo "extracting image skinny_case.tar.xz" >> $RESULTS +tar --no-same-owner -xJf skinny_case.tar.xz || \ + _fail "failed to extract skinny_case.tar.xz" +check_image test.img + +rm test.img diff --git a/tests/fsck-tests/007-bad-offset-snapshots/default_case.img b/tests/fsck-tests/007-bad-offset-snapshots/default_case.img Binary files differnew file mode 100644 index 00000000..b87e9977 --- /dev/null +++ b/tests/fsck-tests/007-bad-offset-snapshots/default_case.img diff --git a/tests/fsck-tests/008-bad-dir-index-name/default_case.img b/tests/fsck-tests/008-bad-dir-index-name/default_case.img Binary files differnew file mode 100644 index 00000000..e004737f --- /dev/null +++ b/tests/fsck-tests/008-bad-dir-index-name/default_case.img diff --git a/tests/fsck-tests/009-no-dir-item-or-index/default_case.img b/tests/fsck-tests/009-no-dir-item-or-index/default_case.img Binary files differnew file mode 100644 index 00000000..d7f22692 --- /dev/null +++ b/tests/fsck-tests/009-no-dir-item-or-index/default_case.img diff --git a/tests/fsck-tests/010-no-rootdir-inode-item/default_case.img b/tests/fsck-tests/010-no-rootdir-inode-item/default_case.img Binary files differnew file mode 100644 index 00000000..6b66fdad --- /dev/null +++ b/tests/fsck-tests/010-no-rootdir-inode-item/default_case.img diff --git a/tests/fsck-tests/011-no-inode-item/default_case.img b/tests/fsck-tests/011-no-inode-item/default_case.img Binary files differnew file mode 100644 index 00000000..352fc021 --- /dev/null +++ b/tests/fsck-tests/011-no-inode-item/default_case.img diff --git a/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz b/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz Binary files differnew file mode 100644 index 00000000..547e5455 --- /dev/null +++ b/tests/fsck-tests/012-leaf-corruption/no_data_extent.tar.xz diff --git a/tests/fsck-tests/012-leaf-corruption/test.sh b/tests/fsck-tests/012-leaf-corruption/test.sh new file mode 100755 index 00000000..a308727d --- /dev/null +++ b/tests/fsck-tests/012-leaf-corruption/test.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +source $TOP/tests/common + +check_prereq btrfs-image + +# Check file list for leaf corruption, no regular/preallocated +# file extent case. +# Corrupted leaf is 20832256, which contains inode 1862~1872 +# +# 1862, ref from leaf 20828160 key 24(DIR_ITEM) +# 1863, ref from leaf 605388 item key 11(DIR_ITEM) +# 1864, no ref to rebuild, no need to rebuild +# 1865, ref from leaf 19767296 key 23(DIR_ITEM) +# 1866-1868 no ref to rebuild, all refs in corrupted leaf +# 1869, ref from leaf 4976640 key 22(DIR_ITEM) +# 1870 no ref to rebuild, all refs in corrupted leaf +# 1871, ref from leaf 19746816 key 38(DIR_ITEM) +# 1872, ref from leaf 19767296 key 14(DIR_ITEM) +# The list format is: +# INO SIZE MODE NAME +# INO: inode number +# SIZE: file size, only checked for regular file +# MODE: raw file mode, get from stat +# NAME: file name +leaf_no_data_ext_list=( + 1862 0 40700 "install.d" + 1862 0 40700 "install.d" + 1863 0 40700 "gdb" + 1865 0 40700 "iptables" + 1869 0 40700 "snmp" + 1871 0 100700 "machine-id" + 1872 0 100700 "adjtime" + 1877 0 40700 "del" +) + +generate_leaf_corrupt_no_data_ext() +{ + dest=$1 + echo "generating leaf_corrupt_no_data_ext.btrfs-image" >> $RESULTS + tar --no-same-owner -xJf ./no_data_extent.tar.xz || \ + _fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image" + $TOP/btrfs-image -r test.img.btrfs-image $dest || \ + _fail "failed to extract leaf_corrupt_no_data_ext.btrfs-image" + + # leaf at 4206592 and 20905984 contains no regular data + # extent, clear its csum to corrupt the leaf. + for x in 4206592 20905984; do + dd if=/dev/zero of=$dest bs=1 count=32 conv=notrunc seek=$x \ + 1>/dev/null 2>&1 + done +} + +check_inode() +{ + path=$1 + ino=$2 + size=$3 + mode=$4 + name=$5 + + # Check whether the inode exists + exists=$($SUDO_HELPER find $path -inum $ino) + if [ -z "$exists" ]; then + _fail "inode $ino not recovered correctly" + fi + + # Check inode type + found_mode=$(printf "%o" 0x$($SUDO_HELPER stat $exists -c %f)) + if [ $found_mode -ne $mode ]; then + echo "$found_mode" + _fail "inode $ino modes not recovered" + fi + + # Check inode size + found_size=$($SUDO_HELPER stat $exists -c %s) + if [ $mode -ne 41700 -a $found_size -ne $size ]; then + _fail "inode $ino size not recovered correctly" + fi + + # Check inode name + if [ "$(basename $exists)" != "$name" ]; then + _fail "inode $ino name not recovered correctly" + else + return 0 + fi +} + +# Check salvaged data in the recovered image +check_leaf_corrupt_no_data_ext() +{ + image=$1 + $SUDO_HELPER mount -o loop $image -o ro $TEST_MNT + + i=0 + while [ $i -lt ${#leaf_no_data_ext_list[@]} ]; do + check_inode $TEST_MNT/lost+found \ + ${leaf_no_data_ext_list[i]} \ + ${leaf_no_data_ext_list[i + 1]} \ + ${leaf_no_data_ext_list[i + 2]} \ + ${leaf_no_data_ext_list[i + 3]} \ + ${leaf_no_data_ext_list[i + 4]} + ((i+=4)) + done + $SUDO_HELPER umount $TEST_MNT +} + +setup_root_helper + +generate_leaf_corrupt_no_data_ext test.img +check_image test.img +check_leaf_corrupt_no_data_ext test.img + +rm test.img +rm test.img.btrfs-image +# Not used, its function is the same as generate_leaf_corrupt_no_data_ext() +rm generate_image.sh diff --git a/tests/fsck-tests/013-extent-tree-rebuild/test.sh b/tests/fsck-tests/013-extent-tree-rebuild/test.sh new file mode 100755 index 00000000..ff7d28e5 --- /dev/null +++ b/tests/fsck-tests/013-extent-tree-rebuild/test.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +source $TOP/tests/common + +check_prereq btrfs-corrupt-block +check_prereq btrfs-debug-tree +check_prereq mkfs.btrfs +check_prereq btrfs + +setup_root_helper +prepare_test_dev 1G + +# test whether fsck can rebuild a corrupted extent tree +test_extent_tree_rebuild() +{ + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $TEST_DEV + + run_check_mount_test_dev + run_check $SUDO_HELPER cp -aR /lib/modules/`uname -r`/ $TEST_MNT + + for i in `seq 1 100`;do + run_check $SUDO_HELPER $TOP/btrfs sub snapshot $TEST_MNT \ + $TEST_MNT/snapaaaaaaa_$i + done + run_check_umount_test_dev + + # get extent root bytenr + extent_root_bytenr=`$SUDO_HELPER $TOP/btrfs-debug-tree -r $TEST_DEV | \ + grep extent | awk '{print $7}'` + if [ -z $extent_root_bytenr ];then + _fail "fail to get extent root bytenr" + fi + + # corrupt extent root node block + run_check $SUDO_HELPER $TOP/btrfs-corrupt-block -l $extent_root_bytenr \ + -b 4096 $TEST_DEV + + $SUDO_HELPER $TOP/btrfs check $TEST_DEV >& /dev/null && \ + _fail "btrfs check should detect failure" + run_check $SUDO_HELPER $TOP/btrfs check --init-extent-tree $TEST_DEV + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +test_extent_tree_rebuild diff --git a/tests/fsck-tests/014-no-extent-info/default_case.img b/tests/fsck-tests/014-no-extent-info/default_case.img Binary files differnew file mode 100644 index 00000000..1ff27434 --- /dev/null +++ b/tests/fsck-tests/014-no-extent-info/default_case.img diff --git a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt b/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt new file mode 100644 index 00000000..9685ed46 --- /dev/null +++ b/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.txt @@ -0,0 +1,254 @@ +URL: https://bugzilla.kernel.org/show_bug.cgi?id=97171 + +The btrfs-image attached to this bug causes the btrfs-userland tool to use +uninitialized memory and ultimately overwrite what seems to be arbitrary memory +locations, dying in the process. Reproduced on x86-64 and i686. + +The kernel seems to be less affected and fails to mount the image. If +/usr/sbin/btrfs is not setuid (which it probably never is), things should be +safe. I didn't investigate further though. + +gdb output: + +GNU gdb (GDB) Fedora 7.8.2-38.fc21 +[... lots of other errors...] +Ignoring transid failure +root 5 inode 260 errors 1000, some csum missing + unresolved ref dir 256 index 7 namelen 5 name b.bin filetype 1 errors 2, no dir index + unresolved ref dir 256 index 7 namelen 5 name b.fin filetype 1 errors 5, no dir item, no inode ref +root 5 inode 261 errors 200, dir isize wrong + +Program received signal SIGSEGV, Segmentation fault. +0x000000000089bb70 in ?? () +(gdb) bt +#0 0x000000000089bb70 in ?? () +#1 0x00007fffffffdb50 in ?? () +#2 0x0000000000894b20 in ?? () +#3 0x00000032629b88e0 in _IO_2_1_stdout_ () from /lib64/libc.so.6 +#4 0x000000000088c010 in ?? () +#5 0x0000000000000000 in ?? () + + +valgrind output: + +[...lots of errors...] +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x436E77: check_block.part.14 (ctree.c:548) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B0E7: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B2AC: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B151: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B162: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B176: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B2CE: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Conditional jump or move depends on uninitialised value(s) +==12638== at 0x4A0B34A: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Use of uninitialised value of size 8 +==12638== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== +==12638== Invalid read of size 1 +==12638== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== Address 0xa25c9de9 is not stack'd, malloc'd or (recently) free'd +==12638== +==12638== +==12638== Process terminating with default action of signal 11 (SIGSEGV) +==12638== Access not within mapped region at address 0xA25C9DE9 +==12638== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==12638== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==12638== by 0x436E99: check_block.part.14 (ctree.c:550) +==12638== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==12638== by 0x438954: btrfs_search_slot (ctree.c:1120) +==12638== by 0x40DD1F: count_csum_range (cmds-check.c:1419) +==12638== by 0x40DD1F: process_file_extent (cmds-check.c:1551) +==12638== by 0x40DD1F: process_one_leaf (cmds-check.c:1617) +==12638== by 0x40DD1F: walk_down_tree (cmds-check.c:1742) +==12638== by 0x40DD1F: check_fs_root (cmds-check.c:3380) +==12638== by 0x40DD1F: check_fs_roots.isra.51 (cmds-check.c:3516) +==12638== by 0x4C64B0F: ??? +==12638== by 0x4C30A2F: ??? +==12638== by 0x4C468CF: ??? +==12638== by 0x32629B88DF: ??? (in /usr/lib64/libc-2.20.so) +==12638== by 0x4C3657F: ??? +==12638== If you believe this happened as a result of a stack +==12638== overflow in your program's main thread (unlikely but +==12638== possible), you can try to increase the size of the +==12638== main thread stack using the --main-stacksize= flag. +==12638== The main thread stack size used in this run was 8388608. +==12638== +==12638== HEAP SUMMARY: +==12638== in use at exit: 46,260 bytes in 56 blocks +==12638== total heap usage: 380 allocs, 324 frees, 218,054 bytes allocated +==12638== +==12638== LEAK SUMMARY: +==12638== definitely lost: 272 bytes in 2 blocks +==12638== indirectly lost: 800 bytes in 8 blocks +==12638== possibly lost: 88 bytes in 1 blocks +==12638== still reachable: 45,100 bytes in 45 blocks +==12638== suppressed: 0 bytes in 0 blocks +==12638== Rerun with --leak-check=full to see details of leaked memory +==12638== +==12638== For counts of detected and suppressed errors, rerun with: -v +==12638== Use --track-origins=yes to see where uninitialised values come from +==12638== ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0) +[1] 12638 segmentation fault valgrind btrfs check btrfs_fukked_memorycorruption.bin diff --git a/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz b/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz Binary files differnew file mode 100644 index 00000000..f3f0944d --- /dev/null +++ b/tests/fsck-tests/015-check-bad-memory-access/bko-97171-btrfs-image.raw.xz diff --git a/tests/fsck-tests/016-wrong-inode-nbytes/default_case.img.xz b/tests/fsck-tests/016-wrong-inode-nbytes/default_case.img.xz Binary files differnew file mode 100644 index 00000000..d513acf5 --- /dev/null +++ b/tests/fsck-tests/016-wrong-inode-nbytes/default_case.img.xz diff --git a/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz b/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz Binary files differnew file mode 100644 index 00000000..10cd4c78 --- /dev/null +++ b/tests/fsck-tests/017-missing-all-file-extent/default_case.img.xz diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz b/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz Binary files differnew file mode 100644 index 00000000..60eb2f97 --- /dev/null +++ b/tests/fsck-tests/018-leaf-crossing-stripes/default_case.raw.xz diff --git a/tests/fsck-tests/018-leaf-crossing-stripes/test.sh b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh new file mode 100755 index 00000000..c453ab5c --- /dev/null +++ b/tests/fsck-tests/018-leaf-crossing-stripes/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +source $TOP/tests/common + +check_prereq btrfs + +image=$(extract_image "./default_case.raw.xz") +run_check_stdout $TOP/btrfs check "$image" 2>&1 | + grep -q "crossing stripe boundary" || + _fail "no expected error message in the output" + +rm -f "$image" diff --git a/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz b/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz Binary files differnew file mode 100644 index 00000000..c35f8bc6 --- /dev/null +++ b/tests/fsck-tests/019-non-skinny-false-alert/default_case.img.xz diff --git a/tests/fsck-tests/019-non-skinny-false-alert/test.sh b/tests/fsck-tests/019-non-skinny-false-alert/test.sh new file mode 100755 index 00000000..a7f8e862 --- /dev/null +++ b/tests/fsck-tests/019-non-skinny-false-alert/test.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# $ btrfs check img +# Checking filesystem on img +# UUID: 17f2bf15-f4c2-4ebc-b1f7-39b7af26257a +# checking extents +# bad extent [29376512, 29392896), type mismatch with chunk +# bad extent [29442048, 29458432), type mismatch with chunk +# bad extent [29589504, 29605888), type mismatch with chunk +# ... +# +# a buggy check leads to the above messages + +source $TOP/tests/common + +check_prereq btrfs + +image=$(extract_image "./default_case.img.xz") +run_check_stdout $TOP/btrfs check "$image" 2>&1 | + grep -q "type mismatch with chunk" && + _fail "unexpected error message in the output" + +rm -f "$image" diff --git a/tests/fuzz-tests.sh b/tests/fuzz-tests.sh new file mode 100755 index 00000000..204dce2d --- /dev/null +++ b/tests/fuzz-tests.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# misc tests on fuzzed or crafted images + +unset TOP +unset LANG +LANG=C +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +TEST_DEV=${TEST_DEV:-} +RESULTS="$TOP/tests/fuzz-tests-results.txt" +IMAGE="$TOP/tests/test.img" + +source $TOP/tests/common + +export TOP +export RESULTS +export LANG +export IMAGE + +rm -f $RESULTS + +check_prereq btrfs + +# The tests are driven by their custom script called 'test.sh' + +for i in $(find $TOP/tests/fuzz-tests -maxdepth 1 -mindepth 1 -type d \ + ${TEST:+-name "$TEST"} | sort) +do + name=$(basename $i) + cd $i + if [ -x test.sh ]; then + echo "=== Entering $i" >> $RESULTS + echo " [TEST/fuzz] $name" + ./test.sh + if [ $? -ne 0 ]; then + _fail "test failed for case $(basename $i)" + fi + fi + cd $TOP +done diff --git a/tests/fuzz-tests/001-simple-unmounted/test.sh b/tests/fuzz-tests/001-simple-unmounted/test.sh new file mode 100755 index 00000000..bf01a3a4 --- /dev/null +++ b/tests/fuzz-tests/001-simple-unmounted/test.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# iterate over all fuzzed images and run 'btrfs check' + +source $TOP/tests/common + +setup_root_helper +check_prereq btrfs + +# redefine the one provided by common +check_image() { + local image + + image=$1 + run_mayfail $TOP/btrfs check "$image" +} + +check_all_images $TOP/tests/fuzz-tests/images + +exit 0 diff --git a/tests/fuzz-tests/images/bad-superblock-1.raw.xz b/tests/fuzz-tests/images/bad-superblock-1.raw.xz Binary files differnew file mode 100644 index 00000000..3d6358f0 --- /dev/null +++ b/tests/fuzz-tests/images/bad-superblock-1.raw.xz diff --git a/tests/fuzz-tests/images/bad-superblock-2.raw.xz b/tests/fuzz-tests/images/bad-superblock-2.raw.xz Binary files differnew file mode 100644 index 00000000..7db7610b --- /dev/null +++ b/tests/fuzz-tests/images/bad-superblock-2.raw.xz diff --git a/tests/fuzz-tests/images/bad-superblock-3.raw.xz b/tests/fuzz-tests/images/bad-superblock-3.raw.xz Binary files differnew file mode 100644 index 00000000..4aa31483 --- /dev/null +++ b/tests/fuzz-tests/images/bad-superblock-3.raw.xz diff --git a/tests/fuzz-tests/images/bad-superblock.txt b/tests/fuzz-tests/images/bad-superblock.txt new file mode 100644 index 00000000..f7dd9aa0 --- /dev/null +++ b/tests/fuzz-tests/images/bad-superblock.txt @@ -0,0 +1,17 @@ +bad-superblock-*.txt + +Crafted images from Jiri Slaby, produced by some symbolic execution framework +that finds unhandled cases at mount time. + +Relevant kernel patches to backport: + +e3540eab29e1b2260bc4b9b3979a49a00e3e3af8 +btrfs: add more checks to btrfs_read_sys_array + +ce7fca5f57ed0fcd7e7b3d7b1a3e1791f8e56fa3 +btrfs: add checks for sys_chunk_array sizes + +75d6ad382bb91f363452119d34238e156589ca2d +btrfs: more superblock checks, lower bounds on devices and sectorsize/nodesize + +(and more from fs/btrfs/super.c) diff --git a/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz Binary files differnew file mode 100644 index 00000000..7848f8b1 --- /dev/null +++ b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.raw.xz diff --git a/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt new file mode 100644 index 00000000..0e829c2e --- /dev/null +++ b/tests/fuzz-tests/images/bko-104131-fsck-oob-read.txt @@ -0,0 +1,31 @@ +URL: https://bugzilla.kernel.org/show_bug.cgi?id=104131 +Hanno Boeck 2015-09-07 07:24:32 UTC + +Created attachment 186941 [details] +malformed btrfs filesystem causing oob read + +The attached malformed filesystem image will cause an invalid heap out of bounds memory read in btrfsck. + +This was found while fuzzing btrfs-progs with american fuzzy lop. + +Stack trace from Address Sanitizer: +==31289==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60f00000f003 at pc 0x0000005d0dbb bp 0x7ffdf444c180 sp 0x7ffdf444c178 +READ of size 8 at 0x60f00000f003 thread T0 + #0 0x5d0dba in btrfs_header_bytenr /mnt/ram/btrfs-progs-v4.1.2/./ctree.h:1797:1 + #1 0x5d0dba in check_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:60 + #2 0x5d0dba in read_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:337 + #3 0x5dc00e in btrfs_setup_chunk_tree_and_device_map /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1169:30 + #4 0x5dcf89 in __open_ctree_fd /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1261:8 + #5 0x5dc50a in open_ctree_fs_info /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:1302:9 + #6 0x52f22f in cmd_check /mnt/ram/btrfs-progs-v4.1.2/cmds-check.c:9333:9 + #7 0x4e7bcc in main /mnt/ram/btrfs-progs-v4.1.2/btrfs.c:245:7 + #8 0x7f98bb101f9f in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.20-r2/work/glibc-2.20/csu/libc-start.c:289 + #9 0x41f748 in _start (/mnt/ram/btrfs/btrfs+0x41f748) + +0x60f00000f003 is located 3 bytes to the right of 176-byte region [0x60f00000ef50,0x60f00000f000) +allocated by thread T0 here: + #0 0x4bade8 in malloc (/mnt/ram/btrfs/btrfs+0x4bade8) + #1 0x622c24 in __alloc_extent_buffer /mnt/ram/btrfs-progs-v4.1.2/extent_io.c:541:7 + #2 0x622c24 in alloc_extent_buffer /mnt/ram/btrfs-progs-v4.1.2/extent_io.c:648 + #3 0x5cf436 in btrfs_find_create_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:186:9 + #4 0x5cf436 in read_tree_block /mnt/ram/btrfs-progs-v4.1.2/disk-io.c:314 diff --git a/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz b/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz Binary files differnew file mode 100644 index 00000000..d24a32f8 --- /dev/null +++ b/tests/fuzz-tests/images/bko-104141-fsck-exception.raw.xz diff --git a/tests/fuzz-tests/images/bko-104141-fsck-exception.txt b/tests/fuzz-tests/images/bko-104141-fsck-exception.txt new file mode 100644 index 00000000..aed91909 --- /dev/null +++ b/tests/fuzz-tests/images/bko-104141-fsck-exception.txt @@ -0,0 +1,9 @@ +URL: https://bugzilla.kernel.org/show_bug.cgi?id=104141 +Hanno Boeck 2015-09-07 07:27:58 UTC + +Created attachment 186951 [details] +malformed filesystem causing floating point exception + +The attacked file will cause a floating point exception in btrfsck. + +This was found while fuzzing btrfs-progs with american fuzzy lop. diff --git a/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt new file mode 100644 index 00000000..f0d81894 --- /dev/null +++ b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.txt @@ -0,0 +1,137 @@ +URL: https://bugzilla.kernel.org/show_bug.cgi?id=97191 +Lukas Lueg 2015-04-23 22:20:35 UTC + +Running btrfs-progs v3.19.1 + +The btrfs-image attached to this bug causes the btrfs-userland tool to +overflow some data structures, leading to unallocated memory being written to +and read from. A segfault results shortly after. Reproduced on x86-64 and +i686. + +The kernel seems to be less affected and fails to mount the image. I didn't +investigate whether the reads/writes could be used to gain control over $EIP. +Since the first invalid write of 8 bytes seems to run into adjacent heap +blocks (crash in unlink()), it may be possible though. + +gdb output: + +Program received signal SIGSEGV, Segmentation fault. +malloc_consolidate (av=av@entry=0x32629b7cc0 <main_arena>) at malloc.c:4151 +4151 unlink(av, p, bck, fwd); +(gdb) bt +#0 malloc_consolidate (av=av@entry=0x32629b7cc0 <main_arena>) at malloc.c:4151 +#1 0x0000003262680628 in _int_malloc (av=av@entry=0x32629b7cc0 <main_arena>, bytes=bytes@entry=4224) at malloc.c:3420 +#2 0x000000326268315e in __GI___libc_malloc (bytes=4224) at malloc.c:2896 +#3 0x0000000000449d15 in __alloc_extent_buffer (tree=0x88c078, bytenr=4288512, blocksize=4096) at extent_io.c:541 +#4 0x000000000044a8b4 in alloc_extent_buffer (tree=0x88c078, bytenr=4288512, blocksize=4096) at extent_io.c:648 +#5 0x000000000043b1a0 in btrfs_find_create_tree_block (root=root@entry=0x895840, bytenr=<optimized out>, + blocksize=<optimized out>) at disk-io.c:159 +#6 0x000000000043ca4e in read_tree_block (root=root@entry=0x895840, bytenr=<optimized out>, blocksize=<optimized out>, + parent_transid=13) at disk-io.c:287 +#7 0x000000000043ccb7 in find_and_setup_root (tree_root=0x88c250, fs_info=<optimized out>, objectid=5, root=0x895840) + at disk-io.c:557 +#8 0x000000000043ce92 in btrfs_read_fs_root_no_cache (fs_info=fs_info@entry=0x88c010, location=location@entry=0x7fffffffd960) + at disk-io.c:640 +#9 0x000000000043d060 in btrfs_read_fs_root (fs_info=fs_info@entry=0x88c010, location=location@entry=0x7fffffffd960) + at disk-io.c:739 +#10 0x000000000043d48c in btrfs_setup_all_roots (fs_info=fs_info@entry=0x88c010, root_tree_bytenr=<optimized out>, + root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE) at disk-io.c:988 +#11 0x000000000043d802 in __open_ctree_fd (fp=fp@entry=3, path=path@entry=0x7fffffffe20d "ramdisk/btrfs_fukked.bin", + sb_bytenr=65536, sb_bytenr@entry=0, root_tree_bytenr=root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE) + at disk-io.c:1199 +#12 0x000000000043d965 in open_ctree_fs_info (filename=0x7fffffffe20d "ramdisk/btrfs_fukked.bin", sb_bytenr=sb_bytenr@entry=0, + root_tree_bytenr=root_tree_bytenr@entry=0, flags=flags@entry=OPEN_CTREE_EXCLUSIVE) at disk-io.c:1231 +#13 0x0000000000427bf5 in cmd_check (argc=1, argv=0x7fffffffdea0) at cmds-check.c:9326 +#14 0x000000000040e5a2 in main (argc=2, argv=0x7fffffffdea0) at btrfs.c:245 + + +valgrind output: + +==32463== Memcheck, a memory error detector +==32463== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. +==32463== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info +==32463== Command: btrfs check ramdisk/btrfs_fukked.bin +==32463== +==32463== Invalid write of size 8 +==32463== at 0x4386FB: btrfs_search_slot (ctree.c:1119) +==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117) +==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== Address 0x4c409f0 is 16 bytes after a block of size 144 alloc'd +==32463== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==32463== by 0x4427AB: btrfs_read_block_groups (extent-tree.c:3162) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== +==32463== Invalid read of size 8 +==32463== at 0x436E70: check_block.part.14 (ctree.c:548) +==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==32463== by 0x438954: btrfs_search_slot (ctree.c:1120) +==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117) +==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== Address 0x4c409f8 is 24 bytes after a block of size 144 in arena "client" +==32463== +==32463== Invalid read of size 4 +==32463== at 0x436E84: UnknownInlinedFun (ctree.h:1605) +==32463== by 0x436E84: UnknownInlinedFun (ctree.h:1612) +==32463== by 0x436E84: check_block.part.14 (ctree.c:550) +==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==32463== by 0x438954: btrfs_search_slot (ctree.c:1120) +==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117) +==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== Address 0x4c409e4 is 4 bytes after a block of size 144 alloc'd +==32463== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==32463== by 0x4427AB: btrfs_read_block_groups (extent-tree.c:3162) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== +==32463== Invalid read of size 1 +==32463== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==32463== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==32463== by 0x436E99: check_block.part.14 (ctree.c:550) +==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==32463== by 0x438954: btrfs_search_slot (ctree.c:1120) +==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117) +==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) +==32463== Address 0x1b1 is not stack'd, malloc'd or (recently) free'd +==32463== +==32463== +==32463== Process terminating with default action of signal 11 (SIGSEGV) +==32463== Access not within mapped region at address 0x1B1 +==32463== at 0x4A0B3A0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==32463== by 0x436E99: UnknownInlinedFun (ctree.h:1613) +==32463== by 0x436E99: check_block.part.14 (ctree.c:550) +==32463== by 0x438954: UnknownInlinedFun (kerncompat.h:91) +==32463== by 0x438954: btrfs_search_slot (ctree.c:1120) +==32463== by 0x4427F7: UnknownInlinedFun (extent-tree.c:3117) +==32463== by 0x4427F7: btrfs_read_block_groups (extent-tree.c:3167) +==32463== by 0x43D4F2: btrfs_setup_all_roots (disk-io.c:983) +==32463== by 0x43D801: __open_ctree_fd (disk-io.c:1199) +==32463== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==32463== by 0x427BF4: cmd_check (cmds-check.c:9326) +==32463== by 0x40E5A1: main (btrfs.c:245) diff --git a/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz Binary files differnew file mode 100644 index 00000000..b2e48c08 --- /dev/null +++ b/tests/fuzz-tests/images/bko-97191-btrfs-image.raw.xz diff --git a/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt new file mode 100644 index 00000000..67f20968 --- /dev/null +++ b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.txt @@ -0,0 +1,54 @@ +URL: https://bugzilla.kernel.org/show_bug.cgi?id=97271 +Lukas Lueg 2015-04-25 20:34:39 UTC + +The attached btrfs-image causes "btrfs check" to write outside of allocated +memory locations and ultimately die due to a segfault. An adjacent heap block's +control structure is overwritten with a `struct extent_buffer *`, which is not +controllable by the user. + +"btrfs version" is v3.19.1. Running "btrfs check" immediately dies with + +*** Error in `btrfs': double free or corruption (!prev): 0x0000000002396ec0 *** +*** Error in `btrfs': malloc(): memory corruption: 0x0000000002396f60 *** + +Debugging with valgrind and gdb gives + +==11670== Invalid write of size 8 +==11670== at 0x4386FB: btrfs_search_slot (ctree.c:1119) +==11670== by 0x44E16E: btrfs_read_chunk_tree (volumes.c:1814) +==11670== by 0x43D654: btrfs_setup_chunk_tree_and_device_map (disk-io.c:1115) +==11670== by 0x43D7D0: __open_ctree_fd (disk-io.c:1190) +==11670== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==11670== by 0x427BF4: cmd_check (cmds-check.c:9326) +==11670== by 0x40E5A1: main (btrfs.c:245) +==11670== Address 0x4c3bb98 is 8 bytes after a block of size 144 alloc'd +==11670== at 0x4A08946: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==11670== by 0x44E133: btrfs_read_chunk_tree (volumes.c:1801) +==11670== by 0x43D654: btrfs_setup_chunk_tree_and_device_map (disk-io.c:1115) +==11670== by 0x43D7D0: __open_ctree_fd (disk-io.c:1190) +==11670== by 0x43D964: open_ctree_fs_info (disk-io.c:1231) +==11670== by 0x427BF4: cmd_check (cmds-check.c:9326) +==11670== by 0x40E5A1: main (btrfs.c:245) + +Program received signal SIGTRAP, Trace/breakpoint trap. +btrfs_search_slot (trans=trans@entry=0x0, root=root@entry=0x4c36d30, key=key@entry=0xffefff830, p=p@entry=0x4c3bb00, + ins_len=ins_len@entry=0, cow=cow@entry=0) at ctree.c:1119 +1119 p->nodes[level] = b; +(gdb) p p->nodes +$1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} +(gdb) p p +$2 = (struct btrfs_path *) 0x4c3bb00 +(gdb) p b +$3 = (struct extent_buffer *) 0x4c3a990 + + +The corresponding part in ctree.c:btrfs_search_slot() seems to fail to check if `level` overflows outside of `node`: + +level = btrfs_header_level(b); +... +if (level != btrfs_header_level(b)) + WARN_ON(1); +level = btrfs_header_level(b); +p->nodes[level] = b; // <- Illegal write + +Maybe the repeated calls to btrfs_header_level() were meant to do something once, they seem to be noise. diff --git a/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz Binary files differnew file mode 100644 index 00000000..3c79edb5 --- /dev/null +++ b/tests/fuzz-tests/images/bko-97271-btrfs-image.raw.xz diff --git a/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt new file mode 100644 index 00000000..bdde4e70 --- /dev/null +++ b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.txt @@ -0,0 +1,30 @@ +URL: http://article.gmane.org/gmane.comp.file-systems.btrfs/50230 +Vegard Nossum, 2015-11-15 + +If sys_array::num_stripes == 0, we hit a BUG_ON during mount: + +BTRFS: device fsid 9006933e-2a9a-44f0-917f-514252aeec2c devid 1 transid 7 /dev/loop0 +BTRFS info (device loop0): disk space caching is enabled +BUG: failure at fs/btrfs/ctree.h:337/btrfs_chunk_item_size()! +Kernel panic - not syncing: BUG! +CPU: 0 PID: 313 Comm: mount Not tainted 4.2.5-00657-ge047887-dirty #25 +Stack: + 637af890 60062489 602aeb2e 604192ba + 60387961 00000011 637af8a0 6038a835 + 637af9c0 6038776b 634ef32b 00000000 +Call Trace: + [<6001c86d>] show_stack+0xfe/0x15b + [<6038a835>] dump_stack+0x2a/0x2c + [<6038776b>] panic+0x13e/0x2b3 + [<6020f099>] btrfs_read_sys_array+0x25d/0x2ff + [<601cfbbe>] open_ctree+0x192d/0x27af + [<6019c2c1>] btrfs_mount+0x8f5/0xb9a + [<600bc9a7>] mount_fs+0x11/0xf3 + [<600d5167>] vfs_kern_mount+0x75/0x11a + [<6019bcb0>] btrfs_mount+0x2e4/0xb9a + [<600bc9a7>] mount_fs+0x11/0xf3 + [<600d5167>] vfs_kern_mount+0x75/0x11a + [<600d710b>] do_mount+0xa35/0xbc9 + [<600d7557>] SyS_mount+0x95/0xc8 + +Fixed by patch (kernel and btrfs-progs): btrfs: handle invalid num_stripes in sys_array diff --git a/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz Binary files differnew file mode 100644 index 00000000..d64fb300 --- /dev/null +++ b/tests/fuzz-tests/images/sys-array-num-stripes-0.raw.xz diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh new file mode 100755 index 00000000..2a7f57c5 --- /dev/null +++ b/tests/misc-tests.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Misc tests + +unset TOP +unset LANG +LANG=C +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +TEST_DEV=${TEST_DEV:-} +RESULTS="$TOP/tests/misc-tests-results.txt" +IMAGE="$TOP/tests/test.img" + +source $TOP/tests/common + +# Allow child test to use $TOP and $RESULTS +export TOP +export RESULTS +# For custom script needs to verfiy recovery +export LANG +# For tests that only use a loop device +export IMAGE + +rm -f $RESULTS + +# test rely on corrupting blocks tool +check_prereq btrfs-corrupt-block +check_prereq btrfs-image +check_prereq btrfstune +check_prereq btrfs + +# The tests are driven by their custom script called 'test.sh' + +for i in $(find $TOP/tests/misc-tests -maxdepth 1 -mindepth 1 -type d \ + ${TEST:+-name "$TEST"} | sort) +do + echo " [TEST/misc] $(basename $i)" + cd $i + echo "=== Entering $i" >> $RESULTS + if [ -x test.sh ]; then + ./test.sh + if [ $? -ne 0 ]; then + _fail "test failed for case $(basename $i)" + fi + fi + cd $TOP +done diff --git a/tests/misc-tests/001-btrfstune-features/test.sh b/tests/misc-tests/001-btrfstune-features/test.sh new file mode 100755 index 00000000..c858d701 --- /dev/null +++ b/tests/misc-tests/001-btrfstune-features/test.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# test btrfstune options that enable filesystem features + +source $TOP/tests/common + +check_prereq btrfs-debug-tree +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfstune +check_prereq btrfs + +setup_root_helper +prepare_test_dev + +# test whether fsck can rebuild a corrupted extent tree +# parameters: +# - option for mkfs.btrfs -O, empty for defaults +# - option for btrfstune +# - string representing the feature in btrfs-show-super dump +test_feature() +{ + local mkfsfeatures + local tuneopt + local sbflag + + mkfsfeatures=${1:+-O ^$1} + tuneopt="$2" + sbflag="$3" + + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $mkfsfeatures $TEST_DEV + if run_check_stdout $TOP/btrfs-show-super $TEST_DEV | \ + grep -q "$sbflag"; then + _fail "FAIL: feature $sbflag must not be set on the base image" + fi + run_check $TOP/btrfstune $tuneopt $TEST_DEV + if ! run_check_stdout $TOP/btrfs-show-super $TEST_DEV | \ + grep -q "$sbflag"; then + _fail "FAIL: feature $sbflag not set" + fi + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +test_feature extref -r EXTENDED_IREF +test_feature skinny-metadata -x SKINNY_METADATA +test_feature no-holes -n NO_HOLES +test_feature '' '-S 1' SEEDING diff --git a/tests/misc-tests/002-uuid-rewrite/test.sh b/tests/misc-tests/002-uuid-rewrite/test.sh new file mode 100755 index 00000000..d84ec6ca --- /dev/null +++ b/tests/misc-tests/002-uuid-rewrite/test.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# test btrfstune uuid rewriting options + +source $TOP/tests/common + +check_prereq btrfs-debug-tree +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfstune +check_prereq btrfs + +prepare_test_dev + +get_fs_uuid() { + local image + + image="$1" + run_check_stdout $TOP/btrfs-show-super "$image" | \ + grep '^fsid' | awk '{print $2}' +} + +test_uuid_random() +{ + local origuuid + + origuuid=11111111-a101-4031-b29a-379d4f8b7a2d + + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \ + --uuid $origuuid \ + --rootdir $TOP/Documentation \ + $TEST_DEV + run_check $TOP/btrfs-show-super "$TEST_DEV" + currentfsid=$(run_check_stdout $TOP/btrfstune -f -u $TEST_DEV | \ + grep -i 'current fsid:' | awk '{print $3}') + if ! [ $currentfsid = $origuuid ]; then + _fail "FAIL: current UUID mismatch" + fi + run_check $TOP/btrfs-show-super "$TEST_DEV" + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +test_uuid_user() +{ + local origuuid + local newuuid + + origuuid=22222222-d324-4f92-80e9-7658bf3b845f + newuuid=33333333-bfc9-4045-9399-a396dc6893b3 + + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \ + --uuid $origuuid \ + --rootdir $TOP/Documentation \ + $TEST_DEV + run_check $TOP/btrfs-show-super "$TEST_DEV" + run_check $TOP/btrfstune -f -U $newuuid \ + $TEST_DEV + # btrfs-show-super is called within get_fs_uuid + fsid=$(get_fs_uuid $TEST_DEV) + if ! [ $fsid = $newuuid ]; then + _fail "FAIL: UUID not rewritten" + fi + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +test_uuid_random +test_uuid_user diff --git a/tests/misc-tests/003-zero-log/test.sh b/tests/misc-tests/003-zero-log/test.sh new file mode 100755 index 00000000..b650930e --- /dev/null +++ b/tests/misc-tests/003-zero-log/test.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# test zero-log + +source $TOP/tests/common + +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfs +prepare_test_dev + +get_log_root() +{ + local image + + image="$1" + $TOP/btrfs-show-super "$image" | \ + grep '^log_root\>' | awk '{print $2}' +} +get_log_root_level() { + local image + + image="$1" + $TOP/btrfs-show-super "$image" | \ + grep '^log_root_level' | awk '{print $2}' +} + +test_zero_log() +{ + # FIXME: we need an image with existing log_root + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \ + --rootdir $TOP/Documentation \ + $TEST_DEV + run_check $TOP/btrfs-show-super $TEST_DEV + if [ "$1" = 'standalone' ]; then + run_check $TOP/btrfs rescue zero-log $TEST_DEV + else + run_check $TOP/btrfs-zero-log $TEST_DEV + fi + log_root=$(get_log_root $TEST_DEV) + log_root_level=$(get_log_root $TEST_DEV) + if [ "$log_root" != 0 ]; then + _fail "FAIL: log_root not reset" + fi + if [ "$log_root_level" != 0 ]; then + _fail "FAIL: log_root_level not reset" + fi + run_check $TOP/btrfs-show-super $TEST_DEV + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +test_zero_log standalone +test_zero_log internal diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh new file mode 100755 index 00000000..88740358 --- /dev/null +++ b/tests/misc-tests/004-shrink-fs/test.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# +# Test getting the minimum size a filesystem can be resized to and verify we +# are able to resize (shrink) it to that size. +# + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs + +setup_root_helper + +# Optionally take id of the device to shrink +shrink_test() +{ + min_size=$(run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal min-dev-size ${1:+--id $1} $TEST_MNT) + min_size=$(echo $min_size | cut -d ' ' -f 1) + echo "min size = ${min_size}" >> $RESULTS + if [ -z "$min_size" ]; then + _fail "Failed to parse minimum size" + fi + run_check $SUDO_HELPER $TOP/btrfs filesystem resize $min_size $TEST_MNT +} + +run_check truncate -s 20G $IMAGE +run_check $TOP/mkfs.btrfs -f $IMAGE +run_check $SUDO_HELPER mount $IMAGE $TEST_MNT +run_check $SUDO_HELPER chmod a+rw $TEST_MNT + +# Create 7 data block groups, each with a size of 1Gb. +for ((i = 1; i <= 7; i++)); do + run_check fallocate -l 1G $TEST_MNT/foo$i +done + +# Make sure they are persisted (all the chunk, device and block group items +# added to the chunk/dev/extent trees). +run_check $TOP/btrfs filesystem sync $TEST_MNT + +# Now remove 3 of those 1G files. This will result in 3 block groups becoming +# unused, which will be automatically deleted by the cleaner kthread, and this +# will result in 3 holes (unallocated space) in the device (each with a size +# of 1Gb). + +run_check rm -f $TEST_MNT/foo2 +run_check rm -f $TEST_MNT/foo4 +run_check rm -f $TEST_MNT/foo6 + +# Sync once to wake up the cleaner kthread which will delete the unused block +# groups - it could have been sleeping when they became unused. Then wait a bit +# to allow the cleaner kthread to delete them and then finally ensure the +# transaction started by the cleaner kthread is committed. +run_check $TOP/btrfs filesystem sync $TEST_MNT +sleep 3 +run_check $TOP/btrfs filesystem sync $TEST_MNT + +# Now attempt to get the minimum size we can resize the filesystem to and verify +# the resize operation succeeds. This size closely matches the sum of the size +# of all the allocated device extents. +for ((i = 1; i <= 3; i++)); do + shrink_test +done + +# Now convert metadata and system chunks to the single profile and check we are +# still able to get a correct minimum size and shrink to that size. +run_check $SUDO_HELPER $TOP/btrfs balance start -mconvert=single \ + -sconvert=single -f $TEST_MNT +for ((i = 1; i <= 3; i++)); do + shrink_test 1 +done + +run_check $SUDO_HELPER umount $TEST_MNT diff --git a/tests/misc-tests/005-convert-progress-thread-crash/test.sh b/tests/misc-tests/005-convert-progress-thread-crash/test.sh new file mode 100755 index 00000000..054069c2 --- /dev/null +++ b/tests/misc-tests/005-convert-progress-thread-crash/test.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# test convert-thread-conflict + +source $TOP/tests/common + +check_prereq btrfs-convert + +mkfs.ext4 -V &>/dev/null || _not_run "mkfs.ext4 not found" +prepare_test_dev 1G + +for ((i = 0; i < 20; i++)); do + echo "loop $i" >>$RESULTS + mkfs.ext4 -F "$TEST_DEV" &>>$RESULTS || _not_run "mkfs.ext4 failed" + run_check $TOP/btrfs-convert "$TEST_DEV" +done diff --git a/tests/misc-tests/006-image-on-missing-device/test.sh b/tests/misc-tests/006-image-on-missing-device/test.sh new file mode 100755 index 00000000..8680a707 --- /dev/null +++ b/tests/misc-tests/006-image-on-missing-device/test.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# test btrfs-image with a missing device (uses loop devices) +# +# - btrfs-image must not loop indefinetelly +# - btrfs-image will expectedly fail to produce the dump + +source $TOP/tests/common + +check_prereq btrfs-show-super +check_prereq btrfs-image +check_prereq mkfs.btrfs +check_prereq btrfs + +ndevs=2 +declare -a devs +dev1= +dev2= + +setup_root_helper + + +# TODO: move the helpers to common + +prepare_devices() +{ + for i in `seq $ndevs`; do + touch img$i + chmod a+rw img$i + truncate -s0 img$i + truncate -s2g img$i + devs[$i]=`run_check_stdout $SUDO_HELPER losetup --find --show img$i` + done +} + +cleanup_devices() +{ + for dev in ${devs[@]}; do + run_mayfail $SUDO_HELPER losetup -d $dev + done + for i in `seq $ndevs`; do + truncate -s0 img$i + done + run_check $SUDO_HELPER losetup --list +} + +test_image_dump() +{ + run_check $SUDO_HELPER $TOP/btrfs check $dev1 + # the output file will be deleted + run_mayfail $SUDO_HELPER $TOP/btrfs-image $dev1 /tmp/test-img.dump +} + +test_run() +{ + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f -d raid1 -m raid1 $dev1 $dev2 + + # we need extents to trigger reading from all devices + run_check $SUDO_HELPER mount $dev1 $TEST_MNT + run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/a bs=1M count=10 + run_check $SUDO_HELPER dd if=/dev/zero of=$TEST_MNT/b bs=4k count=1000 conv=sync + run_check $SUDO_HELPER umount $TEST_MNT + + test_image_dump + run_check btrfs fi show $dev1 + # create a degraded raid1 filesystem, check must succeed + # btrfs-image must not loop + run_mayfail wipefs -a $dev2 + run_check $SUDO_HELPER losetup -d $dev2 + run_check btrfs fi show $dev1 + + test_image_dump +} + +prepare_devices +dev1=${devs[1]} +dev2=${devs[2]} +test_run +cleanup_devices diff --git a/tests/misc-tests/007-subvolume-sync/test.sh b/tests/misc-tests/007-subvolume-sync/test.sh new file mode 100755 index 00000000..a745fb56 --- /dev/null +++ b/tests/misc-tests/007-subvolume-sync/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# test btrfs subvolume run normally with more than one subvolume +# +# - btrfs subvolume must not loop indefinetelly +# - btrfs subvolume return 0 in normal case + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs + +setup_root_helper +prepare_test_dev + +run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$TEST_DEV" +run_check_mount_test_dev + +# to check following thing in both 1 and multiple subvolume case: +# 1: is subvolume sync loop indefinetelly +# 2: is return value right +# +run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol1 +run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol2 +run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol1 +run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol2 +run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT" + +run_check $SUDO_HELPER $TOP/btrfs subvolume create "$TEST_MNT"/mysubvol +run_check $SUDO_HELPER $TOP/btrfs subvolume delete "$TEST_MNT"/mysubvol +run_check $SUDO_HELPER $TOP/btrfs subvolume sync "$TEST_MNT" + +run_check_umount_test_dev diff --git a/tests/misc-tests/008-leaf-crossing-stripes/test.sh b/tests/misc-tests/008-leaf-crossing-stripes/test.sh new file mode 100755 index 00000000..03818062 --- /dev/null +++ b/tests/misc-tests/008-leaf-crossing-stripes/test.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# test if btrfs-convert creates a filesystem without leaf crossing stripes + +source $TOP/tests/common + +check_prereq btrfs-convert +check_prereq btrfs + +# In my test, it happened in 514M~560M, 737M~769M, 929M~917M, +# and HAVE_ERROR=((size + 1) / 2) % 2 if size >= 970 +# +SIZE_FROM=514 +SIZE_END=560 +A_PRIME_NUM=17 +for ((size = SIZE_FROM; size <= SIZE_END; size += A_PRIME_NUM)); do + run_check truncate -s "$size"M "$IMAGE" + run_check mkfs.ext4 -F "$IMAGE" + run_check $TOP/btrfs-convert "$IMAGE" + run_check_stdout $TOP/btrfs check "$IMAGE" 2>&1 | + grep -q "crossing stripe boundary" && + _fail "leaf crossing stripes after btrfs-convert" +done + +# grep will expectedly fail +exit 0 diff --git a/tests/misc-tests/009-subvolume-sync-must-wait/test.sh b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh new file mode 100755 index 00000000..056584e5 --- /dev/null +++ b/tests/misc-tests/009-subvolume-sync-must-wait/test.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Verify that subovolume sync waits until the subvolume is cleaned + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs + +setup_root_helper + +run_check truncate -s 2G $IMAGE +run_check $TOP/mkfs.btrfs -f $IMAGE +run_check $SUDO_HELPER mount $IMAGE $TEST_MNT +run_check $SUDO_HELPER chmod a+rw $TEST_MNT + +cd $TEST_MNT + +for i in `seq 5`; do + run_check dd if=/dev/zero of=file$i bs=1M count=10 +done + +for sn in `seq 4`;do + run_check $SUDO_HELPER $TOP/btrfs subvolume snapshot . snap$sn + for i in `seq 10`; do + run_check dd if=/dev/zero of=snap$sn/file$i bs=1M count=10 + done +done + +run_check $SUDO_HELPER $TOP/btrfs subvolume list . +run_check $SUDO_HELPER $TOP/btrfs subvolume list -d . + +idtodel=`run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal rootid snap3` + +# delete, sync after some time +run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c snap3 +{ sleep 5; run_check $TOP/btrfs filesystem sync $TEST_MNT; } & + +run_check $SUDO_HELPER $TOP/btrfs subvolume sync . $idtodel + +if run_check_stdout $SUDO_HELPER $TOP/btrfs subvolume list -d . | + grep -q "ID $idtodel.*DELETED"; then + _fail "sync did not wait for the subvolume cleanup" +fi + +run_check $TOP/btrfs filesystem sync $TEST_MNT +run_check $SUDO_HELPER $TOP/btrfs subvolume list -d . + +wait +cd .. + +run_check $SUDO_HELPER umount $TEST_MNT diff --git a/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh new file mode 100755 index 00000000..451e453a --- /dev/null +++ b/tests/misc-tests/010-convert-delete-ext2-subvol/test.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# verify that convert rollback finds the ext2_subvolume intact and fails if it +# was partially deleted + +source $TOP/tests/common + +check_prereq btrfs-convert +check_prereq btrfs-debug-tree +check_prereq btrfs + +setup_root_helper +prepare_test_dev + +run_check truncate -s 2G "$TEST_DEV" +run_check mkfs.ext4 -F "$TEST_DEV" +run_check $TOP/btrfs-convert "$TEST_DEV" +run_check $TOP/btrfs-debug-tree "$TEST_DEV" +run_check_mount_test_dev +run_check $SUDO_HELPER $TOP/btrfs subvolume delete -c "$TEST_MNT/ext2_saved" +run_check_umount_test_dev +run_check $TOP/btrfs-debug-tree "$TEST_DEV" +run_check_stdout $TOP/btrfs-convert --rollback "$TEST_DEV" | + grep -q 'is it deleted' || _fail "unexpected rollback" + +exit 0 diff --git a/tests/misc-tests/011-delete-missing-device/test.sh b/tests/misc-tests/011-delete-missing-device/test.sh new file mode 100755 index 00000000..26645f10 --- /dev/null +++ b/tests/misc-tests/011-delete-missing-device/test.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# make sure that 'missing' is accepted for device deletion + +source $TOP/tests/common + +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfs + +ndevs=4 +declare -a devs +dev1= +devtodel= + +setup_root_helper + +prepare_devices() +{ + for i in `seq $ndevs`; do + touch img$i + chmod a+rw img$i + truncate -s0 img$i + truncate -s2g img$i + devs[$i]=`run_check_stdout $SUDO_HELPER losetup --find --show img$i` + done +} + +cleanup_devices() +{ + for dev in ${devs[@]}; do + run_mayfail $SUDO_HELPER losetup -d $dev + done + for i in `seq $ndevs`; do + truncate -s0 img$i + done + run_check $SUDO_HELPER losetup --list +} + +test_do_mkfs() +{ + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $@ ${devs[@]} + run_check $TOP/btrfs-show-super $dev1 + run_check $SUDO_HELPER $TOP/btrfs check $dev1 + run_check $TOP/btrfs filesystem show +} + +test_wipefs() +{ + run_check wipefs -a $devtodel + run_check $SUDO_HELPER losetup -d $devtodel + run_check losetup -a + run_check $TOP/btrfs filesystem show +} +test_delete_missing() +{ + run_check_mount_test_dev -o degraded + run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT + run_check $SUDO_HELPER $TOP/btrfs device delete missing $TEST_MNT + run_check $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT + run_check_umount_test_dev + + run_check_mount_test_dev + local out + out="$(run_check_stdout $SUDO_HELPER $TOP/btrfs filesystem show $TEST_MNT)" + if echo "$out" | grep -q -- "$devtodel"; then + _fail "device $devtodel not deleted" + fi + if echo "$out" | grep -q missing; then + _fail "missing device still present" + fi + run_check_umount_test_dev +} + +prepare_devices +dev1=${devs[1]} +devtodel=${devs[3]} +TEST_DEV=$dev1 + +test_do_mkfs +test_wipefs +test_delete_missing + +cleanup_devices diff --git a/tests/misc-tests/012-find-root-no-result/first_meta_chunk.btrfs-image b/tests/misc-tests/012-find-root-no-result/first_meta_chunk.btrfs-image Binary files differnew file mode 100644 index 00000000..7bf6c509 --- /dev/null +++ b/tests/misc-tests/012-find-root-no-result/first_meta_chunk.btrfs-image diff --git a/tests/misc-tests/012-find-root-no-result/test.sh b/tests/misc-tests/012-find-root-no-result/test.sh new file mode 100755 index 00000000..983a8a1e --- /dev/null +++ b/tests/misc-tests/012-find-root-no-result/test.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Regression test for case btrfs-find-root may print no result on a +# recent fs or balanced fs, whose metadata chunk is the first chunk +# and the only metadata chunk + +source $TOP/tests/common + +check_prereq btrfs-find-root +check_prereq btrfs-image + +run_check $TOP/btrfs-image -r first_meta_chunk.btrfs-image test.img || \ + _fail "failed to extract first_meta_chunk.btrfs-image" + +result=$(run_check_stdout $TOP/btrfs-find-root test.img | sed '/^Superblock/d') + +if [ -z "$result" ]; then + _fail "btrfs-find-root failed to find tree root" +fi + +if ! echo "$result" | grep -q 'Found tree root at'; then + _fail "btrfs-find-root failed to find tree root, unexpected output" +fi + +rm test.img diff --git a/tests/mkfs-tests.sh b/tests/mkfs-tests.sh new file mode 100755 index 00000000..c0635ad1 --- /dev/null +++ b/tests/mkfs-tests.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# mkfs.btrfs tests + +unset TOP +unset LANG +LANG=C +SCRIPT_DIR=$(dirname $(readlink -f $0)) +TOP=$(readlink -f $SCRIPT_DIR/../) +TEST_DEV=${TEST_DEV:-} +RESULTS="$TOP/tests/mkfs-tests-results.txt" +IMAGE="$TOP/tests/test.img" + +source $TOP/tests/common + +# Allow child test to use $TOP and $RESULTS +export TOP +export RESULTS +# For custom script needs to verfiy recovery +export LANG +# For tests that only use a loop device +export IMAGE + +rm -f $RESULTS + +check_prereq mkfs.btrfs +check_prereq btrfs + +# The tests are driven by their custom script called 'test.sh' + +for i in $(find $TOP/tests/mkfs-tests -maxdepth 1 -mindepth 1 -type d \ + ${TEST:+-name "$TEST"} | sort) +do + echo " [TEST/mkfs] $(basename $i)" + cd $i + echo "=== Entering $i" >> $RESULTS + if [ -x test.sh ]; then + ./test.sh + if [ $? -ne 0 ]; then + _fail "test failed for case $(basename $i)" + fi + fi + cd $TOP +done diff --git a/tests/mkfs-tests/001-basic-profiles/test.sh b/tests/mkfs-tests/001-basic-profiles/test.sh new file mode 100755 index 00000000..2747d429 --- /dev/null +++ b/tests/mkfs-tests/001-basic-profiles/test.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# test various blockgroup profile combinations, use loop devices as block +# devices + +source $TOP/tests/common + +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfs + +ndevs=4 +declare -a devs +dev1= + +setup_root_helper + +prepare_devices() +{ + for i in `seq $ndevs`; do + touch img$i + chmod a+rw img$i + truncate -s0 img$i + truncate -s2g img$i + devs[$i]=`run_check_stdout $SUDO_HELPER losetup --find --show img$i` + done +} + +cleanup_devices() +{ + for dev in ${devs[@]}; do + run_check $SUDO_HELPER losetup -d $dev + done + for i in `seq $ndevs`; do + truncate -s0 img$i + done + run_check $SUDO_HELPER losetup --list +} + +test_get_info() +{ + run_check $TOP/btrfs-show-super $dev1 + run_check $SUDO_HELPER $TOP/btrfs check $dev1 + run_check $SUDO_HELPER mount $dev1 $TEST_MNT + run_check $TOP/btrfs filesystem df $TEST_MNT + run_check $SUDO_HELPER $TOP/btrfs filesystem usage $TEST_MNT + run_check $SUDO_HELPER $TOP/btrfs device usage $TEST_MNT + run_check $SUDO_HELPER umount "$TEST_MNT" +} +test_do_mkfs() +{ + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \ + $@ +} + +test_mkfs_single() +{ + test_do_mkfs $@ $dev1 + test_get_info +} +test_mkfs_multi() +{ + test_do_mkfs $@ ${devs[@]} + test_get_info +} + +prepare_devices +dev1=${devs[1]} + +test_mkfs_single +test_mkfs_single -d single -m single +test_mkfs_single -d single -m single --mixed +test_mkfs_single -d single -m dup +test_mkfs_single -d dup -m single +test_mkfs_single -d dup -m dup +test_mkfs_single -d dup -m dup --mixed + +test_mkfs_multi +test_mkfs_multi -d single -m single +test_mkfs_multi -d single -m single --mixed +test_mkfs_multi -d raid0 -m raid0 +test_mkfs_multi -d raid0 -m raid0 --mixed +test_mkfs_multi -d raid1 -m raid1 +test_mkfs_multi -d raid1 -m raid1 --mixed +test_mkfs_multi -d raid10 -m raid10 +test_mkfs_multi -d raid10 -m raid10 --mixed +test_mkfs_multi -d raid5 -m raid5 +test_mkfs_multi -d raid5 -m raid5 --mixed +test_mkfs_multi -d raid6 -m raid6 +test_mkfs_multi -d raid6 -m raid6 --mixed + +cleanup_devices diff --git a/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh new file mode 100755 index 00000000..855fbd18 --- /dev/null +++ b/tests/mkfs-tests/002-no-force-mixed-on-small-volume/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# +# Verify that we do not force mixed block groups on small volumes anymore + +source $TOP/tests/common + +check_prereq mkfs.btrfs + +setup_root_helper + +run_check truncate -s 512M $IMAGE +mixed=$(run_check_stdout $TOP/mkfs.btrfs -n 64k -f $IMAGE | egrep 'Data|Metadata') +echo "$mixed" | grep -q -v 'Data+Metadata:' || _fail "unexpected: created a mixed-bg filesystem" diff --git a/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh new file mode 100755 index 00000000..289d5ff0 --- /dev/null +++ b/tests/mkfs-tests/003-mixed-with-wrong-nodesize/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Mixed mode needs equal sectorsize and nodesize + +source $TOP/tests/common + +check_prereq mkfs.btrfs + +run_check truncate -s 512M $IMAGE +run_mayfail $TOP/mkfs.btrfs -f -M -s 4096 -n 16384 "$IMAGE" && _fail + +exit 0 diff --git a/tests/mkfs-tests/004-rootdir-keeps-size/test.sh b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh new file mode 100755 index 00000000..7038c8ea --- /dev/null +++ b/tests/mkfs-tests/004-rootdir-keeps-size/test.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# make sure that mkfs.btrfs --rootsize does not change size of the image + +source $TOP/tests/common + +check_prereq mkfs.btrfs + +prepare_test_dev + +test_mkfs_with_size() { + local size + local imgsize + local tmp + + size="$1" + run_check truncate -s$size $TEST_DEV + imgsize=$(run_check_stdout stat --format=%s $TEST_DEV) + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f \ + --rootdir $TOP/Documentation \ + $TEST_DEV + tmp=$(run_check_stdout stat --format=%s $TEST_DEV) + if ! [ "$imgsize" = "$tmp" ]; then + _fail "image size changed from $imgsize to $tmp" + fi +} + +test_mkfs_with_size 128M +test_mkfs_with_size 256M +test_mkfs_with_size 512M +test_mkfs_with_size 1G +test_mkfs_with_size 2G diff --git a/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh new file mode 100755 index 00000000..c89ee0e1 --- /dev/null +++ b/tests/mkfs-tests/005-long-device-name-for-ssd/test.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# a long device name must pass the SSD test + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs-show-super + +setup_root_helper +prepare_test_dev + +# prep device +dmname=\ +btrfs-test-with-very-long-name-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +dmdev=/dev/mapper/$dmname + +run_check truncate -s0 img +chmod a+w img +run_check truncate -s2g img + +loopdev=`run_check_stdout $SUDO_HELPER losetup --find --show img` +run_check $SUDO_HELPER dmsetup create $dmname --table "0 1048576 linear $loopdev 0" + +base=`basename "$loopdev"` +rot=/sys/class/block/$base/queue/rotational + +# switch rotational +run_check cat $rot +echo 0 | run_check $SUDO_HELPER tee $rot +run_check cat $rot + +# test +run_check_stdout $SUDO_HELPER $TOP/mkfs.btrfs -f $@ $dmdev | + grep -q 'SSD detected:.*yes' || _fail 'SSD not detected' +run_check $TOP/btrfs-show-super $dmdev + +# cleanup +run_check $SUDO_HELPER dmsetup remove $dmname +run_mayfail $SUDO_HELPER losetup -d $loopdev +run_check truncate -s0 img diff --git a/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g b/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g Binary files differnew file mode 100644 index 00000000..eb057769 --- /dev/null +++ b/tests/mkfs-tests/006-partitioned-loopdev/partition-1g-1g diff --git a/tests/mkfs-tests/006-partitioned-loopdev/test.sh b/tests/mkfs-tests/006-partitioned-loopdev/test.sh new file mode 100755 index 00000000..7c9fb829 --- /dev/null +++ b/tests/mkfs-tests/006-partitioned-loopdev/test.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# recognize partitioned loop devices + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs-show-super + +setup_root_helper + +run_check truncate -s0 img +chmod a+w img +cp partition-1g-1g img +run_check truncate -s2g img + +loopdev=$(run_check_stdout $SUDO_HELPER losetup --partscan --find --show img) +base=$(basename $loopdev) + +# expect partitions named like loop0p1 etc +for looppart in $(ls /dev/$base?*); do + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f $looppart + run_check $TOP/btrfs-show-super $looppart +done + +# cleanup +run_check $SUDO_HELPER losetup -d $loopdev +run_check truncate -s0 img diff --git a/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh new file mode 100755 index 00000000..d5374cbd --- /dev/null +++ b/tests/mkfs-tests/007-mix-nodesize-sectorsize/test.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# iterate over nodesize and sectorsize combinations + +source $TOP/tests/common + +check_prereq btrfs-show-super +check_prereq mkfs.btrfs +check_prereq btrfs + +setup_root_helper +prepare_test_dev + +test_mkfs_single() +{ + run_check $SUDO_HELPER $TOP/mkfs.btrfs -f "$@" $TEST_DEV + run_check $TOP/btrfs-show-super $TEST_DEV + run_check $SUDO_HELPER $TOP/btrfs check $TEST_DEV +} + +# default +test_mkfs_single + +# nodesize >= sectorsize +for nodesize in 4096 8192 16384 32768 65536; do + for sectorsize in 4096 8192 16384 32768 65536; do + [ $nodesize -lt $sectorsize ] && continue + test_mkfs_single -n $nodesize -s $sectorsize -d single -m single + test_mkfs_single -n $nodesize -s $sectorsize -d single -m dup + done +done + +# nodesize, mixed mode +for nodesize in 4k 8k 16k 32k 64k; do + test_mkfs_single -n $nodesize -s $nodesize -d single -m single --mixed + test_mkfs_single -n $nodesize -s $nodesize -d dup -m dup --mixed +done diff --git a/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh new file mode 100755 index 00000000..79cc2b22 --- /dev/null +++ b/tests/mkfs-tests/008-secorsize-nodesize-combination/test.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# test various sectorsize and node size combinations +# including valid and invalid ones +# only do mkfs and fsck check, no mounting as +# sub/multi-pagesize is not supported yet + +source $TOP/tests/common + +check_prereq mkfs.btrfs +check_prereq btrfs + +prepare_test_dev + +# disable mixed bg to avoid sectorsize == nodesize check +features="^mixed-bg" + +# caller need to check whether the combination is valid +do_test() +{ + sectorsize=$1 + nodesize=$2 + run_mayfail $TOP/mkfs.btrfs -O $features -n $nodesize -s $sectorsize \ + $TEST_DEV + ret=$? + if [ $ret == 0 ]; then + run_check $TOP/btrfs check $TEST_DEV + fi + return $ret +} + +# Invalid: Unaligned sectorsize and nodesize +do_test 8191 8191 && _fail + +# Invalid: Aligned sectorsize with unaligned nodesize +do_test 4k 16385 && _fail + +# Invalid: Ungliend sectorsize with aligned nodesize +do_test 8191 16k && _fail + +# Valid: Aligned sectorsize and nodesize +do_test 4k 16k || _fail + +# Invalid: Sectorsize larger than nodesize +do_test 8k 4k && _fail + +# Invalid: too large nodesize +do_test 16k 128k && _fail + +# Valid: large sectorsize +do_test 64k 64k || _fail |