This is a commentary intended to elucidate the data structures used by olpcrd and olpc-update in Early boot and how those data structures differ between unpartitioned and partitioned systems.
- We want to be able to store multiple trees of files.
- At all times, we want to distinguish the file tree that we want to boot on next reboot. Call this tree the "primary" tree.
- We want to make at least one known-good alternate tree available for rollback upon reboot whenever we have one. Call this the "alternate" tree.
- OFW needs to be able to find the correct kernel and initramfs to boot.
- Changes to the assignments of "primary" and "alternate" trees need to be atomic, consistent, and durable with respect to hard power failure.
- For update purposes, we need to know what tree we booted from.
- For update purposes, we need a cryptographic manifest for each available tree.
Design for Unpartitioned Machines
- Multiple trees of files are stored in /versions/pristine.
- We define a data structure is called a "boot config". Abstractly, each boot config is a struct of pointers to tree-ids.
- Each boot config is implemented as a directory in /versions/configs containing one to two symlinks named "current" and, optionally, "alt".
- We identify a distinguished boot config as the target of a symlink at /versions/boot.
- To atomically modify the distinguished boot config:
- making a fresh boot config,
- making a fresh symlink pointing to it
- renaming the freshly created symlink to /versions/boot
- Our initramfs puts a symlink at /versions/running to identify the currently running tree.
- For update purposes, we need to maintain cryptographic manifests of our trees of files. These go in /versions/contents/...
Automatic updates require sufficient free space to install the update. We get that space by
- Making and installing a new boot config with no fallback, thereby unreferencing any non-sticky old builds.
- Deleting any non-sticky old builds.
- Installing the update.
- Verifying the update.
- Making and installing a new boot config with our current running tree as the "alt" image and with the new tree as the "current" image.
This way, the system is *always* in a consistent state.
Design for Partitioned Machines
The design for partitioned systems is simpler than for unpartitioned systems. It works as follows.
- Partitions are identified by colon-delimited prefixes, like boot:, root:, and so on.
- At all times, for some tree-id $a:
- boot:/boot should be a symlink to 'boot-versions/$a'
- boot:/boot-versions/$a should contain:
- a symlink named alt pointing to ../$b for some tree-id $b
- the contents of $root:/boot for some other partition $root:
- possibly some other unspecified metadata
Assume that boot:/boot currently points to boot-versions/$r. Then the boot:/boot-versions/$r/alt symlink may be made to loop by being made to point to ../$r in order to preserve the alt invariant while updating, when no consistent alternate data may be available.
After the partition update is verified complete, when consistent alternate data become available, the new boot configuration may be made visible by
- filling out a new directory boot:/boot-versions/$u
- making a new symlink in boot:/ pointing to boot-versions/$u
- renaming it on top of boot:/boot