From 0eeaad76ec79562ea3790bb377d847d5be02182f Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 25 Mar 2022 09:41:22 +0000 Subject: [PATCH] Modernize and refactor the requirement checking code Before we had FuInstallTask which represented the specific install action (install foo on bar), FuEngineRequest which described why the install task was created, and FuEngine. The latter being both reposible for calling *into* FuInstallTask and also *from* FuInstallTask as well. Depending on the code, we had XbNodes of component and release, *and* FwupdReleases as releases and lots of duplicated code to marshall the former into various forms of the latter. Create a FuRelease wrapper around FwupdRelease to replace FuInstallTask and to simplify the code. Make the FuRelease builder do the device requirement checks, and then use the engine to do checks requiring engine state and context. --- src/fu-engine.c | 1054 +++++++++++----------------------------- src/fu-engine.h | 35 +- src/fu-install-task.c | 551 --------------------- src/fu-install-task.h | 32 -- src/fu-main.c | 215 ++++++--- src/fu-release.c | 1067 +++++++++++++++++++++++++++++++++++++++++ src/fu-release.h | 64 +++ src/fu-self-test.c | 482 +++++++++++++------ src/fu-tool.c | 45 +- src/meson.build | 2 +- 10 files changed, 1922 insertions(+), 1625 deletions(-) delete mode 100644 src/fu-install-task.c delete mode 100644 src/fu-install-task.h create mode 100644 src/fu-release.c create mode 100644 src/fu-release.h diff --git a/src/fu-engine.c b/src/fu-engine.c index 74f365be2..c7dbeb860 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -65,6 +65,7 @@ #include "fu-plugin.h" #include "fu-progress.h" #include "fu-quirks.h" +#include "fu-release.h" #include "fu-remote-list.h" #include "fu-security-attr.h" #include "fu-security-attrs-private.h" @@ -356,120 +357,6 @@ fu_engine_device_changed_cb(FuDeviceList *device_list, FuDevice *device, FuEngin fu_engine_emit_device_changed(self, fu_device_get_id(device)); } -/* convert hex and decimal versions to dotted style */ -static gchar * -fu_engine_get_release_version(FuEngine *self, FuDevice *dev, XbNode *rel, GError **error) -{ - FwupdVersionFormat fmt = fu_device_get_version_format(dev); - const gchar *version; - guint64 ver_uint32; - - /* get version */ - version = xb_node_get_attr(rel, "version"); - if (version == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "version unset"); - return NULL; - } - - /* already dotted notation */ - if (g_strstr_len(version, -1, ".") != NULL) - return g_strdup(version); - - /* don't touch my version! */ - if (fmt == FWUPD_VERSION_FORMAT_PLAIN) - return g_strdup(version); - - /* parse as integer */ - ver_uint32 = fu_common_strtoull(version); - if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN || ver_uint32 == 0 || ver_uint32 > G_MAXUINT32) - return g_strdup(version); - - /* convert to dotted decimal */ - return fu_common_version_from_uint32((guint32)ver_uint32, fmt); -} - -static gint -fu_engine_scheme_compare_cb(gconstpointer a, gconstpointer b, gpointer user_data) -{ - FuEngine *self = FU_ENGINE(user_data); - const gchar *location1 = *((const gchar **)a); - const gchar *location2 = *((const gchar **)b); - g_autofree gchar *scheme1 = fu_common_uri_get_scheme(location1); - g_autofree gchar *scheme2 = fu_common_uri_get_scheme(location2); - guint prio1 = fu_config_get_uri_scheme_prio(self->config, scheme1); - guint prio2 = fu_config_get_uri_scheme_prio(self->config, scheme2); - if (prio1 < prio2) - return -1; - if (prio1 > prio2) - return 1; - return 0; -} - -static gboolean -fu_engine_set_release_from_artifact(FuEngine *self, - FwupdRelease *rel, - FwupdRemote *remote, - XbNode *artifact, - GError **error) -{ - const gchar *filename; - guint64 size; - g_autoptr(GPtrArray) locations = NULL; - g_autoptr(GPtrArray) checksums = NULL; - - /* filename */ - filename = xb_node_query_text(artifact, "filename", NULL); - if (filename != NULL) - fwupd_release_set_filename(rel, filename); - - /* location */ - locations = xb_node_query(artifact, "location", 0, NULL); - if (locations != NULL) { - for (guint i = 0; i < locations->len; i++) { - XbNode *n = g_ptr_array_index(locations, i); - g_autofree gchar *scheme = NULL; - - /* check the scheme is allowed */ - scheme = fu_common_uri_get_scheme(xb_node_get_text(n)); - if (scheme != NULL) { - guint prio = fu_config_get_uri_scheme_prio(self->config, scheme); - if (prio == G_MAXUINT) - continue; - } - - /* build the complete URI */ - if (remote != NULL) { - g_autofree gchar *uri = NULL; - uri = fwupd_remote_build_firmware_uri(remote, - xb_node_get_text(n), - NULL); - if (uri != NULL) { - fwupd_release_add_location(rel, uri); - continue; - } - } - fwupd_release_add_location(rel, xb_node_get_text(n)); - } - } - - /* checksum */ - checksums = xb_node_query(artifact, "checksum", 0, NULL); - if (checksums != NULL) { - for (guint i = 0; i < checksums->len; i++) { - XbNode *n = g_ptr_array_index(checksums, i); - fwupd_release_add_checksum(rel, xb_node_get_text(n)); - } - } - - /* size */ - size = xb_node_query_text_as_uint(artifact, "size[@type='installed']", NULL); - if (size != G_MAXUINT64) - fwupd_release_set_size(rel, size); - - /* success */ - return TRUE; -} - static gchar * fu_engine_request_get_localized_xpath(FuEngineRequest *request, const gchar *element) { @@ -491,11 +378,9 @@ fu_engine_request_get_localized_xpath(FuEngineRequest *request, const gchar *ele /* add any client-side BKC tags */ static gboolean -fu_engine_add_local_release_metadata(FuEngine *self, - FuDevice *dev, - FwupdRelease *rel, - GError **error) +fu_engine_add_local_release_metadata(FuEngine *self, FuRelease *release, GError **error) { + FuDevice *dev = fu_release_get_device(release); GPtrArray *guids = fu_device_get_guids(dev); g_autoptr(XbQuery) query = NULL; g_autoptr(GError) error_query = NULL; @@ -528,7 +413,7 @@ fu_engine_add_local_release_metadata(FuEngine *self, xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 1, - fwupd_release_get_version(rel), + fu_release_get_version(release), NULL); tags = xb_silo_query_with_context(self->silo, query, &context, &error_local); #else @@ -536,7 +421,7 @@ fu_engine_add_local_release_metadata(FuEngine *self, g_prefix_error(error, "failed to bind GUID: "); return FALSE; } - if (!xb_query_bind_str(query, 1, fwupd_release_get_version(rel), error)) { + if (!xb_query_bind_str(query, 1, fu_release_get_version(release), error)) { g_prefix_error(error, "failed to bind version: "); return FALSE; } @@ -551,7 +436,7 @@ fu_engine_add_local_release_metadata(FuEngine *self, } for (guint j = 0; j < tags->len; j++) { XbNode *tag = g_ptr_array_index(tags, j); - fwupd_release_add_tag(rel, xb_node_get_text(tag)); + fu_release_add_tag(release, xb_node_get_text(tag)); } } @@ -559,245 +444,49 @@ fu_engine_add_local_release_metadata(FuEngine *self, return TRUE; } -static gboolean -fu_engine_set_release_from_appstream(FuEngine *self, - FuEngineRequest *request, - FuDevice *dev, - FwupdRelease *rel, - XbNode *component, - XbNode *release, - GError **error) +static void +fu_engine_release_remote_id_changed_cb(FuRelease *release, GParamSpec *pspec, FuEngine *self) { - FwupdRemote *remote = NULL; - const gchar *tmp; - const gchar *remote_id; - guint64 tmp64; - g_autofree gchar *description_xpath = NULL; - g_autofree gchar *name_xpath = NULL; - g_autofree gchar *namevs_xpath = NULL; - g_autofree gchar *summary_xpath = NULL; - g_autofree gchar *version_rel = NULL; - g_autoptr(GPtrArray) cats = NULL; - g_autoptr(GPtrArray) tags = NULL; - g_autoptr(GPtrArray) issues = NULL; - g_autoptr(XbNode) artifact = NULL; - g_autoptr(XbNode) description = NULL; + FwupdRemote *remote; + const gchar *remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); - /* set from the component */ - tmp = xb_node_query_text(component, "id", NULL); - if (tmp != NULL) - fwupd_release_set_appstream_id(rel, tmp); - tmp = xb_node_query_text(component, "url[@type='homepage']", NULL); - if (tmp != NULL) - fwupd_release_set_homepage(rel, tmp); - tmp = xb_node_query_text(component, "project_license", NULL); - if (tmp != NULL) - fwupd_release_set_license(rel, tmp); - name_xpath = fu_engine_request_get_localized_xpath(request, "name"); - tmp = xb_node_query_text(component, name_xpath, NULL); - if (tmp != NULL) - fwupd_release_set_name(rel, tmp); - summary_xpath = fu_engine_request_get_localized_xpath(request, "summary"); - tmp = xb_node_query_text(component, summary_xpath, NULL); - if (tmp != NULL) - fwupd_release_set_summary(rel, tmp); - namevs_xpath = fu_engine_request_get_localized_xpath(request, "name_variant_suffix"); - tmp = xb_node_query_text(component, namevs_xpath, NULL); - if (tmp != NULL) - fwupd_release_set_name_variant_suffix(rel, tmp); - tmp = xb_node_query_text(component, "branch", NULL); - if (tmp != NULL) - fwupd_release_set_branch(rel, tmp); - tmp = xb_node_query_text(component, "developer_name", NULL); - if (tmp != NULL) - fwupd_release_set_vendor(rel, tmp); + if (remote_id == NULL) + return; + remote = fu_remote_list_get_by_id(self->remote_list, remote_id); + if (remote == NULL) { + g_warning("no remote found for %s", remote_id); + return; + } + fu_release_set_remote(release, remote); +} - /* refresh the device and release to the new version format too */ - fu_engine_md_refresh_device_from_component(self, dev, component); +static gboolean +fu_engine_load_release(FuEngine *self, + FuRelease *release, + XbNode *component, + XbNode *rel, + FwupdInstallFlags install_flags, + GError **error) +{ + /* load release from XML */ + fu_release_set_config(release, self->config); - /* the version is fixed up at runtime */ - version_rel = fu_engine_get_release_version(self, dev, release, error); - if (version_rel == NULL) + /* set the FwupdRemote when the remote ID is set */ + g_signal_connect(FU_RELEASE(release), + "notify::remote-id", + G_CALLBACK(fu_engine_release_remote_id_changed_cb), + self); + + /* requirements we can check without the daemon */ + if (!fu_release_load(release, component, rel, install_flags, error)) return FALSE; - fwupd_release_set_version(rel, version_rel); - /* optional release ID -- currently a integer but maybe namespaced in the future */ - fwupd_release_set_id(rel, xb_node_get_attr(release, "id")); - - /* find the remote */ - remote_id = xb_node_query_text(component, "../custom/value[@key='fwupd::RemoteId']", NULL); - if (remote_id != NULL) { - fwupd_release_set_remote_id(rel, remote_id); - remote = fu_remote_list_get_by_id(self->remote_list, remote_id); - if (remote == NULL) - g_warning("no remote found for release %s", version_rel); - } - tmp = xb_node_query_text(component, "../custom/value[@key='LVFS::Distributor']", NULL); - if (g_strcmp0(tmp, "community") == 0) - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_IS_COMMUNITY); - artifact = xb_node_query_first(release, "artifacts/artifact", NULL); - if (artifact != NULL) { - if (!fu_engine_set_release_from_artifact(self, rel, remote, artifact, error)) - return FALSE; - } - description_xpath = fu_engine_request_get_localized_xpath(request, "description"); - description = xb_node_query_first(release, description_xpath, NULL); - if (description != NULL) { - g_autofree gchar *xml = NULL; - g_autoptr(GString) str = NULL; - xml = xb_node_export(description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL); - str = g_string_new(xml); - if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_AFFECTS_FDE) && request != NULL && - !fu_engine_request_has_feature_flag(request, FWUPD_FEATURE_FLAG_FDE_WARNING)) { - g_string_prepend( - str, - "

Some of the platform secrets may be invalidated when " - "updating this firmware. Please ensure you have the volume " - "recovery key before continuing.

"); - } - if (fwupd_release_has_flag(rel, FWUPD_RELEASE_FLAG_IS_COMMUNITY) && - request != NULL && - !fu_engine_request_has_feature_flag(request, - FWUPD_FEATURE_FLAG_COMMUNITY_TEXT)) { - g_string_prepend( - str, - "

This firmware is provided by LVFS community " - "members and is not provided (or supported) by the original " - "hardware vendor. " - "Installing this update may also void any device warranty.

"); - } - if (str->len > 0) - fwupd_release_set_description(rel, str->str); - } - if (artifact == NULL) { - tmp = xb_node_query_text(release, "location", NULL); - if (tmp != NULL) { - g_autofree gchar *uri = NULL; - if (remote != NULL) - uri = fwupd_remote_build_firmware_uri(remote, tmp, NULL); - if (uri == NULL) - uri = g_strdup(tmp); - fwupd_release_add_location(rel, uri); - } - } - if (fwupd_release_get_locations(rel)->len == 0 && remote != NULL && - fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DIRECTORY) { - tmp = xb_node_query_text(component, - "../custom/value[@key='fwupd::FilenameCache']", - NULL); - if (tmp != NULL) { - g_autofree gchar *uri = g_strdup_printf("file://%s", tmp); - fwupd_release_add_location(rel, uri); - } - } - if (artifact == NULL) { - tmp = xb_node_query_text(release, "checksum[@target='content']", NULL); - if (tmp != NULL) - fwupd_release_set_filename(rel, tmp); - } - tmp = xb_node_query_text(release, "url[@type='details']", NULL); - if (tmp != NULL) - fwupd_release_set_details_url(rel, tmp); - tmp = xb_node_query_text(release, "url[@type='source']", NULL); - if (tmp != NULL) - fwupd_release_set_source_url(rel, tmp); - if (artifact == NULL) { - g_autoptr(GPtrArray) checksums = NULL; - checksums = xb_node_query(release, "checksum[@target='container']", 0, NULL); - if (checksums != NULL) { - for (guint i = 0; i < checksums->len; i++) { - XbNode *n = g_ptr_array_index(checksums, i); - if (xb_node_get_text(n) == NULL) - continue; - fwupd_release_add_checksum(rel, xb_node_get_text(n)); - } - } - } - if (artifact == NULL) { - tmp64 = xb_node_query_text_as_uint(release, "size[@type='installed']", NULL); - if (tmp64 != G_MAXUINT64) - fwupd_release_set_size(rel, tmp64); - } - if (fwupd_release_get_size(rel) == 0) { - GBytes *sz = xb_node_get_data(release, "fwupd::ReleaseSize"); - if (sz != NULL) { - const guint64 *sizeptr = g_bytes_get_data(sz, NULL); - fwupd_release_set_size(rel, *sizeptr); - } - } - tmp = xb_node_get_attr(release, "urgency"); - if (tmp != NULL) - fwupd_release_set_urgency(rel, fwupd_release_urgency_from_string(tmp)); - tmp64 = xb_node_get_attr_as_uint(release, "install_duration"); - if (tmp64 != G_MAXUINT64) - fwupd_release_set_install_duration(rel, tmp64); - tmp64 = xb_node_get_attr_as_uint(release, "timestamp"); - if (tmp64 != G_MAXUINT64) - fwupd_release_set_created(rel, tmp64); - cats = xb_node_query(component, "categories/category", 0, NULL); - if (cats != NULL) { - for (guint i = 0; i < cats->len; i++) { - XbNode *n = g_ptr_array_index(cats, i); - fwupd_release_add_category(rel, xb_node_get_text(n)); - } - } - tags = xb_node_query(component, "tags/tag[@namespace=$'lvfs']", 0, NULL); - if (tags != NULL) { - for (guint i = 0; i < tags->len; i++) { - XbNode *tag = g_ptr_array_index(tags, i); - fwupd_release_add_tag(rel, xb_node_get_text(tag)); - } - } - issues = xb_node_query(component, "issues/issue", 0, NULL); - if (issues != NULL) { - for (guint i = 0; i < issues->len; i++) { - XbNode *n = g_ptr_array_index(issues, i); - fwupd_release_add_issue(rel, xb_node_get_text(n)); - } - } - tmp = xb_node_query_text(component, "screenshots/screenshot/caption", NULL); - if (tmp != NULL) - fwupd_release_set_detach_caption(rel, tmp); - tmp = xb_node_query_text(component, "screenshots/screenshot/image", NULL); - if (tmp != NULL) { - if (remote != NULL) { - g_autofree gchar *img = NULL; - img = fwupd_remote_build_firmware_uri(remote, tmp, error); - if (img == NULL) - return FALSE; - fwupd_release_set_detach_image(rel, img); - } else { - fwupd_release_set_detach_image(rel, tmp); - } - } - tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); - if (tmp != NULL) - fwupd_release_set_protocol(rel, tmp); - tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateMessage']", NULL); - if (tmp != NULL) - fwupd_release_set_update_message(rel, tmp); - tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateImage']", NULL); - if (tmp != NULL) { - if (remote != NULL) { - g_autofree gchar *img = NULL; - img = fwupd_remote_build_firmware_uri(remote, tmp, error); - if (img == NULL) - return FALSE; - fwupd_release_set_update_image(rel, img); - } else { - fwupd_release_set_update_image(rel, tmp); - } - } - if (xb_node_get_attr(component, "date_eol") != NULL) - fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_END_OF_LIFE); - - /* sort the locations by scheme */ - g_ptr_array_sort_with_data(fwupd_release_get_locations(rel), - fu_engine_scheme_compare_cb, - self); + /* additional requirements */ + if (!fu_engine_check_requirements(self, release, install_flags, error)) + return FALSE; /* add any client-side BKC tags */ - if (!fu_engine_add_local_release_metadata(self, dev, rel, error)) + if (!fu_engine_add_local_release_metadata(self, release, error)) return FALSE; /* success */ @@ -1845,12 +1534,14 @@ fu_engine_check_requirement_client(FuEngine *self, static gboolean fu_engine_check_requirement(FuEngine *self, - FuEngineRequest *request, + FuRelease *release, XbNode *req, - FuDevice *device, FwupdInstallFlags flags, GError **error) { + FuDevice *device = fu_release_get_device(release); + FuEngineRequest *request = fu_release_get_request(release); + /* ensure component requirement */ if (g_strcmp0(xb_node_get_element(req), "id") == 0) return fu_engine_check_requirement_id(self, req, error); @@ -1880,10 +1571,10 @@ fu_engine_check_requirement(FuEngine *self, } gboolean -fu_engine_check_trust(FuEngine *self, FuInstallTask *task, GError **error) +fu_engine_check_trust(FuEngine *self, FuRelease *release, GError **error) { if (fu_config_get_only_trusted(self->config) && - (fu_install_task_get_trust_flags(task) & FWUPD_TRUST_FLAG_PAYLOAD) == 0) { + (fu_release_get_trust_flags(release) & FWUPD_TRUST_FLAG_PAYLOAD) == 0) { g_autofree gchar *sysconfdir = fu_common_get_path(FU_PATH_KIND_SYSCONFDIR_PKG); g_autofree gchar *fn = g_build_filename(sysconfdir, "daemon.conf", NULL); g_set_error(error, @@ -1899,14 +1590,13 @@ fu_engine_check_trust(FuEngine *self, FuInstallTask *task, GError **error) static gboolean fu_engine_check_soft_requirement(FuEngine *self, - FuEngineRequest *request, + FuRelease *release, XbNode *req, - FuDevice *device, FwupdInstallFlags flags, GError **error) { g_autoptr(GError) error_local = NULL; - if (!fu_engine_check_requirement(self, request, req, device, flags, &error_local)) { + if (!fu_engine_check_requirement(self, release, req, flags, &error_local)) { if (flags & FWUPD_INSTALL_FLAG_FORCE) { g_debug("ignoring soft-requirement due to --force: %s", error_local->message); @@ -1920,73 +1610,30 @@ fu_engine_check_soft_requirement(FuEngine *self, gboolean fu_engine_check_requirements(FuEngine *self, - FuEngineRequest *request, - FuInstallTask *task, + FuRelease *release, FwupdInstallFlags flags, GError **error) { - FuDevice *device = fu_install_task_get_device(task); - const gchar *protocol; - g_autoptr(GError) error_local = NULL; - g_autoptr(GPtrArray) reqs_hard = NULL; - g_autoptr(GPtrArray) reqs_soft = NULL; + GPtrArray *reqs; - /* all install task checks require a device */ - if (device != NULL && - fu_engine_request_get_kind(request) == FU_ENGINE_REQUEST_KIND_ACTIVE) { - if (!fu_install_task_check_requirements(task, flags, error)) - return FALSE; - } - - /* do engine checks */ - reqs_hard = - xb_node_query(fu_install_task_get_component(task), "requires/*", 0, &error_local); - if (reqs_hard != NULL) { - for (guint i = 0; i < reqs_hard->len; i++) { - XbNode *req = g_ptr_array_index(reqs_hard, i); - if (!fu_engine_check_requirement(self, request, req, device, flags, error)) + /* hard requirements */ + reqs = fu_release_get_hard_reqs(release); + if (reqs != NULL) { + for (guint i = 0; i < reqs->len; i++) { + XbNode *req = g_ptr_array_index(reqs, i); + if (!fu_engine_check_requirement(self, release, req, flags, error)) return FALSE; } - } else if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && - !g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { - g_propagate_error(error, g_steal_pointer(&error_local)); - return FALSE; } /* soft requirements */ - g_clear_error(&error_local); - reqs_soft = xb_node_query(fu_install_task_get_component(task), - "suggests/*|recommends/*", - 0, - &error_local); - if (reqs_soft != NULL) { - for (guint i = 0; i < reqs_soft->len; i++) { - XbNode *req = g_ptr_array_index(reqs_soft, i); - if (!fu_engine_check_soft_requirement(self, - request, - req, - device, - flags, - error)) + reqs = fu_release_get_soft_reqs(release); + if (reqs != NULL) { + for (guint i = 0; i < reqs->len; i++) { + XbNode *req = g_ptr_array_index(reqs, i); + if (!fu_engine_check_soft_requirement(self, release, req, flags, error)) return FALSE; } - } else if (!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && - !g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { - g_propagate_error(error, g_steal_pointer(&error_local)); - return FALSE; - } - - /* ensure protocol is registered with the device */ - protocol = xb_node_query_text(fu_install_task_get_component(task), - "custom/value[@key='LVFS::UpdateProtocol']", - NULL); - if (protocol != NULL && !fu_device_has_protocol(device, protocol)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "protocol %s is not supported by device", - protocol); - return FALSE; } /* success */ @@ -2281,11 +1928,22 @@ fu_engine_composite_cleanup(FuEngine *self, GPtrArray *devices, GError **error) return TRUE; } +static gint +fu_engine_sort_release_versions_cb(gconstpointer a, gconstpointer b) +{ + FuRelease *na = *((FuRelease **)a); + FuRelease *nb = *((FuRelease **)b); + FuDevice *device = fu_release_get_device(na); + const gchar *va = fu_release_get_version(na); + const gchar *vb = fu_release_get_version(nb); + return fu_common_vercmp_full(va, vb, fu_device_get_version_format(device)); +} + /** - * fu_engine_install_tasks: + * fu_engine_install_releases: * @self: a #FuEngine * @request: a #FuEngineRequest - * @install_tasks: (element-type FuInstallTask): a device + * @releases: (element-type FuRelease): a device * @blob_cab: the #GBytes of the .cab file * @flags: install flags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE * @error: (nullable): optional return location for an error @@ -2299,15 +1957,14 @@ fu_engine_composite_cleanup(FuEngine *self, GPtrArray *devices, GError **error) * Returns: %TRUE for success **/ gboolean -fu_engine_install_tasks(FuEngine *self, - FuEngineRequest *request, - GPtrArray *install_tasks, - GBytes *blob_cab, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_engine_install_releases(FuEngine *self, + FuEngineRequest *request, + GPtrArray *releases, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) { - FwupdFeatureFlags feature_flags = fu_engine_request_get_feature_flags(request); g_autoptr(FuIdleLocker) locker = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_new = NULL; @@ -2316,14 +1973,17 @@ fu_engine_install_tasks(FuEngine *self, locker = fu_idle_locker_new(self->idle, "update"); g_assert(locker != NULL); + /* install these in the right order */ + g_ptr_array_sort(releases, fu_engine_sort_release_versions_cb); + /* notify the plugins about the composite action */ devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - for (guint i = 0; i < install_tasks->len; i++) { - FuInstallTask *task = g_ptr_array_index(install_tasks, i); + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); g_debug("composite update %u: %s", i + 1, - fu_device_get_id(fu_install_task_get_device(task))); - g_ptr_array_add(devices, g_object_ref(fu_install_task_get_device(task))); + fu_device_get_id(fu_release_get_device(release))); + g_ptr_array_add(devices, g_object_ref(fu_release_get_device(release))); } if (!fu_engine_composite_prepare(self, devices, error)) { g_prefix_error(error, "failed to prepare composite action: "); @@ -2331,16 +1991,15 @@ fu_engine_install_tasks(FuEngine *self, } /* all authenticated, so install all the things */ - fu_progress_set_steps(progress, install_tasks->len); - for (guint i = 0; i < install_tasks->len; i++) { - FuInstallTask *task = g_ptr_array_index(install_tasks, i); - if (!fu_engine_install(self, - task, - blob_cab, - fu_progress_get_child(progress), - flags, - feature_flags, - error)) { + fu_progress_set_steps(progress, releases->len); + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); + if (!fu_engine_install_release(self, + release, + blob_cab, + fu_progress_get_child(progress), + flags, + error)) { g_autoptr(GError) error_local = NULL; if (!fu_engine_composite_cleanup(self, devices, &error_local)) { g_warning("failed to cleanup failed composite action: %s", @@ -2352,9 +2011,9 @@ fu_engine_install_tasks(FuEngine *self, } /* set all the device statuses back to unknown */ - for (guint i = 0; i < install_tasks->len; i++) { - FuInstallTask *task = g_ptr_array_index(install_tasks, i); - FuDevice *device = fu_install_task_get_device(task); + for (guint i = 0; i < releases->len; i++) { + FuRelease *release = g_ptr_array_index(releases, i); + FuDevice *device = fu_release_get_device(release); fwupd_device_set_status(FWUPD_DEVICE(device), FWUPD_STATUS_UNKNOWN); } @@ -2385,27 +2044,23 @@ fu_engine_install_tasks(FuEngine *self, return TRUE; } -static FwupdRelease * -fu_engine_create_release_metadata(FuEngine *self, - FuDevice *device, - FuPlugin *plugin, - GError **error) +static gboolean +fu_engine_add_release_metadata(FuEngine *self, FuRelease *release, FuPlugin *plugin, GError **error) { GPtrArray *metadata_sources; - g_autoptr(FwupdRelease) release = fwupd_release_new(); g_autoptr(GHashTable) metadata_device = NULL; g_autoptr(GHashTable) metadata_hash = NULL; /* build the version metadata */ metadata_hash = fu_engine_get_report_metadata(self, error); if (metadata_hash == NULL) - return NULL; - fwupd_release_add_metadata(release, metadata_hash); + return FALSE; + fu_release_add_metadata(release, metadata_hash); if (fu_plugin_get_report_metadata(plugin) != NULL) - fwupd_release_add_metadata(release, fu_plugin_get_report_metadata(plugin)); - metadata_device = fu_device_report_metadata_pre(device); + fu_release_add_metadata(release, fu_plugin_get_report_metadata(plugin)); + metadata_device = fu_device_report_metadata_pre(fu_release_get_device(release)); if (metadata_device != NULL) - fwupd_release_add_metadata(release, metadata_device); + fu_release_add_metadata(release, metadata_device); /* allow other plugins to contribute metadata too */ metadata_sources = fu_plugin_get_rules(plugin, FU_PLUGIN_RULE_METADATA_SOURCE); @@ -2426,12 +2081,12 @@ fu_engine_create_release_metadata(FuEngine *self, } if (fu_plugin_get_report_metadata(plugin_tmp) != NULL) { fwupd_release_add_metadata( - release, + FWUPD_RELEASE(release), fu_plugin_get_report_metadata(plugin_tmp)); } } } - return g_steal_pointer(&release); + return TRUE; } static gboolean @@ -2605,32 +2260,92 @@ fu_engine_schedule_update(FuEngine *self, return fu_engine_offline_setup(error); } -static gboolean +/** + * fu_engine_install_release: + * @self: a #FuEngine + * @release: a #FuRelease + * @blob_cab: the #GBytes of the .cab file + * @progress: a #FuProgress + * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER + * @error: (nullable): optional return location for an error + * + * Installs a specific release on a device. + * + * By this point all the requirements and tests should have been done in + * fu_engine_check_requirements() so this should not fail before running + * the plugin loader. + * + * Returns: %TRUE for success + **/ +gboolean fu_engine_install_release(FuEngine *self, - FuDevice *device_orig, - XbNode *component, - XbNode *rel, + FuRelease *release, + GBytes *blob_cab, FuProgress *progress, FwupdInstallFlags flags, - FwupdFeatureFlags feature_flags, GError **error) { + FuDevice *device_orig = fu_release_get_device(release); + FuEngineRequest *request = fu_release_get_request(release); FuPlugin *plugin; + FwupdFeatureFlags feature_flags = FWUPD_FEATURE_FLAG_NONE; FwupdVersionFormat fmt; GBytes *blob_fw; const gchar *tmp; + const gchar *version_rel; g_autofree gchar *version_orig = NULL; - g_autofree gchar *version_rel = NULL; + g_autoptr(FuDevice) device = NULL; g_autoptr(FuDevice) device_tmp = NULL; - g_autoptr(FuDevice) device = g_object_ref(device_orig); g_autoptr(GBytes) blob_fw2 = NULL; g_autoptr(GError) error_local = NULL; + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(FU_IS_RELEASE(release), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(blob_cab != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* optional for tests */ + if (request != NULL) + feature_flags = fu_engine_request_get_feature_flags(request); + + /* not in bootloader mode */ + device = g_object_ref(fu_release_get_device(release)); + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + /* both optional; the plugin can specify a fallback */ + tmp = fwupd_release_get_detach_caption(FWUPD_RELEASE(release)); + if (tmp != NULL) + fu_device_set_update_message(device, tmp); + tmp = fwupd_release_get_detach_image(FWUPD_RELEASE(release)); + if (tmp != NULL) + fu_device_set_update_image(device, tmp); + } + + /* schedule this for the next reboot if not in system-update.target, + * but first check if allowed on battery power */ + if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 && !fu_engine_is_running_offline(self)) { + FuPlugin *plugin_tmp = + fu_plugin_list_find_by_name(self->plugin_list, "upower", NULL); + if (plugin_tmp != NULL) { + if (!fu_plugin_runner_prepare(plugin_tmp, device, flags, error)) + return FALSE; + } + fu_progress_set_status(progress, FWUPD_STATUS_SCHEDULING); + if (!fu_engine_add_release_metadata(self, release, plugin_tmp, error)) + return FALSE; + return fu_engine_schedule_update(self, + device, + FWUPD_RELEASE(release), + blob_cab, + flags, + error); + } + /* set this for the callback */ self->write_history = (flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0; /* get per-release firmware blob */ - blob_fw = xb_node_get_data(rel, "fwupd::FirmwareBlob"); + blob_fw = fu_release_get_fw_blob(release); if (blob_fw == NULL) { g_set_error_literal(error, FWUPD_ERROR, @@ -2640,12 +2355,12 @@ fu_engine_install_release(FuEngine *self, } /* use a bubblewrap helper script to build the firmware */ - tmp = g_object_get_data(G_OBJECT(component), "fwupd::BuilderScript"); + tmp = fu_release_get_builder_script(release); if (tmp != NULL) { - const gchar *tmp2 = g_object_get_data(G_OBJECT(component), "fwupd::BuilderOutput"); - if (tmp2 == NULL) - tmp2 = "firmware.bin"; - blob_fw2 = fu_common_firmware_builder(blob_fw, tmp, tmp2, error); + blob_fw2 = fu_common_firmware_builder(blob_fw, + tmp, + fu_release_get_builder_output(release), + error); if (blob_fw2 == NULL) return FALSE; } else { @@ -2658,27 +2373,11 @@ fu_engine_install_release(FuEngine *self, if (plugin == NULL) return FALSE; - /* schedule this for the next reboot if not in system-update.target, - * but first check if allowed on battery power */ - version_rel = fu_engine_get_release_version(self, device, rel, error); - if (version_rel == NULL) { - g_prefix_error(error, "failed to get release version: "); - return FALSE; - } - /* add device to database */ if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { - g_autoptr(FwupdRelease) release_tmp = NULL; - release_tmp = fu_engine_create_release_metadata(self, device, plugin, error); - if (release_tmp == NULL) + if (!fu_engine_add_release_metadata(self, release, plugin, error)) return FALSE; - tmp = xb_node_query_text(component, - "releases/release/checksum[@target='container']", - NULL); - if (tmp != NULL) - fwupd_release_add_checksum(release_tmp, tmp); - fwupd_release_set_version(release_tmp, version_rel); - if (!fu_history_add_device(self->history, device, release_tmp, error)) + if (!fu_history_add_device(self->history, device, FWUPD_RELEASE(release), error)) return FALSE; } @@ -2722,6 +2421,7 @@ fu_engine_install_release(FuEngine *self, /* for online updates, verify the version changed if not a re-install */ fmt = fu_device_get_version_format(device); + version_rel = fu_release_get_version(release); if (version_rel != NULL && fu_common_vercmp_full(version_orig, version_rel, fmt) != 0 && fu_common_vercmp_full(version_orig, fu_device_get_version(device), fmt) == 0 && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { @@ -2733,200 +2433,6 @@ fu_engine_install_release(FuEngine *self, fu_device_set_update_error(device, str); } - return TRUE; -} - -typedef struct { - gboolean ret; - GError **error; - FuEngine *self; - FuDevice *device; -} FuEngineSortHelper; - -static gint -fu_engine_sort_release_versions_cb(gconstpointer a, gconstpointer b, gpointer user_data) -{ - FuEngineSortHelper *helper = (FuEngineSortHelper *)user_data; - XbNode *na = *((XbNode **)a); - XbNode *nb = *((XbNode **)b); - g_autofree gchar *va = NULL; - g_autofree gchar *vb = NULL; - - /* already failed */ - if (!helper->ret) - return 0; - - /* get the semver from the release */ - va = fu_engine_get_release_version(helper->self, helper->device, na, helper->error); - if (va == NULL) { - g_prefix_error(helper->error, "failed to get release version: "); - return 0; - } - vb = fu_engine_get_release_version(helper->self, helper->device, nb, helper->error); - if (vb == NULL) { - g_prefix_error(helper->error, "failed to get release version: "); - return 0; - } - return fu_common_vercmp_full(va, vb, fu_device_get_version_format(helper->device)); -} - -static gboolean -fu_engine_sort_releases(FuEngine *self, FuDevice *device, GPtrArray *rels, GError **error) -{ - FuEngineSortHelper helper = { - .ret = TRUE, - .self = self, - .device = device, - .error = error, - }; - g_ptr_array_sort_with_data(rels, fu_engine_sort_release_versions_cb, &helper); - return helper.ret; -} - -/** - * fu_engine_install: - * @self: a #FuEngine - * @task: a #FuInstallTask - * @blob_cab: the #GBytes of the .cab file - * @progress: a #FuProgress - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER - * @feature_flags: feature flags, e.g. %FWUPD_FEATURE_FLAG_NONE - * @error: (nullable): optional return location for an error - * - * Installs a specific firmware file on a device. - * - * By this point all the requirements and tests should have been done in - * fu_engine_check_requirements() so this should not fail before running - * the plugin loader. - * - * Returns: %TRUE for success - **/ -gboolean -fu_engine_install(FuEngine *self, - FuInstallTask *task, - GBytes *blob_cab, - FuProgress *progress, - FwupdInstallFlags flags, - FwupdFeatureFlags feature_flags, - GError **error) -{ - XbNode *component = fu_install_task_get_component(task); - g_autoptr(FuDevice) device = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(XbNode) rel_newest = NULL; -#if LIBXMLB_CHECK_VERSION(0, 2, 0) - g_autoptr(XbQuery) query = NULL; -#endif - - g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); - g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); - g_return_val_if_fail(XB_IS_NODE(component), FALSE); - g_return_val_if_fail(blob_cab != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - /* not in bootloader mode */ - device = g_object_ref(fu_install_task_get_device(task)); - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - const gchar *tmp = NULL; - - /* both optional; the plugin can specify a fallback */ - tmp = xb_node_query_text(component, "screenshots/screenshot/caption", NULL); - if (tmp != NULL) - fu_device_set_update_message(device, tmp); - tmp = xb_node_query_text(component, "screenshots/screenshot/image", NULL); - if (tmp != NULL) - fu_device_set_update_image(device, tmp); - } - - /* get the newest version */ -#if LIBXMLB_CHECK_VERSION(0, 2, 0) - query = xb_query_new_full(xb_node_get_silo(component), - "releases/release", - XB_QUERY_FLAG_FORCE_NODE_CACHE, - error); - if (query == NULL) - return FALSE; - rel_newest = xb_node_query_first_full(component, query, &error_local); -#else - rel_newest = xb_node_query_first(component, "releases/release", &error_local); -#endif - if (rel_newest == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No releases in the firmware component: %s", - error_local->message); - return FALSE; - } - - /* schedule this for the next reboot if not in system-update.target, - * but first check if allowed on battery power */ - if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 && !fu_engine_is_running_offline(self)) { - FuPlugin *plugin; - g_autoptr(FwupdRelease) release_tmp = NULL; - g_autofree gchar *version_rel = NULL; - version_rel = fu_engine_get_release_version(self, device, rel_newest, error); - if (version_rel == NULL) { - g_prefix_error(error, "failed to get release version: "); - return FALSE; - } - plugin = fu_plugin_list_find_by_name(self->plugin_list, "upower", NULL); - if (plugin != NULL) { - if (!fu_plugin_runner_prepare(plugin, device, flags, error)) - return FALSE; - } - fu_progress_set_status(progress, FWUPD_STATUS_SCHEDULING); - release_tmp = fu_engine_create_release_metadata(self, device, plugin, error); - if (release_tmp == NULL) - return FALSE; - fwupd_release_set_version(release_tmp, version_rel); - return fu_engine_schedule_update(self, device, release_tmp, blob_cab, flags, error); - } - - /* install each intermediate release, or install only the newest version */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) { - g_autoptr(GPtrArray) rels = NULL; -#if LIBXMLB_CHECK_VERSION(0, 2, 0) - rels = xb_node_query_full(component, query, &error_local); -#else - rels = xb_node_query(component, "releases/release", 0, &error_local); -#endif - if (rels == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No releases in the firmware component: %s", - error_local->message); - return FALSE; - } - if (!fu_engine_sort_releases(self, device, rels, error)) - return FALSE; - fu_progress_set_steps(progress, rels->len); - for (guint i = 0; i < rels->len; i++) { - XbNode *rel = g_ptr_array_index(rels, i); - if (!fu_engine_install_release(self, - device, - component, - rel, - fu_progress_get_child(progress), - flags, - feature_flags, - error)) - return FALSE; - fu_progress_step_done(progress); - } - } else { - if (!fu_engine_install_release(self, - device, - component, - rel_newest, - progress, - flags, - feature_flags, - error)) - return FALSE; - } - /* mark success unless needs a reboot */ if (fu_device_get_update_state(device) != FWUPD_UPDATE_STATE_NEEDS_REBOOT) fu_device_set_update_state(device, FWUPD_UPDATE_STATE_SUCCESS); @@ -4512,15 +4018,14 @@ fu_engine_get_result_from_component(FuEngine *self, { FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; g_autofree gchar *description_xpath = NULL; - g_autoptr(FuInstallTask) task = NULL; g_autoptr(FuDevice) dev = NULL; - g_autoptr(FwupdRelease) rel = NULL; + g_autoptr(FuRelease) release = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GError) error_reqs = NULL; g_autoptr(GPtrArray) provides = NULL; g_autoptr(GPtrArray) tags = NULL; g_autoptr(XbNode) description = NULL; - g_autoptr(XbNode) release = NULL; + g_autoptr(XbNode) rel = NULL; #if LIBXMLB_CHECK_VERSION(0, 2, 0) g_autoptr(XbQuery) query = NULL; #endif @@ -4573,17 +4078,23 @@ fu_engine_get_result_from_component(FuEngine *self, if (tags != NULL) { for (guint i = 0; i < tags->len; i++) { XbNode *tag = g_ptr_array_index(tags, i); - fwupd_release_add_tag(rel, xb_node_get_text(tag)); + fu_release_add_tag(release, xb_node_get_text(tag)); } } + /* add EOL flag */ + if (xb_node_get_attr(component, "date_eol") != NULL) + fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_END_OF_LIFE); + /* check we can install it */ - task = fu_install_task_new(NULL, component); - if (!fu_engine_check_requirements(self, - request, - task, - FWUPD_INSTALL_FLAG_IGNORE_VID_PID, - &error_reqs)) { + release = fu_release_new(); + fu_release_set_request(release, request); + if (!fu_engine_load_release(self, + release, + component, + NULL, + FWUPD_INSTALL_FLAG_IGNORE_VID_PID, + &error_reqs)) { fu_device_inhibit(dev, "failed-reqs", error_reqs->message); /* continue */ } @@ -4596,11 +4107,11 @@ fu_engine_get_result_from_component(FuEngine *self, error); if (query == NULL) return NULL; - release = xb_node_query_first_full(component, query, &error_local); + rel = xb_node_query_first_full(component, query, &error_local); #else - release = xb_node_query_first(component, "releases/release", &error_local); + rel = xb_node_query_first(component, "releases/release", &error_local); #endif - if (release == NULL) { + if (rel == NULL) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -4608,7 +4119,7 @@ fu_engine_get_result_from_component(FuEngine *self, error_local->message); return NULL; } - if (!fu_keyring_get_release_flags(release, &release_flags, &error_local)) { + if (!fu_keyring_get_release_flags(rel, &release_flags, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { g_warning("Ignoring verification: %s", error_local->message); } else { @@ -4626,17 +4137,17 @@ fu_engine_get_result_from_component(FuEngine *self, if (xml != NULL) fu_device_set_description(dev, xml); } - rel = fwupd_release_new(); - fwupd_release_set_flags(rel, release_flags); - if (!fu_engine_set_release_from_appstream(self, - request, - dev, - rel, - component, - release, - error)) + + /* refresh the device to the new version format too */ + fu_engine_md_refresh_device_from_component(self, dev, component); + + release = fu_release_new(); + fu_release_set_request(release, request); + fu_release_set_device(release, dev); + fwupd_release_set_flags(FWUPD_RELEASE(release), release_flags); + if (!fu_engine_load_release(self, release, component, rel, FWUPD_INSTALL_FLAG_NONE, error)) return NULL; - fu_device_add_release(dev, rel); + fu_device_add_release(dev, FWUPD_RELEASE(release)); return g_steal_pointer(&dev); } @@ -4734,17 +4245,21 @@ fu_engine_get_details(FuEngine *self, FuEngineRequest *request, gint fd, GError /* if this matched a device on the system, ensure all the * requirements passed before setting UPDATABLE */ if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) { - g_autoptr(FuInstallTask) task = fu_install_task_new(dev, component); + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_req = NULL; - if (!fu_engine_check_requirements( - self, - request, - task, - FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | - FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | - FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | - FWUPD_INSTALL_FLAG_ALLOW_OLDER, - &error_req)) { + FwupdInstallFlags install_flags = + FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_OLDER; + + fu_release_set_device(release, dev); + fu_release_set_request(release, request); + if (!fu_engine_load_release(self, + release, + component, + NULL, + install_flags, + &error_req)) { g_debug("%s failed requirement checks: %s", fu_device_get_id(dev), error_req->message); @@ -5078,9 +4593,9 @@ fu_engine_check_release_is_approved(FuEngine *self, FwupdRelease *rel) } static gboolean -fu_engine_check_release_is_blocked(FuEngine *self, FwupdRelease *rel) +fu_engine_check_release_is_blocked(FuEngine *self, FuRelease *release) { - GPtrArray *csums = fwupd_release_get_checksums(rel); + GPtrArray *csums = fu_release_get_checksums(release); if (self->blocked_firmware == NULL) return FALSE; for (guint i = 0; i < csums->len; i++) { @@ -5102,18 +4617,11 @@ fu_engine_add_releases_for_device_component(FuEngine *self, FwupdFeatureFlags feature_flags; FwupdVersionFormat fmt = fu_device_get_version_format(device); g_autoptr(GError) error_local = NULL; - g_autoptr(FuInstallTask) task = fu_install_task_new(device, component); g_autoptr(GPtrArray) releases_tmp = NULL; - - if (!fu_engine_check_requirements( - self, - request, - task, - FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | - FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | - FWUPD_INSTALL_FLAG_ALLOW_OLDER, - error)) - return FALSE; + FwupdInstallFlags install_flags = + FWUPD_INSTALL_FLAG_OFFLINE | FWUPD_INSTALL_FLAG_IGNORE_VID_PID | + FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | + FWUPD_INSTALL_FLAG_ALLOW_OLDER; /* get all releases */ releases_tmp = xb_node_query(component, "releases/release", 0, &error_local); @@ -5127,96 +4635,98 @@ fu_engine_add_releases_for_device_component(FuEngine *self, } feature_flags = fu_engine_request_get_feature_flags(request); for (guint i = 0; i < releases_tmp->len; i++) { - XbNode *release = g_ptr_array_index(releases_tmp, i); + XbNode *rel = g_ptr_array_index(releases_tmp, i); const gchar *remote_id; const gchar *update_message; const gchar *update_image; gint vercmp; GPtrArray *checksums; GPtrArray *locations; - g_autoptr(FwupdRelease) rel = fwupd_release_new(); + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_loop = NULL; /* create new FwupdRelease for the XbNode */ - if (!fu_engine_set_release_from_appstream(self, - request, - device, - rel, - component, - release, - &error_loop)) { - g_warning("failed to set release for component: %s", error_loop->message); + fu_release_set_request(release, request); + fu_release_set_device(release, device); + if (!fu_engine_load_release(self, + release, + component, + rel, + install_flags, + &error_loop)) { + g_debug("failed to set release for component: %s", error_loop->message); continue; } /* fall back to quirk-provided value */ - if (fwupd_release_get_install_duration(rel) == 0) - fwupd_release_set_install_duration(rel, + if (fwupd_release_get_install_duration(FWUPD_RELEASE(release)) == 0) { + fwupd_release_set_install_duration(FWUPD_RELEASE(release), fu_device_get_install_duration(device)); + } /* invalid */ - locations = fwupd_release_get_locations(rel); + locations = fwupd_release_get_locations(FWUPD_RELEASE(release)); if (locations->len == 0) continue; - checksums = fwupd_release_get_checksums(rel); + checksums = fu_release_get_checksums(release); if (checksums->len == 0) continue; /* different branch */ - if (g_strcmp0(fwupd_release_get_branch(rel), fu_device_get_branch(device)) != 0) { + if (g_strcmp0(fu_release_get_branch(release), fu_device_get_branch(device)) != 0) { if ((feature_flags & FWUPD_FEATURE_FLAG_SWITCH_BRANCH) == 0) { g_debug("client does not understand branches, skipping %s:%s", - fwupd_release_get_branch(rel), - fwupd_release_get_version(rel)); + fu_release_get_branch(release), + fu_release_get_version(release)); continue; } - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH); + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH); } /* test for upgrade or downgrade */ - vercmp = fu_common_vercmp_full(fwupd_release_get_version(rel), + vercmp = fu_common_vercmp_full(fu_release_get_version(release), fu_device_get_version(device), fmt); if (vercmp > 0) - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_IS_UPGRADE); + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_UPGRADE); else if (vercmp < 0) - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_IS_DOWNGRADE); + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_IS_DOWNGRADE); /* lower than allowed to downgrade to */ if (fu_device_get_version_lowest(device) != NULL && - fu_common_vercmp_full(fwupd_release_get_version(rel), + fu_common_vercmp_full(fu_release_get_version(release), fu_device_get_version_lowest(device), fmt) < 0) { - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION); + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_VERSION); } /* manually blocked */ - if (fu_engine_check_release_is_blocked(self, rel)) - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + if (fu_engine_check_release_is_blocked(self, release)) + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); /* check if remote is filtering firmware */ - remote_id = fwupd_release_get_remote_id(rel); + remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); if (remote_id != NULL) { FwupdRemote *remote = fu_engine_get_remote_by_id(self, remote_id, NULL); if (remote != NULL && fwupd_remote_get_approval_required(remote) && - !fu_engine_check_release_is_approved(self, rel)) { - fwupd_release_add_flag(rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); + !fu_engine_check_release_is_approved(self, FWUPD_RELEASE(release))) { + fu_release_add_flag(release, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL); } } /* add update message if exists but device doesn't already have one */ - update_message = fwupd_release_get_update_message(rel); + update_message = fwupd_release_get_update_message(FWUPD_RELEASE(release)); if (fwupd_device_get_update_message(FWUPD_DEVICE(device)) == NULL && update_message != NULL) { fu_device_set_update_message(device, update_message); } - update_image = fwupd_release_get_update_image(rel); + update_image = fwupd_release_get_update_image(FWUPD_RELEASE(release)); if (fwupd_device_get_update_image(FWUPD_DEVICE(device)) == NULL && update_image != NULL) { fwupd_device_set_update_image(FWUPD_DEVICE(device), update_image); } /* success */ - g_ptr_array_add(releases, g_steal_pointer(&rel)); + g_ptr_array_add(releases, g_steal_pointer(&release)); } /* success */ @@ -5238,12 +4748,12 @@ fu_engine_get_releases_for_device(FuEngine *self, GError **error) { GPtrArray *device_guids; - GPtrArray *releases; const gchar *version; g_autoptr(GError) error_all = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) branches = NULL; g_autoptr(GPtrArray) components = NULL; + g_autoptr(GPtrArray) releases = NULL; g_autoptr(GString) xpath = g_string_new(NULL); /* get device version */ @@ -5336,7 +4846,7 @@ fu_engine_get_releases_for_device(FuEngine *self, g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "No releases found"); return NULL; } - return releases; + return g_steal_pointer(&releases); } /** @@ -6082,22 +5592,22 @@ fu_engine_add_device(FuEngine *self, FuDevice *device) component = fu_engine_get_component_by_guids(self, device); if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_LOCKED)) { if (component != NULL) { - g_autoptr(XbNode) release = NULL; - release = xb_node_query_first(component, "releases/release", NULL); - if (release != NULL) { - g_autoptr(FwupdRelease) rel = fwupd_release_new(); + g_autoptr(XbNode) rel = NULL; + rel = xb_node_query_first(component, "releases/release", NULL); + if (rel != NULL) { + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_local = NULL; - if (!fu_engine_set_release_from_appstream(self, - NULL, - device, - rel, - component, - release, - &error_local)) { + fu_release_set_device(release, device); + if (!fu_engine_load_release(self, + release, + component, + rel, + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { g_warning("failed to set AppStream release: %s", error_local->message); } else { - fu_device_add_release(device, rel); + fu_device_add_release(device, FWUPD_RELEASE(release)); } } } diff --git a/src/fu-engine.h b/src/fu-engine.h index 1839241db..51fa57cb8 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -15,9 +15,8 @@ #include "fu-common.h" #include "fu-context.h" -#include "fu-engine-request.h" -#include "fu-install-task.h" #include "fu-plugin.h" +#include "fu-release.h" #include "fu-security-attrs.h" #define FU_TYPE_ENGINE (fu_engine_get_type()) @@ -160,13 +159,12 @@ fu_engine_composite_prepare(FuEngine *self, GPtrArray *devices, GError **error); gboolean fu_engine_composite_cleanup(FuEngine *self, GPtrArray *devices, GError **error); gboolean -fu_engine_install(FuEngine *self, - FuInstallTask *task, - GBytes *blob_cab, - FuProgress *progress, - FwupdInstallFlags flags, - FwupdFeatureFlags feature_flags, - GError **error); +fu_engine_install_release(FuEngine *self, + FuRelease *task, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); gboolean fu_engine_install_blob(FuEngine *self, FuDevice *device, @@ -176,13 +174,13 @@ fu_engine_install_blob(FuEngine *self, FwupdFeatureFlags feature_flags, GError **error); gboolean -fu_engine_install_tasks(FuEngine *self, - FuEngineRequest *request, - GPtrArray *install_tasks, - GBytes *blob_cab, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error); +fu_engine_install_releases(FuEngine *self, + FuEngineRequest *request, + GPtrArray *releases, + GBytes *blob_cab, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); GPtrArray * fu_engine_get_details(FuEngine *self, FuEngineRequest *request, gint fd, GError **error); gboolean @@ -221,11 +219,10 @@ fu_engine_add_plugin(FuEngine *self, FuPlugin *plugin); void fu_engine_add_runtime_version(FuEngine *self, const gchar *component_id, const gchar *version); gboolean -fu_engine_check_trust(FuEngine *self, FuInstallTask *task, GError **error); +fu_engine_check_trust(FuEngine *self, FuRelease *task, GError **error); gboolean fu_engine_check_requirements(FuEngine *self, - FuEngineRequest *request, - FuInstallTask *task, + FuRelease *release, FwupdInstallFlags flags, GError **error); void diff --git a/src/fu-install-task.c b/src/fu-install-task.c deleted file mode 100644 index 759ff10bb..000000000 --- a/src/fu-install-task.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#define G_LOG_DOMAIN "FuInstallTask" - -#include "config.h" - -#include - -#include "fu-common-version.h" -#include "fu-common.h" -#include "fu-device-private.h" -#include "fu-install-task.h" -#include "fu-keyring-utils.h" - -struct _FuInstallTask { - GObject parent_instance; - FuDevice *device; - XbNode *component; - FwupdReleaseFlags trust_flags; - gboolean is_downgrade; -}; - -G_DEFINE_TYPE(FuInstallTask, fu_install_task, G_TYPE_OBJECT) - -/** - * fu_install_task_get_device: - * @self: a #FuInstallTask - * - * Gets the device for this task. - * - * Returns: (transfer none): the device - **/ -FuDevice * -fu_install_task_get_device(FuInstallTask *self) -{ - g_return_val_if_fail(FU_IS_INSTALL_TASK(self), NULL); - return self->device; -} - -/** - * fu_install_task_get_component: - * @self: a #FuInstallTask - * - * Gets the component for this task. - * - * Returns: (transfer none): the component - **/ -XbNode * -fu_install_task_get_component(FuInstallTask *self) -{ - g_return_val_if_fail(FU_IS_INSTALL_TASK(self), NULL); - return self->component; -} - -/** - * fu_install_task_get_trust_flags: - * @self: a #FuInstallTask - * - * Gets the trust flags for this task. - * - * NOTE: This is only set after fu_install_task_check_requirements() has been - * called successfully. - * - * Returns: the #FwupdReleaseFlags, e.g. #FWUPD_TRUST_FLAG_PAYLOAD - **/ -FwupdReleaseFlags -fu_install_task_get_trust_flags(FuInstallTask *self) -{ - g_return_val_if_fail(FU_IS_INSTALL_TASK(self), FALSE); - return self->trust_flags; -} - -/** - * fu_install_task_get_is_downgrade: - * @self: a #FuInstallTask - * - * Gets if this task is to downgrade firmware. - * - * NOTE: This is only set after fu_install_task_check_requirements() has been - * called successfully. - * - * Returns: %TRUE if versions numbers are going backwards - **/ -gboolean -fu_install_task_get_is_downgrade(FuInstallTask *self) -{ - g_return_val_if_fail(FU_IS_INSTALL_TASK(self), FALSE); - return self->is_downgrade; -} - -static gchar * -fu_install_task_verfmts_to_string(GPtrArray *verfmts) -{ - GString *str = g_string_new(NULL); - for (guint i = 0; i < verfmts->len; i++) { - XbNode *verfmt = g_ptr_array_index(verfmts, i); - const gchar *tmp = xb_node_get_text(verfmt); - g_string_append_printf(str, "%s;", tmp); - } - if (str->len > 0) - g_string_truncate(str, str->len - 1); - return g_string_free(str, FALSE); -} - -static gboolean -fu_install_task_check_verfmt(FuInstallTask *self, - GPtrArray *verfmts, - FwupdInstallFlags flags, - GError **error) -{ - FwupdVersionFormat fmt_dev = fu_device_get_version_format(self->device); - g_autofree gchar *verfmts_str = NULL; - - /* no device format */ - if (fmt_dev == FWUPD_VERSION_FORMAT_UNKNOWN && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - verfmts_str = fu_install_task_verfmts_to_string(verfmts); - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "release version format '%s' but no device version format", - verfmts_str); - return FALSE; - } - - /* compare all version formats */ - for (guint i = 0; i < verfmts->len; i++) { - XbNode *verfmt = g_ptr_array_index(verfmts, i); - const gchar *tmp = xb_node_get_text(verfmt); - FwupdVersionFormat fmt_rel = fwupd_version_format_from_string(tmp); - if (fmt_dev == fmt_rel) - return TRUE; - } - verfmts_str = fu_install_task_verfmts_to_string(verfmts); - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Firmware version formats were different, " - "device was '%s' and release is '%s'", - fwupd_version_format_to_string(fmt_dev), - verfmts_str); - return FALSE; - } - g_warning("ignoring version format difference %s:%s", - fwupd_version_format_to_string(fmt_dev), - verfmts_str); - return TRUE; -} - -static gboolean -fu_install_task_check_requirements_version_check(FuInstallTask *self, GError **error) -{ - g_autoptr(GError) error_local = NULL; - g_autoptr(GPtrArray) reqs = NULL; - - reqs = xb_node_query(fu_install_task_get_component(self), "requires/*", 0, &error_local); - if (reqs == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - error_local->message); - return FALSE; - } - for (guint i = 0; i < reqs->len; i++) { - XbNode *req = g_ptr_array_index(reqs, i); - if (g_strcmp0(xb_node_get_element(req), "firmware") == 0 && - xb_node_get_text(req) == NULL) { - return TRUE; - } - } - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no firmware requirement"); - return FALSE; -} - -/** - * fu_install_task_check_requirements: - * @self: a #FuInstallTask - * @flags: install flags, e.g. #FWUPD_INSTALL_FLAG_ALLOW_OLDER - * @error: (nullable): optional return location for an error - * - * Checks any requirements of this task. This will typically involve checking - * that the device can accept the component (the GUIDs match) and that the - * device can be upgraded with this firmware version. - * - * Returns: %TRUE if the requirements passed - **/ -gboolean -fu_install_task_check_requirements(FuInstallTask *self, FwupdInstallFlags flags, GError **error) -{ - const gchar *branch_new; - const gchar *branch_old; - const gchar *protocol; - const gchar *version; - const gchar *version_release_raw; - const gchar *version_lowest; - gboolean matches_guid = FALSE; - gint vercmp; - g_autofree gchar *version_release = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(GPtrArray) provides = NULL; - g_autoptr(GPtrArray) verfmts = NULL; - g_autoptr(XbNode) release = NULL; -#if LIBXMLB_CHECK_VERSION(0, 2, 0) - g_autoptr(XbQuery) query = NULL; -#endif - - g_return_val_if_fail(FU_IS_INSTALL_TASK(self), FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - /* does this component provide a GUID the device has */ - provides = - xb_node_query(self->component, "provides/firmware[@type='flashed']", 0, &error_local); - if (provides == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No supported devices found: %s", - error_local->message); - return FALSE; - } - for (guint i = 0; i < provides->len; i++) { - XbNode *provide = g_ptr_array_index(provides, i); - if (fu_device_has_guid(self->device, xb_node_get_text(provide))) { - matches_guid = TRUE; - break; - } - } - if (!matches_guid) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No supported devices found"); - return FALSE; - } - - /* device requires a version check */ - if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED)) { - if (!fu_install_task_check_requirements_version_check(self, error)) { - g_prefix_error(error, "device requires firmware with a version check: "); - return FALSE; - } - } - - /* does the protocol match */ - protocol = - xb_node_query_text(self->component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); - if (fu_device_get_protocols(self->device)->len != 0 && protocol != NULL && - !fu_device_has_protocol(self->device, protocol) && - (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - g_autofree gchar *str = NULL; - str = fu_common_strjoin_array("|", fu_device_get_protocols(self->device)); - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s does not support %s, only %s", - fu_device_get_name(self->device), - protocol, - str); - return FALSE; - } - - /* check the device is not locked */ - if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_LOCKED)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s [%s] is locked", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); - return FALSE; - } - - /* check the branch is not switching */ - branch_new = xb_node_query_text(self->component, "branch", NULL); - branch_old = fu_device_get_branch(self->device); - if ((flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0 && - g_strcmp0(branch_old, branch_new) != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s [%s] would switch firmware branch from %s to %s", - fu_device_get_name(self->device), - fu_device_get_id(self->device), - branch_old != NULL ? branch_old : "default", - branch_new != NULL ? branch_new : "default"); - return FALSE; - } - - /* no update abilities */ - if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_UPDATABLE)) { - g_autoptr(GString) str = g_string_new(NULL); - g_string_append_printf(str, - "Device %s [%s] does not currently allow updates", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); - if (fu_device_get_update_error(self->device) != NULL) { - g_string_append_printf(str, - ": %s", - fu_device_get_update_error(self->device)); - } - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, str->str); - return FALSE; - } - - /* called with online update, test if device is supposed to allow this */ - if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) == 0 && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && - fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s [%s] only allows offline updates", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); - return FALSE; - } - - /* get device */ - version = fu_device_get_version(self->device); - if (version == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s [%s] has no firmware version", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); - return FALSE; - } - - /* get latest release */ -#if LIBXMLB_CHECK_VERSION(0, 2, 0) - query = xb_query_new_full(xb_node_get_silo(self->component), - "releases/release", - XB_QUERY_FLAG_FORCE_NODE_CACHE, - error); - if (query == NULL) - return FALSE; - release = xb_node_query_first_full(self->component, query, NULL); -#else - release = xb_node_query_first(self->component, "releases/release", NULL); -#endif - if (release == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "%s [%s] has no firmware update metadata", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); - return FALSE; - } - - /* is this a downgrade or re-install */ - version_release_raw = xb_node_get_attr(release, "version"); - if (version_release_raw == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Release has no firmware version"); - return FALSE; - } - - /* check the version formats match if set in the release */ - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && - (flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { - verfmts = xb_node_query(self->component, - "custom/value[@key='LVFS::VersionFormat']", - 0, - NULL); - if (verfmts != NULL) { - if (!fu_install_task_check_verfmt(self, verfmts, flags, error)) - return FALSE; - } - } - - /* compare to the lowest supported version, if it exists */ - version_lowest = fu_device_get_version_lowest(self->device); - if (version_lowest != NULL && - fu_common_vercmp_full(version_lowest, - version, - fu_device_get_version_format(self->device)) > 0 && - (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_NEWER, - "Specified firmware is older than the minimum " - "required version '%s < %s'", - version, - version_lowest); - return FALSE; - } - - /* check semver */ - if (fu_device_get_version_format(self->device) == FWUPD_VERSION_FORMAT_PLAIN) { - version_release = g_strdup(version_release_raw); - } else { - version_release = - fu_common_version_parse_from_format(version_release_raw, - fu_device_get_version_format(self->device)); - } - vercmp = fu_common_vercmp_full(version, - version_release, - fu_device_get_version_format(self->device)); - if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) && - vercmp >= 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device only supports version upgrades"); - return FALSE; - } - if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_SAME, - "Specified firmware is already installed '%s'", - version_release); - return FALSE; - } - self->is_downgrade = vercmp > 0; - if (self->is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0 && - (flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_NEWER, - "Specified firmware is older than installed '%s < %s'", - version_release, - version); - return FALSE; - } - - /* verify */ - if (!fu_keyring_get_release_flags(release, &self->trust_flags, &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_warning("Ignoring verification for %s: %s", - fu_device_get_name(self->device), - error_local->message); - } else { - g_propagate_error(error, g_steal_pointer(&error_local)); - return FALSE; - } - } - return TRUE; -} - -/** - * fu_install_task_get_action_id: - * @self: a #FuEngine - * - * Gets the PolicyKit action ID to use for the install operation. - * - * Returns: string, e.g. `org.freedesktop.fwupd.update-internal-trusted` - **/ -const gchar * -fu_install_task_get_action_id(FuInstallTask *self) -{ - /* relax authentication checks for removable devices */ - if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_INTERNAL)) { - if (self->is_downgrade) { - if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) - return "org.freedesktop.fwupd.downgrade-hotplug-trusted"; - return "org.freedesktop.fwupd.downgrade-hotplug"; - } - if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) - return "org.freedesktop.fwupd.update-hotplug-trusted"; - return "org.freedesktop.fwupd.update-hotplug"; - } - - /* internal device */ - if (self->is_downgrade) { - if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) - return "org.freedesktop.fwupd.downgrade-internal-trusted"; - return "org.freedesktop.fwupd.downgrade-internal"; - } - if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) - return "org.freedesktop.fwupd.update-internal-trusted"; - return "org.freedesktop.fwupd.update-internal"; -} - -static void -fu_install_task_init(FuInstallTask *self) -{ - self->trust_flags = FWUPD_TRUST_FLAG_NONE; -} - -static void -fu_install_task_finalize(GObject *object) -{ - FuInstallTask *self = FU_INSTALL_TASK(object); - - if (self->component != NULL) - g_object_unref(self->component); - if (self->device != NULL) - g_object_unref(self->device); - - G_OBJECT_CLASS(fu_install_task_parent_class)->finalize(object); -} - -static void -fu_install_task_class_init(FuInstallTaskClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = fu_install_task_finalize; -} - -/** - * fu_install_task_compare: - * @task1: first task to compare. - * @task2: second task to compare. - * - * Compares two install tasks. - * - * Returns: 1, 0 or -1 if @task1 is greater, equal, or less than @task2, respectively. - **/ -gint -fu_install_task_compare(FuInstallTask *task1, FuInstallTask *task2) -{ - FuDevice *device1 = fu_install_task_get_device(task1); - FuDevice *device2 = fu_install_task_get_device(task2); - if (fu_device_get_order(device1) < fu_device_get_order(device2)) - return -1; - if (fu_device_get_order(device1) > fu_device_get_order(device2)) - return 1; - return 0; -} - -/** - * fu_install_task_new: - * @device: a device - * @component: a Xmlb node - * - * Creates a new install task that may or may not be valid. - * - * Returns: (transfer full): the #FuInstallTask - **/ -FuInstallTask * -fu_install_task_new(FuDevice *device, XbNode *component) -{ - FuInstallTask *self; - self = g_object_new(FU_TYPE_TASK, NULL); - if (component != NULL) - self->component = g_object_ref(component); - if (device != NULL) - self->device = g_object_ref(device); - return FU_INSTALL_TASK(self); -} diff --git a/src/fu-install-task.h b/src/fu-install-task.h deleted file mode 100644 index f99ee29a7..000000000 --- a/src/fu-install-task.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2015 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#pragma once - -#include -#include - -#include "fu-device.h" - -#define FU_TYPE_TASK (fu_install_task_get_type()) -G_DECLARE_FINAL_TYPE(FuInstallTask, fu_install_task, FU, INSTALL_TASK, GObject) - -FuInstallTask * -fu_install_task_new(FuDevice *device, XbNode *component); -FuDevice * -fu_install_task_get_device(FuInstallTask *self); -XbNode * -fu_install_task_get_component(FuInstallTask *self); -FwupdReleaseFlags -fu_install_task_get_trust_flags(FuInstallTask *self); -gboolean -fu_install_task_get_is_downgrade(FuInstallTask *self); -gboolean -fu_install_task_check_requirements(FuInstallTask *self, FwupdInstallFlags flags, GError **error); -const gchar * -fu_install_task_get_action_id(FuInstallTask *self); -gint -fu_install_task_compare(FuInstallTask *task1, FuInstallTask *task2); diff --git a/src/fu-main.c b/src/fu-main.c index aac715c48..cacb54e2f 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -40,7 +40,7 @@ #include "fu-debug.h" #include "fu-device-private.h" #include "fu-engine.h" -#include "fu-install-task.h" +#include "fu-release.h" #include "fu-security-attrs-private.h" #ifdef HAVE_POLKIT @@ -364,9 +364,10 @@ typedef struct { #ifdef HAVE_POLKIT PolkitSubject *subject; #endif - GPtrArray *install_tasks; + GPtrArray *releases; GPtrArray *action_ids; GPtrArray *checksums; + GPtrArray *errors; guint64 flags; GBytes *blob_cab; FuMainPrivate *priv; @@ -390,12 +391,14 @@ fu_main_auth_helper_free(FuMainAuthHelper *helper) g_object_unref(helper->silo); if (helper->request != NULL) g_object_unref(helper->request); - if (helper->install_tasks != NULL) - g_ptr_array_unref(helper->install_tasks); + if (helper->releases != NULL) + g_ptr_array_unref(helper->releases); if (helper->action_ids != NULL) g_ptr_array_unref(helper->action_ids); if (helper->checksums != NULL) g_ptr_array_unref(helper->checksums); + if (helper->errors != NULL) + g_ptr_array_unref(helper->errors); g_free(helper->device_id); g_free(helper->remote_id); g_free(helper->key); @@ -807,13 +810,13 @@ fu_main_authorize_install_queue(FuMainAuthHelper *helper_ref) /* all authenticated, so install all the things */ priv->update_in_progress = TRUE; - ret = fu_engine_install_tasks(helper->priv->engine, - helper->request, - helper->install_tasks, - helper->blob_cab, - progress, - helper->flags, - &error); + ret = fu_engine_install_releases(helper->priv->engine, + helper->request, + helper->releases, + helper->blob_cab, + progress, + helper->flags, + &error); priv->update_in_progress = FALSE; if (priv->pending_sigterm) g_main_loop_quit(priv->loop); @@ -845,11 +848,126 @@ g_ptr_array_find(GPtrArray *haystack, gconstpointer needle, guint *index_) #endif static gint -fu_main_install_task_sort_cb(gconstpointer a, gconstpointer b) +fu_main_release_sort_cb(gconstpointer a, gconstpointer b) { - FuInstallTask *task_a = *((FuInstallTask **)a); - FuInstallTask *task_b = *((FuInstallTask **)b); - return fu_install_task_compare(task_a, task_b); + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); +} + +static gboolean +fu_main_install_with_helper_device(FuMainAuthHelper *helper, + XbNode *component, + FuDevice *device, + GError **error) +{ + FuMainPrivate *priv = helper->priv; + const gchar *action_id; + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) releases = NULL; + + /* is this component valid for the device */ + fu_release_set_device(release, device); + fu_release_set_request(release, helper->request); + if (!fu_release_load(release, + component, + NULL, + helper->flags | FWUPD_INSTALL_FLAG_FORCE, + &error_local)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + return TRUE; + } + if (!fu_engine_check_requirements(priv->engine, + release, + helper->flags | FWUPD_INSTALL_FLAG_FORCE, + &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("first pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + } + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + return TRUE; + } + + /* possibly update version format */ + fu_engine_md_refresh_device_from_component(priv->engine, device, component); + + /* sync update message from CAB */ + fu_device_incorporate_from_component(device, component); + + /* install each intermediate release */ + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) { + g_autoptr(GPtrArray) rels = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif + /* we get this one "for free" */ + g_ptr_array_add(releases, g_object_ref(release)); + +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + error); + if (query == NULL) + return FALSE; + rels = xb_node_query_full(component, query, NULL); +#else + rels = xb_node_query(component, "releases/release", 0, NULL); +#endif + /* add all but the the first entry */ + for (guint i = 1; i < rels->len; i++) { + XbNode *rel = g_ptr_array_index(rels, i); + g_autoptr(FuRelease) release2 = fu_release_new(); + g_autoptr(GError) error_loop = NULL; + fu_release_set_device(release2, device); + fu_release_set_request(release2, helper->request); + if (!fu_release_load(release2, + component, + rel, + helper->flags, + &error_loop)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_loop)); + continue; + } + g_ptr_array_add(releases, g_object_ref(release2)); + } + } else { + g_ptr_array_add(releases, g_object_ref(release)); + } + + /* make a second pass */ + for (guint i = 0; i < releases->len; i++) { + FuRelease *release_tmp = g_ptr_array_index(releases, i); + if (!fu_engine_check_requirements(priv->engine, + release_tmp, + helper->flags, + &error_local)) { + g_debug("second pass requirement on %s:%s failed: %s", + fu_device_get_id(device), + xb_node_query_text(component, "id", NULL), + error_local->message); + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + continue; + } + if (!fu_engine_check_trust(priv->engine, release_tmp, &error_local)) { + g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); + continue; + } + + /* get the action IDs for the valid device */ + action_id = fu_release_get_action_id(release_tmp); + if (!g_ptr_array_find(helper->action_ids, action_id, NULL)) + g_ptr_array_add(helper->action_ids, g_strdup(action_id)); + g_ptr_array_add(helper->releases, g_object_ref(release_tmp)); + } + + /* success */ + return TRUE; } static gboolean @@ -859,7 +977,6 @@ fu_main_install_with_helper(FuMainAuthHelper *helper_ref, GError **error) g_autoptr(FuMainAuthHelper) helper = helper_ref; g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices_possible = NULL; - g_autoptr(GPtrArray) errors = NULL; /* get a list of devices that in some way match the device_id */ if (g_strcmp0(helper->device_id, FWUPD_DEVICE_ID_ANY) == 0) { @@ -890,73 +1007,25 @@ fu_main_install_with_helper(FuMainAuthHelper *helper_ref, GError **error) if (components == NULL) return FALSE; helper->action_ids = g_ptr_array_new_with_free_func(g_free); - helper->install_tasks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + helper->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + helper->errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + + /* do any devices pass the requirements */ for (guint i = 0; i < components->len; i++) { XbNode *component = g_ptr_array_index(components, i); - - /* do any devices pass the requirements */ for (guint j = 0; j < devices_possible->len; j++) { FuDevice *device = g_ptr_array_index(devices_possible, j); - const gchar *action_id; - g_autoptr(FuInstallTask) task = NULL; - g_autoptr(GError) error_local = NULL; - - /* is this component valid for the device */ - task = fu_install_task_new(device, component); - if (!fu_engine_check_requirements(priv->engine, - helper->request, - task, - helper->flags | FWUPD_INSTALL_FLAG_FORCE, - &error_local)) { - if (!g_error_matches(error_local, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND)) { - g_debug("first pass requirement on %s:%s failed: %s", - fu_device_get_id(device), - xb_node_query_text(component, "id", NULL), - error_local->message); - } - g_ptr_array_add(errors, g_steal_pointer(&error_local)); - continue; - } - - /* make a second pass using possibly updated version format now */ - fu_engine_md_refresh_device_from_component(priv->engine, device, component); - if (!fu_engine_check_requirements(priv->engine, - helper->request, - task, - helper->flags, - &error_local)) { - g_debug("second pass requirement on %s:%s failed: %s", - fu_device_get_id(device), - xb_node_query_text(component, "id", NULL), - error_local->message); - g_ptr_array_add(errors, g_steal_pointer(&error_local)); - continue; - } - if (!fu_engine_check_trust(priv->engine, task, &error_local)) { - g_ptr_array_add(errors, g_steal_pointer(&error_local)); - continue; - } - - /* if component should have an update message from CAB */ - fu_device_incorporate_from_component(device, component); - - /* get the action IDs for the valid device */ - action_id = fu_install_task_get_action_id(task); - if (!g_ptr_array_find(helper->action_ids, action_id, NULL)) - g_ptr_array_add(helper->action_ids, g_strdup(action_id)); - g_ptr_array_add(helper->install_tasks, g_steal_pointer(&task)); + if (!fu_main_install_with_helper_device(helper, component, device, error)) + return FALSE; } } /* order the install tasks by the device priority */ - g_ptr_array_sort(helper->install_tasks, fu_main_install_task_sort_cb); + g_ptr_array_sort(helper->releases, fu_main_release_sort_cb); /* nothing suitable */ - if (helper->install_tasks->len == 0) { - GError *error_tmp = fu_common_error_array_get_best(errors); + if (helper->releases->len == 0) { + GError *error_tmp = fu_common_error_array_get_best(helper->errors); g_propagate_error(error, error_tmp); return FALSE; } diff --git a/src/fu-release.c b/src/fu-release.c new file mode 100644 index 000000000..363aa29f8 --- /dev/null +++ b/src/fu-release.c @@ -0,0 +1,1067 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuRelease" + +#include "config.h" + +#include "fu-device-private.h" +#include "fu-keyring-utils.h" +#include "fu-release.h" + +/** + * FuRelease: + * + * An installable entity that has been loaded and verified for a specific device. + * + * See also: [class@FwupdRelease] + */ + +struct _FuRelease { + FwupdRelease parent_instance; + FuEngineRequest *request; + FuDevice *device; + FwupdRemote *remote; + FuConfig *config; + GBytes *blob_fw; + FwupdReleaseFlags trust_flags; + gboolean is_downgrade; + gchar *builder_script; + gchar *builder_output; + GPtrArray *soft_reqs; /* nullable, element-type XbNode */ + GPtrArray *hard_reqs; /* nullable, element-type XbNode */ +}; + +G_DEFINE_TYPE(FuRelease, fu_release, FWUPD_TYPE_RELEASE) + +/** + * fu_release_get_builder_script: + * @self: a #FuRelease + * + * Gets the builder script to use when installing this release. + * + * Returns: (transfer none) (nullable): filename, e.g. `build.sh` + **/ +const gchar * +fu_release_get_builder_script(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->builder_script; +} + +/** + * fu_release_get_builder_output: + * @self: a #FuRelease + * + * Gets the output filename to use when installing this release. + * + * Returns: (transfer none) (nullable): filename, e.g. `filename.bin` + **/ +const gchar * +fu_release_get_builder_output(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->builder_output; +} + +/** + * fu_release_set_request: + * @self: a #FuRelease + * @request: (nullable): a #FuEngineRequest + * + * Sets the user request which created this operation. + **/ +void +fu_release_set_request(FuRelease *self, FuEngineRequest *request) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->request, request); +} + +/** + * fu_release_get_request: + * @self: a #FuRelease + * + * Gets the user request which created this operation. + * + * Returns: (transfer none) (nullable): request + **/ +FuEngineRequest * +fu_release_get_request(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->request; +} + +/** + * fu_release_set_device: + * @self: a #FuRelease + * @device: (nullable): a #FuDevice + * + * Sets the device this release should use when checking requirements. + **/ +void +fu_release_set_device(FuRelease *self, FuDevice *device) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->device, device); +} + +/** + * fu_release_get_device: + * @self: a #FuRelease + * + * Gets the device this release was loaded for. + * + * Returns: (transfer none) (nullable): device + **/ +FuDevice * +fu_release_get_device(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->device; +} + +/** + * fu_release_get_fw_blob: + * @self: a #FuRelease + * + * Gets the firmware payload to use when installing this release. + * + * Returns: (transfer none) (nullable): data + **/ +GBytes * +fu_release_get_fw_blob(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->blob_fw; +} + +/** + * fu_release_get_soft_reqs: + * @self: a #FuRelease + * + * Gets the additional soft requirements that need to be checked in the engine. + * + * Returns: (transfer none) (nullable) (element-type XbNode): nodes + **/ +GPtrArray * +fu_release_get_soft_reqs(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->soft_reqs; +} + +/** + * fu_release_get_soft_reqs: + * @self: a #FuRelease + * + * Gets the additional hard requirements that need to be checked in the engine. + * + * Returns: (transfer none) (nullable) (element-type XbNode): nodes + **/ +GPtrArray * +fu_release_get_hard_reqs(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->hard_reqs; +} + +/** + * fu_release_set_remote: + * @self: a #FuRelease + * @remote: (nullable): a #FwupdRemote + * + * Sets the remote this release should use when loading. This is typically set by the engine by + *watching the `remote-id` property to be set and then querying the internal cached list of + *`FuRemote`s. + **/ +void +fu_release_set_remote(FuRelease *self, FwupdRemote *remote) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->remote, remote); +} + +/** + * fu_release_set_config: + * @self: a #FuRelease + * @config: (nullable): a #FuConfig + * + * Sets the config to use when loading. The config may be used for things like ordering attributes + *like protocol priority. + **/ +void +fu_release_set_config(FuRelease *self, FuConfig *config) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_set_object(&self->config, config); +} + +static gchar * +fu_release_get_localized_xpath(FuRelease *self, const gchar *element) +{ + GString *xpath = g_string_new(element); + const gchar *locale = NULL; + + /* optional; not set in tests */ + if (self->request != NULL) + locale = fu_engine_request_get_locale(self->request); + + /* prefer the users locale if set */ + if (locale != NULL) { + g_autofree gchar *xpath_locale = NULL; + xpath_locale = g_strdup_printf("%s[@xml:lang='%s']|", element, locale); + g_string_prepend(xpath, xpath_locale); + } + return g_string_free(xpath, FALSE); +} + +/* convert hex and decimal versions to dotted style */ +static gchar * +fu_release_get_release_version(FuRelease *self, const gchar *version, GError **error) +{ + FwupdVersionFormat fmt = fu_device_get_version_format(self->device); + guint64 ver_uint32; + + /* already dotted notation */ + if (g_strstr_len(version, -1, ".") != NULL) + return g_strdup(version); + + /* don't touch my version! */ + if (fmt == FWUPD_VERSION_FORMAT_PLAIN) + return g_strdup(version); + + /* parse as integer */ + ver_uint32 = fu_common_strtoull(version); + if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN || ver_uint32 == 0 || ver_uint32 > G_MAXUINT32) + return g_strdup(version); + + /* convert to dotted decimal */ + return fu_common_version_from_uint32((guint32)ver_uint32, fmt); +} + +static gboolean +fu_release_load_artifact(FuRelease *self, XbNode *artifact, GError **error) +{ + const gchar *filename; + guint64 size; + g_autoptr(GPtrArray) locations = NULL; + g_autoptr(GPtrArray) checksums = NULL; + + /* filename */ + filename = xb_node_query_text(artifact, "filename", NULL); + if (filename != NULL) + fwupd_release_set_filename(FWUPD_RELEASE(self), filename); + + /* location */ + locations = xb_node_query(artifact, "location", 0, NULL); + if (locations != NULL) { + for (guint i = 0; i < locations->len; i++) { + XbNode *n = g_ptr_array_index(locations, i); + g_autofree gchar *scheme = NULL; + + /* check the scheme is allowed */ + scheme = fu_common_uri_get_scheme(xb_node_get_text(n)); + if (scheme != NULL) { + guint prio = fu_config_get_uri_scheme_prio(self->config, scheme); + if (prio == G_MAXUINT) + continue; + } + + /* build the complete URI */ + if (self->remote != NULL) { + g_autofree gchar *uri = NULL; + uri = fwupd_remote_build_firmware_uri(self->remote, + xb_node_get_text(n), + NULL); + if (uri != NULL) { + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + continue; + } + } + fwupd_release_add_location(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + + /* checksum */ + checksums = xb_node_query(artifact, "checksum", 0, NULL); + if (checksums != NULL) { + for (guint i = 0; i < checksums->len; i++) { + XbNode *n = g_ptr_array_index(checksums, i); + fwupd_release_add_checksum(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + + /* size */ + size = xb_node_query_text_as_uint(artifact, "size[@type='installed']", NULL); + if (size != G_MAXUINT64) + fwupd_release_set_size(FWUPD_RELEASE(self), size); + + /* success */ + return TRUE; +} + +static gint +fu_release_scheme_compare_cb(gconstpointer a, gconstpointer b, gpointer user_data) +{ + FuRelease *self = FU_RELEASE(user_data); + const gchar *location1 = *((const gchar **)a); + const gchar *location2 = *((const gchar **)b); + g_autofree gchar *scheme1 = fu_common_uri_get_scheme(location1); + g_autofree gchar *scheme2 = fu_common_uri_get_scheme(location2); + guint prio1 = fu_config_get_uri_scheme_prio(self->config, scheme1); + guint prio2 = fu_config_get_uri_scheme_prio(self->config, scheme2); + if (prio1 < prio2) + return -1; + if (prio1 > prio2) + return 1; + return 0; +} + +static gboolean +fu_release_check_requirements_version_check(FuRelease *self, GError **error) +{ + if (self->hard_reqs != NULL) { + for (guint i = 0; i < self->hard_reqs->len; i++) { + XbNode *req = g_ptr_array_index(self->hard_reqs, i); + if (g_strcmp0(xb_node_get_element(req), "firmware") == 0 && + xb_node_get_text(req) == NULL) { + return TRUE; + } + } + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no firmware requirement"); + return FALSE; +} + +static gchar * +fu_release_verfmts_to_string(GPtrArray *verfmts) +{ + GString *str = g_string_new(NULL); + for (guint i = 0; i < verfmts->len; i++) { + XbNode *verfmt = g_ptr_array_index(verfmts, i); + const gchar *tmp = xb_node_get_text(verfmt); + g_string_append_printf(str, "%s;", tmp); + } + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_string_free(str, FALSE); +} + +static gboolean +fu_release_check_verfmt(FuRelease *self, + GPtrArray *verfmts, + FwupdInstallFlags flags, + GError **error) +{ + FwupdVersionFormat fmt_dev = fu_device_get_version_format(self->device); + g_autofree gchar *verfmts_str = NULL; + + /* no device format */ + if (fmt_dev == FWUPD_VERSION_FORMAT_UNKNOWN && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + verfmts_str = fu_release_verfmts_to_string(verfmts); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "release version format '%s' but no device version format", + verfmts_str); + return FALSE; + } + + /* compare all version formats */ + for (guint i = 0; i < verfmts->len; i++) { + XbNode *verfmt = g_ptr_array_index(verfmts, i); + const gchar *tmp = xb_node_get_text(verfmt); + FwupdVersionFormat fmt_rel = fwupd_version_format_from_string(tmp); + if (fmt_dev == fmt_rel) + return TRUE; + } + verfmts_str = fu_release_verfmts_to_string(verfmts); + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Firmware version formats were different, " + "device was '%s' and release is '%s'", + fwupd_version_format_to_string(fmt_dev), + verfmts_str); + return FALSE; + } + g_warning("ignoring version format difference %s:%s", + fwupd_version_format_to_string(fmt_dev), + verfmts_str); + return TRUE; +} + +/* these can all be done without the daemon */ +static gboolean +fu_release_check_requirements(FuRelease *self, + XbNode *component, + XbNode *rel, + FwupdInstallFlags install_flags, + GError **error) +{ + const gchar *branch_new; + const gchar *branch_old; + const gchar *protocol; + const gchar *version; + const gchar *version_lowest; + gboolean matches_guid = FALSE; + gint vercmp; + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) provides = NULL; + g_autoptr(GPtrArray) verfmts = NULL; + + /* does this component provide a GUID the device has */ + provides = xb_node_query(component, "provides/firmware[@type='flashed']", 0, &error_local); + if (provides == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No supported devices found: %s", + error_local->message); + return FALSE; + } + for (guint i = 0; i < provides->len; i++) { + XbNode *provide = g_ptr_array_index(provides, i); + if (fu_device_has_guid(self->device, xb_node_get_text(provide))) { + matches_guid = TRUE; + break; + } + } + if (!matches_guid) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No supported devices found"); + return FALSE; + } + + /* device requires a version check */ + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED)) { + if (!fu_release_check_requirements_version_check(self, error)) { + g_prefix_error(error, "device requires firmware with a version check: "); + return FALSE; + } + } + + /* does the protocol match */ + protocol = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); + if (fu_device_get_protocols(self->device)->len != 0 && protocol != NULL && + !fu_device_has_protocol(self->device, protocol) && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_autofree gchar *str = NULL; + str = fu_common_strjoin_array("|", fu_device_get_protocols(self->device)); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s does not support %s, only %s", + fu_device_get_name(self->device), + protocol, + str); + return FALSE; + } + + /* check the device is not locked */ + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_LOCKED)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] is locked", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* check the branch is not switching */ + branch_new = xb_node_query_text(component, "branch", NULL); + branch_old = fu_device_get_branch(self->device); + if ((install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0 && + g_strcmp0(branch_old, branch_new) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] would switch firmware branch from %s to %s", + fu_device_get_name(self->device), + fu_device_get_id(self->device), + branch_old != NULL ? branch_old : "default", + branch_new != NULL ? branch_new : "default"); + return FALSE; + } + + /* no update abilities */ + if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_autoptr(GString) str = g_string_new(NULL); + g_string_append_printf(str, + "Device %s [%s] does not currently allow updates", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + if (fu_device_get_update_error(self->device) != NULL) { + g_string_append_printf(str, + ": %s", + fu_device_get_update_error(self->device)); + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, str->str); + return FALSE; + } + + /* called with online update, test if device is supposed to allow this */ + if ((install_flags & FWUPD_INSTALL_FLAG_OFFLINE) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s [%s] only allows offline updates", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* get device */ + version = fu_device_get_version(self->device); + if (version == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s [%s] has no firmware version", + fu_device_get_name(self->device), + fu_device_get_id(self->device)); + return FALSE; + } + + /* check the version formats match if set in the release */ + if ((install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { + verfmts = + xb_node_query(component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL); + if (verfmts != NULL) { + if (!fu_release_check_verfmt(self, verfmts, install_flags, error)) + return FALSE; + } + } + + /* compare to the lowest supported version, if it exists */ + version_lowest = fu_device_get_version_lowest(self->device); + if (version_lowest != NULL && + fu_common_vercmp_full(version_lowest, + fu_release_get_version(self), + fu_device_get_version_format(self->device)) > 0 && + (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Specified firmware is older than the minimum " + "required version '%s < %s'", + version, + version_lowest); + return FALSE; + } + + /* is this a downgrade or re-install */ + vercmp = fu_common_vercmp_full(version, + fu_release_get_version(self), + fu_device_get_version_format(self->device)); + if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) && + vercmp >= 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device only supports version upgrades"); + return FALSE; + } + if (vercmp == 0 && (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_SAME, + "Specified firmware is already installed '%s'", + fu_release_get_version(self)); + return FALSE; + } + self->is_downgrade = vercmp > 0; + if (self->is_downgrade && (install_flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0 && + (install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_NEWER, + "Specified firmware is older than installed '%s < %s'", + fu_release_get_version(self), + version); + return FALSE; + } + + /* verify */ + if (!fu_keyring_get_release_flags(rel, &self->trust_flags, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_warning("Ignoring verification for %s: %s", + fu_device_get_name(self->device), + error_local->message); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + return TRUE; +} + +/** + * fu_release_load: + * @self: a #FuRelease + * @component: (not nullable): a #XbNode + * @rel_optional: (nullable): a #XbNode + * @install_flags: a #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @error: (nullable): optional return location for an error + * + * Loads then checks any requirements of this release. This will typically involve checking + * that the device can accept the component (the GUIDs match) and that the device can be + * upgraded with this firmware version. + * + * Returns: %TRUE if the release was loaded and the requirements passed + **/ +gboolean +fu_release_load(FuRelease *self, + XbNode *component, + XbNode *rel_optional, + FwupdInstallFlags install_flags, + GError **error) +{ + const gchar *tmp; + guint64 tmp64; + GBytes *blob_fw_tmp; + g_autofree gchar *name_xpath = NULL; + g_autofree gchar *namevs_xpath = NULL; + g_autofree gchar *summary_xpath = NULL; + g_autofree gchar *description_xpath = NULL; + g_autoptr(GPtrArray) cats = NULL; + g_autoptr(GPtrArray) tags = NULL; + g_autoptr(GPtrArray) issues = NULL; + g_autoptr(XbNode) artifact = NULL; + g_autoptr(XbNode) description = NULL; + g_autoptr(XbNode) rel = NULL; + g_autoptr(GError) error_soft = NULL; + g_autoptr(GError) error_hard = NULL; + + g_return_val_if_fail(FU_IS_RELEASE(self), FALSE); + g_return_val_if_fail(XB_IS_NODE(component), FALSE); + g_return_val_if_fail(rel_optional == NULL || XB_IS_NODE(rel_optional), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_return_val_if_fail(fwupd_release_get_appstream_id(FWUPD_RELEASE(self)) == NULL, FALSE); + + /* set from the component */ + tmp = xb_node_query_text(component, "id", NULL); + if (tmp != NULL) + fwupd_release_set_appstream_id(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "url[@type='homepage']", NULL); + if (tmp != NULL) + fwupd_release_set_homepage(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "project_license", NULL); + if (tmp != NULL) + fwupd_release_set_license(FWUPD_RELEASE(self), tmp); + name_xpath = fu_release_get_localized_xpath(self, "name"); + tmp = xb_node_query_text(component, name_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_name(FWUPD_RELEASE(self), tmp); + summary_xpath = fu_release_get_localized_xpath(self, "summary"); + tmp = xb_node_query_text(component, summary_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_summary(FWUPD_RELEASE(self), tmp); + namevs_xpath = fu_release_get_localized_xpath(self, "name_variant_suffix"); + tmp = xb_node_query_text(component, namevs_xpath, NULL); + if (tmp != NULL) + fwupd_release_set_name_variant_suffix(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "branch", NULL); + if (tmp != NULL) + fwupd_release_set_branch(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "developer_name", NULL); + if (tmp != NULL) + fwupd_release_set_vendor(FWUPD_RELEASE(self), tmp); + + /* use default release */ + if (rel_optional == NULL) { + g_autoptr(GError) error_local = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + error); + if (query == NULL) + return FALSE; + rel = xb_node_query_first_full(component, query, &error_local); +#else + rel = xb_node_query_first(component, "releases/release", &error_local); +#endif + if (rel == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get default release: %s", + error_local->message); + return FALSE; + } + } else { + rel = g_object_ref(rel_optional); + } + + /* the version is fixed up with the device format */ + tmp = xb_node_get_attr(rel, "version"); + if (tmp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "version unset"); + return FALSE; + } + if (self->device != NULL) { + g_autofree gchar *version_rel = NULL; + version_rel = fu_release_get_release_version(self, tmp, error); + if (version_rel == NULL) + return FALSE; + fwupd_release_set_version(FWUPD_RELEASE(self), version_rel); + } else { + fwupd_release_set_version(FWUPD_RELEASE(self), tmp); + } + + /* optional release ID -- currently a integer but maybe namespaced in the future */ + fwupd_release_set_id(FWUPD_RELEASE(self), xb_node_get_attr(rel, "id")); + + /* find the remote */ + tmp = xb_node_query_text(component, "../custom/value[@key='fwupd::RemoteId']", NULL); + if (tmp != NULL) + fwupd_release_set_remote_id(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "../custom/value[@key='LVFS::Distributor']", NULL); + if (g_strcmp0(tmp, "community") == 0) + fwupd_release_add_flag(FWUPD_RELEASE(self), FWUPD_RELEASE_FLAG_IS_COMMUNITY); + + /* this is the more modern way to do this */ + artifact = xb_node_query_first(rel, "artifacts/artifact", NULL); + if (artifact != NULL) { + if (!fu_release_load_artifact(self, artifact, error)) + return FALSE; + } + description_xpath = fu_release_get_localized_xpath(self, "description"); + description = xb_node_query_first(rel, description_xpath, NULL); + if (description != NULL) { + g_autofree gchar *xml = NULL; + g_autoptr(GString) str = NULL; + xml = xb_node_export(description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL); + str = g_string_new(xml); + if (self->device != NULL && self->request != NULL && + fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_AFFECTS_FDE) && + !fu_engine_request_has_feature_flag(self->request, + FWUPD_FEATURE_FLAG_FDE_WARNING)) { + g_string_prepend( + str, + "

Some of the platform secrets may be invalidated when " + "updating this firmware. Please ensure you have the volume " + "recovery key before continuing.

"); + } + if (fwupd_release_has_flag(FWUPD_RELEASE(self), FWUPD_RELEASE_FLAG_IS_COMMUNITY) && + self->request != NULL && + !fu_engine_request_has_feature_flag(self->request, + FWUPD_FEATURE_FLAG_COMMUNITY_TEXT)) { + g_string_prepend( + str, + "

This firmware is provided by LVFS community " + "members and is not provided (or supported) by the original " + "hardware vendor. " + "Installing this update may also void any device warranty.

"); + } + if (str->len > 0) + fwupd_release_set_description(FWUPD_RELEASE(self), str->str); + } + if (artifact == NULL) { + tmp = xb_node_query_text(rel, "location", NULL); + if (tmp != NULL) { + g_autofree gchar *uri = NULL; + if (self->remote != NULL) + uri = fwupd_remote_build_firmware_uri(self->remote, tmp, NULL); + if (uri == NULL) + uri = g_strdup(tmp); + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + } + } + if (fwupd_release_get_locations(FWUPD_RELEASE(self))->len == 0 && self->remote != NULL && + fwupd_remote_get_kind(self->remote) == FWUPD_REMOTE_KIND_DIRECTORY) { + tmp = xb_node_query_text(component, + "../custom/value[@key='fwupd::FilenameCache']", + NULL); + if (tmp != NULL) { + g_autofree gchar *uri = g_strdup_printf("file://%s", tmp); + fwupd_release_add_location(FWUPD_RELEASE(self), uri); + } + } + if (artifact == NULL) { + tmp = xb_node_query_text(rel, "checksum[@target='content']", NULL); + if (tmp != NULL) + fwupd_release_set_filename(FWUPD_RELEASE(self), tmp); + } + tmp = xb_node_query_text(rel, "url[@type='details']", NULL); + if (tmp != NULL) + fwupd_release_set_details_url(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(rel, "url[@type='source']", NULL); + if (tmp != NULL) + fwupd_release_set_source_url(FWUPD_RELEASE(self), tmp); + if (artifact == NULL) { + g_autoptr(GPtrArray) checksums = NULL; + checksums = xb_node_query(rel, "checksum[@target='container']", 0, NULL); + if (checksums != NULL) { + for (guint i = 0; i < checksums->len; i++) { + XbNode *n = g_ptr_array_index(checksums, i); + if (xb_node_get_text(n) == NULL) + continue; + fwupd_release_add_checksum(FWUPD_RELEASE(self), + xb_node_get_text(n)); + } + } + } + if (artifact == NULL) { + tmp64 = xb_node_query_text_as_uint(rel, "size[@type='installed']", NULL); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_size(FWUPD_RELEASE(self), tmp64); + } + if (fwupd_release_get_size(FWUPD_RELEASE(self)) == 0) { + GBytes *sz = xb_node_get_data(rel, "fwupd::ReleaseSize"); + if (sz != NULL) { + const guint64 *sizeptr = g_bytes_get_data(sz, NULL); + fwupd_release_set_size(FWUPD_RELEASE(self), *sizeptr); + } + } + tmp = xb_node_get_attr(rel, "urgency"); + if (tmp != NULL) + fwupd_release_set_urgency(FWUPD_RELEASE(self), + fwupd_release_urgency_from_string(tmp)); + tmp64 = xb_node_get_attr_as_uint(rel, "install_duration"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_install_duration(FWUPD_RELEASE(self), tmp64); + tmp64 = xb_node_get_attr_as_uint(rel, "timestamp"); + if (tmp64 != G_MAXUINT64) + fwupd_release_set_created(FWUPD_RELEASE(self), tmp64); + cats = xb_node_query(component, "categories/category", 0, NULL); + if (cats != NULL) { + for (guint i = 0; i < cats->len; i++) { + XbNode *n = g_ptr_array_index(cats, i); + fwupd_release_add_category(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + tags = xb_node_query(component, "tags/tag[@namespace=$'lvfs']", 0, NULL); + if (tags != NULL) { + for (guint i = 0; i < tags->len; i++) { + XbNode *tag = g_ptr_array_index(tags, i); + fwupd_release_add_tag(FWUPD_RELEASE(self), xb_node_get_text(tag)); + } + } + issues = xb_node_query(component, "issues/issue", 0, NULL); + if (issues != NULL) { + for (guint i = 0; i < issues->len; i++) { + XbNode *n = g_ptr_array_index(issues, i); + fwupd_release_add_issue(FWUPD_RELEASE(self), xb_node_get_text(n)); + } + } + tmp = xb_node_query_text(component, "screenshots/screenshot/caption", NULL); + if (tmp != NULL) + fwupd_release_set_detach_caption(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "screenshots/screenshot/image", NULL); + if (tmp != NULL) { + if (self->remote != NULL) { + g_autofree gchar *img = NULL; + img = fwupd_remote_build_firmware_uri(self->remote, tmp, error); + if (img == NULL) + return FALSE; + fwupd_release_set_detach_image(FWUPD_RELEASE(self), img); + } else { + fwupd_release_set_detach_image(FWUPD_RELEASE(self), tmp); + } + } + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateProtocol']", NULL); + if (tmp != NULL) + fwupd_release_set_protocol(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateMessage']", NULL); + if (tmp != NULL) + fwupd_release_set_update_message(FWUPD_RELEASE(self), tmp); + tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateImage']", NULL); + if (tmp != NULL) { + if (self->remote != NULL) { + g_autofree gchar *img = NULL; + img = fwupd_remote_build_firmware_uri(self->remote, tmp, error); + if (img == NULL) + return FALSE; + fwupd_release_set_update_image(FWUPD_RELEASE(self), img); + } else { + fwupd_release_set_update_image(FWUPD_RELEASE(self), tmp); + } + } + + /* hard and soft requirements */ + self->hard_reqs = xb_node_query(component, "requires/*", 0, &error_hard); + if (self->hard_reqs == NULL) { + if (!g_error_matches(error_hard, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches(error_hard, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error(error, g_steal_pointer(&error_hard)); + return FALSE; + } + } + self->soft_reqs = xb_node_query(component, "suggests/*|recommends/*", 0, &error_soft); + if (self->soft_reqs == NULL) { + if (!g_error_matches(error_soft, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) && + !g_error_matches(error_soft, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_propagate_error(error, g_steal_pointer(&error_soft)); + return FALSE; + } + } + + /* get per-release firmware blob */ + blob_fw_tmp = xb_node_get_data(rel, "fwupd::FirmwareBlob"); + if (blob_fw_tmp != NULL) + self->blob_fw = g_bytes_ref(blob_fw_tmp); + + /* to build the firmware */ + tmp = g_object_get_data(G_OBJECT(component), "fwupd::BuilderScript"); + if (tmp != NULL) { + self->builder_script = g_strdup(tmp); + tmp = g_object_get_data(G_OBJECT(component), "fwupd::BuilderOutput"); + if (tmp == NULL) + tmp = "firmware.bin"; + self->builder_output = g_strdup(tmp); + } + + /* sort the locations by scheme */ + if (self->config != NULL) { + g_ptr_array_sort_with_data(fwupd_release_get_locations(FWUPD_RELEASE(self)), + fu_release_scheme_compare_cb, + self); + } + + /* check requirements for device */ + if (self->device != NULL && self->request != NULL && + fu_engine_request_get_kind(self->request) == FU_ENGINE_REQUEST_KIND_ACTIVE) { + if (!fu_release_check_requirements(self, component, rel, install_flags, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +/** + * fu_release_get_trust_flags: + * @self: a #FuRelease + * + * Gets the trust flags for this task. + * + * NOTE: This is only set after fu_release_load() has been called successfully, and + * is only valid when a request has been set. + * + * Returns: the #FwupdReleaseFlags, e.g. #FWUPD_TRUST_FLAG_PAYLOAD + **/ +FwupdReleaseFlags +fu_release_get_trust_flags(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), FALSE); + return self->trust_flags; +} + +/** + * fu_release_get_action_id: + * @self: a #FuEngine + * + * Gets the PolicyKit action ID to use for the install operation. + * + * Returns: string, e.g. `org.freedesktop.fwupd.update-internal-trusted` + **/ +const gchar * +fu_release_get_action_id(FuRelease *self) +{ + /* relax authentication checks for removable devices */ + if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_INTERNAL)) { + if (self->is_downgrade) { + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.downgrade-hotplug-trusted"; + return "org.freedesktop.fwupd.downgrade-hotplug"; + } + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.update-hotplug-trusted"; + return "org.freedesktop.fwupd.update-hotplug"; + } + + /* internal device */ + if (self->is_downgrade) { + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.downgrade-internal-trusted"; + return "org.freedesktop.fwupd.downgrade-internal"; + } + if (self->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) + return "org.freedesktop.fwupd.update-internal-trusted"; + return "org.freedesktop.fwupd.update-internal"; +} + +/** + * fu_release_compare: + * @release1: first task to compare. + * @release2: second task to compare. + * + * Compares two install tasks. + * + * Returns: 1, 0 or -1 if @release1 is greater, equal, or less than @release2, respectively. + **/ +gint +fu_release_compare(FuRelease *release1, FuRelease *release2) +{ + FuDevice *device1 = fu_release_get_device(release1); + FuDevice *device2 = fu_release_get_device(release2); + if (fu_device_get_order(device1) < fu_device_get_order(device2)) + return -1; + if (fu_device_get_order(device1) > fu_device_get_order(device2)) + return 1; + return 0; +} + +static void +fu_release_init(FuRelease *self) +{ + self->trust_flags = FWUPD_TRUST_FLAG_NONE; +} + +static void +fu_release_finalize(GObject *obj) +{ + FuRelease *self = FU_RELEASE(obj); + + g_free(self->builder_script); + g_free(self->builder_output); + if (self->request != NULL) + g_object_unref(self->request); + if (self->device != NULL) + g_object_unref(self->device); + if (self->remote != NULL) + g_object_unref(self->remote); + if (self->config != NULL) + g_object_unref(self->config); + if (self->blob_fw != NULL) + g_bytes_unref(self->blob_fw); + if (self->soft_reqs != NULL) + g_ptr_array_unref(self->soft_reqs); + if (self->hard_reqs != NULL) + g_ptr_array_unref(self->hard_reqs); + + G_OBJECT_CLASS(fu_release_parent_class)->finalize(obj); +} + +static void +fu_release_class_init(FuReleaseClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_release_finalize; +} + +FuRelease * +fu_release_new(void) +{ + FuRelease *self; + self = g_object_new(FU_TYPE_RELEASE, NULL); + return FU_RELEASE(self); +} diff --git a/src/fu-release.h b/src/fu-release.h new file mode 100644 index 000000000..b95f68629 --- /dev/null +++ b/src/fu-release.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include +#include + +#include "fu-config.h" +#include "fu-device.h" +#include "fu-engine-request.h" + +#define FU_TYPE_RELEASE (fu_release_get_type()) +G_DECLARE_FINAL_TYPE(FuRelease, fu_release, FU, RELEASE, FwupdRelease) + +FuRelease * +fu_release_new(void); + +#define fu_release_get_version(r) fwupd_release_get_version(FWUPD_RELEASE(r)) +#define fu_release_get_branch(r) fwupd_release_get_branch(FWUPD_RELEASE(r)) +#define fu_release_get_checksums(r) fwupd_release_get_checksums(FWUPD_RELEASE(r)) +#define fu_release_add_flag(r, v) fwupd_release_add_flag(FWUPD_RELEASE(r), v) +#define fu_release_add_tag(r, v) fwupd_release_add_tag(FWUPD_RELEASE(r), v) +#define fu_release_add_metadata(r, v) fwupd_release_add_metadata(FWUPD_RELEASE(r), v) + +FuDevice * +fu_release_get_device(FuRelease *self); +GBytes * +fu_release_get_fw_blob(FuRelease *self); +FuEngineRequest * +fu_release_get_request(FuRelease *self); +const gchar * +fu_release_get_builder_script(FuRelease *self); +const gchar * +fu_release_get_builder_output(FuRelease *self); +GPtrArray * +fu_release_get_soft_reqs(FuRelease *self); +GPtrArray * +fu_release_get_hard_reqs(FuRelease *self); + +void +fu_release_set_request(FuRelease *self, FuEngineRequest *request); +void +fu_release_set_device(FuRelease *self, FuDevice *device); +void +fu_release_set_remote(FuRelease *self, FwupdRemote *remote); +void +fu_release_set_config(FuRelease *self, FuConfig *config); + +gboolean +fu_release_load(FuRelease *self, + XbNode *component, + XbNode *rel, + FwupdInstallFlags flags, + GError **error); +FwupdReleaseFlags +fu_release_get_trust_flags(FuRelease *self); +const gchar * +fu_release_get_action_id(FuRelease *self); +gint +fu_release_compare(FuRelease *release1, FuRelease *release2); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 8c5a001c3..bd0f7fc61 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -24,7 +24,6 @@ #include "fu-engine.h" #include "fu-hash.h" #include "fu-history.h" -#include "fu-install-task.h" #include "fu-plugin-list.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" @@ -206,12 +205,15 @@ fu_engine_requirements_missing_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " not.going.to.exist" " " + " " + " " + " " ""; /* set up a dummy version */ @@ -226,8 +228,11 @@ fu_engine_requirements_missing_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert_false(ret); } @@ -240,12 +245,15 @@ fu_engine_requirements_soft_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " not.going.to.exist" " " + " " + " " + " " ""; /* set up a dummy version */ @@ -260,8 +268,11 @@ fu_engine_requirements_soft_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_FORCE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_FORCE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -274,12 +285,15 @@ fu_engine_requirements_client_fail_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " detach-action" " " + " " + " " + " " ""; /* make the component require one thing */ @@ -291,8 +305,11 @@ fu_engine_requirements_client_fail_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); } @@ -305,12 +322,15 @@ fu_engine_requirements_client_invalid_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " hello-dave" " " + " " + " " + " " ""; /* make the component require one thing */ @@ -322,8 +342,11 @@ fu_engine_requirements_client_invalid_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert_false(ret); } @@ -336,12 +359,15 @@ fu_engine_requirements_client_pass_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " detach-action" " " + " " + " " + " " ""; /* set up a dummy version */ @@ -356,8 +382,11 @@ fu_engine_requirements_client_pass_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -368,9 +397,8 @@ fu_engine_requirements_version_require_func(gconstpointer user_data) FuTest *self = (FuTest *)user_data; gboolean ret; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); - g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -404,14 +432,64 @@ fu_engine_requirements_version_require_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_true( g_str_has_prefix(error->message, "device requires firmware with a version check")); g_assert_false(ret); } +static void +fu_engine_requirements_version_lowest_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version(device, "1.2.3"); + fu_device_set_version_lowest(device, "1.2.3"); + fu_device_add_vendor_id(device, "FFFF"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012"); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_true( + g_str_has_prefix(error->message, "Specified firmware is older than the minimum")); + g_assert_false(ret); +} + static void fu_engine_requirements_unsupported_func(gconstpointer user_data) { @@ -420,12 +498,15 @@ fu_engine_requirements_unsupported_func(gconstpointer user_data) g_autoptr(XbSilo) silo = NULL; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; const gchar *xml = "" " " " " " " + " " + " " + " " ""; /* set up a dummy version */ @@ -440,8 +521,11 @@ fu_engine_requirements_unsupported_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); } @@ -455,7 +539,7 @@ fu_engine_requirements_child_func(gconstpointer user_data) g_autoptr(FuDevice) child = fu_device_new(); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -496,8 +580,12 @@ fu_engine_requirements_child_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -511,7 +599,7 @@ fu_engine_requirements_child_fail_func(gconstpointer user_data) g_autoptr(FuDevice) child = fu_device_new(); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -552,8 +640,12 @@ fu_engine_requirements_child_fail_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_nonnull( g_strstr_len(error->message, -1, "Not compatible with child device version")); @@ -565,7 +657,7 @@ fu_engine_requirements_func(gconstpointer user_data) { gboolean ret; g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; @@ -574,6 +666,9 @@ fu_engine_requirements_func(gconstpointer user_data) " " " org.test.dummy" " " + " " + " " + " " ""; /* set up some dummy versions */ @@ -589,8 +684,11 @@ fu_engine_requirements_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(NULL, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -603,7 +701,7 @@ fu_engine_requirements_device_func(gconstpointer user_data) g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -647,8 +745,12 @@ fu_engine_requirements_device_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -661,7 +763,7 @@ fu_engine_requirements_device_plain_func(gconstpointer user_data) g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -694,8 +796,12 @@ fu_engine_requirements_device_plain_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -706,9 +812,8 @@ fu_engine_requirements_version_format_func(gconstpointer user_data) FuTest *self = (FuTest *)user_data; gboolean ret; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); - g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -743,8 +848,9 @@ fu_engine_requirements_version_format_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_nonnull( g_strstr_len(error->message, -1, "Firmware version formats were different")); @@ -757,9 +863,8 @@ fu_engine_requirements_only_upgrade_func(gconstpointer user_data) FuTest *self = (FuTest *)user_data; gboolean ret; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); - g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -789,12 +894,9 @@ fu_engine_requirements_only_upgrade_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, - request, - task, - FWUPD_INSTALL_FLAG_ALLOW_OLDER, - &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_nonnull(g_strstr_len(error->message, -1, "Device only supports version upgrades")); g_assert_false(ret); @@ -811,8 +913,8 @@ fu_engine_requirements_sibling_device_func(gconstpointer user_data) g_autoptr(FuDevice) parent = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task1 = NULL; - g_autoptr(FuInstallTask) task2 = NULL; + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -879,8 +981,12 @@ fu_engine_requirements_sibling_device_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task1 = fu_install_task_new(device1, component); - ret = fu_engine_check_requirements(engine, request, task1, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release1, device1); + fu_release_set_request(release1, request); + ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release1, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); g_clear_error(&error); @@ -900,8 +1006,12 @@ fu_engine_requirements_sibling_device_func(gconstpointer user_data) fu_engine_add_device(engine, device2); /* check this passes */ - task2 = fu_install_task_new(device1, component); - ret = fu_engine_check_requirements(engine, request, task2, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release2, device1); + fu_release_set_request(release2, request); + ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release2, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -915,7 +1025,7 @@ fu_engine_requirements_other_device_func(gconstpointer user_data) g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -966,8 +1076,12 @@ fu_engine_requirements_other_device_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device1, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device1); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -981,8 +1095,8 @@ fu_engine_requirements_protocol_check_func(gconstpointer user_data) g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); g_autoptr(GPtrArray) devices = NULL; - g_autoptr(FuInstallTask) task1 = NULL; - g_autoptr(FuInstallTask) task2 = NULL; + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -1045,16 +1159,17 @@ fu_engine_requirements_protocol_check_func(gconstpointer user_data) g_assert_nonnull(component); /* check this fails */ - task1 = fu_install_task_new(device1, component); - ret = fu_engine_check_requirements(engine, request, task1, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release1, device1); + fu_release_set_request(release1, request); + ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); g_clear_error(&error); /* check this passes */ - task2 = fu_install_task_new(device2, component); - ret = fu_engine_check_requirements(engine, request, task2, FWUPD_INSTALL_FLAG_NONE, &error); - + fu_release_set_device(release2, device2); + fu_release_set_request(release2, request); + ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -1068,7 +1183,7 @@ fu_engine_requirements_parent_device_func(gconstpointer user_data) g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo = NULL; @@ -1121,8 +1236,12 @@ fu_engine_requirements_parent_device_func(gconstpointer user_data) g_assert_nonnull(component); /* check this passes */ - task = fu_install_task_new(device2, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device2); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); } @@ -1372,7 +1491,7 @@ fu_engine_require_hwid_func(gconstpointer user_data) g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; @@ -1423,8 +1542,12 @@ fu_engine_require_hwid_func(gconstpointer user_data) g_assert_nonnull(component); /* check requirements */ - task = fu_install_task_new(device, component); - ret = fu_engine_check_requirements(engine, request, task, FWUPD_INSTALL_FLAG_NONE, &error); + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_nonnull(error); g_assert_cmpstr(error->message, @@ -1717,7 +1840,7 @@ fu_engine_history_func(gconstpointer user_data) g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuHistory) history = NULL; - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(FwupdDevice) device3 = NULL; g_autoptr(FwupdDevice) device4 = NULL; @@ -1784,14 +1907,16 @@ fu_engine_history_func(gconstpointer user_data) fu_device_set_metadata_integer(device, "nr-update", 0); /* install it */ - task = fu_install_task_new(device, component); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -1860,13 +1985,18 @@ fu_engine_multiple_rels_func(gconstpointer user_data) g_autofree gchar *filename = NULL; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); - g_autoptr(FuInstallTask) task = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo_empty = xb_silo_new(); g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE); + g_autoptr(GPtrArray) releases = NULL; + g_autoptr(GPtrArray) rels = NULL; +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + g_autoptr(XbQuery) query = NULL; +#endif /* ensure empty tree */ fu_self_test_mkroot(); @@ -1921,15 +2051,40 @@ fu_engine_multiple_rels_func(gconstpointer user_data) /* set up counter */ fu_device_set_metadata_integer(device, "nr-update", 0); - /* install it */ - task = fu_install_task_new(device, component); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + /* get all */ +#if LIBXMLB_CHECK_VERSION(0, 2, 0) + query = xb_query_new_full(xb_node_get_silo(component), + "releases/release", + XB_QUERY_FLAG_FORCE_NODE_CACHE, + &error); + g_assert_no_error(error); + g_assert_nonnull(query); + rels = xb_node_query_full(component, query, &error); +#else + rels = xb_node_query(component, "releases/release", 0, &error); +#endif + g_assert_no_error(error); + g_assert_nonnull(rels); + + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint i = 0; i < rels->len; i++) { + XbNode *rel = g_ptr_array_index(rels, i); + g_autoptr(FuRelease) release = fu_release_new(); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, rel, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_ptr_array_add(releases, g_object_ref(release)); + } + + /* install them */ + ret = fu_engine_install_releases(engine, + request, + releases, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -1948,7 +2103,7 @@ fu_engine_history_inherit(gconstpointer user_data) g_autofree gchar *history_db = NULL; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; @@ -2016,14 +2171,16 @@ fu_engine_history_inherit(gconstpointer user_data) /* install it */ g_setenv("FWUPD_PLUGIN_TEST", "requires-activation", TRUE); - task = fu_install_task_new(device, component); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -2045,13 +2202,12 @@ fu_engine_history_inherit(gconstpointer user_data) fu_progress_reset(progress); fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(device, "1.2.2"); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); g_object_unref(engine); @@ -2097,7 +2253,7 @@ fu_engine_install_needs_reboot(gconstpointer user_data) g_autofree gchar *filename = NULL; g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; @@ -2155,14 +2311,16 @@ fu_engine_install_needs_reboot(gconstpointer user_data) /* install it */ g_setenv("FWUPD_PLUGIN_TEST", "requires-reboot", TRUE); - task = fu_install_task_new(device, component); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -2185,7 +2343,7 @@ fu_engine_history_error_func(gconstpointer user_data) g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx); g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE); g_autoptr(FuHistory) history = NULL; - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error2 = NULL; @@ -2240,14 +2398,16 @@ fu_engine_history_error_func(gconstpointer user_data) &error); g_assert_no_error(error); g_assert_nonnull(component); - task = fu_install_task_new(device, component); - ret = fu_engine_install(engine, - task, - blob_cab, - progress, - FWUPD_INSTALL_FLAG_NONE, - FWUPD_FEATURE_FLAG_NONE, - &error); + fu_release_set_device(release, device); + ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_install_release(engine, + release, + blob_cab, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_cmpstr(error->message, ==, "device was not in supported mode"); g_assert_false(ret); @@ -3259,7 +3419,7 @@ fu_plugin_composite_func(gconstpointer user_data) g_autoptr(GBytes) blob = NULL; g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices = NULL; - g_autoptr(GPtrArray) install_tasks = + g_autoptr(GPtrArray) releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(XbSilo) silo_empty = xb_silo_new(); @@ -3361,12 +3521,17 @@ fu_plugin_composite_func(gconstpointer user_data) /* do any devices pass the requirements */ for (guint j = 0; j < devices->len; j++) { FuDevice *device = g_ptr_array_index(devices, j); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_local = NULL; /* is this component valid for the device */ - task = fu_install_task_new(device, component); - if (!fu_engine_check_requirements(engine, request, task, 0, &error_local)) { + fu_release_set_device(release, device); + fu_release_set_request(release, request); + if (!fu_release_load(release, + component, + NULL, + FWUPD_INSTALL_FLAG_NONE, + &error_local)) { g_debug("requirement on %s:%s failed: %s", fu_device_get_id(device), xb_node_query_text(component, "id", NULL), @@ -3374,19 +3539,19 @@ fu_plugin_composite_func(gconstpointer user_data) continue; } - g_ptr_array_add(install_tasks, g_steal_pointer(&task)); + g_ptr_array_add(releases, g_steal_pointer(&release)); } } - g_assert_cmpint(install_tasks->len, ==, 3); + g_assert_cmpint(releases->len, ==, 3); /* install the cab */ - ret = fu_engine_install_tasks(engine, - request, - install_tasks, - blob, - progress, - FWUPD_DEVICE_FLAG_NONE, - &error); + ret = fu_engine_install_releases(engine, + request, + releases, + blob, + progress, + FWUPD_DEVICE_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -3628,39 +3793,43 @@ fu_progressbar_func(gconstpointer user_data) } static gint -fu_install_task_compare_func_cb(gconstpointer a, gconstpointer b) +fu_release_compare_func_cb(gconstpointer a, gconstpointer b) { - FuInstallTask *task_a = *((FuInstallTask **)a); - FuInstallTask *task_b = *((FuInstallTask **)b); - return fu_install_task_compare(task_a, task_b); + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); } static void -fu_install_task_compare_func(gconstpointer user_data) +fu_release_compare_func(gconstpointer user_data) { FuDevice *device_tmp; - g_autoptr(GPtrArray) install_tasks = NULL; + g_autoptr(GPtrArray) releases = g_ptr_array_new(); g_autoptr(FuDevice) device1 = fu_device_new(); g_autoptr(FuDevice) device2 = fu_device_new(); g_autoptr(FuDevice) device3 = fu_device_new(); - - install_tasks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_autoptr(FuRelease) release1 = fu_release_new(); + g_autoptr(FuRelease) release2 = fu_release_new(); + g_autoptr(FuRelease) release3 = fu_release_new(); fu_device_set_order(device1, 99); - g_ptr_array_add(install_tasks, fu_install_task_new(device1, NULL)); + fu_release_set_device(release1, device1); + g_ptr_array_add(releases, release1); fu_device_set_order(device2, 11); - g_ptr_array_add(install_tasks, fu_install_task_new(device2, NULL)); + fu_release_set_device(release2, device2); + g_ptr_array_add(releases, release2); fu_device_set_order(device3, 33); - g_ptr_array_add(install_tasks, fu_install_task_new(device3, NULL)); + fu_release_set_device(release3, device3); + g_ptr_array_add(releases, release3); /* order the install tasks */ - g_ptr_array_sort(install_tasks, fu_install_task_compare_func_cb); - g_assert_cmpint(install_tasks->len, ==, 3); - device_tmp = fu_install_task_get_device(g_ptr_array_index(install_tasks, 0)); + g_ptr_array_sort(releases, fu_release_compare_func_cb); + g_assert_cmpint(releases->len, ==, 3); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 0)); g_assert_cmpint(fu_device_get_order(device_tmp), ==, 11); - device_tmp = fu_install_task_get_device(g_ptr_array_index(install_tasks, 1)); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 1)); g_assert_cmpint(fu_device_get_order(device_tmp), ==, 33); - device_tmp = fu_install_task_get_device(g_ptr_array_index(install_tasks, 2)); + device_tmp = fu_release_get_device(g_ptr_array_index(releases, 2)); g_assert_cmpint(fu_device_get_order(device_tmp), ==, 99); } @@ -3729,7 +3898,7 @@ main(int argc, char **argv) g_test_add_data_func("/fwupd/device-list{remove-chain}", self, fu_device_list_remove_chain_func); - g_test_add_data_func("/fwupd/install-task{compare}", self, fu_install_task_compare_func); + g_test_add_data_func("/fwupd/release{compare}", self, fu_release_compare_func); g_test_add_data_func("/fwupd/engine{device-unlock}", self, fu_engine_device_unlock_func); g_test_add_data_func("/fwupd/engine{multiple-releases}", self, @@ -3772,6 +3941,9 @@ main(int argc, char **argv) g_test_add_data_func("/fwupd/engine{requirements-version-require}", self, fu_engine_requirements_version_require_func); + g_test_add_data_func("/fwupd/engine{requirements-version-lowest}", + self, + fu_engine_requirements_version_lowest_func); g_test_add_data_func("/fwupd/engine{requirements-parent-device}", self, fu_engine_requirements_parent_device_func); diff --git a/src/fu-tool.c b/src/fu-tool.c index 3f3947335..0cdef9091 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1103,11 +1103,11 @@ fu_util_firmware_dump(FuUtilPrivate *priv, gchar **values, GError **error) } static gint -fu_util_install_task_sort_cb(gconstpointer a, gconstpointer b) +fu_util_release_sort_cb(gconstpointer a, gconstpointer b) { - FuInstallTask *task1 = *((FuInstallTask **)a); - FuInstallTask *task2 = *((FuInstallTask **)b); - return fu_install_task_compare(task1, task2); + FuRelease *release1 = *((FuRelease **)a); + FuRelease *release2 = *((FuRelease **)b); + return fu_release_compare(release1, release2); } static void @@ -1175,7 +1175,7 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices_possible = NULL; g_autoptr(GPtrArray) errors = NULL; - g_autoptr(GPtrArray) install_tasks = NULL; + g_autoptr(GPtrArray) releases = NULL; g_autoptr(XbSilo) silo = NULL; /* load engine */ @@ -1229,21 +1229,23 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) /* for each component in the silo */ errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); - install_tasks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); for (guint i = 0; i < components->len; i++) { XbNode *component = g_ptr_array_index(components, i); /* do any devices pass the requirements */ for (guint j = 0; j < devices_possible->len; j++) { FuDevice *device = g_ptr_array_index(devices_possible, j); - g_autoptr(FuInstallTask) task = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_local = NULL; /* is this component valid for the device */ - task = fu_install_task_new(device, component); + fu_release_set_device(release, device); + fu_release_set_request(release, priv->request); + if (!fu_release_load(release, component, NULL, priv->flags, error)) + return FALSE; if (!fu_engine_check_requirements(priv->engine, - priv->request, - task, + release, priv->flags | FWUPD_INSTALL_FLAG_FORCE, &error_local)) { g_debug("first pass requirement on %s:%s failed: %s", @@ -1257,8 +1259,7 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) /* make a second pass using possibly updated version format now */ fu_engine_md_refresh_device_from_component(priv->engine, device, component); if (!fu_engine_check_requirements(priv->engine, - priv->request, - task, + release, priv->flags, &error_local)) { g_debug("second pass requirement on %s:%s failed: %s", @@ -1273,15 +1274,15 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) fu_device_incorporate_from_component(device, component); /* success */ - g_ptr_array_add(install_tasks, g_steal_pointer(&task)); + g_ptr_array_add(releases, g_steal_pointer(&release)); } } /* order the install tasks by the device priority */ - g_ptr_array_sort(install_tasks, fu_util_install_task_sort_cb); + g_ptr_array_sort(releases, fu_util_release_sort_cb); /* nothing suitable */ - if (install_tasks->len == 0) { + if (releases->len == 0) { GError *error_tmp = fu_common_error_array_get_best(errors); g_propagate_error(error, error_tmp); return FALSE; @@ -1294,13 +1295,13 @@ fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) priv); /* install all the tasks */ - if (!fu_engine_install_tasks(priv->engine, - priv->request, - install_tasks, - blob_cab, - priv->progress, - priv->flags, - error)) + if (!fu_engine_install_releases(priv->engine, + priv->request, + releases, + blob_cab, + priv->progress, + priv->flags, + error)) return FALSE; fu_util_display_current_message(priv); diff --git a/src/meson.build b/src/meson.build index 190e3e961..9ef75d93a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -40,7 +40,7 @@ daemon_src = [ 'fu-engine-request.c', 'fu-history.c', 'fu-idle.c', - 'fu-install-task.c', + 'fu-release.c', 'fu-keyring-utils.c', 'fu-plugin-list.c', 'fu-remote-list.c',