#! /bin/sh

# Copyright (C) 2003, 2004  Alexandre Oliva <aoliva@redhat.com>

# Add the modules in a driver disk to the set available to the
# anaconda loader 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 directory containing:
# modules.cgz, a gzipped cpio file in crc format containing the
# modules named e.g. 2.6.5-1.358/i586/*.ko.  Other files typically
# found in driver disks, such as modinfo, modules.dep, modules.pcimap,
# pcitable, may also be present in the directory, and their contents
# will be merged with those in the built-in modules list.

# The remaining arguments name .iso or .img files that contain the
# modules directory to be updated.  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
# modules directory 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:

# ./add-modules /path/to/driver-disk FC2-i386-disc1.iso

# This will create not only FC2-i386-disc1.iso.new, but also
# initrd.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` modules-dir '[disc1.iso] [initrd.img] [rescuecd.iso] [boot.iso]' ... >&2
  exit 1
  ;;
esac

: ${XDELTA=:}

set -e

moddir=$1
if test ! -d "$moddir" || test ! -f "$moddir"/modules.cgz; then
  echo $moddir 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

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

while test $# != 0; do
  base=`basename "$1"`
  file=$base.new
  case $base in
  initrd*.img)
    gunzip < "$1" > "$file".unzip
    mkdir $MNT/mount $MNT/explode
    mount -o loop "$file".unzip $MNT/mount
    rsync -av --delete $MNT/mount/ $MNT/explode/
    umount -d $MNT/mount

    echo "updating initrd:modules/modules.cgz" >&2
    mkdir $MNT/drivers
    zcat $MNT/explode/modules/modules.cgz |
      (cd $MNT/drivers && cpio -i --make-directories)
    zcat "$moddir"/modules.cgz |
      (cd $MNT/drivers && cpio -i --make-directories)
    (cd $MNT/drivers && find * | cpio -o -H crc) |
      gzip -9 > $MNT/explode/modules/modules.cgz
    rm -rf $MNT/drivers

    if test -f "$moddir"/modinfo &&
       test -f $MNT/explode/modules/module-info; then
      dirinfover=`sed 1q "$moddir"/modinfo` &&
      mntinfover=`sed 1q $MNT/explode/modules/module-info` &&
      if test "x$dirinfover" = "x$mntinfover"; then
        echo "updating initrd:modules/module-info from modinfo" >&2
        sed 1d "$moddir"/modinfo >> $MNT/explode/modules/module-info
      else
        echo "module info files have different versions, not merging" >&2
      fi
    fi

    for f in modules.dep modules.pcimap pcitable; do
      if test -f "$moddir"/$f && test -f $MNT/explode/modules/$f; then
	echo "updating initrd:modules/$f from $f" >&2
        cat "$moddir"/$f >> $MNT/explode/modules/$f
      fi
    done

    dd if=/dev/zero of="$file".unzip.new bs=1k count=$INITRDSIZE
    mke2fs -F -i 1024 "$file".unzip.new $INITRDSIZE
    tune2fs -c 0 -i 0 "$file".unzip.new
    mount -o loop "$file".unzip.new $MNT/mount
    rsync -av $MNT/explode/ $MNT/mount/
    umount -d $MNT/mount
    rmdir $MNT/mount

    rm -rf $MNT/explode

    gzip -9 < "$file".unzip.new > "$file"
    rm -f "$file".unzip "$file".unzip.new
    ${XDELTA} delta -9 "$1" "$file" "$file".xdelta || :
    ;;

# there's no bootdisk.img in FC2
#   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
trap : 0 1 2 15

exit 0
