diff --git a/src/flatpak-exports-private.h b/src/flatpak-exports-private.h index 6da3bef4b0714874090e745154b7a117a903519e..6e382fa2d9fd4e5ac41a34ee7c3c6622390aecc6 100644 --- a/src/flatpak-exports-private.h +++ b/src/flatpak-exports-private.h @@ -62,5 +62,8 @@ FlatpakFilesystemMode flatpak_exports_path_get_mode (FlatpakExports *exports, G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakExports, flatpak_exports_free); +void flatpak_exports_take_host_fd (FlatpakExports *exports, + int fd); + #endif /* __FLATPAK_EXPORTS_H__ */ diff --git a/src/flatpak-exports.c b/src/flatpak-exports.c index eb072a21ed0928b92984f266727df5d6e4b3ca58..99d4de03687395bf7c61cd41e942d64657cb46de 100644 --- a/src/flatpak-exports.c +++ b/src/flatpak-exports.c @@ -110,8 +110,59 @@ struct _FlatpakExports GHashTable *hash; FlatpakFilesystemMode host_etc; FlatpakFilesystemMode host_os; + int host_fd; }; +/* + * When populating /run/host, pretend @fd was the root of the host + * filesystem. + */ +void +flatpak_exports_take_host_fd (FlatpakExports *exports, + int fd) +{ + glnx_close_fd (&exports->host_fd); + + if (fd >= 0) + exports->host_fd = fd; +} + +static gboolean +flatpak_exports_stat_in_host (FlatpakExports *exports, + const char *abs_path, + struct stat *buf, + int flags, + GError **error) +{ + g_return_val_if_fail (abs_path[0] == '/', FALSE); + + if (exports->host_fd >= 0) + { + /* If abs_path is "/usr", then stat "usr" relative to host_fd. + * As a special case, if abs_path is "/", stat host_fd itself, + * due to the use of AT_EMPTY_PATH. */ + return glnx_fstatat (exports->host_fd, &abs_path[1], buf, + AT_EMPTY_PATH | flags, + error); + } + + return glnx_fstatat (AT_FDCWD, abs_path, buf, flags, error); +} + +static gchar * +flatpak_exports_readlink_in_host (FlatpakExports *exports, + const char *abs_path, + GError **error) +{ + g_return_val_if_fail (abs_path[0] == '/', FALSE); + + if (exports->host_fd >= 0) + return glnx_readlinkat_malloc (exports->host_fd, &abs_path[1], + NULL, error); + + return glnx_readlinkat_malloc (AT_FDCWD, abs_path, NULL, error); +} + static void exported_path_free (ExportedPath *exported_path) { @@ -125,6 +176,7 @@ flatpak_exports_new (void) FlatpakExports *exports = g_new0 (FlatpakExports, 1); exports->hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GFreeFunc) exported_path_free); + exports->host_fd = -1; return exports; } @@ -250,15 +302,15 @@ path_is_symlink (const char *path) typedef struct { const char *name; - GFileTest test; + int ifmt; } LibsNeedEtc; static const LibsNeedEtc libs_need_etc[] = { /* glibc */ - { "ld.so.cache", G_FILE_TEST_IS_REGULAR }, + { "ld.so.cache", S_IFREG }, /* Used for executables and a few libraries on e.g. Debian */ - { "alternatives", G_FILE_TEST_IS_DIR } + { "alternatives", S_IFDIR } }; void @@ -269,6 +321,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, g_autofree const char **keys = (const char **) g_hash_table_get_keys_as_array (exports->hash, &n_keys); g_autoptr(GList) eps = NULL; GList *l; + struct stat buf; eps = g_hash_table_get_values (exports->hash); eps = g_list_sort (eps, (GCompareFunc) compare_eps); @@ -332,7 +385,8 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, if (exports->host_os == FLATPAK_FILESYSTEM_MODE_READ_ONLY) os_bind_mode = "--ro-bind"; - if (g_file_test ("/usr", G_FILE_TEST_IS_DIR)) + if (flatpak_exports_stat_in_host (exports, "/usr", &buf, 0, NULL) && + S_ISDIR (buf.st_mode)) flatpak_bwrap_add_args (bwrap, os_bind_mode, "/usr", "/run/host/usr", NULL); @@ -345,7 +399,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, g_assert (subdir[0] == '/'); /* e.g. /run/host/lib32 */ run_host_subdir = g_strconcat ("/run/host", subdir, NULL); - target = glnx_readlinkat_malloc (-1, subdir, NULL, NULL); + target = flatpak_exports_readlink_in_host (exports, subdir, NULL); if (target != NULL && g_str_has_prefix (target, "usr/")) @@ -367,7 +421,8 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, "--symlink", target + 1, run_host_subdir, NULL); } - else if (g_file_test (subdir, G_FILE_TEST_IS_DIR)) + else if (flatpak_exports_stat_in_host (exports, subdir, &buf, 0, NULL) && + S_ISDIR (buf.st_mode)) { /* e.g. /lib32 is a symlink to /opt/compat/ia32/lib, * or is a plain directory because the host OS has not @@ -394,7 +449,9 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, const LibsNeedEtc *item = &libs_need_etc[i]; g_autofree gchar *host_path = g_strconcat ("/etc/", item->name, NULL); - if (g_file_test (host_path, item->test)) + if (flatpak_exports_stat_in_host (exports, host_path, + &buf, 0, NULL) && + (buf.st_mode & S_IFMT) == item->ifmt) { g_autofree gchar *run_host_path = g_strconcat ("/run/host/etc/", item->name, NULL); @@ -416,7 +473,8 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, if (exports->host_etc == FLATPAK_FILESYSTEM_MODE_READ_ONLY) etc_bind_mode = "--ro-bind"; - if (g_file_test ("/etc", G_FILE_TEST_IS_DIR)) + if (flatpak_exports_stat_in_host (exports, "/etc", &buf, 0, NULL) && + S_ISDIR (buf.st_mode)) flatpak_bwrap_add_args (bwrap, etc_bind_mode, "/etc", "/run/host/etc", NULL); } @@ -424,9 +482,9 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, /* As per the os-release specification https://www.freedesktop.org/software/systemd/man/os-release.html * always read-only bind-mount /etc/os-release if it exists, or /usr/lib/os-release as a fallback from * the host into the application's /run/host */ - if (g_file_test ("/etc/os-release", G_FILE_TEST_EXISTS)) + if (flatpak_exports_stat_in_host (exports, "/etc/os-release", &buf, 0, NULL)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/os-release", "/run/host/os-release", NULL); - else if (g_file_test ("/usr/lib/os-release", G_FILE_TEST_EXISTS)) + else if (flatpak_exports_stat_in_host (exports, "/usr/lib/os-release", &buf, 0, NULL)) flatpak_bwrap_add_args (bwrap, "--ro-bind", "/usr/lib/os-release", "/run/host/os-release", NULL); }