#!/bin/sh 

if [ "$MOUNT_IMMUTABLE" = "no" ]; then
	exit
fi

# 把这个文件放在 /usr/share/initramfs-tools/scripts/init-bottom/ 目录下。
# 权限为 0755，在自动更新initrd时，这个脚本就被放到initrd中了，例如，
# update-initramfs -u
# 更新后，这个脚本将在initrd 运行到 local-bottom 阶段时自动执行。
# 此时根分区已经挂载到了/tmproot下，可以直接访问该分区的文件。

# PREREQ指定需要在什么脚本之后执行（同目录下的），也就是指定该脚本的依赖，以确定执行顺序。
# 如果没有依赖，就可以是空字符串。 
PREREQ=""

prereqs()
{
	echo "$PREREQ"
}

case $1 in
	prereqs)
		prereqs
		exit 0
		;;
esac

# 上述部分是initramfs脚本的固定格式，不用动它。
# 除非有依赖，才需要改 PREREQ 的值。

#. /scripts/security-functions

SYSOVL_CONF="/root/sysroot/ostree/pkgs/system-ovl.conf"
root_real_path=$(readlink -f "/tmproot${OSTREEROOT}")

##处理更新升级后SYSOVL_CONF不存在的问题
if [ ! -d /root/sysroot/ostree/pkgs ]; then
    mkdir -p /root/sysroot/ostree/pkgs
    echo "Created /root/sysroot/ostree/pkgs directory."
fi

if [ ! -f ${SYSOVL_CONF} ] || [ ! -s ${SYSOVL_CONF} ] ; then
    cp -rf "${root_real_path}/sysroot/ostree/pkgs/system-ovl.conf" ${SYSOVL_CONF}
    echo "copy system-ovl.conf..."
fi

show_text_mesg()
{
        echo "$1"
}

plymouth_is_running()
{
        #if [ -e "/bin/plymouth" ] && /bin/plymouth --ping; then
        if [ -e "/bin/plymouth" ]; then
                return 0
        else
                return 1
        fi
}

show_message()
{
        if plymouth_is_running; then
                /bin/plymouth message --text="$1"
                show_text_mesg "$1"
        else
                show_text_mesg "$1"
        fi
}

clear_message()
{
        if plymouth_is_running; then
                plymouth message --text=""
        fi
        clear
}

isEnglish()
{
        LOCALFILE="/tmproot/etc/default/locale"
	if [ ! -e "$LOCALFILE" ]; then
	    return 0
	fi

        localcontent=`grep -E  "^LANG" $LOCALFILE | grep -i "zh_CN.UTF"`

        #echo "localcontent: $localcontent"

        if [ -z $localcontent ]; then
                #echo "This is English"
                return 1
        else
                #echo "系统支持中文"
                return 0
        fi
}

isEnglish
v_isEnglish=$?

myoutput()
{

        if [ $v_isEnglish -eq 1 ]; then
                echo $1
                #plymouth message --text=$1
                #echo "English"
		show_message "$1"
        else
                echo $2
                #plymouth message --text=$2
                #echo "中文"
		show_message "$2"
        fi
}

#***配置读取与设置
# key和value的分隔符，即等号两边有没有空格
delimeter='='
#delimeter=' = '

# 获取配置文件指定section指定key的value
get_value() {
    file=$1
    section=$2
    key=$3
    #val=$(awk -F "$delimeter" '/\['${section}'\]/{a=1}a==1&&$1~/'${key}'/{print $2;exit}' $file)
    #/^[[:space:]]*#/:跳过#注释行
    #{if ($0 ~ /\['${section}'\]/) {a=1} else if (a) {exit}}:匹配到[${section}]时将a设置为1
    #a==1 && $1~/'${key}'/ {print $2; exit}:在对应的组中（a==1）且匹配到键值key时打印并退出
    val=$(awk -F "$delimeter" '/^[[:space:]]*#/ {next} /\[/ {if ($0 ~ /\['${section}'\]/) {a=1} else if (a) {exit}} a==1 && $1~/'${key}'/ {print $2; exit}' "$file")
    echo ${val}
}


# 更新配置文件指定section指定key的value
set_value() {
    local file="$1"
    local section="$2"
    local key="$3"
    local val="$4"
    local delimiter="="  # 明确指定分隔符为等号

    # 创建临时文件
    tmpfile=$(mktemp) || return 1

    # 使用awk处理并输出到临时文件
    awk -F "$delimiter" -v sect="$section" -v k="$key" -v v="$val" '
        BEGIN { OFS=FS; in_section=0; modified=0 }
        /^[[:space:]]*#/ { print; next }   # 跳过注释行
        /^\[/ {                            # 处理 [section] 行
            if ($0 == "[" sect "]") {
                in_section = 1
            } else {
                in_section = 0
            }
            print
            next
        }
        in_section && $1 == k {            # 在目标 section 中找到目标 key
            $2 = v                         # 仅替换值部分（保留原键名）
            print $1 OFS $2
            modified = 1
            next
        }
        { print }                          # 其他行原样输出
    ' "$file" > "$tmpfile"
    # 将临时文件内容覆盖原文件，避免权限变更
    cat "$tmpfile" > "$file"

    # 清理临时文件
    rm -f "$tmpfile"
}

#***配置读取与设置
deploy_csum_serial="${root_real_path##*/}"  ##313543dcccf89686e998d728f4e54dc2c89dc4d876b56efec00055271db46d8b.1
set_value $SYSOVL_CONF "General" "Deploy" $deploy_csum_serial
OSTREE_PKG=${rootmnt}/sysroot/ostree/pkgs


##兼容旧版本创建usr-ovl/usr-upper目录
if [ ! -d "${OSTREE_PKG}/ovl-${deploy_csum_serial}/usr-ovl/usr-upper" ]; then
    mkdir "${OSTREE_PKG}/ovl-${deploy_csum_serial}/usr-ovl/usr-upper"
    echo "mkdir ${OSTREE_PKG}/ovl-${deploy_csum_serial}/usr-ovl/usr-upper"
fi

# 统一日志输出到控制台与文件
LOG_FILE="${rootmnt}/var/log/ostree.log"
# Boot-time tag
LOG_TAG_SYNC="[boot-sync]"
mkdir -p "$(dirname "$LOG_FILE")"
log_and_echo() {
    local msg="$1"
    echo "$LOG_TAG $msg"
    printf 'system-layer: %s %s\n' "$LOG_TAG_SYNC" "$msg" >> "$LOG_FILE"
}

##条件式目录创建与数据同步：检测${rootmnt}/usr下的所有直接子目录
version=$(get_value $SYSOVL_CONF "Info" "Version")
# 如果未获取到版本号，或版本号小于等于 1.0.0，则执行目录创建与数据同步
if [ -z "$version" ] || [ "$(printf '%s\n' "$version" "1.0.0" | sort -V | tail -n1)" = "1.0.0" ]; then
    # 仅在usr-upper完全为空时执行，避免多次执行
    usr_upper_path="${OSTREE_PKG}/ovl-${deploy_csum_serial}/usr-ovl/usr-upper"
    usr_lower_root="${OSTREE_PKG}/ovl-${deploy_csum_serial}/usr-ovl/usr-lower"

    if [ -d "${rootmnt}/usr" ] && [ -d "$usr_upper_path" ] && [ -d "$usr_lower_root" ]; then
        # 检查usr-upper目录下是否已存在标记文件
        if [ -f "$usr_upper_path/.migrated" ]; then
            # usr-upper下已存在标记文件，跳过整个流程
            log_and_echo "usr-upper migration marker file exists, skipping directory creation and data sync"
        else
            # 找出 usr-lower 中所有字符设备文件，用于在迁移 usr 时排除同名目录
            exclude_args=""
            if [ -d "${usr_lower_root}/" ]; then
                # find 找出字符设备 -> sed 去掉前缀得到相对路径 -> sed 格式化为排除参数 -> tr 连接成一行
                exclude_args=$(find "$usr_lower_root" -type c 2>/dev/null | sort -u | \
                    sed "s|^${usr_lower_root}/||" | \
                    grep -v '^\.\?$' | \
                    sed 's|^| --exclude=/|; s|$|/|' | \
                    tr '\n' ' ')
            fi
            
            # 仅递归迁移 usr 的目录结构，跳过 usr-lower 中存在字符设备文件的同名目录及其子目录
            log_and_echo "Start directory structure migration from usr to usr-upper using rsync (directories only, no files)"
            log_and_echo "Exclude args: $exclude_args"
            rsync -aAXc --inplace --force -H --numeric-ids --ignore-times --atimes --links \
                  --filter='-x trusted.overlay.opaque' \
                  --log-file=${rootmnt}/var/log/ostree.log \
                  --exclude=/etc/ \
                  $exclude_args \
                  -f '+ */' -f '- *' \
                  "${rootmnt}/usr/" "${usr_upper_path}/"
            log_and_echo "Directory structure migration from usr to usr-upper completed"

            # 仅递归迁移 usr-lower 的目录结构（不迁移任何文件）
            log_and_echo "Start directory structure migration from usr-lower to usr-upper using rsync (directories only, no files)"
            rsync -aAXc --inplace --force -H --numeric-ids --ignore-times --atimes --links \
                  --filter='-x trusted.overlay.opaque' \
                  --log-file=${rootmnt}/var/log/ostree.log \
                  -f '+ */' -f '- *' \
                  "${usr_lower_root}/" "${usr_upper_path}/"
            log_and_echo "Directory structure migration from usr-lower to usr-upper completed"

            # 创建标记文件，表示迁移已完成
            touch "$usr_upper_path/.migrated"
            log_and_echo "Migration marker file created in usr-upper"
        fi
    fi
fi


##处理上次运维退出数据迁移、清空失败问题
switch_state=$(get_value $SYSOVL_CONF "General" "SwitchState")
# 定义基础路径和清理函数
OVL_BASE_PATH="${rootmnt}/sysroot/ostree/pkgs/ovl-${deploy_csum_serial}"
# 注意：--log-file-format 里的占位符应使用单个 %，且不需要再转义引号
RSYNC_OPTS="-aXc --inplace --force -H --numeric-ids --ignore-times --atimes --links --log-file=${rootmnt}/var/log/ostree.log"
# Ensure rsync logs also carry the same boot processing tag
RSYNC_LOG_FMT="system-layer: ${LOG_TAG_SYNC} %o %f"

# 数据同步函数
sync_tmpupper_to_upper() {
    local src_base="$1"
    local dst_base="$2"
    log_and_echo "Sync data: $src_base -> $dst_base"
    rsync $RSYNC_OPTS --log-file-format="$RSYNC_LOG_FMT" "$src_base/" "$dst_base/"
    return $?
}

# 清理临时数据函数
cleanup_tmpupper() {
    local base_path="$1"
    log_and_echo "Clean temporary data: $base_path"
    rm -rf $base_path/etc-ovl/etc-tmpupper/*
    rm -rf $base_path/usr-ovl/usr-tmpupper/*
    rm -rf $base_path/var-ovl/var-tmpupper/lib/*
}

# 重置配置函数
reset_switch_config() {
    set_value $SYSOVL_CONF "General" "SwitchState" "stable"
    set_value $SYSOVL_CONF "General" "NextMode" "normal"
}

# 处理保存数据的情况
if [ "$switch_state" = "save-switch" ] || [ "$switch_state" = "save-failed" ]; then
    log_and_echo "Previous maintenance exit: migration/cleanup failed"
    
    # 同步数据（任一失败则不清理 tmpupper）
    sync_tmpupper_to_upper "$OVL_BASE_PATH/etc-ovl/etc-tmpupper" "$OVL_BASE_PATH/etc-ovl/etc-upper"; rc1=$?
    sync_tmpupper_to_upper "$OVL_BASE_PATH/usr-ovl/usr-tmpupper" "$OVL_BASE_PATH/usr-ovl/usr-upper"; rc2=$?
    sync_tmpupper_to_upper "$OVL_BASE_PATH/var-ovl/var-tmpupper/lib" "$OVL_BASE_PATH/var-ovl/var-upper/lib"; rc3=$?

    if [ $rc1 -eq 0 ] && [ $rc2 -eq 0 ] && [ $rc3 -eq 0 ]; then
        # 清理临时数据
        cleanup_tmpupper "$OVL_BASE_PATH"
        # 重置配置
        reset_switch_config
    else
        log_and_echo "Rsync failure detected (rc1=$rc1 rc2=$rc2 rc3=$rc3), skipping tmpupper cleanup"
    fi
fi

# 处理不保存数据的情况
if [ "$switch_state" = "nosave-switch" ] || [ "$switch_state" = "nosave-failed" ] || [ "$switch_state" = "save-synced" ]; then
    log_and_echo "Upcoming cleaning of maintenance mode data"
    
    # 直接清理临时数据
    cleanup_tmpupper "$OVL_BASE_PATH"
    
    # 重置配置
    reset_switch_config
fi  


###获取当前处于哪种模式-通过配置文件，如果存在[boot] mode=maintain则表示处于维护模式，否则处于用户模式
# MODE=
# ### 模式0：维护 、模式1：用户
# conf_file="${OSTREE_PKG}/ostree-ovl.conf"  
#配置文件是否表示为维护
#is_maintain_conf=N
is_maintain_mode=N ##表示当前(mode)是否为maintain
is_maintain_next_mode=N ##表示下次(next_mode)是否为maintain
if [ "$(get_value $SYSOVL_CONF "General" "Mode")" = "maintain" ];then
        is_maintain_mode=Y
fi

if [ "$(get_value $SYSOVL_CONF "General" "NextMode")" = "maintain" ];then
        is_maintain_next_mode=Y
fi


###锁文件判断
is_maintain_lock=N
get_mode_lock()
{
    if [ -f "${OSTREE_PKG}/mm.lock" ];then
        echo "----${OSTREE_PKG}/mm.lock exist!"
        is_maintain_lock=Y
    else
        echo "----${OSTREE_PKG}/mm.lock not exist!"
    fi
}

##批量处理字符串替换，入参为：原始字符串，待替换A1，替换B1，待替换A2，替换B2.....
##例:replace_string $LowerDir "<PathPre>" $PathPre "<stage>" $StageInitrd "<Deploy>" $Deploy
##      字符串LowerDir中，"<PathPre>"替换为变量PathPre，"<stage>"替换为变量StageInitrd，"<Deploy>"替换为变量SDeploy
replace_string() {
    if [ $# -lt 1 ]; then
        echo "Usage: replace_string original_str target1 replacement1 ..."
        return 1
    fi
    original_str=$1
    shift # 移除原始字符串参数，后续参数为替换对
    while [ $# -ge 2 ]; do
        target=$1
        replacement=$2
        original_str=$(echo "$original_str" | sed "s#$target#$replacement#g")  # 全局替换
        shift 2	 # 移动到下一组替换对
    done
    echo "$original_str"
}


##****字符串替换自动化流程

#维护模式与用户模式共同存在变量
StageInitrd=$(get_value $SYSOVL_CONF "Overlay" "StageInitrd")
PathPre=$(get_value $SYSOVL_CONF "Overlay" "PathPre")
Deploy=$(get_value $SYSOVL_CONF "General" "Deploy")

#通用替换处理流程，将字符串中的变量替换为对应的值，保存在变量中
#替换关系为：
#       "<PathPre>"     $PathPre
#       "<stage>"       $StageInitrd
#       "<Deploy>"      $Deploy
#生成的值保存在${dir_type}_${suffix}变量中，例如$LowerDir_etc
#拼接后值为：LowerDir_etc=/root/sysroot/ostree/pkgs/ovl-abc.1/etc-ovl/etc-upper:/root/sysroot/ostree/pkgs/ovl-abc.1/etc-ovl/etc-lower:/root/etc
process_section() {
    section="$1"
    suffix="$2"
    dir_types="LowerDir UpperDir WorkDir MergeDir"

    for dir_type in $dir_types; do
        pend_value=$(get_value "$SYSOVL_CONF" "$section" "$dir_type")
        resolved_value=$(replace_string "$pend_value" \
            "<PathPre>" "$PathPre" \
            "<stage>" "$StageInitrd" \
            "<Deploy>" "$Deploy")

        eval "${dir_type}_${suffix}=\"\$resolved_value\""
        echo "${dir_type}_${suffix}=$resolved_value"
    done
}
##****字符串替换自动化流程

##****根据配置自动化处理挂载
auto_mount_process() {
    sections="$1"
    IFS=' '

    for section in $sections; do
        old_ifs="$IFS"
        IFS=':'
        set -- $section
        section="$1"
        suffix="$2"
        IFS="$old_ifs"
	# 使用间接扩展获取变量值,设置lower_dir的值为LowerDir_$section变量的值
        eval "lower_dir=\$LowerDir_$suffix"
        eval "upper_dir=\$UpperDir_$suffix"
        eval "work_dir=\$WorkDir_$suffix"
        eval "merge_dir=\$MergeDir_$suffix"

 	# 参数验证 - lower_dir、merge_dir对应目录必须存在
        if [ -z "$lower_dir" ] || [ -z "$merge_dir" ]; then
            echo "[ERROR] Missing parameters for $suffix" >&2
            continue
        fi

	# 动态构建挂载参数
        mount_options="lowerdir=$lower_dir"
        if [ -n "$upper_dir" ]; then  ## upper_dir对应目录存在时，workdir必须存在
            mount_options="$mount_options,upperdir=$upper_dir"
            if [ -z "$work_dir" ]; then
                echo "[ERROR] WorkDir required for $suffix"
                continue
            fi
            mount_options="$mount_options,workdir=$work_dir"
        elif [ -n "$work_dir" ]; then  ## upper_dir对应目录不存在时，workdir可以存在
            mount_options="$mount_options,workdir=$work_dir"
        fi

        machine=`uname -m`
        if [ "x$machine" != "xsw_64" ];then 
            mount_cmd=$(printf 'mount -t overlay overlay -o "%s" "%s"'  "$mount_options" "$merge_dir")
        else    ##sw架构额外处理，增加index=off
            mount_cmd=$(printf 'mount -t overlay overlay -o "%s" "%s"'  "${mount_options},index=off" "$merge_dir")
        fi
        
	echo mount_cmd=$mount_cmd
	# 执行挂载命令
    if eval "$mount_cmd"; then
        echo "[OK] $suffix mounted to $merge_dir"
    else
        echo "[FAIL] $suffix mount failed (code:$?)" 
    fi
    done
}
##****根据配置自动化处理挂载

## 维护模式挂载
maintain_mode_mount() {
    # 定义维护模式需要处理的配置块（空格分隔字符串）
    sections="etc-M:etc usr-M:usr var.lib-M:varlib"

    # 循环处理所有配置块
    for entry in $sections; do
        IFS=':' read -r section suffix <<EOF
$entry
EOF
        process_section "$section" "$suffix"
    done

    # 自动化挂载对应模块
    auto_mount_process "$sections"

    if [ "$is_maintain_mode" = "N" ]; then
        set_value "$SYSOVL_CONF" "General" "Mode" "maintain"
    fi
    if [ "$is_maintain_next_mode" = "N" ]; then
        set_value "$SYSOVL_CONF" "General" "NextMode" "maintain"
    fi
    if [ "$is_maintain_lock" = "N" ]; then
        echo "touch maintain lock"
        touch "${OSTREE_PKG}/mm.lock"
    fi
}

## 用户模式挂载
user_mode_mount() {
    # 定义用户模式需要处理的配置块
    sections="etc-N:etc usr-N:usr var.lib-N:varlib"

    # 循环处理所有配置块
    for entry in $sections; do
        IFS=':' read -r section suffix <<EOF
$entry
EOF
        process_section "$section" "$suffix"
    done

    # 自动化挂载对应模块
    auto_mount_process "$sections"

    ##同步配置文件、锁文件为用户模式
        if [ "${is_maintain_mode}" = "Y" ]; then
                ###修改配置文件模式标记
                set_value $SYSOVL_CONF "General" "Mode" "normal"
        fi
        
        if [ "${is_maintain_next_mode}" = "Y" ]; then
                ###修改配置文件模式标记
                set_value $SYSOVL_CONF "General" "NextMode" "normal"
        fi

        if [ "${is_maintain_lock}" = "Y" ]; then
                ###删除标记文件
                echo "delete maintain lock"
                rm -rf /tmproot/ostree/pkgs/mm.lock
        fi

        # set_value $SYSOVL_CONF "General" "Mode" "normal" ##更新配置文件中Mode为用户模式
}


# 判断是否存在backup|restore|rollback-backup等字段，如果存在以上字段则为备份、还原、恢复出厂等模式，不走这套维护不维护的模式 
IS_BACKUP=
IS_OSTREE_RESTORE=
#增加cmdline参数来支持临时进维护状态
IS_MAINTAIN_START=N
if [ -f "/proc/cmdline" ]; then  
    # 使用grep检查文件中是否包含特定的字段  如果grep找到匹配项，它将返回状态码0（成功），否则返回非0状态码  
    if grep -qE "backup|restore|rollback-backup|restore-retain-userdata|factory-restore|ostree-factory-restore" "/proc/cmdline"; then  
        IS_BACKUP=Y
        if grep -qE "ostree-factory-restore" "/proc/cmdline"; then 
                IS_OSTREE_RESTORE=Y
                ##ostree的恢复出厂设置则直接挂载为用户模式，且恢复配置文件以及锁文件
                user_mode_mount
        fi
        echo "is backup、restore、rollback-backup STOP"  
    elif grep -qE "maintain=realtime" "/proc/cmdline"; then
        MAINTAIN_MODE=$(sed -n 's/.*maintain=\([^ ]*\).*/\1/p' /proc/cmdline)
        if [ "$MAINTAIN_MODE" = "realtime" ]; then
            IS_MAINTAIN_START=Y
        fi
    else  
        # 如果都不存在，则设置变量a为y，并继续执行  
        IS_BACKUP=N
        echo "is not backup。"  
        # 在这里添加你的后续操作  
    fi  
else  
    echo "文件 /proc/cmdline 不存在。"  
fi

# ##ostree的恢复出厂设置则直接挂载为用户模式，且恢复配置文件以及锁文件
# if [ "${IS_OSTREE_RESTORE}" = "Y" ]; then 
#         user_mode_mount
# fi

if [ "${IS_MAINTAIN_START}" = "Y" ]; then 
    while plymouth_is_running
        do
            myoutput "Do you want to continue starting the system in [Realtime Maintain Mode] ? [y/n]" "是否继续以临时维护模式启动系统？[y/n]"
            is_sure=$(/bin/plymouth watch-keystroke)
            case $is_sure in
                    y|Y)
                            myoutput "Start the system in realtime maintain mode" " 正在以临时维护模式启动系统 "
                            maintain_mode_mount
                            set_value "$SYSOVL_CONF" "General" "RealtimeMaintain" "true"
                            break
                            ;;
                    n|N)
                            myoutput "Start the system in normal mode" " 正在以用户模式启动系统 "
                            #挂载用户目录
                            user_mode_mount
                            break
                            ;;
                    *)
                            ;;
            esac
        done
fi

## 非恢复出厂设置等标记
if [ "${IS_BACKUP}" = "N" ] && [ "${IS_MAINTAIN_START}" = "N" ]; then
        get_mode_lock
        echo "----is_maintain_conf=${is_maintain_conf}  is_maintain_lock=${is_maintain_lock}--------"
        if [ "${is_maintain_mode}" = "${is_maintain_lock}" ];then
                ##配置文件mode与锁文件状态一致，则上一次是正常退出
                if [ "$is_maintain_next_mode" = "Y" ]; then
                        maintain_mode_mount
                else
                        user_mode_mount
                fi
        else
                ##配置文件mode与锁文件状态不一致，则上一次是异常退出，plymouth询问
                while plymouth_is_running
                do
                        myoutput "There may be an abnormal exit before shutting down. Do you want to continue starting the system in [Operational Mode] ? [y/n]" "关机前可能存在异常退出，是否继续以 [维护模式] 启动系统？[y/n]"
                        is_sure=$(/bin/plymouth watch-keystroke)

                        case $is_sure in
                                y|Y)
                                        myoutput "Start the system in operation and maintenance mode" " 正在以维护模式启动系统 "
                                        maintain_mode_mount
                                        break
                                        ;;
                                n|N)
                                        myoutput "Start the system in normal mode" " 正在以用户模式启动系统 "
                                        #挂载用户目录
                                        user_mode_mount
                                        break
                                        ;;
                                *)      
                                        ;;
                        esac
                done
        fi
fi

exit 0
