From 55a926a15ff32cbf6bb41f793fbebda7828d760c Mon Sep 17 00:00:00 2001 From: Tommaso Gagliardoni Date: Tue, 2 Sep 2025 11:31:50 +0200 Subject: [PATCH] fix: Fix fragscript sflc legacy --- benchmark-suite/sflc-legacy-fragmentation.sh | 407 ++++++++++--------- 1 file changed, 222 insertions(+), 185 deletions(-) diff --git a/benchmark-suite/sflc-legacy-fragmentation.sh b/benchmark-suite/sflc-legacy-fragmentation.sh index 26b793b..7ae5257 100755 --- a/benchmark-suite/sflc-legacy-fragmentation.sh +++ b/benchmark-suite/sflc-legacy-fragmentation.sh @@ -1,39 +1,42 @@ #!/usr/bin/env bash -# Copyright The Shufflecake Project Authors (2022) -# Copyright The Shufflecake Project Contributors (2022) -# Copyright Contributors to the The Shufflecake Project. - -# See the AUTHORS file at the top-level directory of this distribution and at -# - -# This file is part of the program shufflecake-c, which is part of the Shufflecake -# Project. Shufflecake is a plausible deniability (hidden storage) layer for -# Linux. See . - -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 2 of the License, or (at your option) -# any later version. This program is distributed in the hope that it will be -# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. You should have received a copy of the -# GNU General Public License along with this program. -# If not, see . +#  Copyright The Shufflecake Project Authors (2022) +#  Copyright The Shufflecake Project Contributors (2022) +#  Copyright Contributors to the The Shufflecake Project. + +#  See the AUTHORS file at the top-level directory of this distribution and at +#  + +#  This file is part of the program shufflecake-c, which is part of the Shufflecake  +#  Project. Shufflecake is a plausible deniability (hidden storage) layer for  +#  Linux. See . + +#  This program is free software: you can redistribute it and/or modify it  +#  under the terms of the GNU General Public License as published by the Free  +#  Software Foundation, either version 2 of the License, or (at your option)  +#  any later version. This program is distributed in the hope that it will be  +#  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of  +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General  +#  Public License for more details. You should have received a copy of the  +#  GNU General Public License along with this program.  +#  If not, see . # Fragmentation evaluation script for Shufflecake # Global variables SCRIPTNAME=$(basename "$0") SCRIPT_DIR="$(dirname "$(realpath "$0")")" -LOOP_FILENAME="$SCRIPT_DIR/sflc-legacy-frag-loop-file.img" +LOOP_FILENAME="${SCRIPT_DIR}/sflc-legacy-frag-loop-file.img" LOOP_DEVICE="" +BLOCK_DEVICE="" +SFLCVOLUME="" +MNTPOINT="" TIMEFORMAT='%3R' SFLCPATH="" SFLCNAME="" DMSFLC_INSTALLED=false VOLSIZE=0 -NUMPOINTS=11 # number of samples, max 65536 +NUMPOINTS=21 # number of samples, max 65536 FSTYPE="ext4" # fragmentation performance can depend on FS type in theory, so change it here if you want to test others @@ -45,36 +48,36 @@ NC='\033[0m' # No color # Help print_help() { -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars +#         xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   79 chars echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" echo " " echo "This script is used to evaluate Shufflecake Legacy volume fragmentation." echo "This script is part of the Shufflecake benchmark suite." echo "Shufflecake is a plausible deniability (hidden storage) layer for Linux." - echo -e "Please visit ${BLUE}https://www.shufflecake.net${NC} for more info and documentation." + echo -e "Visit ${BLUE}https://www.shufflecake.net${NC} for more info and documentation." echo " " echo "This script requires root because it operates on block devices, please run it " echo -e "with ${BLUE}sudo${NC}. It does the following:" echo "1) Creates a Shufflecake Legacy device with two volumes." - echo "2) Opens the second (hidden) volume, formats it with $FSTYPE and mounts it." + echo "2) Opens the second (hidden) volume, formats it with ${FSTYPE} and mounts it." echo "3) Performs incremental random write up to filling the space." echo "4) After every write round, checks fragmentation status and reports it." echo "5) Unmounts and closes the used volume." echo " " -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars +#         xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   79 chars echo "You must have already compiled and installed Shufflecake in order to run this." echo "The script will search for Shufflecake either in the default installation " echo "directory, or in the current directory, or in the parent directory (one level" echo -e "above this). If the module ${BLUE}dm-sflc${NC} is not loaded, the script " echo "will load it, execute, and then unload it." echo " " -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars +#         xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   79 chars echo "You can pass the path to a block device as an optional argument, otherwise the " echo "script will ask for one. If no path is provided, the script will create a 1 GiB" echo "local file and use it to back a loop device as a virtual block device to be " echo "formatted with the appropriate tools. The file will be removed at the end." echo " " -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars +#         xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   79 chars echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" echo " " exit 0 @@ -85,16 +88,49 @@ bpx() { if [ -z "$1" ]; then echo "BPX: Paused. Press any key to continue..." >&2 else - echo -e "BPX: $1. Press any key to continue..." >&2 + echo "BPX: $1. Press any key to continue..." >&2 fi read -n1 -s } # Show usage usage() { - echo -e "Use ${BLUE}${SCRIPTNAME} --help${NC} for usage and help." + echo -e "Use ${BLUE}${SCRIPTNAME}${NC} --help for usage and help." } +# Clean up function, called by trap on exit +cleanup() { + echo "Exiting and cleaning..." + + # Unmount filesystem if mounted + if [ -n "$MNTPOINT" ] && grep -qs "$MNTPOINT" /proc/mounts; then + echo "Unmounting \"$MNTPOINT\" ..." + umount "$MNTPOINT" + rmdir "$MNTPOINT" + fi + + # Close shufflecake volume if open. + if [ -n "$SFLCVOLUME" ] && [ -b "$SFLCVOLUME" ]; then + echo "Closing Shufflecake device on \"$BLOCK_DEVICE\" ..." + "$SFLCNAME" close "$BLOCK_DEVICE" &> /dev/null + fi + + # Unload kernel module if we loaded it + unload_dmsflc + + # Detach loop device if created + if [[ -n "$LOOP_DEVICE" ]]; then + echo "Detaching \"$LOOP_DEVICE\" ..." + losetup -d "$LOOP_DEVICE" + echo "Deleting \"$LOOP_FILENAME\" ..." + rm -f "$LOOP_FILENAME" + echo "Loop device detached and backing file deleted." + fi +} + +# Set trap to run cleanup function on exit +trap cleanup EXIT SIGHUP SIGINT SIGTERM + # Check that this is run as root check_sudo() { if [[ $EUID -ne 0 ]]; then @@ -109,89 +145,101 @@ find_sflc_path() { local cmd="shufflecake" # Check if the command exists in the current directory - if [[ -x "./$cmd" ]]; then + if [[ -x "./${cmd}" ]]; then SFLCPATH=$(realpath ./) - SFLCNAME=$(realpath ./$cmd) + SFLCNAME=$(realpath "./${cmd}") return fi # Check if the command exists in the parent directory - if [[ -x "../$cmd" ]]; then + if [[ -x "../${cmd}" ]]; then SFLCPATH=$(realpath ../) - SFLCNAME=$(realpath ../$cmd) + SFLCNAME=$(realpath "../${cmd}") return fi # Check if the command exists in the directories listed in PATH IFS=':' read -ra dirs <<< "$PATH" for dir in "${dirs[@]}"; do - if [[ -x "$dir/$cmd" ]]; then - SFLCPATH=$(realpath $dir) - SFLCNAME=$(realpath $dir/$cmd) + if [[ -x "${dir}/${cmd}" ]]; then + SFLCPATH=$(realpath "$dir") + SFLCNAME=$(realpath "${dir}/${cmd}") return fi done # If the command was not found, print an error message - echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 + echo -e "${RED}ERROR: Command '${cmd}' not found${NC}." >&2 exit 1 } # Find and load module dm-sflc load_dmsflc() { local mod="dm-sflc" + # Kernel uses underscores, but module files may use hyphens. + local mod_name_loaded="${mod//-/_}" # First, make sure that dm_mod is loaded modprobe dm_mod # Check if the module is already loaded - if lsmod | grep -q "^$mod "; then + if lsmod | grep -q "^${mod_name_loaded} "; then DMSFLC_INSTALLED=true - echo "Module '$mod' is already loaded." + echo "Module '${mod}' is already loaded." + return + fi + + # Try loading from system modules first + if modprobe "$mod" &> /dev/null; then + echo "Module '${mod}' loaded from system modules." return fi # If not, look for the module file and try to load it - local mod_file="$mod.ko" + local mod_file="${mod}.ko" # Check if the module file exists in the current directory - if [[ -f "./$mod_file" ]]; then - insmod $(realpath ./$mod_file) || exit 1 - echo "Module '$mod' loaded from current directory." + if [[ -f "./${mod_file}" ]]; then + insmod "$(realpath "./${mod_file}")" || exit 1 + echo "Module '${mod}' loaded from current directory." return fi # Check if the module file exists in the parent directory - if [[ -f "../$mod_file" ]]; then - insmod $(realpath ../$mod_file) || exit 1 - echo "Module '$mod' loaded from parent directory." + if [[ -f "../${mod_file}" ]]; then + insmod "$(realpath "../${mod_file}")" || exit 1 + echo "Module '${mod}' loaded from parent directory." return fi # If the module file was not found, print an error message - echo -e "${RED} ERROR: Module file '$mod_file' not found.${NC}." >&2 + echo -e "${RED} ERROR: Module file '${mod_file}' not found.${NC}." >&2 exit 1 } -# Unload dm-sflc if it was loaded locally +# Unload dm-sflc if it was loaded by this script unload_dmsflc() { local mod="dm-sflc" - # Only unload dm-sflc if it was loaded manually when the script was invoked - if ! $DMSFLC_INSTALLED; then - rmmod $mod || exit 1 - echo "Module '$mod' unloaded." - else - echo "Module '$mod' was not unloaded because it was already loaded when the script was run." + # Kernel uses underscores, but module files may use hyphens. + local mod_name_loaded="${mod//-/_}" + + if [ "$DMSFLC_INSTALLED" = true ]; then + echo "Module '${mod}' will not be unloaded because it was already present when the script started." + return + fi + + if lsmod | grep -q "^${mod_name_loaded} "; then + echo "Unloading module '${mod}'..." + rmmod "${mod_name_loaded}" || echo -e "${RED}Warning: Failed to unload module '${mod}'.${NC}" >&2 fi } # Function to check if argument is a block device check_block_device() { if [ -b "$1" ]; then - echo "OK, block device path $1 is valid." >&2 + echo "OK, block device path \"$1\" is valid." >&2 else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - unload_dmsflc + echo -e "${RED}Error: \"$1\" is not a valid block device, aborting.${NC}" usage exit 1 fi @@ -199,36 +247,39 @@ check_block_device() { # Function to create loop device create_loop_device() { - echo "I will now try to create a file $LOOP_FILENAME ..." >&2 + echo "I will now try to create a file \"$LOOP_FILENAME\" ..." >&2 if [ -e "$LOOP_FILENAME" ]; then - echo -e "${RED}Error: Impossible to generate file, $LOOP_FILENAME already exists.${NC}" + echo -e "${RED}Error: Impossible to generate file, \"$LOOP_FILENAME\" already exists.${NC}" exit 1 fi - sudo dd if=/dev/zero of="$LOOP_FILENAME" bs=1M count=1024 > /dev/null + dd if=/dev/zero of="$LOOP_FILENAME" bs=1M count=1024 &> /dev/null echo "Writing of empty file complete. I will now try to attach it to a new loop device..." >&2 - LOOP_DEVICE=$(sudo losetup -f --show "$LOOP_FILENAME") - echo "Successfully created loop device $LOOP_DEVICE ." >&2 + LOOP_DEVICE=$(losetup -f --show "$LOOP_FILENAME") + if [ $? -ne 0 ] || [ -z "$LOOP_DEVICE" ]; then + echo -e "${RED}Error: Failed to create loop device.${NC}" + exit 1 + fi + echo "Successfully created loop device \"$LOOP_DEVICE\" ." >&2 echo "$LOOP_DEVICE" } # Finds the sflc volume that was created last find_sflcvolname() { - # List all files in /dev/mapper, select those starting with sflc_, sort them, and pick the last one. - volname=$(ls /dev/mapper/sflc_* 2>/dev/null | sort -t '_' -k 2n,2 -k 3n,3 | tail -n 1) - # Check if volname is empty (no sflc_ files found). - if [ -z "$volname" ]; then - echo -e "${RED}ERROR: No sflc_ devices found in /dev/mapper !${NC}" + local files=(/dev/mapper/sflc_*) + # Check if the glob expanded to any existing files + if [ ! -e "${files[0]}" ]; then + echo -e "${RED}ERROR: No sflc_ devices found in /dev/mapper !${NC}" >&2 return 1 - else - echo $volname - return 0 fi + # Use printf to list one file per line, then sort and get the last one + printf '%s\n' "${files[@]}" | sort -t '_' -k 2n,2 -k 3n,3 | tail -n 1 + return 0 } # Function for user confirmation confirm() { while true; do - echo -e "${BLUE}Are you sure you want to proceed? All data on disk $BLOCK_DEVICE will be erased. (y/n)${NC}" + echo -e "${BLUE}Are you sure you want to proceed? All data on disk \"$BLOCK_DEVICE\" will be erased. (y/n)${NC}" read -r response case "$response" in [yY]|[yY][eE][sS]) # Responded Yes @@ -244,103 +295,116 @@ confirm() { done } -# Benchmarks -benchmark() { - - SFLCVOLUME="" - VOLNAME="" - MNTPOINT="" - NUMPOINTS=21 # number of graph points +# Main test function +evaluate() { + local VOLNAME="" VOLSIZE=$(blockdev --getsize64 "$BLOCK_DEVICE") # first approximation, in bytes + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Failed to get size of '$BLOCK_DEVICE'.${NC}" >&2; exit 1; fi echo "Starting fragmentation test for Shufflecake Legacy..." # init - echo "Initializing block device $BLOCK_DEVICE with two Shufflecake Legacy volumes (--skip-randfill)..." - etime=$( (time echo -e "passwd1\npasswd2" | $SFLCNAME --skip-randfill --legacy -n 2 init $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo "Initializing block device \"$BLOCK_DEVICE\" with two Shufflecake Legacy volumes (--skip-randfill)..." + etime=$( (time echo -e "passwd1\npasswd2" | "$SFLCNAME" --skip-randfill --legacy -n 2 init "$BLOCK_DEVICE" &> /dev/null) 2>&1 ) + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Shufflecake init failed.${NC}" >&2; exit 1; fi echo -e "${GREEN}Action init took $etime seconds.${NC}" + echo "Shufflecake Legacy device initialized. Opening hidden volume (nr. 2)..." # open - etime=$( (time echo -e "passwd2" | $SFLCNAME --legacy open $BLOCK_DEVICE > /dev/null) 2>&1 ) + etime=$( (time echo -e "passwd2" | "$SFLCNAME" --legacy open "$BLOCK_DEVICE" &> /dev/null) 2>&1 ) + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Shufflecake open failed.${NC}" >&2; exit 1; fi echo -e "${GREEN}Action open took $etime seconds.${NC}" + # fetch SFLCVOLUME SFLCVOLUME=$(find_sflcvolname) + if [ $? -ne 0 ]; then exit 1; fi # Error message is already in the function + # trim path of SFLCVOLUME - VOLNAME=${SFLCVOLUME##*/} - echo "Shufflecake volume opened as $VOLNAME. Formatting with $FSTYPE..." + VOLNAME="${SFLCVOLUME##*/}" + echo "Shufflecake volume opened as \"$VOLNAME\". Formatting with $FSTYPE..." + # format with FSTYPE, but mute output (too verbose) - mkfs.$FSTYPE $SFLCVOLUME > /dev/null - echo "Volume $SFLCVOLUME formatted. Mounting that..." + "mkfs.${FSTYPE}" "$SFLCVOLUME" &> /dev/null + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: mkfs failed on '$SFLCVOLUME'.${NC}" >&2; exit 1; fi + echo "Volume \"$SFLCVOLUME\" formatted. Mounting that..." + # assign and create MNTPOINT MNTPOINT=$(realpath "./sflc_mnt") - mkdir $MNTPOINT + mkdir -p "$MNTPOINT" + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Could not create mountpoint '$MNTPOINT'.${NC}" >&2; exit 1; fi + # mount - mount $SFLCVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fragmentation test..." - # TESTS HERE + mount "$SFLCVOLUME" "$MNTPOINT" + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Mount failed for '$SFLCVOLUME' on '$MNTPOINT'.${NC}" >&2; exit 1; fi + echo "Volume mounted at \"$MNTPOINT\". Starting fragmentation test..." - # Fragmentation test - # Shufflecake allocates data on disk by lazily allocating "slices", and then writing data on those slices. - # If no suitable slice is available for writing data, a new one is allocated. - # The number of allocated slices for a volume is tracked into /sys/module/dm_sflc/bdevs/${MAJ}:${MIN}/${VOLNAME}/mapped_slices - # At this point the volume is already formatted as FSTYPE and mounted at MNTPOINT - # sanity check: NUMPOINTS must be at least 1 and at most 65536, otherwise exit 1 - if [ $NUMPOINTS -lt 1 ] || [ $NUMPOINTS -gt 65536 ]; then - echo -e "${RED}Error: NUMPOINTS must be between 1 and 65536. Aborting...${NC}" + # Sanity check: NUMPOINTS must be at least 1 and at most 65536, otherwise exit 1 + if [ "$NUMPOINTS" -lt 1 ] || [ "$NUMPOINTS" -gt 65536 ]; then + echo -e "${RED}Error: NUMPOINTS must be between 1 and 65536. Aborting...${NC}" >&2 exit 1 fi - # read number of total available slices (round down) from /sys/module/dm_sflc/bdevs/${MAJ}:${MIN}/tot_slices - MOST_RECENT_DIR=$(ls -td /sys/module/dm_sflc/bdevs/*/ | head -n 1) # include final slash / - read -r TOTSLICES < ${MOST_RECENT_DIR}tot_slices + # Find the statistics directory for this device. + local stat_dirs=(/sys/module/dm_sflc/bdevs/*/) + local stat_dir="${stat_dirs[0]}" # Assume only one is created by this script + if [ ! -d "$stat_dir" ]; then + echo -e "${RED}Error: Could not find Shufflecake statistics directory in /sys.${NC}" >&2 + exit 1 + fi + + read -r TOTSLICES < "${stat_dir}tot_slices" - # refine down block device max size according to available slices, to make sure there is no overflow - NEWUPPERBOUND=$((TOTSLICES * 1048576 )) # in bytes + # Refine down block device max size according to available slices, to make sure there is no overflow + local NEWUPPERBOUND=$((TOTSLICES * 1048576 )) # in bytes if ((VOLSIZE > NEWUPPERBOUND)); then VOLSIZE=$NEWUPPERBOUND fi - # manage case NUMPOINTS = 1 - read -r OCCSLICES < ${MOST_RECENT_DIR}${VOLNAME}/mapped_slices - X_COORD=.0 + read -r OCCSLICES < "${stat_dir}${VOLNAME}/mapped_slices" + local X_COORD=.0 + local Y_COORD Y_COORD=$(echo "scale=3; $OCCSLICES/$TOTSLICES" | bc) # Print occupation table for graph echo -e "${GREEN}Occupation table: X = percentage of data written, Y = percentage of slices used.${NC}" echo -e "${GREEN}---------------------------------${NC}" - echo -e "${GREEN}$X_COORD $Y_COORD${NC}" - if [ $NUMPOINTS -gt 1 ]; then + if [ "$NUMPOINTS" -gt 1 ]; then # point 1 is always at X=0 and point NUMPOINT is always at X=1 - DATASIZE=$(( $VOLSIZE / ($NUMPOINTS-1) / 1048576 )) # data increments in MB, round down + local DATASIZE=$(( VOLSIZE / (NUMPOINTS - 1) / 1048576 )) # data increments in MB, round down - # loop for i = 1 to NUMPOINTS + # loop for i = 1 to NUMPOINTS-1 for (( i=1; i<$NUMPOINTS-1; i++ )); do X_COORD=$(echo "scale=3; $i / ($NUMPOINTS-1) " | bc) + local RNDDIRNAME + local RNDFILENAME + # RNDDIRNAME = random name with 16 characters (loop while it does not exist already) while :; do - RNDDIRNAME=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1) + RNDDIRNAME=$(head -c 12 /dev/urandom | base64 | tr -dc 'a-z0-9') [ ! -e "${MNTPOINT}/${RNDDIRNAME}" ] && break done - # create random dir mkdir "${MNTPOINT}/${RNDDIRNAME}" + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Failed to create random directory.${NC}" >&2; exit 1; fi # RNDFILENAME= random name with 16 characters (loop while it does not exist already) while :; do - RNDFILENAME=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1) + RNDFILENAME=$(head -c 12 /dev/urandom | base64 | tr -dc 'a-z0-9') [ ! -e "${MNTPOINT}/${RNDDIRNAME}/${RNDFILENAME}" ] && break done # create random file - dd if=/dev/zero of=${MNTPOINT}/${RNDDIRNAME}/${RNDFILENAME} bs=1M count=$DATASIZE >/dev/null 2>&1 + dd if=/dev/zero of="${MNTPOINT}/${RNDDIRNAME}/${RNDFILENAME}" bs=1M count="$DATASIZE" &> /dev/null + if [ $? -ne 0 ]; then echo -e "${RED}ERROR: dd write failed.${NC}" >&2; exit 1; fi sync # compute slice occupation threshold - read -r OCCSLICES < ${MOST_RECENT_DIR}${VOLNAME}/mapped_slices + read -r OCCSLICES < "${stat_dir}${VOLNAME}/mapped_slices" Y_COORD=$(echo "scale=3; $OCCSLICES / $TOTSLICES" | bc) + # sanity check i.e. Y_COORD is between 0 and 1 if (( $(echo "$Y_COORD < 0" | bc -l) )) || (( $(echo "$Y_COORD > 1" | bc -l) )); then - echo "Error: Y_COORD is not between 0 and 1" + echo -e "${RED}Error: Y_COORD ($Y_COORD) is not between 0 and 1.${NC}" >&2 exit 1 fi # print point coords @@ -348,34 +412,12 @@ benchmark() { done # manually print last point to avoid rounding error artifacts at the last write echo -e "${GREEN}1.0 1.0${NC}" - - fi + fi # end table echo -e "${GREEN}---------------------------------${NC}" - # END TESTS - echo "Shufflecake Legacy fragmentation test ended. Unmounting volume." - # unmount - umount $MNTPOINT - rmdir $MNTPOINT - echo "Volume unmounted. Closing Shufflecake device..." - # close - etime=$( (time $SFLCNAME close $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action close took $etime seconds.${NC}" - #end - -} - -# Clean up -cleanup() { - echo "Exiting and cleaning..." - if [[ -n $LOOP_DEVICE ]]; then - echo "Detaching $LOOP_DEVICE ..." - sudo losetup -d "$LOOP_DEVICE" - echo "Deleting $LOOP_FILENAME ..." - sudo rm -f "$LOOP_FILENAME" - echo "Loop device detached and backing file deleted." - fi + echo "Shufflecake Legacy fragmentation test ended." + # The cleanup trap will handle the rest } @@ -386,74 +428,69 @@ cleanup() { ##################################################################### # BANNER -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars +#                xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   79 chars echo -e "${BLUE}===============================================================================${NC}" -echo -e "${BLUE} Evaluation Script for Shufflecake Legacy Volume Fragmentation${NC}" +echo -e "${BLUE}         Evaluation Script for Shufflecake Legacy Volume Fragmentation${NC}" echo -e "${BLUE}===============================================================================${NC}" -# PRELIMINARY: PARSE HELP, SUDO, AND LOAD SHUFFLECAKE (IF IT EXISTS, OTHERWISE ERROR) - -case "$1" in - # help - --help|-?) - print_help - exit 0 - ;; -esac +# ARGUMENT PARSING +while [ "$#" -gt 0 ]; do + case "$1" in + --help|-?) + print_help + ;; + *) + if [ -n "$BLOCK_DEVICE" ]; then + echo -e "${RED}Error: You can only specify one block device.${NC}" >&2 + usage + exit 1 + fi + BLOCK_DEVICE="$1" + ;; + esac + shift +done +# PRELIMINARY CHECKS check_sudo echo -e "${BLUE}Initializing Shufflecake...${NC}" echo "Searching Shufflecake executable..." find_sflc_path -echo "Shufflecake executable found at $SFLCNAME ." +echo "Shufflecake executable found at \"$SFLCNAME\"." echo "Searching and loading dm-sflc module..." load_dmsflc -echo "Module dm-sflc found and loaded. Status DMSFLC_INSTALLED: $DMSFLC_INSTALLED ." +echo "Module dm-sflc status determined. DMSFLC_INSTALLED flag is: $DMSFLC_INSTALLED." echo " " -# PARSER - -case "$1" in - "") -# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - - echo "Now you will be asked to enter the path for a block device to be used for the " - echo "evaluation (all content will be erased). If no path is provided (default" - echo "choice), then the script will create a 1 GiB file in the current directory and " - echo "use it to back a loop device instead, then the file will be removed at the end." - echo " " - echo -n "Please enter the path for a block device (default: none): " - read BLOCK_DEVICE - if [ -z "$BLOCK_DEVICE" ]; then - echo "No path provided, creating a local file and loop device..." - LOOP_DEVICE=$(create_loop_device) - BLOCK_DEVICE=$LOOP_DEVICE - fi - - ;; - - # argument passed - *) - BLOCK_DEVICE="$1" - ;; -esac +# DETERMINE BLOCK DEVICE +if [ -z "$BLOCK_DEVICE" ]; then + echo "Now you will be asked to enter the path for a block device to be used for the " + echo "evaluation (all content will be erased). If no path is provided (default"  + echo "choice), then the script will create a 1 GiB file in the current directory and " + echo "use it to back a loop device instead, then the file will be removed at the end." + echo " " + echo -n "Please enter the path for a block device (default: none): " + read -r BLOCK_DEVICE + if [ -z "$BLOCK_DEVICE" ]; then + echo "No path provided, creating a local file and loop device..." + LOOP_DEVICE=$(create_loop_device) + BLOCK_DEVICE="$LOOP_DEVICE" + fi +fi check_block_device "$BLOCK_DEVICE" # MAIN PROGRAM if confirm; then - benchmark + evaluate else echo "Aborting..." fi -unload_dmsflc - -cleanup - +# The cleanup trap will automatically run on exit. # END SCRIPT