Created
April 17, 2016 11:10
-
-
Save MilhouseVH/a79c11d9a7597c3c35e345d814c81c65 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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