Skip to content

Commit

Permalink
main: Also automatically remount rw /sysroot for ostree pull etc.
Browse files Browse the repository at this point in the history
See coreos/fedora-coreos-tracker#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.
  • Loading branch information
cgwalters committed Mar 13, 2020
1 parent 2d90195 commit 6a27dff
Showing 1 changed file with 63 additions and 21 deletions.
84 changes: 63 additions & 21 deletions src/ostree/ot-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 6a27dff

Please sign in to comment.