diff options
Diffstat (limited to 'tests/t-local.sh')
-rw-r--r-- | tests/t-local.sh | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/tests/t-local.sh b/tests/t-local.sh new file mode 100644 index 0000000..2ba7ee1 --- /dev/null +++ b/tests/t-local.sh @@ -0,0 +1,196 @@ +# Put test-related bits that are parted-specific here. +# This file is sourced from the testing framework. +sector_size_=${PARTED_SECTOR_SIZE:-512} + +scsi_debug_lock_dir_="$abs_srcdir/scsi_debug.lock" + +require_scsi_debug_module_() +{ + require_udevadm_settle_ + # check for scsi_debug module + modprobe -n scsi_debug || + skip_ "you lack the scsi_debug kernel module" +} + +scsi_debug_modprobe_succeeded_= + +# Always run this cleanup function. +cleanup_final_() { scsi_debug_cleanup_; } + +scsi_debug_cleanup_() +{ + # This function must always release the lock. + # If modprobe succeeded, it must be sure to run rmmod. + if test -n "$scsi_debug_modprobe_succeeded_"; then + # We have to insist. Otherwise, a single rmmod usually fails to remove it, + # due either to "Resource temporarily unavailable" or to + # "Module scsi_debug is in use". + i=0 + udevadm settle + while [ $i -lt 40 ] ; do + rmmod scsi_debug \ + && { test "$VERBOSE" = yes && warn_ $ME_ rmmod scsi_debug...; break; } + sleep .2 || sleep 1 + i=$((i + 1)) + done + udevadm settle + test $i = 40 && framework_failure_ rmmod scsi_debug failed. + fi + rm -fr $scsi_debug_lock_dir_ +} + +# Helper function: wait 2s (via .1s increments) for FILE to appear. +# Usage: wait_for_dev_to_appear_ /dev/sdg +# Return 0 upon success, 1 upon failure. +wait_for_dev_to_appear_() +{ + local file=$1 + local i=0 + local incr=1 + while :; do + udevadm settle + ls "$file" > /dev/null 2>&1 && return 0 + sleep .1 2>/dev/null || { sleep 1; incr=10; } + i=$(expr $i + $incr); test $i = 20 && break + done + return 1 +} + +# Tests that uses "modprobe scsi_debug ..." must not be run in parallel. +scsi_debug_acquire_lock_() +{ + local retries=20 + local lock_timeout_stale_seconds=120 + + # If it was created more than $lock_timeout_stale_seconds ago, remove it. + # FIXME: implement this + + local i=0 + local incr=1 + while :; do + mkdir "$scsi_debug_lock_dir_" && return 0 + sleep .1 2>/dev/null || { sleep 1; incr=10; } + i=$(expr $i + $incr); test $i = $(expr $retries \* 10) && break + done + + warn_ "$ME_: failed to acquire lock: $scsi_debug_lock_dir_" + return 1 +} + +# If there is a scsi_debug device, print the corresponding "sdN" and return 0. +# Otherwise, return 1. +new_sdX_() +{ + local m; m=$(grep -lw scsi_debug /sys/block/sd*/device/model) || return 1 + + # Remove the /sys/block/ prefix, and then the /device/model suffix. + m=${m#/sys/block/} + m=${m%/device/model} + echo "$m" + return 0 +} + +# Create a device using the scsi_debug module with the options passed to +# this function as arguments. Upon success, print the name of the new device. +scsi_debug_setup_() +{ + scsi_debug_acquire_lock_ + + # It is not trivial to determine the name of the device we're creating. + # Record the names of all /sys/block/sd* devices *before* probing: + touch stamp + modprobe scsi_debug opt_blks=64 "$@" || { rm -f stamp; return 1; } + scsi_debug_modprobe_succeeded_=1 + test "$VERBOSE" = yes \ + && echo $ME_ modprobe scsi_debug succeeded 1>&2 + + # Wait up to 2s (via .1s increments) for the list of devices to change. + # Sleeping for a fraction of a second requires GNU sleep, so fall + # back on sleeping 2x1s if that fails. + # FIXME-portability: using "cmp - ..." probably requires GNU cmp. + local incr=1 + local i=0 + local new_dev + while :; do + udevadm settle + new_dev=$(new_sdX_) && break + sleep .1 2>/dev/null || { sleep 1; incr=10; } + i=$(expr $i + $incr); test $i = 20 && break + done + + case $new_dev in + sd[a-z]) ;; + sd[a-z][a-z]) ;; + *) warn_ $ME_ unexpected device name: $new_dev; return 1 ;; + esac + local t=/dev/$new_dev + wait_for_dev_to_appear_ $t + echo $t + return 0 +} + +require_512_byte_sector_size_() +{ + test $sector_size_ = 512 \ + || skip_ FS test with sector size != 512 +} + +peek_() +{ + case $# in 2) ;; *) echo "usage: peek_ FILE 0_BASED_OFFSET" >&2; exit 1;; esac + case $2 in *[^0-9]*) echo "peek_: invalid offset: $2" >&2; exit 1 ;; esac + dd if="$1" bs=1 skip="$2" count=1 +} + +poke_() +{ + case $# in 3) ;; *) echo "usage: poke_ FILE 0_BASED_OFFSET BYTE" >&2; exit 1;; + esac + case $2 in *[^0-9]*) echo "poke_: invalid offset: $2" >&2; exit 1 ;; esac + case $3 in ?) ;; *) echo "poke_: invalid byte: '$3'" >&2; exit 1 ;; esac + printf %s "$3" | dd of="$1" bs=1 seek="$2" count=1 conv=notrunc +} + +# byte 56 of the partition entry is the first byte of its 72-byte name field +gpt1_pte_name_offset_() +{ + local ss=$1 + case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac + expr $ss \* 2 + 56 + return 0 +} + +# Change the name of the first partition in the primary GPT table, +# thus invalidating the PartitionEntryArrayCRC32 checksum. +gpt_corrupt_primary_table_() +{ + case $# in 2) ;; *) echo "$0: expected 2 args, got $#" >&2; return 1;; esac + local dev=$1 + local ss=$2 + case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac + + # get the first byte of the name + local orig_pte_name_byte + orig_pte_name_byte=$(peek_ $dev $(gpt1_pte_name_offset_ $ss)) || return 1 + + local new_byte + test x"$orig_pte_name_byte" = xA && new_byte=B || new_byte=A + + # Replace with a different byte + poke_ $dev $(gpt1_pte_name_offset_ $ss) "$new_byte" || return 1 + + printf %s "$orig_pte_name_byte" + return 0 +} + +gpt_restore_primary_table_() +{ + case $# in 3) ;; *) echo "$0: expected 2 args, got $#" >&2; return 1;; esac + local dev=$1 + local ss=$2 + case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac + local orig_byte=$3 + poke_ $dev $(gpt1_pte_name_offset_ $ss) "$orig_byte" || return 1 +} + +. "$abs_top_srcdir/tests/t-lvm.sh" |