Wednesday, December 23, 2015

Switching to tmpfs and unmounting root in Raspbian

This describes how to make a Raspberry Pi running Raspbian use RAM for the root file system. This allows you do things which cannot be done while the root file system is mounted, such as shrinking it with resize2fs. It would also allow you to write a totally new image to the SD card, overwriting everything. It might also allow you to swap SD cards and write an image to a different card, but I'm not sure that the SD card driver allows it. Here is the script:

cp -r lib bin sbin etc /mnt
mkdir -p mnt/usr/lib/arm-linux-gnueabihf
cp usr/lib/arm-linux-gnueabihf/ mnt/usr/lib/arm-linux-gnueabihf
# Move other mounts
mkdir mnt/dev
mount --move dev mnt/dev
# Pivot root using instructions from pivot_root(8) man page
cd mnt
mkdir old_root
pivot_root . old_root
# The current directory now seems invalid, so fix it
cd /
# Old root can only be unmounted once sh running from old root finishes.
# If enough was copied, you could continue startup normally using init.
exec old_root/usr/sbin/chroot . bin/sh

First, save the script somewhere. To use it, add init=/bin/sh to /boot/cmdline.txt and reboot. When the boot text finishes, you may not see the # prompt because of kernel output, but the shell should be running, and you'll get another prompt if you press enter.

You must run the script in the shell which is running as init, not a shell spawned from it. That is because the final line needs to end that shell. You won't be able to unmount old_root if you're still running a shell from it. So, for example if the script is /root2ram, use . /root2ram to run it.

The final line is the place where any errors will manifest themselves. You would get a kernel panic if chroot or sh can't run. If you're experience a problem, comment out the last line and look at the state of the system at that point.

The script is intentionally minimalist. All of Raspbian won't fit into RAM, so it only copies some parts. Some binaries in /bin and /sbin require libraries from
/usr/lib/arm-linux-gnueabihf/, and only the most commonly used one gets copied. The script does not umount old_root at the end so you can test whatever you want to run and copy anything else you need before manually unmounting it.

This has been tested on a Raspberry Pi 2 B running Raspbian Jessie. If you want to run it on a Raspberry Pi with less RAM, you might need to be more specific when copying from /lib. It is big and there are un-needed things there.

When you are done and you want to boot normally, simply remove init=/bin/sh from /boot/cmdline.txt. You would need to mkdir boot && mount boot. Vi is in /usr/bin, but sed is in /bin and you can use sed -i 's,init=/bin/sh *,,' /boot/cmdline.txt to edit it. Normal rebooting won't work without init, so umount /boot ; sync && reboot -f.

No comments: