From 599ffefe7f069712d54f63f67e4a6f92570c554e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 21 Nov 2022 15:16:04 -0500 Subject: [PATCH] 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 --- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 1 + src/libostree/ostree-sysroot.c | 57 +++++++++++++++++++++++++++++++ src/libostree/ostree-sysroot.h | 3 ++ src/ostree/ot-main.c | 20 ++--------- 5 files changed, 65 insertions(+), 17 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 49f2748b..eb162dc4 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -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 diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 6a82433a..8f374bbe 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -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 diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index b15265f5..4c63a657 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -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 diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index 0cde9e44..23c7139a 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -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); diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c index 770962a4..da4735b6 100644 --- a/src/ostree/ot-main.c +++ b/src/ostree/ot-main.c @@ -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))