#!/sbin/sh
set -e

if [ ! -e "$1" ]; then
    echo "Command file doesn't exist: $1"
    exit 1
fi
mv $1 $1.applying
COMMAND_FILE=$1.applying

REMOVE_LIST="$COMMAND_FILE"

# Used as a security check to see if we would change the password
DATA_FORMAT=0

# Used as indication if we have applied an update or not
UPDATE_APPLIED=0

# System Mountpoint
SYSTEM_MOUNTPOINT=/cache/system

# phablet user uid
PHABLET_UID=32011

# How big of a chunk to unpack before reporting checkpoint progress (in bytes)
PROGRESS_BLOCKSIZE=150000
UNITS_PER_UPDATE=7 # roughly one a second

echo "Starting image upgrader: $(date)"

# Functions
check_filesystem() {
    # $1 => image to check (partition or device img)
    if [ ! -e $1 ]; then
        echo "Partition/image not found: $1"
        return 1
    fi

    # It's fine for e2fsck to return something different than 0
    set +e
    e2fsck -yf $1
    ret=$?
    # From e2fsck man page:
    # 0 - No errors
    # 1 - File system errors corrected
    if [ $ret -ne 0 ] && [ $ret -ne 1 ]; then
        echo "e2fsck is unable to fix partition/image $1, aborting (return code $ret)"
        exit 1
    fi
    set -e
}

emit_verify_progress() {
    # Don't print any of the gpg output, but if we see progress updates,
    # process them.

    if [ -z "$1" ]; then
        return 0
    fi

    set +e # expr can return non-zero

    PREV_PROGRESS=0
    REMAINDER=0

    # We get one tiny gpg verification first (signature?), just ignore it
    PAST_FIRST_VERIFY=false

    while read -r line; do
        if echo $line | grep -q '^\[GNUPG:\] PROGRESS '; then
            GPG_PROGRESS=$(echo $line | cut -d' ' -f5)
            GPG_PROGRESS_TOTAL=$(echo $line | cut -d' ' -f6)

            if $PAST_FIRST_VERIFY; then
                NEW_PROGRESS=$(expr $GPG_PROGRESS - $PREV_PROGRESS)
                PREV_PROGRESS=$GPG_PROGRESS

                # emit ($REMAINDER + $1 * $NEW_PROGRESS) / $GPG_PROGRESS_TOTAL
                NUMERATOR=$(expr $REMAINDER + $1 \* $NEW_PROGRESS)
                REMAINDER=$(expr $NUMERATOR % $GPG_PROGRESS_TOTAL)

                emit_progress "$(expr $NUMERATOR / $GPG_PROGRESS_TOTAL)"
            elif [ $GPG_PROGRESS -eq $GPG_PROGRESS_TOTAL ]; then
                PAST_FIRST_VERIFY=true
            fi
        fi
    done

    if [ $REMAINDER -gt 0 ]; then
        emit_progress 1
    fi

    set -e
}

verify_signature() {
    # $1 => validation keyring name
    # $2 => path to validate
    # $3 => number of progress units for this verification (optional)

    if [ -e /etc/system-image/skip-gpg-verification ]; then
        emit_progress $3
        return 0
    fi

    if [ ! -e $2 ]; then
        echo "File doesn't exist: $2"
        emit_progress $3
        return 1
    fi

    # Check against the blacklist
    if [ -e /tmp/system-image/blacklist/pubring.gpg ]; then
        export GNUPGHOME=/tmp/system-image/blacklist/
        if gpg --ignore-time-conflict --verify $2 >/dev/null 2>&1; then
            echo "File signed by a blacklisted key: $2"
            # Really, we should monitor progress for above, but not super important
            emit_progress $3
            return 1
        fi
    fi

    # Check against the keyring
    export GNUPGHOME=/tmp/system-image/$1/
    if [ ! -e "$GNUPGHOME" ]; then
        echo "Keyring doesn't exist: $1"
        emit_progress $3
        return 1
    fi

    if gpg --enable-progress-filter --status-fd=1 --ignore-time-conflict --verify $2 2>&1 | \
            emit_verify_progress $3; then
        return 0
    fi

    return 1
}

clean_on_failure() {
    rm -Rf $1
    # Remove temporary SWAP
    if [ -e /cache/recovery/SWAP.img ]; then
        echo "Cleaning swap"
        swapoff /cache/recovery/SWAP.img
        rm -f /cache/recovery/SWAP.img
    fi
    return 1
}

install_keyring() {
    # $1 => full path to tarball
    # $2 => full path to signature

    # Some basic checks
    if [ ! -e "$1" ] || [ ! -e "$2" ]; then
        echo "Missing keyring files: $1 => $2"
        return 1
    fi

    # Unpacking
    TMPDIR=$(mktemp -d /tmp/system-image/tmp.XXXXX)
    cd $TMPDIR
    cat $1 | unxz | tar xf - keyring.json >/dev/null 2>&1
    if [ ! -e keyring.json ]; then
        echo "Missing keyring json description: $1"
        clean_on_failure $TMPDIR
    fi

    # Extract the expiry
    keyring_expiry=$(grep "^    \"expiry\": " keyring.json | cut -d: -f2 | sed -e "s/[ \",]//g")
    if [ -n "$keyring_expiry" ] && [ "$keyring_expiry" -lt "$(date +%s)" ]; then
        echo "Keyring expired: $1"
        clean_on_failure $TMPDIR
    fi

    # Extract the keyring type
    keyring_type=$(grep "^    \"type\": " keyring.json | cut -d: -f2 | sed -e "s/[, \"]//g")
    if [ -z "$keyring_type" ]; then
        echo "Missing keyring type: $1"
        clean_on_failure $TMPDIR
    fi

    if [ -e /tmp/system-image/$keyring_type ]; then
        echo "Keyring already loaded: $1"
        clean_on_failure $TMPDIR
    fi

    signer="unknown"
    case "$keyring_type" in
        archive-master)
            signer=""
        ;;

        image-master)
            signer="archive-master"
        ;;

        image-signing|blacklist)
            signer="image-master"
        ;;

        device-signing)
            signer="image-signing"
        ;;
    esac

    if [ -n "$signer" ] && ! verify_signature $signer $2; then
        echo "Invalid key signature: $1"
        clean_on_failure $TMPDIR
    fi

    mkdir /tmp/system-image/$keyring_type
    chmod 700 /tmp/system-image/$keyring_type
    rm keyring.json
    cat $1 | unxz | tar xf - keyring.gpg >/dev/null 2>&1
    if [ ! -e keyring.gpg ]; then
        echo "Missing key file: $1"
        clean_on_failure $TMPDIR
    fi
    mv $TMPDIR/keyring.gpg /tmp/system-image/$keyring_type/pubring.gpg
    chmod 600 /tmp/system-image/$keyring_type/pubring.gpg
    chown 0:0 /tmp/system-image/$keyring_type/pubring.gpg
    rm -Rf $TMPDIR
    return 0
}

property_write() {
    prop="$1"
    prop_value="$2"
    prop_dir="/data/android-data/property"
    # everything is wiped after a format, let's get a skeleton going
    mkdir -p "$prop_dir"
    chown 0:0 "$prop_dir"
    chmod 700 "$prop_dir"
    echo -n "$prop_value" > "$prop_dir/$prop"
    # properties won't be read if they aren't ro root
    chown 0:0 "$prop_dir/$prop"
    chmod 600 "$prop_dir/$prop"
}

adb_keys() {
    if [ "$DATA_FORMAT" -eq 0 ]; then
        return 1
    fi

    keys_dir="/data/android-data/misc/adb"
    keys="adb_keys"
    # if there is any param, then install that key(s) to adb key file
    # otherwise clean all existing keys
    if [ -n "$1" ]; then
        # do not trust full file path from ubuntu_command
        if [ -f /cache/recovery/$(basename $1) ]; then
            mkdir -p "$keys_dir"
            cp /cache/recovery/$(basename $1) "$keys_dir/$keys"
            REMOVE_LIST="$REMOVE_LIST /cache/recovery/$(basename $1)"
            # set owner to phablet
            chown $PHABLET_UID:$PHABLET_UID -R "$keys_dir"
        fi
    else
        rm -f "$keys_dir/$keys"
    fi
}

set_password() {
    if [ "$DATA_FORMAT" -eq 0 ]; then
        return 1
    fi
    user="phablet"
    password="$1"
    if [ -z "$password" ]; then
        return 1
    fi
    path=/bin:/usr/bin:/sbin:/usr/sbin
    PATH=$path chroot "$SYSTEM_MOUNTPOINT" /bin/sh -c "echo -n "$user:$password" | chpasswd"
    return 0
}

unset_password() {
    if [ "$DATA_FORMAT" -eq 0 ]; then
        return 1
    fi
    # Needs implementation
}

usb_enable() {
    prop_dir="/data/android-data/property"
    prop="persist.sys.usb.config"
    prop_val="$1"
    # Property value ordering is important here
    grep -q -s mtp "$prop_dir/$prop" && [ "$prop_val" == "adb" ] && prop_val="mtp,adb"
    grep -q -s adb "$prop_dir/$prop" && [ "$prop_val" == "mtp" ] && prop_val="mtp,adb"
    property_write "$prop" "$prop_val"
}

usb_disable() {
    prop_dir="/data/android-data/property"
    prop="persist.sys.usb.config"
    prop_val="$1"
    remain_prop=""
    # Property value ordering is important here
    grep -q -s mtp "$prop_dir/$prop" && [ "$prop_val" == "adb" ] && remain_prop="mtp"
    grep -q -s adb "$prop_dir/$prop" && [ "$prop_val" == "mtp" ] && remain_prop="adb"
    # we should not allow empty properties for the usb config
    [ "$remain_prop" == "" ] && remain_prop="adb"
    property_write "$prop" "$remain_prop"
}

factory_wipe() {
    # only set this flag if coming from a data wipe
    if [ "$DATA_FORMAT" -eq 0 ]; then
        return 1
    fi

    flag="/data/.factory_wipe"
    # if the param != "true" we just delete the flag
    case $1 in
        true)
            touch "$flag"
        ;;

        false)
            rm -f "$flag"
        ;;

        *)
            echo "Unkown parameter $1, disabling"
            rm -f "$flag"
        ;;
    esac
}

progress_units_for_file_untar() {
    FILE_SIZE=$(stat -t "$1" | cut -d' ' -f2)
    if [ -z "$FILE_SIZE" ]; then
        echo 0
        return
    fi

    # In the rare case we're evenly divisible, we'll be off by 1... but meh
    expr $FILE_SIZE / $PROGRESS_BLOCKSIZE + 1
}

progress_units_for_file_verify() {
    UNTAR_UNITS=$(progress_units_for_file_untar "$1")

    # Add about 20% of this file's untar time to also verify it's gpg signature.
    # In testing, large files took an additional 20% of their time to verify,
    # while small files took as little as 1%.  But I'm preferring large file
    # time, since 20% of a small file's time isn't much anyway.  And we don't
    # want to look stuck while verifying a large file, so we give it generous
    # unit time.
    VERIFY_UNITS=$(expr $UNTAR_UNITS / 5 + 1)

    # Make it even, since we verify in two steps and it will be easier to
    # split this way.
    expr $VERIFY_UNITS + $VERIFY_UNITS % 2
}

PROGRESS_FILE=$(mktemp /tmp/system-image.XXXXXX)
emit_progress() {
    if [ -z "$1" ] || [ "$1" -eq 0 ]; then
        return 0
    fi

    COUNT=$(cat "$PROGRESS_FILE")
    if [ -z "$COUNT" ]; then
        COUNT=0
    fi

    COUNT=$(expr $COUNT + $1)
    echo "progress: $COUNT" 1>&2

    echo "$COUNT" > "$PROGRESS_FILE" # save for future calls
}

emit_untar_progress() {
    set +e # expr can return non-zero

    TMPFILE=$(mktemp /tmp/system-image.XXXXXX)
    REMAINDER=0

    # The goal here is to pass stdin on to stdout in chunks that we report
    # progress on.  Unfortunately, while dd is a great tool for just passing
    # along a chunk of data, it doesn't give easy-to-consume feedback about
    # how much it wrote (a return value would be lovely).  So instead, we write
    # its feedback it does give us to a file and check that.

    while dd bs=$PROGRESS_BLOCKSIZE count=$UNITS_PER_UPDATE 2>"$TMPFILE"; do
        WRITTEN=$(tail -n1 "$TMPFILE" | cut -d' ' -f1)
        if [ -z "$WRITTEN" ] || [ "$WRITTEN" -eq 0 ]; then
            break
        fi

        # Since our busybox version of dd does not support iflag=fullblock, we
        # have to add bytes up ourselves, as dd might have done a short read.
        NUMERATOR="$(expr $WRITTEN + $REMAINDER)"
        emit_progress "$(expr $NUMERATOR / $PROGRESS_BLOCKSIZE)"
        REMAINDER="$(expr $NUMERATOR % $PROGRESS_BLOCKSIZE)"
    done

    if [ $REMAINDER -gt 0 ]; then
        emit_progress 1
    fi

    rm -f "$TMPFILE"
    set -e
}

# Initialize GPG
rm -Rf /tmp/system-image
mkdir -p /tmp/system-image
if [ -e /etc/system-image/archive-master.tar.xz ]; then
    echo "Loading keyring: archive-master.tar.xz"
    install_keyring /etc/system-image/archive-master.tar.xz /etc/system-image/archive-master.tar.xz.asc
fi

# Initialize recovery SWAP
## Without a swap some systems will fail to install the ubuntu rootfs (due its size)
if [ ! -e /cache/recovery/SWAP.img ]; then
    dd if=/dev/zero of=/cache/recovery/SWAP.img bs=4096 count=8192
    mkswap /cache/recovery/SWAP.img
fi
swapon /cache/recovery/SWAP.img

# Check the kernel command line to see whether Ubuntu should be installed to a partition
# or in a file that is loop mounted.
if grep -q systempart= /proc/cmdline; then
    USE_SYSTEM_PARTITION=1
else
    USE_SYSTEM_PARTITION=0
fi

# However, do not use the block device name in the kernel command line, as that is not consistently
# named in booting normal and recovery modes. Expect fstab to have a system mountpoint or use a fallback.
if [ "$USE_SYSTEM_PARTITION" -eq 1 ];then
    SYSTEM_PARTITION=$(grep "/system" /etc/recovery.fstab |cut -f 1 -d\ )
    #Fall back to emmc@android if there's no system in fstab
    if [ "$SYSTEM_PARTITION" == "" ]; then
        SYSTEM_PARTITION = "emmc@android"
    fi

    # When we're booting from system partition then we also need the userdata partition
    # eventually later on so find out which one it is here.
    USERDATA_PARTITION=$(grep "/data" /etc/recovery.fstab |cut -f 1 -d\ )
fi

# Scan commands to see how many units of progress we'll be emitting during the
# upgrade.  There are three major sources of delay:
# - During a delta upgrade, removing files before placing new ones
# - During a full upgrade, verifying the gpg signature on the huge tarballs
# - Actually unpacking the tarballs
#
# So how to rate them beforehand, so that we have a progress unit total to give
# to our caller?
# - A file removal will be our base measurement, at 1 unit of progress. (at
#   volume, that's about 7.5 units per second on average on my sample krillin)
# - Verifying a signature takes about 20% of the time to unpack a tarball for a
#   large tarball (though merely 1% of the time for a small one, but we'll be
#   generous here since the large tarball verifications overwhelm the small
#   ones).
# - And we'll set the rate for unpacking to match the 7.5 units per second
#   above, to keep the progress bar as smooth as possible.  This means (again,
#   at volume) about 150000 compressed bytes per unit on my sample krillin.
#   It's easier to measure compressed bytes, because measuring uncompressed
#   bytes would involve manually reading the xz metadata (the busybox version
#   of xz doesn't support --robot --list) to see how big the files will be
#   ahead of time.
#
# Unfortunately, calculating the progress total takes some time up front,
# during which the progress bar won't move.  But that's not a terrible price
# to pay and it's small in the grand scheme of things (no more than 10s, while
# updates take ~5 minutes -- again, on my sample krillin).

PROGRESS_TOTAL=0
FULL_IMAGE=0
while read line
do
    set -- $line
    case "$1" in
        format)
            echo "Formating: $2"
            case "$2" in
                system)
                    FULL_IMAGE=1
                ;;
            esac
        ;;

        update)
            # Consider any file removals (1 progress unit per file)
            REMOVAL_UNITS=0
            if [ "$FULL_IMAGE" != "1" ]; then
                cat /cache/recovery/$2 | unxz | tar xf - removed -C /cache >/dev/null 2>&1 || true
                if [ -e /cache/removed ]; then
                    REMOVAL_UNITS=$(wc -l /cache/removed | cut -d' ' -f1)
                fi
                rm -f /cache/removed
            fi

            # Consider size of tarball to unpack
            UNTAR_UNITS=$(progress_units_for_file_untar /cache/recovery/$2)

            # And finally consider the time needed to verify the tarball signature
            VERIFY_UNITS=$(progress_units_for_file_verify /cache/recovery/$2)

            # Take it all together!
            PROGRESS_TOTAL=$(expr $PROGRESS_TOTAL + $REMOVAL_UNITS + $UNTAR_UNITS + $VERIFY_UNITS)
        ;;

        *)
        ;;
    esac
done < $COMMAND_FILE
echo "progress total: $PROGRESS_TOTAL" 1>&2

# Process the command file
FULL_IMAGE=0
echo "Processing the command file"
while read line
do
    set -- $line
    case "$1" in
        format)
            echo "Formating: $2"
            case "$2" in
                system)
                    FULL_IMAGE=1
                    if [ "$USE_SYSTEM_PARTITION" -eq 1 ];then
                        make.ext4fs $SYSTEM_PARTITION
                    else
                        #system.img is the deprecated name for ubuntu.img, handle that too
                        rm -f /data/system.img
                        rm -f /data/ubuntu.img
                        dd if=/dev/zero of=/data/ubuntu.img seek=500K bs=4096 count=0
                        mkfs.ext2 -F /data/ubuntu.img
                        ln /data/ubuntu.img /data/system.img
                    fi
                ;;

                data)
                    BACKUP_DIR=`mktemp -d /tmp/backup.XXXXXX`

                    echo "Formatting user data partition .."

                    echo "Backup certain files which are marked as persistent:"
                    # Backup all whitelisted files here so we can restore them later
                    while read file ; do
                        # Skip any comments or empty lines
                        expr "$file" : "#*" && continue
                        [ `expr length "$file"` -eq 0 ] && continue

                        # Don't try to backup if file doesn't exist. Happens for factory
                        # installs.
                        [ ! -e /data/$file ] && continue

                        echo " $file"

                        mkdir -p $BACKUP_DIR/`dirname $file`
                        cp -av /data/$file $BACKUP_DIR/$file
                    done < /etc/persistent-files

                    # Make sure all copied files are really stored in our temporary
                    # backup directory.
                    sync

                    # If Android bits are used from the real system partition and we don't
                    # have any image in /data we need to loop mount we can simply reformat
                    # the userdata partition instead of erasing just files. In the other
                    # case we remove all files from the userdata partition but keeping the
                    # android and ubuntu image files around when available.
                    if [ "$USE_SYSTEM_PARTITION" -eq 1 ];then
                        if [ -z "$USERDATA_PARTITION" ] ; then
                            echo "Could not determine userdata partition"
                            exit 1
                        fi
                        umount /data
                        make.ext4fs $USERDATA_PARTITION
                        mount /data
                    else
                        for entry in `find /data -maxdepth 1 ! -path '/data'` ; do
                            if [ "$entry" = "/data/system.img" -o "$entry" = "/data/ubuntu.img" ]; then
                                continue
                            fi
                            rm -Rf $entry
                        done
                    fi

                    echo "Restoring files marked as persistent and we backuped before:"

                    while read file ; do
                        # Skip any comments and empty lines
                        expr "$file" : "#*" && continue
                        [ `expr length "$file"` -eq 0 ] && continue

                        # Don't try to restore if file doesn't exist. Happens for factory
                        # installs.
                        [ ! -e $BACKUP_DIR/$file ] && continue

                        echo " $file"

                        mkdir -p /data/`dirname $file`
                        cp -av $BACKUP_DIR/$file /data/$file
                    done < /etc/persistent-files

                    rm -rf $BACKUP_DIR

                    # mtp is always enabled by default
                    usb_enable mtp
                    DATA_FORMAT=1
                ;;

                *)
                    echo "Unknown format target: $2"
                ;;
            esac
        ;;

        enable)
            echo "Enabling: $2"
            case "$2" in
                developer_mode)
                    usb_enable adb
                ;;

                mtp)
                    usb_enable mtp
                ;;

                default_password)
                    set_password $3
                ;;

                adb_keys)
                    adb_keys $3
                ;;

                factory_wipe)
                    factory_wipe true
                ;;

                *)
                    echo "Unknown enable target: $2"
                ;;
            esac
        ;;

        disable)
            echo "Disabling: $2"
            case "$2" in
                developer_mode)
                    usb_disable adb
                ;;

                mtp)
                    usb_disable mtp
                ;;

                default_password)
                    unset_password
                ;;

                adb_keys)
                    adb_keys
                ;;

                factory_wipe)
                    factory_wipe false
                ;;

                *)
                    echo "Unknown disable target: $2"
                ;;
            esac
        ;;

        load_keyring)
            if [ ! -e "/cache/recovery/$2" ] || [ ! -e "/cache/recovery/$3" ]; then
                echo "Skipping missing file: $2"
                continue
            fi
            REMOVE_LIST="$REMOVE_LIST /cache/recovery/$2 /cache/recovery/$3"

            echo "Loading keyring: $2"
            install_keyring /cache/recovery/$2 /cache/recovery/$3

            if [ -e /tmp/system-image/image-master/pubring.gpg ] && \
               [ ! -e /tmp/system-image/blacklist/pubring.gpg ] && \
               [ -e /data/system-data/var/lib/system-image/blacklist.tar.xz ] && \
               [ -e /data/system-data/var/lib/system-image/blacklist.tar.xz.asc ]; then
                echo "Loading blacklist keyring"
                install_keyring /data/system-data/var/lib/system-image/blacklist.tar.xz /data/system-data/var/lib/system-image/blacklist.tar.xz.asc
            fi
        ;;

        mount)
            case "$2" in
                system)
                    mkdir -p "$SYSTEM_MOUNTPOINT"
                    if [ "$USE_SYSTEM_PARTITION" -eq 1 ];then
                        check_filesystem $SYSTEM_PARTITION
                        mount $SYSTEM_PARTITION "$SYSTEM_MOUNTPOINT"
                    else
                        if [ ! -e /data/ubuntu.img ]; then
                            ln /data/system.img /data/ubuntu.img
                        fi
                        check_filesystem /data/ubuntu.img
                        mount -o loop /data/ubuntu.img "$SYSTEM_MOUNTPOINT/"
                    fi
                ;;

                *)
                    echo "Unknown mount target: $2"
                ;;
            esac
        ;;

        unmount)
            case "$2" in
                system)
                    umount "$SYSTEM_MOUNTPOINT"
                    rmdir "$SYSTEM_MOUNTPOINT"
                ;;

                *)
                    echo "Unknown mount target: $2"
                ;;
            esac
        ;;

        update)
            if [ ! -e "/cache/recovery/$2" ] || [ ! -e "/cache/recovery/$3" ]; then
                echo "Skipping missing file: $2"
                continue
            fi

            REMOVE_LIST="$REMOVE_LIST /cache/recovery/$2 /cache/recovery/$3"

            # See how long verifying signatures will take.  But we split units
            # in two (guaranteed to be even), since we have two verification
            # steps (and even a failed verification can take some time).
            VERIFY_UNITS=$(progress_units_for_file_verify /cache/recovery/$2)
            VERIFY_UNITS=$(expr $VERIFY_UNITS / 2)

            # Verify signatures
            if verify_signature device-signing /cache/recovery/$3 $VERIFY_UNITS; then
                emit_progress $VERIFY_UNITS # skip second verify step
            elif ! verify_signature image-signing /cache/recovery/$3 $VERIFY_UNITS; then
                echo "Invalid signature"
                exit 1
            fi

            echo "Applying update: $2"
            cd /cache
            rm -Rf partitions

            # Start by removing any file listed in "removed"
            if [ "$FULL_IMAGE" != "1" ]; then
                cat recovery/$2 | unxz | tar xf - removed >/dev/null 2>&1 || true
                if [ -e removed ]; then
                    RMCOUNT=0
                    while read file; do
                        rm -Rf $file
                        RMCOUNT=$(expr $RMCOUNT + 1)
                        if ! expr $RMCOUNT % $UNITS_PER_UPDATE >/dev/null; then
                            emit_progress $RMCOUNT
                            RMCOUNT=0
                        fi
                    done < removed
                    emit_progress $RMCOUNT
                fi
                rm -f removed
            fi

            # Unpack everything else on top of the system partition
            cat recovery/$2 | emit_untar_progress | unxz | tar xf -
            rm -f removed

            # Process partition images
            grep "^/dev" /etc/recovery.fstab | while read line; do
                set -- $line

                part=${2##/}
                path=$1

                if [ -e partitions/${part}.img ] && [ -e $path ]; then
                    echo "Flashing ${part} at ${path}"
                    cat partitions/${part}.img > ${path}
                    rm partitions/${part}.img
                fi
            done

            UPDATE_APPLIED=1
        ;;

        *)
            echo "Unknown command: $1"
        ;;
    esac
done < $COMMAND_FILE

# Remove temporary SWAP
swapoff /cache/recovery/SWAP.img
rm -f /cache/recovery/SWAP.img

# Remove the update files
for file in $REMOVE_LIST; do
    rm -f $file
done

# If a previous SWAP of 512MB is available, remove
if [ -e /data/SWAP.img ]; then
    swap_size=`stat /data/SWAP.img | grep Size | awk -F' ' '{print $2}'`
    if [ "$swap_size" = "536870912" ]; then
        echo "Removing old 512MB swap file."
        rm -f /data/SWAP.img
    fi
fi

# Create the SWAP image if missing
# Here we only want a small SWAP to be created, as we found out
# that the kernel memory manager algorithm behaves better if swap
# is available, even if a minimal one (not to be used by the system)
if [ ! -e /data/SWAP.img ]; then
    echo "Creating SWAP device (32MB)."
    dd if=/dev/zero of=/data/SWAP.img bs=4096 count=8192
    mkswap /data/SWAP.img
fi

# Make sure there are always 5% reserved in /data for root usage
# else filling $HOME can make the system unusable since writable
# files and dirs share the space with $HOME and android does not
# reserve root space in /data
tune2fs -m 5 $(grep "/data " /proc/mounts| sed -e 's/ .*$//')

# Ensure we have sane permissions
if [ "$USE_SYSTEM_PARTITION" -eq 0 ];then
    chmod 600 /data/ubuntu.img
    chown 0:0 /data/ubuntu.img
fi
chmod 600 /data/SWAP.img
chown 0:0 /data/SWAP.img

# Only touch .last_update file when
# * it doesn't exist
# * or an update was applied
# In all other caes we leave it as is to avoid changing it's access time
if [ ! -e /data/.last_update ] || [ "$UPDATE_APPLIED" -eq 1 ] ; then
    touch /data/.last_update
fi

sync

echo "Done upgrading: $(date)"
