Newer
Older
buildroot-MynaPlayer / board / myna-player-odyssey / rootfs_overlay / usr / sbin / pre-init
#!/bin/bash
# Copyright 2020 Jookia <contact@jookia.org>
# SPDX-License-Identifier: GPL-3.0-or-later
# This scripts sets up an OverlayFS using a read-only rootfs mounted by Linux.
# Example usage: Save as /preinit and pass init=/preinit to your kernel.

set -e # Safety feature: Error out if we hit any errors

# Step 0: Check that this is running in the proper environment
# We don't want to run a bunch of complicated system-altering code accidentally
if [[ "$$" != "1" ]]; then
  echo 'Usage: Run as PID 1 before booting your system!' 1>&2
  exit 1
fi

# Step 1: Set up a temporary root on a tmpfs on /run
# Since root is read-only, we'll use /run for our tmpfs mount
# This tmpfs will store all our temporary files used for OverlayFS operation
mount -t tmpfs tmpfs /run

# Step 2: Mount and initialize the data partition at /run/data
# For OverlayFS we need a partition (/dev/mmcblk2p6 in our case) and two
# directories: overlay_root (contains the changes to the read-only root filesystem)
# as well as overlay_work which is used as a temporary store when writing files
# So mount and create these directories.

mkdir /run/data
mount /dev/mmcblk2p6 /run/data
for i in overlay_root overlay_work; do
  mkdir -p /run/data/$i;
done

# Step 3: Prepare for chrooting
# In a moment we're going to move the current root to /run/old_root and
# temporarily chroot to /run for reasons explained in the next step. But this
# is going to break running applications since we don't have bin, sbin or
# lib directories in /run. So create links to them now that will correctly
# resolve once we chroot.

for i in lib bin sbin; do
  ln -s /old_root/$i /run/$i
done

# Step 4: Move current root
# When using OverlayFS we have to make sure both the lower directory (in our
# case the read-only root) and upper directory (in our case the data
# partition's overlay root) don't contain each other.
# In our case the lower does: It's the root directory / which contains
# /run/data/overlay_root. 
# The solution to this is to move the current root mount to /run/old_root.
# The util-linux provides a tool for this: pivot_root.
# In our case the pivot_root utility will move our read-only root filesystem
# from / to /run/old_root.
# The current version of Linux (5.8 as of writing) will automatically chroot
# all running applications to the /run directory, but this is explicitly not
# guaranteed behaviour. So we will have to chroot to /run ourselves next.

mkdir /run/old_root && pivot_root /run/ /run/old_root

# Note: Bash doesn't natively support chrooting and continuing execution, so we
# have to do the previous steps in one long invocation. Apologies.

# Step 5: Chroot to /run so we have a proper root after pivoting
# In order for this step to work we need to have /bin, /sbin and /lib in /run
# with working programs so we can continue execution by calling a shell.
# In step 3 we set up links to the now pivoted old_root's bin, sbin and lib
# directories so everything will work fine.

# Step 6: Mount OverlayFS on /new_root (previously /run/new_root)
# At this point we have the following directories of interest:
# - /old_root containing our read-only root filesystem
# - /data/overlay_root containing our writeable root filesystem
# - /data/overlay_work containing the work directory for OverlayFS root
# - /new_root which we just created to place the OverlayFS root
# When making the OverlayFS root, we use the /old_root as the lowerdir,
# /data/overlay_root as the upperdir, and /data/overlay_work as the workdir.

# Step 7: Chroot to /new_root and run /sbin/init within the root
# This finally boots the system from the overlay root. Yay!
# Since we chroot instead of pivoting root, we leave our /run tmpfs in memory
# but no longer accessible. This shouldn't take up much memory given it's just
# a few mounts and symbolic links.

# Okay, finally do steps 5 to 7.
exec chroot . sh -c "mkdir new_root && mount -t overlay overlay -o lowerdir=/old_root,upperdir=/data/overlay_root,workdir=/data/overlay_work /new_root && exec chroot /new_root /sbin/init"