aboutsummaryrefslogtreecommitdiff
path: root/custom
diff options
context:
space:
mode:
Diffstat (limited to 'custom')
-rwxr-xr-xcustom/archangel165
-rw-r--r--custom/lib/btrfs.sh423
-rw-r--r--custom/lib/disk.sh11
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