The official Raspian stretch image is provided as an img file with a DOS/MBR boot sector and 2 partitions, a boot partition
/boot
and the rootfs mounted at/
We can create a script which allows switching between the filesystem which is mounted at
/
and an alternative recovery root partion and provide tools on the recovery partition to reset the main root partition back to factory state.And we can do the reset over ssh which makes it super easy to automate configuration on the Pi!
This article is a follow up to the previous article and explains the details on how to create a factory restore partition for a raspberry pi. Checkout the repo from here to get the files.
Once a Pi is booted with this modified image, it has 3 partitions rather than 2,
and has a /boot/factory_reset
command which will trigger a factory reset. For
example if you login to the Pi with the modified recovery image and issue the
following commands;
The Pi will reset itself…
And if you wait 10 minutes or so… It will replace the /
file system with a
pristine copy of raspbian (and enabled ssh access at the same time), and you will
be able to login again to fresh Raspberry Pi over ssh;
Offical Raspian Images
The official raspian stretch lite image is about 1.7GB and contains 2 partitions.
The /boot
partition containing the kernal and other stuff, and the /
rootfs
which contains the OS.
You can see that information using the parted
tool
The /boot/cmdline.txt
on the Pi contains the boot information with parameters
including an init
script and the root
partition to boot from;
Working around the Init root partition resizer
The init script /usr/lib/raspi-config/init_resize.sh is run on the first boot, and resizes the last partition to fill the sdcard. In order to work with the resizer script, the idea is to add the recovery partition after the boot partition and before the main rootfs.
If we were to add the recovery partition at the end, the init resizer script
would expand the recovery partition to fill the disk, rather than the live /
Recovery partition
So basically we want to insert the recovery partition here…
Selecting the root=
rootfs or recovery partition on boot
The root=
option in /boot/cmdline.txt
selects the root filesystem which is
then mounted and booted into the OS.
By default that points at the vanilla root. If we add another partition, this value needs updating. It also needs changing in order to boot from the recovery partition.
While coming up with this script I had some issues that I could not get the Pi to use the disk UUID to boot, it would only work with the PARTUUID. However the resizer.sh script resets the PARTUUID during its resizing process which complicates matters.
Generating UUID and PARTUUID for the disks
As we are going to be cloning a disk, we have a problem of a duplicate UUID for that disk. We also need to have a PARTUUID for the cmdline.txt file. So we generate random values for this as a first step;
Creating a blank disk img
file to work with;
This is the image that will be written to the sdcard. This is a way to initialize
a new img
file of a specific size. In this case 2000 x 4M
. Currently that is
fixed, but I would like to inspect those values from the source image as a future
feature.
This creates an empty file with zeros. We need to add a partition table to that;
These values are hard coded to fit the raspian stretch lite image, and it’s on the TODO list to inspect the sizes from the source image file.
We can inspect what was created using the follwing command
We need to make some changes to the paritions in the image, and copy the filesystems from the source image, so we mount them on the loopback device using the following commands;
Now the partitions from the img file are available as loopback devices so we can work with them;
The next step is to mount the source image on loopback device similar to the previous step
Now we can copy the partitions from the source image to the recovery image;
We now write the UUIDs for the disks so they are unique going forward;
The next thing is that the recovery partition is 4G from the sdisk
command
above, but the filesystem that we just wrote is only 1.7 GB
. So we need to
resize that filesystem;
We can now mount those partions and make the changes for factory reset;
The first thing is that we want the Pi to boot off the 3rd partition, rather
than the 2nd, do we need to modify /boot/cmdline.txt
in the restore image;
(and also enable ssh…)
The next thing is to make backup of the cmdline.txt
so we can put it back after
recovery/reset;
So the next step we create a /boot/cmdline.txt
which is used during recovery.
However as the PARTUUID is reset during resizing. we include a token
which is replaced at runtime by our own script, and we tell the Pi to boot our
recovery script when its reset init=/usr/lib/raspi-config/init_resize2.sh
(there is probably a
simpler way to do this) But I couldn’t get root=UUID=xxxxxxxx
or LABEL=recovery
type booting to work. So I seem to be stuck with manipulating the root=PARTUUID=
option.
The next step is to write out the script that actually resets the pi. This is on the recovery partition root fs.
We also need to copy the recovery image (/opt/recovery.img
) to the restore partition, but we do that
last because we want the modifications we are making to be persistent after
restoration.
Note that it also enables ssh for the restored OS.
we also need a command which triggers the recovery from the live system;
Notice that this inspects the blkid of the PARTUUID, and replaces the token
(XXXYYYXXX
) created earlier with the correct runtime PARTUUID
In addition we need to update the /etc/fstab
on each of the root partitions
so they correct mount their respective partitions;
Note: using UUID seems to work fine in OS land, but not in cmdline.txt… weird huh
Write out the rootfs image to a fisk image on the recovery partition so it can be restored. @TODO zip this…
Finally, unmount the filesystems and loop devices;
Use the following command to write the image to an sdcard;
Summary
The file 2018-03-13-raspbian-stretch-lite.restore.img
now contains
- boot partition pointing at partition 3
- recovery script at
/boot/factory_reset
- Recovery partition at partition 2
- Recovery image in
/opt/recovery.img
of recovery partition - Correct
/etc/fstab
for partition 2 and 3 - reset script at
/usr/lib/raspi-config/init_resize2.sh
of recovery partition
So write the img to a file system, and when you want to reset it, issue the following command;
Make a cup of coffee, and wait 10 minutes, and by the time your are back, the Pi will be fresh and new and ready and waiting…