#! /bin/sh

# Copyright (C) Alexandre Oliva <aoliva@redhat.com>
# Update loader program in Fedora Core install images.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to the
# Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.


# The first argument must be the name of the loader program to be installed.

# The remaining arguments name .iso or .img files that contain the
# loader to be replaced.  The supported file names are:

# - initrd*.img: a gzipped initrd filesystem image.

# - bootdisk.img: an uncompressed vfat filesystem containing initrd.img.

# - *.iso: an ISO filesystem, that must contain isolinux/initrd.img
# and, optionally, images/pxeboot/initrd.img and
# dosutils/autoboot/initrd.img (assumed to be the same as
# isolinux/initrd.img), images/bootdisk.img, images/boot.iso and
# images/rescuecd.iso.

# If the second argument is a .iso filename, the files listed above
# will be extracted from it into the current directory.  Then, the
# loader will be updated in them (which in turn extracts
# initrd-bootdisk.img from bootdisk.img), and finally the .iso image
# will be reconstructed with the updated files.

# No file is modified in-place; the `.new' suffix is appended to the
# basename of the original file, and the new files are always created
# in the current directory.  For convenience, the script also creates
# xdelta files between the original and the modified binaries, if the
# XDELTA environment variable is set to xdelta.

# For example, run as root:

# ./loader-update loader yarrow-i386-disc1.iso

# This will create not only yarrow-i386-disc1.iso.new, but also
# initrd.img.new, bootdisk.img.new, boot.iso.new.  In case you have a
# rescue cd, just add rescuecd.iso to the command line, and you'll get
# rescuecd.iso.new.

# Note that it *must* be run as root, because it has to loopback-mount
# filesystems.

case $#
in 0)
  echo usage: `basename $0` loader '[disc1.iso] [initrd.img] [bootdisk.img] [rescuecd.iso] [boot.iso]' ... >&2
  exit 1
  ;;
esac

: ${XDELTA=:}

set -e

loader=$1
if test ! -f "$loader"; then
  echo $loader does not exist >&2
  exit 1
fi
shift

case $1 in
*.iso)
  file=$1
  if isoinfo -R -J -i "$file" -x /images/rescuecd.iso > rescuecd.iso.new &&
     test -s rescuecd.iso.new; then
    ln rescuecd.iso.new rescuecd.iso
    rm rescuecd.iso.new
    set fnord rescuecd.iso ${1+"$@"}; shift
  else
    rm -f rescuecd.iso.new
  fi
  if isoinfo -R -J -i "$file" -x /images/boot.iso > boot.iso.new &&
     test -s boot.iso.new; then
    ln boot.iso.new boot.iso
    rm boot.iso.new
    set fnord boot.iso ${1+"$@"}; shift
  else
    rm -f boot.iso.new
  fi
  if isoinfo -R -J -i "$file" -x /images/bootdisk.img > bootdisk.img.new &&
     test -s bootdisk.img.new; then
    ln bootdisk.img.new bootdisk.img
    rm bootdisk.img.new
    set fnord bootdisk.img ${1+"$@"}; shift
  else
    rm -f bootdisk.img.new
  fi
  if isoinfo -R -J -i "$file" -x /isolinux/initrd.img > initrd.img.new &&
     test -s initrd.img.new; then
    ln initrd.img.new initrd.img
    rm initrd.img.new
    set fnord initrd.img ${1+"$@"}; shift
  else
    rm -f initrd.img.new
  fi
  ;;
esac

MNT=${TMPDIR-/tmp}/mnt.$$
mkdir $MNT
trap "umount -d $MNT/mount || umount -d $MNT || :; rm -rf $MNT || :; echo failed >&2; exit 1" 1 2 15

while test $# != 0; do
  base=`basename "$1"`
  file=$base.new
  case $base in
  initrd*.img)
    gunzip < "$1" > "$file".unzip
    mount -o loop "$file".unzip $MNT
    install "$loader" $MNT/bin/loader
    umount -d $MNT
    gzip -9 < "$file".unzip > "$file"
    rm -f "$file".unzip
    ${XDELTA} delta -9 "$1" "$file" "$file".xdelta || :
    ;;

  bootdisk.img)
    if test -f "$file" && test -f initrd-bootdisk.img.new; then
      mount -o loop "$file" $MNT
      cp initrd-bootdisk.img.new $MNT/initrd.img
      umount -d $MNT
      ${XDELTA} delta -9 "$1" "$file" "$file".xdelta || :
    else
      cp "$1" "$file"
      mount -o loop,ro "$file" $MNT
      cp $MNT/initrd.img initrd-bootdisk.img
      umount -d $MNT
      shift
      set fnord initrd-bootdisk.img $1 ${1+"$@"}
    fi
    ;;

  *.iso)
    if test ! -f initrd.img.new; then
      echo cannot process "$1" before initrd.img, skipping >&2
      continue
    fi
    mkdir $MNT/explode $MNT/mount
    mount -o loop,ro "$1" $MNT/mount
    rsync -av --delete $MNT/mount/ $MNT/explode/ --exclude=TRANS.TBL
    umount -d $MNT/mount
    cp initrd.img.new $MNT/explode/isolinux/initrd.img
    if test -f $MNT/explode/images/pxeboot/initrd.img; then
      ln -f $MNT/explode/isolinux/initrd.img \
        $MNT/explode/images/pxeboot/initrd.img
    fi
    if test -f $MNT/explode/dosutils/autoboot/initrd.img; then
      ln -f $MNT/explode/isolinux/initrd.img \
        $MNT/explode/dosutils/autoboot/initrd.img
    fi
    if test -f $MNT/explode/images/boot.iso && test -f boot.iso.new; then
      cp boot.iso.new $MNT/explode/images/boot.iso
    fi
    if test -f $MNT/explode/images/bootdisk.img &&
       test -f bootdisk.img.new; then
      cp bootdisk.img.new $MNT/explode/images/bootdisk.img
    fi
    mkisofs -o "$file" -b isolinux/isolinux.bin -c isolinux/boot.cat \
       -no-emul-boot -boot-load-size 4 -boot-info-table -R -J -v -T \
       $MNT/explode
    rm -rf $MNT/explode
    rmdir $MNT/mount
    if test -x /usr/lib/anaconda-runtime/implantisomd5; then
      /usr/lib/anaconda-runtime/implantisomd5 "$file"
    else
      echo missing anaconda-runtime, not adding md5 hash to $file >&2
    fi
    ${XDELTA} delta -9 "$1" "$file" "$file".xdelta || :
  esac
  shift
done

rmdir $MNT

exit 0
