Files
Patrick McCarty 6c438ca96f Initial commit
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-24 10:00:31 -08:00

576 lines
14 KiB
Bash
Executable File

#!/bin/bash
shopt -s expand_aliases
templateFolder="./template"
LOG_MISS_TOOL="./miss_tool.txt"
unset trace_out
unset valgrind
unset template_tar_files
colorize=false
unset keep
errCount=0
errLogFile="log_err.txt"
alias exec=exec_func
success=0
error=1
ignore=2
alias return_on_error='err=$?; if [[ "$err" != "$success" ]]; then return $err; fi'
encodingList="raw"
print_usage() {
echo $(basename $0)" [options] compare two folders, create and patch the first folder found and compare the result with the next folder found"
echo " The options are interpreted in the order in which they're entered!"
echo " -b | --base : base folder of versions"
echo " -c | --colorize : colorize output"
echo " -e | --encoding : (list of encoding algorithms) create patches with encoding algorithms given (could be 'raw'/'gzip'/'xz'/'zeros')"
echo " -h | --help : display this help and exit"
echo " -i | --ignore-identical: (y | n) compare file of old and new version before creating/applying/checking a patch (if file are same it's ignored, default is 'yes')"
echo " -k | --keep : keep template files and result"
echo " -o | --output : output trace. Verbosely list executed command (for terminal, use /dev/stderr instead of /dev/stdout=the standard command result)"
echo " -t | --tar : tar name of template files"
echo " -v | --valgrind : execute valgrind for each command"
exit 1
}
parse_options() {
options=$(getopt -s bash -o b:ce:hi:ko:t:v -l base:,colorize,encoding:,help,--ignore-identical:,keep,output:,tar:,valgrind -- "$@")
if [ "$?" != "0" ]; then
print_usage
exit
fi
eval set -- $options
while [ $# -gt 0 ]
do
case $1 in
-b|--base)
folderBase=$(echo $2 | sed "s/'//g")
shift
;;
-c|--colorize)
colorize=true
;;
-e|--encoding)
encodingList=$(to_lower "$2")
shift
;;
-h|--help)
print_usage
;;
-i|--ignore-identical)
compare=$(to_lower $2)
shift
;;
-k|--keep)
keep=true
;;
-o|--output)
trace_out=$(echo $2 | sed "s/'//g")
alias exec=exec_func_out
shift
;;
-t|--tar)
template_tar_files=$(echo $2 | sed "s/'//g")
folderBase=$templateFolder
encodingList="raw gzip xz zeros any"
shift
;;
-v|--valgrind)
valgrind="valgrind --trace-children=yes --track-fds=yes -q "
;;
--)
shift; break
;;
-*)
echo "$0: error - unrecognized option $1" 1>&2; exit 1
;;
(*)
break
;;
esac
shift
done
}
check_tool() {
local ERR=0
CMD="which $@"
TOOL=$@
HIDE_OUT=$(eval $CMD)
ERR=$?
if [ $ERR != "0" ]; then
echo $TOOL >> $LOG_MISS_TOOL
ERR=$error
fi
return $ERR
}
check_dependencies() {
if [ -e $LOG_MISS_TOOL ]; then
rm $LOG_MISS_TOOL
fi
local ERR=0
check_tool "bsdiff"; ERR=$(($ERR + $?))
check_tool "bspatch"; ERR=$(($ERR + $?))
check_tool "cmp"; ERR=$(($ERR + $?))
if [ $ERR != "0" ];then
log_err "check_tool" "$ERR" 'Missing command '$(cat $LOG_MISS_TOOL)
return $ERR
fi
return $success
}
to_lower() {
echo $1 | tr '[:upper:]' '[:lower:]'
}
log_user() {
str=$@
# do nothing it it's just a change of font attribute
if [ "${str:0:2}" = "\e" ] && [ ${#str} -le 5 ]; then
echo -en $str>/dev/stderr
return
fi
if [ -z "$trace_out" ]; then
echo -e $str>/dev/stderr
else
trace $str
fi
}
log_debug() {
log_user $@
}
log_err() {
cmd=$1
err=$2
out=$3
msg="\n$cmd return $fontRed$err$attrReset"
if [ "$out" != "" ]; then
msg="$msg and message \n$fontRed$out$attrReset\n"
fi
msg="$msg"
log_user -en $fontRed"\nERROR:$attrReset $msg"
echo -e "$msg" >>$errLogFile
}
trace() {
str=$@
if [ "$trace_out" != "" ]; then
if [ "${str:0:2}" = "\e" ] && [ ${#str} -le 5 ]; then
return
fi
echo -e $str>>$trace_out
fi
}
exec_func_out() {
cmd=$@
trace [ $fontCyan$cmd$attrReset ]
out=$(eval $cmd)
err=$?
if [ "$out" != "" ]; then # avoid empty echo
echo $out
fi
return $err
}
exec_func() {
res=$(eval $@)
err=$?
if [ "$res" != "" ]; then
echo $res
fi
return $err
}
# Initialize main folder path. Get $1(=folder_BASE = path of the initial version) and determine :
# folderNextVers = path of the next version
# folderResult = path of the folder where the patch will be stored
set_folder_base() {
folderList=( $(exec "ls $1") )
for folder in ${folderList[@]}
do
folder="$1/$folder"
if [ -d "$folder" ]; then
if [ -z $folderCurrVers ]; then
folderCurrVers=$folder
else
folderNextVers=$folder
break
fi
fi
done
folderResult="$folderNextVers""_PATCH"
relativeFolder=""
totalFiles=$(exec "ls -lR $folderCurrVers/")
totalFiles=$(exec "ls -lR $folderCurrVers/ | grep ^- | wc -l")
totalLink=$(exec "ls -lR $folderCurrVers/ | grep ^l | wc -l")
totalFileCount=$(($totalFiles + $totalLink ))
}
# Increment statistic counter
inc_stat() {
local idx=$1
local algo=$2
if [ "$algo" = "" ]; then
algo="filter"
fi
eval stat=\$'stats_'$algo'_'$idx
eval stats'_'$algo'_'$idx=$(($stat + 1))
}
# Before creating,applying,checking a patch, this function check than :
# this is effectively a file
# with a size in the old and previous version > 0 byte
# and extended attributes are the same
# because these cases are not managed by bsdiff
filter_file() {
local file_src=$1
local file_cmp=$2
local size_src
local size_cmp
local err=$ignore
fileCount=$(($fileCount + 1))
percent=$((($fileCount * 100) / $totalFileCount))
if [ "$progress" != "$percent" ]; then
progress=$percent
log_user -n $fontCyan".$progress%"$attrReset
fi
# not a file or not a link on file => ignore
[[ ! -f "$file_src" && ! -L "$file_src" ]] && return $ignore
# not found in new version => ignore
[ ! -e "$file_cmp" ] && inc_stat idx_FileNotFound && inc_stat idx_FileIgnored && return $ignore
# extended attributes has changed ?
xattrV1=( $(ls -Z $file_src) )
xattrV2=( $(ls -Z $file_cmp) )
[ "${xattrV1[0]}" != "${xattrV2[0]}" ] && inc_stat idx_xattrChanged && inc_stat idx_FileIgnored && return $ignore
# File differe => analyze
size_src=$(exec "stat -c %s $file_src 2>/dev/null")
size_cmp=$(exec "stat -c %s $file_cmp 2>/dev/null")
[ "$size_src" != "$size_cmp" ] && inc_stat idx_FileAnalyzed && return $success
# File size = 0 (in old and newest version) => ignore
[[ "$size_src" = "0" && "$size_cmp" = "0" ]] && inc_stat idx_FileEmpty && inc_stat idx_FileIgnored && return $ignore
# Files unchanged => ignore
if [ "$compare" != "n" ]; then
CHANGE=$(eval "cmp -s $file_src $file_cmp")
[ "$?" = "0" ] && inc_stat idx_FileIgnored && inc_stat idx_FileUnchanged && return $ignore
fi
# => analyze
inc_stat idx_FileAnalyzed
return $success
}
parse_folder() {
local folderSrc="$folderCurrVers/$relativeFolder"
local folderCmp="$folderNextVers/$relativeFolder"
local folderDest="$folderResult/$relativeFolder"
local err
local relativeFolderStore
cmd="ls $folderSrc"
local file
fileList=$(exec "$cmd")
if [ "$?" != "0" ]; then
log_err "$cmd" "$?" "$fileList"
exit
fi
for file in $fileList
do
#if it is a folder (and not a link on folder), create the folder, and recurse into
if [[ -d "$folderSrc$file" && ! -L "$folderSrc$file" ]]; then
relativeFolderStore=$relativeFolder
relativeFolder="$relativeFolder$file/"
if [ ! -e "$folderDest$file" ]; then
exec "mkdir $folderDest$file"
fi
parse_folder
relativeFolder=$relativeFolderStore
err=2
else
filter_file "$folderSrc$file" "$folderCmp$file"
err=$?
if [ "$err" = "$success" ]; then
test_patch $file
err=$?
fi
fi
done
}
create_patch() {
local err
local file=$1
local algo=$2
local debug=$3
fileSrc=$folderSrc$file
fileCmp=$folderCmp$file
filePatch="$folderDest$filename".$algo.patch
cmd="$valgrind $diffTool $fileSrc $fileCmp $filePatch $algo $debug"
out=$(exec "$cmd")
err=$?
if [ "$err" -lt "0" ]; then
log_err "$cmd" "$err" "$out"
inc_stat idx_ErrCreatePatch $algo
fi
return $err
}
apply_patch() {
local err
local file=$1
local algo=$2
oldFile=$folderSrc$file
newFile="$folderDest$filename".$algo.patched
patchFile="$folderDest$filename".$algo.patch
cmd="$valgrind $patchTool $oldFile $newFile $patchFile"
out=$(exec "$cmd 2>&1")
err=$?
if [ "$err" != "$success" ]; then
log_err "$cmd" "$err" "$out"
inc_stat idx_ErrApplyPatch $algo
fi
return $err
}
check_result() {
local err
local file=$1
local algo=$2
patchedFile="$folderDest$filename".$algo.patched
newFile="$folderCmp$file"
cmd="$checkTool $patchedFile $newFile"
out=$(exec "$cmd 2>&1")
err=$?
if [ "$err" != "$success" ]; then
log_err "$cmd" "$err" "$out \n patch not well applied on '$folderDest$filename' witch patch '$folderDest$filename.$algo.patch'"
inc_stat idx_ErrCheckPatch $algo
fi
return $err
}
test_patch() {
local file="$1"
filename=$(basename $file)
local err
local algo
if [ ! -z "$trace_out" ]; then
debug='debug'
fi
for algo in ${algoList[*]}
do
create_patch $file $algo $debug
[ "$?" = "0" ] && apply_patch $file $algo $debug
[ "$?" = "0" ] && check_result $file $algo $debug
done
return $?
}
def_font_attributes() {
fontCyan="\e[96m"
fontRed="\e[91m"
fontGreen="\e[92m"
fontBlue="\e[34m"
fontYellow="\e[93m"
fontWhite="\e[97m"
attrReset="\e[0m"
}
rm_logfile() {
if [[ "$trace_out" != "" && -e "$trace_out" && -f "$trace_out" ]]; then
exec "sudo rm $trace_out"
fi
if [ -e "$errLogFile" ]; then
exec "sudo rm $errLogFile"
fi
}
rm_template() {
if [ "$keep" != "true" ]; then
if [[ "$template_tar_files" != "" && -d "$templateFolder" ]]; then
exec "sudo rm -rf $templateFolder"
fi
fi
}
rm_result() {
if [ "$keep" != "true" ]; then
if [ -d "$folderResult" ]; then
exec "rm -rf $folderResult"
fi
fi
}
test() {
parse_folder "$folderCurrVers"
}
init() {
# Check than all variables are declared
# set -o nounset
compare='y'
parse_options "$@"
diffTool=$(which bsdiff)
patchTool=$(which bspatch)
checkTool=$(which cmp)
checkTool="$checkTool -s "
if [[ -z "$template_tar_files" && -z "$folderBase" ]]; then
print_usage
fi
if [ "$colorize" = "true" ]; then
def_font_attributes
fi
check_dependencies
if [ "$?" != "$success" ]; then
exit $?
fi
rm_logfile
rm_template
if [ "$encodingList" != "" ]; then
algoList=$encodingList
else
algoList="raw"
fi
if [ ! -e "$templateFolder" ]; then
exec "mkdir $templateFolder"
fi
if [ "$template_tar_files" != "" ]; then
exec "sudo tar --preserve-permissions --xattrs --selinux -p -xf $template_tar_files -C $templateFolder/"
fi
set_folder_base $folderBase
log_user "folder of current version (folderCurrVers)=$fontGreen$folderCurrVers$attrReset"
log_user "folder of next (newest) version (folderNextVers)=$fontGreen$folderNextVers$attrReset"
log_user "folder of patch (and patched) files (folderResult)=$fontGreen$folderResult$attrReset"
log_user "Found $fontGreen$totalFileCount$attrReset file/link"
log_user "diffTool used=$fontGreen$diffTool$attrReset"
log_user "patchTool used=$fontGreen$patchTool$attrReset"
log_user "checkTool used=$fontGreen$checkTool$attrReset"
if [ -d "$folderResult" ]; then
read -e -i "Y" -p "A result folder have been found ($folderResult). Do you want to remove this folder? ('Y'es/'n'o/'q'uit): "
case $REPLY in
n | N)
# do nothing
;;
y | Y)
exec "rm -rf $folderResult"
;;
q | Q)
exit 0
esac
fi
if [ ! -e "$folderResult" ]; then
exec "mkdir $folderResult"
fi
stats=(0 0 0 0 0 0 0 0)
idx_ErrCreatePatch=0
idx_ErrApplyPatch=1
idx_ErrCheckPatch=2
idx_FileAnalyzed=3
idx_FileIgnored=4
idx_FileNotFound=5
idx_FileUnchanged=6
idx_FileEmpty=7
idx_xattrChanged=8
}
show_stat() {
duration=$(($endTime - $startTime))
log_user "\n-------------------------------------"
log_user "script start at $(date +%c --date @$startTime), ended at $(date +%c --date @$endTime)\n"
log_user "Time to process=$fontGreen$(date +%Mm:%Ss --date @$duration) \n$attrReset"
log_user "-------------------------------------"
[ "$stats_filter_idx_FileAnalyzed" = "" ] && stats_filter_idx_FileAnalyzed=0
log_user $fontBlue"Analysed files : $fontGreen $stats_filter_idx_FileAnalyzed"$attrReset
[ "$stats_filter_idx_FileIgnored" = "" ] && stats_filter_idx_FileIgnored=0
log_user $fontBlue"Ignored files : $stats_filter_idx_FileIgnored"$attrReset
[ "$stats_filter_idx_FileEmpty" = "" ] && stats_filter_idx_FileEmpty=0
log_user $fontBlue".Empty files : $stats_filter_idx_FileEmpty"$attrReset
[ "$stats_filter_idx_FileNotFound" = "" ] && stats_filter_idx_FileNotFound=0
log_user $fontBlue".Not Found in new version : $stats_filter_idx_FileNotFound"$attrReset
[ "$stats_filter_idx_xattrChanged" = "" ] && stats_filter_idx_xattrChanged=0
log_user $fontBlue".Extended attributes have changed : $stats_filter_idx_xattrChanged"$attrReset
if [ "$compare" != "n" ]; then
[ "$stats_filter_idx_FileUnchanged" = "" ] && stats_filter_idx_FileUnchanged=0
log_user $fontBlue".Unchanged files : $stats_filter_idx_FileUnchanged"$attrReset
fi
errCount=0
for algo in ${algoList[*]}
do
eval stat_CreatePatch=\$'stats_'$algo'_idx_ErrCreatePatch'; [ "$stat_CreatePatch" = "" ] && stat_CreatePatch=0
eval stat_ApplyPatch=\$'stats_'$algo'_idx_ErrApplyPatch'; [ "$stat_ApplyPatch" = "" ] && stat_ApplyPatch=0
eval stat_CheckPatch=\$'stats_'$algo'_idx_ErrCheckPatch'; [ "$stat_CheckPatch" = "" ] && stat_CheckPatch=0
eval errCount=$(( $stat_CreatePatch + $stat_ApplyPatch + $stat_CheckPatch ))
if [ "$errCount" = 0 ] ; then
log_user $fontGreen"With '$algo' encoding, count $errCount errors."
else
log_user $fontRed"With '$algo' encoding, count $errCount errors, which is :"
eval stat=\$'stats_'$algo'_idx_ErrCreatePatch'; [ "$stat" = "" ] && stat=0
log_user $fontBlue".on patch created : $fontRed $stat"$attrReset
eval stat=\$'stats_'$algo'_idx_ErrApplyPatch'; [ "$stat" = "" ] && stat=0
log_user $fontBlue".on patch applied : $fontRed $stat"$attrReset
eval stat=\$'stats_'$algo'_idx_ErrCheckPatch'; [ "$stat" = "" ] && stat=0
log_user $fontBlue".on patch checked : $fontRed $stat"$attrReset
fi
done
if [ "$errCount" -gt "0" ]; then
log_user $fontRed"see '$errLogFile' for more informations"
log_user $attrReset
fi
}
cleanup() {
rm_template
rm_result
}
main() {
PATH_STORE=$PATH
PATH=.:..:../..:$PATH
startTime=$(date +%s)
init "$@"
parse_folder "$folderCurrVers"
endTime=$(date +%s)
show_stat
cleanup
PATH=$PATH_STORE
}
main "$@"