bwrap/compose: Add a workaround for Fedora's use of rpm-ostree-in-mock

Decided to test this on Sunday evening.  Of course it was broken =(
(Actually I tested mock-in-Docker but it should be the same)

The core problem is that mock does `chroot()` without using `/`
as a mount point.  This breaks an assumption in bwrap that it is.
Now, in theory we could move this same logic down into bwrap to
work around this situation, but for now let's hack it here.

Mock is old, legacy container code that doesn't really do anything
in a modern way - in fact our goal should be to replace it
with a combination of rpm-ostree and bwrap.  So carrying this
hack here to get us to that future should be OK for now.

Closes: #431
Approved by: jlebon
This commit is contained in:
Colin Walters 2016-08-15 11:58:03 -04:00 committed by Atomic Bot
parent 09c5f9a4fe
commit 3ad4e6c72b
3 changed files with 86 additions and 0 deletions

View File

@ -630,6 +630,10 @@ rpmostree_compose_builtin_tree (int argc,
"compose tree must presently be run as uid 0 (root)"); "compose tree must presently be run as uid 0 (root)");
goto out; goto out;
} }
/* Mock->bwrap bootstrap for Fedora */
if (!rpmostree_bwrap_bootstrap_if_in_mock (error))
goto out;
/* Test whether or not bwrap is going to work - we will fail inside e.g. a Docker /* Test whether or not bwrap is going to work - we will fail inside e.g. a Docker
* container without --privileged or userns exposed. * container without --privileged or userns exposed.
*/ */

View File

@ -108,6 +108,87 @@ rpmostree_run_sync_fchdir_setup (char **argv_array, GSpawnFlags flags,
return TRUE; return TRUE;
} }
/* mock doesn't actually use a mount namespace, and hence bwrap will
* fail to remount /. Work around this by doing it here. Fedora
* runs rpm-ostree inside of mock instead of Docker or something
* more modern.
*/
gboolean
rpmostree_bwrap_bootstrap_if_in_mock (GError **error)
{
const char *env_ps1 = getenv ("PS1");
static const char *mock_mounted_paths[] = { "/proc", "/sys" };
static const char *findmnt_argv[] = { "findmnt", "/", NULL };
g_autofree char *pwd = NULL;
int estatus;
if (!(env_ps1 && strstr (env_ps1, "<mock-chroot>")))
return TRUE;
/* Okay, we detected we're inside mock. Let's double check now
* whether or not / is already a mount point. The simplest way to
* do this is to execute findmnt...maybe someday we'll link to libmount
* but this is legacy.
*/
if (!g_spawn_sync (NULL, (char**)findmnt_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
NULL, NULL, &estatus, error))
{
g_prefix_error (error, "Executing findmnt: ");
return FALSE;
}
/* Did findmnt say / is a mount point? Okay, nothing to do here. */
if (estatus == 0)
return TRUE;
pwd = getcwd (NULL, 0);
g_print ("Detected mock chroot without / as mount, enabling workaround.\n");
if (unshare (CLONE_NEWNS) < 0)
{
glnx_set_prefix_error_from_errno (error, "%s", "unshare(CLONE_NEWNS)");
return FALSE;
}
/* For reasons I don't fully understand, trying to bind mount / -> /
* doesn't work. We seem to hit a check in the kernel:
*
* static int do_change_type(struct path *path, int flag)
* {
* ...
* if (path->dentry != path->mnt->mnt_root)
* return -EINVAL;
*/
if (mount ("/", "/mnt", NULL, MS_MGC_VAL | MS_BIND, NULL) != 0)
{
glnx_set_prefix_error_from_errno (error, "%s", "mount(/ as bind)");
return FALSE;
}
/* Now take the paths that mock mounted (that we need) and move them
* underneath the new rootfs mount.
*/
for (guint i = 0; i < G_N_ELEMENTS (mock_mounted_paths); i++)
{
const char *mockpath = mock_mounted_paths[i];
g_autofree char *destpath = g_strconcat ("/mnt", mockpath, NULL);
if (mount (mockpath, destpath, NULL, MS_MGC_VAL | MS_MOVE, NULL) != 0)
{
glnx_set_prefix_error_from_errno (error, "%s", "mount(move)");
return FALSE;
}
}
if (chroot ("/mnt") < 0)
{
glnx_set_error_from_errno (error);
return FALSE;
}
if (chdir (pwd) < 0)
{
glnx_set_error_from_errno (error);
return FALSE;
}
return TRUE;
}
/* Execute /bin/true inside a bwrap container on the host */ /* Execute /bin/true inside a bwrap container on the host */
gboolean gboolean
rpmostree_bwrap_selftest (GError **error) rpmostree_bwrap_selftest (GError **error)

View File

@ -31,4 +31,5 @@ void rpmostree_ptrarray_append_strdup (GPtrArray *argv_array, ...) G_GNUC_NULL_T
gboolean rpmostree_run_sync_fchdir_setup (char **argv_array, GSpawnFlags flags, gboolean rpmostree_run_sync_fchdir_setup (char **argv_array, GSpawnFlags flags,
int rootfs_fd, GError **error); int rootfs_fd, GError **error);
gboolean rpmostree_bwrap_bootstrap_if_in_mock (GError **error);
gboolean rpmostree_bwrap_selftest (GError **error); gboolean rpmostree_bwrap_selftest (GError **error);