Repacking a Debian ISO with its original boot equipment

This page describes how to determine the ISO 9660 production command that was used when a Debian ISO image was packed up, and how to derive a command to replay this production. This is not trivial because of the special preparations which are needed to boot via the firmware of various computer architectures.

What is a bootable ISO 9660 image ?

It is a list of bytes which form a readily formatted read-only filesystem. The bytes may be brought on some medium like DVD or USB stick, or they may be exposed to the operating system as loop device. In all these cases the filesystem can be mounted and will show a tree of directories and files.

To be bootable by computer firmware, such an ISO image has to bear special entry points where the firmware can learn about the next step to do. This next step is normally the start of a bootloader system, which offers the user a menu of operating systems and is able to start the chosen one.

The entry points on i386 and amd64 systems are: El Torito, MBR, GPT. Similar entry points are defined for other architectures. The topic is wide and can be confusing. This article concentrates on achieving the same boot equipment as in the original ISO without much curiosity about how this equipment actually works.

Learn about the actually used ISO production command

The Debian installation ISO images contain a file

/.disk/mkisofs

which recorded this shell command. For example in debian-9.3.0-amd64-netinst.iso

xorriso -as mkisofs -r -checksum_algorithm_iso md5,sha1,sha256,sha512
-V 'Debian 9.3.0 amd64 n'
-o /srv/cdbuilder.debian.org/dst/deb-cd/out/2amd64/debian-9.3.0-amd64-NETINST-1.iso
-jigdo-jigdo /srv/cdbuilder.debian.org/dst/deb-cd/out/2amd64/debian-9.3.0-amd64-NETINST-1.jigdo
-jigdo-template /srv/cdbuilder.debian.org/dst/deb-cd/out/2amd64/debian-9.3.0-amd64-NETINST-1.template
-jigdo-map Debian=/srv/cdbuilder.debian.org/src/ftp/debian/ -jigdo-exclude boot1
-md5-list /srv/cdbuilder.debian.org/src/deb-cd/tmp/2amd64/stretch/md5-check
-jigdo-min-file-size 1024 -jigdo-exclude 'README*' -jigdo-exclude /doc/
-jigdo-exclude /md5sum.txt -jigdo-exclude /.disk/ -jigdo-exclude /pics/
-jigdo-exclude 'Release*' -jigdo-exclude 'Packages*' -jigdo-exclude 'Sources*'
-J -J -joliet-long -cache-inodes
-isohybrid-mbr syslinux/usr/lib/ISOLINUX/isohdpfx.bin -b isolinux/isolinux.bin
-c isolinux/boot.cat -boot-load-size 4 -boot-info-table -no-emul-boot
-eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat
-isohybrid-apm-hfsplus boot1 CD1

"xorriso" is the program that was used to pack up the ISO. The other words are xorriso command "-as" and its arguments, which are partly compatible with options and arguments of programs "mkisofs" and "genisoimage".

In older Debian ISOs or with architecture "powerpc" you will find "genisoimage" or even "mkisofs" as producer. Most of those runs can be performed by "xorriso -as mkisofs" too, but architecture "powerpc" needs option "-hfs", which xorriso's emulation does not offer.

Remove the unneeded Jigdo production options

The content of file /.disk/mkisofs looks discouraging until we decide that we need no Jigdo files but only the complete ISO image. So we can omit all options which begin by -jigdo together with their subsequent arguments. Further we do not need the jigdo-related options -checksum_algorithm_iso and -md5-list with their arguments.

The remaining shell command is then:

xorriso -as mkisofs \
   -r -V 'Debian 9.3.0 amd64 n' \
   -o /srv/cdbuilder.debian.org/dst/deb-cd/out/2amd64/debian-9.3.0-amd64-NETINST-1.iso \
   -J -J -joliet-long -cache-inodes \
   -isohybrid-mbr syslinux/usr/lib/ISOLINUX/isohdpfx.bin \
   -b isolinux/isolinux.bin \
   -c isolinux/boot.cat \
   -boot-load-size 4 -boot-info-table -no-emul-boot \
   -eltorito-alt-boot \
   -e boot/grub/efi.img \
   -no-emul-boot -isohybrid-gpt-basdat -isohybrid-apm-hfsplus \
    boot1 CD1

Determine those options which need to be adapted on amd64 or i386

Many of these options can be re-used unchanged. But some need to be adapted to the situation that the ISO image is not exactly the same as the hard disk of Debian's production computers. Further we hardly want the new ISO image file to have the same name as the original.

For this purpose we roughly need to understand what the options do. The options of xorriso's mkisofs emulation are described in the man page of xorrisofs(1). It is available online as https://www.gnu.org/software/xorriso/man_1_xorrisofs.html

In above example we have

This assessment yields the following script for repacking the ISO from local directory "$HOME/iso_unpacked_and_modified":

# The example names get mapped to their roles here
orig_iso="$HOME"/debian-9.3.0-amd64-netinst.iso
new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-9.3.0-amd64-modified.iso
mbr_template=isohdpfx.bin

# Extract MBR template file to disk
dd if="$orig_iso" bs=1 count=432 of="$mbr_template"

# Create the new ISO image
xorriso -as mkisofs \
   -r -V 'Debian 9.3.0 amd64 n' \
   -o "$new_iso" \
   -J -J -joliet-long -cache-inodes \
   -isohybrid-mbr "$mbr_template" \
   -b isolinux/isolinux.bin \
   -c isolinux/boot.cat \
   -boot-load-size 4 -boot-info-table -no-emul-boot \
   -eltorito-alt-boot \
   -e boot/grub/efi.img \
   -no-emul-boot -isohybrid-gpt-basdat -isohybrid-apm-hfsplus \
   "$new_files"

CPU architectures other than i386 and amd64, older ISOs, Debian Live

The equipment of the ISOs matches the expectations of the firmwares which shall boot them. i386 and amd64 boot by either legacy BIOS or EFI. Their ISOs are nowadays equipped like the example above. But older ones may lack equipment for EFI or for BIOS booting from USB stick.

Other architectures have different needs. But all their ISOs are supposed to have a /.disk/mkisofs file.

arm64 release 9.4.0

After removing the jigdo-related options, there remain

xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 arm64 n' \
   -o /srv/.../debian-9.4.0-arm64-NETINST-1.iso \
   -J -joliet-long -cache-inodes \
   -e boot/grub/efi.img \
   -no-emul-boot \
   -append_partition 2 0xef /srv/.../efi.img \
   -partition_cyl_align all \
   CD1

Most of them we know from above "amd64" example. We need to invent a name for the new ISO. No need for extracting an MBR template, as there is none. And no need for legacy BIOS preparations, as there is no PC-BIOS on ARM64.

Not yet explained options are:

Partition extraction can be done by looking up the start block and number of blocks in the partition table of the original ISO, and by using dd to create a file on the local disk. In our example, fdisk reports about the partitions in the ISO:

$ /sbin/fdisk -l debian-9.4.0-arm64-netinst.iso 
...
Device                          Boot  Start    End Sectors  Size Id Type
debian-9.4.0-arm64-netinst.iso1           0 411647  411648  201M 83 Linux
debian-9.4.0-arm64-netinst.iso2      411648 413695    2048    1M ef EFI (FAT-12/

So start block is 411648 and size is 2048. The dd run would thus be:

dd if=debian-9.4.0-arm64-netinst.iso bs=512 skip=411648 count=2048 \
   of="$HOME"/efi.img

The resulting file "$HOME"/efi.img is supposed to be recognizable as FAT filesystem image.

The following script for repacking the ISO from local directory "$HOME/iso_unpacked_and_modified" can extract the partition if orig_iso is a single word.

orig_iso="$HOME"/debian-9.4.0-arm64-netinst.iso
# If orig_iso is not a single word, you must set auto_extract_efi to 0
# and extract efi_img manually before running this script.
auto_extract_efi=1
efi_img="$HOME"/efi.img

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-9.4.0-arm64-modified.iso

# If enabled: extract EFI partition image
part_img_ready=1
if test "$auto_extract_efi" = 1
then
  start_block=$(/sbin/fdisk -l "$orig_iso" | fgrep "$orig_iso"2 | \
                awk '{print $2}')
  block_count=$(/sbin/fdisk -l "$orig_iso" | fgrep "$orig_iso"2 | \
                awk '{print $4}')
  if test "$start_block" -gt 0 -a "$block_count" -gt 0 2>/dev/null
  then
    dd if="$orig_iso" bs=512 skip="$start_block" count="$block_count" \
       of="$efi_img"
  else
    echo "Cannot read plausible start block and block count from fdisk" >&2
    part_img_ready=0
  fi
fi

# Create the new ISO image if not partition extraction failed
test "$part_img_ready" = 1 && \
xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 arm64 n' \
   -o "$new_iso" \
   -J -joliet-long -cache-inodes \
   -e boot/grub/efi.img \
   -no-emul-boot \
   -append_partition 2 0xef "$efi_img" \
   -partition_cyl_align all \
   "$new_files"

ppc64el release 9.4.0

After removing the jigdo-related options:

xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 ppc64el n' \
   -o /srv/.../debian-9.4.0-ppc64el-NETINST-1.iso \
   -J -joliet-long -cache-inodes \
   -chrp-boot-part \
   CD1

Only one boot-specific option was used:

Together with the generic need for a new ISO name and a modified input tree, the repacking script is then:

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-9.4.0-ppc64el-modified.iso

# Create the new ISO image
xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 ppc64el n' \
   -o "$new_iso" \
   -J -joliet-long -cache-inodes \
   -chrp-boot-part \
   "$new_files"

mips64el release 9.4.0

After removing the jigdo-related options:

xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 m64el n' \
   -o /srv/.../debian-9.4.0-mips64el-NETINST-1.iso \
   -J -joliet-long -cache-inodes \
   CD1

There are no boot-specific options at all. So:

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-9.4.0-mips64el-modified.iso

# Create the new ISO image
xorriso -as mkisofs \
   -r -V 'Debian 9.4.0 m64el n' \
   -o "$new_iso" \
   -J -joliet-long -cache-inodes \
   "$new_files"

powerpc release 6.0.5

After removing the jigdo-related options:

/home/debian-cd/build/debian-cd.squeeze/../genisoimage \
   -joliet-long -r \
   -V 'Debian 6.0.5 ppc 1' \
   -o /org/.../debian-6.0.5-powerpc-BC-1.iso \
   --iso-level 4 \
   --netatalk -hfs -probe \
   -map /home/debian-cd/build/debian-cd.squeeze/data/hfs.map \
   -hfs-parms MAX_XTCSIZE=2656248 \
   --chrp-boot \
   -part -no-desktop \
   -hfs-bless CD1/install \
   -hfs-volid Debian/PowerPC_squeeze \
   CD1

Sorry, i cannot really explain what all this Apple/HFS stuff means. We keep what we do not understand.

orig_iso="$HOME"/debian-6.0.5-powerpc-businesscard.iso
new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-6.0.5-powerpc-modified.iso
hfs_map_url=https://sources.debian.org/data/main/d/debian-cd/3.1.21/data/hfs.map
hfs_map_file=hfs.map

# Download HFS type/creator map file of debian-cd package
wget "$hfs_map_url"

# Create the new ISO image
genisoimage \
   -joliet-long -r \
   -V 'Debian 6.0.5 ppc 1' \
   -o "$new_iso" \
   --iso-level 4 \
   --netatalk -hfs -probe \
   -map "$hfs_map_file" \
   -hfs-parms MAX_XTCSIZE=2656248 \
   --chrp-boot \
   -part -no-desktop \
   -hfs-bless "$new_files"/install \
   -hfs-volid Debian/PowerPC_squeeze \
   "$new_files"

sparc64 version 9.0

No Jigdo options were used. But we have a surplus "-checksum_algorithm_iso" which we remove.

xorriso -as mkisofs \
   -r \
   -V 'Debian 9.0 sparc64 1' \
   -o /srv/debian-cd-test/debian-9.0-sparc64-NETINST-1.iso \
   -G boot1/boot/isofs.b \
   -B ... \
   boot1 CD1

orig_iso="$HOME"/debian-9.0-sparc64-NETINST-1.iso
new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-9.0-sparc64-modified.iso
sys_area_file=isofs.b

# Extract System Area content to a disk file
dd if="$orig_iso" bs=2048 count=16 of="$sys_area_file"

# Create the new ISO image
xorriso -as mkisofs \
   -r \
   -V 'Debian 9.0 sparc64 1' \
   -o "$new_iso" \
   -G "$sys_area_file" \
   -B ... \
   "$new_files"

kfreebsd amd64 version 7.9.0

/home/93sam/xorriso -as mkisofs \
   -r -V 'Debian 7.9.0 f-amd64 1' \
   -o /org/.../debian-7.9.0-kfreebsd-amd64-NETINST-1.iso \
   -J -joliet-long \
   -b boot/grub/grub_eltorito \
   -c boot/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table \
   -cache-inodes \
   boot1 CD1

All options have been explained already. Not much change is needed.

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-7.9.0-kfreebsd-amd64-modified.iso

# Create the new ISO image
xorriso -as mkisofs \
   -r -V 'Debian 7.9.0 f-amd64 1' \
   -o "$new_iso" \
   -J -joliet-long \
   -b boot/grub/grub_eltorito \
   -c boot/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table \
   -cache-inodes \
   "$new_files"

hppa version 7.0

xorriso -as mkisofs \
   -r \
   -V 'Debian 7.0 hppa 1' \
   -o /srv/.../debian-7.0-hppa-NETINST-1.iso \
   -hppa-cmdline '0/vmlinux initrd=0/ramdisk panic_timeout=60 panic=-1 mirror/protocol=http preseed/url=http://parisc.osuosl.org/debian/preseed.cfg mirror/http/proxy__=http://proxy:8080' \
   -hppa-kernel-32 install/vmlinux-3.14-1-parisc \
   -hppa-kernel-64 install/vmlinux-3.14-1-parisc64-smp \
   -hppa-bootloader install/iplboot \
   -hppa-ramdisk install/initrd.gz \
   CD1

To our great luck, the argument of option -hppa-cmdline is fully opaque to the ISO production and the arguments of the other -hppa-* options are file paths inside the ISO image. So only the generic changes are needed:

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-7.0-hppa-modified-1.iso

# Create the new ISO image
xorriso -as mkisofs \
   -r \
   -V 'Debian 7.0 hppa 1' \
   -o "$new_iso" \
   -hppa-cmdline '0/vmlinux initrd=0/ramdisk panic_timeout=60 panic=-1 mirror/protocol=http preseed/url=http://parisc.osuosl.org/debian/preseed.cfg mirror/http/proxy__=http://proxy:8080' \
   -hppa-kernel-32 install/vmlinux-3.14-1-parisc \
   -hppa-kernel-64 install/vmlinux-3.14-1-parisc64-smp \
   -hppa-bootloader install/iplboot \
   -hppa-ramdisk install/initrd.gz \
   "$new_files"

amd64 release 5.0.4

/home/.../genisoimage \
   -r -V 'Debian 5.0.4 amd64 Bin-1' \
   -o /org/.../debian-504-amd64-NETINST-1.iso \
   -J -J -joliet-long -cache-inodes \
   -b isolinux/isolinux.bin \
   -c isolinux/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table \
   boot1 CD1

No booting from USB stick, no EFI, not much need for change.

new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-504-amd64-modified.iso

# Create the new ISO image
xorriso -as mkisofs \
   -r -V 'Debian 5.0.4 amd64 Bin-1' \
   -o "$new_iso" \
   -J -J -joliet-long -cache-inodes \
   -b isolinux/isolinux.bin \
   -c isolinux/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table \
   "$new_files"

amd64 Live DVD release 9.4.0

Here we have something rare in the world of bootable ISOs: Native xorriso commands rather than xorriso's mkisofs emulation. (No jigdo-related commands.)

xorriso \
   -outdev debian-live-9.4.0-amd64-gnome.iso \
   -volid d-live 9.4.0 gn amd64 \
   -padding 0 \
   -map /w/work/free/gnome/tmp/tmp8HCL24 / \
   -chmod 0755 / -- \
   -boot_image isolinux dir=/isolinux \
   -boot_image isolinux system_area=/usr/lib/ISOLINUX/isohdpfx.bin \
   -boot_image any next \
   -boot_image any efi_path=boot/grub/efi.img \
   -boot_image isolinux partition_entry=gpt_basdat

xorriso's own command set is described in man 1 xorriso. Online: https://www.gnu.org/software/xorriso/man_1_xorriso.html

An important difference between -outdev and -as mkisofs -o is that "-outdev" does not truncate the file "$new_iso" but would rather raise protest because of insufficient multi-session preprations:

xorriso : FAILURE : -indev differs from -outdev and -outdev media holds non-zero data

So we have to remove "$new_iso" before letting xorriso use it as output file.

Another difference is that xorriso by default calls fsync(2) on the output file after each 16 MiB of writing. This makes it lightweight on systems with few memory but also lets it appear much slower than in "-as mkisofs" mode. So if memory is plenty and the -outdev storage medium shall not be removed as soon as xorriso has ended, we may well disable this syncing by command -stdio_sync off.

Anticipating a change in Debian Live 9.5.0 we add command -compliance no_emul_toc which saves 64 KiB of result size at the cost of not recording multi-session history. This history is mostly of interest for incremental backups. It is disabled in the -as mkisofs emulation by default.

orig_iso="$HOME"/debian-live-9.4.0-amd64-gnome.iso
new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-live-9.4.0-amd64-modified.iso
mbr_template=isohdpfx.bin

# Extract MBR template file to disk
dd if="$orig_iso" bs=1 count=432 of="$mbr_template"

# Delete file "$new_iso" if it exists
test -e "$new_iso" && rm "$new_iso"

# Create the new ISO image
xorriso \
   -outdev "$new_iso" \
   -volid 'd-live 9.4.0 gn amd64' \
   -padding 0 \
   -compliance no_emul_toc \
   -map "$new_files" / \
   -chmod 0755 / -- \
   -boot_image isolinux dir=/isolinux \
   -boot_image isolinux system_area="$mbr_template" \
   -boot_image any next \
   -boot_image any efi_path=boot/grub/efi.img \
   -boot_image isolinux partition_entry=gpt_basdat \
   -stdio_sync off

What to do if no file /.disk/mkisofs exists

Up to some point release of Debian 9, the Debian Live CD ISOs did not get a file /.disk/mkisofs with their production shell command. The bootable ISOs of Debian derivates or of other Linux distros probably do not bear such info either.

In this case one may let xorriso analyze the boot equipment and make proposals. The younger the xorriso version, the better:

orig_iso=debian-live-8.4.0-i386-standard.iso

xorriso -report_about warning -indev "$orig_iso" -report_system_area as_mkisofs

reports on stdout

-V 'Debian jessie 20160402-22:24'
--modification-date='2016040221250200'
-isohybrid-mbr --interval:local_fs:0s-15s:zero_mbrpt:'debian-live-8.4.0-i386-standard.iso'
-partition_cyl_align on
-partition_offset 16
-partition_hd_cyl 64
-partition_sec_hd 32
-iso_mbr_part_type 0x17
-c '/isolinux/boot.cat'
-b '/isolinux/isolinux.bin'
-no-emul-boot
-boot-load-size 4
-boot-info-table

orig_iso=debian-live-8.4.0-i386-standard.iso
new_files="$HOME"/iso_unpacked_and_modified
new_iso="$HOME"/debian-live-8.4.0-i386-modified.iso
mbr_template=isohdpfx.bin

# Extract MBR template file to disk
dd if="$orig_iso" bs=1 count=432 of="$mbr_template"

# Create the new ISO image
xorriso -as mkisofs \
   -r -J --joliet-long \
   -V 'Debian jessie 20160402-22:24' \
   -o "$new_iso" \
   -isohybrid-mbr "$mbr_template" \
   -partition_offset 16 \
   -c isolinux/boot.cat \
   -b isolinux/isolinux.bin \
   -no-emul-boot -boot-load-size 4 -boot-info-table \
   "$new_files"

Methods to overwrite, delete, or add files

The Debian wiki shows various ways how to prepare the changes for writing them to the new ISO. xorriso offers its own ways, too.

Copy full ISO directory tree to writable hard disk tree

The above examples assume that the ISO content of directories and data files was extracted to a directory "$HOME"/iso_unpacked_and_modified and that any desired changes were made to this copy of the directory tree.

orig_iso="$HOME"/debian-9.3.0-amd64-netinst.iso
new_files="$HOME"/iso_unpacked_and_modified

# Unpack the ISO directory tree
xorriso -osirrox on -indev "$orig_iso" -extract / "$new_files"

# (Later delete the unpacked tree which has deletion-unfriendly permissions:)
# find "$new_files" -type d -exec chmod u+w '{}' ';'
# rm -rf "$new_files"

More such unpacking methods are shown in ManipulatingISOs#Extraction_of_Files_from_an_ISO.

Add overlay filesystem for doing changes

This method is shown in DebianInstaller/Modify/CD#Helper_scripts:_diunpk.2C_dipk:

...
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}
...

The aufs mount point "${di_rw}" then can fulfill the role of "$new_files" in the ISO repacking examples.

In xorriso override mounted ISO tree by particular changed files

In xorriso, the operation of scheduling files for being part of the ISO is called "mapping". Each time an input file or directory is given, the mapping is adjusted accordingly. It is well possible to overwrite all or parts of a previous mapping by a further subsequent input specification. (genisoimage crashes sometimes at such occasions.)

orig_iso="$HOME"/debian-9.3.0-amd64-netinst.iso
orig_mount=/mnt/iso
changed_isolinux="$HOME"/isolinux_dir_from_iso

# Mount original ISO read-only
sudo mount "$orig_iso" "$orig_mount"

# Copy out a few files which you want to change
cp -r /mnt/iso/isolinux "$changed_isolinux"

# Change or add files in "$changed_isolinux"

# Create the new ISO image from original read-only tree and changed files
xorriso -as mkisofs \
   ... specific options ... \
   "$orig_mount" \
   -graft-points \
   /isolinux="$changed_isolinux"

Option -graft-points enables the pathspec syntax target=origin by which we can choose a particular path in the ISO for the file or tree which gets mapped in. The pathspec /isolinux="$changed_isolinux" overrides conflicting parts of the previous mapping from "$orig_mount"/isolinux to /isolinux.

Mappings do not flatly replace each other but rather get merged on directory level. I.e. files do not get deleted from the mapping if they are missing in the origin of a subsequent pathspec. They can only get overwritten by another file of the same name. But xorriso's own command set may also delete files. For that, we would finally have to leave mkisofs emulation and apply commands like "-rm" or "-rm_r".

If we assume that "/isolinux/f10.txt" would have become obsolete and does not exist in "$changed_isolinux" any more, then it is still mapped by "$orig_mount". This mapping would then need to be deleted before ISO production begins:

xorriso -as mkisofs \
   ... specific options ... \
   "$orig_mount" \
   -graft-points \
   /isolinux="$changed_isolinux"
   -- \
   -rm /isolinux/f10.txt --

First we leave the emulation command "-as" by "--". Then we execute the command "-rm" which has a variable length parameter list. Therefore it needs to be terminated by another "--" argument. About xorriso's own command set see: https://www.gnu.org/software/xorriso/man_1_xorriso.html

But a use case for this manipulation would still have to be found.

In xorriso load ISO tree and write modified new ISO

This method uses xorriso's native command set rather than the emulation of mkisofs. It has the advantage that we do not have to take care of the specific boot options of the original ISO, because "-boot_image any replay" will do this for us.

orig_iso="$HOME"/debian-9.3.0-amd64-netinst.iso
new_iso="$HOME"/debian-9.3.0-amd64-modified.iso
changed_isolinux="$HOME"/isolinux_dir_from_iso

# Get the /isolinux directory from the original ISO
xorriso -osirrox on -indev "$orig_iso" -extract /isolinux "$changed_isolinux"

# Change or add files in "$changed_isolinux"

# If present: Remove an older version of "$new_iso"
test -e "$new_iso" && rm "$new_iso"

# Make new_iso from orig_iso and the changed files
xorriso \
   -indev "$orig_iso" \
   -outdev "$new_iso" \
   \
   -map "$changed_isolinux" /isolinux \
   -rm /isolinux/f10.txt -- \
   \
   -boot_image any replay \
   \
   -compliance no_emul_toc \
   -padding included

Note that the sequence of native xorriso commands matters: First aquire input and output, then manipulate the input by commands like -map or -rm, then replay the boot related commands learned from orig_iso, and make the output more similar to the output of the mkisofs emulation.

See for the native xorriso commands: https://www.gnu.org/software/xorriso/man_1_xorriso.html

See also: