Since most files (stubs, kernels and initrds) on the ESP are properly
input-addressed or content-addressed now, there is no point in
overwriting them any more. Hence we detect what generations are already
properly installed, and don't reinstall them any more.
This approach leads to two distinct improvements:
* Rollbacks are more reliable, because initrd secrets and stubs do not
change any more for existing generations (with the necessary exception
of stubs in case of signature key rotation). In particular, the risk
of a newer stub breaking (for example, because of bad interactions
with certain firmware) old and previously working generations is
avoided.
* Kernels and initrds that are not going to be (re)installed anyway are
not read and hashed any more. This significantly reduces the I/O and
CPU time required for the installation process, particularly when
there is a large number of generations.
The following drawbacks are noted:
* The first time installation is performed after these changes, most of
the ESP is re-written at a different path; as a result, the disk usage
increases to roughly the double until the GC is performed.
* If multiple generations share a bare initrd, but have different
secrets scripts, the final initrds will now be separated, leading to
increased disk usage. However, this situation should be rare, and the
previous behavior was arguably incorrect anyway.
* If the files on the ESP are corrupted, running the installation again
will not overwrite them with the correct versions. Since the files are
written atomically, this situation should not happen except in case of
file system corruption, and it is questionable whether overwriting
really fixes the problem in this case.
The stubs on the ESP are now input-addressed, where the inputs are the
system toplevel and the public key used for signature. This way, it is
guaranteed that any stub at a given path will boot the desired system,
even in the presence of one of the two edge-cases where it was not
previously guaranteed:
* The latest generation was deleted at one point, and its generation
number was reused by a different system configuration. This is
detected because the toplevel will change.
* The secure boot signing key was rotated, so old stubs would not boot
at all any more. This is detected because the public key will change.
Avoiding these two cases will allow to skip reinstallation of stubs that
are already in place at the correct path.
Kernels and initrds on the ESP are now content-addressed. By definition,
it is impossible for two different kernels or initrds to ever end up at
the same place, even in the presence of changing initrd secrets or other
unreproducibility.
The basic advantage of this is that installing the kernel or initrd for
a generation can never break another generation. In turn, this enables
the following two improvements:
* All generations can be installed independently. In particular, the
installation can be performed in one pass, one generation at a time.
As a result, the code is significantly simplified, and memory usage
(due to the temporary files) does not grow with the number of
generations any more.
* Generations that already have their files in place on the ESP do not
need to be reinstalled. This will be taken advantage of in a
subsequent commit.
systemd-boot is now installed once for many generations rather than multiple times.
This means it is not really possible to manage different system in the same "machine", which is a very
obscure usecase, theoretically possible, but not yet encountered.