mirror of
https://codeberg.org/shufflecake/shufflecake-c.git
synced 2026-01-06 02:55:28 -05:00
feat: Add consistency test script
This commit is contained in:
parent
edee3842e5
commit
35522bc595
3 changed files with 421 additions and 2 deletions
2
Makefile
2
Makefile
|
|
@ -36,6 +36,8 @@ debug:
|
|||
|
||||
test:
|
||||
make -C shufflecake-userland test
|
||||
@echo "Running test scripts... This step requires superuser privileges."
|
||||
@bash tests/consistency.sh
|
||||
|
||||
install:
|
||||
# Just a placeholder for installation, for now equivalent to `make`
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ main: $(MAIN_BIN)
|
|||
|
||||
.PHONY: test
|
||||
test: $(TEST_BIN)
|
||||
@echo "Launching compiled tests"
|
||||
@./$(TEST_LINK)
|
||||
|
||||
.PHONY: link_msg
|
||||
link_msg:
|
||||
|
|
@ -101,8 +103,6 @@ $(TEST_BIN): $(PROJ_OBJS_NO_MAIN) $(TEST_OBJS) | link_msg
|
|||
@$(CC) $^ -o $@ $(LDFLAGS)
|
||||
@rm -f $(TEST_LINK)
|
||||
@ln -s $@ $(TEST_LINK)
|
||||
@echo "Done, launching tests"
|
||||
@./$(TEST_LINK)
|
||||
|
||||
# Cancel implicit rule
|
||||
%.o : %.c
|
||||
|
|
|
|||
417
tests/consistency.sh
Executable file
417
tests/consistency.sh
Executable file
|
|
@ -0,0 +1,417 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright The Shufflecake Project Authors (2022)
|
||||
# Copyright The Shufflecake Project Contributors (2022)
|
||||
# Copyright Contributors to the The Shufflecake Project.
|
||||
|
||||
# See the AUTHORS file at the top-level directory of this distribution and at
|
||||
# <https://www.shufflecake.net/permalinks/shufflecake-c/AUTHORS>
|
||||
|
||||
# This file is part of the program shufflecake-c, which is part of the Shufflecake
|
||||
# Project. Shufflecake is a plausible deniability (hidden storage) layer for
|
||||
# Linux. See <https://www.shufflecake.net>.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation, either version 2 of the License, or (at your option)
|
||||
# any later version. This program is distributed in the hope that it will be
|
||||
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details. You should have received a copy of the
|
||||
# GNU General Public License along with this program.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Benchmarking script for Shufflecake
|
||||
|
||||
# Variables
|
||||
SCRIPTNAME=$(basename "$0")
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
LOOP_FILENAME="${SCRIPT_DIR}/sflc-test-loop-file.img"
|
||||
LOOP_DEVICE=""
|
||||
BLOCK_DEVICE=""
|
||||
SFLCVOLUME=""
|
||||
MNTPOINT=""
|
||||
TIMEFORMAT='%3R'
|
||||
SFLCPATH=""
|
||||
SFLCNAME=""
|
||||
DMSFLC_INSTALLED=false
|
||||
|
||||
# Colors
|
||||
BLUE='\033[0;34m'
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
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 test consistency of Shufflecake on this machine."
|
||||
echo "This script is part of the Shufflecake test suite."
|
||||
echo "Shufflecake is a plausible deniability (hidden storage) layer for Linux."
|
||||
echo -e "Visit ${BLUE}https://www.shufflecake.net${NC} for more info and documentation."
|
||||
echo " "
|
||||
echo "This script requires root because it operates on block devices, please run it "
|
||||
echo -e "with ${BLUE}sudo${NC}. It does the following:"
|
||||
echo "1) Creates a Shufflecake (Lite) device with two volumes."
|
||||
echo "2) Opens both volumes, formats them, and mounts them."
|
||||
echo "3) Writes two large random files into each volume, saves their checksums."
|
||||
echo "4) Unmounts and closes the used volume."
|
||||
echo "5) Re-opens both devices, reads the files, and checks they didn't change."
|
||||
# TODO: add slice recovery check
|
||||
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 " "
|
||||
# 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 2 GiB"
|
||||
echo "local file and use it to back loop devices as a virtual block devices 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
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
# Show usage
|
||||
usage() {
|
||||
echo -e "Use ${BLUE}${SCRIPTNAME} --help${NC} for usage and help."
|
||||
}
|
||||
|
||||
# Clean up function, called by trap on exit
|
||||
cleanup() {
|
||||
echo "Exiting and cleaning..."
|
||||
|
||||
# Unmount filesystem if mounted
|
||||
if [ -n "$MNTPOINT" ] && grep -qs "$MNTPOINT" /proc/mounts; then
|
||||
echo "Unmounting \"$MNTPOINT\" ..."
|
||||
umount "$MNTPOINT"
|
||||
fi
|
||||
|
||||
# Remove mount point directory if it exists
|
||||
if [ -n "$MNTPOINT" ] && [ -d "$MNTPOINT" ]; then
|
||||
rmdir "$MNTPOINT"
|
||||
fi
|
||||
|
||||
# Close shufflecake volume if open. Check if the SFLCVOLUME variable
|
||||
# was set and if the corresponding device path still exists.
|
||||
if [ -n "$SFLCVOLUME" ] && [ -b "$SFLCVOLUME" ]; then
|
||||
echo "Closing Shufflecake device on \"$BLOCK_DEVICE\" ..."
|
||||
"$SFLCNAME" close "$BLOCK_DEVICE" &> /dev/null
|
||||
fi
|
||||
|
||||
# Unload kernel module if we loaded it
|
||||
unload_dmsflc
|
||||
|
||||
# Detach loop device if created
|
||||
if [[ -n "$LOOP_DEVICE" ]]; then
|
||||
echo "Detaching \"$LOOP_DEVICE\" ..."
|
||||
losetup -d "$LOOP_DEVICE"
|
||||
echo "Deleting \"$LOOP_FILENAME\" ..."
|
||||
rm -f "$LOOP_FILENAME"
|
||||
echo "Loop device detached and backing file deleted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Set trap to run cleanup function on exit
|
||||
trap cleanup EXIT SIGHUP SIGINT SIGTERM
|
||||
|
||||
# Check that this script 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
|
||||
}
|
||||
|
||||
# Find the path of Shufflecake executable
|
||||
find_sflc_path() {
|
||||
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 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
|
||||
|
||||
# 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"
|
||||
# Kernel uses underscores, but module files may use hyphens.
|
||||
local mod_name_loaded="${mod//-/_}"
|
||||
|
||||
# First, make sure that dm-mod is loaded
|
||||
modprobe dm_mod
|
||||
|
||||
# Check if the module is already loaded
|
||||
if lsmod | grep -q "^${mod_name_loaded} "; then
|
||||
DMSFLC_INSTALLED=true
|
||||
echo "Module '${mod}' is already loaded."
|
||||
return
|
||||
fi
|
||||
|
||||
# Try loading from system modules first
|
||||
if modprobe "$mod" &> /dev/null; then
|
||||
echo "Module '${mod}' loaded from system modules."
|
||||
return
|
||||
fi
|
||||
|
||||
# If not, look for the module file and try to load it
|
||||
local mod_file="${mod}.ko"
|
||||
|
||||
# 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
|
||||
|
||||
# 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 by this script
|
||||
unload_dmsflc() {
|
||||
local mod="dm-sflc"
|
||||
# Kernel uses underscores, but module files may use hyphens.
|
||||
local mod_name_loaded="${mod//-/_}"
|
||||
|
||||
if [ "$DMSFLC_INSTALLED" = true ]; then
|
||||
echo "Module '${mod}' will not be unloaded because it was already present when the script started."
|
||||
return
|
||||
fi
|
||||
|
||||
if lsmod | grep -q "^${mod_name_loaded} "; then
|
||||
echo "Unloading module '${mod}'..."
|
||||
rmmod "${mod_name_loaded}" || echo -e "${RED}Warning: Failed to unload module '${mod}'.${NC}" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check if argument is a block device
|
||||
check_block_device() {
|
||||
if [ -b "$1" ]; then
|
||||
echo "OK, block device path \"$1\" is valid." >&2
|
||||
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
|
||||
dd if=/dev/zero of="$LOOP_FILENAME" bs=1M count=2048 &> /dev/null
|
||||
echo "Writing of empty file complete. I will now try to attach it to a new loop device..." >&2
|
||||
LOOP_DEVICE=$(losetup -f --show "$LOOP_FILENAME")
|
||||
if [ $? -ne 0 ] || [ -z "$LOOP_DEVICE" ]; then
|
||||
echo -e "${RED}Error: Failed to create loop device.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Successfully created loop device \"$LOOP_DEVICE\" ." >&2
|
||||
echo "$LOOP_DEVICE"
|
||||
}
|
||||
|
||||
# Finds the sflc volume that was created last
|
||||
find_sflcvolname() {
|
||||
local files=(/dev/mapper/sflc_*)
|
||||
# Check if the glob expanded to any existing files
|
||||
if [ ! -e "${files[0]}" ]; then
|
||||
echo -e "${RED}ERROR: No sflc_ devices found in /dev/mapper !${NC}" >&2
|
||||
return 1
|
||||
fi
|
||||
# Use printf to list one file per line, then sort and get the last one
|
||||
printf '%s\n' "${files[@]}" | sort -t '_' -k 2n,2 -k 3n,3 | tail -n 1
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Function for user confirmation
|
||||
confirm() {
|
||||
while true; do
|
||||
echo -e "${BLUE}Are you sure you want to proceed? All data on disk \"$BLOCK_DEVICE\" will be erased. (y/n)${NC}"
|
||||
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
|
||||
}
|
||||
|
||||
# Tests
|
||||
do_test() {
|
||||
TESTNAME="sflc-test"
|
||||
RUNTIME="10" # running time in seconds
|
||||
DATASIZE="100M"
|
||||
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 )
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Shufflecake init failed.${NC}" >&2; exit 1; fi
|
||||
echo -e "${GREEN}Action init took $etime seconds.${NC}"
|
||||
|
||||
echo "Shufflecake device initialized. Opening hidden volume (nr. 2)..."
|
||||
etime=$( (time echo -e "passwd2" | "$SFLCNAME" open "$BLOCK_DEVICE" &> /dev/null) 2>&1 )
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Shufflecake open failed.${NC}" >&2; exit 1; fi
|
||||
echo -e "${GREEN}Action open took $etime seconds.${NC}"
|
||||
|
||||
SFLCVOLUME=$(find_sflcvolname)
|
||||
if [ $? -ne 0 ]; then exit 1; fi # Error message is already in the function
|
||||
echo "Shufflecake Lite volume opened as \"$SFLCVOLUME\". Formatting with ext4..."
|
||||
|
||||
mkfs.ext4 "$SFLCVOLUME" &> /dev/null
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: mkfs.ext4 failed on \"$SFLCVOLUME\".${NC}" >&2; exit 1; fi
|
||||
udevadm settle
|
||||
echo "Volume \"$SFLCVOLUME\" formatted. Mounting that..."
|
||||
|
||||
MNTPOINT=$(realpath "./sflc_mnt")
|
||||
mkdir -p "$MNTPOINT"
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Could not create mountpoint \"$MNTPOINT\".${NC}" >&2; exit 1; fi
|
||||
|
||||
mount "$SFLCVOLUME" "$MNTPOINT"
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Mount failed for \"$SFLCVOLUME\" on \"$MNTPOINT\".${NC}" >&2; exit 1; fi
|
||||
echo "Volume mounted at \"$MNTPOINT\". Starting fio tests..."
|
||||
|
||||
# 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}')
|
||||
if [ $? -ne 0 ]; then echo -e "${RED}ERROR: Fio test 01 failed.${NC}" >&2; exit 1; fi
|
||||
printf "${GREEN}%s${NC}\n" "$OUTPUT"
|
||||
|
||||
echo "Shufflecake Lite fio tests ended."
|
||||
# The cleanup trap will handle unmounting and closing.
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
|
||||
# MAIN SCRIPT BODY STARTS HERE
|
||||
|
||||
#####################################################################
|
||||
|
||||
# BANNER
|
||||
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 chars
|
||||
echo -e "${BLUE}===============================================================================${NC}"
|
||||
echo -e "${BLUE} Consistency Test for Shufflecake Lite${NC}"
|
||||
echo -e "${BLUE}===============================================================================${NC}"
|
||||
|
||||
|
||||
# ARGUMENT PARSING
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--help|-?)
|
||||
print_help
|
||||
;;
|
||||
*)
|
||||
if [ -n "$BLOCK_DEVICE" ]; then
|
||||
echo -e "${RED}Error: You can only specify one block device.${NC}" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
BLOCK_DEVICE="$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# PRELIMINARY CHECKS
|
||||
check_sudo
|
||||
|
||||
echo -e "${BLUE}Initializing Shufflecake...${NC}"
|
||||
echo "Searching Shufflecake executable..."
|
||||
find_sflc_path
|
||||
echo "Shufflecake executable found at \"$SFLCNAME\"."
|
||||
echo "Searching and loading dm-sflc module..."
|
||||
load_dmsflc
|
||||
echo "Module dm-sflc status determined. DMSFLC_INSTALLED flag is: $DMSFLC_INSTALLED."
|
||||
echo " "
|
||||
|
||||
|
||||
# DETERMINE BLOCK DEVICE
|
||||
if [ -z "$BLOCK_DEVICE" ]; then
|
||||
echo "Now you will be asked to enter the path for a block device to be used for the "
|
||||
echo "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 -r BLOCK_DEVICE
|
||||
if [ -z "$BLOCK_DEVICE" ]; then
|
||||
echo "No path provided, creating a local file and loop device..."
|
||||
LOOP_DEVICE=$(create_loop_device)
|
||||
BLOCK_DEVICE="$LOOP_DEVICE"
|
||||
fi
|
||||
fi
|
||||
|
||||
check_block_device "$BLOCK_DEVICE"
|
||||
|
||||
# MAIN PROGRAM
|
||||
|
||||
if confirm; then
|
||||
do_test
|
||||
else
|
||||
echo "Aborting..."
|
||||
fi
|
||||
|
||||
# The cleanup trap will automatically run on exit.
|
||||
# END SCRIPT
|
||||
Loading…
Add table
Add a link
Reference in a new issue