Skip to content

Commit b1d0869

Browse files
author
William Squires
authored
Add data partition (#2)
* Create script to add FAT32 data partition to Raspbian img file * Add package dosfstools to Dockerfile for use with add partition script * Add function to convert FAT32 partiton names to uppercase * Add comments to annotate script main vars * Add use of add partion script and mount/unmount of data partiton to build script * Update test script and build docker script to trigger creation of a data partition and check partiton alignment on chroot * Add execute permission to add partition script * Use source to call add pariton script * Add copy of add partition script to Dockerfile * Add install of parted to Dockerfile * Remove aligment check, replace with lsblk to show partitions * Add alignment check for new partition * Update readme file with info about data partiton creation * Add function to remove init function from cmdline.txt
1 parent a3d6419 commit b1d0869

File tree

6 files changed

+160
-1
lines changed

6 files changed

+160
-1
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ FROM debian:stable
22

33
RUN apt-get -y update && \
44
apt-get -y install \
5-
wget unzip kpartx qemu qemu-user-static binfmt-support
5+
wget unzip kpartx qemu qemu-user-static binfmt-support parted dosfstools
66

77
WORKDIR /build/
88

99
COPY build.sh /build/
10+
COPY add-partition.sh /build/
1011

1112
ENTRYPOINT /build/build.sh

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ To specify a script to run in the Raspbian environment use the `SCRIPT` environm
1818

1919
The source image file can also be specified with `SOURCE_IMAGE`. This should be the path to an `.img` file, also found in your mounted volume.
2020

21+
A FAT32 format data partition can be added to the end of the image if `ADD_DATA_PART` is set to `true`. This partiton can be helpful when writing the image to an SD card in macOS and Windows as data files can be placed there. The partition will be `64MiB`, labeled `DATA`, and mounted at `/data`. The `/etc/fstab` and `/boot/config.txt` files will be updated with correct `PARTUUID`s.
22+
2123
```
2224
docker run --privileged \
25+
-e MOUNT=/customisations \
2326
-e SOURCE_IMAGE=/customisations/raspbian-lite.img \
2427
-e SCRIPT=/customisations/customise.sh \
28+
-e ADD_DATA_PART=true \
2529
--mount source=customisations,destination=/customisations \
2630
edwardotme/raspbian-customiser:latest
2731
```

add-partition.sh

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/bin/bash
2+
3+
# Add a FAT32 data partion to a Rasbian .img file
4+
5+
set -e
6+
7+
IMG_FILE=$1
8+
9+
# These could be set by args
10+
# Size should be in MiB
11+
PART_SIZE=64
12+
# Name will be converted to uppercase
13+
PART_NAME=DATA
14+
# Mount is relative to root of image
15+
PART_MOUNT=/data
16+
17+
# Currently only FAT32 is supported
18+
PART_TYPE=fat32
19+
20+
# Use parted to print the partition table
21+
PARTED_OUT=$(parted -s "${IMG_FILE}" unit b print)
22+
echo "PARTED OUTPUT: $PARTED_OUT"
23+
24+
# Find the end of the root partition
25+
# This assumes there are two partitons
26+
ROOT_END=$(echo "$PARTED_OUT" | grep -e '^ 2'| xargs echo -n \
27+
| cut -d" " -f 3 | tr -d B)
28+
29+
# Determine where to start the new partition so that it is properly
30+
# aligned to 4MiB erase blocks
31+
BLOCK=$((4 * 1024 * 1024))
32+
PART_START=$((($ROOT_END + 1) / $BLOCK))
33+
PART_START=$((($PART_START + 1) * $BLOCK))
34+
35+
# Work out how much to expand the image to fit the new partition
36+
PART_EXPAND=
37+
# Convert PART_SIZE from MiB to B
38+
PART_EXPAND=$(($PART_SIZE * 1024 * 1024))
39+
# Add the free space gap required between the end of the previous
40+
# partiton and the new one
41+
PART_EXPAND=$(($PART_EXPAND + ($PART_START - $ROOT_END)))
42+
# Ensure the expansion aligns with 512B sector
43+
PART_EXPAND=$(($PART_EXPAND / 512))
44+
PART_EXPAND=$((($PART_EXPAND + 1) * 512))
45+
# Convert to MiB for quicker dd
46+
PART_EXPAND=$(($PART_EXPAND / (1024 * 1024)))
47+
48+
# Print values
49+
echo "ROOT_END: $ROOT_END"
50+
echo "PART_START: $PART_START"
51+
echo "PART_EXPAND: $PART_EXPAND"
52+
53+
# Expand the image with zeros using dd
54+
dd if=/dev/zero bs=1M count=$PART_EXPAND >> $IMG_FILE
55+
56+
# Use parted to print the partition table
57+
parted -s "${IMG_FILE}" unit b print free
58+
59+
# Create the partition in the new space
60+
parted -a none -s "${IMG_FILE}" mkpart primary "${PART_TYPE}" "${PART_START}B" 100%
61+
62+
# Use parted to print the partition table
63+
parted -s "${IMG_FILE}" unit b print free
64+
65+
# Check the partiton is optimally aligned
66+
parted -s "${IMG_FILE}" align-check opt 3
67+
68+
# Mount the image as a loop device
69+
LOOP_DEV=$(losetup --show --find --partscan $IMG_FILE)
70+
# Wait a second or mount may fail
71+
sleep 1
72+
# Get list of partitions, drop the first line, as this is our
73+
# LOOP_DEV itself, we only what the child partitions
74+
PARTITIONS=$(lsblk --raw --output "MAJ:MIN" --noheadings ${LOOP_DEV} | tail -n +2)
75+
# Manually use mknod to create nodes for partitons on loop device
76+
# Testing indicates this is required when running in a container,
77+
# even if the containter is run --pivileged
78+
COUNTER=1
79+
for i in $PARTITIONS; do
80+
MAJ=$(echo $i | cut -d: -f1)
81+
MIN=$(echo $i | cut -d: -f2)
82+
if [ ! -e "${LOOP_DEV}p${COUNTER}" ]; then mknod ${LOOP_DEV}p${COUNTER} b $MAJ $MIN; fi
83+
COUNTER=$((COUNTER + 1))
84+
done
85+
# Make mount point and mount image
86+
ROOTFS_DIR=/mnt/raspbian
87+
mkdir -p $ROOTFS_DIR
88+
mount -o rw ${LOOP_DEV}p2 $ROOTFS_DIR
89+
mount -o rw ${LOOP_DEV}p1 ${ROOTFS_DIR}/boot
90+
91+
# Format the partiton
92+
PART_NAME=$(echo $PART_NAME | tr a-z A-Z )
93+
mkdosfs -n "${PART_NAME}" -F 32 -v "${LOOP_DEV}p3" > /dev/null
94+
95+
# Use parted to print the partition table
96+
parted -s "${IMG_FILE}" unit b print free
97+
98+
# Create the mount point
99+
mkdir /mnt/raspbian/$PART_MOUNT
100+
mount -o rw ${LOOP_DEV}p2 ${ROOTFS_DIR}/${PART_MOUNT}
101+
102+
# List the contents of mount point to verify mount was successful
103+
echo "CONTENTS OF /:"
104+
ls ${ROOTFS_DIR}
105+
echo "CONTENTS OF /boot:"
106+
ls ${ROOTFS_DIR}/boot
107+
108+
# Add the partition mount to /etc/fstab
109+
IMG_ID="$(dd if="${IMG_FILE}" skip=440 bs=1 count=4 2>/dev/null | xxd -e | cut -f 2 -d' ')"
110+
echo "IMG_ID: $IMG_ID"
111+
BOOT_PARTUUID="${IMG_ID}-01"
112+
ROOT_PARTUUID="${IMG_ID}-02"
113+
DATA_PARTUUID="${IMG_ID}-03"
114+
sed -i "s/PARTUUID=[a-z0-9]*-01/PARTUUID=${BOOT_PARTUUID}/" "${ROOTFS_DIR}/etc/fstab"
115+
sed -i "s/PARTUUID=[a-z0-9]*-02/PARTUUID=${ROOT_PARTUUID}/" "${ROOTFS_DIR}/etc/fstab"
116+
echo "PARTUUID=${DATA_PARTUUID} ${PART_MOUNT} vfat defaults 0 1" >> "${ROOTFS_DIR}/etc/fstab"
117+
echo "FSTAB:"
118+
cat "${ROOTFS_DIR}/etc/fstab"
119+
120+
# Update cmdline.txt to use the new PARTUUID
121+
sed -i "s/PARTUUID=[a-z0-9]*-02/PARTUUID=${ROOT_PARTUUID}/" "${ROOTFS_DIR}/boot/cmdline.txt"
122+
# Remove the reference to the expand rootfs script as it can no longer be
123+
# expanded with the new partition in the way
124+
sed -i "s/init=.*//" "${ROOTFS_DIR}/boot/cmdline.txt"
125+
echo "CMDLINE.TXT:"
126+
cat "${ROOTFS_DIR}/boot/cmdline.txt"
127+
128+
# Unmount partitions and reset loop device
129+
umount /mnt/raspbian/{${PART_MOUNT},boot,}
130+
losetup -d $LOOP_DEV
131+
echo "SUCCESSFULLY ADDED PARTITION AND UNMOUNTED IMG"

build-docker.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ docker run --privileged --rm \
1717
-e MOUNT=/test \
1818
-e SOURCE_IMAGE=/test/${IMAGE_ZIP%.zip}.img \
1919
-e SCRIPT=/test/test.sh \
20+
-e ADD_DATA_PART=true \
2021
--mount type=bind,source="$(pwd)"/test,destination=/test \
2122
edwardotme/raspbian-customiser:$TRAVIS_BRANCH
2223
if [ $? -ne 0 ]; then

build.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
#!/bin/bash
22

3+
set -e
4+
5+
ADD_DATA_PART=${ADD_DATA_PART:-false}
6+
37
echo "MOUNT: $MOUNT"
48
echo "SOURCE_IMAGE: $SOURCE_IMAGE"
59
echo "SCRIPT: $SCRIPT"
10+
echo "ADD_DATA_PART: $ADD_DATA_PART"
11+
12+
if [ $ADD_DATA_PART != false ]; then
13+
source ./add-partition.sh $SOURCE_IMAGE
14+
fi
615

716
# Create loop device map from image partition table
817
LOOP_DEV=$(losetup --show --find --partscan $SOURCE_IMAGE)
@@ -35,6 +44,10 @@ mkdir -p /mnt/raspbian
3544
mount -o rw ${LOOP_DEV}p2 /mnt/raspbian
3645
mount -o rw ${LOOP_DEV}p1 /mnt/raspbian/boot
3746

47+
if [ $ADD_DATA_PART != false ]; then
48+
mount -o rw ${LOOP_DEV}p3 /mnt/raspbian/data
49+
fi
50+
3851
# Create bind mounts for system directories
3952
mount --bind /dev /mnt/raspbian/dev/
4053
mount --bind /sys /mnt/raspbian/sys/
@@ -60,4 +73,8 @@ chroot /mnt/raspbian $SCRIPT
6073
sed -i 's/^#CHROOT //g' /mnt/raspbian/etc/ld.so.preload
6174

6275
# Unmount everything
76+
if [ $ADD_DATA_PART != false ]; then
77+
umount /mnt/raspbian/data
78+
fi
6379
umount /mnt/raspbian/{dev/pts,dev,sys,proc,boot,${MOUNT},}
80+
losetup -d $LOOP_DEV

test/test.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
# Some basic commands to run on the Raspbian system to confirm chroot is working
44
# and generate some information output
55

6+
set -e
7+
68
uname -a
79

810
ls /
911
ls /boot
12+
ls /data
13+
14+
lsblk
1015

1116
apt-get update

0 commit comments

Comments
 (0)