diff options
-rw-r--r-- | packages/openslug-init/openslug-init-0.10/reflash | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/packages/openslug-init/openslug-init-0.10/reflash b/packages/openslug-init/openslug-init-0.10/reflash index e69de29bb2..dc6ef0bbd8 100644 --- a/packages/openslug-init/openslug-init-0.10/reflash +++ b/packages/openslug-init/openslug-init-0.10/reflash @@ -0,0 +1,546 @@ +#!/bin/sh +# reflash +# ensure the flash disk is not mounted +# save configuration files +# update the kernel +# update the flashdisk +# restore the saved configuration files +# the set of configuration files is described by +# /etc/default/conffiles. +# +# /etc/default/functions contains useful utility functions +. /etc/default/functions +# +# CHECKING THE ENVIRONMENT +# ------------------------ +# basic setup. This could be parameterised to use different partitions! +ffspart=Flashdisk +kpart=Kernel +# +ffsdev="$(mtblockdev $ffspart)" +test -n "$ffsdev" -a -b "$ffsdev" || { + echo "reflash: $ffspart($ffsdev): cannot find $ffspart mtd partition." >&2 + echo " check /proc/mtd, either the partition does not exist or there is no" >&2 + echo " corresponding block device." >&2 + exit 1 +} +ffssize="$(devio "<<$ffsdev" 'pr$')" +# +kdev="$(mtblockdev $kpart)" +test -n "$kdev" -a -b "$kdev" || { + echo "reflash: $kpart($kdev): cannot find $kpart mtd partition." >&2 + echo " check /proc/mtd, either the partition does not exist or there is no" >&2 + echo " corresponding block device." >&2 + exit 1 +} +ksize="$(devio "<<$kdev" 'pr$')" +# +# find the device number of the flash partition then make sure it isn't +# mounted anywhere. +ffsno="$(devio "<<$ffsdev" prd)" +test -n "$ffsno" -a "$ffsno" -ge 0 || { + echo "reflash: $ffsdev: device number $ffsno is not valid, cannot continue." >&2 + exit 1 +} +# +# Make sure that Flashdisk isn't mounted on / +if test "$(devio "<</etc/init.d/sysconfsetup" prd)" -eq "$ffsno" +then + echo "reflash: $ffsdev is mounted on /, use turnup ram to reflash" >&2 + exit 1 +fi +# +# CHECKING FOR INPUT (ARGUMENTS ETC) +# ---------------------------------- +# +# find the kernel and the new flash file system, they default to /boot/zImage +# and /boot/rootfs.jffs2, an image file can be used to specify both images. +ffsfile=/boot/rootfs.jffs2 +kfile=/boot/zImage +imgfile= +while test $# -gt 0 +do + case "$1" in + -k) shift + test $# -gt 0 || { + echo "reflash: -k: give the file containing the kernel image" >&2 + exit 1 + } + kfile="$1" + imgfile= + shift;; + -j) shift + test $# -gt 0 || { + echo "reflash: -j: give the file containing the root jffs2 image" >&2 + exit 1 + } + ffsfile="$1" + imgfile= + shift;; + -i) shift + test $# -gt 0 || { + echo "reflash: -i: give the file containing the complete flash image" >&2 + exit 1 + } + imgfile="$1" + shift;; + *) echo "reflash: usage: $0 [-k kernel -j rootfs] | -i image" >&2 + echo " -k [$kfile]: the new compressed kernel image ('zImage')" >&2 + echo " -j [$ffsfile]: the new root file system (jffs2)" >&2 + echo " -i image: a complete flash image (gives both kernel and jffs2)" >&2 + echo " The current jffs2 will be umounted if mounted." >&2 + exit 1;; + esac +done +# +# Perform basic checks on the input (must exist, size must be ok). +if test -n "$imgfile" +then + kfile= + ffsfile= + if test -r "$imgfile" + then + # read the partition table and from this find the offset + # and size of Kernel and Flashdisk partitions. The following + # devio command just dumps the partition table in a format + # similar to /proc/mtd (but it outputs decimal values!) + #NOTE: this uses a here document because this allows the while + # loop to set the variables, a pipe would put the while in + # a sub-shell and the variable settings would be lost. This + # works in ash, no guarantees about other shells! + while read size base name + do + case "$name" in + Kernel) imgksize="$size" + imgkoffset="$base";; + Flashdisk) + imgffssize="$size" + imgffsoffset="$base";; + esac + done <<EOI +$(devio "<<$imgfile" ' + <= $ 0x20000 - + $( 1 + # 0xff byte in name[0] ends the partition table + $? @ 255 = + # output size base name + <= f15+ + .= b 0xfffffff & + <= f4+ + .= b + pf "%lu %lu " + <= f28- + cp 16 + pn + <= f240+ + $) 1') +EOI + # check the result + test -n "$imgksize" -a "$imgksize" -gt 0 -a "$imgksize" -le "$ksize" || { + echo "reflash: $imgfile: bad kernel size ($imgksize, max $ksize)" >&2 + exit 1 + } + test -n "$imgffssize" -a "$imgffssize" -gt 0 -a "$imgffssize" -le "$ffssize" || { + echo "reflash: $imgfile: bad flashdisk size ($imgffssize, max $ffssize)" >&2 + exit 1 + } + else + echo "reflash: $imgfile: image file not found" >&2 + exit 1 + fi +else + if test -r "$kfile" + then + # check the size + s="$(devio "<<$kfile" 'pr$')" + test -n "$s" -a "$s" -gt 0 -a "$s" -le "$ksize" || { + echo "reflash: $kfile: bad size ($s, max $ksize)" >&2 + exit 1 + } + else + echo "reflash: $kfile: kernel file not found" >&2 + exit 1 + fi + if test -r "$ffsfile" + then + s="$(devio "<<$ffsfile" 'pr$')" + test -n "$s" -a "$s" -gt 0 -a "$s" -le "$ffssize" || { + echo "reflash: $ffsfile: bad size ($s, max $ffssize)" >&2 + exit 1 + } + else + echo "reflash: $ffsfile: root file system image file not found" >&2 + exit 1 + fi +fi +# +# INPUTS OK, UMOUNT ANY EXISTING MOUNT OF THE FLASHDISK +# ---------------------------------------------------- +echo "reflash: umounting any existing mount of $ffsdev" >&2 +# +# check each mount point, do this last first because otherwise nested +# mounts of ffsdev cannot be umounted. +ffs_umount() { + local device mp type options stuff + + read device mp type options stuff + test -z "$device" && return 0 + + # handle following entries first + ffs_umount || return 1 + + # handle this entry + case "$type" in + jffs2) test "$(devio "<<$mp/etc/init.d/sysconfsetup" prd)" -ne "$ffsno" || + umount "$mp" || { + echo "reflash: $mp: unable to umount $ffsdev" >&2 + return 1 + };; + esac + + return 0 +} +# +ffs_umount </proc/mounts || { + echo "reflash: umount $ffsdev from all mount points then re-run reflash" >&2 + exit 1 +} +# +# Everything is umounted, now remount on a temporary directory. +ffsdir="/tmp/flashdisk.$$" +mkdir "$ffsdir" || { + echo "reflash: $ffsdir: failed to create temporary directory" >&2 + exit 1 +} +# +mountflash "$ffsdir" -o ro || { + rmdir "$ffsdir" + exit 1 +} +# +# this is a utility function to make the cleanup easier +errorexit() { + umount "$ffsdir" && rmdir "$ffsdir" || + echo "reflash: $ffsdir: temporary directory cleanup failed" >&2 + exit 1 +} +# +test -r "$ffsdir/etc/default/conffiles" || { + echo "reflash: [/initrd]/etc/default/conffiles: file not found" >&2 + errorexit +} +# +# PRESERVE EXISTING CONFIGURATION +# ------------------------------- +echo "reflash: preserving existing configuration file" >&2 +# +# This step produces /tmp/preserve.$$ and /tmp/cpio.$$, the former is +# a list of the preserved configuration files together with the processing +# option, the latter is a directory tree of the preserved files (a directory +# tree makes the restore step easier.) +saved=/tmp/cpio.$$ +list=/tmp/preserve.$$ +mkdir "$saved" || { + echo "reflash: $saved: could not create save directory" >&2 + errorexit +} +# +( cd "$ffsdir" + find etc/*.conf $(sed 's!^/!!' usr/lib/ipkg/info/*.conffiles) ! -type d -newer etc/.configured -print | + sed 's/^/diff /' + exec sed 's/#.*$//;/^[ ]*$/d' etc/default/conffiles +) | sed 's!^/*!!' | awk '{ op=$1; $1=""; file[$0]=op } + END{ for (f in file) if (file[f] != "ignore") print file[f] f }' | +while read op file +do + if test -e "$ffsdir/$file" + then + echo "$op $file" >&3 + echo "$file" + fi +done 3>"$list" | (cd "$ffsdir"; exec cpio -p -d -m -u "$saved") || { + echo "reflash: $saved: copy of saved configuration files failed" >&2 + rm -rf "$saved" + rm "$list" + errorexit +} +# +# If this umount fails do not try to continue... +umount "$ffsdir" || { + echo "reflash: $ffsdir: temporary mount point umount failed" >&2 + echo " No changes have been made." >&2 + rm -rf "$saved" + rm "$list" + exit 1 +} +# +# FLASH THE NEW IMAGES +# -------------------- +echo "reflash: about to flash new images" >&2 +# +# If the images are in separate files do this in two steps, kernel then +# rootfs (this seems like it might be safer if the first fails). For an +# image file use one step but still write the kernel first. +do_devio() { + if test -r "$imgfile" + then + # image file case - copy the whole of the image partition, + # which already includes headers (etc). + devio "$@" "<<$imgfile" ">>$ffsdev" ">>$kdev" ' + # kernel is at imgkoffset[imgksize] + ' "<= $imgkoffset" "cp $imgksize" ' + fb #t-,255 + ' "<>$kdev" ">>$ffsdev" ' + # rootfs is at imgffsoffset[imgffssize] + ' "<= $imgffsoffset" "cp $imgffssize" ' + fb #t-,255' + elif test -r "$kfile" -a -r "$ffsfile" + then + # use separate files, do this in one command to be sure + # that everything can be opened at the start + devio "$@" "<<$ffsfile" ">>$ffsdev" "<<$kfile" ">>$kdev" ' + # kernel write length+16,0,0,0 header, then fill + wb $16+,4 + fb 12,0 + cp $ + fb #t-,255 + ' "<>$kfile" "<>$kdev" "<<$ffsfile" ">>$ffsdev" ' + # rootfs, write the whole image, fill if necessary + cp $ + fb #t-,255' + else + # oops, my checking was wrong... + echo "reflash: internal error: image files not found!" >&2 + echo " No changes have been made." >&2 + exit 2 + fi +} +# +echo "reflash: writing kernel to $kdev and rootfs to $ffsdev..." >&2 +do_devio +st=$? +case "$st" in +0) ;; +1) echo "reflash: flash of new images failed, no changes have been made" >&2 + rm -rf "$saved" + rm "$list" + exit 1;; +3) echo "reflash: WARNING: partial flash, the system is unbootable" >&2 + echo " Reflash from RedBoot or correct the problem here." >&2 + exit 3;; +*) echo "reflash($st): internal error" >&2 + exit $st;; +esac +echo " ... done" >&2 +# +# verify - this just produces a warning +echo "reflash: verifying new flash image..." >&2 +do_devio -v || { + echo "reflash: WARNING: flash image verification failed" >&2 + echo " The system is probably unbootable." >&2 + echo " System configuration files will be restored but this may fail" >&2 + echo " Starting a shell for user recovery (exit to continue)" >&2 + PS1='badflash$ ' sh -i <>/dev/tty >&0 2>&0 +} +echo " ... done" >&2 +# +# RESTORE THE OLD CONFIGURATION +# ----------------------------- +echo "reflash: restoring saved configuration files" >&2 +# +# the file /etc/.configured is the datestamp file used to ensure that +# changed configuration files can be recognised. It is created by +# /etc/rcS.d/S99finish on first boot (if it does not exist). We need +# a timestamp earlier than any files we create so touch it here, this +# also acts as a test on the mounted file system +mountflash "$ffsdir" && :>"$ffsdir/etc/.configured" || { + rmdir "$ffsdir" + echo "reflash: mount of new flash root file system failed" >&2 + if test -d "$ffsdir/etc" + then + echo " The file system does not seem to be writeable." >&2 + echo " The mounted file system is in $ffsdir" >&2 + fi + echo " WARNING: the kernel and root file system have been reflashed," >&2 + echo " HOWEVER the new root file system seems to be unuseable." >&2 + echo " Saved configuration files are in $saved" >&2 + echo " The list of saved configuration files is in $list" >&2 + echo " You should determine the reason for the failed mount, mount the new" >&2 + echo " file system and restore the configuration from $saved - it's just a" >&2 + echo " matter of copying the saved files where required." >&2 + exit 1 +} +# +# verify file +# this is called with the name of a 'diff' file which is, indeed, +# different and with all the std streams connected to the tty. It +# returns a status code to say whether (0) or not (1) to copy the +# file over. +# +verify_help() { + echo "Please specify how to handle this file or link, the options are as follows," + echo "two character abbreviations may be used:" + echo + echo " keep: retain the old file, overwrite the new flash image file" + echo " upgrade: retain the new file, the old (saved) file is not used" + echo " diff: display the differences between the old and the new using diff -u" + echo " shell: temporarily start an interactive shell (sh -i), exit to continue" + echo " skip: ignore this file for the moment. The file is left in the directory" + echo " $saved and many be handled after this script has completed" +} +# +verify() { + local command file + + file="$1" + echo "reflash: $file: configuration file changed." + verify_help "$file" + while : + do + echo -n "option: " + read command + case "$command" in + ke*) return 0;; + up*) rm "$saved/$file" + return 1;; + di*) echo "DIFF OLD($saved) NEW($ffsdir)" + diff -u "$saved/$file" "$ffsdir/$file";; + sh*) PS1="$file: " sh -i;; + sk*) return 1;; + *) verify_help "$file";; + esac + done +} +# the same, but for a link +verify_link() { + local command link + + link="$1" + echo "reflash: $link: configuration link changed." + verify_help "$link" + while : + do + echo -n "option: " + read command + case "$command" in + ke*) return 0;; + up*) rm "$saved/$link" + return 1;; + di*) echo "DIFF:" + echo "OLD($saved): $link -> $(readlink "$saved/$link")" + echo "NEW($ffsdir): $link -> $(readlink "$ffsdir/$link")";; + sh*) PS1="$link: " sh -i;; + sk*) return 1;; + *) verify_help "$link";; + esac + done +} +# +while read op file +do + # handle .configured specially (to preserve the original datestamp) + if test "$file" = "etc/.configured" + then + # this should definately not fail because of the test above! + if cp -a "$saved/$file" "$ffsdir/$file" + then + echo "$file" >&3 + else + echo "reflash: $file: timestamp copy failed (ignored)" >&2 + fi + elif test -h "$saved/file" -o -h "$ffsdir/$file" + then + # new or old symbolic link + if test -h "$saved/$file" -a -h "$ffsdir/$file" && + test "$(readlink "$saved/$file")" = "$(readlink "$ffsdir/$file")" + then + # no change + echo "$file" >&3 + else + # assume a change regardless + case "$op" in + preserve) + echo "$file" + echo "$file" >&3;; + diff) # need user input + if verify_link "$file" <>/dev/tty >&0 2>&0 + then + echo "$file" + echo "$file" >&3 + fi;; + esac + fi + else + # only overwrite if necessary + if test -e "$ffsdir/$file" && cmp -s "$saved/$file" "$ffsdir/$file" + then + # do not overwrite + echo "$file" >&3 + elif test ! -e "$ffsdir/$file" + then + # always preserve + echo "$file" + echo "$file" >&3 + else + case "$op" in + preserve) + echo "$file" + echo "$file" >&3;; + diff) # the files are different, get user input + if verify "$file" <>/dev/tty >&0 2>&0 + then + echo "$file" + echo "$file" >&3 + fi;; + esac + fi + fi +done <"$list" 3>/tmp/restore.$$ | (cd "$saved"; exec cpio -p -d -u "$ffsdir") || { + echo "reflash: $saved: restore of saved configuration files failed" >&2 + echo " The new flash file system is mounted on $ffsdir" >&2 + echo " The saved files are in $saved and the list in $list, the list of" >&2 + echo " files selected for restore is in /tmp/restore.$$" >&2 + echo " You should restore any required configuration from $saved," >&2 + echo " then umount $ffsdir and reboot." >&2 + exit 1 +} +# +# remove the copied files (i.e. the ones which were preserved) +( cd "$saved" + exec rm $(cat /tmp/restore.$$) +) +rm /tmp/restore.$$ +# +# clean up, files left in $saved need to be handled by the user +files="$(find "$saved" ! -type d -print)" +if test -n "$files" +then + echo "reflash: the following saved configuration files remain:" >&2 + echo "$files" >&2 + echo "The full list of preserved files is in $list. To alter the" >&2 + echo "new root file system use the command:" >&2 + echo "" >&2 + echo " mount -t jffs2 $ffsdev /mnt" >&2 + echo "" >&2 + echo "The saved files are in the temporary directory, they will not" >&2 + echo "be retained across a system boot. Copy them elsewhere if you" >&2 + echo "are unsure whether they are needed" >&2 +else + rm -rf "$saved" + rm "$list" +fi +# +# now the final umount +if umount "$ffsdir" +then + rmdir "$ffsdir" + echo "reflash: system upgrade complete. Reboot to continue." >&2 + exit 0 +else + echo "reflash: $ffsdir: temporary mount point umount failed" >&2 + echo " ALL changes have been made successfully, however the umount of" >&2 + echo " the new root file system has failed. You should determine the" >&2 + echo " cause of the failure, umount $ffsdir, then reboot the system (this" >&2 + echo " will use the upgraded kernel and root file system)" >&2 + exit 1 +fi |