/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
2.0.21
src/fu-engine-helper.c
578 строк
19 KB
Richard Hughes
trivial: Run clang-format-21 on the entire source tree
26 ноя 2025, 02:30
26 ноя 2025, 02:30
7299c94
Код
Авторство
О чём код?
/* * Copyright 2020 Mario Limonciello <mario.limonciello@dell.com> * Copyright 2022 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define G_LOG_DOMAIN "FuEngine" #include "config.h" #include <fwupdplugin.h> #include <glib/gi18n.h> #include "fu-cabinet.h" #include "fu-context-private.h" #include "fu-engine-helper.h" #include "fu-engine.h" #include "fu-usb-device-fw-ds20.h" #include "fu-usb-device-ms-ds20.h" void fu_engine_add_firmware_gtypes(FuEngine *self) { FuContext *ctx = fu_engine_get_context(self); fu_context_add_firmware_gtype(ctx, "raw", FU_TYPE_FIRMWARE); fu_context_add_firmware_gtype(ctx, "cab", FU_TYPE_CAB_FIRMWARE); fu_context_add_firmware_gtype(ctx, "cabinet", FU_TYPE_CABINET); fu_context_add_firmware_gtype(ctx, "dfu", FU_TYPE_DFU_FIRMWARE); fu_context_add_firmware_gtype(ctx, "fdt", FU_TYPE_FDT_FIRMWARE); fu_context_add_firmware_gtype(ctx, "csv", FU_TYPE_CSV_FIRMWARE); fu_context_add_firmware_gtype(ctx, "fit", FU_TYPE_FIT_FIRMWARE); fu_context_add_firmware_gtype(ctx, "dfuse", FU_TYPE_DFUSE_FIRMWARE); fu_context_add_firmware_gtype(ctx, "ifwi-cpd", FU_TYPE_IFWI_CPD_FIRMWARE); fu_context_add_firmware_gtype(ctx, "ifwi-fpt", FU_TYPE_IFWI_FPT_FIRMWARE); fu_context_add_firmware_gtype(ctx, "oprom", FU_TYPE_OPROM_FIRMWARE); fu_context_add_firmware_gtype(ctx, "fmap", FU_TYPE_FMAP_FIRMWARE); fu_context_add_firmware_gtype(ctx, "ihex", FU_TYPE_IHEX_FIRMWARE); fu_context_add_firmware_gtype(ctx, "linear", FU_TYPE_LINEAR_FIRMWARE); fu_context_add_firmware_gtype(ctx, "srec", FU_TYPE_SREC_FIRMWARE); fu_context_add_firmware_gtype(ctx, "hid-descriptor", FU_TYPE_HID_DESCRIPTOR); fu_context_add_firmware_gtype(ctx, "archive", FU_TYPE_ARCHIVE_FIRMWARE); fu_context_add_firmware_gtype(ctx, "smbios", FU_TYPE_SMBIOS); fu_context_add_firmware_gtype(ctx, "acpi-table", FU_TYPE_ACPI_TABLE); fu_context_add_firmware_gtype(ctx, "sbatlevel", FU_TYPE_SBATLEVEL_SECTION); fu_context_add_firmware_gtype(ctx, "edid", FU_TYPE_EDID); fu_context_add_firmware_gtype(ctx, "efi-file", FU_TYPE_EFI_FILE); fu_context_add_firmware_gtype(ctx, "efi-signature", FU_TYPE_EFI_SIGNATURE); fu_context_add_firmware_gtype(ctx, "efi-signature-list", FU_TYPE_EFI_SIGNATURE_LIST); fu_context_add_firmware_gtype(ctx, "efi-variable-authentication2", FU_TYPE_EFI_VARIABLE_AUTHENTICATION2); fu_context_add_firmware_gtype(ctx, "efi-load-option", FU_TYPE_EFI_LOAD_OPTION); fu_context_add_firmware_gtype(ctx, "efi-device-path-list", FU_TYPE_EFI_DEVICE_PATH_LIST); fu_context_add_firmware_gtype(ctx, "efi-filesystem", FU_TYPE_EFI_FILESYSTEM); fu_context_add_firmware_gtype(ctx, "efi-section", FU_TYPE_EFI_SECTION); fu_context_add_firmware_gtype(ctx, "efi-volume", FU_TYPE_EFI_VOLUME); fu_context_add_firmware_gtype(ctx, "efi-ftw-store", FU_TYPE_EFI_FTW_STORE); fu_context_add_firmware_gtype(ctx, "efi-vss2-variable-store", FU_TYPE_EFI_VSS2_VARIABLE_STORE); fu_context_add_firmware_gtype(ctx, "json", FU_TYPE_JSON_FIRMWARE); fu_context_add_firmware_gtype(ctx, "ifd-bios", FU_TYPE_IFD_BIOS); fu_context_add_firmware_gtype(ctx, "ifd-firmware", FU_TYPE_IFD_FIRMWARE); fu_context_add_firmware_gtype(ctx, "cfu-offer", FU_TYPE_CFU_OFFER); fu_context_add_firmware_gtype(ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD); fu_context_add_firmware_gtype(ctx, "uswid", FU_TYPE_USWID_FIRMWARE); fu_context_add_firmware_gtype(ctx, "coswid", FU_TYPE_COSWID_FIRMWARE); fu_context_add_firmware_gtype(ctx, "pefile", FU_TYPE_PEFILE_FIRMWARE); fu_context_add_firmware_gtype(ctx, "elf", FU_TYPE_ELF_FIRMWARE); fu_context_add_firmware_gtype(ctx, "x509-certificate", FU_TYPE_X509_CERTIFICATE); fu_context_add_firmware_gtype(ctx, "intel-thunderbolt", FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE); fu_context_add_firmware_gtype(ctx, "intel-thunderbolt-nvm", FU_TYPE_INTEL_THUNDERBOLT_NVM); fu_context_add_firmware_gtype(ctx, "usb-device-fw-ds20", FU_TYPE_USB_DEVICE_FW_DS20); fu_context_add_firmware_gtype(ctx, "usb-device-ms-ds20", FU_TYPE_USB_DEVICE_MS_DS20); } static FwupdRelease * fu_engine_get_release_with_tag(FuEngine *self, FuEngineRequest *request, FwupdDevice *dev, const gchar *host_bkc, GError **error) { g_autoptr(GPtrArray) rels = NULL; g_auto(GStrv) host_bkcs = g_strsplit(host_bkc, ",", -1); /* find the newest release that matches */ rels = fu_engine_get_releases(self, request, fwupd_device_get_id(dev), error); if (rels == NULL) return NULL; for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index(rels, i); for (guint j = 0; host_bkcs[j] != NULL; j++) { if (fwupd_release_has_tag(rel, host_bkcs[j])) return g_object_ref(rel); } } /* no match */ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no matching releases for device"); return NULL; } gboolean fu_engine_update_motd(FuEngine *self, GError **error) { const gchar *host_bkc = fu_engine_get_host_bkc(self); guint upgrade_count = 0; guint sync_count = 0; guint reboot_count = 0; g_autoptr(FuEngineRequest) request = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GString) str = g_string_new(NULL); g_autofree gchar *target = NULL; /* a subset of what fwupdmgr can do */ request = fu_engine_request_new(NULL); fu_engine_request_set_feature_flags(request, FWUPD_FEATURE_FLAG_DETACH_ACTION | FWUPD_FEATURE_FLAG_UPDATE_ACTION); /* get devices from daemon, we even want to know if it's nothing */ devices = fu_engine_get_devices(self, NULL); if (devices != NULL) { for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); g_autoptr(GPtrArray) rels = NULL; /* check if device needs reboot to complete update */ if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { reboot_count++; continue; } /* skip devices with failed updates */ if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED || fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { continue; } /* get the releases for this device */ if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; rels = fu_engine_get_upgrades(self, request, fwupd_device_get_id(dev), NULL); if (rels == NULL) continue; upgrade_count++; } if (host_bkc != NULL) { for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); g_autoptr(FwupdRelease) rel = NULL; /* skip devices with failed updates */ if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED || fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { continue; } if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; rel = fu_engine_get_release_with_tag(self, request, dev, host_bkc, NULL); if (rel == NULL) continue; if (g_strcmp0(fwupd_device_get_version(dev), fwupd_release_get_version(rel)) != 0) sync_count++; } } } /* if running under systemd unit, use the directory as a base */ if (g_getenv("RUNTIME_DIRECTORY") != NULL) { target = g_build_filename(g_getenv("RUNTIME_DIRECTORY"), MOTD_FILE, NULL); /* otherwise use the cache directory */ } else { target = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, MOTD_DIR, MOTD_FILE, NULL); } /* create the directory and file, even if zero devices; we want an empty file then */ if (!fu_path_mkdir_parent(target, error)) return FALSE; /* nag about reboot first, then syncing or updating, but never both */ if (reboot_count > 0) { g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD */ ngettext("%u device has been updated and needs a reboot.", "%u devices have been updated and need a reboot.", reboot_count), reboot_count); g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD */ _("Reboot to complete the update.")); g_string_append(str, "\n\n"); } else if (sync_count > 0) { g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD */ ngettext("%u device is not the best known configuration.", "%u devices are not the best known configuration.", sync_count), sync_count); g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD -- %1 is the * command name, e.g. `fwupdmgr sync` */ _("Run `%s` to complete this action."), "fwupdmgr sync"); g_string_append(str, "\n\n"); } else if (upgrade_count > 0) { g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD */ ngettext("%u device has a firmware upgrade available.", "%u devices have a firmware upgrade available.", upgrade_count), upgrade_count); g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD -- %1 is the * command name, e.g. `fwupdmgr get-upgrades` */ _("Run `%s` for more information."), "fwupdmgr get-upgrades"); g_string_append(str, "\n\n"); } /* success, with an empty file if nothing to say */ g_debug("writing motd target %s", target); return g_file_set_contents(target, str->str, str->len, error); } gboolean fu_engine_update_devices_file(FuEngine *self, GError **error) { FwupdCodecFlags flags = FWUPD_CODEC_FLAG_NONE; gsize len; g_autoptr(JsonBuilder) builder = NULL; g_autoptr(JsonGenerator) generator = NULL; g_autoptr(JsonNode) root = NULL; g_autoptr(GPtrArray) devices = NULL; g_autofree gchar *data = NULL; g_autofree gchar *target = NULL; if (fu_engine_config_get_show_device_private(fu_engine_get_config(self))) flags |= FWUPD_CODEC_FLAG_TRUSTED; builder = json_builder_new(); json_builder_begin_object(builder); devices = fu_engine_get_devices(self, NULL); if (devices != NULL) fwupd_codec_array_to_json(devices, "Devices", builder, flags); root = json_builder_get_root(builder); generator = json_generator_new(); json_generator_set_pretty(generator, TRUE); json_generator_set_root(generator, root); data = json_generator_to_data(generator, &len); if (data == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to convert to JSON string"); return FALSE; } target = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "devices.json", NULL); return g_file_set_contents(target, data, (gssize)len, error); } static void fu_engine_integrity_add_measurement(GHashTable *self, const gchar *id, GBytes *blob) { g_autofree gchar *csum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, blob); g_hash_table_insert(self, g_strdup(id), g_steal_pointer(&csum)); } static void fu_engine_integrity_measure_acpi(FuContext *ctx, GHashTable *self) { const gchar *tables[] = { "SLIC", "MSDM", "TPM2", }; for (guint i = 0; i < G_N_ELEMENTS(tables); i++) { g_autofree gchar *fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, tables[i], NULL); g_autoptr(GBytes) blob = NULL; blob = fu_bytes_get_contents(fn, NULL); if (blob != NULL && g_bytes_get_size(blob) > 0) { g_autofree gchar *id = g_strdup_printf("ACPI:%s", tables[i]); fu_engine_integrity_add_measurement(self, id, blob); } } } static void fu_engine_integrity_measure_uefi(FuContext *ctx, GHashTable *self) { FuEfivars *efivars = fu_context_get_efivars(ctx); struct { const gchar *guid; const gchar *name; } keys[] = { {FU_EFIVARS_GUID_EFI_GLOBAL, "BootCurrent"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "KEK"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "KEKDefault"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "OsIndications"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "OsIndicationsSupported"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "PK"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "PKDefault"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "SecureBoot"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "SetupMode"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "SignatureSupport"}, {FU_EFIVARS_GUID_EFI_GLOBAL, "VendorKeys"}, {FU_EFIVARS_GUID_SECURITY_DATABASE, "db"}, {FU_EFIVARS_GUID_SECURITY_DATABASE, "dbDefault"}, {FU_EFIVARS_GUID_SECURITY_DATABASE, "dbx"}, {FU_EFIVARS_GUID_SECURITY_DATABASE, "dbxDefault"}, }; /* important keys */ for (guint i = 0; i < G_N_ELEMENTS(keys); i++) { g_autoptr(GBytes) blob = fu_efivars_get_data_bytes(efivars, keys[i].guid, keys[i].name, NULL, NULL); if (blob != NULL) { g_autofree gchar *id = g_strdup_printf("UEFI:%s", keys[i].name); fu_engine_integrity_add_measurement(self, id, blob); } } /* UEFI Boot#### */ for (guint i = 0; i < 0xFF; i++) { g_autoptr(GBytes) blob = fu_efivars_get_boot_data(efivars, i, NULL); if (blob != NULL && g_bytes_get_size(blob) > 0) { const guint8 needle[] = "f\0w\0u\0p\0d"; g_autofree gchar *id = g_strdup_printf("UEFI:Boot%04X", i); if (fu_memmem_safe(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob), needle, sizeof(needle), NULL, NULL)) { g_debug("skipping %s as fwupd found", id); continue; } fu_engine_integrity_add_measurement(self, id, blob); } } } GHashTable * fu_engine_integrity_new(FuContext *ctx, GError **error) { g_autoptr(GHashTable) self = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_return_val_if_fail(error == NULL || *error == NULL, NULL); fu_engine_integrity_measure_uefi(ctx, self); fu_engine_integrity_measure_acpi(ctx, self); /* nothing of use */ if (g_hash_table_size(self) == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no measurements"); return NULL; } /* success */ return g_steal_pointer(&self); } gchar * fu_engine_integrity_to_string(GHashTable *self) { GHashTableIter iter; gpointer key, value; g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free); g_return_val_if_fail(self != NULL, NULL); /* sanity check */ if (g_hash_table_size(self) == 0) return NULL; /* build into KV array */ g_hash_table_iter_init(&iter, self); while (g_hash_table_iter_next(&iter, &key, &value)) { g_ptr_array_add(array, g_strdup_printf("%s=%s", (const gchar *)key, (const gchar *)value)); } return fu_strjoin("\n", array); } static const GError * fu_engine_error_array_find(GPtrArray *errors, FwupdError error_code) { for (guint j = 0; j < errors->len; j++) { const GError *error = g_ptr_array_index(errors, j); if (g_error_matches(error, FWUPD_ERROR, error_code)) return error; } return NULL; } static guint fu_engine_error_array_count(GPtrArray *errors, FwupdError error_code) { guint cnt = 0; for (guint j = 0; j < errors->len; j++) { const GError *error = g_ptr_array_index(errors, j); if (g_error_matches(error, FWUPD_ERROR, error_code)) cnt++; } return cnt; } static gboolean fu_engine_error_array_matches_any(GPtrArray *errors, FwupdError *error_codes) { for (guint j = 0; j < errors->len; j++) { const GError *error = g_ptr_array_index(errors, j); gboolean matches_any = FALSE; for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) { if (g_error_matches(error, FWUPD_ERROR, error_codes[i])) { matches_any = TRUE; break; } } if (!matches_any) return FALSE; } return TRUE; } /** * fu_engine_error_array_get_best: * @errors: (element-type GError): array of errors * * Finds the 'best' error to show the user from a array of errors, creating a * completely bespoke error where required. * * Returns: (transfer full): a #GError, never %NULL **/ GError * fu_engine_error_array_get_best(GPtrArray *errors) { FwupdError err_prio[] = {FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_VERSION_SAME, FWUPD_ERROR_VERSION_NEWER, FWUPD_ERROR_NOT_SUPPORTED, FWUPD_ERROR_INTERNAL, FWUPD_ERROR_NOT_FOUND, FWUPD_ERROR_LAST}; FwupdError err_all_uptodate[] = {FWUPD_ERROR_VERSION_SAME, FWUPD_ERROR_NOT_FOUND, FWUPD_ERROR_NOT_SUPPORTED, FWUPD_ERROR_LAST}; FwupdError err_all_newer[] = {FWUPD_ERROR_VERSION_NEWER, FWUPD_ERROR_VERSION_SAME, FWUPD_ERROR_NOT_FOUND, FWUPD_ERROR_NOT_SUPPORTED, FWUPD_ERROR_LAST}; /* are all the errors either GUID-not-matched or version-same? */ if (fu_engine_error_array_count(errors, FWUPD_ERROR_VERSION_SAME) > 1 && fu_engine_error_array_matches_any(errors, err_all_uptodate)) { return g_error_new(FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "All updatable firmware is already installed"); } /* are all the errors either GUID-not-matched or version same or newer? */ if (fu_engine_error_array_count(errors, FWUPD_ERROR_VERSION_NEWER) > 1 && fu_engine_error_array_matches_any(errors, err_all_newer)) { return g_error_new(FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "All updatable devices already have newer versions"); } /* get the most important single error */ for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) { const GError *error_tmp = fu_engine_error_array_find(errors, err_prio[i]); if (error_tmp != NULL) return g_error_copy(error_tmp); } /* fall back to something */ return g_error_new(FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No supported devices found"); } /** * fu_engine_build_machine_id: * @salt: (nullable): optional salt * @error: (nullable): optional return location for an error * * Gets a salted hash of the /etc/machine-id contents. This can be used to * identify a specific machine. It is not possible to recover the original * machine-id from the machine-hash. * * Returns: the SHA256 machine hash, or %NULL if the ID is not present **/ gchar * fu_engine_build_machine_id(const gchar *salt, GError **error) { const gchar *machine_id; gsize bufsz = 0; g_autofree gchar *buf = NULL; g_autoptr(GChecksum) csum = NULL; g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* in test mode */ machine_id = g_getenv("FWUPD_MACHINE_ID"); if (machine_id != NULL) { buf = g_strdup(machine_id); bufsz = strlen(buf); } else { const gchar *fn = NULL; g_autoptr(GPtrArray) fns = g_ptr_array_new_with_free_func(g_free); /* one of these has to exist */ g_ptr_array_add(fns, g_build_filename(FWUPD_SYSCONFDIR, "machine-id", NULL)); g_ptr_array_add( fns, g_build_filename(FWUPD_LOCALSTATEDIR, "lib", "dbus", "machine-id", NULL)); g_ptr_array_add(fns, g_strdup("/etc/machine-id")); g_ptr_array_add(fns, g_strdup("/var/lib/dbus/machine-id")); g_ptr_array_add(fns, g_strdup("/var/db/dbus/machine-id")); /* this is the hardcoded path for homebrew, e.g. `sudo dbus-uuidgen --ensure` */ g_ptr_array_add(fns, g_strdup("/usr/local/var/lib/dbus/machine-id")); for (guint i = 0; i < fns->len; i++) { const gchar *fn_tmp = g_ptr_array_index(fns, i); if (g_file_test(fn_tmp, G_FILE_TEST_EXISTS)) { fn = fn_tmp; break; } } if (fn == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "The machine-id is not present"); return NULL; } if (!g_file_get_contents(fn, &buf, &bufsz, error)) return NULL; if (bufsz == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "The machine-id is present but unset"); return NULL; } } csum = g_checksum_new(G_CHECKSUM_SHA256); if (salt != NULL) g_checksum_update(csum, (const guchar *)salt, (gssize)strlen(salt)); g_checksum_update(csum, (const guchar *)buf, (gssize)bufsz); return g_strdup(g_checksum_get_string(csum)); }