diff --git a/usr/local/bin/install_to_disk.sh b/usr/local/bin/install_to_disk.sh index 60d819b..d9b19cc 100755 --- a/usr/local/bin/install_to_disk.sh +++ b/usr/local/bin/install_to_disk.sh @@ -1,30 +1,41 @@ #!/usr/bin/env bash -# Stormux Gaming Image - Rsync-based Disk Installer -# Copies the live system to a target disk using rsync +# Stormux Gaming Image - USB to Disk Cloner +# Clones the entire USB system to an internal disk set -e -# Text output functions -speak_and_echo() { - echo "$1" - spd-say "$1" 2>/dev/null || true -} - error_exit() { - speak_and_echo "Error: $1" + echo "Error: $1" exit 1 } -# Function to detect available disks -detect_disks() { +# Function to find the source USB device +find_source_device() { + # Look for the device we're currently running from + local root_device=$(findmnt -n -o SOURCE /) + if [[ "$root_device" =~ ^/dev/(sd[a-z]|nvme[0-9]n[0-9]|mmcblk[0-9]) ]]; then + # Extract just the device name (remove partition number) + echo "$root_device" | sed 's/[0-9]*$//' | sed 's/p$//' + else + # Fallback: look for devices with STORMUX label + local labeled_device=$(lsblk -no NAME,LABEL | grep -i stormux | head -1 | awk '{print "/dev/" $1}' | sed 's/[0-9]*$//' | sed 's/p$//') + if [[ -n "$labeled_device" ]]; then + echo "$labeled_device" + else + return 1 + fi + fi +} + +# Function to detect target disks (excluding source) +detect_target_disks() { + local source_device="$1" local disks=() - # Find all block devices that are disks (not partitions) while IFS= read -r disk; do - # Skip if it's a partition, loop device, or CD-ROM - if [[ ! "$disk" =~ [0-9]$ ]] && [[ ! "$disk" =~ ^/dev/loop ]] && [[ ! "$disk" =~ ^/dev/sr ]]; then - # Check if it's actually a disk + # Skip if it's a partition, loop device, CD-ROM, or the source device + if [[ ! "$disk" =~ [0-9]$ ]] && [[ ! "$disk" =~ ^/dev/loop ]] && [[ ! "$disk" =~ ^/dev/sr ]] && [[ "$disk" != "$source_device" ]]; then if [[ -b "$disk" ]]; then disks+=("$disk") fi @@ -34,238 +45,144 @@ detect_disks() { printf '%s\n' "${disks[@]}" } -# Function to get disk size -get_disk_size() { +# Function to get disk info +get_disk_info() { local disk="$1" - lsblk -dpno SIZE "$disk" 2>/dev/null | tr -d ' ' -} - -# Function to get disk model -get_disk_model() { - local disk="$1" - lsblk -dpno MODEL "$disk" 2>/dev/null | tr -d ' ' || echo "Unknown" + local size=$(lsblk -dpno SIZE "$disk" 2>/dev/null | tr -d ' ') + local model=$(lsblk -dpno MODEL "$disk" 2>/dev/null | tr -d ' ' || echo "Unknown") + echo "$size - $model" } # Welcome message clear -speak_and_echo "Stormux Gaming Image Disk Installer" -speak_and_echo "This will install the current live system to a hard disk." +echo "Stormux Gaming Image - USB to Disk Installer" +echo "This will clone the entire USB system to an internal disk." echo -speak_and_echo "WARNING: This will completely erase the target disk!" -# Detect available disks -speak_and_echo "Detecting available disks..." -mapfile -t available_disks < <(detect_disks) - -if [[ ${#available_disks[@]} -eq 0 ]]; then - error_exit "No suitable disks found!" +# Find source device +echo "Detecting source USB device..." +SOURCE_DEVICE=$(find_source_device) +if [[ -z "$SOURCE_DEVICE" ]]; then + error_exit "Could not detect source USB device" fi -# Display available disks +SOURCE_SIZE=$(lsblk -dpno SIZE "$SOURCE_DEVICE" 2>/dev/null | tr -d ' ') +echo "Source device: $SOURCE_DEVICE ($SOURCE_SIZE)" echo -speak_and_echo "Available disks:" -for i in "${!available_disks[@]}"; do - disk="${available_disks[$i]}" - size=$(get_disk_size "$disk") - model=$(get_disk_model "$disk") - echo "$((i+1)). $disk - $size - $model" + +# Detect target disks +echo "Detecting target disks..." +mapfile -t target_disks < <(detect_target_disks "$SOURCE_DEVICE") + +if [[ ${#target_disks[@]} -eq 0 ]]; then + error_exit "No suitable target disks found (excluding source USB)" +fi + +# Display target disks +echo "Available target disks:" +for i in "${!target_disks[@]}"; do + disk="${target_disks[$i]}" + info=$(get_disk_info "$disk") + echo "$((i+1)). $disk - $info" done # Get disk selection while true; do echo - speak_and_echo "Enter the number of the disk to install to:" + echo "Enter the number of the disk to install to:" read -r selection - if [[ "$selection" =~ ^[0-9]+$ ]] && [[ "$selection" -ge 1 ]] && [[ "$selection" -le ${#available_disks[@]} ]]; then - selected_disk="${available_disks[$((selection-1))]}" + if [[ "$selection" =~ ^[0-9]+$ ]] && [[ "$selection" -ge 1 ]] && [[ "$selection" -le ${#target_disks[@]} ]]; then + TARGET_DEVICE="${target_disks[$((selection-1))]}" break else - speak_and_echo "Invalid selection. Please enter a number between 1 and ${#available_disks[@]}." + echo "Invalid selection. Please enter a number between 1 and ${#target_disks[@]}." fi done -# Final confirmation -selected_size=$(get_disk_size "$selected_disk") -selected_model=$(get_disk_model "$selected_disk") +# Check target disk size +TARGET_SIZE_BYTES=$(lsblk -dpno SIZE -b "$TARGET_DEVICE" 2>/dev/null) +SOURCE_SIZE_BYTES=$(lsblk -dpno SIZE -b "$SOURCE_DEVICE" 2>/dev/null) +if [[ "$TARGET_SIZE_BYTES" -lt "$SOURCE_SIZE_BYTES" ]]; then + error_exit "Target disk is smaller than source USB. Cannot proceed." +fi + +# Final confirmation +target_info=$(get_disk_info "$TARGET_DEVICE") echo -speak_and_echo "FINAL WARNING:" -speak_and_echo "You have selected: $selected_disk ($selected_size, $selected_model)" -speak_and_echo "ALL DATA ON THIS DISK WILL BE PERMANENTLY DESTROYED!" +echo "FINAL WARNING:" +echo "Source: $SOURCE_DEVICE ($SOURCE_SIZE)" +echo "Target: $TARGET_DEVICE ($target_info)" +echo "ALL DATA ON THE TARGET DISK WILL BE PERMANENTLY DESTROYED!" echo -speak_and_echo "Type 'yes' to continue or any other key to cancel:" +echo "Type 'yes' to continue or any other key to cancel:" read -r CONFIRM if [[ "$CONFIRM" != "yes" ]]; then - speak_and_echo "Installation cancelled." + echo "Installation cancelled." exit 0 fi -# Check if we're on UEFI or BIOS -if [[ -d /sys/firmware/efi ]]; then - BOOT_MODE="UEFI" - speak_and_echo "Detected UEFI boot mode" -else - BOOT_MODE="BIOS" - speak_and_echo "Detected BIOS boot mode" +# Unmount any mounted partitions on target disk +echo "Unmounting target disk partitions..." +umount "${TARGET_DEVICE}"* 2>/dev/null || true + +# Clone the USB to the target disk +echo "Cloning USB system to target disk..." +echo "This will take several minutes depending on USB size and disk speed." +echo + +# Use dd to clone the entire device +if ! dd if="$SOURCE_DEVICE" of="$TARGET_DEVICE" bs=4M status=progress; then + error_exit "Failed to clone USB to target disk" fi -speak_and_echo "Starting installation to $selected_disk..." +# Sync to ensure all data is written +echo "Syncing data to disk..." +sync -# Unmount any existing partitions on the target disk -speak_and_echo "Unmounting any existing partitions..." -umount "${selected_disk}"* 2>/dev/null || true +# Update the cloned system to remove USB-specific configurations +echo "Finalizing installation..." -# Create partition table -speak_and_echo "Creating partition table..." -if [[ "$BOOT_MODE" == "UEFI" ]]; then - # UEFI: GPT with EFI system partition - parted -s "$selected_disk" mklabel gpt - parted -s "$selected_disk" mkpart primary fat32 1MiB 513MiB - parted -s "$selected_disk" set 1 esp on - parted -s "$selected_disk" mkpart primary ext4 513MiB 100% - - # Set partition variables - if [[ "$selected_disk" =~ nvme|mmcblk ]]; then - EFI_PART="${selected_disk}p1" - ROOT_PART="${selected_disk}p2" - else - EFI_PART="${selected_disk}1" - ROOT_PART="${selected_disk}2" +# Mount the new root partition to make final adjustments +TARGET_ROOT_PART="${TARGET_DEVICE}1" +if [[ "$TARGET_DEVICE" =~ nvme|mmcblk ]]; then + TARGET_ROOT_PART="${TARGET_DEVICE}p1" +fi + +# Find the actual root partition (might not be partition 1) +for part in "${TARGET_DEVICE}"*; do + if [[ "$part" != "$TARGET_DEVICE" ]]; then + local fstype=$(lsblk -no FSTYPE "$part" 2>/dev/null) + if [[ "$fstype" == "ext4" ]]; then + TARGET_ROOT_PART="$part" + break + fi fi -else - # BIOS: MBR with boot flag - parted -s "$selected_disk" mklabel msdos - parted -s "$selected_disk" mkpart primary ext4 1MiB 100% - parted -s "$selected_disk" set 1 boot on +done + +# Mount and make final adjustments +TEMP_MOUNT="/mnt/stormux_target" +mkdir -p "$TEMP_MOUNT" + +if mount "$TARGET_ROOT_PART" "$TEMP_MOUNT" 2>/dev/null; then + # Remove any USB-specific markers + rm -f "$TEMP_MOUNT/home/stormux/.firstboot" 2>/dev/null || true - # Set partition variables - if [[ "$selected_disk" =~ nvme|mmcblk ]]; then - ROOT_PART="${selected_disk}p1" - else - ROOT_PART="${selected_disk}1" - fi + # Update any USB-specific configurations if needed + # (Add any specific cleanups here) + + umount "$TEMP_MOUNT" fi - -# Wait for kernel to recognize partitions -sleep 2 -partprobe "$selected_disk" -sleep 2 - -# Format partitions -speak_and_echo "Formatting partitions..." -if [[ "$BOOT_MODE" == "UEFI" ]]; then - mkfs.fat -F32 -n "STORMUX-EFI" "$EFI_PART" -fi -mkfs.ext4 -F -L "STORMUX-ROOT" "$ROOT_PART" - -# Create mount points -MOUNT_ROOT="/mnt/install_target" -mkdir -p "$MOUNT_ROOT" - -# Mount root partition -speak_and_echo "Mounting partitions..." -mount "$ROOT_PART" "$MOUNT_ROOT" - -# Mount EFI partition if UEFI -if [[ "$BOOT_MODE" == "UEFI" ]]; then - mkdir -p "$MOUNT_ROOT/boot/efi" - mount "$EFI_PART" "$MOUNT_ROOT/boot/efi" -fi - -# Copy system using rsync -speak_and_echo "Copying system files... This may take several minutes." -rsync -avx \ - --exclude=/tmp/* \ - --exclude=/proc/* \ - --exclude=/sys/* \ - --exclude=/dev/* \ - --exclude=/run/* \ - --exclude=/var/tmp/* \ - --exclude=/var/log/journal/* \ - --exclude=/mnt/* \ - --exclude=/media/* \ - --exclude=/lost+found \ - --exclude=/home/stormux/.cache/* \ - --exclude=/home/stormux/Downloads/* \ - / "$MOUNT_ROOT/" - -# Create necessary directories -speak_and_echo "Creating system directories..." -mkdir -p "$MOUNT_ROOT"/{tmp,proc,sys,dev,run,var/tmp,var/log/journal,mnt,media} - -# Generate fstab -speak_and_echo "Generating fstab..." -ROOT_UUID=$(blkid -s UUID -o value "$ROOT_PART") -cat > "$MOUNT_ROOT/etc/fstab" << EOF -# Static information about the filesystems. -# See fstab(5) for details. - -# -UUID=$ROOT_UUID / ext4 rw,relatime 0 1 -EOF - -if [[ "$BOOT_MODE" == "UEFI" ]]; then - EFI_UUID=$(blkid -s UUID -o value "$EFI_PART") - echo "UUID=$EFI_UUID /boot/efi vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2" >> "$MOUNT_ROOT/etc/fstab" -fi - -# Install bootloader -speak_and_echo "Installing bootloader..." -arch-chroot "$MOUNT_ROOT" /bin/bash << EOF -# Update package database and install GRUB -pacman -Sy --noconfirm - -if [[ "$BOOT_MODE" == "UEFI" ]]; then - # UEFI installation - pacman -S --needed --noconfirm grub efibootmgr - grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Stormux -else - # BIOS installation - pacman -S --needed --noconfirm grub - grub-install --target=i386-pc "$selected_disk" -fi - -# Generate GRUB configuration with accessibility -grub-mkconfig -o /boot/grub/grub.cfg - -# Enable accessibility in GRUB by default -if ! grep -q "GRUB_CMDLINE_LINUX_DEFAULT.*accessibility=on" /etc/default/grub; then - sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="accessibility=on /' /etc/default/grub - grub-mkconfig -o /boot/grub/grub.cfg -fi - -# Enable essential services -systemctl enable NetworkManager -systemctl enable pipewire.service -systemctl enable pipewire-pulse.service -systemctl enable unmute-audio.service -systemctl enable shutdown-sound.service -systemctl enable fenrirscreenreader-tty.service -systemctl disable espeakup.service - -# Set up user services -systemctl --global enable pipewire.service -systemctl --global enable pipewire-pulse.service -EOF - -# Remove firstboot marker if it exists -rm -f "$MOUNT_ROOT/home/stormux/.firstboot" - -# Clean up mount points -speak_and_echo "Cleaning up..." -if [[ "$BOOT_MODE" == "UEFI" ]]; then - umount "$MOUNT_ROOT/boot/efi" -fi -umount "$MOUNT_ROOT" -rmdir "$MOUNT_ROOT" +rmdir "$TEMP_MOUNT" # Success message -speak_and_echo "Installation completed successfully!" echo -speak_and_echo "The system has been installed to $selected_disk" -speak_and_echo "You can now reboot and remove the USB drive." +echo "Installation completed successfully!" +echo "The USB system has been cloned to $TARGET_DEVICE" +echo "You can now reboot and remove the USB drive." +echo "The system will boot from the internal disk." echo -speak_and_echo "Press Enter to continue..." +echo "Press Enter to continue..." read -r