From 21a0bd9ed393d7d8873ad80da9616804c5bf3029 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 19 Jan 2017 15:11:08 +0100 Subject: [PATCH] Run: Handle dependencies when exposing symlinks If you are exposing a symlink, say /link (-> /target) and also a child of it, say /link/child, then we run into dependency issues. For instance, the /link symlink is created, pointing to /target, but then we try to to create /link/child before the /target bind mount is setup, which will fail. We work around this by adding a level, and the dependency of a symlink (/target in the above case) will always be handled first. This isn't a perfect solution, but is likely to be good enough in practice. (cherry picked from commit 5c4c4bbe56807ce94ef2eb0cc860b1cf488371f5) --- common/flatpak-run.c | 104 ++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/common/flatpak-run.c b/common/flatpak-run.c index 8874767b9..30d5cd804 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -2235,6 +2235,12 @@ make_relative (const char *base, const char *path) #define FAKE_MODE_HIDDEN 0 #define FAKE_MODE_SYMLINK G_MAXINT +typedef struct { + char *path; + int level; + guint mode; +} ExportedPath; + static gboolean path_is_visible (const char **keys, guint n_keys, @@ -2248,14 +2254,13 @@ path_is_visible (const char **keys, for (i = 0; i < n_keys; i++) { const char *mounted_path = keys[i]; - guint mode; - mode = GPOINTER_TO_INT (g_hash_table_lookup (hash_table, mounted_path)); + ExportedPath *ep = g_hash_table_lookup (hash_table, mounted_path); if (flatpak_has_path_prefix (path, mounted_path)) { - if (mode == FAKE_MODE_HIDDEN) + if (ep->mode == FAKE_MODE_HIDDEN) is_visible = FALSE; - else if (mode != FAKE_MODE_SYMLINK) + else if (ep->mode != FAKE_MODE_SYMLINK) is_visible = TRUE; } } @@ -2263,24 +2268,36 @@ path_is_visible (const char **keys, return is_visible; } +static gint +compare_eps (const ExportedPath *a, + const ExportedPath *b) +{ + if (a->level == b->level) + return g_strcmp0 (a->path, b->path); + else + return b->level - a->level; +} + static void add_file_args (GPtrArray *argv_array, GHashTable *hash_table) { guint n_keys; g_autofree const char **keys = (const char **)g_hash_table_get_keys_as_array (hash_table, &n_keys); - guint i; + g_autoptr(GList) eps = NULL; + GList *l; + + eps = g_hash_table_get_values (hash_table); + eps = g_list_sort (eps, (GCompareFunc)compare_eps); g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL); - for (i = 0; i < n_keys; i++) + for (l = eps; l != NULL; l = l->next) { - const char *path = keys[i]; - guint mode; + ExportedPath *ep = l->data; + const char *path = ep->path; - mode = GPOINTER_TO_INT (g_hash_table_lookup (hash_table, path)); - - if (mode == FAKE_MODE_SYMLINK) + if (ep->mode == FAKE_MODE_SYMLINK) { if (!path_is_visible (keys, n_keys, hash_table, path)) { @@ -2291,7 +2308,7 @@ add_file_args (GPtrArray *argv_array, add_args (argv_array, "--symlink", relative, path, NULL); } } - else if (mode == FAKE_MODE_HIDDEN) + else if (ep->mode == FAKE_MODE_HIDDEN) { /* Mount a tmpfs to hide the subdirectory, but only if either its not visible (then we can always create the @@ -2302,9 +2319,11 @@ add_file_args (GPtrArray *argv_array, add_args (argv_array, "--tmpfs", path, NULL); } else - add_args (argv_array, - (mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) ? "--ro-bind" : "--bind", - path, path, NULL); + { + add_args (argv_array, + (ep->mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) ? "--ro-bind" : "--bind", + path, path, NULL); + } } } @@ -2313,16 +2332,33 @@ add_hide_path (GHashTable *hash_table, const char *path) { guint old_mode; + ExportedPath *ep = g_new0 (ExportedPath, 1); + ExportedPath *old_ep; - old_mode = GPOINTER_TO_INT (g_hash_table_lookup (hash_table, path)); - g_hash_table_insert (hash_table, g_strdup (path), - GINT_TO_POINTER ( MAX (old_mode, FAKE_MODE_HIDDEN))); + old_ep = g_hash_table_lookup (hash_table, path); + if (old_ep) + old_mode = old_ep->mode; + else + old_mode = 0; + + ep->path = g_strdup (path); + ep->level = 0; + ep->mode = MAX (old_mode, FAKE_MODE_HIDDEN); + g_hash_table_insert (hash_table, ep->path, ep); } +/* We use the level to make sure we get the ordering somewhat right. + * For instance if /symlink -> /z_dir is exported, then we want to create + * /z_dir before /symlink, because otherwise an export like /symlink/foo + * will fail. The approach we use is to just bump the sort prio based on the + * symlink resolve depth. This it not perfect, but gets the common situation + * such as --filesystem=/link --filesystem=/link/dir right. + */ static gboolean -add_expose_path (GHashTable *hash_table, - FlatpakFilesystemMode mode, - const char *path) +_add_expose_path (GHashTable *hash_table, + FlatpakFilesystemMode mode, + const char *path, + int level) { g_autofree char *canonical = flatpak_canonicalize_filename (path); struct stat st; @@ -2330,7 +2366,7 @@ add_expose_path (GHashTable *hash_table, if (!g_path_is_absolute (path)) { - g_warning ("Exposing relative path %s", path); + g_debug ("Not exposing relative path %s", path); return FALSE; } @@ -2354,15 +2390,17 @@ add_expose_path (GHashTable *hash_table, S_ISLNK (st.st_mode) || S_ISSOCK (st.st_mode)) { - guint old_mode; + ExportedPath *old_ep = g_hash_table_lookup (hash_table, path); + guint old_mode = 0; - old_mode = GPOINTER_TO_INT (g_hash_table_lookup (hash_table, path)); + if (old_ep != NULL) + old_mode = old_ep->mode; if (S_ISLNK (st.st_mode)) { g_autofree char *resolved = flatpak_resolve_link (path, NULL); - if (resolved && add_expose_path (hash_table, mode, resolved)) + if (resolved && _add_expose_path (hash_table, mode, resolved, level + 1)) mode = FAKE_MODE_SYMLINK; else mode = 0; @@ -2370,7 +2408,11 @@ add_expose_path (GHashTable *hash_table, if (mode > 0) { - g_hash_table_insert (hash_table, g_strdup (path), GINT_TO_POINTER ( MAX (old_mode, mode))); + ExportedPath *ep = g_new0 (ExportedPath, 1); + ep->path = g_strdup (path); + ep->mode = MAX (old_mode, mode); + ep->level = level; + g_hash_table_insert (hash_table, ep->path, ep); return TRUE; } } @@ -2378,6 +2420,14 @@ add_expose_path (GHashTable *hash_table, return FALSE; } +static gboolean +add_expose_path (GHashTable *hash_table, + FlatpakFilesystemMode mode, + const char *path) +{ + return _add_expose_path (hash_table, mode, path, 0); +} + void flatpak_run_add_environment_args (GPtrArray *argv_array, GArray *fd_array, @@ -2396,7 +2446,7 @@ flatpak_run_add_environment_args (GPtrArray *argv_array, GString *xdg_dirs_conf = NULL; FlatpakFilesystemMode fs_mode, home_mode; g_autoptr(GFile) user_flatpak_dir = NULL; - g_autoptr(GHashTable) fs_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + g_autoptr(GHashTable) fs_paths = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); if ((context->shares & FLATPAK_CONTEXT_SHARED_IPC) == 0) {