sysroot: Ensure deployment detection works when using composefs
In the case of composefs, we cannot compare the devino of the rootfs
and the deploy dir, because the root is the composefs mount, not a
bind mount. Instead we check the devino of the etc subdir of the
deploy, because this is a bind mount even when using composefs.
prepare-root: Support using composefs as root filesystem
This changes ostree-prepare-root to use the .ostree.cfs image as a
composefs filesystem, instead of the checkout.
By default, composefs is used if support is built in and the .ostree.cfs
file exists in the deploy dir, otherwise we fall back to the old
method. However, if the ot-composefs kernel option is specified this
can be tweaked as per:
* off: Never use composefsz
* maybe: Use if possible
* on: Fail if not possible
* signed: Fail if the cfs image is not fs-verity signed with
a key in the keyring.
* digest=....: Fail if the cfs image does not match the specified
digest.
The final layout when composefs is active is:
/ ro overlayfs mount for composefs
/sysroot "real" root
/etc rw bind mount to $deploydir/etc
/var rw bind mount to $vardir
We also specify the $deploydir/.ostree-mnt directory as the (internal)
mountpoint for the erofs mount for composefs. This can be used to map
the root fs back to the deploy id/dir in use,
A further note: I didn't test the .usr-ovl-work overlayfs case, but a
comment mentions that you can't mount overlayfs on top of a readonly
mount. That seems incompatible with composefs. If this is needed we
have to merge that with the overlayfs that composefs itself sets up,
which is possible with the libcomposefs APIs.
composefs deploy: Store cfs signature in .ostree.cfs.sig file
In many cases, such as when using osbuild, we are not preparing the final
deployment but rather a rootfs tree that will eventually be copied to the
final location. In that case we don't want to apply the signature directly
but when the deployment is copied in place.
To make this situateion workable we also write the signature to a file
next to the composefs image file. Then whatever mechanism that does
the final copy can apply the signature.
deploy: Write a .ostree.cfs composefs image in the deploy dir
This can be used as a composefs source for the root fs instead of
the checkout by pointing the basedir to /ostree/repo/objects.
We only write the file is `composefs` is enabled.
We enable ensure_rootfs_dirs when building the image which adds the
required root dirs to the image. In particular, this includes /etc
which often isn't in ostree commits in use.
We also create an (empty) .ostree.mnt directory, where composefs
will mount the erofs image that will be used as overlayfs lowerdir
for the root overlayfs mount. This way we can find the deploy
dir from the root overlayfs mount options.
If the commit has composefs digests recorded we verify those with the
created file. It also applies the fs-verity signature if it is
recorded, unless this is disabled with the
ex-integrity.composefs-apply-sign=false option.
Commit: Add composefs digest and sig to the commit metadata
If `composefs-apply-sig` is enabled (default no) we add an
ostree.composefs digest to the commit metadata. This can be verified
on deploy.
This is a separate option from the generic `composefs` option which
controls whether composefs is used during deploy. It is separate
because we want to not have to force use of fs-verity, etc during the
build.
If the `composefs-certfile` and `composefs-keyfile` keys in the
ex-integrity group are set, then the commit metadata also gets a
ostree.composefs-sig containing the signature of the composefs file.
lib: Add (private) API for checking out commits into a composefs image
This supports checking out a commit into a tree which is then
converted into a composefs image containing fs-verity digests for all
the regular files, and payloads that are relative to a the
`repo/objects` directory of a bare ostree repo.
Some specal files are always created in the image. This ensures that
various directories (usr, etc, boot, var, sysroot) exists in the
created image, even if they were not in the source commit. These are
needed (as bindmount targets) if you want to boot from the image. In
the non-composefs case these are just created as needed in the checked
out deploydir, but we can't do that here.
This is all controlled by the new ex-integrity config section, which
has the following layout:
The `fsverity` key overrides the old `ex-fsverity` section if
specified. The default for all these is for the new behaviour to be
disabled. Additionally, enabling composefs implies fsverity defaults
to `maybe`, to avoid having to set both.
Jonathan Lebon [Sat, 27 May 2023 14:37:30 +0000 (10:37 -0400)]
lib/deploy: Use `fallocate` for early prune space check
The `f_bfree` member of the `statvfs` struct is documented as the
"number of free blocks". However, different filesystems have different
interpretations of this. E.g. on XFS, this is truly the number of blocks
free for allocating data. On ext4 however, it includes blocks that
are actually reserved by the filesystem and cannot be used for file
data. (Note this is separate from the distinction between `f_bfree` and
`f_bavail` which isn't relevant to us here since we're privileged.)
If a kernel and initrd is sized just right so that it's still within the
`f_bfree` limit but above what we can actually allocate, the early prune
code won't kick in since it'll think that there is enough space. So we
end up hitting `ENOSPC` when we actually copy the files in.
Rework the early prune code to instead use `fallocate` which guarantees
us that a file of a certain size can fit on the filesystem. `fallocate`
requires filesystem support, but all the filesystems we care about for
the bootfs support it (including even FAT).
(There's technically a TOCTOU race here that existed also with the
`statvfs` code where free space could change between when we check
and when we copy. Ideally we'd be able to pass down that fd to the
copying bits, but anyway in practice the bootfs is pretty much owned by
libostree and one doesn't expect concurrent writes during a finalization
operation.)
Colin Walters [Tue, 23 May 2023 18:42:15 +0000 (14:42 -0400)]
prepare-root: Move sysroot.tmp creation earlier
Main motivation is prep for composefs in
https://github.com/ostreedev/ostree/pull/2640
In the interest of that, we add a `bool using_composefs` but
it's currently always `false`.
Co-authored-by: Alexander Larsson <alexl@redhat.com>
Colin Walters [Wed, 10 May 2023 13:21:45 +0000 (09:21 -0400)]
tests/inst: Add xshell and use it in one place
I've deprecated sh-inline; in the end I think it is better
to minimize the amount of bash code we have. xshell solves
the core convenience problem of taking local variables and mapping
them to command arguments.
A full port would be nontrivial; this just starts the ball
rolling.
Jonathan Lebon [Thu, 13 Apr 2023 21:22:43 +0000 (17:22 -0400)]
lib/sysroot-deploy: Add experimental support for automatic early prune
During the early design of FCOS and RHCOS, we chose a value of 384M
for the boot partition. This turned out to be too small: some arches
other than x86_64 have larger initrds, kernel binaries, or additional
artifacts (like device tree blobs). We'll likely bump the boot partition
size in the future, but we don't want to abandon all the nodes deployed
with the current size.[[1]]
Because stale entries in `/boot` are cleaned up after new entries are
written, there is a window in the update process during which the bootfs
temporarily must host all the `(kernel, initrd)` pairs for the union of
current and new deployments.
This patch determines if the bootfs is capable of holding all the
pairs. If it can't but it could hold all the pairs from just the new
deployments, the outgoing deployments (e.g. rollbacks) are deleted
*before* new deployments are written. This is done by updating the
bootloader in two steps to maintain atomicity.
Since this is a lot of new logic in an important section of the
code, this feature is gated for now behind an environment variable
(`OSTREE_ENABLE_AUTO_EARLY_PRUNE`). Once we gain more experience with
it, we can consider turning it on by default.
This strategy increases the fallibility of the update system since one
would no longer be able to rollback to the previous deployment if a bug
is present in the bootloader update logic after auto-pruning (see [[2]]
and following). This is however mitigated by the fact that the heuristic
is opportunistic: the rollback is pruned *only if* it's the only way for
the system to update.
Colin Walters [Wed, 26 Apr 2023 13:37:22 +0000 (09:37 -0400)]
ci: Turn off errors for deprecated-declarations
Having `-Werror` on in CI only by default has generally worked OK,
but I don't think it's worth trying to immediately scramble to port
when they deprecate APIs.
Motivated in this case by
```
src/libostree/ostree-fetcher-curl.c: In function 'initiate_next_curl_request':
src/libostree/ostree-fetcher-curl.c:876:3: error: 'CURLOPT_PROTOCOLS' is deprecated: since 7.85.0. Use CURLOPT_PROTOCOLS_STR [-Werror=deprecated-declarations]
876 | rc = curl_easy_setopt (req->easy, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FILE));
| ^~
```
Jonathan Lebon [Fri, 14 Apr 2023 16:21:05 +0000 (12:21 -0400)]
tests/kolainst: Add `make localinstall`
When hacking and testing locally with `cosa build-fast` and `kola run`,
I prefer to leave testing framework stuff within the work directory
rather than installed in my pet container. Add a `localinstall` target
for this which puts the tests in `tests/kola`. Then a simple `kola run`
will pick it up.
Jonathan Lebon [Fri, 14 Apr 2023 16:19:30 +0000 (12:19 -0400)]
tests/kola: delete unused .gitignore
AFAICT, I don't see how `runkola.sh` or the Makefile in `tests/kolainst`
can create files in `tests/kola` since it's geared towards installing
under `/usr`.
Jonathan Lebon [Fri, 14 Apr 2023 16:11:05 +0000 (12:11 -0400)]
lib/sysroot-deploy: Nuke `finalize-failure.stamp` on successful finalization
In the unusual case where one is manually finalizing staged deployments,
as can happen in testing, we expect a successful finalization to remove
the failure stamp file.
Jonathan Lebon [Thu, 13 Apr 2023 21:22:40 +0000 (17:22 -0400)]
lib/sysroot-cleanup: Factor out bootfs cleanup
Crawling through the bootfs and the deployment dirs was already mostly
separate. The only inefficiency here is that we now iterate over the
array of active deployments twice when building the hash tables. No
functional change otherwise.
Dan Nicholson [Fri, 7 Apr 2023 15:47:14 +0000 (09:47 -0600)]
fetcher/soup3: Rewrite without threads
soup3 works best using only the async API from a single thread[1].
Rework the fetcher to stop using worker threads. In order to maximize
session usage across requests, sessions will be reused for each main
context.
Dan Nicholson [Wed, 5 Apr 2023 22:26:10 +0000 (16:26 -0600)]
tests: Ensure real GIO backends aren't used
Set a few environment variables during tests to ensure fake GIO backends
are used. This is particularly important with the soup fetcher backend
as it can cause strange test errors in containerized test environments.
Upstream soup has been setting these 3 environment variables for their
tests since 2015.
Jonathan Lebon [Wed, 1 Mar 2023 14:42:46 +0000 (09:42 -0500)]
ci: update for new kolaTestIso()
The `skipMetal4k` and `skipMultipath` parameters no longer do anything.
Instead use `--denylist-test` which is interpreted directly by the
new `kola testiso` code.
This will run more tests than before. We'll likely cut down tests we
consider redundant soon, but that should be transparent to this code.
Simon McVittie [Fri, 17 Mar 2023 12:56:39 +0000 (12:56 +0000)]
write_content_object: Don't assume file info has standard::size
The file info object for symlinks might validly not have this attribute.
If not, behave as though it was 0, matching what happened with older
versions of GLib.
Signed-off-by: Simon McVittie <smcv@collabora.com>
Simon McVittie [Fri, 17 Mar 2023 12:53:42 +0000 (12:53 +0000)]
ostree_raw_file_to_content_stream: Make size default to 0
Some existing code calls into ostree_raw_file_to_content_stream() with
file objects that do not have the standard::size attribute. Since GLib
2.76.0, attempting to access the size of such an object raises a
critical warning. Handle this more gracefully by defaulting the size
to 0, like earlier versions of GLib did.
Signed-off-by: Simon McVittie <smcv@collabora.com>
Colin Walters [Fri, 17 Mar 2023 12:20:16 +0000 (08:20 -0400)]
repo: Fix nullability for remote options
Unfortunately, the nullability of the output value here is
dependent on whether the `default_value` parameter is provided. There's
no way to express this in introspection or Rust.
Evan Anderson [Thu, 16 Mar 2023 02:33:21 +0000 (21:33 -0500)]
core: Ensure glib standard::size attribute is always set
* A recent change in glib [1] requires that the appropriate attribute
be available when calling getters. ostree core only sets this attribute
on regular files, and frequently triggers the critical warning. Solve
this by setting standard::size to zero for non-regular files.
Colin Walters [Tue, 14 Mar 2023 20:51:49 +0000 (16:51 -0400)]
itest-pull-space: Use mkfs.ext4, align to at least 512b
XFS now seems to want filesystems larger than 300MB, so switch
to ext4. Also use `20MiB` so we align to 512b sectors to squash
a `losetup` warning.
Also tweak some of the numbers to still work.
Philip Withnall [Fri, 10 Mar 2023 11:54:34 +0000 (11:54 +0000)]
lib/fetcher-util: Wake up main context when a request is complete
Since the value of `data->done` is not watched by the main context, the
context doesn’t know to wake up from `g_main_context_iteration()` when
that value is changed. The code currently relies on something else
happening to wake the main context up shortly after `data->done` is set.
That doesn’t seem very reliable, so wake the main context up explicitly.
Spotted this while reading the code while trying to debug a stall with
backtrace:
```
5 0x00007ffff68bbbfb in g_main_context_iteration (context=0x60f000136900, may_block=1) at ../../source/glib/glib/gmain.c:4343
6 0x00007fffdc0e50db in _ostree_fetcher_mirrored_request_to_membuf_once (fetcher=0x604001138c10, mirrorlist=0x603001df18b0, filename=0x7fffdc1049e7 "summary.sig", flags=OSTREE_FETCHER_REQUEST_OPTIONAL_CONTENT, if_none_match=0x602002f3b7f0 "\"640a49ff-250\"", if_modified_since=1678395903, out_contents=0x7fffdadd0e80, out_not_modified=0x7fffdadd0e38, out_etag=0x7fffdadd0e28, out_last_modified=0x7fffdadd0e00, max_size=10485760, cancellable=0x6060004bd720, error=0x7fffdadd0ca0) at src/libostree/ostree-fetcher-util.c:95
7 0x00007fffdc0e52e0 in _ostree_fetcher_mirrored_request_to_membuf (fetcher=0x604001138c10, mirrorlist=0x603001df18b0, filename=0x7fffdc1049e7 "summary.sig", flags=OSTREE_FETCHER_REQUEST_OPTIONAL_CONTENT, if_none_match=0x602002f3b7f0 "\"640a49ff-250\"", if_modified_since=1678395903, n_network_retries=5, out_contents=0x7fffdadd0e80, out_not_modified=0x7fffdadd0e38, out_etag=0x7fffdadd0e28, out_last_modified=0x7fffdadd0e00, max_size=10485760, cancellable=0x6060004bd720, error=0x7fffdadd10c0) at src/libostree/ostree-fetcher-util.c:155
8 0x00007fffdc08d937 in _ostree_preload_metadata_file (self=0x61600057bd80, fetcher=0x604001138c10, mirrorlist=0x603001df18b0, filename=0x7fffdc1049e7 "summary.sig", is_metalink=0, if_none_match=0x602002f3b7f0 "\"640a49ff-250\"", if_modified_since=1678395903, n_network_retries=5, out_bytes=0x7fffdadd0e80, out_not_modified=0x7fffdadd0e38, out_etag=0x7fffdadd0e28, out_last_modified=0x7fffdadd0e00, cancellable=0x6060004bd720, error=0x7fffdadd10c0) at src/libostree/ostree-repo-pull.c:3329
9 0x00007fffdc099712 in ostree_repo_remote_fetch_summary_with_options (self=0x61600057bd80, name=0x6020007f4fd0 "eos-apps", options=0x0, out_summary=0x7fffdadd0f88, out_signatures=0x7fffdadd0f80, cancellable=0x6060004bd720, error=0x7fffdadd10c0) at src/libostree/ostree-repo-pull.c:6675
10 0x00007fffdc06887f in ostree_repo_remote_fetch_summary (self=0x61600057bd80, name=0x6020007f4fd0 "eos-apps", out_summary=0x7fffdadd0f88, out_signatures=0x7fffdadd0f80, cancellable=0x6060004bd720, error=0x7fffdadd10c0) at src/libostree/ostree-repo.c:2706
11 0x00007fffdc18f5de in flatpak_dir_remote_fetch_summary (self=0x60c00577e640, name_or_uri=0x6020007f4fd0 "eos-apps", only_cached=0, out_summary=0x7fffdadd10f0, out_summary_sig=0x7fffdadd10e8, cancellable=0x6060004bd720, error=0x7fffdadd10c0) at /opt/gnome/source/flatpak/common/flatpak-dir.c:12235
12 0x00007fffdc1918cb in _flatpak_dir_get_remote_state (self=0x60c00577e640, remote_or_uri=0x6020007f4fd0 "eos-apps", optional=1, local_only=0, only_cached=0, opt_summary_is_index=0, opt_summary=0x0, opt_summary_sig=0x0, cancellable=0x6060004bd720, error=0x7fffdadd15a0) at /opt/gnome/source/flatpak/common/flatpak-dir.c:12789
13 0x00007fffdc19206d in flatpak_dir_get_remote_state_optional (self=0x60c00577e640, remote=0x6020007f4fd0 "eos-apps", only_cached=0, cancellable=0x6060004bd720, error=0x7fffdadd15a0) at /opt/gnome/source/flatpak/common/flatpak-dir.c:12953
14 0x00007fffdc1784f2 in flatpak_dir_update_appstream (self=0x60c00577e640, remote=0x6020007f4fd0 "eos-apps", arch=0x7fffdc239f30 "x86_64", out_changed=0x0, progress=0x6110007479c0, cancellable=0x6060004bd720, error=0x7fffdadd15a0) at /opt/gnome/source/flatpak/common/flatpak-dir.c:5182
```
I don’t think it’ll fix it (calling `g_main_context_wakeup()` on the
context in the debugger didn’t help), but it can’t hurt.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>