mirror of
https://git.proxmox.com/git/fwupd
synced 2025-04-30 23:57:44 +00:00
305 lines
7.9 KiB
C
305 lines
7.9 KiB
C
/*
|
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
|
* Copyright (C) 2022 Gaël PORTAY <gael.portay@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuFirmware"
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-archive-firmware.h"
|
|
#include "fu-archive.h"
|
|
#include "fu-common.h"
|
|
#include "fu-path.h"
|
|
|
|
/**
|
|
* FuArchiveFirmware:
|
|
*
|
|
* An archive firmware image, typically for nested firmware volumes.
|
|
*
|
|
* See also: [class@FuFirmware]
|
|
*/
|
|
|
|
typedef struct {
|
|
FuArchiveFormat format;
|
|
FuArchiveCompression compression;
|
|
} FuArchiveFirmwarePrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FuArchiveFirmware, fu_archive_firmware, FU_TYPE_FIRMWARE)
|
|
#define GET_PRIVATE(o) (fu_archive_firmware_get_instance_private(o))
|
|
|
|
static void
|
|
fu_archive_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
|
|
{
|
|
FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware);
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
fu_xmlb_builder_insert_kv(bn, "format", fu_archive_format_to_string(priv->format));
|
|
fu_xmlb_builder_insert_kv(bn,
|
|
"compression",
|
|
fu_archive_compression_to_string(priv->compression));
|
|
}
|
|
|
|
static gboolean
|
|
fu_archive_firmware_parse_cb(FuArchive *self,
|
|
const gchar *filename,
|
|
GBytes *bytes,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
FuFirmware *firmware = FU_FIRMWARE(user_data);
|
|
g_autoptr(FuFirmware) img = fu_firmware_new_from_bytes(bytes);
|
|
fu_firmware_set_id(img, filename);
|
|
fu_firmware_add_image(firmware, img);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_archive_firmware_parse(FuFirmware *firmware,
|
|
GBytes *fw,
|
|
gsize offset,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
g_autoptr(FuArchive) archive = NULL;
|
|
|
|
/* load archive */
|
|
archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error);
|
|
if (archive == NULL)
|
|
return FALSE;
|
|
|
|
/* decompress each image in the archive */
|
|
return fu_archive_iterate(archive, fu_archive_firmware_parse_cb, firmware, error);
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_get_format:
|
|
* @self: a #FuArchiveFirmware
|
|
*
|
|
* Gets the archive format.
|
|
*
|
|
* Returns: format
|
|
*
|
|
* Since: 1.8.1
|
|
**/
|
|
FuArchiveFormat
|
|
fu_archive_firmware_get_format(FuArchiveFirmware *self)
|
|
{
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_ARCHIVE_FIRMWARE(self), FU_ARCHIVE_FORMAT_UNKNOWN);
|
|
return priv->format;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_set_format:
|
|
* @self: a #FuArchiveFirmware
|
|
* @format: the archive format, e.g. %FU_ARCHIVE_FORMAT_ZIP
|
|
*
|
|
* Sets the archive format.
|
|
*
|
|
* Since: 1.8.1
|
|
**/
|
|
void
|
|
fu_archive_firmware_set_format(FuArchiveFirmware *self, FuArchiveFormat format)
|
|
{
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
g_return_if_fail(FU_IS_ARCHIVE_FIRMWARE(self));
|
|
priv->format = format;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_get_compression:
|
|
* @self: A #FuArchiveFirmware
|
|
*
|
|
* Returns the compression.
|
|
*
|
|
* Returns: compression
|
|
*
|
|
* Since: 1.8.1
|
|
**/
|
|
FuArchiveCompression
|
|
fu_archive_firmware_get_compression(FuArchiveFirmware *self)
|
|
{
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_ARCHIVE_FIRMWARE(self), FU_ARCHIVE_COMPRESSION_UNKNOWN);
|
|
return priv->compression;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_set_compression:
|
|
* @self: A #FuArchiveFirmware
|
|
* @compression: the compression, e.g. %FU_ARCHIVE_COMPRESSION_NONE
|
|
*
|
|
* Sets the compression.
|
|
*
|
|
* Since: 1.8.1
|
|
**/
|
|
void
|
|
fu_archive_firmware_set_compression(FuArchiveFirmware *self, FuArchiveCompression compression)
|
|
{
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
g_return_if_fail(FU_IS_ARCHIVE_FIRMWARE(self));
|
|
priv->compression = compression;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_get_image_fnmatch:
|
|
* @self: a #FuPlugin
|
|
* @pattern: (not nullable): a glob pattern, e.g. `*foo*`
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets a single firmware image using the image ID pattern. It is also an error for multiple images
|
|
* to match.
|
|
*
|
|
* Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.8.9
|
|
**/
|
|
FuFirmware *
|
|
fu_archive_firmware_get_image_fnmatch(FuArchiveFirmware *self, const gchar *pattern, GError **error)
|
|
{
|
|
g_autoptr(FuFirmware) img_match = NULL;
|
|
g_autoptr(GPtrArray) imgs = fu_firmware_get_images(FU_FIRMWARE(self));
|
|
|
|
g_return_val_if_fail(FU_IS_ARCHIVE_FIRMWARE(self), NULL);
|
|
g_return_val_if_fail(pattern != NULL, NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
|
|
for (guint i = 0; i < imgs->len; i++) {
|
|
FuFirmware *img = g_ptr_array_index(imgs, i);
|
|
const gchar *fn = fu_firmware_get_id(img);
|
|
if (!fu_path_fnmatch(pattern, fn))
|
|
continue;
|
|
if (img_match != NULL) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_ARGUMENT,
|
|
"multiple images matched %s",
|
|
pattern);
|
|
return NULL;
|
|
}
|
|
img_match = g_object_ref(img);
|
|
}
|
|
if (img_match == NULL) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_FOUND,
|
|
"no image matched %s",
|
|
pattern);
|
|
return NULL;
|
|
}
|
|
return g_steal_pointer(&img_match);
|
|
}
|
|
|
|
static GBytes *
|
|
fu_archive_firmware_write(FuFirmware *firmware, GError **error)
|
|
{
|
|
FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware);
|
|
FuArchiveFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(FuArchive) archive = NULL;
|
|
g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
|
|
|
|
/* sanity check */
|
|
if (priv->format == FU_ARCHIVE_FORMAT_UNKNOWN) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"firmware archive format unspecified");
|
|
return NULL;
|
|
}
|
|
if (priv->compression == FU_ARCHIVE_COMPRESSION_UNKNOWN) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"firmware archive compression unspecified");
|
|
return NULL;
|
|
}
|
|
|
|
/* save archive and compress each image to the archive */
|
|
archive = fu_archive_new(NULL, FU_ARCHIVE_FLAG_NONE, NULL);
|
|
for (guint i = 0; i < images->len; i++) {
|
|
FuFirmware *img = g_ptr_array_index(images, i);
|
|
g_autoptr(GBytes) blob = NULL;
|
|
|
|
if (fu_firmware_get_id(img) == NULL) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"image has no ID");
|
|
return NULL;
|
|
}
|
|
blob = fu_firmware_get_bytes(img, error);
|
|
if (blob == NULL)
|
|
return NULL;
|
|
fu_archive_add_entry(archive, fu_firmware_get_id(img), blob);
|
|
}
|
|
return fu_archive_write(archive, priv->format, priv->compression, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_archive_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
|
|
{
|
|
FuArchiveFirmware *self = FU_ARCHIVE_FIRMWARE(firmware);
|
|
const gchar *tmp;
|
|
|
|
/* simple properties */
|
|
tmp = xb_node_query_text(n, "format", NULL);
|
|
if (tmp != NULL) {
|
|
FuArchiveFormat format = fu_archive_format_from_string(tmp);
|
|
if (format == FU_ARCHIVE_FORMAT_UNKNOWN) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"format %s not supported",
|
|
tmp);
|
|
return FALSE;
|
|
}
|
|
fu_archive_firmware_set_format(self, format);
|
|
}
|
|
tmp = xb_node_query_text(n, "compression", NULL);
|
|
if (tmp != NULL) {
|
|
FuArchiveCompression compression = fu_archive_compression_from_string(tmp);
|
|
if (compression == FU_ARCHIVE_COMPRESSION_UNKNOWN) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"compression %s not supported",
|
|
tmp);
|
|
return FALSE;
|
|
}
|
|
fu_archive_firmware_set_compression(self, compression);
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_archive_firmware_init(FuArchiveFirmware *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fu_archive_firmware_class_init(FuArchiveFirmwareClass *klass)
|
|
{
|
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
|
klass_firmware->parse = fu_archive_firmware_parse;
|
|
klass_firmware->write = fu_archive_firmware_write;
|
|
klass_firmware->build = fu_archive_firmware_build;
|
|
klass_firmware->export = fu_archive_firmware_export;
|
|
}
|
|
|
|
/**
|
|
* fu_archive_firmware_new:
|
|
*
|
|
* Creates a new archive #FuFirmware
|
|
*
|
|
* Since: 1.7.3
|
|
**/
|
|
FuFirmware *
|
|
fu_archive_firmware_new(void)
|
|
{
|
|
return FU_FIRMWARE(g_object_new(FU_TYPE_ARCHIVE_FIRMWARE, NULL));
|
|
}
|