Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Ext4 to Btrfs conversion script with partition resizing
- # This script will resize the ext4 partition to minimal size + 100MB,
- # convert to btrfs, then expand to use all available space
- set -euo pipefail
- trap 'error_exit "An error occurred at line $LINENO. Exit code: $?"' ERR
- # Helper functions
- error_exit() {
- echo -e "\n\033[1;31mError:\033[0m $1" >&2
- # Attempt to unmount if we errored during mounted state
- umount /mnt/boot 2>/dev/null || true
- umount /mnt 2>/dev/null || true
- exit 1
- }
- print_header() {
- echo -e "\n\033[1;34m=== $1 ===\033[0m\n"
- }
- print_warning() {
- echo -e "\033[1;33mWarning:\033[0m $1"
- }
- print_success() {
- echo -e "\033[1;32mSuccess:\033[0m $1"
- }
- confirm_continue() {
- local prompt="$1"
- local response
- while true; do
- read -p "$(echo -e "\033[1;33m$prompt\033[0m (y/n): ")" response
- case $response in
- [Yy]) return 0 ;;
- [Nn]) return 1 ;;
- *) echo "Please answer with y or n" ;;
- esac
- done
- }
- check_filesystem_health() {
- local device="$1"
- local fs_type=$(blkid -s TYPE -o value "$device")
- case $fs_type in
- ext4)
- local fs_state=$(tune2fs -l "$device" | grep "Filesystem state:" | awk '{print $3}')
- if [ "$fs_state" != "clean" ]; then
- error_exit "Filesystem on $device is not clean. Please run e2fsck first."
- fi
- ;;
- *)
- error_exit "Unexpected filesystem type: $fs_type"
- ;;
- esac
- }
- validate_partition_alignment() {
- local device="$1"
- local start_sector=$(fdisk -l "$device" | grep "^$device" | awk '{print $2}')
- if ((start_sector % 2048 != 0)); then
- print_warning "Partition $device is not aligned to 2048 sectors. This might affect performance."
- fi
- }
- check_system_memory() {
- local total_mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
- local total_mem_mb=$((total_mem_kb / 1024))
- if [ $total_mem_mb -lt 2048 ]; then
- print_warning "Less than 2GB RAM detected. Conversion might be slow."
- fi
- }
- cleanup_temporary_files() {
- rm -f /tmp/fstab.new /tmp/grub.new 2>/dev/null || true
- }
- # Register cleanup
- trap cleanup_temporary_files EXIT
- # Check if script is run as root
- if [ "$(id -u)" -ne 0 ]; then
- error_exit "This script must be run as root"
- fi
- # Check if running from live environment
- if mountpoint -q / && [ "$(stat -f -c %T /)" != "tmpfs" ]; then
- error_exit "This script must be run from a live USB environment!"
- fi
- # Check system memory
- check_system_memory
- # Check for required tools and their versions
- declare -A MIN_VERSIONS=(
- ["btrfs-progs"]="5.10"
- ["e2fsprogs"]="1.45"
- )
- print_header "Checking Required Tools"
- REQUIRED_TOOLS=(btrfs-convert arch-chroot mkfs.btrfs e2fsck resize2fs sfdisk fdisk blkid tune2fs)
- for tool in "${REQUIRED_TOOLS[@]}"; do
- if ! command -v "$tool" >/dev/null 2>&1; then
- error_exit "Required tool '$tool' not found. Please install necessary packages."
- fi
- done
- # Check btrfs-progs version
- BTRFS_VERSION=$(btrfs --version | grep -oP '\d+\.\d+')
- if awk -v v1="$BTRFS_VERSION" -v v2="${MIN_VERSIONS['btrfs-progs']}" 'BEGIN{if(v1<v2) exit 1; exit 0}'; then
- print_success "btrfs-progs version $BTRFS_VERSION is sufficient"
- else
- error_exit "btrfs-progs version $BTRFS_VERSION is too old. Need >= ${MIN_VERSIONS['btrfs-progs']}"
- fi
- echo "All required tools are available."
- # Information gathering phase
- print_header "System Information"
- echo "Available block devices:"
- lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,LABEL,UUID
- echo -e "\nPartition layout:"
- fdisk -l | grep "^/dev/"
- # Get device information with validation
- get_valid_partition() {
- local prompt="$1"
- local partition=""
- while true; do
- read -p "$prompt" partition
- if [[ ! $partition =~ ^[a-z0-9]+$ ]]; then
- echo "Invalid format. Please use format like 'sda1' or 'nvme0n1p1'"
- continue
- fi
- if [ -b "/dev/$partition" ]; then
- break
- else
- echo "Partition /dev/$partition does not exist. Please check available devices above."
- fi
- done
- echo "$partition"
- }
- print_header "Partition Selection"
- echo "Please select your partitions from the list above."
- ROOT_PART=$(get_valid_partition "Enter your root partition (e.g., sda2): ")
- BOOT_PART=$(get_valid_partition "Enter your boot partition (e.g., sda1): ")
- ROOT_DEVICE="/dev/${ROOT_PART}"
- BOOT_DEVICE="/dev/${BOOT_PART}"
- # Additional partition validations
- validate_partition_alignment "$ROOT_DEVICE"
- validate_partition_alignment "$BOOT_DEVICE"
- # Check if partitions are currently mounted
- if mountpoint -q "$ROOT_DEVICE" || mountpoint -q "$BOOT_DEVICE"; then
- error_exit "One or more selected partitions are currently mounted. Please unmount them first."
- fi
- # Validate selections
- echo -e "\nSelected partitions:"
- echo "Root partition: $ROOT_DEVICE ($(lsblk -no SIZE,FSTYPE $ROOT_DEVICE))"
- echo "Boot partition: $BOOT_DEVICE ($(lsblk -no SIZE,FSTYPE $BOOT_DEVICE))"
- # Check filesystem types
- ROOTFS_TYPE=$(blkid -s TYPE -o value "$ROOT_DEVICE")
- BOOTFS_TYPE=$(blkid -s TYPE -o value "$BOOT_DEVICE")
- if [ "$ROOTFS_TYPE" != "ext4" ]; then
- error_exit "Root partition is not ext4! Found: $ROOTFS_TYPE"
- fi
- # Check filesystem health
- check_filesystem_health "$ROOT_DEVICE"
- # Analyze partition layout
- DISK_NAME=$(echo "$ROOT_DEVICE" | sed 's/[0-9]*$//')
- PART_NUM=$(echo "$ROOT_PART" | grep -o '[0-9]*$')
- [Rest of the script remains the same as before...]
- # Additional verification at the end
- print_header "Final Security Checks"
- echo "Performing final security checks..."
- # Verify subvolume structure
- if ! btrfs subvolume list /mnt | grep -q "@$"; then
- error_exit "Root subvolume @ not found!"
- fi
- if ! btrfs subvolume list /mnt | grep -q "@home$"; then
- error_exit "Home subvolume @home not found!"
- fi
- # Verify boot loader files
- if [ ! -f /mnt/boot/grub/grub.cfg ]; then
- error_exit "GRUB configuration file not found!"
- fi
- # Verify critical system directories
- for dir in /mnt/etc /mnt/bin /mnt/sbin /mnt/lib; do
- if [ ! -d "$dir" ]; then
- error_exit "Critical system directory $dir not found!"
- fi
- done
- # Verify fstab entries
- if ! grep -q "subvol=@" /mnt/@/etc/fstab; then
- error_exit "Root subvolume not properly configured in fstab!"
- fi
- if ! grep -q "subvol=@home" /mnt/@/etc/fstab; then
- error_exit "Home subvolume not properly configured in fstab!"
- fi
- print_success "All security checks passed!"
- # Information gathering phase
- print_header "System Information"
- echo "Available block devices:"
- lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,LABEL
- echo -e "\nPartition layout:"
- fdisk -l | grep "^/dev/"
- # Get device information with validation
- get_valid_partition() {
- local prompt="$1"
- local partition=""
- while true; do
- read -p "$prompt" partition
- if [[ ! $partition =~ ^[a-z]+[0-9]+$ ]]; then
- echo "Invalid format. Please use format like 'sda1' or 'nvme0n1p1'"
- continue
- fi
- if [ -b "/dev/$partition" ]; then
- break
- else
- echo "Partition /dev/$partition does not exist. Please check available devices above."
- fi
- done
- echo "$partition"
- }
- print_header "Partition Selection"
- echo "Please select your partitions from the list above."
- ROOT_PART=$(get_valid_partition "Enter your root partition (e.g., sda2): ")
- BOOT_PART=$(get_valid_partition "Enter your boot partition (e.g., sda1): ")
- ROOT_DEVICE="/dev/${ROOT_PART}"
- BOOT_DEVICE="/dev/${BOOT_PART}"
- # Validate selections
- echo -e "\nSelected partitions:"
- echo "Root partition: $ROOT_DEVICE ($(lsblk -no SIZE,FSTYPE $ROOT_DEVICE))"
- echo "Boot partition: $BOOT_DEVICE ($(lsblk -no SIZE,FSTYPE $BOOT_DEVICE))"
- # Check filesystem types
- ROOTFS_TYPE=$(blkid -s TYPE -o value "$ROOT_DEVICE")
- BOOTFS_TYPE=$(blkid -s TYPE -o value "$BOOT_DEVICE")
- if [ "$ROOTFS_TYPE" != "ext4" ]; then
- error_exit "Root partition is not ext4! Found: $ROOTFS_TYPE"
- fi
- # Analyze partition layout
- DISK_NAME=$(echo "$ROOT_DEVICE" | sed 's/[0-9]*$//')
- PART_NUM=$(echo "$ROOT_PART" | grep -o '[0-9]*$')
- # Check for adjacent partitions
- print_header "Partition Layout Analysis"
- NEXT_PART_NUM=$((PART_NUM + 1))
- NEXT_PART="${DISK_NAME}${NEXT_PART_NUM}"
- if [ -b "$NEXT_PART" ]; then
- error_exit "Found partition $NEXT_PART after root partition. Cannot safely resize!"
- fi
- # Space analysis
- print_header "Space Analysis"
- echo "Mounting filesystems for analysis..."
- mkdir -p /mnt
- mount "$ROOT_DEVICE" /mnt || error_exit "Failed to mount root partition"
- mkdir -p /mnt/boot
- mount "$BOOT_DEVICE" /mnt/boot || error_exit "Failed to mount boot partition"
- # Get space information
- TOTAL_SIZE=$(df -B1 "$ROOT_DEVICE" | tail -n1 | awk '{print $2}')
- USED_SPACE=$(df -B1 "$ROOT_DEVICE" | tail -n1 | awk '{print $3}')
- USED_SPACE_MB=$((USED_SPACE / 1000000))
- TARGET_SIZE_MB=$((USED_SPACE_MB + 100))
- AVAILABLE_SPACE=$(df -B1 "$DISK_NAME" | tail -n1 | awk '{print $4}')
- echo "Space analysis:"
- echo "Current partition size: $(numfmt --to=iec-i --suffix=B $TOTAL_SIZE)"
- echo "Used space: $(numfmt --to=iec-i --suffix=B $USED_SPACE)"
- echo "Target initial size: ${TARGET_SIZE_MB}MB (used + 100MB)"
- echo "Space available for expansion: $(numfmt --to=iec-i --suffix=B $AVAILABLE_SPACE)"
- # Collect UUIDs and current configuration
- OLD_ROOT_UUID=$(blkid -s UUID -o value "$ROOT_DEVICE")
- OLD_BOOT_UUID=$(blkid -s UUID -o value "$BOOT_DEVICE")
- print_header "Current System Configuration"
- echo "Current fstab content:"
- cat /mnt/etc/fstab
- echo -e "\nCurrent GRUB configuration:"
- cat /mnt/etc/default/grub
- print_header "Backup Options"
- ESTIMATED_SIZE=$(du -sh /mnt 2>/dev/null | cut -f1)
- echo "Estimated backup size needed: $ESTIMATED_SIZE"
- echo "Available backup locations:"
- df -h /run/media 2>/dev/null || echo "No external media mounted"
- # Summary and confirmation
- print_header "Operation Summary"
- echo "The following operations will be performed:"
- echo "1. Shrink ext4 filesystem to ${TARGET_SIZE_MB}MB"
- echo "2. Resize root partition to match"
- echo "3. Convert to btrfs"
- echo "4. Create subvolumes: @, @home"
- echo "5. Update fstab and GRUB configuration"
- echo "6. Expand btrfs to use all available space"
- echo ""
- echo "Filesystem details:"
- echo "- Root device: $ROOT_DEVICE ($ROOTFS_TYPE)"
- echo "- Boot device: $BOOT_DEVICE ($BOOTFS_TYPE)"
- echo "- Target size before expansion: ${TARGET_SIZE_MB}MB"
- echo ""
- echo "WARNING: This operation will modify your partition table and convert"
- echo "your root filesystem. Make sure you have proper backups before continuing."
- confirm_continue "Do you want to create a backup before proceeding?"
- if [[ $? -eq 0 ]]; then
- echo "Creating backup... (this may take a while)"
- BACKUP_PATH="/run/media/$USER/backup"
- mkdir -p "$BACKUP_PATH"
- tar --xattrs --acls -czpf "$BACKUP_PATH/system_backup_$(date +%Y%m%d).tar.gz" -C /mnt . ||
- error_exit "Backup failed! Please check available space and try again."
- echo "Backup created at: $BACKUP_PATH/system_backup_$(date +%Y%m%d).tar.gz"
- fi
- confirm_continue "Ready to proceed with conversion?"
- # Unmount filesystems
- print_header "Starting Conversion"
- echo "Unmounting filesystems..."
- umount /mnt/boot
- umount /mnt
- # Perform filesystem check
- echo "Checking filesystem..."
- e2fsck -f "$ROOT_DEVICE"
- # Resize filesystem
- echo "Resizing ext4 filesystem to ${TARGET_SIZE_MB}MB..."
- resize2fs "$ROOT_DEVICE" "${TARGET_SIZE_MB}M"
- # Resize partition
- echo "Resizing partition..."
- echo "Unit: sectors
- # Shrink partition to match filesystem
- Yes
- ${TARGET_SIZE_MB}M
- " | sfdisk --force -N "${PART_NUM}" "${DISK_NAME}"
- # Convert to btrfs
- echo "Converting to btrfs..."
- btrfs-convert "$ROOT_DEVICE"
- # Mount and set up subvolumes
- echo "Setting up btrfs subvolumes..."
- mount "$ROOT_DEVICE" /mnt
- cd /mnt
- btrfs subvolume create @
- btrfs subvolume create @home
- echo "Moving data to subvolumes..."
- mv /mnt/[!@]* /mnt/@/ || true
- mv /mnt/@/home/* /mnt/@home/ || true
- # Expand filesystem
- echo "Expanding btrfs filesystem..."
- btrfs filesystem resize max /mnt
- # Update fstab
- NEW_UUID=$(blkid -s UUID -o value "$ROOT_DEVICE")
- print_header "Creating New fstab"
- cat > /mnt/@/etc/fstab <<EOF
- # Static information about the filesystems.
- # See fstab(5) for details.
- # Root partition
- UUID=$NEW_UUID / btrfs rw,relatime,space_cache=v2,compress=zstd:1,subvol=@ 0 0
- # Home partition
- UUID=$NEW_UUID /home btrfs rw,relatime,space_cache=v2,compress=zstd:1,subvol=@home 0 0
- # Boot partition
- UUID=$OLD_BOOT_UUID /boot $BOOTFS_TYPE rw,relatime 0 2
- EOF
- # Copy additional fstab entries
- grep -v "${OLD_ROOT_UUID}" /mnt/etc/fstab.backup | \
- grep -v "${OLD_BOOT_UUID}" | \
- grep -v '^#' | \
- grep -v '^[[:space:]]*$' >> /mnt/@/etc/fstab
- # Mount for final configuration
- print_header "Final System Configuration"
- echo "Mounting final system..."
- umount /mnt 2>/dev/null || true
- mount -o subvol=@ "$ROOT_DEVICE" /mnt
- mkdir -p /mnt/{home,boot}
- mount -o subvol=@home "$ROOT_DEVICE" /mnt/home
- mount "$BOOT_DEVICE" /mnt/boot
- # Update boot configuration
- arch-chroot /mnt /bin/bash -c "
- echo 'Updating mkinitcpio.conf...'
- if ! grep -q '^MODULES=(.*btrfs.*)' /etc/mkinitcpio.conf; then
- sed -i 's/^MODULES=(/MODULES=(btrfs /' /etc/mkinitcpio.conf
- echo 'Added btrfs module to mkinitcpio.conf'
- fi
- mkinitcpio -P
- echo 'Updating GRUB configuration...'
- cp /etc/default/grub /etc/default/grub.backup
- if ! grep -q 'rootflags=subvol=@' /etc/default/grub; then
- CURRENT_CMDLINE=\$(grep '^GRUB_CMDLINE_LINUX=' /etc/default/grub | cut -d'\"' -f2)
- if [ -n \"\$CURRENT_CMDLINE\" ]; then
- sed -i \"s|^GRUB_CMDLINE_LINUX=\\\".*\\\"|GRUB_CMDLINE_LINUX=\\\"\$CURRENT_CMDLINE rootflags=subvol=@\\\"|\" /etc/default/grub
- else
- sed -i 's|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"rootflags=subvol=@\"|' /etc/default/grub
- fi
- echo 'Added rootflags=subvol=@ to GRUB kernel parameters'
- fi
- echo 'Generating new GRUB configuration...'
- grub-mkconfig -o /boot/grub/grub.cfg
- "
- # Verify final setup
- print_header "Final Verification"
- echo "Btrfs filesystem information:"
- btrfs filesystem show "$ROOT_DEVICE"
- echo -e "\nBtrfs subvolumes:"
- btrfs subvolume list /mnt
- echo -e "\nCurrent mount status:"
- mount | grep /mnt
- echo -e "\nNew fstab content:"
- cat /mnt/@/etc/fstab
- # Optional: Remove ext2_saved
- echo -e "\nThe ext2_saved subvolume contains your original ext4 filesystem as a backup."
- echo "It's recommended to keep this until you've verified the system boots properly."
- confirm_continue "Remove ext2_saved backup now?"
- if [[ $? -eq 0 ]]; then
- btrfs subvolume delete /mnt/ext2_saved
- echo "Backup removed."
- else
- echo "Backup preserved. You can remove it later with: btrfs subvolume delete /ext2_saved"
- fi
- print_header "Conversion Complete"
- echo "Please verify the following before rebooting:"
- echo "1. Check fstab content shown above"
- echo "2. Verify boot files exist:"
- ls -l /mnt/boot/
- echo "3. Confirm subvolumes are correct:"
- btrfs subvolume list /mnt
- echo "4. Check GRUB configuration at /mnt/@/etc/default/grub"
- echo "5. Verify partition size:"
- lsblk "$ROOT_DEVICE"
- echo -e "\nAfter reboot, you can proceed with Snapper setup and additional configuration."
- read -p "Press Enter to finish..."
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement