style: Convert leading spaces to tabs in benchmarks, change duration to 2s for testing

This commit is contained in:
Tommaso Gagliardoni 2025-09-02 09:53:51 +02:00
parent 327ff6b672
commit c1c8052f1b
6 changed files with 1356 additions and 1356 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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