Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

非efi固件,u盘启动刷机以后,删除硬盘分区再从u盘启动,disk id冲突导致overlay失败,quickstart刷机也失败 #281

Closed
Tracked by #18
jjm2473 opened this issue Jul 4, 2022 · 5 comments

Comments

@jjm2473
Copy link
Contributor

jjm2473 commented Jul 4, 2022

disk id冲突本身就是有问题的,不打算修复固件,以免引发其他问题。
打算修改quickstart的刷机程序:
1.修复启动硬盘的判断逻辑,在发生冲突的情况下能正常刷机(解析/proc/self/mountinfo来获取实际启动的磁盘)
2.增加判断disk id冲突的情况提醒用户
3.刷机以后修改disk id(参考x86的platform.sh)

@jjm2473

This comment was marked as outdated.

@jjm2473

This comment was marked as outdated.

@jjm2473

This comment was marked as outdated.

@jjm2473
Copy link
Contributor Author

jjm2473 commented Jan 16, 2023

clone_istoreos.sh

#!/bin/sh
# Copyright (C) 2022-2023 jjm2473@gmail.com
# Copy current running iStoreOS to other disk

. /lib/upgrade/common.sh

export_rootdevice() {
    local DEVNO=`grep -Fw /dev/root /proc/self/mountinfo | cut -d' ' -f3`
    if [ -z "$DEVNO" ]; then
        echo "Parse /proc/self/mountinfo failed!" >&2
        return 1
    fi
    local uevent=/sys/dev/block/$DEVNO/uevent
    export ROOTPART_NUM=
    if [ -e "$uevent" ]; then
        DEVTYPE=
        PARTN=
        while read line; do
            export -n "$line"
        done < "$uevent"
        if [[ "$DEVTYPE" = partition ]]; then
            export ROOTPART_NUM="$PARTN"
        fi
    fi

    if [ -z "$ROOTPART_NUM" ]; then
        echo "Unknown root partition" >&2
        return 1
    fi

    DEVNO="`cat /sys/dev/block/$DEVNO/../dev`"
    if [ -z "$DEVNO" ]; then
        echo "Get disk of root partition failed!" >&2
        return 1
    fi
    export ROOTDEV_MAJOR="${DEVNO%:*}"
    export ROOTDEV_MINOR="${DEVNO#*:}"
}

export_rootpart() {
    local var="$1" offset="$2"

    devno_to_devname $var $ROOTDEV_MAJOR:$(($ROOTDEV_MINOR + $offset))
}

devno_to_devname() {
    local var="$1" devno="$2"
    local uevent line MAJOR MINOR DEVNAME DEVTYPE

    uevent=/sys/dev/block/$devno/uevent
    if [ -e "$uevent" ]; then
        while read line; do
            export -n "$line"
        done < "$uevent"
        if [ -b "/dev/$DEVNAME" ]; then
            export "$var=$DEVNAME"
            return 0
        fi
    fi

    return 1
}

devname_to_devno() {
    local var="$1" devname="$2"
    local path="/sys/class/block/$devname/dev"
    local DEVNO
    if [ -e "$path" ]; then
        DEVNO=`cat "$path"`
        export "$var=$DEVNO"
        return 0
    fi
    return 1
}

rescan_partitions() {
    local devname="$1"
    partprobe "/dev/${devname#/dev/}"
}

efi_crc32() {
    gzip -1 -c | tail -c8 | head -c4
}

get_mbr_diskid() {
    local var="$1" devname="$2"
    local ID
    devname="/dev/${devname#/dev/}"

    ID=`dd if="$devname" bs=1 skip=440 count=4 2>/dev/null | hexdump -v -n 4 -e '/4 "0x%08x"'`
    ID=`hex_le32_to_cpu $ID`

    export "$var=$ID"

    return 0
}

set_mbr_diskid() {
    local to="$1" id="$2"
    if [ -z "$to" ]; then
        echo "devnames are empty" >&2
        return 1
    fi
    to="/dev/${to#/dev/}"

    echo -ne "$id" | dd of="$to" bs=1 seek=440 count=4 conv=notrunc,fsync 2>/dev/null

    return 0
}

xor_mbr_diskid() {
    local from="$1" mask="$2"
    local id
    if [ -z "$from" ]; then
        echo "devnames are empty" >&2
        return 1
    fi
    from="/dev/${from#/dev/}"

    get_mbr_diskid id $from
    id=`hex_le32_to_cpu $id`

    id=$(( $id ^ $mask ))

    id=`printf "0x%08x" $id`

    id=`hex_le32_to_cpu $id`

    echo "\x${id:8:2}\x${id:6:2}\x${id:4:2}\x${id:2:2}"
}

upgrade_grub_x86() {
    local devname="$1"
    local bootpart
    local devno id parttable=msdos
    devname="${devname#/dev/}"
    if [ -z "$devname" ]; then
        echo "devname is empty" >&2
        return 127;
    fi
    devname="/dev/${devname}"
    rescan_partitions "$devname"
    if part_magic_efi "$devname"; then
        set -- $(dd if="$devname" bs=1 skip=1168 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"')
        id="$4$3$2$1-$6$5-$8$7-$9"
        parttable=gpt
    else
        id=`dd if="$devname" bs=1 skip=440 count=4 2>/dev/null | hexdump -v -n 4 -e '/4 "%08x"'`
        id="$id-02"
    fi
    devname_to_devno devno "${devname#/dev/}"
    if devno_to_devname bootpart "${devno%:*}:$(( ${devno#*:} + 1 ))"; then
        mkdir -p /tmp/boot
        mount -o rw,noatime "/dev/$bootpart" /tmp/boot
        sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1$id/ig" /tmp/boot/boot/grub/grub.cfg

        echo "(hd0) $devname" > /tmp/device.map
		grub-bios-setup \
			-m "/tmp/device.map" \
			-d "/tmp/boot/boot/grub" \
			-r "hd0,${parttable}1" \
			"$devname" \
		&& touch /tmp/boot/boot/grub/upgraded

        umount /tmp/boot
        return 0
    fi
    return 1
}

install_istoreos() {
    local from to="$1" lastpart lastpart_start lastpart_end mounted overlay=0 diskid offset parted_script boot_parts
    to="${to#/dev/}"
    if [ -z "$to" ]; then
        echo "devname is empty" >&2
        return 1
    fi
    if export_rootdevice; then
        if export_rootpart from 0; then
            if [[ "$from" = "$to" ]]; then
                echo "target device is busy" >&2
                return 1
            fi
            echo "boot disk: $from" >&2
            boot_parts=$ROOTPART_NUM
            if ! export_rootpart lastpart $boot_parts; then
                echo "get root part failed, maybe this is a bug" >&2
                return 1
            fi
            if grep -Fqm1 ' - squashfs /dev/root ' /proc/self/mountinfo; then
                echo "rootfs is squashfs" >&2
                if [ -f /usr/sbin/mount_root ] && export_rootpart lastpart $(( $boot_parts + 1 )); then
                    boot_parts=$(( $boot_parts + 1 ))
                    overlay=1
                fi
            fi
            lastpart_start=`cat "/sys/class/block/$lastpart/start"`
            lastpart_end=`cat "/sys/class/block/$lastpart/size"`
            lastpart_end=$(($lastpart_end + $lastpart_start))

            echo "umount /dev/$to*" >&2
            umount "/dev/$to"* 2>/dev/null
            mounted="`mount | cut -d' ' -f1 | grep -Fm1 "/dev/$to"`"
            if [ -n "$mounted" ] ; then
                echo "can not umount $mounted" >&2
                return 1
            fi

            parted -ms "/dev/$to" unit GiB print 2>/dev/null | grep -qm1 "^/dev/$to:" || {
                echo "disk $to unavalible after umount" >&2
                return 1
            }

            echo "copy data $from => $to" >&2
            if [[ "$overlay" = "1" ]]; then
                dd if="/dev/$from" of="/dev/$to" bs=8192 count=$((($lastpart_start + 15) >> 4)) conv=notrunc
                echo "RESET000" | dd of="/dev/$to" bs=512 seek=$lastpart_start count=1 conv=sync,notrunc,fsync >/dev/null 2>&1
            else
                dd if="/dev/$from" of="/dev/$to" bs=8192 count=$((($lastpart_end + 31) >> 4)) conv=notrunc
            fi

            diskid="`xor_mbr_diskid $from 0xea5e9105`"
            echo "new disk id: $diskid" >&2
            if part_magic_efi "/dev/$from"; then
                echo "patch EFI firmware" >&2

                # rebuild target disk GPT
                parted -s "/dev/$to" mktable gpt
                # disk guid
                dd if="/dev/$from" of="/dev/$to" bs=8 skip=71 seek=71 count=2 conv=notrunc,fsync 2>/dev/null
                # partitions
                dd if="/dev/$from" of="/dev/$to" bs=128 skip=8 seek=8 count=$boot_parts conv=notrunc,fsync 2>/dev/null

                # copy bios_grub partition entry
                dd if="/dev/$from" of="/dev/$to" bs=128 skip=135 seek=135 count=1 conv=notrunc,fsync 2>/dev/null

                # update disk and part uuid
                set_mbr_diskid "$to" "$diskid" || return 1
                # normal partition
                for offset in `seq 1 $boot_parts` ; do
                    echo -ne "$diskid" | dd of="/dev/$to" bs=4 seek=$(( ($offset - 1) * 0x20 + 0x104 )) count=1 conv=notrunc,fsync 2>/dev/null
                done
                # disk guid and bios_grub partition
                for offset in 142 4324 ; do
                    echo -ne "$diskid" | dd of="/dev/$to" bs=4 seek=$offset count=1 conv=notrunc,fsync 2>/dev/null
                done

                # fix GPT CRC32 of partition entries (little endian)
                dd if="/dev/$to" bs=512 skip=2 count=32 2>/dev/null | efi_crc32 | dd of="/dev/$to" bs=4 seek=150 count=1 conv=notrunc,fsync 2>/dev/null

                # fix GPT CRC32 of header
                { dd if="/dev/$to" bs=4 skip=128 count=4 2>/dev/null; 
                    dd if=/dev/zero bs=4 count=1 2>/dev/null; 
                    dd if="/dev/$to" bs=4 skip=133 count=18 2>/dev/null;
                } | efi_crc32 | dd of="/dev/$to" bs=4 seek=132 count=1 conv=notrunc,fsync 2>/dev/null

                # fix alternative GPT
                echo Fix | parted "/dev/$to" print >/dev/null
                for offset in `seq 2 $boot_parts` ; do
                    parted -s "/dev/$to" set $offset msftdata on
                done
            else
                echo "patch Legacy firmware" >&2

                # delete user partitions
                for offset in `seq $boot_parts 3` ; do
                    dd if=/dev/zero of=/dev/sda bs=2 seek=$(( $offset * 8 + 223 )) count=8 conv=notrunc,fsync 2>/dev/null
                done

                set_mbr_diskid "$to" "$diskid" || return 1
            fi
            if [[ "`uname -m`" = "x86*" ]]; then
                # update grub and cmdline partuuid for x86 platform
                upgrade_grub_x86 "$to"
            else
                rescan_partitions "$to"
            fi
            sync
            sync
            echo 3 >/proc/sys/vm/drop_caches
            echo "Success"
            return 0
        fi
    fi

    echo "unknown running root disk!" >&2

    return 1
}

[ -n "$1" -a -b "/dev/$1" ] && install_istoreos $1

@jjm2473
Copy link
Contributor Author

jjm2473 commented Jan 17, 2023

作为库使用

  1. 显示当前的启动盘
. ./clone_istoreos.sh
export_rootdevice
devno_to_devname bootdisk $ROOTDEV_MAJOR:$ROOTDEV_MINOR
echo "boot disk: $bootdisk"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant