前面我发过一篇blog说明如何使用overlayfs打造一个只读的不怕意外关机的树莓派Raspberry Pi。
https://blog.csdn.net/zhufu86/article/details/78906046
下面我来实现在树莓派的GPIO接口上用跳线(短路帽)选择启用或者禁用overlayfs影子系统。
【接线】
我是用了用了如下材料
--- 跳线帽(短路帽) * 1
--- 连接线 * 2 (可选)
--- LED * 1 (可选)
--- 电阻 1k欧姆 * 1 (可选)
如上图所示,在BCM GPIO 4 以及和右边的一位针脚(GND)上使用跳线帽,在BCM GPIO 18和其右边的一位针脚(GND)上连接LED,并串联1K电阻,LED的阴极接GND,阳极接电阻,电阻的另一端接GPIO 18。
接LED是为了在启动时指示脚本的运行状态,并不是必须要接的,如果不接也可以。
【原理】
需要在原overlayRoot.sh脚本的基础上,代码开头增加检测GPIO端口的代码。
首先,要mount sysfs,以便于后面访问GPIO
mount -t sysfs sysfs /sys
if [ $? -ne 0 ]; then
fail "ERROR: could not mount sysfs"
fi
设置GPIO引脚变量,可以按照你自己的实际接线更改设置。
# The jumper is on BCM GPIO 4
GPIO_PIN_JP=4
# The LED is connected on BCM GPIO 18
GPIO_PIN_LED=18
定义闪LED的函数,功能是点亮LED 0.4秒钟并熄灭。
# Function - Flash/light-up the LED one time
FlashLED()
{
# Export LED pin
echo $GPIO_PIN_LED > /sys/class/gpio/export
# Set LED pin as output port
echo out > /sys/class/gpio/gpio$GPIO_PIN_LED/direction
# Set LED pin output HIGH
echo 1 > /sys/class/gpio/gpio$GPIO_PIN_LED/value
sleep 0.4
# Set LED pin output LOW
echo 0 > /sys/class/gpio/gpio$GPIO_PIN_LED/value
# Set LED pin as input port
echo in > /sys/class/gpio/gpio$GPIO_PIN_LED/direction
# Unexport LED pin
echo $GPIO_PIN_LED > /sys/class/gpio/unexport
sleep 0.4
}
如果不打算连接LED,而且GPIO 18也可能别有他用,可以把这段代码改为无用代码:
# Function - Flash/light-up the LED one time
FlashLED()
{
echo I am a LED.
}
后面的代码开始干正事儿。
首先点亮LED 0.4秒后熄灭。
# Flash the LED one time
FlashLED
然后每隔50ms检测一次有没有跳线短接GPIO 4到GND,连续检测10次
# Export Jumper pin
echo "$GPIO_PIN_JP" > /sys/class/gpio/export
# Set Jumper pin as input port
echo in > /sys/class/gpio/gpio$GPIO_PIN_JP/direction
i=1
count=0
while [ "$i" -le 10 ]
do
# Detect the jumper pin every 0.05sec
sleep 0.05
# If the Jumper is shorted to GND
if [ $(cat "/sys/class/gpio/gpio${GPIO_PIN_JP}/value") = 0 ]; then
count=$((count+1))
fi
i=$((i+1))
done
# Uncomment to print the count value for test purpose
#echo $count
# Unexport the Jumper pin
echo $GPIO_PIN_JP > /sys/class/gpio/unexport
如果发现10次检测有9次以上为低电平(即GPIO短路到GND),则点亮LED 0.4秒后熄灭,然后继续常规的init过程(/sbin/init)而不开启overlayfs模式,否则继续后面的开启overlayfs模式的代码。 继续之前不忘记unmount sysfs。
if [ $count -ge 9 ]; then
# if the Jumper is shorted to GND, exit to the normal init.
# Flash the LED one time
FlashLED
umount /sys
echo [Info.] Overlayfs jumper setting DISABLE.
# Continue with regular init
exec /sbin/init
#exit 0
else
umount /sys
echo [Info.] Overlayfs jumper setting ENABLE.
# Continue with overlayfs activation process.
fi
【完整的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
#
# * Modified 2018-Jul-18 by Yang Chen
#
# 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
}
# Add the function +++
# Use a jumper on the Head connector to activate(OPEN) or
# deactivate(SHORT) the overlayfs mode
# Added by C.Yang 20180718
# jumper function code start @@@@@
mount -t sysfs sysfs /sys
if [ $? -ne 0 ]; then
fail "ERROR: could not mount sysfs"
fi
# The jumper is on BCM GPIO 4
GPIO_PIN_JP=4
# The LED is connected on BCM GPIO 18
GPIO_PIN_LED=18
# Function - Flash/light-up the LED one time
FlashLED()
{
# Export LED pin
echo $GPIO_PIN_LED > /sys/class/gpio/export
# Set LED pin as output port
echo out > /sys/class/gpio/gpio$GPIO_PIN_LED/direction
# Set LED pin output HIGH
echo 1 > /sys/class/gpio/gpio$GPIO_PIN_LED/value
sleep 0.4
# Set LED pin output LOW
echo 0 > /sys/class/gpio/gpio$GPIO_PIN_LED/value
# Set LED pin as input port
echo in > /sys/class/gpio/gpio$GPIO_PIN_LED/direction
# Unexport LED pin
echo $GPIO_PIN_LED > /sys/class/gpio/unexport
sleep 0.4
}
# Flash the LED one time
FlashLED
# Export Jumper pin
echo "$GPIO_PIN_JP" > /sys/class/gpio/export
# Set Jumper pin as input port
echo in > /sys/class/gpio/gpio$GPIO_PIN_JP/direction
i=1
count=0
while [ "$i" -le 10 ]
do
# Detect the jumper pin every 0.05sec
sleep 0.05
# If the Jumper is shorted to GND
if [ $(cat "/sys/class/gpio/gpio${GPIO_PIN_JP}/value") = 0 ]; then
count=$((count+1))
fi
i=$((i+1))
done
# Uncomment to print the count value for test purpose
#echo $count
# Unexport the Jumper pin
echo $GPIO_PIN_JP > /sys/class/gpio/unexport
if [ $count -ge 9 ]; then
# if the Jumper is shorted to GND, exit to the normal init.
# Flash the LED one time
FlashLED
umount /sys
echo [Info.] Overlayfs jumper setting DISABLE.
# Continue with regular init
exec /sbin/init
#exit 0
else
umount /sys
echo [Info.] Overlayfs jumper setting ENABLE.
# Continue with overlayfs activation process.
fi
# jumper function code END @@@@@
# 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 <
【备注】
启动之前使用跳线帽短路GPIO 4和GND,则启动过程会看到LED闪烁两次,启动后可以看到overlayfs并未启用。
启动之前如果空开GPIO 4和GND,即不短接跳线帽,则启动过程只会看到LED闪烁一次,启动后可以看到overlayfs已启用。
不连接LED,脚本也是可以正常工作的。GPIO 18如果别有他用,则需要按文中所述方法修改闪烁LED的那段函数。
理论上来说应该在内核启动时设置跳线帽连接的那个GPIO(我的例子里为GPIO 4)为上拉状态,不过我没有做任何处理,似乎也没问题。
欢迎邮件交流