2021年起的 RaspberryPi 官網提供的 RaspiOS 内置了 Overlay 模式,啟用或者停用設定在 raspi-config 中進行。具體方法為在 shell 中運作 sudo raspi-config。
#-----------------------------------------------------------------------------------------------------------------------------
複制下面内容,儲存為readme.txt,使用時按照本文檔說明實施。
腳本用途
============================================================
将樹莓派系統在可讀寫模式和隻讀模式下轉換。
使用場景
============================================================
按照通常的方式,樹莓派系統安裝于TF/SD卡模式時,系統運作過程中,
可能會頻繁的對TF/SD卡做讀寫操作,容易造成TF/SD卡的損壞。
将系統設定為隻讀模式後,所有的讀寫操作均隻在記憶體中進行,不需要
對TF/SD卡進行寫,能有效延長TF/SD卡的使用壽命。
隻讀模式适用于用樹莓派做固定控制時使用。
支援環境
============================================================
硬體:RaspberryPi B+
軟體:raspbian-jessie
目前腳本隻對采用/dev/mmcblk0px方式索引磁盤分區的場景有效,對
采用PARTUUID方式索引磁盤分區時,腳本無效,待後續研究。
腳本組成
============================================================
overlay_enable.py 将系統由讀寫狀态切換至隻讀狀态,重新開機後生效。
overlay_disable.py将系統由隻讀狀态切換至讀寫狀态,重新開機後生效。
腳本使用
============================================================
在讀寫模式下,指令行執行
sudo python3 overlay_enable.py
或運作
chmod +x overlay_enable.py
設定為可執行後執行
sudo ./overlay_enable.py
将系統由讀寫狀态切換至隻讀狀态,重新開機後生效。
在隻讀模式下,指令行執行
sudo python3 overlay_disable.py
或運作
chmod +x overlay_disable.py
設定為可執行後執行
sudo ./overlay_disable.py
将系統由隻讀狀态切換至讀寫狀态,重新開機後生效。
隻讀狀态下臨時啟用可寫
============================================================
在指令行執行
sudo mount -o remount,rw /boot
将使/boot分區進入可寫模式,操作完畢運作
sudo mount -o remount,ro /boot
将/boot分區切換回隻讀狀态。
在指令行執行
sudo mount -o remount,rw /ro
将使/ro分區進入可寫模式,操作完畢運作
sudo mount -o remount,ro /ro
将/ro分區切換回隻讀狀态。
注:使用本腳本将系統切換進隻讀模式後,/dev/mmcblk0p2挂載到
/ro,故要使/dev/mmcblk0p2可寫,需要對/ro進行重新挂載。
要儲存的對/dev/mmcblk0p2中檔案的修改,都需要通過對/ro下對應
位置的檔案修改來實作。
補充說明
============================================================
Raspbian全系列官方下載下傳位址
https://downloads.raspberrypi.org/raspbian_lite/images/
官方網站放出的最晚一版raspbian-jessie為
2017-07-05-raspbian-jessie
該版本燒寫到TF/SD卡後,采用PARTUUID方式進行磁盤分區索引。
為适用本腳本,需要将磁盤索引更改為/dev/mmcblk0px方式,更改後
系統可以正常運作(raspbian-stretch及以後版本貌似不行)。
需要更改的位置有:
/boot/cmdline.txt 中,root=PARTUUID=....-02 更改為 root=/dev/mmcblk0p2
/etc/fstab中,PARTUUID=....-01更改為/dev/mmcblk0p1
PARTUUID=....-02更改為/dev/mmcblk0p2
先完成更改再運作腳本,腳本依賴前面修改的内容。
參考
============================================================
本腳本在
https://blog.csdn.net/zhufu86/article/details/78906046
介紹方法的基礎上完成。
複制下面内容為 overlay_enable.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 21 15:20:19 2019
@author: farman
"""
import os
def execute(command):
return os.system(command)
def step_1_disable_swap_files():
flag = 0
flag |= execute("sudo dphys-swapfile swapoff")
flag |= execute("sudo dphys-swapfile uninstall")
flag |= execute("sudo update-rc.d dphys-swapfile remove")
return flag
def step_2_update_system():
flag = 0
flag |= execute("sudo apt update")
flag |= execute("sudo apt upgrade -y")
return flag
def step_3_make_shell_script():
content = '''#!/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
# <http://www.gnu.org/licenses/>.
#
#
# 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 <<END
# Move ro and rw mounts to the new root
mount --move /mnt/mnt/lower/ /ro
if [ $? -ne 0 ]; then
echo "ERROR: could not move ro-root into newroot"
/bin/bash
fi
mount --move /mnt/mnt/rw /rw
if [ $? -ne 0 ]; then
echo "ERROR: could not move tempfs rw mount into newroot"
/bin/bash
fi
# Unmount unneeded mounts so we can unmout the old readonly root
umount /mnt/mnt
umount /mnt/proc
umount /mnt/dev
umount /mnt
# Continue with regular init
exec /sbin/init
END
)"
'''
with open('/boot/cmdline.txt', 'r') as f:
line = f.read()
begin = line.find('root=') + len('root=')
end = line.find(' ', begin)
part = line[begin:end]
content = content.replace("rootDev=/dev/mmcblk0p2", "rootDev="+part)
with open("/home/pi/overlayroot.sh", 'w') as f:
f.write(content)
flag = False
flag |= execute("sudo mv /home/pi/overlayroot.sh /sbin/overlayroot.sh")
flag |= execute("sudo chmod +x /sbin/overlayroot.sh")
return flag
def step_4_edit_cmdline():
with open("/boot/cmdline.txt", "r") as f:
line = f.read()
tail = "init=/sbin/overlayroot.sh"
if line.find(tail) == -1:
line = line.rstrip('\n')
line += ' '
line += tail
with open("/boot/cmdline.txt", 'w') as f:
f.write(line)
return
def step_5_edit_fstab():
lines = []
with open('/etc/fstab', 'r') as f:
for line in f:
lines.append(line.rstrip('\n'))
with open('/etc/fstab', 'w') as f:
for line in lines:
if line.find('/boot') > -1:
segs = line.split()
if segs[3].find('ro') == -1:
segs[3] += ',ro'
line = ' '.join(segs)
f.write(line + '\n')
return
def step_6_set_prompt():
with open('/home/pi/.bashrc', 'r') as f:
content = f.read()
if content.find('overlay') == -1:
content += '\n'
content += '''
find_overlay=`mount | grep overlay`
if [ ${#find_overlay} -gt 0 ]; then
PS1="[OVL] $PS1"
fi
'''
with open('/home/pi/.bashrc', 'w') as f:
f.write(content)
return
if __name__ == '__main__':
step_1_disable_swap_files()
#step_2_update_system()
step_3_make_shell_script()
step_4_edit_cmdline()
step_5_edit_fstab()
step_6_set_prompt()
複制下面代碼,儲存為overlay_disable.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 21 15:20:19 2019
@author: farman
"""
import os
def execute(command):
return os.system(command)
def step_1_make_partition_writable():
execute("sudo mount -o remount,rw /boot")
execute("sudo mount -o remount,rw /ro")
return
def step_2_edit_cmdline():
with open("/boot/cmdline.txt", "r") as f:
line = f.read()
tail = "init=/sbin/overlayroot.sh"
line = line.replace(tail, '')
with open("/boot/cmdline.txt", 'w') as f:
f.write(line)
return
def step_3_edit_fstab():
lines = []
with open('/ro/etc/fstab', 'r') as f:
for line in f:
lines.append(line.rstrip('\n'))
with open('/ro/etc/fstab', 'w') as f:
for line in lines:
if line.find('/boot') > -1:
segs = line.split(' ')
while segs.count(''):
segs.remove('')
segs[3] = segs[3].replace(',ro', '')
line = ' '.join(segs)
f.write(line + '\n')
return
if __name__ == '__main__':
step_1_make_partition_writable()
step_2_edit_cmdline()
step_3_edit_fstab()