使用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条命令:

[plain]  view plain  copy
  1. sudo dphys-swapfile swapoff  
  2. sudo dphys-swapfile uninstall  
  3. sudo update-rc.d dphys-swapfile remove  

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

[plain]  view plain  copy
  1. sudo apt-get update  
  2. sudo ap-get upgrade  


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

[plain]  view plain  copy
  1. #!/bin/sh  
  2. #  Read-only Root-FS for Raspian using overlayfs  
  3. #  Version 1.0  
  4. #  
  5. #  Created 2017 by Pascal Suter @ DALCO AG, Switzerland  
  6. #  to work on Raspian as custom init script  
  7. #  (raspbian does not use an initramfs on boot)  
  8. #  
  9. #  Modified 2017-Apr-21 by Tony McBeardsley   
  10. #  
  11. #  This program is free software: you can redistribute it and/or modify  
  12. #  it under the terms of the GNU General Public License as published by  
  13. #  the Free Software Foundation, either version 3 of the License, or  
  14. #  (at your option) any later version.  
  15. #  
  16. #  This program is distributed in the hope that it will be useful,  
  17. #  but WITHOUT ANY WARRANTY; without even the implied warranty of  
  18. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
  19. #  GNU General Public License for more details.  
  20. #  
  21. #    You should have received a copy of the GNU General Public License  
  22. #    along with this program.  If not, see  
  23. #    <http://www.gnu.org/licenses/>.  
  24. #  
  25. #  
  26. #  Tested with Raspbian mini, 2017-01-11  
  27. #  
  28. #  This script will mount the root filesystem read-only and overlay it with a temporary tempfs   
  29. #  which is read-write mounted. This is done using the overlayFS which is part of the linux kernel   
  30. #  since version 3.18.   
  31. #  when this script is in use, all changes made to anywhere in the root filesystem mount will be lost   
  32. #  upon reboot of the system. The SD card will only be accessed as read-only drive, which significantly  
  33. #  helps to prolong its life and prevent filesystem coruption in environments where the system is usually  
  34. #  not shut down properly   
  35. #  
  36. #  Install:   
  37. #  copy this script to /sbin/overlayRoot.sh and add "init=/sbin/overlayRoot.sh" to the cmdline.txt   
  38. #  file in the raspbian image's boot partition.   
  39. #  I strongly recommend to disable swapping before using this. it will work with swap but that just does   
  40. #  not make sens as the swap file will be stored in the tempfs which again resides in the ram.  
  41. #  run these commands on the booted raspberry pi BEFORE you set the init=/sbin/overlayRoot.sh boot option:  
  42. #  sudo dphys-swapfile swapoff  
  43. #  sudo dphys-swapfile uninstall  
  44. #  sudo update-rc.d dphys-swapfile remove  
  45. #  
  46. #  To install software, run upgrades and do other changes to the raspberry setup, simply remove the init=   
  47. #  entry from the cmdline.txt file and reboot, make the changes, add the init= entry and reboot once more.   
  48.   
  49. fail(){  
  50.     echo -e "$1"  
  51.     /bin/bash  
  52. }  
  53.   
  54.   
  55. # Load overlay module  
  56. modprobe overlay  
  57. if [ $? -ne 0 ]; then  
  58.     fail "ERROR: missing overlay kernel module"  
  59. fi  
  60.   
  61.   
  62. # Mount /proc  
  63. mount -t proc proc /proc  
  64. if [ $? -ne 0 ]; then  
  65.     fail "ERROR: could not mount proc"  
  66. fi  
  67.   
  68.   
  69. # Create a writable fs on /mnt to then create our mountpoints   
  70. mount -t tmpfs inittemp /mnt  
  71. if [ $? -ne 0 ]; then  
  72.     fail "ERROR: could not create a temporary filesystem to mount the base filesystems for overlayfs"  
  73. fi  
  74.   
  75.   
  76. # Mount a tmpfs under /mnt/rw  
  77. mkdir /mnt/rw  
  78. mount -t tmpfs root-rw /mnt/rw  
  79. if [ $? -ne 0 ]; then  
  80.     fail "ERROR: could not create tempfs for upper filesystem"  
  81. fi  
  82.   
  83.   
  84.   
  85. # Identify root fs device, PARTUUID, mount options and fs type  
  86.   
  87. #rootDev=`blkid -o list | awk '$3 == "/" {print $1}'`  
  88. # Changed here(point to / ) in case the cmd above doesn't work # By ChenYang 20171122  
  89. rootDev=/dev/mmcblk0p2  
  90. rootPARTUUID=`awk '$2 == "/" {print $1}' /etc/fstab`  
  91. rootMountOpt=`awk '$2 == "/" {print $4}' /etc/fstab`  
  92. rootFsType=`awk '$2 == "/" {print $3}' /etc/fstab`  
  93.   
  94.   
  95. # Mount original root filesystem readonly under /mnt/lower  
  96. mkdir /mnt/lower  
  97. mount -t ${rootFsType} -o ${rootMountOpt},ro ${rootDev} /mnt/lower  
  98. if [ $? -ne 0 ]; then  
  99.     fail "ERROR: could not ro-mount original root partition"  
  100. fi  
  101.   
  102.   
  103. # Mount the overlay filesystem  
  104. mkdir /mnt/rw/upper  
  105. mkdir /mnt/rw/work  
  106. mkdir /mnt/newroot  
  107. mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work overlayfs-root /mnt/newroot  
  108. if [ $? -ne 0 ]; then  
  109.     fail "ERROR: could not mount overlayFS"  
  110. fi  
  111.   
  112.   
  113. # Create mountpoints inside the new root filesystem-overlay  
  114. mkdir /mnt/newroot/ro  
  115. mkdir /mnt/newroot/rw  
  116.   
  117. # Remove root mount from fstab (this is already a non-permanent modification)  
  118. grep -v "$rootPARTUUID" /mnt/lower/etc/fstab > /mnt/newroot/etc/fstab  
  119. echo "#the original root mount has been removed by overlayRoot.sh" >> /mnt/newroot/etc/fstab  
  120. echo "#this is only a temporary modification, the original fstab" >> /mnt/newroot/etc/fstab  
  121. echo "#stored on the disk can be found in /ro/etc/fstab" >> /mnt/newroot/etc/fstab  
  122.   
  123.   
  124. # Change to the new overlay root  
  125. cd /mnt/newroot  
  126. pivot_root . mnt  
  127. exec chroot . sh -c "$(cat <<END  
  128.   
  129.     # Move ro and rw mounts to the new root  
  130.     mount --move /mnt/mnt/lower/ /ro  
  131.     if [ $? -ne 0 ]; then  
  132.         echo "ERROR: could not move ro-root into newroot"  
  133.         /bin/bash  
  134.     fi  
  135.     mount --move /mnt/mnt/rw /rw  
  136.     if [ $? -ne 0 ]; then  
  137.         echo "ERROR: could not move tempfs rw mount into newroot"  
  138.         /bin/bash  
  139.     fi  
  140.   
  141.     # Unmount unneeded mounts so we can unmout the old readonly root  
  142.     umount /mnt/mnt  
  143.     umount /mnt/proc  
  144.     umount /mnt/dev  
  145.     umount /mnt  
  146.   
  147.     # Continue with regular init  
  148.     exec /sbin/init  
  149. END  
  150. )"  


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

[plain]  view plain  copy
  1. rootDev=`blkid -o list | awk '$3 == "/" {print $1}'`  

改成了

[plain]  view plain  copy
  1. rootDev=/dev/mmcblk0p2  

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




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

[plain]  view plain  copy
  1. sudo cp overlayRoot.sh /sbin/overlayRoot.sh  
  2. sudo chmod a+x /sbin/overlayRoot.sh  

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

[plain]  view plain  copy
  1. pi@raspberrypi:~ $ cat /boot/cmdline.txt   
  2. 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文件内容如下

[plain]  view plain  copy
  1. pi@raspberrypi:~ $ cat /etc/fstab   
  2. proc            /proc           proc    defaults          0       0  
  3. PARTUUID=0e82c2e4-01  /boot           vfat    defaults,ro          0       2  
  4. PARTUUID=0e82c2e4-02  /               ext4    defaults,noatime  0       1  




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

[plain]  view plain  copy
  1. root-rw on /rw type tmpfs (rw,relatime)  
  2. /dev/mmcblk0p2 on /ro type ext4 (ro,noatime,data=ordered)  
  3. overlayfs-root on / type overlay (rw,relatime,lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work)  
  4. sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)  
  5. proc on /proc type proc (rw,relatime)  
  6. devtmpfs on /dev type devtmpfs (rw,nosuid,size=470160k,nr_inodes=117540,mode=755)  
  7. tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)  
  8. devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)  
  9. tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)  
  10. tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)  
  11. tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)  
  12. cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)  
  13. cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)  
  14. cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)  
  15. cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)  
  16. cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)  
  17. cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)  
  18. cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)  
  19. cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)  
  20. systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=25,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)  
  21. debugfs on /sys/kernel/debug type debugfs (rw,relatime)  
  22. sunrpc on /run/rpc_pipefs type rpc_pipefs (rw,relatime)  
  23. mqueue on /dev/mqueue type mqueue (rw,relatime)  
  24. configfs on /sys/kernel/config type configfs (rw,relatime)  
  25. /dev/mmcblk0p1 on /boot type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)  
  26. 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为可写

[plain]  view plain  copy
  1. pi@raspberrypi:~ $ sudo mount -o remount,rw /boot  

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

[plain]  view plain  copy
  1. 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 的方式,我在官方系统中可以使用,但是在修改了内核的系统中,运行失败。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值