blob: 0626235d04f7550338fd2508a28dbe218a2b0ef5 [file] [log] [blame]
Richard Hughes943d2c92017-06-21 09:04:39 +01001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <config.h>
23
24#include <gio/gunixinputstream.h>
Richard Hughes954dd9f2017-08-08 13:36:25 +010025#include <glib/gstdio.h>
26
Richard Hughes94f939a2017-08-08 12:21:39 +010027#include <archive_entry.h>
28#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010029#include <errno.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010031
32#include "fwupd-error.h"
33
34#include "fu-common.h"
35
36/**
Richard Hughes4eada342017-10-03 21:20:32 +010037 * SECTION:fu-common
38 * @short_description: common functionality for plugins to use
39 *
40 * Helper functions that can be used by the daemon and plugins.
41 *
42 * See also: #FuPlugin
43 */
44
45/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010046 * fu_common_rmtree:
47 * @directory: a directory name
48 * @error: A #GError or %NULL
49 *
50 * Recursively removes a directory.
51 *
52 * Returns: %TRUE for success, %FALSE otherwise
53 **/
54gboolean
55fu_common_rmtree (const gchar *directory, GError **error)
56{
57 const gchar *filename;
58 g_autoptr(GDir) dir = NULL;
59
60 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010061 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010062 dir = g_dir_open (directory, 0, error);
63 if (dir == NULL)
64 return FALSE;
65
66 /* find each */
67 while ((filename = g_dir_read_name (dir))) {
68 g_autofree gchar *src = NULL;
69 src = g_build_filename (directory, filename, NULL);
70 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
71 if (!fu_common_rmtree (src, error))
72 return FALSE;
73 } else {
74 if (g_unlink (src) != 0) {
75 g_set_error (error,
76 FWUPD_ERROR,
77 FWUPD_ERROR_INTERNAL,
78 "Failed to delete: %s", src);
79 return FALSE;
80 }
81 }
82 }
83 if (g_remove (directory) != 0) {
84 g_set_error (error,
85 FWUPD_ERROR,
86 FWUPD_ERROR_INTERNAL,
87 "Failed to delete: %s", directory);
88 return FALSE;
89 }
90 return TRUE;
91}
92
93/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +010094 * fu_common_mkdir_parent:
95 * @filename: A full pathname
96 * @error: A #GError, or %NULL
97 *
98 * Creates any required directories, including any parent directories.
99 *
100 * Returns: %TRUE for success
101 **/
102gboolean
103fu_common_mkdir_parent (const gchar *filename, GError **error)
104{
105 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100106
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100107 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100108 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100109 if (g_mkdir_with_parents (parent, 0755) == -1) {
110 g_set_error (error,
111 FWUPD_ERROR,
112 FWUPD_ERROR_INTERNAL,
113 "Failed to create '%s': %s",
114 parent, g_strerror (errno));
115 return FALSE;
116 }
117 return TRUE;
118}
119
120/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100121 * fu_common_set_contents_bytes:
122 * @filename: A filename
123 * @bytes: The data to write
124 * @error: A #GError, or %NULL
125 *
126 * Writes a blob of data to a filename, creating the parent directories as
127 * required.
128 *
129 * Returns: %TRUE for success
130 **/
131gboolean
132fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
133{
134 const gchar *data;
135 gsize size;
136 g_autoptr(GFile) file = NULL;
137 g_autoptr(GFile) file_parent = NULL;
138
139 file = g_file_new_for_path (filename);
140 file_parent = g_file_get_parent (file);
141 if (!g_file_query_exists (file_parent, NULL)) {
142 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
143 return FALSE;
144 }
145 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100146 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100147 return g_file_set_contents (filename, data, size, error);
148}
149
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100150/**
151 * fu_common_get_contents_bytes:
152 * @filename: A filename
153 * @error: A #GError, or %NULL
154 *
155 * Reads a blob of data from a file.
156 *
157 * Returns: a #GBytes, or %NULL for failure
158 **/
159GBytes *
160fu_common_get_contents_bytes (const gchar *filename, GError **error)
161{
162 gchar *data = NULL;
163 gsize len = 0;
164 if (!g_file_get_contents (filename, &data, &len, error))
165 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100166 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100167 return g_bytes_new_take (data, len);
168}
Richard Hughes943d2c92017-06-21 09:04:39 +0100169
170/**
171 * fu_common_get_contents_fd:
172 * @fd: A file descriptor
173 * @count: The maximum number of bytes to read
174 * @error: A #GError, or %NULL
175 *
176 * Reads a blob from a specific file descriptor.
177 *
178 * Note: this will close the fd when done
179 *
Richard Hughes4eada342017-10-03 21:20:32 +0100180 * Returns: (transfer full): a #GBytes, or %NULL
Richard Hughes943d2c92017-06-21 09:04:39 +0100181 **/
182GBytes *
183fu_common_get_contents_fd (gint fd, gsize count, GError **error)
184{
185 g_autoptr(GBytes) blob = NULL;
186 g_autoptr(GError) error_local = NULL;
187 g_autoptr(GInputStream) stream = NULL;
188
189 g_return_val_if_fail (fd > 0, NULL);
190 g_return_val_if_fail (count > 0, NULL);
191 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
192
193 /* read the entire fd to a data blob */
194 stream = g_unix_input_stream_new (fd, TRUE);
195 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
196 if (blob == NULL) {
197 g_set_error_literal (error,
198 FWUPD_ERROR,
199 FWUPD_ERROR_INVALID_FILE,
200 error_local->message);
201 return NULL;
202 }
203 return g_steal_pointer (&blob);
204}
Richard Hughes94f939a2017-08-08 12:21:39 +0100205
206static gboolean
207fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
208{
209 const gchar *tmp;
210 g_autofree gchar *buf = NULL;
211
212 /* no output file */
213 if (archive_entry_pathname (entry) == NULL)
214 return FALSE;
215
216 /* update output path */
217 tmp = archive_entry_pathname (entry);
218 buf = g_build_filename (dir, tmp, NULL);
219 archive_entry_update_pathname_utf8 (entry, buf);
220 return TRUE;
221}
222
223/**
224 * fu_common_extract_archive:
225 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100226 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100227 * @error: A #GError, or %NULL
228 *
229 * Extracts an achive to a directory.
230 *
231 * Returns: %TRUE for success
232 **/
233gboolean
234fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
235{
236 gboolean ret = TRUE;
237 int r;
238 struct archive *arch = NULL;
239 struct archive_entry *entry;
240
241 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100242 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100243 arch = archive_read_new ();
244 archive_read_support_format_all (arch);
245 archive_read_support_filter_all (arch);
246 r = archive_read_open_memory (arch,
247 (void *) g_bytes_get_data (blob, NULL),
248 (size_t) g_bytes_get_size (blob));
249 if (r != 0) {
250 ret = FALSE;
251 g_set_error (error,
252 FWUPD_ERROR,
253 FWUPD_ERROR_INTERNAL,
254 "Cannot open: %s",
255 archive_error_string (arch));
256 goto out;
257 }
258 for (;;) {
259 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100260 r = archive_read_next_header (arch, &entry);
261 if (r == ARCHIVE_EOF)
262 break;
263 if (r != ARCHIVE_OK) {
264 ret = FALSE;
265 g_set_error (error,
266 FWUPD_ERROR,
267 FWUPD_ERROR_INTERNAL,
268 "Cannot read header: %s",
269 archive_error_string (arch));
270 goto out;
271 }
272
273 /* only extract if valid */
274 valid = fu_common_extract_archive_entry (entry, dir);
275 if (!valid)
276 continue;
277 r = archive_read_extract (arch, entry, 0);
278 if (r != ARCHIVE_OK) {
279 ret = FALSE;
280 g_set_error (error,
281 FWUPD_ERROR,
282 FWUPD_ERROR_INTERNAL,
283 "Cannot extract: %s",
284 archive_error_string (arch));
285 goto out;
286 }
287 }
288out:
289 if (arch != NULL) {
290 archive_read_close (arch);
291 archive_read_free (arch);
292 }
293 return ret;
294}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100295
296static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300297fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
298
299static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100300fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
301{
302 va_list args;
303 g_autofree gchar *tmp = NULL;
304 g_auto(GStrv) split = NULL;
305
306 va_start (args, fmt);
307 tmp = g_strdup_vprintf (fmt, args);
308 va_end (args);
309
310 split = g_strsplit (tmp, " ", -1);
311 for (guint i = 0; split[i] != NULL; i++)
312 g_ptr_array_add (argv, g_strdup (split[i]));
313}
314
315/**
316 * fu_common_firmware_builder:
317 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100318 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
319 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100320 * @error: A #GError, or %NULL
321 *
322 * Builds a firmware file using tools from the host session in a bubblewrap
323 * jail. Several things happen during build:
324 *
325 * 1. The @bytes data is untarred to a temporary location
326 * 2. A bubblewrap container is set up
327 * 3. The startup.sh script is run inside the container
328 * 4. The firmware.bin is extracted from the container
329 * 5. The temporary location is deleted
330 *
331 * Returns: a new #GBytes, or %NULL for error
332 **/
333GBytes *
334fu_common_firmware_builder (GBytes *bytes,
335 const gchar *script_fn,
336 const gchar *output_fn,
337 GError **error)
338{
339 gint rc = 0;
340 g_autofree gchar *argv_str = NULL;
341 g_autofree gchar *localstatedir = NULL;
342 g_autofree gchar *output2_fn = NULL;
343 g_autofree gchar *standard_error = NULL;
344 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100345 g_autofree gchar *tmpdir = NULL;
346 g_autoptr(GBytes) firmware_blob = NULL;
347 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
348
349 g_return_val_if_fail (bytes != NULL, NULL);
350 g_return_val_if_fail (script_fn != NULL, NULL);
351 g_return_val_if_fail (output_fn != NULL, NULL);
352 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
353
354 /* untar file to temp location */
355 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
356 if (tmpdir == NULL)
357 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100358 if (!fu_common_extract_archive (bytes, tmpdir, error))
359 return NULL;
360
361 /* this is shared with the plugins */
362 localstatedir = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", "builder", NULL);
363
364 /* launch bubblewrap and generate firmware */
Richard Hughesf6f72a42017-08-09 16:25:25 +0100365 g_ptr_array_add (argv, g_strdup ("bwrap"));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100366 fu_common_add_argv (argv, "--die-with-parent");
367 fu_common_add_argv (argv, "--ro-bind /usr /usr");
368 fu_common_add_argv (argv, "--dir /tmp");
369 fu_common_add_argv (argv, "--dir /var");
370 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
371 if (g_file_test (localstatedir, G_FILE_TEST_EXISTS))
372 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatedir);
373 fu_common_add_argv (argv, "--dev /dev");
374 fu_common_add_argv (argv, "--symlink usr/lib /lib");
375 fu_common_add_argv (argv, "--symlink usr/lib64 /lib64");
376 fu_common_add_argv (argv, "--symlink usr/bin /bin");
377 fu_common_add_argv (argv, "--symlink usr/sbin /sbin");
378 fu_common_add_argv (argv, "--chdir /tmp");
379 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100380 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100381 g_ptr_array_add (argv, NULL);
382 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
383 g_debug ("running '%s' in %s", argv_str, tmpdir);
384 if (!g_spawn_sync ("/tmp",
385 (gchar **) argv->pdata,
386 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100387 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100388 NULL, NULL, /* child_setup */
389 &standard_output,
390 &standard_error,
391 &rc,
392 error)) {
393 g_prefix_error (error, "failed to run '%s': ", argv_str);
394 return NULL;
395 }
396 if (standard_output != NULL && standard_output[0] != '\0')
397 g_debug ("console output was: %s", standard_output);
398 if (rc != 0) {
399 g_set_error (error,
400 FWUPD_ERROR,
401 FWUPD_ERROR_INTERNAL,
402 "failed to build firmware: %s",
403 standard_error);
404 return NULL;
405 }
406
407 /* get generated file */
408 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
409 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
410 if (firmware_blob == NULL)
411 return NULL;
412
413 /* cleanup temp directory */
414 if (!fu_common_rmtree (tmpdir, error))
415 return NULL;
416
417 /* success */
418 return g_steal_pointer (&firmware_blob);
419}
Richard Hughes049ccc82017-08-09 15:26:56 +0100420
421typedef struct {
422 FuOutputHandler handler_cb;
423 gpointer handler_user_data;
424 GMainLoop *loop;
425 GSource *source;
426 GInputStream *stream;
427 GCancellable *cancellable;
428} FuCommonSpawnHelper;
429
430static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
431
432static gboolean
433fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
434{
435 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
436 gchar buffer[1024];
437 gssize sz;
438 g_auto(GStrv) split = NULL;
439 g_autoptr(GError) error = NULL;
440
441 /* read from stream */
442 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
443 buffer,
444 sizeof(buffer) - 1,
445 NULL,
446 &error);
447 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100448 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
449 g_warning ("failed to get read from nonblocking fd: %s",
450 error->message);
451 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100452 return G_SOURCE_REMOVE;
453 }
454
455 /* no read possible */
456 if (sz == 0)
457 g_main_loop_quit (helper->loop);
458
459 /* emit lines */
460 if (helper->handler_cb != NULL) {
461 buffer[sz] = '\0';
462 split = g_strsplit (buffer, "\n", -1);
463 for (guint i = 0; split[i] != NULL; i++) {
464 if (split[i][0] == '\0')
465 continue;
466 helper->handler_cb (split[i], helper->handler_user_data);
467 }
468 }
469
470 /* set up the source for the next read */
471 fu_common_spawn_create_pollable_source (helper);
472 return G_SOURCE_REMOVE;
473}
474
475static void
476fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
477{
478 if (helper->source != NULL)
479 g_source_destroy (helper->source);
480 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
481 helper->cancellable);
482 g_source_attach (helper->source, NULL);
483 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
484}
485
486static void
487fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
488{
489 if (helper->stream != NULL)
490 g_object_unref (helper->stream);
491 if (helper->source != NULL)
492 g_source_destroy (helper->source);
493 if (helper->loop != NULL)
494 g_main_loop_unref (helper->loop);
495 g_free (helper);
496}
497
498G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
499
500/**
501 * fu_common_spawn_sync:
502 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100503 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
504 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughes049ccc82017-08-09 15:26:56 +0100505 * @cancellable: a #GCancellable, or %NULL
506 * @error: A #GError or %NULL
507 *
508 * Runs a subprocess and waits for it to exit. Any output on standard out or
509 * standard error will be forwarded to @handler_cb as whole lines.
510 *
511 * Returns: %TRUE for success
512 **/
513gboolean
514fu_common_spawn_sync (const gchar * const * argv,
515 FuOutputHandler handler_cb,
516 gpointer handler_user_data,
517 GCancellable *cancellable, GError **error)
518{
519 g_autoptr(FuCommonSpawnHelper) helper = NULL;
520 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100521 g_autofree gchar *argv_str = NULL;
Richard Hughes049ccc82017-08-09 15:26:56 +0100522
523 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100524 argv_str = g_strjoinv (" ", (gchar **) argv);
525 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100526 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
527 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
528 if (subprocess == NULL)
529 return FALSE;
530
531 /* watch for process to exit */
532 helper = g_new0 (FuCommonSpawnHelper, 1);
533 helper->handler_cb = handler_cb;
534 helper->handler_user_data = handler_user_data;
535 helper->loop = g_main_loop_new (NULL, FALSE);
536 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
537 helper->cancellable = cancellable;
538 fu_common_spawn_create_pollable_source (helper);
539 g_main_loop_run (helper->loop);
540 return g_subprocess_wait_check (subprocess, cancellable, error);
541}
Richard Hughesae252cd2017-12-08 10:48:15 +0000542
543/**
544 * fu_common_write_uint16:
545 * @buf: A writable buffer
546 * @val_native: a value in host byte-order
547 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
548 *
549 * Writes a value to a buffer using a specified endian.
550 **/
551void
552fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
553{
554 guint16 val_hw;
555 switch (endian) {
556 case G_BIG_ENDIAN:
557 val_hw = GUINT16_TO_BE(val_native);
558 break;
559 case G_LITTLE_ENDIAN:
560 val_hw = GUINT16_TO_LE(val_native);
561 break;
562 default:
563 g_assert_not_reached ();
564 }
565 memcpy (buf, &val_hw, sizeof(val_hw));
566}
567
568/**
569 * fu_common_write_uint32:
570 * @buf: A writable buffer
571 * @val_native: a value in host byte-order
572 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
573 *
574 * Writes a value to a buffer using a specified endian.
575 **/
576void
577fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
578{
579 guint32 val_hw;
580 switch (endian) {
581 case G_BIG_ENDIAN:
582 val_hw = GUINT32_TO_BE(val_native);
583 break;
584 case G_LITTLE_ENDIAN:
585 val_hw = GUINT32_TO_LE(val_native);
586 break;
587 default:
588 g_assert_not_reached ();
589 }
590 memcpy (buf, &val_hw, sizeof(val_hw));
591}
592
593/**
594 * fu_common_read_uint16:
595 * @buf: A readable buffer
596 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
597 *
598 * Read a value from a buffer using a specified endian.
599 *
600 * Returns: a value in host byte-order
601 **/
602guint16
603fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
604{
605 guint16 val_hw, val_native;
606 memcpy (&val_hw, buf, sizeof(val_hw));
607 switch (endian) {
608 case G_BIG_ENDIAN:
609 val_native = GUINT16_FROM_BE(val_hw);
610 break;
611 case G_LITTLE_ENDIAN:
612 val_native = GUINT16_FROM_LE(val_hw);
613 break;
614 default:
615 g_assert_not_reached ();
616 }
617 return val_native;
618}
619
620/**
621 * fu_common_read_uint32:
622 * @buf: A readable buffer
623 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
624 *
625 * Read a value from a buffer using a specified endian.
626 *
627 * Returns: a value in host byte-order
628 **/
629guint32
630fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
631{
632 guint32 val_hw, val_native;
633 memcpy (&val_hw, buf, sizeof(val_hw));
634 switch (endian) {
635 case G_BIG_ENDIAN:
636 val_native = GUINT32_FROM_BE(val_hw);
637 break;
638 case G_LITTLE_ENDIAN:
639 val_native = GUINT32_FROM_LE(val_hw);
640 break;
641 default:
642 g_assert_not_reached ();
643 }
644 return val_native;
645}