-
-
Save zhongsheng94/71db7c397ed5f577f725f120e7b19c39 to your computer and use it in GitHub Desktop.
Read-only Root-FS with overlayfs for Raspian
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# | |
# Read-only Root-FS for Raspian | |
# | |
# Modified 2018 by Zhong Sheng to support using filesystem other than tmpfs as upper filesystem | |
# | |
# Modified 2015 by Pascal Rosin to work on raspian-ua-netinst with | |
# overlayfs integrated in Linux Kernel >= 3.18. | |
# | |
# Originally written by Axel Heider (Copyright 2012) for Ubuntu 11.10. | |
# This version can be found here: | |
# https://help.ubuntu.com/community/aufsRootFileSystemOnUsbFlash#Overlayfs | |
# | |
# Based on scripts from | |
# Sebastian P. | |
# Nicholas A. Schembri State College PA USA | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see | |
# <http://www.gnu.org/licenses/>. | |
# | |
# | |
# Changelog: | |
# | |
# v1.0.0 | |
# - written by Axel Heider for Ubuntu 11.10 | |
# | |
# v2.0.0 | |
# - Modified to work with overlayfs integrated in Linux Kernel (>= 3.18) | |
# - introduce workdir needed for new overlayfs | |
# - change `mount --move` to `mount -o move` to drop busybox requirement | |
# - Tested with raspian-ua-netinst v1.0.7 | |
# (Linux 3.18.0-trunk-rpi, Debian 3.18.5) on a Raspberry Pi. | |
# The aufs part is not tested! | |
# | |
# Notes: | |
# * no changes to the root fs are made by this script. | |
# * if /home/[user] is on the RO root fs and layed fs is tmpfs, files are not saved. | |
# | |
# Used Boot Parameters: | |
# | |
# disable-root-ro={some_string} - set it to *true* to mount root fs regularly | |
# root-ro-driver={overlay,aufs} - layered fs drvier to be used, if not specified, it will use *overlay* | |
# upper-device={$upper_device} - upper device, same format described in `man 5 fstab` and `man 8 mount`, if not specified, it will use *tmpfs* as upper fs | |
# upper-fs-driver={$upper_fs_driver} - specified the fs type of upper device, this isn't necessary when upper device is *tmpfs* | |
# | |
# Install: | |
# | |
# Setup Boot Parameters: | |
# - add `upper-device=${upper_device} upper-fs-driver={$upper_fs_driver}` to the line in /boot/cmdline.txt | |
# - add `root-ro-driver=${root-ro-driver}` to the line in /boot/cmdline.txt | |
# | |
# Update Initramfs: | |
# - # cp ${/path/to/root-ro} /etc/initramfs-tools/scripts/init-bottom/root-ro | |
# - # chmod 0755 /etc/initramfs-tools/scripts/init-bottom/root-ro | |
# - # echo "${root-ro-driver}" >> /etc/initramfs-tools/modules if layered fs drvier not included | |
# - # echo "${upper_fs_drvier}" >> /etc/initramfs-tools/modules if upper fs drvier not included | |
# - # update-initramfs -u | |
# | |
# Enable Initramfs: | |
# - # echo "initramfs ${name-of-initramfs-file} followkernel" | |
# | |
# | |
# Disable read-only root fs | |
# * option 1: kernel boot parameter "disable-root-ro=true" | |
# * option 2: create file "/disable-root-ro" | |
# | |
# ROOT_RO_DRIVER variable controls which driver isused for the ro/rw layering | |
# Supported drivers are: overlayfs, aufs | |
# the kernel parameter "root-ro-driver=[driver]" can be used to initialize | |
# the variable ROOT_RO_DRIVER. If nothing is given, overlayfs is used. | |
# | |
# no pre requirement | |
PREREQ="" | |
prereqs() | |
{ | |
echo "${PREREQ}" | |
} | |
case "$1" in | |
prereqs) | |
prereqs | |
exit 0 | |
;; | |
esac | |
# import /usr/share/initramfs-tools/scripts/functions | |
. /scripts/functions | |
MYTAG="root-ro" | |
DISABLE_MAGIC_FILE="/disable-root-ro" | |
# parse kernel boot command line | |
ROOT_RO_DRIVER= | |
DISABLE_ROOT_RO= | |
UPPER_DEVICE= | |
UPPER_FS_DRIVER= | |
for CMD_PARAM in $(cat /proc/cmdline); do | |
case ${CMD_PARAM} in | |
disable-root-ro=*) | |
DISABLE_ROOT_RO=${CMD_PARAM#disable-root-ro=} | |
;; | |
root-ro-driver=*) | |
ROOT_RO_DRIVER=${CMD_PARAM#root-ro-driver=} | |
;; | |
upper-device=*) | |
UPPER_DEVICE=${CMD_PARAM#upper-device=} | |
;; | |
upper-fs-driver=*) | |
UPPER_FS_DRIVER=${CMD_PARAM#upper-fs-driver=} | |
;; | |
esac | |
done | |
log_message_and_exit() | |
{ | |
log_failure_msg "$1" | |
exit 0 | |
} | |
# check if read-only root fs is disabled | |
[ ! -z "${DISABLE_ROOT_RO}" ] && log_message_and_exit "${MYTAG}: disabled, found boot parameter disable-root-ro=${DISABLE_ROOT_RO}" | |
[ -e "${rootmnt}${DISABLE_MAGIC_FILE}" ] && log_message_and_exit "${MYTAG}: disabled, found file ${rootmnt}${DISABLE_MAGIC_FILE}" | |
########################################### | |
## | |
## START TO MOUNT LAYERED FILESYSTEM | |
## | |
########################################### | |
# set those undefined var to default | |
if [ -z "${ROOT_RO_DRIVER}" ]; then | |
ROOT_RO_DRIVER=overlay | |
fi | |
if [ -z "${UPPER_DEVICE}" ]; then | |
UPPER_DEVICE=tmpfs-root | |
UPPER_FS_DRIVER=tmpfs | |
elif [ -z "${UPPER_FS_DRIVER}" ]; then | |
log_failure_msg "${MYTAG}: upper device ${UPPER_DEVICE}'s driver is unknown,fallback to tmpfs" | |
UPPER_DEVICE=tmpfs-root | |
UPPER_FS_DRIVER=tmpfs | |
fi | |
move_lower_ro_fs() | |
{ | |
OLD_MOUNT_POINT="$1" | |
NEW_MOUNT_POINT="$2" | |
# make the mount point on the init root fs ${ROOT_RO} | |
[ -d ${NEW_MOUNT_POINT} ] || mkdir -p ${NEW_MOUNT_POINT} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 4: failed to create ${NEW_MOUNT_POINT}" | |
return 1 | |
fi | |
# root is mounted on ${rootmnt}, move it to ${ROOT_RO}. | |
mount -o move ${OLD_MOUNT_POINT} ${NEW_MOUNT_POINT} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 7: failed to move root away from ${OLD_MOUNT_POINT} to ${NEW_MOUNT_POINT}" | |
return 1 | |
fi | |
} | |
mount_upper_fs() | |
{ | |
UPPER_MOUNT_POINT="$1" | |
UPPER_DEVICE="$2" | |
UPPER_FS_DRIVER="$3" | |
[ -d ${UPPER_MOUNT_POINT} ] || mkdir -p ${UPPER_MOUNT_POINT} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 3: failed to create dir ${UPPER_MOUNT_POINT}" | |
return 1 | |
fi | |
# mount the upper filesystem | |
mount -t "${UPPER_FS_DRIVER}" "${UPPER_DEVICE}" ${UPPER_MOUNT_POINT} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 3: failed to mount upper fs ${UPPER_DEVICE}" | |
return 1 | |
fi | |
} | |
mount_layered_fs() | |
{ | |
LAYED_FS_DRIVER="$1" | |
ROOT_LAYERED="$2" | |
ROOT_UPPER="$3" | |
ROOT_LOWER="$4" | |
# check if kernel module exists | |
modprobe -qb ${LAYED_FS_DRIVER} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 2: missing kernel module ${LAYED_FS_DRIVER} for layered fs" | |
return 1 | |
fi | |
# mount virtual fs ${rootmnt} with rw-fs ${ROOT_RW} on top or ro-fs ${ROOT_RO}. | |
case ${LAYED_FS_DRIVER} in | |
overlay) | |
setup_for_overlay "${ROOT_UPPER}" | |
mount -t overlay -o lowerdir=${ROOT_LOWER},upperdir=${ROOT_UPPER}/upper,workdir=${ROOT_UPPER}/work overlay ${ROOT_LAYERED} | |
return $? | |
;; | |
# aufs) | |
# MOUNT_PARMS="-t aufs -o dirs=${ROOT_UPPER}:${ROOT_LOWER}=ro aufs-root ${ROOT_LAYERED}" | |
# ;; | |
*) | |
log_failure_msg "${MYTAG} ERROR 1: invalid LAYED_FS_DRIVER ${LAYED_FS_DRIVER}" | |
return 1 | |
;; | |
esac | |
} | |
setup_for_overlay() | |
{ | |
OVERLAY_UPPER="$1" | |
# create necessary folders | |
[ -d ${OVERLAY_UPPER}/upper ] || mkdir -p ${OVERLAY_UPPER}/upper | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 6.1: failed to create ${OVERLAY_UPPER}/upper" | |
return 1 | |
fi | |
[ -d ${OVERLAY_UPPER}/work ] || mkdir -p ${OVERLAY_UPPER}/work | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 6.2: failed to create ${OVERLAY_UPPER}/work" | |
return 1 | |
fi | |
} | |
move_mount_to_layered_root() | |
{ | |
OLD_MOUNT_POINT="$1" | |
NEW_MOUNT_POINT="$2" | |
[ -d ${NEW_MOUNT_POINT} ] || mkdir -p ${NEW_MOUNT_POINT} | |
mount -o move ${OLD_MOUNT_POINT} ${NEW_MOUNT_POINT} | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 9: failed to move ${OLD_MOUNT_POINT} to ${NEW_MOUNT_POINT}" | |
return 1 | |
fi | |
} | |
# generic settings | |
# ${ROOT} and ${rootmnt} are predefined by caller of this script. Note that | |
# the root fs ${rootmnt} it mounted readonly on the initrams, which fits nicely | |
# for our purposes. | |
ROOT_RO=/mnt/root-ro | |
ROOT_RW=/mnt/root-rw | |
# move lower filesystem to ${ROOT_RO} | |
move_lower_ro_fs "${rootmnt}" "${ROOT_RO}" \ | |
&& log_success_msg "${MYTAG} sucessfully move lower fs to ${ROOT_RO}" \ | |
|| panic "${MYTAG} ERROR 8: failed to move lower fs" | |
# mount upper filesystem | |
mount_upper_fs "${ROOT_RW}" "${UPPER_DEVICE}" "${UPPER_FS_DRIVER}" && \ | |
log_success_msg "${MYTAG} sucessfully mount upper fs ${UPPER_DEVICE} to ${ROOT_RW}" | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} INFO: fallback upper filesystem to tmpfs" | |
umount "${ROOT_RW}" | |
mount_upper_fs "${ROOT_RW}" tmpfs-root tmpfs || panic "${MYTAG} ERROR 8: failed to fallback to tmpfs" | |
fi | |
# there is nothing left at ${rootmnt} now. So for any error we get we should | |
# either do recovery to restore ${rootmnt} for drop to a initramfs shell using | |
# "panic". Otherwise the boot process is very likely to fail with even more | |
# errors and leave the system in a wired state. | |
mount_layered_fs "${ROOT_RO_DRIVER}" "${rootmnt}" "${ROOT_RW}" "${ROOT_RO}" | |
if [ $? -ne 0 ]; then | |
# thats bad, drop to shell to let the user try fixing this | |
panic "${MYTAG} ERROR 8: failed to create new ro/rw layerd ${rootmnt}" | |
exit 0 | |
else | |
log_success_msg "${MYTAG} sucessfully mount ${ROOT_RO_DRIVER} layered fs" | |
fi | |
# now the real root fs is on ${ROOT_RO} of the init file system, our layered | |
# root fs is set up at ${rootmnt}. So we can write anywhere in {rootmnt} and the | |
# changes will end up in ${ROOT_RW} while ${ROOT_RO} it not touched. However | |
# ${ROOT_RO} and ${ROOT_RW} are on the initramfs root fs, which will be removed | |
# an replaced by ${rootmnt}. Thus we must move ${ROOT_RO} and ${ROOT_RW} to the | |
# rootfs visible later, ie. ${rootmnt}${ROOT_RO} and ${rootmnt}${ROOT_RO}. | |
# Since the layered ro/rw is already up, these changes also end up on | |
# ${ROOT_RW} while ${ROOT_RO} is not touched. | |
move_mount_to_layered_root "${ROOT_RO}" "${rootmnt}/${ROOT_RO}" | |
move_mount_to_layered_root "${ROOT_RW}" "${rootmnt}/${ROOT_RW}" | |
# technically, everything is set up nicely now. Since ${rootmnt} had beend | |
# mounted read-only on the initfamfs already, ${rootmnt}${ROOT_RO} is it, too. | |
# Now we init process could run - but unfortunately, we may have to prepare | |
# some more things here. | |
# Basically, there are two ways to deal with the read-only root fs. If the | |
# system is made aware of this, things can be simplified a lot. | |
# If it is not, things need to be done to our best knowledge. | |
# | |
# So we assume here, the system does not really know about our read-only root fs. | |
# | |
# Let's deal with /etc/fstab first. It usually contains an entry for the root | |
# fs, which is no longer valid now. We have to remove it and add our new | |
# ${ROOT_RO} entry. | |
# Remember we are still on the initramfs root fs here, so we have to work on | |
# ${rootmnt}/etc/fstab. The original fstab is ${rootmnt}${ROOT_RO}/etc/fstab. | |
## TODO | |
modify_fstab_on_layered_fs() | |
{ | |
ROOT_TYPE=$(cat /proc/mounts | ${rootmnt}/bin/grep ${ROOT} | ${rootmnt}/usr/bin/cut -d' ' -f3) | |
ROOT_OPTIONS=$(cat /proc/mounts | ${rootmnt}/bin/grep ${ROOT} | ${rootmnt}/usr/bin/cut -d' ' -f4) | |
cat ${rootmnt}/etc/fstab | ${rootmnt}/bin/grep " ${ROOT_RO} " | |
if [ $? -ne 0 ];then | |
echo "${ROOT} ${ROOT_RO} ${ROOT_TYPE} ${ROOT_OPTIONS} 0 0" > ${rootmnt}/etc/fstab.new | |
fi | |
#remove root entry and swap from fstab | |
cat ${rootmnt}/etc/fstab | ${rootmnt}/bin/grep -v ' / ' | ${rootmnt}/bin/grep -v swap |${rootmnt}/bin/grep -v -F " ${ROOT_RO} " >>${rootmnt}/etc/fstab.new | |
${rootmnt}/bin/mv ${rootmnt}/etc/fstab.new ${rootmnt}/etc/fstab | |
if [ $? -ne 0 ]; then | |
log_failure_msg "${MYTAG} ERROR 12: failed to modify etc/fstab" | |
exit 0 | |
fi | |
} | |
modify_fstab_on_layered_fs | |
# now we are done. Additinal steps may be necessary depending on the actualy | |
# distribution and/or its configuration. | |
log_success_msg "${MYTAG} curr mount table:\n`mount`" | |
log_success_msg "${MYTAG} sucessfully set up ro/tmpfs-rw layered root fs using ${ROOT_RO_DRIVER}" | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment