File: fu-bytes.c

package info (click to toggle)
fwupd 2.0.20-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 32,504 kB
  • sloc: ansic: 277,388; python: 11,485; xml: 9,493; sh: 1,625; makefile: 167; cpp: 19; asm: 11; javascript: 9
file content (333 lines) | stat: -rw-r--r-- 8,523 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#define G_LOG_DOMAIN "FuCommon"

#include "config.h"

#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_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 = g_bytes_get_data(bytes1, &bufsz1);
	buf2 = g_bytes_get_data(bytes2, &bufsz2);
	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)); /* nocheck:blocked */
}