From d647bc105e9697d61d2aee3fa00bfcc539e1e1bd Mon Sep 17 00:00:00 2001 From: Matthew Leeds Date: Wed, 18 Sep 2019 20:02:36 -0700 Subject: [PATCH] installation: Make fetching remote refs work offline Currently flatpak_installation_fetch_remote_ref_sync() does not work offline. It returns an error when it fails to fetch the remote's summary in flatpak_dir_get_remote_state(). This is a problem since GNOME Software (or at least the Endless fork) uses this library function to display apps it finds on a USB drive (see gs_plugin_refine_item_origin() in gs-flatpak.c) and that's something that should work even offline. So this commit changes flatpak_dir_get_remote_state_optional() so that it accepts the only_cached option, and updates the call sites. Also have fetch_remote_ref_sync() use flatpak_dir_get_remote_state_optional(), which means that when we're offline we will use the xa.cache data in the ostree-metadata ref as a list of refs list instead of using a summary. However since the commit checksums are not in xa.cache, we don't have enough information to form a FlatpakRemoteRef. So also call ostree_repo_find_remotes_async() to get the commit from any LAN or USB sources that may be available. This may not be very performant but at least it only happens if the ref wasn't found in a remote summary; see https://github.com/flatpak/flatpak/issues/1862 It's sad this code is so long-winded but it's difficult to break out a helper function that could be shared with list_remotes_for_configured_remote() above. Longer term we could improve the ostree_repo_find_remotes_async() API and add options to remove the need to manually handle OstreeRepoFinder objects. Closes: #3114 Approved by: alexlarsson --- app/flatpak-builtins-create-usb.c | 2 +- common/flatpak-dir-private.h | 1 + common/flatpak-dir.c | 11 ++-- common/flatpak-installation.c | 83 +++++++++++++++++++++++++-- common/flatpak-transaction.c | 2 +- system-helper/flatpak-system-helper.c | 4 +- 6 files changed, 88 insertions(+), 15 deletions(-) diff --git a/app/flatpak-builtins-create-usb.c b/app/flatpak-builtins-create-usb.c index 1e8e0294..7d814ca6 100644 --- a/app/flatpak-builtins-create-usb.c +++ b/app/flatpak-builtins-create-usb.c @@ -664,7 +664,7 @@ flatpak_builtin_create_usb (int argc, char **argv, GCancellable *cancellable, GE /* Try to update the repo metadata by creating a FlatpakRemoteState object, * but don't fail on error because we want this to work offline. */ - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, &local_error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, &local_error); if (state == NULL) { g_printerr (_("Warning: Couldn't update repo metadata for remote ā€˜%s’: %s\n"), diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h index f98da5b4..9b146404 100644 --- a/common/flatpak-dir-private.h +++ b/common/flatpak-dir-private.h @@ -882,6 +882,7 @@ gboolean flatpak_dir_remote_make_oci_summary (FlatpakDir *self, GError **error); FlatpakRemoteState * flatpak_dir_get_remote_state_optional (FlatpakDir *self, const char *remote, + gboolean only_cached, GCancellable *cancellable, GError **error); FlatpakRemoteState * flatpak_dir_get_remote_state_local_only (FlatpakDir *self, diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index ea807c0a..f25784e1 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -4153,7 +4153,7 @@ flatpak_dir_update_appstream (FlatpakDir *self, is_oci = flatpak_dir_get_remote_oci (self, remote); - state = flatpak_dir_get_remote_state_optional (self, remote, cancellable, error); + state = flatpak_dir_get_remote_state_optional (self, remote, FALSE, cancellable, error); if (state == NULL) return FALSE; @@ -10876,10 +10876,11 @@ flatpak_dir_get_remote_state_for_summary (FlatpakDir *self, FlatpakRemoteState * flatpak_dir_get_remote_state_optional (FlatpakDir *self, const char *remote, + gboolean only_cached, GCancellable *cancellable, GError **error) { - return _flatpak_dir_get_remote_state (self, remote, TRUE, FALSE, FALSE, NULL, NULL, cancellable, error); + return _flatpak_dir_get_remote_state (self, remote, TRUE, FALSE, only_cached, NULL, NULL, cancellable, error); } @@ -10902,7 +10903,7 @@ flatpak_dir_remote_has_ref (FlatpakDir *self, g_autoptr(GError) local_error = NULL; g_autoptr(FlatpakRemoteState) state = NULL; - state = flatpak_dir_get_remote_state_optional (self, remote, NULL, &local_error); + state = flatpak_dir_get_remote_state_optional (self, remote, FALSE, NULL, &local_error); if (state == NULL) { g_debug ("Can't get state for remote %s: %s", remote, local_error->message); @@ -11275,7 +11276,7 @@ flatpak_dir_find_remote_refs (FlatpakDir *self, g_autoptr(FlatpakRemoteState) state = NULL; GPtrArray *matched_refs; - state = flatpak_dir_get_remote_state_optional (self, remote, cancellable, error); + state = flatpak_dir_get_remote_state_optional (self, remote, FALSE, cancellable, error); if (state == NULL) return NULL; @@ -11372,7 +11373,7 @@ flatpak_dir_find_remote_ref (FlatpakDir *self, g_autoptr(FlatpakRemoteState) state = NULL; g_autoptr(GError) my_error = NULL; - state = flatpak_dir_get_remote_state_optional (self, remote, cancellable, error); + state = flatpak_dir_get_remote_state_optional (self, remote, FALSE, cancellable, error); if (state == NULL) return NULL; diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c index 3cb300b2..0d9ac5fb 100644 --- a/common/flatpak-installation.c +++ b/common/flatpak-installation.c @@ -1995,7 +1995,7 @@ flatpak_installation_install_full (FlatpakInstallation *self, return NULL; } - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); if (state == NULL) return NULL; @@ -2158,7 +2158,7 @@ flatpak_installation_update_full (FlatpakInstallation *self, if (remote_name == NULL) return NULL; - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); if (state == NULL) return NULL; @@ -2392,7 +2392,7 @@ flatpak_installation_fetch_remote_size_sync (FlatpakInstallation *self, if (dir == NULL) return FALSE; - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); if (state == NULL) return FALSE; @@ -2433,7 +2433,7 @@ flatpak_installation_fetch_remote_metadata_sync (FlatpakInstallation *self, if (dir == NULL) return NULL; - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); if (state == NULL) return FALSE; @@ -2598,7 +2598,7 @@ flatpak_installation_fetch_remote_ref_sync_full (FlatpakInstallation *self, if (dir == NULL) return NULL; - state = flatpak_dir_get_remote_state (dir, remote_name, (flags & FLATPAK_QUERY_FLAGS_ONLY_CACHED) != 0, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, (flags & FLATPAK_QUERY_FLAGS_ONLY_CACHED) != 0, cancellable, error); if (state == NULL) return NULL; @@ -2624,6 +2624,77 @@ flatpak_installation_fetch_remote_ref_sync_full (FlatpakInstallation *self, coll_ref = flatpak_collection_ref_new (collection_id, ref); checksum = g_hash_table_lookup (ht, coll_ref); + /* Check LAN/USB sources too in case we're offline */ + if (checksum == NULL && collection_id != NULL && *collection_id != '\0') + { + OstreeRepo *repo; + const char * const *default_repo_finders; + g_autoptr(GAsyncResult) result = NULL; + OstreeCollectionRef ostree_coll_ref; + const OstreeCollectionRef *refs[2] = { NULL, }; + OstreeRepoFinder *finders[3] = { NULL, }; + guint finder_index = 0; + gsize i; + g_autoptr(GMainContextPopDefault) context = NULL; + g_autoptr(OstreeRepoFinder) finder_mount = NULL, finder_avahi = NULL; + + context = flatpak_main_context_new_default (); + + ostree_coll_ref.collection_id = collection_id; + ostree_coll_ref.ref_name = ref; + refs[0] = &ostree_coll_ref; + + if (!flatpak_dir_ensure_repo (dir, cancellable, error)) + return NULL; + repo = flatpak_dir_get_repo (dir); + default_repo_finders = ostree_repo_get_default_repo_finders (repo); + if (default_repo_finders == NULL || g_strv_contains (default_repo_finders, "mount")) + { + finder_mount = OSTREE_REPO_FINDER (ostree_repo_finder_mount_new (NULL)); + finders[finder_index++] = finder_mount; + } + + if (default_repo_finders == NULL || g_strv_contains (default_repo_finders, "lan")) + { + g_autoptr(GError) local_error = NULL; + finder_avahi = OSTREE_REPO_FINDER (ostree_repo_finder_avahi_new (context)); + finders[finder_index++] = finder_avahi; + + /* The Avahi finder may fail to start on, for example, a CI server. */ + ostree_repo_finder_avahi_start (OSTREE_REPO_FINDER_AVAHI (finder_avahi), &local_error); + if (local_error != NULL) + { + finders[--finder_index] = NULL; + g_clear_object (&finder_avahi); + } + } + + if (finders[0] != NULL) + { + g_auto(OstreeRepoFinderResultv) results = NULL; + + ostree_repo_find_remotes_async (repo, (const OstreeCollectionRef * const *)refs, + NULL, finders, NULL, cancellable, async_result_cb, &result); + while (result == NULL) + g_main_context_iteration (context, TRUE); + + results = ostree_repo_find_remotes_finish (repo, result, error); + + if (finder_avahi != NULL) + ostree_repo_finder_avahi_stop (OSTREE_REPO_FINDER_AVAHI (finder_avahi)); + + if (results == NULL) + return NULL; + + for (i = 0; results[i] != NULL; i++) + { + checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ostree_coll_ref); + if (checksum != NULL) + break; + } + } + } + /* If there was not a match, it may be because the collection ID is * not set in the local configuration, or it is wrong, so we resort to * trying to match just the ref name */ @@ -2814,7 +2885,7 @@ flatpak_installation_list_remote_related_refs_sync (FlatpakInstallation *self, if (dir == NULL) return NULL; - state = flatpak_dir_get_remote_state_optional (dir, remote_name, cancellable, error); + state = flatpak_dir_get_remote_state_optional (dir, remote_name, FALSE, cancellable, error); if (state == NULL) return NULL; diff --git a/common/flatpak-transaction.c b/common/flatpak-transaction.c index 402b4d00..9dfbe99a 100644 --- a/common/flatpak-transaction.c +++ b/common/flatpak-transaction.c @@ -1412,7 +1412,7 @@ flatpak_transaction_ensure_remote_state (FlatpakTransaction *self, if (state) return flatpak_remote_state_ref (state); - state = flatpak_dir_get_remote_state_optional (priv->dir, remote, NULL, error); + state = flatpak_dir_get_remote_state_optional (priv->dir, remote, FALSE, NULL, error); if (state) g_hash_table_insert (priv->remote_states, state->remote_name, flatpak_remote_state_ref (state)); diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c index 4f90c866..7d97c027 100644 --- a/system-helper/flatpak-system-helper.c +++ b/system-helper/flatpak-system-helper.c @@ -645,7 +645,7 @@ handle_deploy (FlatpakSystemHelper *object, return TRUE; } - state = flatpak_dir_get_remote_state_optional (system, arg_origin, NULL, &error); + state = flatpak_dir_get_remote_state_optional (system, arg_origin, FALSE, NULL, &error); if (state == NULL) { flatpak_invocation_return_error (invocation, error, "Error getting remote state"); @@ -885,7 +885,7 @@ handle_deploy_appstream (FlatpakSystemHelper *object, return TRUE; } - state = flatpak_dir_get_remote_state_optional (system, arg_origin, NULL, &error); + state = flatpak_dir_get_remote_state_optional (system, arg_origin, FALSE, NULL, &error); if (state == NULL) { flatpak_invocation_return_error (invocation, error, "Error getting remote state");