diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index ef4858cc7fb2..1a4693b7d777 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -99,7 +99,8 @@ tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit', tags = ['functional', 'channel_program', 'synctask_core'] [tests/functional/checksum] -tests = ['run_sha2_test', 'run_skein_test', 'filetest_001_pos'] +tests = ['run_sha2_test', 'run_skein_test', 'filetest_001_pos', + 'filetest_002_pos'] tags = ['functional', 'checksum'] [tests/functional/clean_mirror] diff --git a/tests/zfs-tests/include/blkdev.shlib b/tests/zfs-tests/include/blkdev.shlib index b34f2c04d743..3f29d4f594a1 100644 --- a/tests/zfs-tests/include/blkdev.shlib +++ b/tests/zfs-tests/include/blkdev.shlib @@ -548,22 +548,37 @@ function list_file_blocks # input_file # # Establish a mapping between vdev ids as shown in a DVA and the - # pathnames they correspond to in ${VDEV_MAP[]}. + # pathnames they correspond to in ${VDEV_MAP[][]}. + # + # The vdev bits in a DVA refer to the top level vdev id. + # ${VDEV_MAP[$id]} is an array of the vdev paths within that vdev. # eval $(zdb -C $pool | awk ' - BEGIN { - printf("typeset VDEV_MAP\n"); - looking = 0; - } - /^ children/ { - id = $1; - looking = 1; - } - /path: / && looking == 1 { - print id" "$2; - looking = 0; - } - ' | sed -n 's/^children\[\([0-9]\)\]: \(.*\)$/VDEV_MAP[\1]=\2/p') + BEGIN { printf "typeset -a VDEV_MAP;" } + function subscript(s) { + # "[#]" is more convenient than the bare "#" + match(s, /\[[0-9]*\]/) + return substr(s, RSTART, RLENGTH) + } + id && !/^ / { + # left a top level vdev + id = 0 + } + id && $1 ~ /^path:$/ { + # found a vdev path; save it in the map + printf "VDEV_MAP%s%s=%s;", id, child, $2 + } + /^ children/ { + # entering a top level vdev + id = subscript($0) + child = "[0]" # default in case there is no nested vdev + printf "typeset -a VDEV_MAP%s;", id + } + /^ children/ { + # entering a nested vdev (e.g. child of a top level mirror) + child = subscript($0) + } + ') # # The awk below parses the output of zdb, printing out the level @@ -571,22 +586,40 @@ function list_file_blocks # input_file # two are converted to decimal in the while loop. 4M is added to # the offset to compensate for the first two labels and boot # block. Lastly, the offset and length are printed in units of - # 512b blocks for ease of use with dd. + # 512B blocks for ease of use with dd. # + typeset level vdev path offset length + if awk -n '' 2>/dev/null; then + # gawk needs -n to decode hex + AWK='awk -n' + else + AWK='awk' + fi log_must zpool sync -f - typeset level path offset length - zdb -ddddd $ds $objnum | awk -F: ' - BEGIN { looking = 0 } - /^Indirect blocks:/ { looking = 1} - /^\t\tsegment / { looking = 0} - /L[0-8]/ && looking == 1 { print $0} - ' | sed -n 's/^.*\(L[0-9]\) \([0-9]*\):\([0-9a-f]*\):\([0-9a-f]*\) .*$/\1 \2 \3 \4/p' | \ - while read level path offset length; do - offset=$((16#$offset)) # Conversion from hex - length=$((16#$length)) - offset="$(((offset + 4 * 1024 * 1024) / 512))" - length="$((length / 512))" - echo "$level ${VDEV_MAP[$path]} $offset $length" + zdb -dddddd $ds $objnum | $AWK -v pad=$((4<<20)) -v bs=512 ' + /^$/ { looking = 0 } + looking { + level = $2 + field = 3 + while (split($field, dva, ":") == 3) { + # top level vdev id + vdev = int(dva[1]) + # offset + 4M label/boot pad in 512B blocks + offset = (int("0x"dva[2]) + pad) / bs + # length in 512B blocks + len = int("0x"dva[3]) / bs + + print level, vdev, offset, len + + ++field + } + } + /^Indirect blocks:/ { looking = 1 } + ' | \ + while read level vdev offset length; do + for path in ${VDEV_MAP[$vdev][@]}; do + echo "$level $path $offset $length" + done done 2>/dev/null } diff --git a/tests/zfs-tests/tests/functional/checksum/Makefile.am b/tests/zfs-tests/tests/functional/checksum/Makefile.am index 3ad48ccd4d96..ddabc0302010 100644 --- a/tests/zfs-tests/tests/functional/checksum/Makefile.am +++ b/tests/zfs-tests/tests/functional/checksum/Makefile.am @@ -12,7 +12,8 @@ dist_pkgdata_SCRIPTS = \ run_edonr_test.ksh \ run_sha2_test.ksh \ run_skein_test.ksh \ - filetest_001_pos.ksh + filetest_001_pos.ksh \ + filetest_002_pos.ksh dist_pkgdata_DATA = \ default.cfg diff --git a/tests/zfs-tests/tests/functional/checksum/filetest_001_pos.ksh b/tests/zfs-tests/tests/functional/checksum/filetest_001_pos.ksh index 0cad8047cdee..615b41f312b6 100755 --- a/tests/zfs-tests/tests/functional/checksum/filetest_001_pos.ksh +++ b/tests/zfs-tests/tests/functional/checksum/filetest_001_pos.ksh @@ -62,6 +62,7 @@ log_assert "Create and read back files with using different checksum algorithms" log_onexit cleanup WRITESZ=1048576 +NWRITES=5 # Get a list of vdevs in our pool set -A array $(get_disklist_fullpath) @@ -75,7 +76,7 @@ while [[ $i -lt ${#CHECKSUM_TYPES[*]} ]]; do type=${CHECKSUM_TYPES[i]} log_must zfs set checksum=$type $TESTPOOL log_must file_write -o overwrite -f $TESTDIR/test_$type \ - -b $WRITESZ -c 5 -d R + -b $WRITESZ -c $NWRITES -d R (( i = i + 1 )) done @@ -96,7 +97,7 @@ while [[ $j -lt ${#CHECKSUM_TYPES[*]} ]]; do type=${CHECKSUM_TYPES[$j]} log_must zfs set checksum=$type $TESTPOOL log_must file_write -o overwrite -f $TESTDIR/test_$type \ - -b $WRITESZ -c 5 -d R + -b $WRITESZ -c $NWRITES -d R # Corrupt the level 0 blocks of this file corrupt_blocks_at_level $TESTDIR/test_$type diff --git a/tests/zfs-tests/tests/functional/checksum/filetest_002_pos.ksh b/tests/zfs-tests/tests/functional/checksum/filetest_002_pos.ksh new file mode 100755 index 000000000000..921a4b392a45 --- /dev/null +++ b/tests/zfs-tests/tests/functional/checksum/filetest_002_pos.ksh @@ -0,0 +1,91 @@ +#! /bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2018, 2019 by Delphix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/include/properties.shlib +. $STF_SUITE/tests/functional/checksum/default.cfg + +# DESCRIPTION: +# Sanity test to make sure checksum algorithms work. +# For each checksum, create a file in the pool using that checksum. Verify +# that there are no checksum errors. Next, for each checksum, create a single +# file in the pool using that checksum, corrupt the file, and verify that we +# correctly catch the checksum errors. +# +# STRATEGY: +# Test 1 +# 1. For each checksum: +# 2. Create a file using the checksum +# 3. Corrupt all level 1 blocks in the file +# 4. Export and import the pool +# 5. Verify that there are checksum errors + +verify_runnable "both" + +function cleanup +{ + rm -fr $TESTDIR/* +} + +log_assert "Test corrupting files at L1 and seeing checksum errors" + +log_onexit cleanup + +WRITESZ=1048576 +NWRITES=5 + +# Get a list of vdevs in our pool +set -A array $(get_disklist_fullpath) + +# Get the first vdev, since we will corrupt it later +firstvdev=${array[0]} + +typeset -i j=1 +while [[ $j -lt ${#CHECKSUM_TYPES[*]} ]]; do + type=${CHECKSUM_TYPES[$j]} + log_must zfs set checksum=$type $TESTPOOL + log_must file_write -o overwrite -f $TESTDIR/test_$type \ + -b $WRITESZ -c $NWRITES -d R + + # Corrupt the level 1 blocks of this file + corrupt_blocks_at_level $TESTDIR/test_$type 1 + + log_must zpool export $TESTPOOL + log_must zpool import $TESTPOOL + + log_mustnot eval "cat $TESTDIR/test_$type >/dev/null" + + cksum=$(zpool status -P -v $TESTPOOL | grep "$firstvdev" | \ + awk '{print $5}') + + log_assert "Checksum '$type' caught $cksum checksum errors" + log_must [ $cksum -ne 0 ] + + rm -f $TESTDIR/test_$type + log_must zpool clear $TESTPOOL + + (( j = j + 1 )) +done