#!/usr/bin/env bash # adafruit-pi-externalroot-helper # # Configure a Raspbian system to use an external USB drive as root filesystem. # # See README.md for details of usage. set -e function print_version() { echo "Adafruit Pi External Root Helper v0.1.0" exit 1 } function print_help() { echo "Usage: $0 -d [target device]" echo " -h Print this help" echo " -v Print version information" echo " -d [device] Specify path of device to convert to root" echo echo "You must specify a device. See:" echo "https://learn.adafruit.com/external-drive-as-raspberry-pi-root" exit 1 } # Display an error message and quit: function bail() { FG="1;31m" BG="40m" echo -en "[\033[${FG}\033[${BG}error\033[0m] " echo "$*" exit 1 } # Display an info message: function info() { task="$1" shift FG="1;32m" BG="40m" echo -e "[\033[${FG}\033[${BG}${task}\033[0m] $*" } if [[ $EUID -ne 0 ]]; then bail "must be run as root. try: sudo adafruit-pi-externalroot-helper" fi # Handle arguments: args=$(getopt -uo 'hvd:' -- $*) [ $? != 0 ] && print_help set -- $args for i do case "$i" in -h) print_help ;; -v) print_version ;; -d) target_drive="$2" echo "Target drive = ${2}" shift shift ;; esac done if [[ ! -e "$target_drive" ]]; then bail "Target ${target_drive} must be existing device (use -d /dev/foo to specify)" fi info "start" "Will create new ext4 filesystem on ${target_drive}" info "start" "If there is data on ${target_drive}, it will be lost." read -p "Really proceed? (y)es / (n)o " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]] then echo "Quitting." exit fi export target_partition="${target_drive}1" info "dependencies" "Installing gdisk, rsync, and parted." # All except gdisk are probably installed, but let's make sure. apt-get install gdisk rsync parted info "fs create" "Creating ${target_partition}" # The alternative here seems to be to pipe a series of commands # to fdisk(1), similar to how it's done by raspi-config: # https://github.com/asb/raspi-config/blob/3a5d75340a1f9fe5d7eebfb28fee0e24033f4fd3/raspi-config#L68 # This seemed to work, but I was running into weirdness because # that doesn't seem to make a GPT, and later on I couldn't get # partition unique GUID from gdisk. parted(1) is also a nice # option because it's scriptable and allows partition sizes to # be specified in percentages. parted --script "${target_drive}" mklabel gpt parted --script --align optimal "${target_drive}" mkpart primary ext4 0% 100% info "fs create" "Creating ext4 filesystem on ${target_partition}" mkfs -t ext4 -L rootfs "${target_partition}" info "fs id" "Getting UUID for target partition" eval `blkid -o export "${target_partition}"` export target_partition_uuid=$UUID info "fs id" "Getting Partition unique GUID for target filesystem" # Ok, so the only way I was able to get this was using gdisk. # I don't quite understand the different between this value and # the one you can get with blkid and tune2fs (which seem to give # the same thing). Nevertheless, this seems to be necessary to # get a value that can be used in cmdline.txt. I think it's a # GUID specifically for the GPT partition table entry. export partition_unique_guid=`echo 'i' | sudo gdisk "${target_drive}" | grep 'Partition unique GUID:' | awk '{print $4}'` info "fs id" "Target partition UUID: ${target_partition_uuid}" info "fs id" "Partition unique GUID: ${partition_unique_guid}" info "fs copy" "Mounting ${target_partition} on /mnt" mount "${target_partition}" /mnt info "fs copy" "Copying root filesystem to ${target_partition} with rsync" info "fs copy" "This will take quite a while. Please be patient!" rsync -ax / /mnt info "boot config" "Configuring boot from {$target_partition}" # rootdelay=5 is likely not necessary here, but seems to do no harm. cp /boot/cmdline.txt /boot/cmdline.txt.bak sed -i "s|root=\/dev\/mmcblk0p2|root=PARTUUID=${partition_unique_guid} rootdelay=5|" /boot/cmdline.txt info "boot config" "Commenting out old root partition in /etc/fstab, adding new one" # These changes are made on the new drive after copying so that they # don't have to be undone in order to switch back to booting from the # SD card. sed -i '/mmcblk0p2/s/^/#/' /mnt/etc/fstab echo "/dev/disk/by-uuid/${target_partition_uuid} / ext4 defaults,noatime 0 1" >> /mnt/etc/fstab info "boot config" "Ok, your system should be ready. You may wish to check:" info "boot config" " /mnt/etc/fstab" info "boot config" " /boot/cmdline.txt" info "boot config" "Your new root drive is currently accessible under /mnt." info "boot config" "In order to restart with this drive at /, please type:" info "boot config" "sudo reboot"