blob: 8ea2cf05f8a585fd94c8b2112c9992aa2b10c52f [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);
Richard Hughes943d2c92017-06-21 09:04:39 +0100190 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
191
Richard Hughes919f8ab2018-02-14 10:24:56 +0000192 /* this is invalid */
193 if (count == 0) {
194 g_set_error_literal (error,
195 FWUPD_ERROR,
196 FWUPD_ERROR_NOT_SUPPORTED,
197 "A maximum read size must be specified");
198 return NULL;
199 }
200
Richard Hughes943d2c92017-06-21 09:04:39 +0100201 /* read the entire fd to a data blob */
202 stream = g_unix_input_stream_new (fd, TRUE);
203 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
204 if (blob == NULL) {
205 g_set_error_literal (error,
206 FWUPD_ERROR,
207 FWUPD_ERROR_INVALID_FILE,
208 error_local->message);
209 return NULL;
210 }
211 return g_steal_pointer (&blob);
212}
Richard Hughes94f939a2017-08-08 12:21:39 +0100213
214static gboolean
215fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
216{
217 const gchar *tmp;
218 g_autofree gchar *buf = NULL;
219
220 /* no output file */
221 if (archive_entry_pathname (entry) == NULL)
222 return FALSE;
223
224 /* update output path */
225 tmp = archive_entry_pathname (entry);
226 buf = g_build_filename (dir, tmp, NULL);
227 archive_entry_update_pathname_utf8 (entry, buf);
228 return TRUE;
229}
230
231/**
232 * fu_common_extract_archive:
233 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100234 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100235 * @error: A #GError, or %NULL
236 *
237 * Extracts an achive to a directory.
238 *
239 * Returns: %TRUE for success
240 **/
241gboolean
242fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
243{
244 gboolean ret = TRUE;
245 int r;
246 struct archive *arch = NULL;
247 struct archive_entry *entry;
248
249 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100250 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100251 arch = archive_read_new ();
252 archive_read_support_format_all (arch);
253 archive_read_support_filter_all (arch);
254 r = archive_read_open_memory (arch,
255 (void *) g_bytes_get_data (blob, NULL),
256 (size_t) g_bytes_get_size (blob));
257 if (r != 0) {
258 ret = FALSE;
259 g_set_error (error,
260 FWUPD_ERROR,
261 FWUPD_ERROR_INTERNAL,
262 "Cannot open: %s",
263 archive_error_string (arch));
264 goto out;
265 }
266 for (;;) {
267 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100268 r = archive_read_next_header (arch, &entry);
269 if (r == ARCHIVE_EOF)
270 break;
271 if (r != ARCHIVE_OK) {
272 ret = FALSE;
273 g_set_error (error,
274 FWUPD_ERROR,
275 FWUPD_ERROR_INTERNAL,
276 "Cannot read header: %s",
277 archive_error_string (arch));
278 goto out;
279 }
280
281 /* only extract if valid */
282 valid = fu_common_extract_archive_entry (entry, dir);
283 if (!valid)
284 continue;
285 r = archive_read_extract (arch, entry, 0);
286 if (r != ARCHIVE_OK) {
287 ret = FALSE;
288 g_set_error (error,
289 FWUPD_ERROR,
290 FWUPD_ERROR_INTERNAL,
291 "Cannot extract: %s",
292 archive_error_string (arch));
293 goto out;
294 }
295 }
296out:
297 if (arch != NULL) {
298 archive_read_close (arch);
299 archive_read_free (arch);
300 }
301 return ret;
302}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100303
304static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300305fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
306
307static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100308fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
309{
310 va_list args;
311 g_autofree gchar *tmp = NULL;
312 g_auto(GStrv) split = NULL;
313
314 va_start (args, fmt);
315 tmp = g_strdup_vprintf (fmt, args);
316 va_end (args);
317
318 split = g_strsplit (tmp, " ", -1);
319 for (guint i = 0; split[i] != NULL; i++)
320 g_ptr_array_add (argv, g_strdup (split[i]));
321}
322
323/**
324 * fu_common_firmware_builder:
325 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100326 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
327 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100328 * @error: A #GError, or %NULL
329 *
330 * Builds a firmware file using tools from the host session in a bubblewrap
331 * jail. Several things happen during build:
332 *
333 * 1. The @bytes data is untarred to a temporary location
334 * 2. A bubblewrap container is set up
335 * 3. The startup.sh script is run inside the container
336 * 4. The firmware.bin is extracted from the container
337 * 5. The temporary location is deleted
338 *
339 * Returns: a new #GBytes, or %NULL for error
340 **/
341GBytes *
342fu_common_firmware_builder (GBytes *bytes,
343 const gchar *script_fn,
344 const gchar *output_fn,
345 GError **error)
346{
347 gint rc = 0;
348 g_autofree gchar *argv_str = NULL;
349 g_autofree gchar *localstatedir = NULL;
350 g_autofree gchar *output2_fn = NULL;
351 g_autofree gchar *standard_error = NULL;
352 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100353 g_autofree gchar *tmpdir = NULL;
354 g_autoptr(GBytes) firmware_blob = NULL;
355 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
356
357 g_return_val_if_fail (bytes != NULL, NULL);
358 g_return_val_if_fail (script_fn != NULL, NULL);
359 g_return_val_if_fail (output_fn != NULL, NULL);
360 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
361
362 /* untar file to temp location */
363 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
364 if (tmpdir == NULL)
365 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100366 if (!fu_common_extract_archive (bytes, tmpdir, error))
367 return NULL;
368
369 /* this is shared with the plugins */
370 localstatedir = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", "builder", NULL);
371
372 /* launch bubblewrap and generate firmware */
Richard Hughesf6f72a42017-08-09 16:25:25 +0100373 g_ptr_array_add (argv, g_strdup ("bwrap"));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100374 fu_common_add_argv (argv, "--die-with-parent");
375 fu_common_add_argv (argv, "--ro-bind /usr /usr");
376 fu_common_add_argv (argv, "--dir /tmp");
377 fu_common_add_argv (argv, "--dir /var");
378 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
379 if (g_file_test (localstatedir, G_FILE_TEST_EXISTS))
380 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatedir);
381 fu_common_add_argv (argv, "--dev /dev");
382 fu_common_add_argv (argv, "--symlink usr/lib /lib");
383 fu_common_add_argv (argv, "--symlink usr/lib64 /lib64");
384 fu_common_add_argv (argv, "--symlink usr/bin /bin");
385 fu_common_add_argv (argv, "--symlink usr/sbin /sbin");
386 fu_common_add_argv (argv, "--chdir /tmp");
387 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100388 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100389 g_ptr_array_add (argv, NULL);
390 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
391 g_debug ("running '%s' in %s", argv_str, tmpdir);
392 if (!g_spawn_sync ("/tmp",
393 (gchar **) argv->pdata,
394 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100395 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100396 NULL, NULL, /* child_setup */
397 &standard_output,
398 &standard_error,
399 &rc,
400 error)) {
401 g_prefix_error (error, "failed to run '%s': ", argv_str);
402 return NULL;
403 }
404 if (standard_output != NULL && standard_output[0] != '\0')
405 g_debug ("console output was: %s", standard_output);
406 if (rc != 0) {
407 g_set_error (error,
408 FWUPD_ERROR,
409 FWUPD_ERROR_INTERNAL,
410 "failed to build firmware: %s",
411 standard_error);
412 return NULL;
413 }
414
415 /* get generated file */
416 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
417 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
418 if (firmware_blob == NULL)
419 return NULL;
420
421 /* cleanup temp directory */
422 if (!fu_common_rmtree (tmpdir, error))
423 return NULL;
424
425 /* success */
426 return g_steal_pointer (&firmware_blob);
427}
Richard Hughes049ccc82017-08-09 15:26:56 +0100428
429typedef struct {
430 FuOutputHandler handler_cb;
431 gpointer handler_user_data;
432 GMainLoop *loop;
433 GSource *source;
434 GInputStream *stream;
435 GCancellable *cancellable;
436} FuCommonSpawnHelper;
437
438static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
439
440static gboolean
441fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
442{
443 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
444 gchar buffer[1024];
445 gssize sz;
446 g_auto(GStrv) split = NULL;
447 g_autoptr(GError) error = NULL;
448
449 /* read from stream */
450 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
451 buffer,
452 sizeof(buffer) - 1,
453 NULL,
454 &error);
455 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100456 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
457 g_warning ("failed to get read from nonblocking fd: %s",
458 error->message);
459 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100460 return G_SOURCE_REMOVE;
461 }
462
463 /* no read possible */
464 if (sz == 0)
465 g_main_loop_quit (helper->loop);
466
467 /* emit lines */
468 if (helper->handler_cb != NULL) {
469 buffer[sz] = '\0';
470 split = g_strsplit (buffer, "\n", -1);
471 for (guint i = 0; split[i] != NULL; i++) {
472 if (split[i][0] == '\0')
473 continue;
474 helper->handler_cb (split[i], helper->handler_user_data);
475 }
476 }
477
478 /* set up the source for the next read */
479 fu_common_spawn_create_pollable_source (helper);
480 return G_SOURCE_REMOVE;
481}
482
483static void
484fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
485{
486 if (helper->source != NULL)
487 g_source_destroy (helper->source);
488 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
489 helper->cancellable);
490 g_source_attach (helper->source, NULL);
491 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
492}
493
494static void
495fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
496{
497 if (helper->stream != NULL)
498 g_object_unref (helper->stream);
499 if (helper->source != NULL)
500 g_source_destroy (helper->source);
501 if (helper->loop != NULL)
502 g_main_loop_unref (helper->loop);
503 g_free (helper);
504}
505
506G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
507
508/**
509 * fu_common_spawn_sync:
510 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100511 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
512 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughes049ccc82017-08-09 15:26:56 +0100513 * @cancellable: a #GCancellable, or %NULL
514 * @error: A #GError or %NULL
515 *
516 * Runs a subprocess and waits for it to exit. Any output on standard out or
517 * standard error will be forwarded to @handler_cb as whole lines.
518 *
519 * Returns: %TRUE for success
520 **/
521gboolean
522fu_common_spawn_sync (const gchar * const * argv,
523 FuOutputHandler handler_cb,
524 gpointer handler_user_data,
525 GCancellable *cancellable, GError **error)
526{
527 g_autoptr(FuCommonSpawnHelper) helper = NULL;
528 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100529 g_autofree gchar *argv_str = NULL;
Richard Hughes049ccc82017-08-09 15:26:56 +0100530
531 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100532 argv_str = g_strjoinv (" ", (gchar **) argv);
533 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100534 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
535 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
536 if (subprocess == NULL)
537 return FALSE;
538
539 /* watch for process to exit */
540 helper = g_new0 (FuCommonSpawnHelper, 1);
541 helper->handler_cb = handler_cb;
542 helper->handler_user_data = handler_user_data;
543 helper->loop = g_main_loop_new (NULL, FALSE);
544 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
545 helper->cancellable = cancellable;
546 fu_common_spawn_create_pollable_source (helper);
547 g_main_loop_run (helper->loop);
548 return g_subprocess_wait_check (subprocess, cancellable, error);
549}
Richard Hughesae252cd2017-12-08 10:48:15 +0000550
551/**
552 * fu_common_write_uint16:
553 * @buf: A writable buffer
554 * @val_native: a value in host byte-order
555 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
556 *
557 * Writes a value to a buffer using a specified endian.
558 **/
559void
560fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
561{
562 guint16 val_hw;
563 switch (endian) {
564 case G_BIG_ENDIAN:
565 val_hw = GUINT16_TO_BE(val_native);
566 break;
567 case G_LITTLE_ENDIAN:
568 val_hw = GUINT16_TO_LE(val_native);
569 break;
570 default:
571 g_assert_not_reached ();
572 }
573 memcpy (buf, &val_hw, sizeof(val_hw));
574}
575
576/**
577 * fu_common_write_uint32:
578 * @buf: A writable buffer
579 * @val_native: a value in host byte-order
580 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
581 *
582 * Writes a value to a buffer using a specified endian.
583 **/
584void
585fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
586{
587 guint32 val_hw;
588 switch (endian) {
589 case G_BIG_ENDIAN:
590 val_hw = GUINT32_TO_BE(val_native);
591 break;
592 case G_LITTLE_ENDIAN:
593 val_hw = GUINT32_TO_LE(val_native);
594 break;
595 default:
596 g_assert_not_reached ();
597 }
598 memcpy (buf, &val_hw, sizeof(val_hw));
599}
600
601/**
602 * fu_common_read_uint16:
603 * @buf: A readable buffer
604 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
605 *
606 * Read a value from a buffer using a specified endian.
607 *
608 * Returns: a value in host byte-order
609 **/
610guint16
611fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
612{
613 guint16 val_hw, val_native;
614 memcpy (&val_hw, buf, sizeof(val_hw));
615 switch (endian) {
616 case G_BIG_ENDIAN:
617 val_native = GUINT16_FROM_BE(val_hw);
618 break;
619 case G_LITTLE_ENDIAN:
620 val_native = GUINT16_FROM_LE(val_hw);
621 break;
622 default:
623 g_assert_not_reached ();
624 }
625 return val_native;
626}
627
628/**
629 * fu_common_read_uint32:
630 * @buf: A readable buffer
631 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
632 *
633 * Read a value from a buffer using a specified endian.
634 *
635 * Returns: a value in host byte-order
636 **/
637guint32
638fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
639{
640 guint32 val_hw, val_native;
641 memcpy (&val_hw, buf, sizeof(val_hw));
642 switch (endian) {
643 case G_BIG_ENDIAN:
644 val_native = GUINT32_FROM_BE(val_hw);
645 break;
646 case G_LITTLE_ENDIAN:
647 val_native = GUINT32_FROM_LE(val_hw);
648 break;
649 default:
650 g_assert_not_reached ();
651 }
652 return val_native;
653}