From 1a612582395c4259e036d8b2f0f42ed7e441e59b Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 29 Sep 2020 19:39:38 +0100 Subject: [PATCH] Allow devices to save the old firmware to disk for recovery This would also help, for example, to go back to the nonfree firmware when the alternate firmware did not work as well as hoped. It would also allow flashing the firmware using an SPI programmer if everything went very wrong indeed. --- libfwupd/fwupd-enums.c | 4 ++++ libfwupd/fwupd-enums.h | 2 ++ libfwupdplugin/fu-plugin.c | 26 ++++++++++++++++++++++++++ src/fu-util-common.c | 4 ++++ 4 files changed, 36 insertions(+) diff --git a/libfwupd/fwupd-enums.c b/libfwupd/fwupd-enums.c index af349107f..a35cf31e7 100644 --- a/libfwupd/fwupd-enums.c +++ b/libfwupd/fwupd-enums.c @@ -199,6 +199,8 @@ fwupd_device_flag_to_string (FwupdDeviceFlags device_flag) return "skips-restart"; if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) return "has-multiple-branches"; + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) + return "backup-before-install"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown"; return NULL; @@ -299,6 +301,8 @@ fwupd_device_flag_from_string (const gchar *device_flag) return FWUPD_DEVICE_FLAG_SKIPS_RESTART; if (g_strcmp0 (device_flag, "has-multiple-branches") == 0) return FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + if (g_strcmp0 (device_flag, "backup-before-install") == 0) + return FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL; return FWUPD_DEVICE_FLAG_UNKNOWN; } diff --git a/libfwupd/fwupd-enums.h b/libfwupd/fwupd-enums.h index 785a28a72..cda52db1f 100644 --- a/libfwupd/fwupd-enums.h +++ b/libfwupd/fwupd-enums.h @@ -127,6 +127,7 @@ typedef enum { * @FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN: Device is updatable but should not be called by the client * @FWUPD_DEVICE_FLAG_SKIPS_RESTART: Device relies upon activation or power cycle to load firmware * @FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES: Device supports switching to a different stream of firmware + * @FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL: Device firmware should be saved before installing firmware * * The device flags. **/ @@ -171,6 +172,7 @@ typedef enum { #define FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN (1llu << 37) /* Since: 1.4.1 */ #define FWUPD_DEVICE_FLAG_SKIPS_RESTART (1llu << 38) /* Since: 1.5.0 */ #define FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES (1llu << 39) /* Since: 1.5.0 */ +#define FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL (1llu << 40) /* Since: 1.5.0 */ #define FWUPD_DEVICE_FLAG_UNKNOWN G_MAXUINT64 /* Since: 0.7.3 */ typedef guint64 FwupdDeviceFlags; diff --git a/libfwupdplugin/fu-plugin.c b/libfwupdplugin/fu-plugin.c index 027884de9..f7df74d68 100644 --- a/libfwupdplugin/fu-plugin.c +++ b/libfwupdplugin/fu-plugin.c @@ -1052,6 +1052,32 @@ fu_plugin_device_write_firmware (FuPlugin *self, FuDevice *device, locker = fu_device_locker_new (device, error); if (locker == NULL) return FALSE; + + /* back the old firmware up to /var/lib/fwupd */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL)) { + g_autoptr(GBytes) fw_old = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *localstatedir = NULL; + + fw_old = fu_device_dump_firmware (device, error); + if (fw_old == NULL) { + g_prefix_error (error, "failed to backup old firmware: "); + return FALSE; + } + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + fn = g_strdup_printf ("%s.bin", fu_device_get_version (device)); + path = g_build_filename (localstatedir, + "backup", + fu_device_get_id (device), + fu_device_get_serial (device) != NULL ? + fu_device_get_serial (device) : + "default", + fn, NULL); + if (!fu_common_set_contents_bytes (path, fw_old, error)) + return FALSE; + } + return fu_device_write_firmware (device, fw, flags, error); } diff --git a/src/fu-util-common.c b/src/fu-util-common.c index c0cc85d1e..3daf56e3b 100644 --- a/src/fu-util-common.c +++ b/src/fu-util-common.c @@ -1077,6 +1077,10 @@ fu_util_device_flag_to_string (guint64 device_flag) /* TRANSLATORS: there is more than one supplier of the firmware */ return _("Device supports switching to a different branch of firmware"); } + if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) { + /* TRANSLATORS: save the old firmware to disk before installing the new one */ + return _("Device will backup firmware before installing"); + } if (device_flag == FWUPD_DEVICE_FLAG_MD_SET_NAME) { /* skip */ return NULL;