Skip to content

Commit

Permalink
libeos-updater-util: Add method to detect if /boot is an automount
Browse files Browse the repository at this point in the history
On some hosts, systemd sets up `/boot` as an autofs automount.
Unfortunately, this configuration has some bugs that make usage of
`/boot` less than robust.

Add a method to see if `/boot` is an automount. This uses libmount to
parse `/proc/self/mountinfo` and check if the first mountpoint for the
sysroot's boot directory has the `autofs` filesystem type. libmount
version 2.24 was chosen as a minimum because that's when `libmnt_table`
and `libmnt_cache` reference counting was added.

https://phabricator.endlessm.com/T33136
  • Loading branch information
dbnicholson committed Mar 9, 2022
1 parent 4aac33c commit 7538608
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Build-Depends:
libgsystem-dev,
libjson-glib-dev (>= 1.2.6-1endless2),
libmogwai-schedule-client-0-dev,
libmount-dev (>= 2.24),
libnm-dev (>= 1.2.0),
libostree-dev (>= 2017.12),
libsoup2.4-dev,
Expand Down
1 change: 1 addition & 0 deletions libeos-updater-util/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ libeos_updater_util_deps = [
dependency('gobject-2.0', version: '>= 2.62'),
dependency('json-glib-1.0', version: '>= 1.2.6'),
dependency('libsoup-2.4'),
dependency('mount', version: '>= 2.24'),
dependency('ostree-1', version: '>= 2018.6'),
]

Expand Down
77 changes: 77 additions & 0 deletions libeos-updater-util/ostree-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <glib.h>
#include <libeos-updater-util/ostree-util.h>
#include <libeos-updater-util/util.h>
#include <libmount.h>
#include <libsoup/soup.h>
#include <ostree.h>
#include <string.h>
Expand Down Expand Up @@ -311,3 +312,79 @@ eos_updater_get_ostree_path (OstreeRepo *repo,
*ostree_path = g_steal_pointer (&path);
return TRUE;
}

/**
* eos_updater_sysroot_boot_is_automount:
* @sysroot: loaded OSTree sysroot to use
* @mountinfo: (nullable): path to mountinfo file
*
* Find if the boot directory in @sysroot is an automount mount. The first
* mountpoint for the boot directory in @mountinfo is found. If the filesystem
* type for the mountpoint is autofs, then the boot directory is automounted.
*
* If @mountinfo is %NULL, /proc/self/mountinfo is used.
*
* Returns: %TRUE if boot is an automount
*/
gboolean
eos_updater_sysroot_boot_is_automount (OstreeSysroot *sysroot,
const gchar *mountinfo)
{
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;
}

if (mountinfo == NULL)
mountinfo = "/proc/self/mountinfo";
tb = mnt_new_table_from_file (mountinfo);
if (tb == NULL)
{
g_warning ("Failed to parse %s: %s", mountinfo, 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, as the autofs mount is
* listed first in mountinfo.
*/
fs = mnt_table_find_mountpoint (tb, boot_path, MNT_ITER_FORWARD);
if (fs == NULL)
{
g_warning ("Failed to find mountpoint for %s: %s", boot_path,
g_strerror (errno));
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;
}
3 changes: 3 additions & 0 deletions libeos-updater-util/ostree-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ gboolean eos_updater_get_ostree_path (OstreeRepo *repo,
gchar **ostree_path,
GError **error);

gboolean eos_updater_sysroot_boot_is_automount (OstreeSysroot *sysroot,
const gchar *mountinfo);

G_END_DECLS
81 changes: 81 additions & 0 deletions libeos-updater-util/tests/ostree-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,85 @@ test_ostree_no_deployments (Fixture *fixture,
g_assert_cmpuint (commit_timestamp, ==, 0);
}

static void
test_ostree_sysroot_boot_automount (Fixture *fixture,
gconstpointer user_data G_GNUC_UNUSED)
{
g_autoptr(GError) error = NULL;
gboolean retval;
GFile *sysroot_file = ostree_sysroot_get_path (fixture->sysroot);
g_autofree gchar *sysroot_path = g_file_get_path (sysroot_file);
g_autoptr(GFile) boot_file = g_file_get_child (sysroot_file, "boot");
g_autofree gchar *boot_path = g_file_get_path (boot_file);
g_autoptr(GFile) mountinfo_file = g_file_get_child (fixture->tmp_dir,
"mountinfo");
g_autofree gchar *mountinfo_path = g_file_get_path (mountinfo_file);
g_autofree gchar *mountinfo_contents = NULL;

retval = g_file_make_directory (boot_file, NULL, &error);
g_assert_no_error (error);
g_assert_true (retval);

/* No separate /boot mount */
g_clear_pointer (&mountinfo_contents, g_free);
mountinfo_contents =
g_strdup_printf ("1 1 1:1 / %s rw - ext4 /dev/sda1 rw\n", sysroot_path);
g_test_message ("boot %s, mountinfo:\n%s", boot_path, mountinfo_contents);
retval = g_file_replace_contents (mountinfo_file, mountinfo_contents,
strlen (mountinfo_contents), NULL, FALSE,
G_FILE_CREATE_NONE, NULL, NULL, &error);
g_assert_no_error (error);
g_assert_true (retval);
retval = eos_updater_sysroot_boot_is_automount (fixture->sysroot, mountinfo_path);
g_assert_false (retval);

/* Non-automount /boot */
g_clear_pointer (&mountinfo_contents, g_free);
mountinfo_contents =
g_strdup_printf ("1 1 1:2 / %s rw - ext4 /dev/sda2 rw\n"
"2 1 1:1 / %s rw - ext4 /dev/sda1 rw\n",
sysroot_path, boot_path);
g_test_message ("boot %s, mountinfo:\n%s", boot_path, mountinfo_contents);
retval = g_file_replace_contents (mountinfo_file, mountinfo_contents,
strlen (mountinfo_contents), NULL, FALSE,
G_FILE_CREATE_NONE, NULL, NULL, &error);
g_assert_no_error (error);
g_assert_true (retval);
retval = eos_updater_sysroot_boot_is_automount (fixture->sysroot, mountinfo_path);
g_assert_false (retval);

/* Automount /boot without target mount */
g_clear_pointer (&mountinfo_contents, g_free);
mountinfo_contents =
g_strdup_printf ("1 1 1:2 / %s rw - ext4 /dev/sda2 rw\n"
"2 1 0:1 / %s rw - autofs systemd-1 rw\n",
sysroot_path, boot_path);
g_test_message ("boot %s, mountinfo:\n%s", boot_path, mountinfo_contents);
retval = g_file_replace_contents (mountinfo_file, mountinfo_contents,
strlen (mountinfo_contents), NULL, FALSE,
G_FILE_CREATE_NONE, NULL, NULL, &error);
g_assert_no_error (error);
g_assert_true (retval);
retval = eos_updater_sysroot_boot_is_automount (fixture->sysroot, mountinfo_path);
g_assert_true (retval);

/* Automount /boot with target mount */
g_clear_pointer (&mountinfo_contents, g_free);
mountinfo_contents =
g_strdup_printf ("1 1 1:2 / %s rw - ext4 /dev/sda2 rw\n"
"2 1 0:1 / %s rw - autofs systemd-1 rw\n"
"3 2 1:1 / %s rw - vfat /dev/sda1 rw\n",
sysroot_path, boot_path, boot_path);
g_test_message ("boot %s, mountinfo:\n%s", boot_path, mountinfo_contents);
retval = g_file_replace_contents (mountinfo_file, mountinfo_contents,
strlen (mountinfo_contents), NULL, FALSE,
G_FILE_CREATE_NONE, NULL, NULL, &error);
g_assert_no_error (error);
g_assert_true (retval);
retval = eos_updater_sysroot_boot_is_automount (fixture->sysroot, mountinfo_path);
g_assert_true (retval);
}

int
main (int argc,
char *argv[])
Expand All @@ -157,6 +236,8 @@ main (int argc,

g_test_add ("/ostree/no-deployments", Fixture, NULL, setup,
test_ostree_no_deployments, teardown);
g_test_add ("/ostree/sysroot-boot-automount", Fixture, NULL, setup,
test_ostree_sysroot_boot_automount, teardown);
/* TODO: More */

return g_test_run ();
Expand Down

0 comments on commit 7538608

Please sign in to comment.