Major reorganization - initramfs structure is now externalized in separate folder structure. Added support for overlay from folders and disk images. Overlay structure has been externalized in separate folder. Added support to use preconfigured ".config" files for BusyBox and kernel.

This commit is contained in:
Ivan Davidov 2016-04-16 21:23:20 +03:00
parent 2bb6218b8d
commit 05b62fd32e
15 changed files with 359 additions and 242 deletions

View File

@ -24,3 +24,31 @@ BUSYBOX_SOURCE_URL=http://busybox.net/downloads/busybox-1.24.2.tar.bz2
#
SYSLINUX_SOURCE_URL=https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.xz
### ### ###
### ### ###
### ### ###
# Use predefined '.config' file when building the kernel. This overrides the config generation
# in '02_build_kernel.sh' and uses the config file provided in this parameter. You can comment,
# leave undefined or use nonexisting file in order to disable the property and rely on automatic
# config generation.
#
# KERNEL_CONFIG_FILE=kernel.config
# Use predefined '.config' file when building BusyBox. This overrides the config generation in
# '07_build_busybox.sh' and uses the config file provided in this parameter. Leave undefined or
# comment the property in order to disable it and rely on automatic config generation.
#
# BUSYBOX_CONFIG_FILE=busybox.config
# Define the overlay type to use. Possible values are 'sparse' and 'folder'. You can use any other
# value, no value, or comment the property in order to disable it.
#
# sparse - use sparse file with hardcoded maximal size of 1MB (see 11_generate_iso.sh). This option
# requires root permissions. The generated ISO image is larger.
#
# folder - use normal folder structure. Currently this doesn't work if the file system is 'vfat'
# because FAT requires special handling, e.g. POSIX overlay (http://sf.net/p/posixovl).
#
OVERLAY_TYPE=sparse

View File

@ -1,23 +1,35 @@
#!/bin/sh
SRC_DIR=$(pwd)
cd work/kernel
# Change to the first directory ls finds, e.g. 'linux-4.4.6'.
cd $(ls -d *)
# Cleans up the kernel sources, including configuration files.
make mrproper
# make mrproper
# Create default configuration file for the kernel.
make defconfig
# Read the 'KERNEL_CONFIG_FILE' property from '.config'
KERNEL_CONFIG_FILE="$SRC_DIR/$(grep -i KERNEL_CONFIG_FILE $SRC_DIR/.config | cut -f2 -d'=')"
# Changes the name of the system to 'minimal'.
sed -i "s/.*CONFIG_DEFAULT_HOSTNAME.*/CONFIG_DEFAULT_HOSTNAME=\"minimal\"/" .config
if [ -f $KERNEL_CONFIG_FILE ] ; then
# Use predefined configuration file for the kernel.
cp $KERNEL_CONFIG_FILE .config
else
# Create default configuration file for the kernel.
make defconfig
# Enable overlay support, e.g. merge ro and rw directories.
sed -i "s/.*CONFIG_OVERLAY_FS.*/CONFIG_OVERLAY_FS=y/" .config
# Changes the name of the system to 'minimal'.
sed -i "s/.*CONFIG_DEFAULT_HOSTNAME.*/CONFIG_DEFAULT_HOSTNAME=\"minimal\"/" .config
# Compile the kernel with optimization for "parallel jobs" = "number of processors".
# Enable overlay support, e.g. merge ro and rw directories.
sed -i "s/.*CONFIG_OVERLAY_FS.*/CONFIG_OVERLAY_FS=y/" .config
fi
exit 0
# Compile the kernel with optimization for 'parallel jobs' = 'number of processors'.
# Good explanation of the different kernels:
# http://unix.stackexchange.com/questions/5518/what-is-the-difference-between-the-following-kernel-makefile-terms-vmlinux-vmlinux
make bzImage -j $(grep ^processor /proc/cpuinfo | wc -l)

View File

@ -1,5 +1,7 @@
#!/bin/sh
SRC_DIR=$(pwd)
# Find the glibc installation area.
cd work/glibc
cd $(ls -d *)
@ -13,10 +15,25 @@ cd work/busybox
cd $(ls -d *)
# Remove previously generated artifacts.
make distclean
# make distclean
# Create default configuration file.
make defconfig
# Read the 'BUSYBOX_CONFIG_FILE' property from '.config'
BUSYBOX_CONFIG_FILE="$SRC_DIR/$(grep -iBUSYBOX_CONFIG_FILE $SRC_DIR/.config | cut -f2 -d'=')"
if [ -f $BUSYBOX_CONFIG_FILE ] ; then
# Use predefined configuration file for Busybox.
cp $BUSYBOX_CONFIG_FILE .config
else
# Create default configuration file.
make defconfig
# The 'inetd' applet fails to compile because we use the glibc installation area as
# main pointer to the kernel headers (see 05_prepare_glibc.sh) and some headers are
# not resolved. The easiest solution is to ignore this particular applet.
sed -i "s/.*CONFIG_INETD.*/CONFIG_INETD=n/" .config
fi
exit 0
# This variable holds the full path to the glibc installation area as quoted string.
# All back slashes are escaped (/ => \/) in order to keep the 'sed' command stable.
@ -25,11 +42,6 @@ GLIBC_INSTALLED_ESCAPED=$(echo \"$GLIBC_INSTALLED\" | sed 's/\//\\\//g')
# Now we tell BusyBox to use the glibc installation area.
sed -i "s/.*CONFIG_SYSROOT.*/CONFIG_SYSROOT=$GLIBC_INSTALLED_ESCAPED/" .config
# The 'inetd' applet fails to compile because we use the glibc installation area as
# main pointer to the kernel headers (see 05_prepare_glibc.sh) and some headers are
# not resolved. The easiest solution is to ignore this particular applet.
sed -i "s/.*CONFIG_INETD.*/CONFIG_INETD=n/" .config
# Compile busybox with optimization for "parallel jobs" = "number of processors".
make busybox -j $(grep ^processor /proc/cpuinfo | wc -l)

View File

@ -18,234 +18,26 @@ cd busybox
cd $(ls -d *)
# Copy all BusyBox generated stuff to the location of our 'initramfs' folder.
cp -R _install ../../rootfs
cp -r _install ../../rootfs
# Copy all rootfs stuff to the location of our 'initramfs' folder.
cp -r ../../../08_generate_rootfs/* ../../rootfs
cd ../../rootfs
# Remove 'linuxrc' which is used when we boot in 'RAM disk' mode.
rm -f linuxrc
# Create the missing root FS folders.
mkdir etc
mkdir dev
mkdir lib
mkdir mnt
mkdir proc
mkdir root
mkdir src
mkdir sys
mkdir tmp
cd etc
# The script '/etc/prepare.sh' is automatically executed as part of the '/init'
# process. We suppress most kernel messages and mount all cryitical file systems.
cat > prepare.sh << EOF
#!/bin/sh
dmesg -n 1
mount -t devtmpfs none /dev
mount -t proc none /proc
mount -t tmpfs none /tmp -o mode=1777
mount -t sysfs none /sys
EOF
chmod +x prepare.sh
# The script '/etc/switch.sh' is automatically executed as part of the '/init'
# process. We overlay the '/minimal/rootfs' folder from the boot media, copy all
# files and folders to new mountpoint and then execute the command 'switch_root'.
cat > switch.sh << EOF
#!/bin/sh
# Create the new mountpoint in RAM.
mount -t tmpfs none /mnt
# Create folders for all critical file systems.
echo "Create folders for all critical file systems..."
mkdir /mnt/dev
mkdir /mnt/sys
mkdir /mnt/proc
mkdir /mnt/tmp
echo "...done."
# Copy root folders in the new mountpoint.
echo "Copying the root file system to /mnt..."
cp -a bin etc lib lib64 root sbin src usr /mnt
echo "...done."
mkdir /tmp/mnt
echo "Searching available devices for /minimal/rootfs folder..."
for DEVICE in /dev/* ; do
DEV=\$(echo "\${DEVICE##*/}")
SYSDEV=\$(echo "/sys/class/block/\$DEV")
case \$DEV in
*loop*) continue ;;
esac
if [ ! -d "\$SYSDEV" ] ; then
continue
fi
mount \$DEVICE /tmp/mnt 2>/dev/null
if [ -d /tmp/mnt/minimal/rootfs ] ; then
echo " Found /minimal/rootfs folder on device \$DEV."
touch /tmp/mnt/minimal/rootfs/minimal.pid 2>/dev/null
if [ -f /tmp/mnt/minimal/rootfs/minimal.pid ] ; then
echo " Trying to overlay in read/write mode..."
rm -f /tmp/mnt/minimal/rootfs/minimal.pid
mount -t overlay -o lowerdir=/mnt,upperdir=/tmp/mnt/minimal/rootfs,workdir=/tmp/mnt/minimal/work none /mnt
OUT=\$?
if [ ! "\$OUT" = "0" ] ; then
echo " Mount failed (probably on vfat), moving on with other devices."
umount /tmp/mnt 2>/dev/null
continue
fi
else
echo " Trying to overlay in read only mode..."
mkdir -p /tmp/minimal/work
mkdir -p /tmp/minimal/rootfs
mount -t overlay -o lowerdir=/mnt:/tmp/mnt/minimal/rootfs,upperdir=/tmp/minimal/rootfs,workdir=/tmp/minimal/work none /mnt
fi
echo " ...done."
break
else
umount /tmp/mnt 2>/dev/null
fi
done
echo "...done."
# Move critical file systems to the new mountpoint.
echo "Remounting /dev, /sys, /tmp and /proc in /mnt..."
mount --move /dev /mnt/dev
mount --move /sys /mnt/sys
mount --move /proc /mnt/proc
mount --move /tmp /mnt/tmp
echo "...done."
# The new mountpoint becomes file system root. All original root folders are
# deleted automatically as part of the command execution. The '/sbin/init'
# process is invoked and it becomes the new PID 1 parent process.
exec switch_root /mnt /sbin/init
echo "You can never see this... unless there is a serious bug..."
sleep 99999
EOF
chmod +x switch.sh
# The script '/etc/bootscript.sh' is automatically executed as part of the
# '/sbin/init' proess. All core boot configuration has been completed and now we
# need to do the rest of the configuration on the user space level. Here we loop
# through all available network devices and we configure them through DHCP.
cat > bootscript.sh << EOF
#!/bin/sh
echo "Welcome to \"Minimal Linux Live\" (/sbin/init)"
for DEVICE in /sys/class/net/* ; do
echo "Found network device \${DEVICE##*/}"
ip link set \${DEVICE##*/} up
[ \${DEVICE##*/} != lo ] && udhcpc -b -i \${DEVICE##*/} -s /etc/rc.dhcp
done
EOF
chmod +x bootscript.sh
# The script '/etc/rc.dhcp' is automatically invoked for each network device.
cat > rc.dhcp << EOF
#!/bin/sh
ip addr add \$ip/\$mask dev \$interface
if [ "\$router" ]; then
ip route add default via \$router dev \$interface
fi
if [ "\$ip" ]; then
echo "DHCP configuration for device \$interface"
echo "ip: \$ip"
echo "mask: \$mask"
echo "router: \$router"
fi
EOF
chmod +x rc.dhcp
# DNS resolving is done by using Google's public DNS servers.
cat > resolv.conf << EOF
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF
# The file '/etc/welcome.txt' is displayed on every boot of the system in each
# available terminal.
cat > welcome.txt << EOF
#####################################
# #
# Welcome to "Minimal Linux Live" #
# #
#####################################
EOF
# The file '/etc/inittab' contains the configuration which defines how the
# system will be initialized. Check the following URL for more details:
# http://git.busybox.net/busybox/tree/examples/inittab
cat > inittab << EOF
::sysinit:/etc/bootscript.sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::once:cat /etc/welcome.txt
::respawn:/bin/cttyhack /bin/sh
tty2::once:cat /etc/welcome.txt
tty2::respawn:/bin/sh
tty3::once:cat /etc/welcome.txt
tty3::respawn:/bin/sh
tty4::once:cat /etc/welcome.txt
tty4::respawn:/bin/sh
EOF
cd ..
# The '/init' script is the first place where we gain execution control after
# the kernel has been loaded. This script prepares the core file systems, then
# creates new mountpoint in RAM which we use as new root location and finally
# the execution is passed to the script '/sbin/init' which in turn looks for
# the configuration file '/etc/inittab'.
cat > init << EOF
#!/bin/sh
echo "Welcome to \"Minimal Linux Live\" (/init)"
# Let's mount all core file systems.
/etc/prepare.sh
# Now let's create new mountpoint in RAM and make it our new root location.
exec /etc/switch.sh
EOF
chmod +x init
# Copy all source files to '/src'. Note that the scripts won't work there.
cp ../../*.sh src
cp ../../.config src
cp ../../*.txt src
chmod +rx src/*.sh
chmod +r src/.config
chmod +r src/*.txt
cp -r ../../08_generate_rootfs src
cp -r ../../11_generate_iso src
chmod -R +rx **/*.sh
chmod -R +r **/.config
chmod -R +r **/*.txt
# Copy all necessary 'glibc' libraries to '/lib' BEGIN.

View File

@ -0,0 +1,10 @@
#!/bin/sh
echo "Welcome to \"Minimal Linux Live\" (/sbin/init)"
for DEVICE in /sys/class/net/* ; do
echo "Found network device ${DEVICE##*/}"
ip link set ${DEVICE##*/} up
[ ${DEVICE##*/} != lo ] && udhcpc -b -i ${DEVICE##*/} -s /etc/rc.dhcp
done

View File

@ -0,0 +1,12 @@
::sysinit:/etc/bootscript.sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::once:cat /etc/welcome.txt
::respawn:/bin/cttyhack /bin/sh
tty2::once:cat /etc/welcome.txt
tty2::respawn:/bin/sh
tty3::once:cat /etc/welcome.txt
tty3::respawn:/bin/sh
tty4::once:cat /etc/welcome.txt
tty4::respawn:/bin/sh

View File

@ -0,0 +1,9 @@
#!/bin/sh
dmesg -n 1
mount -t devtmpfs none /dev
mount -t proc none /proc
mount -t tmpfs none /tmp -o mode=1777
mount -t sysfs none /sys

View File

@ -0,0 +1,15 @@
#!/bin/sh
ip addr add $ip/$mask dev $interface
if [ "$router" ]; then
ip route add default via $router dev $interface
fi
if [ "$ip" ]; then
echo "DHCP configuration for device $interface"
echo "ip: $ip"
echo "mask: $mask"
echo "router: $router"
fi

View File

@ -0,0 +1,3 @@
nameserver 8.8.8.8
nameserver 8.8.4.4

View File

@ -0,0 +1,149 @@
#!/bin/sh
# Create the new mountpoint in RAM.
mount -t tmpfs none /mnt
# Create folders for all critical file systems.
echo "Create folders for all critical file systems..."
mkdir /mnt/dev
mkdir /mnt/sys
mkdir /mnt/proc
mkdir /mnt/tmp
# Copy root folders in the new mountpoint.
echo "Copying the root file system to /mnt..."
cp -a bin etc lib lib64 root sbin src usr /mnt
DEFAULT_OVERLAY_DIR="/tmp/minimal/overlay"
DEFAULT_UPPER_DIR="/tmp/minimal/rootfs"
DEFAULT_WORK_DIR="/tmp/minimal/work"
echo "Searching available devices for overlay content..."
for DEVICE in /dev/* ; do
DEV=$(echo "${DEVICE##*/}")
SYSDEV=$(echo "/sys/class/block/$DEV")
case $DEV in
*loop*) continue ;;
esac
if [ ! -d "$SYSDEV" ] ; then
continue
fi
mkdir -p /tmp/mnt/device
DEVICE_MNT=/tmp/mnt/device
OVERLAY_DIR=""
OVERLAY_MNT=""
UPPER_DIR=""
WORK_DIR=""
mount $DEVICE $DEVICE_MNT 2>/dev/null
if [ -d $DEVICE_MNT/minimal/rootfs ] && [ -d $DEVICE_MNT/minimal/work ] ; then
# folder
echo " Found '/minimal' folder on device '$DEVICE'."
touch $DEVICE_MNT/minimal/rootfs/minimal.pid 2>/dev/null
if [ -f $DEVICE_MNT/minimal/rootfs/minimal.pid ] ; then
# read/write mode
echo " Device '$DEVICE' is mounted in read/write mode."
rm -f $DEVICE_MNT/minimal/rootfs/minimal.pid
OVERLAY_DIR=$DEFAULT_OVERLAY_DIR
OVERLAY_MNT=$DEVICE_MNT
UPPER_DIR=$DEVICE_MNT/minimal/rootfs
WORK_DIR=$DEVICE_MNT/minimal/work
else
# read only mode
echo " Device '$DEVICE' is mounted in read only mode."
OVERLAY_DIR=$DEVICE_MNT/minimal/rootfs
OVERLAY_MNT=$DEVICE_MNT
UPPER_DIR=$DEFAULT_UPPER_DIR
WORK_DIR=$DEFAULT_WORK_DIR
fi
elif [ -f $DEVICE_MNT/minimal.img ] ; then
#image
echo " Found '/minimal.img' image on device '$DEVICE'."
mkdir -p /tmp/mnt/image
IMAGE_MNT=/tmp/mnt/image
LOOP_DEVICE=$(losetup -f)
losetup $LOOP_DEVICE $DEVICE_MNT/minimal.img
mount $LOOP_DEVICE $IMAGE_MNT
if [ -d $IMAGE_MNT/rootfs -a -d $IMAGE_MNT/work ] ; then
touch $IMAGE_MNT/rootfs/minimal.pid 2>/dev/null
if [ -f $IMAGE_MNT/rootfs/minimal.pid ] ; then
# read/write mode
echo " Image '$DEVICE/minimal.img' is mounted in read/write mode."
rm -f $IMAGE_MNT/rootfs/minimal.pid
OVERLAY_DIR=$DEFAULT_OVERLAY_DIR
OVERLAY_MNT=$IMAGE_MNT
UPPER_DIR=$IMAGE_MNT/rootfs
WORK_DIR=$IMAGE_MNT/work
else
# read only mode
echo " Image '$DEVICE/minimal.img' is mounted in read only mode."
OVERLAY_DIR=$IMAGE_MNT/rootfs
OVERLAY_MNT=$IMAGE_MNT
UPPER_DIR=$DEFAULT_UPPER_DIR
WORK_DIR=$DEFAULT_WORK_DIR
fi
else
umount $IMAGE_MNT
rm -rf $IMAGE_MNT
fi
fi
if [ "$OVERLAY_DIR" != "" -a "$UPPER_DIR" != "" -a "$WORK_DIR" != "" ] ; then
mkdir -p $OVERLAY_DIR
mkdir -p $UPPER_DIR
mkdir -p $WORK_DIR
mount -t overlay -o lowerdir=/mnt:$OVERLAY_DIR,upperdir=$UPPER_DIR,workdir=$WORK_DIR none /mnt 2>/dev/null
OUT=$?
if [ ! "$OUT" = "0" ] ; then
echo " Mount failed (probably on vfat)."
umount $OVERLAY_MNT 2>/dev/null
rmdir $OVERLAY_MNT 2>/dev/null
rmdir $DEFAULT_OVERLAY_DIR 2>/dev/null
rmdir $DEFAULT_UPPER_DIR 2>/dev/null
rmdir $DEFAULT_WORK_DIR 2>/dev/null
else
# All done, time to go.
echo " Overlay data from device '$DEVICE' has been merged."
break
fi
else
echo " Device '$DEVICE' has no proper overlay structure."
fi
umount $DEVICE_MNT 2>/dev/null
rm -rf $DEVICE_MNT 2>/dev/null
done
# Move critical file systems to the new mountpoint.
echo "Remounting /dev, /sys, /tmp and /proc in /mnt..."
mount --move /dev /mnt/dev
mount --move /sys /mnt/sys
mount --move /proc /mnt/proc
mount --move /tmp /mnt/tmp
# The new mountpoint becomes file system root. All original root folders are
# deleted automatically as part of the command execution. The '/sbin/init'
# process is invoked and it becomes the new PID 1 parent process.
exec switch_root /mnt /sbin/init
echo "You can never see this... unless there is a serious bug..."
sleep 99999

View File

@ -0,0 +1,7 @@
#####################################
# #
# Welcome to "Minimal Linux Live" #
# #
#####################################

10
src/08_generate_rootfs/init Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
echo "Welcome to \"Minimal Linux Live\" (/init)"
# Let's mount all core file systems.
/etc/prepare.sh
# Now let's create new mountpoint in RAM and make it our new root location.
exec /etc/switch.sh

View File

@ -1,5 +1,7 @@
#!/bin/sh
SRC_DIR=$(pwd)
# Find the kernel build directory.
cd work/kernel
cd $(ls -d *)
@ -41,19 +43,67 @@ chmod +rx src/*.sh
chmod +r src/.config
chmod +r src/*.txt
# Read the 'OVERLAY_TYPE' property from '.config'
OVERLAY_TYPE="$(grep -i OVERLAY_TYPE $SRC_DIR/.config | cut -f2 -d'=')"
if [ "$OVERLAY_TYPE" = "sparse" -a "$(id -u)" = "0" ] ; then
# sparse
echo "Using sparse file for overlay."
# This is the BusyBox executable.
BUSYBOX=../rootfs/bin/busybox
# Create sparse image file with 1MB size.
$BUSYBOX truncate -s 1M minimal.img
# Find available loop device.
LOOP_DEVICE=$($BUSYBOX losetup -f)
# Associate loop device with the sparse image file.
$BUSYBOX losetup $LOOP_DEVICE minimal.img
# Format the sparse image file with Ext2 file system.
$BUSYBOX mkfs.ext2 $LOOP_DEVICE
# Mount the sparse file in folder 'tmp_min".
mkdir tmp_min
$BUSYBOX mount minimal.img tmp_min
# Create the overlay folders.
mkdir -p tmp_min/rootfs
mkdir -p tmp_min/work
# Copy the overlay content.
cp -r $SRC_DIR/11_generate_iso/* tmp_min/rootfs/
# Unmount the sparse file and thelete the temporary folder.
$BUSYBOX umount tmp_min
rm -rf tmp_min
# Detach the loop device.
$BUSYBOX losetup -d $LOOP_DEVICE
elif [ "$OVERLAY_TYPE" = "folder" ] ; then
# folder
echo "Using folder structure for overlay."
mkdir -p minimal/rootfs
mkdir -p minimal/work
cp -r $SRC_DIR/11_generate_iso/* minimal/rootfs/
fi
# Create the overlay directory '/minimal/rootfs'. All files in this folder are
# merged in the root folder and can be manipulated thanks to overlayfs.
mkdir -p minimal/rootfs
cd minimal/rootfs
echo 'Sample file 1 from CD.' > file_from_cd_1.txt
echo 'Sample file 2 from CD.' > file_from_cd_2.txt
cd ../..
#mkdir -p minimal/rootfs
#cd minimal/rootfs
#echo 'Sample file 1 from CD.' > file_from_cd_1.txt
#echo 'Sample file 2 from CD.' > file_from_cd_2.txt
#cd ../..
# Create ISOLINUX configuration file.
echo 'default kernel.bz initrd=rootfs.gz' > ./isolinux.cfg
# Now we generate the ISO image file.
genisoimage -J -r -o ../minimal_linux_live.iso -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table ./
genisoimage -J -r -o ../minimal_linux_live.iso -b isolinux.bin -c boot.cat -input-charset UTF-8 -no-emul-boot -boot-load-size 4 -boot-info-table ./
# This allows the ISO image to be bootable if it is burned on USB flash drive.
isohybrid ../minimal_linux_live.iso 2>/dev/null || true

View File

@ -0,0 +1,7 @@
This file is located in directory '11_generate_iso'. You can use this directory
to put your own content (files and folders) which will be visible in the root
directory of your 'Minimal Linux Live' system, just like this file is visible.
The files/folders will override the content in the root folder, so be careful
what you put there because you may end up with broken system. :)

View File

@ -1,4 +1,5 @@
#!/bin/sh
qemu-system-i386 -m 64M -hda hdd.img -cdrom minimal_linux_live.iso -boot d
qemu-system-i386 -m 64M -cdrom minimal_linux_live.iso -boot d
#qemu-system-i386 -m 64M -hda hdd.img -cdrom minimal_linux_live.iso -boot d