sysroot: Add an API to initialize with mountns
authorColin Walters <walters@verbum.org>
Mon, 21 Nov 2022 20:16:04 +0000 (15:16 -0500)
committerColin Walters <walters@verbum.org>
Tue, 22 Nov 2022 13:45:13 +0000 (08:45 -0500)
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
src/libostree/libostree-devel.sym
src/libostree/ostree-sysroot.c
src/libostree/ostree-sysroot.h
src/ostree/ot-main.c

index 49f2748b0f87d9c2903a43a27d432c01909ffa41..eb162dc45818b4eae9a291c605278469fa42fe50 100644 (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
index 6a82433a63243b72881d64ef6e9f752f1c595ae9..8f374bbeb7ae8856098d080fdad71854704593ae 100644 (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
index b15265f5f6b88e2dd56750562b6b6e1c68ce484e..4c63a657a39dacdae4bd946c08f502f66375d564 100644 (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
index 0cde9e4423e7581204fc4f6b33d93e0635fd14af..23c7139aaa759a312c41b91f512a67d49e967e42 100644 (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);
 
index 770962a43aef92bafe2cda84891c2a9406dfdce7..da4735b693912d3420e3e5fdeac951d6b39cf34f 100644 (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))