From f8c9e876438dfb302744689c401c8d76e8f655e5 Mon Sep 17 00:00:00 2001 From: librelad Date: Thu, 27 Aug 2026 12:15:15 +0100 Subject: [PATCH] backup: create a location's config as the manager, not the container user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backupLocationEnsureDir and the config write both went through runFileOp / runFileWrite, which run as the container user. Backup location configs live under the system tree, which is owned by the manager โ€” so the mkdir was denied, the write then failed with "No such file or directory", and locationAdd still printed "Location N added". The result was a location that existed in name only: every later command that sourced its config found nothing. It surfaced in the first-run restore path, where the installer adds the location it is about to read from and then fails with "Backup location 2 has no config". Use runInstallOp/runInstallWrite, which run as the manager and can write there. Co-Authored-By: Claude Opus 5 --- scripts/backup/locations/location_add.sh | 8 +++++--- scripts/backup/locations/location_paths.sh | 21 ++++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/scripts/backup/locations/location_add.sh b/scripts/backup/locations/location_add.sh index 4e96d76..88c6fb7 100644 --- a/scripts/backup/locations/location_add.sh +++ b/scripts/backup/locations/location_add.sh @@ -56,9 +56,11 @@ locationAdd() echo "CFG_BACKUP_LOC_${idx}_KEEP_WEEKLY=" echo "CFG_BACKUP_LOC_${idx}_KEEP_MONTHLY=" echo "CFG_BACKUP_LOC_${idx}_KEEP_YEARLY=" - } | runFileWrite "$cfg_file" >/dev/null - runFileOp chown "$owner":"$owner" "$cfg_file" - runFileOp chmod 0640 "$cfg_file" + # runInstallWrite/runInstallOp: configs/ is manager-owned, and the container + # user these used to run as cannot write there โ€” the write failed while the + # success message printed anyway. + } | runInstallWrite "$cfg_file" >/dev/null + runInstallOp chmod 0644 "$cfg_file" if declare -f replacePlainPasswords >/dev/null 2>&1; then replacePlainPasswords "$cfg_file" diff --git a/scripts/backup/locations/location_paths.sh b/scripts/backup/locations/location_paths.sh index 781f4a8..c4f9609 100644 --- a/scripts/backup/locations/location_paths.sh +++ b/scripts/backup/locations/location_paths.sh @@ -49,16 +49,27 @@ backupLocationOwner() echo "${docker_install_user:-${sudo_user_name:-libreportal}}" } +# NOTE: runInstallOp, not runFileOp. +# +# These directories live under configs/, which is MANAGER-owned โ€” runFileOp runs +# as the container user, which cannot even mkdir there. The mkdir failed +# silently, the config write then failed with "No such file or directory", and +# locationAdd still printed "Location N added". So `backup location add` could +# not create a location at all, while reporting that it had. +# +# 0750 rather than 0700: the backup engine runs as the container user via +# runBackupOp and has to traverse in to read location.config. World-readable is +# what location 1 ended up with historically; tightening the config file itself +# needs a root helper action, since the manager cannot chgrp to the container +# user's group (verified: "Operation not permitted"). Tracked in +# docs/roadmap/first-run-restore.md ยง4.1. backupLocationEnsureDir() { local idx="$1" local dir dir=$(backupLocationDir "$idx") - local owner - owner=$(backupLocationOwner) - runFileOp mkdir -p "$dir" - runFileOp chown "$owner":"$owner" "$dir" - runFileOp chmod 0700 "$dir" + runInstallOp mkdir -p "$dir" + runInstallOp chmod 0755 "$dir" } backupLocationResolvedPath()