|
@@ -0,0 +1,146 @@
|
|
|
+#!/bin/bash
|
|
|
+# (c) 2015 Helge Jung <hej@c3pb.de>
|
|
|
+#
|
|
|
+# This script is controlled by environment variables,
|
|
|
+# only the first being mandatory:
|
|
|
+#
|
|
|
+# CANDIDATE = directory in which to find the to-be-checked firmware images
|
|
|
+# REFERENCE = directory of your build firmware, defaults to './src/images'
|
|
|
+# MANIFEST = if given, use files referenced in Gluon manifest file
|
|
|
+# if left empty (unset), 'find' is used to examine all '.bin' files
|
|
|
+# VERBOSE = 0 (default) or 1, putting lots of debug output
|
|
|
+#
|
|
|
+
|
|
|
+MY_DIR=$(dirname $0)
|
|
|
+MY_DIR=$(readlink -f "$MY_DIR")
|
|
|
+
|
|
|
+pushd "$MY_DIR" > /dev/null
|
|
|
+. functions.sh
|
|
|
+
|
|
|
+# check for necessary tools
|
|
|
+for tool in bbe binwalk find gawk unsquashfs; do
|
|
|
+ [ -x "$(which $tool)" ] || abort "Missing necessary tool '$tool'."
|
|
|
+done
|
|
|
+
|
|
|
+# check that CANDIDATE dir is given and existing
|
|
|
+[ "_$CANDIDATE" == "_" ] && abort "Please specify CANDIDATE environment variable point to the directory of to-be-checked firmware images."
|
|
|
+[ ! -d "$CANDIDATE" ] && abort "The directory indicated by CANDIDATE was not found."
|
|
|
+CANDIDATE=$(readlink -f $CANDIDATE)
|
|
|
+
|
|
|
+# set defaults for environment variables
|
|
|
+[ -n "${REFERENCE}" ] || REFERENCE="./src/images"
|
|
|
+if [ "_$VERBOSE" == "_1" ]; then VERBOSE=1; else VERBOSE=0; fi
|
|
|
+
|
|
|
+# assemble file list
|
|
|
+declare -a FILELIST
|
|
|
+if [ -n "$MANIFEST" ]; then
|
|
|
+ progress "Assembling file list from manifest file ..."
|
|
|
+ FILELIST=()
|
|
|
+else
|
|
|
+ [ ! -d "$REFERENCE" ] && abort "The REFERENCE directory was not found. Have you built the firmware?"
|
|
|
+ REFERENCE=$(readlink -f $REFERENCE)
|
|
|
+
|
|
|
+ progress "Assembling file list ..."
|
|
|
+ FILELIST=($(find "$REFERENCE" -type f -name "*.bin"))
|
|
|
+fi
|
|
|
+
|
|
|
+COUNT=${#FILELIST[*]}
|
|
|
+[ "$COUNT" = 0 ] && abort "No firmware files found. Please check that you built software in the REFERENCE dir."
|
|
|
+
|
|
|
+info "Need to check $COUNT files."
|
|
|
+
|
|
|
+# setup temp dir for checking
|
|
|
+TEMPDIR=$(mktemp -d)
|
|
|
+debug "Temporary dir = $TEMPDIR"
|
|
|
+
|
|
|
+# check each file
|
|
|
+IDX=0
|
|
|
+declare -a ERRORS
|
|
|
+
|
|
|
+filter_timestamp() {
|
|
|
+ file=$1
|
|
|
+ cp "$file" "$file.orig"
|
|
|
+ egrep -ao '(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} UTC [0-9]{4}' "$file" | sort -u | while read ts; do
|
|
|
+ debug "Normalizing timestamp: '$ts'"
|
|
|
+ bbe -e "s/$ts/Abc Def 12 34:56:78 UTC 9010/" -o "${file}" "${file}.orig"
|
|
|
+ done
|
|
|
+}
|
|
|
+
|
|
|
+for ref in ${FILELIST[*]}; do
|
|
|
+ IDX=$(($IDX + 1))
|
|
|
+ NAME=`basename "$ref"`
|
|
|
+ [ $VERBOSE -eq 1 ] && progress "[${IDX}/${COUNT}] $NAME"
|
|
|
+
|
|
|
+ if [ ! -r "$ref" ]; then
|
|
|
+ ERRORS=( "${ERRORS[@]}" "$NAME: missing reference file ($ref)" )
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ cand=$(readlink -f "${CANDIDATE}/${NAME}")
|
|
|
+ if [ ! -r "$cand" ]; then
|
|
|
+ ERRORS=( "${ERRORS[@]}" "$NAME: missing candidate file in $CANDIDATE" )
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ pushd "$TEMPDIR" > /dev/null
|
|
|
+
|
|
|
+ rm -Rf candidate ; mkdir candidate
|
|
|
+ rm -Rf reference ; mkdir reference
|
|
|
+
|
|
|
+ # TODO: check output of binwalk that there are only the two expected parts
|
|
|
+ cd candidate
|
|
|
+ binwalk -qe "$cand" 2>/dev/null
|
|
|
+ cd ../reference
|
|
|
+ binwalk -qe "$ref" 2>/dev/null
|
|
|
+ cd ..
|
|
|
+
|
|
|
+ # check ROM part
|
|
|
+ debug "Filtering timestamps in ROM part (uboot+kernel) ..."
|
|
|
+ filter_timestamp candidate/200
|
|
|
+ filter_timestamp reference/200
|
|
|
+
|
|
|
+ hash_rom_cand=$(sha512sum candidate/200)
|
|
|
+ hash_rom_ref=$(sha512sum reference/200)
|
|
|
+ if [ "$hash_rom_cand" != "$hash_rom_ref" ]; then
|
|
|
+ ERRORS=( "${ERRORS[@]}" "$NAME: ROM part mismatch" )
|
|
|
+
|
|
|
+ debug "Calling vbindiff for your visual pleasure."
|
|
|
+ vbindiff reference/200 candidate/200
|
|
|
+ fi
|
|
|
+
|
|
|
+ # extract root filesystem
|
|
|
+ for dir in candidate reference; do
|
|
|
+ cd $dir
|
|
|
+ unsquashfs -n -f *.squashfs > /dev/null
|
|
|
+ cd ..
|
|
|
+ done
|
|
|
+
|
|
|
+ fsdiff=$(diff -ur reference/squashfs-root candidate/squashfs-root 2>&1)
|
|
|
+ # TODO: intelligent diff statt 'diff -r', Verhalten abhängig vom Dateinamen
|
|
|
+ # squashfs-root/usr/lib/opkg/info/*.list: erst sortieren, dann diffen
|
|
|
+ # squashfs-root/usr/lib/opkg/info/*.control: Unterschied in "Installed Size" ignorieren (kommt vermutlich durch unterschiedliche Reihenfolge und Block-Alignments?)
|
|
|
+ # squashfs-root/usr/lib/opkg/status: Delta in "Installed-Time: " Zeilen ignorieren
|
|
|
+ # squashfs-root/usr/lib/lua/luci/version.lua: neoraider/openwrt hauen, die Änderung im luciversion-String kommt durch manuelles git patching
|
|
|
+ # else: binary file compare, sha512sum
|
|
|
+ if [ "$?" -ne 0 ]; then
|
|
|
+ ERRORS=( "${ERRORS[@]}" "$NAME: Filesystems do not match" )
|
|
|
+ echo $fsdiff | less
|
|
|
+ fi
|
|
|
+
|
|
|
+ popd > /dev/null
|
|
|
+done
|
|
|
+rm -R "$TEMPDIR"
|
|
|
+
|
|
|
+# check if everything was fine - if yes, exit clean+smooth
|
|
|
+if [ ${#ERRORS[*]} -eq 0 ]; then
|
|
|
+ success "Everything OK, what a nice and lovely built firmware \o/"
|
|
|
+ exit 0
|
|
|
+fi
|
|
|
+
|
|
|
+# got some errors - output them
|
|
|
+for errmsg in "${ERRORS[@]}"; do
|
|
|
+ echo $errmsg
|
|
|
+done
|
|
|
+
|
|
|
+abort "Firmware check failed, see above :("
|
|
|
+
|