From 26b0d4c357459a67dd0ad71bf63ee5c0cbc530e8 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 1 Sep 2026 13:42:31 -0700 Subject: [PATCH] deploy-remote: privileged rename + deep merge in migrate_data_root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rename() of a directory requires write on the directory itself; mariadb/ redis data roots are owned by the containers' mapped subuids (100998), so the deploy user cannot move them. Use run_privileged mv (numeric ownership preserved = rootless mapping contract) and skip the chown pass for romm via the new keep mode — chowning mapped subuid data to the deploy uid breaks the in-container mysql/redis users. Collisions with rescued anonymous-volume stubs now merge one level deep instead of stranding real data under /opt. Reverse the podman rm order so the dependent romm container is removed before its backends. --- scripts/deploy-remote.sh | 56 ++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh index 2ad5cbc..d3cef9f 100755 --- a/scripts/deploy-remote.sh +++ b/scripts/deploy-remote.sh @@ -537,25 +537,43 @@ wait_container_healthy() { # the unit; re-running the handler on an already-active unit restarts only the # selected unit. -migrate_data_root() { # $1 = old /opt path, $2 = new data-root path (same fs: rename/merge) - local from="$1" to="$2" entry +migrate_data_root() { # $1=old /opt path, $2=new data root, $3=chown|keep (default chown) + local from="$1" to="$2" mode="${3:-chown}" entry sub base [[ -d "$from" ]] || return 0 - if [[ -d "$to" && -n "$(ls -A "$to" 2>/dev/null)" ]]; then - # The destination already holds seeded state (env file, rescued - # anonymous-volume data). Merge the old root into it without ever - # overwriting an existing entry, then drop the emptied source. - shopt -s dotglob - for entry in "$from"/*; do - [[ -e "$entry" ]] || continue - mv -n -- "$entry" "$to/" - done - shopt -u dotglob - rmdir -- "$from" 2>/dev/null || \ - echo "WARN: $from not empty after merge (collisions kept old side); left in place" >&2 - else - mv -- "$from" "$to" + # Renaming a directory needs WRITE permission on the directory being + # renamed itself. Compose-era data roots contain directories owned by + # the containers' mapped host uids (mariadb/redis land in the subuid + # range as 100998), which the deploy user cannot rename. Privileged mv + # preserves numeric ownership — the rootless mapping contract. + mkdir -p "$to" + shopt -s dotglob + for entry in "$from"/*; do + [[ -e "$entry" || -L "$entry" ]] || continue + base="${entry##*/}" + if [[ -e "$to/$base" ]]; then + # Collides with pre-seeded state (env file) or a rescued + # anonymous-volume stub: merge one level deep, never overwriting + # an existing entry. + if [[ -d "$entry" && -d "$to/$base" ]]; then + for sub in "$entry"/*; do + [[ -e "$sub" || -L "$sub" ]] || continue + run_privileged mv -n -- "$sub" "$to/$base/" + done + run_privileged rmdir -- "$entry" 2>/dev/null || \ + echo "WARN: $entry not empty after merge; left in place" >&2 + else + echo "WARN: collision on $to/$base kept existing; $entry left in place" >&2 + fi + continue + fi + run_privileged mv -- "$entry" "$to/" + done + shopt -u dotglob + run_privileged rmdir -- "$from" 2>/dev/null || \ + echo "WARN: $from not empty after migration; left in place" >&2 + if [[ "$mode" == chown ]]; then + chown -R "$(id -u):$(id -g)" "$to" fi - chown -R "$(id -u):$(id -g)" "$to" } deploy_romm_stack() { @@ -580,7 +598,7 @@ deploy_romm_stack() { local vols vols="$(podman inspect -f '{{range .Mounts}}{{if eq .Type "volume"}}{{.Name}} {{end}}{{end}}' romm 2>/dev/null || true)" podman stop -t 60 romm-mariadb romm-redis romm || true - podman rm romm-mariadb romm-redis romm || true + podman rm romm romm-redis romm-mariadb || true for vol in $vols; do tmp="$(mktemp -d)" podman volume export --output "$tmp/v.tar" "$vol" @@ -589,7 +607,7 @@ deploy_romm_stack() { tar --skip-old-files -C /home/dank/Containers/romm -xf "$tmp/v.tar" rm -rf -- "$tmp" done - migrate_data_root /opt/romm /home/dank/Containers/romm + migrate_data_root /opt/romm /home/dank/Containers/romm keep systemctl --user start romm-mariadb.service wait_container_healthy romm-mariadb 120 || return 1 systemctl --user start romm-redis.service