diff --git a/src/flatpak-exports-private.h b/src/flatpak-exports-private.h
index 6e382fa2d9fd4e5ac41a34ee7c3c6622390aecc6..18d5f6b2486aa4c9873b0049cb2952b0585525fd 100644
--- a/src/flatpak-exports-private.h
+++ b/src/flatpak-exports-private.h
@@ -36,6 +36,8 @@ typedef enum {
 
 typedef struct _FlatpakExports FlatpakExports;
 
+extern const char *dont_export_in[];
+
 void flatpak_exports_free (FlatpakExports *exports);
 FlatpakExports *flatpak_exports_new (void);
 void flatpak_exports_append_bwrap_args (FlatpakExports *exports,
diff --git a/src/wrap.c b/src/wrap.c
index f379bd11fef2ce6f87946d405b077ea9acf3351b..f4d3f24e8ab76a7928d35651406c4ba47b593784 100644
--- a/src/wrap.c
+++ b/src/wrap.c
@@ -243,13 +243,15 @@ check_flatpak_spawn (void)
  * on the real root filesystem.
  */
 static gboolean
-export_root_dirs_like_filesystem_host (FlatpakBwrap *bwrap,
+export_root_dirs_like_filesystem_host (FlatpakExports *exports,
+                                       FlatpakFilesystemMode mode,
                                        GError **error)
 {
   g_autoptr(GDir) dir = NULL;
   const char *member = NULL;
 
-  g_return_val_if_fail (bwrap != NULL, FALSE);
+  g_return_val_if_fail (exports != NULL, FALSE);
+  g_return_val_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   dir = g_dir_open ("/", 0, error);
@@ -267,16 +269,11 @@ export_root_dirs_like_filesystem_host (FlatpakBwrap *bwrap,
         continue;
 
       path = g_build_filename ("/", member, NULL);
-      flatpak_bwrap_add_args (bwrap,
-                              "--bind", path, path,
-                              NULL);
+      flatpak_exports_add_path_expose (exports, mode, path);
     }
 
   /* For parity with Flatpak's handling of --filesystem=host */
-  if (g_file_test ("/run/media", G_FILE_TEST_EXISTS))
-    flatpak_bwrap_add_args (bwrap,
-                            "--bind", "/run/media", "/run/media",
-                            NULL);
+  flatpak_exports_add_path_expose (exports, mode, "/run/media");
 
   return TRUE;
 }
@@ -329,12 +326,20 @@ export_contents_of_run (FlatpakBwrap *bwrap,
 }
 
 static void
-bind_and_propagate_from_environ (const char *variable,
-                                 FlatpakBwrap *bwrap)
+bind_and_propagate_from_environ (FlatpakExports *exports,
+                                 FlatpakBwrap *bwrap,
+                                 FlatpakFilesystemMode mode,
+                                 const char *variable)
 {
   g_autofree gchar *value_host = NULL;
   g_autofree gchar *canon = NULL;
-  const char *value = g_getenv (variable);
+  const char *value;
+
+  g_return_if_fail (exports != NULL);
+  g_return_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST);
+  g_return_if_fail (variable != NULL);
+
+  value = g_getenv (variable);
 
   if (value == NULL)
     return;
@@ -351,13 +356,7 @@ bind_and_propagate_from_environ (const char *variable,
 
   g_debug ("Bind-mounting %s=\"%s\" from the current env as %s=\"%s\" in the host",
            variable, value, variable, value_host);
-
-  /* TODO: If it's a symbolic link, ideally we should jump through the
-   * same hoops as Flatpak to bind-mount the *target* of the symlink
-   * instead, and then create the same symlink in the container. */
-  flatpak_bwrap_add_args (bwrap,
-                          "--bind", value_host, value_host,
-                          NULL);
+  flatpak_exports_add_path_expose (exports, mode, canon);
 
   if (strcmp (value, value_host) != 0)
     flatpak_bwrap_add_args (bwrap,
@@ -374,7 +373,8 @@ static const char * const steam_api_subdirs[] =
 };
 
 static gboolean
-use_fake_home (FlatpakBwrap *bwrap,
+use_fake_home (FlatpakExports *exports,
+               FlatpakBwrap *bwrap,
                const gchar *fake_home,
                GError **error)
 {
@@ -389,10 +389,10 @@ use_fake_home (FlatpakBwrap *bwrap,
   g_autofree gchar *data2 = g_build_filename (fake_home, "data", NULL);
   g_autofree gchar *steam_pid = NULL;
   g_autofree gchar *steam_pipe = NULL;
-  g_autoptr(GHashTable) mounted = NULL;
   gsize i;
 
   g_return_val_if_fail (bwrap != NULL, FALSE);
+  g_return_val_if_fail (exports != NULL, FALSE);
   g_return_val_if_fail (fake_home != NULL, FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
@@ -435,15 +435,15 @@ use_fake_home (FlatpakBwrap *bwrap,
 
   flatpak_bwrap_add_args (bwrap,
                           "--bind", fake_home, real_home,
-                          "--bind", fake_home, fake_home,
                           "--bind", tmp, "/var/tmp",
                           "--setenv", "XDG_CACHE_HOME", cache,
                           "--setenv", "XDG_CONFIG_HOME", config,
                           "--setenv", "XDG_DATA_HOME", data,
                           NULL);
 
-  mounted = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                   g_free, NULL);
+  flatpak_exports_add_path_expose (exports,
+                                   FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                   fake_home);
 
   /*
    * These might be API entry points, according to Steam/steam.sh.
@@ -474,47 +474,92 @@ use_fake_home (FlatpakBwrap *bwrap,
           /* Remove any symlinks that might have already been there. */
           if (unlink (mount_point) != 0 && errno != ENOENT)
             g_debug ("unlink %s: %s", mount_point, g_strerror (errno));
-
-          flatpak_bwrap_add_args (bwrap, "--symlink", target, dir, NULL);
-
-          if (strcmp (steam_api_subdirs[i], "root") == 0
-              || strcmp (steam_api_subdirs[i], "steam") == 0
-              || strcmp (steam_api_subdirs[i], "steambeta") == 0)
-            {
-              flatpak_bwrap_add_args (bwrap,
-                                      "--ro-bind", target, target,
-                                      NULL);
-              g_hash_table_add (mounted, g_steal_pointer (&target));
-            }
-        }
-      else if (g_file_test (dir, G_FILE_TEST_EXISTS) &&
-               !g_hash_table_contains (mounted, dir))
-        {
-          flatpak_bwrap_add_args (bwrap, "--ro-bind", dir, dir, NULL);
-          g_hash_table_add (mounted, g_steal_pointer (&dir));
         }
+
+      flatpak_exports_add_path_expose (exports,
+                                       FLATPAK_FILESYSTEM_MODE_READ_ONLY,
+                                       dir);
     }
 
   /* steamclient.so relies on this for communication with Steam */
   steam_pid = g_build_filename (real_home, ".steam", "steam.pid", NULL);
-
-  if (g_file_test (steam_pid, G_FILE_TEST_EXISTS))
-    flatpak_bwrap_add_args (bwrap,
-                            "--ro-bind", steam_pid, steam_pid,
-                            NULL);
+  flatpak_exports_add_path_expose (exports,
+                                   FLATPAK_FILESYSTEM_MODE_READ_ONLY,
+                                   steam_pid);
 
   /* Make sure Steam IPC is available.
    * TODO: do we need this? do we need more? */
   steam_pipe = g_build_filename (real_home, ".steam", "steam.pipe", NULL);
-
-  if (g_file_test (steam_pipe, G_FILE_TEST_EXISTS))
-    flatpak_bwrap_add_args (bwrap,
-                            "--bind", steam_pipe, steam_pipe,
-                            NULL);
+  flatpak_exports_add_path_expose (exports,
+                                   FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                   steam_pipe);
 
   return TRUE;
 }
 
+/*
+ * @bwrap: Arguments produced by flatpak_exports_append_bwrap_args(),
+ *  not including an executable name (the 0'th argument must be
+ *  `--bind` or similar)
+ * @home: The home directory
+ *
+ * Adjust arguments in @bwrap to cope with potentially running in a
+ * container.
+ */
+static void
+adjust_exports (FlatpakBwrap *bwrap,
+                const char *home)
+{
+  gsize i = 0;
+
+  while (i < bwrap->argv->len)
+    {
+      const char *opt = bwrap->argv->pdata[i];
+
+      g_assert (opt != NULL);
+
+      if (g_str_equal (opt, "--symlink"))
+        {
+          g_assert (i + 3 <= bwrap->argv->len);
+          /* pdata[i + 1] is the target: unchanged. */
+          /* pdata[i + 2] is a path in the final container: unchanged. */
+          i += 3;
+        }
+      else if (g_str_equal (opt, "--dir") ||
+               g_str_equal (opt, "--tmpfs"))
+        {
+          g_assert (i + 2 <= bwrap->argv->len);
+          /* pdata[i + 1] is a path in the final container: unchanged. */
+          i += 2;
+        }
+      else if (g_str_equal (opt, "--ro-bind") ||
+               g_str_equal (opt, "--bind"))
+        {
+          g_autofree gchar *src = NULL;
+
+          g_assert (i + 3 <= bwrap->argv->len);
+          src = g_steal_pointer (&bwrap->argv->pdata[i + 1]);
+          /* pdata[i + 2] is a path in the final container: unchanged. */
+
+          /* Paths in the home directory might need adjusting.
+           * Paths outside the home directory do not: if they're part of
+           * /run/host, they've been adjusted already by
+           * flatpak_exports_take_host_fd(), and if not, they appear in
+           * the container with the same path as on the host. */
+          if (flatpak_has_path_prefix (src, home))
+            bwrap->argv->pdata[i + 1] = pv_current_namespace_path_to_host_path (src);
+          else
+            bwrap->argv->pdata[i + 1] = g_steal_pointer (&src);
+
+          i += 3;
+        }
+      else
+        {
+          g_return_if_reached ();
+        }
+    }
+}
+
 typedef enum
 {
   TRISTATE_NO = 0,
@@ -969,6 +1014,8 @@ main (int argc,
   int original_argc = argc;
   gboolean is_flatpak_env = g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR);
   g_autoptr(FlatpakBwrap) bwrap = NULL;
+  g_autoptr(FlatpakBwrap) exports_bwrap = NULL;
+  g_autoptr(FlatpakExports) exports = NULL;
   g_autoptr(FlatpakBwrap) adverb_args = NULL;
   g_autofree gchar *adverb_in_container = NULL;
   g_autoptr(FlatpakBwrap) wrapped_command = NULL;
@@ -990,7 +1037,6 @@ main (int argc,
       "STEAM_COMPAT_DATA_PATH",
       "STEAM_COMPAT_TOOL_PATH",
     };
-  const char *host_in_current_ns;
 
   my_pid = getpid ();
 
@@ -1258,11 +1304,6 @@ main (int argc,
    * us exit 1. */
   ret = 1;
 
-  if (is_flatpak_env)
-    host_in_current_ns = "/run/host";
-  else
-    host_in_current_ns = "/";
-
   if (opt_terminal != PV_TERMINAL_TTY)
     {
       int fd;
@@ -1463,6 +1504,21 @@ main (int argc,
 
   bwrap = flatpak_bwrap_new (NULL);
   flatpak_bwrap_add_arg (bwrap, bwrap_executable);
+  exports = flatpak_exports_new ();
+
+  if (is_flatpak_env)
+    {
+      glnx_autofd int fd = TEMP_FAILURE_RETRY (open ("/run/host",
+                                                     O_CLOEXEC | O_PATH));
+
+      if (fd < 0)
+        {
+          glnx_throw_errno_prefix (error, "Unable to open /run/host");
+          goto out;
+        }
+
+      flatpak_exports_take_host_fd (exports, glnx_steal_fd (&fd));
+    }
 
   /* Protect the controlling terminal from the app/game, unless we are
    * running an interactive shell in which case that would break its
@@ -1474,22 +1530,9 @@ main (int argc,
    * and the standard API filesystems */
   pv_bwrap_add_api_filesystems (bwrap);
 
-  if (!pv_bwrap_bind_usr (bwrap, "/", host_in_current_ns, "/run/host", error))
-    goto out;
-
-  /* https://github.com/flatpak/flatpak/pull/3733 */
-  if (pv_file_test_in_sysroot (host_in_current_ns, "/etc/os-release",
-                               G_FILE_TEST_EXISTS))
-    flatpak_bwrap_add_args (bwrap,
-                            "--ro-bind", "/etc/os-release",
-                            "/run/host/os-release",
-                            NULL);
-  else if (pv_file_test_in_sysroot (host_in_current_ns, "/usr/lib/os-release",
-                               G_FILE_TEST_EXISTS))
-    flatpak_bwrap_add_args (bwrap,
-                            "--ro-bind", "/usr/lib/os-release",
-                            "/run/host/os-release",
-                            NULL);
+  /* The FlatpakExports will populate /run/host for us */
+  flatpak_exports_add_host_os_expose (exports,
+                                      FLATPAK_FILESYSTEM_MODE_READ_ONLY);
 
   /* steam-runtime-system-info uses this to detect pressure-vessel, so we
    * need to create it even if it will be empty */
@@ -1577,7 +1620,9 @@ main (int argc,
        *  (all handled by pv_bwrap_bind_usr() above)
        * /var (handled by export_os_mutable)
        */
-      if (!export_root_dirs_like_filesystem_host (bwrap, error))
+      if (!export_root_dirs_like_filesystem_host (exports,
+                                                  FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                                  error))
         goto out;
     }
 
@@ -1592,13 +1637,13 @@ main (int argc,
 
   if (opt_fake_home == NULL)
     {
-      flatpak_bwrap_add_args (bwrap,
-                              "--bind", home, home,
-                              NULL);
+      flatpak_exports_add_path_expose (exports,
+                                       FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                       home);
     }
   else
     {
-      if (!use_fake_home (bwrap, opt_fake_home, error))
+      if (!use_fake_home (exports, bwrap, opt_fake_home, error))
         goto out;
     }
 
@@ -1655,9 +1700,9 @@ main (int argc,
                 }
               else
                 {
-                  flatpak_bwrap_add_args (bwrap,
-                                          "--ro-bind", preload, preload,
-                                          NULL);
+                  flatpak_exports_add_path_expose (exports,
+                                                   FLATPAK_FILESYSTEM_MODE_READ_ONLY,
+                                                   preload);
                   pv_search_path_append (adjusted_ld_preload, preload);
                 }
             }
@@ -1684,7 +1729,9 @@ main (int argc,
 
   g_debug ("Making Steam environment variables available if required...");
   for (i = 0; i < G_N_ELEMENTS (known_required_env); i++)
-    bind_and_propagate_from_environ (known_required_env[i], bwrap);
+    bind_and_propagate_from_environ (exports, bwrap,
+                                     FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                     known_required_env[i]);
 
   /* Make arbitrary filesystems available. This is not as complete as
    * Flatpak yet. */
@@ -1698,10 +1745,9 @@ main (int argc,
           g_assert (g_path_is_absolute (opt_filesystems[i]));
 
           g_debug ("Bind-mounting \"%s\"", opt_filesystems[i]);
-          flatpak_bwrap_add_args (bwrap,
-                                  "--bind", opt_filesystems[i],
-                                  opt_filesystems[i],
-                                  NULL);
+          flatpak_exports_add_path_expose (exports,
+                                           FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                           opt_filesystems[i]);
         }
     }
 
@@ -1719,9 +1765,13 @@ main (int argc,
     }
   else
     {
-      flatpak_bwrap_add_args (bwrap,
-                              "--bind", cwd_p_host, cwd_p_host,
-                              NULL);
+      /* If in Flatpak, we assume that cwd_p_host is visible in the
+       * current namespace as well as in the host, because it's
+       * either in our ~/.var/app/$FLATPAK_ID, or a --filesystem that
+       * was exposed from the host. */
+      flatpak_exports_add_path_expose (exports,
+                                       FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                       cwd_p_host);
     }
 
   flatpak_bwrap_add_args (bwrap,
@@ -1746,12 +1796,9 @@ main (int argc,
               g_assert (equals != NULL);
 
               if (g_str_has_prefix (opt_env_if_host[i], "STEAM_RUNTIME=/"))
-                {
-                  flatpak_bwrap_add_args (bwrap,
-                                          "--ro-bind", equals + 1,
-                                          equals + 1,
-                                          NULL);
-                }
+                flatpak_exports_add_path_expose (exports,
+                                                 FLATPAK_FILESYSTEM_MODE_READ_ONLY,
+                                                 equals + 1);
 
               *equals = '\0';
               /* We do this via --setenv instead of flatpak_bwrap_set_env()
@@ -1765,6 +1812,12 @@ main (int argc,
         }
     }
 
+  /* Convert the exported directories into extra bubblewrap arguments */
+  exports_bwrap = flatpak_bwrap_new (flatpak_bwrap_empty_env);
+  flatpak_exports_append_bwrap_args (exports, exports_bwrap);
+  adjust_exports (exports_bwrap, home);
+  flatpak_bwrap_append_bwrap (bwrap, exports_bwrap);
+
   /* We need to set up IPC rendezvous points relatively late, so that
    * even if we are sharing /tmp via --filesystem=/tmp, we'll still
    * mount our own /tmp/.X11-unix over the top of the OS's. */