| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- #!/bin/bash
- # Цвета
- C_RESET='\033[0m'
- C_RED='\033[0;31m'
- C_GREEN='\033[0;32m'
- C_YELLOW='\033[0;33m'
- C_BLUE='\033[0;34m'
- C_CYAN='\033[0;36m'
- # Файл для сохранения состояния
- CONFIG_FILE=".btrfs_setup.conf"
- # --- Функции Помощники ---
- info() { echo -e "${C_CYAN}INFO:${C_RESET} $1"; }
- warn() { echo -e "${C_YELLOW}WARN:${C_RESET} $1"; }
- error() { echo -e "${C_RED}ERROR:${C_RESET} $1"; }
- success() { echo -e "${C_GREEN}SUCCESS:${C_RESET} $1"; }
- cmd() { echo -e "${C_BLUE}CMD:${C_RESET} $1"; }
- ask_confirm() {
- while true; do
- read -p "$(echo -e "${C_YELLOW}CONFIRM:${C_RESET} $1 (y/n): ")" yn
- case $yn in
- [Yy]* ) return 0;;
- [Nn]* ) return 1;;
- * ) echo "Ответьте 'y' или 'n'.";;
- esac
- done
- }
- # --- Управление Конфигурацией ---
- # Загрузка переменных из файла
- load_config() {
- if [ -f "$CONFIG_FILE" ]; then
- source "$CONFIG_FILE"
- fi
- }
- # Сохранение переменной в файл
- save_config() {
- local key=$1
- local value=$2
- # Удаляем старую запись, если она есть
- sed -i "/^${key}=/d" "$CONFIG_FILE"
- # Добавляем новую
- echo "${key}='${value}'" >> "$CONFIG_FILE"
- }
- # --- Главы Установки ---
- # Глава 0: Проверка Live-среды
- step0_check_live() {
- info "--- Глава 0: Проверка Live-среды ---"
- info "Проверяю наличие 'btrfs-progs'..."
- if ! pacman -S --needed --noconfirm btrfs-progs; then
- error "Не удалось установить 'btrfs-progs'. Проверьте интернет."
- return 1
- fi
- success "Live-среда готова."
- }
- # Глава 1: Форматирование
- step1_format() {
- info "--- Глава 1: Форматирование ---"
- read -p "EFI раздел [${DEFAULT_EFI_PART:-/dev/sda1}]: " EFI_PART
- EFI_PART=${EFI_PART:-${DEFAULT_EFI_PART:-/dev/sda1}}
-
- read -p "BTRFS раздел [${DEFAULT_BTRFS_PART:-/dev/sda2}]: " BTRFS_PART
- BTRFS_PART=${BTRFS_PART:-${DEFAULT_BTRFS_PART:-/dev/sda2}}
- if [ -z "$EFI_PART" ] || [ -z "$BTRFS_PART" ]; then
- error "Разделы не могут быть пустыми."; return 1;
- fi
- warn "!! ВНИМАНИЕ !! Это действие необратимо."
- if ask_confirm "Отформатировать $EFI_PART (fat32) и $BTRFS_PART (btrfs)?"; then
- cmd "mkfs.fat -F 32 $EFI_PART"
- mkfs.fat -F 32 "$EFI_PART" || { error "Ошибка форматирования EFI."; return 1; }
-
- cmd "mkfs.btrfs -f $BTRFS_PART"
- mkfs.btrfs -f "$BTRFS_PART" || { error "Ошибка форматирования Btrfs."; return 1; }
-
- save_config "DEFAULT_EFI_PART" "$EFI_PART"
- save_config "DEFAULT_BTRFS_PART" "$BTRFS_PART"
- success "Форматирование завершено."
- else
- info "Форматирование отменено."; return 1;
- fi
- }
- # Глава 2: Создание подтомов
- step2_create_subvols() {
- info "--- Глава 2: Создание подтомов ---"
- if [ -z "$DEFAULT_BTRFS_PART" ]; then
- error "Сначала выполните Главу 1 (Форматирование)."; return 1;
- fi
- cmd "mount -o compress=zstd:1 $DEFAULT_BTRFS_PART /mnt"
- mount -o compress=zstd:1 "$DEFAULT_BTRFS_PART" /mnt || { error "Не удалось смонтировать /mnt."; return 1; }
- local default_vols=("@" "@home" "@srv" "@logs" "@cache" "@snapshots" "@swap")
- local SUBVOLS_RAW=${DEFAULT_SUBVOLS:-${default_vols[*]}}
-
- info "Текущий список подтомов: $SUBVOLS_RAW"
- if ask_confirm "Хотите настроить список подтомов?"; then
- read -p "Введите новый список (через пробел): " SUBVOLS_RAW
- fi
- local SUBVOLS_LIST=($SUBVOLS_RAW)
- info "Создаю подтома: ${SUBVOLS_LIST[*]}"
- for vol in "${SUBVOLS_LIST[@]}"; do
- cmd "btrfs subvolume create /mnt/$vol"
- btrfs subvolume create "/mnt/$vol"
- done
-
- save_config "DEFAULT_SUBVOLS" "$SUBVOLS_RAW"
- success "Подтома созданы."
- cmd "umount /mnt"
- umount /mnt
- }
- # Глава 3: Монтирование подтомов
- step3_mount_all() {
- info "--- Глава 3: Монтирование подтомов ---"
- if [ -z "$DEFAULT_BTRFS_PART" ] || [ -z "$DEFAULT_SUBVOLS" ]; then
- error "Сначала выполните Главы 1 и 2."; return 1;
- fi
- local SUBVOLS_LIST=($DEFAULT_SUBVOLS)
- declare -A MOUNT_MAP
- MOUNT_MAP["@"]="/mnt"
- MOUNT_MAP["@home"]="/mnt/home"
- MOUNT_MAP["@srv"]="/mnt/srv"
- MOUNT_MAP["@logs"]="/mnt/var/log"
- MOUNT_MAP["@cache"]="/mnt/var/cache"
- MOUNT_MAP["@snapshots"]="/mnt/.snapshots"
- MOUNT_MAP["@swap"]="/mnt/swap"
- # Добавляем кастомные, если они были
- for vol in "${SUBVOLS_LIST[@]}"; do
- if [[ -z "${MOUNT_MAP[$vol]}" ]]; then
- read -p "Введите точку монтирования для '$vol' (e.g., /mnt/custom): " custom_mount
- MOUNT_MAP["$vol"]="$custom_mount"
- fi
- done
- read -p "Уровень сжатия по умолчанию [zstd:3]: " DEFAULT_COMPRESS
- DEFAULT_COMPRESS=${DEFAULT_COMPRESS:-zstd:3}
-
- info "Монтирование корня (@)..."
- local root_opts="defaults,compress=$DEFAULT_COMPRESS"
- if [[ " ${SUBVOLS_LIST[*]} " =~ " @cache " ]]; then # Корень не должен иметь noatime
- root_opts="defaults,compress=$DEFAULT_COMPRESS"
- fi
- mkdir -p "${MOUNT_MAP['@']}"
- cmd "mount -o $root_opts,subvol=@ $DEFAULT_BTRFS_PART ${MOUNT_MAP['@']}"
- mount -o "$root_opts,subvol=@" "$DEFAULT_BTRFS_PART" "${MOUNT_MAP['@']}" || { error "Ошибка монтирования корня."; return 1; }
- # Монтируем все остальное
- for vol in "${SUBVOLS_LIST[@]}"; do
- if [[ "$vol" == "@" ]]; then continue; fi
-
- local mount_point="${MOUNT_MAP[$vol]}"
- local default_opts="defaults,compress=$DEFAULT_COMPRESS"
-
- if [[ "$vol" == "@cache" || "$vol" == "@swap" ]]; then
- default_opts="defaults,noatime"
- fi
-
- read -p "Опции для '$vol' [${default_opts}]: " custom_opts
- local opts=${custom_opts:-$default_opts}
-
- info "Монтирование '$vol' в '$mount_point'..."
- mkdir -p "$mount_point"
- cmd "mount -o $opts,subvol=$vol $DEFAULT_BTRFS_PART $mount_point"
- mount -o "$opts,subvol=$vol" "$DEFAULT_BTRFS_PART" "$mount_point" || warn "Не удалось смонтировать $vol."
- done
- info "Монтирование EFI..."
- mkdir -p /mnt/efi
- cmd "mount $DEFAULT_EFI_PART /mnt/efi"
- mount "$DEFAULT_EFI_PART" /mnt/efi || { error "Ошибка монтирования EFI."; return 1; }
- success "Все подтома смонтированы."
- }
- # Глава 4: Swap-файл
- step4_swapfile() {
- info "--- Глава 4: Swap-файл ---"
- if [[ ! " $DEFAULT_SUBVOLS " =~ " @swap " ]]; then
- warn "Подтом '@swap' не найден, пропускаю."; return 0;
- fi
-
- read -p "Размер swap-файла [4G]: " SWAP_SIZE
- SWAP_SIZE=${SWAP_SIZE:-4G}
-
- info "Создаю swap-файл /mnt/swap/swapfile..."
- cmd "btrfs filesystem mkswapfile --size $SWAP_SIZE --uuid clear /mnt/swap/swapfile"
- btrfs filesystem mkswapfile --size "$SWAP_SIZE" --uuid clear /mnt/swap/swapfile
-
- cmd "swapon /mnt/swap/swapfile"
- swapon /mnt/swap/swapfile
- save_config "SWAP_SIZE" "$SWAP_SIZE"
- success "Swap-файл создан и активирован."
- }
- # Глава 5: Pacstrap
- step5_pacstrap() {
- info "--- Глава 5: Установка (pacstrap) ---"
-
- # Предлагаем "умный" список
- local BASE_PKGS="base linux linux-firmware nano"
- local BTRFS_UTILS=""
- if [[ " $DEFAULT_SUBVOLS " =~ " @snapshots " ]]; then
- BTRFS_UTILS="btrfs-progs grub efibootmgr snapper grub-btrfs snap-pac inotify-tools"
- else
- BTRFS_UTILS="btrfs-progs grub efibootmgr"
- fi
-
- local DEFAULT_PACKAGES="${BASE_PKGS} ${BTRFS_UTILS}"
- info "Рекомендуемый список пакетов:"
- info "$DEFAULT_PACKAGES"
-
- read -p "Введите список пакетов (Enter = принять): " PACKAGES
- PACKAGES=${PACKAGES:-$DEFAULT_PACKAGES}
-
- info "Запуск pacstrap (это займет время)..."
- cmd "pacstrap -K /mnt $PACKAGES"
- if pacstrap -K /mnt $PACKAGES; then
- save_config "INSTALLED_PACKAGES" "$PACKAGES"
- success "Базовая система установлена."
- else
- error "Ошибка pacstrap!"; return 1;
- fi
- }
- # Глава 6: Fstab
- step6_fstab() {
- info "--- Глава 6: Fstab ---"
- info "Генерирую fstab..."
- cmd "genfstab -U /mnt >> /mnt/etc/fstab"
- genfstab -U /mnt >> /mnt/etc/fstab
- if [ -n "$SWAP_SIZE" ]; then
- info "Добавляю swap-файл в fstab..."
- cmd "echo '/swap/swapfile none swap defaults 0 0' >> /mnt/etc/fstab"
- echo "/swap/swapfile none swap defaults 0 0" >> /mnt/etc/fstab
- fi
- warn "ВАЖНО: genfstab мог 'забыть' опции сжатия."
- if ask_confirm "Открыть /mnt/etc/fstab в nano для ручной проверки?"; then
- nano /mnt/etc/fstab
- fi
- success "Fstab готов."
- }
- # Глава 7: Шпаргалка (Chroot)
- step7_show_helper() {
- info "--- Глава 7: Шпаргалка по настройке (внутри chroot) ---"
- if [ -z "$INSTALLED_PACKAGES" ]; then
- warn "Пакеты еще не установлены (Глава 5). Шпаргалка может быть неполной."
- fi
-
- echo -e "${C_CYAN}==========================================================="
- echo " Дальнейшие шаги (выполнять в 'arch-chroot /mnt') "
- echo "===========================================================${C_RESET}"
- echo ""
- echo "Не забудьте про: passwd, ln -sf /usr/share/zoneinfo/..., hwclock, locale.gen, hostname, hosts"
- echo ""
- # --- Динамическая секция Snapper ---
- if [[ "$INSTALLED_PACKAGES" == *"snapper"* ]]; then
- echo -e "${C_YELLOW}### 1. Настройка Snapper ###${C_RESET}"
- warn "Рекомендация: этот шаг лучше делать ПОСЛЕ первой загрузки, а не в chroot."
- echo "cmd: snapper -c root create-config /"
- echo "cmd: rm -fdir /.snapshots"
- echo "cmd: mkdir /.snapshots"
- echo "cmd: mount -a"
- echo "cmd: nano /etc/snapper/configs/root"
- echo " > Измени: TIMELINE_CREATE=\"no\""
- echo ""
- fi
- # --- Динамическая секция GRUB ---
- if [[ "$INSTALLED_PACKAGES" == *"grub"* ]]; then
- echo -e "${C_YELLOW}### 2. Настройка GRUB ###${C_RESET}"
- echo "cmd: grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=Arch"
-
- if [[ "$INSTALLED_PACKAGES" == *"grub-btrfs"* ]]; then
- echo "info: Добавляем поддержку снимков в GRUB..."
- echo "cmd: nano /etc/default/grub"
- echo " > Добавь/раскомментируй: GRUB_BTRFS_SNAPSHOT_BOOT=true"
- fi
-
- echo "cmd: grub-mkconfig -o /boot/grub/grub.cfg"
- echo ""
- fi
- # --- Динамическая секция Сервисов ---
- if [[ "$INSTALLED_PACKAGES" == *"snapper"* || "$INSTALLED_PACKAGES" == *"grub-btrfs"* ]]; then
- echo -e "${C_YELLOW}### 3. Включение сервисов ###${C_RESET}"
- if [[ "$INSTALLED_PACKAGES" == *"snapper"* ]]; then
- echo "cmd: systemctl enable snapper-timeline.timer"
- echo "cmd: systemctl enable snapper-cleanup.timer"
- fi
- if [[ "$INSTALLED_PACKAGES" == *"grub-btrfs"* ]]; then
- echo "cmd: systemctl enable grub-btrfsd.service"
- fi
- echo ""
- fi
- echo -e "${C_CYAN}===========================================================${C_RESET}"
- }
- # --- Главное Меню ---
- main_menu() {
- while true; do
- clear
- load_config # Загружаем состояние при каждом показе меню
-
- echo -e "${C_GREEN}--- Интерактивный Btrfs-помощник (v2.0) ---${C_RESET}"
- echo "Файл состояния: $CONFIG_FILE"
- echo ""
- echo -e " ${C_CYAN}Разделы:${C_RESET} ${DEFAULT_EFI_PART:-Не задан} | ${DEFAULT_BTRFS_PART:-Не задан}"
- echo -e " ${C_CYAN}Подтома:${C_RESET} ${DEFAULT_SUBVOLS:-Не заданы}"
- echo -e " ${C_CYAN}Пакеты:${C_RESET} ${INSTALLED_PACKAGES:-Не установлены}"
- echo ""
- echo "Выберите главу:"
- echo "-----------------------------------"
- echo " 0. Проверить Live-среду (pacman)"
- echo " 1. Форматирование разделов"
- echo " 2. Создание подтомов Btrfs"
- echo " 3. Монтирование подтомов"
- echo " 4. Создание Swap-файла"
- echo "-----------------------------------"
- echo " 5. Установка системы (pacstrap)"
- echo " 6. Генерация Fstab"
- echo " 7. Показать шпаргалку (Chroot)"
- echo "-----------------------------------"
- echo " q. Выход"
- echo ""
-
- read -p "Ваш выбор: " choice
-
- # Переменная для паузы
- local pause=true
-
- case $choice in
- 0) step0_check_live ;;
- 1) step1_format ;;
- 2) step2_create_subvols ;;
- 3) step3_mount_all ;;
- 4. | 4) step4_swapfile ;;
- 5) step5_pacstrap ;;
- 6) step6_fstab ;;
- 7) step7_show_helper ;;
- q|Q) break ;;
- *) warn "Неверный выбор." ;;
- esac
-
- if [ "$pause" = true ]; then
- echo ""
- read -p "Нажмите Enter для возврата в меню..."
- fi
- done
- info "Готово. Удачи!"
- }
- # --- Запуск ---
- main_menu
|