The default Digi Embedded Yocto root file system is writable. This allows users to modify configuration files and add or remove other files. You can also build a read-only root file system with Digi Embedded Yocto. A read-only root file systems has several advantages:

  • You can separate user-specific changes from system configuration.

  • It allows for factory reset by erasing the user data from the writable partition.

  • With TrustFence, you can sign the root file system image and authenticate it at boot because the image will not change.

Configure your project for read-only rootfs

To build an image with read-only rootfs, edit your project’s conf/local.conf configuration file and add:

conf/local.conf
EXTRA_IMAGE_FEATURES += "read-only-rootfs"

This read-only-rootfs is a Yocto IMAGE_FEATURE. When you add this IMAGE_FEATURE to your project, Yocto changes some recipes to prevent them from modifying root file system files during runtime.

Build the read-only rootfs image

Build an image for your project, such as:

$ bitbake dey-image-qt

The build produces a SQUASHFS root file system image called dey-image-qt-xwayland-ccimx8x-sbc-pro.squashfs.

Signed read-only rootfs (optional)

If TrustFence is enabled, Digi Embedded Yocto signs the read-only rootfs image. This forces closed devices to authenticate the read-only rootfs before booting.

See Secure boot for more information.

Prepare your target for read-only rootfs

  1. Use the update command to program the SQUASHFS root file system image directly on the target. For example:

    => update rootfs tftp dey-image-qt-xwayland-ccimx8x-sbc-pro.squashfs
  2. Boot the target:

    => boot
    If TrustFence is enabled, the rootfs image is authenticated before boot.

You can see on the boot log that the root file system is read-only:

...
platform regulatory.0: Falling back to sysfs fallback for: regulatory.db
ALSA device list:
  #0: imx-max98088
VFS: Mounted root (squashfs filesystem) readonly on device 179:3.
devtmpfs: mounted
Freeing unused kernel memory: 2496K...

Customize the read-only file system

Read-only root file systems do not allow modification of system configuration files, such as network settings.

There are two methods to overcome this:

  • Modify configuration files at build time.

  • Mount an overlay file system over the read-only root file system.

Modify configuration files at build time

This method creates fixed configuration files at build time that will be part of the final read-only root file system.

  • Advantages

    • The system is fully read-only, but configured with your desired settings.

  • Disadvantages

    • Any device programmed with the read-only image will have the exact same settings, which may be inconvenient if several devices are working in the same network.

Other system settings

For any system configuration file that you want to modify, do the following:

  1. Locate the recipe that contains and installs the configuration file.

  2. If the file is installed by meta-digi layer, edit the file and apply your modifications. If not, create a recipe .bbappend (either in *meta-digi layer or on a custom layer with bigger priority) that overrides the configuration file with your changes.

Mount an overlay file system

Yocto read-only support allows you to define volatile binds. Volatile binds are mount points that can overlay folders on a read-write partition over the read-only root file system. This creates a union file system between the read-only and the read-write partitions. The overlayed folders and their files become read-write, and modifications are stored on the read-write partition.

To define volatile binds, add pairs in the form <writable_folder> <read-only_folder>\n to VOLATILE_BINDS in your project’s conf/local.conf:

conf/local.conf
VOLATILE_BINDS_append = " \
	<writable_folder> <read-only_folder>\n\
	<writable_folder> <read-only_folder>\n\
	<writable_folder> <read-only_folder>\n\
"

Some processes will try to use folders under the read-only system by default. For example, pulseaudio needs to create a .config file under /home/root. Since the file system is read-only, the process will fail. Similarly, NetworkManager will fail when trying to create system connection settings at /etc/NetworkManager.

You can solve these issues by adding volatile binds that mount these target folders on a writable media, such as a USB memory.

Considering a USB memory is mounted at /run/media/sda1, add the following VOLATILE_BINDS in your project’s conf/local.conf:

conf/local.conf
# Pulseaudio creates a folder under /home/root ".config"
# NetworkManager needs a place to write the current system connections settings
VOLATILE_BINDS += " \
	/run/media/sda1/home/root /home/root\n\
	/run/media/sda1/NetworkManager /etc/NetworkManager/\n\
"

Build the project after doing this change and re-program the images.

On the USB memory, create the folders for the mount points, then insert it on the target. When you boot the new images, pulseaudio will be able to write the file in /home/root and NetworkManager will be able to write to /etc/NetworkManager which now are overlayed on the USB memory.