From 1eb7c7443eb44773dd477134f3757bae27a984b0 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 7 Jan 2020 10:58:32 +0000 Subject: [PATCH] Allow quirking devices that always require a version check These are devices that we have to be careful with the version numbers, for instance only updating from versions that have already had data migration completed. The new flag can be set in quirk files or on the objects directly. --- libfwupd/fwupd-enums.c | 4 ++++ libfwupd/fwupd-enums.h | 2 ++ src/fu-install-task.c | 37 +++++++++++++++++++++++++++++++ src/fu-self-test.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/fu-util-common.c | 4 ++++ 5 files changed, 97 insertions(+) diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index 4edd8b9d9..ce23c3664 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -179,6 +179,8 @@ fwupd_device_flag_to_string (FwupdDeviceFlags device_flag) return "self-recovery"; if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) return "usable-during-update"; + if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) + return "version-check-required"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown"; return NULL; @@ -259,6 +261,8 @@ fwupd_device_flag_from_string (const gchar *device_flag) return FWUPD_DEVICE_FLAG_SELF_RECOVERY; if (g_strcmp0 (device_flag, "usable-during-update") == 0) return FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE; + if (g_strcmp0 (device_flag, "version-check-required") == 0) + return FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED; return FWUPD_DEVICE_FLAG_UNKNOWN; } diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index 97f5ea150..c68aba87e 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -97,6 +97,7 @@ typedef enum { * @FWUPD_DEVICE_FLAG_DUAL_IMAGE: Device update architecture uses A/B partitions for updates * @FWUPD_DEVICE_FLAG_SELF_RECOVERY: In flashing mode device will only accept intended payloads * @FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE: Device remains usable while fwupd flashes or schedules the update + * @FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED: All firmware updates for this device require a firmware version check * * The device flags. **/ @@ -131,6 +132,7 @@ typedef enum { #define FWUPD_DEVICE_FLAG_DUAL_IMAGE (1u << 27) /* Since: 1.3.3 */ #define FWUPD_DEVICE_FLAG_SELF_RECOVERY (1u << 28) /* Since: 1.3.3 */ #define FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE (1u << 29) /* Since: 1.3.3 */ +#define FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED (1u << 30) /* Since: 1.3.7 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; diff --git a/src/fu-install-task.c b/src/fu-install-task.c index f3a042296..6d6ac321a 100644 --- a/src/fu-install-task.c +++ b/src/fu-install-task.c @@ -152,6 +152,35 @@ fu_install_task_check_verfmt (FuInstallTask *self, 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 @@ -211,6 +240,14 @@ fu_install_task_check_requirements (FuInstallTask *self, return FALSE; } + /* device requries 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']", diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 37391a648..f48c0b360 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -225,6 +225,53 @@ fu_engine_requirements_missing_func (gconstpointer user_data) g_assert (!ret); } +static void +fu_engine_requirements_version_require_func (gconstpointer user_data) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE); + g_autoptr(FuInstallTask) task = NULL; + 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 (device, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_bootloader (device, "4.5.6"); + fu_device_set_vendor_id (device, "FFFF"); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED); + 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 */ + task = fu_install_task_new (device, component); + ret = fu_engine_check_requirements (engine, task, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert (g_str_has_prefix (error->message, "device requires firmware with a version check")); + g_assert (!ret); +} + static void fu_engine_requirements_unsupported_func (gconstpointer user_data) { @@ -439,6 +486,7 @@ fu_engine_requirements_device_func (gconstpointer user_data) fu_device_set_version_bootloader (device, "4.5.6"); fu_device_set_vendor_id (device, "FFFF"); fu_device_add_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag (device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED); fu_device_add_guid (device, "12345678-1234-1234-1234-123456789012"); /* make the component require three things */ @@ -2902,6 +2950,8 @@ main (int argc, char **argv) fu_engine_requirements_func); g_test_add_data_func ("/fwupd/engine{requirements-missing}", self, fu_engine_requirements_missing_func); + 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-parent-device}", self, fu_engine_requirements_parent_device_func); g_test_add_data_func ("/fwupd/engine{requirements-not-child}", self, diff --git a/src/fu-util-common.c b/src/fu-util-common.c index 2fb819f4d..91313d8c3 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -1066,6 +1066,10 @@ fu_util_device_flag_to_string (guint64 device_flag) /* TRANSLATORS: Device remains usable during update */ return _("Device is usable for the duration of the update"); } + if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) { + /* TRANSLATORS: a version check is required for all firmware */ + return _("Device firmware is required to have a version check"); + } if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { return NULL; }