#!/bin/bash

usage() {
  bn=`basename "${0}"`
  echo
  echo "Usage: ${0} {disk device} [--raw|--gpt] [--ext|--xfs]"
  echo "    [--crypto [--blank] | --info | --tokinit | --tokadd [--slot {n}] ]"
  echo
  echo "Vanilla Examples"
  echo "  Init disk, EXT4 on raw disk:  ${bn} /dev/sdx --raw --ext"
  echo "  Init disk, XFS on part 1:     ${bn} /dev/sdx --gpt --xfs"
  echo
  echo "Crypto Examples"
  echo "  Init disk (break-glass key):  ${bn} /dev/sdx --crypto --gpt --xfs"
  echo "  Show info from existing disk: ${bn} /dev/sdx1 --crypto --info"
  echo "  Initialise token (once only): ${bn} /dev/sdx1 --crypto --tokinit"
  echo "  Enroll primary Yubikey:       ${bn} /dev/sdx1 --crypto --tokadd --slot 1"
  echo "  Enroll recovery Yubikey:      ${bn} /dev/sdx1 --crypto --tokadd --slot 2"
  echo "  Delete key material, slot 2:  ${bn} /dev/sdx1 --crypto --tokdel --slot 2"
  echo
  echo "Recommended"
  echo "  Format, break-glass pw: ${bn} /dev/sda --crypto --gpt --xfs"
  echo "  Enroll the Yubikey Neo: ${bn} /dev/sda1 --crypto --tokadd --slot 1"
  echo
  exit 1
}

do_gpt() {
  echo
  echo "+---- Initialising device '${1}' with GPT .."
  dd if=/dev/zero of=${1} bs=512 count=4096
  sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | gdisk "${1}"
    2  # create new GPT
    Y  # Yes, delete all partitions
    o  # clear the in memory partition table
    Y  # Yes, delete all partitions
    n  # create new partition
    1  # parition 1
       # default, first block
       # default, last block
       # default, 8300 Linux Filesystem
    p  # print the partition table
    w  # write the partition to disk
    Y  # Yes, delete all partitions
    q  # quit the program
EOF
  echo
}

do_xfs() {
  echo
  echo "+---- Initialising device '${1}' with XFS .."
  mkfs.xfs -s size=4k -f "${1}"
  echo
}

do_ext() {
  echo
  echo "+---- Initialising device '${1}' with EXT4 .."
  mke2fs -t ext4 -F -J size=256 "${1}"
  echo
}

do_metadata() {
  echo
  echo "+---- Writing metadata structure to '${1}' .."
  mkdir "${1}/metadata"
  mkdir "${1}/metadata/master"
  mkdir "${1}/metadata/logger"
  chmod -R 755 "${1}/metadata"
  chown -R mfs:mfs "${1}/metadata"
  if [ -L "/metadata" ]
  then
    echo "Warning: symlink exists, skipping metadata linkage and seeding."
  else
    ln -s "${1}/metadata" "/metadata"
    grep "MFSMASTER_ENABLE=true" "/etc/default/moosefs-master" >/dev/null 2>&1
    if [ ${?} -eq 0 ]
    then
      mv /var/lib/mfs/metadata.* "${1}/metadata/master/"
      chown mfs:mfs "${1}/metadata/master"/*
    fi
    sed -i "s#var\/lib\/mfs#metadata\/master#g" "/lib/systemd/system/moosefs-master.service"
    sed -i "s#var\/lib\/mfs#metadata\/logger#g" "/lib/systemd/system/moosefs-metalogger.service"
  fi
}

info() {
  echo
  echo "+---- Dumping info for device '${1}' .."
  cryptsetup luksDump "${1}"
  echo
  exit 0
}

tokinit() {
  echo
  echo "+---- Initialising token position two for challenge-response .."
  ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -ochal-btn-trig -oserial-api-visible
  echo
  exit 0
}

tokadd() {
  echo
  echo "+---- Enrolling token for device '${1}' in slot '${2}' .."
  yubikey-luks-enroll -d "${1}" -s "${2}"
  echo
  exit 0
}

tokdel() {
  echo
  echo "+---- Deleting auth material for device '${1}' in slot '${2}' .."
  cryptsetup luksKillSlot "${1}" "${2}"
  echo
  exit 0
}

if [ ! ${#} -gt 0 ]
then
  echo "Error: This program requires at least one parameter."
  usage
fi

dev=""
tin="false"
tad="false"
tde="false"
inf="false"
raw="false"
ext="false"
xfs="false"
gpt="false"
rnd="false"
cry="false"
slot="1"
while :; do
  case "${1}" in
    -b|--blank)
      rnd="true"
      ;;
    -c|--crypto)
      cry="true"
      ;;
    -e|--ext)
      ext="true"
      ;;
    -g|--gpt)
      gpt="true"
      ;;
    -h|--help)
      usage
      ;;
    -i|--info)
      inf="true"
      ;;
    -r|--raw)
      raw="true"
      ;;
    -s|--slot)
      shift
      if [ ${#2} -eq 1 -a -n "${2}" -a ${2} -lt 8 ] 2>/dev/null
      then
        slot="${2}"
      fi
      ;;
    -ta|--tokadd)
      tad="true"
      ;;
    -td|--tokdel)
      tde="true"
      ;;
    -ti|--tokinit)
      tin="true"
      ;;
    -x|--xfs)
      xfs="true"
      ;;
    *)
      if [ ${#1} -eq 0 ]
      then
        break
      fi
      dev="${1}"
      ;;
  esac
  shift
done

if [ "${dev}x" == "x" ]
then
  echo "Error: This program requires at least the device paramater."
  usage
fi

if [ "${raw}x" == "truex" -a "${gpt}x" == "truex" ]
then
  echo "Error: Raw and GPT have been selected, please select one or the other."
  usage
fi

if [ "${ext}x" == "falsex" -a "${xfs}x" == "falsex" ]
then
  if [ "${cry}x" == "falsex" ]
  then
    echo "Error: Neither EXT nor XFS has been selected, please choose one."
    usage
  else
    if [ "${raw}x" == "truex" -o "${gpt}x" == "truex" ]
    then
      echo "Error: Neither EXT nor XFS has been selected, please choose one."
      usage
    fi
  fi
fi
if [ "${ext}x" == "truex" -a "${xfs}x" == "truex" ]
then
  echo "Error: EXT and XFS have been selected, please select one or the other."
  usage
fi

if [ ! -b "${dev}" ]
then
  echo "Error: Block device '${dev}' does not exist or is invalid."
  usage
fi
dsk=`basename "${dev}"`

if [ "${inf}x" == "truex" ]
then
  if [ "${cry}x" == "falsex" ]
  then
    echo "Error: Info is a crypto function but crypto was not specified."
    usage
  fi
  info "${dev}"
fi

if [ "${tad}x" == "truex" ]
then
  if [ "${cry}x" == "falsex" ]
  then
    echo "Error: Token adding is a crypto function but crypto was not specified."
    usage
  fi
  tokadd "${dev}" "${slot}"
fi

if [ "${tde}x" == "truex" ]
then
  if [ "${cry}x" == "falsex" ]
  then
    echo "Error: Token deleting is a crypto function but crypto was not specified."
    usage
  fi
  tokdel "${dev}" "${slot}"
fi

if [ "${tin}x" == "truex" ]
then
  if [ "${cry}x" == "falsex" ]
  then
    echo "Error: Token initialisation is a crypto function but crypto was not specified."
    usage
  fi
  tokinit "${dev}"
fi

# echo $dev
# echo $raw
# echo $rnd

if [ -n "${dev: -1}" -a "${dev: -1}" -eq "${dev: -1}" ] 2>/dev/null
then
  if [ "${raw}x" == "truex" ]
  then
    echo "Error: Raw disk device selected but '${dev}' has numeric index (partition ${dev: -1}?)"
    usage
  fi
  if [ "${gpt}x" == "truex" ]
  then
    echo "Error: GPT selected but '${dev}' has numeric index (partition ${dev: -1}?)"
    usage
  fi
else
  if [ "${gpt}x" == "falsex" -a "${raw}x" == "falsex" ]
  then
    echo "Error: partitioned device selected but '${dev}' has no numeric index (whole disk?}"
    usage
  fi
fi

part="${dev}"
if [ "${gpt}x" == "truex" ]
then
  part="${dev}1"
fi

echo "About to erase/overwrite '${dev}' <-- are you sure? [enter twice for ok]"
read line
read line

if [ "${gpt}x" == "truex" ]
then
  echo
  echo "+---- Partitioning device '${dev}' with GPT .."
  do_gpt "${dev}"
fi

if [ "${cry}x" == "falsex" ]
then
  if [ "${ext}x" == "truex" ]
  then
    echo
    echo "+---- Writing EXT4 filesystem to '${part}' .."
    do_ext "${part}"
  elif [ "${xfs}x" == "truex" ]
  then
    echo
    echo "+---- Writing XFS filesystem to '${part}' .."
    do_xfs "${part}"
  fi
  uid=$(blkid -o value -s UUID "${part}")
  if [ ${?} -ne 0 ]
  then
    echo "Error: unable to get UUID for '${part}' (see blkid)"
    exit 2
  fi
  mnt="/chunks/${uid}"

  echo
  echo "+---- Mounting/Unmounting new filesystem '${part}' -> '${mnt}'"
  if [ ! -d "${mnt}" ]
  then
    mkdir -p "${mnt}"
    chown root:root "${mnt}"
    chmod 755 "${mnt}"
  fi
  mount "${part}" "${mnt}"
  if [ ${?} -eq 0 ]
  then
    chown mfs:mfs "${mnt}"
    chmod 755 "${mnt}"
    do_metadata "${mnt}"
    umount "${mnt}"
  else
    echo "Error: failed to mount '${part}' on '${mnt}' ..?"
    exit 1
  fi

  grep "${mnt}" /etc/fstab >/dev/null 2>&1
  if [ ${?} -eq 1 ]
  then
    if [ "${ext}x" == "truex" ]
    then
      echo "UUID=${uid} ${mnt} ext4 rw,noatime,nodiratime,nodev,nosuid,noexec,barrier=1 0 2" >> /etc/fstab
    fi
    if [ "${xfs}x" == "truex" ]
    then
      echo "UUID=${uid} ${mnt} xfs rw,noatime,nodiratime,nodev,nosuid,noexec,attr2,largeio,inode64,noquota 0 2" >> /etc/fstab
    fi
  else
    echo "Warning: ${mnt} is already in /etc/fstab, skipping"
  fi
  grep "${mnt}" /etc/mfs/mfshdd.cfg >/dev/null 2>&1
  if [ ${?} -eq 1 ]
  then
    echo "${mnt}" >> /etc/mfs/mfshdd.cfg
  else
    echo "Warning: ${mnt} is already in /etc/mfs/mfshdd.cfg, skipping"
  fi

else
  echo
  echo "+---- Formatting device '${part}' as LUKS device .."
  cryptsetup -v -y --cipher aes-cbc-essiv:sha256 --key-size 256 luksFormat "${part}"
  if [ ${?} -ne 0 ]
  then
    echo "Error: cryptsetup did not execute successfully (${?}), exiting."
    exit 2
  fi
  uid=$(blkid -o value -s UUID "${part}")
  if [ ${?} -ne 0 ]
  then
    echo "Error: unable to get UUID for '${part}' (see blkid)"
    exit 2
  fi
  map="${uid}"
  mnt="/chunks/${map}"

  echo
  echo "+---- Opening device '${part}' as LUKS device '/dev/mapper/${map}' .."
  cryptsetup -v luksOpen "${part}" "${map}"
  if [ ${?} -ne 0 ]
  then
    echo "Error: cryptsetup did not execute successfully (${?}), exiting."
    exit 2
  fi
  if [ ! -b "/dev/mapper/${map}" ]
  then
    echo "Error: LUKS device '/dev/mapper/${map}' does not exist or is not valid."
    exit 2
  fi

  if [ "${rnd}x" == "truex" ]
  then
    echo
    echo "+---- Zeroing new LUKS device '/dev/mapper/${map}' will take hours (8hrs/TB on USB3) .."
    dd if=/dev/zero of="/dev/mapper/${map}" bs=128M status=progress
  fi

  if [ "${ext}x" == "truex" ]
  then
    echo
    echo "+---- Writing EXT4 filesystem to '/dev/mapper/${map}' .."
    do_ext "/dev/mapper/${map}"
  elif [ "${xfs}x" == "truex" ]
  then
    echo
    echo "+---- Writing XFS filesystem to '/dev/mapper/${map}' .."
    do_xfs "/dev/mapper/${map}"
  fi

  echo
  echo "+---- Mounting/Unmounting new filesystem '/dev/mapper/${map}' -> '${mnt}'"
  if [ ! -d "${mnt}" ]
  then
    mkdir -p "${mnt}"
    chown root:root "${mnt}"
    chmod 755 "${mnt}"
  fi
  mount "/dev/mapper/${map}" "${mnt}"
  if [ ${?} -eq 0 ]
  then
    chown mfs:mfs "${mnt}"
    chmod 755 "${mnt}"
    do_metadata "${mnt}"
    umount "${mnt}"
  else
    echo "Error: failed to mount '/dev/mapper/${map}' on '${mnt}' ..?"
    exit 1
  fi

  echo
  echo "+---- Closing LUKS device '/dev/mapper/${map}' .."
  cryptsetup luksClose "/dev/mapper/${map}"
  sync

  echo
  echo "+---- Adding LUKS device '/dev/mapper/${map}' to LizardFS as '${mnt}' .."
  if [ ! -f "/etc/crypttab" ]
  then
    touch "/etc/crypttab"
    chmod 600 "/etc/crypttab"
  fi
  grep "^${map}" /etc/crypttab >/dev/null 2>&1
  if [ ${?} -eq 1 ]
  then
    echo "${map} UUID=${uid} none noauto,luks,keyscript=/usr/share/yubikey-luks/ykluks-keyscript" >> /etc/crypttab
    #### for the non-yubikey
    ## echo "${map} UUID=${uid} none luks" >> /etc/crypttab
  else
    echo "Warning: ^${map} is already in /etc/crypttab, skipping"
  fi
  grep "${mnt}" /etc/fstab >/dev/null 2>&1
  if [ ${?} -eq 1 ]
  then
    if [ "${ext}x" == "truex" ]
    then
      echo "/dev/mapper/${map} ${mnt} ext4 rw,noauto,noatime,nodiratime,nodev,nosuid,noexec,barrier=1 0 2" >> /etc/fstab
    fi
    if [ "${xfs}x" == "truex" ]
    then
      echo "/dev/mapper/${map} ${mnt} xfs rw,noauto,noatime,nodiratime,nodev,nosuid,noexec,attr2,largeio,inode64,noquota 0 2" >> /etc/fstab
    fi
  else
    echo "Warning: ${mnt} is already in /etc/fstab, skipping"
  fi
  grep "${mnt}" /etc/mfs/mfshdd.cfg >/dev/null 2>&1
  if [ ${?} -eq 1 ]
  then
    echo "${mnt}" >> /etc/mfs/mfshdd.cfg
  else
    echo "Warning: ${mnt} is already in /etc/mfs/mfshdd.cfg, skipping"
  fi
fi

echo
echo "+---- Complete"
# echo "  Next step: vi /etc/mfs/mfshdd.cfg"
# echo "  Then:      systemctl restart moosefs-chunkserver"
echo "  Next steps:"
echo "      reboot"
echo "    then;"
echo "      mount-all"
# echo "        cryptdisks_start ${uid}"
# echo "        mount -a"
# echo "        systemctl restart moosefs-chunkserver"
echo

exit 0

