Advertisement
Bungeetaco

Update2BTRFS

Nov 9th, 2024 (edited)
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 15.45 KB | None | 0 0
  1. #!/bin/bash
  2. # Ext4 to Btrfs conversion script with partition resizing
  3. # This script will resize the ext4 partition to minimal size + 100MB,
  4. # convert to btrfs, then expand to use all available space
  5.  
  6. set -euo pipefail
  7. trap 'error_exit "An error occurred at line $LINENO. Exit code: $?"' ERR
  8.  
  9. # Helper functions
  10. error_exit() {
  11.     echo -e "\n\033[1;31mError:\033[0m $1" >&2
  12.     # Attempt to unmount if we errored during mounted state
  13.     umount /mnt/boot 2>/dev/null || true
  14.     umount /mnt 2>/dev/null || true
  15.     exit 1
  16. }
  17.  
  18. print_header() {
  19.     echo -e "\n\033[1;34m=== $1 ===\033[0m\n"
  20. }
  21.  
  22. print_warning() {
  23.     echo -e "\033[1;33mWarning:\033[0m $1"
  24. }
  25.  
  26. print_success() {
  27.     echo -e "\033[1;32mSuccess:\033[0m $1"
  28. }
  29.  
  30. confirm_continue() {
  31.     local prompt="$1"
  32.     local response
  33.     while true; do
  34.         read -p "$(echo -e "\033[1;33m$prompt\033[0m (y/n): ")" response
  35.        case $response in
  36.            [Yy]) return 0 ;;
  37.            [Nn]) return 1 ;;
  38.            *) echo "Please answer with y or n" ;;
  39.        esac
  40.    done
  41. }
  42.  
  43. check_filesystem_health() {
  44.    local device="$1"
  45.    local fs_type=$(blkid -s TYPE -o value "$device")
  46.    
  47.    case $fs_type in
  48.        ext4)
  49.            local fs_state=$(tune2fs -l "$device" | grep "Filesystem state:" | awk '{print $3}')
  50.            if [ "$fs_state" != "clean" ]; then
  51.                error_exit "Filesystem on $device is not clean. Please run e2fsck first."
  52.            fi
  53.            ;;
  54.        *)
  55.            error_exit "Unexpected filesystem type: $fs_type"
  56.            ;;
  57.    esac
  58. }
  59.  
  60. validate_partition_alignment() {
  61.    local device="$1"
  62.    local start_sector=$(fdisk -l "$device" | grep "^$device" | awk '{print $2}')
  63.    if ((start_sector % 2048 != 0)); then
  64.        print_warning "Partition $device is not aligned to 2048 sectors. This might affect performance."
  65.    fi
  66. }
  67.  
  68. check_system_memory() {
  69.    local total_mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
  70.    local total_mem_mb=$((total_mem_kb / 1024))
  71.    if [ $total_mem_mb -lt 2048 ]; then
  72.        print_warning "Less than 2GB RAM detected. Conversion might be slow."
  73.    fi
  74. }
  75.  
  76. cleanup_temporary_files() {
  77.    rm -f /tmp/fstab.new /tmp/grub.new 2>/dev/null || true
  78. }
  79.  
  80. # Register cleanup
  81. trap cleanup_temporary_files EXIT
  82.  
  83. # Check if script is run as root
  84. if [ "$(id -u)" -ne 0 ]; then
  85.    error_exit "This script must be run as root"
  86. fi
  87.  
  88. # Check if running from live environment
  89. if mountpoint -q / && [ "$(stat -f -c %T /)" != "tmpfs" ]; then
  90.    error_exit "This script must be run from a live USB environment!"
  91. fi
  92.  
  93. # Check system memory
  94. check_system_memory
  95.  
  96. # Check for required tools and their versions
  97. declare -A MIN_VERSIONS=(
  98.    ["btrfs-progs"]="5.10"
  99.    ["e2fsprogs"]="1.45"
  100. )
  101.  
  102. print_header "Checking Required Tools"
  103. REQUIRED_TOOLS=(btrfs-convert arch-chroot mkfs.btrfs e2fsck resize2fs sfdisk fdisk blkid tune2fs)
  104. for tool in "${REQUIRED_TOOLS[@]}"; do
  105.    if ! command -v "$tool" >/dev/null 2>&1; then
  106.        error_exit "Required tool '$tool' not found. Please install necessary packages."
  107.    fi
  108. done
  109.  
  110. # Check btrfs-progs version
  111. BTRFS_VERSION=$(btrfs --version | grep -oP '\d+\.\d+')
  112. if awk -v v1="$BTRFS_VERSION" -v v2="${MIN_VERSIONS['btrfs-progs']}" 'BEGIN{if(v1<v2) exit 1; exit 0}'; then
  113.    print_success "btrfs-progs version $BTRFS_VERSION is sufficient"
  114. else
  115.    error_exit "btrfs-progs version $BTRFS_VERSION is too old. Need >= ${MIN_VERSIONS['btrfs-progs']}"
  116. fi
  117.  
  118. echo "All required tools are available."
  119.  
  120. # Information gathering phase
  121. print_header "System Information"
  122. echo "Available block devices:"
  123. lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,LABEL,UUID
  124. echo -e "\nPartition layout:"
  125. fdisk -l | grep "^/dev/"
  126.  
  127. # Get device information with validation
  128. get_valid_partition() {
  129.    local prompt="$1"
  130.    local partition=""
  131.    while true; do
  132.        read -p "$prompt" partition
  133.        if [[ ! $partition =~ ^[a-z0-9]+$ ]]; then
  134.            echo "Invalid format. Please use format like 'sda1' or 'nvme0n1p1'"
  135.            continue
  136.        fi
  137.        if [ -b "/dev/$partition" ]; then
  138.            break
  139.        else
  140.            echo "Partition /dev/$partition does not exist. Please check available devices above."
  141.        fi
  142.    done
  143.    echo "$partition"
  144. }
  145.  
  146. print_header "Partition Selection"
  147. echo "Please select your partitions from the list above."
  148. ROOT_PART=$(get_valid_partition "Enter your root partition (e.g., sda2): ")
  149. BOOT_PART=$(get_valid_partition "Enter your boot partition (e.g., sda1): ")
  150. ROOT_DEVICE="/dev/${ROOT_PART}"
  151. BOOT_DEVICE="/dev/${BOOT_PART}"
  152.  
  153. # Additional partition validations
  154. validate_partition_alignment "$ROOT_DEVICE"
  155. validate_partition_alignment "$BOOT_DEVICE"
  156.  
  157. # Check if partitions are currently mounted
  158. if mountpoint -q "$ROOT_DEVICE" || mountpoint -q "$BOOT_DEVICE"; then
  159.     error_exit "One or more selected partitions are currently mounted. Please unmount them first."
  160. fi
  161.  
  162. # Validate selections
  163. echo -e "\nSelected partitions:"
  164. echo "Root partition: $ROOT_DEVICE ($(lsblk -no SIZE,FSTYPE $ROOT_DEVICE))"
  165. echo "Boot partition: $BOOT_DEVICE ($(lsblk -no SIZE,FSTYPE $BOOT_DEVICE))"
  166.  
  167. # Check filesystem types
  168. ROOTFS_TYPE=$(blkid -s TYPE -o value "$ROOT_DEVICE")
  169. BOOTFS_TYPE=$(blkid -s TYPE -o value "$BOOT_DEVICE")
  170.  
  171. if [ "$ROOTFS_TYPE" != "ext4" ]; then
  172.     error_exit "Root partition is not ext4! Found: $ROOTFS_TYPE"
  173. fi
  174.  
  175. # Check filesystem health
  176. check_filesystem_health "$ROOT_DEVICE"
  177.  
  178. # Analyze partition layout
  179. DISK_NAME=$(echo "$ROOT_DEVICE" | sed 's/[0-9]*$//')
  180. PART_NUM=$(echo "$ROOT_PART" | grep -o '[0-9]*$')
  181.  
  182. [Rest of the script remains the same as before...]
  183.  
  184. # Additional verification at the end
  185. print_header "Final Security Checks"
  186. echo "Performing final security checks..."
  187.  
  188. # Verify subvolume structure
  189. if ! btrfs subvolume list /mnt | grep -q "@$"; then
  190.     error_exit "Root subvolume @ not found!"
  191. fi
  192. if ! btrfs subvolume list /mnt | grep -q "@home$"; then
  193.     error_exit "Home subvolume @home not found!"
  194. fi
  195.  
  196. # Verify boot loader files
  197. if [ ! -f /mnt/boot/grub/grub.cfg ]; then
  198.     error_exit "GRUB configuration file not found!"
  199. fi
  200.  
  201. # Verify critical system directories
  202. for dir in /mnt/etc /mnt/bin /mnt/sbin /mnt/lib; do
  203.     if [ ! -d "$dir" ]; then
  204.         error_exit "Critical system directory $dir not found!"
  205.     fi
  206. done
  207.  
  208. # Verify fstab entries
  209. if ! grep -q "subvol=@" /mnt/@/etc/fstab; then
  210.     error_exit "Root subvolume not properly configured in fstab!"
  211. fi
  212. if ! grep -q "subvol=@home" /mnt/@/etc/fstab; then
  213.     error_exit "Home subvolume not properly configured in fstab!"
  214. fi
  215.  
  216. print_success "All security checks passed!"
  217.  
  218. # Information gathering phase
  219. print_header "System Information"
  220. echo "Available block devices:"
  221. lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,LABEL
  222. echo -e "\nPartition layout:"
  223. fdisk -l | grep "^/dev/"
  224.  
  225. # Get device information with validation
  226. get_valid_partition() {
  227.     local prompt="$1"
  228.     local partition=""
  229.     while true; do
  230.         read -p "$prompt" partition
  231.         if [[ ! $partition =~ ^[a-z]+[0-9]+$ ]]; then
  232.             echo "Invalid format. Please use format like 'sda1' or 'nvme0n1p1'"
  233.             continue
  234.         fi
  235.         if [ -b "/dev/$partition" ]; then
  236.             break
  237.         else
  238.             echo "Partition /dev/$partition does not exist. Please check available devices above."
  239.         fi
  240.     done
  241.     echo "$partition"
  242. }
  243.  
  244. print_header "Partition Selection"
  245. echo "Please select your partitions from the list above."
  246. ROOT_PART=$(get_valid_partition "Enter your root partition (e.g., sda2): ")
  247. BOOT_PART=$(get_valid_partition "Enter your boot partition (e.g., sda1): ")
  248. ROOT_DEVICE="/dev/${ROOT_PART}"
  249. BOOT_DEVICE="/dev/${BOOT_PART}"
  250.  
  251. # Validate selections
  252. echo -e "\nSelected partitions:"
  253. echo "Root partition: $ROOT_DEVICE ($(lsblk -no SIZE,FSTYPE $ROOT_DEVICE))"
  254. echo "Boot partition: $BOOT_DEVICE ($(lsblk -no SIZE,FSTYPE $BOOT_DEVICE))"
  255.  
  256. # Check filesystem types
  257. ROOTFS_TYPE=$(blkid -s TYPE -o value "$ROOT_DEVICE")
  258. BOOTFS_TYPE=$(blkid -s TYPE -o value "$BOOT_DEVICE")
  259.  
  260. if [ "$ROOTFS_TYPE" != "ext4" ]; then
  261.     error_exit "Root partition is not ext4! Found: $ROOTFS_TYPE"
  262. fi
  263.  
  264. # Analyze partition layout
  265. DISK_NAME=$(echo "$ROOT_DEVICE" | sed 's/[0-9]*$//')
  266. PART_NUM=$(echo "$ROOT_PART" | grep -o '[0-9]*$')
  267.  
  268. # Check for adjacent partitions
  269. print_header "Partition Layout Analysis"
  270. NEXT_PART_NUM=$((PART_NUM + 1))
  271. NEXT_PART="${DISK_NAME}${NEXT_PART_NUM}"
  272. if [ -b "$NEXT_PART" ]; then
  273.     error_exit "Found partition $NEXT_PART after root partition. Cannot safely resize!"
  274. fi
  275.  
  276. # Space analysis
  277. print_header "Space Analysis"
  278. echo "Mounting filesystems for analysis..."
  279. mkdir -p /mnt
  280. mount "$ROOT_DEVICE" /mnt || error_exit "Failed to mount root partition"
  281. mkdir -p /mnt/boot
  282. mount "$BOOT_DEVICE" /mnt/boot || error_exit "Failed to mount boot partition"
  283.  
  284. # Get space information
  285. TOTAL_SIZE=$(df -B1 "$ROOT_DEVICE" | tail -n1 | awk '{print $2}')
  286. USED_SPACE=$(df -B1 "$ROOT_DEVICE" | tail -n1 | awk '{print $3}')
  287. USED_SPACE_MB=$((USED_SPACE / 1000000))
  288. TARGET_SIZE_MB=$((USED_SPACE_MB + 100))
  289. AVAILABLE_SPACE=$(df -B1 "$DISK_NAME" | tail -n1 | awk '{print $4}')
  290.  
  291. echo "Space analysis:"
  292. echo "Current partition size: $(numfmt --to=iec-i --suffix=B $TOTAL_SIZE)"
  293. echo "Used space: $(numfmt --to=iec-i --suffix=B $USED_SPACE)"
  294. echo "Target initial size: ${TARGET_SIZE_MB}MB (used + 100MB)"
  295. echo "Space available for expansion: $(numfmt --to=iec-i --suffix=B $AVAILABLE_SPACE)"
  296.  
  297. # Collect UUIDs and current configuration
  298. OLD_ROOT_UUID=$(blkid -s UUID -o value "$ROOT_DEVICE")
  299. OLD_BOOT_UUID=$(blkid -s UUID -o value "$BOOT_DEVICE")
  300.  
  301. print_header "Current System Configuration"
  302. echo "Current fstab content:"
  303. cat /mnt/etc/fstab
  304.  
  305. echo -e "\nCurrent GRUB configuration:"
  306. cat /mnt/etc/default/grub
  307.  
  308. print_header "Backup Options"
  309. ESTIMATED_SIZE=$(du -sh /mnt 2>/dev/null | cut -f1)
  310. echo "Estimated backup size needed: $ESTIMATED_SIZE"
  311. echo "Available backup locations:"
  312. df -h /run/media 2>/dev/null || echo "No external media mounted"
  313.  
  314. # Summary and confirmation
  315. print_header "Operation Summary"
  316. echo "The following operations will be performed:"
  317. echo "1. Shrink ext4 filesystem to ${TARGET_SIZE_MB}MB"
  318. echo "2. Resize root partition to match"
  319. echo "3. Convert to btrfs"
  320. echo "4. Create subvolumes: @, @home"
  321. echo "5. Update fstab and GRUB configuration"
  322. echo "6. Expand btrfs to use all available space"
  323. echo ""
  324. echo "Filesystem details:"
  325. echo "- Root device: $ROOT_DEVICE ($ROOTFS_TYPE)"
  326. echo "- Boot device: $BOOT_DEVICE ($BOOTFS_TYPE)"
  327. echo "- Target size before expansion: ${TARGET_SIZE_MB}MB"
  328. echo ""
  329. echo "WARNING: This operation will modify your partition table and convert"
  330. echo "your root filesystem. Make sure you have proper backups before continuing."
  331.  
  332. confirm_continue "Do you want to create a backup before proceeding?"
  333. if [[ $? -eq 0 ]]; then
  334.     echo "Creating backup... (this may take a while)"
  335.     BACKUP_PATH="/run/media/$USER/backup"
  336.     mkdir -p "$BACKUP_PATH"
  337.     tar --xattrs --acls -czpf "$BACKUP_PATH/system_backup_$(date +%Y%m%d).tar.gz" -C /mnt . ||
  338.         error_exit "Backup failed! Please check available space and try again."
  339.     echo "Backup created at: $BACKUP_PATH/system_backup_$(date +%Y%m%d).tar.gz"
  340. fi
  341.  
  342. confirm_continue "Ready to proceed with conversion?"
  343.  
  344. # Unmount filesystems
  345. print_header "Starting Conversion"
  346. echo "Unmounting filesystems..."
  347. umount /mnt/boot
  348. umount /mnt
  349.  
  350. # Perform filesystem check
  351. echo "Checking filesystem..."
  352. e2fsck -f "$ROOT_DEVICE"
  353.  
  354. # Resize filesystem
  355. echo "Resizing ext4 filesystem to ${TARGET_SIZE_MB}MB..."
  356. resize2fs "$ROOT_DEVICE" "${TARGET_SIZE_MB}M"
  357.  
  358. # Resize partition
  359. echo "Resizing partition..."
  360. echo "Unit: sectors
  361.  
  362. # Shrink partition to match filesystem
  363. Yes
  364. ${TARGET_SIZE_MB}M
  365. " | sfdisk --force -N "${PART_NUM}" "${DISK_NAME}"
  366.  
  367. # Convert to btrfs
  368. echo "Converting to btrfs..."
  369. btrfs-convert "$ROOT_DEVICE"
  370.  
  371. # Mount and set up subvolumes
  372. echo "Setting up btrfs subvolumes..."
  373. mount "$ROOT_DEVICE" /mnt
  374.  
  375. cd /mnt
  376. btrfs subvolume create @
  377. btrfs subvolume create @home
  378.  
  379. echo "Moving data to subvolumes..."
  380. mv /mnt/[!@]* /mnt/@/ || true
  381. mv /mnt/@/home/* /mnt/@home/ || true
  382.  
  383. # Expand filesystem
  384. echo "Expanding btrfs filesystem..."
  385. btrfs filesystem resize max /mnt
  386.  
  387. # Update fstab
  388. NEW_UUID=$(blkid -s UUID -o value "$ROOT_DEVICE")
  389.  
  390. print_header "Creating New fstab"
  391. cat > /mnt/@/etc/fstab <<EOF
  392. # Static information about the filesystems.
  393. # See fstab(5) for details.
  394.  
  395. # Root partition
  396. UUID=$NEW_UUID    /         btrfs    rw,relatime,space_cache=v2,compress=zstd:1,subvol=@     0 0
  397.  
  398. # Home partition
  399. UUID=$NEW_UUID    /home     btrfs    rw,relatime,space_cache=v2,compress=zstd:1,subvol=@home 0 0
  400.  
  401. # Boot partition
  402. UUID=$OLD_BOOT_UUID    /boot    $BOOTFS_TYPE    rw,relatime    0 2
  403.  
  404. EOF
  405.  
  406. # Copy additional fstab entries
  407. grep -v "${OLD_ROOT_UUID}" /mnt/etc/fstab.backup | \
  408. grep -v "${OLD_BOOT_UUID}" | \
  409. grep -v '^#' | \
  410. grep -v '^[[:space:]]*$' >> /mnt/@/etc/fstab
  411.  
  412. # Mount for final configuration
  413. print_header "Final System Configuration"
  414. echo "Mounting final system..."
  415. umount /mnt 2>/dev/null || true
  416. mount -o subvol=@ "$ROOT_DEVICE" /mnt
  417. mkdir -p /mnt/{home,boot}
  418. mount -o subvol=@home "$ROOT_DEVICE" /mnt/home
  419. mount "$BOOT_DEVICE" /mnt/boot
  420.  
  421. # Update boot configuration
  422. arch-chroot /mnt /bin/bash -c "
  423.    echo 'Updating mkinitcpio.conf...'
  424.    if ! grep -q '^MODULES=(.*btrfs.*)'  /etc/mkinitcpio.conf; then
  425.        sed -i 's/^MODULES=(/MODULES=(btrfs /' /etc/mkinitcpio.conf
  426.        echo 'Added btrfs module to mkinitcpio.conf'
  427.    fi
  428.    mkinitcpio -P
  429.  
  430.    echo 'Updating GRUB configuration...'
  431.    cp /etc/default/grub /etc/default/grub.backup
  432.    
  433.    if ! grep -q 'rootflags=subvol=@' /etc/default/grub; then
  434.        CURRENT_CMDLINE=\$(grep '^GRUB_CMDLINE_LINUX=' /etc/default/grub | cut -d'\"' -f2)
  435.        if [ -n \"\$CURRENT_CMDLINE\" ]; then
  436.            sed -i \"s|^GRUB_CMDLINE_LINUX=\\\".*\\\"|GRUB_CMDLINE_LINUX=\\\"\$CURRENT_CMDLINE rootflags=subvol=@\\\"|\" /etc/default/grub
  437.        else
  438.            sed -i 's|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"rootflags=subvol=@\"|' /etc/default/grub
  439.        fi
  440.        echo 'Added rootflags=subvol=@ to GRUB kernel parameters'
  441.    fi
  442.    
  443.    echo 'Generating new GRUB configuration...'
  444.    grub-mkconfig -o /boot/grub/grub.cfg
  445. "
  446.  
  447. # Verify final setup
  448. print_header "Final Verification"
  449. echo "Btrfs filesystem information:"
  450. btrfs filesystem show "$ROOT_DEVICE"
  451. echo -e "\nBtrfs subvolumes:"
  452. btrfs subvolume list /mnt
  453. echo -e "\nCurrent mount status:"
  454. mount | grep /mnt
  455. echo -e "\nNew fstab content:"
  456. cat /mnt/@/etc/fstab
  457.  
  458. # Optional: Remove ext2_saved
  459. echo -e "\nThe ext2_saved subvolume contains your original ext4 filesystem as a backup."
  460. echo "It's recommended to keep this until you've verified the system boots properly."
  461. confirm_continue "Remove ext2_saved backup now?"
  462. if [[ $? -eq 0 ]]; then
  463.     btrfs subvolume delete /mnt/ext2_saved
  464.     echo "Backup removed."
  465. else
  466.     echo "Backup preserved. You can remove it later with: btrfs subvolume delete /ext2_saved"
  467. fi
  468.  
  469. print_header "Conversion Complete"
  470. echo "Please verify the following before rebooting:"
  471. echo "1. Check fstab content shown above"
  472. echo "2. Verify boot files exist:"
  473. ls -l /mnt/boot/
  474. echo "3. Confirm subvolumes are correct:"
  475. btrfs subvolume list /mnt
  476. echo "4. Check GRUB configuration at /mnt/@/etc/default/grub"
  477. echo "5. Verify partition size:"
  478. lsblk "$ROOT_DEVICE"
  479. echo -e "\nAfter reboot, you can proceed with Snapper setup and additional configuration."
  480.  
  481. read -p "Press Enter to finish..."
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement