mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-16 19:44:59 +00:00
203 lines
4.6 KiB
C
203 lines
4.6 KiB
C
/*
|
|
* Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "dfu-element.h"
|
|
#include "dfu-format-metadata.h"
|
|
#include "dfu-image.h"
|
|
|
|
#include "fwupd-error.h"
|
|
|
|
/**
|
|
* dfu_firmware_from_metadata: (skip)
|
|
* @firmware: a #DfuFirmware
|
|
* @bytes: data to parse
|
|
* @flags: some #DfuFirmwareParseFlags
|
|
* @error: a #GError, or %NULL
|
|
*
|
|
* Unpacks into a firmware object from metadata data.
|
|
*
|
|
* The representation in memory is as follows:
|
|
*
|
|
* uint16 signature='MD'
|
|
* uint8 number_of_keys
|
|
* uint8 number_of_keys
|
|
* uint8 key(n)_length
|
|
* ... key(n) (no NUL)
|
|
* uint8 value(n)_length
|
|
* ... value(n) (no NUL)
|
|
* <existing DFU footer>
|
|
|
|
* Returns: %TRUE for success
|
|
**/
|
|
gboolean
|
|
dfu_firmware_from_metadata (DfuFirmware *firmware,
|
|
GBytes *bytes,
|
|
DfuFirmwareParseFlags flags,
|
|
GError **error)
|
|
{
|
|
const guint8 *data;
|
|
gsize data_length;
|
|
guint idx = 2;
|
|
guint kvlen;
|
|
guint number_keys;
|
|
|
|
/* not big enough */
|
|
data = g_bytes_get_data (bytes, &data_length);
|
|
if (data_length <= 0x10)
|
|
return TRUE;
|
|
|
|
/* signature invalid */
|
|
if (memcmp (data, "MD", 2) != 0)
|
|
return TRUE;
|
|
|
|
/* parse key=value store */
|
|
number_keys = data[idx++];
|
|
for (guint i = 0; i < number_keys; i++) {
|
|
g_autofree gchar *key = NULL;
|
|
g_autofree gchar *value = NULL;
|
|
|
|
/* parse key */
|
|
kvlen = data[idx++];
|
|
if (kvlen > 233) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"metadata table corrupt, key=%u",
|
|
kvlen);
|
|
return FALSE;
|
|
}
|
|
if (idx + kvlen + 0x10 > data_length) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"metadata table corrupt, k-kvlen=%u",
|
|
kvlen);
|
|
return FALSE;
|
|
}
|
|
key = g_strndup ((const gchar *) data + idx, kvlen);
|
|
idx += kvlen;
|
|
|
|
/* parse value */
|
|
kvlen = data[idx++];
|
|
if (kvlen > 233) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"metadata table corrupt, value=%u",
|
|
kvlen);
|
|
return FALSE;
|
|
}
|
|
if (idx + kvlen + 0x10 > data_length) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"metadata table corrupt, v-kvlen=%u",
|
|
kvlen);
|
|
return FALSE;
|
|
}
|
|
value = g_strndup ((const gchar *) data + idx, kvlen);
|
|
idx += kvlen;
|
|
dfu_firmware_set_metadata (firmware, key, value);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* dfu_firmware_to_metadata: (skip)
|
|
* @firmware: a #DfuFirmware
|
|
* @error: a #GError, or %NULL
|
|
*
|
|
* Packs metadata firmware
|
|
*
|
|
* Returns: (transfer full): the packed data
|
|
**/
|
|
GBytes *
|
|
dfu_firmware_to_metadata (DfuFirmware *firmware, GError **error)
|
|
{
|
|
GHashTable *metadata;
|
|
guint8 mdbuf[239];
|
|
guint idx = 0;
|
|
guint number_keys;
|
|
g_autoptr(GList) keys = NULL;
|
|
|
|
/* no metadata */
|
|
metadata = dfu_firmware_get_metadata_table (firmware);
|
|
if (g_hash_table_size (metadata) == 0)
|
|
return g_bytes_new (NULL, 0);
|
|
|
|
/* check the number of keys */
|
|
keys = g_hash_table_get_keys (metadata);
|
|
number_keys = g_list_length (keys);
|
|
if (number_keys > 59) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"too many metadata keys (%u)",
|
|
number_keys);
|
|
return NULL;
|
|
}
|
|
|
|
/* write the signature */
|
|
mdbuf[idx++] = 'M';
|
|
mdbuf[idx++] = 'D';
|
|
mdbuf[idx++] = (guint8) number_keys;
|
|
for (GList *l = keys; l != NULL; l = l->next) {
|
|
const gchar *key;
|
|
const gchar *value;
|
|
guint key_len;
|
|
guint value_len;
|
|
|
|
/* check key and value length */
|
|
key = l->data;
|
|
key_len = (guint) strlen (key);
|
|
if (key_len > 233) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"metdata key too long: %s",
|
|
key);
|
|
return NULL;
|
|
}
|
|
value = g_hash_table_lookup (metadata, key);
|
|
value_len = (guint) strlen (value);
|
|
if (value_len > 233) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"value too long: %s",
|
|
value);
|
|
return NULL;
|
|
}
|
|
|
|
/* do we still have space? */
|
|
if (idx + key_len + value_len + 2 > sizeof(mdbuf)) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"not enough space in metadata table, "
|
|
"already used %u bytes", idx);
|
|
return NULL;
|
|
}
|
|
|
|
/* write the key */
|
|
mdbuf[idx++] = (guint8) key_len;
|
|
memcpy(mdbuf + idx, key, key_len);
|
|
idx += key_len;
|
|
|
|
/* write the value */
|
|
mdbuf[idx++] = (guint8) value_len;
|
|
memcpy(mdbuf + idx, value, value_len);
|
|
idx += value_len;
|
|
}
|
|
g_debug ("metadata table was %u/%" G_GSIZE_FORMAT " bytes",
|
|
idx, sizeof(mdbuf));
|
|
return g_bytes_new (mdbuf, idx);
|
|
}
|