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.
This commit is contained in:
Colin Walters 2020-03-12 21:10:29 +00:00
parent 2d901957b8
commit 6a27dff941

View File

@ -28,6 +28,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <sys/mount.h>
#include <linux/fs.h>
#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 */