#!/bin/sh
#(c) Copyright Barry Kauler, December 2016. License: GPL v3 (ref: /usr/share/doc/legal)
#the code here is adapted from pre-december 2016 /usr/sbin/snapshot-manager
#passed in params: mount-point of partition to roll back, 
# optional 2nd param: date of snapshot to roll back to.
#161212 first release.

export TEXTDOMAIN=easyinit
export OUTPUT_CHARSET=UTF-8

PRE="$1" #mount-point of partition to rollback.
CHOSENDATE=""
[ $2 ] && CHOSENDATE="$2"

[ ! -d ${PRE}/audit/snapshots ] && exit 1 #precaution.

mount -o remount,rw $PRE

cd ${PRE}/audit/snapshots

EXT='sfs'
mkdir -p /tmp/audit

echo -e "\\033[1;35m" #140109 purple. 34=blue, 33=yellow, 32=green, 31=red, 35=purple, 36=aquablue, 38=black.
echo "$(gettext 'Snapshot Manager: console recovery mode')"
echo -en "\\033[0;39m"
DATELIST=""
FIRSTSNAP="$(find ./ -mindepth 1 -maxdepth 1 -type f -name "[0-9]*.${EXT}" | sed -e 's%./%%' -e "s%.${EXT}$%%" | sort -n | head -n 1)" #there should only be one of these.
DELTASNAPS="$(find ./ -mindepth 1 -maxdepth 1 -type f -name '[0-9]*.xdelta' | sed -e 's%./%%' -e 's%.xdelta$%%' | sort -n -r)" #sort in reverse, need to go back in time down list.
if [ "$FIRSTSNAP" ];then
 DATELIST="$FIRSTSNAP"
 if [ "$DELTASNAPS" ];then
  DATELIST="${DATELIST}
$(echo "$DELTASNAPS" | cut -f 2 -d '_')" #2nd field has the older date.
 fi
 xDATELIST="$(echo "$DATELIST" | sed -r -e 's%([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])%\1-\2-\3 \4:\5%')" #reformat 201312221645 to 2013-12-22 16:45
else
 echo "$(gettext 'There are no snapshots. Aborting.')"
 exit 1
fi
#/sbin/init can call this script with passed-param of required rollback date...
if [ "$CHOSENDATE" ];then
 OLDESTDATE="$CHOSENDATE"
else
 echo "$(gettext 'These are snapshots of the system partition, enabling rollback')"
 echo "$(gettext 'of your entire Quirky installation to an earlier date:')"
 echo
 CNT=1
 for ADATE in `echo -n "$DATELIST" | tr '\n' ' '`
 do
  xADATE="$(echo "$ADATE" | sed -r -e 's%([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])%\1-\2-\3 \4:\5%')" #reformat 201312221645 to 2013-12-22 16:45
  echo "${CNT} ${xADATE}"
  CNT=`expr $CNT + 1`
 done
 echo "${CNT} $(gettext 'Abort recovery')"
 echo -n "$(gettext 'Type a number from left column (then press ENTER key):') "
 read numof
 [ "$numof" = "$CNT" ] && exit 0 #abort.
 OLDESTDATE="$(echo "$DATELIST" | head -n ${numof} | tail -n 1)" #ex: 201312240052
fi

#reconstitute sfs of desired date to roll back to...
MARKDONE=0
echo "${FIRSTSNAP}.${EXT}" > /tmp/audit/snapshot-mark-delete #as going to roll back, mark later snapshots for deletion.
[ ! "$DELTASNAPS" ] && AOLD="$FIRSTSNAP"
if [ "$OLDESTDATE" != "$FIRSTSNAP" ];then
 for ADELTA in $DELTASNAPS
 do
  echo "${ADELTA}.xdelta" >> /tmp/audit/snapshot-mark-delete
  AOLD="$(echo -n "$ADELTA" | cut -f 2 -d '_')"
  ANEW="$(echo -n "$ADELTA" | cut -f 1 -d '_')"
  [ "$AOLD" = "$OLDESTDATE" ] && MARKDONE=1
  xAOLD="$(echo -n "$AOLD" | sed -r -e 's%([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])%\1-\2-\3 \4:\5%')"
  echo "$(gettext 'Reconstituting snapshot:') ${xAOLD}. $(gettext 'Please wait...')"
  #format: xdelta3 -d -s "$SOURCEFILE_OLD" "$SOURCEFILE_DELTA" "$SOURCEFILE_NEW"
  echo "xdelta3 -d -s ${ANEW}.${EXT} ${ADELTA}.xdelta ${AOLD}.${EXT}" >> /tmp/audit/recover-xdelta-operations
  xdelta3 -d -s ${ANEW}.${EXT} ${ADELTA}.xdelta ${AOLD}.${EXT}
  sync
  [ "$ANEW" != "$FIRSTSNAP" ] && rm -f ${ANEW}.${EXT}
  [ $MARKDONE -eq 1 ] && break
 done
else
 #want to recover the last snapshot.
 if [ "$DELTASNAPS" ];then
  AOLD="$FIRSTSNAP"
  #need to reconstitute a sfs for previous snapshot...
  PREVDATE="`echo "$DELTASNAPS" | head -n 1 | cut -f 2 -d '_'`"
  echo "xdelta3 -d -s ${FIRSTSNAP}.${EXT} ${FIRSTSNAP}_${PREVDATE}.xdelta ${PREVDATE}.${EXT}" >> /tmp/audit/recover-xdelta-operations
  xdelta3 -d -s ${FIRSTSNAP}.${EXT} ${FIRSTSNAP}_${PREVDATE}.xdelta ${PREVDATE}.${EXT}
  sync
  echo "${FIRSTSNAP}_${PREVDATE}.xdelta" >> /tmp/audit/snapshot-mark-delete
 fi
fi

#bug report...
if [ ! -f ${AOLD}.${EXT} ];then
 echo "Error: Have not created \$AOLD.\$EXT=${AOLD}.${EXT}" > recover-bug-report
 echo "FIRSTSNAP=${FIRSTSNAP} OLDESTDATE=${OLDESTDATE}
DELTASNAPS=${DELTASNAPS}
DATELIST=${DATELIST}

Files in /audit/snapshots:"  >> recover-bug-report
 ls -1 >> recover-bug-report
 echo "
Contents of /tmp/audit/snapshot-mark-delete:" >> recover-bug-report 
 cat /tmp/audit/snapshot-mark-delete >> recover-bug-report
 echo "
Contents of /tmp/audit/recover-xdelta-operations:" >> recover-bug-report
 cat /tmp/audit/recover-xdelta-operations >> recover-bug-report
 echo -n -e "\\033[1;31m" #red.
 echo -n "ERROR: logged to /audit/snapshots/recover-bug-report. Aborting recovery."
 echo -e "\\033[0;39m"
 sync
 sleep 10
 exit 1
fi

#now do the actual rollback...
echo "$(gettext 'Restoring system from snapshot, please wait...')"
mkdir -p /tmp/audit/snapshot-mntpt
mount -t squashfs -o loop,ro ${AOLD}.${EXT} /tmp/audit/snapshot-mntpt
if [ $? -ne 0 ];then
 echo -n -e "\\033[1;31m" #red.
 echo -n "ERROR: Failed to mount ${AOLD}.${EXT}. Aborting recovery."
 echo -e "\\033[0;39m"
 sync
 sleep 10
 exit 1
fi
echo "$(gettext ' removing files from system...')"
#leave out 'audit' and 'file'
rm -rf ${PRE}/bin
rm -rf ${PRE}/sbin
rm -rf ${PRE}/lib
rm -rf ${PRE}/root
rm -rf ${PRE}/tmp/* 2> /dev/null
rm -rf ${PRE}/boot
[ -d ${PRE}/opt ] && rm -rf ${PRE}/opt
sync
rm -rf ${PRE}/usr
sync
echo "$(gettext ' copying rolled-back files...')"
cp -a /tmp/audit/snapshot-mntpt/bin ${PRE}/
cp -a /tmp/audit/snapshot-mntpt/sbin ${PRE}/
cp -a /tmp/audit/snapshot-mntpt/lib ${PRE}/
cp -a /tmp/audit/snapshot-mntpt/root ${PRE}/
cp -a /tmp/audit/snapshot-mntpt/boot ${PRE}/
cp -a /tmp/audit/snapshot-mntpt/opt ${PRE}/
sync
cp -a /tmp/audit/snapshot-mntpt/usr ${PRE}/
sync

umount /tmp/audit/snapshot-mntpt

#delete forward-time, leave ${AOLD}.${EXT} and any older delta-files...
#if there is only one snapshot, that have just recovered from, do not delete it...
CNTDELTA=`find ${PRE}/audit/snapshots -mindepth 1 -maxdepth 1 -type f -name '*.xdelta' | wc -l`
CNTSNAP=`find ${PRE}/audit/snapshots -mindepth 1 -maxdepth 1 -type f -name "*.${EXT}" 2>/dev/null | wc -l`
if [ $CNTSNAP -eq 1 -a $CNTDELTA -eq 0 ];then
 echo "$(gettext 'Have recovered from snapshot, but not deleting it.')"
else
 for ADEL in `cat /tmp/audit/snapshot-mark-delete | tr '\n' ' '`
 do
  rm -f ${ADEL} #note, current-dir is ${PRE}/audit/snapshots, so only specify filename.
 done
fi
sync
 
#in case of booted off removable card/drive, may have a vfat boot partition...
#note, same code as in service packs pinstall.sh
PDEVsys="$(mount | grep '/tmp/audit/snapshot-mntpt' | cut -f 1 -d ' ' | cut -f 3 -d '/')"

FLAGP1=0
PPARTNUM="$(echo -n "$PDEVsys" | grep -o -E '[a-z][0-9]$|[a-z][0-9][0-9]$' | cut -c 2-)" #ex: 2
PDRIVE="$(echo -n "$PDEVsys" | sed -e "s%${PPARTNUM}$%%" -e 's%p$%%')" #ex: sdb
P=''
[ "$(echo -n "$PDEVsys" | grep '[0-9]p[0-9]')" != "" ] && P='p' #ex: mmcblk0p1
if [ "$PPARTNUM" = "2" ];then
 PART1FS="$(blkid /dev/${PDRIVE}${P}1 | grep -o ' TYPE=.*' | cut -f 2 -d '"')"
 if [ "$PART1FS" = "vfat" ];then
  P1MNTPT="$(mount | grep "^/dev/${PDRIVE}${P}1 " | tr -s ' ' | cut -f 3 -d ' ')"
  if [ ! "$P1MNTPT" ];then
   mkdir -p /mnt/${PDRIVE}${P}1
   mount -t ${PART1FS} /dev/${PDRIVE}${P}1 /mnt/${PDRIVE}${P}1
   if [ $? -eq 0 ];then
    P1MNTPT="/mnt/${PDRIVE}${P}1"
    FLAGP1=1
   fi
  fi
  if [ "$P1MNTPT" ];then
   if [ -f ${P1MNTPT}/logo.16 ];then
    echo "Copying logo.16 and vmlinuz into first partition..."
    cp -a -f ${PRE}/boot/syslinux/logo.16 ${P1MNTPT}/
    cp -a -f ${PRE}/boot/syslinux/help.msg ${P1MNTPT}/
    cp -a -f ${PRE}/boot/vmlinuz ${P1MNTPT}/
    sync
   fi
   #sd-card update, ex raspberry-pi...
   if [ -d ${PRE}/boot/part1 ];then #see 3builddistro
    echo "Copying ${PRE}/boot/part1/* into first partition /dev/${PDRIVE}${P}1 ..."
    [ -f ${P1MNTPT}/config.txt ] && mv -f ${P1MNTPT}/config.txt /tmp/sp-config.txt-part1 #161125
    cp -a -f ${PRE}/boot/part1/* ${P1MNTPT}/
    [ -f /tmp/sp-config.txt-part1 ] && mv -f /tmp/sp-config.txt-part1 ${P1MNTPT}/config.txt #161125
    sync
   fi
   [ $FLAGP1 -eq 1 ] && umount ${P1MNTPT}
  fi
 fi
fi

xAOLD="$(echo -n "$AOLD" | sed -r -e 's%([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])%\1-\2-\3 \4:\5%')"
echo "$(gettext 'Finished! Your installation has been rolled back to:') ${xAOLD}"
sleep 1
exit 0
###end###
