mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-22 23:12:32 +00:00
af34733593
It has been reported that ubinized nand sysupgrade fails under certain circumstances, being unable to detach the existing ubi partition due to volumes within the partition being mounted. This is an attempt to solve such issues by unmounting and removing ubiblock devices and unmounting ubi volumes within the target partition prior to detaching and formatting it. Signed-off-by: Rodrigo Balerdi <lanchon@gmail.com>
404 lines
10 KiB
Bash
404 lines
10 KiB
Bash
# Copyright (C) 2014 OpenWrt.org
|
|
#
|
|
|
|
. /lib/functions.sh
|
|
|
|
# 'kernel' partition or UBI volume on NAND contains the kernel
|
|
CI_KERNPART="${CI_KERNPART:-kernel}"
|
|
|
|
# 'ubi' partition on NAND contains UBI
|
|
CI_UBIPART="${CI_UBIPART:-ubi}"
|
|
|
|
# 'rootfs' UBI volume on NAND contains the rootfs
|
|
CI_ROOTPART="${CI_ROOTPART:-rootfs}"
|
|
|
|
ubi_mknod() {
|
|
local dir="$1"
|
|
local dev="/dev/$(basename $dir)"
|
|
|
|
[ -e "$dev" ] && return 0
|
|
|
|
local devid="$(cat $dir/dev)"
|
|
local major="${devid%%:*}"
|
|
local minor="${devid##*:}"
|
|
mknod "$dev" c $major $minor
|
|
}
|
|
|
|
nand_find_volume() {
|
|
local ubidevdir ubivoldir
|
|
ubidevdir="/sys/devices/virtual/ubi/$1"
|
|
[ ! -d "$ubidevdir" ] && return 1
|
|
for ubivoldir in $ubidevdir/${1}_*; do
|
|
[ ! -d "$ubivoldir" ] && continue
|
|
if [ "$( cat $ubivoldir/name )" = "$2" ]; then
|
|
basename $ubivoldir
|
|
ubi_mknod "$ubivoldir"
|
|
return 0
|
|
fi
|
|
done
|
|
}
|
|
|
|
nand_find_ubi() {
|
|
local ubidevdir ubidev mtdnum
|
|
mtdnum="$( find_mtd_index $1 )"
|
|
[ ! "$mtdnum" ] && return 1
|
|
for ubidevdir in /sys/devices/virtual/ubi/ubi*; do
|
|
[ ! -d "$ubidevdir" ] && continue
|
|
cmtdnum="$( cat $ubidevdir/mtd_num )"
|
|
[ ! "$mtdnum" ] && continue
|
|
if [ "$mtdnum" = "$cmtdnum" ]; then
|
|
ubidev=$( basename $ubidevdir )
|
|
ubi_mknod "$ubidevdir"
|
|
echo $ubidev
|
|
return 0
|
|
fi
|
|
done
|
|
}
|
|
|
|
nand_get_magic_long() {
|
|
dd if="$1" skip=$2 bs=4 count=1 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
|
|
}
|
|
|
|
get_magic_long_tar() {
|
|
( tar xf $1 $2 -O | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
|
|
}
|
|
|
|
identify_magic() {
|
|
local magic=$1
|
|
case "$magic" in
|
|
"55424923")
|
|
echo "ubi"
|
|
;;
|
|
"31181006")
|
|
echo "ubifs"
|
|
;;
|
|
"68737173")
|
|
echo "squashfs"
|
|
;;
|
|
"d00dfeed")
|
|
echo "fit"
|
|
;;
|
|
"4349"*)
|
|
echo "combined"
|
|
;;
|
|
*)
|
|
echo "unknown $magic"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
identify() {
|
|
identify_magic $(nand_get_magic_long "$1" "${2:-0}")
|
|
}
|
|
|
|
identify_tar() {
|
|
identify_magic $(get_magic_long_tar "$1" "$2")
|
|
}
|
|
|
|
nand_restore_config() {
|
|
local ubidev=$( nand_find_ubi "$CI_UBIPART" )
|
|
local ubivol="$( nand_find_volume $ubidev rootfs_data )"
|
|
if [ ! "$ubivol" ]; then
|
|
ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
|
|
if [ ! "$ubivol" ]; then
|
|
echo "cannot find ubifs data volume"
|
|
return 1
|
|
fi
|
|
fi
|
|
mkdir /tmp/new_root
|
|
if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
|
|
echo "cannot mount ubifs volume $ubivol"
|
|
rmdir /tmp/new_root
|
|
return 1
|
|
fi
|
|
if mv "$1" "/tmp/new_root/$BACKUP_FILE"; then
|
|
if umount /tmp/new_root; then
|
|
echo "configuration saved"
|
|
rmdir /tmp/new_root
|
|
return 0
|
|
fi
|
|
else
|
|
umount /tmp/new_root
|
|
fi
|
|
echo "could not save configuration to ubifs volume $ubivol"
|
|
rmdir /tmp/new_root
|
|
return 1
|
|
}
|
|
|
|
nand_remove_ubiblock() {
|
|
local ubivol="$1"
|
|
|
|
local ubiblk="ubiblock${ubivol:3}"
|
|
if [ -e "/dev/$ubiblk" ]; then
|
|
umount "/dev/$ubiblk" && echo "unmounted /dev/$ubiblk" || :
|
|
if ! ubiblock -r "/dev/$ubivol"; then
|
|
echo "cannot remove $ubiblk"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nand_detach_ubi() {
|
|
local ubipart="$1"
|
|
|
|
local mtdnum="$( find_mtd_index "$ubipart" )"
|
|
if [ ! "$mtdnum" ]; then
|
|
echo "cannot find ubi mtd partition $ubipart"
|
|
return 1
|
|
fi
|
|
|
|
local ubidev="$( nand_find_ubi "$ubipart" )"
|
|
if [ "$ubidev" ]; then
|
|
for ubivol in $(find /dev -name "${ubidev}_*" -maxdepth 1 | sort); do
|
|
ubivol="${ubivol:5}"
|
|
nand_remove_ubiblock "$ubivol" || :
|
|
umount "/dev/$ubivol" && echo "unmounted /dev/$ubivol" || :
|
|
done
|
|
if ! ubidetach -m "$mtdnum"; then
|
|
echo "cannot detach ubi mtd partition $ubipart"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
nand_upgrade_prepare_ubi() {
|
|
local rootfs_length="$1"
|
|
local rootfs_type="$2"
|
|
local rootfs_data_max="$(fw_printenv -n rootfs_data_max 2>/dev/null)"
|
|
[ -n "$rootfs_data_max" ] && rootfs_data_max=$((rootfs_data_max))
|
|
|
|
local kernel_length="$3"
|
|
local has_env="${4:-0}"
|
|
|
|
[ -n "$rootfs_length" -o -n "$kernel_length" ] || return 1
|
|
|
|
local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
|
|
if [ ! "$mtdnum" ]; then
|
|
echo "cannot find ubi mtd partition $CI_UBIPART"
|
|
return 1
|
|
fi
|
|
|
|
local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
|
|
if [ ! "$ubidev" ]; then
|
|
ubiattach -m "$mtdnum"
|
|
ubidev="$( nand_find_ubi "$CI_UBIPART" )"
|
|
|
|
if [ ! "$ubidev" ]; then
|
|
ubiformat /dev/mtd$mtdnum -y
|
|
ubiattach -m "$mtdnum"
|
|
ubidev="$( nand_find_ubi "$CI_UBIPART" )"
|
|
|
|
if [ ! "$ubidev" ]; then
|
|
echo "cannot attach ubi mtd partition $CI_UBIPART"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$has_env" -gt 0 ]; then
|
|
ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB
|
|
ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )"
|
|
local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
|
|
local data_ubivol="$( nand_find_volume $ubidev rootfs_data )"
|
|
[ "$root_ubivol" = "$kern_ubivol" ] && root_ubivol=
|
|
|
|
# remove ubiblocks
|
|
[ "$kern_ubivol" ] && { nand_remove_ubiblock $kern_ubivol || return 1; }
|
|
[ "$root_ubivol" ] && { nand_remove_ubiblock $root_ubivol || return 1; }
|
|
[ "$data_ubivol" ] && { nand_remove_ubiblock $data_ubivol || return 1; }
|
|
|
|
# kill volumes
|
|
[ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N "$CI_KERNPART" || :
|
|
[ "$root_ubivol" ] && ubirmvol /dev/$ubidev -N "$CI_ROOTPART" || :
|
|
[ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs_data || :
|
|
|
|
# create kernel vol
|
|
if [ -n "$kernel_length" ]; then
|
|
if ! ubimkvol /dev/$ubidev -N "$CI_KERNPART" -s $kernel_length; then
|
|
echo "cannot create kernel volume"
|
|
return 1;
|
|
fi
|
|
fi
|
|
|
|
# create rootfs vol
|
|
if [ -n "$rootfs_length" ]; then
|
|
local rootfs_size_param
|
|
if [ "$rootfs_type" = "ubifs" ]; then
|
|
rootfs_size_param="-m"
|
|
else
|
|
rootfs_size_param="-s $rootfs_length"
|
|
fi
|
|
if ! ubimkvol /dev/$ubidev -N "$CI_ROOTPART" $rootfs_size_param; then
|
|
echo "cannot create rootfs volume"
|
|
return 1;
|
|
fi
|
|
fi
|
|
|
|
# create rootfs_data vol for non-ubifs rootfs
|
|
if [ "$rootfs_type" != "ubifs" ]; then
|
|
local rootfs_data_size_param="-m"
|
|
if [ -n "$rootfs_data_max" ]; then
|
|
rootfs_data_size_param="-s $rootfs_data_max"
|
|
fi
|
|
if ! ubimkvol /dev/$ubidev -N rootfs_data $rootfs_data_size_param; then
|
|
if ! ubimkvol /dev/$ubidev -N rootfs_data -m; then
|
|
echo "cannot initialize rootfs_data volume"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Write the UBI image to MTD ubi partition
|
|
nand_upgrade_ubinized() {
|
|
local ubi_file="$1"
|
|
|
|
nand_detach_ubi "$CI_UBIPART" || return 1
|
|
|
|
local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
|
|
ubiformat "/dev/mtd$mtdnum" -y -f "$ubi_file" && ubiattach -m "$mtdnum"
|
|
}
|
|
|
|
# Write the UBIFS image to UBI rootfs volume
|
|
nand_upgrade_ubifs() {
|
|
local rootfs_length=$( (cat $1 | wc -c) 2> /dev/null)
|
|
|
|
nand_upgrade_prepare_ubi "$rootfs_length" "ubifs" "" "" || return 1
|
|
|
|
local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
|
|
local root_ubivol="$(nand_find_volume $ubidev "$CI_ROOTPART")"
|
|
ubiupdatevol /dev/$root_ubivol -s $rootfs_length $1
|
|
}
|
|
|
|
# Write the FIT image to UBI kernel volume
|
|
nand_upgrade_fit() {
|
|
local fit_file="$1"
|
|
local fit_length="$(wc -c < "$fit_file")"
|
|
|
|
nand_upgrade_prepare_ubi "" "" "$fit_length" "1" || return 1
|
|
|
|
local fit_ubidev="$(nand_find_ubi "$CI_UBIPART")"
|
|
local fit_ubivol="$(nand_find_volume $fit_ubidev "$CI_KERNPART")"
|
|
ubiupdatevol /dev/$fit_ubivol -s $fit_length $fit_file
|
|
}
|
|
|
|
# Write images in the TAR file to MTD partitions and/or UBI volumes as required
|
|
nand_upgrade_tar() {
|
|
local tar_file="$1"
|
|
|
|
# WARNING: This fails if tar contains more than one 'sysupgrade-*' directory.
|
|
local board_dir="$(tar tf "$tar_file" | grep -m 1 '^sysupgrade-.*/$')"
|
|
board_dir="${board_dir%/}"
|
|
|
|
local kernel_mtd kernel_length
|
|
if [ "$CI_KERNPART" != "none" ]; then
|
|
kernel_mtd="$(find_mtd_index "$CI_KERNPART")"
|
|
kernel_length=$( (tar xf "$tar_file" "$board_dir/kernel" -O | wc -c) 2> /dev/null)
|
|
[ "$kernel_length" = 0 ] && kernel_length=
|
|
fi
|
|
local rootfs_length=$( (tar xf "$tar_file" "$board_dir/root" -O | wc -c) 2> /dev/null)
|
|
[ "$rootfs_length" = 0 ] && rootfs_length=
|
|
local rootfs_type
|
|
[ "$rootfs_length" ] && rootfs_type="$(identify_tar "$tar_file" "$board_dir/root")"
|
|
|
|
local ubi_kernel_length
|
|
if [ "$kernel_length" ]; then
|
|
if [ "$kernel_mtd" ]; then
|
|
# On some devices, the raw kernel and ubi partitions overlap.
|
|
# These devices brick if the kernel partition is erased.
|
|
# Hence only invalidate kernel for now.
|
|
dd if=/dev/zero bs=4096 count=1 2>/dev/null | \
|
|
mtd write - "$CI_KERNPART"
|
|
else
|
|
ubi_kernel_length="$kernel_length"
|
|
fi
|
|
fi
|
|
local has_env=0
|
|
nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "$ubi_kernel_length" "$has_env" || return 1
|
|
|
|
local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
|
|
if [ "$rootfs_length" ]; then
|
|
local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )"
|
|
tar xf "$tar_file" "$board_dir/root" -O | \
|
|
ubiupdatevol /dev/$root_ubivol -s $rootfs_length -
|
|
fi
|
|
if [ "$kernel_length" ]; then
|
|
if [ "$kernel_mtd" ]; then
|
|
tar xf "$tar_file" "$board_dir/kernel" -O | \
|
|
mtd write - "$CI_KERNPART"
|
|
else
|
|
local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )"
|
|
tar xf "$tar_file" "$board_dir/kernel" -O | \
|
|
ubiupdatevol /dev/$kern_ubivol -s $kernel_length -
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
nand_do_flash_file() {
|
|
local file_type=$(identify "$1")
|
|
|
|
[ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART=rootfs
|
|
|
|
case "$file_type" in
|
|
"fit") nand_upgrade_fit "$1";;
|
|
"ubi") nand_upgrade_ubinized "$1";;
|
|
"ubifs") nand_upgrade_ubifs "$1";;
|
|
*) nand_upgrade_tar "$1";;
|
|
esac
|
|
}
|
|
|
|
nand_do_restore_config() {
|
|
local conf_tar="/tmp/sysupgrade.tgz"
|
|
[ ! -f "$conf_tar" ] || nand_restore_config "$conf_tar"
|
|
}
|
|
|
|
# Recognize type of passed file and start the upgrade process
|
|
nand_do_upgrade() {
|
|
sync
|
|
if nand_do_flash_file "$1" && nand_do_restore_config && sync; then
|
|
echo "sysupgrade successful"
|
|
umount -a
|
|
reboot -f
|
|
fi
|
|
|
|
sync
|
|
echo "sysupgrade failed"
|
|
# Should we reboot or bring up some failsafe mode instead?
|
|
umount -a
|
|
reboot -f
|
|
}
|
|
|
|
# Check if passed file is a valid one for NAND sysupgrade.
|
|
# Currently it accepts 4 types of files:
|
|
# 1) UBI: a ubinized image containing required UBI volumes.
|
|
# 2) UBIFS: a UBIFS rootfs volume image.
|
|
# 3) FIT: a FIT image containing kernel and rootfs.
|
|
# 4) TAR: an archive that includes directory "sysupgrade-${BOARD_NAME}" containing
|
|
# a non-empty "CONTROL" file and required partition and/or volume images.
|
|
#
|
|
# You usually want to call this function in platform_check_image.
|
|
#
|
|
# $(1): board name, used in case of passing TAR file
|
|
# $(2): file to be checked
|
|
nand_do_platform_check() {
|
|
local board_name="$1"
|
|
local tar_file="$2"
|
|
local control_length=$( (tar xf $tar_file sysupgrade-$board_name/CONTROL -O | wc -c) 2> /dev/null)
|
|
local file_type="$(identify $2)"
|
|
|
|
[ "$control_length" = 0 -a "$file_type" != "ubi" -a "$file_type" != "ubifs" -a "$file_type" != "fit" ] && {
|
|
echo "Invalid sysupgrade file."
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|