From a4906010d6e421fe92e3c43d36b52334bd72942f Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 May 2022 12:30:56 +0100 Subject: [PATCH] Add a way to read the device firmware in fwupdtool In most cases 'fwupdtool firmware-dump' and 'fwupdtool firmware-read' are going to be the same. This isn't true in all cases, especially when dealing with composite firmware like archives. See https://github.com/fwupd/fwupd/pull/4623#issuecomment-1129227133 --- data/bash-completion/fwupdtool | 1 + src/fu-engine.c | 24 ++++++++++++ src/fu-engine.h | 6 +++ src/fu-tool.c | 72 ++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) diff --git a/data/bash-completion/fwupdtool b/data/bash-completion/fwupdtool index ac5c9fb37..cee5ec766 100644 --- a/data/bash-completion/fwupdtool +++ b/data/bash-completion/fwupdtool @@ -36,6 +36,7 @@ _fwupdtool_cmd_list=( 'attach' 'detach' 'firmware-dump' + 'firmware-read' 'refresh' 'verify-update' 'watch' diff --git a/src/fu-engine.c b/src/fu-engine.c index 801b14a89..0237e5610 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -2954,6 +2954,30 @@ fu_engine_firmware_dump(FuEngine *self, return fu_device_dump_firmware(device, progress, error); } +FuFirmware * +fu_engine_firmware_read(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDeviceLocker) poll_locker = NULL; + + /* pause the polling */ + poll_locker = fu_device_poll_locker_new(device, error); + if (poll_locker == NULL) + return NULL; + + /* open, read, close */ + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error(error, "failed to open device for firmware read: "); + return NULL; + } + return fu_device_read_firmware(device, progress, error); +} + gboolean fu_engine_install_blob(FuEngine *self, FuDevice *device, diff --git a/src/fu-engine.h b/src/fu-engine.h index d4290cf50..917b2bc6b 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -144,6 +144,12 @@ fu_engine_firmware_dump(FuEngine *self, FuProgress *progress, FwupdInstallFlags flags, GError **error); +FuFirmware * +fu_engine_firmware_read(FuEngine *self, + FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); gboolean fu_engine_modify_remote(FuEngine *self, const gchar *remote_id, diff --git a/src/fu-tool.c b/src/fu-tool.c index a08c34e65..5c0908660 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1110,6 +1110,71 @@ fu_util_firmware_dump(FuUtilPrivate *priv, gchar **values, GError **error) return fu_common_set_contents_bytes(values[0], blob_fw, error); } +static gboolean +fu_util_firmware_read(FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) fw = NULL; + g_autoptr(GBytes) blob_empty = g_bytes_new(NULL, 0); + g_autoptr(GBytes) blob_fw = NULL; + + /* invalid args */ + if (g_strv_length(values) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* file already exists */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + g_file_test(values[0], G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Filename already exists"); + return FALSE; + } + + /* write a zero length file to ensure the destination is writable to + * avoid failing at the end of a potentially lengthy operation */ + if (!fu_common_set_contents_bytes(values[0], blob_empty, error)) + return FALSE; + + /* load engine */ + if (!fu_util_start_engine(priv, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, + error)) + return FALSE; + + /* get device */ + priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + if (g_strv_length(values) >= 2) { + device = fu_util_get_device(priv, values[1], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device(priv, NULL, error); + if (device == NULL) + return FALSE; + } + priv->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect(FU_ENGINE(priv->engine), + "device-changed", + G_CALLBACK(fu_util_update_device_changed_cb), + priv); + + /* read firmware into the container format */ + fw = fu_engine_firmware_read(priv->engine, device, priv->progress, priv->flags, error); + if (fw == NULL) + return FALSE; + blob_fw = fu_firmware_write(fw, error); + if (blob_fw == NULL) + return FALSE; + return fu_common_set_contents_bytes(values[0], blob_fw, error); +} + static gint fu_util_release_sort_cb(gconstpointer a, gconstpointer b) { @@ -3541,6 +3606,13 @@ main(int argc, char *argv[]) /* TRANSLATORS: command description */ _("Read a firmware blob from a device"), fu_util_firmware_dump); + fu_util_cmd_array_add(cmd_array, + "firmware-read", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("FILENAME [DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Read a firmware from a device"), + fu_util_firmware_read); fu_util_cmd_array_add(cmd_array, "firmware-patch", /* TRANSLATORS: command argument: uppercase, spaces->dashes */