This is Gentoo's testing wiki. It is a non-operational environment and its textual content is outdated.

Please visit our production wiki at https://wiki.gentoo.org

Early Userspace Mounting

From Gentoo Wiki (test)
Jump to:navigation Jump to:search
Article status
This article has some todo items:
  • mention embedded (in kernel) initramfs
  • EFI stub kernel needs CONFIG_INITRAMFS_SOURCE

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 a custom 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:

  1. Mounts the root partition on /mnt/root as read-only.
  2. Symlinks the fstab from the root partition to the initramfs environment.
  3. Checks the filesystem of our /usr device using the embedded /sbin/fsck binary.
  4. Mounts /usr, then moves it to /mnt/root/usr using the --move mount parameter.
  5. Switches 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.

Requirements

The most important package here is sys-apps/busybox as it provides utilities suitable for a initramfs. It is also critical that you emerge it with static USE flag enabled:

root #USE="static" emerge --ask sys-apps/busybox
Note
Enabling the static USE flag will disable PAM support, regardless if its enabled or not.

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

Make sure that these two are executable:

root #cd /usr/src/linux
root #chmod +x usr/gen_init_cpio scripts/gen_initramfs_list.sh

Make sure that your running kernel is built with the devtmpfs option enabled. It is required by the init script below and udev:

KERNEL
'"`UNIQ--pre-00000000-QINU`"'

Next up is the initramfs_list file which will tell gen_initramfs_list.sh how to construct the initramfs:

FILE /usr/src/initramfs/initramfs_list
# 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 /lib64      755 0 0
#dir /lib32      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/libmount.so.1      /lib/libmount.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    /lib/librt.so.1         /lib/librt.so.1                     755 0 0
file    /lib/libdl.so.2         /lib/libdl.so.2                     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
Note
Please note that if the computer under maintenance is running the amd64 version, it is necessary to do a bit of editing. You need to reference to lib64 rather than lib for the libraries required by /sbin/fsck.ext4 and /sbin/fsck. Additionally, the ld-linux library needs to explicitly point to the x86-64 version. For example, the first three lines of that section would now be:
FILE /usr/src/initramfs/initramfs_list
# libraries required by /sbin/fsck.ext4 and /sbin/fsck
file    /lib64/ld-linux-x86-64.so.2      /lib64/ld-linux-x86-64.so.2                  755 0 0
file    /lib64/libext2fs.so.2   /lib64/libext2fs.so.2               755 0 0

Once all the libraries are pointing to their 64bit counterparts it is also necessary to uncomment the lines under #directory structure. The script has now been adapted for amd64 usage.

Copy and save the contents of the above to /usr/src/initramfs/initramfs_list after adjusting for your architecutre.

Last up is the actual init file which will execute the initramfs:

FILE /usr/src/initramfs/init
#!/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" #note: you can add more than just usr, but make sure they are declared in /usr/src/initramfs/initramfs_list

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

Run the 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 #cd /usr/src/linux
root #scripts/gen_initramfs_list.sh -o /boot/initrd.cpio.gz /usr/src/initramfs/initramfs_list

System preparation

In 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:

FILE /etc/fstab
/dev/sdb3   /usr    ext4   noatime  0 0

Bootloader configuration

To use the initramfs, the bootloader needs to be configured as shown below for GRUB and LILO as examples.

Configuring GRUB

Add the initrd line to /boot/grub/grub.conf:

FILE grub.conf
title Gentoo Linux <version>
root (hd0,0)
kernel /boot/kernel-<version>-gentoo
initrd /boot/initrd.cpio.gz

Configuring LILO

Add the initrd and append line to /etc/lilo.conf:

FILE lilo.conf
image = /boot/vmlinuz-<version>-gentoo
  #root = /dev/sda4
  label = gentoo
  read-only
  append = "real_root=/dev/sda4"
  initrd = boot/initrd.cpio.gz

Result

When booting, the output looks like this:

CODE output of the initramfs when booting
Mounting rootfs
Checking local filesystem :  /usr
/dev/sdb3: clean, 285754/1640160 files, 1663220/6556528 blocks
Mounting /usr
(Potentially other fs checks on other partitions here...)
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
[...]

See also

  • Custom Initramfs — the successor of initrd. It provides early userspace which can do things the kernel can't easily do by itself during the boot process.

External resources