#!/bin/sh # Copyright (c) 2006, 2007 # Thorsten Glaser # Copyright (c) 2009 # Waldemar Brodkorb # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission # is granted to deal in this work without restriction, including un- # limited rights to use, publicly perform, distribute, sell, modify, # merge, give away, or sublicence. # # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to # the utmost extent permitted by applicable law, neither express nor # implied; without malicious intent or gross negligence. In no event # may a licensor, author or contributor be held liable for indirect, # direct, other damage, loss, or other issues arising in any way out # of dealing in the work, even if advised of the possibility of such # damage or existence of a defect, except proven that it results out # of said person's immediate fault when using the work as intended. # Possible return values: # 0 - everything ok # 1 - syntax error # 1 - no 'cfgfs' mtd/cf partition found # 1 - cfgfs erase: failed # 1 - cfgfs setup: already run # 3 - cfgfs setup: mount --bind problems # 4 - cfgfs setup: can't create or write to temporary filesystem # 5 - cfgfs setup: can't bind the tmpfs to /etc # 6 - cfgfs commit: cannot write to partition # 6 - cfgfs restore: cannot write to partition # 7 - cfgfs commit: won't write to flash because of unclean setup # 8 - cfgfs status: differences found # 9 - cfgfs status: old status file not found # 10 - cfgfs dump: failed # 11 - cfgfs commit: cfgfs setup not yet run (use -f to force) # 11 - cfgfs status: cfgfs setup not yet run # 12 - cfgfs restore: cannot read the backup # 255 - cfgfs erase: failed # 255 - internal error export PATH=/bin:/sbin:/usr/bin:/usr/sbin wd=$(pwd) cd / what='Configuration Filesystem Utility (cfgfs), Version 1.06' who=$(id -u) if [ $who -ne 0 ]; then echo 'Exit. Configuration Filesystem Utility must be run as root.' exit 1 fi usage() { cat >&2 <&2 <] EOF exit 1 ;; esac # find backend device, first try to find partition with ID 88 mtd=0 part=$(fdisk -l|awk '$5 == 88 { print $1 }') if [ -z $part ]; then # otherwise search for MTD device with name cfgfs part=/dev/mtd$(fgrep '"cfgfs"' /proc/mtd 2>/dev/null | sed 's/^mtd\([^:]*\):.*$/\1/')ro mtd=1 fi if [[ ! -e $part ]]; then echo 'cfgfs: fatal error: no "cfgfs" partition found!' exit 1 fi if test $1 = erase; then dd if="$part" 2>&1 | md5sum 2>&1 >/dev/urandom if [ $mtd -eq 1 ]; then cfgfs.helper -Me | mtd -F write - cfgfs else cfgfs.helper -Me | cat > $part fi exit $? fi if test $1 = setup; then if test -e /tmp/.cfgfs; then echo 'cfgfs: error: "cfgfs setup" already run!' exit 1 fi mkdir /tmp/.cfgfs if test ! -d /tmp/.cfgfs; then echo 'cfgfs: error: cannot create temporary directory!' exit 4 fi chown 0:0 /tmp/.cfgfs chmod 700 /tmp/.cfgfs mkdir /tmp/.cfgfs/root mount --bind /etc /tmp/.cfgfs/root mkdir /tmp/.cfgfs/temp mount -t tmpfs none /tmp/.cfgfs/temp (cd /tmp/.cfgfs/root; tar cf - .) | (cd /tmp/.cfgfs/temp; tar xpf -) unclean=0 if [[ $1 = -N ]]; then unclean=2 else x=$(dd if="$part" bs=4 count=1 2>/dev/null) [[ "$x" = "FWCF" ]] || \ if [ $mtd -eq 1 ]; then cfgfs.helper -Me | mtd -F write - cfgfs else cfgfs.helper -Me | cat > $part fi if ! cfgfs.helper -U /tmp/.cfgfs/temp <"$part"; then unclean=1 echo 'cfgfs: error: cannot extract' echo unclean startup | logger -t 'cfgfs setup' fi if test -e /tmp/.cfgfs/temp/.cfgfs_deleted; then while IFS= read -r file; do rm -f "/tmp/.cfgfs/temp/$file" done /tmp/.cfgfs/temp/.cfgfs_unclean rm -f /tmp/.cfgfs/temp/.cfgfs_done if test -e /tmp/.cfgfs/temp/.cfgfs_done; then echo 'cfgfs: fatal: this is not Kansas any more' umount /tmp/.cfgfs/temp umount /tmp/.cfgfs/root rm -rf /tmp/.cfgfs exit 3 fi echo -n >/tmp/.cfgfs/temp/.cfgfs_done if test ! -e /tmp/.cfgfs/temp/.cfgfs_done; then echo 'cfgfs: fatal: cannot write to tmpfs' umount /tmp/.cfgfs/temp umount /tmp/.cfgfs/root rm -rf /tmp/.cfgfs exit 4 fi chmod 755 /tmp/.cfgfs/temp mount --bind /tmp/.cfgfs/temp /etc if test ! -e /etc/.cfgfs_done; then umount /etc echo 'cfgfs: fatal: binding to /etc failed' if test $unclean = 0; then echo 'cfgfs: configuration is preserved' \ in /tmp/.cfgfs/temp else umount /tmp/.cfgfs/temp fi exit 5 fi umount /tmp/.cfgfs/temp echo complete, unclean=$unclean | logger -t 'cfgfs setup' cd /etc rm -f .rnd find . -type f | grep -v -e '^./.cfgfs' -e '^./.rnd$' | sort | \ xargs md5sum | sed 's! ./! !' | \ cfgfs.helper -Z - /tmp/.cfgfs/status.asz exit 0 fi if test $1 = commit; then umount /tmp/.cfgfs/temp >/dev/null 2>&1 if test ! -e /tmp/.cfgfs; then cat >&2 <<-EOF cfgfs: error: not yet initialised explanation: "cfgfs setup" was not yet run EOF [[ $1 = -f ]] || exit 11 fi if test -e /etc/.cfgfs_unclean; then cat >&2 <<-EOF cfgfs: error: unclean startup (or setup run with -N)! explanation: during boot, the cfgfs filesystem could not be extracted; saving the current /etc to flash will result in data loss; to override this check, remove the file /etc/.cfgfs_unclean and try again. EOF [[ $1 = -f ]] || exit 7 fi mount -t tmpfs none /tmp/.cfgfs/temp (cd /etc; tar cf - .) | (cd /tmp/.cfgfs/temp; tar xpf -) cd /tmp/.cfgfs/temp find . -type f | grep -v -e '^./.cfgfs' -e '^./.rnd$' | sort | \ xargs md5sum | sed 's! ./! !' | \ cfgfs.helper -Z - /tmp/.cfgfs/status.asz cd /tmp/.cfgfs/root rm -f /tmp/.cfgfs/temp/.cfgfs_* /tmp/.cfgfs/temp/.rnd find /tmp/.cfgfs/temp -type d -empty -delete find . -type f | while read f; do f=${f#./} if [[ ! -e /tmp/.cfgfs/temp/$f ]]; then [[ $f = .rnd ]] && continue printf '%s\n' "$f" >>/tmp/.cfgfs/temp/.cfgfs_deleted continue fi x=$(md5sum "$f" 2>/dev/null) y=$(cd ../temp; md5sum "$f" 2>/dev/null) [[ "$x" = "$y" ]] && rm "../temp/$f" done rv=0 if [ $mtd -eq 1 ]; then if ! ( cfgfs.helper -M /tmp/.cfgfs/temp | mtd -F write - cfgfs ); then echo 'cfgfs: error: cannot write to $part!' rv=6 fi else if ! ( cfgfs.helper -M /tmp/.cfgfs/temp | cat > $part ); then echo 'cfgfs: error: cannot write to $part!' rv=6 fi fi umount /tmp/.cfgfs/temp exit $rv fi if test $1 = status; then if test ! -e /tmp/.cfgfs; then cat >&2 <<-EOF cfgfs: error: not yet initialised explanation: "cfgfs setup" was not yet run EOF [[ $1 = -f ]] || exit 11 fi rm -f /tmp/.cfgfs/*_status /tmp/.cfgfs/*_files rflag=0 q=printf # or : (true) if -q shift while getopts "rq" ch; do case $ch in (r) rflag=1 ;; (q) q=: ;; esac done shift $((OPTIND - 1)) if test $rflag = 1; then f=/tmp/.cfgfs/rom_status cd /tmp/.cfgfs/root find . -type f | grep -v -e '^./.cfgfs' -e '^./.rnd$' | sort | \ xargs md5sum | sed 's! ./! !' >$f else f=/tmp/.cfgfs/status cfgfs.helper -Zd $f.asz $f || rm -f $f fi if [[ ! -e $f ]]; then echo 'cfgfs: error: old status file not found' exit 9 fi cd /etc find . -type f | grep -v -e '^./.cfgfs' -e '^./.rnd$' | sort | \ xargs md5sum | sed 's! ./! !' >/tmp/.cfgfs/cur_status || exit 255 cd /tmp/.cfgfs sed 's/^[0-9a-f]* //' <$f >old_files sed 's/^[0-9a-f]* //' cur_files # make *_status be of exactly the same length, for benefit of the # while ... read $name" >>cur_status done comm -13 old_files cur_files | while read name; do echo " $name" >>$f done # this implementation of sort -o sucks: doesn't do in-place edits sort -k2 -o sold_status $f sort -k2 -o snew_status cur_status gotany=0 while :; do IFS=' ' read oldsum oldname <&3 || break IFS=' ' read newsum newname <&4 || exit 255 [[ "$oldname" = "$newname" ]] || exit 255 [[ "$oldsum" = "$newsum" ]] && continue [[ $gotany = 0 ]] && $q '%-32s %-32s %s\n' \ 'MD5 hash of old file' 'MD5 hash of new file' 'filename' gotany=8 test $q = : && break $q '%32s %32s %s\n' "$oldsum" "$newsum" "$oldname" done 3/dev/null 2>&1 tar -cf - dump seed | (cd "$wd"; cfgfs.helper -Z - $fn) cd / rm -rf /tmp/.cfgfs.dump case $fn in (-) echo "cfgfs: dump to standard output complete." ;; (*) echo "cfgfs: dump to '$fn' complete." ls -l "$fn" >&2 ;; esac exit 0 fi if test $1 = restore; then if test -e /tmp/.cfgfs; then echo 'cfgfs: warning: "cfgfs setup" already run!' echo 'please reboot after restoring; in no event' echo 'run "cfgfs commit" to prevent data loss' echo -n >/etc/.cfgfs_unclean fi fn=$2 [[ -n $fn ]] || fn=- rm -rf /tmp/.cfgfs.restore mkdir -m 0700 /tmp/.cfgfs.restore cd /tmp/.cfgfs.restore if ! (cd "$wd"; cfgfs.helper -Zd "$fn") | tar -xf -; then cd / rm -rf /tmp/.cfgfs.restore exit 12 fi dd if=seed of=/dev/urandom bs=256 count=1 >/dev/null 2>&1 if test ! -e dump; then echo 'cfgfs: error: invalid backup' cd / rm -rf /tmp/.cfgfs.restore exit 12 fi if [ $mtd -eq 1 ]; then if ! ( cfgfs.helper -MD dump | mtd -F write - cfgfs ); then echo 'cfgfs: error: cannot write to $part!' exit 6 fi else if ! ( cfgfs.helper -MD dump | cat > $part ); then echo 'cfgfs: error: cannot write to $part!' exit 6 fi fi cd / rm -rf /tmp/.cfgfs.restore case $fn in (-) echo "cfgfs: restore from standard output complete." ;; (*) echo "cfgfs: restore from '$fn' complete." ls -l "$fn" >&2 ;; esac exit 0 fi echo 'cfgfs: cannot be reached...' exit 255