#!/bin/sh
#
# Copyright (C) 2012 Colomban Wendling <ban@herbesfolles.org>
#
# 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 3 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 <http://www.gnu.org/licenses/>.
#

# drop-in replacement and wrapper script for sfill to work around its lack of
# support for file size limits.


SRM="/usr/bin/srm"
SFILL="/usr/bin/sfill"

OPT_f=
OPT_i=
OPT_I=
OPT_l=
OPT_v=
OPT_z=

MISSING_ROOT=0


# die [MESSAGE]
die() {
  echo "$@" >&2
  exit 1
}


# check we have the required tools
which "$SRM" >/dev/null || \
  die "$SRM: command not found. " \
      "Please make sure secure-delete is correctly installed."
which "$SFILL" >/dev/null || \
  die "$SFILL: command not found. "\
      "Please make sure secure-delete is correctly installed."


# parse arguments
while getopts 'fiIlvz' o; do
  case "$o" in
    f) OPT_f=-f;;
    i) OPT_i=-i;;
    I) OPT_I=-I;;
    l) OPT_l="$OPT_l -l";;
    v) OPT_v=-v;;
    z) OPT_z=-z;;
  esac
done
shift $(expr $OPTIND - 1)
[ $# -eq 1 ] || die "wrong arguments"


# do our work in a temp directory so in the worst case if we fail to cleanup we
# don't leave the user with tons of temp files but a single directory
dir=$(mktemp -d --tmpdir="$1") || die "failed to create temp directory"

# runs a command asynchronously and wait for the trap handler to run
# immediately.  the pid of the launched command is in $cmdpid
#
# this allows for trap handlers to run while the command is running rather than
# to wait until command termination before running.  since we expect the user
# to maybe kill us to abort, we better react immediately rather than waiting
# for a command that can take several minutes to complete -- and that the user
# wants to stop.
cmdpid=
launch_cmd() {
  "$@" &
  cmdpid=$!
  wait $cmdpid
  cmdpid=
}

cleanup() {
  echo 'Terminated by signal. Clean exit.' >&2
  rm -rf '$dir'
  
  # kill current background job if any
  # using kill %1 would be better but it doesn't seem to work with dash
  [ -n "$cmdpid" ] && kill $cmdpid
  
  exit 1
}

# progress_step
progress_step_STEP=0
progress_step() {
  if [ -n $OPT_v ]; then
    case "$(echo $OPT_l | wc -w)" in
      0)
        echo '*******************';;
      1)
        echo '*';;
      *)
        [ "$progress_step_STEP" -gt 0 ] && echo '*'
        ;;
    esac
    progress_step_STEP=1
  fi
}


trap "rm -rf '$dir'" EXIT
trap cleanup INT QUIT TERM


if [ `whoami` != root ]; then
  # before trying to become root, check whether the FS on which we will work
  # actually requires root privileges
  fstype=$(df -T "$dir" | tail -n1 | tr -s ' ' | cut -d' ' -f 2)
  case $fstype in
    msdos|umsdos|vfat) # no need to be root for those FS
      [ -n $OPT_v ] || echo "File system type is $fstype, no need to be root"
      ;;
    
    *) # guess we better be root for all other FS types, so try to become root!
      if which pkexec >/dev/null; then
        pkexec --user root "$0" $OPT_f $OPT_i $OPT_I $OPT_l $OPT_v $OPT_z "$dir"
        code=$?
        if [ $code -ne 126 -a $code -ne 127 ]; then
          # if becoming root succeeded, abort this instance
          exit $code
        fi
      fi
      
      echo "Continuing without root privileges, wipe may be incomplete" >&2
      MISSING_ROOT=1
      ;;
  esac
fi


# fill disk space, workaround for file size limit
if [ -z "$OPT_i" ]; then
  input=/dev/zero
  [ -r "$input" ] || die "$input is not readable"
  
  while [ 1 ]; do
    file=$(mktemp --tmpdir="$dir") || die "failed to create temp file"
    
    launch_cmd dd if="$input" of="$file" bs=8M conv=fdatasync 2>&1
    
    # if the file we tried to create has size 0, guess there's actually no
    # space left on device
    size=$(stat -c '%s' "$file") || die "failed to stat file '$f'"
    [ "$size" = 0 ] && break
  done
  sync
  progress_step
fi

# launch sfill as wanted, now there is not much free space
sfill_v=$OPT_v
# don't report progress if we're also filling disk space
[ -z "$OPT_i" ] && sfill_v=
launch_cmd "$SFILL" $OPT_f $OPT_i $OPT_I $OPT_l $sfill_v $OPT_z "$dir"

# and finally securely remove temporary files created earlier
if [ -z "$OPT_i" ]; then
  launch_cmd "$SRM" -r $OPT_f $OPT_l $OPT_z "$dir"
  
  progress_step
fi


# if we missed root permissions, report this as a special failure (42)
if [ $MISSING_ROOT -ne 0 ]; then
  echo "Wipe succeeded but may have been incomplete due to missing root privileges.">&2
  exit 42
fi
