Skip to content

Instantly share code, notes, and snippets.

@MilhouseVH
Created April 17, 2016 11:10
Show Gist options
  • Save MilhouseVH/a79c11d9a7597c3c35e345d814c81c65 to your computer and use it in GitHub Desktop.
Save MilhouseVH/a79c11d9a7597c3c35e345d814c81c65 to your computer and use it in GitHub Desktop.
#!/bin/bash
################################################################################
# This file is part of LibreELEC - https://LibreELEC.tv
# Copyright (C) 2016 Team LibreELEC
#
# LibreELEC 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 2 of the License, or
# (at your option) any later version.
#
# LibreELEC 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 LibreELEC. If not, see <http://www.gnu.org/licenses/>.
################################################################################
[ "$(pidof -x $(basename $0))" != "$$" ] && exit 0
[ -z "${USER_ROOT_DIR}" ] && USER_ROOT_DIR=/var/www/sites/libreelec
LOGUSER=
log() {
local msg="$(date "+%Y-%m-%d %H:%M:%S"): $1"
local logfile=${USER_ROOT_DIR}/${LOGUSER}/deltas.txt
echo "${msg}"
if [ -n "${LOGUSER}" ]; then
echo "${msg}" >>"${logfile}"
chown "$(stat -c "%U:%G" "$(dirname "${logfile}")")" "${logfile}"
fi
}
process_delta_file() {
local tarfile="$1"
local elements=(${tarfile//\// })
local user=${elements[1]}
LOGUSER=${user}
log "Processing file ${tarfile}"
# Deleted before it could be processed?
if [ ! -f ${tarfile} ]; then
log " File removed before it could be processed"
# Deleted before it could be processed? Or it's a directory
elif [ -d ${tarfile} ]; then
log " File is a directory"
# Ignore non-tar files - delete them, as only .tar files should be in this directory
elif [[ ! ${tarfile} =~ .*\.tar$ ]]; then
log " File is a not a .tar file"
else
apply_delta "${tarfile}"
fi
log " Removing file ${tarfile}"
rm -fr ${tarfile}
LOGUSER=
}
apply_delta() {
local tarfile="$1"
local elements=(${tarfile//\// })
local user=${elements[1]}
local userdir=${USER_ROOT_DIR}/${user}
local deltadir=${userdir}/${elements[2]}
local tmpdir=${deltadir}/.tmp
local manifest=()
local deltatype oldfile newfile md5hash timestamp file_mode user_group result=0
local psize fsize savedb savedp
rm -fr ${tmpdir}
mkdir -p ${tmpdir}
# Extract what should be the manifest and patch files
if ! tar xf ${tarfile} -C ${tmpdir} 2>/dev/null; then
log " ERROR! Failed to extract files from tar archive"
result=1
elif [ ! -f ${tmpdir}/manifest -o ! -f ${tmpdir}/patch ]; then
log " ERROR! Archive does not contain manifest and patch files"
result=1
fi
if [ ${result} -eq 0 ]; then
manifest=($(grep -av "^#" ${tmpdir}/manifest)) || result=1
deltatype="${manifest[0]}"
oldfile="${userdir}/${manifest[1]}"
newfile="${userdir}/${manifest[2]}"
md5hash="${manifest[3]}"
timestamp="${manifest[4]}"
file_mode="${manifest[5]}"
user_group="$(stat -c "%U:%G" ${tarfile})"
[ "${file_mode}" == "0" ] && file_mode="644"
log " Delta type: ${deltatype}"
log " Old file: ${oldfile}"
log " New file: ${newfile}"
log " MD5 hash: ${md5hash}"
log " Timestamp: ${timestamp} ($(date --date="@${timestamp}" "+%Y-%m-%d %H:%M:%S"))"
log " File mode: ${file_mode}"
log " User:group: ${user_group}"
if [ ! -f ${oldfile} ]; then
log " ERROR! Old file ${oldfile} does not exist"
result=1
elif [ ! -d $(dirname "${newfile}") ]; then
log " ERROR! New file ${newfile} - directory does not exist"
result=1
elif [[ ! ${timestamp} =~ ^[0-9a-f]*$ ]]; then
log " ERROR! Timestamp ${timestamp} is not valid"
result=1
elif [[ ! ${file_mode} =~ ^[0-9]*$ ]]; then
log " ERROR! File mode ${file_mode} is not valid"
result=1
elif [[ ! ${user_group} =~ ^.*:.*$ ]]; then
log " ERROR! user:group ${user:group} is not valid"
result=1
fi
fi
if [ ${result} -eq 0 ]; then
case ${deltatype,,} in
xdelta3)
/usr/bin/xdelta3 -d -s ${oldfile} ${tmpdir}/patch ${tmpdir}/newfile || result=1
;;
bspatch)
/usr/bin/bspatch ${oldfile} ${tmpdir}/newfile ${tmpdir}/patch || result=1
;;
courgette)
/usr/bin/courgette -apply ${oldfile} ${tmpdir}/patch ${tmpdir}/newfile || result=1
;;
*)
log " ERROR! Delta encoding type ${deltatype} is not valid"
result=1
;;
esac
fi
if [ ${result} -eq 0 -a -f ${tmpdir}/newfile ]; then
if [ "$(md5sum ${tmpdir}/newfile | awk '{ print $1 }')" == "${md5hash}" ]; then
psize=$(stat -c%s ${tmpdir}/patch)
fsize=$(stat -c%s ${tmpdir}/newfile)
savedb=$((fsize - psize))
savedp=$(echo ${psize} ${fsize} | awk '{ printf "%3.1f", 100 - ($1 / $2 * 100) }')
mv ${tmpdir}/newfile ${newfile}
chmod ${file_mode} ${newfile}
chown ${user_group} ${newfile}
touch --date "@${timestamp}" ${newfile}
log " New file successfully created - stats: $(printf "patch %'.0f; new file %'.0f; saving %'.0f bytes (%s%%)" "${psize}" "${fsize}" "${savedb}" "${savedp}")"
log " $(ls -l ${newfile})"
else
log " ERROR! computed checksum of new file does not match ${md5hash} - new file not created"
result=1
fi
fi
rm -fr ${tmpdir}
return ${result}
}
log "$0 is starting, monitoring ${USER_ROOT_DIR}/*/.deltas"
log "Monitoring all ${USER_ROOT_DIR}/*/.deltas folders"
if ! cd ${USER_ROOT_DIR}; then
echo "ERROR: Unable to enter USER_ROOT_DIR folder ${USER_ROOT_DIR}"
exit 1
fi
# Monitor the .deltas folder in all home directories
MONITOR_DIRS=$(find . -maxdepth 2 -name ".deltas" 2>/dev/null)
if [ -z "${MONITOR_DIRS}" ]; then
log "No folders to monitor - exiting"
exit 0
fi
log "Identified user folders: $(echo "${MONITOR_DIRS}" | sed 's#./\(.*\)/.*$#\1#g' | sort | tr -s '\n' ' ')"
# Process outstanding files, oldest first
for dir in ${MONITOR_DIRS}; do
for filename in $(ls -1tr ${dir}); do
process_delta_file "${dir}/${filename}"
done
done
# Process files as they are created
inotifywait -q -m -e close_write --format '%w%f' ${MONITOR_DIRS} | \
while read filename; do
process_delta_file "${filename}"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment