/travis-raspbian-image

Test repo for creating customised Raspberry Pi images with Travis CI

Primary LanguageShell

travis-raspbian-image

Build Status

Test repo for creating customised Raspberry Pi images with Travis CI

Using this guide as the main reference.

Read Travis CI docs on GitHub releases for more details regarding upload.

Mounting the Image

In order to modify the Raspbian disk image, we want to mount it as a loop device. For that we will use the losetup command, once can read a detailed manual about it in the losetup man pages.

In order to use losetup I need to get the byte offset of the image file. It is where the file system starts. One can use fdisk -l disk.img to get all the information (the -l is L, not one).

fdisk -l disk.img

One should get a similar output as the one following.

Disk Stick.img: 3984 MB, 3984588800 bytes
249 heads, 6 sectors/track, 5209 cylinders, total 7782400 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0004bfaa

    Device Boot      Start         End      Blocks   Id  System
Stick.img1   *         128     8015999     4007936    b  W95 FAT32

It outputs essential information about the file system stored in the .img file and a table of partitions, Start and End sectors for each. Sectors are blocks of bytes. In order to get the byte offset, we need to know the Sector size. It is easy to spot that in the output, but how do we automate that?

This where grep and sed comes handy. With grep you can find the line that contains a specific text. Like so.

fdisk -l disk.img | grep "Sector size"

You can read more about grep on the grep man page. Here we also use the concept of the Unix pipe which is this symbol |. Anything that the fdisk -l disk.img command outputs is being forwarded to grep "Sector size" as an input. You should get a line similar to the one below.

Sector size (logical/physical): 512 bytes / 512 bytes

How do we get the 512 part? We can use sed. The following command removes anything that is not a number and returns the output.

sed -e s/[^0-9]//g

However this will return 512 two times.

512512

This is where I searched for and found a sed tutorial! sed uses regular expressions to search for a pattern. One can use regexr to make it easier to create regular expressions.

Basic sed syntax.

sed -r "s/search/replace"

The -r (-E on OS X) part enables serious regular expressions. By default sed has limitations. In order to get the Sector size I used the following sed command.

sed -r "s/[^0-9]*([0-9]+).*/\1/"

Here we are looking for a pattern that starts with anything that is not a number ([^0-9]*) a number that consists of one or more digits ([0-9]+) followed by zero or more characters of any kind (.*).

The full version of getting the Sector size from output of fdisk -l disk.img looks like the following.

fdisk -l opm.img | grep "Sector size" | sed -r "s/[^0-9]*([0-9]+).*/\1/"

It outputs 512. Great! We can store that in a variable. To do that, we need to use the $(...) syntax to enclose the expression between round brackets.

secsize=$(fdisk -l opm.img | grep "Sector size" | sed -r "s/[^0-9]*([0-9]+).*/\1/")

Now we can use echo $secsize to print out the value.

Next we need to get the Start sectors for two of the partitions in the image file. In a regular Raspbian image there are two partions, one is used during boot (the /boot partition) and the other one is where the files are.

We will use the following grep command to get information about the first partition.

fdisk -l opm.img | grep ".img1"

And the following, to get information about the second partition.

fdisk -l opm.img | grep ".img2"

Next, we need to get the first number preceding a space character. We are going to use sed for this once more. This is a bit more complicated since the line we get from grep begins with text that has numbers after it. We are looking for the first number after that text.

To define the first part we use [^0-9]*[0-9][^0-9] which means anything that is not a number ([^0-9]*) followed by a single digit ([0-9]) and then again with anything that is not a number ([^0-9]). The rest of it is the same as for getting the Sector size.

sed -r "s/[^0-9]*[0-9][^0-9]([0-9]+).*/\1/"

The following two lines we can now use to get Start sectors for both partitions.

start1=$(fdisk -l opm.img | grep ".img1" | sed -r "s/[^0-9]*[0-9][^0-9]([0-9]+).*/\1/")
start1=$(fdisk -l opm.img | grep ".img2" | sed -r "s/[^0-9]*[0-9][^0-9]([0-9]+).*/\1/")

Now we use multiplication to get the offset bytes for both partitions. We have to use the bash arithmetic expansion operator.

off1="$(($secsize*$start1))"
off2="$(($secsize*$start2))"

And lastly we can mount or .img file!!!

mkdir mnt
mount -o loop,offset=$off2 opm.img mnt
mount -o loop,offset=$off1 opm.img mnt/boot

Here is another guide that includes how you can modify the disk image size (very important, since I want to install a lot of packages!!!)

First you would need to add some bytes to the disk image. In this example we add 1G.

dd if=/dev/zero bs=1M count=1024 >> opm.img

After a few days of errors, there were more things on top of grep and sed that I learnt. First of all, to resize a martition, one does not need to use mount and calculate offsets. It is much easier to do it with losetup.

To mount a .img file with losetup do the following.

loopdev=$(losetup --find --show "${image}")
echo "Created loopback device ${loopdev}"

Use parted to resize the partition.

parted --script "${loopdev}" print
parted --script "${loopdev}" resizepart 2 100%
parted --script "${loopdev}" print
e2fsck -f "${loopdev}p2"
resize2fs "${loopdev}p2"

Notice the resizepart command. In most tutorials one can find online, instead of using resizepart one has to remove the partition and create a new one which involves knowing where it starts in terms of blocks and bytes.

You do not have to use losetup -d /dev/loopN if you want to mount the loop devices in order to access files and launch setup scripts.

Use the following to mount the loop device without knowing offsets or similar.

bootdev=$(ls "${loopdev}"*1)
rootdev=$(ls "${loopdev}"*2)
partprobe "${loopdev}"

[ ! -d "${mount}" ] && mkdir "${mount}"
mount "${rootdev}" "${mount}"
[ ! -d "${mount}/boot" ] && mkdir "${mount}/boot"
mount "${bootdev}" "${mount}/boot"

Then you install your script that contains all you would like to do from within the Raspberry Pi.

install -Dm755 "${script}" "${mount}/tmp/${script}"

For qemu you might need to mount additional directories.

mount --bind /proc "${mount}/proc"
mount --bind /sys "${mount}/sys"
mount --bind /dev "${mount}/dev"
mount --bind /dev/pts "${mount}/dev/pts"

Change things on the filesystem for qemu.

cp /etc/resolv.conf "${mount}/etc/resolv.conf"
cp /usr/bin/qemu-arm-static "${mount}/usr/bin"
cp "${mount}/etc/ld.so.preload" "${mount}/etc/_ld.so.preload"
echo "" > "${mount}/etc/ld.so.preload"

When we resized the image with parted the PARTUUID of the block device was changed. The latest Raspbian image files use the ID for boot purposes. The root boot device in /boot/cmdline.txt is set by using a PARTUUID. For some reason it is not possible to get the PARTUUID of a .img file on Travis. This is why we will use the good old /dev/mmcblk0p2 as the root boot device. We also disable automatic expansion of the filesystem here.

echo "dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet" > "${mount}/boot/cmdline.txt"

Another thing that has to be changed is the /etc/fstab flie. The PARTUUID pats have to be changed to /dev/mmcbnlk0p1 and /dev/mmcblk0p2 as well.

echo "proc            /proc           proc    defaults          0       0" > "${mount}/etc/fstab"
echo "/dev/mmcblk0p1  /boot           vfat    defaults          0       2" >> "${mount}/etc/fstab"
echo "/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1" >> "${mount}/etc/fstab"

This can supposedly go into the script part as well. The reason it being present in the create-image part might be if one has to get the PARTUUID before and replace things with it in the cmdline.txt and fstab files.

At this point we are ready to run the script as root.

chroot "${mount}" "/tmp/${script}"

And when it is done, we put the old ld.so.preload script in place.

mv "${mount}/etc/_ld.so.preload" "${mount}/etc/ld.so.preload"