mkinitrd

Cool project repo :D
git clone git://git.emmett1.my/mkinitrd.git
Log | Files | Refs

commit 02b21c70dda53e0be3cbc87755e09bf2b3671f43
parent a2066f1d48a3dbb79543cbe9b5a0599abd540a6c
Author: emmett1 <emmett1.2miligrams@gmail.com>
Date:   Thu, 11 Jun 2020 13:03:10 +0800

initial commit

Diffstat:
AMakefile | 19+++++++++++++++++++
Ainit | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amkinitrd | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 438 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,19 @@ +BINDIR = /usr/bin +DATADIR = /usr/share/mkinitrd +IBINDIR = $(DESTDIR)$(BINDIR) +IDATADIR = $(DESTDIR)$(DATADIR) + +all: + @echo "Run 'make install' to install" + +install: + install -d $(IBINDIR) + install -m755 mkinitrd $(IBINDIR) + install -d $(IDATADIR) + install -m755 init $(IDATADIR) + +uninstall: + rm -f $(IBINDIR)/mkinitrd + rm -f $(IDATADIR)/init + +.PHONY: all install uninstall diff --git a/init b/init @@ -0,0 +1,177 @@ +#!/bin/sh + +export PATH=/bin:/usr/bin:/sbin:/usr/sbin + +shell() { + setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1' +} + +problem() { + printf "Encountered a problem!\n\nDropping you to a shell.\n\n" + shell +} + +msg() { + echo ":: $*" +} + +no_device() { + printf "The device %s, which is supposed to contain the\n" $1 + printf "root file system, does not exist.\n" + printf "Please fix this problem and exit this shell.\n\n" +} + +no_mount() { + printf "Could not mount device %s\n" $1 + printf "Sleeping forever. Please reboot and fix the kernel command line.\n\n" + printf "Maybe the device is formatted with an unsupported file system?\n\n" + printf "Or maybe filesystem type autodetection went wrong, in which case\n" + printf "you should add the rootfstype=... parameter to the kernel command line.\n\n" + printf "Available partitions:\n" +} + +do_mount_root() { + mkdir $newroot + [ -n "$rootflags" ] && rootflags="$rootflags," + rootflags="$rootflags$ro" + + case "$root" in + /dev/* ) device=$root ;; + UUID=* ) eval $root; device="/dev/disk/by-uuid/$UUID" ;; + PARTUUID=*) eval $root; device="/dev/disk/by-partuuid/$PARTUUID" ;; + LABEL=* ) eval $root; device="/dev/disk/by-label/$LABEL" ;; + "" ) echo "No root device specified." ; problem ;; + esac + + while [ ! -b "$device" ] ; do + no_device $device + problem + done + + if ! mount -n -t "$rootfstype" -o "$rootflags" "$device" $newroot ; then + no_mount $device + cat /proc/partitions + while true ; do sleep 10000 ; done + fi +} + +do_boot_live() { + mkdir $newroot + modprobe loop + + MEDIA=/dev/disk/by-label/LIVECD + MEDIUM=/run/initramfs/medium + SYSTEM=/run/initramfs/system + WRITEDIR=/run/initramfs/overlayfs/write + WORKDIR=/run/initramfs/overlayfs/work + sfsimg=/run/initramfs/medium/rootfs/filesystem.sfs + delay=${delay:-5} + + mkdir -p $MEDIUM $SYSTEM $WRITEDIR $WORKDIR + + if [ ! -e $MEDIA ]; then + msg "wait $delay seconds..." + sleep $delay + if [ ! -e $MEDIA ]; then + msg "media is not appeared even after wait $delay seconds..." + msg "try increase delay by append 'delay=<seconds>' to boot cmdline" + sleep 9999 + fi + fi + + mount -o ro $MEDIA $MEDIUM || problem + sfs_dev=$(losetup --find --show --read-only $sfsimg) + mount -o defaults -r $sfs_dev $SYSTEM || problem + mount -t overlay overlay -o upperdir=$WRITEDIR,lowerdir=$SYSTEM,workdir=$WORKDIR $newroot || problem + + # Tell system to skip fsck during startup + > $newroot/fastboot +} + +do_try_resume() { + case "$resume" in + UUID=* ) eval $resume; resume="/dev/disk/by-uuid/$UUID" ;; + LABEL=*) eval $resume; resume="/dev/disk/by-label/$LABEL" ;; + esac + + if $noresume || ! [ -b "$resume" ]; then return; fi + + ls -lH "$resume" | ( read x x x x maj min x + echo -n ${maj%,}:$min > /sys/power/resume ) +} + +init=/sbin/init +root= +newroot=/.root +rootdelay= +rootfstype=auto +ro="ro" +rootflags= +device= +resume= +noresume=false + +mount -t proc proc /proc -o nosuid,noexec,nodev +mount -t sysfs sys /sys -o nosuid,noexec,nodev +mount -t devtmpfs dev /dev -o mode=0755,nosuid +mount -t tmpfs run /run -o nosuid,nodev,mode=0755 + +read -r cmdline < /proc/cmdline + +for param in $cmdline ; do + case $param in + init=* ) init=${param#init=} ;; + root=* ) root=${param#root=} ;; + rootdelay=* ) rootdelay=${param#rootdelay=} ;; + rootfstype=*) rootfstype=${param#rootfstype=} ;; + rootflags=* ) rootflags=${param#rootflags=} ;; + resume=* ) resume=${param#resume=} ;; + noresume ) noresume=true ;; + ro ) ro="ro" ;; + rw ) ro="rw" ;; + initrd ) initrd=true ;; + live ) live=true ;; + esac +done + +# udevd location depends on version +if [ -x /sbin/udevd ]; then + UDEVD=/sbin/udevd +elif [ -x /lib/udev/udevd ]; then + UDEVD=/lib/udev/udevd +elif [ -x /lib/systemd/systemd-udevd ]; then + UDEVD=/lib/systemd/systemd-udevd +else + echo "Cannot find udevd nor systemd-udevd" + problem +fi + +${UDEVD} --daemon --resolve-names=never +udevadm trigger --action=add --type=subsystems +udevadm trigger --action=add --type=devices +udevadm trigger --action=change --type=devices +udevadm settle + +if [ -f /etc/mdadm.conf ] ; then mdadm -As ; fi +if [ -x /sbin/vgchange ] ; then /sbin/vgchange -a y > /dev/null ; fi +if [ -n "$rootdelay" ] ; then sleep "$rootdelay" ; fi + +if [ "$initrd" = true ]; then + shell +fi + +if [ "$live" = true ]; then + do_boot_live +else + do_try_resume # This function will not return if resuming from disk + do_mount_root +fi + +killall -w ${UDEVD##*/} + +mount --move /proc $newroot/proc +mount --move /sys $newroot/sys +mount --move /dev $newroot/dev +mount --move /run $newroot/run + +exec switch_root $newroot "$init" "$@" diff --git a/mkinitrd b/mkinitrd @@ -0,0 +1,242 @@ +#!/bin/bash + +add_file() { + # add_file file [ dest ][ mode ] + local file dest mode + [ "$1" ] || return + if [ -f $1 ]; then + file=$1 + else + echo "missing file: $1" + return + fi + if [ -L $file ]; then + add_file $(readlink -f $file) + fi + if [ "${file:0:1}" != "/" ]; then + echo "absolute source path needed: $file" + return + fi + if [ "$2" ]; then + dest="$2" + else + dest=${file/\//} + fi + if [ "${dest:0:1}" = "/" ]; then + echo "destination path must without leading '/': $dest" + return + fi + mode=${3:-$(stat -c %a "$file")} + if [ ! "$mode" ]; then + echo "failed get file mode: $file" + return + fi + install -Dm$mode $file $INITRDDIR/$dest +} + + +add_binary() { + # add_binary binary + local bin lib + [ "$1" ] || return + bin=$(type -p $1) + if [ ! "$bin" ]; then + echo "missing bin: $1" + return + fi + if [ -x $INITRDDIR/$bin ]; then + return + fi + add_file $bin + for i in $(ldd $bin | sed "s/\t//" | cut -d " " -f1); do + case $i in + linux-vdso.so.1|linux-gate.so.1) continue;; + esac + lib=$(PATH=/lib:/lib64:/usr/lib type -p $i) + if [ ! "$lib" ]; then + echo "missing lib: $i" + continue + fi + if [ -e $INITRDDIR/$lib ]; then + continue + fi + add_file $lib + done +} + +add_module() { + # add_module modname + local modname modpath + [ "$1" ] || return + if modinfo -k $KERNEL $1 &>/dev/null; then + modname=$(modinfo -k $KERNEL -F name $1 | cut -d ' ' -f1 | head -n1) + [ "$modname" = "name:" ] && return 0 + modpath=$(modinfo -k $KERNEL -F filename $1 | cut -d ' ' -f1 | head -n1) + [ "$modpath" = "name:" ] && return 0 + else + echo "missing module: $1" + return + fi + [ -f $INITDIR/lib/modules/$KERNEL/kernel/$(basename $modpath) ] && return + add_file "$modpath" lib/modules/$KERNEL/kernel/$(basename $modpath) + modinfo -F firmware -k $KERNEL $modname | while read -r line; do + if [ ! -f /lib/firmware/$line ]; then + echo "missing firmware for $modname: $line" + else + add_file /lib/firmware/$line + fi + done + modinfo -F depends -k $KERNEL $modname | while IFS=',' read -r -a line; do + for l in ${line[@]}; do + add_module "$l" + done + done +} + +finalize_modules() { + local file + [ -d $INITRDDIR/lib/modules/$KERNEL/kernel ] || return + for file in /lib/modules/$KERNEL/modules.*; do + add_file $file + done + awk -F'/' '{ print "kernel/" $NF }' /lib/modules/$KERNEL/modules.order > $INITDIR/lib/modules/$KERNEL/modules.order + depmod -b $INITRDDIR $KERNEL +} + +add_dir() { + # add_dir source [ target ] + [ "$1" ] || return + source=$1 + if [ ! -d $source ]; then + echo "directory not exist: $source" + return + fi + if [ $2 ]; then + target=$2 + else + target=$(dirname $source) + fi + mkdir -p $INITRDDIR/$target + cp -a $source $INITRDDIR/$target +} + +make_dir() { + # make_dir path [ mode ] + local path=$1 mode=${2:-755} + [ "$path" ] || return + if [ ${path:0:1} = / ]; then + echo "path should not leading '/': $path" + return + fi + install -dm$mode $INITRDDIR/$path +} + +if [ $1 ] ; then + KERNEL=$1 +else + KERNEL=$(uname -r) +fi +INITRD=/boot/initrd-$KERNEL.img + +if [ ! -d "/lib/modules/$1" ] ; then + echo "No modules directory named $1" + exit 1 +fi + +if [ ! $(type -p cpio) ]; then + echo "'cpio' not found" + exit 1 +fi + +DATADIR=/usr/share/mkinitrd +INITIN=init + +# create a temporary working directory +INITRDDIR=$(mktemp -d /tmp/initrd-work.XXXXXXXXXX) + +echo "Creating $INITRD... " + +# make required dirs +for d in dev run sys proc; do + make_dir $d +done + +# add the init file +add_file $DATADIR/$INITIN init 755 + +# add required binaries +for b in bash sh cat cp killall ls mkdir mount umount sed awk sleep ln rm setsid \ + uname readlink basename modprobe blkid switch_root depmod kmod lsmod insmod; do + add_binary $b +done + +# add dirs +add_dir /etc/modprobe.d/ + +# add required terminfo +add_file /usr/share/terminfo/l/linux + +# udev +if [ -f /etc/udev/udev.conf ]; then + add_file /etc/udev/udev.conf + add_dir /lib/udev/rules.d + add_dir /etc/udev/rules.d/ + add_binary udevadm + add_binary udevd + add_binary /lib/udev/ata_id + add_binary /lib/udev/scsi_id + # Add udevd if not in PATH + if [ -x /lib/udev/udevd ] ; then + add_binary /lib/udev/udevd + elif [ -x /lib/systemd/systemd-udevd ] ; then + add_binary /lib/systemd/systemd-udevd + fi +fi + +# LVM +if [ $(type -p lvm) ]; then + for i in lvm dmsetup lvchange lvrename lvrename lvextend lvcreate lvdisplay lvscan \ + pvchange pvck pvcreate pvdisplay pvscan \ + vgchange vgcreate vgscan vgrename vgck; do + add_binary $i + done + add_dir /etc/lvm +fi + +# systemd +for dir in /lib/systemd /lib/elogind; do + if [ -d $dir ]; then + add_dir $dir + fi +done + +# for livecd +add_binary /lib/udev/cdrom_id +add_binary blockdev +add_binary losetup +add_module cdrom +add_module loop +add_module overlay + +# kernel modules +for mod in kernel/crypto kernel/fs kernel/lib kernel/drivers/block kernel/drivers/ata kernel/drivers/md \ + kernel/drivers/firewire kernel/drivers/input kernel/drivers/scsi kernel/drivers/message \ + kernel/drivers/pcmcia kernel/drivers/virtio kernel/drivers/hid kernel/drivers/usb/host kernel/drivers/usb/storage; do + FTGT="$FTGT /lib/modules/$KERNEL/$mod" +done +for m in $(find $FTGT -type f -name "*.ko*" 2> /dev/null); do + m=$(echo ${m%*.ko*}) + add_module $(basename $m) +done + +finalize_modules + +( cd $INITRDDIR ; find . | cpio -o -H newc --quiet | gzip -9 ) > $INITRD + +# Remove the temporary directories +rm -rf $INITRDDIR + +size=$(du -sh $INITRD | awk '{print $1}') +echo "done ($size)" + +exit 0