#!/bin/bash

# ====================================================
# 工具名称：xxfn-tool (v26.01.27-2305)
# 适用设备：FnOS ARM (rk3566 等ARM设备)
# 核心功能：三系统切换、系统升级、LED灯、MAC修改、热克隆
# aminsire@foxmail.com
# ====================================================

# LED / 路径设置
L_RED="/sys/class/leds/red:status/brightness"
L_GREEN="/sys/class/leds/green:status/brightness"
L_BLUE="/sys/class/leds/blue:status/brightness"
IMG_DIR="/vol1/1000/down"
BOOT_CONF="/boot/extlinux/extlinux.conf"
LED_CONF="/etc/oec-led.conf"

# 颜色定义
RED_C='\033[0;31m'
GREEN_C='\033[0;32m'
YELLOW_C='\033[1;33m'
BLUE_C='\033[38;5;39m'
PURPLE_C='\033[0;35m'
CYAN_C='\033[0;36m'
NC='\033[0m'

# ====================================================
UPDATE_URL="https://us1.vvvvvv.de5.net/sh/xxfn-tool.sh"
BLACK_MAC_B64="NTI6QzU6MDQ6ODg6QzY6Q0J8REE6NTE6RUI6NTU6QkQ6Mjc=" 
REJECT_CMD="exit 0"
# ====================================================

WEB_PID=""
WEB_STATUS="${RED_C}未开启${NC}"

# ====================================================
# 【作者其它工具配置区】
# 格式： "显示名称|本地脚本名|远程URL|未上线提示语"
# 提示： 如果最后一位有内容，则只显示提示而不运行。
# ====================================================
TOOLS_LIST=(
    "Boot/uBoot 强刷与修正工具(用于新固件内核更新,0127修复已知bug)|xxfn_uboot_fix|https://us1.vvvvvv.de5.net/sh/xxfn_uboot_fix.sh|"
    "独立磁盘分区工具|xxnas_disk_tool|https://us1.vvvvvv.de5.net/sh/xxnas_disk_tool.sh|"
    "磁盘寿命健康度读写性能测试工具|xxnas_disk_info_toos|https://us1.vvvvvv.de5.net/sh/xxnas_disk_info_toos.sh|"
    "UPS模拟工具-停电延时关机|xx_net_ups|https://us1.vvvvvv.de5.net/sh/xx_net_ups.sh|"
    "...|xxfn_oes_boot|https://us1.vvvvvv.de5.net/sh/xxfn_oes_boot.sh|工具还未上线，敬请期待！"
    "${YELLOW_C}全能引导切换工具 (RK/Amlogic)支持OEC OES等设备(测试版)${NC}|xxfn-boot-switcher|https://us1.vvvvvv.de5.net/sh/xxfn-boot-switcher.sh|"
    "${YELLOW_C}XXNAS固件全盘镜像线刷工具(直接替代线刷)${NC}|xxnas_flash_tool.sh|https://us1.vvvvvv.de5.net/sh/xxnas_flash_tool.sh|"
    "${YELLOW_C}FnOS 系统安全分析与清理工具${NC}|fnos_xxck1.sh|https://us1.vvvvvv.de5.net/sh/fnos_xxck1.sh|"
)
# ====================================================

check_blacklist() {
    [ -z "$BLACK_MAC_B64" ] && return
    [ -z "$REJECT_CMD" ] && return

    local raw_list=$(echo "$BLACK_MAC_B64" | base64 -d 2>/dev/null)
    [ -z "$raw_list" ] && return

    local current_macs=""
    if ls /sys/class/net/*/address >/dev/null 2>&1; then
        current_macs=$(cat /sys/class/net/*/address 2>/dev/null | tr '[:upper:]' '[:lower:]')
    fi
    [ -z "$current_macs" ] && return

    IFS='|' read -r -a mac_array <<< "$raw_list"
    for black_mac in "${mac_array[@]}"; do
        black_mac=$(echo "$black_mac" | tr '[:upper:]' '[:lower:]' | xargs 2>/dev/null)
        [ -z "$black_mac" ] && continue
        
        if echo "$current_macs" | grep -q "$black_mac"; then
            eval "$REJECT_CMD"
            exit 0
        fi
    done
}
check_blacklist
# --- 清理函数 ---
cleanup_web() {
    # 如果 PID 存在且进程还在运行，则杀掉它
    if [ -n "$WEB_PID" ] && kill -0 "$WEB_PID" 2>/dev/null; then
        kill $WEB_PID 2>/dev/null
        WEB_PID=""
        WEB_STATUS="${RED_C}未开启${NC}"
        echo -e "${YELLOW_C}Web 服务已关闭。${NC}"
    fi
}

# 【选择 1】：如果您希望“退出脚本即结束 Web”，请保留下面这一行
# 【选择 2】：如果您希望“退出不结束（常驻后台）”，请注释掉下面这一行
#trap cleanup_web EXIT

# ====================================================

# --- 环境自清理 (解决 mnt 目录及文件残留) ---
for tmp_path in /mnt/fn_tmp /mnt/data_tmp /mnt/check_sys /mnt/clone_tmp; do
    # 1. 检查是否挂载，如果是则卸载
    if mountpoint -q "$tmp_path"; then
        umount -l "$tmp_path" 2>/dev/null
    fi
    
    # 2. 只有在确定已经卸载的情况下，才执行强制删除目录
    if [ -d "$tmp_path" ] && ! mountpoint -q "$tmp_path"; then
        rm -rf "$tmp_path" 2>/dev/null
    fi
done
# ====================================================

# --- LED 持久化初始化 ---
init_led_service() {
    if [ ! -f /etc/systemd/system/oec-led.service ]; then
        echo -e "\n${YELLOW_C}>>> 正在初始化灯效持久化服务...${NC}"
        [ ! -f "$LED_CONF" ] && echo "0 1 1" > "$LED_CONF"
        cat << 'EOF' > /usr/local/bin/oec-led
#!/bin/bash
LED_CONF="/etc/oec-led.conf"
[ ! -f "$LED_CONF" ] && exit 0
read R G B < "$LED_CONF"
echo $R > /sys/class/leds/red:status/brightness 2>/dev/null
echo $G > /sys/class/leds/green:status/brightness 2>/dev/null
echo $B > /sys/class/leds/blue:status/brightness 2>/dev/null
EOF
        chmod +x /usr/local/bin/oec-led
        cat << 'EOF' > /etc/systemd/system/oec-led.service
[Unit]
Description=FnOS OEC LED Persistence Service
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/oec-led
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
        systemctl daemon-reload
        systemctl enable oec-led.service >/dev/null 2>&1
        echo -e "${GREEN_C}>>> 服务初始化完成。${NC}"
        sleep 1
    fi
}

set_led() {
    echo $1 > $L_RED 2>/dev/null
    echo $2 > $L_GREEN 2>/dev/null
    echo $3 > $L_BLUE 2>/dev/null
}

# --- 核心状态获取函数 ---
get_status() {
    local root_dev=$(findmnt -n -o SOURCE /)
    if [[ "$root_dev" == *"/dev/sda1"* ]]; then
        CURRENT_ENV="SATA 硬盘系统"
        ENV_CODE="SATA"
    elif [[ "$root_dev" == *"/dev/sdb1"* ]]; then
        CURRENT_ENV="USB 硬盘系统"
        ENV_CODE="USB"
    elif [[ "$root_dev" == *"/dev/mmcblk0p2"* ]]; then
        CURRENT_ENV="eMMC 内置系统"
        ENV_CODE="EMMC"
    else
        CURRENT_ENV="未知系统"
        ENV_CODE="UNKNOWN"
    fi

    local next_root=$(grep -oP 'root=\K[^ ]+' "$BOOT_CONF")
    case "$next_root" in
        "/dev/sda1") NEXT_BOOT="SATA 硬盘系统" ;;
        "/dev/sdb1") NEXT_BOOT="USB 硬盘系统" ;;
        "/dev/mmcblk0p2") NEXT_BOOT="eMMC 内置系统" ;;
        *) NEXT_BOOT="未知 ($next_root)" ;;
    esac

    # 【核心逻辑】：实时找回后台运行的 Web 进程
    local existing_pid=$(lsof -t -i:5680 2>/dev/null | head -n 1)
    if [ -n "$existing_pid" ]; then
        WEB_PID="$existing_pid"
        local local_ip=$(ip addr show | grep -w inet | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -n 1)
        WEB_STATUS="${GREEN_C}运行中 (PID: $WEB_PID) http://$local_ip:5680 ${NC}"
    else
        WEB_PID=""
        WEB_STATUS="${RED_C}未开启${NC}"
    fi
}

# --- 校验目标分区是否存在系统 ---
check_system_exists() {
    local target_p=$1
    local target_name=$2
    if [ ! -b "$target_p" ]; then
        echo -e "${RED_C}错误: 未检测到 $target_name 分区 ($target_p)${NC}"
        echo -e "${YELLOW_C}请检查硬盘是否插入或是否已经进行[初始化分区]。${NC}"
        return 1
    fi
    local tmp_mnt="/mnt/check_sys"
    mkdir -p $tmp_mnt
    mount -o ro "$target_p" $tmp_mnt 2>/dev/null
    if [ $? -ne 0 ]; then
        echo -e "${RED_C}错误: 无法读取 $target_name 分区，可能未格式化。${NC}"
        return 1
    fi
    if [ ! -f "$tmp_mnt/sbin/init" ] && [ ! -f "$tmp_mnt/etc/fstab" ]; then
        umount $tmp_mnt
        echo -e "${RED_C}错误: 在 $target_name 分区中未检测到有效的 Linux 系统文件！${NC}"
        echo -e "${YELLOW_C}请先通过[升级刷入新镜像]功能安装系统。${NC}"
        return 1
    fi
    umount $tmp_mnt
    return 0
}

mac_manager() {
    echo -e "\n${CYAN_C}--- 修改 MAC 地址 ---${NC}"
    local iface=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | head -n 1 | cut -d: -f1)
    [ -z "$iface" ] && iface=$(ls /sys/class/net | grep -v "lo" | head -n 1)
    local conn_name=$(nmcli -t -f NAME,TYPE connection show --active | grep ethernet | head -n 1 | cut -d: -f1)
    [ -z "$conn_name" ] && conn_name="Wired connection 1"
    local current_mac=$(cat /sys/class/net/$iface/address 2>/dev/null)
    echo -e "检测到网卡: $iface | 当前 MAC: $current_mac"
    read -p "请输入新 MAC (格式 00:11:22:33:44:55, 回车取消): " new_mac
    if [ -n "$new_mac" ] && [[ $new_mac =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
        echo -e "${YELLOW_C}正在应用新 MAC 地址...${NC}"
        nmcli connection modify "$conn_name" ethernet.cloned-mac-address "$new_mac"
        nmcli connection down "$conn_name" >/dev/null 2>&1
        nmcli connection up "$conn_name" >/dev/null 2>&1
        echo -e "${GREEN_C}>>> MAC 地址修改成功！网络已重新连接。${NC}"
    fi
    read -p "按回车键返回菜单..." temp
}

ask_reboot() {
    echo -e "\n${GREEN_C}>>> 引导配置修改成功！${NC}"
    echo -e "${YELLOW_C}提示：系统需要重启后才会进入新引导的系统。${NC}"
    read -p "是否立即重启设备? (y/n): " res
    if [[ "$res" == "y" || "$res" == "Y" ]]; then
        echo "正在同步数据并准备重启..."
        sync && reboot
    else
        echo -e "\n${CYAN_C}已取消重启，请稍后手动重启。${NC}"
    fi
}

init_disk() {
    # --- 新增工具检查 ---
    if ! command -v parted &> /dev/null || ! command -v mkfs.ext4 &> /dev/null; then
        echo -e "${RED_C}错误: 请切换sudo -i 或 系统缺少必要工具 (parted 或 e2fsprogs)${NC}"
        echo -e "${YELLOW_C}请先执行: apt update && apt install parted e2fsprogs -y${NC}"
        read -p "按回车返回..." t; return
    fi
    # ------------------
    
    local dev=$1
    local name=$2
    if [ ! -b "$dev" ]; then
        echo -e "${RED_C}错误: 未检测到 $name ($dev)${NC}"
        read -p "按回车返回..." t; return
    fi
    echo -e "${RED_C}!!! 警告: 此操作将清空 $name 上的所有数据 !!!${NC}"
    
    # --- 智能分区大小逻辑 ---
    read -p "请输入分区1(系统区)的大小 (例如 64, 默认 32): " part_size
    if [ -z "$part_size" ]; then
        part_size="32GiB"
    # 如果用户只输入了数字，默认补齐 GiB（最推荐的单位）
    elif [[ "$part_size" =~ ^[0-9]+$ ]]; then
        part_size="${part_size}GiB"
    fi
    # --------------------------

    read -p "确定执行格式化并分区吗? (y/n): " confirm
    if [ "$confirm" == "y" ]; then
        #红灯
        #set_led 1 0 0
        echo -e "${YELLOW_C}正在清理并重新分区...${NC}"
        umount ${dev}* &>/dev/null
        # 强制使用 GPT 分区表
        parted $dev -s mklabel gpt
        # 分区1：起始扇区 2048s (1MB对齐)，结束位置由变量决定
        parted $dev -s mkpart primary btrfs 2048s "$part_size"
        # 分区2：从分区1结束的位置开始，直到磁盘末尾 (100%)
        parted $dev -s mkpart primary ext4 "$part_size" 100%
        sync && sleep 2
        # 格式化数据分区
        mkfs.ext4 -F ${dev}2
        #绿灯
        #set_led 0 1 0
        echo -e "${GREEN_C}$name 初始化成功！(系统区大小: $part_size)${NC}"
    fi
    read -p "按回车键返回菜单..." temp
}

post_write_fix_BAKBAK() {
    local target=$1
    local mnt="/mnt/fn_tmp"
    mkdir -p $mnt && mount $target $mnt
    
    echo -e "${CYAN_C}正在执行扩容与 fstab 局部修正...${NC}"
    btrfs filesystem resize max $mnt &>/dev/null

    # 1. 备份原文件
    cp "$mnt/etc/fstab" "$mnt/etc/fstab.bak"

    # 2. 局部替换根分区设备 (仅修改开头的路径)
    # 逻辑：匹配所有以 / 为挂载点的行，将其开头的设备名(无论是UUID还是/dev/xxx)替换为 $target
    # 使用 [[:space:]] 确保匹配的是独立的挂载点列
    sed -i "s|^[^[:space:]]\+[[:space:]]\+\/[[:space:]]|$target         /         |" "$mnt/etc/fstab"

    # 3. 局部替换 /boot 分区设备 (强制指向 eMMC 第一分区)
    sed -i "s|^[^[:space:]]\+[[:space:]]\+\/boot[[:space:]]|/dev/mmcblk0p1   /boot   |" "$mnt/etc/fstab"

    umount $mnt && sync
    echo -e "${GREEN_C}fstab 修正完成 (已保留原参数)。${NC}"
}

post_write_fix() {
    local target=$1
    local mnt="/mnt/fn_tmp"
    
    echo -e "${CYAN_C}>>> 正在清理冲突并准备修正系统配置...${NC}"
    
    # 1. 强制卸载目标分区的所有挂载点（预防 I/O 错误）
    findmnt -n -o TARGET "$target" | xargs -r umount -l 2>/dev/null
    sync && sleep 2

    # 2. 刷新内核分区表识别
    partprobe "$(echo $target | sed 's/p[0-9]$//')" 2>/dev/null
    sleep 1

    # 3. 重新挂载
    mkdir -p "$mnt"
    mount "$target" "$mnt" || { echo -e "${RED_C}挂载失败，尝试重试...${NC}"; sleep 2; mount "$target" "$mnt"; }

    if mountpoint -q "$mnt"; then
        # 4. 执行分区扩容
        echo -e "${CYAN_C}正在执行分区扩容...${NC}"
        btrfs filesystem resize max "$mnt" &>/dev/null

        # 5. 查找 fstab (兼容标准路径与 Btrfs 子卷路径)
        local fstab_file=""
        if [ -f "$mnt/etc/fstab" ]; then
            fstab_file="$mnt/etc/fstab"
        elif [ -f "$mnt/@/etc/fstab" ]; then
            fstab_file="$mnt/@/etc/fstab"
        fi

        if [ -n "$fstab_file" ]; then
            echo -e "${CYAN_C}检测到 fstab 路径: $fstab_file，正在修正...${NC}"
            cp "$fstab_file" "${fstab_file}.bak"
            
            # 修正根分区和 boot 分区路径 (保持格式对齐)
            sed -i "s|^[^[:space:]]\+[[:space:]]\+\/[[:space:]]|$target         /         |" "$fstab_file"
            sed -i "s|^[^[:space:]]\+[[:space:]]\+\/boot[[:space:]]|/dev/mmcblk0p1   /boot   |" "$fstab_file"
            
            echo -e "${GREEN_C}fstab 修正完成。${NC}"
            
            # --- 核心修复：重载 systemd 消除 "fstab modified" 警告 ---
            sync
            systemctl daemon-reload >/dev/null 2>&1
        else
            echo -e "${RED_C}错误：在挂载点中未找到 etc/fstab${NC}"
        fi

        # 6. 安全卸载并同步
        sync && umount -l "$mnt"
        [ -d "$mnt" ] && rm -rf "$mnt"
    else
        echo -e "${RED_C}错误：无法挂载目标分区 $target，请重启后手动操作。${NC}"
    fi
}

# --- 通用系统热克隆逻辑 ---
clone_system() {
    local src_dev="/"
    local target_dev=$1
    local target_name=$2
    local mnt="/mnt/clone_tmp"

    # 安全检查：防止自己克隆给自己
    local current_root=$(findmnt -n -o SOURCE /)
    if [[ "$current_root" == *"$target_dev"* ]]; then
        echo -e "${RED_C}错误: 目标分区 $target_dev 是当前正在运行的系统，无法克隆！${NC}"
        read -p "按回车返回..." t; return
    fi

    echo -e "${YELLOW_C}即将开始系统热克隆: [当前系统] -> $target_name ($target_dev)...${NC}"
    echo -e "${RED_C}警告: 目标分区的数据将被彻底覆盖！${NC}"
    read -p "确认开始吗? (y/n): " confirm
    if [ "$confirm" != "y" ]; then return; fi

    #mkdir -p $mnt && mount $target_dev $mnt 2>/dev/null
    #if [ $? -ne 0 ]; then
    #    echo -e "${RED_C}错误: 无法挂载目标分区！${NC}"
    #    read -p "按回车返回..." t; return
    #fi
    echo -e "${YELLOW_C}正在强制刷新并准备目标分区...${NC}"
    
    # 1. 尝试卸载所有相关占用
    umount -l "$target_dev" 2>/dev/null
    umount -l "$mnt" 2>/dev/null

    # 2. 【关键修复】：强制抹除旧文件系统头信息并格式化为 Btrfs
    # 这一步能解决 "failed to recognize exfat type" 的报错
    echo -e "${CYAN_C}正在执行强制格式化 (抹除旧残留)...${NC}"
    mkfs.btrfs -f "$target_dev" &>/dev/null
    
    # 3. 强制内核重读分区表并扫描
    partprobe "$target_dev" 2>/dev/null
    btrfs device scan 2>/dev/null
    sync && sleep 2

    # 4. 执行正式挂载
    #mkdir -p "$mnt"
    #mount -t btrfs "$target_dev" "$mnt" 2>/dev/null
    # 4. 【核心修复】：执行挂载时直接开启透明压缩
    mkdir -p "$mnt"
    # 增加 -o compress=zstd:1 挂载选项，这是确保压缩生效的最稳妥办法
    mount -t btrfs -o compress=zstd:1 "$target_dev" "$mnt" 2>/dev/null
    
    if [ $? -ne 0 ]; then
        local error_msg=$(dmesg | tail -n 1)
        echo -e "${RED_C}错误: 依然无法挂载目标分区 $target_dev${NC}"
        echo -e "${YELLOW_C}最终建议：${NC}"
        echo -e " - 内核报错: $error_msg"
        echo -e " - 请尝试物理拔插硬盘后再运行 [初始化分区]${NC}"
        read -p "按回车返回..." t; return
    fi

    # 【核心加入】：开启 Btrfs 透明压缩
    echo -e "${CYAN_C}正在开启 Btrfs 透明压缩...${NC}"
    btrfs property set "$mnt" compression zstd:1 2>/dev/null

    #set_led 1 1 0  # 黄灯
    echo -e "${PURPLE_C}正在同步文件 (rsync)...${NC}"

    # 执行同步

    #rsync -av --exclude=/proc --exclude=/sys --exclude=/dev --exclude=/mnt --exclude=/tmp --exclude=/boot --exclude=/media --exclude=/vol* / /$mnt/

    # 修正排除语法，增加 -H 保留硬链接，-X 保留扩展属性（对某些权限很重要）
    #rsync -avHX --delete \
    #    --exclude=/proc --exclude=/sys --exclude=/dev \
    #    --exclude=/mnt --exclude=/tmp --exclude=/boot \
    #    --exclude=/media --exclude=/vol* --exclude=/lost+found \
    #    / $mnt/
    # 【核心修复】：执行同步
    # 增加 -x: 绝对不跨越文件系统（防止递归拷贝挂载点）
    # 增加 -S: 处理稀疏文件（防止日志等文件膨胀）
    # 修正 --exclude: 使用 /* 确保目标盘保留空的挂载点目录
    rsync -axHAXS --info=progress2 --numeric-ids --delete \
        --exclude={"/proc/*","/sys/*","/dev/*","/mnt/*","/tmp/*","/boot/*","/media/*","/vol*/*","/lost+found"} \
        / "$mnt/"

    # 获取 rsync 的退出状态
    local res=$?

    # 【核心修复】：允许 0(完美), 23(部分文件属性报错), 24(文件在传输时消失)
    if [ $res -eq 0 ] || [ $res -eq 23 ] || [ $res -eq 24 ]; then
        sync  # 确保所有缓存数据已提交到磁盘
        
        # --- 增加交互判断：是否进一步优化 ---
        echo -e "${YELLOW_C}>>> 是否执行进一步存储优化(收缩空间)? [y/N] (10秒后自动跳过): ${NC}"
        read -t 10 -n 1 opt_choice
        echo "" # 换行

        if [[ "$opt_choice" == "y" || "$opt_choice" == "Y" ]]; then
            echo -e "${CYAN_C}正在执行最终存储优化 (收缩空间)...${NC}"
            echo -e "${CYAN_C}这可能需要几分钟, 完成后系统物理占用将降至最低...${NC}"
            # 执行深度碎片整理和压缩
            btrfs filesystem defragment -r -czstd "$mnt" &>/dev/null
            echo -e "${GREEN_C}存储优化已完成！${NC}"
        else
            echo -e "${BLUE_C}已跳过深度优化。由于已开启透明压缩，当前占用已处于较低水平。${NC}"
        fi
        # -----------------------------------
        
        umount $mnt
        post_write_fix $target_dev
        #set_led 0 1 0 #绿灯
        echo -e "${GREEN_C}>>> 系统克隆完成！(注：已忽略非致命的临时文件报错)${NC}"
        echo -e "${YELLOW_C}返回菜单后 切换引导重启后即可进入此系统${NC}"
    else
        #set_led 1 0 0 #红灯
        echo -e "${RED_C}>>> 克隆失败！rsync 错误码: $res${NC}"
        umount $mnt
    fi
    read -p "按回车键返回菜单..." temp
}

upgrade_logic() {
    local target_dev=$1; local target_name=$2; local mode=$3
    local current_img_dir="$IMG_DIR"
    local mounted=0

    # --- 1. 挂载与探测逻辑 ---
    # 首先检查默认目录是否存在镜像
    if [ ! -d "$current_img_dir" ] || [ -z "$(ls $current_img_dir/*.img 2>/dev/null)" ]; then
        echo -e "${YELLOW_C}提示：默认目录未找到镜像，尝试探测硬盘数据分区...${NC}"
        
        # 根据选择的模式判断数据分区位置（SATA/eMMC 对应 sda2, USB 对应 sdb2）
        local data_p="/dev/sda2"
        [[ "$mode" == "USB" ]] && data_p="/dev/sdb2"
        
        if [ -b "$data_p" ]; then
            read -p "检测到数据分区 $data_p，是否尝试挂载寻找镜像? (y/n): " try_mnt
            if [[ "$try_mnt" == "y" || "$try_mnt" == "Y" ]]; then
                current_img_dir="/mnt/data_tmp"
                mkdir -p "$current_img_dir"
                
                # 1. 无论之前有没有挂载，先尝试强制以读写模式重新挂载并赋予全掩码
                mount -o remount,rw,umask=000 "$data_p" "$current_img_dir" 2>/dev/null || \
                mount -o rw,umask=000 "$data_p" "$current_img_dir" 2>/dev/null || \
                mount "$data_p" "$current_img_dir" 2>/dev/null

                # 2. 强行刷新一次目录权限
                chmod 777 "$current_img_dir" 2>/dev/null
                mounted=1

                # 3. 强制显示路径和上传指引 (不再受 mountpoint 判断限制)
                echo -e "\n${GREEN_C}>>> 权限补丁已执行！${NC}"
                echo -e "${CYAN_C}数据盘挂载路径：${YELLOW_C} $current_img_dir ${NC}"
                echo -e "${CYAN_C}读写权限状态：${GREEN_C} 强制开启 (777) ${NC}"
                echo -e "${BLUE_C}------------------------------------------------${NC}"
                echo -e "${PURPLE_C}请现在通过 WinSCP或FinalShell或WEB 上传 .img 镜像文件到该目录${NC}"
                
                # 进入等待循环
                while true; do
                    read -p "--- 是否已完成上传？(y:开始扫描 / n:退出): " confirm_upload
                    if [[ "$confirm_upload" == "y" || "$confirm_confirm" == "Y" ]]; then
                        if [ -z "$(ls $current_img_dir/*.img 2>/dev/null)" ]; then
                            echo -e "${RED_C}扫描结果：未发现 .img 文件！请检查文件名或上传路径。${NC}"
                        else
                            echo -e "${GREEN_C}扫描成功！已发现镜像。${NC}"
                            break
                        fi
                    elif [[ "$confirm_upload" == "n" ]]; then
                        return
                    fi
                done
            fi
        fi
    fi

    # --- 2. 核心交互循环：强制拦截并等待上传 ---
    while true; do
        # 实时扫描当前目录下的所有 .img 文件
        local imgs=($(ls $current_img_dir/*.img 2>/dev/null))
        
        if [ ${#imgs[@]} -gt 0 ]; then
            echo -e "\n${GREEN_C}>>> 在 [ $current_img_dir ] 发现以下镜像：${NC}"
            
            # 使用 select 让用户选择镜像
            # 将提示语设为空，防止干扰
            PS3="请输入镜像编号 (输入 q 退出): "
            select img in "${imgs[@]}"; do
                if [ "$REPLY" == "q" ]; then
                    [ $mounted -eq 1 ] && umount -l "$current_img_dir" 2>/dev/null
                    return
                elif [ -n "$img" ]; then
                    # --- 3. 运行环境安全检查 ---
                    get_status # 刷新当前 ENV_CODE
                    if [ "$ENV_CODE" == "$mode" ]; then
                        echo -e "${RED_C}错误: 无法在运行中的系统升级当前系统！${NC}"
                        echo -e "${YELLOW_C}请切换到其它系统后再刷入 $target_name。${NC}"
                        read -p "按回车返回..." t
                        [ $mounted -eq 1 ] && umount -l "$current_img_dir" 2>/dev/null
                        return
                    fi

                    # --- 4. 正式开始刷入流程 ---
                    #set_led 1 0 1 # 紫灯：工作状态
                    
                    echo -e "\n${YELLOW_C}正在预清理目标设备挂载状态，防止写入冲突...${NC}"
                    # 强制解除目标设备的所有挂载点
                    findmnt -n -o TARGET "$target_dev" | xargs -r umount -l 2>/dev/null
                    sync && sleep 1

                    echo -e "\n${PURPLE_C}开始刷入，这通常需要 1-2 分钟，请勿断电...${NC}"
                    # 创建回环设备并 dd 镜像的第二个分区（FnOS 系统分区）
                    local loop_dev=$(losetup -fP --show "$img")
                    if [ -z "$loop_dev" ]; then
                        echo -e "${RED_C}错误：无法创建回环设备！${NC}"; return
                    fi
                    
                    dd if="${loop_dev}p2" of="$target_dev" bs=4M status=progress conv=fsync
                    
                    # 清理回环设备
                    losetup -D
                    
                    # 执行分区扩容与 fstab 修正 (调用你已有的修复函数)
                    post_write_fix "$target_dev"
                    
                    #set_led 0 1 0 # 绿灯：完成
                    [ $mounted -eq 1 ] && umount -l "$current_img_dir" 2>/dev/null
                    
                    echo -e "\n${GREEN_C}>>> 系统升级完成！${NC}"
                    echo -e "${YELLOW_C}返回菜单后 切换引导重启后即可进入此系统${NC}"
                    read -p "按回车键返回菜单..." temp
                    return # 任务完成，退出函数
                else
                    echo -e "${RED_C}无效选择，请重新输入编号。${NC}"
                fi
            done
        else
            # --- 5. 容错处理：没找到镜像时的交互菜单 ---
            echo -e "\n${RED_C}当前目录 [ $current_img_dir ] 未找到 .img 镜像文件！${NC}"
            echo -e "${CYAN_C}建议：${NC}"
            echo -e " 1. [y] 我已通过 WinSCP/Web 上传完镜像，点此【重新扫描】"
            echo -e " 2. [p] 手动输入镜像所在的【其它文件夹路径】"
            echo -e " 3. [n] 放弃升级，返回主菜单"
            echo -e "${BLUE_C}------------------------------------------------${NC}"
            read -p "请输入选择 [y/p/n]: " user_opt

            case $user_opt in
                y|Y) continue ;; # 跳回循环开头重新执行 ls
                p|P) 
                    read -p "请输入完整路径 (例如 /vol1/1000/down): " custom_path
                    if [ -d "$custom_path" ]; then
                        current_img_dir="$custom_path"
                    else
                        echo -e "${RED_C}路径无效或不存在！${NC}"
                    fi
                    ;;
                n|N)
                    [ $mounted -eq 1 ] && umount -l "$current_img_dir" 2>/dev/null
                    return
                    ;;
                *) echo "输入无效" ;;
            esac
        fi
    done
}

# --- Web 上传控制逻辑 ---
web_upload_toggle() {
    local mode=$1
    local py_script="/tmp/xxnas_file.py"

    if [ "$mode" == "on" ]; then
        if [ -n "$WEB_PID" ]; then
            echo -e "${YELLOW_C}提示：Web 上传服务已在运行中${NC}"; sleep 1; return
        fi

        # 1. 校验本地脚本
        if [ -f "$py_script" ] && [ -s "$py_script" ]; then
            echo -e "${GREEN_C}>>> 检测到本地已存在服务脚本，正在启动...${NC}"
        else
            echo -e "${YELLOW_C}>>> 正在创建WEB上传服务脚本...${NC}"
            # 增加时间戳防止缓存
            local timestamp=$(date +%s)
            curl -s -L --retry 3 -o "$py_script" "https://us1.vvvvvv.de5.net/sh/xxnas_file.py?t=${timestamp}"
            if [ $? -ne 0 ] || [ ! -f "$py_script" ]; then
                echo -e "${RED_C}错误：脚本创建失败，请检查网络或 /tmp 写入权限！${NC}"
                sleep 1; return
            fi
            chmod +x "$py_script"
        fi

        # 2. 获取端口号
        local web_port
        read -p "请输入服务端口 (直接回车默认 5680): " web_port
        [ -z "$web_port" ] && web_port="5680"

        # 校验端口是否被占用
        if ss -tuln | grep -q ":$web_port "; then
            echo -e "${RED_C}错误：端口 $web_port 已被占用，请更换端口！${NC}"
            sleep 1; return
        fi

        # 3. 设置访问密码
        local web_pass
        read -p "设置访问密码 (直接回车无密码): " web_pass

        # 4. 获取本机 IP
        local local_ip=$(ip addr show | grep -w inet | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -n 1)

        # 5. 启动服务逻辑
        if [ -n "$web_pass" ]; then
            python3 "$py_script" "$web_port" "$web_pass" >/dev/null 2>&1 &
        else
            python3 "$py_script" "$web_port" >/dev/null 2>&1 &
        fi
        WEB_PID=$!; 
        echo -e "${GREEN_C}WEB上传服务已启动 PID: $WEB_PID (端口: $web_port)${NC}"

        if [ -n "$web_pass" ]; then
            echo -e "${GREEN_C}浏览器访问 http://$local_ip:$web_port (账号admin 密码$web_pass)${NC}"
        else
            echo -e "${GREEN_C}浏览器访问 http://$local_ip:$web_port ${NC}"
        fi
        
    else
        # 关闭服务
        if [ -n "$WEB_PID" ]; then
            kill $WEB_PID 2>/dev/null
            WEB_PID=""
            WEB_STATUS="${RED_C}未开启${NC}"
            echo -e "${YELLOW_C}Web上传服务已关闭。${NC}"
        else
            echo -e "${YELLOW_C}Web上传服务当前未运行。${NC}"
        fi
    fi
    sleep 2
}

# ====================================================
# 【作者其它工具管理逻辑】
# ====================================================
extra_tools_manager() {
    clear
    echo -e "${BLUE_C}================================================${NC}"
    echo -e "${CYAN_C}             作者其它工具列表${NC}"
    echo -e "${BLUE_C}================================================${NC}"
    
    local i=1
    for item in "${TOOLS_LIST[@]}"; do
        local name=$(echo "$item" | cut -d'|' -f1)
        echo -e "  $i. $name"
        echo -e "${BLUE_C}------------------------------------------------${NC}"
        ((i++))
    done
    
    echo -e "  d. 清理本地工具列表(下次执行将会自动更新)"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "  q. 返回主菜单"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    
    read -p " 请输入选择: " t_choice
    [[ "$t_choice" == "q" ]] && return

    # --- 新增：清理逻辑 ---
    if [[ "$t_choice" == "d" ]]; then
        echo -e "\n${YELLOW_C}正在清理本地缓存脚本...${NC}"
        for item in "${TOOLS_LIST[@]}"; do
            local t_file=$(echo "$item" | cut -d'|' -f2)
            if [ -f "/tmp/$t_file" ]; then
                rm -f "/tmp/$t_file"
                echo -e "  已删除: $t_file"
            fi
        done
        echo -e "${GREEN_C}清理完成！${NC}"
        sleep 1
        return
    fi

    # 校验输入是否为数字
    if [[ "$t_choice" =~ ^[0-9]+$ ]] && [ "$t_choice" -ge 1 ] && [ "$t_choice" -lt "$i" ]; then
        local target_item="${TOOLS_LIST[$((t_choice-1))]}"
        local t_name=$(echo "$target_item" | cut -d'|' -f1)
        local t_file=$(echo "$target_item" | cut -d'|' -f2)
        local t_url=$(echo "$target_item" | cut -d'|' -f3)
        local t_tip=$(echo "$target_item" | cut -d'|' -f4)
        local t_path="/tmp/$t_file"
        local timestamp=$(date +%s)

        # --- 新增：未上线提示判断 ---
        if [ -n "$t_tip" ]; then
            echo -e "\n${YELLOW_C}提示: $t_tip${NC}"
            read -p "按回车返回..." t
            return
        fi

        echo -e "\n${YELLOW_C}>>> 正在准备工具: $t_name ...${NC}"
        
        if [ ! -f "$t_path" ]; then
            echo -e "${CYAN_C}正在从远程获取脚本...${NC}"
            #curl -s -L --retry 3 -o "$t_path" "$t_url"
            curl -s -L --retry 3 -o "$t_path" "${t_url}?t=${timestamp}"
            if [ $? -ne 0 ] || [ ! -s "$t_path" ]; then
                echo -e "${RED_C}错误: 下载失败，请检查网络！${NC}"
                read -p "按回车返回..." t; return
            fi
            chmod +x "$t_path"
        fi

        echo -e "${GREEN_C}>>> 正在启动 $t_name ...${NC}"
        sleep 1
        bash "$t_path"

        # 【核心修改点 2】：增加阻塞提示，防止手机端直接 clear 清屏
        echo -e "\n${YELLOW_C}------------------------------------------------${NC}"
        echo -e "${GREEN_C}>>> $t_name 运行已结束${NC}"
        read -p "请按[回车键]返回其它工具列表..." temp
    else
        echo -e "${RED_C}输入无效！${NC}"
        sleep 1
    fi
}

# --- 更新脚本自身 ---
update_self() {
    # 生成随机时间戳防止缓存
    local timestamp=$(date +%s)
    echo -e "\n${YELLOW_C}>>> 正在检查脚本更新...${NC}"
    local tmp_file="/tmp/xxfn_new.sh"
    
    # 在 URL 后面拼接时间戳
    curl -s -L --retry 3 -o "$tmp_file" "${UPDATE_URL}?t=${timestamp}"
    
    if [ $? -eq 0 ] && [ -s "$tmp_file" ]; then
        if grep -q "#!/bin/bash" "$tmp_file"; then
            mv "$tmp_file" "$0"
            chmod +x "$0"
            echo -e "${GREEN_C}>>> 脚本已更新完成！正在重新启动...${NC}"
            sleep 1
            exec bash "$0"
        else
            echo -e "${RED_C}错误: 下载的文件内容不完整或无效！${NC}"
        fi
    else
        echo -e "${RED_C}错误: 下载更新失败，请检查网络！${NC}"
    fi
    read -p "按回车返回..." t
}

while true; do
    get_status
    clear
    echo -e "${BLUE_C}================================================${NC}"
    echo -e "${BLUE_C}   xxfn-tool v26.01.27-2305 | aminsire@foxmail.com   ${NC}"
    echo -e "${BLUE_C}   适用设备：FnOS ARM (rk3566 /  等设备)        ${NC}"
    echo -e "${BLUE_C}   核心功能：三系统切换、系统升级、LED灯、MAC修改  ${NC}"
    echo -e "${BLUE_C}   脚本工具无任何依赖，干净纯净，可放心使用        ${NC}"
    echo -e "${RED_C}   脚本仅供技术交流测试，出现问题由使用者自行负责，数据无价 请多备份数据${NC}"
    echo -e "${BLUE_C}================================================${NC}"
    echo -e " 当前运行: ${GREEN_C}$CURRENT_ENV${NC}"
    
    if [ "$CURRENT_ENV" != "$NEXT_BOOT" ]; then
        echo -e " 下次启动: ${YELLOW_C}$NEXT_BOOT (待重启)${NC}"
    else
        echo -e " 下次启动: ${CYAN_C}$NEXT_BOOT${NC}"
    fi

    echo -e " 镜像目录: ${CYAN_C}$IMG_DIR${NC}"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e " 飞牛固件最新版本: 1.1.20, arm刷机包${GREEN_C}公测版${NC}"
    echo -e " 飞牛固件/fpk应用发布页，问题反馈: https://us1.vvvvvv.de5.net/soft"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "${YELLOW_C}--------- QQ群：760176092 暗号xxfn-tool ---------${NC}"
    echo -e "注意: 切换引导需要目标硬盘分区已经安装好了系统"
    echo -e "刷机方式：首次使用请先执行7初始化[分区]再执行4升级刷入新镜像(根据提示上传镜像.img)"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "${YELLOW_C}切换引导 优先用18.其他工具里面的6.全能引导切换工具(兼容更多设备)${NC}"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "  1. 切换引导：从 [SATA 硬盘] 启动 |  4. 升级 刷入镜像到 [SATA 硬盘 sda1]"
    echo -e "  2. 切换引导：从 [eMMC 内置] 启动 |  5. 升级 刷入镜像到 [eMMC 内置 mmcblk0p2]"
    echo -e "  3. 切换引导：从 [USB   硬盘] 启动 |  6. 升级 刷入镜像到 [USB   硬盘 sdb1]"
    echo -e "  提示. USB启动有个别设备无法启动，慎用，用USB先lsblk命令查看USB设备确保是sdb"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "  7. 初始化[分区] SATA 硬盘       |  8. 初始化[分区] USB 硬盘"
    echo -e "------------------------------------------------"
    echo -e "  9. LED 灯效管理                 |  10. 修改 MAC 地址"
    echo -e "------------------------------------------------"
    echo -e "克隆方式：直接克隆磁盘方式，先执行7或8分区，再执行13或14克隆"
    echo -e "------------------------------------------------"
    echo -e "  13. 克隆 eMMC系统 到 [SATA硬盘 sda1]|  14. 克隆 eMMC系统 到 [USB硬盘 sdb1]"
    echo -e "  15. 克隆 当前[${GREEN_C}$CURRENT_ENV${NC}] 到 [eMMC内置系统 mmcblk0p2]  "
    echo -e "------------------------------------------------"
    echo -e "  WEB文件上传: $WEB_STATUS"
    echo -e "  16. 开启 Web 上传               |  17. 关闭 Web 上传"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "  ${YELLOW_C}18. 作者其它工具(不定时更新)${NC}                |  ${GREEN_C}19. 更新本工具(不定时更新)${NC}"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    echo -e "  11. 重启设备                    |  12. 退出脚本"
    echo -e "${BLUE_C}------------------------------------------------${NC}"
    read -p " 请输入数字选择功能 [1-19]: " choice
    case $choice in
        1) 
            check_system_exists "/dev/sda1" "SATA 硬盘"
            if [ $? -eq 0 ]; then
                # 优化点：使用正则表达式精准替换 append 行中的 root 参数，支持 /dev/ 和 UUID 格式
                sed -i '/^[[:space:]]*append/s|root=[^[:space:]]*|root=/dev/sda1|' $BOOT_CONF
                # 校验是否修改成功
                if grep -q "root=/dev/sda1" "$BOOT_CONF"; then
                    sync && ask_reboot 
                else
                    echo -e "${RED_C}错误: 引导配置文件修改失败，请检查文件权限！${NC}"
                    read -p "按回车返回..." t
                fi
            else
                read -p "按回车返回菜单..." t
            fi
            ;;
        2) 
            check_system_exists "/dev/mmcblk0p2" "eMMC 内置"
            if [ $? -eq 0 ]; then
                sed -i '/^[[:space:]]*append/s|root=[^[:space:]]*|root=/dev/mmcblk0p2|' $BOOT_CONF
                # 校验是否修改成功
                if grep -q "root=/dev/mmcblk0p2" "$BOOT_CONF"; then
                    sync && ask_reboot 
                else
                    echo -e "${RED_C}错误: 引导配置文件修改失败，请检查文件权限！${NC}"
                    read -p "按回车返回..." t
                fi
            else
                read -p "按回车返回菜单..." t
            fi
            ;;
        3) 
            check_system_exists "/dev/sdb1" "USB 硬盘"
            if [ $? -eq 0 ]; then
                sed -i '/^[[:space:]]*append/s|root=[^[:space:]]*|root=/dev/sdb1|' $BOOT_CONF
                # 校验是否修改成功
                if grep -q "root=/dev/sdb1" "$BOOT_CONF"; then
                    sync && ask_reboot 
                else
                    echo -e "${RED_C}错误: 引导配置文件修改失败，请检查文件权限！${NC}"
                    read -p "按回车返回..." t
                fi
            else
                read -p "按回车返回菜单..." t
            fi
            ;;
        4) upgrade_logic "/dev/sda1" "SATA 硬盘" "SATA" ;;
        5) upgrade_logic "/dev/mmcblk0p2" "eMMC 内置" "EMMC" ;;
        6) upgrade_logic "/dev/sdb1" "USB 硬盘" "USB" ;;
        7) init_disk "/dev/sda" "SATA 硬盘" ;;
        8) init_disk "/dev/sdb" "USB 硬盘" ;;
        9) init_led_service; 
           echo -e "\n${CYAN_C}--- LED 灯效控制 ---${NC}"
           echo -e "0:全灭 1:红 2:绿 3:蓝 4:黄 5:紫 6:青 7:白 11:跑马灯${NC}"
           read -p "请输入颜色编号: " lm
           case $lm in
               11) 
                   echo -e "${YELLOW_C}跑马灯已开启，按 Ctrl+C 立即停止...${NC}"
                   (
                       trap "set_led 0 1 1; exit" SIGINT SIGTERM
                       while true; do
                           for c in "1 0 0" "0 1 0" "0 0 1"; do
                               set_led $c
                               sleep 0.4
                           done
                       done
                   ) & 
                   led_pid=$!
                   trap "kill $led_pid 2>/dev/null; trap - SIGINT; return" SIGINT
                   wait $led_pid 2>/dev/null
                   init_led_service >/dev/null 2>&1
                   echo -e "\n${GREEN_C}>>> 跑马灯已停止。${NC}"
                   sleep 1
                   ;;
               *) 
                   vals=$(echo $lm | sed 's/0/0 0 0/;s/1/1 0 0/;s/2/0 1 0/;s/3/0 0 1/;s/4/1 1 0/;s/5/1 0 1/;s/6/0 1 1/;s/7/1 1 1/')
                   set_led $vals
                   echo "$vals" > "$LED_CONF"
                   echo -e "${GREEN_C}灯效保存成功。${NC}"
                   sleep 1 
                   ;;
           esac ;;
        10) mac_manager ;;
        11) sync; reboot ;;
        12) exit 0 ;;
        13) clone_system "/dev/sda1" "SATA 硬盘" ;;
        14) clone_system "/dev/sdb1" "USB 硬盘" ;;
        15) clone_system "/dev/mmcblk0p2" "eMMC 内置" ;;
        16) web_upload_toggle "on" ;;
        17) web_upload_toggle "off" ;;
        18) extra_tools_manager ;;
        19) update_self ;;
        *) echo "无效选择"; sleep 1 ;;
    esac
done
# aminsire@foxmail.com