Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
colisee committed Oct 1, 2021
1 parent 46866fa commit ef64a5c
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 2 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
# rpi2GPT
Copy a raspiOS image to a GPT-partitioned device
# rpi-img2GPT
Copy a raspiOS disk image to a GPT-partitioned device (eg. hard disk)

## Usage:
```
sudo rpi-img2GPT -d|--device device_name -i|--image rpi_disk_image
```

Example:

sudo rpi-img2GPT --device /dev/sdc --image 2021-05-07-raspios-buster-armhf-lite.img

## What the command does
This command will:
- Erase the specified device (usually a hard disk)
- Partition the device with the GPT scheme and create 2 partitions - boot (64MB) and root (the rest of the device)
- Copy the content of the disk image to the device
- Change the file boot/config.txt to define a 2 x 5-second-delay to run the bootloader and kernel to leave enough time for the device to spin
- Change the file boot/cmdline.txt to define the new root mount (switching in the process to PARTLABEL from PARTUUID) and remove the call to the initial root partition resize
- Change the file root/etc/fstab to define the 2 mount points - /boot and / - (switching in the process to PARTLABEL from PARTUUID)
191 changes: 191 additions & 0 deletions rpi-img2GPT
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#!/bin/bash

DisplayUsage() {

cat <<-EOF
${COMMAND} copies a raspiOS disk image to a GPT-partitioned device and performs a few adjustments to allow the raspberry-pi to boot directly from the device.
Usage:
${COMMAND} --device|-d device_name --image|-i disk_image
EOF

exit 0
}

GetArguments() {
## Translate long options to short ones
for arg in $@; do
delim=""
case ${arg} in
"--help") args="${args}-h" ;;
"--device") args="${args}-d" ;;
"--image") args="${args}-i" ;;
*) [[ "${arg:0:1}" == "-" ]] || delim="\""
args="${args}${delim}${arg}${delim} ";;
esac
done

# reset the translated args
eval set -- "${args}"

## Parse short options
while getopts "hd:i:" opt; do
case ${opt} in
"h") DisplayUsage; exit 0 ;;
"d") DEVICE_NAME=${OPTARG} ;;
"i") DISK_IMAGE=${OPTARG} ;;
*) DisplayUsage ;;
esac
done
}

CheckConditions() {

# Check that the program is run by root
if [ $(id -u) -ne 0 ]; then
echo "${COMMAND} must be run as root" >&2
exit 1
fi

# Check that the device exists
if [ -z ${DEVICE_NAME} ]; then
echo "Device name not specified" >&2
exit 2
fi

mountpoints=$(lsblk --noheadings --list -o MOUNTPOINTS ${DEVICE_NAME} 2>/dev/null)
if [ ${?} -ne 0 ]; then
echo "Device ${DEVICE_NAME} not found" >&2
exit 3
fi

# Check that the disk image exists
if [ -z ${DISK_IMAGE} ]; then
echo "Disk image not specified" >&2
exit 4
fi

if [ ! -f ${DISK_IMAGE} ]; then
echo "Disk image ${DISK_IMAGE} not found" >&2
exit 5
fi
}

Cleanup() {

echo "Cleaning up..."
# Unmount partitions
umount ${TEMP_DIR}/*

# Unmap disk image
kpartx -d ${DISK_IMAGE}

# Delete the temporary directory
if [ -d ${TEMP_DIR} ]; then
rm -rf ${TEMP_DIR}
fi
}

# Initialize the program

## Set constants
readonly COMMAND=$(basename ${0})

## Get arguments
GetArguments $@

## Check conditions
CheckConditions

## Set program execution flags
set -euo pipefail

## Intercept interruptions
trap Cleanup SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM

# STEP-1: Prepare the hard disk
## unmount the partitions of the specified device, if any
for mp in ${mountpoints}; do
umount ${mp}
done

## Remove previous content
echo "Erasing device ${DEVICE_NAME}..."
sgdisk --zap-all ${DEVICE_NAME} 1>/dev/null

## Create 2 partitions
echo "Preparing device ${DEVICE_NAME}..."
sgdisk --new=0:0:+64M --typecode=0:0700 --change-name=0:boot ${DEVICE_NAME} 1>/dev/null
sgdisk --new=0:0:0 --typecode=0:8300 --change-name=0:root ${DEVICE_NAME} 1>/dev/null

## Create an hybrid MBR with partition type "0c" so that bootloader finds it
cat << EOF | gdisk ${DEVICE_NAME} 1>/dev/null
r
h
1
N
0c
N
Y
EE
w
Y
EOF

## Format the 3 partitions
mkfs.fat -F 32 -n BOOT ${DEVICE_NAME}1 1>/dev/null 2>&1
mkfs.ext4 -F -L root ${DEVICE_NAME}2 1>/dev/null

# STEP-2: Copy the content of the disk image to the hard disk
## Create a temporary directory
readonly TEMP_DIR=$(mktemp -d)

## Mount the 2 hard-disk partitions
mkdir ${TEMP_DIR}/d1
mount ${DEVICE_NAME}1 ${TEMP_DIR}/d1
mkdir ${TEMP_DIR}/d2
mount ${DEVICE_NAME}2 ${TEMP_DIR}/d2

## Map the disk image to a loop device
readonly LIST_MAPPINGS=$(kpartx -asv ${DISK_IMAGE})

## Mount the 2 disk image partitions
device=$(echo ${LIST_MAPPINGS} | cut -d" " -f3)
mkdir ${TEMP_DIR}/s1
mount /dev/mapper/${device} ${TEMP_DIR}/s1

device=$(echo ${LIST_MAPPINGS} | cut -d" " -f12)
mkdir ${TEMP_DIR}/s2
mount /dev/mapper/${device} ${TEMP_DIR}/s2

## Copy content of the disk image to the device
echo "Copying boot-partition files..."
rsync -a ${TEMP_DIR}/s1/ ${TEMP_DIR}/d1
echo "Copying root-partition files..."
rsync -a ${TEMP_DIR}/s2/ ${TEMP_DIR}/d2

# STEP-3: adjust some settings

## /boot/config.txt: Add bootcode_delay and boot_delay
echo "Making few adjustments..."
echo 'bootcode_delay=5' >> ${TEMP_DIR}/d1/config.txt
echo 'boot_delay=5' >> ${TEMP_DIR}/d1/config.txt

## /boot/cmdline.txt: Remove one-time root file system resizing, quiet option and fsck option
sed -e 's; init=/usr/lib/raspi-config/init_resize.sh;;' -i ${TEMP_DIR}/d1/cmdline.txt
sed -e 's; quiet;;' -i ${TEMP_DIR}/d1/cmdline.txt
sed -e 's; fsck.repair=yes; fsck.repair=no;' -i ${TEMP_DIR}/d1/cmdline.txt

## Change mount point in /boot/cmdline.txt
sed -e 's;root=PARTUUID=[0-9,a-f,\-]*;root=PARTLABEL=root;' -i ${TEMP_DIR}/d1/cmdline.txt

## Change mount points in /etc/fstab
sed -e 's;^PARTUUID=.*/boot;PARTLABEL=boot \t/boot;' -i ${TEMP_DIR}/d2/etc/fstab
sed -e 's;^PARTUUID=.*/[[:blank:]];PARTLABEL=root \t/;' -i ${TEMP_DIR}/d2/etc/fstab

# Clean up and exit
Cleanup
echo "Operation completed"
exit 0

0 comments on commit ef64a5c

Please sign in to comment.