From b4fabfbff866c7e2040d64fd6e55ac99df9a8aa9 Mon Sep 17 00:00:00 2001 From: Tom Lehmann Date: Mon, 28 Sep 2026 10:15:55 +0200 Subject: [PATCH] fix(linbo_sync): fall back to the plain group for windows boot files linbo_create_image stores the windows boot files BCD and bsmbr in the image keyed by $HOSTGROUP (older images may also carry gptlabel), and linbo_sync restores them by the same name. Once hardware groups of non-default schools become school qualified (+, #178), a migrated client syncing an image created before looks for e.g. BCD.abc+raum101, finds only BCD.raum101 and skips the BCD and boot sector restore (and the gpt label restore for older images). On EFI that can leave Windows unbootable. Look the files up through a new group_bootfile(), which falls back to the plain group (the part after the "+") when only that file exists. For default-school groups (no "+") both names are the same, so behaviour there is unchanged. Refs #178 Signed-off-by: Tom Lehmann Assisted-by: Claude --- src/linbofs/usr/bin/linbo_sync | 27 +++++++++++---- tests/shell/test_linbo_sync.sh | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 tests/shell/test_linbo_sync.sh diff --git a/src/linbofs/usr/bin/linbo_sync b/src/linbofs/usr/bin/linbo_sync index a29f9c6..4561241 100755 --- a/src/linbofs/usr/bin/linbo_sync +++ b/src/linbofs/usr/bin/linbo_sync @@ -221,6 +221,19 @@ set_guid(){ } +# group_bootfile path [suffix] +# prints path.$HOSTGROUP[suffix], a group keyed windows boot file stored in the +# image. Falls back to the plain group name (the part after "+") if +# only that one exists, as in images created before hardware groups of +# non-default schools became school qualified (#178). +group_bootfile(){ + local file="$1.$HOSTGROUP$2" + local plainfile="$1.${HOSTGROUP#*+}$2" + [ ! -e "$file" -a -e "$plainfile" ] && file="$plainfile" + echo "$file" +} + + # patch fstab with root & efi partitions: patch_fstab rootdev patch_fstab(){ echo -n "patch_fstab " ; printargs "$@" @@ -397,11 +410,11 @@ syncl(){ local srcdir="$(ls -d /mnt/[Ww][Ii][Nn][Dd][Oo][Ww][Ss]/[Bb][Oo][Oo][Tt]/[Ee][Ff][Ii] 2> /dev/null)" [ -n "$srcdir" ] && cp -r "$srcdir"/* "$bootdir/" fi - bcd_backup="$bootdir"/BCD."$HOSTGROUP"."$partname" + bcd_backup="$(group_bootfile "$bootdir"/BCD ."$partname")" else # bios bootdir="$(ls -d /mnt/[Bb][Oo][Oo][Tt])" - bcd_backup="$bootdir"/BCD."$HOSTGROUP" + bcd_backup="$(group_bootfile "$bootdir"/BCD)" fi # restore bcd @@ -412,11 +425,11 @@ syncl(){ # restore disk boot sector # detect old versions - local bsmbr="$bootdir"/bsmbr."$HOSTGROUP" - local bsmbr_old="$bootdir"/winmbr446."$HOSTGROUP" + local bsmbr="$(group_bootfile "$bootdir"/bsmbr)" + local bsmbr_old="$(group_bootfile "$bootdir"/winmbr446)" [ -e "$bsmbr_old" ] && mv "$bsmbr_old" "$bsmbr" - [ -e "$bsmbr" ] || bsmbr="$bootdir"/winmbr."$HOSTGROUP" - [ -e "$bsmbr" ] || bsmbr="$bootdir"/win7mbr."$HOSTGROUP" + [ -e "$bsmbr" ] || bsmbr="$(group_bootfile "$bootdir"/winmbr)" + [ -e "$bsmbr" ] || bsmbr="$(group_bootfile "$bootdir"/win7mbr)" if [ -e "$bsmbr" ]; then echo "Restoring windows bootloader from $(basename "$bsmbr")" case "$bsmbr" in @@ -435,7 +448,7 @@ syncl(){ # restore disk & partition uuids on efi systems if [ -n "$efipart" ]; then # restore old gptlabel - local gptlabel="$bootdir"/gptlabel."$HOSTGROUP" + local gptlabel="$(group_bootfile "$bootdir"/gptlabel)" # only if no disk guid file exists if [ -s "$gptlabel" -a ! -s /mnt/.guid.disk ]; then echo "Saving current gpt label." diff --git a/tests/shell/test_linbo_sync.sh b/tests/shell/test_linbo_sync.sh new file mode 100644 index 0000000..10ebb09 --- /dev/null +++ b/tests/shell/test_linbo_sync.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# +# test_linbo_sync.sh +# tom.lehmann@netzint.de +# 20260928 +# +# group_bootfile: fallback from the school qualified to the plain hardware +# group for windows boot files stored in existing images (#178). + +SCRIPT="$(dirname "$0")/../../src/linbofs/usr/bin/linbo_sync" + +oneTimeSetUp() { + . "$(dirname "$0")/lib/extract_function.sh" + extract_function "$SCRIPT" group_bootfile > "$SHUNIT_TMPDIR/linbo_sync_group_bootfile.sh" + . "$SHUNIT_TMPDIR/linbo_sync_group_bootfile.sh" +} + +setUp() { + bootdir="$SHUNIT_TMPDIR/boot" + rm -rf "$bootdir" + mkdir -p "$bootdir" +} + +test_default_school_group_is_unchanged() { + HOSTGROUP="raum101" + assertEquals "$bootdir/BCD.raum101" "$(group_bootfile "$bootdir"/BCD)" + touch "$bootdir/BCD.raum101" + assertEquals "$bootdir/BCD.raum101" "$(group_bootfile "$bootdir"/BCD)" +} + +test_qualified_file_is_preferred() { + HOSTGROUP="abc+raum101" + touch "$bootdir/bsmbr.abc+raum101" "$bootdir/bsmbr.raum101" + assertEquals "$bootdir/bsmbr.abc+raum101" "$(group_bootfile "$bootdir"/bsmbr)" +} + +test_falls_back_to_plain_group() { + HOSTGROUP="abc+raum101" + touch "$bootdir/gptlabel.raum101" + assertEquals "$bootdir/gptlabel.raum101" "$(group_bootfile "$bootdir"/gptlabel)" +} + +test_suffix_is_kept_on_fallback() { + HOSTGROUP="abc+raum101" + touch "$bootdir/BCD.raum101.nvme0n1p3" + assertEquals "$bootdir/BCD.raum101.nvme0n1p3" "$(group_bootfile "$bootdir"/BCD .nvme0n1p3)" +} + +test_neither_exists_gives_qualified_name() { + HOSTGROUP="abc+raum101" + assertEquals "$bootdir/BCD.abc+raum101" "$(group_bootfile "$bootdir"/BCD)" +} + +test_plain_group_with_hyphen() { + HOSTGROUP="gs-nord+raum-1_a" + touch "$bootdir/BCD.raum-1_a" + assertEquals "$bootdir/BCD.raum-1_a" "$(group_bootfile "$bootdir"/BCD)" +} + +. "$(dirname "$0")/shunit2"