aboutsummaryrefslogtreecommitdiff
path: root/custom
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-24 14:28:51 -0600
committerCraig Jennings <c@cjennings.net>2026-01-24 14:28:51 -0600
commit33ab3dba2cdba0c94f8dde7b421a2a2b0ec8dae3 (patch)
tree692361ab00ab22050679cd9d812b8c8de54c1cca /custom
parentfc11bf6e60fe5e6b53cb87fb5d6c29ac9b00af4d (diff)
downloadarchangel-33ab3dba2cdba0c94f8dde7b421a2a2b0ec8dae3.tar.gz
archangel-33ab3dba2cdba0c94f8dde7b421a2a2b0ec8dae3.zip
Phase 3: Add multi-disk btrfs support (RAID0/RAID1)
- RAID1 (mirror) and RAID0 (stripe) for 2+ disks - Multi-disk LUKS with single passphrase prompt - EFI redundancy: GRUB installed on all disks - Pacman hook syncs GRUB updates across EFI partitions - btrfs initramfs hook for multi-device assembly at boot
Diffstat (limited to 'custom')
-rwxr-xr-xcustom/archangel83
-rw-r--r--custom/lib/btrfs.sh276
2 files changed, 322 insertions, 37 deletions
diff --git a/custom/archangel b/custom/archangel
index ba0c94e..14679cb 100755
--- a/custom/archangel
+++ b/custom/archangel
@@ -1596,32 +1596,57 @@ install_zfs() {
#############################
install_btrfs() {
- # Get partition references
- local root_part=$(get_root_partition "${SELECTED_DISKS[0]}")
- local efi_part=$(get_efi_partition "${SELECTED_DISKS[0]}")
- local btrfs_device="$root_part"
+ local num_disks=${#SELECTED_DISKS[@]}
+ local btrfs_devices=()
+ local efi_parts=()
+ local root_parts=()
- # Partition and format
- partition_disk "${SELECTED_DISKS[0]}"
- format_efi "$efi_part"
+ # Collect partition references for all disks
+ for disk in "${SELECTED_DISKS[@]}"; do
+ root_parts+=("$(get_root_partition "$disk")")
+ efi_parts+=("$(get_efi_partition "$disk")")
+ done
+
+ # Partition all disks
+ for disk in "${SELECTED_DISKS[@]}"; do
+ partition_disk "$disk"
+ done
+
+ # Format all EFI partitions
+ format_efi_partitions "${SELECTED_DISKS[@]}"
# LUKS encryption (if enabled)
if [[ "$NO_ENCRYPT" != "yes" ]]; then
- create_luks_container "$root_part" "$LUKS_PASSPHRASE"
- open_luks_container "$root_part" "$LUKS_PASSPHRASE"
- btrfs_device="/dev/mapper/$LUKS_MAPPER_NAME"
+ if [[ $num_disks -eq 1 ]]; then
+ # Single disk LUKS
+ create_luks_container "${root_parts[0]}" "$LUKS_PASSPHRASE"
+ open_luks_container "${root_parts[0]}" "$LUKS_PASSPHRASE"
+ btrfs_devices=("/dev/mapper/$LUKS_MAPPER_NAME")
+ else
+ # Multi-disk LUKS - encrypt each partition
+ create_luks_containers "$LUKS_PASSPHRASE" "${root_parts[@]}"
+ open_luks_containers "$LUKS_PASSPHRASE" "${root_parts[@]}"
+ btrfs_devices=($(get_luks_devices $num_disks))
+ fi
+ else
+ # No encryption - use raw partitions
+ btrfs_devices=("${root_parts[@]}")
fi
- # Create btrfs on the device (raw partition or LUKS mapper)
- create_btrfs_volume "$btrfs_device"
+ # Create btrfs filesystem
+ if [[ $num_disks -eq 1 ]]; then
+ create_btrfs_volume "${btrfs_devices[0]}"
+ else
+ create_btrfs_volume "${btrfs_devices[@]}" --raid-level "$RAID_LEVEL"
+ fi
- # Create and mount subvolumes
- create_btrfs_subvolumes "$btrfs_device"
- mount_btrfs_subvolumes "$btrfs_device"
+ # Create and mount subvolumes (use first device for mount)
+ create_btrfs_subvolumes "${btrfs_devices[0]}"
+ mount_btrfs_subvolumes "${btrfs_devices[0]}"
- # Mount EFI
+ # Mount primary EFI
mkdir -p /mnt/efi
- mount "$efi_part" /mnt/efi
+ mount "${efi_parts[0]}" /mnt/efi
# Install base system
install_base_btrfs
@@ -1633,14 +1658,24 @@ install_btrfs() {
# Configure encryption if enabled
if [[ "$NO_ENCRYPT" != "yes" ]]; then
- configure_crypttab "$root_part"
- configure_luks_grub "$root_part"
+ configure_crypttab "${root_parts[@]}"
+ configure_luks_grub "${root_parts[0]}"
configure_luks_initramfs
fi
- generate_btrfs_fstab "$btrfs_device" "$efi_part"
+ generate_btrfs_fstab "${btrfs_devices[0]}" "${efi_parts[0]}"
configure_btrfs_initramfs
- configure_grub "$efi_part"
+
+ # GRUB installation
+ if [[ $num_disks -eq 1 ]]; then
+ configure_grub "${efi_parts[0]}"
+ else
+ # Multi-disk: install GRUB to all EFI partitions
+ configure_grub "${efi_parts[0]}"
+ install_grub_all_efi "${efi_parts[@]}"
+ create_grub_sync_hook "${efi_parts[@]}"
+ fi
+
configure_snapper
configure_btrfs_services
configure_btrfs_pacman_hook
@@ -1652,7 +1687,11 @@ install_btrfs() {
# Cleanup
btrfs_cleanup
if [[ "$NO_ENCRYPT" != "yes" ]]; then
- close_luks_container
+ if [[ $num_disks -eq 1 ]]; then
+ close_luks_container
+ else
+ close_luks_containers $num_disks
+ fi
fi
print_btrfs_summary
}
diff --git a/custom/lib/btrfs.sh b/custom/lib/btrfs.sh
index 90c5e6d..279897e 100644
--- a/custom/lib/btrfs.sh
+++ b/custom/lib/btrfs.sh
@@ -68,19 +68,92 @@ close_luks_container() {
cryptsetup close "$name" 2>/dev/null || true
}
+# Multi-disk LUKS functions
+create_luks_containers() {
+ local passphrase="$1"
+ shift
+ local partitions=("$@")
+
+ step "Creating LUKS Encrypted Containers"
+
+ local i=0
+ for partition in "${partitions[@]}"; do
+ info "Setting up LUKS encryption on $partition..."
+ echo -n "$passphrase" | cryptsetup luksFormat --type luks2 \
+ --cipher aes-xts-plain64 --key-size 512 --hash sha512 \
+ --iter-time 2000 --pbkdf argon2id \
+ "$partition" - \
+ || error "Failed to create LUKS container on $partition"
+ ((i++))
+ done
+
+ info "Created $i LUKS containers."
+}
+
+open_luks_containers() {
+ local passphrase="$1"
+ shift
+ local partitions=("$@")
+
+ step "Opening LUKS Containers"
+
+ local i=0
+ for partition in "${partitions[@]}"; do
+ local name="${LUKS_MAPPER_NAME}${i}"
+ [[ $i -eq 0 ]] && name="$LUKS_MAPPER_NAME" # First one has no suffix
+ info "Opening LUKS container: $partition -> /dev/mapper/$name"
+ echo -n "$passphrase" | cryptsetup open "$partition" "$name" - \
+ || error "Failed to open LUKS container: $partition"
+ ((i++))
+ done
+
+ info "Opened ${#partitions[@]} LUKS containers."
+}
+
+close_luks_containers() {
+ local count="${1:-1}"
+
+ for ((i=0; i<count; i++)); do
+ local name="${LUKS_MAPPER_NAME}${i}"
+ [[ $i -eq 0 ]] && name="$LUKS_MAPPER_NAME"
+ cryptsetup close "$name" 2>/dev/null || true
+ done
+}
+
+# Get list of opened LUKS mapper devices
+get_luks_devices() {
+ local count="$1"
+ local devices=()
+
+ for ((i=0; i<count; i++)); do
+ local name="${LUKS_MAPPER_NAME}${i}"
+ [[ $i -eq 0 ]] && name="$LUKS_MAPPER_NAME"
+ devices+=("/dev/mapper/$name")
+ done
+
+ echo "${devices[@]}"
+}
+
configure_crypttab() {
- local partition="$1"
+ local partitions=("$@")
step "Configuring crypttab"
- local uuid
- uuid=$(blkid -s UUID -o value "$partition")
+ echo "# LUKS encrypted root partitions" > /mnt/etc/crypttab
- # Create crypttab entry
- echo "# LUKS encrypted root" > /mnt/etc/crypttab
- echo "$LUKS_MAPPER_NAME UUID=$uuid none luks,discard" >> /mnt/etc/crypttab
+ local i=0
+ for partition in "${partitions[@]}"; do
+ local uuid
+ uuid=$(blkid -s UUID -o value "$partition")
+ local name="${LUKS_MAPPER_NAME}${i}"
+ [[ $i -eq 0 ]] && name="$LUKS_MAPPER_NAME"
- info "crypttab configured for $LUKS_MAPPER_NAME"
+ echo "$name UUID=$uuid none luks,discard" >> /mnt/etc/crypttab
+ info "crypttab: $name -> UUID=$uuid"
+ ((i++))
+ done
+
+ info "crypttab configured for $i partition(s)"
}
configure_luks_initramfs() {
@@ -139,15 +212,66 @@ btrfs_preflight() {
# Btrfs Volume Creation
#############################
+# Create btrfs filesystem (single or multi-device)
+# Usage: create_btrfs_volume device1 [device2 ...] [--raid-level level]
create_btrfs_volume() {
- local partition="$1"
+ local devices=()
+ local raid_level=""
+
+ # Parse arguments
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --raid-level)
+ raid_level="$2"
+ shift 2
+ ;;
+ *)
+ devices+=("$1")
+ shift
+ ;;
+ esac
+ done
step "Creating Btrfs Filesystem"
- info "Formatting $partition as btrfs..."
- mkfs.btrfs -f -L "archroot" "$partition" || error "Failed to create btrfs filesystem"
-
- info "Btrfs filesystem created on $partition"
+ local num_devices=${#devices[@]}
+
+ if [[ $num_devices -eq 1 ]]; then
+ # Single device
+ info "Formatting ${devices[0]} as btrfs..."
+ mkfs.btrfs -f -L "archroot" "${devices[0]}" || error "Failed to create btrfs filesystem"
+ info "Btrfs filesystem created on ${devices[0]}"
+ else
+ # Multi-device RAID
+ local data_profile="raid1"
+ local meta_profile="raid1"
+
+ case "$raid_level" in
+ stripe)
+ data_profile="raid0"
+ meta_profile="raid1" # Always mirror metadata for safety
+ info "Creating striped btrfs (RAID0 data, RAID1 metadata) with $num_devices devices..."
+ ;;
+ mirror)
+ data_profile="raid1"
+ meta_profile="raid1"
+ info "Creating mirrored btrfs (RAID1) with $num_devices devices..."
+ ;;
+ *)
+ # Default to mirror for safety
+ data_profile="raid1"
+ meta_profile="raid1"
+ info "Creating mirrored btrfs (RAID1) with $num_devices devices..."
+ ;;
+ esac
+
+ mkfs.btrfs -f -L "archroot" \
+ -d "$data_profile" \
+ -m "$meta_profile" \
+ "${devices[@]}" || error "Failed to create btrfs filesystem"
+
+ info "Btrfs $raid_level filesystem created on ${devices[*]}"
+ fi
}
#############################
@@ -444,6 +568,121 @@ EOF
}
#############################
+# EFI Redundancy (Multi-disk)
+#############################
+
+# Install GRUB to all EFI partitions for redundancy
+install_grub_all_efi() {
+ local efi_partitions=("$@")
+
+ step "Installing GRUB to All EFI Partitions"
+
+ local i=1
+ for efi_part in "${efi_partitions[@]}"; do
+ # First EFI at /efi (already mounted), subsequent at /efi2, /efi3, etc.
+ local chroot_efi_dir="/efi"
+ local mount_point="/mnt/efi"
+ local bootloader_id="GRUB"
+
+ if [[ $i -gt 1 ]]; then
+ chroot_efi_dir="/efi${i}"
+ mount_point="/mnt/efi${i}"
+ bootloader_id="GRUB-disk${i}"
+
+ # Mount secondary EFI partitions
+ if ! mountpoint -q "$mount_point" 2>/dev/null; then
+ mkdir -p "$mount_point"
+ mount "$efi_part" "$mount_point" || { warn "Failed to mount $efi_part"; ((i++)); continue; }
+ # Also create the directory in chroot for grub-install
+ mkdir -p "/mnt${chroot_efi_dir}"
+ mount --bind "$mount_point" "/mnt${chroot_efi_dir}"
+ fi
+ fi
+
+ info "Installing GRUB to $efi_part ($bootloader_id)..."
+ arch-chroot /mnt grub-install --target=x86_64-efi \
+ --efi-directory="$chroot_efi_dir" \
+ --bootloader-id="$bootloader_id" \
+ --boot-directory=/boot \
+ || warn "GRUB install to $efi_part may have failed (continuing)"
+
+ ((i++))
+ done
+
+ info "GRUB installed to ${#efi_partitions[@]} EFI partition(s)."
+}
+
+# Create pacman hook to sync GRUB across all EFI partitions
+create_grub_sync_hook() {
+ local efi_partitions=("$@")
+
+ step "Creating GRUB Sync Hook"
+
+ # Only needed for multi-disk
+ if [[ ${#efi_partitions[@]} -lt 2 ]]; then
+ info "Single disk - no sync hook needed."
+ return
+ fi
+
+ # Create sync script
+ local script_content='#!/bin/bash
+# Sync GRUB to all EFI partitions after grub package update
+# Generated by archangel installer
+
+set -e
+
+EFI_PARTITIONS=('
+ for part in "${efi_partitions[@]}"; do
+ script_content+="\"$part\" "
+ done
+ script_content+=')
+
+PRIMARY_EFI="/efi"
+
+sync_grub() {
+ local i=0
+ for part in "${EFI_PARTITIONS[@]}"; do
+ if [[ $i -eq 0 ]]; then
+ # Primary - just reinstall GRUB
+ grub-install --target=x86_64-efi --efi-directory="$PRIMARY_EFI" \
+ --bootloader-id=GRUB --boot-directory=/boot 2>/dev/null || true
+ else
+ # Secondary - mount, install, unmount
+ local mount_point="/tmp/efi-sync-$i"
+ mkdir -p "$mount_point"
+ mount "$part" "$mount_point" 2>/dev/null || continue
+ grub-install --target=x86_64-efi --efi-directory="$mount_point" \
+ --bootloader-id="GRUB-disk$((i+1))" --boot-directory=/boot 2>/dev/null || true
+ umount "$mount_point" 2>/dev/null || true
+ rmdir "$mount_point" 2>/dev/null || true
+ fi
+ ((i++))
+ done
+}
+
+sync_grub
+'
+ echo "$script_content" > /mnt/usr/local/bin/grub-sync-efi
+ chmod +x /mnt/usr/local/bin/grub-sync-efi
+
+ # Create pacman hook
+ mkdir -p /mnt/etc/pacman.d/hooks
+ cat > /mnt/etc/pacman.d/hooks/99-grub-sync-efi.hook << 'HOOKEOF'
+[Trigger]
+Type = Package
+Operation = Upgrade
+Target = grub
+
+[Action]
+Description = Syncing GRUB to all EFI partitions...
+When = PostTransaction
+Exec = /usr/local/bin/grub-sync-efi
+HOOKEOF
+
+ info "GRUB sync hook created for ${#efi_partitions[@]} EFI partitions."
+}
+
+#############################
# Pacman Snapshot Hook
#############################
@@ -520,9 +759,16 @@ fallback_options="-S autodetect"
EOF
# Configure hooks for btrfs
- # btrfs module is built into kernel, but we need the btrfs hook for multi-device
- sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block filesystems fsck)/' \
- /mnt/etc/mkinitcpio.conf
+ # For multi-device btrfs, we need the btrfs hook for device assembly at boot
+ local num_disks=${#SELECTED_DISKS[@]}
+ if [[ $num_disks -gt 1 ]]; then
+ info "Multi-device btrfs: adding btrfs hook for device assembly"
+ sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block btrfs filesystems fsck)/' \
+ /mnt/etc/mkinitcpio.conf
+ else
+ sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block filesystems fsck)/' \
+ /mnt/etc/mkinitcpio.conf
+ fi
# Regenerate initramfs
info "Regenerating initramfs..."