Coverage Report

Created: 2026-04-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-bytes.c
Line
Count
Source
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuCommon"
8
9
#include "config.h"
10
11
#include "fwupd-error.h"
12
13
#include "fu-byte-array.h"
14
#include "fu-bytes.h"
15
#include "fu-mem.h"
16
17
/**
18
 * fu_bytes_set_contents:
19
 * @filename: a filename
20
 * @bytes: data to write
21
 * @error: (nullable): optional return location for an error
22
 *
23
 * Writes a blob of data to a filename, creating the parent directories as
24
 * required.
25
 *
26
 * Returns: %TRUE for success
27
 *
28
 * Since: 1.8.2
29
 **/
30
gboolean
31
fu_bytes_set_contents(const gchar *filename, GBytes *bytes, GError **error)
32
0
{
33
0
  const gchar *data;
34
0
  gsize size;
35
0
  g_autoptr(GFile) file = NULL;
36
0
  g_autoptr(GFile) file_parent = NULL;
37
38
0
  g_return_val_if_fail(filename != NULL, FALSE);
39
0
  g_return_val_if_fail(bytes != NULL, FALSE);
40
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
41
42
0
  file = g_file_new_for_path(filename);
43
0
  file_parent = g_file_get_parent(file);
44
0
  if (!g_file_query_exists(file_parent, NULL)) {
45
0
    if (!g_file_make_directory_with_parents(file_parent, NULL, error))
46
0
      return FALSE;
47
0
  }
48
0
  data = g_bytes_get_data(bytes, &size);
49
0
  g_debug("writing %s with 0x%x bytes", filename, (guint)size);
50
0
  return g_file_set_contents(filename, data, size, error);
51
0
}
52
53
/**
54
 * fu_bytes_get_contents:
55
 * @filename: a filename
56
 * @error: (nullable): optional return location for an error
57
 *
58
 * Reads a blob of data from a file.
59
 *
60
 * Returns: a #GBytes, or %NULL for failure
61
 *
62
 * Since: 1.8.2
63
 **/
64
GBytes *
65
fu_bytes_get_contents(const gchar *filename, GError **error)
66
0
{
67
0
  g_autoptr(GError) error_local = NULL;
68
0
  g_autoptr(GMappedFile) mapped_file = NULL;
69
70
0
  g_return_val_if_fail(filename != NULL, NULL);
71
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
72
73
  /* try as a mapped file, falling back to reading it as a blob instead */
74
0
  mapped_file = g_mapped_file_new(filename, FALSE, &error_local);
75
0
  if (mapped_file == NULL || g_mapped_file_get_length(mapped_file) == 0) {
76
0
    gchar *data = NULL;
77
0
    gsize len = 0;
78
0
    if (!g_file_get_contents(filename, &data, &len, error)) {
79
0
      fwupd_error_convert(error);
80
0
      return NULL;
81
0
    }
82
0
    g_debug("failed to read as mapped file, "
83
0
      "so reading %s of size 0x%x: %s",
84
0
      filename,
85
0
      (guint)len,
86
0
      error_local != NULL ? error_local->message : "zero size");
87
0
    return g_bytes_new_take(data, len);
88
0
  }
89
0
  g_debug("mapped file %s of size 0x%x",
90
0
    filename,
91
0
    (guint)g_mapped_file_get_length(mapped_file));
92
0
  return g_mapped_file_get_bytes(mapped_file);
93
0
}
94
95
/**
96
 * fu_bytes_align:
97
 * @bytes: data blob
98
 * @blksz: block size in bytes
99
 * @padval: the byte used to pad the byte buffer
100
 *
101
 * Aligns a block of memory to @blksize using the @padval value; if
102
 * the block is already aligned then the original @bytes is returned.
103
 *
104
 * Returns: (transfer full): a #GBytes, possibly @bytes
105
 *
106
 * Since: 1.8.2
107
 **/
108
GBytes *
109
fu_bytes_align(GBytes *bytes, gsize blksz, gchar padval)
110
0
{
111
0
  const guint8 *data;
112
0
  gsize sz;
113
114
0
  g_return_val_if_fail(bytes != NULL, NULL);
115
0
  g_return_val_if_fail(blksz > 0, NULL);
116
117
  /* pad */
118
0
  data = g_bytes_get_data(bytes, &sz);
119
0
  if (sz % blksz != 0) {
120
0
    gsize sz_align = ((sz / blksz) + 1) * blksz;
121
0
    guint8 *data_align = g_malloc(sz_align);
122
0
    memcpy(data_align, data, sz); /* nocheck:blocked */
123
0
    memset(data_align + sz, padval, sz_align - sz);
124
0
    g_debug("aligning 0x%x bytes to 0x%x", (guint)sz, (guint)sz_align);
125
0
    return g_bytes_new_take(data_align, sz_align);
126
0
  }
127
128
  /* perfectly aligned */
129
0
  return g_bytes_ref(bytes);
130
0
}
131
132
/**
133
 * fu_bytes_is_empty:
134
 * @bytes: data blob
135
 *
136
 * Checks if a byte array are just empty (0xff) bytes.
137
 *
138
 * Returns: %TRUE if @bytes is empty
139
 *
140
 * Since: 1.8.2
141
 **/
142
gboolean
143
fu_bytes_is_empty(GBytes *bytes)
144
0
{
145
0
  gsize sz = 0;
146
0
  const guint8 *buf = g_bytes_get_data(bytes, &sz);
147
0
  for (gsize i = 0; i < sz; i++) {
148
0
    if (buf[i] != 0xff)
149
0
      return FALSE;
150
0
  }
151
0
  return TRUE;
152
0
}
153
154
/**
155
 * fu_bytes_compare:
156
 * @bytes1: a data blob
157
 * @bytes2: another #GBytes
158
 * @error: (nullable): optional return location for an error
159
 *
160
 * Compares the buffers for equality.
161
 *
162
 * Returns: %TRUE if @bytes1 and @bytes2 are identical
163
 *
164
 * Since: 1.8.2
165
 **/
166
gboolean
167
fu_bytes_compare(GBytes *bytes1, GBytes *bytes2, GError **error)
168
0
{
169
0
  const guint8 *buf1;
170
0
  const guint8 *buf2;
171
0
  gsize bufsz1;
172
0
  gsize bufsz2;
173
174
0
  g_return_val_if_fail(bytes1 != NULL, FALSE);
175
0
  g_return_val_if_fail(bytes2 != NULL, FALSE);
176
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
177
178
0
  buf1 = g_bytes_get_data(bytes1, &bufsz1);
179
0
  buf2 = g_bytes_get_data(bytes2, &bufsz2);
180
0
  return fu_memcmp_safe(buf1, bufsz1, 0x0, buf2, bufsz2, 0x0, MAX(bufsz1, bufsz2), error);
181
0
}
182
183
/**
184
 * fu_bytes_pad:
185
 * @bytes: data blob
186
 * @sz: the desired size in bytes
187
 * @data: the byte used to pad the array
188
 *
189
 * Pads a GBytes to a minimum @sz with `0xff`.
190
 *
191
 * Returns: (transfer full): a data blob
192
 *
193
 * Since: 2.0.7
194
 **/
195
GBytes *
196
fu_bytes_pad(GBytes *bytes, gsize sz, guint8 data)
197
896
{
198
896
  gsize bytes_sz;
199
200
896
  g_return_val_if_fail(bytes != NULL, NULL);
201
896
  g_return_val_if_fail(sz != 0, NULL);
202
203
  /* pad */
204
896
  bytes_sz = g_bytes_get_size(bytes);
205
896
  if (bytes_sz < sz) {
206
858
    const guint8 *data_old = g_bytes_get_data(bytes, NULL);
207
858
    guint8 *data_new = g_malloc(sz);
208
858
    if (data_old != NULL)
209
331
      memcpy(data_new, data_old, bytes_sz); /* nocheck:blocked */
210
858
    memset(data_new + bytes_sz, data, sz - bytes_sz);
211
858
    return g_bytes_new_take(data_new, sz);
212
858
  }
213
214
  /* not required */
215
38
  return g_bytes_ref(bytes);
216
896
}
217
218
/**
219
 * fu_bytes_new_offset:
220
 * @bytes: data blob
221
 * @offset: where subsection starts at
222
 * @length: length of subsection
223
 * @error: (nullable): optional return location for an error
224
 *
225
 * Creates a #GBytes which is a subsection of another #GBytes.
226
 *
227
 * Returns: (transfer full): a #GBytes, or #NULL if range is invalid
228
 *
229
 * Since: 1.8.2
230
 **/
231
GBytes *
232
fu_bytes_new_offset(GBytes *bytes, gsize offset, gsize length, GError **error)
233
772k
{
234
772k
  g_return_val_if_fail(bytes != NULL, NULL);
235
772k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
236
237
  /* optimize */
238
772k
  if (offset == 0x0 && g_bytes_get_size(bytes) == length)
239
25.1k
    return g_bytes_ref(bytes);
240
241
  /* sanity check */
242
747k
  if (offset + length < length || offset + length > g_bytes_get_size(bytes)) {
243
138
    g_set_error(error,
244
138
          FWUPD_ERROR,
245
138
          FWUPD_ERROR_INVALID_DATA,
246
138
          "cannot create bytes @0x%02x for 0x%02x "
247
138
          "as buffer only 0x%04x bytes in size",
248
138
          (guint)offset,
249
138
          (guint)length,
250
138
          (guint)g_bytes_get_size(bytes));
251
138
    return NULL;
252
138
  }
253
747k
  return g_bytes_new_from_bytes(bytes, offset, length);
254
747k
}
255
256
/**
257
 * fu_bytes_get_data_safe:
258
 * @bytes: data blob
259
 * @bufsz: (out) (optional): location to return size of byte data
260
 * @error: (nullable): optional return location for an error
261
 *
262
 * Get the byte data in the #GBytes. This data should not be modified.
263
 * This function will always return the same pointer for a given #GBytes.
264
 *
265
 * If the size of @bytes is zero, then %NULL is returned and the @error is set,
266
 * which differs in behavior to that of g_bytes_get_data().
267
 *
268
 * This may be useful when calling g_mapped_file_new() on a zero-length file.
269
 *
270
 * Returns: a pointer to the byte data, or %NULL.
271
 *
272
 * Since: 1.6.0
273
 **/
274
const guint8 *
275
fu_bytes_get_data_safe(GBytes *bytes, gsize *bufsz, GError **error)
276
8.04M
{
277
8.04M
  const guint8 *buf = g_bytes_get_data(bytes, bufsz);
278
8.04M
  if (buf == NULL) {
279
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid data");
280
0
    return NULL;
281
0
  }
282
8.04M
  return buf;
283
8.04M
}
284
285
/**
286
 * fu_bytes_to_string:
287
 * @bytes: data blob
288
 *
289
 * Converts @bytes to a lowercase hex string.
290
 *
291
 * Returns: (transfer full): a string, which may be zero length
292
 *
293
 * Since: 2.0.4
294
 **/
295
gchar *
296
fu_bytes_to_string(GBytes *bytes)
297
0
{
298
0
  const guint8 *buf;
299
0
  gsize bufsz = 0;
300
0
  g_autoptr(GString) str = g_string_new(NULL);
301
302
0
  g_return_val_if_fail(bytes != NULL, NULL);
303
304
0
  buf = g_bytes_get_data(bytes, &bufsz);
305
0
  for (gsize i = 0; i < bufsz; i++)
306
0
    g_string_append_printf(str, "%02x", buf[i]);
307
0
  return g_string_free(g_steal_pointer(&str), FALSE);
308
0
}
309
310
/**
311
 * fu_bytes_from_string:
312
 * @str: a hex string
313
 * @error: (nullable): optional return location for an error
314
 *
315
 * Converts a lowercase hex string to a #GBytes.
316
 *
317
 * Returns: (transfer full): a #GBytes, or %NULL on error
318
 *
319
 * Since: 2.0.5
320
 **/
321
GBytes *
322
fu_bytes_from_string(const gchar *str, GError **error)
323
0
{
324
0
  g_autoptr(GByteArray) buf = NULL;
325
326
0
  g_return_val_if_fail(str != NULL, NULL);
327
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
328
329
0
  buf = fu_byte_array_from_string(str, error);
330
0
  if (buf == NULL)
331
0
    return NULL;
332
0
  return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); /* nocheck:blocked */
333
0
}