mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:03:13 +02:00
221 lines
8.7 KiB
Bash
Executable File
221 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib/backup.sh — Incremental backup of APKs, app data, media, and security state.
|
|
# Requires: adb_common.sh and backup_manifest.sh sourced, ADB_SERIAL set.
|
|
set -euo pipefail
|
|
|
|
readonly _BACKUP_REMOTE_DIR="/data/local/tmp/focus_mode"
|
|
|
|
_backup_validate_package_name() {
|
|
local package_name="$1"
|
|
|
|
[[ "${package_name}" =~ ^[A-Za-z0-9._-]+$ ]] || _fatal "Unsafe package name rejected: ${package_name}"
|
|
}
|
|
|
|
backup_make_snapshot_dir() {
|
|
local device_id="$1"
|
|
local timestamp=""
|
|
local snapshot_dir=""
|
|
|
|
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
snapshot_dir="${PHONE_BACKUP_ROOT}/${device_id}/history/${timestamp}"
|
|
mkdir -p \
|
|
"${snapshot_dir}/device_info" \
|
|
"${snapshot_dir}/security_state" \
|
|
"${snapshot_dir}/apks" \
|
|
"${snapshot_dir}/app_data" \
|
|
"${snapshot_dir}/media" \
|
|
"${snapshot_dir}/monitoring"
|
|
printf '%s' "${snapshot_dir}"
|
|
}
|
|
|
|
_backup_update_latest() {
|
|
local snapshot_dir="$1"
|
|
local device_root="$2"
|
|
local latest_dir="${device_root}/latest"
|
|
|
|
rm -rf "${latest_dir}"
|
|
if ln -s "${snapshot_dir}" "${latest_dir}" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p "${latest_dir}"
|
|
cp -a "${snapshot_dir}/." "${latest_dir}/"
|
|
}
|
|
|
|
backup_device_info() {
|
|
local output_dir="$1/device_info"
|
|
|
|
_info "Backing up device info → ${output_dir}"
|
|
adb_cmd shell getprop >"${output_dir}/getprop.txt" 2>/dev/null || true
|
|
adb_cmd shell pm list packages -f >"${output_dir}/packages_full.txt" 2>/dev/null || true
|
|
adb_cmd shell pm list packages >"${output_dir}/packages.txt" 2>/dev/null || true
|
|
adb_cmd shell df >"${output_dir}/df.txt" 2>/dev/null || true
|
|
printf '%s\n' "${ADB_SERIAL}" >"${output_dir}/serial.txt"
|
|
adb_cmd shell getprop ro.product.model >"${output_dir}/model.txt" 2>/dev/null || true
|
|
adb_cmd shell getprop ro.build.fingerprint >"${output_dir}/fingerprint.txt" 2>/dev/null || true
|
|
}
|
|
|
|
backup_security_state() {
|
|
local output_dir="$1/security_state"
|
|
local source_path=""
|
|
local destination_dir=""
|
|
|
|
_info "Backing up security state → ${output_dir}"
|
|
|
|
for source_path in "${SECURITY_STATE_FILES[@]}"; do
|
|
destination_dir="${output_dir}$(dirname "${source_path}")"
|
|
mkdir -p "${destination_dir}"
|
|
adb_root_shell "cat '${source_path}'" >"${destination_dir}/$(basename "${source_path}")" 2>/dev/null || \
|
|
_warn "Could not back up ${source_path} (may not exist yet)"
|
|
done
|
|
|
|
adb_root_shell "sh '${_BACKUP_REMOTE_DIR}/focus_ctl.sh' status" >"${output_dir}/focus_status.txt" 2>/dev/null || true
|
|
adb_root_shell "sh '${_BACKUP_REMOTE_DIR}/focus_ctl.sh' hosts-status" >"${output_dir}/hosts_status.txt" 2>/dev/null || true
|
|
adb_root_shell "sh '${_BACKUP_REMOTE_DIR}/focus_ctl.sh' dns-status" >"${output_dir}/dns_status.txt" 2>/dev/null || true
|
|
adb_root_shell "sh '${_BACKUP_REMOTE_DIR}/focus_ctl.sh' launcher-status" >"${output_dir}/launcher_status.txt" 2>/dev/null || true
|
|
adb_root_shell "sh '${_BACKUP_REMOTE_DIR}/focus_ctl.sh' notif-status" >"${output_dir}/notif_status.txt" 2>/dev/null || true
|
|
adb_root_shell "settings get global private_dns_mode" >"${output_dir}/private_dns_mode.txt" 2>/dev/null || true
|
|
}
|
|
|
|
backup_apks() {
|
|
local output_dir="$1/apks"
|
|
local entry=""
|
|
local package_name=""
|
|
local restore_policy=""
|
|
local apk_path=""
|
|
local destination_dir=""
|
|
|
|
_info "Backing up APKs → ${output_dir}"
|
|
|
|
for entry in "${APK_ITEMS[@]}"; do
|
|
package_name="${entry%%|*}"
|
|
restore_policy="$(printf '%s' "${entry}" | cut -d'|' -f2)"
|
|
_backup_validate_package_name "${package_name}"
|
|
|
|
apk_path="$(adb_cmd shell "pm path ${package_name} | head -1 | sed 's/^package://'" 2>/dev/null | tr -d '\r' || true)"
|
|
if [[ -z "${apk_path}" ]]; then
|
|
_warn "APK not found on device: ${package_name}"
|
|
continue
|
|
fi
|
|
|
|
destination_dir="${output_dir}/${package_name}"
|
|
mkdir -p "${destination_dir}"
|
|
if adb_cmd pull "${apk_path}" "${destination_dir}/base.apk" >/dev/null 2>&1; then
|
|
_info " Backed up ${package_name} (policy: ${restore_policy})"
|
|
else
|
|
_warn " Failed to pull APK for ${package_name}"
|
|
fi
|
|
printf '%s\n' "${restore_policy}" >"${destination_dir}/restore_policy.txt"
|
|
if [[ "${restore_policy}" == "manual_only" ]]; then
|
|
printf '%s\n' 'NOTE: manual_only — restore requires explicit operator action' >>"${destination_dir}/restore_policy.txt"
|
|
elif [[ "${restore_policy}" == "backup_only" ]]; then
|
|
printf '%s\n' 'NOTE: backup_only — backed up for safekeeping; automated restore is not implemented' >>"${destination_dir}/restore_policy.txt"
|
|
fi
|
|
done
|
|
}
|
|
|
|
backup_app_data() {
|
|
local output_dir="$1/app_data"
|
|
local entry=""
|
|
local package_name=""
|
|
local data_path=""
|
|
local restore_policy=""
|
|
local destination_dir=""
|
|
local parent_dir=""
|
|
local base_dir=""
|
|
local tar_command=""
|
|
|
|
_info "Backing up app data → ${output_dir}"
|
|
|
|
for entry in "${APP_DATA_ITEMS[@]}"; do
|
|
package_name="${entry%%|*}"
|
|
data_path="$(printf '%s' "${entry}" | cut -d'|' -f2)"
|
|
restore_policy="$(printf '%s' "${entry}" | cut -d'|' -f3)"
|
|
_backup_validate_package_name "${package_name}"
|
|
|
|
destination_dir="${output_dir}/${package_name}"
|
|
mkdir -p "${destination_dir}"
|
|
|
|
if ! adb_root_shell "test -d '${data_path}'" >/dev/null 2>&1; then
|
|
_warn "App data path not found: ${data_path} for ${package_name}"
|
|
continue
|
|
fi
|
|
|
|
parent_dir="$(dirname "${data_path}")"
|
|
base_dir="$(basename "${data_path}")"
|
|
tar_command="tar -czf - -C '${parent_dir}' '${base_dir}'"
|
|
if adb_root_shell "${tar_command}" >"${destination_dir}/data.tar.gz" 2>/dev/null; then
|
|
_info " Backed up app data for ${package_name} (policy: ${restore_policy})"
|
|
else
|
|
_warn " Failed to back up app data for ${package_name}"
|
|
fi
|
|
|
|
printf '%s\n' "${restore_policy}" >"${destination_dir}/restore_policy.txt"
|
|
if [[ "${restore_policy}" == "manual_only" ]]; then
|
|
printf '%s\n' 'NOTE: manual_only — restore requires explicit operator action' >>"${destination_dir}/restore_policy.txt"
|
|
elif [[ "${restore_policy}" == "backup_only" ]]; then
|
|
printf '%s\n' 'NOTE: backup_only — backed up for safekeeping; automated restore is not implemented' >>"${destination_dir}/restore_policy.txt"
|
|
fi
|
|
done
|
|
}
|
|
|
|
backup_media() {
|
|
local output_dir="$1/media"
|
|
local entry=""
|
|
local name=""
|
|
local on_device_path=""
|
|
local restore_policy=""
|
|
local destination_dir=""
|
|
|
|
_info "Backing up media → ${output_dir}"
|
|
|
|
for entry in "${MEDIA_ITEMS[@]}"; do
|
|
name="${entry%%|*}"
|
|
on_device_path="$(printf '%s' "${entry}" | cut -d'|' -f2)"
|
|
restore_policy="$(printf '%s' "${entry}" | cut -d'|' -f3)"
|
|
destination_dir="${output_dir}/${name}"
|
|
mkdir -p "${destination_dir}"
|
|
|
|
if adb_cmd pull "${on_device_path}" "${destination_dir}/" >/dev/null 2>&1; then
|
|
_info " Backed up media/${name} from ${on_device_path} (policy: ${restore_policy})"
|
|
else
|
|
_warn " Could not pull media/${name} from ${on_device_path}"
|
|
fi
|
|
printf '%s\n' "${restore_policy}" >"${destination_dir}/restore_policy.txt"
|
|
if [[ "${restore_policy}" == "manual_only" ]]; then
|
|
printf '%s\n' 'NOTE: manual_only — restore requires explicit operator action' >>"${destination_dir}/restore_policy.txt"
|
|
elif [[ "${restore_policy}" == "backup_only" ]]; then
|
|
printf '%s\n' 'NOTE: backup_only — backed up for safekeeping; automated restore is not implemented' >>"${destination_dir}/restore_policy.txt"
|
|
fi
|
|
done
|
|
}
|
|
|
|
backup_prune_history() {
|
|
local device_root="$1"
|
|
local history_dir="${device_root}/history"
|
|
|
|
[[ -d "${history_dir}" ]] || return 0
|
|
_info "Pruning history older than ${HISTORY_KEEP_DAYS} days in ${history_dir}"
|
|
find "${history_dir}" -maxdepth 1 -mindepth 1 -type d -mtime "+${HISTORY_KEEP_DAYS}" -print -exec rm -rf '{}' + || true
|
|
}
|
|
|
|
backup_run_incremental() {
|
|
local device_id="$1"
|
|
local device_root="${PHONE_BACKUP_ROOT}/${device_id}"
|
|
local snapshot_dir=""
|
|
|
|
snapshot_dir="$(backup_make_snapshot_dir "${device_id}")"
|
|
_info "Starting incremental backup to ${snapshot_dir}"
|
|
|
|
backup_device_info "${snapshot_dir}"
|
|
backup_security_state "${snapshot_dir}"
|
|
backup_apks "${snapshot_dir}"
|
|
backup_app_data "${snapshot_dir}"
|
|
backup_media "${snapshot_dir}"
|
|
_backup_update_latest "${snapshot_dir}" "${device_root}"
|
|
backup_prune_history "${device_root}"
|
|
|
|
_info "Backup complete: ${snapshot_dir}"
|
|
printf '%s' "${snapshot_dir}"
|
|
}
|