Skip to content

Commit

Permalink
apply: Don't use staged deployments when /boot is automounted
Browse files Browse the repository at this point in the history
The ostree staged deployment process works by waiting until shutdown to
swap the `/boot` symlinks to make the new deployment the default.
However, when `/boot` is the EFI System Partition and there's no `fstab`
entry, `systemd-gpt-auto-generator` sets up an automount so that the
VFAT filesystem is only exposed when needed.

Unfortunately, there are 2 bugs that make this process very fragile:

* Once a systemd automount unit is scheduled to be stopped, it ignores
  notifications from autofs that the target filesystem should be
  mounted. Therefore, if `/boot` isn't mounted when shutdown begins,
  `ostree admin finalize-staged` will fail. See
  systemd/systemd#22528.

* autofs is not mount namespace aware, so it will begin the expiration
  timer for a mount unit unless a process in the root namespace is
  keeping it active. Since `ostree admin finalize-staged` is run from a
  mount namespace (either via systemd or its own to ensure `/sysroot`
  and `/boot` are mounted read-write), the automount daemon (systemd)
  will try to unmount the filesystem if it expires during this process.
  See https://bugzilla.redhat.com/show_bug.cgi?id=2056090.

Therefore, if `/boot` is an autofs filesystem, use a full deployment
instead of a staged deployment. Since systems with an automounted
`/boot` are not common, we want to retain the benefit of staged
deployments for more normal systems. See
ostreedev/ostree#2543 for potential future
fixes in ostree.

https://phabricator.endlessm.com/T33136
  • Loading branch information
dbnicholson committed Mar 3, 2022
1 parent 4aac33c commit b6fc706
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 5 deletions.
70 changes: 66 additions & 4 deletions eos-updater/apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <libeos-updater-util/types.h>
#include <libeos-updater-util/util.h>
#include <ostree.h>
#include <libmount.h>

/* Closure containing the data for the apply worker thread. The
* worker thread must not access EosUpdater or EosUpdaterData directly,
Expand Down Expand Up @@ -227,6 +228,65 @@ update_remote_branches (OstreeRepo *repo,
return TRUE;
}

static gboolean
sysroot_boot_is_automount (OstreeSysroot *sysroot)
{
GFile *sysroot_file;
g_autoptr(GFile) boot_file = NULL;
g_autofree gchar *boot_path = NULL;
gboolean ret = FALSE;
struct libmnt_table *tb = NULL;
struct libmnt_cache *cache;
struct libmnt_fs *fs;

sysroot_file = ostree_sysroot_get_path (sysroot);
boot_file = g_file_get_child (sysroot_file, "boot");
boot_path = g_file_get_path (boot_file);
if (boot_path == NULL)
{
g_autofree gchar *sysroot_path = g_file_get_path (sysroot_file);
g_warning ("No boot directory in sysroot %s", sysroot_path);
goto out;
}

tb = mnt_new_table_from_file ("/proc/self/mountinfo");
if (tb == NULL)
{
g_warning ("Failed to parse /proc/self/mountinfo: %s",
g_strerror (errno));
goto out;
}

cache = mnt_new_cache ();
if (cache == NULL)
{
g_warning ("Failed to create libmount cache: %s", g_strerror (errno));
goto out;
}
mnt_table_set_cache (tb, cache);
mnt_unref_cache (cache);

/* Find the /boot mountpoint iterating forward so that the autofs mount is
* found and not the automount target filesystem.
*/
fs = mnt_table_find_mountpoint (tb, boot_path, MNT_ITER_FORWARD);
if (fs == NULL)
{
g_warning ("Failed to find mountpoint for %s", boot_path);
goto out;
}

ret = g_strcmp0 (mnt_fs_get_fstype (fs), "autofs") == 0;
g_debug ("Boot directory %s is%s on an autofs filesystem",
boot_path, ret ? "" : " not");

out:
if (tb != NULL)
mnt_unref_table (tb);

return ret;
}

static gboolean
apply_internal (ApplyData *apply_data,
GCancellable *cancellable,
Expand Down Expand Up @@ -270,11 +330,13 @@ apply_internal (ApplyData *apply_data,

origin = ostree_sysroot_origin_new_from_refspec (sysroot, update_refspec);

/* When booted into an OSTree system, stage the deployment so that the
* /etc merge happens during shutdown. Otherwise (primarily the test
* suite), deploy the finalized tree immediately.
/* When booted into an OSTree system without an automount /boot, stage the
* deployment so that the /etc merge happens during shutdown. Otherwise
* (primarily sd-boot and the test suite), deploy the finalized tree
* immediately.
*/
staged_deploy = ostree_sysroot_is_booted (sysroot);
staged_deploy = ostree_sysroot_is_booted (sysroot) &&
!sysroot_boot_is_automount (sysroot);
if (staged_deploy)
{
g_message ("Creating staged deployment for revision %s", update_id);
Expand Down
5 changes: 4 additions & 1 deletion eos-updater/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ eos_updater_sources = [
'poll-common.h',
] + eos_updater_resources

eos_updater_deps = libeos_updater_dbus_deps + [libeos_updater_dbus_dep]
eos_updater_deps = libeos_updater_dbus_deps + [
dependency('mount', version: '>= 2.24'),
libeos_updater_dbus_dep,
]

eos_updater_cppflags = [
'-DG_LOG_DOMAIN="eos-updater"',
Expand Down

0 comments on commit b6fc706

Please sign in to comment.