sysroot: Add an API to initialize with mountns

This lowers down into the C library some logic we
have in the binary/app logic, in prep for having more Rust-native
CLI code in https://github.com/ostreedev/ostree-rs-ext/pull/412

Basically we want to *ensure* a mount namespace by invoking
`unshare()` if necessary, instead of requiring our callers
to do this dance.

This also helps fix e.g.
Closes: https://github.com/ostreedev/ostree/issues/2769
This commit is contained in:
Colin Walters 2022-11-21 15:16:04 -05:00
parent 9d652be3b1
commit 599ffefe7f
5 changed files with 65 additions and 17 deletions

View File

@ -548,6 +548,7 @@ OstreeSysroot
ostree_sysroot_new
ostree_sysroot_new_default
ostree_sysroot_initialize
ostree_sysroot_initialize_with_mount_namespace
ostree_sysroot_get_path
ostree_sysroot_load
ostree_sysroot_load_if_changed

View File

@ -24,6 +24,7 @@ LIBOSTREE_2022.7 {
global:
ostree_kernel_args_contains;
ostree_kernel_args_delete_if_present;
ostree_sysroot_initialize_with_mount_namespace;
} LIBOSTREE_2022.5;
/* Stub section for the stable release *after* this development one; don't

View File

@ -259,6 +259,63 @@ ostree_sysroot_set_mount_namespace_in_use (OstreeSysroot *self)
self->mount_namespace_in_use = TRUE;
}
/**
* ostree_sysroot_initialize_with_mount_namespace:
*
* Prepare the current process for modifying a booted sysroot, if applicable.
* This function subsumes the functionality of `ostree_sysroot_initialize`
* and may be invoked wherever that function is.
*
* If the sysroot does not appear to be booted, or where the current process is not uid 0,
* this function returns successfully.
*
* Otherwise, if the process is in the same mount namespace as pid 1, create
* a new namespace.
*
* If you invoke this function, it must be before ostree_sysroot_load(); it may
* be invoked before or after ostree_sysroot_initialize().
*
* Since: 2022.7
*/
gboolean
ostree_sysroot_initialize_with_mount_namespace (OstreeSysroot *self, GCancellable *cancellable, GError **error)
{
GLNX_AUTO_PREFIX_ERROR ("Initializing with mountns", error);
/* Must be before we're loaded, as otherwise we'd have to close/reopen all our
fds, e.g. the repo */
g_assert (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED);
if (!ostree_sysroot_initialize (self, error))
return FALSE;
/* Do nothing if we're not privileged */
if (getuid () != 0)
return TRUE;
/* We also assume operating on non-booted roots won't have a readonly sysroot */
if (!self->root_is_ostree_booted)
return TRUE;
g_autofree char *mntns_pid1 =
glnx_readlinkat_malloc (AT_FDCWD, "/proc/1/ns/mnt", cancellable, error);
if (!mntns_pid1)
return glnx_prefix_error (error, "Reading /proc/1/ns/mnt");
g_autofree char *mntns_self =
glnx_readlinkat_malloc (AT_FDCWD, "/proc/self/ns/mnt", cancellable, error);
if (!mntns_self)
return glnx_prefix_error (error, "Reading /proc/self/ns/mnt");
// If the mount namespaces are the same, we need to unshare().
if (strcmp (mntns_pid1, mntns_self) == 0)
{
if (unshare (CLONE_NEWNS) < 0)
return glnx_throw_errno_prefix (error, "Failed to invoke unshare(CLONE_NEWNS)");
}
ostree_sysroot_set_mount_namespace_in_use (self);
return TRUE;
}
/**
* ostree_sysroot_get_path:
* @self: Sysroot

View File

@ -50,6 +50,9 @@ OstreeSysroot* ostree_sysroot_new_default (void);
_OSTREE_PUBLIC
void ostree_sysroot_set_mount_namespace_in_use (OstreeSysroot *self);
_OSTREE_PUBLIC
gboolean ostree_sysroot_initialize_with_mount_namespace (OstreeSysroot *self, GCancellable *cancellable, GError **error);
_OSTREE_PUBLIC
GFile *ostree_sysroot_get_path (OstreeSysroot *self);

View File

@ -586,23 +586,9 @@ ostree_admin_sysroot_load (OstreeSysroot *sysroot,
{
if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0)
{
/* If we're requested to lock the sysroot, first check if we're operating
* on a booted (not physical) sysroot. Then find out if the /sysroot
* subdir is a read-only mount point, and if so, create a new mount
* namespace and tell the sysroot that we've done so. See the docs for
* ostree_sysroot_set_mount_namespace_in_use().
*
* This is a conservative approach; we could just always
* unshare() too.
*/
if (ostree_sysroot_is_booted (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);
}
/* Set up the mount namespace, if applicable */
if (!ostree_sysroot_initialize_with_mount_namespace (sysroot, cancellable, error))
return FALSE;
/* Released when sysroot is finalized, or on process exit */
if (!ot_admin_sysroot_lock (sysroot, error))