#!/bin/sh

set -e

lib=$(cd "$(dirname "$0")" && pwd)/../lib/charliecloud
. "${lib}/base.sh"

# shellcheck disable=SC2034
usage=$(cat <<EOF
Unpack an image tarball into a directory.

Usage:

  $ $(basename "$0") TARBALL DIR
EOF
)

sentinel=WEIRD_AL_YANKOVIC

parse_basic_args "$@"

if [ $# -lt 2 ]; then
    usage
fi

# Complain clearly if destination not valid (issue #340).
if [ ! -e "${2}" ]; then
    echo "can't unpack: ${2} does not exist" 1>&2
    exit 1
fi
if [ ! -d "${2}" ]; then
    echo "can't unpack: ${2} is not a directory" 1>&2
    exit 1
fi

# Figure out the real tarball name. If the provided $1 already has a tar
# extension, just test that name; if not, also append the plausible extensions
# and try those too.
for ext in '' .tar.gz .tar.xz .tgz .tar; do
    c=${1}${ext}
    if [ ! -f "$c" ] || [ ! -r "$c" ]; then
        echo "can't read: ${c}" 1>&2
        case $1 in
            *.tar.*|*.tgz)
                break
                ;;
            *)
                continue
                ;;
        esac
    fi
    tarball=$c
    if [ -n "$ext" ]; then
        echo "found: ${tarball}" 1>&2
    fi
    # Infer decompression argument because GNU tar is unable to do so if input
    # is a pipe, and we want to keep PV. See:
    # https://www.gnu.org/software/tar/manual/html_section/tar_68.html
    case $tarball in
        *.tar)
            newroot=${2}/$(basename "${tarball%.tar}")
            decompress=
            ;;
        *.tar.gz)
            newroot=${2}/$(basename "${tarball%.tar.gz}")
            decompress=z
            ;;
        *.tar.xz)
            newroot=${2}/$(basename "${tarball%.tar.xz}")
            decompress=J
            ;;
        *.tgz)
            newroot=${2}/$(basename "${tarball%.tgz}")
            decompress=z
            ;;
        *)
            echo "unknown extension: ${tarball}" 1>&2
            exit 1
            ;;
    esac
    break
done
if [ -z "$tarball" ]; then
    echo "no input found" 1>&2
    exit 1
fi

if [ ! -d "$newroot" ]; then
    echo "creating new image ${newroot}"
else
    if    [ -f "${newroot}/${sentinel}" ] \
       && [ -d "${newroot}/bin" ] \
       && [ -d "${newroot}/dev" ] \
       && [ -d "${newroot}/usr" ]; then
        echo "replacing existing image ${newroot}" 1>&2
        rm -Rf --one-file-system "${newroot}"
    else
        echo "${newroot} exists but does not appear to be an image" 1>&2
        exit 1
    fi
fi

mkdir "$newroot"
# Use a pipe because PV ignores arguments if it's cat rather than PV.
#
# See FAQ on /dev exclusion. --no-wildcards-match-slash is needed to prevent *
# matching multiple directories; the tar default differs from sh behavior.
size=$(stat -c%s "$tarball")
  pv_ -s "$size" < "$tarball" \
| tar x$decompress -C "$newroot" -f - \
      --anchored --no-wildcards-match-slash \
      --exclude='dev/*' --exclude='*/dev/*'
# Make all directories writeable so we can delete image later (hello, Red Hat).
find "$newroot" -type d -a ! -perm /200 -exec chmod u+w {} +

# If tarball had a single containing directory, move the contents up a level
# and remove the containing directory. It is non-trivial in POSIX sh to deal
# with hidden files; see https://unix.stackexchange.com/a/6397.
files=$(ls -Aq "$newroot")
if [ "$(echo "$files" | wc -l)" -eq 1 ]; then
    ( cd "${newroot}/${files}"
      for f in * .[!.]* ..?*; do
          if [ -e "$f" ]; then mv -- "$f" ..; fi
      done )
    rmdir "${newroot}/${files}"
fi

# Ensure directories that ch-run needs exist.
echo 'This directory is a Charliecloud image.' > "${newroot}/${sentinel}"
mkdir -p "${newroot}/dev" "${newroot}/etc" "${newroot}/proc" "${newroot}/sys"
touch "${newroot}/etc/hosts" "${newroot}/etc/resolv.conf"
for i in $(seq 0 9); do mkdir -p "${newroot}/mnt/${i}"; done

echo "${newroot} unpacked ok"
