diff --git a/benchmark-suite/luks-benchmark.sh b/benchmark-suite/luks-benchmark.sh index 2f029ee..76fb4a5 100755 --- a/benchmark-suite/luks-benchmark.sh +++ b/benchmark-suite/luks-benchmark.sh @@ -39,176 +39,176 @@ NC='\033[0m' # No color # Help print_help() { # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" - echo " " - echo "This script is used to benchmark LUKS/dm-crypt on this machine." - 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 " " - 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 LUKS volume within a given device." - echo "2) Opens the volume, formats it with ext4 and mounts it." - echo "3) Performs various fio r/w stress operations on it." - echo "4) Unmounts and closes the used volume." - echo " " + echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" + echo " " + echo "This script is used to benchmark LUKS/dm-crypt on this machine." + 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 " " + 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 LUKS volume within a given device." + echo "2) Opens the volume, formats it with ext4 and mounts it." + echo "3) Performs various fio r/w stress operations on it." + echo "4) Unmounts and closes the used volume." + echo " " # 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 " " + 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 - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging bpx() { - if [ -z "$1" ]; then - echo "BPX: Paused. Press any key to continue..." >&2 - else - echo "BPX: $1. Press any key to continue..." >&2 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + 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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - usage - exit 1 - fi + if [ -b "$1" ]; then + echo "OK, block device path $1 is valid." >&2 + else + echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" + usage + exit 1 + fi } # Function to create loop device create_loop_device() { - 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}" - exit 1 - fi - sudo 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 - echo "$LOOP_DEVICE" + 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}" + exit 1 + fi + sudo 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 + echo "$LOOP_DEVICE" } # Generate a suitable LUKS volume name find_luksvolname() { - volname="/dev/mapper/luks-benchmark-testvol" #placeholder - echo "$volname" + volname="/dev/mapper/luks-benchmark-testvol" #placeholder + echo "$volname" } # 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}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + 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}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - LUKSVOLUME="luks-test" - MNTPOINT="" - PASSPHRASE="mypassword" - TESTNAME="luks" - RUNTIME="10" # running time in seconds FOR EACH TEST - DATASIZE="500M" - TESTFILENAME="testfile" - echo "Starting benchmark for LUKS/dm-crypt..." + LUKSVOLUME="luks-test" + MNTPOINT="" + PASSPHRASE="mypassword" + TESTNAME="luks" + RUNTIME="2" # running time in seconds FOR EACH TEST + DATASIZE="500M" + TESTFILENAME="testfile" + echo "Starting benchmark for LUKS/dm-crypt..." # First, make sure that dm_mod is loaded modprobe dm_mod # Volume setup - echo "Initializing block device $BLOCK_DEVICE as a LUKS volume..." - etime=$( (time echo -n "$PASSPHRASE" | cryptsetup --batch-mode --cipher aes-xts-plain64 luksFormat $BLOCK_DEVICE - > /dev/null) 2>&1 ) - echo -e "${GREEN}Action luksFormat took $etime seconds.${NC}" - echo "LUKS device initialized. Opening encrypted volume..." - etime=$( (time echo -n "$PASSPHRASE" | cryptsetup luksOpen $BLOCK_DEVICE $LUKSVOLUME - > /dev/null) 2>&1 ) - echo -e "${GREEN}Action luksOpen took $etime seconds.${NC}" - echo "LUKS volume opened as /dev/mapper/$LUKSVOLUME. Formatting with ext4..." - # format with ext4, but mute output (too verbose) - mkfs.ext4 /dev/mapper/$LUKSVOLUME > /dev/null - echo "Volume /dev/mapper/$SFLCVOLUME formatted. Mounting that..." - # assign and create MNTPOINT - MNTPOINT=$(realpath "./luks_mnt") - mkdir $MNTPOINT - # mount - mount /dev/mapper/$LUKSVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fio tests..." - # TESTS HERE - # test 01: random read - echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 02: random write - echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 03: sequential read - echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 04: sequential write - echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # END TESTS - echo "LUKS/dm-crypt fio tests ended. Unmounting volume." - # unmount - umount $MNTPOINT - rmdir $MNTPOINT - echo "Volume unmounted. Closing LUKS device..." - # close - etime=$( (time cryptsetup luksClose $LUKSVOLUME > /dev/null) 2>&1 ) - echo -e "${GREEN}Action close took $etime seconds.${NC}" - #end + echo "Initializing block device $BLOCK_DEVICE as a LUKS volume..." + etime=$( (time echo -n "$PASSPHRASE" | cryptsetup --batch-mode --cipher aes-xts-plain64 luksFormat $BLOCK_DEVICE - > /dev/null) 2>&1 ) + echo -e "${GREEN}Action luksFormat took $etime seconds.${NC}" + echo "LUKS device initialized. Opening encrypted volume..." + etime=$( (time echo -n "$PASSPHRASE" | cryptsetup luksOpen $BLOCK_DEVICE $LUKSVOLUME - > /dev/null) 2>&1 ) + echo -e "${GREEN}Action luksOpen took $etime seconds.${NC}" + echo "LUKS volume opened as /dev/mapper/$LUKSVOLUME. Formatting with ext4..." + # format with ext4, but mute output (too verbose) + mkfs.ext4 /dev/mapper/$LUKSVOLUME > /dev/null + echo "Volume /dev/mapper/$SFLCVOLUME formatted. Mounting that..." + # assign and create MNTPOINT + MNTPOINT=$(realpath "./luks_mnt") + mkdir $MNTPOINT + # mount + mount /dev/mapper/$LUKSVOLUME $MNTPOINT + echo "Volume mounted at $MNTPOINT. Starting fio tests..." + # TESTS HERE + # test 01: random read + echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 02: random write + echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 03: sequential read + echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 04: sequential write + echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # END TESTS + echo "LUKS/dm-crypt fio tests ended. Unmounting volume." + # unmount + umount $MNTPOINT + rmdir $MNTPOINT + echo "Volume unmounted. Closing LUKS device..." + # close + etime=$( (time cryptsetup luksClose $LUKSVOLUME > /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 "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 } @@ -229,17 +229,17 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo if ! which cryptsetup >/dev/null; then - echo -e "${RED}ERROR: cryptsetup not found, please install it.${NC}" - exit 1 + echo -e "${RED}ERROR: cryptsetup not found, please install it.${NC}" + exit 1 fi echo " " @@ -248,28 +248,28 @@ 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 "benchmarks (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" - ;; + echo "Now you will be asked to enter the path for a block device to be used for the " + echo "benchmarks (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 check_block_device "$BLOCK_DEVICE" @@ -277,9 +277,9 @@ check_block_device "$BLOCK_DEVICE" # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi cleanup diff --git a/benchmark-suite/sflc-legacy-benchmark.sh b/benchmark-suite/sflc-legacy-benchmark.sh index ba622c5..1ec00f1 100755 --- a/benchmark-suite/sflc-legacy-benchmark.sh +++ b/benchmark-suite/sflc-legacy-benchmark.sh @@ -42,272 +42,272 @@ NC='\033[0m' # No color # Help print_help() { # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" - echo " " - echo "This script is used to benchmark Shufflecake Legacy on this machine." - 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 " " - 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 ext4 and mounts it." - echo "3) Performs various fio r/w stress operations on it." - echo "4) Unmounts and closes the used volume." - echo " " + echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" + echo " " + echo "This script is used to benchmark Shufflecake Legacy on this machine." + 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 " " + 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 ext4 and mounts it." + echo "3) Performs various fio r/w stress operations on it." + echo "4) Unmounts and closes the used volume." + echo " " # 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 " " + 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 - 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 " " + 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 - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging bpx() { - if [ -z "$1" ]; then - echo "BPX: Paused. Press any key to continue..." >&2 - else - echo "BPX: $1. Press any key to continue..." >&2 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + 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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + fi } # Find the path of Shufflecake executable find_sflc_path() { - local cmd="shufflecake" + local cmd="shufflecake" - # Check if the command exists in the current directory - if [[ -x "./$cmd" ]]; then - SFLCPATH=$(realpath ./) - SFLCNAME=$(realpath ./$cmd) - return - fi + # Check if the command exists in the current directory + if [[ -x "./$cmd" ]]; then + SFLCPATH=$(realpath ./) + SFLCNAME=$(realpath ./$cmd) + return + fi - # Check if the command exists in the parent directory - if [[ -x "../$cmd" ]]; then - SFLCPATH=$(realpath ../) - SFLCNAME=$(realpath ../$cmd) - return - fi + # Check if the command exists in the parent directory + if [[ -x "../$cmd" ]]; then + SFLCPATH=$(realpath ../) + 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) - return - fi - done + # 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) + return + fi + done - # If the command was not found, print an error message - echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 - exit 1 + # If the command was not found, print an error message + echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 + exit 1 } # Find and load module dm-sflc load_dmsflc() { - local mod="dm-sflc" + local mod="dm-sflc" # First, make sure that dm_mod is loaded modprobe dm_mod - # Check if the module is already loaded - if lsmod | grep -q "^$mod "; then - DMSFLC_INSTALLED=true - echo "Module '$mod' is already loaded." - return - fi + # Check if the module is already loaded + if lsmod | grep -q "^$mod "; then + DMSFLC_INSTALLED=true + echo "Module '$mod' is already loaded." + return + fi - # If not, look for the module file and try to load it - local mod_file="$mod.ko" + # If not, look for the module file and try to load it + 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." - return - fi + # 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." + 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." - 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." + 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 - exit 1 + # If the module file was not found, print an error message + echo -e "${RED} ERROR: Module file '$mod_file' not found.${NC}." >&2 + exit 1 } # Unload dm-sflc if it was loaded locally 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." - fi + 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." + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - unload_dmsflc - usage - exit 1 - fi + if [ -b "$1" ]; then + 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 + usage + exit 1 + fi } # Function to create loop device create_loop_device() { - 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}" - exit 1 - fi - sudo 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 - echo "$LOOP_DEVICE" + 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}" + exit 1 + fi + sudo 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 + 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}" - return 1 - else - echo $volname - return 0 - fi + # 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}" + return 1 + else + echo $volname + return 0 + fi } # 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}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + 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}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - SFLCVOLUME="" - MNTPOINT="" - TESTNAME="sflc-lgc" - RUNTIME="10" # running time in seconds FOR EACH TEST - DATASIZE="500M" - TESTFILENAME="testfile" - echo "Starting benchmark for Shufflecake Legacy..." - 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 -e "${GREEN}Action init took $etime seconds.${NC}" - echo "Shufflecake device initialized. Opening hidden volume (nr. 2)..." - # open - etime=$( (time echo -e "passwd2" | $SFLCNAME --legacy open $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action open took $etime seconds.${NC}" - # fetch SFLCVOLUME - SFLCVOLUME=$(find_sflcvolname) - echo "Shufflecake Legacy volume opened as $SFLCVOLUME. Formatting with ext4..." - # format with ext4, but mute output (too verbose) - mkfs.ext4 $SFLCVOLUME > /dev/null - echo "Volume $SFLCVOLUME formatted. Mounting that..." - # assign and create MNTPOINT - MNTPOINT=$(realpath "./sflc_mnt") - mkdir $MNTPOINT - # mount - mount $SFLCVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fio tests..." - # TESTS HERE - # test 01: random read - echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 02: random write - echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 03: sequential read - echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 04: sequential write - echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # END TESTS - echo "Shufflecake Legacy fio tests 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 - + SFLCVOLUME="" + MNTPOINT="" + TESTNAME="sflc-lgc" + RUNTIME="2" # running time in seconds FOR EACH TEST + DATASIZE="500M" + TESTFILENAME="testfile" + echo "Starting benchmark for Shufflecake Legacy..." + 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 -e "${GREEN}Action init took $etime seconds.${NC}" + echo "Shufflecake device initialized. Opening hidden volume (nr. 2)..." + # open + etime=$( (time echo -e "passwd2" | $SFLCNAME --legacy open $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo -e "${GREEN}Action open took $etime seconds.${NC}" + # fetch SFLCVOLUME + SFLCVOLUME=$(find_sflcvolname) + echo "Shufflecake Legacy volume opened as $SFLCVOLUME. Formatting with ext4..." + # format with ext4, but mute output (too verbose) + mkfs.ext4 $SFLCVOLUME > /dev/null + echo "Volume $SFLCVOLUME formatted. Mounting that..." + # assign and create MNTPOINT + MNTPOINT=$(realpath "./sflc_mnt") + mkdir $MNTPOINT + # mount + mount $SFLCVOLUME $MNTPOINT + echo "Volume mounted at $MNTPOINT. Starting fio tests..." + # TESTS HERE + # test 01: random read + echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 02: random write + echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 03: sequential read + echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 04: sequential write + echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # END TESTS + echo "Shufflecake Legacy fio tests 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 "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 } @@ -328,14 +328,14 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo - + echo -e "${BLUE}Initializing Shufflecake...${NC}" echo "Searching Shufflecake executable..." find_sflc_path @@ -349,38 +349,38 @@ 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 "benchmarks (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" - ;; + echo "Now you will be asked to enter the path for a block device to be used for the " + echo "benchmarks (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 check_block_device "$BLOCK_DEVICE" - + # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi unload_dmsflc diff --git a/benchmark-suite/sflc-legacy-fragmentation.sh b/benchmark-suite/sflc-legacy-fragmentation.sh index ca6020f..4176eb7 100755 --- a/benchmark-suite/sflc-legacy-fragmentation.sh +++ b/benchmark-suite/sflc-legacy-fragmentation.sh @@ -46,336 +46,336 @@ NC='\033[0m' # No color # Help print_help() { # 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 " " - 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 "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 " " + 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 " " + 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 "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 - 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 " " + 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 - 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 " " + 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 - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging 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 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + echo -e "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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + fi } # Find the path of Shufflecake executable find_sflc_path() { - local cmd="shufflecake" + local cmd="shufflecake" - # Check if the command exists in the current directory - if [[ -x "./$cmd" ]]; then - SFLCPATH=$(realpath ./) - SFLCNAME=$(realpath ./$cmd) - return - fi + # Check if the command exists in the current directory + if [[ -x "./$cmd" ]]; then + SFLCPATH=$(realpath ./) + SFLCNAME=$(realpath ./$cmd) + return + fi - # Check if the command exists in the parent directory - if [[ -x "../$cmd" ]]; then - SFLCPATH=$(realpath ../) - SFLCNAME=$(realpath ../$cmd) - return - fi + # Check if the command exists in the parent directory + if [[ -x "../$cmd" ]]; then + SFLCPATH=$(realpath ../) + 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) - return - fi - done + # 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) + return + fi + done - # If the command was not found, print an error message - echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 - exit 1 + # If the command was not found, print an error message + echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 + exit 1 } # Find and load module dm-sflc load_dmsflc() { - local mod="dm-sflc" + local mod="dm-sflc" # First, make sure that dm_mod is loaded modprobe dm_mod - # Check if the module is already loaded - if lsmod | grep -q "^$mod "; then - DMSFLC_INSTALLED=true - echo "Module '$mod' is already loaded." - return - fi + # Check if the module is already loaded + if lsmod | grep -q "^$mod "; then + DMSFLC_INSTALLED=true + echo "Module '$mod' is already loaded." + return + fi - # If not, look for the module file and try to load it - local mod_file="$mod.ko" + # If not, look for the module file and try to load it + 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." - return - fi + # 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." + 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." - 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." + 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 - exit 1 + # If the module file was not found, print an error message + echo -e "${RED} ERROR: Module file '$mod_file' not found.${NC}." >&2 + exit 1 } # Unload dm-sflc if it was loaded locally 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." - fi + 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." + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - unload_dmsflc - usage - exit 1 - fi + if [ -b "$1" ]; then + 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 + usage + exit 1 + fi } # Function to create loop device create_loop_device() { - 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}" - exit 1 - fi - sudo 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 - echo "$LOOP_DEVICE" + 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}" + exit 1 + fi + sudo 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 + 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}" - return 1 - else - echo $volname - return 0 - fi + # 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}" + return 1 + else + echo $volname + return 0 + fi } # 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}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + 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}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - SFLCVOLUME="" - VOLNAME="" - MNTPOINT="" - NUMPOINTS=21 # number of graph points - VOLSIZE=$(blockdev --getsize64 "$BLOCK_DEVICE") # first approximation, in bytes + SFLCVOLUME="" + VOLNAME="" + MNTPOINT="" + NUMPOINTS=21 # number of graph points + VOLSIZE=$(blockdev --getsize64 "$BLOCK_DEVICE") # first approximation, in bytes - 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 -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 ) - echo -e "${GREEN}Action open took $etime seconds.${NC}" - # fetch SFLCVOLUME - SFLCVOLUME=$(find_sflcvolname) - # trim path of SFLCVOLUME - 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..." - # assign and create MNTPOINT - MNTPOINT=$(realpath "./sflc_mnt") - mkdir $MNTPOINT - # mount - mount $SFLCVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fragmentation test..." - # TESTS HERE - - # 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}" - 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 - - # refine down block device max size according to available slices, to make sure there is no overflow - 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 - 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}" + 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 -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 ) + echo -e "${GREEN}Action open took $etime seconds.${NC}" + # fetch SFLCVOLUME + SFLCVOLUME=$(find_sflcvolname) + # trim path of SFLCVOLUME + 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..." + # assign and create MNTPOINT + MNTPOINT=$(realpath "./sflc_mnt") + mkdir $MNTPOINT + # mount + mount $SFLCVOLUME $MNTPOINT + echo "Volume mounted at $MNTPOINT. Starting fragmentation test..." + # TESTS HERE + + # 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}" + 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 + + # refine down block device max size according to available slices, to make sure there is no overflow + 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 + 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 - # 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 - - # loop for i = 1 to NUMPOINTS - for (( i=1; i<$NUMPOINTS-1; i++ )); do - X_COORD=$(echo "scale=3; $i / ($NUMPOINTS-1) " | bc) - # 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) - [ ! -e "${MNTPOINT}/${RNDDIRNAME}" ] && break - done - # create random dir - mkdir "${MNTPOINT}/${RNDDIRNAME}" - - # 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) - [ ! -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 - sync + 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 + + # loop for i = 1 to NUMPOINTS + for (( i=1; i<$NUMPOINTS-1; i++ )); do + X_COORD=$(echo "scale=3; $i / ($NUMPOINTS-1) " | bc) + # 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) + [ ! -e "${MNTPOINT}/${RNDDIRNAME}" ] && break + done + # create random dir + mkdir "${MNTPOINT}/${RNDDIRNAME}" + + # 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) + [ ! -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 + sync - # compute slice occupation threshold - read -r OCCSLICES < ${MOST_RECENT_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" - exit 1 - fi - # print point coords - echo -e "${GREEN}$X_COORD $Y_COORD${NC}" - done - # manually print last point to avoid rounding error artifacts at the last write - echo -e "${GREEN}1.0 1.0${NC}" - - 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 - + # compute slice occupation threshold + read -r OCCSLICES < ${MOST_RECENT_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" + exit 1 + fi + # print point coords + echo -e "${GREEN}$X_COORD $Y_COORD${NC}" + done + # manually print last point to avoid rounding error artifacts at the last write + echo -e "${GREEN}1.0 1.0${NC}" + + 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 "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 } @@ -396,14 +396,14 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo - + echo -e "${BLUE}Initializing Shufflecake...${NC}" echo "Searching Shufflecake executable..." find_sflc_path @@ -417,28 +417,28 @@ 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" - ;; + 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 check_block_device "$BLOCK_DEVICE" @@ -446,9 +446,9 @@ check_block_device "$BLOCK_DEVICE" # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi unload_dmsflc diff --git a/benchmark-suite/sflc-lite-benchmark.sh b/benchmark-suite/sflc-lite-benchmark.sh index 43e5a1a..23d029e 100755 --- a/benchmark-suite/sflc-lite-benchmark.sh +++ b/benchmark-suite/sflc-lite-benchmark.sh @@ -42,272 +42,272 @@ NC='\033[0m' # No color # Help print_help() { # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" - echo " " - echo "This script is used to benchmark Shufflecake Lite on this machine." - 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 " " - 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 (Lite) device with two volumes." - echo "2) Opens the second (hidden) volume, formats it with ext4 and mounts it." - echo "3) Performs various fio r/w stress operations on it." - echo "4) Unmounts and closes the used volume." - echo " " + echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" + echo " " + echo "This script is used to benchmark Shufflecake Lite on this machine." + 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 " " + 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 (Lite) device with two volumes." + echo "2) Opens the second (hidden) volume, formats it with ext4 and mounts it." + echo "3) Performs various fio r/w stress operations on it." + echo "4) Unmounts and closes the used volume." + echo " " # 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 " " + 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 - 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 " " + 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 - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging bpx() { - if [ -z "$1" ]; then - echo "BPX: Paused. Press any key to continue..." >&2 - else - echo "BPX: $1. Press any key to continue..." >&2 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + 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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + fi } # Find the path of Shufflecake executable find_sflc_path() { - local cmd="shufflecake" + local cmd="shufflecake" - # Check if the command exists in the current directory - if [[ -x "./$cmd" ]]; then - SFLCPATH=$(realpath ./) - SFLCNAME=$(realpath ./$cmd) - return - fi + # Check if the command exists in the current directory + if [[ -x "./$cmd" ]]; then + SFLCPATH=$(realpath ./) + SFLCNAME=$(realpath ./$cmd) + return + fi - # Check if the command exists in the parent directory - if [[ -x "../$cmd" ]]; then - SFLCPATH=$(realpath ../) - SFLCNAME=$(realpath ../$cmd) - return - fi + # Check if the command exists in the parent directory + if [[ -x "../$cmd" ]]; then + SFLCPATH=$(realpath ../) + 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) - return - fi - done + # 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) + return + fi + done - # If the command was not found, print an error message - echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 - exit 1 + # If the command was not found, print an error message + echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 + exit 1 } # Find and load module dm-sflc load_dmsflc() { - local mod="dm-sflc" + local mod="dm-sflc" # First, make sure that dm_mod is loaded modprobe dm_mod - # Check if the module is already loaded - if lsmod | grep -q "^$mod "; then - DMSFLC_INSTALLED=true - echo "Module '$mod' is already loaded." - return - fi + # Check if the module is already loaded + if lsmod | grep -q "^$mod "; then + DMSFLC_INSTALLED=true + echo "Module '$mod' is already loaded." + return + fi - # If not, look for the module file and try to load it - local mod_file="$mod.ko" + # If not, look for the module file and try to load it + 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." - return - fi + # 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." + 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." - 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." + 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 - exit 1 + # If the module file was not found, print an error message + echo -e "${RED} ERROR: Module file '$mod_file' not found.${NC}." >&2 + exit 1 } # Unload dm-sflc if it was loaded locally 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." - fi + 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." + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - unload_dmsflc - usage - exit 1 - fi + if [ -b "$1" ]; then + 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 + usage + exit 1 + fi } # Function to create loop device create_loop_device() { - 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}" - exit 1 - fi - sudo 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 - echo "$LOOP_DEVICE" + 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}" + exit 1 + fi + sudo 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 + 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}" - return 1 - else - echo $volname - return 0 - fi + # 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}" + return 1 + else + echo $volname + return 0 + fi } # 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}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + 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}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - SFLCVOLUME="" - MNTPOINT="" - TESTNAME="sflc-lite" - RUNTIME="10" # running time in seconds FOR EACH TEST - DATASIZE="500M" - TESTFILENAME="testfile" - echo "Starting benchmark for Shufflecake Lite..." - echo "Initializing block device $BLOCK_DEVICE with two Shufflecake Lite volumes (--skip-randfill)..." - etime=$( (time echo -e "passwd1\npasswd2" | $SFLCNAME --skip-randfill -n 2 init $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action init took $etime seconds.${NC}" - echo "Shufflecake device initialized. Opening hidden volume (nr. 2)..." - # open - etime=$( (time echo -e "passwd2" | $SFLCNAME open $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action open took $etime seconds.${NC}" - # fetch SFLCVOLUME - SFLCVOLUME=$(find_sflcvolname) - echo "Shufflecake Lite volume opened as $SFLCVOLUME. Formatting with ext4..." - # format with ext4, but mute output (too verbose) - mkfs.ext4 $SFLCVOLUME > /dev/null - echo "Volume $SFLCVOLUME formatted. Mounting that..." - # assign and create MNTPOINT - MNTPOINT=$(realpath "./sflc_mnt") - mkdir $MNTPOINT - # mount - mount $SFLCVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fio tests..." - # TESTS HERE - # test 01: random read - echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 02: random write - echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 03: sequential read - echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 04: sequential write - echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # END TESTS - echo "Shufflecake Lite fio tests 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 - + SFLCVOLUME="" + MNTPOINT="" + TESTNAME="sflc-lite" + RUNTIME="2" # running time in seconds FOR EACH TEST + DATASIZE="500M" + TESTFILENAME="testfile" + echo "Starting benchmark for Shufflecake Lite..." + echo "Initializing block device $BLOCK_DEVICE with two Shufflecake Lite volumes (--skip-randfill)..." + etime=$( (time echo -e "passwd1\npasswd2" | $SFLCNAME --skip-randfill -n 2 init $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo -e "${GREEN}Action init took $etime seconds.${NC}" + echo "Shufflecake device initialized. Opening hidden volume (nr. 2)..." + # open + etime=$( (time echo -e "passwd2" | $SFLCNAME open $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo -e "${GREEN}Action open took $etime seconds.${NC}" + # fetch SFLCVOLUME + SFLCVOLUME=$(find_sflcvolname) + echo "Shufflecake Lite volume opened as $SFLCVOLUME. Formatting with ext4..." + # format with ext4, but mute output (too verbose) + mkfs.ext4 $SFLCVOLUME > /dev/null + echo "Volume $SFLCVOLUME formatted. Mounting that..." + # assign and create MNTPOINT + MNTPOINT=$(realpath "./sflc_mnt") + mkdir $MNTPOINT + # mount + mount $SFLCVOLUME $MNTPOINT + echo "Volume mounted at $MNTPOINT. Starting fio tests..." + # TESTS HERE + # test 01: random read + echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 02: random write + echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 03: sequential read + echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 04: sequential write + echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # END TESTS + echo "Shufflecake Lite fio tests 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 "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 } @@ -328,14 +328,14 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo - + echo -e "${BLUE}Initializing Shufflecake...${NC}" echo "Searching Shufflecake executable..." find_sflc_path @@ -349,38 +349,38 @@ 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 "benchmarks (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" - ;; + echo "Now you will be asked to enter the path for a block device to be used for the " + echo "benchmarks (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 check_block_device "$BLOCK_DEVICE" - + # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi unload_dmsflc diff --git a/benchmark-suite/sflc-lite-fragmentation.sh b/benchmark-suite/sflc-lite-fragmentation.sh index da3dfe4..7261e5b 100755 --- a/benchmark-suite/sflc-lite-fragmentation.sh +++ b/benchmark-suite/sflc-lite-fragmentation.sh @@ -46,336 +46,336 @@ NC='\033[0m' # No color # Help print_help() { # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" - echo " " - echo "This script is used to evaluate Shufflecake Lite 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 " " - 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 Lite device with two volumes." - 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 " " + echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" + echo " " + echo "This script is used to evaluate Shufflecake Lite 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 " " + 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 Lite device with two volumes." + 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 - 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 " " + 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 - 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 " " + 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 - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging 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 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + echo -e "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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + fi } # Find the path of Shufflecake executable find_sflc_path() { - local cmd="shufflecake" + local cmd="shufflecake" - # Check if the command exists in the current directory - if [[ -x "./$cmd" ]]; then - SFLCPATH=$(realpath ./) - SFLCNAME=$(realpath ./$cmd) - return - fi + # Check if the command exists in the current directory + if [[ -x "./$cmd" ]]; then + SFLCPATH=$(realpath ./) + SFLCNAME=$(realpath ./$cmd) + return + fi - # Check if the command exists in the parent directory - if [[ -x "../$cmd" ]]; then - SFLCPATH=$(realpath ../) - SFLCNAME=$(realpath ../$cmd) - return - fi + # Check if the command exists in the parent directory + if [[ -x "../$cmd" ]]; then + SFLCPATH=$(realpath ../) + 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) - return - fi - done + # 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) + return + fi + done - # If the command was not found, print an error message - echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 - exit 1 + # If the command was not found, print an error message + echo -e "${RED}ERROR: Command '$cmd' not found${NC}." >&2 + exit 1 } # Find and load module dm-sflc load_dmsflc() { - local mod="dm-sflc" + local mod="dm-sflc" # First, make sure that dm_mod is loaded modprobe dm_mod - # Check if the module is already loaded - if lsmod | grep -q "^$mod "; then - DMSFLC_INSTALLED=true - echo "Module '$mod' is already loaded." - return - fi + # Check if the module is already loaded + if lsmod | grep -q "^$mod "; then + DMSFLC_INSTALLED=true + echo "Module '$mod' is already loaded." + return + fi - # If not, look for the module file and try to load it - local mod_file="$mod.ko" + # If not, look for the module file and try to load it + 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." - return - fi + # 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." + 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." - 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." + 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 - exit 1 + # If the module file was not found, print an error message + echo -e "${RED} ERROR: Module file '$mod_file' not found.${NC}." >&2 + exit 1 } # Unload dm-sflc if it was loaded locally 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." - fi + 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." + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - unload_dmsflc - usage - exit 1 - fi + if [ -b "$1" ]; then + 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 + usage + exit 1 + fi } # Function to create loop device create_loop_device() { - 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}" - exit 1 - fi - sudo 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 - echo "$LOOP_DEVICE" + 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}" + exit 1 + fi + sudo 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 + 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}" - return 1 - else - echo $volname - return 0 - fi + # 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}" + return 1 + else + echo $volname + return 0 + fi } # 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}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + 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}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - SFLCVOLUME="" - VOLNAME="" - MNTPOINT="" - NUMPOINTS=21 # number of graph points - VOLSIZE=$(blockdev --getsize64 "$BLOCK_DEVICE") # first approximation, in bytes + SFLCVOLUME="" + VOLNAME="" + MNTPOINT="" + NUMPOINTS=21 # number of graph points + VOLSIZE=$(blockdev --getsize64 "$BLOCK_DEVICE") # first approximation, in bytes - echo "Starting fragmentation test for Shufflecake Lite..." - # init - echo "Initializing block device $BLOCK_DEVICE with two Shufflecake Lite volumes (--skip-randfill)..." - etime=$( (time echo -e "passwd1\npasswd2" | $SFLCNAME --skip-randfill -n 2 init $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action init took $etime seconds.${NC}" - echo "Shufflecake Lite device initialized. Opening hidden volume (nr. 2)..." - # open - etime=$( (time echo -e "passwd2" | $SFLCNAME open $BLOCK_DEVICE > /dev/null) 2>&1 ) - echo -e "${GREEN}Action open took $etime seconds.${NC}" - # fetch SFLCVOLUME - SFLCVOLUME=$(find_sflcvolname) - # trim path of SFLCVOLUME - 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..." - # assign and create MNTPOINT - MNTPOINT=$(realpath "./sflc_mnt") - mkdir $MNTPOINT - # mount - mount $SFLCVOLUME $MNTPOINT - echo "Volume mounted at $MNTPOINT. Starting fragmentation test..." - # TESTS HERE - - # 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}" - 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 - - # refine down block device max size according to available slices, to make sure there is no overflow - 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 - 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}" + echo "Starting fragmentation test for Shufflecake Lite..." + # init + echo "Initializing block device $BLOCK_DEVICE with two Shufflecake Lite volumes (--skip-randfill)..." + etime=$( (time echo -e "passwd1\npasswd2" | $SFLCNAME --skip-randfill -n 2 init $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo -e "${GREEN}Action init took $etime seconds.${NC}" + echo "Shufflecake Lite device initialized. Opening hidden volume (nr. 2)..." + # open + etime=$( (time echo -e "passwd2" | $SFLCNAME open $BLOCK_DEVICE > /dev/null) 2>&1 ) + echo -e "${GREEN}Action open took $etime seconds.${NC}" + # fetch SFLCVOLUME + SFLCVOLUME=$(find_sflcvolname) + # trim path of SFLCVOLUME + 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..." + # assign and create MNTPOINT + MNTPOINT=$(realpath "./sflc_mnt") + mkdir $MNTPOINT + # mount + mount $SFLCVOLUME $MNTPOINT + echo "Volume mounted at $MNTPOINT. Starting fragmentation test..." + # TESTS HERE + + # 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}" + 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 + + # refine down block device max size according to available slices, to make sure there is no overflow + 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 + 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 - # 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 - - # loop for i = 1 to NUMPOINTS - for (( i=1; i<$NUMPOINTS-1; i++ )); do - X_COORD=$(echo "scale=3; $i / ($NUMPOINTS-1) " | bc) - # 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) - [ ! -e "${MNTPOINT}/${RNDDIRNAME}" ] && break - done - # create random dir - mkdir "${MNTPOINT}/${RNDDIRNAME}" - - # 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) - [ ! -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 - sync + 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 + + # loop for i = 1 to NUMPOINTS + for (( i=1; i<$NUMPOINTS-1; i++ )); do + X_COORD=$(echo "scale=3; $i / ($NUMPOINTS-1) " | bc) + # 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) + [ ! -e "${MNTPOINT}/${RNDDIRNAME}" ] && break + done + # create random dir + mkdir "${MNTPOINT}/${RNDDIRNAME}" + + # 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) + [ ! -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 + sync - # compute slice occupation threshold - read -r OCCSLICES < ${MOST_RECENT_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" - exit 1 - fi - # print point coords - echo -e "${GREEN}$X_COORD $Y_COORD${NC}" - done - # manually print last point to avoid rounding error artifacts at the last write - echo -e "${GREEN}1.0 1.0${NC}" - - fi - # end table - echo -e "${GREEN}---------------------------------${NC}" - - # END TESTS - echo "Shufflecake Lite 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 - + # compute slice occupation threshold + read -r OCCSLICES < ${MOST_RECENT_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" + exit 1 + fi + # print point coords + echo -e "${GREEN}$X_COORD $Y_COORD${NC}" + done + # manually print last point to avoid rounding error artifacts at the last write + echo -e "${GREEN}1.0 1.0${NC}" + + fi + # end table + echo -e "${GREEN}---------------------------------${NC}" + + # END TESTS + echo "Shufflecake Lite 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 "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 } @@ -396,14 +396,14 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo - + echo -e "${BLUE}Initializing Shufflecake...${NC}" echo "Searching Shufflecake executable..." find_sflc_path @@ -417,28 +417,28 @@ 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" - ;; + 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 check_block_device "$BLOCK_DEVICE" @@ -446,9 +446,9 @@ check_block_device "$BLOCK_DEVICE" # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi unload_dmsflc diff --git a/benchmark-suite/veracrypt-benchmark.sh b/benchmark-suite/veracrypt-benchmark.sh index 2ecbd9f..e818c60 100755 --- a/benchmark-suite/veracrypt-benchmark.sh +++ b/benchmark-suite/veracrypt-benchmark.sh @@ -38,174 +38,174 @@ NC='\033[0m' # No color # Help print_help() { # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" - echo " " - echo "This script is used to benchmark VeraCrypt on this machine." - 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 " " - 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 standard and unformatted VeraCrypt volumes within a given device." - echo "2) Creates a hidden ext4 VeraCrypt volume within the standard one." - echo "3) Opens the hidden volume, and mounts it." - echo "4) Performs various fio r/w stress operations on it." - echo "5) Unmounts and closes the used volume." - echo " " + echo -e "${BLUE}Usage: ${SCRIPTNAME} [OPTION]... [BLOCKDEVICE]${NC}" + echo " " + echo "This script is used to benchmark VeraCrypt on this machine." + 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 " " + 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 standard and unformatted VeraCrypt volumes within a given device." + echo "2) Creates a hidden ext4 VeraCrypt volume within the standard one." + echo "3) Opens the hidden volume, and mounts it." + echo "4) Performs various fio r/w stress operations on it." + echo "5) Unmounts and closes the used volume." + echo " " # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo "You can pass the path to a (non-loop) block device as an optional argument, " - echo "otherwise the script will ask for one. If no path is provided, the script " - echo "will create a 1 GiB VeraCrypt container file which will be removed at the end." - echo "NOTICE: This script has been tested only with VeraCrypt v 1.25.9." - echo " " + echo "You can pass the path to a (non-loop) block device as an optional argument, " + echo "otherwise the script will ask for one. If no path is provided, the script " + echo "will create a 1 GiB VeraCrypt container file which will be removed at the end." + echo "NOTICE: This script has been tested only with VeraCrypt v 1.25.9." + echo " " # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" - echo " " - exit 0 + echo -e "${BLUE}WARNING: ALL CONTENT OF THE PROVIDED BLOCK DEVICE WILL BE ERASED!${NC}" + echo " " + exit 0 } # Function for debugging bpx() { - if [ -z "$1" ]; then - echo "BPX: Paused. Press any key to continue..." >&2 - else - echo "BPX: $1. Press any key to continue..." >&2 - fi - read -n1 -s + if [ -z "$1" ]; then + echo "BPX: Paused. Press any key to continue..." >&2 + else + 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} --help${NC} for usage and help." } # Check that this is run as root check_sudo() { - if [[ $EUID -ne 0 ]]; then - echo -e "${RED}Error: This script must be run as root.${NC}" - usage - exit 1 - fi + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}Error: This script must be run as root.${NC}" + usage + exit 1 + 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 - else - echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" - usage - exit 1 - fi + if [ -b "$1" ]; then + echo "OK, block device path $1 is valid." >&2 + else + echo -e "${RED}Error: $1 is not a valid block device, aborting.${NC}" + usage + exit 1 + fi } check_not_loopdevice() { - DEVCHECK=$1 - if [[ $DEVCHECK == /dev/loop* ]]; then - echo -e "${RED}Error: $DEVCHECK is a loop device, this script does not work well with this type of devices due to how VeraCrypt works. Please either enter a non-loop block device, or just do not pass any path as input, this script will work on a container file directly.${NC}" - exit 1 - fi + DEVCHECK=$1 + if [[ $DEVCHECK == /dev/loop* ]]; then + echo -e "${RED}Error: $DEVCHECK is a loop device, this script does not work well with this type of devices due to how VeraCrypt works. Please either enter a non-loop block device, or just do not pass any path as input, this script will work on a container file directly.${NC}" + exit 1 + fi } # Function to create container file create_container_file() { - CONTAINER_FILENAME="$SCRIPT_DIR/veracrypt-benchmark-container-file.img" - echo "Creating an empty file $CONTAINER_FILENAME ..." >&2 - if [ -e "$CONTAINER_FILENAME" ]; then - echo -e "${RED}Error: Impossible to generate file, $CONTAINER_FILENAME already exists.${NC}" >&2 - exit 1 - fi - touch $CONTAINER_FILENAME - #dd if=/dev/zero of="$CONTAINER_FILENAME" bs=1M count=1024 > /dev/null - #echo "Writing of empty file complete." >&2 - echo "$CONTAINER_FILENAME" + CONTAINER_FILENAME="$SCRIPT_DIR/veracrypt-benchmark-container-file.img" + echo "Creating an empty file $CONTAINER_FILENAME ..." >&2 + if [ -e "$CONTAINER_FILENAME" ]; then + echo -e "${RED}Error: Impossible to generate file, $CONTAINER_FILENAME already exists.${NC}" >&2 + exit 1 + fi + touch $CONTAINER_FILENAME + #dd if=/dev/zero of="$CONTAINER_FILENAME" bs=1M count=1024 > /dev/null + #echo "Writing of empty file complete." >&2 + echo "$CONTAINER_FILENAME" } # Function for user confirmation confirm() { - while true; do - echo -e "${BLUE}Are you sure you want to proceed? All data in $BLOCK_DEVICE will be erased. (y/n)${NC}" - read -r response - case "$response" in - [yY]|[yY][eE][sS]) # Responded Yes - return 0 # Return 0 for Yes (success, convention for bash scripting) - ;; - [nN]|[nN][oO]) # Responded No - return 1 # Return 1 for No (error, convention for bash scripting) - ;; - *) # Responded something else - echo "Please press only (y)es or (n)o." - ;; - esac - done + while true; do + echo -e "${BLUE}Are you sure you want to proceed? All data in $BLOCK_DEVICE will be erased. (y/n)${NC}" + read -r response + case "$response" in + [yY]|[yY][eE][sS]) # Responded Yes + return 0 # Return 0 for Yes (success, convention for bash scripting) + ;; + [nN]|[nN][oO]) # Responded No + return 1 # Return 1 for No (error, convention for bash scripting) + ;; + *) # Responded something else + echo "Please press only (y)es or (n)o." + ;; + esac + done } # Benchmarks benchmark() { - MNTPOINT="" - TESTNAME="vc" - RUNTIME="10" # running time in seconds FOR EACH TEST - DATASIZE="500M" - TESTFILENAME="testfile" - echo "Starting benchmark for VeraCrypt..." + MNTPOINT="" + TESTNAME="vc" + RUNTIME="2" # running time in seconds FOR EACH TEST + DATASIZE="500M" + TESTFILENAME="testfile" + echo "Starting benchmark for VeraCrypt..." # First, make sure that dm_mod is loaded modprobe dm_mod - # Create a new standard volume - if [ -z "$CONTAINER_FILENAME" ]; then - # it's a real block device, do not specify size - etime=$( (time echo "passdecoy" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --volume-type=normal --password - --encryption=AES --hash=SHA-512 --filesystem=None --random-source=/dev/urandom > /dev/null) 2>&1 ) - else - # it's file-based, then VeraCrypt requires specifying the size - etime=$( (time echo "passdecoy" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --size=1G --volume-type=normal --password - --encryption=AES --hash=SHA-512 --filesystem=None --random-source=/dev/urandom > /dev/null) 2>&1 ) - fi - echo -e "${GREEN}Creation of standard VeraCrypt volume took $etime seconds.${NC}" - # Create a hidden ext4 volume inside the previous one - etime=$( (time echo "passhidden" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --volume-type=hidden --size="800M" --password - --encryption=AES --hash=SHA-512 --filesystem=ext4 --random-source=/dev/urandom > /dev/null) 2>&1 ) - echo -e "${GREEN}Creation of hidden VeraCrypt volume took $etime seconds.${NC}" - # assign and create MNTPOINT - MNTPOINT=$(realpath "./veracrypt_mnt") - mkdir $MNTPOINT - # mount hidden volume - etime=$( (time echo -"passhidden" | veracrypt --text --non-interactive $BLOCK_DEVICE $MNTPOINT --password - > /dev/null) 2>&1 ) - echo -e "${GREEN}Opening and mounting hidden VeraCrypt volume took $etime seconds.${NC}" - # TESTS HERE - # test 01: random read - echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 02: random write - echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 03: sequential read - echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # test 04: sequential write - echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." - OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') - echo -e "${GREEN}${OUTPUT}${NC}" - # END TESTS - # close - echo "Veracrypt fio tests ended. Detaching VeraCrypt volume..." - etime=$( (time veracrypt -d $MNTPOINT > /dev/null) 2>&1 ) - echo -e "${GREEN}Action close took $etime seconds.${NC}" - rmdir $MNTPOINT - echo "Volume detached and local mountpoint removed." - #end + # Create a new standard volume + if [ -z "$CONTAINER_FILENAME" ]; then + # it's a real block device, do not specify size + etime=$( (time echo "passdecoy" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --volume-type=normal --password - --encryption=AES --hash=SHA-512 --filesystem=None --random-source=/dev/urandom > /dev/null) 2>&1 ) + else + # it's file-based, then VeraCrypt requires specifying the size + etime=$( (time echo "passdecoy" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --size=1G --volume-type=normal --password - --encryption=AES --hash=SHA-512 --filesystem=None --random-source=/dev/urandom > /dev/null) 2>&1 ) + fi + echo -e "${GREEN}Creation of standard VeraCrypt volume took $etime seconds.${NC}" + # Create a hidden ext4 volume inside the previous one + etime=$( (time echo "passhidden" | veracrypt --text --non-interactive --quick --create $BLOCK_DEVICE --volume-type=hidden --size="800M" --password - --encryption=AES --hash=SHA-512 --filesystem=ext4 --random-source=/dev/urandom > /dev/null) 2>&1 ) + echo -e "${GREEN}Creation of hidden VeraCrypt volume took $etime seconds.${NC}" + # assign and create MNTPOINT + MNTPOINT=$(realpath "./veracrypt_mnt") + mkdir $MNTPOINT + # mount hidden volume + etime=$( (time echo -"passhidden" | veracrypt --text --non-interactive $BLOCK_DEVICE $MNTPOINT --password - > /dev/null) 2>&1 ) + echo -e "${GREEN}Opening and mounting hidden VeraCrypt volume took $etime seconds.${NC}" + # TESTS HERE + # test 01: random read + echo "Test 01: random read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-rnd --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 02: random write + echo "Test 02: random write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-rnd --ioengine=libaio --iodepth=32 --rw=randwrite --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 03: sequential read + echo "Test 03: sequential read with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-r-seq --ioengine=libaio --iodepth=32 --rw=read --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, read_iops: .read.iops, read_bw: .read.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # test 04: sequential write + echo "Test 04: sequential write with a queue of 32 4kiB blocks on a file (${RUNTIME}s)..." + OUTPUT=$(fio --name=$TESTNAME-w-seq --ioengine=libaio --iodepth=32 --rw=write --bs=4k --numjobs=1 --direct=1 --size=$DATASIZE --runtime=$RUNTIME --time_based --end_fsync=1 --filename=$MNTPOINT/$TESTFILENAME --output-format=json | jq '.jobs[] | {name: .jobname, write_iops: .write.iops, write_bw: .write.bw}') + echo -e "${GREEN}${OUTPUT}${NC}" + # END TESTS + # close + echo "Veracrypt fio tests ended. Detaching VeraCrypt volume..." + etime=$( (time veracrypt -d $MNTPOINT > /dev/null) 2>&1 ) + echo -e "${GREEN}Action close took $etime seconds.${NC}" + rmdir $MNTPOINT + echo "Volume detached and local mountpoint removed." + #end } # Clean up cleanup() { - echo "Exiting and cleaning..." - if [[ -n $CONTAINER_FILENAME ]]; then - echo "Deleting $CONTAINER_FILENAME..." - rm -f "$CONTAINER_FILENAME" - echo "Container file deleted." - fi + echo "Exiting and cleaning..." + if [[ -n $CONTAINER_FILENAME ]]; then + echo "Deleting $CONTAINER_FILENAME..." + rm -f "$CONTAINER_FILENAME" + echo "Container file deleted." + fi } @@ -226,17 +226,17 @@ echo -e "${BLUE}================================================================ case "$1" in # help - --help|-?) - print_help - exit 0 - ;; + --help|-?) + print_help + exit 0 + ;; esac check_sudo if ! which veracrypt >/dev/null; then - echo -e "${RED}ERROR: VeraCrypt not found, please install it.${NC}" - exit 1 + echo -e "${RED}ERROR: VeraCrypt not found, please install it.${NC}" + exit 1 fi echo " " @@ -245,34 +245,34 @@ echo " " # PARSER case "$1" in - "") # no argument passed + "") # no argument passed # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars - echo "Now you will be asked to enter the path for a block device to be used for the " - echo "benchmarks (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 a VeraCrypt container instead, then the file will be removed at the end." - echo " " - echo -n "Please enter the path for a (non-loop) block device (default: none): " - read BLOCK_DEVICE - if [ -z "$BLOCK_DEVICE" ]; then - echo "No path provided, using a local container file." - CONTAINER_FILENAME=$(create_container_file) - if [ -z "$CONTAINER_FILENAME" ]; then - exit - fi - BLOCK_DEVICE="$CONTAINER_FILENAME" - else - check_block_device "$BLOCK_DEVICE" - fi - - ;; - - # argument passed - *) - BLOCK_DEVICE="$1" - check_block_device "$BLOCK_DEVICE" - ;; + echo "Now you will be asked to enter the path for a block device to be used for the " + echo "benchmarks (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 a VeraCrypt container instead, then the file will be removed at the end." + echo " " + echo -n "Please enter the path for a (non-loop) block device (default: none): " + read BLOCK_DEVICE + if [ -z "$BLOCK_DEVICE" ]; then + echo "No path provided, using a local container file." + CONTAINER_FILENAME=$(create_container_file) + if [ -z "$CONTAINER_FILENAME" ]; then + exit + fi + BLOCK_DEVICE="$CONTAINER_FILENAME" + else + check_block_device "$BLOCK_DEVICE" + fi + + ;; + + # argument passed + *) + BLOCK_DEVICE="$1" + check_block_device "$BLOCK_DEVICE" + ;; esac check_not_loopdevice "$BLOCK_DEVICE" @@ -280,9 +280,9 @@ check_not_loopdevice "$BLOCK_DEVICE" # MAIN PROGRAM if confirm; then - benchmark + benchmark else - echo "Aborting..." + echo "Aborting..." fi cleanup