523 lines
12 KiB
Bash
Executable file
523 lines
12 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
if [[ "$(id -u)" -ne 0 ]]; then
|
|
echo "ERROR: This needs to be run as root"
|
|
exit 250
|
|
fi
|
|
|
|
declare -rA SUBVOLS_DEFAULT=(
|
|
["@home"]="home"
|
|
["@root"]="root"
|
|
["@srv"]="srv"
|
|
["@opt"]="opt"
|
|
["@local"]="usr/local"
|
|
["@cache"]="var/cache"
|
|
["@repos"]="/var/db/repos"
|
|
["@containers"]="var/lib/containers"
|
|
["@libvirt"]="var/lib/libvirt/images"
|
|
["@machines"]="var/lib/machines"
|
|
["@portables"]="var/lib/portables"
|
|
["@log"]="var/log"
|
|
["@spool"]="var/spool"
|
|
["@www"]="var/www"
|
|
["@snapshots"]=".snapshots"
|
|
)
|
|
|
|
function show_help() {
|
|
echo "Usage: $0 <disk> [<options>...]"
|
|
echo ""
|
|
echo "Positional Arguments:"
|
|
echo "<drive> Disk device used for system"
|
|
echo ""
|
|
echo "Options:"
|
|
echo "-h, --help Help on this tool."
|
|
echo "-e, --encryption Enable LUKS encryption."
|
|
echo "-c, --compression Enable BtrFS compression."
|
|
echo "-s, --swap Enable Swap/Hibernation support."
|
|
echo "-d, --debug Enable DEBUG mode for testing."
|
|
echo "-m, --mount Mount an existing install."
|
|
echo ""
|
|
echo "--stage <stage> Installation using stagefile <stage>, for stage3 or stage4"
|
|
echo "--clean Cleanup disk for clean slate"
|
|
exit 0
|
|
}
|
|
|
|
function errormsg() {
|
|
local err=$1
|
|
shift
|
|
|
|
echo "$*" >&2
|
|
exit "$err"
|
|
}
|
|
|
|
function prepare_disk() {
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
if [[ -b "$RootDisk" ]]; then
|
|
if [[ -b "$EFIPart" || -b "$RootPart" ]]; then
|
|
echo "FATAL: Disk already provisioned. Clean required to continue."
|
|
exit 250
|
|
fi
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
${cmd} parted --script --align=optimal -- "$RootDisk" \
|
|
mklabel gpt \
|
|
mkpart primary 1MiB 100MiB \
|
|
mkpart primary 100MiB 2048MiB \
|
|
mkpart primary 2148MiB -2048s \
|
|
set 1 esp
|
|
#mkfs.vfat -F 32 -n "EFI" "$RootPart"
|
|
#mkfs.ext4 -L "Boot" -m 0 "$EFIPart"
|
|
#mkfs.btrfs -L "System" "$RootPart"
|
|
else
|
|
${cmd} parted --script --align=optimal -- "$RootDisk" \
|
|
mklabel gpt \
|
|
mkpart primary 1MiB 100MiB \
|
|
mkpart primary 100MiB -2048s \
|
|
set 1 esp
|
|
#mkfs.vfat -F 32 -n "EFI" "$RootPart"
|
|
#mkfs.btrfs -L "System" "$RootPart"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function create_luks() {
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
${cmd} cryptsetup --batch-mode --cipher aes-xts-plain64 --hash sha512 --use-random --verify-passphrase luksFormat "$RootPart"
|
|
echo
|
|
echo "Mounting new LUKS volume to continue preparation. Please unlock below:"
|
|
${cmd} cryptsetup luksOpen "$RootPart" luksvol
|
|
}
|
|
|
|
function prepare_format() {
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
echo "Formatting EFI: $EFIPart"
|
|
${cmd} mkfs.fat -F32 -n "EFI" "$EFIPart"
|
|
|
|
if [[ "$BootPart" != "@boot" ]]; then
|
|
echo "Formatting Boot: $BootPart"
|
|
${cmd} mkfs.ext4 -FF -L "Boot" "$BootPart"
|
|
fi
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
echo "Formatting Root: /dev/mapper/luksvol"
|
|
${cmd} mkfs.btrfs -f -L "System" /dev/mapper/luksvol
|
|
else
|
|
echo "Formatting Root: $RootPart"
|
|
${cmd} mkfs.btrfs -f -L "System" "$RootPart"
|
|
fi
|
|
}
|
|
|
|
function create_subvolumes() {
|
|
local -a subvols=("@")
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
if [[ "$BootPart" == "@boot" ]]; then
|
|
subvols+=("@boot")
|
|
fi
|
|
|
|
subvols+=("@swap")
|
|
subvols+=("${!SUBVOLS_DEFAULT[@]}")
|
|
|
|
[[ ! -d /mnt/btrfs ]] && ${cmd} mkdir -p /mnt/btrfs
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
${cmd} mount "/dev/mapper/luksvol" /mnt/btrfs
|
|
else
|
|
${cmd} mount "$RootPart" /mnt/btrfs
|
|
fi
|
|
|
|
for subvol in "${subvols[@]}"
|
|
do
|
|
${cmd} btrfs subvolume create /mnt/btrfs/"$subvol"
|
|
done
|
|
|
|
${cmd} umount /mnt/btrfs
|
|
${cmd} rmdir /mnt/btrfs
|
|
}
|
|
|
|
function get_hibernate_size() {
|
|
free --giga | awk '/^Mem:/{print $2}'
|
|
}
|
|
|
|
function prepare_target() {
|
|
local subvol
|
|
local rootmount
|
|
local compopt
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
[[ ! -d /mnt/gentoo ]] && ${cmd} mkdir /mnt/gentoo
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
rootmount="/dev/mapper/luksvol"
|
|
if [[ ! -b "$rootmount" ]]; then
|
|
${cmd} cryptsetup luksOpen "$RootPart" luksvol
|
|
fi
|
|
else
|
|
rootmount="$RootPart"
|
|
fi
|
|
|
|
if [[ "$COMPRESSION" ]]; then
|
|
compopt=",compress=zstd:3"
|
|
fi
|
|
|
|
${cmd} mount -o noatime,space_cache=v2${compopt},ssd,subvol=@ "$rootmount" /mnt/gentoo
|
|
|
|
for subvol in "${!SUBVOLS_DEFAULT[@]}"
|
|
do
|
|
${cmd} mkdir -p /mnt/gentoo/"${SUBVOLS_DEFAULT[$subvol]}"
|
|
done
|
|
|
|
${cmd} mkdir -p /mnt/gentoo/boot
|
|
if [[ "$BootPart" == "@boot" ]]; then
|
|
${cmd} mount -o noatime,space_cache=v2,ssd,subvol=@boot "$rootmount" /mnt/gentoo/boot
|
|
else
|
|
${cmd} mount "$BootPart" /mnt/gentoo/boot
|
|
fi
|
|
|
|
for subvol in "${!SUBVOLS_DEFAULT[@]}"
|
|
do
|
|
${cmd} mount -o noatime,space_cache=v2,ssd,subvol="$subvol" "$rootmount" /mnt/gentoo/"${SUBVOLS_DEFAULT[$subvol]}"
|
|
done
|
|
|
|
${cmd} mkdir -p /mnt/gentoo/efi
|
|
${cmd} mount "$EFIPart" /mnt/gentoo/efi
|
|
|
|
${cmd} mkdir -p /mnt/gentoo/swap
|
|
${cmd} mount -o noatime,ssd,subvol=@swap "$rootmount" /mnt/gentoo/swap
|
|
|
|
if [[ "$SWAP" && ! -f /mnt/gentoo/swap/hibernate.swp ]]; then
|
|
#${cmd} mkdir -p /mnt/gentoo/swap
|
|
#${cmd} mount -o noatime,ssd,subvol=@swap "$rootmount" /mnt/gentoo/swap
|
|
${cmd} btrfs filesystem mkswapfile --size "$(get_hibernate_size)g" --uuid clear /mnt/gentoo/swap/hibernate.swp
|
|
fi
|
|
}
|
|
|
|
function stage_step() {
|
|
local s4file SwapUUID SwapOffset
|
|
local cmd
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
cmd="echo"
|
|
else
|
|
cmd=""
|
|
fi
|
|
|
|
#FIXME check $INSTALL_STAGE for http or local file
|
|
if [[ "$INSTALL_STAGE" == http* ]]; then
|
|
s4file="${INSTALL_STAGE##*/}"
|
|
${cmd} curl -o "/mnt/gentoo/${s4file}" "$INSTALL_STAGE" || errormsg 1 "Failed to download stage4 archive"
|
|
s4file="/mnt/gentoo/${INSTALL_STAGE##*/}"
|
|
else
|
|
s4file="${INSTALL_STAGE}"
|
|
fi
|
|
|
|
${cmd} git clone "https://github.com/erenfro/gen2stage4" "$HOME/gen2stage4" || errormsg 1 "Failed to download gen2stage4"
|
|
${cmd} "$HOME/gen2stage4/gen2extract" -t /mnt/gentoo "$s4file"
|
|
${cmd} rm -rf "$HOME/gen2stage4"
|
|
|
|
#FIXME post stage extraction:
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
echo "genfstab -U /mnt/gentoo > /mnt/gentoo/etc/fstab"
|
|
else
|
|
genfstab -U /mnt/gentoo > /mnt/gentoo/etc/fstab
|
|
fi
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
eval "$(blkid -p --output export "$RootPart" | grep UUID)"
|
|
if [[ "$DEBUG" ]]; then
|
|
echo "echo \"luksvol UUID=$UUID none luks\" >> /mnt/gentoo/etc/crypttab"
|
|
else
|
|
echo "luksvol UUID=$UUID none luks" >> /mnt/gentoo/etc/crypttab
|
|
fi
|
|
fi
|
|
|
|
if [[ "$SWAP" ]]; then
|
|
if [[ "$DEBUG" ]]; then
|
|
echo "echo \"/swap/hibernate.swp none swap defaults 0 0\" >> /mnt/gentoo/etc/fstab"
|
|
else
|
|
echo "/swap/hibernate.swp none swap defaults 0 0" >> /mnt/gentoo/etc/fstab
|
|
fi
|
|
SwapUUID=$(grep btrfs /mnt/gentoo/etc/fstab | head -n1 | cut -f1)
|
|
SwapOffset=$(btrfs inspect-internal map-swapfile -r /mnt/gentoo/swap/hibernate.swp)
|
|
${cmd} sed -i "s/^#GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"resume=${SwapUUID} resume_offset=${SwapOffset}\"/g" /mnt/gentoo/etc/default/grub
|
|
fi
|
|
|
|
arch-chroot /mnt/gentoo emerge --sync
|
|
arch-chroot /mnt/gentoo grub-install --efi-directory=/efi
|
|
#arch-chroot /mnt/gentoo dracut --host-only
|
|
arch-chroot /mnt/gentoo grub-mkconfig -o /boot/grub/grub.cfg
|
|
}
|
|
|
|
function show_options() {
|
|
echo "System Disk: $RootDisk"
|
|
echo "Root Partition: $RootPart"
|
|
echo "Boot Partition: $BootPart"
|
|
echo "EFI Partition: $EFIPart"
|
|
|
|
if [[ "$INSTALL_MODE" == "stage" ]]; then
|
|
echo "Install Mode: Stage"
|
|
echo "STAGE File: $INSTALL_STAGE"
|
|
elif [[ "$INSTALL_MODE" == "clean" ]]; then
|
|
echo "Install Mode: Clean"
|
|
else
|
|
echo "Install Mode: Normal"
|
|
fi
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
echo "Encryption: Enabled"
|
|
else
|
|
echo "Encryption: Disabled"
|
|
fi
|
|
|
|
if [[ "$COMPRESSION" ]]; then
|
|
echo "Compression: Enabled"
|
|
else
|
|
echo "Compression: Disabled"
|
|
fi
|
|
|
|
if [[ "$SWAP" ]]; then
|
|
echo "Swap: Enabled"
|
|
else
|
|
echo "Swap: Disabled"
|
|
fi
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
echo "Debug: Enabled"
|
|
else
|
|
echo "Debug: Disabled"
|
|
fi
|
|
echo
|
|
}
|
|
|
|
function install_prep() {
|
|
show_options
|
|
|
|
read -rsn1 -p"Preparation: To proceed, press enter to continue." proceed
|
|
echo
|
|
if [[ "$proceed" != "" ]]; then
|
|
echo "Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Creating Partitions on ${RootDisk}..."
|
|
prepare_disk
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
echo
|
|
echo "Initiallizing LUKSv2 on ${RootPart}:"
|
|
create_luks
|
|
fi
|
|
|
|
echo
|
|
echo "Formatting Filesystems..."
|
|
prepare_format
|
|
|
|
echo
|
|
echo "Preparing Subvolumes..."
|
|
create_subvolumes
|
|
|
|
echo
|
|
echo "Preparing Installation Target..."
|
|
prepare_target
|
|
|
|
echo
|
|
echo "Installation is ready and mounted in /mnt/gentoo for stage3 or stage4 install"
|
|
echo "Please verify all went well, and re-run this script with --stage <stagefile>"
|
|
echo "as appropriate, to continue."
|
|
}
|
|
|
|
function install_mount() {
|
|
show_options
|
|
|
|
read -rsn1 -p"Mounting: To proceed, press enter to continue." proceed
|
|
echo
|
|
if [[ "$proceed" != "" ]]; then
|
|
echo "Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Mounting Partitions on ${RootDisk}..."
|
|
prepare_target
|
|
}
|
|
|
|
function install_umount() {
|
|
show_options
|
|
|
|
read -rsn1 -p"Unounting: To proceed, press enter to continue." proceed
|
|
echo
|
|
if [[ "$proceed" != "" ]]; then
|
|
echo "Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Mounting Partitions on ${RootDisk}..."
|
|
umount -R /mnt/gentoo
|
|
}
|
|
|
|
function install_stage() {
|
|
show_options
|
|
|
|
read -rsn1 -p"Stage-Installation: To proceed, press enter to continue." proceed
|
|
echo
|
|
|
|
if [[ "$proceed" != "" ]]; then
|
|
echo "Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
echo "Running Stage-Mode Installation Steps..."
|
|
stage_step
|
|
}
|
|
|
|
function install_cleanup() {
|
|
show_options
|
|
|
|
echo "!!!WARNING!!! This is about to destructively wipe ${RootDisk}!"
|
|
read -r -p"Enter YES to continue: " proceed
|
|
echo
|
|
if [[ "${proceed,,}" != "yes" ]]; then
|
|
echo "Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$DEBUG" ]]; then
|
|
local cmd="echo"
|
|
else
|
|
local cmd=""
|
|
fi
|
|
|
|
for subvol in "${!SUBVOLS_DEFAULT[@]}"
|
|
do
|
|
${cmd} umount /mnt/gentoo/"${SUBVOLS_DEFAULT[$subvol]}"
|
|
done
|
|
|
|
${cmd} umount /mnt/gentoo/efi
|
|
${cmd} umount /mnt/gentoo/boot
|
|
${cmd} umount /mnt/gentoo/swap
|
|
${cmd} umount /mnt/gentoo
|
|
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
${cmd} cryptsetup luksClose luksvol
|
|
fi
|
|
|
|
${cmd} dd if=/dev/zero of="${RootDisk}" bs=1024 count=10
|
|
}
|
|
|
|
|
|
declare -a POSITIONAL_ARGS=()
|
|
declare INSTALL_MODE="normal"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_help
|
|
;;
|
|
-e|--encryption)
|
|
ENCRYPTION=true
|
|
shift
|
|
;;
|
|
-c|--compression)
|
|
COMPRESSION=true
|
|
shift
|
|
;;
|
|
-s|--swap)
|
|
SWAP=true
|
|
shift
|
|
;;
|
|
-d|--debug)
|
|
DEBUG=true
|
|
shift
|
|
;;
|
|
--mount)
|
|
INSTALL_MODE=mount
|
|
shift
|
|
;;
|
|
--umount)
|
|
INSTALL_MODE=umount
|
|
shift
|
|
;;
|
|
--stage)
|
|
INSTALL_MODE=stage
|
|
INSTALL_STAGE=$2
|
|
shift 2
|
|
;;
|
|
--clean)
|
|
INSTALL_MODE=clean
|
|
shift
|
|
;;
|
|
*)
|
|
POSITIONAL_ARGS+=("$1") # save positional arg
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
|
|
|
|
RootDisk="$1"
|
|
|
|
if [[ ! -b "$RootDisk" ]]; then
|
|
echo "ERROR: Invalid parameters. See --help for help"
|
|
exit 3
|
|
else
|
|
if [[ "$ENCRYPTION" ]]; then
|
|
diskdev="${RootDisk##*/}"
|
|
if [[ "$diskdev" =~ ^nvme.* ]]; then
|
|
RootPart="${RootDisk}p3"
|
|
BootPart="${RootDisk}p2"
|
|
EFIPart="${RootDisk}p1"
|
|
else
|
|
RootPart="${RootDisk}3"
|
|
BootPart="${RootDisk}2"
|
|
EFIPart="${RootDisk}1"
|
|
fi
|
|
else
|
|
BootPart="@boot"
|
|
if [[ "$diskdev" =~ ^nvme.* ]]; then
|
|
RootPart="${RootDisk}p2"
|
|
EFIPart="${RootDisk}p1"
|
|
else
|
|
RootPart="${RootDisk}2"
|
|
EFIPart="${RootDisk}1"
|
|
fi
|
|
fi
|
|
|
|
case "$INSTALL_MODE" in
|
|
normal) install_prep;;
|
|
mount) install_mount;;
|
|
umount) install_umount;;
|
|
stage) install_stage;;
|
|
clean) install_cleanup;;
|
|
*)
|
|
echo "Error, unknown installation mode detected."
|
|
exit 3
|
|
;;
|
|
esac
|
|
fi
|