summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2016-04-26 17:01:18 +0200
committerAlexander Larsson <alexl@redhat.com>2016-04-26 17:01:18 +0200
commiteeece1a78953b19e7f56d19e3e5b3fe79fa87555 (patch)
treeca2cbcb290d99e2df809527f8980e0aa433e7120 /common
parent839e17abf0a619227aeac14e731146020f5a16d2 (diff)
parent07e5b4a219e6d4b7fef2cc88bdd36a9481a75fc6 (diff)
downloadxdg-app-eeece1a78953b19e7f56d19e3e5b3fe79fa87555.tar.gz
Merge branch 'privileged-helper'
Diffstat (limited to 'common')
-rw-r--r--common/xdg-app-dir.c558
-rw-r--r--common/xdg-app-dir.h41
-rw-r--r--common/xdg-app-utils.c131
-rw-r--r--common/xdg-app-utils.h11
4 files changed, 720 insertions, 21 deletions
diff --git a/common/xdg-app-dir.c b/common/xdg-app-dir.c
index fa45f2d..77ad21c 100644
--- a/common/xdg-app-dir.c
+++ b/common/xdg-app-dir.c
@@ -37,6 +37,8 @@
#include "errno.h"
+#define NO_SYSTEM_HELPER ((XdgAppSystemHelper *)(gpointer)1)
+
struct XdgAppDir {
GObject parent;
@@ -44,6 +46,8 @@ struct XdgAppDir {
GFile *basedir;
OstreeRepo *repo;
+ XdgAppSystemHelper *system_helper;
+
SoupSession *soup_session;
};
@@ -163,6 +167,83 @@ xdg_app_get_user_base_dir_location (void)
return g_file_new_for_path (base);
}
+GFile *
+xdg_app_get_user_cache_dir_location (void)
+{
+ g_autoptr(GFile) base_dir = NULL;
+
+ base_dir = xdg_app_get_user_base_dir_location ();
+ return g_file_get_child (base_dir, "system-cache");
+}
+
+GFile *
+xdg_app_ensure_user_cache_dir_location (GError **error)
+{
+ g_autoptr(GFile) cache_dir = NULL;
+ g_autofree char *cache_path = NULL;
+
+ cache_dir = xdg_app_get_user_cache_dir_location ();
+ cache_path = g_file_get_path (cache_dir);
+
+ if (g_mkdir_with_parents (cache_path, 0755) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ return NULL;
+ }
+
+ return g_steal_pointer (&cache_dir);
+}
+
+static XdgAppSystemHelper *
+xdg_app_dir_get_system_helper (XdgAppDir *self)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (g_once_init_enter (&self->system_helper))
+ {
+ XdgAppSystemHelper *system_helper;
+ system_helper =
+ xdg_app_system_helper_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ "org.freedesktop.XdgApp.SystemHelper",
+ "/org/freedesktop/XdgApp/SystemHelper",
+ NULL, &error);
+ if (error != NULL)
+ {
+ g_warning ("Can't find org.freedesktop.XdgApp.SystemHelper: %s\n", error->message);
+ system_helper = NO_SYSTEM_HELPER;
+ }
+
+ g_once_init_leave (&self->system_helper, system_helper);
+ }
+
+ if (self->system_helper != NO_SYSTEM_HELPER)
+ return self->system_helper;
+ return NULL;
+}
+
+gboolean
+xdg_app_dir_use_child_repo (XdgAppDir *self)
+{
+ XdgAppSystemHelper *system_helper;
+
+ if (self->user || getuid () == 0)
+ return FALSE;
+
+ system_helper = xdg_app_dir_get_system_helper (self);
+
+ return system_helper != NULL;
+}
+
+static OstreeRepo *
+system_ostree_repo_new (GFile *repodir)
+{
+ return g_object_new (OSTREE_TYPE_REPO, "path", repodir,
+ "remotes-config-dir", XDG_APP_CONFIGDIR "/remotes.d",
+ NULL);
+}
+
static void
xdg_app_dir_finalize (GObject *object)
{
@@ -171,6 +252,8 @@ xdg_app_dir_finalize (GObject *object)
g_clear_object (&self->repo);
g_clear_object (&self->basedir);
+ g_clear_object (&self->system_helper);
+
g_clear_object (&self->soup_session);
G_OBJECT_CLASS (xdg_app_dir_parent_class)->finalize (object);
@@ -629,24 +712,16 @@ xdg_app_dir_ensure_repo (XdgAppDir *self,
repo = ostree_repo_new (repodir);
else
{
- g_autoptr(GFile) base_dir = NULL;
g_autoptr(GFile) cache_dir = NULL;
g_autofree char *cache_path = NULL;
- repo = g_object_new (OSTREE_TYPE_REPO, "path", repodir,
- "remotes-config-dir", XDG_APP_CONFIGDIR "/remotes.d",
- NULL);
+ repo = system_ostree_repo_new (repodir);
- base_dir = xdg_app_get_user_base_dir_location ();
- cache_dir = g_file_get_child (base_dir, "system-cache");
- cache_path = g_file_get_path (cache_dir);
-
- if (g_mkdir_with_parents (cache_path, 0755) != 0)
- {
- glnx_set_error_from_errno (error);
- goto out;
- }
+ cache_dir = xdg_app_ensure_user_cache_dir_location (error);
+ if (cache_dir == NULL)
+ goto out;
+ cache_path = g_file_get_path (cache_dir);
if (!ostree_repo_set_cache_dir (repo,
AT_FDCWD, cache_path,
cancellable, error))
@@ -797,7 +872,7 @@ xdg_app_dir_update_appstream (XdgAppDir *self,
if (!ostree_repo_resolve_rev (self->repo, remote_and_branch, TRUE, &old_checksum, error))
return FALSE;
- if (!xdg_app_dir_pull (self, remote, branch, NULL, progress,
+ if (!xdg_app_dir_pull (self, remote, branch, NULL, NULL, OSTREE_REPO_PULL_FLAGS_NONE, progress,
cancellable, error))
return FALSE;
@@ -931,6 +1006,8 @@ xdg_app_dir_pull (XdgAppDir *self,
const char *repository,
const char *ref,
char **subpaths,
+ OstreeRepo *repo,
+ OstreeRepoPullFlags flags,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error)
@@ -953,6 +1030,9 @@ xdg_app_dir_pull (XdgAppDir *self,
if (*url == 0)
return TRUE; /* Empty url, silently disables updates */
+ if (repo == NULL)
+ repo = self->repo;
+
if (progress == NULL)
{
console = gs_console_get ();
@@ -964,13 +1044,14 @@ xdg_app_dir_pull (XdgAppDir *self,
}
}
+
refs[0] = ref;
refs[1] = NULL;
if (subpaths == NULL || subpaths[0] == NULL)
{
- if (!ostree_repo_pull (self->repo, repository,
- (char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
+ if (!ostree_repo_pull (repo, repository,
+ (char **)refs, flags,
progress,
cancellable, error))
{
@@ -982,9 +1063,9 @@ xdg_app_dir_pull (XdgAppDir *self,
{
int i;
- if (!repo_pull_one_dir (self->repo, repository,
+ if (!repo_pull_one_dir (repo, repository,
"/metadata",
- (char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
+ (char **)refs, flags,
progress,
cancellable, error))
{
@@ -996,9 +1077,9 @@ xdg_app_dir_pull (XdgAppDir *self,
for (i = 0; subpaths[i] != NULL; i++)
{
g_autofree char *subpath = g_build_filename ("/files", subpaths[i], NULL);
- if (!repo_pull_one_dir (self->repo, repository,
+ if (!repo_pull_one_dir (repo, repository,
subpath,
- (char **)refs, OSTREE_REPO_PULL_FLAGS_NONE,
+ (char **)refs, flags,
progress,
cancellable, error))
{
@@ -1021,6 +1102,215 @@ xdg_app_dir_pull (XdgAppDir *self,
return ret;
}
+static gboolean
+repo_pull_one_untrusted (OstreeRepo *self,
+ const char *remote_name,
+ const char *url,
+ const char *dir_to_pull,
+ const char *ref,
+ const char *checksum,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoPullFlags flags = OSTREE_REPO_PULL_FLAGS_UNTRUSTED;
+ GVariantBuilder builder;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ const char *refs[2] = { NULL, NULL };
+ const char *commits[2] = { NULL, NULL };
+
+ refs[0] = ref;
+ commits[0] = checksum;
+
+ g_variant_builder_add (&builder, "{s@v}", "flags",
+ g_variant_new_variant (g_variant_new_int32 (flags)));
+ g_variant_builder_add (&builder, "{s@v}", "refs",
+ g_variant_new_variant (g_variant_new_strv ((const char *const*) refs, -1)));
+ g_variant_builder_add (&builder, "{s@v}", "override-commit-ids",
+ g_variant_new_variant (g_variant_new_strv ((const char *const*) commits, -1)));
+ g_variant_builder_add (&builder, "{s@v}", "override-remote-name",
+ g_variant_new_variant (g_variant_new_string (remote_name)));
+ g_variant_builder_add (&builder, "{s@v}", "gpg-verify",
+ g_variant_new_variant (g_variant_new_boolean (TRUE)));
+ g_variant_builder_add (&builder, "{s@v}", "gpg-verify-summary",
+ g_variant_new_variant (g_variant_new_boolean (TRUE)));
+
+ if (dir_to_pull)
+ {
+ g_variant_builder_add (&builder, "{s@v}", "subdir",
+ g_variant_new_variant (g_variant_new_string (dir_to_pull)));
+ g_variant_builder_add (&builder, "{s@v}", "disable-static-deltas",
+ g_variant_new_variant (g_variant_new_boolean (TRUE)));
+ }
+
+ return ostree_repo_pull_with_options (self, url, g_variant_builder_end (&builder),
+ progress, cancellable, error);
+}
+
+gboolean
+xdg_app_dir_pull_untrusted_local (XdgAppDir *self,
+ const char *src_path,
+ const char *remote_name,
+ const char *ref,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GSConsole *console = NULL;
+ g_autoptr(OstreeAsyncProgress) console_progress = NULL;
+ g_autoptr(GFile) path_file = g_file_new_for_path (src_path);
+ g_autoptr(GFile) summary_file = g_file_get_child (path_file, "summary");
+ g_autoptr(GFile) summary_sig_file = g_file_get_child (path_file, "summary.sig");
+ g_autofree char *url = g_file_get_uri (path_file);
+ g_autofree char *checksum = NULL;
+ gboolean gpg_verify_summary;
+ gboolean gpg_verify;
+ char *summary_data = NULL;
+ char *summary_sig_data = NULL;
+ gsize summary_data_size, summary_sig_data_size;
+ g_autoptr(GBytes) summary_bytes = NULL;
+ g_autoptr(GBytes) summary_sig_bytes = NULL;
+ g_autoptr(OstreeGpgVerifyResult) gpg_result = NULL;
+ g_autoptr(GVariant) summary = NULL;
+ g_autoptr(GVariant) old_commit = NULL;
+
+ if (!xdg_app_dir_ensure_repo (self, cancellable, error))
+ return FALSE;
+
+ if (!ostree_repo_remote_get_gpg_verify_summary (self->repo, remote_name,
+ &gpg_verify_summary, error))
+ return FALSE;
+
+ if (!ostree_repo_remote_get_gpg_verify (self->repo, remote_name,
+ &gpg_verify, error))
+ return FALSE;
+
+ if (!gpg_verify_summary || !gpg_verify)
+ return xdg_app_fail (error, "Can't pull from untrusted non-gpg verified remote");
+
+ /* We verify the summary manually before anything else to make sure
+ we've got something right before looking too hard at the repo and
+ so we can check for a downgrade before pulling and updating the
+ ref */
+
+ if (!g_file_load_contents (summary_sig_file, cancellable,
+ &summary_sig_data, &summary_sig_data_size, NULL, NULL))
+ return xdg_app_fail (error, "GPG verification enabled, but no summary signatures found");
+
+ summary_sig_bytes = g_bytes_new_take (summary_sig_data, summary_sig_data_size);
+
+ if (!g_file_load_contents (summary_file, cancellable,
+ &summary_data, &summary_data_size, NULL, NULL))
+ return xdg_app_fail (error, "No summary found");
+ summary_bytes = g_bytes_new_take (summary_data, summary_data_size);
+
+ gpg_result = ostree_repo_verify_summary (self->repo,
+ remote_name,
+ summary_bytes,
+ summary_sig_bytes,
+ cancellable, error);
+ if (gpg_result == NULL)
+ return FALSE;
+
+ if (ostree_gpg_verify_result_count_valid (gpg_result) == 0)
+ return xdg_app_fail (error, "GPG signatures found, but none are in trusted keyring");
+
+ summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE));
+ if (!xdg_app_summary_lookup_ref (summary,
+ ref,
+ &checksum))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "Can't find %sin remote %s", ref, remote_name);
+ return FALSE;
+ }
+
+ (void)ostree_repo_load_commit (self->repo, checksum, &old_commit, NULL, NULL);
+
+ if (old_commit)
+ {
+ g_autoptr(OstreeRepo) src_repo = ostree_repo_new (path_file);
+ g_autoptr(GVariant) new_commit = NULL;
+ guint64 old_timestamp;
+ guint64 new_timestamp;
+
+ if (!ostree_repo_open (src_repo, cancellable, error))
+ return FALSE;
+
+ if (!ostree_repo_load_commit (src_repo, checksum, &new_commit, NULL, error))
+ return FALSE;
+
+ old_timestamp = ostree_commit_get_timestamp (old_commit);
+ new_timestamp = ostree_commit_get_timestamp (new_commit);
+
+ if (new_timestamp < old_timestamp)
+ return xdg_app_fail (error, "Not allowed to downgrade %s", ref);
+ }
+
+
+ if (progress == NULL)
+ {
+ console = gs_console_get ();
+ if (console)
+ {
+ gs_console_begin_status_line (console, "", NULL, NULL);
+ console_progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
+ progress = console_progress;
+ }
+ }
+
+ if (subpaths == NULL || subpaths[0] == NULL)
+ {
+ if (!repo_pull_one_untrusted (self->repo, remote_name,url,
+ NULL, ref, checksum, progress,
+ cancellable, error))
+ {
+ g_prefix_error (error, "While pulling %s from remote %s: ", ref, remote_name);
+ goto out;
+ }
+ }
+ else
+ {
+ int i;
+
+ if (!repo_pull_one_untrusted (self->repo, remote_name,url,
+ "/metadata", ref, checksum, progress,
+ cancellable, error))
+ {
+ g_prefix_error (error, "While pulling %s from remote %s, metadata: ",
+ ref, remote_name);
+ goto out;
+ }
+
+ for (i = 0; subpaths[i] != NULL; i++)
+ {
+ g_autofree char *subpath = g_build_filename ("/files", subpaths[i], NULL);
+ if (!repo_pull_one_untrusted (self->repo, remote_name,url,
+ subpath, ref, checksum, progress,
+ cancellable, error))
+ {
+ g_prefix_error (error, "While pulling %s from remote %s, subpath %s: ",
+ ref, remote_name, subpaths[i]);
+ goto out;
+ }
+ }
+ }
+
+ ret = TRUE;
+
+ out:
+ if (console)
+ {
+ ostree_async_progress_finish (progress);
+ gs_console_end_status_line (console, NULL, NULL);
+ }
+
+ return ret;
+}
+
+
char *
xdg_app_dir_current_ref (XdgAppDir *self,
const char *name,
@@ -2257,6 +2547,7 @@ xdg_app_dir_deploy_install (XdgAppDir *self,
gboolean
xdg_app_dir_deploy_update (XdgAppDir *self,
const char *ref,
+ const char *remote_name,
const char *checksum_or_latest,
GCancellable *cancellable,
GError **error)
@@ -2312,6 +2603,231 @@ xdg_app_dir_deploy_update (XdgAppDir *self,
return TRUE;
}
+static OstreeRepo *
+xdg_app_dir_create_system_child_repo (XdgAppDir *self,
+ GLnxLockFile *file_lock,
+ GError **error)
+{
+ g_autoptr(GFile) cache_dir = NULL;
+ g_autoptr(GFile) repo_dir = NULL;
+ g_autoptr(GFile) repo_dir_config = NULL;
+ g_autoptr(OstreeRepo) repo = NULL;
+ g_autofree char *tmpdir_name = NULL;
+ g_autoptr(OstreeRepo) new_repo = NULL;
+ g_autoptr(GKeyFile) config = NULL;
+
+ g_assert (!self->user);
+
+ if (!xdg_app_dir_ensure_repo (self, NULL, error))
+ return NULL;
+
+ cache_dir = xdg_app_ensure_user_cache_dir_location (error);
+ if (cache_dir == NULL)
+ return NULL;
+
+ if (!xdg_app_allocate_tmpdir (AT_FDCWD,
+ gs_file_get_path_cached (cache_dir),
+ "repo-", &tmpdir_name,
+ NULL,
+ file_lock,
+ NULL,
+ NULL, error))
+ return NULL;
+
+ repo_dir = g_file_get_child (cache_dir, tmpdir_name);
+
+ new_repo = ostree_repo_new (repo_dir);
+
+ repo_dir_config = g_file_get_child (repo_dir, "config");
+ if (!g_file_query_exists (repo_dir_config, NULL))
+ {
+ if (!ostree_repo_create (new_repo,
+ OSTREE_REPO_MODE_BARE_USER,
+ NULL, error))
+ return NULL;
+ }
+ else
+ {
+ if (!ostree_repo_open (new_repo, NULL, error))
+ return NULL;
+ }
+
+ /* Ensure the config is updated */
+ config = ostree_repo_copy_config (new_repo);
+ g_key_file_set_string (config, "core", "parent",
+ gs_file_get_path_cached (ostree_repo_get_path (self->repo)));
+
+ if (!ostree_repo_write_config (new_repo, config, error))
+ return NULL;
+
+ /* We need to reopen to apply the parent config */
+ repo = system_ostree_repo_new (repo_dir);
+ if (!ostree_repo_open (repo, NULL, error))
+ return NULL;
+
+ return g_steal_pointer (&repo);
+}
+
+gboolean
+xdg_app_dir_install (XdgAppDir *self,
+ gboolean no_pull,
+ gboolean no_deploy,
+ const char *ref,
+ const char *remote_name,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (xdg_app_dir_use_child_repo (self))
+ {
+ g_autoptr(OstreeRepo) child_repo = NULL;
+ g_auto(GLnxLockFile) child_repo_lock = GLNX_LOCK_FILE_INIT;
+ char *empty_subpaths[] = {NULL};
+ XdgAppSystemHelper *system_helper;
+
+ if (no_pull)
+ return xdg_app_fail (error, "No-pull install not supported without root permissions");
+
+ if (no_deploy)
+ return xdg_app_fail (error, "No-deploy install not supported without root permissions");
+
+ child_repo = xdg_app_dir_create_system_child_repo (self, &child_repo_lock, error);
+ if (child_repo == NULL)
+ return FALSE;
+
+ system_helper = xdg_app_dir_get_system_helper (self);
+
+ g_assert (system_helper != NULL);
+
+ if (!xdg_app_dir_pull (self, remote_name, ref, subpaths,
+ child_repo, OSTREE_REPO_PULL_FLAGS_MIRROR,
+ progress, cancellable, error))
+ return FALSE;
+
+ if (!xdg_app_system_helper_call_deploy_sync (system_helper,
+ gs_file_get_path_cached (ostree_repo_get_path (child_repo)),
+ XDG_APP_HELPER_DEPLOY_FLAGS_NONE,
+ ref,
+ remote_name,
+ (const char *const *)(subpaths ? subpaths : empty_subpaths),
+ cancellable,
+ error))
+ return FALSE;
+
+ (void) glnx_shutil_rm_rf_at (AT_FDCWD,
+ gs_file_get_path_cached (ostree_repo_get_path (child_repo)),
+ NULL, NULL);
+
+ return TRUE;
+ }
+
+
+ if (!no_pull)
+ {
+ if (!xdg_app_dir_pull (self, remote_name, ref, subpaths, NULL, OSTREE_REPO_PULL_FLAGS_NONE, progress,
+ cancellable, error))
+ return FALSE;
+ }
+
+ if (!no_deploy)
+ {
+ if (!xdg_app_dir_deploy_install (self, ref, remote_name, subpaths,
+ cancellable, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+xdg_app_dir_update (XdgAppDir *self,
+ gboolean no_pull,
+ gboolean no_deploy,
+ const char *ref,
+ const char *remote_name,
+ const char *checksum_or_latest,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (xdg_app_dir_use_child_repo (self))
+ {
+ g_autoptr(OstreeRepo) child_repo = NULL;
+ g_auto(GLnxLockFile) child_repo_lock = GLNX_LOCK_FILE_INIT;
+ char *empty_subpaths[] = {NULL};
+ g_autofree char *pulled_checksum = NULL;
+ g_autofree char *active_checksum = NULL;
+ XdgAppSystemHelper *system_helper;
+
+ if (no_pull)
+ return xdg_app_fail (error, "No-pull update not supported without root permissions");
+
+ if (no_deploy)
+ return xdg_app_fail (error, "No-deploy update not supported without root permissions");
+
+ if (checksum_or_latest != NULL)
+ return xdg_app_fail (error, "Can't update to a specific commit without root permissions");
+
+ child_repo = xdg_app_dir_create_system_child_repo (self, &child_repo_lock, error);
+ if (child_repo == NULL)
+ return FALSE;
+
+ system_helper = xdg_app_dir_get_system_helper (self);
+
+ g_assert (system_helper != NULL);
+
+ if (!xdg_app_dir_pull (self, remote_name, ref, subpaths,
+ child_repo, OSTREE_REPO_PULL_FLAGS_MIRROR,
+ progress, cancellable, error))
+ return FALSE;
+
+ if (!ostree_repo_resolve_rev (child_repo, ref, FALSE, &pulled_checksum, error))
+ return FALSE;
+
+ active_checksum = xdg_app_dir_read_active (self, ref, NULL);
+ if (g_strcmp0 (active_checksum, pulled_checksum) != 0)
+ {
+
+ if (!xdg_app_system_helper_call_deploy_sync (system_helper,
+ gs_file_get_path_cached (ostree_repo_get_path (child_repo)),
+ XDG_APP_HELPER_DEPLOY_FLAGS_UPDATE,
+ ref,
+ remote_name,
+ (const char *const *)empty_subpaths,
+ cancellable,
+ error))
+ return FALSE;
+ }
+
+ (void) glnx_shutil_rm_rf_at (AT_FDCWD,
+ gs_file_get_path_cached (ostree_repo_get_path (child_repo)),
+ NULL, NULL);
+
+ return TRUE;
+ }
+
+
+ if (!no_pull)
+ {
+ if (!xdg_app_dir_pull (self, remote_name, ref, subpaths,
+ NULL, OSTREE_REPO_PULL_FLAGS_NONE, progress,
+ cancellable, error))
+ return FALSE;
+ }
+
+ if (!no_deploy)
+ {
+ if (!xdg_app_dir_deploy_update (self, ref, remote_name, checksum_or_latest,
+ cancellable, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
gboolean
xdg_app_dir_collect_deployed_refs (XdgAppDir *self,
@@ -2843,7 +3359,7 @@ xdg_app_dir_find_remote_ref (XdgAppDir *self,
return NULL;
}
- summary = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE);
+ summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE));
refs = g_variant_get_child_value (summary, 0);
if (app_ref && xdg_app_summary_lookup_ref (summary, app_ref, NULL))
diff --git a/common/xdg-app-dir.h b/common/xdg-app-dir.h
index c04126c..2ca8f31 100644
--- a/common/xdg-app-dir.h
+++ b/common/xdg-app-dir.h
@@ -23,6 +23,7 @@
#include <ostree.h>
+#include "libglnx/libglnx.h"
#include <xdg-app-common-types.h>
#define XDG_APP_TYPE_DIR xdg_app_dir_get_type()
@@ -47,6 +48,13 @@ typedef enum {
XDG_APP_DIR_ERROR_NOT_DEPLOYED,
} XdgAppDirErrorEnum;
+typedef enum {
+ XDG_APP_HELPER_DEPLOY_FLAGS_NONE = 0,
+ XDG_APP_HELPER_DEPLOY_FLAGS_UPDATE = 1<<0,
+} XdgAppHelperDeployFlags;
+
+#define XDG_APP_HELPER_DEPLOY_FLAGS_ALL (XDG_APP_HELPER_DEPLOY_FLAGS_UPDATE)
+
GQuark xdg_app_dir_error_quark (void);
GFile * xdg_app_get_system_base_dir_location (void);
@@ -134,6 +142,9 @@ OstreeRepo *xdg_app_dir_get_repo (XdgAppDir *self);
gboolean xdg_app_dir_ensure_path (XdgAppDir *self,
GCancellable *cancellable,
GError **error);
+gboolean xdg_app_dir_use_child_repo (XdgAppDir *self);
+gboolean xdg_app_dir_ensure_system_child_repo (XdgAppDir *self,
+ GError **error);
gboolean xdg_app_dir_ensure_repo (XdgAppDir *self,
GCancellable *cancellable,
GError **error);
@@ -154,9 +165,19 @@ gboolean xdg_app_dir_pull (XdgAppDir *self,
const char *repository,
const char *ref,
char **subpaths,
+ OstreeRepo *repo,
+ OstreeRepoPullFlags flags,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error);
+gboolean xdg_app_dir_pull_untrusted_local (XdgAppDir *self,
+ const char *src_path,
+ const char *remote_name,
+ const char *ref,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error);
gboolean xdg_app_dir_list_refs_for_name (XdgAppDir *self,
const char *kind,
const char *name,
@@ -208,6 +229,7 @@ gboolean xdg_app_dir_deploy (XdgAppDir *self,
GError **error);
gboolean xdg_app_dir_deploy_update (XdgAppDir *self,
const char *ref,
+ const char *origin,
const char *checksum,
GCancellable *cancellable,
GError **error);
@@ -217,6 +239,25 @@ gboolean xdg_app_dir_deploy_install (XdgAppDir *self,
char **subpaths,
GCancellable *cancellable,
GError **error);
+gboolean xdg_app_dir_install (XdgAppDir *self,
+ gboolean no_pull,
+ gboolean no_deploy,
+ const char *ref,
+ const char *remote_name,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error);
+gboolean xdg_app_dir_update (XdgAppDir *self,
+ gboolean no_pull,
+ gboolean no_deploy,
+ const char *ref,
+ const char *remote_name,
+ const char *checksum_or_latest,
+ char **subpaths,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error);
gboolean xdg_app_dir_undeploy (XdgAppDir *self,
const char *ref,
const char *checksum,
diff --git a/common/xdg-app-utils.c b/common/xdg-app-utils.c
index 1c1021b..23c46b5 100644
--- a/common/xdg-app-utils.c
+++ b/common/xdg-app-utils.c
@@ -2759,3 +2759,134 @@ xdg_app_pull_from_bundle (OstreeRepo *repo,
return TRUE;
}
+
+/* This allocates and locks a subdir of the tmp dir, using an existing
+ * one with the same prefix if it is not in use already. */
+gboolean
+xdg_app_allocate_tmpdir (int tmpdir_dfd,
+ const char *tmpdir_relpath,
+ const char *tmpdir_prefix,
+ char **tmpdir_name_out,
+ int *tmpdir_fd_out,
+ GLnxLockFile *file_lock_out,
+ gboolean *reusing_dir_out,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean reusing_dir = FALSE;
+ g_autofree char *tmpdir_name = NULL;
+ glnx_fd_close int tmpdir_fd = -1;
+ g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+
+ /* Look for existing tmpdir (with same prefix) to reuse */
+ if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, tmpdir_relpath ? tmpdir_relpath : ".", FALSE, &dfd_iter, error))
+ return FALSE;
+
+ while (tmpdir_name == NULL)
+ {
+ gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
+ struct dirent *dent;
+ glnx_fd_close int existing_tmpdir_fd = -1;
+ g_autoptr(GError) local_error = NULL;
+ g_autofree char *lock_name = NULL;
+
+ if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
+ return FALSE;
+
+ if (dent == NULL)
+ break;
+
+ if (!g_str_has_prefix (dent->d_name, tmpdir_prefix))
+ continue;
+
+ /* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */
+ if (dent->d_type != DT_UNKNOWN &&
+ dent->d_type != DT_DIR)
+ continue;
+
+ if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE,
+ &existing_tmpdir_fd, &local_error))
+ {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+ continue;
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
+ }
+
+ lock_name = g_strconcat (dent->d_name, "-lock", NULL);
+
+ /* We put the lock outside the dir, so we can hold the lock
+ * until the directory is fully removed */
+ if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB,
+ file_lock_out, &local_error))
+ {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ continue;
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
+ }
+
+ /* Touch the reused directory so that we don't accidentally
+ * remove it due to being old when cleaning up the tmpdir
+ */
+ (void)futimens (existing_tmpdir_fd, NULL);
+
+ /* We found an existing tmpdir which we managed to lock */
+ tmpdir_name = g_strdup (dent->d_name);
+ tmpdir_fd = glnx_steal_fd (&existing_tmpdir_fd);
+ reusing_dir = TRUE;
+ }
+
+ while (tmpdir_name == NULL)
+ {
+ g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
+ glnx_fd_close int new_tmpdir_fd = -1;
+ g_autoptr(GError) local_error = NULL;
+ g_autofree char *lock_name = NULL;
+
+ /* No existing tmpdir found, create a new */
+
+ if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
+ return FALSE;
+
+ if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
+ &new_tmpdir_fd, error))
+ return FALSE;
+
+ lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL);
+
+ /* Note, at this point we can race with another process that picks up this
+ * new directory. If that happens we need to retry, making a new directory. */
+ if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
+ file_lock_out, &local_error))
+ {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ continue;
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
+ }
+
+ tmpdir_name = g_steal_pointer (&tmpdir_name_template);
+ tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd);
+ }
+
+ if (tmpdir_name_out)
+ *tmpdir_name_out = g_steal_pointer (&tmpdir_name);
+
+ if (tmpdir_fd_out)
+ *tmpdir_fd_out = glnx_steal_fd (&tmpdir_fd);
+
+ if (reusing_dir_out)
+ *reusing_dir_out = reusing_dir;
+
+ return TRUE;
+}
diff --git a/common/xdg-app-utils.h b/common/xdg-app-utils.h
index 6715d27..b82a59e 100644
--- a/common/xdg-app-utils.h
+++ b/common/xdg-app-utils.h
@@ -321,4 +321,15 @@ gboolean xdg_app_repo_generate_appstream (OstreeRepo *repo,
GCancellable *cancellable,
GError **error);
+gboolean xdg_app_allocate_tmpdir (int tmpdir_dfd,
+ const char *tmpdir_relpath,
+ const char *tmpdir_prefix,
+ char **tmpdir_name_out,
+ int *tmpdir_fd_out,
+ GLnxLockFile *file_lock_out,
+ gboolean *reusing_dir_out,
+ GCancellable *cancellable,
+ GError **error);
+
+
#endif /* __XDG_APP_UTILS_H__ */