使用overlayfs打造一个只读的不怕意外关机的树莓派Raspberry Pi

树莓派的本领就不多说了。但是在树莓派的应用场合,关机的时候还是显得尴尬,先不说执行 sudo halt 要么需要ssh上去,要么需要有键盘和显示器,更不要说,有的场景可能连网络和显示器都没有,真正的 headless。 但是如果不执行sudo halt直接关电源,那么有很大的概率会损坏SD卡上的文件系统,甚至损坏SD卡。

overlayfs是linux系统下的一种影子文件系统,它可以把真正的存储文件系统作为只读挂载,而把所有文件改动都存放在RAM中,关机或者重启就失效,真正的保护了存储文件系统。  下面就说一下我在树莓派中实施overlayfs的方法,其实很简单。

我参考了这个帖子中介绍的方法:

https://www.raspberrypi.org/forums/viewtopic.php?t=173063#p1151405

 

1. 首先,禁用交换空间,毕竟树莓派里的交换空间也是占用RAM(tempfs)的,所以如果使用了overlayfs后,交换空间就显得没有意义了。

执行如下3条命令:

 

  sudo dphys-swapfile swapoff
  sudo dphys-swapfile uninstall
  sudo update-rc.d dphys-swapfile remove


2. 更新系统,保证系统为最新状态

 

 

sudo apt-get update
sudo apt-get upgrade

 

 

 

 

 

3. 保存如下脚本为 overlayRoot.sh

 

#!/bin/sh
#  Read-only Root-FS for Raspian using overlayfs
#  Version 1.0
#
#  Created 2017 by Pascal Suter @ DALCO AG, Switzerland
#  to work on Raspian as custom init script
#  (raspbian does not use an initramfs on boot)
#
#  Modified 2017-Apr-21 by Tony McBeardsley 
#
#  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
#    .
#
#
#  Tested with Raspbian mini, 2017-01-11
#
#  This script will mount the root filesystem read-only and overlay it with a temporary tempfs 
#  which is read-write mounted. This is done using the overlayFS which is part of the linux kernel 
#  since version 3.18. 
#  when this script is in use, all changes made to anywhere in the root filesystem mount will be lost 
#  upon reboot of the system. The SD card will only be accessed as read-only drive, which significantly
#  helps to prolong its life and prevent filesystem coruption in environments where the system is usually
#  not shut down properly 
#
#  Install: 
#  copy this script to /sbin/overlayRoot.sh and add "init=/sbin/overlayRoot.sh" to the cmdline.txt 
#  file in the raspbian image's boot partition. 
#  I strongly recommend to disable swapping before using this. it will work with swap but that just does 
#  not make sens as the swap file will be stored in the tempfs which again resides in the ram.
#  run these commands on the booted raspberry pi BEFORE you set the init=/sbin/overlayRoot.sh boot option:
#  sudo dphys-swapfile swapoff
#  sudo dphys-swapfile uninstall
#  sudo update-rc.d dphys-swapfile remove
#
#  To install software, run upgrades and do other changes to the raspberry setup, simply remove the init= 
#  entry from the cmdline.txt file and reboot, make the changes, add the init= entry and reboot once more. 

fail(){
	echo -e "$1"
	/bin/bash
}


# Load overlay module
modprobe overlay
if [ $? -ne 0 ]; then
    fail "ERROR: missing overlay kernel module"
fi


# Mount /proc
mount -t proc proc /proc
if [ $? -ne 0 ]; then
    fail "ERROR: could not mount proc"
fi


# Create a writable fs on /mnt to then create our mountpoints 
mount -t tmpfs inittemp /mnt
if [ $? -ne 0 ]; then
    fail "ERROR: could not create a temporary filesystem to mount the base filesystems for overlayfs"
fi


# Mount a tmpfs under /mnt/rw
mkdir /mnt/rw
mount -t tmpfs root-rw /mnt/rw
if [ $? -ne 0 ]; then
    fail "ERROR: could not create tempfs for upper filesystem"
fi



# Identify root fs device, PARTUUID, mount options and fs type

#rootDev=`blkid -o list | awk '$3 == "/" {print $1}'`
# Changed here(point to / ) in case the cmd above doesn't work # By ChenYang 20171122
rootDev=/dev/mmcblk0p2
rootPARTUUID=`awk '$2 == "/" {print $1}' /etc/fstab`
rootMountOpt=`awk '$2 == "/" {print $4}' /etc/fstab`
rootFsType=`awk '$2 == "/" {print $3}' /etc/fstab`


# Mount original root filesystem readonly under /mnt/lower
mkdir /mnt/lower
mount -t ${rootFsType} -o ${rootMountOpt},ro ${rootDev} /mnt/lower
if [ $? -ne 0 ]; then
    fail "ERROR: could not ro-mount original root partition"
fi


# Mount the overlay filesystem
mkdir /mnt/rw/upper
mkdir /mnt/rw/work
mkdir /mnt/newroot
mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work overlayfs-root /mnt/newroot
if [ $? -ne 0 ]; then
    fail "ERROR: could not mount overlayFS"
fi


# Create mountpoints inside the new root filesystem-overlay
mkdir /mnt/newroot/ro
mkdir /mnt/newroot/rw

# Remove root mount from fstab (this is already a non-permanent modification)
grep -v "$rootPARTUUID" /mnt/lower/etc/fstab > /mnt/newroot/etc/fstab
echo "#the original root mount has been removed by overlayRoot.sh" >> /mnt/newroot/etc/fstab
echo "#this is only a temporary modification, the original fstab" >> /mnt/newroot/etc/fstab
echo "#stored on the disk can be found in /ro/etc/fstab" >> /mnt/newroot/etc/fstab


# Change to the new overlay root
cd /mnt/newroot
pivot_root . mnt
exec chroot . sh -c "$(cat <

 

 

注意,我的脚本和帖子里面的有些不同,我把

 

rootDev=`blkid -o list | awk '$3 == "/" {print $1}'`


改成了

 

 

rootDev=/dev/mmcblk0p2


不改的话,我这里overlayRoot.sh会运行失败

 

20180503更新)另外需要注意,如果在Windows下编辑overlayRoot.sh文件,需要保存为Unix文本文档格式,即行结束符为单个LF字符,而不是Windows风格的CR+LR两个字符。否则你可能会看到如下错误:

---[ end Kernel panic - not syncing: Requested init /sbin/overlayRoot.sh failed (error -2).

 


4. 将脚本拷贝到 /sbin/overlayRoot.sh

 

sudo cp overlayRoot.sh /sbin/overlayRoot.sh
sudo chmod a+x /sbin/overlayRoot.sh


5. 修改 cmdline.txt 文件,在行尾加上 "init=/sbin/overlayRoot.sh",如下是我的 /boot/cmdline.txt 文件的内容

 

 

pi@raspberrypi:~ $ cat /boot/cmdline.txt 
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=0e82c2e4-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait init=/sbin/overlayRoot.sh


6. 为了保证整个SD卡都是只读,将 /boot 分区也修改为只读, 修改 fstab 文件,把/boot 对应的行改为ro,我的/etc/fstab文件内容如下

 

 

pi@raspberrypi:~ $ cat /etc/fstab 
proc            /proc           proc    defaults          0       0
PARTUUID=0e82c2e4-01  /boot           vfat    defaults,ro          0       2
PARTUUID=0e82c2e4-02  /               ext4    defaults,noatime  0       1

 

 


7. 到这里 sudo reboot 重启,重启后,文件系统就处于影子系统的保护之下了,所有对于文件系统的改动,在重启后都将恢复原状。 用mount命令可以确认overlayfs的正常工作。

 

 

root-rw on /rw type tmpfs (rw,relatime)
/dev/mmcblk0p2 on /ro type ext4 (ro,noatime,data=ordered)
overlayfs-root on / type overlay (rw,relatime,lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,relatime)
devtmpfs on /dev type devtmpfs (rw,nosuid,size=470160k,nr_inodes=117540,mode=755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=25,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
sunrpc on /run/rpc_pipefs type rpc_pipefs (rw,relatime)
mqueue on /dev/mqueue type mqueue (rw,relatime)
configfs on /sys/kernel/config type configfs (rw,relatime)
/dev/mmcblk0p1 on /boot type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=94952k,mode=700,uid=1000,gid=1000)

 

 

 

原来的根文件系统 /dev/mmcblk0p2 改为挂载在 /ro ,并且是只读;/boot 也挂载为只读;而 / 的 type 变成了overlay。

 

好了,直接拔电试试看吧。事实上你可以反复尝试不正常关机,都不会有问题。

 

8. 以后如果想禁用overlayfs,可以修改 cmdline.txt 把 "init=/sbin/overlayRoot.sh" 删掉,重启即可。 当然修改前要先把 /boot remount 成可写状态。

更新系统等操作都需要先禁用overlayfs后再执行。

 

9. 如果临时想修改原 /boot 以及根文件系统的内容可以如下命令remount

 

要remount /boot为可写

 

pi@raspberrypi:~ $ sudo mount -o remount,rw /boot


原来的根文件系统在overlayfs下是挂载在 /ro 的,要remount的话

 

 

pi@raspberrypi:~ $ sudo mount -o remount,rw /ro

 

 

 

注意,remount文件系统为可写后,要重启和关机需要使用sudo reboot或者sudo halt,不可以强行非正常关机,以免损坏文件系统。

 

 

 

PS. 网上有人报告,如果在不接显示器的情况下(即 headless),如上脚本会运行失败,但是我不接显示器时并没有遇到此问题 。

PS2. 网上有另外一个脚本,链接如下

https://gist.githubusercontent.com/niun/34c945d70753fc9e2cc7/raw/3d60338cd8d8daf740692f426a5a1ec17839d613/root-ro

使用了  update-initramfs 的方式,我在官方系统中可以使用,但是在修改了内核的系统中,运行失败。

 

20180717后记

--- 如果开启了overlay以后,root分区的所有变动重启后都会丢失,如果要保存数据,可以缩小root分区后另外建立一个分区,使用FAT32,并mount为rw, sync, flush,用来保存数据,另外最好使用f2fs分区格式,据说f2fs就是针对SD卡之类的存储优化设计的。当然保存数据的频率不要太高,否则异常关机仍有可能会损坏SD卡,就失去使用overlay的意义了。 如果保存数据的频率很高,比如log,可以考虑保存到外接U盘。

--- 也有人把 /boot 挂载为rw并临时存放数据,我认为那是不可取的,因为 /boot 里面存放的是启动设置信息以及内核等。

--- 网上也有人修改脚本,(https://github.com/jacobalberty/root-ro),使得可以短路某GPIO后启动系统就可写,断开后启动就只读。这是个好主意,有空研究一下。  

【20180718更新】 另写了一篇来说明上面这一条的方法。   https://blog.csdn.net/zhufu86/article/details/81100710

【20180720更新】 为了方便识别当前系统是不是在overlayfs模式下,我在 .bashrc 的结尾加上如下几行(当然必须在非overlayfs下修改,否则修改内容不会保存),这样如果是在overlayfs模式下,命令提示行的前面会加上“[OVL]”

find_overlay=`mount | grep overlay`

if [ ${#find_overlay} -gt 0 ]; then
  PS1="[OVL] $PS1"
fi

 

欢迎交流

 

 

你可能感兴趣的:(使用overlayfs打造一个只读的不怕意外关机的树莓派Raspberry Pi)