diff --git a/pressure-vessel/bwrap.c b/pressure-vessel/bwrap.c index 754b2ffcfe804b0a74f87e713fde8bf921f25ce8..99e47752a69ecf4f9738937316f9f7da620d82ad 100644 --- a/pressure-vessel/bwrap.c +++ b/pressure-vessel/bwrap.c @@ -23,7 +23,7 @@ #include "bwrap.h" -#include "resolve-in-sysroot.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" /** * pv_bwrap_run_sync: diff --git a/pressure-vessel/meson.build b/pressure-vessel/meson.build index 8281b56ddeaf66aaeb37d8ce0c6b46b3d3be143d..e6de0fb184a995359d7e9846ca478b87eab8e077 100644 --- a/pressure-vessel/meson.build +++ b/pressure-vessel/meson.build @@ -57,7 +57,6 @@ endforeach # Headers to scan for enum/flags types. headers = [ - 'resolve-in-sysroot.h', 'runtime.h', ] @@ -86,8 +85,6 @@ pressure_vessel_utils = static_library( 'flatpak-utils-base-private.h', 'flatpak-utils.c', 'flatpak-utils-private.h', - 'resolve-in-sysroot.c', - 'resolve-in-sysroot.h', 'tree-copy.c', 'tree-copy.h', 'utils.c', diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c index d449eda5d75905d57bf074121c12ad6ce4d1f121..b0ddafd4eed62617e112e5c921932f7741acd72f 100644 --- a/pressure-vessel/runtime.c +++ b/pressure-vessel/runtime.c @@ -29,13 +29,14 @@ #include "libglnx/libglnx.h" #include <steam-runtime-tools/steam-runtime-tools.h> +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" +#include "steam-runtime-tools/utils-internal.h" #include "bwrap.h" #include "bwrap-lock.h" #include "elf-utils.h" #include "enumtypes.h" #include "flatpak-run-private.h" -#include "resolve-in-sysroot.h" #include "tree-copy.h" #include "utils.h" @@ -1590,8 +1591,8 @@ bind_runtime (PvRuntime *self, /* If we are in a Flatpak environment, we need to test if these files are * available in the host, and not in the current environment, because we will * run bwrap in the host system */ - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/machine-id", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/machine-id", G_FILE_TEST_EXISTS)) { flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/machine-id", "/etc/machine-id", @@ -1602,8 +1603,9 @@ bind_runtime (PvRuntime *self, /* We leave this for completeness but in practice we do not expect to have * access to the "/var" host directory because Flatpak usually just binds * the host's "etc" and "usr". */ - else if (pv_file_test_in_sysroot (self->host_in_current_namespace, - "/var/lib/dbus/machine-id", G_FILE_TEST_EXISTS)) + else if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/var/lib/dbus/machine-id", + G_FILE_TEST_EXISTS)) { flatpak_bwrap_add_args (bwrap, "--ro-bind", "/var/lib/dbus/machine-id", @@ -1613,34 +1615,34 @@ bind_runtime (PvRuntime *self, NULL); } - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/resolv.conf", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/resolv.conf", G_FILE_TEST_EXISTS)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/resolv.conf", "/etc/resolv.conf", NULL); - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/host.conf", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/host.conf", G_FILE_TEST_EXISTS)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/host.conf", "/etc/host.conf", NULL); - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/hosts", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/hosts", G_FILE_TEST_EXISTS)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/hosts", "/etc/hosts", NULL); /* TODO: Synthesize a passwd with only the user and nobody, * like Flatpak does? */ - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/passwd", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/passwd", G_FILE_TEST_EXISTS)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/passwd", "/etc/passwd", NULL); - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/group", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/group", G_FILE_TEST_EXISTS)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/group", "/etc/group", NULL); @@ -1665,8 +1667,8 @@ bind_runtime (PvRuntime *self, * non-existing targets), in which case we don't want to attempt to create * bogus symlinks or bind mounts, as that will cause flatpak run to fail. */ - if (pv_file_test_in_sysroot (self->host_in_current_namespace, "/etc/localtime", - G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + "/etc/localtime", G_FILE_TEST_EXISTS)) { g_autofree char *target = NULL; gboolean is_reachable = FALSE; @@ -1734,15 +1736,15 @@ pv_runtime_take_from_provider (PvRuntime *self, if (flags & TAKE_FROM_PROVIDER_FLAGS_IF_DIR) { - if (!pv_file_test_in_sysroot (self->provider_in_current_namespace, - source_in_provider, G_FILE_TEST_IS_DIR)) + if (!_srt_file_test_in_sysroot (self->provider_in_current_namespace, -1, + source_in_provider, G_FILE_TEST_IS_DIR)) return TRUE; } if (flags & TAKE_FROM_PROVIDER_FLAGS_IF_EXISTS) { - if (!pv_file_test_in_sysroot (self->provider_in_current_namespace, - source_in_provider, G_FILE_TEST_EXISTS)) + if (!_srt_file_test_in_sysroot (self->provider_in_current_namespace, -1, + source_in_provider, G_FILE_TEST_EXISTS)) return TRUE; } @@ -1756,10 +1758,10 @@ pv_runtime_take_from_provider (PvRuntime *self, glnx_autofd int parent_dirfd = -1; parent_in_container = g_path_get_dirname (dest_in_container); - parent_dirfd = pv_resolve_in_sysroot (self->mutable_sysroot_fd, - parent_in_container, - PV_RESOLVE_FLAGS_MKDIR_P, - NULL, error); + parent_dirfd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, + parent_in_container, + SRT_RESOLVE_FLAGS_MKDIR_P, + NULL, error); if (parent_dirfd < 0) return FALSE; @@ -1783,9 +1785,9 @@ pv_runtime_take_from_provider (PvRuntime *self, FALSE, &sysroot_fd, error)) return FALSE; - file_fd = pv_resolve_in_sysroot (sysroot_fd, source_in_provider, - PV_RESOLVE_FLAGS_READABLE, - NULL, error); + file_fd = _srt_resolve_in_sysroot (sysroot_fd, source_in_provider, + SRT_RESOLVE_FLAGS_READABLE, + NULL, error); if (file_fd < 0) return FALSE; @@ -1904,10 +1906,10 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, { g_autoptr(GError) local_error = NULL; - libdir_fd = pv_resolve_in_sysroot (self->mutable_sysroot_fd, - multiarch_libdirs[i], - PV_RESOLVE_FLAGS_READABLE, - NULL, &local_error); + libdir_fd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, + multiarch_libdirs[i], + SRT_RESOLVE_FLAGS_READABLE, + NULL, &local_error); if (libdir_fd < 0) { @@ -2018,9 +2020,9 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, } } - libfd = pv_resolve_in_sysroot (self->mutable_sysroot_fd, path, - PV_RESOLVE_FLAGS_READABLE, NULL, - &local_error); + libfd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, path, + SRT_RESOLVE_FLAGS_READABLE, NULL, + &local_error); if (libfd < 0) { @@ -2120,9 +2122,9 @@ pv_runtime_take_ld_so_from_provider (PvRuntime *self, if (!glnx_opendirat (-1, self->provider_in_current_namespace, FALSE, &provider_fd, error)) return FALSE; - path_fd = pv_resolve_in_sysroot (provider_fd, - arch->ld_so, PV_RESOLVE_FLAGS_READABLE, - &ld_so_relative_to_provider, error); + path_fd = _srt_resolve_in_sysroot (provider_fd, + arch->ld_so, SRT_RESOLVE_FLAGS_READABLE, + &ld_so_relative_to_provider, error); if (path_fd < 0) return glnx_throw_errno_prefix (error, @@ -2192,8 +2194,8 @@ pv_runtime_search_in_path_and_bin (PvRuntime *self, g_autofree gchar *test_path = g_build_filename (common_bin_dirs[i], program_name, NULL); - if (pv_file_test_in_sysroot (self->host_in_current_namespace, - test_path, G_FILE_TEST_IS_EXECUTABLE)) + if (_srt_file_test_in_sysroot (self->host_in_current_namespace, -1, + test_path, G_FILE_TEST_IS_EXECUTABLE)) return g_steal_pointer (&test_path); } @@ -2341,11 +2343,11 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self, { glnx_autofd int fd = -1; - fd = pv_resolve_in_sysroot (self->mutable_sysroot_fd, - arch->ld_so, - PV_RESOLVE_FLAGS_NONE, - &ld_so_in_runtime, - error); + fd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, + arch->ld_so, + SRT_RESOLVE_FLAGS_NONE, + &ld_so_in_runtime, + error); if (fd < 0) return FALSE; @@ -2608,8 +2610,10 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self, gconv_dir_in_provider = g_build_filename ("/usr", dir, "gconv", NULL); - if (pv_file_test_in_sysroot (self->provider_in_current_namespace, - gconv_dir_in_provider, G_FILE_TEST_IS_DIR)) + if (_srt_file_test_in_sysroot (self->provider_in_current_namespace, + -1, + gconv_dir_in_provider, + G_FILE_TEST_IS_DIR)) { g_hash_table_add (gconv_in_provider, g_steal_pointer (&gconv_dir_in_provider)); found = TRUE; @@ -2638,8 +2642,10 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self, g_clear_pointer (&gconv_dir_in_provider, g_free); gconv_dir_in_provider = g_build_filename ("/usr", dir, "gconv", NULL); - if (pv_file_test_in_sysroot (self->provider_in_current_namespace, - gconv_dir_in_provider, G_FILE_TEST_IS_DIR)) + if (_srt_file_test_in_sysroot (self->provider_in_current_namespace, + -1, + gconv_dir_in_provider, + G_FILE_TEST_IS_DIR)) { g_hash_table_add (gconv_in_provider, g_steal_pointer (&gconv_dir_in_provider)); found = TRUE; @@ -2696,8 +2702,10 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self, libdrm_dir_in_provider = g_build_filename (dir, "share", "libdrm", NULL); - if (pv_file_test_in_sysroot (self->provider_in_current_namespace, - libdrm_dir_in_provider, G_FILE_TEST_IS_DIR)) + if (_srt_file_test_in_sysroot (self->provider_in_current_namespace, + -1, + libdrm_dir_in_provider, + G_FILE_TEST_IS_DIR)) { g_hash_table_add (libdrm_data_in_provider, g_steal_pointer (&libdrm_dir_in_provider)); @@ -3188,10 +3196,10 @@ pv_runtime_bind (PvRuntime *self, g_autofree gchar *dest = NULL; glnx_autofd int parent_dirfd = -1; - parent_dirfd = pv_resolve_in_sysroot (self->mutable_sysroot_fd, - "/usr/lib/pressure-vessel", - PV_RESOLVE_FLAGS_MKDIR_P, - NULL, error); + parent_dirfd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, + "/usr/lib/pressure-vessel", + SRT_RESOLVE_FLAGS_MKDIR_P, + NULL, error); if (parent_dirfd < 0) return FALSE; diff --git a/pressure-vessel/utils.c b/pressure-vessel/utils.c index 4236f3328078d93194c642dfb3fb6e989fd1a0ad..5c4add5b7ce75b9456a4325f07800ad8c1031dd9 100644 --- a/pressure-vessel/utils.c +++ b/pressure-vessel/utils.c @@ -36,12 +36,11 @@ #include "libglnx/libglnx.h" #include "steam-runtime-tools/glib-backports-internal.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" #include "flatpak-bwrap-private.h" #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" -#include "resolve-in-sysroot.h" - static void child_setup_cb (gpointer user_data) { @@ -920,74 +919,3 @@ pv_current_namespace_path_to_host_path (const gchar *current_env_path) return path_on_host; } - -/** - * pv_file_test_in_sysroot: - * @sysroot: (type filename): A path used as the root - * @filename: (type filename): A path below the root directory, either - * absolute or relative (to the root) - * @test: The test to perform on the resolved file in @sysroot. - * G_FILE_TEST_IS_SYMLINK is not a valid GFileTest value because the - * path is resolved following symlinks too. - * - * Returns: %TRUE if the @filename resolved in @sysroot passes the @test. - */ -gboolean -pv_file_test_in_sysroot (const char *sysroot, - const char *filename, - GFileTest test) -{ - glnx_autofd int file_fd = -1; - glnx_autofd int sysroot_fd = -1; - struct stat stat_buf; - g_autofree gchar *file_realpath_in_sysroot = NULL; - g_autoptr(GError) error = NULL; - - g_return_val_if_fail (sysroot != NULL, FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - /* We reject G_FILE_TEST_IS_SYMLINK because the provided filename is resolved - * in sysroot, following the eventual symlinks too. So it is not possible for - * the resolved filename to be a symlink */ - g_return_val_if_fail ((test & (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE - | G_FILE_TEST_IS_REGULAR - | G_FILE_TEST_IS_DIR)) == test, FALSE); - - if (!glnx_opendirat (-1, sysroot, FALSE, &sysroot_fd, &error)) - { - g_debug ("An error occurred trying to open %s: %s", sysroot, - error->message); - return FALSE; - } - - file_fd = pv_resolve_in_sysroot (sysroot_fd, - filename, PV_RESOLVE_FLAGS_NONE, - &file_realpath_in_sysroot, &error); - - if (file_fd < 0) - { - g_debug ("An error occurred trying to resolve %s in sysroot: %s", - filename, error->message); - return FALSE; - } - - if (fstat (file_fd, &stat_buf) != 0) - { - g_debug ("fstat %s/%s: %s", - sysroot, file_realpath_in_sysroot, g_strerror (errno)); - return FALSE; - } - - if (test & G_FILE_TEST_EXISTS) - return TRUE; - - if ((test & G_FILE_TEST_IS_EXECUTABLE) && (stat_buf.st_mode & 0111)) - return TRUE; - - if ((test & G_FILE_TEST_IS_REGULAR) && S_ISREG (stat_buf.st_mode)) - return TRUE; - - if ((test & G_FILE_TEST_IS_DIR) && S_ISDIR (stat_buf.st_mode)) - return TRUE; - - return FALSE; -} diff --git a/pressure-vessel/utils.h b/pressure-vessel/utils.h index 3809104722f8c3475c3160270e14e5ec695ae9f8..a30011e09a2c4189e5518be57603cd296b02bf37 100644 --- a/pressure-vessel/utils.h +++ b/pressure-vessel/utils.h @@ -71,7 +71,3 @@ gboolean pv_terminate_all_child_processes (GTimeSpan wait_period, GError **error); gchar *pv_current_namespace_path_to_host_path (const gchar *current_env_path); - -gboolean pv_file_test_in_sysroot (const char *sysroot, - const char *filename, - GFileTest test); diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c index 861c3a696a8e912d2b720539b24e087933338a81..d5e82151c667a0d6581d304c1d6c7a5d1eb2a0ff 100644 --- a/steam-runtime-tools/graphics.c +++ b/steam-runtime-tools/graphics.c @@ -1743,8 +1743,7 @@ srt_icd_write_to_file (const SrtIcd *self, /* * load_json_dir: - * @sysroot: (nullable): Interpret directory names as being inside this - * sysroot, mainly for unit testing + * @sysroot: (not nullable): The root directory, usually `/` * @dir: A directory to search * @suffix: (nullable): A path to append to @dir, such as `"vulkan/icd.d"` * @sort: (nullable): If not %NULL, load ICDs sorted by filename in this order @@ -1761,6 +1760,7 @@ load_json_dir (const char *sysroot, { g_autoptr(GError) error = NULL; g_autoptr(GDir) dir_iter = NULL; + g_autofree gchar *canon = NULL; g_autofree gchar *sysrooted_dir = NULL; g_autofree gchar *suffixed_dir = NULL; const char *iter_dir; @@ -1768,26 +1768,28 @@ load_json_dir (const char *sysroot, g_autoptr(GPtrArray) members = NULL; gsize i; + g_return_if_fail (sysroot != NULL); g_return_if_fail (load_json_cb != NULL); if (dir == NULL) return; + if (!g_path_is_absolute (dir)) + { + canon = g_canonicalize_filename (dir, NULL); + dir = canon; + } + if (suffix != NULL) { suffixed_dir = g_build_filename (dir, suffix, NULL); dir = suffixed_dir; } - iter_dir = dir; + sysrooted_dir = g_build_filename (sysroot, dir, NULL); + iter_dir = sysrooted_dir; - if (sysroot != NULL) - { - sysrooted_dir = g_build_filename (sysroot, dir, NULL); - iter_dir = sysrooted_dir; - } - - g_debug ("Looking for ICDs in %s...", dir); + g_debug ("Looking for ICDs in %s (in sysroot %s)...", dir, sysroot); dir_iter = g_dir_open (iter_dir, 0, &error); @@ -1823,8 +1825,7 @@ load_json_dir (const char *sysroot, /* * load_json_dir: - * @sysroot: (nullable): Interpret directory names as being inside this - * sysroot, mainly for unit testing + * @sysroot: (not nullable): The root directory, usually `/` * @search_paths: Directories to search * @suffix: (nullable): A path to append to @dir, such as `"vulkan/icd.d"` * @sort: (nullable): If not %NULL, load ICDs sorted by filename in this order @@ -1841,6 +1842,7 @@ load_json_dirs (const char *sysroot, { gchar **iter; + g_return_if_fail (sysroot != NULL); g_return_if_fail (load_json_cb != NULL); for (iter = search_paths; @@ -2322,8 +2324,7 @@ srt_egl_icd_get_library_path (SrtEglIcd *self) /* * egl_icd_load_json: - * @sysroot: Interpret the filename as being inside this sysroot, - * mainly for unit testing + * @sysroot: (not nullable): The root directory, usually `/` * @filename: The filename of the metadata * @list: (element-type SrtEglIcd) (inout): Prepend the * resulting #SrtEglIcd to this list @@ -2336,16 +2337,22 @@ egl_icd_load_json (const char *sysroot, GList **list) { g_autoptr(GError) error = NULL; + g_autofree gchar *canon = NULL; g_autofree gchar *in_sysroot = NULL; g_autofree gchar *library_path = NULL; + g_return_if_fail (sysroot != NULL); g_return_if_fail (list != NULL); - if (sysroot != NULL) - in_sysroot = g_build_filename (sysroot, filename, NULL); + if (!g_path_is_absolute (filename)) + { + canon = g_canonicalize_filename (filename, NULL); + filename = canon; + } + + in_sysroot = g_build_filename (sysroot, filename, NULL); - if (load_json (SRT_TYPE_EGL_ICD, - in_sysroot == NULL ? filename : in_sysroot, + if (load_json (SRT_TYPE_EGL_ICD, in_sysroot, NULL, &library_path, &error)) { g_assert (library_path != NULL); @@ -2473,7 +2480,7 @@ get_glvnd_datadir (void) /* * _srt_load_egl_icds: - * @sysroot: (nullable): Look in this directory instead of the real root + * @sysroot: (not nullable): The root directory, usually `/` * @envp: (array zero-terminated=1): Behave as though `environ` was this * array * @multiarch_tuples: (nullable): If not %NULL, and a Flatpak environment @@ -2497,6 +2504,7 @@ _srt_load_egl_icds (const char *sysroot, * then reverse it at the end. */ GList *ret = NULL; + g_return_val_if_fail (sysroot != NULL, NULL); g_return_val_if_fail (_srt_check_not_setuid (), NULL); if (envp == NULL) @@ -2527,8 +2535,7 @@ _srt_load_egl_icds (const char *sysroot, value = g_environ_getenv (envp, "__EGL_VENDOR_LIBRARY_DIRS"); - flatpak_info = g_build_filename (sysroot != NULL ? sysroot : "/", - ".flatpak-info", NULL); + flatpak_info = g_build_filename (sysroot, ".flatpak-info", NULL); if (value != NULL) { @@ -3288,7 +3295,7 @@ out: /** * _srt_get_modules_full: - * @sysroot: (nullable): Look in this directory instead of the real root + * @sysroot: (not nullable): The root directory, usually `/` * @envp: (array zero-terminated=1): Behave as though `environ` was this array * @helpers_path: (nullable): An optional path to find "inspect-library" helper, PATH is used if %NULL * @multiarch_tuple: (not nullable) (type filename): A Debian-style multiarch tuple @@ -3331,6 +3338,7 @@ _srt_get_modules_full (const char *sysroot, g_return_if_fail (multiarch_tuple != NULL); g_return_if_fail (drivers_out != NULL); + g_return_if_fail (sysroot != NULL); g_return_if_fail (_srt_check_not_setuid ()); switch (module) @@ -3369,9 +3377,6 @@ _srt_get_modules_full (const char *sysroot, ld_library_path = g_getenv ("LD_LIBRARY_PATH"); } - if (sysroot == NULL) - sysroot = "/"; - flatpak_info = g_build_filename (sysroot, ".flatpak-info", NULL); drivers_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); @@ -3661,7 +3666,7 @@ out: /* * _srt_list_glx_icds: - * @sysroot: (nullable): Look in this directory instead of the real root + * @sysroot: (not nullable): The root directory, usually `/` * @envp: (array zero-terminated=1): Behave as though `environ` was this array * @helpers_path: (nullable): An optional path to find "capsule-capture-libs" helper, * PATH is used if %NULL @@ -3687,6 +3692,7 @@ _srt_list_glx_icds (const char *sysroot, gchar *overrides_path = NULL; GHashTable *known_libs = NULL; + g_return_if_fail (sysroot != NULL); g_return_if_fail (multiarch_tuple != NULL); g_return_if_fail (drivers_out != NULL); g_return_if_fail (_srt_check_not_setuid ()); @@ -3713,7 +3719,7 @@ _srt_list_glx_icds (const char *sysroot, /* When in a container we might miss valid GLX drivers because the `ld.so.cache` in * use doesn't have a reference about them. To fix that we also include every * "libGLX_*.so.*" libraries that we find in the "/overrides/lib/${multiarch}" folder */ - overrides_path = sysroot ? g_build_filename (sysroot, "/overrides", NULL) : g_strdup ("/overrides"); + overrides_path = g_build_filename (sysroot, "/overrides", NULL); if (g_file_test (overrides_path, G_FILE_TEST_IS_DIR)) { overrides_tmp_dir = g_dir_make_tmp ("glx-icds-XXXXXX", &error); @@ -3757,7 +3763,7 @@ out: /** * _srt_list_graphics_modules: - * @sysroot: (nullable): Look in this directory instead of the real root + * @sysroot: (not nullable): The root directory, usually `/` * @envp: (array zero-terminated=1): Behave as though `environ` was this array * @helpers_path: (nullable): An optional path to find "inspect-library" helper, PATH is used if %NULL * @multiarch_tuple: (not nullable) (type filename): A Debian-style multiarch tuple @@ -3786,6 +3792,7 @@ _srt_list_graphics_modules (const char *sysroot, { GList *drivers = NULL; + g_return_val_if_fail (sysroot != NULL, NULL); g_return_val_if_fail (multiarch_tuple != NULL, NULL); if (which == SRT_GRAPHICS_GLX_MODULE) @@ -4747,8 +4754,7 @@ srt_vulkan_icd_new_replace_library_path (SrtVulkanIcd *self, /* * vulkan_icd_load_json: - * @sysroot: Interpret the filename as being inside this sysroot, - * mainly for unit testing + * @sysroot: (not nullable): The root directory, usually `/` * @filename: The filename of the metadata * @list: (element-type SrtVulkanIcd) (inout): Prepend the * resulting #SrtVulkanIcd to this list @@ -4761,17 +4767,22 @@ vulkan_icd_load_json (const char *sysroot, GList **list) { g_autoptr(GError) error = NULL; + g_autofree gchar *canon = NULL; g_autofree gchar *in_sysroot = NULL; g_autofree gchar *api_version = NULL; g_autofree gchar *library_path = NULL; g_return_if_fail (list != NULL); - if (sysroot != NULL) - in_sysroot = g_build_filename (sysroot, filename, NULL); + if (!g_path_is_absolute (filename)) + { + canon = g_canonicalize_filename (filename, NULL); + filename = canon; + } + + in_sysroot = g_build_filename (sysroot, filename, NULL); - if (load_json (SRT_TYPE_VULKAN_ICD, - in_sysroot == NULL ? filename : in_sysroot, + if (load_json (SRT_TYPE_VULKAN_ICD, in_sysroot, &api_version, &library_path, &error)) { g_assert (api_version != NULL); @@ -4814,7 +4825,7 @@ get_vulkan_sysconfdir (void) /* * _srt_load_vulkan_icds: - * @sysroot: (nullable): Look in this directory instead of the real root + * @sysroot: (not nullable): The root directory, usually `/` * @envp: (array zero-terminated=1): Behave as though `environ` was this * array * @multiarch_tuples: (nullable): If not %NULL, and a Flatpak environment @@ -4898,8 +4909,7 @@ _srt_load_vulkan_icds (const char *sysroot, load_json_dir (sysroot, "/etc", VULKAN_ICD_SUFFIX, READDIR_ORDER, vulkan_icd_load_json_cb, &ret); - flatpak_info = g_build_filename (sysroot != NULL ? sysroot : "/", - ".flatpak-info", NULL); + flatpak_info = g_build_filename (sysroot, ".flatpak-info", NULL); /* freedesktop-sdk patches the Vulkan loader to look here. */ if (g_file_test (flatpak_info, G_FILE_TEST_EXISTS) diff --git a/steam-runtime-tools/meson.build b/steam-runtime-tools/meson.build index 86e8b85cceaf0df574ade38a861e36b48931d867..4030b0052060581c6467eaf4ab7ec9777ad47163 100644 --- a/steam-runtime-tools/meson.build +++ b/steam-runtime-tools/meson.build @@ -38,6 +38,8 @@ libsteamrt_sources = [ 'locale.c', 'os-internal.h', 'os.c', + 'resolve-in-sysroot-internal.h', + 'resolve-in-sysroot.c', 'runtime-internal.h', 'runtime.c', 'steam-internal.h', diff --git a/steam-runtime-tools/os-internal.h b/steam-runtime-tools/os-internal.h index 01cd0cfa567762b0c8bcb8e3bba7322397fd3d27..959b184e7d2d96d2e8e2b1bf30fea42cd2559be6 100644 --- a/steam-runtime-tools/os-internal.h +++ b/steam-runtime-tools/os-internal.h @@ -46,7 +46,8 @@ typedef struct G_GNUC_INTERNAL void _srt_os_release_init (SrtOsRelease *self); G_GNUC_INTERNAL void _srt_os_release_populate (SrtOsRelease *self, - const char *sysroot); + const char *sysroot, + int sysroot_fd); G_GNUC_INTERNAL void _srt_os_release_clear (SrtOsRelease *self); void _srt_os_release_populate_from_report (JsonObject *json_obj, SrtOsRelease *self); diff --git a/steam-runtime-tools/os.c b/steam-runtime-tools/os.c index e408e5430175646949ff35aec5d9e380aba0e68e..3f0a4a00a52a4777d3ea8864e89ae534ec3bc2a3 100644 --- a/steam-runtime-tools/os.c +++ b/steam-runtime-tools/os.c @@ -141,7 +141,8 @@ do_line (SrtOsRelease *self, G_GNUC_INTERNAL void _srt_os_release_populate (SrtOsRelease *self, - const char *sysroot) + const char *sysroot, + int sysroot_fd) { gsize i; @@ -155,33 +156,28 @@ _srt_os_release_populate (SrtOsRelease *self, g_return_if_fail (self->variant_id == NULL); g_return_if_fail (self->version_codename == NULL); g_return_if_fail (self->version_id == NULL); + g_return_if_fail (sysroot != NULL); + g_return_if_fail (sysroot_fd >= 0); for (i = 0; i < G_N_ELEMENTS (os_release_paths); i++) { + g_autoptr(GError) local_error = NULL; const char *path = os_release_paths[i].path; gboolean only_in_run_host = os_release_paths[i].only_in_run_host; - gchar *built_path = NULL; - gchar *contents = NULL; + g_autofree gchar *contents = NULL; char *beginning_of_line; - GError *local_error = NULL; gsize len; gsize j; if (only_in_run_host - && (sysroot == NULL || !g_str_has_suffix (sysroot, "/run/host"))) + && !g_str_has_suffix (sysroot, "/run/host")) continue; - if (sysroot != NULL) + if (!_srt_file_get_contents_in_sysroot (sysroot_fd, path, + &contents, &len, + &local_error)) { - built_path = g_build_filename (sysroot, path, NULL); - path = built_path; - } - - if (!g_file_get_contents (path, &contents, &len, &local_error)) - { - g_debug ("Unable to open %s: %s", path, local_error->message); - g_clear_error (&local_error); - g_free (built_path); + g_debug ("%s", local_error->message); continue; } @@ -201,9 +197,6 @@ _srt_os_release_populate (SrtOsRelease *self, /* Collect a possible partial line */ do_line (self, path, beginning_of_line); - - g_free (contents); - g_free (built_path); break; } diff --git a/pressure-vessel/resolve-in-sysroot.h b/steam-runtime-tools/resolve-in-sysroot-internal.h similarity index 51% rename from pressure-vessel/resolve-in-sysroot.h rename to steam-runtime-tools/resolve-in-sysroot-internal.h index 2941030c63d2328301b0e1df4fe80541d3bcf17a..7d47024e76d347b474ebdea332486d4dbc93a10f 100644 --- a/pressure-vessel/resolve-in-sysroot.h +++ b/steam-runtime-tools/resolve-in-sysroot-internal.h @@ -22,31 +22,35 @@ #include <glib.h> /* - * PvResolveFlags: - * @PV_RESOLVE_FLAGS_MKDIR_P: Create the filename to be resolved and + * SrtResolveFlags: + * @SRT_RESOLVE_FLAGS_MKDIR_P: Create the filename to be resolved and * all of its ancestors as directories. If any already exist, they * must be directories or symlinks to directories. - * @PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK: If the last component of + * @SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK: If the last component of * the path is a symlink, return a fd pointing to the symlink itself. - * @PV_RESOLVE_FLAGS_REJECT_SYMLINKS: If any component of + * @SRT_RESOLVE_FLAGS_REJECT_SYMLINKS: If any component of * the path is a symlink, fail with %G_IO_ERROR_TOO_MANY_LINKS. - * @PV_RESOLVE_FLAGS_READABLE: Open the last component of the path + * @SRT_RESOLVE_FLAGS_READABLE: Open the last component of the path * for reading, instead of just as `O_PATH`. - * @PV_RESOLVE_FLAGS_NONE: No special behaviour. + * @SRT_RESOLVE_FLAGS_DIRECTORY: Open the last component of the path + * for reading, and it must be a directory. + * @SRT_RESOLVE_FLAGS_NONE: No special behaviour. * - * Flags affecting how pv_resolve_in_sysroot() behaves. + * Flags affecting how _srt_resolve_in_sysroot() behaves. */ typedef enum { - PV_RESOLVE_FLAGS_MKDIR_P = (1 << 0), - PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK = (1 << 1), - PV_RESOLVE_FLAGS_REJECT_SYMLINKS = (1 << 2), - PV_RESOLVE_FLAGS_READABLE = (1 << 3), - PV_RESOLVE_FLAGS_NONE = 0 -} PvResolveFlags; + SRT_RESOLVE_FLAGS_MKDIR_P = (1 << 0), + SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK = (1 << 1), + SRT_RESOLVE_FLAGS_REJECT_SYMLINKS = (1 << 2), + SRT_RESOLVE_FLAGS_READABLE = (1 << 3), + SRT_RESOLVE_FLAGS_DIRECTORY = (1 << 4), + SRT_RESOLVE_FLAGS_NONE = 0 +} SrtResolveFlags; -int pv_resolve_in_sysroot (int sysroot, - const char *descendant, - PvResolveFlags flags, - gchar **real_path_out, - GError **error) G_GNUC_WARN_UNUSED_RESULT; +G_GNUC_INTERNAL +int _srt_resolve_in_sysroot (int sysroot, + const char *descendant, + SrtResolveFlags flags, + gchar **real_path_out, + GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/pressure-vessel/resolve-in-sysroot.c b/steam-runtime-tools/resolve-in-sysroot.c similarity index 91% rename from pressure-vessel/resolve-in-sysroot.c rename to steam-runtime-tools/resolve-in-sysroot.c index 58a74bc7702ca5f4faf5a21a143f6cd220d455b7..21965157ee34bc16262527ab77dfc6487fe2496e 100644 --- a/pressure-vessel/resolve-in-sysroot.c +++ b/steam-runtime-tools/resolve-in-sysroot.c @@ -17,7 +17,7 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ -#include "resolve-in-sysroot.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" #include <glib.h> #include <glib/gstdio.h> @@ -67,7 +67,7 @@ fd_array_take (GArray *fds, } /* - * pv_resolve_in_sysroot: + * _srt_resolve_in_sysroot: * @sysroot: (transfer none): A file descriptor representing the root * @descendant: (type filename): A path below the root directory, either * absolute or relative (to the root) @@ -78,7 +78,7 @@ fd_array_take (GArray *fds, * * Open @descendant as though @sysroot was the root directory. * - * If %PV_RESOLVE_FLAGS_MKDIR_P is in @flags, each path segment in + * If %SRT_RESOLVE_FLAGS_MKDIR_P is in @flags, each path segment in * @descendant must be a directory, a symbolic link to a directory, * or nonexistent (in which case a directory will be created, currently * with hard-coded 0700 permissions). @@ -87,11 +87,11 @@ fd_array_take (GArray *fds, * or -1 on error */ int -pv_resolve_in_sysroot (int sysroot, - const char *descendant, - PvResolveFlags flags, - gchar **real_path_out, - GError **error) +_srt_resolve_in_sysroot (int sysroot, + const char *descendant, + SrtResolveFlags flags, + gchar **real_path_out, + GError **error) { g_autoptr(GString) current_path = g_string_new (""); /* Array of fds pointing to directories beneath @sysroot. @@ -196,7 +196,7 @@ pv_resolve_in_sysroot (int sysroot, fd = TEMP_FAILURE_RETRY (openat (g_array_index (fds, int, fds->len - 1), next, open_flags)); - if (fd < 0 && errno == ENOENT && (flags & PV_RESOLVE_FLAGS_MKDIR_P) != 0) + if (fd < 0 && errno == ENOENT && (flags & SRT_RESOLVE_FLAGS_MKDIR_P) != 0) { if (TEMP_FAILURE_RETRY (mkdirat (g_array_index (fds, int, fds->len - 1), next, 0700)) != 0) @@ -225,13 +225,13 @@ pv_resolve_in_sysroot (int sysroot, if (target != NULL) /* Yes, it's a symlink */ { - if (flags & PV_RESOLVE_FLAGS_REJECT_SYMLINKS) + if (flags & SRT_RESOLVE_FLAGS_REJECT_SYMLINKS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS, "\"%s/%s\" is a symlink", current_path->str, next); return -1; } - else if ((flags & PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK) != 0 && + else if ((flags & SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK) != 0 && remaining == NULL) { /* Treat as though not a symlink. */ @@ -273,7 +273,7 @@ pv_resolve_in_sysroot (int sysroot, { /* If we are emulating mkdir -p, or if we will go on to open * a member of @fd, then it had better be a directory. */ - if ((flags & PV_RESOLVE_FLAGS_MKDIR_P) != 0 || + if ((flags & SRT_RESOLVE_FLAGS_MKDIR_P) != 0 || remaining != NULL) { struct stat stat_buf; @@ -303,15 +303,23 @@ pv_resolve_in_sysroot (int sysroot, } } - if (flags & PV_RESOLVE_FLAGS_READABLE) + if (flags & (SRT_RESOLVE_FLAGS_READABLE|SRT_RESOLVE_FLAGS_DIRECTORY)) { g_autofree char *proc_fd_name = g_strdup_printf ("/proc/self/fd/%d", g_array_index (fds, int, fds->len - 1)); glnx_autofd int fd = -1; - if (!glnx_openat_rdonly (-1, proc_fd_name, TRUE, &fd, error)) - return -1; + if (flags & SRT_RESOLVE_FLAGS_DIRECTORY) + { + if (!glnx_opendirat (-1, proc_fd_name, TRUE, &fd, error)) + return -1; + } + else + { + if (!glnx_openat_rdonly (-1, proc_fd_name, TRUE, &fd, error)) + return -1; + } if (real_path_out != NULL) *real_path_out = g_string_free (g_steal_pointer (¤t_path), FALSE); diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c index a4fab61ece18277dccfbb21fd15e7b5d0c91ceae..770c701d059d89ff11e569af1ad4613377689bdf 100644 --- a/steam-runtime-tools/system-info.c +++ b/steam-runtime-tools/system-info.c @@ -35,6 +35,7 @@ #include "steam-runtime-tools/library-internal.h" #include "steam-runtime-tools/locale-internal.h" #include "steam-runtime-tools/os-internal.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" #include "steam-runtime-tools/runtime-internal.h" #include "steam-runtime-tools/steam-internal.h" #include "steam-runtime-tools/utils-internal.h" @@ -95,7 +96,7 @@ struct _SrtSystemInfo GObject parent; /* "" if we have tried and failed to auto-detect */ gchar *expectations; - /* Fake root directory, or %NULL to use the real root */ + /* Root directory to inspect, usually "/" */ gchar *sysroot; /* Fake environment variables, or %NULL to use the real environment */ gchar **env; @@ -170,6 +171,7 @@ struct _SrtSystemInfo gchar **cached_driver_environment; /* (element-type Abi) */ GPtrArray *abis; + int sysroot_fd; }; struct _SrtSystemInfoClass @@ -310,6 +312,7 @@ static void srt_system_info_init (SrtSystemInfo *self) { self->can_write_uinput = TRI_MAYBE; + self->sysroot_fd = -1; /* Assume that in practice we will usually add two ABIs: amd64 and i386 */ self->abis = g_ptr_array_new_full (2, abi_free); @@ -317,6 +320,8 @@ srt_system_info_init (SrtSystemInfo *self) _srt_os_release_init (&self->os_release); self->container.type = SRT_CONTAINER_TYPE_UNKNOWN; + + srt_system_info_set_sysroot (self, "/"); } static void @@ -480,6 +485,7 @@ srt_system_info_finalize (GObject *object) g_free (self->expectations); g_free (self->helpers_path); g_free (self->sysroot); + glnx_close_fd (&self->sysroot_fd); g_strfreev (self->env); g_clear_pointer (&self->cached_driver_environment, g_strfreev); if (self->cached_hidden_deps) @@ -1024,7 +1030,7 @@ ensure_overrides_cached (SrtSystemInfo *self) if (g_strcmp0 (runtime, "/") != 0) goto out; - if (!g_spawn_sync (self->sysroot == NULL ? "/" : self->sysroot, /* working directory */ + if (!g_spawn_sync (self->sysroot, /* working directory */ (gchar **) argv, get_environ (self), G_SPAWN_SEARCH_PATH, @@ -1908,9 +1914,14 @@ void srt_system_info_set_sysroot (SrtSystemInfo *self, const char *root) { + g_autoptr(GError) local_error = NULL; + g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); g_return_if_fail (!self->immutable_values); + if (root == NULL) + root = "/"; + forget_container_info (self); forget_graphics_modules (self); forget_libraries (self); @@ -1919,7 +1930,19 @@ srt_system_info_set_sysroot (SrtSystemInfo *self, forget_os (self); forget_overrides (self); g_free (self->sysroot); + glnx_close_fd (&self->sysroot_fd); + self->sysroot = g_strdup (root); + + if (!glnx_opendirat (-1, + self->sysroot, + FALSE, + &self->sysroot_fd, + &local_error)) + { + g_debug ("Unable to open sysroot %s: %s", + self->sysroot, local_error->message); + } } static void @@ -2078,8 +2101,11 @@ srt_system_info_dup_steam_bin32_path (SrtSystemInfo *self) static void ensure_os_cached (SrtSystemInfo *self) { - if (!self->os_release.populated && !self->immutable_values) - _srt_os_release_populate (&self->os_release, self->sysroot); + if (!self->os_release.populated + && !self->immutable_values + && self->sysroot_fd >= 0) + _srt_os_release_populate (&self->os_release, self->sysroot, + self->sysroot_fd); } /** @@ -3215,114 +3241,98 @@ container_type_from_name (const char *name) static void ensure_container_info (SrtSystemInfo *self) { - const char *sysroot = NULL; - gchar *contents = NULL; - gchar *filename = NULL; + g_autofree gchar *contents = NULL; + g_autofree gchar *run_host_path = NULL; + glnx_autofd int run_host_fd = -1; if (self->container.have_data || self->immutable_values) return; g_assert (self->container.host_directory == NULL); g_assert (self->container.type == SRT_CONTAINER_TYPE_UNKNOWN); + g_assert (self->sysroot != NULL); - sysroot = self->sysroot; + if (self->sysroot_fd < 0) + { + g_debug ("Cannot find container info: previously failed to open " + "sysroot %s", self->sysroot); + goto out; + } - if (sysroot == NULL) - sysroot = "/"; + g_debug ("Finding container info in sysroot %s...", self->sysroot); - g_debug ("Finding container info in sysroot %s...", sysroot); + run_host_fd = _srt_resolve_in_sysroot (self->sysroot_fd, "/run/host", + SRT_RESOLVE_FLAGS_DIRECTORY, + &run_host_path, NULL); - filename = g_build_filename (sysroot, "run", "systemd", "container", NULL); + if (run_host_path != NULL) + self->container.host_directory = g_build_filename (self->sysroot, + run_host_path, + NULL); - if (g_file_get_contents (filename, &contents, NULL, NULL)) + if (_srt_file_get_contents_in_sysroot (self->sysroot_fd, + "/run/systemd/container", + &contents, NULL, NULL)) { g_strchomp (contents); self->container.type = container_type_from_name (contents); - g_debug ("Type %d based on %s", self->container.type, filename); + g_debug ("Type %d based on /run/systemd/container", + self->container.type); goto out; } - g_clear_pointer (&filename, g_free); - filename = g_build_filename (sysroot, ".flatpak-info", NULL); - - if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + if (_srt_file_test_in_sysroot (self->sysroot, self->sysroot_fd, + "/.flatpak-info", G_FILE_TEST_IS_REGULAR)) { self->container.type = SRT_CONTAINER_TYPE_FLATPAK; - g_debug ("Flatpak based on %s", filename); + g_debug ("Flatpak based on /.flatpak-info"); goto out; } - g_clear_pointer (&filename, g_free); - filename = g_build_filename (sysroot, "run", "pressure-vessel", NULL); - - if (g_file_test (filename, G_FILE_TEST_IS_DIR)) + if (_srt_file_test_in_sysroot (self->sysroot, self->sysroot_fd, + "/run/pressure-vessel", G_FILE_TEST_IS_DIR)) { self->container.type = SRT_CONTAINER_TYPE_PRESSURE_VESSEL; - g_debug ("pressure-vessel based on %s", filename); + g_debug ("pressure-vessel based on /run/pressure-vessel"); goto out; } - g_clear_pointer (&filename, g_free); - filename = g_build_filename (sysroot, ".dockerenv", NULL); - - if (g_file_test (filename, G_FILE_TEST_EXISTS)) + if (_srt_file_test_in_sysroot (self->sysroot, self->sysroot_fd, + "/.dockerenv", G_FILE_TEST_EXISTS)) { self->container.type = SRT_CONTAINER_TYPE_DOCKER; - g_debug ("Docker based on %s", filename); + g_debug ("Docker based on /.dockerenv"); goto out; } - g_clear_pointer (&filename, g_free); - filename = g_build_filename (sysroot, "proc", "1", "cgroup", NULL); - - if (g_file_get_contents (filename, &contents, NULL, NULL)) + if (_srt_file_get_contents_in_sysroot (self->sysroot_fd, + "/proc/1/cgroup", + &contents, NULL, NULL)) { if (strstr (contents, "/docker/") != NULL) self->container.type = SRT_CONTAINER_TYPE_DOCKER; if (self->container.type != SRT_CONTAINER_TYPE_UNKNOWN) { - g_debug ("Type %d based on %s", self->container.type, filename); + g_debug ("Type %d based on /proc/1/cgroup", self->container.type); goto out; } - } - g_clear_pointer (&filename, g_free); - filename = g_build_filename (sysroot, "run", "host", NULL); + g_clear_pointer (&contents, g_free); + } - if (g_file_test (filename, G_FILE_TEST_IS_DIR)) + if (run_host_fd >= 0) { - g_debug ("Unknown container technology based on %s", filename); + g_debug ("Unknown container technology based on /run/host"); self->container.type = SRT_CONTAINER_TYPE_UNKNOWN; - self->container.host_directory = g_steal_pointer (&filename); goto out; } - g_clear_pointer (&filename, g_free); - /* We haven't found any particular evidence of being in a container */ g_debug ("Probably not a container"); self->container.type = SRT_CONTAINER_TYPE_NONE; out: - g_free (contents); - g_free (filename); - - switch (self->container.type) - { - case SRT_CONTAINER_TYPE_FLATPAK: - case SRT_CONTAINER_TYPE_PRESSURE_VESSEL: - self->container.host_directory = g_build_filename (sysroot, "run", - "host", NULL); - break; - - case SRT_CONTAINER_TYPE_DOCKER: - case SRT_CONTAINER_TYPE_UNKNOWN: - case SRT_CONTAINER_TYPE_NONE: - default: - break; - } - self->container.have_data = TRUE; } diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h index 308e60a1b9ec084a34820ab3d0ae549ae775e423..dc5d7c92cca8997275e46ec252244b0dabdb5c60 100644 --- a/steam-runtime-tools/utils-internal.h +++ b/steam-runtime-tools/utils-internal.h @@ -87,3 +87,16 @@ gchar ** _srt_json_array_to_strv (JsonObject *json_obj, gboolean _srt_rm_rf (const char *directory); FILE *_srt_divert_stdout_to_stderr (GError **error); + +G_GNUC_INTERNAL +gboolean _srt_file_get_contents_in_sysroot (int sysroot_fd, + const char *path, + gchar **contents, + gsize *len, + GError **error); + +G_GNUC_INTERNAL +gboolean _srt_file_test_in_sysroot (const char *sysroot, + int sysroot_fd, + const char *filename, + GFileTest test); diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c index cd059711b13c33d3820ee7a33a99befa83287921..223a0656518c9845842bd2893e9d211d248717f5 100644 --- a/steam-runtime-tools/utils.c +++ b/steam-runtime-tools/utils.c @@ -44,6 +44,7 @@ #include <glib-object.h> #include <gio/gio.h> #include "steam-runtime-tools/glib-backports-internal.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" #ifdef HAVE_GETAUXVAL #define getauxval_AT_SECURE() getauxval (AT_SECURE) @@ -862,3 +863,136 @@ _srt_divert_stdout_to_stderr (GError **error) return g_steal_pointer (&original_stdout); } + +/* + * _srt_file_get_contents_in_sysroot: + * @sysroot_fd: A directory fd opened on the sysroot or `/` + * @path: Absolute or root-relative path within @sysroot_fd + * @contents: (out) (not optional): Used to return contents + * @len: (out) (optional): Used to return length + * @error: Used to return error on failure + * + * Like g_file_get_contents(), but the file is in a sysroot, and we + * follow symlinks as though @sysroot_fd was the root directory + * (similar to `fakechroot`). + * + * Returns: %TRUE if successful + */ +gboolean +_srt_file_get_contents_in_sysroot (int sysroot_fd, + const char *path, + gchar **contents, + gsize *len, + GError **error) +{ + g_autofree gchar *real_path = NULL; + g_autofree gchar *fd_path = NULL; + g_autofree gchar *ignored = NULL; + glnx_autofd int fd = -1; + + g_return_val_if_fail (sysroot_fd >= 0, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (contents != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (contents == NULL) + contents = &ignored; + + fd = _srt_resolve_in_sysroot (sysroot_fd, + path, + SRT_RESOLVE_FLAGS_READABLE, + &real_path, + error); + + if (fd < 0) + return FALSE; + + fd_path = g_strdup_printf ("/proc/self/fd/%d", fd); + + if (!g_file_get_contents (fd_path, contents, len, error)) + { + g_prefix_error (error, "Unable to read %s: ", real_path); + return FALSE; + } + + return TRUE; +} + +/* + * _srt_file_test_in_sysroot: + * @sysroot: (type filename): A path used as the root + * @sysroot_fd: A file descriptor opened on @sysroot, or negative to + * reopen it + * @filename: (type filename): A path below the root directory, either + * absolute or relative (to the root) + * @test: The test to perform on the resolved file in @sysroot. + * G_FILE_TEST_IS_SYMLINK is not a valid GFileTest value because the + * path is resolved following symlinks too. + * + * Returns: %TRUE if the @filename resolved in @sysroot passes the @test. + */ +gboolean +_srt_file_test_in_sysroot (const char *sysroot, + int sysroot_fd, + const char *filename, + GFileTest test) +{ + glnx_autofd int file_fd = -1; + glnx_autofd int local_sysroot_fd = -1; + struct stat stat_buf; + g_autofree gchar *file_realpath_in_sysroot = NULL; + g_autoptr(GError) error = NULL; + + g_return_val_if_fail (sysroot != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + /* We reject G_FILE_TEST_IS_SYMLINK because the provided filename is resolved + * in sysroot, following the eventual symlinks too. So it is not possible for + * the resolved filename to be a symlink */ + g_return_val_if_fail ((test & (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE + | G_FILE_TEST_IS_REGULAR + | G_FILE_TEST_IS_DIR)) == test, FALSE); + + if (sysroot_fd < 0) + { + if (!glnx_opendirat (-1, sysroot, FALSE, &local_sysroot_fd, &error)) + { + g_debug ("An error occurred trying to open %s: %s", sysroot, + error->message); + return FALSE; + } + + sysroot_fd = local_sysroot_fd; + } + + file_fd = _srt_resolve_in_sysroot (sysroot_fd, + filename, SRT_RESOLVE_FLAGS_NONE, + &file_realpath_in_sysroot, &error); + + if (file_fd < 0) + { + g_debug ("An error occurred trying to resolve %s in sysroot: %s", + filename, error->message); + return FALSE; + } + + if (fstat (file_fd, &stat_buf) != 0) + { + g_debug ("fstat %s/%s: %s", + sysroot, file_realpath_in_sysroot, g_strerror (errno)); + return FALSE; + } + + if (test & G_FILE_TEST_EXISTS) + return TRUE; + + if ((test & G_FILE_TEST_IS_EXECUTABLE) && (stat_buf.st_mode & 0111)) + return TRUE; + + if ((test & G_FILE_TEST_IS_REGULAR) && S_ISREG (stat_buf.st_mode)) + return TRUE; + + if ((test & G_FILE_TEST_IS_DIR) && S_ISDIR (stat_buf.st_mode)) + return TRUE; + + return FALSE; +} diff --git a/tests/pressure-vessel/resolve-in-sysroot.c b/tests/pressure-vessel/resolve-in-sysroot.c index df57deab13f16ea3480771c19dfdc5b9119c11b8..bade3586d342b48abf63d1188f8a3992e5ada64d 100644 --- a/tests/pressure-vessel/resolve-in-sysroot.c +++ b/tests/pressure-vessel/resolve-in-sysroot.c @@ -30,9 +30,9 @@ #include <glib/gstdio.h> #include "steam-runtime-tools/glib-backports-internal.h" +#include "steam-runtime-tools/resolve-in-sysroot-internal.h" #include "libglnx/libglnx.h" -#include "resolve-in-sysroot.h" #include "tests/test-utils.h" #include "utils.h" @@ -91,7 +91,7 @@ typedef struct struct { const char *path; - PvResolveFlags flags; + SrtResolveFlags flags; ResolveCallFlags test_flags; } call; struct @@ -110,6 +110,10 @@ test_resolve_in_sysroot (Fixture *f, "a/b/c/d/e", "a/b2/c2/d2/e2", }; + static const char * const prepare_files[] = + { + "a/b/c/file", + }; static const Symlink prepare_symlinks[] = { { "a/b/symlink_to_c", "c" }, @@ -123,60 +127,75 @@ test_resolve_in_sysroot (Fixture *f, { { { "a/b/c/d" }, { "a/b/c/d" } }, { - { "a/b/c/d", PV_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH }, + { "a/b/c/d", SRT_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH }, { "a/b/c/d" }, }, - { { "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } }, + { { "a/b/c/d", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } }, { - { "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P, RESOLVE_CALL_FLAGS_IGNORE_PATH }, + { "a/b/c/d", SRT_RESOLVE_FLAGS_MKDIR_P, RESOLVE_CALL_FLAGS_IGNORE_PATH }, { "a/b/c/d" }, }, { { "create_me" }, { NULL, G_IO_ERROR_NOT_FOUND } }, { - { "create_me", PV_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH }, + { "create_me", SRT_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } }, + { { "a/b/c/d", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } }, + { { "a/b/c/d", SRT_RESOLVE_FLAGS_READABLE }, { "a/b/c/d" } }, + { { "a/b/c/d", SRT_RESOLVE_FLAGS_DIRECTORY }, { "a/b/c/d" } }, + { + { "a/b/c/d", SRT_RESOLVE_FLAGS_READABLE|SRT_RESOLVE_FLAGS_DIRECTORY }, + { "a/b/c/d" } + }, + { { "a/b/c/file", SRT_RESOLVE_FLAGS_READABLE }, { "a/b/c/file" } }, + { + { "a/b/c/file", SRT_RESOLVE_FLAGS_DIRECTORY }, + { NULL, G_IO_ERROR_NOT_DIRECTORY } + }, + { + { "a/b/c/file", SRT_RESOLVE_FLAGS_READABLE|SRT_RESOLVE_FLAGS_DIRECTORY }, + { NULL, G_IO_ERROR_NOT_DIRECTORY } + }, { { "a/b///////.////./././///././c/d" }, { "a/b/c/d" } }, { { "/a/b///////.////././../b2////././c2/d2" }, { "a/b2/c2/d2" } }, { { "a/b/c/d/e/f" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "a/b/c/d/e/f", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } }, + { { "a/b/c/d/e/f", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } }, { { "a/b/c/d/e/f" }, { "a/b/c/d/e/f" } }, - { { "a/b/c/d/e/f", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } }, + { { "a/b/c/d/e/f", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } }, { { "a3/b3/c3" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "a3/b3/c3", PV_RESOLVE_FLAGS_MKDIR_P }, { "a3/b3/c3" } }, + { { "a3/b3/c3", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a3/b3/c3" } }, { { "a/b/symlink_to_c" }, { "a/b/c" } }, { { "a/b/symlink_to_c/d" }, { "a/b/c/d" } }, { - { "a/b/symlink_to_c/d", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, + { "a/b/symlink_to_c/d", SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, { "a/b/c/d" } }, { - { "a/b/symlink_to_c/d", PV_RESOLVE_FLAGS_REJECT_SYMLINKS }, + { "a/b/symlink_to_c/d", SRT_RESOLVE_FLAGS_REJECT_SYMLINKS }, { NULL, G_IO_ERROR_TOO_MANY_LINKS } }, { { "a/b/symlink_to_b2" }, { "a/b2" } }, { { "a/b/symlink_to_c2" }, { "a/b2/c2" } }, { { "a/b/abs_symlink_to_run" }, { NULL, G_IO_ERROR_NOT_FOUND } }, { - { "a/b/abs_symlink_to_run", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, + { "a/b/abs_symlink_to_run", SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, { "a/b/abs_symlink_to_run" } }, { { "run" }, { NULL, G_IO_ERROR_NOT_FOUND } }, /* Wasn't created yet */ - { { "a/b/abs_symlink_to_run", PV_RESOLVE_FLAGS_MKDIR_P }, { "run" } }, + { { "a/b/abs_symlink_to_run", SRT_RESOLVE_FLAGS_MKDIR_P }, { "run" } }, { { "a/b/abs_symlink_to_run/host" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "a/b/abs_symlink_to_run/host", PV_RESOLVE_FLAGS_MKDIR_P }, { "run/host" } }, + { { "a/b/abs_symlink_to_run/host", SRT_RESOLVE_FLAGS_MKDIR_P }, { "run/host" } }, { { "a/b/long_symlink_to_dev" }, { NULL, G_IO_ERROR_NOT_FOUND } }, { { "a/b/long_symlink_to_dev/shm" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "a/b/long_symlink_to_dev/shm", PV_RESOLVE_FLAGS_MKDIR_P }, { "dev/shm" } }, - { { "a/b/../b2/c2/../c3", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b2/c3" } }, + { { "a/b/long_symlink_to_dev/shm", SRT_RESOLVE_FLAGS_MKDIR_P }, { "dev/shm" } }, + { { "a/b/../b2/c2/../c3", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b2/c3" } }, { { "x" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "x", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, { "x" } }, + { { "x", SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, { "x" } }, /* This is a bit odd: unlike mkdir -p, we create targets for dangling * symlinks. It's easier to do this than not, and for pressure-vessel's * use-case it probably even makes more sense than not. */ { { "x/y" }, { NULL, G_IO_ERROR_NOT_FOUND } }, - { { "x/y", PV_RESOLVE_FLAGS_MKDIR_P }, { "create_me/y" } }, + { { "x/y", SRT_RESOLVE_FLAGS_MKDIR_P }, { "create_me/y" } }, }; g_autoptr(GError) error = NULL; g_auto(GLnxTmpDir) tmpdir = { FALSE }; @@ -193,6 +212,16 @@ test_resolve_in_sysroot (Fixture *f, g_assert_no_error (error); } + for (i = 0; i < G_N_ELEMENTS (prepare_files); i++) + { + const char *it = prepare_files[i]; + + glnx_file_replace_contents_at (tmpdir.fd, it, + (guint8 *) "hello", 5, + 0, NULL, &error); + g_assert_no_error (error); + } + for (i = 0; i < G_N_ELEMENTS (prepare_symlinks); i++) { const Symlink *it = &prepare_symlinks[i]; @@ -212,15 +241,21 @@ test_resolve_in_sysroot (Fixture *f, old_fds = tests_check_fd_leaks_enter (); - if (it->call.flags & PV_RESOLVE_FLAGS_MKDIR_P) + if (it->call.flags & SRT_RESOLVE_FLAGS_MKDIR_P) g_string_append (description, " (creating directories)"); - if (it->call.flags & PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK) + if (it->call.flags & SRT_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK) g_string_append (description, " (not following final symlink)"); - if (it->call.flags & PV_RESOLVE_FLAGS_REJECT_SYMLINKS) + if (it->call.flags & SRT_RESOLVE_FLAGS_REJECT_SYMLINKS) g_string_append (description, " (not following any symlink)"); + if (it->call.flags & SRT_RESOLVE_FLAGS_DIRECTORY) + g_string_append (description, " (must be a directory)"); + + if (it->call.flags & SRT_RESOLVE_FLAGS_READABLE) + g_string_append (description, " (open for reading)"); + g_test_message ("%" G_GSIZE_FORMAT ": Resolving %s%s", i, it->call.path, description->str); @@ -229,8 +264,8 @@ test_resolve_in_sysroot (Fixture *f, else out_path = &path; - fd = pv_resolve_in_sysroot (tmpdir.fd, it->call.path, - it->call.flags, out_path, &error); + fd = _srt_resolve_in_sysroot (tmpdir.fd, it->call.path, + it->call.flags, out_path, &error); if (it->expect.path != NULL) { diff --git a/tests/pressure-vessel/utils.c b/tests/pressure-vessel/utils.c index 5b396fa89a1f6f3c33c4201ff29cbf8181d37692..f400bc7de22bb6ef1466885ead504a42ead9352e 100644 --- a/tests/pressure-vessel/utils.c +++ b/tests/pressure-vessel/utils.c @@ -30,6 +30,7 @@ #include <glib/gstdio.h> #include "steam-runtime-tools/glib-backports-internal.h" +#include "steam-runtime-tools/utils-internal.h" #include "libglnx/libglnx.h" #include "tests/test-utils.h" @@ -374,7 +375,8 @@ test_file_in_sysroot (Fixture *f, { const InSysrootTest *it = &tests[i]; - g_assert_cmpint (pv_file_test_in_sysroot (tmpdir.path, it->path, it->test), + g_assert_cmpint (_srt_file_test_in_sysroot (tmpdir.path, -1, + it->path, it->test), ==, it->expected_result); } } diff --git a/tests/system-info.c b/tests/system-info.c index 9ba94ba51ef767338f9f78e8969c150e60007e4e..a5162237d2fc4bf7fc5a8263f38efe9ee5cef415 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -2227,14 +2227,14 @@ static const ContainerTest container_tests[] = "debian10", SRT_CONTAINER_TYPE_UNKNOWN, NULL }, { "Has 'docker' in /run/systemd/container", "fedora", SRT_CONTAINER_TYPE_DOCKER, NULL }, - { "Has /.flatpak-info", + { "Has /.flatpak-info and /run/host", "flatpak-example", SRT_CONTAINER_TYPE_FLATPAK, "/run/host" }, { "Has /run/host", "invalid-os-release", SRT_CONTAINER_TYPE_UNKNOWN, "/run/host" }, { "Has no evidence of being a container", "no-os-release", SRT_CONTAINER_TYPE_NONE, NULL }, { "Has /run/pressure-vessel", - "steamrt", SRT_CONTAINER_TYPE_PRESSURE_VESSEL, "/run/host" }, + "steamrt", SRT_CONTAINER_TYPE_PRESSURE_VESSEL, NULL }, { "Has a Docker-looking /proc/1/cgroup", "steamrt-unofficial", SRT_CONTAINER_TYPE_DOCKER, NULL }, };