feat: add maple-upgrade + maple-restore for sysupgrade auto-config save/restore

maple-upgrade (/sbin/maple-upgrade):
  - Backup 21 critical configs to p4 (/mnt/mmc2-4/.maple-backup/)
  - Then sysupgrade -n (skip broken backup/restore flow)
  - Supports -b (backup-only), --no-backup (direct flash), -h

maple-restore (/etc/uci-defaults/99-maple-restore):
  - Auto-restores configs from p4 on first boot after upgrade
  - Restores: shadow, openclash, frpc, firewall, network, dhcp,
    fstab, dropbear, transmission, aria2, tailscale, dockerd, etc.
  - Restarts services (frpc, openclash, cron, dropbear)
  - Runs once via uci-defaults, then self-deletes

Fixes the long-standing issue: sysupgrade without -n hangs due to
0-byte tgz backup + stage2 ubus deadlock. With maple-upgrade:
config is safely backed up to p4 (survives reflash), then -n flash
works perfectly, and config is restored automatically.
This commit is contained in:
xiexiaobao 2026-07-08 10:19:03 +08:00
parent 981e49c6f8
commit d1f08269c6
5 changed files with 276 additions and 0 deletions

View File

@ -0,0 +1,48 @@
#
# Copyright (C) 2026 xiexiaobao
#
# This is free software, licensed under the GPL 2.0 license.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=maple-restore
PKG_VERSION:=1.0
PKG_RELEASE:=1
PKG_MAINTAINER:=xiexiaobao
PKG_LICENSE:=GPL-2.0
include $(INCLUDE_DIR)/package.mk
define Package/maple-restore
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Auto-restore configs from p4 after MapleOS sysupgrade
PKGARCH:=all
DEPENDS:=
endef
define Package/maple-restore/description
UCI defaults script that auto-restores backed-up configs from
/mnt/mmc2-4/.maple-backup/ on first boot after a maple-upgrade.
Restores: shadow, frpc, openclash, firewall, network, dhcp, fstab,
dropbear, luci, system, uhttpd, transmission, aria2, tinyproxy,
tailscale, dockerd, crontab, and more.
Runs exactly once via uci-defaults, then self-deletes.
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/maple-restore/install
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) ./files/99-maple-restore.sh $(1)/etc/uci-defaults/99-maple-restore
endef
$(eval $(call BuildPackage,maple-restore))

View File

@ -0,0 +1,52 @@
#!/bin/sh
# /etc/uci-defaults/99-maple-restore
# Auto-restore configs from p4 after MapleOS sysupgrade.
# Runs once on first boot, then deleted by uci-defaults.
BACKUP_DIR="/mnt/mmc2-4/.maple-backup"
DONE_FILE="/etc/maple-restore.done"
# Guard: only run once
[ -f "$DONE_FILE" ] && exit 0
# Guard: backup must exist
if [ ! -d "$BACKUP_DIR" ]; then
logger -t "maple-restore" "No backup found at $BACKUP_DIR, skipping"
exit 0
fi
# Count files to restore
RESTORED=0
for f in $(cd "$BACKUP_DIR" && find . -type f 2>/dev/null); do
src="${BACKUP_DIR}/${f}"
dst="/${f}"
# Skip if destination exists and is identical
[ -f "$dst" ] && cmp -s "$src" "$dst" && continue
dst_dir=$(dirname "$dst")
mkdir -p "$dst_dir" 2>/dev/null
# For shadow, preserve original permissions
if [ "$dst" = "/etc/shadow" ]; then
cp -a "$src" "$dst" 2>/dev/null && chmod 0640 "$dst" 2>/dev/null
else
cp -a "$src" "$dst" 2>/dev/null
fi
RESTORED=$((RESTORED + 1))
done
logger -t "maple-restore" "Restored $RESTORED config files from p4 backup"
# Restart services to pick up restored configs
for svc in frpc openclash cron dropbear; do
if [ -x "/etc/init.d/$svc" ]; then
/etc/init.d/$svc restart 2>/dev/null
logger -t "maple-restore" "Restarted $svc"
fi
done
# Mark done
touch "$DONE_FILE"
exit 0

View File

@ -0,0 +1,50 @@
#
# Copyright (C) 2026 xiexiaobao
#
# This is free software, licensed under the GPL 2.0 license.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=maple-upgrade
PKG_VERSION:=1.0
PKG_RELEASE:=1
PKG_MAINTAINER:=xiexiaobao
PKG_LICENSE:=GPL-2.0
include $(INCLUDE_DIR)/package.mk
define Package/maple-upgrade
SECTION:=utils
CATEGORY:=Utilities
TITLE:=MapleOS firmware upgrade with auto-config backup
PKGARCH:=all
DEPENDS:=
endef
define Package/maple-upgrade/description
Safely upgrade MapleOS firmware with automatic config backup to p4.
Usage:
maple-upgrade <firmware.img.gz> Back up configs then sysupgrade -n
maple-upgrade -b Backup-only mode (no flash)
maple-upgrade --no-backup <file> Direct sysupgrade -n (skip backup)
maple-upgrade -h Show help
Backups go to /mnt/mmc2-4/.maple-backup/ and are auto-restored
by maple-restore on next boot.
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/maple-upgrade/install
$(INSTALL_DIR) $(1)/sbin
$(INSTALL_BIN) ./files/maple-upgrade.sh $(1)/sbin/maple-upgrade
endef
$(eval $(call BuildPackage,maple-upgrade))

View File

@ -0,0 +1,122 @@
#!/bin/sh
# /sbin/maple-upgrade - MapleOS firmware upgrade with auto-config backup
#
# Usage:
# maple-upgrade <firmware.img.gz> Backup configs then sysupgrade -n
# maple-upgrade -b Backup-only mode (no flash)
# maple-upgrade --no-backup <file> Direct sysupgrade -n (skip backup)
# maple-upgrade -h Show help
BACKUP_DIR="/mnt/mmc2-4/.maple-backup"
BACKUP_LIST="
/etc/shadow
/etc/config/dropbear
/etc/config/openclash
/etc/config/firewall
/etc/config/frpc
/etc/config/dhcp
/etc/config/fstab
/etc/config/network
/etc/config/luci
/etc/config/system
/etc/config/uhttpd
/etc/config/tinyproxy
/etc/config/nlbwmon
/etc/config/aria2
/etc/config/hd-idle
/etc/config/transmission
/etc/config/ttyd
/etc/config/tailscale
/etc/config/dockerd
/etc/config/upnpd
/etc/crontabs/root
"
SERVICES_RESTORE="frpc openclash cron dropbear"
log() {
echo "[maple-upgrade] $*"
}
die() {
echo "[maple-upgrade] ERROR: $*" >&2
exit 1
}
show_help() {
cat <<'EOF'
MapleOS firmware upgrade with auto-config backup to p4
Usage:
maple-upgrade <firmware.img.gz> Back up configs, then sysupgrade -n
maple-upgrade -b Backup only, do not flash
maple-upgrade --no-backup <file> Skip backup, direct sysupgrade -n
maple-upgrade -h Show this help
Backups stored in /mnt/mmc2-4/.maple-backup/
Auto-restored by maple-restore (uci-defaults) on first boot after upgrade.
EOF
}
backup_configs() {
local count=0
# Ensure backup directory on p4
if ! mountpoint -q /mnt/mmc2-4 2>/dev/null; then
# p4 might not be mounted yet; try to mount it
mkdir -p /mnt/mmc2-4 2>/dev/null
mount /dev/mmcblk2p4 /mnt/mmc2-4 2>/dev/null || {
die "Cannot mount p4 (/dev/mmcblk2p4) for backup. Is the partition table set up?"
}
fi
mkdir -p "$BACKUP_DIR" || die "Cannot create $BACKUP_DIR"
# Remove old backup
rm -rf "${BACKUP_DIR:?}"/*
log "Backing up configs to $BACKUP_DIR ..."
for f in $BACKUP_LIST; do
if [ -f "$f" ]; then
dir="$BACKUP_DIR$(dirname "$f")"
mkdir -p "$dir" 2>/dev/null
cp -a "$f" "${BACKUP_DIR}${f}" 2>/dev/null && count=$((count + 1))
fi
done
log "Backed up $count files."
[ "$count" -gt 0 ] || die "No config files were backed up!"
}
# ---- Main ----
case "${1:-}" in
-h|--help)
show_help
exit 0
;;
-b|--backup)
backup_configs
log "Backup complete. Run 'maple-upgrade <firmware>' to upgrade."
exit 0
;;
--no-backup)
[ -n "$2" ] || die "Missing firmware file. Usage: maple-upgrade --no-backup <firmware.img.gz>"
shift
log "Skipping backup, running sysupgrade -n on $1 ..."
sysupgrade -n "$1"
;;
"")
show_help
exit 1
;;
*)
FIRMWARE="$1"
[ -f "$FIRMWARE" ] || die "Firmware file not found: $FIRMWARE"
backup_configs
log "Backup done. Running sysupgrade -n ..."
sysupgrade -n "$FIRMWARE"
;;
esac

View File

@ -208,6 +208,10 @@ CONFIG_PACKAGE_openssh-sftp-server=y
# Custom branding
CONFIG_PACKAGE_branding=y
# MapleOS auto-config backup/restore (sysupgrade)
CONFIG_PACKAGE_maple-upgrade=y
CONFIG_PACKAGE_maple-restore=y
# Rockchip hardware acceleration
CONFIG_PACKAGE_kmod-rk_vcodec=y
CONFIG_PACKAGE_kmod-rga3=y