Skip to content
Snippets Groups Projects
Commit 16c06951 authored by Simon McVittie's avatar Simon McVittie
Browse files

Use FlatpakExports to make host paths available in container


This gives us access to the same tricks that Flatpak uses to get host
paths into the container in a more robust way: in particular, it sorts
paths to put parent before child, and exports symbolic links as symbolic
links plus a separate mount point for the target (which we previously
did for the subdirectories of ~/.steam, but nowhere else).

Resolves: pressure-vessel#2
Resolves: pressure-vessel#18
Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent 6772f82a
No related branches found
No related tags found
No related merge requests found
...@@ -36,6 +36,8 @@ typedef enum { ...@@ -36,6 +36,8 @@ typedef enum {
typedef struct _FlatpakExports FlatpakExports; typedef struct _FlatpakExports FlatpakExports;
extern const char *dont_export_in[];
void flatpak_exports_free (FlatpakExports *exports); void flatpak_exports_free (FlatpakExports *exports);
FlatpakExports *flatpak_exports_new (void); FlatpakExports *flatpak_exports_new (void);
void flatpak_exports_append_bwrap_args (FlatpakExports *exports, void flatpak_exports_append_bwrap_args (FlatpakExports *exports,
......
...@@ -243,13 +243,15 @@ check_flatpak_spawn (void) ...@@ -243,13 +243,15 @@ check_flatpak_spawn (void)
* on the real root filesystem. * on the real root filesystem.
*/ */
static gboolean static gboolean
export_root_dirs_like_filesystem_host (FlatpakBwrap *bwrap, export_root_dirs_like_filesystem_host (FlatpakExports *exports,
FlatpakFilesystemMode mode,
GError **error) GError **error)
{ {
g_autoptr(GDir) dir = NULL; g_autoptr(GDir) dir = NULL;
const char *member = 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); g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
dir = g_dir_open ("/", 0, error); dir = g_dir_open ("/", 0, error);
...@@ -267,16 +269,11 @@ export_root_dirs_like_filesystem_host (FlatpakBwrap *bwrap, ...@@ -267,16 +269,11 @@ export_root_dirs_like_filesystem_host (FlatpakBwrap *bwrap,
continue; continue;
path = g_build_filename ("/", member, NULL); path = g_build_filename ("/", member, NULL);
flatpak_bwrap_add_args (bwrap, flatpak_exports_add_path_expose (exports, mode, path);
"--bind", path, path,
NULL);
} }
/* For parity with Flatpak's handling of --filesystem=host */ /* For parity with Flatpak's handling of --filesystem=host */
if (g_file_test ("/run/media", G_FILE_TEST_EXISTS)) flatpak_exports_add_path_expose (exports, mode, "/run/media");
flatpak_bwrap_add_args (bwrap,
"--bind", "/run/media", "/run/media",
NULL);
return TRUE; return TRUE;
} }
...@@ -329,12 +326,20 @@ export_contents_of_run (FlatpakBwrap *bwrap, ...@@ -329,12 +326,20 @@ export_contents_of_run (FlatpakBwrap *bwrap,
} }
static void static void
bind_and_propagate_from_environ (const char *variable, bind_and_propagate_from_environ (FlatpakExports *exports,
FlatpakBwrap *bwrap) FlatpakBwrap *bwrap,
FlatpakFilesystemMode mode,
const char *variable)
{ {
g_autofree gchar *value_host = NULL; g_autofree gchar *value_host = NULL;
g_autofree gchar *canon = 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) if (value == NULL)
return; return;
...@@ -351,13 +356,7 @@ bind_and_propagate_from_environ (const char *variable, ...@@ -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", g_debug ("Bind-mounting %s=\"%s\" from the current env as %s=\"%s\" in the host",
variable, value, variable, value_host); variable, value, variable, value_host);
flatpak_exports_add_path_expose (exports, mode, canon);
/* 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);
if (strcmp (value, value_host) != 0) if (strcmp (value, value_host) != 0)
flatpak_bwrap_add_args (bwrap, flatpak_bwrap_add_args (bwrap,
...@@ -374,7 +373,8 @@ static const char * const steam_api_subdirs[] = ...@@ -374,7 +373,8 @@ static const char * const steam_api_subdirs[] =
}; };
static gboolean static gboolean
use_fake_home (FlatpakBwrap *bwrap, use_fake_home (FlatpakExports *exports,
FlatpakBwrap *bwrap,
const gchar *fake_home, const gchar *fake_home,
GError **error) GError **error)
{ {
...@@ -389,10 +389,10 @@ use_fake_home (FlatpakBwrap *bwrap, ...@@ -389,10 +389,10 @@ use_fake_home (FlatpakBwrap *bwrap,
g_autofree gchar *data2 = g_build_filename (fake_home, "data", NULL); g_autofree gchar *data2 = g_build_filename (fake_home, "data", NULL);
g_autofree gchar *steam_pid = NULL; g_autofree gchar *steam_pid = NULL;
g_autofree gchar *steam_pipe = NULL; g_autofree gchar *steam_pipe = NULL;
g_autoptr(GHashTable) mounted = NULL;
gsize i; gsize i;
g_return_val_if_fail (bwrap != NULL, FALSE); 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 (fake_home != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
...@@ -435,15 +435,15 @@ use_fake_home (FlatpakBwrap *bwrap, ...@@ -435,15 +435,15 @@ use_fake_home (FlatpakBwrap *bwrap,
flatpak_bwrap_add_args (bwrap, flatpak_bwrap_add_args (bwrap,
"--bind", fake_home, real_home, "--bind", fake_home, real_home,
"--bind", fake_home, fake_home,
"--bind", tmp, "/var/tmp", "--bind", tmp, "/var/tmp",
"--setenv", "XDG_CACHE_HOME", cache, "--setenv", "XDG_CACHE_HOME", cache,
"--setenv", "XDG_CONFIG_HOME", config, "--setenv", "XDG_CONFIG_HOME", config,
"--setenv", "XDG_DATA_HOME", data, "--setenv", "XDG_DATA_HOME", data,
NULL); NULL);
mounted = g_hash_table_new_full (g_str_hash, g_str_equal, flatpak_exports_add_path_expose (exports,
g_free, NULL); FLATPAK_FILESYSTEM_MODE_READ_WRITE,
fake_home);
/* /*
* These might be API entry points, according to Steam/steam.sh. * These might be API entry points, according to Steam/steam.sh.
...@@ -474,47 +474,92 @@ use_fake_home (FlatpakBwrap *bwrap, ...@@ -474,47 +474,92 @@ use_fake_home (FlatpakBwrap *bwrap,
/* Remove any symlinks that might have already been there. */ /* Remove any symlinks that might have already been there. */
if (unlink (mount_point) != 0 && errno != ENOENT) if (unlink (mount_point) != 0 && errno != ENOENT)
g_debug ("unlink %s: %s", mount_point, g_strerror (errno)); 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 */ /* steamclient.so relies on this for communication with Steam */
steam_pid = g_build_filename (real_home, ".steam", "steam.pid", NULL); steam_pid = g_build_filename (real_home, ".steam", "steam.pid", NULL);
flatpak_exports_add_path_expose (exports,
if (g_file_test (steam_pid, G_FILE_TEST_EXISTS)) FLATPAK_FILESYSTEM_MODE_READ_ONLY,
flatpak_bwrap_add_args (bwrap, steam_pid);
"--ro-bind", steam_pid, steam_pid,
NULL);
/* Make sure Steam IPC is available. /* Make sure Steam IPC is available.
* TODO: do we need this? do we need more? */ * TODO: do we need this? do we need more? */
steam_pipe = g_build_filename (real_home, ".steam", "steam.pipe", NULL); steam_pipe = g_build_filename (real_home, ".steam", "steam.pipe", NULL);
flatpak_exports_add_path_expose (exports,
if (g_file_test (steam_pipe, G_FILE_TEST_EXISTS)) FLATPAK_FILESYSTEM_MODE_READ_WRITE,
flatpak_bwrap_add_args (bwrap, steam_pipe);
"--bind", steam_pipe, steam_pipe,
NULL);
return TRUE; 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 typedef enum
{ {
TRISTATE_NO = 0, TRISTATE_NO = 0,
...@@ -969,6 +1014,8 @@ main (int argc, ...@@ -969,6 +1014,8 @@ main (int argc,
int original_argc = argc; int original_argc = argc;
gboolean is_flatpak_env = g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR); gboolean is_flatpak_env = g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR);
g_autoptr(FlatpakBwrap) bwrap = NULL; g_autoptr(FlatpakBwrap) bwrap = NULL;
g_autoptr(FlatpakBwrap) exports_bwrap = NULL;
g_autoptr(FlatpakExports) exports = NULL;
g_autoptr(FlatpakBwrap) adverb_args = NULL; g_autoptr(FlatpakBwrap) adverb_args = NULL;
g_autofree gchar *adverb_in_container = NULL; g_autofree gchar *adverb_in_container = NULL;
g_autoptr(FlatpakBwrap) wrapped_command = NULL; g_autoptr(FlatpakBwrap) wrapped_command = NULL;
...@@ -990,7 +1037,6 @@ main (int argc, ...@@ -990,7 +1037,6 @@ main (int argc,
"STEAM_COMPAT_DATA_PATH", "STEAM_COMPAT_DATA_PATH",
"STEAM_COMPAT_TOOL_PATH", "STEAM_COMPAT_TOOL_PATH",
}; };
const char *host_in_current_ns;
my_pid = getpid (); my_pid = getpid ();
...@@ -1258,11 +1304,6 @@ main (int argc, ...@@ -1258,11 +1304,6 @@ main (int argc,
* us exit 1. */ * us exit 1. */
ret = 1; ret = 1;
if (is_flatpak_env)
host_in_current_ns = "/run/host";
else
host_in_current_ns = "/";
if (opt_terminal != PV_TERMINAL_TTY) if (opt_terminal != PV_TERMINAL_TTY)
{ {
int fd; int fd;
...@@ -1463,6 +1504,21 @@ main (int argc, ...@@ -1463,6 +1504,21 @@ main (int argc,
bwrap = flatpak_bwrap_new (NULL); bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_arg (bwrap, bwrap_executable); 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 /* Protect the controlling terminal from the app/game, unless we are
* running an interactive shell in which case that would break its * running an interactive shell in which case that would break its
...@@ -1474,22 +1530,9 @@ main (int argc, ...@@ -1474,22 +1530,9 @@ main (int argc,
* and the standard API filesystems */ * and the standard API filesystems */
pv_bwrap_add_api_filesystems (bwrap); pv_bwrap_add_api_filesystems (bwrap);
if (!pv_bwrap_bind_usr (bwrap, "/", host_in_current_ns, "/run/host", error)) /* The FlatpakExports will populate /run/host for us */
goto out; flatpak_exports_add_host_os_expose (exports,
FLATPAK_FILESYSTEM_MODE_READ_ONLY);
/* 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);
/* steam-runtime-system-info uses this to detect pressure-vessel, so we /* steam-runtime-system-info uses this to detect pressure-vessel, so we
* need to create it even if it will be empty */ * need to create it even if it will be empty */
...@@ -1577,7 +1620,9 @@ main (int argc, ...@@ -1577,7 +1620,9 @@ main (int argc,
* (all handled by pv_bwrap_bind_usr() above) * (all handled by pv_bwrap_bind_usr() above)
* /var (handled by export_os_mutable) * /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; goto out;
} }
...@@ -1592,13 +1637,13 @@ main (int argc, ...@@ -1592,13 +1637,13 @@ main (int argc,
if (opt_fake_home == NULL) if (opt_fake_home == NULL)
{ {
flatpak_bwrap_add_args (bwrap, flatpak_exports_add_path_expose (exports,
"--bind", home, home, FLATPAK_FILESYSTEM_MODE_READ_WRITE,
NULL); home);
} }
else else
{ {
if (!use_fake_home (bwrap, opt_fake_home, error)) if (!use_fake_home (exports, bwrap, opt_fake_home, error))
goto out; goto out;
} }
...@@ -1655,9 +1700,9 @@ main (int argc, ...@@ -1655,9 +1700,9 @@ main (int argc,
} }
else else
{ {
flatpak_bwrap_add_args (bwrap, flatpak_exports_add_path_expose (exports,
"--ro-bind", preload, preload, FLATPAK_FILESYSTEM_MODE_READ_ONLY,
NULL); preload);
pv_search_path_append (adjusted_ld_preload, preload); pv_search_path_append (adjusted_ld_preload, preload);
} }
} }
...@@ -1684,7 +1729,9 @@ main (int argc, ...@@ -1684,7 +1729,9 @@ main (int argc,
g_debug ("Making Steam environment variables available if required..."); g_debug ("Making Steam environment variables available if required...");
for (i = 0; i < G_N_ELEMENTS (known_required_env); i++) 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 /* Make arbitrary filesystems available. This is not as complete as
* Flatpak yet. */ * Flatpak yet. */
...@@ -1698,10 +1745,9 @@ main (int argc, ...@@ -1698,10 +1745,9 @@ main (int argc,
g_assert (g_path_is_absolute (opt_filesystems[i])); g_assert (g_path_is_absolute (opt_filesystems[i]));
g_debug ("Bind-mounting \"%s\"", opt_filesystems[i]); g_debug ("Bind-mounting \"%s\"", opt_filesystems[i]);
flatpak_bwrap_add_args (bwrap, flatpak_exports_add_path_expose (exports,
"--bind", opt_filesystems[i], FLATPAK_FILESYSTEM_MODE_READ_WRITE,
opt_filesystems[i], opt_filesystems[i]);
NULL);
} }
} }
...@@ -1719,9 +1765,13 @@ main (int argc, ...@@ -1719,9 +1765,13 @@ main (int argc,
} }
else else
{ {
flatpak_bwrap_add_args (bwrap, /* If in Flatpak, we assume that cwd_p_host is visible in the
"--bind", cwd_p_host, cwd_p_host, * current namespace as well as in the host, because it's
NULL); * 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, flatpak_bwrap_add_args (bwrap,
...@@ -1746,12 +1796,9 @@ main (int argc, ...@@ -1746,12 +1796,9 @@ main (int argc,
g_assert (equals != NULL); g_assert (equals != NULL);
if (g_str_has_prefix (opt_env_if_host[i], "STEAM_RUNTIME=/")) if (g_str_has_prefix (opt_env_if_host[i], "STEAM_RUNTIME=/"))
{ flatpak_exports_add_path_expose (exports,
flatpak_bwrap_add_args (bwrap, FLATPAK_FILESYSTEM_MODE_READ_ONLY,
"--ro-bind", equals + 1, equals + 1);
equals + 1,
NULL);
}
*equals = '\0'; *equals = '\0';
/* We do this via --setenv instead of flatpak_bwrap_set_env() /* We do this via --setenv instead of flatpak_bwrap_set_env()
...@@ -1765,6 +1812,12 @@ main (int argc, ...@@ -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 /* We need to set up IPC rendezvous points relatively late, so that
* even if we are sharing /tmp via --filesystem=/tmp, we'll still * 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. */ * mount our own /tmp/.X11-unix over the top of the OS's. */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment