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)
This commit is contained in:
Alexander Larsson committed 2017-01-19 15:33:47 +01:00
1 parent 1851df22e4
commit 21a0bd9ed3
1 file changed
+77 -27
+77 -27
View File
@@ -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)
{