/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libfwupdplugin/fu-bytes.c
400 строк
10 KB
Richard Hughes
trivial: Protect fu_bytes_compare() against empty GBytes
07 июл 2026, 22:00
07 июл 2026, 22:00
b08ec6c
Код
Авторство
О чём код?
/* * Copyright 2017 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define G_LOG_DOMAIN "FuCommon" #include "config.h" #include <glib.h> #if !GLIB_CHECK_VERSION(2, 66, 0) #include <errno.h> #include <glib/gstdio.h> #endif #include "fwupd-error.h" #include "fu-byte-array.h" #include "fu-bytes.h" #include "fu-mem.h" /** * fu_bytes_set_contents: * @filename: a filename * @bytes: data to write * @error: (nullable): optional return location for an error * * Writes a blob of data to a filename, creating the parent directories as * required. * * Returns: %TRUE for success * * Since: 1.8.2 **/ gboolean fu_bytes_set_contents(const gchar *filename, GBytes *bytes, GError **error) { const gchar *data; gsize size; g_autoptr(GFile) file = NULL; g_autoptr(GFile) file_parent = NULL; g_return_val_if_fail(filename != NULL, FALSE); g_return_val_if_fail(bytes != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); file = g_file_new_for_path(filename); file_parent = g_file_get_parent(file); if (!g_file_query_exists(file_parent, NULL)) { if (!g_file_make_directory_with_parents(file_parent, NULL, error)) return FALSE; } data = g_bytes_get_data(bytes, &size); g_debug("writing %s with 0x%x bytes", filename, (guint)size); return g_file_set_contents(filename, data, size, error); } /** * fu_bytes_set_contents_full: * @filename: a filename * @bytes: data to write * @mode: file permission, e.g. `0644` * @error: (nullable): optional return location for an error * * Writes a blob of data to a filename with specific permissions, creating the parent directories * as required. * * Returns: %TRUE for success * * Since: 2.1.3 **/ gboolean fu_bytes_set_contents_full(const gchar *filename, GBytes *bytes, gint mode, GError **error) { const gchar *data; gsize size; g_autoptr(GFile) file = NULL; g_autoptr(GFile) file_parent = NULL; g_return_val_if_fail(filename != NULL, FALSE); g_return_val_if_fail(bytes != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); file = g_file_new_for_path(filename); file_parent = g_file_get_parent(file); if (!g_file_query_exists(file_parent, NULL)) { if (!g_file_make_directory_with_parents(file_parent, NULL, error)) return FALSE; } data = g_bytes_get_data(bytes, &size); g_debug("writing %s with %zu bytes", filename, size); #if GLIB_CHECK_VERSION(2, 66, 0) return g_file_set_contents_full(filename, data, size, G_FILE_SET_CONTENTS_CONSISTENT, mode, error); #else if (!g_file_set_contents(filename, data, size, error)) return FALSE; if (g_chmod(filename, mode) != 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "failed to set file mode: %s", fwupd_strerror(errno)); return FALSE; } return TRUE; #endif } /** * fu_bytes_get_contents: * @filename: a filename * @error: (nullable): optional return location for an error * * Reads a blob of data from a file. * * Returns: a #GBytes, or %NULL for failure * * Since: 1.8.2 **/ GBytes * fu_bytes_get_contents(const gchar *filename, GError **error) { g_autoptr(GError) error_local = NULL; g_autoptr(GMappedFile) mapped_file = NULL; g_return_val_if_fail(filename != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* try as a mapped file, falling back to reading it as a blob instead */ mapped_file = g_mapped_file_new(filename, FALSE, &error_local); if (mapped_file == NULL || g_mapped_file_get_length(mapped_file) == 0) { gchar *data = NULL; gsize len = 0; if (!g_file_get_contents(filename, &data, &len, error)) { fwupd_error_convert(error); return NULL; } g_debug("failed to read as mapped file, " "so reading %s of size 0x%x: %s", filename, (guint)len, error_local != NULL ? error_local->message : "zero size"); return g_bytes_new_take(data, len); } g_debug("mapped file %s of size 0x%x", filename, (guint)g_mapped_file_get_length(mapped_file)); return g_mapped_file_get_bytes(mapped_file); } /** * fu_bytes_align: * @bytes: data blob * @blksz: block size in bytes * @padval: the byte used to pad the byte buffer * * Aligns a block of memory to @blksize using the @padval value; if * the block is already aligned then the original @bytes is returned. * * Returns: (transfer full): a #GBytes, possibly @bytes * * Since: 1.8.2 **/ GBytes * fu_bytes_align(GBytes *bytes, gsize blksz, gchar padval) { const guint8 *data; gsize sz; g_return_val_if_fail(bytes != NULL, NULL); g_return_val_if_fail(blksz > 0, NULL); /* pad */ data = g_bytes_get_data(bytes, &sz); if (sz % blksz != 0) { gsize sz_align = ((sz / blksz) + 1) * blksz; guint8 *data_align = g_malloc(sz_align); memcpy(data_align, data, sz); /* nocheck:blocked */ memset(data_align + sz, padval, sz_align - sz); g_debug("aligning 0x%x bytes to 0x%x", (guint)sz, (guint)sz_align); return g_bytes_new_take(data_align, sz_align); } /* perfectly aligned */ return g_bytes_ref(bytes); } /** * fu_bytes_is_empty: * @bytes: data blob * * Checks if a byte array are just empty (0xff) bytes. * * Returns: %TRUE if @bytes is empty * * Since: 1.8.2 **/ gboolean fu_bytes_is_empty(GBytes *bytes) { gsize sz = 0; const guint8 *buf = g_bytes_get_data(bytes, &sz); for (gsize i = 0; i < sz; i++) { if (buf[i] != 0xff) return FALSE; } return TRUE; } /** * fu_bytes_compare: * @bytes1: a data blob * @bytes2: another #GBytes * @error: (nullable): optional return location for an error * * Compares the buffers for equality. * * Returns: %TRUE if @bytes1 and @bytes2 are identical * * Since: 1.8.2 **/ gboolean fu_bytes_compare(GBytes *bytes1, GBytes *bytes2, GError **error) { const guint8 *buf1; const guint8 *buf2; gsize bufsz1; gsize bufsz2; g_return_val_if_fail(bytes1 != NULL, FALSE); g_return_val_if_fail(bytes2 != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); buf1 = fu_bytes_get_data_safe(bytes1, &bufsz1, error); if (buf1 == NULL) return FALSE; buf2 = fu_bytes_get_data_safe(bytes2, &bufsz2, error); if (buf2 == NULL) return FALSE; return fu_memcmp_safe(buf1, bufsz1, 0x0, buf2, bufsz2, 0x0, MAX(bufsz1, bufsz2), error); } /** * fu_bytes_pad: * @bytes: data blob * @sz: the desired size in bytes * @data: the byte used to pad the array * * Pads a GBytes to a minimum @sz with `0xff`. * * Returns: (transfer full): a data blob * * Since: 2.0.7 **/ GBytes * fu_bytes_pad(GBytes *bytes, gsize sz, guint8 data) { gsize bytes_sz; g_return_val_if_fail(bytes != NULL, NULL); g_return_val_if_fail(sz != 0, NULL); /* pad */ bytes_sz = g_bytes_get_size(bytes); if (bytes_sz < sz) { const guint8 *data_old = g_bytes_get_data(bytes, NULL); guint8 *data_new = g_malloc(sz); if (data_old != NULL) memcpy(data_new, data_old, bytes_sz); /* nocheck:blocked */ memset(data_new + bytes_sz, data, sz - bytes_sz); return g_bytes_new_take(data_new, sz); } /* not required */ return g_bytes_ref(bytes); } /** * fu_bytes_new_offset: * @bytes: data blob * @offset: where subsection starts at * @length: length of subsection * @error: (nullable): optional return location for an error * * Creates a #GBytes which is a subsection of another #GBytes. * * Returns: (transfer full): a #GBytes, or #NULL if range is invalid * * Since: 1.8.2 **/ GBytes * fu_bytes_new_offset(GBytes *bytes, gsize offset, gsize length, GError **error) { g_return_val_if_fail(bytes != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* optimize */ if (offset == 0x0 && g_bytes_get_size(bytes) == length) return g_bytes_ref(bytes); /* sanity check */ if (offset + length < length || offset + length > g_bytes_get_size(bytes)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot create bytes @0x%02x for 0x%02x " "as buffer only 0x%04x bytes in size", (guint)offset, (guint)length, (guint)g_bytes_get_size(bytes)); return NULL; } return g_bytes_new_from_bytes(bytes, offset, length); } /** * fu_bytes_get_data_safe: * @bytes: data blob * @bufsz: (out) (optional): location to return size of byte data * @error: (nullable): optional return location for an error * * Get the byte data in the #GBytes. This data should not be modified. * This function will always return the same pointer for a given #GBytes. * * If the size of @bytes is zero, then %NULL is returned and the @error is set, * which differs in behavior to that of g_bytes_get_data(). * * This may be useful when calling g_mapped_file_new() on a zero-length file. * * Returns: a pointer to the byte data, or %NULL. * * Since: 1.6.0 **/ const guint8 * fu_bytes_get_data_safe(GBytes *bytes, gsize *bufsz, GError **error) { const guint8 *buf = g_bytes_get_data(bytes, bufsz); if (buf == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid data"); return NULL; } return buf; } /** * fu_bytes_to_string: * @bytes: data blob * * Converts @bytes to a lowercase hex string. * * Returns: (transfer full): a string, which may be zero length * * Since: 2.0.4 **/ gchar * fu_bytes_to_string(GBytes *bytes) { const guint8 *buf; gsize bufsz = 0; g_autoptr(GString) str = g_string_new(NULL); g_return_val_if_fail(bytes != NULL, NULL); buf = g_bytes_get_data(bytes, &bufsz); for (gsize i = 0; i < bufsz; i++) g_string_append_printf(str, "%02x", buf[i]); return g_string_free(g_steal_pointer(&str), FALSE); } /** * fu_bytes_from_string: * @str: a hex string * @error: (nullable): optional return location for an error * * Converts a lowercase hex string to a #GBytes. * * Returns: (transfer full): a #GBytes, or %NULL on error * * Since: 2.0.5 **/ GBytes * fu_bytes_from_string(const gchar *str, GError **error) { g_autoptr(GByteArray) buf = NULL; g_return_val_if_fail(str != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); buf = fu_byte_array_from_string(str, error); if (buf == NULL) return NULL; return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); }