一 入口脚本函数:
do_mount_ubifs_overlay() {
ubifs_error_check_and_recover
{ ubifs_volume_support && \
try_ubifs_syscfg_mount && \
ubifs_syscfg_rootfs_pivot ; } || mount_no_ubifs_syscfg_mtd
}
首先介绍Shell脚本的一些基础知识。
1 在一个脚本函数中一个{}中的语句是作为一个整体的block执行。
2 cmda && cmdb 是指只有cmda执行成功(Shell中命令执行成功是指命令返回0)cmdb才会执行;
3 cmda || cmdb 是指cmda执行失败,cmdb命令才会执行。
基于如上,这个Shell函数先执行
ubifs_error_check_and_recover
然后执行{}的内容,作为一个整体block,执行成功后不会执行mount_no_ubifs_syscfg_mtd,
执行失败后才会执行mount_no_ubifs_syscfg_mtd。
下边看{}中的内容。
{ ubifs_volume_support && try_ubifs_syscfg_mount && ubifs_syscfg_rootfs_pivot ; }
先执行ubifs_volume_support,如果执行成功继续执行try_ubifs_syscfg_mount,如果执行成功继续执行ubifs_syscfg_rootfs_pivot。
中间有一个执行失败就会中断,且整体{}的结果是执行失败。
二 ubifs_error_check_and_recover
ubifs_error_check_and_recover() {
local mtdnum
ubifs_err="$(cat /proc/sys/kernel/ubifs_error)"
[ -z "$ubifs_err" -o "$ubifs_err" = "0" ] && return 0
mtdnum=$(($ubifs_err >> 16))
mtdnum=$(($mtdnum & 0xff))
echo "erase mtd${mtdnum} to recover ubifs error $ubifs_err"
mtd erase /dev/mtd${mtdnum}
echo 0 > /proc/sys/kernel/ubifs_error
return 0
}
/proc/sys/kernel/ubifs_error具体的含义可以参考kernel源码fs/ubifs/io.c里的ramdump_ubifs_flag的赋值,如果ubi文件系统有错误的时候,kernel会把相应的mtdnum的信息置入/proc/sys/kernel/ubifs_error。所以后边可以通过ubifs_error的值反算出是哪个mtd出现了错误。这里的修复方法是重新erase掉该mtd分区。
三 ubifs_volume_support
# return 1 on failed 0 for success
ubifs_volume_support() {
mtdpart_idx="$(find_mtd_index rootfs_data)"
[ -z "$mtdpart_idx" ] && return 1
# for SDTIM support
mtdpart_idx_oem="$(find_mtd_index oem_data$SLOT-mount)"
if [ -z "$mtdpart_idx_oem" ]
then
mtdpart_idx_oem="$(find_mtd_index oem_data$SLOT)"
if [ -z "$mtdpart_idx_oem" ]
then
# oem_data may has only one partition
mtdpart_idx_oem="$(find_mtd_index oem_data)"
[ -z "$mtdpart_idx_oem" ] && return 1
fi
fi
grep -qs ubifs /proc/filesystems || return 1
echo "found rootfs_data partition and ubifs support"
return 0
}
这里边的find_mtd_index是为了找寻一个分区的mtd序号。$SLOT是指"-a"或者"-b",或者为空。这个Shell函数主要是为了做一个基本判断,是不是支持ubi文件系统,是不是能找到系统预置的一些基本ubi分区信息。
四 try_ubifs_syscfg_mount
__try_ubifs_syscfg_mount() {
overlay_mountpoint=$1
if [ -z $overlay_mountpoint ]
then
overlay_mountpoint=/overlay
fi
recover_ubifs=0
[ ! -e /dev/ubi0 ] && ubiattach /dev/ubi_ctrl -m $mtdpart_idx -d 0 || recover_ubifs=1
if [ $recover_ubifs -eq 0 ]
then
ubi0_nod_id=`cat /sys/class/ubi/ubi0/dev | tr -s ":" " "`
[ ! -e /dev/ubi0 ] && mknod /dev/ubi0 c ${ubi0_nod_id}
if [ ! -e /sys/class/ubi/ubi0_0/dev ]
then
# no volume
recover_ubifs=1
else
# check for "data" volume
ubi0_0_nod_id=`cat /sys/class/ubi/ubi0_0/dev | tr -s ":" " "`
[ ! -e /dev/ubi0_0 ] && mknod /dev/ubi0_0 c ${ubi0_0_nod_id}
{ ubinfo /dev/ubi0_0 | grep Name | grep -qs "data" ; } || \
recover_ubifs=1
fi
fi
if [ $recover_ubifs -eq 1 ]
then
echo "ubifs syscfg partition is damaged"
echo "try to recover by formatting $mtdpart..."
[ -e /dev/ubi0 ] && ubidetach -m $mtdpart_idx
ubiformat -y -q /dev/mtd$mtdpart_idx
ubiattach -m $mtdpart_idx /dev/ubi_ctrl
ubi0_nod_id=`cat /sys/class/ubi/ubi0/dev | tr -s ":" " "`
[ ! -e /dev/ubi0 ] && mknod /dev/ubi0 c ${ubi0_nod_id}
ubimkvol /dev/ubi0 -n 1 -N etc -t dynamic -s 5MiB
ubimkvol /dev/ubi0 -n 2 -N nvm -t dynamic -s 4MiB
ubimkvol /dev/ubi0 -n 0 -N data -t dynamic --maxavsize
fi
# finally mount the ubifs
mount -t ubifs -o noatime ubi0:data /data || return 1
mount -t ubifs -o noatime ubi0:data /mnt || return 1
mount -t ubifs -o noatime ubi0:data /log || return 1
mount -t ubifs -o noatime ubi0:etc $overlay_mountpoint/etc || return 1
mount -t ubifs -o noatime ubi0:nvm $overlay_mountpoint/nvm || return 1
# clean up uci tmp file to avoid wirtable partition full
rm -rf $overlay_mountpoint/etc/root/config/.*.uci-*
return 0
}
try_ubifs_syscfg_mount() {
__try_ubifs_syscfg_mount || {
echo "roofs_data mount fail, try to recover by erase..."
umount $overlay_mountpoint/nvm
umount $overlay_mountpoint/etc
umount /log
umount /mnt
umount /data
mtd erase rootfs_data
__try_ubifs_syscfg_mount
}
return 0
}
这个Shell函数重点看__try_ubifs_syscfg_mount(syscfg原意是openwrt的系统配置分区,每个vendor平台可能有不同的延伸含义)
overlay_mountpoint这个变量指向的是/overlay。
这里主要关注recover_ubifs
其实这个函数主要是判定ubi0_0分区是否被损坏,然后重新格式化。我们考虑正常情况下
mount -t ubifs -o noatime ubi0:data /data || return 1
mount -t ubifs -o noatime ubi0:data /mnt || return 1
mount -t ubifs -o noatime ubi0:data /log || return 1
ubi0:data这个volume mount到三个目录 /data /mnt /log 一个分区挂载到多个目录,是不建议的,但是这里不知道为啥ASR采用这个机制。
mount -t ubifs -o noatime ubi0:etc $overlay_mountpoint/etc || return 1
mount -t ubifs -o noatime ubi0:nvm $overlay_mountpoint/nvm || return 1
ubi0:etc挂载到/overlay/etc
ubi0:nvm挂载到/overlay/nvm
再次梳理:rootfs_data这个mtd分区是ubi0这个占据,然后分为了3个volume!
五 ubifs_syscfg_rootfs_pivot
重点和精华来了,就是overlay和root的切换。
create_overlay() { # <lowerdir> <upper_dir> <target>
mkdir -p $2/root $2/work
/bin/mount -o noatime,lowerdir=$1,upperdir=$2/root,workdir=$2/work -t overlay "overlayfs:$2" $3
/bin/umount $2
}
ubifs_syscfg_rootfs_pivot() {
echo "switching to ubifs sysfs overlay"
# PIPE mode need to recover this file in /etc/init.d/network
cp -rf /etc/config/network /tmp/network_pipe
# Dir /etc and /NVM can be written after creating overlay
create_overlay /system/etc $overlay_mountpoint/etc /system/etc
create_overlay /NVM $overlay_mountpoint/nvm /NVM
ubifs_oem_data_mount
}