#!/bin/bash
set -e
set -o pipefail
set -u

TestName="$(basename "$(pwd)")"
export TestName

#shellcheck source=../../environment.in
. ./environment
#shellcheck source=test-config.in
. ./test-config

#shellcheck source=../../scripts/functions
. "${BAREOS_SCRIPTS_DIR}"/functions
#shellcheck source=../../../core/scripts/bareos-config-lib.sh.in
. "${BAREOS_SCRIPTS_DIR}"/bareos-config-lib.sh

on_error()
{
  local lc="${BASH_COMMAND}"
  echo "Error occurred in testrunner script [${lc}]"
  export estat=1
  exit 1
}
trap 'on_error' ERR

# Prepare KEK and initial tests
ltodrive="${TAPE_DEVICES0[${USE_TAPE_DEVICE}]}"
changer="${CHANGER_DEVICE0}"
keyfile="etc/bareos/.ltocrypt-keyfile"
mybsc="sbin/bscrypto-${TestName}"
backup_log="$tmp/${TestName}-backup.out"
restore_log="$tmp/${TestName}-restore.out"
export ltodrive changer keyfile mybsc backup_log restore_log

reset_eject_drive()
{
  # cleanup before bareos tests
  /usr/bin/mt -f "${ltodrive}" rewind || :
  /usr/bin/mt -f "${ltodrive}" weof || :
  ${mybsc} -c ${ltodrive}
  # Empty the drive without failing.
  /usr/sbin/mtx -f "${changer}" unload 1 0 || :
  /usr/sbin/mtx -f "${changer}" inventory || :
}

start_test

# Create kek
if ! ${mybsc} -g "${keyfile}"; then
  printf "Creating KEK failed \n"
  export estat=1
  exit 1
fi
# Read keyfile and compare to previously generated
gen_kek="$(cat ${keyfile})"
read_kek="$($mybsc -k ${keyfile})"
if [ "${gen_kek}" != "$read_kek" ]; then
  printf "Created KEK [%s] differ from Read KEK [%s] failure. \n" "${gen_kek}" "${read_kek}"
  export estat=1
  exit 1
fi

# Set the KEK to configuration
# first stage is cleaning any previous run
sed -i "/Key Encryption Key =.*/d" etc/bareos/bareos-dir.d/director/bareos-dir.conf
sed -i "/Key Encryption Key =.*/d" etc/bareos/bareos-sd.d/director/bareos-dir.conf
# we don't use our config-libs set(get)_config_param_in_file
# due to bad parsing and writing of complex string like KEK
# here we also use œ char that will never appear in KEK
sed -i "s \(^}$\) Key\ Encryption\ Key\ =\ \"${gen_kek}\"\n\1 g" etc/bareos/bareos-dir.d/director/bareos-dir.conf
sed -i "s \(^}$\) Key\ Encryption\ Key\ =\ \"${gen_kek}\"\n\1 g" etc/bareos/bareos-sd.d/director/bareos-dir.conf
# Can be checked with
echo -n "configured global KEK: "
sbin/bareos_dir-scsicrypto --config etc/bareos --xc Director | grep -w KeyEncryptionKey

# We stop and restart daemon to take KEK into account.
stop_bareos
start_bareos

# Load first tape in, ignore errors
echo Checking drive and volume state
# Remove any leftover key from previous run
if ! ${mybsc} --clear-encryption ${ltodrive}; then
  printf "clearing encryption on ${ltodrive} failed\n"
  export estat=1
  exit 1
fi
/usr/sbin/mtx -f "${changer}" load 1 0 || :
sleep 1
/usr/sbin/mtx -f "${changer}" status

# Check that drive is not encrypted.
# Expected output like in 21.1.6 mhvtl
#Drive encryption status:
#   Encryption Mode: Disabled
#   Decryption Mode: Disabled
#   Raw Decryption Mode Disabled (RDMD): Disabled
#   Volume Contains Encrypted Logical Blocks (VCELB): Disabled
#   Logical Block encryption parameters: No report
# or with quadstor vtl
#   Logical Block encryption parameters: Drive Managed
drive_state="$(${mybsc} --drive-encryption-status "${ltodrive}" | awk -F ':' '/[De,En]cryption Mode:/ {gsub("^ ","",$2);print $2}' | xargs | sed -e 's/\n/ /g')"
if [ "${drive_state}" != "Disabled Disabled" ]; then
  printf "Drive state [%s] differs from expected [Disabled Disabled] \n" "${drive_state}"
  export estat=1
  exit 1
fi

# Check that tape is not encrypted.
# Expected output like in 21.1.6
# mhvtl
# Volume encryption status:
#   Compression Status: Unknown
#   Encryption Status: Illegal logical block
#   Raw Decryption Mode Disabled Status (RDMDS): Disabled
#   Encryption Mode External Status (EMES): Disabled
# quadstore vtl
# Volume encryption status:
#   Compression Status: Unavailable
#   Encryption Status: Unavailable
#   Raw Decryption Mode Disabled Status (RDMDS): Disabled
#   Encryption Mode External Status (EMES): Disabled

tape_state="$(${mybsc} --volume-encryption-status "${ltodrive}" | awk -F ':' '/Encryption Status:/ {gsub("^ ","",$2);print $2}' | xargs | sed -e 's/\n/ /g')"
if ([ "${tape_state}" != "Illegal logical block" ] && [ "$tape_state" != "Unavailable" ]); then
  printf "Tape state [%s] differs from expected [Illegal logical block] or [Unavailable] \n" "${tape_state}"
  export estat=1
  exit 1
fi

# Additionals tests can take place here to prove low level scsicrypto is working.
# + Set the key to drive and volume
# + do a tar job
# + check again volume
reset_eject_drive

# Bareos tests
cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages
@$out $backup_log
setdebug level=250 trace=1 timestamp=1 storage=Tape-0
update slots
label barcodes encrypt slot=1-4 drive=0 pool=Scratch storage=Tape-0 yes
wait
list media
run job=backup-bareos-fd Level=Full pool=Full storage=Tape-0 spooldata=yes yes
wait
status director
status client
wait
release storage=Tape-0 drive=0
update slots
wait
status slots storage=Tape-0
wait
messages
setdebug level=0 all
END_OF_DATA

run_bconsole

volume=$(awk '/^.*Volume name\(s\):.*/ {print $3}' ${backup_log})

# Test a restore
cat <<END_OF_DATA >$tmp/bconcmds
@$out ${NULL_DEV}
messages
@$out $restore_log
setdebug level=250 trace=1 timestamp=1 storage=Tape-0
update slots
restore client=bareos-fd where=$tmp/bareos-restores select all done yes
wait
messages
release storage=Tape-0 drive=0
update slots
wait
status slots storage=Tape-0
wait
messages
setdebug level=0 all
quit
END_OF_DATA

run_bconsole

# check_for_zombie_jobs storage=Tape-0
stop_bareos
sleep 1

expect_grep "Backup OK" \
  "$backup_log" \
  "Backup job failed."

expect_grep "Restore OK" \
  "$restore_log" \
  "Restore job failed."

# todo find a way to detect if encryption is used
# maybe in the trace log
#expect_grep "Encryption Mode: Encrypt" \
#            "$backup_log" \
#            "Encryption mode wrong or not found."
#
#expect_grep "Decryption Mode: Mixed" \
#            "$restore_log" \
#            "Decryption mode wrong or not found."

check_restore_diff "${BackupDirectory}"

# Dump cache
crypto_cache_log=$tmp/crypto_cache.log
crypto_cache="${working_dir}/bareos-sd.${BAREOS_STORAGE_PORT}.cryptoc"
if ! "${mybsc}" -d250 --dt --dump-cache "${crypto_cache}" >"${crypto_cache_log}"; then
  printf "Dumping crypto cache failed \n"
  export estat=1
  exit 1
fi
expect_grep "${volume}" \
  "${crypto_cache_log}" \
  "Volume ${volume} not found in cache dump".

# Check if crypto cache is there and readable.
# Again clear the key from the drive
if ! "${mybsc}" -d250 --dt --clear-encryption "${ltodrive}"; then
  printf "Clearing KEK failed \n"
  export estat=1
  exit 1
fi

# With wrapped key volume in crypto cache we should be able
# to check the drive and volume encryption status

# Using the SD KEK we should be able to unwrap the key in cache
# then use the key to setup the drive and being then able to
# to read back data from the volume

# the test should also check that when the drive as no key,
# then no data can be read from the volume.
# Otherwise it mean we wrote clear data on it !!!

# beware to work the tools bls,bextract etc need the have
# --director(-D) parameter to determined which KEK to use
# (defined in bareos-sd.d/director resource)

# Test volume content listing
# export confdir for mtx-changer so mtx-changer find it.
export confdir=$(pwd)/etc/bareos
# load the tape into the drive
slot=$(mtx -f $changer status | awk -F ":" "/VolumeTag=${volume}/ {gsub(\"Storage Element\",\"\",\$1);gsub(\" \",\"\",\$1); print \$1}")
/usr/sbin/mtx -f "${changer}" load ${slot} 0 || :
/usr/bin/mt -f "${ltodrive}" rewind || :
bls_lj_log=$tmp/bls-label_job_output.log
tape_device=$(awk -F '=' '/^ .*Name.*=.*tapedrive-0.*/ {gsub(" ","",$2);gsub("\"","",$2);print $2}' etc/bareos/bareos-sd.d/device/tape_devices.conf)
sbin/bls-"${TestName}" -d10 -v -c "$(pwd)/etc/bareos" --director "bareos-dir" -L -j -V ${volume} ${tape_device} >"${bls_lj_log}"

expect_grep "Crypto cache read 1 entries" \
  "$bls_lj_log" \
  "bls: no crypto cache entry found."

expect_grep "JobId             : 1" \
  "$bls_lj_log" \
  "BLS jobid not found."

bls_log=$tmp/bls-full_output.log
sbin/bls-"${TestName}" -d10 -v -c "$(pwd)/etc/bareos" --director "bareos-dir" -V ${volume} ${tape_device} >"${bls_log}"
expect_grep "bls JobId 0: Ready to read from volume \"${volume}\" on device \"${tape_device}\"" \
  "${bls_log}" \
  "BLS failed to read volume."

restore_directory=${tmp}/bareos-restores
rm -rf "${restore_directory}"
mkdir -p "${restore_directory}" || :
bextract_log="${tmp}/bextract-output.log"
# Test volume extraction
sbin/bextract-"${TestName}" -d10 -v -c "$(pwd)/etc/bareos" --director "bareos-dir" -V ${volume} ${tape_device} "${restore_directory}" >"${bextract_log}"
expect_grep "Crypto cache read 1 entries" \
  "${bextract_log}" \
  "bextract: no crypto cache entry found."

check_restore_diff "${BackupDirectory}"

end_test
