b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | RAM_ROOT=/tmp/root |
| 2 | |
| 3 | export BACKUP_FILE=sysupgrade.tgz # file extracted by preinit |
| 4 | |
| 5 | [ -x /usr/bin/ldd ] || ldd() { LD_TRACE_LOADED_OBJECTS=1 $*; } |
| 6 | libs() { ldd $* 2>/dev/null | sed -E 's/(.* => )?(.*) .*/\2/'; } |
| 7 | |
| 8 | install_file() { # <file> [ <file> ... ] |
| 9 | local target dest dir |
| 10 | for file in "$@"; do |
| 11 | if [ -L "$file" ]; then |
| 12 | target="$(readlink -f "$file")" |
| 13 | dest="$RAM_ROOT/$file" |
| 14 | [ ! -f "$dest" ] && { |
| 15 | dir="$(dirname "$dest")" |
| 16 | mkdir -p "$dir" |
| 17 | ln -s "$target" "$dest" |
| 18 | } |
| 19 | file="$target" |
| 20 | fi |
| 21 | dest="$RAM_ROOT/$file" |
| 22 | [ -f "$file" -a ! -f "$dest" ] && { |
| 23 | dir="$(dirname "$dest")" |
| 24 | mkdir -p "$dir" |
| 25 | cp "$file" "$dest" |
| 26 | } |
| 27 | done |
| 28 | } |
| 29 | |
| 30 | install_bin() { |
| 31 | local src files |
| 32 | src=$1 |
| 33 | files=$1 |
| 34 | [ -x "$src" ] && files="$src $(libs $src)" |
| 35 | install_file $files |
| 36 | } |
| 37 | |
| 38 | run_hooks() { |
| 39 | local arg="$1"; shift |
| 40 | for func in "$@"; do |
| 41 | eval "$func $arg" |
| 42 | done |
| 43 | } |
| 44 | |
| 45 | ask_bool() { |
| 46 | local default="$1"; shift; |
| 47 | local answer="$default" |
| 48 | |
| 49 | [ "$INTERACTIVE" -eq 1 ] && { |
| 50 | case "$default" in |
| 51 | 0) echo -n "$* (y/N): ";; |
| 52 | *) echo -n "$* (Y/n): ";; |
| 53 | esac |
| 54 | read answer |
| 55 | case "$answer" in |
| 56 | y*) answer=1;; |
| 57 | n*) answer=0;; |
| 58 | *) answer="$default";; |
| 59 | esac |
| 60 | } |
| 61 | [ "$answer" -gt 0 ] |
| 62 | } |
| 63 | |
| 64 | _v() { |
| 65 | [ -n "$VERBOSE" ] && [ "$VERBOSE" -ge 1 ] && echo "$*" >&2 |
| 66 | } |
| 67 | |
| 68 | v() { |
| 69 | _v "$(date) upgrade: $@" |
| 70 | logger -p info -t upgrade "$@" |
| 71 | } |
| 72 | |
| 73 | json_string() { |
| 74 | local v="$1" |
| 75 | v="${v//\\/\\\\}" |
| 76 | v="${v//\"/\\\"}" |
| 77 | echo "\"$v\"" |
| 78 | } |
| 79 | |
| 80 | rootfs_type() { |
| 81 | /bin/mount | awk '($3 ~ /^\/$/) && ($5 !~ /rootfs/) { print $5 }' |
| 82 | } |
| 83 | |
| 84 | get_image() { # <source> [ <command> ] |
| 85 | local from="$1" |
| 86 | local cmd="$2" |
| 87 | |
| 88 | if [ -z "$cmd" ]; then |
| 89 | local magic="$(dd if="$from" bs=2 count=1 2>/dev/null | hexdump -n 2 -e '1/1 "%02x"')" |
| 90 | case "$magic" in |
| 91 | 1f8b) cmd="busybox zcat";; |
| 92 | *) cmd="cat";; |
| 93 | esac |
| 94 | fi |
| 95 | |
| 96 | $cmd <"$from" |
| 97 | } |
| 98 | |
| 99 | get_image_dd() { |
| 100 | local from="$1"; shift |
| 101 | |
| 102 | ( |
| 103 | exec 3>&2 |
| 104 | ( exec 3>&2; get_image "$from" 2>&1 1>&3 | grep -v -F ' Broken pipe' ) 2>&1 1>&3 \ |
| 105 | | ( exec 3>&2; dd "$@" 2>&1 1>&3 | grep -v -E ' records (in|out)') 2>&1 1>&3 |
| 106 | exec 3>&- |
| 107 | ) |
| 108 | } |
| 109 | |
| 110 | get_magic_word() { |
| 111 | (get_image "$@" | dd bs=2 count=1 | hexdump -v -n 2 -e '1/1 "%02x"') 2>/dev/null |
| 112 | } |
| 113 | |
| 114 | get_magic_long() { |
| 115 | (get_image "$@" | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2>/dev/null |
| 116 | } |
| 117 | |
| 118 | get_magic_gpt() { |
| 119 | (get_image "$@" | dd bs=8 count=1 skip=64) 2>/dev/null |
| 120 | } |
| 121 | |
| 122 | get_magic_vfat() { |
| 123 | (get_image "$@" | dd bs=3 count=1 skip=18) 2>/dev/null |
| 124 | } |
| 125 | |
| 126 | get_magic_fat32() { |
| 127 | (get_image "$@" | dd bs=1 count=5 skip=82) 2>/dev/null |
| 128 | } |
| 129 | |
| 130 | identify_magic_long() { |
| 131 | local magic=$1 |
| 132 | case "$magic" in |
| 133 | "55424923") |
| 134 | echo "ubi" |
| 135 | ;; |
| 136 | "31181006") |
| 137 | echo "ubifs" |
| 138 | ;; |
| 139 | "68737173") |
| 140 | echo "squashfs" |
| 141 | ;; |
| 142 | "d00dfeed") |
| 143 | echo "fit" |
| 144 | ;; |
| 145 | "4349"*) |
| 146 | echo "combined" |
| 147 | ;; |
| 148 | "1f8b"*) |
| 149 | echo "gzip" |
| 150 | ;; |
| 151 | *) |
| 152 | echo "unknown $magic" |
| 153 | ;; |
| 154 | esac |
| 155 | } |
| 156 | |
| 157 | part_magic_efi() { |
| 158 | local magic=$(get_magic_gpt "$@") |
| 159 | [ "$magic" = "EFI PART" ] |
| 160 | } |
| 161 | |
| 162 | part_magic_fat() { |
| 163 | local magic=$(get_magic_vfat "$@") |
| 164 | local magic_fat32=$(get_magic_fat32 "$@") |
| 165 | [ "$magic" = "FAT" ] || [ "$magic_fat32" = "FAT32" ] |
| 166 | } |
| 167 | |
| 168 | export_bootdevice() { |
| 169 | local cmdline uuid blockdev uevent line class |
| 170 | local MAJOR MINOR DEVNAME DEVTYPE |
| 171 | local rootpart="$(cmdline_get_var root)" |
| 172 | |
| 173 | case "$rootpart" in |
| 174 | PARTUUID=[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]-[a-f0-9][a-f0-9]) |
| 175 | uuid="${rootpart#PARTUUID=}" |
| 176 | uuid="${uuid%-[a-f0-9][a-f0-9]}" |
| 177 | for blockdev in $(find /dev -type b); do |
| 178 | set -- $(dd if=$blockdev bs=1 skip=440 count=4 2>/dev/null | hexdump -v -e '4/1 "%02x "') |
| 179 | if [ "$4$3$2$1" = "$uuid" ]; then |
| 180 | uevent="/sys/class/block/${blockdev##*/}/uevent" |
| 181 | break |
| 182 | fi |
| 183 | done |
| 184 | ;; |
| 185 | PARTUUID=????????-????-????-????-??????????0?/PARTNROFF=1 | \ |
| 186 | PARTUUID=????????-????-????-????-??????????02) |
| 187 | uuid="${rootpart#PARTUUID=}" |
| 188 | uuid="${uuid%/PARTNROFF=1}" |
| 189 | uuid="${uuid%0?}00" |
| 190 | for disk in $(find /dev -type b); do |
| 191 | set -- $(dd if=$disk bs=1 skip=568 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"') |
| 192 | if [ "$4$3$2$1-$6$5-$8$7-$9" = "$uuid" ]; then |
| 193 | uevent="/sys/class/block/${disk##*/}/uevent" |
| 194 | break |
| 195 | fi |
| 196 | done |
| 197 | ;; |
| 198 | /dev/*) |
| 199 | uevent="/sys/class/block/${rootpart##*/}/../uevent" |
| 200 | ;; |
| 201 | 0x[a-f0-9][a-f0-9][a-f0-9] | 0x[a-f0-9][a-f0-9][a-f0-9][a-f0-9] | \ |
| 202 | [a-f0-9][a-f0-9][a-f0-9] | [a-f0-9][a-f0-9][a-f0-9][a-f0-9]) |
| 203 | rootpart=0x${rootpart#0x} |
| 204 | for class in /sys/class/block/*; do |
| 205 | while read line; do |
| 206 | export -n "$line" |
| 207 | done < "$class/uevent" |
| 208 | if [ $((rootpart/256)) = $MAJOR -a $((rootpart%256)) = $MINOR ]; then |
| 209 | uevent="$class/../uevent" |
| 210 | fi |
| 211 | done |
| 212 | ;; |
| 213 | esac |
| 214 | |
| 215 | if [ -e "$uevent" ]; then |
| 216 | while read line; do |
| 217 | export -n "$line" |
| 218 | done < "$uevent" |
| 219 | export BOOTDEV_MAJOR=$MAJOR |
| 220 | export BOOTDEV_MINOR=$MINOR |
| 221 | return 0 |
| 222 | fi |
| 223 | |
| 224 | return 1 |
| 225 | } |
| 226 | |
| 227 | export_partdevice() { |
| 228 | local var="$1" offset="$2" |
| 229 | local uevent line MAJOR MINOR DEVNAME DEVTYPE |
| 230 | |
| 231 | for uevent in /sys/class/block/*/uevent; do |
| 232 | while read line; do |
| 233 | export -n "$line" |
| 234 | done < "$uevent" |
| 235 | if [ "$BOOTDEV_MAJOR" = "$MAJOR" -a $(($BOOTDEV_MINOR + $offset)) = "$MINOR" -a -b "/dev/$DEVNAME" ]; then |
| 236 | export "$var=$DEVNAME" |
| 237 | return 0 |
| 238 | fi |
| 239 | done |
| 240 | |
| 241 | return 1 |
| 242 | } |
| 243 | |
| 244 | hex_le32_to_cpu() { |
| 245 | [ "$(echo 01 | hexdump -v -n 2 -e '/2 "%x"')" = "3031" ] && { |
| 246 | echo "${1:0:2}${1:8:2}${1:6:2}${1:4:2}${1:2:2}" |
| 247 | return |
| 248 | } |
| 249 | echo "$@" |
| 250 | } |
| 251 | |
| 252 | get_partitions() { # <device> <filename> |
| 253 | local disk="$1" |
| 254 | local filename="$2" |
| 255 | |
| 256 | if [ -b "$disk" -o -f "$disk" ]; then |
| 257 | v "Reading partition table from $filename..." |
| 258 | |
| 259 | local magic=$(dd if="$disk" bs=2 count=1 skip=255 2>/dev/null) |
| 260 | if [ "$magic" != $'\x55\xAA' ]; then |
| 261 | v "Invalid partition table on $disk" |
| 262 | exit |
| 263 | fi |
| 264 | |
| 265 | rm -f "/tmp/partmap.$filename" |
| 266 | |
| 267 | local part |
| 268 | part_magic_efi "$disk" && { |
| 269 | #export_partdevice will fail when partition number is greater than 15, as |
| 270 | #the partition major device number is not equal to the disk major device number |
| 271 | for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do |
| 272 | set -- $(hexdump -v -n 48 -s "$((0x380 + $part * 0x80))" -e '4/4 "%08x"" "4/4 "%08x"" "4/4 "0x%08X "' "$disk") |
| 273 | |
| 274 | local type="$1" |
| 275 | local lba="$(( $(hex_le32_to_cpu $4) * 0x100000000 + $(hex_le32_to_cpu $3) ))" |
| 276 | local end="$(( $(hex_le32_to_cpu $6) * 0x100000000 + $(hex_le32_to_cpu $5) ))" |
| 277 | local num="$(( $end - $lba + 1 ))" |
| 278 | |
| 279 | [ "$type" = "00000000000000000000000000000000" ] && continue |
| 280 | |
| 281 | printf "%2d %5d %7d\n" $part $lba $num >> "/tmp/partmap.$filename" |
| 282 | done |
| 283 | } || { |
| 284 | for part in 1 2 3 4; do |
| 285 | set -- $(hexdump -v -n 12 -s "$((0x1B2 + $part * 16))" -e '3/4 "0x%08X "' "$disk") |
| 286 | |
| 287 | local type="$(( $(hex_le32_to_cpu $1) % 256))" |
| 288 | local lba="$(( $(hex_le32_to_cpu $2) ))" |
| 289 | local num="$(( $(hex_le32_to_cpu $3) ))" |
| 290 | |
| 291 | [ $type -gt 0 ] || continue |
| 292 | |
| 293 | printf "%2d %5d %7d\n" $part $lba $num >> "/tmp/partmap.$filename" |
| 294 | done |
| 295 | } |
| 296 | fi |
| 297 | } |
| 298 | |
| 299 | indicate_upgrade() { |
| 300 | . /etc/diag.sh |
| 301 | set_state upgrade |
| 302 | } |
| 303 | |
| 304 | # Flash firmware to MTD partition |
| 305 | # |
| 306 | # $(1): path to image |
| 307 | # $(2): (optional) pipe command to extract firmware, e.g. dd bs=n skip=m |
| 308 | default_do_upgrade() { |
| 309 | sync |
| 310 | echo 3 > /proc/sys/vm/drop_caches |
| 311 | if [ -n "$UPGRADE_BACKUP" ]; then |
| 312 | get_image "$1" "$2" | mtd $MTD_ARGS $MTD_CONFIG_ARGS -j "$UPGRADE_BACKUP" write - "${PART_NAME:-image}" |
| 313 | else |
| 314 | get_image "$1" "$2" | mtd $MTD_ARGS write - "${PART_NAME:-image}" |
| 315 | fi |
| 316 | [ $? -ne 0 ] && exit 1 |
| 317 | } |