From 2acb3f81a5b7952d7259d5b2babdda2a8f8d595f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 14 Nov 2016 18:50:49 +0100 Subject: [PATCH] Special handling of accesible XDG_XYZ_HOME subdirs If the app is explictitly given access to a subdirectory of one of the xdg config/cache/data directories, with read-write (or create) access, then also bind-mount that directory into the corresponding .var/app/$APPID directory. This allows apps that want it to keep re-using global directories for storing per-app information. For instance, if your app uses "~/.config/foo" to store configuration data, then you can use --filesystem=xdg-config/foo, which will look for the directory, and if it exist, let the app access it, as well as mirror the content in ~/.var/app/$appid/config/foo. You can also use a ":create" to enforce the directory to be created and thus always do the mapping. --- common/flatpak-run.c | 80 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/common/flatpak-run.c b/common/flatpak-run.c index f5c5a9d2..7f9d86f8 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -523,22 +523,29 @@ flatpak_context_set_persistent (FlatpakContext *context, static gboolean get_xdg_dir_from_prefix (const char *prefix, + const char **where, const char **dir) { if (strcmp (prefix, "xdg-data") == 0) { + if (where) + *where = "data"; if (dir) *dir = g_get_user_data_dir (); return TRUE; } if (strcmp (prefix, "xdg-cache") == 0) { + if (where) + *where = "cache"; if (dir) *dir = g_get_user_cache_dir (); return TRUE; } if (strcmp (prefix, "xdg-config") == 0) { + if (where) + *where = "config"; if (dir) *dir = g_get_user_config_dir (); return TRUE; @@ -546,6 +553,41 @@ get_xdg_dir_from_prefix (const char *prefix, return FALSE; } +/* This looks only in the xdg dirs (config, cache, data), not the user + definable ones */ +static char * +get_xdg_dir_from_string (const char *filesystem, + const char **suffix, + const char **where) +{ + char *slash; + const char *rest; + g_autofree char *prefix; + const char *dir = NULL; + gsize len; + + slash = strchr (filesystem, '/'); + + if (slash) + len = slash - filesystem; + else + len = strlen (filesystem); + + rest = filesystem + len; + while (*rest == '/') + rest++; + + if (suffix != NULL) + *suffix = rest; + + prefix = g_strndup (filesystem, len); + + if (get_xdg_dir_from_prefix (prefix, where, &dir)) + return g_build_filename (dir, rest, NULL); + + return NULL; +} + static gboolean get_xdg_user_dir_from_string (const char *filesystem, const char **config_key, @@ -637,7 +679,7 @@ get_xdg_user_dir_from_string (const char *filesystem, *dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS); return TRUE; } - if (get_xdg_dir_from_prefix (prefix, dir)) + if (get_xdg_dir_from_prefix (prefix, NULL, dir)) { if (config_key) *config_key = NULL; @@ -2504,6 +2546,42 @@ flatpak_run_add_environment_args (GPtrArray *argv_array, /* This actually outputs the args for the hide/expose operations above */ add_file_args (argv_array, fs_paths); + /* Special case subdirectories of the cache, config and data xdg dirs. + * If these are accessible explicilty, in a read-write fashion, then + * we bind-mount these in the app-id dir. This allows applications to + * explicitly opt out of keeping some config/cache/data in the + * app-specific directory. + */ + if (app_id_dir) + { + g_hash_table_iter_init (&iter, context->filesystems); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + const char *filesystem = key; + FlatpakFilesystemMode mode = GPOINTER_TO_INT (value); + g_autofree char *xdg_path = NULL; + const char *rest, *where; + + xdg_path = get_xdg_dir_from_string (filesystem, &rest, &where); + + if (xdg_path != NULL && *rest != 0 && + mode > FLATPAK_FILESYSTEM_MODE_READ_WRITE) + { + g_autoptr(GFile) app_version = g_file_get_child (app_id_dir, where); + g_autoptr(GFile) app_version_subdir = g_file_resolve_relative_path (app_version, rest); + + if (g_file_test (xdg_path, G_FILE_TEST_IS_DIR)) + { + g_autofree char *xdg_path_in_app = g_file_get_path (app_version_subdir); + g_mkdir_with_parents (xdg_path_in_app, 0755); + add_args (argv_array, + "--bind", xdg_path, xdg_path_in_app, + NULL); + } + } + } + } + if (home_access && app_id_dir != NULL) { g_autofree char *src_path = g_build_filename (g_get_user_config_dir (),