#!/bin/sh


# 1. Bash scripts are not supported in busybox
# 2. There is no lsblk command in the busybox environment, only blkid command
# 3. In the busybox environment, blkid does not support additional parameters, only blkid or blkid [device]
# 4. In POSIX sh, 'declare' and 'local' is undefined
# 5. grep supports -E parameters in busybox environment, not -P parameters

VERSION="1.0.0"

show_version() {
    echo "$VERSION"
}

# Logging support: when --kmsg is passed, log messages are written to
# /dev/kmsg (the kernel ring buffer) so they can be retrieved via dmesg
# after boot.  Without --kmsg, log messages go to stderr (console).
KMSG=false

log_msg() {
    if $KMSG && [ -w /dev/kmsg ]; then
        echo "<6>deepin-immutable-mount-root: $*" > /dev/kmsg
    else
        echo "deepin-immutable-mount-root: INFO: $*" >&2
    fi
}

log_warn() {
    if $KMSG && [ -w /dev/kmsg ]; then
        echo "<4>deepin-immutable-mount-root: $*" > /dev/kmsg
    else
        echo "deepin-immutable-mount-root: WARN: $*" >&2
    fi
}

log_err() {
    if $KMSG && [ -w /dev/kmsg ]; then
        echo "<3>deepin-immutable-mount-root: $*" > /dev/kmsg
    else
        echo "deepin-immutable-mount-root: ERR: $*" >&2
    fi
}

get_fstype() {
    local DEVICE
    DEVICE=$1
    local type_val=$(blkid "$DEVICE" | grep -oE 'TYPE="[^"]+"')
    type_val=${type_val#TYPE=\"}
    type_val=${type_val%\"}
    echo $type_val
}

resolve_device() {
    DEV="$1"

	case "$DEV" in
	LABEL=* | UUID=* | PARTLABEL=* | PARTUUID=*)
		# For disk encryption, take the last result from blkid output for the same disk
		DEV=$(blkid -t "${DEV}" -o device | tail -n 1) || return 1
		;;
	esac
	[ -e "$DEV" ] && echo "$DEV"
}

# Add a function to wait for device using udevadm
wait_for_device() {
    local device=$1
    local timeout=10
    local dev_path=""

    case "$device" in
    LABEL=*)
        dev_path="/dev/disk/by-label/${device#LABEL=}"
        ;;
    UUID=*)
        dev_path="/dev/disk/by-uuid/${device#UUID=}"
        ;;
    PARTLABEL=*)
        dev_path="/dev/disk/by-partlabel/${device#PARTLABEL=}"
        ;;
    PARTUUID=*)
        dev_path="/dev/disk/by-partuuid/${device#PARTUUID=}"
        ;;
    *)
        # For direct device paths
        dev_path="$device"
        ;;
    esac

    if [ -n "$dev_path" ] && [ ! -e "$dev_path" ]; then
        log_msg "waiting for device $device to appear"
        if ! udevadm wait --timeout=$timeout "$dev_path" >/dev/null 2>&1; then
            log_err "timed out waiting for device $device ($dev_path)"
            return 1
        fi
    fi
}

try_get_ostree() {
    local rootmnt
    rootmnt=$1
    # get ostree from cmdline
    local OSTREE BOOT_IMAGE
    for param in $(cat /proc/cmdline)
    do
        case "${param}" in
        ostree=*)
            OSTREE="${param#ostree=}"
            ;;
        BOOT_IMAGE=*)
            BOOT_IMAGE="${param#BOOT_IMAGE=}"
            ;;
        esac
    done

    # If OSTREE is set and is not "auto", use it directly
    if [ -n "$OSTREE" ] && [ "$OSTREE" != "auto" ] ;then
        echo $OSTREE
        return
    fi

    # If BOOT_IMAGE exists and matches the required format, extract the hash value
    if [ -n "$BOOT_IMAGE" ]; then
        # Check if BOOT_IMAGE matches the format /boot/immutable/vmlinuz-*
        if echo "$BOOT_IMAGE" | grep -q "^/boot/immutable/.*/vmlinuz-"; then
            local deploy_id
            deploy_id=$(echo "$BOOT_IMAGE" | sed -E 's|^/boot/immutable/([^/]+)(\.[0-9]+)/vmlinuz-.*|\1\2|')
            if [ -n "$deploy_id" ]; then
                OSTREE="/ostree/data/$deploy_id/checkout"
                echo $OSTREE
                return
            fi
        fi
    fi

    # get ostree from status.list
    if [ -f $rootmnt/persistent/ostree/data/status.list ];then
        local deploy_id=$(cat $rootmnt/persistent/ostree/data/status.list| head -n 1)
        echo "/ostree/data/$deploy_id/checkout"
    else
        echo ""
    fi
}

is_mountpoint() {
    path=$1
    mount_infos=$(grep -E "[0-9]{1,2}:[0-9]{1,2}[[:space:]]/[^[:space:]]*[[:space:]]$path[[:space:]]" /proc/self/mountinfo || echo "")
    if [ -z "$mount_infos" ];then
        return 1
    fi
    return 0
}

# Return 0 if $path should be replaced before creating a directory:
# any symlink (even one pointing to a directory, which -d would follow
# and misclassify) or an existing non-directory entry.  A dangling
# symlink is caught by -L directly.  Returns 1 for a real directory or
# a non-existent path.
is_non_directory() {
    local path=$1
    [ -L "$path" ] || { [ -e "$path" ] && [ ! -d "$path" ]; }
}

recursive_umount() {
    local rootmnt=$1

    # Get all mount point information and filter out those under /rootmnt
    mount_points=$(grep -E "[0-9]{1,2}:[0-9]{1,2}[[:space:]]/[^[:space:]]*[[:space:]]$rootmnt(/[^[:space:]]*)?[[:space:]]" /proc/self/mountinfo | awk '{print $5}')

    # Remove duplicate mount points
    mount_points=$(echo "$mount_points" | sort -u)

    # Sort mount points by depth to ensure child mounts are unmounted first
    sorted_mount_points=$(echo "$mount_points" | awk -F'/' '{print NF, $0}' | sort -rn | awk '{print $2}')
    log_msg "mount points: $sorted_mount_points"

    # Unmount each mount point
    for mount_point in $sorted_mount_points; do
        while is_mountpoint "$mount_point"; do
            log_msg "unmounting $mount_point"
            umount "$mount_point"
        done
    done
}

run_hooks() {
    if [ "$MOUNT_ROOT_HOOK_ENABLED" != 1 ]; then
        return
    fi

    local hooks_dir="/usr/share/deepin-immutable-mount-root-hooks/$1"
    shift
    if [ ! -d "$hooks_dir" ]; then
        log_msg "hooks directory $hooks_dir does not exist"
        return
    fi
    # iterate hooks_dir
    for hook_file in "$hooks_dir"/*; do
        if [ -x "$hook_file" ]; then
            # check hook file is owned by root
            local file_stat
            file_stat=$(stat -c '%u:%g' "$hook_file" 2>/dev/null) || {
                log_warn "unable to get file status for $hook_file"
                continue
            }
            if [ "$file_stat" != "0:0" ]; then
                log_warn "skipping hook $hook_file: not owned by root (uid:gid=$file_stat)"
                continue
            fi
            log_msg "executing hook: $hook_file"
            "$hook_file" "$@" || log_warn "failed to run hook $hook_file"
        fi
    done
}

# Migrate the persistent /var directory to the stable path. The caller must
# ensure that legacy_var's parent directory exists. Return 2 when migration
# cannot complete but the caller can safely use the legacy directory.
migrate_persistent_var() {
    local legacy_var=$1
    local new_var=$2
    local legacy_target="../../../var"

    if [ -d "$new_var" ]; then
        if [ -L "$legacy_var" ]; then
            if [ "$(readlink "$legacy_var")" = "$legacy_target" ]; then
                return 0
            fi
            log_err "persistent var legacy link has unexpected target: $legacy_var"
            return 1
        fi
        if [ ! -e "$legacy_var" ]; then
            if ! ln -s "$legacy_target" "$legacy_var"; then
                log_warn "failed to create persistent var compatibility link: $legacy_var; continuing with $new_var and will retry next boot"
            fi
            return 0
        fi
        if [ -d "$legacy_var" ]; then
            log_err "persistent var migration conflict: both $legacy_var and $new_var exist"
            return 2
        fi
        log_warn "persistent var legacy path is not a directory: $legacy_var; continuing with $new_var"
        return 0
    fi

    if [ -d "$legacy_var" ] && [ ! -L "$legacy_var" ] && [ ! -e "$new_var" ]; then
        if ! mv "$legacy_var" "$new_var"; then
            log_warn "failed to migrate persistent var from $legacy_var to $new_var; continuing with $legacy_var and will retry next boot"
            return 2
        fi
        if ! ln -s "$legacy_target" "$legacy_var"; then
            log_warn "failed to create persistent var compatibility link: $legacy_var; continuing with $new_var and will retry next boot"
        fi
        return 0
    fi

    if [ ! -e "$legacy_var" ] && [ ! -e "$new_var" ]; then
        log_warn "persistent var migration has no usable source: $legacy_var, $new_var; creating $new_var"
        if ! mkdir -p "$new_var"; then
            log_err "failed to create persistent var directory: $new_var"
            return 1
        fi
        return 0
    fi

    log_err "persistent var migration has no usable source: $legacy_var, $new_var"
    return 1
}

# is_safe_ref_id validates that a reference ID is safe for use in filesystem
# path construction.  It rejects empty strings, "." / "..", and any value
# containing path separators, colons, whitespace, or non-printable characters.
#
# Args:
#   $1 ref_id
#
# Returns:
#   0 if the ID is safe, 1 otherwise.
is_safe_ref_id() {
    local ref_id=$1

    if [ -z "$ref_id" ] || [ "$ref_id" = "." ] || [ "$ref_id" = ".." ]; then
        return 1
    fi
    case "$ref_id" in
        */* | *\\* | *:* | *".."* | *[[:space:]]* | *[![:print:]]*)
            return 1
            ;;
    esac
    return 0
}


# resolve_opt_ref resolves opt_upper/opt_lower references.
#
# Args:
#   $1 rootmnt
#   $2 current data deploy ID
#   $3 ref
#   $4 upper flag: 1 for upper, 0 for lower
#
# Output is written to global variables:
#   resolved_opt_ref
#   resolved_opt_deploy_id
#   resolved_opt_dir
resolve_opt_ref() {
    local rootmnt=$1
    local data_deploy_id=$2
    local ref=$3
    local upper=$4

    if [ -z "$ref" ]; then
        if [ "$upper" = 1 ]; then
            ref=default
        else
            ref=none
        fi
    fi

    if [ "$ref" = none ]; then
        if [ "$upper" = 1 ]; then
            log_err "opt_upper cannot be none"
            return 1
        fi
        resolved_opt_ref=none
        resolved_opt_deploy_id=$data_deploy_id
        resolved_opt_dir=
        return 0
    fi

    if ! is_safe_ref_id "$data_deploy_id"; then
        log_err "invalid data deploy ID: $data_deploy_id"
        return 1
    fi

    case "$ref" in
        default)
            resolved_opt_ref=default
            resolved_opt_deploy_id=$data_deploy_id
            resolved_opt_dir="$rootmnt/persistent/overlay/data/$data_deploy_id/opt-upper"
            ;;
        default:*)
            resolved_opt_deploy_id=${ref#default:}
            if ! is_safe_ref_id "$resolved_opt_deploy_id"; then
                log_err "invalid default opt deploy ID: $resolved_opt_deploy_id"
                return 1
            fi
            resolved_opt_ref=default
            resolved_opt_dir="$rootmnt/persistent/overlay/data/$resolved_opt_deploy_id/opt-upper"
            ;;
        *)
            if ! is_safe_ref_id "$ref"; then
                log_err "invalid opt layer ID: $ref"
                return 1
            fi
            resolved_opt_ref=$ref
            resolved_opt_deploy_id=$data_deploy_id
            resolved_opt_dir="$rootmnt/persistent/overlay/data/layer-$ref/opt"
            ;;
    esac
}

# append_lower_dir appends a directory to an overlay lowerdir list (a
# colon-separated string suitable for the mount option "lowerdir=...").  It
# enforces two overlay constraints: new_dir must not equal upper_dir, and
# must not already appear in the existing list.
#
# Args:
#   $1 upper_dir  upper directory for the overlay (for duplicate check)
#   $2 lower_dir  existing colon-separated lowerdir list (may be empty)
#   $3 new_dir    directory to append
#
# Output (stdout): the resulting lowerdir string.
# Returns: 0 on success, 1 if the new directory is invalid.
append_lower_dir() {
    local upper_dir=$1
    local lower_dir=$2
    local new_dir=$3

    [ -z "$new_dir" ] && echo "$lower_dir" && return 0

    if [ "$new_dir" = "$upper_dir" ]; then
        log_err "overlay lowerdir must not include upperdir: $new_dir"
        return 1
    fi

    case ":$lower_dir:" in
        *:"$new_dir":*)
            log_err "duplicate overlay lowerdir: $new_dir"
            return 1
            ;;
    esac

    if [ -z "$lower_dir" ]; then
        echo "$new_dir"
    else
        echo "$lower_dir:$new_dir"
    fi
}

data_layer_mount() {
    local rootmnt=$1
    local OSTREE=$2
    local baseDeploy=$3

    local datadir=$rootmnt/persistent
    local extDeploy
    if ! extDeploy=$(realpath "$datadir/$OSTREE") || [ ! -d "$extDeploy" ]; then
        log_err "invalid data deployment path: $datadir/$OSTREE"
        return 1
    fi
    log_msg "data_layer_mount: rootmnt=$rootmnt, baseDeploy=$baseDeploy"

    # remove opt symlink if exists
    if [ -L "$rootmnt/opt" ]; then
        rm "$rootmnt/opt" || {
            log_warn "failed to remove opt symlink: $rootmnt/opt"
        }
    fi

    if [ -L "$rootmnt/media" ]; then
        rm "$rootmnt/media" || {
            log_warn "failed to remove media symlink: $rootmnt/media"
        }
    fi

    mkdir -p "$rootmnt/boot" \
            "$rootmnt/dev" \
            "$rootmnt/etc" \
            "$rootmnt/proc" \
            "$rootmnt/root" \
            "$rootmnt/run" \
            "$rootmnt/sys" \
            "$rootmnt/sysroot" \
            "$rootmnt/tmp" \
            "$rootmnt/usr" \
            "$rootmnt/var" || {
        log_err "failed to create root directory layout"
        return 1
    }

    mkdir -p "$rootmnt/home" "$rootmnt/mnt" "$rootmnt/media" "$rootmnt/opt" "$rootmnt/srv" || \
        log_warn "failed to create optional root directories"

    chmod 1777 "$rootmnt/tmp" || {
        log_warn "failed to set sticky bit on $rootmnt/tmp"
    }

    # When the sysroot partition is the actual root or just a regular directory,
    # binding /sysroot/boot to /boot might change the actual /boot directory, causing issues.
    # Use a symlink to keep the data in sync.
    if grep -q "/sysroot/boot /boot none defaults,bind,rw 0 0" /etc/fstab; then
        if [ -e "$rootmnt/sysroot/boot" ] || [ -L "$rootmnt/sysroot/boot" ]; then
            if [ ! -L "$rootmnt/sysroot/boot" ] || [ "$(readlink "$rootmnt/sysroot/boot")" != /boot ]; then
                log_warn "unexpected sysroot boot path: $rootmnt/sysroot/boot"
            fi
        elif ! ln -s /boot "$rootmnt/sysroot/boot"; then
            log_warn "failed to create sysroot boot symlink"
        fi
    fi

    # Remove the last slash and everything after it
    data_commitid_idx="${extDeploy%/*}"
    # Get the content after the last slash
    data_commitid_idx="${data_commitid_idx##*/}"
    data_overlay_root=$rootmnt/persistent/overlay/data/$data_commitid_idx
    mkdir -p "$data_overlay_root" || {
        log_err "failed to create data overlay root: $data_overlay_root"
        return 1
    }

    cfgFile=$(dirname $extDeploy)/config
    cfg_upper=
    cfg_lower=
    cfg_opt_upper=
    cfg_opt_lower=
    if [ -f "$cfgFile" ]; then
        while IFS='=' read -r key value; do
            # Ignore empty lines and comment lines
            if [ -z "$key" ] || [ "${key#\#}" != "$key" ]; then
                continue
            fi

            # Remove leading and trailing spaces
            key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
            value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
            case "$key" in
                upper)
                    cfg_upper=$value
                    ;;
                lower)
                    cfg_lower=$value
                    ;;
                opt_upper)
                    cfg_opt_upper=$value
                    ;;
                opt_lower)
                    cfg_opt_lower=$value
                    ;;
            esac
        done < "$cfgFile"
    fi
    if [ -z "$cfg_upper" ]; then
        cfg_upper=default
    fi
    if [ -z "$cfg_lower" ]; then
        cfg_lower=none
    fi

    modprobe overlay || {
        log_err "failed to load overlay module"
        return 1
    }
    for mountParam in usr:ro opt:rw etc:rw;do
        # ash in busybox does not support <<< syntax.
        # `echo "$mountParam" | IFS=':' read -r dir perm` will also have problems,
        # because when read runs in a pipeline, it will run a shell process separately, resulting in the external failure to use dir and perm
        IFS=':' read -r dir perm << EOF
$mountParam
EOF
        log_msg "data_layer_mount: mounting $dir-overlay (perm=$perm)"
        if ! mkdir -p "$baseDeploy/$dir"; then
            if [ "$dir" = opt ]; then
                log_warn "failed to create base deployment directory for opt; skipping opt overlay"
                continue
            fi
            log_err "failed to create base deployment directory for $dir"
            return 1
        fi

        if [ "$dir" = opt ]; then
            opt_upper_ref=$cfg_opt_upper
            opt_lower_ref=$cfg_opt_lower
            # Fallback: when opt_upper is not explicitly set, use the generic
            # "upper" config value.
            if [ -z "$opt_upper_ref" ]; then
                opt_upper_ref=$cfg_upper
            fi
            # Fallback: when opt_lower is not explicitly set, use the generic
            # "lower" config value.
            if [ -z "$opt_lower_ref" ]; then
                opt_lower_ref=$cfg_lower
            fi

            if ! resolve_opt_ref "$rootmnt" "$data_commitid_idx" "$opt_upper_ref" 1; then
                log_warn "failed to resolve opt upper reference; skipping opt overlay"
                continue
            fi
            upper_dir=$resolved_opt_dir
            opt_upper_resolved_ref=$resolved_opt_ref
            opt_upper_resolved_deploy_id=$resolved_opt_deploy_id

            if ! resolve_opt_ref "$rootmnt" "$data_commitid_idx" "$opt_lower_ref" 0; then
                log_warn "failed to resolve opt lower reference; skipping opt overlay"
                continue
            fi
            opt_lower_dir=$resolved_opt_dir
            opt_lower_resolved_ref=$resolved_opt_ref
            opt_lower_resolved_deploy_id=$resolved_opt_deploy_id

            mkdir -p "$upper_dir" || {
                log_warn "failed to create upper directory for opt: $upper_dir; skipping opt overlay"
                continue
            }

            lower_dir=
            if [ "$opt_lower_resolved_ref" != none ]; then
                mkdir -p "$opt_lower_dir" || {
                    log_warn "failed to create opt lower directory: $opt_lower_dir; skipping opt overlay"
                    continue
                }
                if ! lower_dir=$(append_lower_dir "$upper_dir" "$lower_dir" "$opt_lower_dir"); then
                    log_warn "failed to build opt lower directory list; skipping opt overlay"
                    continue
                fi
            fi
            if ! lower_dir=$(append_lower_dir "$upper_dir" "$lower_dir" "$extDeploy/$dir"); then
                log_warn "failed to build opt lower directory list; skipping opt overlay"
                continue
            fi
            if ! lower_dir=$(append_lower_dir "$upper_dir" "$lower_dir" "$baseDeploy/$dir"); then
                log_warn "failed to build opt lower directory list; skipping opt overlay"
                continue
            fi
        elif [ "$cfg_upper" = "default" ]; then
            upper_dir=$data_overlay_root/$dir-upper
        elif echo "$cfg_upper" | grep -q '^default:'; then
            ref_deploy_id=${cfg_upper#default:}
            if ! is_safe_ref_id "$ref_deploy_id"; then
                log_err "unsafe deployID in upper ref: $cfg_upper"
                return 1
            fi
            upper_dir=$rootmnt/persistent/overlay/data/$ref_deploy_id/$dir-upper
        else
            upper_dir=$rootmnt/persistent/overlay/data/layer-$cfg_upper/$dir
        fi

        if [ "$dir" = opt ]; then
            :
        elif [ "$cfg_lower" = "none" ]; then
            lower_dir=$extDeploy/$dir:$baseDeploy/$dir
        else
            if [ "$cfg_lower" = "default" ]; then
                first_lower_dir=$data_overlay_root/$dir-upper
                mkdir -p "$first_lower_dir" || {
                    log_err "failed to create lower directory for $dir: $first_lower_dir"
                    return 1
                }
                lower_dir=$first_lower_dir:$extDeploy/$dir:$baseDeploy/$dir
            elif echo "$cfg_lower" | grep -q '^default:'; then
                ref_deploy_id=${cfg_lower#default:}
                if ! is_safe_ref_id "$ref_deploy_id"; then
                    log_err "unsafe deployID in lower ref: $cfg_lower"
                    return 1
                fi
                first_lower_dir=$rootmnt/persistent/overlay/data/$ref_deploy_id/$dir-upper
                mkdir -p "$first_lower_dir" || {
                    log_err "failed to create lower directory for $dir: $first_lower_dir"
                    return 1
                }
                lower_dir=$first_lower_dir:$extDeploy/$dir:$baseDeploy/$dir
            else
                first_lower_dir=$rootmnt/persistent/overlay/data/layer-$cfg_lower/$dir
                mkdir -p "$first_lower_dir" || {
                    log_err "failed to create lower directory for $dir: $first_lower_dir"
                    return 1
                }
                lower_dir=$first_lower_dir:$extDeploy/$dir:$baseDeploy/$dir
            fi
            if [ "$dir" = usr ]; then
                # CRITICAL WARNING: DO NOT CHANGE THE ORDER OF DIRECTORIES IN lower_dir!
                # The overlay merge logic in overlay merger depends on this specific order.
                # Changing this order will break the overlay merge functionality!
                run_hooks before_mount_usr_overlay "$(dirname $extDeploy)" "$lower_dir" "$upper_dir"
            fi
        fi
        workDir=$data_overlay_root/$dir-work
        if ! mkdir -p "$upper_dir" "$workDir" "$rootmnt/$dir"; then
            if [ "$dir" = opt ]; then
                log_warn "failed to create overlay directories for opt; skipping opt overlay"
                continue
            fi
            log_err "failed to create overlay directories for $dir"
            return 1
        fi
        if [ "$dir" = usr ]; then
            # Ensure /usr/local is a real directory in the overlay upper
            # directory before mounting.  The usr overlay is mounted
            # read-only so this must be done beforehand; a directory in the
            # upper layer shadows any non-directory entry (symlink or file)
            # present in the deployment (lower) layers.
            if is_non_directory "$upper_dir/local"; then
                rm "$upper_dir/local" || {
                    log_err "failed to remove existing non-directory: $upper_dir/local"
                    return 1
                }
            fi
            mkdir -p "$upper_dir/local" || {
                log_err "failed to create directory: $upper_dir/local"
                return 1
            }
        fi
        log_msg "data_layer_mount: mount -t overlay $dir-overlay at $rootmnt/$dir"
        if ! LIBMOUNT_FORCE_MOUNT2=always mount -t overlay -o "$perm,relatime,lowerdir=$lower_dir,upperdir=$upper_dir,workdir=$workDir" "$dir-overlay" "$rootmnt/$dir"; then
            if [ "$dir" = opt ]; then
                log_warn "failed to mount opt overlay; continuing without it"
                continue
            fi
            log_err "failed to mount $dir overlay (upper=$upper_dir lower=$lower_dir)"
            return 1
        fi
    done

    # Create symlinks for bin, lib, lib64, and sbin if they don't exist.
    # This must run AFTER the usr overlay is mounted so that the existence
    # check against usr/lib64 reflects the real deployment content rather
    # than the empty mountpoint created earlier.
    if [ ! -e "$rootmnt/bin" ] && [ ! -L "$rootmnt/bin" ] && ! ln -s usr/bin "$rootmnt/bin"; then
        log_warn "failed to create bin compatibility symlink"
    fi
    if [ ! -e "$rootmnt/lib" ] && [ ! -L "$rootmnt/lib" ] && ! ln -s usr/lib "$rootmnt/lib"; then
        log_warn "failed to create lib compatibility symlink"
    fi
    if [ ! -e "$rootmnt/lib64" ] && [ ! -L "$rootmnt/lib64" ] && [ -d "$rootmnt/usr/lib64" ] && ! ln -s usr/lib64 "$rootmnt/lib64"; then
        log_warn "failed to create lib64 compatibility symlink"
    fi
    if [ ! -e "$rootmnt/sbin" ] && [ ! -L "$rootmnt/sbin" ] && ! ln -s usr/sbin "$rootmnt/sbin"; then
        log_warn "failed to create sbin compatibility symlink"
    fi

    # Clean up dangling lib64 symlink left by historical versions
    # when usr/lib64 does not exist (e.g., on ARM64)
    if [ -L "$rootmnt/lib64" ] && [ ! -e "$rootmnt/lib64" ] && [ ! -d "$rootmnt/usr/lib64" ]; then
        rm -f "$rootmnt/lib64" || log_warn "failed to remove dangling lib64 symlink: $rootmnt/lib64"
    fi

    # make etc without hardlink, some apps not work well with hardlink
    if hardlink_count=$(stat -c "%h" "$rootmnt/etc/deepin-immutable-ctl/deepin-immutable-ctl.conf"); then
        if [ "$hardlink_count" -gt 1 ] && [ -f /usr/bin/cp.fix ]; then
            if ! tmp_copyed=$(mktemp -d "$rootmnt/persistent/ostree/data/tmp-XXXXXX"); then
                log_warn "failed to create etc recovery directory; skipping etc de-hardlinking"
            elif ! /usr/bin/cp.fix --preserve=all -r "$rootmnt/etc" "$tmp_copyed"; then
                log_warn "failed to copy etc before removing hardlinks; skipping etc de-hardlinking"
            else
                # Clear the overlay contents without trying to remove the /etc mountpoint,
                # which returns EBUSY after creating whiteouts for its children.
                if ! rm -rf "$rootmnt/etc"/* \
                            "$rootmnt/etc"/.[!.]* \
                            "$rootmnt/etc"/..?*; then
                    log_warn "failed to clear some etc contents; restoring backup"
                fi
                if ! /usr/bin/cp.fix --preserve=all -r "$tmp_copyed/etc" "$rootmnt"; then
                    log_err "failed to restore etc after removing hardlinks"
                    return 1
                fi
                rm -rf "$tmp_copyed" || log_warn "failed to remove etc recovery directory: $tmp_copyed"
            fi
        fi
    else
        log_warn "failed to read hardlink count; skipping etc de-hardlinking"
    fi

    log_msg "data_layer_mount: binding sysroot and ostree mounts"
    mount -o bind "$rootmnt/sysroot" "$rootmnt/sysroot" || {
        log_err "failed to bind mount sysroot"
        return 1
    }
    mkdir -p "$rootmnt/sysroot/ostree" || {
        log_err "failed to create sysroot ostree directory"
        return 1
    }
    mount -o bind "$rootmnt/ostree" "$rootmnt/sysroot/ostree" || {
        log_err "failed to bind mount sysroot ostree"
        return 1
    }
    mount -o remount,bind,ro "$rootmnt/sysroot/ostree" "$rootmnt/sysroot/ostree" || {
        log_err "failed to remount sysroot ostree read-only"
        return 1
    }

    mount -o bind "$rootmnt/ostree" "$rootmnt/ostree" || {
        log_err "failed to bind mount ostree"
        return 1
    }
    mount -o remount,bind,ro "$rootmnt/ostree" "$rootmnt/ostree" || {
        log_err "failed to remount ostree read-only"
        return 1
    }

    local legacy_var_parent=$datadir/ostree/deploy/deepin
    local legacy_var=$legacy_var_parent/var
    local new_var=$datadir/var
    local var_source
    if [ ! -e "$legacy_var_parent" ]; then
        # During initial installation the legacy OSTree deploy directory does
        # not exist yet. Create the new persistent /var without creating the
        # legacy directory just to add a compatibility link.
        if [ -e "$new_var" ] && [ ! -d "$new_var" ]; then
            log_err "persistent var path is not a directory: $new_var"
            return 1
        fi
        mkdir -p "$new_var" || {
            log_err "failed to create persistent var directory: $new_var"
            return 1
        }
        var_source=$new_var
    elif [ ! -d "$legacy_var_parent" ]; then
        log_warn "persistent var legacy parent is not a directory: $legacy_var_parent; continuing without legacy var migration"
        if [ -e "$new_var" ] && [ ! -d "$new_var" ]; then
            log_err "persistent var path is not a directory: $new_var"
            return 1
        fi
        mkdir -p "$new_var" || {
            log_err "failed to create persistent var directory: $new_var"
            return 1
        }
        var_source=$new_var
    else
        migrate_persistent_var "$legacy_var" "$new_var"
        # Preserve the migration result before any future logging or tracing
        # command can replace $?.
        local migration_result=$?
        case "$migration_result" in
            0) var_source=$new_var ;;
            2) var_source=$legacy_var ;;
            *)
                log_err "failed to migrate persistent var"
                return 1
                ;;
        esac
    fi

    mount -o bind "$rootmnt/persistent/ostree" "$rootmnt/persistent/ostree" || {
        log_err "failed to bind mount persistent ostree"
        return 1
    }
    mount -o remount,bind,ro "$rootmnt/persistent/ostree" "$rootmnt/persistent/ostree" || {
        log_err "failed to remount persistent ostree read-only"
        return 1
    }

    if [ -z "$var_source" ]; then
        log_err "var mount source is empty"
        return 1
    fi
    mount -o bind "$var_source" "$rootmnt/var" || {
        log_err "failed to bind mount persistent var"
        return 1
    }
    mount -o remount,bind,rw "$rootmnt/var" "$rootmnt/var" || {
        log_err "failed to remount persistent var read-write"
        return 1
    }

    run_hooks before_mount_var_overlay "$data_overlay_root" "$rootmnt/var"

    if [ -d "$data_overlay_root/var-upper" ]; then
        log_msg "data_layer_mount: mounting var-overlay at $rootmnt/var"
        mkdir -p "$data_overlay_root/var-work" || {
            log_warn "failed to create var overlay work directory; continuing without var overlay"
        }
        if [ -d "$data_overlay_root/var-work" ]; then
            mount -t overlay -o "rw,relatime,lowerdir=$rootmnt/var,upperdir=$data_overlay_root/var-upper,workdir=$data_overlay_root/var-work" var-overlay "$rootmnt/var" || \
                log_warn "failed to mount var overlay; continuing without it"
        fi
    fi

    # Bind mount /var/usrlocal to /usr/local instead of using a symlink.
    # /var must already be mounted (and writable) at this point.
    if [ -d "$rootmnt/usr/local" ]; then
        if is_non_directory "$rootmnt/var/usrlocal"; then
            rm "$rootmnt/var/usrlocal" || {
                log_err "failed to remove existing non-directory: $rootmnt/var/usrlocal"
                return 1
            }
        fi
        mkdir -p "$rootmnt/var/usrlocal" || {
            log_err "failed to create $rootmnt/var/usrlocal"
            return 1
        }
        mount -o bind "$rootmnt/var/usrlocal" "$rootmnt/usr/local" || \
            log_warn "failed to bind mount $rootmnt/var/usrlocal to $rootmnt/usr/local; /usr/local content will not persist across deployments"
    else
        log_warn "$rootmnt/usr/local is not a directory, skipping bind mount"
    fi

    log_msg "data_layer_mount: completed"
    # chroot to roomnt and start init
    return 0
}

ostree_mount_root() {
    local rootmnt=$1
    local OSTREE=$2

    local datadir=$rootmnt/persistent

    local extDeploy
    if ! extDeploy=$(realpath "$datadir/$OSTREE") || [ ! -d "$extDeploy" ]; then
        log_err "invalid deployment path: $datadir/$OSTREE"
        return 1
    fi
    local sys_commit_idx
    local ostree_parent_path="$extDeploy/usr/share/deepin-immutable-ctl/state/ostree-parent"
    if ! sys_commit_idx=$(cat "$ostree_parent_path") || [ -z "$sys_commit_idx" ]; then
        log_err "failed to read deployment parent commit: $ostree_parent_path"
        return 1
    fi
    log_msg "ostree_mount_root: extDeploy=$extDeploy, sys_commit_idx=$sys_commit_idx"

    sysroot="$rootmnt"
    ostree_path=/ostree/deploy/deepin/deploy/$sys_commit_idx

    case $extDeploy in
        */ostree/data/*)
            log_msg "ostree_mount_root: using data_layer_mount"
            data_layer_mount "$rootmnt" "$OSTREE" "$sysroot$ostree_path"
            return $?
            ;;
        *)
            # ext_mount() has been removed; all deployments are expected to
            # use the data_layer_mount path.  Any other deployment type is
            # unsupported and treated as a hard error.
            log_err "ostree_mount_root: unknown deployment type: $extDeploy"
            return 1
    esac
}

# Parse mountroot --root=uuid=xxx --persistent=uuid=xxx --rootflags=xxxx --persistentflags=xxxx --ostree=xxxx /root using getopt.
mountroot() {
    local ROOT ROOTFLAGS PERSISTENT PERSISTENTFLAGS OSTREE rootmnt
    local OPTIND
    local OPTS
    local UMOUNT=false
    local HAS_PERSISTENT=false
    local _orig_args="$*"

    OPTS=$(getopt -o r:p:f:F:o:uhvk --long root:,persistent:,rootflags:,persistentflags:,ostree:,umount,help,version,kmsg -- "$@")
    if [ $? != 0 ]; then
        log_err "failed to parse options"
        return 1
    fi

    eval set -- "$OPTS"

    while true; do
        case "$1" in
            -r | --root)
                ROOT=$2
                shift 2
                ;;
            -p | --persistent)
                HAS_PERSISTENT=true
                PERSISTENT=$2
                shift 2
                ;;
            -f | --rootflags)
                ROOTFLAGS="-o $2"
                shift 2
                ;;
            -F | --persistentflags)
                PERSISTENTFLAGS="-o $2"
                shift 2
                ;;
            -o | --ostree)
                OSTREE=$2
                shift 2
                ;;
            -u | --umount)
                UMOUNT=true
                shift 1
                ;;
            -k | --kmsg)
                KMSG=true
                shift 1
                ;;
            -v | --version)
                show_version
                return 0
                ;;
            -h | --help)
                echo "Usage: $0 [OPTIONS] rootmnt"
                echo
                echo "Options:"
                echo "  -r, --root=DEVICE          Root device (e.g., UUID=xxx)"
                echo "  -p, --persistent=DEVICE    Persistent device (e.g., UUID=xxx)"
                echo "  -f, --rootflags=FLAGS      Flags for root device (e.g., rw)"
                echo "  -F, --persistentflags=FLAGS Flags for persistent device (e.g., rw)"
                echo "  -o, --ostree=PATH          OSTree path"
                echo "  -u, --umount               Unmount all mount points under rootmnt"
                echo "  -k, --kmsg                 Log to kernel message buffer (/dev/kmsg)"
                echo "  -v, --version              Show version"
                echo "  -h, --help                 Show this help message"
                return 0
                ;;
            --)
                shift
                break
                ;;
            *)
                log_err "invalid option: $1"
                return 1
                ;;
        esac
    done

    log_msg "deepin-immutable-mount-root invoked with args: $_orig_args"

    # get rootmnt param
    if [ $# -eq 1 ]; then
        rootmnt=$1
    elif [ $# -eq 2 ];then
        rootmnt=$1
        OSTREE=$2
    else
        log_err "missing rootmnt argument"
        return 1
    fi

    log_msg "mountroot started: rootmnt=$rootmnt"

    if $UMOUNT; then
        log_msg "umount mode, unmounting $rootmnt"
        recursive_umount "$rootmnt"
        return 0
    fi

    mkdir -p "$rootmnt" || {
        log_err "failed to create root mount directory: $rootmnt"
        return 1
    }

    if [ -n "$ROOT" ];then
        # Wait for device to be available
        wait_for_device "$ROOT" || {
            log_err "timed out waiting for root device: $ROOT"
            return 1
        }
        ROOT=$(resolve_device "$ROOT")
        if [ -z "$ROOT" ]; then
            log_err "failed to resolve root device"
            return 1
        fi
        ROOTFSTYPE=$(get_fstype "$ROOT")
        log_msg "root device: $ROOT, fstype: $ROOTFSTYPE"
        if ! is_mountpoint "$rootmnt";then
            log_msg "mounting root: mount -t $ROOTFSTYPE $ROOTFLAGS $ROOT $rootmnt"
            mount -t "$ROOTFSTYPE" $ROOTFLAGS "$ROOT" "$rootmnt" || {
                log_err "failed to mount root device: $ROOT"
                return 1
            }
        fi
    fi

    mount -o remount,rw "$rootmnt" || {
        log_err "failed to remount root read-write: $rootmnt"
        return 1
    }
    log_msg "root remounted rw at $rootmnt"

    mkdir -p "$rootmnt/persistent" || {
        log_err "failed to create persistent mount directory"
        return 1
    }
    if [ -n "$PERSISTENT" ]; then
        # Wait for device to be available
        wait_for_device "$PERSISTENT" || {
            log_err "timed out waiting for persistent device: $PERSISTENT"
            return 1
        }
        PERSISTENT=$(resolve_device "$PERSISTENT")
        if [ -z "$PERSISTENT" ]; then
            log_err "failed to resolve persistent device"
            return 1
        fi
        if [ -n "$PERSISTENT" ];then
            PERSISTENTFSTYPE=$(get_fstype "$PERSISTENT")
            log_msg "persistent device: $PERSISTENT, fstype: $PERSISTENTFSTYPE"
            if ! is_mountpoint "$rootmnt/persistent";then
                log_msg "mounting persistent: mount -t $PERSISTENTFSTYPE $PERSISTENTFLAGS $PERSISTENT $rootmnt/persistent"
                mount -t "$PERSISTENTFSTYPE" $PERSISTENTFLAGS "$PERSISTENT" "$rootmnt/persistent" || {
                    log_err "failed to mount persistent device: $PERSISTENT"
                    return 1
                }
            fi
        fi
    fi

    if [ -z "$OSTREE" ]; then
        OSTREE=$(try_get_ostree "$rootmnt")
    fi
    if [ -z "$OSTREE" ]; then
        log_err "failed to determine OSTree deployment"
        return 1
    fi
    log_msg "OSTREE=$OSTREE"
    if ! ostree_mount_root "$rootmnt" "$OSTREE"; then
        log_err "failed to mount OSTree root"
        return 1
    fi
    return 0
}

# 1. mount root device to $mnt
# 2. mount data device to $mnt/persistent

#mount /dev/vda4 $mnt
#mount /dev/vda5 $mnt/persistent

#ostree_mount_root $ostree_arg
mountroot $@
ret=$?
log_msg "deepin-immutable-mount-root finished"
exit $ret

# you can do chroot here
