How can I create a bootable drive for ArchLInux on macOS?

I'm trying to figure out how to do the equivalent of these instructions for creating an archlinux bootable drive on a Mac. (I'm using a Micro SD to USB adapter.) I found these instructions on partitioning a flash drive, but I'm not sure how to apply them, since the instructions I'm trying to "translate" into macOS terms talk about sectors, and that's very much a lower level than I'm used to working in. To be expressly clear, my question is this: how do I achieve the same stuff as in the instructions using macOS 10.12.6? Thanks!


Solution 1:

There is no command in macOS to format a partition to use a linux file system. In other words, there is no equivalent for the Arch Linux mkfs.ext4 command. There are many possible solutions to this problem. Below is one such solution.

I propose that the best way, to accomplish the creation of the bootable drive, is to boot the Arch Linux operation system on your Mac. This will allow you to use the commands shown in the web link you provided. This boot of Arch Linux will be done from a 1 GB or larger flash drive. The advantage to using a flash drive is that you will not have to install Arch Linux on any of your internal drives.

Manual Approach to Creating a Mac Bootable Arch Linux Flash Drive

Note: You can copy and paste each command below into the Terminal application window. This will reduce the amount of typing and the risk of type errors.

  1. Download an .iso file. The Mac compatible file I chose to download is named archlinux-2017.12.01-x86_64.iso. You can find this file at this Arch Linux web site. This file was downloaded to my "Downloads" folder. I will assume you will do the same.
  2. Open a Terminal application window. Enter the following command to navigate to your "Downloads" folder.

    cd  ~/Downloads
    
  3. Convert this .iso file to a format that is compatible with flash drives. The command below accomplishes this task.

    hdiutil  convert  archlinux-2017.12.01-x86_64.iso  -format  UDRW  -o  target.img
    
  4. The .dmg characters may be appended to the output file name. Enter the following command to check if this occurred.

    ls  target.img.dmg
    

    If file named target.img.dmg appears, then enter the next command to rename this file.

    mv  -f  target.img.dmg  target.img
    
  5. Enter the command below to get a before list of drives.

    ls  /dev  |  egrep  ^disk\\d+$
    
  6. Insert your flash drive.

  7. Enter the command below again to get an after list of drives. Next, determine the identifier assigned to your flash drive (e.g. The identifier that did not appear in step 5)

    ls  /dev  |  egrep  ^disk\\d+$
    

    Note: In the subsequent steps, replace diskN with the identifier assigned to the flash drive.

  8. Enter the command below to unmount the flash drive.

    diskutil  unmountdisk  diskN
    

    Note: If you see the error Unmount of diskN failed: at least one volume could not be unmounted, open the Disk Utility application and unmount the volume(s) (don't eject).

  9. Enter the command below to copy target.img to your flash drive.

    sudo  dd  if=target.img  bs=1m  of=/dev/diskN
    

    Note the following:

    • This command will require you to enter your login password.
    • To view the progress of the copy operation, press the control+T key, while the command is executing.
    • Using /dev/rdisk instead of /dev/disk may be faster.
    • If you see the error dd: Invalid number '1m', you are using the GNU dd command. Use the same command but replace bs=1m with bs=1M.
    • If you see the error dd: /dev/diskN: Resource busy, make sure the drive is not in use. Open the Disk Utility application and unmount the volume(s) (don't eject).
  10. When the 'dd' command completes, you will receive the following pop up message. You should select "Ignore".

    e1

    Note: The the disk (flash drive) is readable by your computer. The message should have said: "The disk you inserted was not readable by macOS."

    To continue to the next part of this procedure, you will need to leave the flash drive plugged in. If you do need to eject the flash drive, you can either use the Disk Utility application or enter the command given below.

    diskutil  eject  /dev/diskN
    

Initialization of the Micro SD Card

The instructions, you referenced, assume the micro SD card is either uninitialized or has been MBR initialized with no partitions. You may want to preform the following steps to insure your micro SD card is properly initialized.

  1. Enter the command below to get a before list of drives.

    ls  /dev  |  egrep  ^disk\\d+$
    
  2. Using the adapter, plug the micro SD card into the Mac.

  3. Enter the command below again to get an after list of drives. Next, determine the identifier assigned to your micro SD card (e.g. The identifier that did not appear in step 1)

    ls   /dev  |  egrep  ^disk\\d+$
    

    Note: In the subsequent steps, replace diskN with the identifier assigned to the micro SD card.

  4. Enter the following command to initialize the micro SD card.

    diskutil  partitiondisk  diskN  1  mbr  free  none  r
    
  5. Enter the command below to unmount the micro SD card.

    diskutil  eject  diskN
    

Boot Your Mac to Arch Linux

  1. If the Mac bootable Arch Linux flash drive is not plugged in, do so now.
  2. Restart your Mac and immediately hold down the option key, until the Startup Manager appears.
  3. Select the icon labeled "EFI Boot", then select the arrow below the label.
  4. Select "Arch Linux archiso x86_64 UEFI CD" or wait for this to happen automatically.

A Note About Drive Identifiers

Hardware used by machines running the Linux, Unix, macOS and legacy OS X operating systems are given character string device identifiers. In the context of your question, we are only dealing with devices that are drives. The macOS operating system uses diskN to identify HDD, SSD, SD cards, micro SD cards, USB flash drives, etc. The value N is some integer greater than zero. The Arch Linux operating system uses a different scheme. Here, the form of the drive identifier is sdX, where X is a lower case letter in the English alphabet.

Micro SD Card Creation

Note: Since you are booting to a "Live" version of Arch Linux, I made some changes to the procedure you referenced.

  1. Insert a micro SD card into your computer and record which device identifier it is (dmesg | tail)
  2. Make sure it's not mounted (umount /dev/sdX* or umount /dev/mmcblk*)
  3. Start fdisk to partition the SD card:

    fdisk  /dev/sdX
    
  4. At the fdisk prompt, create the new partitions:

    a. Type n, then p for primary, 1 for the first partition on the drive, the return key to accept the default starting sectors, and +100M for the ending sector.

    b. Type t to set the type, then c to set it to FAT.

    c. Type n, then p for primary, 2 for the second partition, and press the return key twice to accept default values.

    d. Exit by typing w.

  5. Create and mount the vfat filesystem:

    mkfs.vfat  /dev/sdX1
    mkdir  boot
    mount  /dev/sdX1 boot
    
  6. Create and mount the ext4 filesystem:

    mkfs.ext4  /dev/sdX2
    mkdir  root
    mount  /dev/sdX2 root
    
  7. Download and extract the root filesystem:

    cd  ~/root
    wget  http://os.archlinuxarm.org/os/ArchLinuxARM-utilite-latest.tar.gz
    cd  ~
    bsdtar  -xpf  root/ArchLinuxARM-utilite-latest.tar.gz  -C  root
    rm  root/ArchLinuxARM-utilite-latest.tar.gz
    
  8. Copy boot files to the boot partition:

    cp  root/boot/*  boot
    
  9. Unmount the partitions:

    umount  boot  root
    
  10. Remove the micro SD card from your computer and insert it into the Utilite on the front panel

Solution 2:

This is how you do it. This works with rpi of 1st, 2nd and 3rd generation.

I separated the code in bash functions to make it more readable, hopefully.

There may be extraneous mount/unmounts, but most diskutil action result in the volumes being auto-mounted by MacOS, so we need to unmount them before continuing otherwise the commands fail saying the resource is busy.

We need to install a few dependencies. This part is not as automated as I'd like it to be.

curl -OL https://raw.githubusercontent.com/yalp/homebrew-core/fuse-ext2/Formula/fuse-ext2.rb

Now edit that file and add to it after the caveat section:

  patch do
    url "https://github.com/alperakcan/fuse-ext2/files/2576060/0001-Fix-new-Xcode-compilation.patch.txt"
    sha256 "a2a8ff14f36754aead1745b4b5f53b0333376d1bf6abe659ec4eacdbb232aceb"
  end

Now we install the dependencies:

brew install \
    findutils \
    e2fsprogs \
    pv

brew install --head ./fuse-ext2.rb

sudo cp -pR /usr/local/opt/fuse-ext2/System/Library/Filesystems/fuse-ext2.fs /Library/Filesystems/
sudo chown -R root:wheel /Library/Filesystems/fuse-ext2.fs

sudo cp -pR /usr/local/opt/fuse-ext2/System/Library/PreferencePanes/fuse-ext2.prefPane /Library/PreferencePanes/
sudo chown -R root:wheel /Library/PreferencePanes/fuse-ext2.prefPane

Finally, this is the script to format the SD card. Copy paste this script, make it executable chmod a+x FILE and call it with two arguments DEVICE and MODEL.

The script is smart enough to propose you with correct values for DEVICE and MODEL.

#!/bin/bash

function require_device() {
    local device="$1"
    local usage="$2"
    local available_devices=$(get_available_devices)
    if [ -z "$available_devices" ]; then
        echo "$usage"
        echo
        echo "No compatible device found."
        exit 1
    fi
    if [ -z "$device" ] || ! contains "$available_devices" "$device"; then
        echo "$usage"
        echo
        echo "DEVICE must be one of:"
        echo "$available_devices"
        exit 1
    fi

    echo "/dev/$device"
}

function get_available_devices() {
    diskutil list external physical \
        | grep /dev \
        | cut -d" " -f1 \
        | cut -d/ -f3
}

function require_model() {
    local model="$1"
    local usage="$2"
    local available_models=$(echo -e "rpi\nrpi2\nrpi3")
    if [ -z "$model" ] || ! contains "$available_models" "$model"; then
        echo "$usage"
        echo
        echo "MODEL must be one of:"
        echo "$available_models"
        exit 1
    fi

    echo "$model"
}

function get_arch_filename() {
    local model="$1"

    if [ "$model" = "rpi" ]; then
        echo 'ArchLinuxARM-rpi-latest.tar.gz'
    elif [ "$model" = "rpi2" ]; then
        echo 'ArchLinuxARM-rpi-2-latest.tar.gz'
    elif [ "$model" = "rpi3" ]; then
        echo 'ArchLinuxARM-rpi-3-latest.tar.gz'
    else
        echo "Unsupported model $model"
        exit 1
    fi
}
function download_archlinux() {
    local model="$1"
    local filename="$2"

    echo "Checking if we need to download the latest ArchLinuxARM iso"
    curl --silent --location \
         --output "new-md5-$model" \
         "http://os.archlinuxarm.org/os/$filename.md5"
    local current_md5="$(md5sum "$filename")"

    echo "Newest md5:" "$(cat "new-md5-$model")"
    echo "Current md5:" "$current_md5"

    if [ "$(cat "new-md5-$model")" != "$current_md5" ]; then
        echo "We do, downloading in the background..."
        (
            if curl --silent --location \
                    --remote-name \
                    "http://os.archlinuxarm.org/os/$filename"; then
                echoerr "Download done."
            else
                echoerr "Failed to download."
            fi
        )&
        local process=$!
    else
        echo "We don't, continuing"
        local process=0
    fi

    return "$process"
}

function wait_for_download() {
    local process="$1"

    if [ "$process" -ne 0 ]; then
        echo "Waiting for download to finish."
        wait "$process" || exit 1
    fi
}

function untar_and_copy_arch() {
    local filename="$1"
    local device_dir="$2"

    echo 'Untaring into root.'
    if [ ! -f "$device_dir/root/root/.bootstrap" ]; then
        sudo sh -c "pv $filename | bsdtar -xpf - -C $device_dir/root" \
             && sudo touch "$device_dir/root/root/.bootstrap"
    fi
    sudo mv "$device_dir"/root/boot/* "$device_dir"/boot
    echo 'Running sync, can take a few minutes...'
    sync
}

function umount_device() {
    local tmp_dir="$1"
    local device="$2"

    diskutil unmountDisk "$device"

    if mount | grep "$tmp_dir/root" > /dev/null \
            || mount | grep "${device}s2" > /dev/null; then
        echoerr "Unmounting mounted root."
        sudo umount "$p2"
    fi
}

function mount_device() {
    local tmp_dir="$1"
    local device="$2"

    echo "Mounting $device root and boot on $tmp_dir"
    mkdir -p "$tmp_dir/root" "$tmp_dir/boot" || exit 1
    sudo diskutil mount -mountPoint "$tmp_dir/boot" "${device}s2" || exit 1
    sudo fuse-ext2 "${device}s2" "$tmp_dir/root" -o rw+ || exit 1
}

usage="$0 DEVICE MODEL"

device="$(require_device "$1" "$usage")" || exit 1
device_root="$("${device}s2")"

model="$(require_model "$2" "$usage")" || exit 1
filename="$(get_arch_filename "$model")"


download_archlinux "$model" "$filename"
process="$?"

umount_device "$tmp_dir" "$device"


diskutil partitionDisk "$device" 2 MBR \
        "MS-DOS FAT32" BOOT 100M \
        "MS-DOS FAT32" ROOT R \
        || exit 1
umount_device "$tmp_dir" "$device"

format_ext4 "$device_root" ROOT || exit 1

wait_for_download "$process"

sleep 3
umount_device "$tmp_dir" "$device"
mount_device "$tmp_dir" "$device"

untar_and_copy_arch "$filename" "$tmp_dir"

umount_device "$tmp_dir" "$device"