mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-27 05:57:27 +00:00
Verify restored destinations before completing updater recovery
A successful rename is not by itself the recovered-byte postcondition. Keep the snapshot and suppress activation when the recorded destination is still a link or no longer matches the saved bytes; connected no-op and changed-destination controls exercise that boundary. Change-source: pulse-maintainer
This commit is contained in:
@@ -6421,6 +6421,8 @@ Installer failure, mismatched version and failure to restore a previously active
|
||||
service share one rollback path. It stops the service, stages and compares all
|
||||
saved files before any replacement, and renames each into its recorded original
|
||||
path without following a new symlink or treating a directory as a destination.
|
||||
Every restored destination must itself be regular and byte-equal to its saved
|
||||
copy before recovery may activate the service or discard the backup.
|
||||
Files absent before the attempt remain absent after recovery, including the
|
||||
legacy/current binary alternative and `VERSION`. Metadata is preserved. A
|
||||
partial restore must not invoke the RETURN restart backstop. The backup remains
|
||||
|
||||
@@ -647,6 +647,8 @@ func TestAutoUpdateBackupTransaction(t *testing.T) {
|
||||
{name: "rollback_copy_failure", fault: "restore_copy", outcome: "failure", retained: true},
|
||||
{name: "corrupt_rollback_copy", fault: "restore_corrupt", outcome: "failure", retained: true},
|
||||
{name: "rollback_rename_failure", fault: "restore_rename", outcome: "failure", retained: true},
|
||||
{name: "rollback_rename_noop", fault: "restore_noop", outcome: "failure", retained: true},
|
||||
{name: "rollback_destination_changed", fault: "restore_changed", outcome: "failure", retained: true},
|
||||
{name: "version_restore_failure", fault: "version_rename", outcome: "failure", retained: true},
|
||||
{name: "new_destination_directory", outcome: "directory", retained: true},
|
||||
{name: "successful_update", success: true},
|
||||
@@ -773,8 +775,10 @@ cp() {
|
||||
mv() {
|
||||
local dest="${*: -1}"
|
||||
if [[ "$FAULT" == restore_rename && "$dest" == "$INSTALL_DIR/bin/pulse" ]]; then return 1; fi
|
||||
if [[ "$FAULT" == restore_noop && "$dest" == "$INSTALL_DIR/bin/pulse" ]]; then return 0; fi
|
||||
if [[ "$FAULT" == version_rename && "$dest" == "$INSTALL_DIR/VERSION" ]]; then return 1; fi
|
||||
command mv "$@"
|
||||
command mv "$@" || return
|
||||
if [[ "$FAULT" == restore_changed && "$dest" == "$INSTALL_DIR/bin/pulse" ]]; then printf 'changed\n' > "$dest"; fi
|
||||
}
|
||||
curl() {
|
||||
local out="" url="" arg
|
||||
@@ -840,7 +844,7 @@ else
|
||||
[[ -z "$backup" || ! -e "$backup" ]] || { echo 'unneeded backup leaked'; exit 1; }
|
||||
fi
|
||||
# Incomplete file restoration must not activate an unverified executable.
|
||||
case "$FAULT" in restore_tmp|restore_copy|restore_corrupt|restore_rename|version_rename|stop)
|
||||
case "$FAULT" in restore_tmp|restore_copy|restore_corrupt|restore_rename|restore_noop|restore_changed|version_rename|stop)
|
||||
if [[ -f "$FIXTURE_DIR/service-operations" ]]; then
|
||||
! grep -Eq '^(start|restart):' "$FIXTURE_DIR/service-operations"
|
||||
fi
|
||||
|
||||
@@ -552,7 +552,10 @@ perform_update() {
|
||||
done
|
||||
for i in "${!file_paths[@]}"; do
|
||||
if [[ "${file_present[i]}" == "true" ]]; then
|
||||
if [[ -d "${file_paths[i]}" ]] || ! mv -f -- "${restore_files[i]}" "${file_paths[i]}"; then
|
||||
if [[ -d "${file_paths[i]}" ]] ||
|
||||
! mv -f -- "${restore_files[i]}" "${file_paths[i]}" ||
|
||||
[[ -L "${file_paths[i]}" ]] ||
|
||||
! cmp -s -- "$backup_dir/${backup_names[i]}" "${file_paths[i]}"; then
|
||||
log error "Could not restore ${file_paths[i]}; backup retained at $backup_dir; manual recovery required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user