diff options
Diffstat (limited to 'custom')
| -rwxr-xr-x | custom/archangel | 165 | ||||
| -rw-r--r-- | custom/lib/btrfs.sh | 423 | ||||
| -rw-r--r-- | custom/lib/disk.sh | 11 |
3 files changed, 584 insertions, 15 deletions
diff --git a/custom/archangel b/custom/archangel index f9bc637..c887a4d 100755 --- a/custom/archangel +++ b/custom/archangel @@ -29,6 +29,7 @@ source "$SCRIPT_DIR/lib/common.sh" source "$SCRIPT_DIR/lib/config.sh" source "$SCRIPT_DIR/lib/disk.sh" source "$SCRIPT_DIR/lib/zfs.sh" +source "$SCRIPT_DIR/lib/btrfs.sh" ############################# # Configuration @@ -80,7 +81,15 @@ echo "" preflight_checks() { require_root - zfs_preflight +} + +# Filesystem-specific preflight (called after filesystem is selected) +filesystem_preflight() { + if [[ "$FILESYSTEM" == "zfs" ]]; then + zfs_preflight + elif [[ "$FILESYSTEM" == "btrfs" ]]; then + btrfs_preflight + fi } ############################# @@ -108,9 +117,9 @@ gather_input() { fi fi - # Btrfs not yet implemented - if [[ "$FILESYSTEM" == "btrfs" ]]; then - error "Btrfs support not yet implemented. Use FILESYSTEM=zfs" + # Validate filesystem choice + if [[ "$FILESYSTEM" != "zfs" && "$FILESYSTEM" != "btrfs" ]]; then + error "Invalid FILESYSTEM: $FILESYSTEM (must be 'zfs' or 'btrfs')" fi # Determine RAID level if not specified @@ -143,12 +152,6 @@ gather_input() { echo "" select_filesystem - - # Check for btrfs (not yet implemented) - if [[ "$FILESYSTEM" == "btrfs" ]]; then - error "Btrfs support not yet implemented. Please select ZFS." - fi - get_hostname get_timezone get_locale @@ -156,8 +159,17 @@ gather_input() { get_disks get_raid_level get_wifi - get_encryption_choice - [[ "$NO_ENCRYPT" != "yes" ]] && get_zfs_passphrase + + # Encryption handling (filesystem-specific) + if [[ "$FILESYSTEM" == "zfs" ]]; then + get_encryption_choice + [[ "$NO_ENCRYPT" != "yes" ]] && get_zfs_passphrase + elif [[ "$FILESYSTEM" == "btrfs" ]]; then + # Btrfs encryption (LUKS) not yet implemented - Phase 2.8 + info "Note: Btrfs encryption (LUKS) will be available in a future update." + NO_ENCRYPT="yes" + fi + get_root_password get_ssh_config show_summary @@ -882,6 +894,44 @@ EOF info "Base system installed." } +install_base_btrfs() { + step "Installing Base System (Btrfs)" + + info "Updating pacman keys..." + pacman-key --init + pacman-key --populate archlinux + + info "Installing base packages (this takes a while)..." + yes "" | pacstrap -K /mnt \ + base \ + base-devel \ + linux-lts \ + linux-lts-headers \ + linux-firmware \ + btrfs-progs \ + grub \ + grub-btrfs \ + efibootmgr \ + snapper \ + snap-pac \ + networkmanager \ + avahi \ + nss-mdns \ + openssh \ + git \ + vim \ + sudo \ + zsh \ + nodejs \ + npm \ + ttf-dejavu \ + fzf \ + wget \ + wireless-regdb + + info "Base system installed." +} + configure_system() { step "Configuring System" @@ -1392,6 +1442,37 @@ print_summary() { echo "" } +print_btrfs_summary() { + echo "" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ Installation Complete! ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo "" + echo "System Configuration:" + echo " Hostname: $HOSTNAME" + echo " Timezone: $TIMEZONE" + echo " Filesystem: Btrfs" + echo "" + echo "Btrfs Snapshot Features:" + echo " - Boot from any snapshot via GRUB menu" + echo " - Genesis snapshot: pristine post-install state" + echo " - Pre/post pacman snapshots via snap-pac" + echo " - Timeline snapshots: 6 hourly, 7 daily, 2 weekly, 1 monthly" + echo "" + echo "GRUB Boot Menu:" + echo " - Select 'Arch Linux snapshots' submenu to boot from snapshots" + echo " - Snapshots auto-added when created by snapper" + echo "" + echo "Useful Commands:" + echo " List snapshots: snapper -c root list" + echo " Manual snapshot: snapper -c root create -d 'description'" + echo " Rollback: snapper -c root rollback <number>" + echo " Compare: snapper -c root diff <num1>..<num2>" + echo "" + info "Installation log: $LOGFILE" + echo "" +} + ############################# # Main ############################# @@ -1401,14 +1482,27 @@ main() { preflight_checks check_config gather_input + filesystem_preflight # Unattended installation begins echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "Beginning unattended installation..." + echo "Beginning unattended installation ($FILESYSTEM)..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" + if [[ "$FILESYSTEM" == "zfs" ]]; then + install_zfs + elif [[ "$FILESYSTEM" == "btrfs" ]]; then + install_btrfs + fi +} + +############################# +# ZFS Installation Path +############################# + +install_zfs() { partition_disks create_zfs_pool create_datasets @@ -1429,6 +1523,51 @@ main() { print_summary } +############################# +# Btrfs Installation Path +############################# + +install_btrfs() { + # Get partition references + local root_part=$(get_root_partition "${SELECTED_DISKS[0]}") + local efi_part=$(get_efi_partition "${SELECTED_DISKS[0]}") + + # Partition and format + partition_disk "${SELECTED_DISKS[0]}" + format_efi "$efi_part" + create_btrfs_volume "$root_part" + + # Create and mount subvolumes + create_btrfs_subvolumes "$root_part" + mount_btrfs_subvolumes "$root_part" + + # Mount EFI + mkdir -p /mnt/efi + mount "$efi_part" /mnt/efi + + # Install base system + install_base_btrfs + + # Configure system + configure_system + configure_wifi + configure_ssh + generate_btrfs_fstab "$root_part" "$efi_part" + configure_btrfs_initramfs + configure_grub "$efi_part" + configure_snapper + configure_btrfs_services + configure_btrfs_pacman_hook + copy_archsetup + + # Genesis snapshot + create_btrfs_genesis_snapshot + + # Cleanup + btrfs_cleanup + print_btrfs_summary +} + trap 'error "Installation interrupted!"' INT TERM main "$@" diff --git a/custom/lib/btrfs.sh b/custom/lib/btrfs.sh new file mode 100644 index 0000000..c264fce --- /dev/null +++ b/custom/lib/btrfs.sh @@ -0,0 +1,423 @@ +#!/usr/bin/env bash +# btrfs.sh - Btrfs-specific functions for archangel installer +# Source this file after common.sh, config.sh, disk.sh + +############################# +# Btrfs Constants +############################# + +# Mount options for btrfs subvolumes +BTRFS_OPTS="noatime,compress=zstd,space_cache=v2,discard=async" + +# Subvolume layout (matches ZFS dataset structure) +# Format: "name:mountpoint:extra_opts" +BTRFS_SUBVOLS=( + "@:/::" + "@home:/home::" + "@snapshots:/.snapshots::" + "@var_log:/var/log::" + "@var_cache:/var/cache::" + "@tmp:/tmp::nosuid,nodev" + "@var_tmp:/var/tmp::nosuid,nodev" + "@media:/media::compress=no" + "@vms:/vms::nodatacow,compress=no" + "@var_lib_docker:/var/lib/docker::" +) + +############################# +# Btrfs Pre-flight +############################# + +btrfs_preflight() { + step "Checking Btrfs Requirements" + + # Check for btrfs-progs + if ! command_exists mkfs.btrfs; then + error "btrfs-progs not installed. Cannot create btrfs filesystem." + fi + info "btrfs-progs available." + + # Check for required tools + require_command btrfs + require_command grub-install + + info "Btrfs preflight checks passed." +} + +############################# +# Btrfs Volume Creation +############################# + +create_btrfs_volume() { + local partition="$1" + + 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" +} + +############################# +# Subvolume Creation +############################# + +create_btrfs_subvolumes() { + local partition="$1" + + step "Creating Btrfs Subvolumes" + + # Mount the raw btrfs volume temporarily + mount "$partition" /mnt || error "Failed to mount btrfs volume" + + # Create each subvolume + for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do + IFS=':' read -r name mountpoint extra <<< "$subvol_spec" + info "Creating subvolume: $name -> $mountpoint" + btrfs subvolume create "/mnt/$name" || error "Failed to create subvolume $name" + done + + # Unmount raw volume + umount /mnt + + info "Created ${#BTRFS_SUBVOLS[@]} subvolumes." +} + +############################# +# Btrfs Mount Functions +############################# + +mount_btrfs_subvolumes() { + local partition="$1" + + step "Mounting Btrfs Subvolumes" + + # Mount root subvolume first + info "Mounting @ -> /mnt" + mount -o "subvol=@,$BTRFS_OPTS" "$partition" /mnt || error "Failed to mount root subvolume" + + # Create mount points and mount remaining subvolumes + for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do + IFS=':' read -r name mountpoint extra <<< "$subvol_spec" + + # Skip root, already mounted + [[ "$name" == "@" ]] && continue + + # Build mount options + local opts="subvol=$name,$BTRFS_OPTS" + + # Apply extra options (override defaults where specified) + if [[ -n "$extra" ]]; then + # Handle compress=no by removing compress from opts and not adding it + if [[ "$extra" == *"compress=no"* ]]; then + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + # Handle nodatacow + if [[ "$extra" == *"nodatacow"* ]]; then + opts="$opts,nodatacow" + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + # Handle nosuid,nodev for tmp + if [[ "$extra" == *"nosuid"* ]]; then + opts="$opts,nosuid,nodev" + fi + fi + + info "Mounting $name -> /mnt$mountpoint" + mkdir -p "/mnt$mountpoint" + mount -o "$opts" "$partition" "/mnt$mountpoint" || error "Failed to mount $name" + done + + # Set permissions on tmp directories + chmod 1777 /mnt/tmp /mnt/var/tmp + + info "All subvolumes mounted." +} + +############################# +# Fstab Generation +############################# + +generate_btrfs_fstab() { + local partition="$1" + local efi_partition="$2" + + step "Generating fstab" + + local uuid + uuid=$(blkid -s UUID -o value "$partition") + + # Start with header + cat > /mnt/etc/fstab << EOF +# /etc/fstab - Btrfs subvolume mounts +# IMPORTANT: Using subvol= NOT subvolid= for snapshot compatibility +# Generated by archangel installer + +EOF + + # Add each subvolume + for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do + IFS=':' read -r name mountpoint extra <<< "$subvol_spec" + + # Build mount options + local opts="subvol=$name,$BTRFS_OPTS" + + # Apply extra options + if [[ -n "$extra" ]]; then + if [[ "$extra" == *"compress=no"* ]]; then + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + if [[ "$extra" == *"nodatacow"* ]]; then + opts="$opts,nodatacow" + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + if [[ "$extra" == *"nosuid"* ]]; then + opts="$opts,nosuid,nodev" + fi + fi + + echo "UUID=$uuid $mountpoint btrfs $opts 0 0" >> /mnt/etc/fstab + done + + # Add EFI partition + local efi_uuid + efi_uuid=$(blkid -s UUID -o value "$efi_partition") + echo "" >> /mnt/etc/fstab + echo "# EFI System Partition" >> /mnt/etc/fstab + echo "UUID=$efi_uuid /efi vfat defaults,noatime 0 2" >> /mnt/etc/fstab + + info "fstab generated with ${#BTRFS_SUBVOLS[@]} btrfs mounts + EFI" +} + +############################# +# Snapper Configuration +############################# + +configure_snapper() { + step "Configuring Snapper" + + # Snapper config for root + # Note: snapper expects /.snapshots to exist and be a subvolume + info "Creating snapper config for root..." + + arch-chroot /mnt snapper -c root create-config / || error "Failed to create snapper config" + + # Snapper creates its own .snapshots subvolume, but we already have @snapshots + # Delete snapper's and use ours + arch-chroot /mnt btrfs subvolume delete /.snapshots 2>/dev/null || true + mkdir -p /mnt/.snapshots + + # Set snapper timeline settings + # Keep: 6 hourly, 7 daily, 2 weekly, 1 monthly + info "Configuring snapshot retention policy..." + cat > /mnt/etc/snapper/configs/root << 'EOF' +# Snapper config for root filesystem +SUBVOLUME="/" +FSTYPE="btrfs" +QGROUP="" + +# Automatic timeline snapshots +TIMELINE_CREATE="yes" +TIMELINE_CLEANUP="yes" + +# Retention policy +TIMELINE_MIN_AGE="1800" +TIMELINE_LIMIT_HOURLY="6" +TIMELINE_LIMIT_DAILY="7" +TIMELINE_LIMIT_WEEKLY="2" +TIMELINE_LIMIT_MONTHLY="1" +TIMELINE_LIMIT_YEARLY="0" + +# Cleanup settings +NUMBER_CLEANUP="yes" +NUMBER_MIN_AGE="1800" +NUMBER_LIMIT="50" +NUMBER_LIMIT_IMPORTANT="10" + +# User access +ALLOW_USERS="" +ALLOW_GROUPS="" + +# Sync ACL +SYNC_ACL="no" + +# Background comparison +BACKGROUND_COMPARISON="yes" + +# Empty pre-post cleanup +EMPTY_PRE_POST_CLEANUP="yes" +EMPTY_PRE_POST_MIN_AGE="1800" +EOF + + # Enable snapper timers + arch-chroot /mnt systemctl enable snapper-timeline.timer + arch-chroot /mnt systemctl enable snapper-cleanup.timer + + info "Snapper configured with timeline snapshots enabled." +} + +############################# +# GRUB Configuration +############################# + +configure_grub() { + local efi_partition="$1" + + step "Configuring GRUB Bootloader" + + # Mount EFI partition + mkdir -p /mnt/efi + mount "$efi_partition" /mnt/efi + + # Configure GRUB defaults + info "Setting GRUB configuration..." + cat > /mnt/etc/default/grub << 'EOF' +# GRUB configuration for btrfs root with snapshots +GRUB_DEFAULT=0 +GRUB_TIMEOUT=5 +GRUB_DISTRIBUTOR="Arch" +GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet" +GRUB_CMDLINE_LINUX="" + +# Btrfs snapshot boot support +GRUB_BTRFS_GRUB_DIRNAME="/efi/grub" + +# Disable os-prober (single-boot system) +GRUB_DISABLE_OS_PROBER=true +EOF + + # Install GRUB to EFI + info "Installing GRUB to EFI partition..." + arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB \ + || error "GRUB installation failed" + + # Generate GRUB config + info "Generating GRUB configuration..." + arch-chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg \ + || error "Failed to generate GRUB config" + + # Enable grub-btrfsd for automatic snapshot menu updates + info "Enabling grub-btrfs daemon..." + arch-chroot /mnt systemctl enable grub-btrfsd + + info "GRUB configured with btrfs snapshot support." +} + +############################# +# Pacman Snapshot Hook +############################# + +configure_btrfs_pacman_hook() { + step "Configuring Pacman Snapshot Hook" + + # snap-pac handles this automatically when installed + # Just verify it's set up + info "snap-pac will create pre/post snapshots for pacman transactions." + info "Snapshots visible in GRUB menu via grub-btrfs." +} + +############################# +# Genesis Snapshot +############################# + +create_btrfs_genesis_snapshot() { + step "Creating Genesis Snapshot" + + # Use snapper to create the genesis snapshot + arch-chroot /mnt snapper -c root create --description "genesis" \ + || error "Failed to create genesis snapshot" + + info "Genesis snapshot created." + info "Restore with: snapper -c root rollback <number>" + + # Show the snapshot + arch-chroot /mnt snapper -c root list +} + +############################# +# Btrfs Services +############################# + +configure_btrfs_services() { + step "Configuring System Services" + + # Enable standard services + arch-chroot /mnt systemctl enable NetworkManager + arch-chroot /mnt systemctl enable avahi-daemon + + # Snapper timers (already enabled in configure_snapper) + + # grub-btrfsd (already enabled in configure_grub) + + info "System services configured." +} + +############################# +# Btrfs Initramfs +############################# + +configure_btrfs_initramfs() { + step "Configuring Initramfs for Btrfs" + + # Backup original + cp /mnt/etc/mkinitcpio.conf /mnt/etc/mkinitcpio.conf.bak + + # Remove archiso drop-in if present + if [[ -f /mnt/etc/mkinitcpio.conf.d/archiso.conf ]]; then + info "Removing archiso drop-in config..." + rm -f /mnt/etc/mkinitcpio.conf.d/archiso.conf + fi + + # Create proper linux-lts preset + info "Creating linux-lts preset..." + cat > /mnt/etc/mkinitcpio.d/linux-lts.preset << 'EOF' +# mkinitcpio preset file for linux-lts + +PRESETS=(default fallback) + +ALL_kver="/boot/vmlinuz-linux-lts" + +default_image="/boot/initramfs-linux-lts.img" + +fallback_image="/boot/initramfs-linux-lts-fallback.img" +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 + + # Regenerate initramfs + info "Regenerating initramfs..." + arch-chroot /mnt mkinitcpio -P + + info "Initramfs configured for btrfs." +} + +############################# +# Btrfs Cleanup +############################# + +btrfs_cleanup() { + step "Cleaning Up Btrfs" + + # Unmount in reverse order + info "Unmounting subvolumes..." + + # Unmount EFI first + umount /mnt/efi 2>/dev/null || true + + # Unmount all btrfs subvolumes (reverse order) + for ((i=${#BTRFS_SUBVOLS[@]}-1; i>=0; i--)); do + IFS=':' read -r name mountpoint extra <<< "${BTRFS_SUBVOLS[$i]}" + [[ "$name" == "@" ]] && continue + umount "/mnt$mountpoint" 2>/dev/null || true + done + + # Unmount root last + umount /mnt 2>/dev/null || true + + info "Btrfs cleanup complete." +} diff --git a/custom/lib/disk.sh b/custom/lib/disk.sh index fa3dbd2..38c6dd9 100644 --- a/custom/lib/disk.sh +++ b/custom/lib/disk.sh @@ -8,10 +8,17 @@ # Partition a single disk for ZFS/Btrfs installation # Creates: EFI partition (512M) + root partition (rest) +# Uses global FILESYSTEM variable to determine partition type partition_disk() { local disk="$1" local efi_size="${2:-512M}" + # Determine root partition type based on filesystem + local root_type="BF00" # ZFS (Solaris root) + if [[ "$FILESYSTEM" == "btrfs" ]]; then + root_type="8300" # Linux filesystem + fi + info "Partitioning $disk..." # Wipe existing partition table @@ -20,8 +27,8 @@ partition_disk() { # Create EFI partition (512M, type EF00) sgdisk -n 1:0:+${efi_size} -t 1:EF00 -c 1:"EFI" "$disk" || error "Failed to create EFI partition on $disk" - # Create root partition (rest of disk, type BF00 for ZFS or 8300 for Linux) - sgdisk -n 2:0:0 -t 2:BF00 -c 2:"ROOT" "$disk" || error "Failed to create root partition on $disk" + # Create root partition (rest of disk) + sgdisk -n 2:0:0 -t 2:$root_type -c 2:"ROOT" "$disk" || error "Failed to create root partition on $disk" # Notify kernel of partition changes partprobe "$disk" 2>/dev/null || true |
