]> pilppa.org Git - familiar-h63xx-build.git/blob - org.handhelds.familiar/packages/openslug-init/openslug-init-0.10/reflash
OE tree imported from monotone branch org.openembedded.oz354fam083 at revision 8b12e3...
[familiar-h63xx-build.git] / org.handhelds.familiar / packages / openslug-init / openslug-init-0.10 / reflash
1 #!/bin/sh
2 # reflash
3 #  ensure the flash disk is not mounted
4 #  save configuration files
5 #  update the kernel
6 #  update the flashdisk
7 #  restore the saved configuration files
8 # the set of configuration files is described by
9 # /etc/default/conffiles.
10 #
11 # /etc/default/functions contains useful utility functions
12 . /etc/default/functions
13 #
14 # CHECKING FOR INPUT (ARGUMENTS ETC)
15 # ----------------------------------
16 #
17 # find the kernel and the new flash file system, an image file can
18 # be used to specify both images.
19 ffsfile=
20 kfile=
21 imgfile=
22 while test $# -gt 0
23 do
24         case "$1" in
25         -k)     shift
26                 test $# -gt 0 || {
27                         echo "reflash: -k: give the file containing the kernel image" >&2
28                         exit 1
29                 }
30                 kfile="$1"
31                 shift;;
32         -[jr])  shift
33                 test $# -gt 0 || {
34                         echo "reflash: -j: give the file containing the root jffs2 image" >&2
35                         exit 1
36                 }
37                 ffsfile="$1"
38                 shift;;
39         -i)     shift
40                 test $# -gt 0 || {
41                         echo "reflash: -i: give the file containing the complete flash image" >&2
42                         exit 1
43                 }
44                 imgfile="$1"
45                 shift;;
46         *)      echo "reflash: usage: $0 [-k kernel] [-j rootfs] [-i image]" >&2
47                 echo "  -k file: the new compressed kernel image ('zImage')" >&2
48                 echo "  -j file: the new root file system (jffs2)" >&2
49                 echo "  -i file: a complete flash image (gives both kernel and jffs2)" >&2
50                 echo " The current jffs2 will be umounted if mounted." >&2
51                 exit 1;;
52         esac
53 done
54 #
55 # Sanity check on the arguments
56 if test -n "$imgfile" -a -n "$ffsfile" -a -n "$kfile"
57 then
58         echo "reflash: -k,-j,-i: specify at most two files" >&2
59         echo "  -i has both a kernel and rootfs, the kernel from -k and" >&2
60         echo "  the rootfs from -j override the one in the image (if given)" >&2
61         exit 1
62 elif test -z "$imgfile" -a -z "$ffsfile" -a -z "$kfile"
63 then
64         echo "reflash: -k,-j,-i: specify at least one file to flash" >&2
65         exit 1
66 fi
67 #
68 # Perform basic checks on the input (must exist, size must be ok).
69 if test -n "$imgfile"
70 then
71         if test -r "$imgfile"
72         then
73                 # read the partition table and from this find the offset
74                 # and size of Kernel and Flashdisk partitions.  The following
75                 # devio command just dumps the partition table in a format
76                 # similar to /proc/mtd (but it outputs decimal values!)
77                 #NOTE: this uses a here document because this allows the while
78                 # loop to set the variables, a pipe would put the while in
79                 # a sub-shell and the variable settings would be lost.  This
80                 # works in ash, no guarantees about other shells!
81                 while read size base name
82                 do
83                         case "$name" in
84                         Kernel) imgksize="$size"
85                                 imgkoffset="$base";;
86                         Flashdisk)
87                                 imgffssize="$size"
88                                 imgffsoffset="$base";;
89                         esac
90                 done <<EOI
91 $(devio "<<$imgfile" '
92         <= $ 0x20000 -
93         L= 0x1000
94         $( 1
95                 # 0xff byte in name[0] ends the partition table
96                 $? @ 255 =
97                 # output size base name
98                 <= f15+
99                 .= b 0xfffffff &
100                 <= f4+
101                 .= b
102                 pf "%lu %lu "
103                 <= f28-
104                 cp 16
105                 pn
106                 <= f240+
107                 L= L256-
108         $) L255>')
109 EOI
110                 # check the result
111                 test "$imgksize" -gt 0 -a "$imgkoffset" -ge 0 || {
112                         echo "reflash: $imgfile: failed to find Kernel partition in image" >&2
113                         exit 1
114                 }
115                 # the kernel is after a 16 byte header which holds the
116                 # values length,0,0,0  Get the true size.
117                 ktmp="$(devio "<<$imgfile" "L=$imgksize" "O=$imgkoffset" '
118                         $( OL+$>!
119                                 <= O
120                                 A= b
121                                 $( AL>!
122                                         pr A
123                                 $) 0
124                         $) 0')"
125                 test "$ktmp" -gt 0 || {
126                         echo "reflash: $imgfile($imgkoffset,$imgksize): invalid kernel offset/size" >&2
127                         exit 1
128                 }
129                 # update the size and offset to these values (the offset is 16+ because
130                 # of the header).
131                 imgksize="$ktmp"
132                 imgkoffset="$(devio "O=$imgkoffset" 'pr O16+')"
133                 # just test the size for the rootfs
134                 test "$imgffssize" -gt 0 -a "$imgffsoffset" -ge 0 || {
135                         echo "reflash: $imgfile: failed to find Flashdisk" >&2
136                         exit 1
137                 }
138         else
139                 echo "reflash: $imgfile: image file not found" >&2
140                 exit 1
141         fi
142 fi
143 if test -n "$kfile"
144 then
145         if test ! -r "$kfile"
146         then
147                 echo "reflash: $kfile: kernel file not found" >&2
148                 exit 1
149         fi
150         # the file values override anything from the image.
151         imgksize="$(devio "<<$kfile" 'pr$')"
152         imgkoffset=0
153 else
154         kfile="$imgfile"
155 fi
156 if test -n "$ffsfile"
157 then
158         if test ! -r "$ffsfile"
159         then
160                 echo "reflash: $ffsfile: root file system image file not found" >&2
161                 exit 1
162         fi
163         # values override those from the image
164         imgffssize="$(devio "<<$ffsfile" 'pr$')"
165         imgffsoffset=0
166 else
167         ffsfile="$imgfile"
168 fi
169 #
170 # INPUTS OK, CHECKING THE ENVIRONMENT
171 # -----------------------------------
172 # basic setup.  This could be parameterised to use different partitions!
173 kpart=Kernel
174 ffspart=Flashdisk
175 #
176 kdev=
177 ksize=0
178 if test -n "$kfile"
179 then
180         # we have a new kernel
181         kdev="$(mtblockdev $kpart)"
182         test -n "$kdev" -a -b "$kdev" || {
183                 echo "reflash: $kpart($kdev): cannot find $kpart mtd partition." >&2
184                 echo "  check /proc/mtd, either the partition does not exist or there is no" >&2
185                 echo "  corresponding block device." >&2
186                 exit 1
187         }
188         ksize="$(devio "<<$kdev" 'pr$')"
189         #
190         # check the input file size
191         test -n "$imgksize" -a "$imgksize" -gt 0 -a "$imgksize" -le "$ksize" || {
192                 echo "reflash: $kfile: bad Kernel size ($s, max $ksize)" >&2
193                 exit 1
194         }
195 fi
196 #
197 ffsdev=
198 ffssize=0
199 if test -n "$ffsfile"
200 then
201         ffsdev="$(mtblockdev $ffspart)"
202         test -n "$ffsdev" -a -b "$ffsdev" || {
203                 echo "reflash: $ffspart($ffsdev): cannot find $ffspart mtd partition." >&2
204                 echo "  check /proc/mtd, either the partition does not exist or there is no" >&2
205                 echo "  corresponding block device." >&2
206                 exit 1
207         }
208         ffssize="$(devio "<<$ffsdev" 'pr$')"
209         #
210         # check the input file size
211         test -n "$imgffssize" -a "$imgffssize" -gt 0 -a "$imgffssize" -le "$ffssize" || {
212                 echo "reflash: $ffsfile: bad Flashdisk size ($s, max $ffssize)" >&2
213                 exit 1
214         }
215 fi
216
217 #
218 # INPUTS OK, ENVIRONMENT OK, UMOUNT ANY EXISTING MOUNT OF THE FLASHDISK
219 # ---------------------------------------------------------------------
220 # This is only required if the device is going to be used
221 if test -n "$ffsdev"
222 then
223         # -r causes this to fail if the flash device is mounted on /
224         umountflash -r "$ffsdev" || exit 1
225         #
226         # Everything is umounted, now remount on a temporary directory.
227         ffsdir="/tmp/flashdisk.$$"
228         mkdir "$ffsdir" || {
229                 echo "reflash: $ffsdir: failed to create temporary directory" >&2
230                 exit 1
231         }
232         #
233         mountflash "$ffsdev" "$ffsdir" -o ro || {
234                 rmdir "$ffsdir"
235                 exit 1
236         }
237         #
238         # this is a utility function to make the cleanup easier
239         errorexit() {
240                 umount "$ffsdir" && rmdir "$ffsdir" ||
241                         echo "reflash: $ffsdir: temporary directory cleanup failed" >&2
242                 exit 1
243         }
244         #
245         test -r "$ffsdir/etc/default/conffiles" || {
246                 echo "reflash: [/initrd]/etc/default/conffiles: file not found" >&2
247                 errorexit
248         }
249 else
250         errorexit() {
251                 exit 1
252         }
253 fi
254 #
255 # PRESERVE EXISTING CONFIGURATION
256 # -------------------------------
257 # Only required if the flash partition will be written
258 if test -n "$ffsdev"
259 then
260         echo "reflash: preserving existing configuration file" >&2
261         #
262         # This step produces /tmp/preserve.$$ and /tmp/cpio.$$, the former is
263         # a list of the preserved configuration files together with the processing
264         # option, the latter is a directory tree of the preserved files (a directory
265         # tree makes the restore step easier.)
266         saved=/tmp/cpio.$$
267         list=/tmp/preserve.$$
268         mkdir "$saved" || {
269                 echo "reflash: $saved: could not create save directory" >&2
270                 errorexit
271         }
272         #
273         (       cd "$ffsdir"
274                 find etc/*.conf $(sed 's!^/!!' usr/lib/ipkg/info/*.conffiles) ! -type d -newer etc/.configured -print |
275                         sed 's/^/diff /'
276                 exec sed 's/#.*$//;/^[  ]*$/d' etc/default/conffiles
277         ) | sed 's!^/*!!' | awk '{ op=$1; $1=""; file[$0]=op }
278                 END{ for (f in file) if (file[f] != "ignore") print file[f] f }' |
279         while read op file
280         do
281                 if test -e "$ffsdir/$file"
282                 then
283                         echo "$op $file" >&3
284                         echo "$file"
285                 fi
286         done 3>"$list" | (cd "$ffsdir"; exec cpio -p -d -m -u "$saved") || {
287                 echo "reflash: $saved: copy of saved configuration files failed" >&2
288                 rm -rf "$saved"
289                 rm "$list"
290                 errorexit
291         }
292         #
293         # If this umount fails do not try to continue...
294         umount "$ffsdir" || {
295                 echo "reflash: $ffsdir: temporary mount point umount failed" >&2
296                 echo "  No changes have been made." >&2
297                 rm -rf "$saved"
298                 rm "$list"
299                 exit 1
300         }
301 fi
302 #
303 # FLASH THE NEW IMAGES
304 # --------------------
305 echo "reflash: about to flash new image" >&2
306 #
307 # There are four possibilities here - kernel only, flashdisk only, then
308 # kernel&flashdisk in either one or two different files.  The code used
309 # to attempt to do everything in one step, but this complicates it,
310 # so two steps are used (as required).  A failure between the two
311 # steps is a problem, but then so is a failure in a partial write.
312 # Write the flashdisk first because this is larger (most likely to
313 # fail).
314 # Temporarily check for devio progress indicator capability this way...
315 if devio -p '' 2>/dev/null
316 then
317         progress=-p
318 else
319         progress=
320 fi
321 do_kernel() {
322         devio $progress "$@" "<<$kfile" ">>$kdev" '
323                 # kernel is at imgkoffset[imgksize]
324                 ' "<= $imgkoffset" "L=$imgksize" '
325                 # kernel write length,0,0,0 header, then fill
326                 wb L,4
327                 fb 12,0
328                 cp L
329                 # fill with 255
330                 fb #t-,255'
331 }
332 #
333 do_ffs() {
334         devio $progress "$@" "<<$ffsfile" ">>$ffsdev" '
335                 # rootfs is at imgffsoffset[imgffssize]
336                 ' "<= $imgffsoffset" "cp $imgffssize" '
337                 # fill with 255
338                 fb #t-,255'
339 }
340 #
341 # check_status $? type file(offset,size) device
342 #  check the devio status code (given in $1)
343 check_status() {
344         case "$1" in
345         0)      echo " done" >&2;;
346         1)      echo " failed" >&2
347                 echo "reflash: $3: flash $2 failed, no changes have been made to $4" >&2
348                 if test "$2" = rootfs
349                 then
350                         rm -rf "$saved"
351                         rm "$list"
352                         exit 1
353                 else
354                         echo "reflash: $2: continuing with rootfs changes" >&2
355                         echo "  NOTE: the old kernel is still in $4!" >&2
356                 fi;;
357         3)      echo " failed" >&2
358                 echo "reflash: $3: WARNING: partial flash of $2 to $4 the system is unbootable" >&2
359                 echo "  Reflash from RedBoot or correct the problem here." >&2
360                 if test "$2" = rootfs
361                 then
362                         exit 3
363                 else
364                         echo "reflash: $2: continuing with rootfs changes" >&2
365                         echo "  NOTE: the kernel in $4 must be reflashed!" >&2
366                 fi;;
367         *)      echo " failed" >&2
368                 echo "reflash($1): $3: internal error flashing $2 to $4" >&2
369                 exit $1;;
370         esac
371 }
372 #
373 if test -n "$ffsdev"
374 then
375         echo -n "reflash: writing rootfs to $ffsdev " >&2
376         do_ffs
377         check_status $? rootfs "$ffsfile($imgffsoffset,$imgffssize)" "$ffsdev"
378 fi
379 #
380 if test -n "$kdev"
381 then
382         echo -n "reflash: writing kernel to $kdev " >&2
383         do_kernel
384         check_status $? kernel "$kfile($imgkoffset,$imgksize)" "$kdev"
385 fi
386 #
387 # verify - this just produces a warning
388 if test -n "$ffsdev"
389 then
390         echo -n "reflash: verifying new flash image " >&2
391         if do_ffs -v
392         then
393                 echo " done" >&2
394         else
395                 echo " failed" >&2
396                 echo "reflash: WARNING: rootfs flash image verification failed" >&2
397                 echo "  The system is probably unbootable." >&2
398                 echo "  System configuration files will be restored but this may fail" >&2
399                 echo "  Starting a shell for user recovery (exit to continue)" >&2
400                 PS1='badflash$ ' sh -i <>/dev/tty >&0 2>&0
401         fi
402 fi
403 #
404 if test -n "$kdev"
405 then
406         echo -n "reflash: verifying new kernel image " >&2
407         if do_kernel -v
408         then
409                 echo " done" >&2
410         else
411                 echo " failed" >&2
412                 echo "reflash: WARNING: kernel flash image verification failed" >&2
413                 echo "  The system is probably unbootable." >&2
414                 echo "  System configuration files will be restored in the rootfs." >&2
415         fi
416 fi
417 #
418 # RESTORE THE OLD CONFIGURATION
419 # -----------------------------
420 # If not write the rootfs none of the following is required - exit now.
421 test -n "$ffsdev" || exit 0
422 #
423 echo "reflash: restoring saved configuration files" >&2
424 #
425 # the file /etc/.configured is the datestamp file used to ensure that
426 # changed configuration files can be recognised.  It is created by
427 # /etc/rcS.d/S99finish on first boot (if it does not exist).  We need
428 # a timestamp earlier than any files we create so touch it here, this
429 # also acts as a test on the mounted file system
430 mountflash "$ffsdev" "$ffsdir" && :>"$ffsdir/etc/.configured" || {
431         rmdir "$ffsdir"
432         echo "reflash: mount of new flash root file system failed" >&2
433         if test -d "$ffsdir/etc"
434         then
435                 echo "    The file system does not seem to be writeable." >&2
436                 echo "    The mounted file system is in $ffsdir" >&2
437         fi
438         echo "  WARNING: the kernel and root file system have been reflashed," >&2
439         echo "  HOWEVER the new root file system seems to be unuseable." >&2
440         echo "  Saved configuration files are in $saved" >&2
441         echo "  The list of saved configuration files is in $list" >&2
442         echo " You should determine the reason for the failed mount, mount the new" >&2
443         echo " file system and restore the configuration from $saved - it's just a" >&2
444         echo " matter of copying the saved files where required." >&2
445         exit 1
446 }
447 #
448 # verify file
449 #  this is called with the name of a 'diff' file which is, indeed,
450 #  different and with all the std streams connected to the tty.  It
451 #  returns a status code to say whether (0) or not (1) to copy the
452 #  file over.
453 #
454 verify_help() {
455         echo "Please specify how to handle this file or link, the options are as follows,"
456         echo "two character abbreviations may be used:"
457         echo
458         echo " keep:    retain the old file, overwrite the new flash image file"
459         echo " upgrade: retain the new file, the old (saved) file is not used"
460         echo " diff:    display the differences between the old and the new using diff -u"
461         echo " shell:   temporarily start an interactive shell (sh -i), exit to continue"
462         echo " skip:    ignore this file for the moment.  The file is left in the directory"
463         echo "          $saved and many be handled after this script has completed"
464 }
465 #
466 verify() {
467         local command file
468
469         file="$1"
470         echo "reflash: $file: configuration file changed."
471         verify_help "$file"
472         while :
473         do
474                 echo -n "option: "
475                 read command
476                 case "$command" in
477                 ke*)    return 0;;
478                 up*)    rm "$saved/$file"
479                         return 1;;
480                 di*)    echo "DIFF OLD($saved) NEW($ffsdir)"
481                         diff -u "$saved/$file" "$ffsdir/$file";;
482                 sh*)    PS1="$file: " sh -i;;
483                 sk*)    return 1;;
484                 *)      verify_help "$file";;
485                 esac
486         done
487 }
488 # the same, but for a link
489 verify_link() {
490         local command link
491
492         link="$1"
493         echo "reflash: $link: configuration link changed."
494         verify_help "$link"
495         while :
496         do
497                 echo -n "option: "
498                 read command
499                 case "$command" in
500                 ke*)    return 0;;
501                 up*)    rm "$saved/$link"
502                         return 1;;
503                 di*)    echo "DIFF:"
504                         echo "OLD($saved): $link -> $(readlink "$saved/$link")"
505                         echo "NEW($ffsdir): $link -> $(readlink "$ffsdir/$link")";;
506                 sh*)    PS1="$link: " sh -i;;
507                 sk*)    return 1;;
508                 *)      verify_help "$link";;
509                 esac
510         done
511 }
512 #
513 while read op file
514 do
515         # handle .configured specially (to preserve the original datestamp)
516         if test "$file" = "etc/.configured"
517         then
518                 # this should definately not fail because of the test above!
519                 if cp -a "$saved/$file" "$ffsdir/$file"
520                 then
521                         echo "$file" >&3
522                 else
523                         echo "reflash: $file: timestamp copy failed (ignored)" >&2
524                 fi
525         elif test -h "$saved/file" -o -h "$ffsdir/$file"
526         then
527                 # new or old symbolic link
528                 if test -h "$saved/$file" -a -h "$ffsdir/$file" &&
529                         test "$(readlink "$saved/$file")" = "$(readlink "$ffsdir/$file")"
530                 then
531                         # no change
532                         echo "$file" >&3
533                 else
534                         # assume a change regardless
535                         case "$op" in
536                         preserve)
537                                 echo "$file"
538                                 echo "$file" >&3;;
539                         diff)   # need user input
540                                 if verify_link "$file" <>/dev/tty >&0 2>&0
541                                 then
542                                         echo "$file"
543                                         echo "$file" >&3
544                                 fi;;
545                         esac
546                 fi
547         else
548                 # only overwrite if necessary
549                 if test -e "$ffsdir/$file" && cmp -s "$saved/$file" "$ffsdir/$file"
550                 then
551                         # do not overwrite
552                         echo "$file" >&3
553                 elif test ! -e "$ffsdir/$file"
554                 then
555                         # always preserve
556                         echo "$file"
557                         echo "$file" >&3
558                 else
559                         case "$op" in
560                         preserve)
561                                 echo "$file"
562                                 echo "$file" >&3;;
563                         diff)   # the files are different, get user input
564                                 if verify "$file" <>/dev/tty >&0 2>&0
565                                 then
566                                         echo "$file"
567                                         echo "$file" >&3
568                                 fi;;
569                         esac
570                 fi
571         fi
572 done <"$list" 3>/tmp/restore.$$ | (cd "$saved"; exec cpio -p -d -u "$ffsdir") || {
573         echo "reflash: $saved: restore of saved configuration files failed" >&2
574         echo "  The new flash file system is mounted on $ffsdir" >&2
575         echo "  The saved files are in $saved and the list in $list, the list of" >&2
576         echo "  files selected for restore is in /tmp/restore.$$" >&2
577         echo "  You should restore any required configuration from $saved," >&2
578         echo "  then umount $ffsdir and reboot." >&2
579         exit 1
580 }
581 #
582 # remove the copied files (i.e. the ones which were preserved)
583 (       cd "$saved"
584         exec rm $(cat /tmp/restore.$$)
585 )
586 rm /tmp/restore.$$
587 #
588 # clean up, files left in $saved need to be handled by the user
589 files="$(find "$saved" ! -type d -print)"
590 if test -n "$files"
591 then
592         echo "reflash: the following saved configuration files remain:" >&2
593         echo "$files" >&2
594         echo "The full list of preserved files is in $list.  To alter the" >&2
595         echo "new root file system use the command:" >&2
596         echo "" >&2
597         echo "  mount -t jffs2 $ffsdev /mnt" >&2
598         echo "" >&2
599         echo "The saved files are in the temporary directory, they will not" >&2
600         echo "be retained across a system boot.  Copy them elsewhere if you" >&2
601         echo "are unsure whether they are needed" >&2
602 else
603         rm -rf "$saved"
604         rm "$list"
605 fi
606 #
607 # now the final umount
608 if umount "$ffsdir"
609 then
610         rmdir "$ffsdir"
611         echo "reflash: system upgrade complete.  Reboot to continue." >&2
612         exit 0
613 else
614         echo "reflash: $ffsdir: temporary mount point umount failed" >&2
615         echo "  ALL changes have been made successfully, however the umount of" >&2
616         echo "  the new root file system has failed.  You should determine the" >&2
617         echo "  cause of the failure, umount $ffsdir, then reboot the system (this" >&2
618         echo "  will use the upgraded kernel and root file system)" >&2
619         exit 1
620 fi