Early Userspace Mounting
Because of various changes in udev (see bug #364235), the need for a minimal initramfs that checks the /usr filesystem and pre-mounts /usr arose. This article will detail how build this initramfs.
In this article we'll be working with the following:
- sys-apps/busybox
- An initramfs content list
- The gen_init_cpio and gen_initramfs_list.sh utilities, provided by the kernel itself.
The initramfs also contains the required libraries and binaries to run an ext4 fsck.
Most of the code to run the fsck is coming from the /etc/init.d/fsck script.
If you are using any other filesystem than ext4, you will need to add the required binaries/libraries into the initramfs list.
Basically, the init script is doing following actions :
- Mounts the root partition on /mnt/root as readonly.
- Symlinks the fstab from the root partition to the initramfs environment.
- Checks the filesystem of our /usr device using the embedded /sbin/fsck binary.
- Mounts /usr, then move the /usr tree to /mnt/root/usr using the --move mount parameter.
- Switchs to real root and executes init.
The article also assumes we are working in /usr/src/initramfs/, so for the sake of ease, begin with creating this directory.
Contents |
Requirements
The most important package here is sys-apps/busybox as it provides utilities suitable for a initramfs. It's also critical that you emerge it with USE static enabled:
root # emerge --ask sys-apps/busyboxYou will also need the kernel sources, in this article we'll use sys-kernel/gentoo-sources-3.2.0. The kernel sources provide the gen_init_cpio and gen_initramfs_list.sh utilities. The gen_init_cpio utility does not come prepackaged and needs to be built:
root # make -C /usr/src/linux/usr/ gen_init_cpio
cc gen_init_cpio.c -o gen_init_cpioMake sure that these two are executable:
root # chmod +x /usr/src/linux/usr/gen_init_cpio /usr/src/linux/scripts/gen_initramfs_list.shMake sure that your running kernel is built with the CONFIG_DEVTMPFS option enabled. It is required by the init script below and sys-fs/udev.
-> Device Drivers -> Generic Driver Options [*] Maintain a devtmpfs filesystem to mount at /dev
Next up is the initramfs_list file which will tell gen_initramfs_list.sh how to construct the initramfs:
# directory structure dir /proc 755 0 0 dir /usr 755 0 0 dir /bin 755 0 0 dir /sys 755 0 0 dir /var 755 0 0 dir /lib 755 0 0 dir /sbin 755 0 0 dir /mnt 755 0 0 dir /mnt/root 755 0 0 dir /etc 755 0 0 dir /root 700 0 0 dir /dev 755 0 0 # busybox file /bin/busybox /bin/busybox 755 0 0 # libraries required by /sbin/fsck.ext4 and /sbin/fsck file /lib/ld-linux.so.2 /lib/ld-linux.so.2 755 0 0 file /lib/libext2fs.so.2 /lib/libext2fs.so.2 755 0 0 file /lib/libcom_err.so.2 /lib/libcom_err.so.2 755 0 0 file /lib/libpthread.so.0 /lib/libpthread.so.0 755 0 0 file /lib/libblkid.so.1 /lib/libblkid.so.1 755 0 0 file /lib/libuuid.so.1 /lib/libuuid.so.1 755 0 0 file /lib/libe2p.so.2 /lib/libe2p.so.2 755 0 0 file /lib/libc.so.6 /lib/libc.so.6 755 0 0 file /sbin/fsck /sbin/fsck 755 0 0 file /sbin/fsck.ext4 /sbin/fsck.ext4 755 0 0 # our init script file /init /usr/src/initramfs/init 755 0 0
Copy and save the contents of the above to /usr/src/initramfs/initramfs_list.
Last up is the actual ./init file which will execute the initramfs:
#!/bin/busybox sh
rescue_shell() {
echo "$@"
echo "Something went wrong. Dropping you to a shell."
busybox --install -s
exec /bin/sh
}
uuidlabel_root() {
for cmd in $(cat /proc/cmdline) ; do
case $cmd in
root=*)
type=$(echo $cmd | cut -d= -f2)
echo "Mounting rootfs"
if [ $type == "LABEL" ] || [ $type == "UUID" ] ; then
uuid=$(echo $cmd | cut -d= -f3)
mount -o ro $(findfs "$type"="$uuid") /mnt/root
else
mount -o ro $(echo $cmd | cut -d= -f2) /mnt/root
fi
;;
esac
done
}
check_filesystem() {
# most of code coming from /etc/init.d/fsck
local fsck_opts= check_extra= RC_UNAME=$(uname -s)
# FIXME : get_bootparam forcefsck
if [ -e /forcefsck ]; then
fsck_opts="$fsck_opts -f"
check_extra="(check forced)"
fi
echo "Checking local filesystem $check_extra : $1"
if [ "$RC_UNAME" = Linux ]; then
fsck_opts="$fsck_opts -C0 -T"
fi
trap : INT QUIT
# using our own fsck, not the builtin one from busybox
/sbin/fsck -p $fsck_opts $1
case $? in
0) return 0;;
1) echo "Filesystem repaired"; return 0;;
2|3) if [ "$RC_UNAME" = Linux ]; then
echo "Filesystem repaired, but reboot needed"
reboot -f
else
rescue_shell "Filesystem still have errors; manual fsck required"
fi;;
4) if [ "$RC_UNAME" = Linux ]; then
rescue_shell "Fileystem errors left uncorrected, aborting"
else
echo "Filesystem repaired, but reboot needed"
reboot
fi;;
8) echo "Operational error"; return 0;;
12) echo "fsck interrupted";;
*) echo "Filesystem couldn't be fixed";;
esac
rescue_shell
}
# temporarily mount proc and sys
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
# disable kernel messages from popping onto the screen
echo 0 > /proc/sys/kernel/printk
# clear the screen
clear
# mounting rootfs on /mnt/root
uuidlabel_root || rescue_shell "Error with uuidlabel_root"
# space separated list of mountpoints that ...
mountpoints="/usr"
# ... we want to find in /etc/fstab ...
ln -s /mnt/root/etc/fstab /etc/fstab
# ... to check filesystems and mount our devices.
for m in $mountpoints ; do
check_filesystem $m
echo "Mounting $m"
# mount the device and ...
mount $m || rescue_shell "Error while mounting $m"
# ... move the tree to its final location
mount --move $m "/mnt/root"$m || rescue_shell "Error while moving $m"
done
echo "All done. Switching to real root."
# clean up. The init process will remount proc sys and dev later
umount /proc
umount /sys
umount /dev
# switch to the real root and execute init
exec switch_root /mnt/root /sbin/init
Copy and save the contents of the above to /usr/src/initramfs/init.
Generating the Initramfs
Change to the top kernel source directory:
root # cd /usr/src/linuxFrom here we run the scripts/gen_initramfs_list.sh script with the -o argument pointing to where we want the initramfs image to be placed followed by the path to our initramfs_list file:
root # scripts/gen_initramfs_list.sh -o /boot/initrd.cpio.gz /usr/src/initramfs/initramfs_listSystem Preparation
In /etc/fstab, we must set the sixth field for the /usr entry to 0, this will prevent the OpenRC fsck init script to try to check the filesystem for the already mounted /usr:
/dev/sdb3 /usr ext4 noatime 0 '''0'''
Configuring GRUB
Add the initrd line to grub.conf:
title Gentoo 3.0.20 root (hd0,0) kernel /boot/kernel-3.0.20-gentoo [... kernel parameters ...] initrd /boot/initrd.cpio.gz
Result
When booting, the output looks like this :
Mounting rootfs
Checking local filesystem : /usr
/dev/sdb3: clean, 285754/1640160 files, 1663220/6556528 blocks
Mounting /usr
All done. Switching to real root.
INIT: version 2.88 booting
OpenRC 0.9.8.4 is starting up Gentoo Linux (i686)
Press I to enter interactive boot mode
[...]