Difference between revisions of "BuildISO"

From LFScript
m (Protected "BuildISO": Counter-productive edit warring: This is page represents a core feature of LFScript, and therefore I'd like to maintain it myself. ([edit=sysop] (indefinite) [move=sysop] (indefinite)))
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
<build>mkdir -p live/boot/{isolinux,$(uname -m)}
+
[[Category:Scripts]]
cp -v /usr/share/syslinux/isolinux.bin live/boot/isolinux
+
This page defines one of the core features of LFScript; The ability to create a [[Live CD]].
cp -v /boot/vmlinuz-*                  live/boot/$(uname -m)/vmlinuz
 
cp -v /boot/config-*                  live/boot/$(uname -m)/config</build>
 
  
<build>mkdir mnt
+
This page is set up in way so that it can be imported into LFScript automatically, just like the pages for the other [[Extra packages]] are. Al though you should be able to run these commands yourself, it is intended for them to be [[Creating a Live CD|executed by LFScript]].
mount -o loop,ro /sources/rootfs-$(uname -m).img mnt</build>
 
  
<build>FAIL="0"
+
<scriptname>buildiso</scriptname>
  
if [ ! -r mnt/usr/bin/$(uname -m)-*linux-gnu-gcc ]; then
+
== Sources ==
    echo -n "ERROR: Unable to verify image architecture; Your system image does"
+
* <sourcecode>file://rootfs-$(uname -m).img</sourcecode>
    echo " not contain the file '/usr/bin/$(uname -m)-*linux-gnu-gcc'"
 
    FAIL="1"
 
fi
 
  
if [ ! -d mnt/etc ]; then
+
'''Note:''' You obviously can not download this file, it [[Creating a Live CD|can only be created by you]].
    echo "ERROR: Your system image does not contain the directory '/etc'"
 
    FAIL="1"
 
fi
 
  
if [ ! -x mnt/sbin/init ]; then
+
== Dependencies ==
    echo "ERROR: Your system image does not contain the executable '/sbin/init'"
+
* <dependency value="buildiso-dep"></dependency>[[BuildISO Dependencies]]
    FAIL="1"
 
fi
 
  
if [ ! -e mnt/dev/console ]; then
+
== Installation ==
    echo "ERROR: Your system image does not contain the device '/dev/console'"
 
    FAIL="1"
 
fi
 
  
if [ "${FAIL}" == "1" ]; then
+
=== isolinux.cfg ===
    return 1
+
This creates a default configuration file for the [[Syslinux|isolinux]] boot loader. If you have created a [[BuildISO Customisation|custom configuration]], this file file be ignored:
fi</build>
 
  
 
<build>cat > isolinux.cfg << EOF
 
<build>cat > isolinux.cfg << EOF
Line 41: Line 26:
  
 
LABEL live
 
LABEL live
     MENU LABEL ^Boot the live system ($(uname -m))
+
     MENU LABEL ^Boot live ($(uname -m), default)
 
     MENU DEFAULT
 
     MENU DEFAULT
 
     KERNEL /boot/$(uname -m)/vmlinuz
 
     KERNEL /boot/$(uname -m)/vmlinuz
     APPEND initrd=/boot/$(uname -m)/initramfs.cpgz quiet
+
     APPEND initrd=/boot/$(uname -m)/initram.fs quiet
 +
 
 +
LABEL live_force_vga
 +
    MENU LABEL ^Boot live ($(uname -m), 1024x768)
 +
    KERNEL /boot/$(uname -m)/vmlinuz
 +
    APPEND initrd=/boot/$(uname -m)/initram.fs quiet vga=792
  
 
LABEL harddisk
 
LABEL harddisk
Line 52: Line 42:
 
EOF</build>
 
EOF</build>
  
<build>if [ -e "mnt/usr/share/live/medium" ]; then
+
=== Init script ===
    cp -vR mnt/usr/share/live/medium/* live
+
This creates the core initialisation script for the Live CD. It is this script that runs immediately after the Linux kernel has been loaded by the boot loader. It is responsible for locating and mounting the CD medium, mounting the root file system image and then give control to the Operating System inside that image:
fi</build>
 
 
 
<build>if [ ! -e "live/boot/isolinux/isolinux.cfg" ]; then
 
    cp -v /usr/share/syslinux/menu.c32 live/boot/isolinux
 
    cp -v isolinux.cfg                live/boot/isolinux
 
fi</build>
 
 
 
<build>dd if=/dev/random of=live/boot/$(uname -m)/id_label bs=1 count=64</build>
 
  
 
<build>cat > init.sh << "EndOfFile"
 
<build>cat > init.sh << "EndOfFile"
 
#!/bin/busybox sh
 
#!/bin/busybox sh
  
# Initramfs boot script 1.3 (2012-01-19)
+
# Initramfs boot script 1.4.0 (2017-02-26)
# Copyright (c) 2010-2012   Marcel van den Boer
+
# Copyright (c) 2010-2017   Marcel van den Boer
 
#
 
#
 
# Permission is hereby granted, free of charge, to any person obtaining a copy
 
# Permission is hereby granted, free of charge, to any person obtaining a copy
Line 131: Line 113:
 
cd /
 
cd /
  
#########################################
+
#############################################
}; unionMount() { # UNIONFS LIVECD MODE #
+
}; overlayMount() { # OVERLAYFS LIVECD MODE #
#########################################
+
#############################################
  
# A union mount takes one or more directories and combines them transparantly
+
# An overlay mount takes one or more directories and combines them transparantly
 
# in a third. This function creates a writable directory in memory (tmpfs) and
 
# in a third. This function creates a writable directory in memory (tmpfs) and
 
# uses it to overlay the read-only system image, resulting in a fully writable
 
# uses it to overlay the read-only system image, resulting in a fully writable
 
# root file system.
 
# root file system.
#
 
# The only downside to this method is that it requires a union type filesystem
 
# in the kernel, which can only be accomplished by patching the kernel as there
 
# is no such feature in a vanilla kernel.
 
  
 
mkdir -p /mnt/writable
 
mkdir -p /mnt/writable
 
mount -t tmpfs -o rw tmpfs /mnt/writable
 
mount -t tmpfs -o rw tmpfs /mnt/writable
 +
mkdir -p /mnt/writable/upper
 +
mkdir -p /mnt/writable/work
 +
 +
D_LOWER="/mnt/system"
 +
D_UPPER="/mnt/writable/upper"
 +
D_WORK="/mnt/writable/work"
 +
OVERLAYFSOPT="lowerdir=${D_LOWER},upperdir=${D_UPPER},workdir=${D_WORK}"
  
UNIONFSOPT="/mnt/writable=rw:/mnt/system=ro"
+
mount -t overlay overlay -o ${OVERLAYFSOPT} ${ROOT} 2> /dev/null || {
mount -t unionfs -o dirs=${UNIONFSOPT} unionfs ${ROOT} 2> /dev/null || {
 
  
     # If UnionFS fails, fall back to copy/bind mounting
+
     # If OverlayFS fails, fall back to copy/bind mounting
 
     copyBindMount
 
     copyBindMount
 
}
 
}
Line 159: Line 143:
  
 
# Make required applets easier to access
 
# Make required applets easier to access
for applet in clear cat chmod cp ls mkdir mknod mount umount switch_root; do
+
for applet in cat chmod cp cut grep ls mkdir mknod mount umount switch_root; do
  /bin/busybox ln /bin/busybox /bin/${applet}
+
    /bin/busybox ln /bin/busybox /bin/${applet}
 
done
 
done
  
Line 171: Line 155:
 
# Note: /dev/console will already be available in the ramfs
 
# Note: /dev/console will already be available in the ramfs
 
mknod /dev/null c  1  3
 
mknod /dev/null c  1  3
mknod /dev/scd0 b 11  0  # +----
+
 
 +
mknod /dev/scd0 b 11  0  # +--------
 
mknod /dev/scd1 b 11  1  # |
 
mknod /dev/scd1 b 11  1  # |
mknod /dev/scd2 b 11  2  # | <----
+
mknod /dev/scd2 b 11  2  # |
mknod /dev/scd3 b 11 3 # |   Devices which could contain the
+
mknod /dev/scd3 b 11  3  # |
mknod /dev/hda  3  0 # |   boot medium...
+
                        # |
mknod /dev/hdb 3 64 # |
+
mknod /dev/sda  b  8  0  # |
mknod /dev/hdc b 22 0 # |
+
mknod /dev/sda1 b  8  1  # |
mknod /dev/hdd b 22 64 # +----
+
mknod /dev/sda2 b  8  2  # |
 +
mknod /dev/sda3 b  8  3  # |
 +
mknod /dev/sda4 b  8  4  # |
 +
                        # |
 +
mknod /dev/sdb  b  8 16  # |    <----
 +
mknod /dev/sdb1 8 17 # |       Devices which could be or contain the
 +
mknod /dev/sdb2 8 18 # |       boot medium...
 +
mknod /dev/sdb3 b  8 19 # |
 +
mknod /dev/sdb4 b  8 20  # |
 +
                        # |
 +
mknod /dev/sdc  b  8 32  # |
 +
mknod /dev/sdc1 b  8 33  # |
 +
mknod /dev/sdc2 b  8 34  # |
 +
mknod /dev/sdc3 8 35 # |
 +
mknod /dev/sdc4 b  8 36 # |
 +
                        # |
 +
mknod /dev/sdd  b  8 48  # |
 +
mknod /dev/sdd1 8 49  # |
 +
mknod /dev/sdd2 b  8 50 # |
 +
mknod /dev/sdd3 b  8 51 # |
 +
mknod /dev/sdd4 b 8 52 # +--------
  
 
# Create mount points for filesystems
 
# Create mount points for filesystems
Line 184: Line 189:
 
mkdir -p /mnt/system
 
mkdir -p /mnt/system
 
mkdir -p /mnt/rootfs
 
mkdir -p /mnt/rootfs
 +
 +
# Mount the /proc filesystem (enables filesystem detection for 'mount')
 +
mkdir /proc
 +
mount -t proc proc /proc
  
 
# Search for, and mount the boot medium
 
# Search for, and mount the boot medium
 
LABEL="$(cat /boot/id_label)"
 
LABEL="$(cat /boot/id_label)"
for device in scd0 scd1 scd2 scd3 hda hdb hdc hdd; do
+
for device in $(ls /dev); do
     mount -t iso9660 -o ro /dev/${device} /mnt/medium 2> /dev/null &&
+
    [ "${device}" == "console" ] && continue
 +
    [ "${device}" == "null"    ] && continue
 +
 
 +
     mount -o ro /dev/${device} /mnt/medium 2> /dev/null && \
 
     if [ "$(cat /mnt/medium/boot/${ARCH}/id_label)" != "${LABEL}" ]; then
 
     if [ "$(cat /mnt/medium/boot/${ARCH}/id_label)" != "${LABEL}" ]; then
 
         umount /mnt/medium
 
         umount /mnt/medium
Line 198: Line 210:
  
 
if [ "${DEVICE}" == "" ]; then
 
if [ "${DEVICE}" == "" ]; then
     echo "FATAL: Boot medium not found."
+
     echo "STOP: Boot medium not found."
     exit 1
+
     exec /bin/busybox sh
 
fi
 
fi
  
 
# Mount the system image
 
# Mount the system image
mount -t squashfs -o ro,loop /mnt/medium/boot/${ARCH}/root.sqfs /mnt/system || {
+
mount -t squashfs -o ro,loop /mnt/medium/boot/${ARCH}/root.sfs /mnt/system || {
     echo "FATAL: Boot medium found, but system image is missing."
+
     if [ -r /mnt/medium/boot/${ARCH}/root.sfs ]; then
     exit 1
+
        echo "STOP: Unable to mount system image. The kernel probably lacks"
 +
        echo "      SquashFS support. You may need to recompile it."
 +
    else
 +
        echo "STOP: Unable to mount system image. It seems to be missing."
 +
     fi
 +
 
 +
    exec /bin/busybox sh
 
}
 
}
  
Line 212: Line 230:
  
 
# Select LiveCD mode
 
# Select LiveCD mode
unionMount # Might fall back to copyBindMount
+
overlayMount # Might fall back to copyBindMount
  
 
# Move current mounts to directories accessible in the new root
 
# Move current mounts to directories accessible in the new root
Line 224: Line 242:
 
cd /
 
cd /
  
# Eject the medium before a shutdown
+
# If the boot medium is a CD, eject it on shutdown
 +
[ "$(mount | grep iso9660)" != "" ] && \
 
cat > ${ROOT}/etc/rc.d/rc0.d/S98eject << EOF
 
cat > ${ROOT}/etc/rc.d/rc0.d/S98eject << EOF
 
#!/bin/sh
 
#!/bin/sh
Line 240: Line 259:
 
EOF
 
EOF
 
chmod +x ${ROOT}/etc/rc.d/rc0.d/S98eject
 
chmod +x ${ROOT}/etc/rc.d/rc0.d/S98eject
 
# Overwrite /etc/fstab
 
# FIXME: This file is specific to LFS-SVN-20120116+, could it be made portable?
 
cat > ${ROOT}/etc/fstab << "EOF"
 
# Begin /etc/fstab
 
 
# file system  mount-point  type    options            dump  fsck
 
#                                                              order
 
 
rootfs        /            rootfs  defaults            0    0
 
proc          /proc        proc    nosuid,noexec,nodev 0    0
 
sysfs          /sys        sysfs    nosuid,noexec,nodev 0    0
 
devpts        /dev/pts    devpts  gid=4,mode=620      0    0
 
tmpfs          /run        tmpfs    defaults            0    0
 
devtmpfs      /dev        devtmpfs mode=0755,nosuid    0    0
 
 
# End /etc/fstab
 
EOF
 
  
 
# Run secondary initialization (if the system provides it)
 
# Run secondary initialization (if the system provides it)
Line 263: Line 264:
 
     . ${ROOT}/usr/share/live/sec_init.sh
 
     . ${ROOT}/usr/share/live/sec_init.sh
 
fi
 
fi
 +
 +
# Clean up
 +
umount /proc
  
 
# Switch to the new root and launch INIT!
 
# Switch to the new root and launch INIT!
Line 268: Line 272:
  
 
EndOfFile</build>
 
EndOfFile</build>
 +
 +
=== Identifying the boot medium ===
 +
The initialisation script must have some way of identifying the medium from which it has been started.
 +
 +
The following command saves a 512-bit random number, to be used as a unique identifier. It will later be copied to the <code>initramfs</code> created after this and to the ISO file system itself:
 +
<build>dd if=/dev/random of=id_label bs=1 count=64</build>
 +
 +
=== The initramfs ===
 +
The initialisation script relies on certain system commands to be available during boot. However, because the root file system is not available yet (the very purpose of <code>init.sh</code> is to set it up), these commands must be available in advance.
 +
 +
Luckily, Linux supports the use of an initial RAM disk or <code>initramfs</code>. With this, we can provide the kernel with a file which has a minimal set of utilities (and <code>init.sh</code>) embedded. The kernel will automatically copy the contents of the file to a temporary root filesystem, and then turn over control to <code>/init</code> (<code>init.sh</code>).
 +
 +
In stead of copying the required utilities from the system image, many Live CD's opt to use a statically linked [[BusyBox]] in stead. This makes the <code>initramfs</code> not dependent on any libraries in the system image. And because <code>busybox</code> is only a single binary file, it makes the creation of the initial file system easy:
  
 
<build>mkdir -pv mnt_init/{bin,boot}
 
<build>mkdir -pv mnt_init/{bin,boot}
cp -v live/boot/$(uname -m)/id_label mnt_init/boot
+
cp -v id_label mnt_init/boot
 
cp -v /bin/busybox mnt_init/bin
 
cp -v /bin/busybox mnt_init/bin
cat init.sh > mnt_init/init
+
mv init.sh mnt_init/init
 
sed -i "s/<ARCH>/$(uname -m)/g" mnt_init/init
 
sed -i "s/<ARCH>/$(uname -m)/g" mnt_init/init
 
chmod +x mnt_init/init</build>
 
chmod +x mnt_init/init</build>
  
<build>cd mnt_init
+
Once populated, compress the initial file system. The Linux kernel expects an <code>cpio</code> file, optionally compressed with <code>gzip</code>:
 +
<build>pushd mnt_init
 
find . | /bin/busybox cpio -o -H newc -F ../initramfs.cpio
 
find . | /bin/busybox cpio -o -H newc -F ../initramfs.cpio
cd ../
+
popd
 
gzip -9 initramfs.cpio
 
gzip -9 initramfs.cpio
mv -v initramfs.cpio.gz live/boot/$(uname -m)/initramfs.cpgz
+
rm&nbsp;-rf mnt_init</build>
rm -rf mnt_init</build>
+
 
 +
=== Mount, verify and compress the system image ===
 +
In order to compress the system image, it must be mounted (read-only) first:
 +
 
 +
<build>mkdir mnt
 +
mount -o loop,ro rootfs-$(uname -m).img mnt</build>
 +
 
 +
This next block performs some verification on the system image to ensure it can be used as a root file system. If you are executing the commands on this page manually, you should skip this block:
 +
<build>FAIL="0"
 +
 
 +
if [ ! -r mnt/usr/bin/$(uname -m)-*linux-gnu-gcc ]; then
 +
    echo -n "ERROR: Unable to verify image architecture; Your system image does"
 +
    echo " not contain the file '/usr/bin/$(uname -m)-*linux-gnu-gcc'"
 +
    FAIL="1"
 +
fi
 +
 
 +
if [ ! -d mnt/etc ]; then
 +
    echo "ERROR: Your system image does not contain the directory '/etc'"
 +
    FAIL="1"
 +
fi
 +
 
 +
if [ ! -x mnt/sbin/init ]; then
 +
    echo "ERROR: Your system image does not contain the executable '/sbin/init'"
 +
    FAIL="1"
 +
fi
 +
 
 +
if [ ! -e mnt/dev/console ]; then
 +
    echo "ERROR: Your system image does not contain the device '/dev/console'"
 +
    FAIL="1"
 +
fi
 +
 
 +
if [ "${FAIL}" == "1" ]; then
 +
    return 1
 +
fi</build>
 +
 
 +
Then, proceed to compress the system image:
 +
<build>mksquashfs mnt root.sfs -comp xz</build>
 +
 
 +
=== Create the ISO image ===
 +
Now that all components are ready, it's time to package it all up in a single ISO file.
 +
 
 +
First, copy the boot loader and Linux kernel to the CD file system:
 +
<build>mkdir -p live/boot/{isolinux,$(uname -m)}
 +
cp -v /usr/share/syslinux/isolinux.bin live/boot/isolinux
 +
cp -v /usr/share/syslinux/ldlinux.c32 live/boot/isolinux
 +
 
 +
cp -v mnt/boot/vmlinuz-* live/boot/$(uname -m)/vmlinuz
 +
cp -v mnt/boot/config-*  live/boot/$(uname -m)/config</build>
 +
 
 +
Copy all files from the root file system image that have been prepared to [[BuildISO Customisation|customize the Live CD]]:
 +
<build>if [ -e "mnt/usr/share/live/medium" ]; then
 +
    cp -vR mnt/usr/share/live/medium/* live
 +
fi</build>
 +
 
 +
Now, unmount the root file system image as it is no longer needed:
 +
<build>umount mnt
 +
rm&nbsp;-rf mnt</build>
 +
 
 +
Unless a customised configuration has been copied a moment ago, use the boot loader configuration created when we started:
 +
<build>if [ ! -e "live/boot/isolinux/isolinux.cfg" ]; then
 +
    cp -v /usr/share/syslinux/menu.c32    live/boot/isolinux
 +
    cp -v /usr/share/syslinux/libutil.c32 live/boot/isolinux
 +
    mv -v isolinux.cfg                    live/boot/isolinux
 +
else
 +
    rm&nbsp;-rf isolinux.cfg
 +
fi</build>
 +
 
 +
Move the various components to the CD file system:
 +
<build>mv -v root.sfs live/boot/$(uname -m)
 +
mv -v id_label live/boot/$(uname -m)
 +
mv -v initramfs.cpio.gz live/boot/$(uname -m)/initram.fs</build>
  
<build>mksquashfs mnt live/boot/$(uname -m)/root.sqfs -comp xz
+
Write out the final ISO:
umount ./mnt
+
<build>xorrisofs -o system-$(uname -m).iso    \
rm -rf mnt</build>
+
          -b boot/isolinux/isolinux.bin \
 +
          -c boot.cat                  \
 +
          -no-emul-boot                \
 +
          -boot-load-size 4            \
 +
          -boot-info-table              \
 +
          -joliet -l -R                \
 +
          live
 +
rm&nbsp;-rf live</build>
  
<build>mkisofs -o /sources/buildmgr/packages/system-$(uname -m).iso \
+
Move the ISO image to LFScript's <code>newpackages</code> directory (you can skip this if you are running these commands manually):
        -b boot/isolinux/isolinux.bin \
+
<build>mkdir -p /sources/buildmgr/newpackages
        -c boot.cat                  \
+
mv -v system-$(uname -m).iso /sources/buildmgr/newpackages</build>
        -no-emul-boot                \
 
        -boot-load-size 4            \
 
        -boot-info-table              \
 
        -joliet -l -R                \
 
        live</build>
 
  
<build>rm -rf live
+
Done!
rm -rf ${FAKEROOT}/*</build>
 

Latest revision as of 23:41, 15 April 2017

This page defines one of the core features of LFScript; The ability to create a Live CD.

This page is set up in way so that it can be imported into LFScript automatically, just like the pages for the other Extra packages are. Al though you should be able to run these commands yourself, it is intended for them to be executed by LFScript.

LFScript can find this software under the name buildiso.

Sources

Note: You obviously can not download this file, it can only be created by you.

Dependencies

Installation

isolinux.cfg

This creates a default configuration file for the isolinux boot loader. If you have created a custom configuration, this file file be ignored:

cat > isolinux.cfg << EOF
DEFAULT menu.c32
PROMPT 0
MENU TITLE Select an option...
TIMEOUT 300

LABEL live
    MENU LABEL ^Boot live ($(uname -m), default)
    MENU DEFAULT
    KERNEL /boot/$(uname -m)/vmlinuz
    APPEND initrd=/boot/$(uname -m)/initram.fs quiet

LABEL live_force_vga
    MENU LABEL ^Boot live ($(uname -m), 1024x768)
    KERNEL /boot/$(uname -m)/vmlinuz
    APPEND initrd=/boot/$(uname -m)/initram.fs quiet vga=792

LABEL harddisk
    MENU LABEL Boot from first ^Hard disk
    LOCALBOOT 0x80

EOF

Init script

This creates the core initialisation script for the Live CD. It is this script that runs immediately after the Linux kernel has been loaded by the boot loader. It is responsible for locating and mounting the CD medium, mounting the root file system image and then give control to the Operating System inside that image:

cat > init.sh << "EndOfFile"
#!/bin/busybox sh

# Initramfs boot script 1.4.0 (2017-02-26)
# Copyright (c) 2010-2017   Marcel van den Boer
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

# FS layout at the start of this script:
# - /boot/id_label
# - /bin/busybox
# - /dev/console (created by kernel)
# - /init (this file)

set -e

ARCH="<ARCH>"

###########################################
copyBindMount() { # COPY/BIND LIVECD MODE #
###########################################

# This function bind-mounts directories which are designed to be capable of
# read-only access and copies the remaining directories to a tmpfs.
#
# The downside of this method is that the resulting root filesystem is not
# fully writable. So, for example, installation of new programs will not be
# possible.
#
# However, this function can be used without any modification to the kernel and
# is therefore perfect for use as a fallback if other options are not available.

# Mount a tmpfs where the new rootfs will be.
mount -t tmpfs tmpfs ${ROOT} # Allows remounting root in the bootscripts

# Bind mount read-only filesystems, copy the rest
cd /mnt/system
for dir in $(ls -1); do
    case ${dir} in
        lost+found)
            ;;
        bin | boot | lib | opt | sbin | usr)
            mkdir ${ROOT}/${dir}
            mount --bind ${dir} ${ROOT}/${dir}
            ;;
        *)
            cp -R ${dir} ${ROOT}
            ;;
    esac
done
cd /

#############################################
}; overlayMount() { # OVERLAYFS LIVECD MODE #
#############################################

# An overlay mount takes one or more directories and combines them transparantly
# in a third. This function creates a writable directory in memory (tmpfs) and
# uses it to overlay the read-only system image, resulting in a fully writable
# root file system.

mkdir -p /mnt/writable
mount -t tmpfs -o rw tmpfs /mnt/writable
mkdir -p /mnt/writable/upper
mkdir -p /mnt/writable/work

D_LOWER="/mnt/system"
D_UPPER="/mnt/writable/upper"
D_WORK="/mnt/writable/work"
OVERLAYFSOPT="lowerdir=${D_LOWER},upperdir=${D_UPPER},workdir=${D_WORK}"

mount -t overlay overlay -o ${OVERLAYFSOPT} ${ROOT} 2> /dev/null || {

    # If OverlayFS fails, fall back to copy/bind mounting
    copyBindMount
}

######################
} # END OF FUNCTIONS #
######################

# Make required applets easier to access
for applet in cat chmod cp cut grep ls mkdir mknod mount umount switch_root; do
    /bin/busybox ln /bin/busybox /bin/${applet}
done

# Clear the screen
#clear # Don't! This will clear the Linux boot logo when using a framebuffer.
       # If you want to clear the screen on boot add the "clear" command to
       # '/usr/share/live/sec_init.sh' in the system image.

# Create device nodes required to run this script
# Note: /dev/console will already be available in the ramfs
mknod /dev/null c  1  3

mknod /dev/scd0 b 11  0  # +--------
mknod /dev/scd1 b 11  1  # |
mknod /dev/scd2 b 11  2  # |
mknod /dev/scd3 b 11  3  # |
                         # |
mknod /dev/sda  b  8  0  # |
mknod /dev/sda1 b  8  1  # |
mknod /dev/sda2 b  8  2  # |
mknod /dev/sda3 b  8  3  # |
mknod /dev/sda4 b  8  4  # |
                         # |
mknod /dev/sdb  b  8 16  # |    <----
mknod /dev/sdb1 b  8 17  # |        Devices which could be or contain the
mknod /dev/sdb2 b  8 18  # |        boot medium...
mknod /dev/sdb3 b  8 19  # |
mknod /dev/sdb4 b  8 20  # |
                         # |
mknod /dev/sdc  b  8 32  # |
mknod /dev/sdc1 b  8 33  # |
mknod /dev/sdc2 b  8 34  # |
mknod /dev/sdc3 b  8 35  # |
mknod /dev/sdc4 b  8 36  # |
                         # |
mknod /dev/sdd  b  8 48  # |
mknod /dev/sdd1 b  8 49  # |
mknod /dev/sdd2 b  8 50  # |
mknod /dev/sdd3 b  8 51  # |
mknod /dev/sdd4 b  8 52  # +--------

# Create mount points for filesystems
mkdir -p /mnt/medium
mkdir -p /mnt/system
mkdir -p /mnt/rootfs

# Mount the /proc filesystem (enables filesystem detection for 'mount')
mkdir /proc
mount -t proc proc /proc

# Search for, and mount the boot medium
LABEL="$(cat /boot/id_label)"
for device in $(ls /dev); do
    [ "${device}" == "console" ] && continue
    [ "${device}" == "null"    ] && continue

    mount -o ro /dev/${device} /mnt/medium 2> /dev/null && \
    if [ "$(cat /mnt/medium/boot/${ARCH}/id_label)" != "${LABEL}" ]; then
        umount /mnt/medium
    else
        DEVICE="${device}"
        break
    fi
done

if [ "${DEVICE}" == "" ]; then
    echo "STOP: Boot medium not found."
    exec /bin/busybox sh
fi

# Mount the system image
mount -t squashfs -o ro,loop /mnt/medium/boot/${ARCH}/root.sfs /mnt/system || {
    if [ -r /mnt/medium/boot/${ARCH}/root.sfs ]; then
        echo "STOP: Unable to mount system image. The kernel probably lacks"
        echo "      SquashFS support. You may need to recompile it."
    else
        echo "STOP: Unable to mount system image. It seems to be missing."
    fi

    exec /bin/busybox sh
}

# Define where the new root filesystem will be
ROOT="/mnt/rootfs" # Also needed for /usr/share/live/sec_init.sh

# Select LiveCD mode
overlayMount # Might fall back to copyBindMount

# Move current mounts to directories accessible in the new root
cd /mnt
for dir in $(ls -1); do
    if [ "${dir}" != "rootfs" ]; then
        mkdir -p ${ROOT}/mnt/.boot/${dir}
        mount --move /mnt/${dir} ${ROOT}/mnt/.boot/${dir}
    fi
done
cd /

# If the boot medium is a CD, eject it on shutdown
[ "$(mount | grep iso9660)" != "" ] && \
cat > ${ROOT}/etc/rc.d/rc0.d/S98eject << EOF
#!/bin/sh

if [ -x /usr/bin/cdrecord ]; then
    /usr/bin/cdrecord -eject dev=/dev/${DEVICE} &> /dev/null

    echo -e "\033[31m"
    echo -en "Remove the boot medium, close the tray (if any), "
    echo -en "then press ENTER to power off."
    echo -e "\033[0m"

    read
fi
EOF
chmod +x ${ROOT}/etc/rc.d/rc0.d/S98eject

# Run secondary initialization (if the system provides it)
if [ -x ${ROOT}/usr/share/live/sec_init.sh ]; then
    . ${ROOT}/usr/share/live/sec_init.sh
fi

# Clean up
umount /proc

# Switch to the new root and launch INIT!
exec switch_root -c /dev/console ${ROOT} /sbin/init

EndOfFile

Identifying the boot medium

The initialisation script must have some way of identifying the medium from which it has been started.

The following command saves a 512-bit random number, to be used as a unique identifier. It will later be copied to the initramfs created after this and to the ISO file system itself:

dd if=/dev/random of=id_label bs=1 count=64

The initramfs

The initialisation script relies on certain system commands to be available during boot. However, because the root file system is not available yet (the very purpose of init.sh is to set it up), these commands must be available in advance.

Luckily, Linux supports the use of an initial RAM disk or initramfs. With this, we can provide the kernel with a file which has a minimal set of utilities (and init.sh) embedded. The kernel will automatically copy the contents of the file to a temporary root filesystem, and then turn over control to /init (init.sh).

In stead of copying the required utilities from the system image, many Live CD's opt to use a statically linked BusyBox in stead. This makes the initramfs not dependent on any libraries in the system image. And because busybox is only a single binary file, it makes the creation of the initial file system easy:

mkdir -pv mnt_init/{bin,boot}
cp -v id_label mnt_init/boot
cp -v /bin/busybox mnt_init/bin
mv init.sh mnt_init/init
sed -i "s/<ARCH>/$(uname -m)/g" mnt_init/init
chmod +x mnt_init/init

Once populated, compress the initial file system. The Linux kernel expects an cpio file, optionally compressed with gzip:

pushd mnt_init
find . | /bin/busybox cpio -o -H newc -F ../initramfs.cpio
popd
gzip -9 initramfs.cpio
rm -rf mnt_init

Mount, verify and compress the system image

In order to compress the system image, it must be mounted (read-only) first:

mkdir mnt
mount -o loop,ro rootfs-$(uname -m).img mnt

This next block performs some verification on the system image to ensure it can be used as a root file system. If you are executing the commands on this page manually, you should skip this block:

FAIL="0"

if [ ! -r mnt/usr/bin/$(uname -m)-*linux-gnu-gcc ]; then
    echo -n "ERROR: Unable to verify image architecture; Your system image does"
    echo " not contain the file '/usr/bin/$(uname -m)-*linux-gnu-gcc'"
    FAIL="1"
fi

if [ ! -d mnt/etc ]; then
    echo "ERROR: Your system image does not contain the directory '/etc'"
    FAIL="1"
fi

if [ ! -x mnt/sbin/init ]; then
    echo "ERROR: Your system image does not contain the executable '/sbin/init'"
    FAIL="1"
fi

if [ ! -e mnt/dev/console ]; then
    echo "ERROR: Your system image does not contain the device '/dev/console'"
    FAIL="1"
fi

if [ "${FAIL}" == "1" ]; then
    return 1
fi

Then, proceed to compress the system image:

mksquashfs mnt root.sfs -comp xz

Create the ISO image

Now that all components are ready, it's time to package it all up in a single ISO file.

First, copy the boot loader and Linux kernel to the CD file system:

mkdir -p live/boot/{isolinux,$(uname -m)}
cp -v /usr/share/syslinux/isolinux.bin live/boot/isolinux
cp -v /usr/share/syslinux/ldlinux.c32 live/boot/isolinux

cp -v mnt/boot/vmlinuz-* live/boot/$(uname -m)/vmlinuz
cp -v mnt/boot/config-*  live/boot/$(uname -m)/config

Copy all files from the root file system image that have been prepared to customize the Live CD:

if [ -e "mnt/usr/share/live/medium" ]; then
    cp -vR mnt/usr/share/live/medium/* live
fi

Now, unmount the root file system image as it is no longer needed:

umount mnt
rm -rf mnt

Unless a customised configuration has been copied a moment ago, use the boot loader configuration created when we started:

if [ ! -e "live/boot/isolinux/isolinux.cfg" ]; then
    cp -v /usr/share/syslinux/menu.c32    live/boot/isolinux
    cp -v /usr/share/syslinux/libutil.c32 live/boot/isolinux
    mv -v isolinux.cfg                    live/boot/isolinux
else
    rm -rf isolinux.cfg
fi

Move the various components to the CD file system:

mv -v root.sfs live/boot/$(uname -m)
mv -v id_label live/boot/$(uname -m)
mv -v initramfs.cpio.gz live/boot/$(uname -m)/initram.fs

Write out the final ISO:

xorrisofs -o system-$(uname -m).iso     \
          -b boot/isolinux/isolinux.bin \
          -c boot.cat                   \
          -no-emul-boot                 \
          -boot-load-size 4             \
          -boot-info-table              \
          -joliet -l -R                 \
          live
rm -rf live

Move the ISO image to LFScript's newpackages directory (you can skip this if you are running these commands manually):

mkdir -p /sources/buildmgr/newpackages
mv -v system-$(uname -m).iso /sources/buildmgr/newpackages

Done!