From 6a27dff94100c31edf392b26ace05009f4dedf9c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 12 Mar 2020 21:10:29 +0000 Subject: [PATCH] main: Also automatically remount rw /sysroot for `ostree pull` etc. See https://github.com/coreos/fedora-coreos-tracker/issues/343 When we added the read-only sysroot support it broke using "raw" `ostree pull` and `ostree refs --create` and all of the core repo CLIs that just operate on a repo and not a sysroot. Fixing this is a bit ugly as it "layer crosses" things even more. Extract a helper function that works in both cases. --- src/ostree/ot-main.c | 84 +++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c index a044cef23f..bffa40c4d4 100644 --- a/src/ostree/ot-main.c +++ b/src/ostree/ot-main.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "ot-main.h" #include "ostree.h" @@ -100,6 +102,49 @@ ostree_usage (OstreeCommand *commands, return (is_error ? 1 : 0); } +/* If we're running as root, booted into an OSTree system and have a read-only + * /sysroot, then assume we may need write access. Create a new mount namespace + * if so, and return *out_ns = TRUE. Otherwise, *out_ns = FALSE. + */ +static gboolean +maybe_setup_mount_namespace (gboolean *out_ns, + GError **error) +{ + *out_ns = FALSE; + + /* If we're not root, then we almost certainly can't be remounting anything */ + if (getuid () != 0) + return TRUE; + + /* If the system isn't booted via libostree, also nothing to do */ + if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error)) + return FALSE; + if (errno == ENOENT) + return TRUE; + + glnx_autofd int sysroot_subdir_fd = glnx_opendirat_with_errno (AT_FDCWD, "/sysroot", TRUE); + if (sysroot_subdir_fd < 0) + { + if (errno != ENOENT) + return glnx_throw_errno_prefix (error, "opendirat"); + /* No /sysroot - nothing to do */ + return TRUE; + } + + struct statvfs stvfs; + if (fstatvfs (sysroot_subdir_fd, &stvfs) < 0) + return glnx_throw_errno_prefix (error, "fstatvfs"); + if (stvfs.f_flag & ST_RDONLY) + { + if (unshare (CLONE_NEWNS) < 0) + return glnx_throw_errno_prefix (error, "preparing writable sysroot: unshare (CLONE_NEWNS)"); + + *out_ns = TRUE; + } + + return TRUE; +} + static void message_handler (const gchar *log_domain, GLogLevelFlags log_level, @@ -220,6 +265,19 @@ parse_repo_option (GOptionContext *context, { g_autoptr(OstreeRepo) repo = NULL; + /* This is a bit of a brutal hack; we set up a mount + * namespace if it appears that we may need it. It'd + * be better to do this more precisely in the future. + */ + gboolean setup_ns = FALSE; + if (!maybe_setup_mount_namespace (&setup_ns, error)) + return FALSE; + if (setup_ns) + { + if (mount ("/sysroot", "/sysroot", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) + return glnx_null_throw_errno_prefix (error, "Remounting /sysroot read-write"); + } + if (repo_path == NULL) { g_autoptr(GError) local_error = NULL; @@ -452,27 +510,11 @@ ostree_admin_option_context_parse (GOptionContext *context, */ if (ostree_sysroot_is_booted (sysroot)) { - int sysroot_fd = ostree_sysroot_get_fd (sysroot); - g_assert_cmpint (sysroot_fd, !=, -1); - - glnx_autofd int sysroot_subdir_fd = glnx_opendirat_with_errno (sysroot_fd, "sysroot", TRUE); - if (sysroot_subdir_fd < 0) - { - if (errno != ENOENT) - return glnx_throw_errno_prefix (error, "opendirat"); - } - else if (getuid () == 0) - { - struct statvfs stvfs; - if (fstatvfs (sysroot_subdir_fd, &stvfs) < 0) - return glnx_throw_errno_prefix (error, "fstatvfs"); - if (stvfs.f_flag & ST_RDONLY) - { - if (unshare (CLONE_NEWNS) < 0) - return glnx_throw_errno_prefix (error, "preparing writable sysroot: unshare (CLONE_NEWNS)"); - ostree_sysroot_set_mount_namespace_in_use (sysroot); - } - } + gboolean setup_ns = FALSE; + if (!maybe_setup_mount_namespace (&setup_ns, error)) + return FALSE; + if (setup_ns) + ostree_sysroot_set_mount_namespace_in_use (sysroot); } /* Released when sysroot is finalized, or on process exit */