diff options
Diffstat (limited to 'recipes/slugos-init/files/reflash')
-rw-r--r-- | recipes/slugos-init/files/reflash | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/recipes/slugos-init/files/reflash b/recipes/slugos-init/files/reflash new file mode 100644 index 0000000000..22a18bb068 --- /dev/null +++ b/recipes/slugos-init/files/reflash @@ -0,0 +1,658 @@ +#!/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 +load_functions sysconf +# +# NSLU2 flash layout is non-standard. +case "$(machine)" in +nslu2) + isnslu2=1 + isdsmg600= + isnas100d= + imageok=1 + apexpart="Loader" + usrpart= + kpart="Kernel" + ffspart="Flashdisk";; +nas100d) + isnslu2= + isdsmg600= + isnas100d=1 + imageok=1 + apexpart= + usrpart="usr" + kpart="kernel" + ffspart="filesystem";; +dsmg600) + isnslu2= + isdsmg600=1 + isnas100d= + imageok=1 + apexpart= + usrpart="usr" + kpart="kernel" + ffspart="filesystem";; +*) + isnslu2= + isdsmg600= + isnas100d= + imageok= + apexpart= + usrpart= + kpart="kernel" + ffspart="filesystem";; +esac +# +# CHECKING FOR INPUT (ARGUMENTS ETC) +# ---------------------------------- +# +# find the kernel and the new flash file system, an image file can +# be used to specify both images. +ffsfile= +kfile= +imgfile= +preserve_config=1 +while test $# -gt 0 +do + case "$1" in + -n) preserve_config= + shift;; + -k) shift + test $# -gt 0 || { + echo "reflash: -k: give the file containing the kernel image" >&2 + exit 1 + } + kfile="$1" + shift;; + -[jr]) shift + test $# -gt 0 || { + echo "reflash: -j: give the file containing the root jffs2 image" >&2 + exit 1 + } + ffsfile="$1" + shift;; + -i) shift + test -n "$imageok" || { + echo "reflash: -i: only supported on the LinkSys NSLU2," >&2 + echo " Iomega NAS 100d and D-Link DSM-G600 systems; use -k and -j" >&2 + echo " to specify the kernel and root file system instead." >&2 + exit 1 + } + test $# -gt 0 || { + echo "reflash: -i: give the file containing the complete flash image" >&2 + exit 1 + } + imgfile="$1" + shift;; + *) if test -n "$imageok" + then + echo "reflash: usage: $0 [-n] [-k kernel] [-j rootfs] [-i image]" >&2 + else + echo "reflash: usage: $0 [-n] [-k kernel] [-j rootfs]" >&2 + fi + echo " -n: do not attempt to preserve the configuration" >&2 + echo " -k file: the new compressed kernel image ('zImage')" >&2 + echo " -j file: the new root file system (jffs2)" >&2 + test -n "$imageok" && + echo " -i file: a complete flash image (gives both kernel and jffs2)" >&2 + echo " The current jffs2 will be umounted if mounted." >&2 + exit 1;; + esac +done +# +# Sanity check on the arguments (note that the first case can only fire +# on NSLU2 or DSM-G600 because of the check for -i above.) +if test -n "$imgfile" -a -n "$ffsfile" -a -n "$kfile" +then + echo "reflash: specify at most two files" >&2 + echo " -i has both a kernel and rootfs, the kernel from -k and" >&2 + echo " the rootfs from -j override the one in the image (if given)" >&2 + exit 1 +elif test -z "$imgfile" -a -z "$ffsfile" -a -z "$kfile" +then + echo "reflash: specify at least one file to flash" >&2 + exit 1 +fi +# +# Perform basic checks on the input (must exist, size must be ok). +if test -n "$imgfile" +then + if test -r "$imgfile" -a -n "$isnslu2" + then + # read the partition table and from this find the offset + # and size of $kpart and $ffspart 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 + if test "$name" = "$apexpart" + then + imgapexsize="$size" + imgapexoffset="$base" + elif test "$name" = "$kpart" + then + imgksize="$size" + imgkoffset="$base" + elif test "$name" = "$ffspart" -o "$name" = "rootfs" + then + imgffssize="$size" + imgffsoffset="$base" + fi + done <<EOI +$(devio "<<$imgfile" ' + <= $ 0x20000 - + L= 0x1000 + $( 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+ + L= L256- + $) L255>') +EOI + # check the result + test "$imgksize" -gt 0 -a "$imgkoffset" -ge 0 || { + echo "reflash: $imgfile: failed to find $kpart partition in image" >&2 + exit 1 + } + # the kernel is after a 16 byte header which holds the + # values length,0,0,0 Get the true size. + ktmp="$(devio "<<$imgfile" "L=$imgksize" "O=$imgkoffset" ' + $( OL+$>! + <= O + A= b + $( AL>! + pr A + $) 0 + $) 0')" + test "$ktmp" -gt 0 || { + echo "reflash: $imgfile($imgkoffset,$imgksize): invalid kernel offset/size" >&2 + exit 1 + } + # update the size and offset to these values (the offset is 16+ because + # of the header). + imgksize="$ktmp" + imgkoffset="$(devio "O=$imgkoffset" 'pr O16+')" + # just test the size for the rootfs + test "$imgffssize" -gt 0 -a "$imgffsoffset" -ge 0 || { + echo "reflash: $imgfile: failed to find $ffspart" >&2 + exit 1 + } + elif test -r "$imgfile" -a \( -n "$isdsmg600" -o -n "$isnas100d" \) + then + # + # For the DSM-G600, this is really easy - the image is just + # a tar file. So, extract the contents of the tar file, and + # set the kernel and filesystem variables (if not already set) + # to point to the extracted content. Content will look like: + # + # drwxr-xr-x 500/500 0 2006-11-25 23:47:59 firmupgrade + # -rw-r--r-- 500/500 4718592 2006-12-02 16:32:51 firmupgrade/rootfs.gz + # -rw-r--r-- 500/500 40 2006-11-25 22:15:41 firmupgrade/version.msg + # -rw-r--r-- 500/500 0 2006-11-25 23:47:59 firmupgrade/usr.cramfs + # -rw-rw-r-- 500/500 1306872 2006-12-02 16:33:37 firmupgrade/ip-ramdisk + # + # Heuristic: if the size of usr.cramfs is zero, the firmware + # is not a D-Link firmware for the device. (The version.msg + # file is not useful for this purpose; it describes the hardware, + # not the firmware version in the image!) + # + # TODO: If usr.cramfs is non-zero, we should flash that, too, just + # to make sure that it matches the native firmware's kernel + # and rootfs that we're now flashing back onto the device. + + echo "reflash: unpacking DSM-G600/NAS-100d image file" >&2 + tar -x -f "$imgfile" -C /var/tmp || { + echo "reflash: unable to unpack image file to be flashed" >&2 + exit 1 + } + + if test -z "$kfile" + then + kfile="/var/tmp/firmupgrade/ip-ramdisk" + fi + + if test -z "$ffsfile" + then + ffsfile="/var/tmp/firmupgrade/rootfs.gz" + fi + + if test -s "/var/tmp/firmupgrade/usr.cramfs" + then + echo "reflash: Native flash being restored" >&2 + usrfile="/var/tmp/firmupgrade/usr.cramfs" + preserve_config= + fi + + else + echo "reflash: $imgfile: image file not found" >&2 + exit 1 + fi +fi +if test -n "$kfile" +then + if test ! -r "$kfile" + then + echo "reflash: $kfile: kernel file not found" >&2 + exit 1 + fi + # the file values override anything from the image. + imgksize="$(devio "<<$kfile" 'pr$')" + imgkoffset=0 +else + kfile="$imgfile" +fi +if test -n "$ffsfile" +then + if test ! -r "$ffsfile" + then + echo "reflash: $ffsfile: root file system image file not found" >&2 + exit 1 + fi + # values override those from the image + imgffssize="$(devio "<<$ffsfile" 'pr$')" + imgffsoffset=0 +else + ffsfile="$imgfile" +fi +if test -n "$usrfile" +then + if test ! -r "$usrfile" + then + echo "reflash: $usrfile: usr file system image file not found" >&2 + exit 1 + fi + # values override those from the image + imgusrsize="$(devio "<<$usrfile" 'pr$')" + imgusroffset=0 +else + usrfile= +fi +# +# INPUTS OK, CHECKING THE ENVIRONMENT +# ----------------------------------- +# basic setup. This could be parameterised to use different partitions! +# +kdev= +ksize=0 +if test -n "$kfile" +then + # we have a new kernel + 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$')" + # + # check the input file size + test -n "$imgksize" -a "$imgksize" -gt 0 -a "$imgksize" -le "$ksize" || { + echo "reflash: $kfile: bad $kpart size ($imgksize, max $ksize)" >&2 + exit 1 + } +fi +# +ffsdev= +ffssize=0 +if test -n "$ffsfile" +then + ffsdev="$(mtblockdev $ffspart)" + [ -n "$ffsdev" ] || \ + ffsdev="$(mtblockdev rootfs)" + 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$')" + # + # check the input file size + test -n "$imgffssize" -a "$imgffssize" -gt 0 -a "$imgffssize" -le "$ffssize" || { + echo "reflash: $ffsfile: bad $ffspart size ($imgffsize, max $ffssize)" >&2 + exit 1 + } +fi +# +usrdev= +usrsize=0 +if test -n "$usrfile" +then + usrdev="$(mtblockdev $usrpart)" + test -n "$usrdev" -a -b "$usrdev" || { + echo "reflash: $usrpart($usrdev): cannot find $usrpart 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 + } + usrsize="$(devio "<<$usrdev" 'pr$')" + # + # check the input file size + test -n "$imgusrsize" -a "$imgusrsize" -gt 0 -a "$imgusrsize" -le "$usrsize" || { + echo "reflash: $usrfile: bad $usrpart size ($imgusrsize, max $usrsize)" >&2 + exit 1 + } +fi + +# +# INPUTS OK, ENVIRONMENT OK, UMOUNT ANY EXISTING MOUNT OF THE FLASHDISK +# --------------------------------------------------------------------- +# This is only required if the device is going to be used +if test -n "$ffsdev" +then + # -r causes this to fail if the flash device is mounted on / + umountflash -r "$ffsdev" || 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 "$ffsdev" "$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 + } +else + errorexit() { + exit 1 + } +fi +# +# PRESERVE EXISTING CONFIGURATION +# ------------------------------- +# Only required if the flash partition will be written +if test -n "$ffsdev" -a -n "$preserve_config" +then + 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 + } + # + # sysconf_save_conffiles <flash-directory> <dest> <list> + sysconf_save_conffiles "$ffsdir" "$saved" "$list" || { + 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 + } +fi +# +# FLASH THE NEW IMAGES +# -------------------- +echo "reflash: about to flash new image" >&2 +# +# There are four possibilities here - kernel only, flashdisk only, then +# kernel&flashdisk in either one or two different files. The code used +# to attempt to do everything in one step, but this complicates it, +# so two steps are used (as required). A failure between the two +# steps is a problem, but then so is a failure in a partial write. +# Write the flashdisk first because this is larger (most likely to +# fail). +# +# -p causes the progress indicator to be displayed +progress=-p +do_kernel() { + local cmd + if test -n "$isnslu2" + then + # NSLU2: write length,0,0,0 header, then fill + cmd="wb L,4; fb 12,0; cpL" + else + # Other: just write the kernel bytes + cmd="cpL" + fi + devio $progress "$@" "<<$kfile" ">>$kdev" ' + # kernel is at imgkoffset[imgksize] + ' "<= $imgkoffset" "L=$imgksize" "$cmd" ' + # fill with 255 + fb #t-,255' +} +# +do_ffs() { + devio $progress "$@" "<<$ffsfile" ">>$ffsdev" ' + # rootfs is at imgffsoffset[imgffssize] + ' "<= $imgffsoffset" "cp $imgffssize" ' + # fill with 255 + fb #t-,255' +} +# +do_usr() { + devio $progress "$@" "<<$usrfile" ">>$usrdev" ' + # usrfs is at imgusroffset[imgusrsize] + ' "<= $imgusroffset" "cp $imgusrsize" ' + # fill with 255 + fb #t-,255' +} +# +# check_status $? type file(offset,size) device +# check the devio status code (given in $1) +check_status() { + case "$1" in + 0) echo " done" >&2;; + 1) echo " failed" >&2 + echo "reflash: $3: flash $2 failed, no changes have been made to $4" >&2 + if test "$2" = rootfs + then + rm -rf "$saved" + rm "$list" + exit 1 + else + echo "reflash: $2: continuing with rootfs changes" >&2 + echo " NOTE: the old kernel is still in $4!" >&2 + fi;; + 3) echo " failed" >&2 + echo "reflash: $3: WARNING: partial flash of $2 to $4 the system is unbootable" >&2 + echo " Reflash from RedBoot or correct the problem here." >&2 + if test "$2" = rootfs + then + exit 3 + else + echo "reflash: $2: continuing with rootfs changes" >&2 + echo " NOTE: the kernel in $4 must be reflashed!" >&2 + fi;; + *) echo " failed" >&2 + echo "reflash($1): $3: internal error flashing $2 to $4" >&2 + exit $1;; + esac +} +# +if test -n "$usrdev" +then + echo -n "reflash: writing usrfs to $usrdev " >&2 + do_usr + check_status $? usrfs "$usrfile($imgusroffset,$imgusrsize)" "$usrdev" +fi +# +if test -n "$ffsdev" +then + echo -n "reflash: writing rootfs to $ffsdev " >&2 + do_ffs + check_status $? rootfs "$ffsfile($imgffsoffset,$imgffssize)" "$ffsdev" +fi +# +if test -n "$kdev" +then + echo -n "reflash: writing kernel to $kdev " >&2 + do_kernel + check_status $? kernel "$kfile($imgkoffset,$imgksize)" "$kdev" +fi +# +# verify - this just produces a warning +if test -n "$usrdev" +then + echo -n "reflash: verifying new usr image " >&2 + if do_usr -v + then + echo " done" >&2 + else + echo " failed" >&2 + echo "reflash: WARNING: usrfs flash image verification failed" >&2 + echo " The system is may be bootable." >&2 + fi +fi +# +if test -n "$ffsdev" +then + echo -n "reflash: verifying new flash image " >&2 + if do_ffs -v + then + echo " done" >&2 + else + echo " failed" >&2 + echo "reflash: WARNING: rootfs 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 + fi +fi +# +if test -n "$kdev" +then + echo -n "reflash: verifying new kernel image " >&2 + if do_kernel -v + then + echo " done" >&2 + else + echo " failed" >&2 + echo "reflash: WARNING: kernel flash image verification failed" >&2 + echo " The system is probably unbootable." >&2 + echo " System configuration files will be restored in the rootfs." >&2 + fi +fi +# +# RESTORE THE OLD CONFIGURATION +# ----------------------------- +# If not write the rootfs none of the following is required - exit now. +test -n "$ffsdev" -a -n "$preserve_config" || exit 0 +# +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 "$ffsdev" "$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 +} +# +# sysconf_restore_conffiles <flash-directory> <source-dir> <restore> +restore="/tmp/restore.$$" +sysconf_restore_conffiles "$ffsdir" "$saved" "$restore" <"$list" || { + 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 $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 "$restore") +) +rm "$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 |