From 5f802959c25a03f8b095f798dfb3913d4a53e9f8 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Thu, 22 Oct 2026 10:14:14 -0500 Subject: [PATCH] Fixed hub upgrade PG migration failing with Permission denied on old binaries Ticket: ENT-14899 Changelog: Title --- packaging/common/cfengine-hub/postinstall.sh | 20 ++++++++++++++++++- packaging/common/cfengine-hub/preinstall.sh | 8 +++++++- .../common/script-templates/script-common.sh | 15 ++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packaging/common/cfengine-hub/postinstall.sh b/packaging/common/cfengine-hub/postinstall.sh index 508080cf2..a20c7d4be 100644 --- a/packaging/common/cfengine-hub/postinstall.sh +++ b/packaging/common/cfengine-hub/postinstall.sh @@ -631,7 +631,9 @@ migrate_db_using_dump_file() { safe_mv "$PREFIX/share" postgresql "$PREFIX/share.new" # restore old backup safe_cp "$BACKUP_DIR" data "$PREFIX/state/pg" - safe_cp "$BACKUP_DIR" bin "$PREFIX" + safe_cp_nolink "$BACKUP_DIR" bin "$PREFIX" + # Ensure restored old binaries are executable by cfpostgres. + chmod 755 "$PREFIX/bin"/* 2>/dev/null || true on_files cp "$BACKUP_DIR/lib" "$PREFIX/lib" safe_cp "$BACKUP_DIR/lib" postgresql "$PREFIX/lib" safe_cp "$BACKUP_DIR/share" postgresql "$PREFIX/share" @@ -745,6 +747,22 @@ do_migration() { else DEBUG=0 fi + + # Preflight: verify cfpostgres can execute the old binaries before any + # destructive operation. If not, attempt a one-shot chmod repair. + if ! su cfpostgres -c "LD_LIBRARY_PATH=$BACKUP_DIR/lib/ $BACKUP_DIR/bin/pg_ctl --version" >/dev/null 2>&1; then + cf_console echo "cfpostgres cannot execute $BACKUP_DIR/bin/pg_ctl — attempting mode repair" + chmod 755 "$BACKUP_DIR/bin"/* 2>/dev/null || true + chmod 711 "$BACKUP_DIR" "$BACKUP_DIR/bin" 2>/dev/null || true + if ! su cfpostgres -c "LD_LIBRARY_PATH=$BACKUP_DIR/lib/ $BACKUP_DIR/bin/pg_ctl --version" >/dev/null 2>&1; then + cf_console echo "ERROR: cfpostgres still cannot execute $BACKUP_DIR/bin/pg_ctl after chmod repair." + ls -la "$BACKUP_DIR/bin/" >&2 + cf_console echo "Please fix permissions on $BACKUP_DIR/bin/ and retry the upgrade." + exit 1 + fi + cf_console echo "Mode repair succeeded, continuing migration." + fi + cd /tmp cf_console echo "Migrating database using pg_upgrade utility..." cf_console echo diff --git a/packaging/common/cfengine-hub/preinstall.sh b/packaging/common/cfengine-hub/preinstall.sh index 5e2b81368..c2a78dd18 100644 --- a/packaging/common/cfengine-hub/preinstall.sh +++ b/packaging/common/cfengine-hub/preinstall.sh @@ -252,7 +252,13 @@ if migrating_postgres; then # the postgresql.conf file was modified, we should try to use it after migration cp -a "$BACKUP_DIR/data/postgresql.conf" "$BACKUP_DIR/data/postgresql.conf.modified" fi - safe_cp "$PREFIX" bin "$BACKUP_DIR" + # Copy, don't hardlink: policy managing perms under $PREFIX/state/pg (e.g. + # CFE-951 in masterfiles) would otherwise change the live binaries too. + safe_cp_nolink "$PREFIX" bin "$BACKUP_DIR" + # Normalize executable permissions so cfpostgres can run old PG binaries + # during migration, whatever mode damage the source already had. + chmod 755 "$BACKUP_DIR/bin"/* 2>/dev/null || true + chmod 711 "$BACKUP_DIR/bin" 2>/dev/null || true on_files cp "$PREFIX/lib" "$BACKUP_DIR/lib" safe_cp "$PREFIX/lib" postgresql "$BACKUP_DIR/lib" safe_cp "$PREFIX/share" postgresql "$BACKUP_DIR/share" diff --git a/packaging/common/script-templates/script-common.sh b/packaging/common/script-templates/script-common.sh index 432102315..6797104a9 100644 --- a/packaging/common/script-templates/script-common.sh +++ b/packaging/common/script-templates/script-common.sh @@ -136,6 +136,21 @@ safe_cp() { return 1 } +safe_cp_nolink() { + # Like `safe_cp`, but never creates hardlinks, so the copy does not share + # mode and owner with the source. Args are same as in safe_cp. + test "$#" -eq 3 || return 2 + from="$1" + name="$2" + to="$3" + if cp -a "$from/$name" "$to"; then + return 0 + fi + echo "Copy failed, so removing partially-copied data and aborting" + rm -rf "$to/$name" + return 1 +} + safe_mv() { # "safe" alternative to `mv`. Executes `safe_cp` and deletes source dir if # that succeeds.