aboutsummaryrefslogtreecommitdiff
path: root/cli-tests/run.sh
diff options
context:
space:
mode:
Diffstat (limited to 'cli-tests/run.sh')
-rwxr-xr-xcli-tests/run.sh307
1 files changed, 307 insertions, 0 deletions
diff --git a/cli-tests/run.sh b/cli-tests/run.sh
new file mode 100755
index 0000000..9ab5b78
--- /dev/null
+++ b/cli-tests/run.sh
@@ -0,0 +1,307 @@
+#!/bin/bash
+#
+# run.sh - run the fscrypt command-line interface tests
+#
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+
+# Use extra-strict mode.
+set -e -u -o pipefail
+
+# Ensure we're in the cli-tests/ directory.
+cd "$(dirname "$0")"
+
+# Names of the test devices.
+# Variables with these names are exported to the tests.
+DEVICES=(DEV DEV_ROOT)
+
+# Names of the mountpoint of each test device.
+# Variables with these names are exported to the tests.
+MOUNTS=(MNT MNT_ROOT)
+
+# Name of the test user. This user will be created and deleted by this script.
+# This variable is exported to the tests.
+TEST_USER=fscrypt-test-user
+
+# The temporary directory to use.
+# This variable is exported to the tests.
+TMPDIR=$(mktemp -d /tmp/fscrypt.XXXXXX)
+
+# The loopback devices that correspond to each test device.
+LOOPS=()
+
+# Update the expected output files to match the actual output?
+UPDATE_OUTPUT=false
+
+LONGOPTS_ARRAY=(
+'help'
+'update-output'
+)
+LONGOPTS=$(echo "${LONGOPTS_ARRAY[*]}" | tr ' ' ,)
+
+cleanup()
+{
+ local mnt loop
+
+ # Unmount all the test filesystems.
+ for mnt in "${MOUNTS[@]}"; do
+ mnt="$TMPDIR/$mnt"
+ if mountpoint "$mnt" &> /dev/null; then
+ umount "$mnt"
+ fi
+ done
+
+ # Delete the loopback device of each test device.
+ for loop in "${LOOPS[@]}"; do
+ losetup -d "$loop"
+ done
+
+ # Delete all temporary files.
+ rm -rf "${TMPDIR:?}"/*
+}
+
+cleanup_full()
+{
+ cleanup
+ rm -rf "$TMPDIR"
+ userdel "$TEST_USER" || true
+}
+
+# Filters the output of the test script to make the output consistent on every
+# run of the test. For example, references to the mountpoint like
+# /tmp/fscrypt.4OTb6y/MNT will be replaced with simply MNT, since the name of
+# the temporary directory is different every time.
+filter_test_output()
+{
+ local sedscript=""
+ local raw_output=$TMPDIR/raw-test-output
+ local i
+
+ cat > "$raw_output"
+
+ # Filter mountpoint and device names.
+ for i in "${!DEVICES[@]}"; do
+ sedscript+="s@$TMPDIR/${MOUNTS[$i]}@${MOUNTS[$i]}@g;"
+ sedscript+="s@${LOOPS[$i]}@${DEVICES[$i]}@g;"
+ done
+
+ # Filter the path to fscrypt.conf.
+ sedscript+="s@$FSCRYPT_CONF@FSCRYPT_CONF@g;"
+
+ # Filter policy and protector descriptors.
+ sedscript+=$(grep -E -o '\<([a-f0-9]{16})|([a-f0-9]{32})\>' \
+ "$raw_output" \
+ | awk '{ printf "s@\\<" $1 "\\>@desc" NR "@g;" }')
+
+ # Filter any other paths in TMPDIR.
+ sedscript+="s@$TMPDIR@TMPDIR@g;"
+
+ # At some point, 'bash -c COMMAND' started showing error messages as
+ # "bash: line 1: " instead of just "bash: ". Filter out the "line 1: ".
+ sedscript+="s@^bash: line 1: @bash: @;"
+
+ # Work around protojson whitespace randomization.
+ sedscript+="/^Options: /s@ @ @g;"
+ sedscript+="s@^Options: @Options: @;"
+
+ sed -e "$sedscript" "$raw_output"
+}
+
+# Prepares to run a test script.
+setup_for_test()
+{
+ local i dev_var mnt_var img mnt loop
+
+ # Start with a clean state.
+ cleanup
+
+ # ../bin/fscrypt might not be accessible to $TEST_USER. Copy it into
+ # $TMPDIR so that $TEST_USER is guaranteed to have access to it.
+ mkdir "$TMPDIR/bin"
+ cp ../bin/fscrypt "$TMPDIR/bin/"
+ chmod 755 "$TMPDIR" "$TMPDIR/bin" "$TMPDIR/bin/fscrypt"
+
+ # Create the test filesystems and mountpoints.
+ LOOPS=()
+ for i in "${!DEVICES[@]}"; do
+ dev_var=${DEVICES[$i]}
+ mnt_var=${MOUNTS[$i]}
+ img="$TMPDIR/$dev_var"
+ if ! mkfs.ext4 -O encrypt -F -b 4096 -I 256 "$img" $((1<<15)) \
+ &> "$TMPDIR/mkfs.out"
+ then
+ cat 1>&2 "$TMPDIR/mkfs.out"
+ exit 1
+ fi
+ loop=$(losetup --find --show "$img")
+ LOOPS+=("$loop")
+ export "$dev_var=$loop"
+ mnt="$TMPDIR/$mnt_var"
+ export "$mnt_var=$mnt"
+ mkdir -p "$mnt"
+ mount "$loop" "$mnt"
+ done
+
+ # Give the tests their own "root" mount for storing login protectors, so
+ # they don't use the real "/".
+ export FSCRYPT_ROOT_MNT="$MNT_ROOT"
+
+ # Enable consistent output mode.
+ export FSCRYPT_CONSISTENT_OUTPUT="1"
+
+ # Give the tests their own fscrypt.conf.
+ export FSCRYPT_CONF="$TMPDIR/fscrypt.conf"
+ fscrypt setup --time=1ms --quiet --all-users > /dev/null
+
+ # The tests assume kernel support for v2 policies.
+ if ! grep -E -q '"policy_version": +"2"' "$FSCRYPT_CONF"; then
+ cat 1>&2 << EOF
+ERROR: Can't run these tests because your kernel doesn't support v2 policies.
+You need kernel v5.4 or later.
+EOF
+ exit 1
+ fi
+
+ # Set up the test filesystems that aren't already set up.
+ fscrypt setup --quiet --all-users "$MNT" > /dev/null
+}
+
+run_test()
+{
+ local t=$1
+
+ # Run the test script.
+ set +e
+ "./$1.sh" |& filter_test_output > "$t.out.actual"
+ status=${PIPESTATUS[0]}
+ set -e
+
+ # Check for failure status.
+ if [ "$status" != 0 ]; then
+ echo 1>&2 "FAILED: $t [exited with failure status $status]"
+ if [ -s "$t.out.actual" ]; then
+ if (( $(wc -l "$t.out.actual" | cut -f1 -d' ') > 10 )); then
+ echo 1>&2 "Last 10 lines of test output:"
+ tail -n10 "$t.out.actual" | sed 1>&2 's/^/ /'
+ echo 1>&2
+ echo 1>&2 "See $t.out.actual for the full output."
+ else
+ echo 1>&2 "Test output:"
+ sed 1>&2 's/^/ /' < "$t.out.actual"
+ fi
+ fi
+ exit 1
+ fi
+
+ # Check for output mismatch.
+ if ! cmp "$t.out" "$t.out.actual" &> /dev/null; then
+ if $UPDATE_OUTPUT; then
+ cp "$t.out.actual" "$t.out"
+ echo "Updated $t.out"
+ else
+ echo 1>&2 "FAILED: $t [output mismatch]"
+ echo 1>&2 "Differences between $t.out and $t.out.actual:"
+ echo 1>&2
+ diff 1>&2 "$t.out" "$t.out.actual"
+ exit 1
+ fi
+ fi
+ rm -f "$t.out.actual"
+}
+
+usage()
+{
+ cat << EOF
+Usage: run.sh [--update-output] [TEST_SCRIPT_NAME]..."
+EOF
+ exit 1
+}
+
+if ! options=$(getopt -o "" -l "$LONGOPTS" -- "$@"); then
+ usage
+fi
+eval set -- "$options"
+while (( $# >= 1 )); do
+ case "$1" in
+ --update-output)
+ UPDATE_OUTPUT=true
+ ;;
+ --)
+ shift
+ break
+ ;;
+ --help|*)
+ usage
+ ;;
+ esac
+ shift
+done
+
+if [ "$(id -u)" != 0 ]; then
+ echo 1>&2 "ERROR: You must be root to run these tests."
+ exit 1
+fi
+
+# Check for prerequisites.
+PREREQ_CMDS=(mkfs.ext4 expect keyctl)
+PREREQ_PKGS=(e2fsprogs expect keyutils)
+for i in ${!PREREQ_CMDS[*]}; do
+ if ! type -P "${PREREQ_CMDS[$i]}" > /dev/null; then
+ cat 1>&2 << EOF
+ERROR: You must install the '${PREREQ_PKGS[$i]}' package to run these tests.
+ Try a command like 'sudo apt-get install ${PREREQ_PKGS[$i]}'.
+EOF
+ exit 1
+ fi
+done
+
+# Use a consistent umask.
+umask 022
+
+# Use a consistent locale, to prevent output mismatches.
+export LANG=C
+export LC_ALL=C
+
+# Always cleanup fully on exit.
+trap cleanup_full EXIT
+
+# Create a test user, so that we can test non-root use of fscrypt. Give them a
+# password, so that we can test creating login passphrase protected directories.
+userdel "$TEST_USER" &> /dev/null || true
+useradd "$TEST_USER"
+echo "$TEST_USER:TEST_USER_PASS" | chpasswd
+export TEST_USER
+
+# Let the tests use $TMPDIR if they need it.
+export TMPDIR
+
+# Make it so that running 'fscrypt' in the tests runs the correct binary.
+export PATH="$TMPDIR/bin:$PATH"
+
+if (( $# >= 1 )); then
+ # Tests specified on command line.
+ tests=("$@")
+else
+ # No tests specified on command line. Just run everything.
+ tests=(t_*.sh)
+fi
+for t in "${tests[@]}"; do
+ t=${t%.sh}
+ echo "Running $t"
+ setup_for_test
+ run_test "$t"
+done
+
+echo "All tests passed!"