|  | # | 
|  | # Common btrfs specific functions | 
|  | # | 
|  |  | 
|  | _btrfs_get_subvolid() | 
|  | { | 
|  | mnt=$1 | 
|  | name=$2 | 
|  |  | 
|  | $BTRFS_UTIL_PROG sub list $mnt | egrep "\s$name$" | awk '{ print $2 }' | 
|  | } | 
|  |  | 
|  | # _require_btrfs_command <command> [<subcommand>|<option>] | 
|  | # We check for btrfs and (optionally) features of the btrfs command | 
|  | # This function support both subfunction like "inspect-internal dump-tree" and | 
|  | # options like "check --qgroup-report", and also subfunction options like | 
|  | # "subvolume delete --subvolid" | 
|  | _require_btrfs_command() | 
|  | { | 
|  | local cmd=$1 | 
|  | local param=$2 | 
|  | local param_arg=$3 | 
|  | local safe_param | 
|  |  | 
|  | _require_command "$BTRFS_UTIL_PROG" btrfs | 
|  | if [ -z "$1" ]; then | 
|  | return 1; | 
|  | fi | 
|  | $BTRFS_UTIL_PROG $cmd --help &> /dev/null | 
|  | [ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old (must support $cmd)" | 
|  |  | 
|  | test -z "$param" && return | 
|  |  | 
|  | # If $param is an option, replace leading "-"s for grep | 
|  | if [ ${param:0:1} == "-" ]; then | 
|  | safe_param=$(echo $param | sed 's/^-*//') | 
|  | $BTRFS_UTIL_PROG $cmd --help | grep -wq $safe_param || \ | 
|  | _notrun "$BTRFS_UTIL_PROG too old (must support $cmd $param)" | 
|  | return | 
|  | fi | 
|  |  | 
|  | $BTRFS_UTIL_PROG $cmd $param --help &> /dev/null | 
|  | [ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old (must support $cmd $param)" | 
|  |  | 
|  | test -z "$param_arg" && return | 
|  |  | 
|  | # replace leading "-"s for grep | 
|  | safe_param=$(echo $param_arg | sed 's/^-*//') | 
|  | $BTRFS_UTIL_PROG $cmd $param --help | grep -wq $safe_param || \ | 
|  | _notrun "$BTRFS_UTIL_PROG too old (must support $cmd $param $param_arg)" | 
|  | } | 
|  |  | 
|  | # Require extra check on btrfs qgroup numbers | 
|  | _require_btrfs_qgroup_report() | 
|  | { | 
|  | _require_btrfs_command check --qgroup-report | 
|  | touch ${RESULT_DIR}/require_scratch.require_qgroup_report | 
|  | } | 
|  |  | 
|  | _require_btrfs_dump_super() | 
|  | { | 
|  | if [ ! -x "$BTRFS_SHOW_SUPER_PROG" ]; then | 
|  | _require_command "$BTRFS_UTIL_PROG" btrfs | 
|  | if ! $BTRFS_UTIL_PROG inspect-internal dump-super --help >& /dev/null; then | 
|  | _notrun "Missing btrfs-show-super or inspect-internal dump-super" | 
|  | fi | 
|  | BTRFS_SHOW_SUPER_PROG="$BTRFS_UTIL_PROG inspect-internal dump-super" | 
|  | fi | 
|  | } | 
|  |  | 
|  | _run_btrfs_util_prog() | 
|  | { | 
|  | run_check $BTRFS_UTIL_PROG $* | 
|  | } | 
|  |  | 
|  | _require_btrfs_mkfs_feature() | 
|  | { | 
|  | if [ -z $1 ]; then | 
|  | echo "Missing feature name argument for _require_btrfs_mkfs_feature" | 
|  | exit 1 | 
|  | fi | 
|  | feat=$1 | 
|  | $MKFS_BTRFS_PROG -O list-all 2>&1 | \ | 
|  | grep '^[ \t]*'"$feat"'\b' > /dev/null 2>&1 | 
|  | [ $? -eq 0 ] || \ | 
|  | _notrun "Feature $feat not supported in the available version of mkfs.btrfs" | 
|  | } | 
|  |  | 
|  | _require_btrfs_fs_feature() | 
|  | { | 
|  | if [ -z $1 ]; then | 
|  | echo "Missing feature name argument for _require_btrfs_fs_feature" | 
|  | exit 1 | 
|  | fi | 
|  | feat=$1 | 
|  | modprobe btrfs > /dev/null 2>&1 | 
|  | [ -e /sys/fs/btrfs/features/$feat ] || \ | 
|  | _notrun "Feature $feat not supported by the available btrfs version" | 
|  | } | 
|  |  | 
|  | _require_btrfs_fs_sysfs() | 
|  | { | 
|  | modprobe btrfs > /dev/null 2>&1 | 
|  | [ -e /sys/fs/btrfs/features ] || \ | 
|  | _notrun "Sysfs not supported by the available btrfs version" | 
|  |  | 
|  | } | 
|  |  | 
|  | _check_btrfs_filesystem() | 
|  | { | 
|  | device=$1 | 
|  |  | 
|  | # If type is set, we're mounted | 
|  | type=`_fs_type $device` | 
|  | ok=1 | 
|  |  | 
|  | if [ "$type" = "$FSTYP" ]; then | 
|  | # mounted ... | 
|  | mountpoint=`_umount_or_remount_ro $device` | 
|  | fi | 
|  |  | 
|  | if [ -f ${RESULT_DIR}/require_scratch.require_qgroup_report ]; then | 
|  | $BTRFS_UTIL_PROG check $device --qgroup-report > $tmp.qgroup_report 2>&1 | 
|  | if grep -qE "Counts for qgroup.*are different" $tmp.qgroup_report ; then | 
|  | _log_err "_check_btrfs_filesystem: filesystem on $device has wrong qgroup numbers" | 
|  | echo "*** qgroup_report.$FSTYP output ***"	>>$seqres.full | 
|  | cat $tmp.qgroup_report				>>$seqres.full | 
|  | echo "*** qgroup_report.$FSTYP output ***"	>>$seqres.full | 
|  | fi | 
|  | rm -f $tmp.qgroup_report | 
|  | fi | 
|  |  | 
|  | $BTRFS_UTIL_PROG check $device >$tmp.fsck 2>&1 | 
|  | if [ $? -ne 0 ]; then | 
|  | _log_err "_check_btrfs_filesystem: filesystem on $device is inconsistent" | 
|  | echo "*** fsck.$FSTYP output ***"	>>$seqres.full | 
|  | cat $tmp.fsck				>>$seqres.full | 
|  | echo "*** end fsck.$FSTYP output"	>>$seqres.full | 
|  |  | 
|  | ok=0 | 
|  | fi | 
|  | rm -f $tmp.fsck | 
|  |  | 
|  | if [ $ok -eq 0 ]; then | 
|  | echo "*** mount output ***"		>>$seqres.full | 
|  | _mount					>>$seqres.full | 
|  | echo "*** end mount output"		>>$seqres.full | 
|  | elif [ "$type" = "$FSTYP" ]; then | 
|  | # was mounted ... | 
|  | _mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint | 
|  | ok=$? | 
|  | fi | 
|  |  | 
|  | if [ $ok -eq 0 ]; then | 
|  | status=1 | 
|  | if [ "$iam" != "check" ]; then | 
|  | exit 1 | 
|  | fi | 
|  | return 1 | 
|  | fi | 
|  |  | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | _require_btrfs_dev_del_by_devid() | 
|  | { | 
|  | $BTRFS_UTIL_PROG device delete --help | egrep devid > /dev/null 2>&1 | 
|  | [ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old "\ | 
|  | "(must support 'btrfs device delete <devid> /<mnt>')" | 
|  | } | 
|  |  | 
|  | # get btrfs profile configs being tested | 
|  | # | 
|  | # A set of pre-set profile configs are exported via _btrfs_profile_configs | 
|  | # array. Default configs can be overridden by setting BTRFS_PROFILE_CONFIGS | 
|  | # var in the format "metadata_profile:data_profile", multiple configs can be | 
|  | # seperated by space, e.g. | 
|  | # export BTRFS_PROFILE_CONFIGS="raid0:raid0 raid1:raid1 dup:single" | 
|  | _btrfs_get_profile_configs() | 
|  | { | 
|  | if [ "$FSTYP" != "btrfs" ]; then | 
|  | return | 
|  | fi | 
|  |  | 
|  | if [ -z "$BTRFS_PROFILE_CONFIGS" ]; then | 
|  | # Default configurations to test. | 
|  | local configs=( | 
|  | "single:single" | 
|  | "dup:single" | 
|  | "raid0:raid0" | 
|  | "raid1:raid0" | 
|  | "raid1:raid1" | 
|  | "raid10:raid10" | 
|  | "raid5:raid5" | 
|  | "raid6:raid6" | 
|  | ) | 
|  | else | 
|  | # User-provided configurations. | 
|  | local configs=(${BTRFS_PROFILE_CONFIGS[@]}) | 
|  | fi | 
|  |  | 
|  | _btrfs_profile_configs=() | 
|  | for cfg in "${configs[@]}"; do | 
|  | local supported=true | 
|  | local profiles=(${cfg/:/ }) | 
|  | if [ "$1" == "replace" ]; then | 
|  | # We can't do replace with these profiles because they | 
|  | # imply only one device ($SCRATCH_DEV), and we need to | 
|  | # keep $SCRATCH_DEV around for _scratch_mount | 
|  | # and _check_scratch_fs. | 
|  | local unsupported=( | 
|  | "dup" | 
|  | ) | 
|  | elif [ "$1" == "replace-missing" ]; then | 
|  | # We can't replace missing devices with these profiles | 
|  | # because there isn't enough redundancy. | 
|  | local unsupported=( | 
|  | "single" | 
|  | "dup" | 
|  | "raid0" | 
|  | ) | 
|  | else | 
|  | local unsupported=() | 
|  | fi | 
|  | for unsupp in "${unsupported[@]}"; do | 
|  | if [ "${profiles[0]}" == "$unsupp" -o "${profiles[1]}" == "$unsupp" ]; then | 
|  | if [ -z "$BTRFS_PROFILE_CONFIGS" ]; then | 
|  | # For the default config, just omit it. | 
|  | supported=false | 
|  | else | 
|  | # For user-provided config, don't run the test. | 
|  | _notrun "Profile $unsupp not supported for $1" | 
|  | fi | 
|  | fi | 
|  | done | 
|  | if "$supported"; then | 
|  | _btrfs_profile_configs+=("-m ${profiles[0]} -d ${profiles[1]}") | 
|  | fi | 
|  | done | 
|  | export _btrfs_profile_configs | 
|  | } | 
|  |  | 
|  | # stress btrfs by running balance operation in a loop | 
|  | _btrfs_stress_balance() | 
|  | { | 
|  | local options=$@ | 
|  | while true; do | 
|  | _run_btrfs_balance_start $options >> $seqres.full | 
|  | done | 
|  | } | 
|  |  | 
|  | # stress btrfs by creating/mounting/umounting/deleting subvolume in a loop | 
|  | _btrfs_stress_subvolume() | 
|  | { | 
|  | local btrfs_dev=$1 | 
|  | local btrfs_mnt=$2 | 
|  | local subvol_name=$3 | 
|  | local subvol_mnt=$4 | 
|  | local stop_file=$5 | 
|  |  | 
|  | mkdir -p $subvol_mnt | 
|  | while [ ! -e $stop_file ]; do | 
|  | $BTRFS_UTIL_PROG subvolume create $btrfs_mnt/$subvol_name | 
|  | $MOUNT_PROG -o subvol=$subvol_name $btrfs_dev $subvol_mnt | 
|  | $UMOUNT_PROG $subvol_mnt | 
|  | $BTRFS_UTIL_PROG subvolume delete $btrfs_mnt/$subvol_name | 
|  | done | 
|  | } | 
|  |  | 
|  | # stress btrfs by running scrub in a loop | 
|  | _btrfs_stress_scrub() | 
|  | { | 
|  | local btrfs_mnt=$1 | 
|  | while true; do | 
|  | $BTRFS_UTIL_PROG scrub start -B $btrfs_mnt | 
|  | done | 
|  | } | 
|  |  | 
|  | # stress btrfs by defragmenting every file/dir in a loop and compress file | 
|  | # contents while defragmenting if second argument is not "nocompress" | 
|  | _btrfs_stress_defrag() | 
|  | { | 
|  | local btrfs_mnt=$1 | 
|  | local compress=$2 | 
|  |  | 
|  | while true; do | 
|  | if [ "$compress" == "nocompress" ]; then | 
|  | find $btrfs_mnt \( -type f -o -type d \) -exec \ | 
|  | $BTRFS_UTIL_PROG filesystem defrag {} \; | 
|  | else | 
|  | find $btrfs_mnt \( -type f -o -type d \) -exec \ | 
|  | $BTRFS_UTIL_PROG filesystem defrag -clzo {} \; | 
|  | find $btrfs_mnt \( -type f -o -type d \) -exec \ | 
|  | $BTRFS_UTIL_PROG filesystem defrag -czlib {} \; | 
|  | fi | 
|  | done | 
|  | } | 
|  |  | 
|  | # stress btrfs by remounting it with different compression algorithms in a loop | 
|  | # run this with fsstress running at background could exercise the compression | 
|  | # code path and ensure no race when switching compression algorithm with constant | 
|  | # I/O activity. | 
|  | _btrfs_stress_remount_compress() | 
|  | { | 
|  | local btrfs_mnt=$1 | 
|  | while true; do | 
|  | for algo in no zlib lzo; do | 
|  | $MOUNT_PROG -o remount,compress=$algo $btrfs_mnt | 
|  | done | 
|  | done | 
|  | } | 
|  |  | 
|  | # stress btrfs by replacing devices in a loop | 
|  | # Note that at least 3 devices are needed in SCRATCH_DEV_POOL and the last | 
|  | # device should be free(not used by btrfs) | 
|  | _btrfs_stress_replace() | 
|  | { | 
|  | local btrfs_mnt=$1 | 
|  |  | 
|  | # The device number in SCRATCH_DEV_POOL should be at least 3, | 
|  | # one is SCRATCH_DEV, one is to be replaced, one is free device | 
|  | # we won't replace SCRATCH_DEV, see below for reason | 
|  | if [ "`echo $SCRATCH_DEV_POOL | wc -w`" -lt 3 ]; then | 
|  | echo "_btrfs_stress_replace requires at least 3 devices in SCRATCH_DEV_POOL" | 
|  | return | 
|  | fi | 
|  |  | 
|  | # take the last device as the first free_dev | 
|  | local free_dev="`echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $NF}'`" | 
|  |  | 
|  | # free_dev should be really free | 
|  | if $BTRFS_UTIL_PROG filesystem show $btrfs_mnt | grep -q "$free_dev"; then | 
|  | echo "_btrfs_stress_replace: $free_dev is used by btrfs" | 
|  | return | 
|  | fi | 
|  |  | 
|  | # dev_pool is device list being currently used by btrfs (excluding SCRATCH_DEV) | 
|  | # and can be replaced. We don't replace SCRATCH_DEV because it will be used in | 
|  | # _scratch_mount and _check_scratch_fs etc. | 
|  | local dev_pool=`echo $SCRATCH_DEV_POOL | sed -e "s# *$SCRATCH_DEV *##" \ | 
|  | -e "s# *$free_dev *##"` | 
|  |  | 
|  | # set the first device in dev_pool as the first src_dev to be replaced | 
|  | local src_dev=`echo $dev_pool | $AWK_PROG '{print $1}'` | 
|  |  | 
|  | echo "dev_pool=$dev_pool" | 
|  | echo "free_dev=$free_dev, src_dev=$src_dev" | 
|  | while true; do | 
|  | echo "Replacing $src_dev with $free_dev" | 
|  | $BTRFS_UTIL_PROG replace start -fB $src_dev $free_dev $btrfs_mnt | 
|  | if [ $? -ne 0 ]; then | 
|  | # don't update src_dev and free_dev if replace failed | 
|  | continue | 
|  | fi | 
|  | dev_pool="$dev_pool $free_dev" | 
|  | dev_pool=`echo $dev_pool | sed -e "s# *$src_dev *##"` | 
|  | free_dev=$src_dev | 
|  | src_dev=`echo $dev_pool | $AWK_PROG '{print $1}'` | 
|  | done | 
|  | } | 
|  |  | 
|  | # find the right option to force output in bytes, older versions of btrfs-progs | 
|  | # print that by default, newer print human readable numbers with unit suffix | 
|  | _btrfs_qgroup_units() | 
|  | { | 
|  | $BTRFS_UTIL_PROG qgroup show --help 2>&1 | grep -q -- --raw && echo "--raw" | 
|  | } | 
|  |  | 
|  | _btrfs_compression_algos() | 
|  | { | 
|  | echo zlib | 
|  | for feature in /sys/fs/btrfs/features/compress_*; do | 
|  | echo "${feature#/sys/fs/btrfs/features/compress_}" | 
|  | done | 
|  | } | 
|  |  | 
|  | # run btrfs balance start with required --full-balance if available. | 
|  | _run_btrfs_balance_start() | 
|  | { | 
|  | local bal_opt="" | 
|  |  | 
|  | $BTRFS_UTIL_PROG balance start --help | grep -q "full-balance" | 
|  | (( $? == 0 )) && bal_opt="--full-balance" | 
|  |  | 
|  | $BTRFS_UTIL_PROG balance start $bal_opt $* | 
|  | } | 
|  |  | 
|  | #return the sector size of the btrfs scratch fs | 
|  | _scratch_btrfs_sectorsize() | 
|  | { | 
|  | $BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV |\ | 
|  | grep sectorsize | awk '{print $2}' | 
|  | } | 
|  |  | 
|  | _btrfs_supports_forget() | 
|  | { | 
|  | $BTRFS_UTIL_PROG device scan --help | grep -wq forget && \ | 
|  | $BTRFS_UTIL_PROG device scan --forget > /dev/null 2>&1 | 
|  | } | 
|  |  | 
|  | _require_btrfs_forget_or_module_loadable() | 
|  | { | 
|  | _btrfs_supports_forget && return | 
|  |  | 
|  | _require_loadable_fs_module "btrfs" | 
|  | } | 
|  |  | 
|  | _btrfs_forget_or_module_reload() | 
|  | { | 
|  | _btrfs_supports_forget && return | 
|  |  | 
|  | _reload_fs_module "btrfs" | 
|  | } | 
|  |  | 
|  | # Test cases which utilized _btrfs_forget_or_module_reload() must call this | 
|  | # to make sure TEST_DEV can still be mounted. As TEST_DEV can be part of a | 
|  | # multi-device btrfs. | 
|  | _btrfs_rescan_devices() | 
|  | { | 
|  | $BTRFS_UTIL_PROG device scan &> /dev/null | 
|  | } |