#!/bin/bash

#LABEL_FOR_BUILD


##################################global define##################################
g_util_name=p2v-cloud-tool
g_util_desc="P2V Cloud Tool"
g_util_version=1.5.0

# log
g_cur_dir=$(dirname $(readlink -f $0))
g_log_dir="$g_cur_dir"/Logs
mkdir -p "$g_log_dir"
g_log_file=${g_log_dir}/p2vs_tool.log
g_log_err_file=${g_log_dir}/p2vs_tool_err.log
g_mount_file=${g_log_dir}/temp_mount_file.log
g_os_name=""
g_disk_index=0
g_part_index=0

function global::print_version() {
    cat <<EOF
$g_util_desc $g_util_version.
EOF
}


##################################common util##################################

#common error code
CODE_ERROR_COMMON_ERROR=1
CODE_ERROR_COMMON_NOT_FOUND=2
CODE_ERROR_COMMON_EMPTY_VAR=3
CODE_ERROR_COMMON_INVALID_PARAM=4

function common::get_datetime() {
    date +"%Y-%m-%d %H:%M:%S.%3N"
}

function common::get_datetime_string() {
    date +"%Y%m%d%H%M%S%3N"
}

function common::log() {
    local log_msg=$1
    local log_level=$2
    local log_function=$3
    if [ "$log_level" == "" ]; then
        log_level="Debug"
    fi
    if [ "$log_function" == "" ]; then
        log_function=${FUNCNAME[1]}
    fi
    log_info="[$log_level]\t[$log_function] $log_msg"
    log_info_time="$(common::get_datetime) $log_info"
    echo -e "$log_info_time" >>"$g_log_file"
}

function common::log_function() {
    local log_msg="Function: ${FUNCNAME[1]} $*"
    common::log "$log_msg" "Debug" "${FUNCNAME[1]}"
}

function common::log_var_exit() {
    if [ -z "$2" ]; then
        common::log "var $1 is empty" "Error" "${FUNCNAME[1]}"
        exit $((CODE_ERROR_COMMON_EMPTY_VAR))
    else
        common::log "var $1 is $2" "Debug" "${FUNCNAME[1]}"
    fi
}

function common::log_var() {
    if [ -z "$2" ]; then
        common::log "var $1 is empty" "Debug" "${FUNCNAME[1]}"
    else
        common::log "var $1 is $2" "Debug" "${FUNCNAME[1]}"
    fi
}

function common::backup_file() {
    common::log_function "$*"
    local file_path=$1
    common::log_var "file_path" "$file_path"
    if [ -z "$file_path" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi

    if [ ! -e "$file_path" ]; then
        common::log "$file_path not exist" "Error"
        return $((CODE_ERROR_COMMON_NOT_FOUND))
    fi

    local dest_file_path=${file_path}_p2v_bak_$(common::get_datetime_string)
    cp "$file_path" "$dest_file_path"
    local ret=$?
    if [[ $ret -ne 0 ]]; then
        common::log "backup $file_path to $dest_file_path failed" "Error"
    fi
    return $((ret))
}

function common::temp_mount() {
    common::log_function "$*"
    local mount_dev=$1
    local mount_path=$2
    common::log_var "mount_dev" "$mount_dev"
    common::log_var "mount_path" "$mount_path"
    if [ -z "$mount_dev" ] || [ -z "$mount_path" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi

    mount $mount_options "$mount_dev" "$mount_path" 2>$g_log_err_file
    local ret=$?
    if [[ $ret -ne 0 ]]; then
        dmesg | tail >>$g_log_err_file
        common::log "mount $mount_dev to $mount_path failed return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        return $((ret))
    fi

    # save to mount file by reserve
    if [ ! -e "$g_mount_file" ]; then
        common::log "create mount file: $g_mount_file"
        echo " " >"$g_mount_file"
    fi
    sed -i "1 i\ ${mount_dev}:${mount_path}" "$g_mount_file"
    return 0
}

function common::force_umount() {
    common::log_function "$*"
    local path=$1
    common::log_var_exit "path" "$path"
    if mount 2>$g_log_err_file | grep "$path"; then
        #try umount path
        for ((; a<20; a++)) do
            if umount "$path" 2>$g_log_err_file; then
                common::log "$path umounted"
                return 0
            fi
            sleep 3s
        done
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::temp_umount_all() {
    common::log_function "$*"
    # load from mount file
    if [ -e "$g_mount_file" ]; then
        while read line ; do
            if [ -z "$line" ]; then
                continue
            fi
            local mount_dev=""
            local mount_path=""
            mount_dev=$(echo "$line" | awk -F':' '{printf $1}')
            mount_path=$(echo "$line" | awk -F':' '{printf $2}')
            if [ -z "$mount_dev" ] && [ -z "$mount_path" ]; then
                continue
            fi
            common::log "try umount $mount_dev($mount_path)..."
            if mount 2>$g_log_err_file | grep "$mount_dev"; then
                umount "$mount_dev"
            fi
            if mount 2>$g_log_err_file | grep "$mount_path"; then
                umount "$mount_path" 2>$g_log_err_file
            fi
        done <"$g_mount_file"
        common::log "clear mount file"
        # clear mount file
        echo "" >"$g_mount_file"
    fi
}

function common::is_centos6() {
    common::log_function
    if uname -r 2>$g_log_err_file | grep "2\.6\.32"; then
        return 0
    else
        return 1
    fi
}

function common::check_whoami() {
    common::log_function
    if [ "$(id -u)" == "0" ]; then
        return 0
    else
        common::log "please use root or sudo" "Error"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_architecure() {
    local arch=""
    arch=$(uname -m)
    if [ -z "$arch" ]; then
        arch=$(uname -p)
    fi
    common::log "arch: $arch"
    echo "$arch"
}

function common::check_architecure() {
    common::log_function
    local arch=""
    arch=$(common::get_architecure)
    if echo "$arch" | grep -E 'x86_64|i[36]86|amd64|aarch64'; then
        return 0
    else
        common::log "not supported current architecture" "Error"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_os_issue() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local os_issue=""
    if [ -e "$root_dir"/etc/redhat-release ]; then
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/redhat-release)"
        common::log "read os_issue from redhat-release: $os_issue"
    elif [ -e "$root_dir"/etc/centos-release ]; then
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/centos-release)"
        common::log "read os_issue from centos-release: $os_issue"
    elif [ -e "$root_dir"/etc/os-release ]; then
        os_issue=$(grep "PRETTY_NAME" < ${root_dir}/etc/os-release | awk -F\" '{print $2}')
        common::log "read os_issue from os-release: $os_issue"
    else
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/issue)"
        common::log "read os_issue from issue: $os_issue"
    fi
    echo "$os_issue"
}

function common::get_disk_part_fstype() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local part_fstype=""
    part_fstype=$(blkid -s TYPE "$disk_part" | awk -F"\"" '{print $2}')
    common::log "part_fstype: $part_fstype"
    echo "$part_fstype"
}

function common::get_disk_part_uuid() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local part_uuid=""
    part_uuid=$(blkid -s UUID "$disk_part" | awk -F"\"" '{print $2}')
    common::log "part_uuid: $part_uuid"
    echo "$part_uuid"
}

function common::gen_disk_part_fstab_line() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local mount_path=$2
    common::log_var_exit "mount_path" "$mount_path"
    local mount_arg1=$3
    common::log_var_exit "mount_arg1" "$mount_arg1"
    local mount_arg2=$4
    common::log_var_exit "mount_arg2" "$mount_arg2"
    local mount_arg3=$5
    common::log_var_exit "mount_arg3" "$mount_arg3"

    local part_uuid=""
    part_uuid=$(common::get_disk_part_uuid "$disk_part")
    if [ -z "$part_uuid" ]; then
        echo ""
        return
    fi

    local part_fstype=""
    part_fstype=$(common::get_disk_part_fstype "$disk_part")
    if [ -z "$part_fstype" ]; then
        echo ""
        return
    fi

    local fstab_line="UUID=$part_uuid    $mount_path    $part_fstype    $mount_arg1    $mount_arg2 $mount_arg3"
    common::log "fstab_line: $fstab_line"
    echo "$fstab_line"
}

function common::get_data_disk_devs() {
    common::log_function "$*"
    local root_disk_dev=$1
    local trans_disk_dev=$(common::get_disk_dev 1)
    local all_devs=$(export LC_ALL=C; fdisk -l 2>$g_log_err_file | grep -v mapper | grep -oE "Disk /dev/(nvme[0-9]+n[0-9]+|[a-z]+)" | awk '{printf ""$2" "}')
    common::log "all_devs: $all_devs"

    for dev in $all_devs
    do
        if [[ "$dev" != "$root_disk_dev" ]] && [[ "$dev" != "$trans_disk_dev" ]]; then
            data_disk_devs+="$dev "
        fi
    done

    common::log "data_disk_devs: $data_disk_devs"
    if [ -z "$data_disk_devs" ]; then
        echo ""
        return
    fi
    echo "$data_disk_devs" | awk '{$1=$1};1'
}

function common::get_disk_dev() {
    common::log_function "$*"
    local disk_num=$1
    local disk_dev=""

    disk_dev=$(export LC_ALL=C; fdisk -l 2>$g_log_err_file | grep -v mapper | grep -oE "Disk /dev/(nvme[0-9]+n[0-9]+|[a-z]+)" | sort | head -"$disk_num" | tail -1 | awk '{printf $2}')
    common::log "disk_dev: $disk_dev"
    if [ -z "$disk_dev" ]; then
        echo ""
        return
    fi
    echo "$disk_dev"
}

function common::get_disk_parts() {
    common::log_function "$*"
    local disk_dev=$1
    common::log_var_exit "disk_dev" "$disk_dev"
    local disk_name=${disk_dev##*/}
    common::log "disk_name: $disk_name"

    local disk_parts=""
    disk_parts=$(lsblk -n -no NAME -r /dev/${disk_name} 2>$g_log_err_file | grep -E "${disk_name}p?[0-9]+" | awk '{printf "/dev/"$1" "}')
    common::log "disk_parts: $disk_parts"
    echo "$disk_parts"
}

# check if disk part mount dir contain a sub path
# $1: disk part mount dir
# $2: sub path
function common::check_part_sub_path() {
    common::log_function "$*"
    local part_dir=$1
    local sub_path=$2
    local file_path="$part_dir/$sub_path"

    if [ -e "$file_path" ]; then
        common::log "found $file_path"
        return 0
    else
        return $((CODE_ERROR_COMMON_NOT_FOUND))
    fi
}

function common::mk_temp_dir() {
    common::log_function "$*"
    local name=$1
    local temp_dir=""
    temp_dir="/tmp/${name}$(common::get_datetime_string)"
    if ! mkdir -p "$temp_dir" 2>$g_log_err_file; then
        common::log "mkdir $temp_dir failed"
        temp_dir=""
    fi
    echo "$temp_dir"
}

# check if disk part contain sub paths
# $1: disk part
# $2: sub path
function common::check_part_sub_paths() {
    common::log_function "$*"
    local temp_dir=$1
    common::log_var "temp_dir" "$temp_dir"
    if [ -z "$temp_dir" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    local sub_path1=$2
    common::log_var "sub_path1" "$sub_path1"
    if [ -z "$sub_path1" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    local sub_path2=$3

    local ret=1
    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        if common::check_part_sub_path "$temp_dir" "$sub_path1"; then
            if [ -n "$sub_path2" ]; then
                if common::check_part_sub_path "$temp_dir" "$sub_path2"; then
                    ret=0
                fi
            else
                ret=0
            fi
        fi
    done

    if [[ $ret -eq 1 ]]; then
        return 1
    else
        return 0
    fi
}

function common::check_root_part() {
    common::log_function "$*"
    if common::check_part_sub_paths "$1" boot etc; then
        return 0
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_efi_part() {
    common::log_function "$*"
    local temp_dir=$1
    if common::check_part_sub_path "$temp_dir" EFI; then
        local efi_temp_dir="$temp_dir"/EFI
        if find "$efi_temp_dir" -name "*.efi" 2>$g_log_err_file; then
            return 0
        fi
    fi
        return $((CODE_ERROR_COMMON_ERROR))
}

function common::check_boot_part() {
    common::log_function "$*"
    local boot_dir=$1
    common::log_var "boot_dir" "$boot_dir"
    if [ -z "$boot_dir" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    if ls $boot_dir/initr* 2>$g_log_err_file; then
        if ls $boot_dir/vmlinu* 2>$g_log_err_file; then
            return 0
        else
            return $((CODE_ERROR_COMMON_ERROR))
        fi
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_lib_part() {
    common::log_function "$*"
    local lib_dir=$1
    local ls_output=""
    ls_output=$(ls -l "$lib_dir" 2>/dev/null)
    common::log "ls -l $lib_dir: $ls_output"
    ls_output=$(ls -l "$lib_dir/" 2>/dev/null)
    common::log "ls -l $lib_dir/: $ls_output"
    if test -d "$lib_dir"/modules; then
        common::log "g_lib_path=/lib"
        return 0
    elif test -d "$lib_dir"/lib/modules; then
        common::log "g_lib_path=/usr"
        return 0
    else
        common::log "g_lib_path=none"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_hidden_part(){
    common::log_function "$*"
    local disk_dev=$1
    local part_num=$2
    local part_flag=$(parted -m -s "$disk_dev" print 2>$g_log_err_file | grep "^$part_num:" | cut -d: -f7)
    common::log "The flag of part $part_num is $part_flag" "Info"
    if [[ "$part_flag" == *"bios_grub"* ]] || [[ "$part_flag" == *"boot"* ]]; then
        return 0
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_disk_part_dev() {
    common::log_function "$*"
    local disk_dev=$1
    local path_num=$2
    local disk_part_dev=""
    if [[ "$disk_dev" == "/dev/nvme"* ]]; then
        disk_part_dev="${disk_dev}p${path_num}"
    else
        disk_part_dev="${disk_dev}${path_num}"
    fi
    common::log "disk_part_dev: $disk_part_dev"
    echo "$disk_part_dev"
}

function common::get_part_off() {
    common::log_function "$*"
    local disk_dev=$1
    local part_off=0
    local tmp_part_nums=$(parted -s "$disk_dev" print  2>$g_log_err_file | awk '/^ / {print $1}')
    for tmp_part_num in $tmp_part_nums; do
        if common::check_hidden_part $disk_dev $tmp_part_num; then
            ((part_off++))
        fi
    done
    echo "$part_off"
}

function common::flush_disks() {
    common::log_function
    common::log "do disk data sync..."
    sync
}

function common::xfs_repair_part() {
    common::log_function "$*"
    local device=$1
    local mem_total=""
    mem_total=$(free -m | awk '/Mem:/ {print $2}')
    local code=$?
    if [[ $code -eq 0 ]]; then
        common::log "get total memory: ${mem_total} MB"
    else
        mem_total=1024
        common::log "get total memory failed, use default value: ${mem_total} MB"
    fi

    local ag_mem=0
    if [ "$mem_total" -ge 8192 ]; then
        ag_mem=1024
    elif [ "$mem_total" -ge 4096 ]; then
        ag_mem=512
    elif [ "$mem_total" -ge 2048 ]; then
        ag_mem=256
    else
        ag_mem=128
    fi
    common::log "set ag_mem: ${ag_mem} MB"

    common::log "do xfs_repair -L -m ${ag_mem} ${device}"
    xfs_repair -L -m "${ag_mem}" "${device}" >/dev/null 2>$g_log_err_file
    code=$?
    return $((code))
}

function common::fsck_part() {
    common::log_function "$*"
    local part_dev=$1
    common::log_var_exit "part_dev" "$part_dev"
    local part_fstype=$2
    common::log_var_exit "part_fstype" "$part_fstype"
    local fsck_flag=$3
    common::log_var_exit "fsck_flag" "$fsck_flag"

    # do disk data sync
    common::flush_disks

    #try umount part dev
    common::force_umount "$part_dev"
    local code=0
    if [ "$part_fstype" == "ext2" ] || [ "$part_fstype" == "ext3" ] ||
        [ "$part_fstype" == "ext4" ]; then
        local tune2fs_exec="$g_cur_dir"/bin/tune2fs
        chmod +x "$tune2fs_exec"
        local has_journal=0
        common::log "get and check dev $part_dev filesystem features..." "Info"
        if [ "$fsck_flag" == "1" ] && dumpe2fs -h "$part_dev" 2>/dev/null | grep 'Filesystem features' | grep 'has_journal'; then
            has_journal=1
            # force to disable ext journal function
            common::log "found filesystem features has_journal, try to turn off it..." "Info"
            "$tune2fs_exec" -ff -O ^has_journal "$part_dev"
        fi
        # do ext fsck
        common::log "do fsck.$part_fstype dev $part_dev..." "Info"
        fsck."$part_fstype" -fy "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "fsck return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
        fi
        if [ "$fsck_flag" == "1" ] && [[ $has_journal -eq 1 ]]; then
            # enable journal function again
            common::log "turn on filesystem features has_journal..." "Info"
            "$tune2fs_exec" -O has_journal "$part_dev"
            # do ext fsck again
            common::log "do fsck.$part_fstype dev $part_dev..." "Info"
            fsck."$part_fstype" -fy "$part_dev" >/dev/null 2>$g_log_err_file
            code=$?
        fi
    elif [ "$part_fstype" == "btrfs" ]; then
        common::log "do btrfsck dev $part_dev..." "Info"
        btrfsck --repair "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
    elif [ "$part_fstype" == "xfs" ]; then
        common::xfs_repair_part "$part_dev"
        code=$?
    elif [ "$part_fstype" == "ntfs" ]; then
        #ntfsfix -n "$part_dev" >/dev/null
        common::log "do nothing for ntfs $part_dev..." "Info"
    else
        common::log "try to do fsck(unknown fstype) dev $part_dev..." "Info"
        fsck -fy "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
    fi
    if [[ $code -ne 0 ]]; then
        common::log "fsck return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
    fi
}

function common::get_disk_part_mount_path() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local mount_path=""
    mount_path=$(df -P | grep "$disk_part" | awk '{print $6}')
    common::log "mount_path: $mount_path"
    echo "$mount_path"
}

function common::to_little_endian_hex() {
    local num=$1
    local count=$2
    local hex_str=$(printf "%0${count}x\n" $num)
    local len=${#hex_str}
    local result=""
    for ((i=$((len-2)); i>=0; i=i-2)); do
        result="${result}${hex_str:$i:2}"
    done

    echo "$result"
}

# sed with '[^[:space:]]+' will clear quotation which is in last position, such as:
#   sed -i -r -e "/console=[^[:space:]]+/s;console=[^[:space:]]+;;g" "/etc/default/grub"
#   input:  /etc/default/grub: GRUB_CMD_LINE="xxx=xx yyy=yy console=tty0"
#   output: /etc/default/grub: GRUB_CMD_LINE="xxx=xx yyy=yy
# the lack of quotation will cause grub mkconfig issues.
function common::add_sed_space_protect() {
    # insert 2 blankspaces to " before fixing
    sed -i -r -e "s/\"/  \"/g" "$1"
}

function common::remove_sed_space_protect() {
    # remove 2 blankspaces from " after fixing to recovery
    sed -i -r -e "s/  \"/\"/g" "$1"
}


export LC_ALL=C
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:/usr/local/bin
export LVM_SUPPRESS_FD_WARNINGS=1

function tool::print_usage() {
    global::print_version
    cat <<EOF
Usage: $(basename "$g_util_name") [OPTION...]
Examples:
  $g_util_name --help                           # show usage
  $g_util_name --version                        # show version
EOF
}

# boot mode
BOOT_MODE_BIOS=BIOS
BOOT_MODE_UEFI=UEFI
# grub flag
GRUB_FLAG_0XX=0
GRUB_FLAG_1XX=1
GRUB_FLAG_2XX=3

#SRC TYPE CODE
SRC_TYPE_WIN=9999
SRC_TYPE_VMWARE=9998

#error code
CODE_ERROR_GET_OS_NAME=11
CODE_ERROR_MOUNT_ROOT=30
CODE_ERROR_MOUNT_BOOT=31
CODE_ERROR_MOUNT_EFI=32
CODE_ERROR_FSTAB_NO_BOOT=40
CODE_ERROR_FSTAB_NO_ROOT=41
CODE_ERROR_MKINITRD=160
CODE_ERROR_MKINITRD2=161
CODE_ERROR_GET_DISK_PARTS=171
CODE_ERROR_GRUB_NO_BOOT_PART=201
CODE_ERROR_GRUB_NO_ROOT_PART=202
CODE_ERROR_GRUB_CFG_NOT_FOUND=203
CODE_ERROR_GRUB_V2_MKCONFIG=204
CODE_ERROR_GRUB_V2_INSTALL=205
CODE_ERROR_GRUB_LEGACY_INSTALL=206
CODE_ERROR_GRUB_V2_UEFI_SECURE_BOOT=207
CODE_ERROR_GRUB_FIX_CONFS=208
CODE_ERROR_GRUB_FIX_LEGACY=209
CODE_ERROR_GRUB_MS_2003_MBR=221
CODE_ERROR_GRUB_MS_HIGH_MBR=222
CODE_ERROR_GRUB_MS_WDS=223
CODE_ERROR_GRUB_MS_NBS=224 #NTFS boot sector
CODE_ERROR_GRUB_MS_ATT=225
CODE_ERROR_GRUB_MS_BCD=226

#target instance system disk num
#1001 = LastTargetDiskIndex(999) + 2
ECS_TARGET_SYS_DISK_NUM=1001

GRUB_OPTIONS_DEFAULT="net.ifnames=0 biosdevname=0"

#global vars
g_src_mount_infos=""
g_grub_flag=""
g_grub_install=""
g_grub_mkconfig=""
g_src_part_num=""
g_part_off=""
g_architecture=""
g_boot_mode=$BOOT_MODE_BIOS
g_is_win=0
g_is_vmware=0
g_sync_grub=0
g_has_lvm=0
g_use_fstab_swap=0
g_lib_path=""
g_grub_efi_file=""


function tool::get_disk_part_paths() {
    common::log_function "$*"
    local disk_dev=$1
    common::log_var_exit "disk_dev" "$disk_dev"

    local part_paths=""
    # travel data disk devs
    local disk_parts=""
    disk_parts=$(common::get_disk_parts "$disk_dev")
    common::log_var_exit "disk_parts" "$disk_parts"

    # travel disk parts
    common::log "travel disk parts..." "Info"
    for disk_part in $disk_parts
    do
        if [ -z "$disk_part" ]; then
            continue
        fi

        common::log "travel disk part: $disk_part" "Info"
        local part_fstype=""
        part_fstype=$(common::get_disk_part_fstype "$disk_part")

        if echo "$part_fstype" | grep -i "lvm"; then
            # handle lvm parts
            local vg_name=""
            common::log "pvdisplay $disk_part..." "Info"
            vg_name=$(pvdisplay "$disk_part" | grep -w "VG Name" | awk '{print $3}')
            common::log "vg_name: $vg_name" "Info"
            if [ -z "$vg_name" ]; then
                continue
            fi

            common::log "try activate vg $vg_name..." "Info"
            echo y | vgchange -ay $vg_name

            local lv_paths=""
            common::log "lvdisplay $vg_name..." "Info"
            lv_paths=$(lvdisplay "$vg_name" | grep -w "LV Path" | grep -Eo "/dev/.+")
            common::log "lv_paths: $lv_paths"
            if [ -z "$lv_paths" ]; then
                continue
            fi

            # travel lv paths
            for lv_path in $lv_paths
            do
                if [ -z "$lv_path" ]; then
                    continue
                fi
                part_paths="$part_paths $lv_path"
            done
        else
            # handle standard parts
            part_paths="$part_paths $disk_part"
        fi
        common::log "part_paths: $part_paths"
    done
    echo "$part_paths"
}

function tool::config_efigrub_cfg_param() {
    common::log_function "$*"
    local grub_cfg=$1
    common::log_var_exit "grub_cfg" "$grub_cfg"
    local boot_params=$2
    common::log_var_exit "boot_params" "$boot_params"
    local config_option=$3
    common::log_var_exit "config_option" "$config_option"

    common::backup_file "$grub_cfg"
    grep -n -E 'linuxefi[[:space:]]+(/boot)?/vmlinuz-.*root=.*' "$grub_cfg" | \
    while read line; do
        if [[ "$config_option" == "add" ]]; then
            if ! echo "$line" | grep "$boot_params"; then
                line_no=${line%%:*}
                chmod 600 "$grub_cfg"
                common::log "$config_option $boot_params at $line_no..."
                sed -i "$line_no s/$/ $boot_params/" "$grub_cfg"
            fi
        elif [[ "$config_option" == "del" ]]; then
            if echo "$line" | grep "$boot_params"; then
                line_no=${line%%:*}
                chmod 600 "$grub_cfg"
                common::log "$config_option $boot_params at $line_no..."
                sed -i "$line_no s/$boot_params//" "$grub_cfg"
            fi
        fi
    done
}

function tool::fix_efi_boot_file() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"

    # get efi file name
    local efi_boot_file=""
    local grub_efi_file_name=""
    local grub_efi_file_path=""
    if [ "$g_architecture" == "x86_64" ]; then
        efi_boot_file="$root_dir"/boot/efi/EFI/BOOT/BOOTX64.EFI
        grub_efi_file_name="grubx64.efi"
    elif [ "$g_architecture" == "arm64" ]; then
        efi_boot_file="$root_dir"/boot/efi/EFI/BOOT/BOOTAA64.EFI
        grub_efi_file_name="grubaa64.efi"
    else
        return
    fi
    # check efi boot file exist
    if [ -f "$efi_boot_file" ]; then
        common::log "$efi_boot_file is exist" "Info"
        return
    fi
    # find useful efi file
    grub_efi_file_path=$(find "$root_dir"/boot/efi/EFI -name $grub_efi_file_name | head -1)
    if [ -n "$grub_efi_file_path" ]; then
        common::log "find useful efi file: $grub_efi_file_path" "Info"
    fi
    # create efi boot dir
    local efi_boot_dir="$root_dir"/boot/efi/EFI/BOOT
    if ! test -d "$efi_boot_dir"; then
        common::log "make efi boot dir: $efi_boot_dir" "Info"
        mkdir -p "$efi_boot_dir"
    fi
    # check src efi boot file exist
    local src_efi_boot_file=""
    if [ -n "$g_grub_efi_file" ]; then
        src_efi_boot_file="$root_dir"/boot/efi"$g_grub_efi_file"
        if [ ! -f "$src_efi_boot_file" ]; then
            common::log "src efi boot file $src_efi_boot_file is not exist" "Info"
            src_efi_boot_file=""
        fi
    fi
    # copy to efi boot file
    if [ -n "$src_efi_boot_file" ]; then
        if [[ "$src_efi_boot_file" != *"$grub_efi_file_name" ]] && [ ! -f "$efi_boot_dir"/"$grub_efi_file_name" ]; then
            common::log "copy grub efi to BOOT directory"
            if [ -n "$grub_efi_file_path" ]; then
                cp "$grub_efi_file_path" "$efi_boot_dir"/"$grub_efi_file_name"
            else
                return
            fi
        fi
        common::log "copy $src_efi_boot_file to $efi_boot_file" "Info"
        cp "$src_efi_boot_file" "$efi_boot_file"
        # remove NvVars
        local nvvars="$root_dir"/boot/efi/NvVars
        if [ -f "$nvvars" ]; then
            common::log "try remove nvvars: $nvvars" "Info"
            rm -f "$nvvars"
        fi
    elif [ -n "$grub_efi_file_path" ]; then
        common::log "copy $grub_efi_file_path to $efi_boot_file" "Info"
        cp "$grub_efi_file_path" "$efi_boot_file"
        # remove NvVars
        local nvvars="$root_dir"/boot/efi/NvVars
        if [ -f "$nvvars" ]; then
            common::log "try remove nvvars: $nvvars" "Info"
            rm -f "$nvvars"
        fi
    fi
}

function tool::fix_one_grub_conf() {
    common::log_function "$*"
    local grub_conf=$1
    common::log_var_exit "grub_conf" "$grub_conf"
    local root_uuid=$2
    common::log_var "root_uuid" "$root_uuid"
    local boot_is_root_part=$3
    common::log_var "boot_is_root_part" "$boot_is_root_part"
    local grub_options=$4
    common::log_var "grub_options" "$grub_options"
    if [ "$grub_options" == "-" ]; then
        grub_options=""
    fi
    local efi_part=$5

    # update conf
    # fix root part dev
    if [[ $g_has_lvm -eq 0 ]]; then
        sed -i -r -e "/root=[^\ '\'''(']*[\ ]/s#root=[^\ '\'''(']*[\ ]#root=UUID=$root_uuid $grub_options #" \
            -e "/resume=[^\ '\'''(']*[\ ]/s#resume=[^\ '\'''(']*[\ ]#resume=UUID=$root_uuid #" \
            "$grub_conf" 2>$g_log_err_file
        ret=$?
        if [[ $ret -ne 0 ]] ; then
            common::log "sed grub root conf return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
            return $((ret))
        fi
    else
        sed -i -r -e "/root=\/dev\/mapper/!s#root=[^\ '\'''(']*[\ ]#root=UUID=$root_uuid $grub_options #" \
            -e "/resume=\/dev\/mapper/!s#resume=[^\ '\'''(']*[\ ]#resume=UUID=$root_uuid #" \
            "$grub_conf" 2>$g_log_err_file
        ret=$?
        if [[ $ret -ne 0 ]] ; then
            common::log "sed grub lvm root conf return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
            return $((ret))
        fi
    fi

    local boot_part_num=0
    if [ -n "$efi_part" ]; then
        # if efi part is the the 1st part which number is 0, boot part should be the 2nd part, which number is 1
        local efi_part_num=""
        efi_part_num=${efi_part: -1}
        if [[ $efi_part_num -eq 1 ]]; then
            boot_part_num=1
            common::log "check efi_part_num is 1, set boot_part_num=1" "Info"
        fi
    fi

    common::add_sed_space_protect "$grub_conf"
    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        # fix others
        if [[ $boot_is_root_part -eq 1 ]]; then
            # if boot part is root part, remove '/boot' prefix path
            sed -i -r -e "/root[[:space:]]+\(hd/s;\(hd.+,.+\);\(hd0,$boot_part_num\);g"  \
                    -e '/kernel[[:space:]]+\/vmlinuz/s;kernel /;kernel /boot/;g' \
                    -e '/initrd[[:space:]]+\/init/s;initrd /;initrd /boot/;g' \
                    -e '/kernel[[:space:]]+\/tzboot/s;kernel /;kernel /boot/;g' \
                    -e '/Kernel[[:space:]]+\/tzboot/s;Kernel /;Kernel /boot/;g' \
                    -e '/module[[:space:]]+\/vmlinuz/s;module /;module /boot/;g' \
                    -e '/module[[:space:]]+\/initr/s;module /;module /boot/;g' \
                    -e '/initrd[[:space:]]+\/modules/s;initrd /;initrd /boot/;g' \
                    -e "/disk=[^[:space:]]+/s;disk=[^[:space:]]+;;g" \
                    -e "/gfxmenu[[:space:]]+\(hd/cgfxmenu (hd0,$boot_part_num)/boot/message" \
                    "$grub_conf" 2>$g_log_err_file
            ret=$?
            if [[ $ret -ne 0 ]] ; then
                common::log "conf update1 return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
                break
            fi
        else
            sed -i -r -e "/root[[:space:]]+\(hd/s;\(hd.+,.+\);\(hd0,$boot_part_num\);g"  \
                    -e "/disk=[^[:space:]]+/s;disk=[^[:space:]]+;;g" \
                    -e "/gfxmenu[[:space:]]+\(hd/cgfxmenu (hd0,$boot_part_num)/boot/message" \
                    "$grub_conf" 2>$g_log_err_file
            ret=$?
            if [[ $ret -ne 0 ]] ; then
                common::log "conf update2 return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
                break
            fi
        fi

        sed -i -r -e "/console=[^[:space:]]+/s;console=[^[:space:]]+;;g" "$grub_conf"

        if [[ $g_has_lvm -eq 0 ]]; then
            sed -i -r -e "/rd_LVM_VG=[^[:space:]]+/s#rd_LVM_VG=[^[:space:]]+##" \
                -e "/rd_LVM_LV=[^[:space:]]+/s;rd_LVM_LV=[^[:space:]]+;;g" \
                -e "/resume=\/dev\/mapper[^[:space:]]+/s;resume=\/dev\/mapper[^[:space:]]+;;g" \
                -e "/rd\.lvm[^[:space:]]+/s;rd\.lvm[^[:space:]]+;;g" \
                "$grub_conf" 2>$g_log_err_file
            ret=$?
            if [[ $ret -ne 0 ]] ; then
                common::log "sed remove lvm conf return: $ret, error_msg: $(cat $g_log_err_file)" "Warn"
            fi
            ret=0
        fi

        sed -i 's/^serial/#&/' "$grub_conf"
        sed -i 's/^terminal/#&/' "$grub_conf"

        sed -i -r -e "/kernel/s/$/ $GRUB_OPTIONS_DEFAULT/g" "$grub_conf"
    done
    common::remove_sed_space_protect "$grub_conf"
    return $((ret))
}

function tool::fix_grub_confs() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local root_uuid=$2
    common::log_var "root_uuid" "$root_uuid"
    local boot_is_root_part=$3
    common::log_var "boot_is_root_part" "$boot_is_root_part"
    local efi_part=$4

    common::log "get grub options..." "Info"
    # get grub options
    local grub_options=""
    local grub_option_acpi_off=0
    local grub_option_ide_core=0
    local kernel_numbers=""
    kernel_numbers=$(ls "$root_dir"/lib/modules/)
    common::log "get /lib/modules/ kernel_numbers: $kernel_numbers" "Info"
    for kernel_number in $kernel_numbers; do
        common::log "check kernel_number: $kernel_number" "Info"
        if test -d "$root_dir"/lib/modules/"$kernel_number"; then
            local kernel_num_r=""
            local kernel_num_x=""
            local kernel_num_y_temp=""
            local kernel_num_y=""
            kernel_num_r=$(echo "$kernel_number" | awk -F. '{printf $1}')
            kernel_num_x=$(echo "$kernel_number" | awk -F. '{printf $2}')
            kernel_num_y_temp=$(echo "$kernel_number" | awk -F. '{printf $3}')
            kernel_num_y=$(echo "$kernel_num_y_temp" | awk -F'-' '{printf $1}')
            common::log "split kernel_number: $kernel_num_r, $kernel_num_x, $kernel_num_y" "Info"
            # fix acpi for ucloud kernel 2.6
            if [[ $kernel_num_r -eq 2 ]] && [[ $kernel_num_x -eq 6 ]] &&
                echo "$kernel_number" | grep -E "ucloud"; then
                grub_option_acpi_off=1
                common::log "found ucloud kernel, set grub_option_acpi_off=1" "Info"
            fi
            if [[ $kernel_num_r -gt 2 ]] || [[ $kernel_num_x -gt 6 ]] || [[ $kernel_num_y -gt 25 ]]; then
                grub_option_ide_core=1
                common::log "found greater-than-2.6.25 kernel, set grub_option_ide_core=1" "Info"
            fi
        fi
    done

    if [[ $grub_option_ide_core -eq 1 ]]; then
        grub_options="${grub_options} ide_core.noprobe=0.0"
    else
        grub_options="${grub_options} ide0.noprobe"
    fi
    if [[ $grub_option_acpi_off -eq 1 ]]; then
        grub_options="${grub_options} acpi=off"
    fi

    if [[ $g_has_lvm -eq 0 ]]; then
        local src_mount_infos=""
        src_mount_infos=${g_src_mount_infos//;/ }
        common::log "travel src_mount_infos: $src_mount_infos"
        if [ -n "$src_mount_infos" ]; then
            for src_mount_info in $src_mount_infos; do
                common::log "check src_mount_info: $src_mount_info"
                if [ -z "$src_mount_info" ]; then
                    continue
                fi
                local mount_dev=""
                mount_dev=$(echo "$src_mount_info" | awk -F":" '{printf $7}')
                if [[ $mount_dev == "/dev/vg"* ]]; then
                    local rd_lvm_lv_str="rd.lvm.lv=${mount_dev##"/dev/"}"
                    grub_options+="$rd_lvm_lv_str "
                fi
            done
        fi
    fi
    common::log "get grub options: $grub_options" "Info"

    local code=0
    local grub_conf="$root_dir"/boot/grub/grub.conf
    if [ -f "$grub_conf" ]; then
        tool::fix_one_grub_conf "$grub_conf" "$root_uuid" "$boot_is_root_part" "$grub_options"
        code=$?
        if [[ $code -ne 0 ]]; then
            return $((code))
        fi
    fi
    local menu_lst="$root_dir"/boot/grub/menu.lst
    if [ -f "$menu_lst" ]; then
        tool::fix_one_grub_conf "$menu_lst" "$root_uuid" "$boot_is_root_part" "$grub_options"
        code=$?
        if [[ $code -ne 0 ]]; then
            return $((code))
        fi
    fi
    if [ -n "$g_src_part_num" ] && [ ! -f "$grub_conf" ] && [ ! -f "$menu_lst" ] && [ -d "$root_dir"/boot/efi/EFI ]; then
        local temp_grub_conf=""
        temp_grub_conf=$(find "$root_dir"/boot/efi/EFI -name "grub.conf")
        if [ -f "$grub_conf" ]; then
            tool::fix_one_grub_conf "$temp_grub_conf" "$root_uuid" "$boot_is_root_part" "$grub_options" "$efi_part"
            code=$?
            if [[ $code -ne 0 ]]; then
                return $((code))
            fi
        fi
    fi

    return 0
}

function tool::fix_vmware_grub_confs() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local root_uuid=$2
    common::log_var "root_uuid" "$root_uuid"
    local boot_is_root_part=$3
    common::log_var "boot_is_root_part" "$boot_is_root_part"
    local efi_part=$4

    common::backup_file "$grub_conf"

    # make link /boot/grub/menu.lst to /boot/grub/grub.conf
    local real_grub_conf=""
    local grub_conf="$root_dir"/boot/grub/grub.conf
    local menu_lst="$root_dir"/boot/grub/menu.lst
    local grub_dir="$root_dir"/boot/grub
    mkdir -p $grub_dir
    local last_cur_dir=$PWD
    if ! cd "$grub_dir" 2>$g_log_err_file; then
        common::log "cd $grub_dir" "Error"
        return 1
    fi

    if [ ! -f "$grub_conf" ] && [ -f "$menu_lst" ]; then
        real_grub_conf="$root_dir"/boot/grub/menu.lst
        common::log "found grub_conf(menu_lst): $real_grub_conf" "Info"
    elif [ -f "$grub_conf" ] && [ ! -f "$menu_lst" ]; then
        real_grub_conf="$root_dir"/boot/grub/grub.conf
        common::log "found grub_conf: $real_grub_conf" "Info"
    elif [ -f "$grub_conf" ] && [ -f "$menu_lst" ]; then
        real_grub_conf="$root_dir"/boot/grub/menu.lst
        common::log "found both grub_conf $grub_conf and menu_lst $menu_lst" "Info"
    elif [ ! -f "$grub_conf" ] && [ ! -f "$menu_lst" ] && [ -d "$root_dir"/boot/efi/EFI ]; then
        # /boot/efi/EFI dir may have grub.conf, stage1, stage2
        local temp_grub_conf=""
        temp_grub_conf=$(find "$root_dir"/boot/efi/EFI -name "grub.conf")
        common::log "find grub_conf in EFI: $temp_grub_conf" "Info"
        if [ -n "$temp_grub_conf" ]; then
            real_grub_conf="$temp_grub_conf"
        fi
    fi

    if ! cd "$last_cur_dir"; then
        common::log "cd $last_cur_dir" "Error"
        return 1
    fi

    # update conf
    if [ -n "$real_grub_conf" ]; then
        common::log "update grub_conf: $real_grub_conf..." "Info"
        # get grub options
        local grub_options="$GRUB_OPTIONS_DEFAULT"
        local grub_option_ide_core=0
        local kernel_numbers=""
        kernel_numbers=$(ls "$root_dir"/lib/modules/)
        common::log "get /lib/modules/ kernel_numbers: $kernel_numbers" "Info"
        for kernel_number in $kernel_numbers; do
            common::log "check kernel_number: $kernel_number" "Info"
            if test -d "$root_dir"/lib/modules/"$kernel_number"; then
                local kernel_num_r=""
                local kernel_num_x=""
                local kernel_num_y_temp=""
                local kernel_num_y=""
                kernel_num_r=$(echo "$kernel_number" | awk -F. '{printf $1}')
                kernel_num_x=$(echo "$kernel_number" | awk -F. '{printf $2}')
                kernel_num_y_temp=$(echo "$kernel_number" | awk -F. '{printf $3}')
                kernel_num_y=$(echo "$kernel_num_y_temp" | awk -F'-' '{printf $1}')
                common::log "split kernel_number: $kernel_num_r, $kernel_num_x, $kernel_num_y" "Info"
                if [[ $kernel_num_r -gt 2 ]] || [[ $kernel_num_x -gt 6 ]] || [[ $kernel_num_y -gt 25 ]]; then
                    grub_option_ide_core=1
                    common::log "found greater-than-2.6.25 kernel, set grub_option_ide_core=1" "Info"
                fi
            fi
        done

        if [[ $grub_option_ide_core -eq 1 ]]; then
            grub_options="${grub_options} ide_core.noprobe=0.0"
        else
            grub_options="${grub_options} ide0.noprobe"
        fi
        sed -i -r -e "/kernel/s/$/ $grub_options /g" "$real_grub_conf"

        common::add_sed_space_protect "$real_grub_conf"
        # fix others
        sed -i -r -e "/console=[^[:space:]]+/s;console=[^[:space:]]+;;g" "$real_grub_conf"
        common::remove_sed_space_protect "$real_grub_conf"

        if [[ $g_has_lvm -eq 0 ]]; then
            sed -i -r -e "/\/dev\/sd/s;\/dev\/vd;;g" \
                -e "/\/dev\/xvd/s;\/dev\/vd;;g" \
            "$real_grub_conf"
        fi

        sed -i 's/^serial/#&/' "$real_grub_conf"
        sed -i 's/^terminal/#&/' "$real_grub_conf"
    fi

    return 0
}

function tool::fix_grub_cfg() {
    common::log_function "$*"
    local grub_cfg=$1
    common::log_var_exit "grub_cfg" "$grub_cfg"
    if [ ! -e "$grub_cfg" ]; then
        common::log "$grub_cfg not exist" "Error"
        return 2
    fi

    if [[ g_is_vmware -eq 1 ]]; then
        tool::fix_vmware_grub_cfg "$grub_cfg"
        ret=$?
        return $((ret))
    fi

    local root_uuid=$2
    common::log_var_exit "root_uuid" "$root_uuid"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local boot_part=$4
    local boot_uuid=""
    if [ -n "$boot_part" ] && [ "$boot_part" != "$root_part" ]; then
        boot_uuid=$(common::get_disk_part_uuid "$boot_part")
    else
        boot_uuid=$root_uuid
    fi
    common::log_var_exit "boot_uuid" "$boot_uuid"

    common::backup_file "$grub_cfg"

    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        # modify grub cfg
        # fix root part dev
        if [[ $g_has_lvm -eq 0 ]]; then
            # remove lvm cfg
            common::add_sed_space_protect "$grub_cfg"
            sed -i -r -e "/rd_LVM_VG=[^[:space:]]+/s#rd_LVM_VG=[^[:space:]]+##" \
                -e "/rd_LVM_LV=[^[:space:]]+/s;rd_LVM_LV=[^[:space:]]+;;g" \
                -e "/resume=\/dev\/mapper[^[:space:]]+/s;resume=\/dev\/mapper[^[:space:]]+;;g" \
                -e "/rd\.lvm[^[:space:]]+/s;rd\.lvm[^[:space:]]+;;g" \
                "$grub_cfg" 2>$g_log_err_file
            ret=$?
            common::remove_sed_space_protect "$grub_cfg"
            if [[ $ret -ne 0 ]] ; then
                common::log "sed remove lvm cfg return: $ret, error_msg: $(cat $g_log_err_file)" "Warn"
            fi

            local src_mount_infos=""
            local rd_lvm_lv_strs=""
            src_mount_infos=${g_src_mount_infos//;/ }
            common::log "travel src_mount_infos: $src_mount_infos"
            if [ -n "$src_mount_infos" ]; then
                for src_mount_info in $src_mount_infos; do
                    common::log "check src_mount_info: $src_mount_info"
                    if [ -z "$src_mount_info" ]; then
                        continue
                    fi
                    local mount_dev=""
                    mount_dev=$(echo "$src_mount_info" | awk -F":" '{printf $7}')
                    if [[ $mount_dev == "/dev/vg"* ]]; then
                        local rd_lvm_lv_str="rd.lvm.lv=${mount_dev##"/dev/"}"
                        rd_lvm_lv_strs+="$rd_lvm_lv_str "
                    fi
                done
            fi
            sed -i -r -e "/root=[^\ '\'''(']*[\ ]/s#root=[^\ '\'''(']*[\ ]#root=UUID=$root_uuid $rd_lvm_lv_strs#" \
                "$grub_cfg" 2>$g_log_err_file
            ret=$?
            if [[ $ret -ne 0 ]] ; then
                common::log "sed grub root cfg return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
                break
            fi
            ret=0
        else
            sed -i -r -e "/root=\/dev\/mapper/!s#root=[^\ '\'''(']*[\ ]#root=UUID=$root_uuid #" \
                "$grub_cfg" 2>$g_log_err_file
            ret=$?
            if [[ $ret -ne 0 ]] ; then
                common::log "sed grub lvm root cfg return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
                break
            fi
        fi
        # fix others
        sed -i -r -e "s/disk=[^[:space:]^\"]+//g" \
            -e "s/root=LABEL=[^[:space:]^\"]+//g" \
            -e "s/root=\/dev\/disk\/by-label[^[:space:]^\"]+//g" \
            -e "/--set=boot/ s/([0-9a-f-]{36})/$boot_uuid/g" \
            -e "/--set=root/ s/([0-9a-f-]{36})/$boot_uuid/g" \
            -e "/search\.fs_uuid/ s/([0-9a-f-]{36})/$boot_uuid/g" \
            -e "/--set=dev/ s/([0-9a-f-]{36})/$boot_uuid/g" \
            "$grub_cfg" 2>$g_log_err_file
        ret=$?
        if [[ $ret -ne 0 ]] ; then
            common::log "sed grub other cfg return: $ret, error_msg: $(cat $g_log_err_file)" "Error"
            break
        fi
        local grub_options="$GRUB_OPTIONS_DEFAULT"
        if [ "$g_architecture" == "arm64" ]; then
            common::add_sed_space_protect "$grub_cfg"
            sed -i -r -e "/console=[^[:space:]]+/s;console=[^[:space:]]+;;g" "$grub_cfg" 2>$g_log_err_file
            common::remove_sed_space_protect "$grub_cfg"
            grub_options="$grub_options console=tty0 console=ttyAMA0,115200n8"
        fi

        if grep -q "linux16" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set linux16 grub_options: $grub_options"
            sed -i -r -e "/linux16/s/$/ $grub_options/g" "$grub_cfg" 2>"$g_log_err_file"
        fi
        if grep -q "set kernelopts=\"" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set kernelopts=\"\" grub_options: $grub_options"
            sed -i -r -e "s/set kernelopts=\"/set kernelopts=\"${grub_options} /" "$grub_cfg" 2>"$g_log_err_file"
        fi
        if grep -q "kernelopts=[^\"]" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set kernelopts grub_options: $grub_options"
            sed -i -r -e "/kernelopts=[^\"]/s/$/ $grub_options/g" "$grub_cfg" 2>"$g_log_err_file"
        fi
        if grep -q "options " "$grub_cfg" 2>$g_log_err_file && ! grep -qE "options.*kernelopts" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set options grub_options: $grub_options"
            sed -i -r -e "/options /s/$/ $grub_options/g" "$grub_cfg" 2>"$g_log_err_file"
        fi
        if grep -q "GRUB_CMDLINE_LINUX=\"" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set GRUB_CMDLINE_LINUX grub_options: $grub_options"
            sed -i -r -e "s/GRUB_CMDLINE_LINUX=\"/GRUB_CMDLINE_LINUX=\"${grub_options} /" "$grub_cfg" 2>"$g_log_err_file"
            sed -i -r -e "s/root=LABEL=[^[:space:]^\"]+//g" "$grub_cfg" 2>"$g_log_err_file"
        fi
        if grep -q "GRUB_CMDLINE_LINUX_DEFAULT=\"" "$grub_cfg" 2>$g_log_err_file; then
            common::log "set GRUB_CMDLINE_LINUX_DEFAULT grub_options: $grub_options"
            sed -i -r -e "s/GRUB_CMDLINE_LINUX_DEFAULT=\"/GRUB_CMDLINE_LINUX_DEFAULT=\"${grub_options} /" "$grub_cfg" 2>"$g_log_err_file"
            sed -i -r -e "s/root=LABEL=[^[:space:]^\"]+//g" "$grub_cfg" 2>"$g_log_err_file"
        fi
    done
    return $((ret))
}

function tool::fix_vmware_grub_cfg() {
    common::log_function "$*"
    local grub_cfg=$1
    common::log_var_exit "grub_cfg" "$grub_cfg"
    if [ ! -e "$grub_cfg" ]; then
        common::log "$grub_cfg not exist" "Error"
        return 1
    fi

    common::backup_file "$grub_cfg"

    # fix grub cfg
    sed -i -r -e "/\/dev\/sd/s;\/dev\/vd;;g" \
            -e "/\/dev\/xvd/s;\/dev\/vd;;g" \
            "$grub_cfg" 2>$g_log_err_file
    ret=$?
    if [[ $ret -ne 0 ]] ; then
        common::log "sed grub cfg return: $ret, error_msg: $(cat $g_log_err_file)" "Warn"
    fi

    sed -i -r -e "/linux16/s/$/ $GRUB_OPTIONS_DEFAULT/g" "$grub_cfg" 2>$g_log_err_file
    return 0
}

function tool::fix_one_initrd() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local kernel_number=$2
    common::log_var_exit "kernel_number" "$kernel_number"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$4
    common::log_var "root_uuid" "$root_uuid"

    common::log "try fix initrd: ${kernel_number}..." "Info"
    for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
    local initrd_cmd=""
    local initrd_cmd2=""
    common::log "check os_name: $g_os_name" "Info"
    if echo "$g_os_name" | grep -E '(CentOS|Red Hat).* 5\.[[:digit:]]+'; then
        common::log "found CentOS* 5"
        initrd_cmd="mkinitrd -f --allow-missing \
                          --with=xen-vbd  --preload=xen-vbd \
                          --with=xen-platform-pci --preload=xen-platform-pci \
                          --with=virtio_blk --preload=virtio_blk \
                          --with=virtio_pci --preload=virtio_pci \
                          --with=virtio_console --preload=virtio_console \
                          --with=hvc_console --preload=hvc_console \
                          --with=ahci --preload=ahci \
                    /boot/initrd-${kernel_number}.img $kernel_number"
    elif echo "$g_os_name" | grep -E '(CentOS|Red Hat|Anolis|Rocky Linux|AlmaLinux|Oracle Linux).* (6\.[[:digit:]]+|7|8)'; then
        common::log "found CentOS* 6|7|8"
        initrd_cmd="mkinitrd -f --allow-missing \
                          --with=xen-blkfront --preload=xen-blkfront \
                          --with=virtio_blk --preload=virtio_blk \
                          --with=virtio_pci --preload=virtio_pci \
                          --with=virtio_console --preload=virtio_console \
                    /boot/initramfs-${kernel_number}.img $kernel_number"
        initrd_cmd2="dracut -f --add-drivers \" virtio_blk virtio_net virtio_pci virtio_ring virtio \" --kver $kernel_number"
    elif echo "$g_os_name" | grep -E '(Ubuntu|Debian)'; then
        common::log "found Ubuntu|Debian"
        local initramfs_tools_modules="$root_dir"/etc/initramfs-tools/modules
        # modify initramfs_tools_modules
        echo -e 'xen-blkfront\nvirtio_blk\nvirtio_pci\nvirtio_console' >> "$initramfs_tools_modules"
        initrd_cmd="mkinitramfs $kernel_number -o /boot/initrd.img-${kernel_number}"
        initrd_cmd2="update-initramfs -u"
    elif echo "$g_os_name" | grep -E 'openSUSE.* (10|11|12)' ||
        echo "$g_os_name" | grep -E '(SUSE|SLES).* (9|10|11)'; then
        common::log "found openSUSE 10|11|12|Debian 9|10|11"
        if [ -n "$root_uuid" ]; then
            local bootloader="$root_dir"/etc/sysconfig/bootloader
            common::backup_file "$bootloader"
            common::add_sed_space_protect "$bootloader"
            # modify bootloader
            sed -i -r -e "/resume=[^[:space:]]+/s;resume=[^[:space:]]+;resume=/dev/disk/by-uuid/${root_uuid};g" "$bootloader" 2>$g_log_err_file
            common::remove_sed_space_protect "$bootloader"
        fi
        if echo "$g_os_name" | grep -E '(SUSE|SLES).* (9|10|11)'; then
            local modprobe_conf_local="$root_dir"/etc/modprobe.conf.local
            common::backup_file "$modprobe_conf_local"
            # modify modprobe_conf_local
            local modules_list="ahci libahci libata scsi_mod xen_platform_pci xen-vbd xen-vnif virtio virtio_console virtio_net virtio_blk virtio_pci"
            echo 'options xen_platform_pci dev_unplug=all' >> "$modprobe_conf_local"
        fi
        local sysconfig_kernel="$root_dir"/etc/sysconfig/kernel
        common::backup_file "$sysconfig_kernel"
        # modify sysconfig_kernel
        local modules_list="ahci libahci libata scsi_mod xen_platform_pci xen-vbd xen-vnif virtio virtio_console virtio_net virtio_blk virtio_pci"
        sed -i "/^INITRD_MODULES/cINITRD_MODULES=\"$modules_list\"" "$sysconfig_kernel" 2>$g_log_err_file
        local setup_72_block="$root_dir"/lib/mkinitrd/setup/72-block.sh
        common::backup_file "$setup_72_block"
        # modify setup_72_block
        sed -i -r -e '/loop\*/s/loop\*/loop\*|nbd\*/' "$setup_72_block" 2>$g_log_err_file
        initrd_cmd="mkinitrd -f lvm2 -k vmlinuz-${kernel_number} -i initrd-${kernel_number} -d $root_part"
        initrd_cmd2="update-initramfs -u"
    elif echo "$g_os_name" | grep -E 'openSUSE.* (13|15|42)' ||
        echo "$g_os_name" | grep -E '(SUSE|SLES).* (12|15)'; then
        common::log "found openSUSE 13|15|42|Debian 12|15"
        if [ -n "$root_uuid" ]; then
            local bootloader="$root_dir"/etc/sysconfig/bootloader
            common::backup_file "$bootloader"
            common::add_sed_space_protect "$bootloader"
            # modify bootloader
            sed -i -r -e "/resume=[^[:space:]]+/s;resume=[^[:space:]]+;resume=/dev/disk/by-uuid/${root_uuid};g" "$bootloader" 2>$g_log_err_file
            common::remove_sed_space_protect "$bootloader"
        fi
        if echo "$g_os_name" | grep -E '(SUSE|SLES).* (12|15)'; then
            if [ -d /usr/lib/grub2/x86_64-efi ]; then
                sed -i -r -e '/LOADER_TYPE=/c\LOADER_TYPE=\"grub2-efi\"' "$bootloader" 2>$g_log_err_file
            fi
            local modprobe_conf_local="$root_dir"/etc/modprobe.conf.local
            common::backup_file "$modprobe_conf_local"
            # modify modprobe_conf_local
            echo 'options xen_platform_pci dev_unplug=all' >> "$modprobe_conf_local"
        fi
        local spec_conf="$root_dir"/etc/dracut.conf.d/spec.conf
        common::backup_file "$spec_conf"
        # modify spec_conf
        local modules_list=" xen_platform_pci xen-vbd xen-vnif virtio virtio_console virtio_net virtio_blk virtio_pci"
        echo "add_drivers+=\"$modules_list\"" >> "$spec_conf"
        initrd_cmd="mkinitrd -f lvm2 -k vmlinuz-${kernel_number} -i initrd-${kernel_number} -d $root_part"
        initrd_cmd2="dracut -f --add-drivers \" virtio_blk virtio_net virtio_pci virtio_ring virtio \" --kver $kernel_number"
    elif echo "$g_os_name" | grep -E 'Amazon Linux.*202[[:digit:]]'; then
        common::log "found Amazon Linux 202*"
        local ec2_conf="$root_dir"/etc/dracut.conf.d/ec2.conf
        if [ -f "$ec2_conf" ]; then
            \mv -f "$ec2_conf" "${ec2_conf}.disable_by_smc"
        fi
        initrd_cmd="dracut -f --add-drivers \" virtio_blk virtio_net virtio_pci virtio_ring virtio \" --kver $kernel_number"
    elif chroot "$root_dir" /bin/sh -c "which apt-get >/dev/null 2>&1" 2>$g_log_err_file; then
        common::log "found apt-get"
        local initramfs_tools_modules="$root_dir"/etc/initramfs-tools/modules
        # modify initramfs_tools_modules
        echo -e 'xen-blkfront\nvirtio_blk\nvirtio_pci\nvirtio_console' >> "$initramfs_tools_modules"
        initrd_cmd="mkinitramfs $kernel_number -o /boot/initrd.img-${kernel_number}"
        initrd_cmd2="update-initramfs -u"
    else
        common::log "found unknown os"
        initrd_cmd="test -f /boot/initramfs-${kernel_number}.img && mkinitrd -f --allow-missing \
                          --with=virtio_blk --preload=virtio_blk \
                          --with=virtio_pci --preload=virtio_pci \
                          --with=virtio_console --preload=virtio_console \
                    /boot/initramfs-${kernel_number}.img $kernel_number; \
                    test -f /boot/initrd-${kernel_number}.img && mkinitrd -f --allow-missing \
                          --with=xen-vbd  --preload=xen-vbd \
                          --with=xen-platform-pci --preload=xen-platform-pci \
                          --with=virtio_blk --preload=virtio_blk \
                          --with=virtio_pci --preload=virtio_pci \
                          --with=virtio_console --preload=virtio_console \
                          --with=hvc_console --preload=hvc_console \
                          --with=ahci --preload=ahci \
                    /boot/initrd-${kernel_number}.img $kernel_number"
        initrd_cmd2="dracut -f --add-drivers \" virtio_blk virtio_net virtio_pci virtio_ring virtio \" --kver $kernel_number"
    fi

    # try create /var/tmp dir
    local need_clear_var_tmp=0
    local var_tmp_dir="$root_dir"/var/tmp
    if [ ! -e "$var_tmp_dir" ]; then
        mkdir -p "$var_tmp_dir" 2>&1
        need_clear_var_tmp=1
        common::log "create var_tmp_dir: $var_tmp_dir"
    fi

    local ret=0
    local chroot_initrd_cmd="export PATH=/bin:/usr/bin:$PATH; $initrd_cmd"
    common::log "chroot and exec mkinitrd cmd: $chroot_initrd_cmd"
    chroot "$root_dir" /bin/sh -c "$chroot_initrd_cmd" >>"$g_log_file" 2>$g_log_err_file
    local code=$?
    if [[ $code -ne 0 ]]; then
        common::log "mkinitrd return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
        if [ -n "$initrd_cmd2" ]; then
            chroot_initrd_cmd="export PATH=/bin:/usr/bin:$PATH; $initrd_cmd2"
            common::log "chroot and exec mkinitrd2 cmd: $chroot_initrd_cmd"
            chroot "$root_dir" /bin/sh -c "$chroot_initrd_cmd" >>"$g_log_file" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                common::log "mkinitrd2 return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
                ret=$CODE_ERROR_MKINITRD2
            else
                cat $g_log_err_file
            fi
        else
            ret=$CODE_ERROR_MKINITRD
        fi
    else
        cat $g_log_err_file
    fi
    sleep 2s
    umount "$root_dir"/{dev,proc,sys}

    # clear /var/tmp dir
    if [[ $need_clear_var_tmp -eq 1 ]] && [ -n "$var_tmp_dir" ]; then
        rm -rf "$var_tmp_dir"
        common::log "remove var_tmp_dir: $var_tmp_dir"
    fi

    return $((ret))
}

function tool::fix_all_initrds() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local root_part=$2
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$3
    common::log_var "root_uuid" "$root_uuid"

    local device_map=""
    device_map=$(find "$root_dir"/boot -name "device.map" 2>/dev/null)
    common::log "device_map: $device_map"
    if [ -n "$device_map" ]; then
        common::log "write device_map"
        echo "(hd0) /dev/vda" > "$device_map"
    fi

    local ret=0
    local kernel_numbers=""
    kernel_numbers=$(ls "$root_dir"/lib/modules/)
    common::log "get /lib/modules/ kernel_numbers: $kernel_numbers" "Info"
    for kernel_number in $kernel_numbers; do
        common::log "check kernel_number: $kernel_number" "Info"
        if test -d "$root_dir"/lib/modules/"$kernel_number" &&
            test -f "$root_dir"/boot/config-"$kernel_number"; then
            tool::fix_one_initrd "$root_dir" "$kernel_number" "$root_part" "$root_uuid"
            ret=$?
            if [[ $ret -ne 0 ]]; then
                common::log "try fix initrd: $kernel_number failed" "Warn"
            fi
        fi
    done

    return 0
}

function tool::fix_network() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"

    if echo "$g_os_name" | grep -E 'Amazon Linux.*202[[:digit:]]'; then
        #fix net config
        local net_dev=eth0
        local network_dir="$root_dir"/etc/systemd/network
        mkdir -p "$network_dir"
        local net_cfg=${network_dir}/10-${net_dev}.network
        cat > "$net_cfg" <<EOF
[Match]
Name=$net_dev

[Network]
DHCP=yes
EOF
        #enable systemd-networkd
        local systemd_network_cmd="systemctl enable systemd-networkd"
        common::log "chroot and exec enable systemd-network cmd: $systemd_network_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        chroot "$root_dir" /bin/sh -c "$systemd_network_cmd" >>"$g_log_file" 2>$g_log_err_file
        umount "$root_dir"/{dev,proc,sys}
    fi
}

function tool::fix_cloud_init() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"

    local cfg_path="$root_dir"/etc/cloud
    local cfg_file="$cfg_path"/cloud.cfg
    if [ ! -f "$cfg_file" ]; then
        return
    fi

    if grep -E "AliYun" "$cfg_file"; then
        return
    fi

    common::log "try fix cloud init config" "Info"
    local cfg_bak_path="$cfg_path"_p2v_bak_$(common::get_datetime_string)
    mv $cfg_path "$cfg_bak_path"
    local smc_prepare_dir="$g_cur_dir"/smc_prepare
    unalias cp
    cp -rf "$smc_prepare_dir"/* "$root_dir"/
}

function tool::fix_upstart_confs() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"

    # fix google upstart confs
    if find "$root_dir"/etc/init -name "*google*" 2>/dev/null; then
        local google_confs=""
        google_confs=$(find "$root_dir"/etc/init -name "*google*")
        common::log "google_confs: $google_confs" "Debug"
        for google_conf in $google_confs
        do
            if [ -n "$google_conf" ]; then
                common::log "try disable $google_conf" "Info"
                \mv -f "$google_conf" "$google_conf".disabled_by_smc
            fi
        done
    fi
}

function tool::fix_systemd_confs() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"

    # disable google systemd confs
    if find "$root_dir"/etc/systemd -name "*google*" 2>/dev/null; then
        local google_confs=""
        google_confs=$(find "$root_dir"/etc/systemd -name "*google*" 2>/dev/null)
        for google_conf in $google_confs
        do
            if [ -n "$google_conf" ]; then
                common::log "try remove $google_conf" "Info"
                rm -f "$google_conf"
            fi
        done
    fi
    if echo "$g_os_name" | grep -E 'Amazon Linux.*202[[:digit:]]'; then
        #disable amazon ssm agent
        local disable_cmd="systemctl disable amazon-ssm-agent.service"
        common::log "chroot and exec disable amazon-ssm-agent.service cmd: $disable_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        chroot "$root_dir" /bin/sh -c "$disable_cmd" >>"$g_log_file" 2>$g_log_err_file
        umount "$root_dir"/{dev,proc,sys}
    fi
}

function tool::fix_fstab() {
    common::log_function "$*"
    local root_disk_num=$1
    common::log_var_exit "root_disk_num" "$root_disk_num"
    local root_dir=$2
    common::log_var_exit "root_dir" "$root_dir"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$4
    common::log_var "root_uuid" "$root_uuid"
    local boot_part=$5
    common::log_var "boot_part" "$boot_part"

    # travel fstab lines, find root / mount point
    common::log "try fix /etc/fstab" "Info"
    local etc_fstab="$root_dir"/etc/fstab
    local valid_fstab_lines=""
    valid_fstab_lines=$(grep -E "^$|^#" "$etc_fstab")
    common::log "valid_fstab_lines: $valid_fstab_lines"

    local need_boot_part=0
    if [ -n "$boot_part" ] && [ "$boot_part" != "$root_part" ]; then
        need_boot_part=1
        common::log "set need_boot_part=1"
    fi
    local need_root_part=1

    # g_src_mount_infos format e.g.
    # disk_num:part_num:mount_path:mount_param1:mount_param2:mount_param3;...
    local src_mount_infos=""
    src_mount_infos=${g_src_mount_infos//;/ }
    common::log "travel src_mount_infos: $src_mount_infos"
    if [ -n "$src_mount_infos" ]; then
        for src_mount_info in $src_mount_infos; do
            common::log "check src_mount_info: $src_mount_info"
            if [ -z "$src_mount_info" ]; then
                continue
            fi

            local disk_index=0
            local part_index=0
            local src_part_num=0
            local mount_path=""
            local mount_arg1=""
            local mount_arg2=0
            local mount_arg3=0
            local lv_dev=""
            disk_index=$(echo "$src_mount_info" | awk -F":" '{printf $1}')
            part_index=$(echo "$src_mount_info" | awk -F":" '{printf $2}')
            src_part_num=$(echo "$src_mount_info" | awk -F":" '{printf $3}')
            mount_path=$(echo "$src_mount_info" | awk -F":" '{printf $4}')
            mount_arg1=$(echo "$src_mount_info" | awk -F":" '{printf $5}')
            mount_arg2=$(echo "$src_mount_info" | awk -F":" '{printf $6}')
            mount_arg3=$(echo "$src_mount_info" | awk -F":" '{printf $7}')
            lv_dev=$(echo "$src_mount_info" | awk -F":" '{printf $8}')
            common::log "split src_mount_info: $disk_index, $part_index, $src_part_num, $mount_path, $mount_arg1, $mount_arg2, $mount_arg3, $mount_dev"
            if [ -z "$disk_index" ] || [ -z "$part_index" ] || [ -z "$src_part_num" ] || [ -z "$mount_path" ] ||
                [ -z "$mount_arg1" ] || [ -z "$mount_arg2" ] || [ -z "$mount_arg3" ]; then
                continue
            fi
            if [ "$lv_dev" == "-" ]; then
                lv_dev=""
            fi

            local is_swap_part=0
            local part_dev=""
            local mount_dev=""
            if [ -z "$lv_dev" ]; then
                local disk_num=$((disk_index+2))
                if [ "$disk_num" == "$ECS_TARGET_SYS_DISK_NUM" ]; then
                    disk_num=$root_disk_num
                fi
                local disk_dev=""
                disk_dev=$(common::get_disk_dev "$disk_num")
                if [ "$src_part_num" == "-1" ] || [ "$src_part_num" == "-" ] ; then
                    src_part_num=""
                fi

                local is_gpt=0
                if parted "$disk_dev" -s print 2>$g_log_err_file | grep -w "gpt"; then
                    is_gpt=1
                fi

                local part_off=0
                # get part index offset, such as gpt system parts(boot_grub/efi part)
                if [ -z "$src_part_num" ] && [[ $is_gpt -eq 1 ]]; then
                    part_off=$(common::get_part_off $disk_dev)
                    if [[ $part_off -eq 0 ]]; then
                        part_off=""
                    else
                        ((part_index+=part_off))
                    fi
                fi

                local part_num=0
                if [ -n "$src_part_num" ]; then
                    part_num=$src_part_num
                else
                    # if part index >= 3, mbr need a extend part to place logical parts,
                    # but gpt not.
                    if [[ $part_index -ge 3 ]] && [[ $is_gpt -eq 0 ]]; then
                        ((part_num=part_index+2))
                    else
                        ((part_num=part_index+1))
                    fi
                fi

                if [ -n "$src_part_num" ]; then
                    if [[ $src_part_num -gt 0 ]]; then
                        part_dev=$(common::get_disk_part_dev $disk_dev $src_part_num)
                    else
                        part_dev=${disk_dev}
                    fi
                else
                    part_dev=$(common::get_disk_part_dev $disk_dev $part_num)
                fi

                local part_uuid=""
                part_uuid=$(common::get_disk_part_uuid "$part_dev")
                if [ -z "$part_uuid" ]; then
                    continue
                fi

                mount_dev="UUID=${part_uuid}"
            else
                part_dev=$lv_dev

                # mk swap part
                if [ "$mount_path" == "swap" ] || [ "$mount_path" == "none" ] || [ "$mount_arg1" == "sw" ]; then
                    local temp_fstype=""
                    temp_fstype=$(common::get_disk_part_fstype "$part_dev")
                    if [ "$temp_fstype" != "swap" ]; then
                        common::log "make swap: $part_dev"
                        mkswap "$part_dev" 2>$g_log_err_file
                    fi
                    is_swap_part=1
                fi

                mount_dev=$lv_dev
            fi

            local mount_fstype=""
            mount_fstype=$(common::get_disk_part_fstype "$part_dev")
            if [ -z "$mount_fstype" ]; then
                continue
            fi

            if [[ $need_boot_part -eq 1 ]] && [ "$part_dev" == "$boot_part" ]; then
                need_boot_part=0
            fi
            if [ "$part_dev" == "$root_part" ] || [ "$mount_path" == "/" ]; then
                need_root_part=0
            fi

            local new_fstab_line="$mount_dev    $mount_path    $mount_fstype    $mount_arg1    $mount_arg2 $mount_arg3"
            if [[ $is_swap_part -eq 1 ]]; then
                 if [[ $g_use_fstab_swap -ne 1 ]]; then
                    # disable swap part in fstab
                    new_fstab_line="#${new_fstab_line}"
                 fi
            else
                if [ "$mount_path" != "/" ] && [ "$mount_path" != "/boot" ] && [ "$mount_path" != "/boot/efi" ] && [ "$mount_path" != "$g_lib_path" ]; then
                    common::temp_mount "$part_dev" "${root_dir}/${mount_path}"
                fi
            fi
            common::log "add new_fstab_line: $new_fstab_line"
            valid_fstab_lines="${valid_fstab_lines}\n${new_fstab_line}"
        done
    fi

    if [[ $need_boot_part -eq 1 ]]; then
        common::log "need boot part..."
        # get boot fstab line
        local boot_fstab_line=""
        boot_fstab_line=$(common::gen_disk_part_fstab_line "$boot_part" /boot defaults 1 2)
        if [ -z "$boot_fstab_line" ]; then
            common::log "get boot fstab line" "Error"
            return $((CODE_ERROR_FSTAB_NO_BOOT))
        fi
        common::log "add boot_fstab_line: $boot_fstab_line"
        valid_fstab_lines="${valid_fstab_lines}\n${boot_fstab_line}"
    fi
    if [[ $need_root_part -eq 1 ]]; then
        common::log "need root part..."
        # get root fstab line
        local root_fstab_line=""
        root_fstab_line=$(common::gen_disk_part_fstab_line "$root_part" / defaults 1 1)
        if [ -z "$root_fstab_line" ]; then
            common::log "get root fstab line" "Error"
            return $((CODE_ERROR_FSTAB_NO_ROOT))
        fi
        common::log "add root_fstab_line: $root_fstab_line"
        valid_fstab_lines="${valid_fstab_lines}\n${root_fstab_line}"
    fi

    common::backup_file "$etc_fstab"

    # keep other fstab lines
    local temp_etc_fstab=/tmp/fstab
    \cp -f "$etc_fstab" "$temp_etc_fstab"
    local other_fstab_lines=""
    sed -i -r "/tmpfs|devpts|sysfs|debugfs|iso9660|proc/!d" "$temp_etc_fstab" 2>$g_log_err_file
    other_fstab_lines=$(cat "$temp_etc_fstab")

    # modify /etc/fstab
    common::log "write valid_fstab_lines: $valid_fstab_lines"
    echo -e "$valid_fstab_lines" > "$etc_fstab"
    common::log "write other_fstab_lines: $other_fstab_lines"
    echo -e "$other_fstab_lines" >> "$etc_fstab"
    return 0
}

# src_data_part_uuids format e.g.
# /dev/sda1:xxxx-xxxx-xxxx1;/dev/sdb1:xxxx-xxxx-xxxx2;
function tool::get_part_mount_uuid() {
    common::log_function "$*"
    local mount_dev=$1
    common::log_var_exit "mount_dev" "$mount_dev"
    local src_data_part_uuids=$2
    common::log_var_exit "src_data_part_uuids" "$src_data_part_uuids"
    local src_data_part_uuid=""
    src_data_part_uuid=$(echo "$src_data_part_uuids" | awk -F";" '{print $1}' | grep -E "${mount_dev}:")
    common::log "src_data_part_uuid: $src_data_part_uuid"
    local part_uuid=""
    part_uuid=$(echo "$src_data_part_uuid" | awk -F":" '{print $2}')
    common::log "part_uuid: $part_uuid"
    echo "$part_uuid"
}

function tool::fix_vmware_fstab() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local root_part=$2
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$3
    common::log_var "root_uuid" "$root_uuid"
    local boot_part=$4
    common::log_var "boot_part" "$boot_part"
    local src_data_part_uuids=$5
    common::log_var "src_data_part_uuids" "$src_data_part_uuids"

    # travel fstab lines, find root / mount point
    common::log "try fix /etc/fstab" "Info"
    local etc_fstab="$root_dir"/etc/fstab
    local valid_fstab_lines=""
    local has_root_mount=0
    while read -r fstab_line
    do
        if echo "$fstab_line" | grep -E "^$|^#"; then
            valid_fstab_lines="${valid_fstab_lines}\n${fstab_line}"
            continue
        fi
        if echo "$fstab_line" | grep -Ew "tmpfs|devpts|sysfs|debugfs|iso9660|proc"; then
            valid_fstab_lines="${valid_fstab_lines}\n${fstab_line}"
            continue
        fi

        common::log "check fstab_line: $fstab_line"
        local mount_dev=""
        local mount_path=""
        local mount_fstype=""
        local mount_arg1=""
        local mount_arg2=1
        local mount_arg3=1
        mount_dev=$(echo "$fstab_line" | awk '{print $1}')
        mount_path=$(echo "$fstab_line" | awk '{print $2}')
        mount_fstype=$(echo "$fstab_line" | awk '{print $3}')
        mount_arg1=$(echo "$fstab_line" | awk '{print $4}')
        mount_arg2=$(echo "$fstab_line" | awk '{print $5}')
        mount_arg3=$(echo "$fstab_line" | awk '{print $6}')
        common::log "split fstab_line: $mount_dev, $mount_path, $mount_fstype, $mount_arg1, $mount_arg2, $mount_arg3"
        if [ -z "$mount_dev" ] || [ -z "$mount_path" ] || [ -z "$mount_fstype" ]; then
        continue
        fi
        if [ -z "$mount_arg1" ]; then
            mount_arg1="defaults,nofail"
            common::log "set mount_arg1: $mount_arg1"
        elif ! echo "$mount_arg1" | grep -iw "nofail"; then
            mount_arg1="${mount_arg1},nofail"
            common::log "set mount_arg1: $mount_arg1"
        fi

        local is_root_part=0
        local is_boot_part=0
        local is_dev_sd_part=0
        local is_swap_part=0
        if [ "$mount_path" == "/" ]; then
            is_root_part=1
            has_root_mount=1
            mount_arg1="defaults"
            common::log "set root part mount_arg1: $mount_arg1"
        elif [ "$mount_path" == "/boot" ]; then
            is_boot_part=1
            mount_arg1="defaults"
            common::log "set boot part mount_arg1: $mount_arg1"
        else
            if echo "$mount_dev" | grep -E "^/dev/sd.[0-9]+"; then
                is_dev_sd_part=1
                mount_arg1="defaults,nofail"
                common::log "set nofail part mount_arg1: $mount_arg1"
            fi
            # check swap part
            if [ "$mount_path" == "swap" ] || [ "$mount_path" == "none" ] || [ "$mount_arg1" == "sw" ]; then
                is_swap_part=1
            fi
        fi

        if echo "$mount_dev" | grep -E "^UUID="; then
            if [[ $is_swap_part -eq 1 ]] && [[ $g_use_fstab_swap -ne 1 ]]; then
                fstab_line="#${fstab_line}"
                common::log "disable swap part fstab line: ${fstab_line}"
            fi
            common::log "copy fstab_line with UUID..."
            valid_fstab_lines="${valid_fstab_lines}\n${fstab_line}"
        else
            common::log "fix fstab_line without UUID..."
            local new_mount_dev=""
            # check lvm type/part uuid, get new mount dev
            if lvdisplay "$mount_dev" 2>$g_log_err_file; then
                new_mount_dev="$mount_dev"
                common::log "set lvm part mount_dev: $new_mount_dev"
            elif [[ $is_root_part -eq 1 ]] && [ -n "$root_uuid" ]; then
                new_mount_dev="UUID=${root_uuid}"
                common::log "set root part mount_dev: $new_mount_dev"
            else
                local need_get_new_mount_dev="true"
                if [[ $is_boot_part -eq 1 ]] && [ -n "$boot_part" ]; then
                    local boot_uuid=""
                    # check boot part lvm type/part uuid
                    if ! lvdisplay "$boot_part" 2>$g_log_err_file; then
                        boot_uuid=$(common::get_disk_part_uuid "$boot_part")
                        common::log_var "boot_uuid" "$boot_uuid"
                        if [ -n "$boot_uuid" ]; then
                            new_mount_dev="UUID=${boot_uuid}"
                            need_get_new_mount_dev="false"
                            common::log "set boot part mount_dev: $new_mount_dev"
                        fi
                    fi
                fi
                if [ "$need_get_new_mount_dev" == "true" ]; then
                    if [ -n "$src_data_part_uuids" ]; then
                        local part_uuid=""
                        part_uuid=$(tool::get_part_mount_uuid "$mount_dev" "$src_data_part_uuids")
                        common::log_var "part_uuid" "$part_uuid"
                        if [ -z "$part_uuid" ]; then
                            continue
                        fi
                        new_mount_dev="UUID=${part_uuid}"
                        common::log "set part mount_dev from src_data_part_uuids: $new_mount_dev"
                    elif [[ "$is_dev_sd_part" -eq 1 ]]; then
                        new_mount_dev=${mount_dev/\/dev\/sd/\/dev\/vd}
                        common::log "set part mount_dev from dev_sd_part: $new_mount_dev"
                    fi
                fi
            fi
            if [ -z "$new_mount_dev" ]; then
                new_mount_dev=$mount_dev
            fi

            if [ -z "$mount_arg2" ]; then
                if [[ $is_root_part -eq 1 ]]; then
                    mount_arg2=1
                else
                    mount_arg2=0
                fi
            fi
            if [ -z "$mount_arg3" ]; then
                if [[ $is_root_part -eq 1 ]]; then
                    mount_arg3=1
                else
                    mount_arg3=0
                fi
            fi
            local new_fstab_line="$new_mount_dev    $mount_path    $mount_fstype    $mount_arg1    $mount_arg2 $mount_arg3"
            if [[ $is_swap_part -eq 1 ]] && [[ $g_use_fstab_swap -ne 1 ]]; then
                # disable swap part in fstab
                new_fstab_line="#${new_fstab_line}"
                common::log "disable swap part new fstab line: ${new_fstab_line}"
            fi
            common::log "add new_fstab_line: $new_fstab_line"
            valid_fstab_lines="${valid_fstab_lines}\n${new_fstab_line}"
        fi
    done < "$etc_fstab"

    if [[ $has_root_mount -ne 1 ]]; then
        common::log "no root fstab" "Error"
        return $((CODE_ERROR_FSTAB_NO_ROOT))
    fi
 
    common::backup_file "$etc_fstab"
    # modify /etc/fstab
    common::log "write valid_fstab_lines: $valid_fstab_lines"
    echo -e "$valid_fstab_lines" > "$etc_fstab"
    return 0
}

function tool::fix_grub_env() {
    common::log_function "$*"
    local grub_env=$1
    common::log_var_exit "grub_env" "$grub_env"
    local root_uuid=$2
    common::log_var_exit "root_uuid" "$root_uuid"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local boot_part=$4
    # use the same fix for grub cfg
    tool::fix_grub_cfg "$grub_env" "$root_uuid" "$root_part" "$boot_part"
}

function tool::fix_boot_loader_entry() {
    common::log_function "$*"
    local boot_loader_entry=$1
    common::log_var_exit "boot_loader_entry" "$boot_loader_entry"
    if grep -q "options " "$boot_loader_entry" 2>$g_log_err_file &&
        ! grep -qE "options.*kernelopts" "$boot_loader_entry" 2>$g_log_err_file; then
        local root_uuid=$2
        common::log_var_exit "root_uuid" "$root_uuid"
        local root_part=$3
        common::log_var_exit "root_part" "$root_part"
        local boot_part=$4

        # use the same fix for grub cfg
        tool::fix_grub_cfg "$boot_loader_entry" "$root_uuid" "$root_part" "$boot_part"
    else
        common::log "no need to set boot_loader_entry kernelopts: $boot_loader_entry"
    fi
}

function tool::fix_default_grub() {
    common::log_function "$*"
    local boot_loader_entry=$1
    common::log_var_exit "boot_loader_entry" "$boot_loader_entry"
    local root_uuid=$2
    common::log_var_exit "root_uuid" "$root_uuid"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local boot_part=$4

    # fix default grub
    local default_grub="$root_dir"/etc/default/grub
    if [ -e "$default_grub" ]; then
        common::log "check default grub GRUB_CMDLINE_LINUX..."
        if ! grep "GRUB_CMDLINE_LINUX=" "$default_grub" >>"$g_log_file" 2>$g_log_err_file; then
            common::log "GRUB_CMDLINE_LINUX not exist, append..."
            echo "GRUB_CMDLINE_LINUX=\"\"" >> "$default_grub"
        fi
        if ! grep -q "GRUB_DISABLE_OS_PROBER=\"true\"" "$default_grub" 2>$g_log_err_file; then
            echo "GRUB_DISABLE_OS_PROBER=\"true\"" >> "$default_grub"
        fi
        # use the same fix for grub cfg
        tool::fix_grub_cfg "$default_grub" "$root_uuid" "$root_part" "$boot_part"
    fi
}

function tool::do_grub_mkconfig() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local root_uuid=$2
    common::log_var_exit "root_uuid" "$root_uuid"
    local efi_part=$3

    local ret=0
    local code=0
    # For GRUB 2 version
    common::log "try grub mkconfig for grub v2..." "Info"

    local grub_cfg=""
    local efi_grub_cfg=""
    if [ -n "$efi_part" ]; then
        efi_grub_cfg=$(find "$root_dir"/boot/efi -name "grub.cfg")
        common::log "get efi_grub_cfg: $efi_grub_cfg"
        if [ -n "$efi_grub_cfg" ]; then
            efi_grub_cfg=${efi_grub_cfg//$root_dir/}
            common::log "get relative efi_grub_cfg: $efi_grub_cfg"
        fi
    fi
    if [ "$g_architecture" == "arm64" ] && [ -n "$efi_grub_cfg" ]; then
        grub_cfg=$efi_grub_cfg
        common::log "found arm64 efi grub_cfg: $grub_cfg"
    elif [ "$g_grub_mkconfig" == "grub2-mkconfig" ]; then
        grub_cfg=/boot/grub2/grub.cfg
        common::log "set grub_cfg by grub2-mkconfig: $grub_cfg"
        test -d "$root_dir"/boot/grub2 || mkdir "$root_dir"/boot/grub2
    elif [ "$g_grub_mkconfig" == "grub-mkconfig" ]; then
        grub_cfg=/boot/grub/grub.cfg
        common::log "set grub_cfg by grub-mkconfig: $grub_cfg"
        test -d "$root_dir"/boot/grub || mkdir "$root_dir"/boot/grub
    elif [ -e "$root_dir"/boot/grub2/grub.cfg ]; then
        grub_cfg=/boot/grub2/grub.cfg
        common::log "found grub_cfg: $grub_cfg"
    elif [ -e "$root_dir"/boot/grub/grub.cfg ]; then
        grub_cfg=/boot/grub/grub.cfg
        common::log "found grub_cfg: $grub_cfg"
    elif [[ g_is_vmware -eq 0 ]]; then
        test -d "$root_dir"/boot/grub || mkdir "$root_dir"/boot/grub
        test -d "$root_dir"/boot/grub2 || mkdir "$root_dir"/boot/grub2
        grub_cfg=/boot/grub/grub.cfg
        common::log "found and set need copy grub_cfg: $grub_cfg"
    elif [ -n "$efi_grub_cfg" ]; then
        grub_cfg=$efi_grub_cfg
        common::log "found efi grub_cfg: $grub_cfg"
    fi

    if [ -z "$grub_cfg" ]; then
        common::log "no grub cfg" "Error"
        return $((CODE_ERROR_GRUB_CFG_NOT_FOUND))
    fi

    if [ -n "$g_grub_mkconfig" ] && [ "$g_grub_mkconfig" != "-" ] && [[ g_is_vmware -eq 0 ]]; then
        common::backup_file "$root_dir"/"$grub_cfg"
        # chroot and do grub mkconfig
        local chroot_mkconfig_cmd="export PATH=/sbin:/usr/sbin:$PATH; $g_grub_mkconfig -o $grub_cfg"
        common::log "chroot and exec grub mkconfig cmd: $chroot_mkconfig_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        local disable_firmware_efi_dir=0
        local temp_empty_dir=""
        if [ "$g_boot_mode" != "$BOOT_MODE_UEFI" ] && [ -d /sys/firmware/efi ]; then
            #if target boot mode is not UEFI and this is a UEFI p2vs OS env, we need to
            #disable /sys/firmware/efi for grub2-mkconfig to generate a non-UEFI grub.cfg
            temp_empty_dir=$(common::mk_temp_dir "empty_sys_firmware")
            common::log "mount --bind $temp_empty_dir $root_dir/sys/firmware to disable /sys/firmware/efi"
            mount --bind $temp_empty_dir $root_dir/sys/firmware && disable_firmware_efi_dir=1
        fi
        chroot "$root_dir" /bin/sh -c "$chroot_mkconfig_cmd" >>"$g_log_file" 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "grub v2 mkconfig return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            ret=$CODE_ERROR_GRUB_V2_MKCONFIG
        else
            cat $g_log_err_file
            # copy to /boot/grub2/grub.cfg
            if [ ! -e "$root_dir/boot/grub2/grub.cfg" ]; then
                common::log "copy $grub_cfg to /boot/grub2/grub.cfg"
                \cp -f "$root_dir"/"$grub_cfg" "$root_dir"/boot/grub2/grub.cfg
            fi
        fi
        if [[ $disable_firmware_efi_dir -eq 1 ]]; then
            umount $root_dir/sys/firmware && rmdir $temp_empty_dir
        fi
        umount "$root_dir"/{dev,proc,sys}
    else
        common::log "grub mkconfig skip"
    fi

    if [[ $ret -ne 0 ]]; then
        common::log "do grub mkconfig failed." "Error"
        return $((ret))
    else
        if [ -e "$root_dir"/boot/grub2/grub.cfg ] && [ ! -e "$root_dir"/boot/grub/grub.cfg ]; then
            common::log "copy grub2 cfg to grub..."
            \cp -f "$root_dir"/boot/grub2/grub.cfg "$root_dir"/boot/grub/grub.cfg
        elif [ -e "$root_dir"/boot/grub/grub.cfg ] && [ ! -e "$root_dir"/boot/grub2/grub.cfg ]; then
            common::log "copy grub cfg to grub2..."
            \cp -f "$root_dir"/boot/grub/grub.cfg "$root_dir"/boot/grub2/grub.cfg
        fi
        common::log "do grub mkconfig done." "Info"
        return 0
    fi
}

function tool::do_grub_install() {
    common::log_function "$*"
    local root_disk_dev=$1
    local root_dir=$2
    common::log_var_exit "root_dir" "$root_dir"
    local is_grub_legacy=$3
    common::log_var_exit "is_grub_legacy" "$is_grub_legacy"
    local efi_part=$4

    local ret=0
    local code=0
    # For GRUB 2 version
    if [[ $is_grub_legacy == 0 ]]; then
        common::log "do grub install for GRUB 2 version..." "Info"
        # For GRUB 2 version 1.98 and earlier (Ubuntu 10.04), the installation command is slightly different.
        # Rather than designating the --boot-directory, the switch is --root-directory.
        grub_cmd=""
        local grub_options="--no-floppy"
        if parted "$root_disk_dev" -s print 2>$g_log_err_file | grep -w "gpt"; then
            grub_options="$grub_options --modules=part_gpt"
            common::log "set gpt grub_options: $grub_options"
        else
            grub_options="$grub_options --modules=part_msdos"
            common::log "set msdos grub_options: $grub_options"
        fi
        local grub_target_option=""
        if [ "$g_boot_mode" == "$BOOT_MODE_UEFI" ]; then
            if [ -n "$efi_part" ]; then
                grub_options="$grub_options --efi-directory=/boot/efi"
                common::log "set efi directory grub_options: $grub_options"
            fi
            if [ "$g_architecture" == "arm64" ]; then
                grub_target_option="--target=arm64-efi"
            else
                grub_target_option="--target=x86_64-efi"
            fi
        else
            if [ "$g_grub_flag" == "$GRUB_FLAG_1XX" ]; then
                grub_options="$grub_options --root-directory=/"
                common::log "set root directory grub grub_options: $grub_options"
            else
                grub_options="$grub_options --boot-directory=/boot"
                common::log "set boot directory grub_options: $grub_options"
            fi
            grub_target_option="--target=i386-pc"
        fi
        local test_grub_target_cmd="$g_grub_install --version; $g_grub_install --help | grep '\-\-target'"
        local chroot_test_grub_target_cmd="export PATH=/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:$PATH; $test_grub_target_cmd"
        common::log "chroot and exec test grub target cmd: $chroot_test_grub_target_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        if ! chroot "$root_dir" /bin/sh -c "$chroot_test_grub_target_cmd" 2>$g_log_err_file; then
            #not support for --target option
            common::log "set grub target option empty"
            grub_target_option=""
        fi
        umount "$root_dir"/{dev,proc,sys}

        grub_cmd="$g_grub_install $grub_options $grub_target_option $root_disk_dev"
        local chroot_grub_cmd="export PATH=/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:$PATH; $grub_cmd"
        common::log "chroot and exec grub v2 install cmd: $chroot_grub_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        chroot "$root_dir" /bin/sh -c "$chroot_grub_cmd" >>"$g_log_file" 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "grub v2 install return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            if grep -qi "not support UEFI Secure Boot" $g_log_err_file 2>/dev/null; then
                ret=$CODE_ERROR_GRUB_V2_UEFI_SECURE_BOOT
            else
                ret=$CODE_ERROR_GRUB_V2_INSTALL
            fi
        else
            cat $g_log_err_file
        fi
        umount "$root_dir"/{dev,proc,sys}
    else
        local boot_part_num=0
        if [ -n "$efi_part" ]; then
            # if efi part is the the 1st part which number is 0, boot part should be the 2nd part, which number is 1
            local efi_part_num=""
            efi_part_num=${efi_part: -1}
            if [[ $efi_part_num -eq 1 ]]; then
                boot_part_num=1
                common::log "set boot_part_num=1"
            fi
        fi
        echo "(hd0) $root_disk_dev" > "$root_dir"/tmp/device.map
        cat >> "$root_dir"/tmp/grub_batch.txt <<EOF
root (hd0,$boot_part_num)
setup (hd0)
EOF
        local chroot_grub_cmd="export PATH=/sbin:/usr/sbin:$PATH; $g_grub_install --device-map=/tmp/device.map --batch < /tmp/grub_batch.txt";
        common::log "chroot and exec grub legacy install cmd: $chroot_grub_cmd"
        for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
        chroot "$root_dir" /bin/sh -c "$chroot_grub_cmd" >$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "grub legacy install return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            ret=$CODE_ERROR_GRUB_LEGACY_INSTALL
        else
            cat $g_log_err_file
            local error_msg=""
            error_msg=$(tail -2 $g_log_err_file | head -1 | grep Error)
            if [ -n "$error_msg" ]; then
                common::log "grub legacy install msg found error, error_msg: $error_msg" "Error"
                ret=$CODE_ERROR_GRUB_LEGACY_INSTALL
            fi
        fi
        umount "$root_dir"/{dev,proc,sys}

        unlink "$root_dir"/tmp/device.map
        unlink "$root_dir"/tmp/grub_batch.txt
    fi

    if [[ $ret -eq 0 ]]; then
        common::log "do grub install successfully!" "Done"
        return 0
    else
        common::log "do grub install failed." "Error"
        return $((ret))
    fi
}

function tool::fix_grub_legacy(){
    local root_disk_dev=$1
    common::log_var_exit "root_disk_dev" "$root_disk_dev"
    local root_dir=$2
    common::log_var_exit "root_dir" "$root_dir"
    local boot_part=$3
    common::log_var_exit "boot_part" "$boot_part"
    local efi_part=$4

    # check efi
    if [ -n "$efi_part" ]; then
        common::log "no need to fix grub legacy on efi"
        return 0
    fi
    # check support stage1_5
    starts_address=$(dd if="$root_disk_dev" bs=1 count=2 skip=66 2>>$g_log_file | od -An -tx2)
    common::log "get starts_address: $starts_address"
    if [[ $starts_address -eq "2000" ]]; then
        common::log "no need to fix grub legacy with stage1_5"
        return 0
    fi
    if [[ $starts_address -ne "8000" ]]; then
        common::log "STAGE1_STAGE2_ADDRESS $starts_address is invalid." "Error"
        return 1
    fi
    # check stage2
    stage2_file=$(find "$root_dir"/boot -name "stage2")
    common::log "get stage2_file from /boot: $stage2_file"
    if [ -z "$stage2_file" ]; then
        common::log "Stage2 file is not found: $stage2_file"
        return 1
    fi

    start_sector=$(dd if="$root_disk_dev" bs=1 count=4 skip=68 2>>$g_log_file | od -An -tx4)
    part_start_sector=$(parted "$root_disk_dev" -s Unit S print | grep -E "[0-9]+s" |
        grep -E "boot" | tail -1 | awk '{printf $2}' | grep -oE "[0-9]+")
    # get stage2 blocklists
    part_fstype=$(common::get_disk_part_fstype "$boot_part")
    boot_dir=$(common::get_disk_part_mount_path "$boot_part")
    relative_path=${stage2_file#$boot_dir}
    common::log "get relative_path: $relative_path"
    if [ "$part_fstype" == "ext2" ] || [ "$part_fstype" == "ext3" ] || [ "$part_fstype" == "ext4" ]; then
        blocksize=$(debugfs -R "stats" "$boot_part" 2>/dev/null |
            grep "Block size:" | awk '{print $3}')
        blocklists=$(debugfs -R "stat $relative_path" "$boot_part" 2>/dev/null |
            grep -E '([0-9]+-[0-9]+)' |
            awk -F', *' '{for (i=1; i<=NF; i+=1) {if (index($i,"IND")==0) print $i}}' |
            awk -F': *' '{print $2}')
    elif [ "$part_fstype" == "xfs" ]; then
        blocksize=$(xfs_db -r "$boot_part" -c "sb" -c "p" 2>/dev/null |
            grep "blocksize" | awk '{print $3}')
        inode_number=$(stat -c %i "$stage2_file")
        blocklists=$(xfs_db -r "$boot_part" -c "inode $inode_number" -c "p" 2>/dev/null |
            grep -oE '[0-9]+:\[[0-9,]+\]$' |
            awk -F ',' '{last_block = $2 + $3 - 1; print $2 "-" last_block}')
    else
        common::log "The fstype $part_fstype is unsupport" "Error"
        return 1
    fi
    common::log "get stage2 blocklists: $blocklists"
    IFS=$'\n' read -d '' -ra blocklist_array <<< "$blocklists"
    if [[ ${#blocklist_array[@]} -lt 1 ]]; then
        common::log "get stage2 blocklists failed." "Error"
        return 1
    else
        common::log "get stage2 blocklists success, list_count is ${#blocklist_array[@]}." "Info"
    fi
    # fix stage1 and starts in stage2
    list_num=0
    firstlist=512
    listsize=8
    seg=2080
    sector_size=512
    sector_per_block=$((blocksize / sector_size))
    for blocklist in "${blocklist_array[@]}"; do
        common::log "list_num $list_num blocklist is $blocklist." "Info"
        IFS='-' read -r first_block last_block <<< "$blocklist"
        first_sector=$((first_block * sector_per_block + part_start_sector))
        sector_num=$(((last_block - first_block + 1) * sector_per_block))
        hex_first_sector_str=$(common::to_little_endian_hex $first_sector 8)
        hex_sector_num_str=$(common::to_little_endian_hex $sector_num 4)
        hex_seg_str=$(common::to_little_endian_hex $seg 4)
        # fix stage1
        if [ "$list_num" -eq 0 ] && [ "$hex_first_sector_str" != "$start_sector" ]; then
            common::log "fix grub legacy stage1" "Info"
            echo -n "$hex_first_sector_str" | xxd -r -p > hex_first_sector_str.bin
            dd if="hex_first_sector_str.bin" of="$root_disk_dev" bs=1 seek=68 count=4 conv=notrunc 2>>$g_log_file
            first_sector=$((first_sector + 1))
            sector_num=$((sector_num - 1))
            hex_first_sector_str=$(common::to_little_endian_hex $first_sector 8)
            hex_sector_num_str=$(common::to_little_endian_hex $sector_num 4)
        fi

        hex_combined="${hex_first_sector_str}${hex_sector_num_str}${hex_seg_str}"
        common::log "get hex_combined: $hex_combined"
        # fix starts in stage2
        ori_blocklist=$(dd if="$stage2_file" bs=1 count=8 skip=$((firstlist - listsize)) 2>>$g_log_file | od -An -L -tx8)
        if [ "$ori_blocklist" != "$hex_combined" ]; then
            common::log "fix grub legacy starts in stage2" "Info"
            echo -n "$hex_combined" | xxd -r -p > hex_combined$list_num.bin
            dd if="hex_combined$list_num.bin" of="$stage2_file" bs=1 seek=$((firstlist - listsize)) count=8 conv=notrunc 2>>$g_log_file
        fi
        list_num=$((list_num + 1))
        firstlist=$((firstlist - listsize))
        seg=$((seg + sector_num * 32))
    done
    # check next sector num in blocklist
    next_sector_num=$(dd if="$stage2_file" bs=1 count=4 skip=$((firstlist - listsize + 4)) 2>>$g_log_file | od -An -tx4)
    if [[ $next_sector_num  -ne 0 ]]; then
        common::log "fix grub legacy useless blocklist" "Info"
        dd if=/dev/zero of="$stage2_file" bs=1 seek=$((firstlist - listsize + 4)) count=4 conv=notrunc 2>>$g_log_file
    fi
    # fix partition
    dd if=/dev/zero of="$stage2_file" bs=1 seek=522 count=1 conv=notrunc 2>>$g_log_file

    return 0
}

function tool::fix_os_confs() {
    common::log_function "$*"
    local root_disk_num=$1
    common::log_var_exit "root_disk_num" "$root_disk_num"
    local root_dir=$2
    common::log_var_exit "root_dir" "$root_dir"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$4
    common::log_var_exit "root_uuid" "$root_uuid"
    local boot_part=$5
    common::log_var_exit "boot_part" "$boot_part"

    local ret=0
    # fix selinux config
    local selinux_config="$root_dir"/etc/selinux/config
    if [ -f "$selinux_config" ]; then
        common::log "try fix selinux config" "Info"
        common::backup_file "$selinux_config"
        # modify selinux config
        sed -i -e "/^SELINUX=/cSELINUX=disabled" "$selinux_config"
    fi

    # try fix network
    tool::fix_network "$root_dir"

    # try fix cloud init config
    tool::fix_cloud_init "$root_dir"

    # fix Ubuntu/Debian blacklist
    if echo "$g_os_name" | grep -E 'Ubuntu|Debian' ||
        which apt-get >/dev/null; then
        local black_list="$root_dir"/etc/modprobe.d/blacklist-xen.conf
        common::log "try fix Ubuntu/Debian blacklist: $black_list" "Info"
        if [ ! -e "$black_list" ]; then
            common::log "create blacklist: $black_list"
            touch "$black_list"
        else
            common::backup_file "$black_list"
        fi
        _blacklist_xen=$(cat "$black_list")
        common::log "fix blacklist xen"
        if [ -n "$_blacklist_xen" ]; then
            sed -i '/blacklist xen_fbfront/d' "$black_list"
            sed -i 'a\blacklist xen_fbfront' "$black_list"
        else
            echo 'blacklist xen_fbfront' >> "$black_list"
        fi
    fi

    # fix upstart confs
    tool::fix_upstart_confs "$root_dir"

    # fix systemd confs
    tool::fix_systemd_confs "$root_dir"

    # fix /etc/fstab
    if [[ g_is_vmware -eq 0 ]]; then
        tool::fix_fstab "$root_disk_num" "$root_dir" "$root_part" "$root_uuid" "$boot_part"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            return $((ret))
        fi
    else
        tool::fix_vmware_fstab "$root_dir" "$root_part" "$root_uuid" "$boot_part"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            return $((ret))
        fi
    fi
    return 0
}

function tool::fix_os_grubs() {
    common::log_function "$*"
    local root_disk_dev=$1
    common::log_var_exit "root_disk_dev" "$root_disk_dev"
    local root_dir=$2
    common::log_var_exit "root_dir" "$root_dir"
    local root_part=$3
    common::log_var_exit "root_part" "$root_part"
    local root_uuid=$4
    common::log_var_exit "root_uuid" "$root_uuid"
    local boot_part=$5
    common::log_var_exit "boot_part" "$boot_part"
    local efi_part=$6

    local root_uuid=""
    # check root part lvm type/part uuid
    root_uuid=$(common::get_disk_part_uuid "$root_part")
    common::log_var_exit "root_uuid" "$root_uuid"

    # check is_grub_legacy
    common::log "check is grub legacy..." "Info"
    local is_grub_legacy=1
    local boot_stage1=""
    boot_stage1=$(find "$root_dir"/boot/grub -name "stage1")
    if [ -z "$boot_stage1" ]; then
        common::log "no boot_stage1 found from /boot/grub"
        local usr_share_stage1=""
        usr_share_stage1=$(find "$root_dir"/usr/share/grub -name "stage1")
        if [ -n "$usr_share_stage1" ]; then
            common::log "get boot_stage1 from /usr/share/grub: $usr_share_stage1"
            local usr_share_grub_dir=${usr_share_stage1%/*}
            common::log "copy stage1 and files from $usr_share_grub_dir" "Info"
            mkdir -p "$root_dir"/boot/grub
            cp -rf "$usr_share_grub_dir"/* "$root_dir"/boot/grub/
            # sync to disk
            sync && sync
            boot_stage1=$(find "$root_dir"/boot/grub -name "stage1")
        else
            common::log "no boot_stage1 found from /usr/share/grub"
        fi
    else
        common::log "get boot_stage1 from /boot/grub: $boot_stage1"
    fi
    local boot_stage2=""
    boot_stage2=$(find "$root_dir"/boot/grub -name "stage2")
    if [ -z "$boot_stage2" ]; then
        common::log "no boot_stage2 found from /boot/grub"
        local usr_share_stage2=""
        usr_share_stage2=$(find "$root_dir"/usr/share/grub -name "stage2")
        if [ -n "$usr_share_stage2" ]; then
            common::log "get boot_stage2 from /usr/share/grub: $usr_share_stage2"
            local usr_share_grub_dir=${usr_share_stage2%/*}
            common::log "copy stage2 and files from $usr_share_grub_dir" "Info"
            mkdir -p "$root_dir"/boot/grub
            cp -rf "$usr_share_grub_dir"/* "$root_dir"/boot/grub/
            # sync to disk
            sync && sync
        else
            common::log "no boot_stage2 found from /usr/share/grub"
        fi
    else
        common::log "get boot_stage2 from /boot/grub: $boot_stage2"
    fi

    local grub_conf_menu_lst=""
    grub_conf_menu_lst=$(find "$root_dir"/boot -name "grub.conf")
    common::log "get grub.conf: $grub_conf_menu_lst"
    if [ -z "$grub_conf_menu_lst" ]; then
        grub_conf_menu_lst=$(find "$root_dir"/boot -name "menu.lst")
        common::log "get menu.lst: $grub_conf_menu_lst"
    fi

    if [ -z "$boot_stage1" ] || [ -z "$grub_conf_menu_lst" ] ||
        [ "$g_grub_flag" != "$GRUB_FLAG_0XX" ]; then
        is_grub_legacy=0
        common::log "check is_grub_legacy=0" "Info"

        # check and get grub.cfg
        local grub_cfg=""
        if [ -n "$efi_part" ]; then
            grub_cfg=$(find "$root_dir"/boot/efi -name "grub.cfg")
            common::log "get efi grub_cfg: $grub_cfg"
        elif [ "$g_grub_flag" == "$GRUB_FLAG_2XX" ] && [ -e "$root_dir"/boot/grub2/grub.cfg ]; then
            grub_cfg=/boot/grub2/grub.cfg
            common::log "found grub_cfg: $grub_cfg"
        elif [ -e "$root_dir"/boot/grub/grub.cfg ]; then
            grub_cfg=/boot/grub/grub.cfg
            common::log "found grub_cfg: $grub_cfg"
        else
            test -d "$root_dir"/boot/grub || mkdir "$root_dir"/boot/grub
            grub_cfg=/boot/grub/grub.cfg
            common::log "not found and set grub_cfg: $grub_cfg"
            # in case the default grub cfg path is /boot/grub2/grub.cfg
            test -d "$root_dir"/boot/grub2 || mkdir "$root_dir"/boot/grub2
            cp "$root_dir"/"$grub_cfg" "$root_dir"/boot/grub2/grub.cfg
        fi

        # remove unnecessary grub cfg files
        # aws ec2 instance might have 40-force-partuuid.cfg, need to remove
        local force_partuuid_file="$root_dir"/etc/default/grub.d/40-force-partuuid.cfg
        if test -f "$force_partuuid_file"; then
            common::log "remove unnecessary grub cfg file: $force_partuuid_file"
            rm -f "$force_partuuid_file"
        fi

    else
        is_grub_legacy=1
        common::log "check is_grub_legacy=1" "Info"

        # check and get grub.conf
        local grub_conf="$root_dir"/boot/grub/grub.conf
        local menu_lst="$root_dir"/boot/grub/menu.lst
        local grub_dir="$root_dir"/boot/grub
        mkdir -p "$grub_dir"
        local last_cur_dir=$PWD
        if ! cd "$grub_dir"; then
            common::log "cd $root_dir" "Error"
            return 1
        fi
        # check and try make link /boot/grub/menu.lst to /boot/grub/grub.conf
        if [ ! -f "$grub_conf" ] && [ -f "$menu_lst" ]; then
            ln -s ./menu.lst ./grub.conf
            grub_conf="$root_dir"/boot/grub/menu.lst
            common::log "found grub_conf(menu_lst): $grub_conf" "Info"
        elif [ -f "$grub_conf" ] && [ ! -f "$menu_lst" ]; then
            ln -s ./grub.conf ./menu.lst
            grub_conf="$root_dir"/boot/grub/grub.conf
            common::log "found grub_conf: $grub_conf" "Info"
        elif [ -f "$grub_conf" ] && [ -f "$menu_lst" ]; then
            common::log "found both grub_conf $grub_conf and menu_lst $menu_lst" "Info"
            local grub_conf_temp=""
            grub_conf_temp=$(readlink -f "$grub_conf")
            local menu_lst_temp=""
            menu_lst_temp=$(readlink -f "$menu_lst")
            common::log "check grub_conf link $grub_conf_temp and menu_lst link $menu_lst_temp" "Info"
            if [ "$grub_conf_temp" != "$menu_lst" ] && [ "$menu_lst_temp" != "$grub_conf" ]; then
                rm -f ./menu.lst
                ln -s ./grub.conf ./menu.lst
                grub_conf="$root_dir"/boot/grub/grub.conf
                common::log "ln -s ./grub.conf ./menu.lst..." "Info"
            elif [ "$grub_conf_temp" == "$menu_lst" ]; then
                grub_conf="$root_dir"/boot/grub/menu.lst
            elif [ "$menu_lst_temp" == "$grub_conf" ]; then
                grub_conf="$root_dir"/boot/grub/grub.conf
            fi
            common::log "let grub_conf=$grub_conf" "Info"
        elif [ ! -f "$grub_conf" ] && [ ! -f "$menu_lst" ] && [ -d "$root_dir"/boot/efi/EFI ]; then
            # check /boot/efi/EFI may have grub.conf
            local temp_grub_conf=""
            temp_grub_conf=$(find "$root_dir"/boot/efi/EFI -name "grub.conf")
            if [ -n "$temp_grub_conf" ]; then
                common::log "find grub_conf in EFI: $temp_grub_conf" "Info"
                if [ -z "$g_src_part_num" ]; then
                    common::log "copy grub_conf from $temp_grub_conf" "Info"
                    rm -f ./grub.conf
                    cp -f "$temp_grub_conf" ./grub.conf
                    rm -f ./menu.lst
                    ln -s ./grub.conf ./menu.lst
                else
                    common::log "sync disk header and no need copy grub_conf from $temp_grub_conf" "Info"
                fi
            else
                common::log "no grub.conf found from /boot/efi/EFI"
            fi
        fi
        cd "$last_cur_dir"
        code=$?
        if [[ $code -ne 0 ]] ; then
            common::log "cd $last_cur_dir" "Error"
            return $((code))
        fi
    fi

    local ret=0
    # check grubenv for grub2
    if [[ $is_grub_legacy != 1 ]]; then
        local grubenv_path="$root_dir"/boot/grub2/grubenv
        if ls -l "$grubenv_path" 2>$g_log_err_file; then
            local grubenv_link_path=""
            grubenv_link_path=$(readlink "$grubenv_path")
            if [ -n "$grubenv_link_path" ]; then
                common::log "found grubenv link to $grubenv_link_path, delete grubenv link $grubenv_path"
                rm -f "$grubenv_path"
            fi
        fi
    fi

    # try fix grub legacy for sync disk header or sync disk grub
    if [ -n "$g_src_part_num" ] || [[ g_sync_grub -eq 1 ]]; then
        if [[ $is_grub_legacy -eq 1 ]]; then
            tool::fix_grub_legacy "$root_disk_dev" "$root_dir" "$boot_part" "$efi_part"
            ret=$?
            if [[ $ret -ne 0 ]]; then
                common::log "fix grub legacy failed" "Error"
                ret=$CODE_ERROR_GRUB_FIX_LEGACY
                return $((ret))
            else
                common::log "fix grub legacy successfully!" "Done"
            fi
        fi
    fi

    local fix_default_grub=0
    local grub_install_done=0
    # do grub install and grub mkconfig when it doesn't sync disk header
    if [ -z "$g_src_part_num" ] && [[ g_sync_grub -eq 0 ]]; then
        # do grub install
        if [[ g_is_vmware -eq 0 ]]; then
            tool::do_grub_install "$root_disk_dev" "$root_dir" "$is_grub_legacy" "$efi_part"
            ret=$?
            if [[ $ret -ne 0 ]]; then
                return $((ret))
            fi
            grub_install_done=1
        fi

        # do grub mkconfig
        if [[ $is_grub_legacy != 1 ]]; then
            tool::fix_default_grub "$root_dir" "$root_uuid" "$root_part" "$boot_part"
            fix_default_grub=1
            if [ "$g_architecture" == "arm64" ]; then
                common::log "try do grub mkconfig for arm64..." "Info"
                tool::do_grub_mkconfig "$root_dir" "$root_uuid" "$efi_part"
                ret=$?
                if [[ $ret -ne 0 ]] ; then
                    return $((ret))
                fi
            else
                if [ -n "$g_grub_mkconfig" ] && [ "$g_grub_mkconfig" != "-" ]; then
                    common::log "try do grub mkconfig..." "Info"
                    tool::do_grub_mkconfig "$root_dir" "$root_uuid"
                    ret=$?
                    if [[ $ret -ne 0 ]] ; then
                        return $((ret))
                    fi
                fi
            fi
        fi
    fi

    # do fix grub confs/cfg
    if [[ $is_grub_legacy == 1 ]]; then
        common::log "try fix grub legacy confs" "Info"
        # try fix grub confs
        # make link /boot/grub/menu.lst to /boot/grub/grub.conf
        local boot_is_root_part=0
        if [ "$root_part" == "$boot_part" ]; then
            boot_is_root_part=1
            common::log "set boot_is_root_part=1"
        fi
        if [[ g_is_vmware -eq 0 ]]; then
            tool::fix_grub_confs "$root_dir" "$root_uuid" "$boot_is_root_part" "$efi_part"
            ret=$?
            if [[ $ret -ne 0 ]]; then
                ret=$CODE_ERROR_GRUB_FIX_CONFS
                return $((ret))
            fi
        else
            tool::fix_vmware_grub_confs "$root_dir" "$root_uuid" "$boot_is_root_part" "$efi_part"
        fi
    else
        common::log "try fix grub cfgs" "Info"
        if [[ $fix_default_grub -eq 0 ]]; then
            tool::fix_default_grub "$root_dir" "$root_uuid" "$root_part" "$boot_part"
            fix_default_grub=1
        fi
        # try fix grub cfgs
        if [ -n "$efi_part" ]; then
            common::log "try fix grub cfgs in efi dir..." "Info"
            local grub_cfgs=""
            grub_cfgs=$(find "$root_dir"/boot/efi/EFI -name "grub.cfg" 2>>$g_log_file)
            if [ -n "$grub_cfgs" ]; then
                common::log "found efi grub cfgs: $grub_cfgs" "Info"
                for grub_cfg in $grub_cfgs; do
                    common::log "fix efi grub cfg: $grub_cfg"
                    if [ -e "$grub_cfg" ]; then
                        tool::fix_grub_cfg "$grub_cfg" "$root_uuid" "$root_part" "$boot_part"
                        # add grub nomodeset for efi
                        tool::config_efigrub_cfg_param "$grub_cfg" "nomodeset" "add"
                    fi
                done
            else
                common::log "no efi grub cfg found"
            fi

            common::log "try fix grub envs in efi dir..." "Info"
            local grub_envs=""
            grub_envs=$(find "$root_dir"/boot/efi/EFI -name "grubenv" 2>>$g_log_file)
            if [ -n "$grub_envs" ]; then
                common::log "found efi grub envs: $grub_envs" "Info"
                for grub_env in $grub_envs; do
                    if [ -e "$grub_env" ]; then
                        common::log "fix efi grub env: $grub_env"
                        tool::fix_grub_env "$grub_env" "$root_uuid" "$root_part" "$boot_part"
                    fi
                done
            else
                common::log "no efi grub env found"
            fi
        fi

        local grub_cfg="$root_dir"/boot/grub/grub.cfg
        if [ -e "$grub_cfg" ]; then
            common::log "fix grub cfg: $grub_cfg"
            tool::fix_grub_cfg "$grub_cfg" "$root_uuid" "$root_part" "$boot_part"
        fi
        local grub_env="$root_dir"/boot/grub/grubenv
        if [ -e "$grub_env" ]; then
            common::log "fix grub env: $grub_env"
            tool::fix_grub_env "$grub_env" "$root_uuid" "$root_part" "$boot_part"
        fi

        local grub2_cfg="$root_dir"/boot/grub2/grub.cfg
        if [ -e "$grub2_cfg" ]; then
            common::log "fix grub2 cfg: $grub2_cfg"
            tool::fix_grub_cfg "$grub2_cfg" "$root_uuid" "$root_part" "$boot_part"
        fi
        local grub2_env="$root_dir"/boot/grub2/grubenv
        if [ -e "$grub2_env" ]; then
            common::log "fix grub2 env: $grub2_env"
            tool::fix_grub_env "$grub2_env" "$root_uuid" "$root_part" "$boot_part"
        fi

        #fix boot loader entries
        local boot_entries_dir="$root_dir"/boot/loader/entries
        if [ -d "$boot_entries_dir" ]; then
            local boot_entries=""
            boot_entries=$(ls "$boot_entries_dir"/*.conf)
            common::log "get /boot/loader/entries boot_entries: $boot_entries" "Info"
            for boot_entry in $boot_entries; do
                common::log "fix boot_entry: $boot_entry" "Info"
                tool::fix_boot_loader_entry "$boot_entry" "$root_uuid" "$root_part" "$boot_part"
            done
        fi

        # fix efi boot file
        if [ -n "$efi_part" ] && [[ $grub_install_done -eq 0 ]]; then
            tool::fix_efi_boot_file "$root_dir"
        fi
    fi

    return 0
}

function tool::try_run_pre_script() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local pre_script="$g_cur_dir"/smc_pre.sh
    # find vmware pre script
    local vmware_pre_script=$(find $g_cur_dir -type f -regex '.*/smc_pre_[0-9]+\.sh' | head -1 2>/dev/null)
    if [ -n "$vmware_pre_script" ]; then
        common::log "find vmware pre srcipt: $vmware_pre_script"
        pre_script="$vmware_pre_script"
    fi

    if [ ! -f "$pre_script" ]; then
        common::log "pre script not exist, skip"
        return 0
    fi

    common::log "pre script found, read to run..."
    local tmp_dir="$root_dir"/var/tmp
    mkdir -p "$tmp_dir"
    local pre_script_dest_path="$tmp_dir"/smc_pre.sh
    \cp -pf $pre_script $pre_script_dest_path
    chmod 777 "$pre_script_dest_path"

    local log_dir="$root_dir"/var/log
    mkdir -p $log_dir
    local local_pre_log=/var/log/smc_pre.log
    local chroot_pre_cmd="export PATH=/sbin:/usr/sbin:$PATH; /var/tmp/smc_pre.sh >$local_pre_log 2>&1"
    local ret=0
    common::log "chroot and exec pre run cmd: $chroot_pre_cmd"
    for i in dev proc sys; do mount --bind /$i "$root_dir"/$i; done
    chroot "$root_dir" /bin/sh -c "$chroot_pre_cmd"
    ret=$?
    if [[ $ret -eq 0 ]]; then
        sleep 2s
    fi
    common::log "pre run cmd return code: $ret"
    umount "$root_dir"/{dev,proc,sys}
    return $((ret))
}

function tool::try_install_post_script() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local post_script="$g_cur_dir"/smc_post.sh
    # find vmware post script
    local vmware_post_script=$(find $g_cur_dir -type f -regex '.*/smc_post_[0-9]+\.sh' | head -1 2>/dev/null)
    if [ -n "$vmware_post_script" ]; then
        common::log "find vmware post srcipt: $vmware_post_script"
        post_script="$vmware_post_script"
    fi

    if [ ! -f "$post_script" ]; then
        common::log "post script not exist, skip"
        return 0
    fi

    common::log "post script found, ready to install..."
    local etc_dir="$root_dir"/etc
    local rc_local_script="$etc_dir"/rc.d/rc.local
    if ! test -f "$rc_local_script"; then
        rc_local_script="$etc_dir"/rc.local
        if ! test -f "$rc_local_script"; then
            mkdir -p "$etc_dir"
            echo '#!/bin/bash' > "$rc_local_script"
            echo "echo THIS_FILE_CREATED_BY_SMC_POST" >> "$rc_local_script"
            if test -f "$rc_local_script"; then
                common::log "install post script /etc/rc.d/rc.local not exist, created"
            else
                common::log "install post script error: rc.local not exist" "Warn"
                return 0
            fi
        fi
    fi
    chmod 777 "$rc_local_script"

    local tmp_dir="$root_dir"/var/tmp
    mkdir -p "$tmp_dir"
    local post_script_dest_path="$tmp_dir"/smc_post.sh
    common::log "copy $post_script to $post_script_dest_path"
    \cp -pf $post_script $post_script_dest_path
    chmod 777 "$post_script_dest_path"

    local log_dir="$root_dir"/var/log
    mkdir -p $log_dir
    local local_post_log=/var/log/smc_post.log
    local rc_local_post_cmd="echo SMC_POST_BEGIN; /var/tmp/smc_post.sh >$local_post_log 2>&1"
    common::log "install rc local post cmd to $rc_local_script: $rc_local_post_cmd"
    sed -i -r -e '/^echo SMC_POST_BEGIN/d' "$rc_local_script"
    echo "$rc_local_post_cmd" >> "$rc_local_script"
    common::log "cat $rc_local_script:"
    cat "$rc_local_script"
    return 0
}

function tool::check_os_info() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    # check os info
    common::log "check os info..." "Info"
    g_os_name=$(common::get_os_issue "$root_dir")
    common::log_var "g_os_name" "$g_os_name"
    if [ -z "$g_os_name" ]; then
        common::log "get os issue" "Error"
        return $((CODE_ERROR_GET_OS_NAME))
    fi
    common::log "get os name: $g_os_name" "Info"
    return 0
}

function tool::fix_win_gpt_sys_part_attr() {
    common::log_function "$*"
    local disk_dev=$1
    local src_part_num=$2
    common::log "get disk old attribute..." "Info"
    local old_attr=""
    ( echo "i"; echo "$src_part_num"; ) | gdisk $disk_dev >>$g_log_file 2>$g_log_err_file
    old_attr=$( ( echo "i"; echo "$src_part_num"; ) | gdisk $disk_dev 2>$g_log_err_file | grep  "Attribute flags" | grep -oE "[0-9]+")
    common::log "get disk old attribute: $old_attr" "Info"

    if [[ "${old_attr:0:2}" == "80" ]]; then
        # if old attribute has win-do-no-automount flag, need to clear it
        common::log "disk old attribute has win-do-no-automount flag, clear..." "Info"
        ( echo "x"; echo "a"; echo "$src_part_num"; echo "63"; echo "64"; echo "w"; echo "y"; ) | gdisk $disk_dev >>"$g_log_file" 2>$g_log_err_file
        local code=$?
        if [[ $code -ne 0 ]]; then
            common::log "clear disk old attribute failed return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            return $code
        fi
        local cur_attr=""
        ( echo "i"; echo "$src_part_num"; ) | gdisk $disk_dev >>$g_log_file 2>$g_log_err_file
        cur_attr=$( ( echo "i"; echo "$src_part_num"; ) | gdisk $disk_dev 2>$g_log_err_file | grep  "Attribute flags" | grep -oE "[0-9]+")
        common::log "get disk current attribute: $cur_attr" "Info"
        if [[ "${cur_attr:0:2}" == "00" ]]; then
            common::log "disk old attribute has win-do-no-automount flag, clear done!" "Info"
            return 0
        else
            common::log "clear disk old attribute failed error_msg: $(cat $g_log_err_file)" "Error"
            return 1
        fi
    else
        common::log "disk old attribute has no win-do-no-automount flag, skip" "Info"
        return 0
    fi
}

function tool::do_win_grub() {
    common::log_function "$*"
    local ret=0
    local code=0
    local root_disk_dev=$1
    if parted "$disk_dev" -s print 2>$g_log_err_file | grep -w "gpt" &&
        [ -n "$g_src_part_num" ]; then
        # if it is gpt, we need to try to fix gpt part table attributes
        local sys_part_num=$g_src_part_num
        if tool::fix_win_gpt_sys_part_attr "$root_disk_dev" "$sys_part_num"; then
            return 0
        else
            ret=$CODE_ERROR_GRUB_MS_ATT
            return $((ret))
        fi
    fi

    local more_info=$2
    local win_disk_info=""
    win_disk_info=$(echo "$more_info" | awk -F":" '{printf $1}')
    common::log_var_exit "win_disk_info" "$win_disk_info"
    if [ -z "$win_disk_info" ] || [ "$win_disk_info" == "-" ]; then
        win_disk_info=""
    fi
    local is_win_2003=""
    is_win_2003=$(echo "$more_info" | awk -F":" '{printf $2}')
    common::log_var_exit "is_win_2003" "$is_win_2003"
    if [ -z "$is_win_2003" ] || [ "$is_win_2003" == "-" ]; then
        is_win_2003=0
    fi
    local is_replace_bcd=""
    is_replace_bcd=$(echo "$more_info" | awk -F":" '{printf $3}')
    common::log_var "is_replace_bcd" "$is_replace_bcd"
    if [ -z "$is_replace_bcd" ] || [ "$is_replace_bcd" == "-" ]; then
        is_replace_bcd=0
    fi

    # fix root disk boot record
    local ms_exec="$g_cur_dir"/bin/ms-sys
    chmod +x "$ms_exec" >>"$g_log_file" 2>&1
    common::log "first check disk $root_disk_dev info:" "Info"
    $ms_exec "$root_disk_dev" >>"$g_log_file" 2>&1
    local first_part_dev=""
    first_part_dev=$(common::get_disk_part_dev $root_disk_dev 1)
    common::log "first check disk part ${first_part_dev} info:" "Info"
    $ms_exec "${first_part_dev}"

    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        if [[ $is_win_2003 -eq 1 ]]; then
            common::log "write win 2003 mbr..." "Info"
            $ms_exec -m "$root_disk_dev" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                common::log "write 2003 mbr to $root_disk_dev return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                ret=$CODE_ERROR_GRUB_MS_2003_MBR
                continue
            fi
        else
            common::log "write win higher mbr..." "Info"
            $ms_exec -7 "$root_disk_dev" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                common::log "write higher mbr to $root_disk_dev return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                ret=$CODE_ERROR_GRUB_MS_HIGH_MBR
                continue
            fi
        fi
        common::log "second check disk $root_disk_dev info:" "Info"
        $ms_exec "$root_disk_dev"
        if [ -n "$win_disk_info" ]; then
            common::log "write disk sign to $root_disk_dev..." "Info"
            $ms_exec --writewds $win_disk_info "$root_disk_dev" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                common::log "write disk sign to $root_disk_dev return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                ret=$CODE_ERROR_GRUB_MS_WDS
                continue
            fi
        fi
        common::log "write ntfs boot sector to ${first_part_dev}..." "Info"
        $ms_exec -n -p -H 255 "${first_part_dev}" 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "write ntfs boot sector to $first_part_dev return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            ret=$CODE_ERROR_GRUB_MS_NBS
            continue
        fi
        common::log "third check disk $root_disk_dev info:" "Info"
        $ms_exec "$root_disk_dev"
    done

    if [[ $ret -eq 0 ]] && [[ $is_replace_bcd -eq 1 ]]; then
        common::log "replace bcd to $root_disk_dev" "Info"
        # mount $root_disk_dev (ntfs) and replace bcd (/smc/appdata/BCD-to-replace) to /Boot/BCD
        # then umount $root_disk_dev
        local temp_mount_dir=""
        temp_mount_dir=$(common::mk_temp_dir "bcd_mount")
        if [ -z "$temp_mount_dir" ]; then
            common::log "create temp mount dir failed" "Error"
            ret=$CODE_ERROR_GRUB_MS_BCD
            return $((ret))
        fi

        if ! common::temp_mount "$first_part_dev" "$temp_mount_dir"; then
            common::log "mount $first_part_dev to $temp_mount_dir failed" "Error"
            rm -rf "$temp_mount_dir"
            ret=$CODE_ERROR_GRUB_MS_BCD
            return $((ret))
        fi

        local bcd_to_replace="$temp_mount_dir"/smc/appdata/BCD-to-replace
        if [ ! -f "$bcd_to_replace" ]; then
            common::log "bcd to replace file not found: $bcd_to_replace" "Error"
            umount "$temp_mount_dir"
            rm -rf "$temp_mount_dir"
            ret=$CODE_ERROR_GRUB_MS_BCD
            return $((ret))
        fi

        local boot_dir="$temp_mount_dir"/Boot
        local boot_bcd_path="$boot_dir"/BCD
        if [ ! -d "$boot_dir" ]; then
            mkdir -p "$boot_dir"
        fi

        common::log "copy $bcd_to_replace to $boot_bcd_path"
        \cp -f "$bcd_to_replace" "$boot_bcd_path"
        local code=$?
        if [[ $code -ne 0 ]]; then
            common::log "copy bcd file failed, return: $code" "Error"
            umount "$temp_mount_dir"
            rm -rf "$temp_mount_dir"
            ret=$CODE_ERROR_GRUB_MS_BCD
            return $((ret))
        fi

        sync
        umount "$temp_mount_dir"
        rm -rf "$temp_mount_dir"
        common::log "replace bcd file success" "Info"
    fi
    return $((ret))
}

function tool::do_linux_grub() {
    common::log_function "$*"
    local ret=0
    local root_disk_dev=$1
    local more_info=$2

    g_architecture=$(echo "$more_info" | awk -F":" '{printf $1}')
    common::log_var "g_architecture" "$g_architecture"
    g_boot_mode=$(echo "$more_info" | awk -F":" '{printf $2}')
    common::log_var "g_boot_mode" "$g_boot_mode"
    if [ -z "$g_boot_mode" ] || [ "$g_boot_mode" == "-" ]; then
        g_boot_mode=$BOOT_MODE_BIOS
    fi
    g_has_lvm=$(echo "$more_info" | awk -F":" '{printf $3}')
    common::log_var "g_has_lvm" "$g_has_lvm"
    if [ -z "$g_has_lvm" ] || [ "$g_has_lvm" == "-" ]; then
        g_has_lvm=0
    fi
    g_use_fstab_swap=$(echo "$more_info" | awk -F":" '{printf $4}')
    common::log_var "g_use_fstab_swap" "$g_use_fstab_swap"
    if [ -z "$g_use_fstab_swap" ] || [ "$g_use_fstab_swap" == "-" ]; then
        g_use_fstab_swap=0
    fi
    g_grub_efi_file=$(echo "$more_info" | awk -F":" '{printf $5}')
    common::log_var "g_grub_efi_file" "$g_grub_efi_file"
    if [ -z "$g_grub_efi_file" ] || [ "$g_grub_efi_file" == "-" ]; then
        g_grub_efi_file=""
    fi

    # refresh lvm parts
    common::log "try pvscan..."
    pvscan
    common::log "try partprobe..."
    partprobe -s

    # get root/boot/efi part
    local root_part=""
    local boot_part=""
    local efi_part=""

    # get root disk part paths
    local root_part_paths=""
    root_part_paths=$(tool::get_disk_part_paths "$root_disk_dev")
    if [ -z "$root_part_paths" ]; then
        sleep 1s
        #FIXME: try again if get disk parts empty
        root_part_paths=$(tool::get_disk_part_paths "$root_disk_dev")
        if [ -z "$root_part_paths" ]; then
            common::log "tool::get_disk_parts for $root_disk_dev failed" "error"
            exit $((CODE_ERROR_GET_DISK_PARTS))
        fi
    fi
    common::log_var "root_part_paths" "$root_part_paths"

    for root_part_path in $root_part_paths
    do
        common::log "check disk part: $root_part_path"
        local part_fstype=""
        part_fstype=$(common::get_disk_part_fstype "$root_part_path")
        if [ -z "$part_fstype" ] || [ "$part_fstype" == "swap" ]; then
            continue
        fi

        # try mount part
        local temp_part_dir=""
        temp_part_dir=$(common::mk_temp_dir "part")
        common::log_var "temp_part_dir" "$temp_part_dir"
        if [ -z "$temp_part_dir" ]; then
            continue
        fi
        if ! common::temp_mount "$root_part_path" "$temp_part_dir"; then
            common::log "mount $root_part_path $temp_part_dir failed " "Warn"
            continue
        fi

        if [ -z "$root_part" ] && common::check_root_part "$temp_part_dir"; then
            root_part="$root_part_path"
            common::log "find root part: $root_part_path" "Info"

            # check boot path
            if common::check_boot_part "${temp_part_dir}/boot"; then
                boot_part="$root_part_path"
                common::log "find boot part: $boot_part" "Info"
            fi
        elif [ -z "$boot_part" ] && common::check_boot_part "$temp_part_dir"; then
            boot_part="$root_part_path"
            common::log "find single boot part: $boot_part" "Info"
        elif [ -z "$efi_part" ] && common::check_efi_part "$temp_part_dir"; then
            efi_part="$root_part_path"
            common::log "find efi part: $efi_part" "Info"
        fi

        if [[ $g_is_vmware -eq 0 ]]; then
            #try clear smcss cow file.
            local smcss_cow="${temp_part_dir}/.smcss_cow"
            if test -f "$smcss_cow"; then
                common::log "found and clear smcss cow file: $smcss_cow"
                \rm -f "$smcss_cow"
            fi
            local smcss_cow_bb="${temp_part_dir}/.smcss_cow_bb"
            if test -f "$smcss_cow_bb"; then
                common::log "found and clear smcss cow blocks file: $smcss_cow_bb"
                \rm -f "$smcss_cow_bb"
            fi
        fi

        # try umount part
        if ! common::force_umount "$root_part_path"; then
            common::log "umount $root_part_path failed " "Warn"
        fi
        rmdir "$temp_part_dir"
    done

    # check root/boot part
    if [ -z "$root_part" ]; then
        common::log "can not find root part" "Error"
        exit $((CODE_ERROR_GRUB_NO_ROOT_PART))
    fi
    if [ -z "$boot_part" ]; then
        common::log "can not find boot part" "Error"
        exit $((CODE_ERROR_GRUB_NO_BOOT_PART))
    fi

    # try mount root part
    local temp_root_dir=""
    temp_root_dir=$(common::mk_temp_dir "root")
    common::log_var_exit "temp_root_dir" "$temp_root_dir"

    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        common::log "mount $root_part $temp_root_dir as root..." "Info"
        if ! common::temp_mount "$root_part" "$temp_root_dir"; then
            common::log "mount $root_part $temp_root_dir" "Error"
            ret=$CODE_ERROR_MOUNT_ROOT
            break
        fi

        local temp_boot_dir="$temp_root_dir"/boot
        if [ "$boot_part" != "$root_part" ]; then
            common::log "mount $boot_part $temp_boot_dir as boot..." "Info"
            # try mount boot part
            if ! common::temp_mount "$boot_part" "$temp_boot_dir"; then
                common::log "mount $boot_part $temp_boot_dir" "Error"
                ret=$CODE_ERROR_MOUNT_BOOT
                break
            fi
        fi

        # check efi part
        if [ -n "$efi_part" ]; then
            # try mount efi part
            local temp_efi_dir="$temp_boot_dir"/efi
            common::log "mount $efi_part $temp_efi_dir as efi..." "Info"
            if ! common::temp_mount "$efi_part" "$temp_efi_dir"; then
                common::log "mount $efi_part $temp_efi_dir" "Error"
                ret=$CODE_ERROR_MOUNT_EFI
                break
            fi
        fi

        # check os info
        tool::check_os_info "$temp_root_dir"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            break
        fi
        # try run pre script
        tool::try_run_pre_script "$temp_root_dir"

        local root_uuid=""
        # check root part uuid
        root_uuid=$(common::get_disk_part_uuid "$root_part")
        common::log_var_exit "root_uuid" "$root_uuid"

        # fix os configs
        common::log "fix os configs begin..." "Info"
        tool::fix_os_confs "$disk_num" "$temp_root_dir" "$root_part" "$root_uuid" "$boot_part"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            common::log "fix os configs failed" "Error"
            break
        fi
        common::log "fix os configs successfully!" "Done"

        # fix os grubs
        common::log "fix os grubs begin..." "Info"
        tool::fix_os_grubs "$root_disk_dev" "$temp_root_dir" "$root_part" "$root_uuid" "$boot_part" "$efi_part"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            common::log "fix os grubs failed" "Error"
            break
        fi
        common::log "fix os grubs successfully!" "Done"

        # fix initrd
        common::log "try fix all initrds" "Info"
        tool::fix_all_initrds "$temp_root_dir" "$root_part" "$root_uuid"

        # try install post script
        tool::try_install_post_script "$temp_root_dir"

        if [ -n "$efi_part" ]; then
            common::force_umount "$efi_part" || common::log "umount efi $efi_part" "Warn"
        fi

        if [ -n "$boot_part" ] && [ "$boot_part" != "$root_part" ]; then
            common::force_umount "$boot_part" || common::log "umount boot $boot_part" "Warn"
            if [[ $g_is_vmware -eq 0 ]]; then
                boot_fs_type=$(common::get_disk_part_fstype "$boot_part")
                if [ -n "$boot_fs_type" ]; then
                    common::fsck_part "$boot_part" "$boot_fs_type" 0
                fi
            fi
        fi

        # umount all temp mount before umount root part
        common::temp_umount_all
        common::force_umount "$root_part" || common::log "umount root $root_part" "Warn"
        if [[ $g_is_vmware -eq 0 ]]; then
            root_fs_type=$(common::get_disk_part_fstype "$root_part")
            if [ -n "$root_fs_type" ]; then
                common::fsck_part "$root_part" "$root_fs_type" 0
            fi
        fi
        break
    done
    # umount
    common::temp_umount_all
    rmdir "$temp_root_dir" || common::log "clean temp root dir $temp_root_dir" "Warn"
    return $((ret))
}

function tool::main_entry() {
    common::log_function "$*"
    local action=$1
    common::log_var "action" "$action"

    if [ "$action" == "--version" ]; then
        global::print_version
        return 0
    elif [ "$action" == "--help" ]; then
        tool::print_usage
        return 0
    fi

    #clear last server config error log
    if [ -n "$g_log_err_file" ] && test -f "$g_log_err_file"; then
        rm -f "$g_log_err_file"
    fi

    local ret=0
    common::log "====== $g_util_desc $g_util_version =====" "Info"
    common::log "P2V Cloud Tool $action Begin..." "Info"
    # try refresh devs
    common::log "try refresh devs..." "Info"
    fdisk -l
    sleep 1s

    if [ "$action" == "--grub" ]; then
        # check params
        local disk_index=$2
        common::log_var "disk_index" "$disk_index"
        local disk_num=0
        ((disk_num=disk_index+2))
        common::log_var_exit "disk_num" "$disk_num"

        local disk_dev=""
        disk_dev=$(common::get_disk_dev "$disk_num")
        common::log_var_exit "disk_dev" "$disk_dev"

        local sys_disk_dev=""
        sys_disk_dev=$(fdisk -l 2>/dev/null | grep -oE "Disk /dev/(nvme[0-9]*n[0-9]*|[a-z]*)" | grep -v mapper | sort | head -1 | tail -1 | awk '{printf $2}')
        common::log_var_exit "sys_disk_dev" "$sys_disk_dev"
        if [[ "$disk_dev" == "$sys_disk_dev" ]]; then
            common::log "!![disk device] can't be sys device!!" "Error"
            exit $((CODE_ERROR_COMMON_INVALID_PARAM))
        fi

         # no use
        local part_index=$3
        common::log_var_exit "part_index" "$part_index"

        g_src_part_num=$4
        common::log_var_exit "g_src_part_num" "$g_src_part_num"
        if [ "$g_src_part_num" == "-" ]; then
            g_src_part_num=""
        fi

        local grub_info=$5
        common::log_var_exit "grub_info" "$grub_info"

        g_src_mount_infos=$6
        common::log_var_exit "g_src_mount_infos" "$g_src_mount_infos"
        if [ "$g_src_mount_infos" == "-" ];then
            g_src_mount_infos=""
        fi

        local src_type=$7
        common::log_var_exit "src_type" "$src_type"
        if [ "$src_type" == "$SRC_TYPE_WIN" ]; then
            g_is_win=1
            common::log_var "g_is_win" "$g_is_win"
        elif [ "$src_type" == "$SRC_TYPE_VMWARE" ]; then
            g_is_vmware=1
            common::log_var "g_is_vmware" "$g_is_vmware"
        else
            g_grub_flag=$(echo "$grub_info" | awk -F":" '{printf $1}')
            g_grub_install=$(echo "$grub_info" | awk -F":" '{printf $2}')
            common::log_var_exit "g_grub_install" "$g_grub_install"
            if [ "$g_grub_install" == "-" ]; then
                g_grub_install=""
            fi
            g_grub_mkconfig=$(echo "$grub_info" | awk -F":" '{printf $3}')
            if [ "$g_grub_mkconfig" == "-" ]; then
                g_grub_mkconfig=""
            fi
            g_sync_grub=$(echo "$grub_info" | awk -F":" '{printf $4}')
            common::log_var "g_sync_grub" "$g_sync_grub"
        fi

        local more_info=$8
        common::log_var "more_info" "$more_info"

        if [[ $g_is_win -ne 1 ]] && [[ $g_is_vmware -ne 1 ]]; then
            #try fsck gpt disk system part
            if parted "$disk_dev" -s print 2>$g_log_err_file | grep -w "gpt"; then
                common::log "found gpt disk: $disk_dev"
                # travel data disk devs
                local disk_parts=""
                disk_parts=$(common::get_disk_parts "$disk_dev")
                if [ -z "$disk_parts" ]; then
                    sleep 1s
                    #FIXME: try again if get disk parts empty
                    disk_parts=$(common::get_disk_parts "$disk_dev")
                    if [ -z "$disk_parts" ]; then
                        common::log "tool::get_disk_parts for $disk_dev failed" "error"
                        exit $((CODE_ERROR_GET_DISK_PARTS))
                    fi
                fi
                common::log_var "disk_parts" "$disk_parts"
                # travel disk parts
                for disk_part in $disk_parts
                do
                    common::log "check gpt disk dev part: $disk_part"
                    if [ -z "$disk_part" ]; then
                        continue
                    fi

                    local part_fstype=""
                    part_fstype=$(common::get_disk_part_fstype "$disk_part")
                    if [ -z "$part_fstype" ]; then
                        continue
                    fi

                    if echo "$part_fstype" | grep -i "lvm"; then
                        # handle lvm parts
                        local vg_name=""
                        common::log "pvdisplay $disk_part..." "Info"
                        vg_name=$(pvdisplay "$disk_part" | grep -w "VG Name" | awk '{print $3}')
                        common::log "vg_name: $vg_name" "Info"
                        if [ -z "$vg_name" ]; then
                            continue
                        fi

                        common::log "try activate vg $vg_name..." "Info"
                        echo y | vgchange -ay $vg_name

                        local lv_parts=""
                        common::log "lvdisplay $vg_name..." "Info"
                        lv_parts=$(lvdisplay "$vg_name" | grep -w "LV Path" | grep -Eo "/dev/.+")
                        common::log "lv_parts: $lv_parts"
                        if [ -z "$lv_parts" ]; then
                            continue
                        fi

                        # travel lv paths
                        for lv_part in $lv_parts
                        do
                            if [ -z "$lv_part" ]; then
                                continue
                            fi
                            local lv_part_fstype=""
                            lv_part_fstype=$(common::get_disk_part_fstype "$lv_part")
                            if [ -z "$lv_part_fstype" ]; then
                                continue
                            fi
                            common::force_umount "$lv_part"
                            common::fsck_part "$lv_part" "$lv_part_fstype" 0
                        done
                    else
                        common::force_umount "$disk_part"
                        common::fsck_part "$disk_part" "$part_fstype" 0
                    fi
                done
            fi
        fi

        if [[ $g_is_win -eq 1 ]]; then
            tool::do_win_grub "$disk_dev" "$more_info"
            ret=$?
        else
            tool::do_linux_grub "$disk_dev" "$more_info"
            ret=$?
        fi
    else 
        echo "Invalid option: [$action]"
        tool::print_usage
        exit 1
    fi

    if [ $ret == 0 ]; then
        common::log "P2V Cloud Tool $action Finished!" "Done"
    else
        common::log "P2V Cloud Tool $action Not Finished!" "Error"
    fi
    exit $((ret))
}

# all exec output to log
exec >>"$g_log_file"

tool::main_entry $*
