From 8881f9ccda5f0ee4fde6b8f94a8d61ca3da3dcee Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Wed, 21 Jul 2021 20:46:52 +0100
Subject: [PATCH] pv-runtime: Don't rewrite /nix/store/... to
 /run/host/nix/store/...

We must expose /nix in the sandbox as /nix, not /run/host/nix, because
hard-coding paths below /nix is ubiquitous on NixOS. There's already
a special case in wrap.c to mount /nix read-only.

This resolves a regression that occurred when we switched to a runtime
structure that relies on PRESSURE_VESSEL_COPY_RUNTIME.

Resolves: https://github.com/ValveSoftware/steam-runtime/issues/431
Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/runtime.c | 49 ++++++++++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 8 deletions(-)

diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c
index 89e950771..ba0b7c6bf 100644
--- a/pressure-vessel/runtime.c
+++ b/pressure-vessel/runtime.c
@@ -129,6 +129,26 @@ G_DEFINE_TYPE_WITH_CODE (PvRuntime, pv_runtime, G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                 pv_runtime_initable_iface_init))
 
+/*
+ * Return whether @path is likely to be visible as-is in the container.
+ */
+static gboolean
+path_visible_in_container_namespace (PvRuntimeFlags flags,
+                                     const char *path)
+{
+  while (path[0] == '/')
+    path++;
+
+  /* This is mounted in wrap.c as a special case: NixOS uses a lot of
+   * absolute paths in /nix/store which we need to make available. */
+  if (!(flags & PV_RUNTIME_FLAGS_FLATPAK_SUBSANDBOX)
+      && g_str_has_prefix (path, "nix")
+      && (path[3] == '\0' || path[3] == '/'))
+    return TRUE;
+
+  return FALSE;
+}
+
 /*
  * Return whether @path is likely to be visible in the provider mount point
  * (e.g. /run/host).
@@ -2911,7 +2931,19 @@ pv_runtime_take_from_provider (PvRuntime *self,
 
       /* If it isn't in /usr, /lib, etc., then the symlink will be
        * dangling and this probably isn't going to work. */
-      if (!path_visible_in_provider_namespace (self->flags, source_in_provider))
+      if (path_visible_in_provider_namespace (self->flags, source_in_provider))
+        {
+          target = g_build_filename (self->provider->path_in_container_ns,
+                                     source_in_provider, NULL);
+        }
+      /* A few paths are always available as-is in the container, such
+       * as /nix */
+      else if (path_visible_in_container_namespace (self->flags,
+                                                    source_in_provider))
+        {
+          target = g_build_filename ("/", source_in_provider, NULL);
+        }
+      else
         {
           if (flags & TAKE_FROM_PROVIDER_FLAGS_COPY_FALLBACK)
             {
@@ -2958,15 +2990,16 @@ pv_runtime_take_from_provider (PvRuntime *self,
 
               return TRUE;
             }
-          else
-            {
-              g_warning ("\"%s\" is unlikely to appear in \"%s\"",
-                         source_in_provider, self->provider->path_in_container_ns);
-              /* ... but try it anyway, it can't hurt */
-            }
+
+          g_warning ("\"%s\" is unlikely to appear in \"%s\"",
+                     source_in_provider, self->provider->path_in_container_ns);
+          /* ... but try it anyway, it can't hurt.
+           * TODO: Is "/" more likely to work than "/run/host" here? */
+          target = g_build_filename (self->provider->path_in_container_ns,
+                                     source_in_provider, NULL);
         }
 
-      target = g_build_filename (self->provider->path_in_container_ns, source_in_provider, NULL);
+      g_return_val_if_fail (target != NULL, FALSE);
 
       if (TEMP_FAILURE_RETRY (symlinkat (target, parent_dirfd, base)) != 0)
         return glnx_throw_errno_prefix (error,
-- 
GitLab