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.
- Download an
.iso
file. The Mac compatible file I chose to download is namedarchlinux-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. -
Open a Terminal application window. Enter the following command to navigate to your "Downloads" folder.
cd ~/Downloads
-
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
-
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
-
Enter the command below to get a before list of drives.
ls /dev | egrep ^disk\\d+$
Insert your flash drive.
-
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. -
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). -
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 GNUdd
command. Use the same command but replacebs=1m
withbs=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).
-
When the 'dd' command completes, you will receive the following pop up message. You should select "Ignore".
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.
-
Enter the command below to get a before list of drives.
ls /dev | egrep ^disk\\d+$
Using the adapter, plug the micro SD card into the Mac.
-
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. -
Enter the following command to initialize the micro SD card.
diskutil partitiondisk diskN 1 mbr free none r
-
Enter the command below to unmount the micro SD card.
diskutil eject diskN
Boot Your Mac to Arch Linux
- If the Mac bootable Arch Linux flash drive is not plugged in, do so now.
- Restart your Mac and immediately hold down the option key, until the Startup Manager appears.
- Select the icon labeled "EFI Boot", then select the arrow below the label.
- 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.
- Insert a micro SD card into your computer and record which device identifier it is (
dmesg | tail
) - Make sure it's not mounted (
umount /dev/sdX*
orumount /dev/mmcblk*
) -
Start
fdisk
to partition the SD card:fdisk /dev/sdX
-
At the
fdisk
prompt, create the new partitions:a. Type
n
, thenp
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, thenc
to set it to FAT.c. Type
n
, thenp
for primary,2
for the second partition, and press the return key twice to accept default values.d. Exit by typing
w
. -
Create and mount the vfat filesystem:
mkfs.vfat /dev/sdX1 mkdir boot mount /dev/sdX1 boot
-
Create and mount the ext4 filesystem:
mkfs.ext4 /dev/sdX2 mkdir root mount /dev/sdX2 root
-
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
-
Copy boot files to the boot partition:
cp root/boot/* boot
-
Unmount the partitions:
umount boot root
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"