Skip to content

Instantly share code, notes, and snippets.

@jfernandz
Created June 24, 2021 14:48
Show Gist options
  • Save jfernandz/bfb1285114c136ac2b86320cb06c3904 to your computer and use it in GitHub Desktop.
Save jfernandz/bfb1285114c136ac2b86320cb06c3904 to your computer and use it in GitHub Desktop.
Script to toggle overlayfs for rootfs in Debian
#!/bin/sh
# Part of raspi-config https://github.com/RPi-Distro/raspi-config
#
# See LICENSE file for copyright and license details
INTERACTIVE=True
ASK_TO_REBOOT=0
get_overlay_now() {
grep -q "boot=overlay" /proc/cmdline
}
get_overlay_conf() {
grep -q "boot=overlay" /boot/grub/grub.cfg
}
get_bootro_now() {
findmnt /boot | grep -q " ro,"
}
get_bootro_conf() {
grep /boot /etc/fstab | grep -q "defaults.*,ro"
}
enable_overlayfs() {
KERN=$(uname -r)
INITRD=initrd.img-"$KERN"-overlay
# mount the boot partition as writable if it isn't already
if get_bootro_now ; then
if ! mount -o remount,rw /boot 2>/dev/null ; then
echo "Unable to mount boot partition as writable - cannot enable"
return 1
fi
BOOTRO=yes
else
BOOTRO=no
fi
cat > /etc/initramfs-tools/scripts/overlay << 'EOF'
# Local filesystem mounting -*- shell-script -*-
#
# This script overrides local_mount_root() in /scripts/local
# and mounts root as a read-only filesystem with a temporary (rw)
# overlay filesystem.
#
. /scripts/local
local_mount_root()
{
local_top
local_device_setup "${ROOT}" "root file system"
ROOT="${DEV}"
# Get the root filesystem type if not set
if [ -z "${ROOTFSTYPE}" ]; then
FSTYPE=$(get_fstype "${ROOT}")
else
FSTYPE=${ROOTFSTYPE}
fi
local_premount
# CHANGES TO THE ORIGINAL FUNCTION BEGIN HERE
# N.B. this code still lacks error checking
modprobe ${FSTYPE}
checkfs ${ROOT} root "${FSTYPE}"
# Create directories for root and the overlay
mkdir /lower /upper
# Mount read-only root to /lower
if [ "${FSTYPE}" != "unknown" ]; then
mount -r -t ${FSTYPE} ${ROOTFLAGS} ${ROOT} /lower
else
mount -r ${ROOTFLAGS} ${ROOT} /lower
fi
modprobe overlay || insmod "/lower/lib/modules/$(uname -r)/kernel/fs/overlayfs/overlay.ko"
# Mount a tmpfs for the overlay in /upper
mount -t tmpfs tmpfs /upper
mkdir /upper/data /upper/work
# Mount the final overlay-root in $rootmnt
mount -t overlay \
-olowerdir=/lower,upperdir=/upper/data,workdir=/upper/work \
overlay ${rootmnt}
}
EOF
# add the overlay to the list of modules
if ! grep overlay /etc/initramfs-tools/modules > /dev/null; then
echo overlay >> /etc/initramfs-tools/modules
fi
# build the new initramfs
update-initramfs -c -k "$KERN"
# rename it so we know it has overlay added
mv /boot/initrd.img-"$KERN" /boot/"$INITRD"
# there is now a modified initramfs ready for use...
# modify initrd in /boot/grub/grub.conf
sed -i /boot/grub/grub.cfg -e 's/initrd\.img-'"$KERN"'/'"$INITRD"'/'
# modify command line
if ! grep -q "boot=overlay" /boot/grub/grub.cfg ; then
sed -i '/vmlinuz-'"$KERN"'/ s/$/ boot=overlay/' /boot/grub/grub.cfg
fi
if [ "$BOOTRO" = "yes" ] ; then
if ! mount -o remount,ro /boot 2>/dev/null ; then
echo "Unable to remount boot partition as read-only"
fi
fi
}
is_uname_current() {
test -d "/lib/modules/$(uname -r)"
return $?
}
disable_overlayfs() {
KERN=$(uname -r)
INITRD=initrd.img-"$KERN"-overlay
# mount the boot partition as writable if it isn't already
if get_bootro_now ; then
if ! mount -o remount,rw /boot 2>/dev/null ; then
echo "Unable to mount boot partition as writable - cannot disable"
return 1
fi
BOOTRO=yes
else
BOOTRO=no
fi
# remove the overlay to the list of modules
if grep -q "overlay" /etc/initramfs-tools/modules ; then
sed -i /etc/initramfs-tools/modules -e '/overlay/d'
fi
if update-initramfs -c -k "${KERN}" ; then
update-initramfs -d -k "${KERN}-overlay"
fi
# modify the initrd in /boot/grub/grub.cfg
sed -i /boot/grub/grub.cfg -e 's/'"$INITRD"'/initrd\.img-'"$KERN"'/'
# modify command line
sed -i '/vmlinuz-'"$KERN"'/ s/ boot=overlay//' /boot/grub/grub.cfg
if [ "$BOOTRO" = "yes" ] ; then
if ! mount -o remount,ro /boot 2>/dev/null ; then
echo "Unable to remount boot partition as read-only"
fi
fi
}
enable_bootro() {
if get_overlay_now ; then
echo "Overlay in use; cannot update fstab"
return 1
fi
sed -i /etc/fstab -e "s/\(.*\/boot.*\)defaults\(.*\)/\1defaults,ro\2/"
}
disable_bootro() {
if get_overlay_now ; then
echo "Overlay in use; cannot update fstab"
return 1
fi
sed -i /etc/fstab -e "s/\(.*\/boot.*\)defaults,ro\(.*\)/\1defaults\2/"
}
do_finish() {
if [ $ASK_TO_REBOOT -eq 1 ]; then
whiptail --yesno "Would you like to reboot now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
fi
exit 0
}
do_overlayfs() {
DEFAULT=--defaultno
CURRENT=0
STATUS="disabled"
if [ "$INTERACTIVE" = True ] && ! is_uname_current; then
whiptail --msgbox "Could not find modules for the running kernel ($(uname -r))." 20 60 1
return 1
fi
if get_overlay_conf; then
DEFAULT=
CURRENT=1
STATUS="enabled"
fi
if [ "$INTERACTIVE" = True ]; then
whiptail --yesno "Would you like the overlay file system to be enabled?" $DEFAULT 20 60 2
RET=$?
else
RET=$1
fi
if [ $RET -eq $CURRENT ]; then
if [ $RET -eq 0 ]; then
if enable_overlayfs; then
STATUS="enabled"
ASK_TO_REBOOT=1
else
STATUS="unchanged"
fi
elif [ $RET -eq 1 ]; then
if disable_overlayfs; then
STATUS="disabled"
ASK_TO_REBOOT=1
else
STATUS="unchanged"
fi
else
return $RET
fi
fi
if [ "$INTERACTIVE" = True ]; then
whiptail --msgbox "The overlay file system is $STATUS." 20 60 1
fi
if get_overlay_now ; then
if get_bootro_conf; then
BPRO="read-only"
else
BPRO="writable"
fi
whiptail --msgbox "The boot partition is currently $BPRO. This cannot be changed while an overlay file system is enabled." 20 60 1
else
DEFAULT=--defaultno
CURRENT=0
STATUS="writable"
if get_bootro_conf; then
DEFAULT=
CURRENT=1
STATUS="read-only"
fi
if [ "$INTERACTIVE" = True ]; then
whiptail --yesno "Would you like the boot partition to be write-protected?" $DEFAULT 20 60 2
RET=$?
else
RET=$1
fi
if [ $RET -eq $CURRENT ]; then
if [ $RET -eq 0 ]; then
if enable_bootro; then
STATUS="read-only"
ASK_TO_REBOOT=1
else
STATUS="unchanged"
fi
elif [ $RET -eq 1 ]; then
if disable_bootro; then
STATUS="writable"
ASK_TO_REBOOT=1
else
STATUS="unchanged"
fi
else
return $RET
fi
fi
if [ "$INTERACTIVE" = True ]; then
whiptail --msgbox "The boot partition is $STATUS." 20 60 1
fi
fi
do_finish
}
do_overlayfs
@bathtime
Copy link

Script worked like a charm on my Debian testing system. I'd advise anyone using it to make a backup copy of their /boot/initrd.img-* to maybe /boot/initrd.img.bak before running it.

Of course, trying to disable overlay mode whilst in overlay is not possible, but easy to overcome:

When at the grub boot screen, press 'e' to edit. Take away the 'boot=overlay' flag. Change the line 'initrd /boot/initrd.img-overlay' to 'initrd /boot/initrd.img.bak' (or whatever your image was named). Then press F10 to boot. Once booted, run the script again, choosing the option to remove the overlay.

In any event, I just edited my /boot/grub/grub.cfg file to include both an overlay and a regular boot option, and it works perfectly.

Thanks so much for this!

@jfernandz
Copy link
Author

If you are interested in contributing ... I could release a more complete version and also with debian packaging 🤔

Thank you 👍

@bathtime
Copy link

I should let you know that I'm experienced enough to get this stuff working if instructions are given, but I don't think I'd be much help as a designer or with packaging.

Currently, I run my system off a USB stick. The system has a duplicate backup partition that can easily be synced, so it's just a few commands to restore like new. That said, I'm more than happy to take any risks and do testing for you—I test and break my system on a daily basis! ;)

Also, just incase you're interested, there's another somewhat similar project which mounts your system into ram (tmpfs): https://gist.github.com/avinash-oza/9791c4edd78a03540dc69d6fbf21bd9c

@AntonioSun
Copy link

I could release a more complete version and also with debian packaging

That'd be super.

Would you update it to a more complete version please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment