Debian-Installer: How to modify an existing CD image

DebianInstaller itself is based on udebs. Many of those udebs are on the CD, but some of them are in an initrd. If you want to modify the udeb's in the initrd, then you should follow the instructions on DebianInstaller/Modify/CustomKernel.

But when you want to modify non-initrd udeb's or you want to substitute or amend the *.debs, you can do this without the source code.

Create copy of the image

You need an already built ISO image from http://cdimage.debian.org/cdimage/daily-builds/sid_d-i/. That is the basis of the image that gets modified. Because you can't modify an ?ISO9660 image you must extract it first:

 $ rm -rf cd
 $ mkdir cd
 $ bsdtar -C cd -xf your-image.iso

Files and hooks

A list of hooks available for debian-installer: http://git.debian.org/?p=d-i/debian-installer.git;a=blob;f=doc/devel/internals/available-hooks.xml (XML source for the D-I Internals manual - does anyone know of this manual being available as compiled HTML anywhere?)

 (filepaths as seen from the installer)

List of packages (udebs) to install or not install in into the d-i ramdisk.

  ''cdrom''.disk/udeb_include ["anna"]
  ''cdrom''.disk/udeb_exclude

The content of these files are taken as lists of packages to install or not into the target area.

  ''cdrom''.disk/base_include [base-installer]
  ''cdrom''.disk/base_exclude

If the file ''cdrom''.disk/base_installable exists, the base-installer udeb selects the cdrom as source for the installation.

Modify Image

Now put all the packages that you want on your image into pool/main (or below) in this copy. You also have to change/add md5 checksum in md5sum.txt in cd

Workaround bug in deboostrap

There is this bug in debootstrap: debootstrap script install base-files and base-passwd depends on them in Packages file's sequence. Basically, debootstrap is sensitive to whether the base-files or base-passwd is listed first in the Packages file. It shouldn't care.

Because of it, we have two options if we want our modified CD to actually work:

  1. Patch the debootstrap functions file on the CD.

  2. Modify the sequence of packages in Packages generated by apt-ftparchive

Here, we do the first alternative above and patch debootstrap. Save this as a script and run it:

# For some reason, the wiki software eats my first script line which is
#!/bin/bash

# -e  Exit immediately if a command exits with a non-zero status
set -e
# -x  Print commands and their arguments as they are executed
set -x

# I tried to put the patch inline as a here-doc, but the wiki software messed
# up the whitespace and so the patch was corrupt. The patch here is from 
# #601670 - debootstrap script install base-files and base-passwd depends on them in Packages file's sequence - Debian Bug report logs
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=601670
wget -O debootstrapFunctions.patch http://pastebin.com/download.php?i=JudHSnWE

mkdir -p debootstrapPatch/DEBIAN
dpkg-deb --fsys-tarfile cd/pool/main/d/debootstrap/debootstrap*.udeb | tar -C debootstrapPatch -x
dpkg-deb -e cd/pool/main/d/debootstrap/debootstrap*.udeb debootstrapPatch/DEBIAN/
patch debootstrapPatch/usr/share/debootstrap/functions < debootstrapFunctions.patch 
# Prints:
# patching file debootstrapPatch/usr/share/debootstrap/functions
dpkg-deb --build debootstrapPatch
# Prints:
# dpkg-deb: building package `debootstrap-udeb' in `debootstrapPatch.deb'.
mv debootstrapPatch.deb cd/pool/main/d/debootstrap/debootstrap*.udeb

# Clean up
rm -r debootstrapPatch debootstrapFunctions.patch

Create new Release and Packages files

This step is required only if you have added or changed packages.

Create a config-udeb file with something like this :

 Dir {
    ArchiveDir "cd";
    OverrideDir "indices";
    CacheDir "indices";
 };
            
 TreeDefault {
    Directory "pool/";
 };
                    
 BinDirectory "pool/main" {
    Packages "dists/squeeze/main/debian-installer/binary-i386/Packages";
    BinOverride "override";
    ExtraOverride "override.extra";
 };
                                   
 Default {
    Packages {
        Extensions ".udeb";
    };
 };

and a config-deb file with something like this :

 Dir {
    ArchiveDir "cd";
    OverrideDir "indices";
    CacheDir "indices";
 };
            
 TreeDefault {
    Directory "pool/";
 };
                    
 BinDirectory "pool/main" {
    Packages "dists/squeeze/main/binary-i386/Packages";
    BinOverride "override";
    ExtraOverride "override.extra";
 };
                                   
 Default {
    Packages {
        Extensions ".deb";
    };
 };

The first is for the .udeb packages used by the Debian installer; the second is for Debian .deb packages. Because of the BinOverride line you also need the override file, you can get it from any debian mirror, e.g. get http://ftp.de.debian.org/debian/indices/override.squeeze.main.gz and extract it to the indices/override.

Now go into the directory with your config file and run

 # apt-ftparchive generate config-udeb
 # apt-ftparchive generate config-deb

to generate the Packages and Releases files.

To update the cd/dists/squeeze/Release file, make a new text file called config-rel with something like this:

APT::FTPArchive::Release::Codename "squeeze";
APT::FTPArchive::Release::Origin "Debian";
APT::FTPArchive::Release::Components "main";
APT::FTPArchive::Release::Label "Debian";
APT::FTPArchive::Release::Architectures "i386";
APT::FTPArchive::Release::Suite "testing";

To generate the Release file, run

 # apt-ftparchive -c config-rel release cd/dists/squeeze > cd/dists/squeeze/Release

Alternate Method

If you use apt-move to store the packages apt downloads when updating your system, you can use the mirror it creates to fill the cdrom with all the packages on your current system. Remember to call "apt-move sync" before mastering the image, to make sure, all dependencies are met.

Fix md5sum's

 # cd cd; md5sum `find ! -name "md5sum.txt" ! -path "./isolinux/*" -follow -type f` > md5sum.txt; cd ..

Create new image

To make the cdrom bootable, you need to run mkisofs with appropriate parameters. Here is how to do it for x86/amd64, using isolinux.

Change to the top of the cd directory then:

To create the cdrom image using the isolinux boot image:

 # mkisofs -o test.iso -r -J -no-emul-boot -boot-load-size 4 \
 -boot-info-table -b isolinux/isolinux.bin -c isolinux/boot.cat ./cd 

or, if your extra dists and pool (the apt-move mirror) are in a separate place

 # mkisofs -o test.iso -r -J -no-emul-boot -boot-load-size 4 \
 -boot-info-table -b isolinux/isolinux.bin -c isolinux/boot.cat \
 extra/dists=pathto/mirror/dists extra/pool=pathto/mirror/pool \
 -graft-points ./cd

if you want you could do a (very) quick test of the iso using qemu (you need to apt-get it):

 # qemu -user-net -cdrom test.iso

burn the image to cdrom

 # cdrecord -dev /dev/hd? test.iso

Finally, from the build directory (please adapt to your local paths):

 #!/bin/sh
 make clean
 TYPE=cdrom fakeroot make image
 cp myudebs/*udeb \
       /home/vmware/bubulle/src/debian/d-i/archive/pool/main
 MYPWD=`pwd`
 cd /home/vmware/bubulle/src/debian/d-i/archive
 apt-ftparchive generate config
 cd $MYPWD
 mkisofs -r -J -b cdrom-image.img -o test.iso dest \
    /home/vmware/bubulle/src/debian/d-i/archive
 mkisofs -r -J -b cdrom-image.img -o test.iso dest /mnt/loop

A bit clumsy but this works, at least for me. This is how I tested the modified partitioner (which does not go to initrd as it requires fdisk-udeb which is huge). For smaller package tests, you may modify pkg-lists/cdrom/<arch>, add them there and drop the udebs to localudebs.

Helper scripts: diunpk, dipk

Here are example scripts for unpacking and packing d-i CD image file. You need to have aufs modules available.

* diunpk: execute this with d-i.iso file as its argument. This generates tree under dated directory and cd into it.

#!/bin/sh -e

# Local customization to match your id (check with 'id' command).
uid=1000
gid=1000

# Default values
# $1 d-i iso image file
# $2 d-i ro mount point
# $3 d-i rw tree

di_iso=${1:-d-i.iso}
di_ro=${2:-d-i.ro}
di_rw=${3:-d-i.rw}

pwd=$(pwd)
timestamp=$(date -u +%Y%m%d%H%M%S)
mkdir $timestamp

mkdir $timestamp/$di_ro
mkdir $timestamp/$di_rw

sudo mount ${di_iso} $timestamp/${di_ro} -t iso9660 -o loop,uid=${uid},gid=${gid}
sudo mount -t aufs -o br:$timestamp/${di_rw}:$timestamp/${di_ro} none $timestamp/${di_rw}
sudo chmod u+w $timestamp/${di_rw}
sudo chmod u+w $timestamp/${di_rw}/md5sum.txt
sudo find $timestamp/${di_rw}/dists -exec chmod u+w {} \;
sudo find $timestamp/${di_rw}/pool  -type d -exec chmod u+w {} \;
cd $timestamp

* dipk: execute this in dated directory.

#!/bin/sh -e
set -x
# Local customization
uid=1000
gid=1000
arch=amd64
release=squeeze

# Default values
# $1 d-i iso image file
# $2 d-i ro mount point
# $3 d-i rw tree

di_iso=${1:-d-i.iso}
di_ro=${2:-d-i.ro}
di_rw=${3:-d-i.rw}

cat > config << EOF
Dir {
    ArchiveDir ".";
    OverrideDir ".";
    CacheDir ".";
 };
            
 TreeDefault {
    Directory "pool/";
 };
                    
 BinDirectory "pool/main" {
    Packages "dists/${release}/main/debian-installer/binary-${arch}/Packages";
 };
                                   
 Default {
    Packages {
        Extensions ".udeb";
    };
 };
EOF

cd $di_rw
sudo apt-ftparchive generate ../config
sudo md5sum $(find ! -name "md5sum.txt" ! -path "./isolinux/*" -follow -type f) > md5sum.txt
cd -

#genisoimage ...
sudo genisoimage -r -o $di_iso -V di$(date -u +%m%d%H%M%S) \
   -b isolinux/isolinux.bin -c isolinux/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table $di_rw

# check mounted by "mount"
#sudo umount ${di_rw}
#sudo umount ${di_ro}
#rm -rf $di_rw