blob: 183c719ad9d72286998e6d1311bf9eb2238bd75e [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes943d2c92017-06-21 09:04:39 +01002 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes943d2c92017-06-21 09:04:39 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuCommon"
8
Richard Hughes943d2c92017-06-21 09:04:39 +01009#include <config.h>
10
Richard Hughes9e5675e2019-11-22 09:35:03 +000011#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +010012#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#endif
Richard Hughes954dd9f2017-08-08 13:36:25 +010014#include <glib/gstdio.h>
15
Richard Hughes5c508de2019-11-22 09:57:34 +000016#ifdef HAVE_FNMATCH_H
17#include <fnmatch.h>
Richard Hughes45a00732019-11-22 16:57:14 +000018#elif _WIN32
19#include <shlwapi.h>
Richard Hughes5c508de2019-11-22 09:57:34 +000020#endif
21
Richard Hughes68175e92021-01-14 09:43:33 +000022#ifdef _WIN32
23#include <sysinfoapi.h>
24#endif
25
Richard Hughesbd444322020-05-21 12:05:03 +010026#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010027#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010028#endif
Richard Hughes9223c892020-05-09 20:32:08 +010029
Richard Hughes5add3a72021-01-13 19:25:10 +000030#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +010031#include <archive_entry.h>
32#include <archive.h>
Richard Hughes5add3a72021-01-13 19:25:10 +000033#endif
Richard Hughes7ee42fe2017-08-15 14:06:21 +010034#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000035#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000036#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000037#include <stdlib.h>
Richard Hughes68175e92021-01-14 09:43:33 +000038#include <unistd.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010039
40#include "fwupd-error.h"
41
42#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010043#include "fu-volume-private.h"
44
Richard Hughes43417b22020-10-30 14:46:16 +000045#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
46#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
47#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
48#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
49#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
50#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010051
52/**
Richard Hughes4eada342017-10-03 21:20:32 +010053 * SECTION:fu-common
54 * @short_description: common functionality for plugins to use
55 *
56 * Helper functions that can be used by the daemon and plugins.
57 *
58 * See also: #FuPlugin
59 */
60
61/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010062 * fu_common_rmtree:
63 * @directory: a directory name
64 * @error: A #GError or %NULL
65 *
66 * Recursively removes a directory.
67 *
68 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060069 *
70 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010071 **/
72gboolean
73fu_common_rmtree (const gchar *directory, GError **error)
74{
75 const gchar *filename;
76 g_autoptr(GDir) dir = NULL;
77
Richard Hughes6a489a92020-12-22 10:32:06 +000078 g_return_val_if_fail (directory != NULL, FALSE);
79 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
80
Richard Hughes954dd9f2017-08-08 13:36:25 +010081 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010082 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010083 dir = g_dir_open (directory, 0, error);
84 if (dir == NULL)
85 return FALSE;
86
87 /* find each */
88 while ((filename = g_dir_read_name (dir))) {
89 g_autofree gchar *src = NULL;
90 src = g_build_filename (directory, filename, NULL);
91 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
92 if (!fu_common_rmtree (src, error))
93 return FALSE;
94 } else {
95 if (g_unlink (src) != 0) {
96 g_set_error (error,
97 FWUPD_ERROR,
98 FWUPD_ERROR_INTERNAL,
99 "Failed to delete: %s", src);
100 return FALSE;
101 }
102 }
103 }
104 if (g_remove (directory) != 0) {
105 g_set_error (error,
106 FWUPD_ERROR,
107 FWUPD_ERROR_INTERNAL,
108 "Failed to delete: %s", directory);
109 return FALSE;
110 }
111 return TRUE;
112}
113
Richard Hughes89e968b2018-03-07 10:01:08 +0000114static gboolean
115fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
116{
117 const gchar *filename;
118 g_autoptr(GDir) dir = NULL;
119
120 /* try to open */
121 dir = g_dir_open (directory, 0, error);
122 if (dir == NULL)
123 return FALSE;
124
125 /* find each */
126 while ((filename = g_dir_read_name (dir))) {
127 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
128 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
129 if (!fu_common_get_file_list_internal (files, src, error))
130 return FALSE;
131 } else {
132 g_ptr_array_add (files, g_steal_pointer (&src));
133 }
134 }
135 return TRUE;
136
137}
138
139/**
140 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100141 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000142 * @error: A #GError or %NULL
143 *
144 * Returns every file found under @directory, and any subdirectory.
145 * If any path under @directory cannot be accessed due to permissions an error
146 * will be returned.
147 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000148 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600149 *
150 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000151 **/
152GPtrArray *
153fu_common_get_files_recursive (const gchar *path, GError **error)
154{
155 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +0000156
157 g_return_val_if_fail (path != NULL, NULL);
158 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
159
Richard Hughes89e968b2018-03-07 10:01:08 +0000160 if (!fu_common_get_file_list_internal (files, path, error))
161 return NULL;
162 return g_steal_pointer (&files);
163}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100164/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100165 * fu_common_mkdir_parent:
166 * @filename: A full pathname
167 * @error: A #GError, or %NULL
168 *
169 * Creates any required directories, including any parent directories.
170 *
171 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600172 *
173 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100174 **/
175gboolean
176fu_common_mkdir_parent (const gchar *filename, GError **error)
177{
178 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100179
Richard Hughes6a489a92020-12-22 10:32:06 +0000180 g_return_val_if_fail (filename != NULL, FALSE);
181 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
182
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100183 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500184 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
185 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100186 if (g_mkdir_with_parents (parent, 0755) == -1) {
187 g_set_error (error,
188 FWUPD_ERROR,
189 FWUPD_ERROR_INTERNAL,
190 "Failed to create '%s': %s",
191 parent, g_strerror (errno));
192 return FALSE;
193 }
194 return TRUE;
195}
196
197/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100198 * fu_common_set_contents_bytes:
199 * @filename: A filename
200 * @bytes: The data to write
201 * @error: A #GError, or %NULL
202 *
203 * Writes a blob of data to a filename, creating the parent directories as
204 * required.
205 *
206 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600207 *
208 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100209 **/
210gboolean
211fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
212{
213 const gchar *data;
214 gsize size;
215 g_autoptr(GFile) file = NULL;
216 g_autoptr(GFile) file_parent = NULL;
217
Richard Hughes6a489a92020-12-22 10:32:06 +0000218 g_return_val_if_fail (filename != NULL, FALSE);
219 g_return_val_if_fail (bytes != NULL, FALSE);
220 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
221
Richard Hughes943d2c92017-06-21 09:04:39 +0100222 file = g_file_new_for_path (filename);
223 file_parent = g_file_get_parent (file);
224 if (!g_file_query_exists (file_parent, NULL)) {
225 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
226 return FALSE;
227 }
228 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100229 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100230 return g_file_set_contents (filename, data, size, error);
231}
232
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100233/**
234 * fu_common_get_contents_bytes:
235 * @filename: A filename
236 * @error: A #GError, or %NULL
237 *
238 * Reads a blob of data from a file.
239 *
240 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600241 *
242 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100243 **/
244GBytes *
245fu_common_get_contents_bytes (const gchar *filename, GError **error)
246{
247 gchar *data = NULL;
248 gsize len = 0;
Richard Hughes6a489a92020-12-22 10:32:06 +0000249
250 g_return_val_if_fail (filename != NULL, NULL);
251 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
252
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100253 if (!g_file_get_contents (filename, &data, &len, error))
254 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100255 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100256 return g_bytes_new_take (data, len);
257}
Richard Hughes943d2c92017-06-21 09:04:39 +0100258
259/**
260 * fu_common_get_contents_fd:
261 * @fd: A file descriptor
262 * @count: The maximum number of bytes to read
263 * @error: A #GError, or %NULL
264 *
265 * Reads a blob from a specific file descriptor.
266 *
267 * Note: this will close the fd when done
268 *
Richard Hughes4eada342017-10-03 21:20:32 +0100269 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600270 *
271 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100272 **/
273GBytes *
274fu_common_get_contents_fd (gint fd, gsize count, GError **error)
275{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000276#ifdef HAVE_GIO_UNIX
Richard Hughes97ad3352021-01-14 20:07:47 +0000277 guint8 tmp[0x8000] = { 0x0 };
278 g_autoptr(GByteArray) buf = g_byte_array_new ();
Richard Hughes943d2c92017-06-21 09:04:39 +0100279 g_autoptr(GError) error_local = NULL;
280 g_autoptr(GInputStream) stream = NULL;
281
282 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100283 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
284
Richard Hughes919f8ab2018-02-14 10:24:56 +0000285 /* this is invalid */
286 if (count == 0) {
287 g_set_error_literal (error,
288 FWUPD_ERROR,
289 FWUPD_ERROR_NOT_SUPPORTED,
290 "A maximum read size must be specified");
291 return NULL;
292 }
293
Richard Hughes943d2c92017-06-21 09:04:39 +0100294 /* read the entire fd to a data blob */
295 stream = g_unix_input_stream_new (fd, TRUE);
Richard Hughes97ad3352021-01-14 20:07:47 +0000296
297 /* read from stream in 32kB chunks */
298 while (TRUE) {
299 gssize sz;
300 sz = g_input_stream_read (stream, tmp, sizeof(tmp), NULL, &error_local);
301 if (sz == 0)
302 break;
303 if (sz < 0) {
304 g_set_error_literal (error,
305 FWUPD_ERROR,
306 FWUPD_ERROR_INVALID_FILE,
307 error_local->message);
308 return NULL;
309 }
310 g_byte_array_append (buf, tmp, sz);
311 if (buf->len > count) {
312 g_set_error (error,
Richard Hughes943d2c92017-06-21 09:04:39 +0100313 FWUPD_ERROR,
314 FWUPD_ERROR_INVALID_FILE,
Richard Hughes97ad3352021-01-14 20:07:47 +0000315 "cannot read from fd: 0x%x > 0x%x",
316 buf->len, (guint) count);
317 return NULL;
318 }
Richard Hughes943d2c92017-06-21 09:04:39 +0100319 }
Richard Hughes97ad3352021-01-14 20:07:47 +0000320 return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
Richard Hughes9e5675e2019-11-22 09:35:03 +0000321#else
322 g_set_error_literal (error,
323 FWUPD_ERROR,
324 FWUPD_ERROR_NOT_SUPPORTED,
325 "Not supported as <glib-unix.h> is unavailable");
326 return NULL;
327#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100328}
Richard Hughes94f939a2017-08-08 12:21:39 +0100329
Richard Hughes5add3a72021-01-13 19:25:10 +0000330#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100331static gboolean
332fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
333{
334 const gchar *tmp;
335 g_autofree gchar *buf = NULL;
336
337 /* no output file */
338 if (archive_entry_pathname (entry) == NULL)
339 return FALSE;
340
341 /* update output path */
342 tmp = archive_entry_pathname (entry);
343 buf = g_build_filename (dir, tmp, NULL);
344 archive_entry_update_pathname_utf8 (entry, buf);
345 return TRUE;
346}
Richard Hughes5add3a72021-01-13 19:25:10 +0000347#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100348
349/**
350 * fu_common_extract_archive:
351 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100352 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100353 * @error: A #GError, or %NULL
354 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000355 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100356 *
357 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600358 *
359 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100360 **/
361gboolean
362fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
363{
Richard Hughes5add3a72021-01-13 19:25:10 +0000364#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100365 gboolean ret = TRUE;
366 int r;
367 struct archive *arch = NULL;
368 struct archive_entry *entry;
369
Richard Hughes6a489a92020-12-22 10:32:06 +0000370 g_return_val_if_fail (blob != NULL, FALSE);
371 g_return_val_if_fail (dir != NULL, FALSE);
372 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
373
Richard Hughes94f939a2017-08-08 12:21:39 +0100374 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100375 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100376 arch = archive_read_new ();
377 archive_read_support_format_all (arch);
378 archive_read_support_filter_all (arch);
379 r = archive_read_open_memory (arch,
380 (void *) g_bytes_get_data (blob, NULL),
381 (size_t) g_bytes_get_size (blob));
382 if (r != 0) {
383 ret = FALSE;
384 g_set_error (error,
385 FWUPD_ERROR,
386 FWUPD_ERROR_INTERNAL,
387 "Cannot open: %s",
388 archive_error_string (arch));
389 goto out;
390 }
391 for (;;) {
392 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100393 r = archive_read_next_header (arch, &entry);
394 if (r == ARCHIVE_EOF)
395 break;
396 if (r != ARCHIVE_OK) {
397 ret = FALSE;
398 g_set_error (error,
399 FWUPD_ERROR,
400 FWUPD_ERROR_INTERNAL,
401 "Cannot read header: %s",
402 archive_error_string (arch));
403 goto out;
404 }
405
406 /* only extract if valid */
407 valid = fu_common_extract_archive_entry (entry, dir);
408 if (!valid)
409 continue;
410 r = archive_read_extract (arch, entry, 0);
411 if (r != ARCHIVE_OK) {
412 ret = FALSE;
413 g_set_error (error,
414 FWUPD_ERROR,
415 FWUPD_ERROR_INTERNAL,
416 "Cannot extract: %s",
417 archive_error_string (arch));
418 goto out;
419 }
420 }
421out:
422 if (arch != NULL) {
423 archive_read_close (arch);
424 archive_read_free (arch);
425 }
426 return ret;
Richard Hughes5add3a72021-01-13 19:25:10 +0000427#else
428 g_set_error_literal (error,
429 FWUPD_ERROR,
430 FWUPD_ERROR_NOT_SUPPORTED,
431 "missing libarchive support");
432 return FALSE;
433#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100434}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100435
436static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300437fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
438
439static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100440fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
441{
442 va_list args;
443 g_autofree gchar *tmp = NULL;
444 g_auto(GStrv) split = NULL;
445
446 va_start (args, fmt);
447 tmp = g_strdup_vprintf (fmt, args);
448 va_end (args);
449
450 split = g_strsplit (tmp, " ", -1);
451 for (guint i = 0; split[i] != NULL; i++)
452 g_ptr_array_add (argv, g_strdup (split[i]));
453}
454
Mario Limonciello1a680f32019-11-25 19:44:53 -0600455/**
456 * fu_common_find_program_in_path:
457 * @basename: The program to search
458 * @error: A #GError, or %NULL
459 *
460 * Looks for a program in the PATH variable
461 *
462 * Returns: a new #gchar, or %NULL for error
463 *
464 * Since: 1.1.2
465 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100466gchar *
467fu_common_find_program_in_path (const gchar *basename, GError **error)
468{
469 gchar *fn = g_find_program_in_path (basename);
470 if (fn == NULL) {
471 g_set_error (error,
472 FWUPD_ERROR,
473 FWUPD_ERROR_NOT_SUPPORTED,
474 "missing executable %s in PATH",
475 basename);
476 return NULL;
477 }
478 return fn;
479}
480
481static gboolean
482fu_common_test_namespace_support (GError **error)
483{
484 /* test if CONFIG_USER_NS is valid */
485 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
486 g_set_error (error,
487 FWUPD_ERROR,
488 FWUPD_ERROR_NOT_SUPPORTED,
489 "missing CONFIG_USER_NS in kernel");
490 return FALSE;
491 }
492 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
493 g_autofree gchar *clone = NULL;
494 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
495 return FALSE;
496 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
497 g_set_error (error,
498 FWUPD_ERROR,
499 FWUPD_ERROR_NOT_SUPPORTED,
500 "unprivileged user namespace clones disabled by distro");
501 return FALSE;
502 }
503 }
504 return TRUE;
505}
506
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507/**
508 * fu_common_firmware_builder:
509 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100510 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
511 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100512 * @error: A #GError, or %NULL
513 *
514 * Builds a firmware file using tools from the host session in a bubblewrap
515 * jail. Several things happen during build:
516 *
517 * 1. The @bytes data is untarred to a temporary location
518 * 2. A bubblewrap container is set up
519 * 3. The startup.sh script is run inside the container
520 * 4. The firmware.bin is extracted from the container
521 * 5. The temporary location is deleted
522 *
523 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600524 *
525 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100526 **/
527GBytes *
528fu_common_firmware_builder (GBytes *bytes,
529 const gchar *script_fn,
530 const gchar *output_fn,
531 GError **error)
532{
533 gint rc = 0;
534 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500535 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100536 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100537 g_autofree gchar *localstatedir = NULL;
538 g_autofree gchar *output2_fn = NULL;
539 g_autofree gchar *standard_error = NULL;
540 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100541 g_autofree gchar *tmpdir = NULL;
542 g_autoptr(GBytes) firmware_blob = NULL;
543 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
544
545 g_return_val_if_fail (bytes != NULL, NULL);
546 g_return_val_if_fail (script_fn != NULL, NULL);
547 g_return_val_if_fail (output_fn != NULL, NULL);
548 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
549
Mario Limonciello37b59582018-08-13 08:38:01 -0500550 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100551 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
552 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100553 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500554
555 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100556 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100557 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500558
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100559 /* untar file to temp location */
560 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
561 if (tmpdir == NULL)
562 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100563 if (!fu_common_extract_archive (bytes, tmpdir, error))
564 return NULL;
565
566 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100567 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
568 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100569
570 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500571 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100572 fu_common_add_argv (argv, "--die-with-parent");
573 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500574 fu_common_add_argv (argv, "--ro-bind /lib /lib");
Mario Limoncielloed4e9122020-12-15 20:26:50 -0600575 fu_common_add_argv (argv, "--ro-bind-try /lib64 /lib64");
Mario Limonciellob8215572018-07-13 09:49:55 -0500576 fu_common_add_argv (argv, "--ro-bind /bin /bin");
577 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100578 fu_common_add_argv (argv, "--dir /tmp");
579 fu_common_add_argv (argv, "--dir /var");
580 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100581 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
582 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100583 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100584 fu_common_add_argv (argv, "--chdir /tmp");
585 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100586 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100587 g_ptr_array_add (argv, NULL);
588 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
589 g_debug ("running '%s' in %s", argv_str, tmpdir);
590 if (!g_spawn_sync ("/tmp",
591 (gchar **) argv->pdata,
592 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100593 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100594 NULL, NULL, /* child_setup */
595 &standard_output,
596 &standard_error,
597 &rc,
598 error)) {
599 g_prefix_error (error, "failed to run '%s': ", argv_str);
600 return NULL;
601 }
602 if (standard_output != NULL && standard_output[0] != '\0')
603 g_debug ("console output was: %s", standard_output);
604 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500605 FwupdError code = FWUPD_ERROR_INTERNAL;
606 if (errno == ENOTTY)
607 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100608 g_set_error (error,
609 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500610 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100611 "failed to build firmware: %s",
612 standard_error);
613 return NULL;
614 }
615
616 /* get generated file */
617 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
618 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
619 if (firmware_blob == NULL)
620 return NULL;
621
622 /* cleanup temp directory */
623 if (!fu_common_rmtree (tmpdir, error))
624 return NULL;
625
626 /* success */
627 return g_steal_pointer (&firmware_blob);
628}
Richard Hughes049ccc82017-08-09 15:26:56 +0100629
630typedef struct {
631 FuOutputHandler handler_cb;
632 gpointer handler_user_data;
633 GMainLoop *loop;
634 GSource *source;
635 GInputStream *stream;
636 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000637 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100638} FuCommonSpawnHelper;
639
640static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
641
642static gboolean
643fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
644{
645 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
646 gchar buffer[1024];
647 gssize sz;
648 g_auto(GStrv) split = NULL;
649 g_autoptr(GError) error = NULL;
650
651 /* read from stream */
652 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
653 buffer,
654 sizeof(buffer) - 1,
655 NULL,
656 &error);
657 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100658 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
659 g_warning ("failed to get read from nonblocking fd: %s",
660 error->message);
661 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100662 return G_SOURCE_REMOVE;
663 }
664
665 /* no read possible */
666 if (sz == 0)
667 g_main_loop_quit (helper->loop);
668
669 /* emit lines */
670 if (helper->handler_cb != NULL) {
671 buffer[sz] = '\0';
672 split = g_strsplit (buffer, "\n", -1);
673 for (guint i = 0; split[i] != NULL; i++) {
674 if (split[i][0] == '\0')
675 continue;
676 helper->handler_cb (split[i], helper->handler_user_data);
677 }
678 }
679
680 /* set up the source for the next read */
681 fu_common_spawn_create_pollable_source (helper);
682 return G_SOURCE_REMOVE;
683}
684
685static void
686fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
687{
688 if (helper->source != NULL)
689 g_source_destroy (helper->source);
690 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
691 helper->cancellable);
692 g_source_attach (helper->source, NULL);
693 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
694}
695
696static void
697fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
698{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000699 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100700 if (helper->stream != NULL)
701 g_object_unref (helper->stream);
702 if (helper->source != NULL)
703 g_source_destroy (helper->source);
704 if (helper->loop != NULL)
705 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000706 if (helper->timeout_id != 0)
707 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100708 g_free (helper);
709}
710
Mario Limoncielloa98df552018-04-16 12:15:51 -0500711#pragma clang diagnostic push
712#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100713G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500714#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100715
Richard Hughesb768e4d2019-02-26 13:55:18 +0000716static gboolean
717fu_common_spawn_timeout_cb (gpointer user_data)
718{
719 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
720 g_cancellable_cancel (helper->cancellable);
721 g_main_loop_quit (helper->loop);
722 helper->timeout_id = 0;
723 return G_SOURCE_REMOVE;
724}
725
726static void
727fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
728{
729 /* just propagate */
730 g_cancellable_cancel (helper->cancellable);
731}
732
Richard Hughes049ccc82017-08-09 15:26:56 +0100733/**
734 * fu_common_spawn_sync:
735 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100736 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
737 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000738 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100739 * @cancellable: a #GCancellable, or %NULL
740 * @error: A #GError or %NULL
741 *
742 * Runs a subprocess and waits for it to exit. Any output on standard out or
743 * standard error will be forwarded to @handler_cb as whole lines.
744 *
745 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600746 *
747 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100748 **/
749gboolean
750fu_common_spawn_sync (const gchar * const * argv,
751 FuOutputHandler handler_cb,
752 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000753 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100754 GCancellable *cancellable, GError **error)
755{
756 g_autoptr(FuCommonSpawnHelper) helper = NULL;
757 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100758 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000759 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100760
Richard Hughes6a489a92020-12-22 10:32:06 +0000761 g_return_val_if_fail (argv != NULL, FALSE);
762 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
763 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
764
Richard Hughes049ccc82017-08-09 15:26:56 +0100765 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100766 argv_str = g_strjoinv (" ", (gchar **) argv);
767 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100768 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
769 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
770 if (subprocess == NULL)
771 return FALSE;
772
773 /* watch for process to exit */
774 helper = g_new0 (FuCommonSpawnHelper, 1);
775 helper->handler_cb = handler_cb;
776 helper->handler_user_data = handler_user_data;
777 helper->loop = g_main_loop_new (NULL, FALSE);
778 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000779
780 /* always create a cancellable, and connect up the parent */
781 helper->cancellable = g_cancellable_new ();
782 if (cancellable != NULL) {
783 cancellable_id = g_cancellable_connect (cancellable,
784 G_CALLBACK (fu_common_spawn_cancelled_cb),
785 helper, NULL);
786 }
787
788 /* allow timeout */
789 if (timeout_ms > 0) {
790 helper->timeout_id = g_timeout_add (timeout_ms,
791 fu_common_spawn_timeout_cb,
792 helper);
793 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100794 fu_common_spawn_create_pollable_source (helper);
795 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000796 g_cancellable_disconnect (cancellable, cancellable_id);
797 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
798 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100799 return g_subprocess_wait_check (subprocess, cancellable, error);
800}
Richard Hughesae252cd2017-12-08 10:48:15 +0000801
802/**
803 * fu_common_write_uint16:
804 * @buf: A writable buffer
805 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100806 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000807 *
808 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600809 *
810 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000811 **/
812void
813fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
814{
815 guint16 val_hw;
816 switch (endian) {
817 case G_BIG_ENDIAN:
818 val_hw = GUINT16_TO_BE(val_native);
819 break;
820 case G_LITTLE_ENDIAN:
821 val_hw = GUINT16_TO_LE(val_native);
822 break;
823 default:
824 g_assert_not_reached ();
825 }
826 memcpy (buf, &val_hw, sizeof(val_hw));
827}
828
829/**
830 * fu_common_write_uint32:
831 * @buf: A writable buffer
832 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100833 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000834 *
835 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600836 *
837 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000838 **/
839void
840fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
841{
842 guint32 val_hw;
843 switch (endian) {
844 case G_BIG_ENDIAN:
845 val_hw = GUINT32_TO_BE(val_native);
846 break;
847 case G_LITTLE_ENDIAN:
848 val_hw = GUINT32_TO_LE(val_native);
849 break;
850 default:
851 g_assert_not_reached ();
852 }
853 memcpy (buf, &val_hw, sizeof(val_hw));
854}
855
856/**
Richard Hughesf2849d22021-03-05 17:19:17 +0000857 * fu_common_write_uint64:
858 * @buf: A writable buffer
859 * @val_native: a value in host byte-order
860 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
861 *
862 * Writes a value to a buffer using a specified endian.
863 *
864 * Since: 1.5.8
865 **/
866void
867fu_common_write_uint64 (guint8 *buf, guint64 val_native, FuEndianType endian)
868{
869 guint64 val_hw;
870 switch (endian) {
871 case G_BIG_ENDIAN:
872 val_hw = GUINT64_TO_BE(val_native);
873 break;
874 case G_LITTLE_ENDIAN:
875 val_hw = GUINT64_TO_LE(val_native);
876 break;
877 default:
878 g_assert_not_reached ();
879 }
880 memcpy (buf, &val_hw, sizeof(val_hw));
881}
882
883/**
Richard Hughesae252cd2017-12-08 10:48:15 +0000884 * fu_common_read_uint16:
885 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100886 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000887 *
888 * Read a value from a buffer using a specified endian.
889 *
890 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600891 *
892 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000893 **/
894guint16
895fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
896{
897 guint16 val_hw, val_native;
898 memcpy (&val_hw, buf, sizeof(val_hw));
899 switch (endian) {
900 case G_BIG_ENDIAN:
901 val_native = GUINT16_FROM_BE(val_hw);
902 break;
903 case G_LITTLE_ENDIAN:
904 val_native = GUINT16_FROM_LE(val_hw);
905 break;
906 default:
907 g_assert_not_reached ();
908 }
909 return val_native;
910}
911
912/**
913 * fu_common_read_uint32:
914 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100915 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000916 *
917 * Read a value from a buffer using a specified endian.
918 *
919 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600920 *
921 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000922 **/
923guint32
924fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
925{
926 guint32 val_hw, val_native;
927 memcpy (&val_hw, buf, sizeof(val_hw));
928 switch (endian) {
929 case G_BIG_ENDIAN:
930 val_native = GUINT32_FROM_BE(val_hw);
931 break;
932 case G_LITTLE_ENDIAN:
933 val_native = GUINT32_FROM_LE(val_hw);
934 break;
935 default:
936 g_assert_not_reached ();
937 }
938 return val_native;
939}
Richard Hughese82eef32018-05-20 10:41:26 +0100940
Richard Hughes73bf2332018-08-28 09:38:09 +0100941/**
Richard Hughesf2849d22021-03-05 17:19:17 +0000942 * fu_common_read_uint64:
943 * @buf: A readable buffer
944 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
945 *
946 * Read a value from a buffer using a specified endian.
947 *
948 * Returns: a value in host byte-order
949 *
950 * Since: 1.5.8
951 **/
952guint64
953fu_common_read_uint64 (const guint8 *buf, FuEndianType endian)
954{
955 guint64 val_hw, val_native;
956 memcpy (&val_hw, buf, sizeof(val_hw));
957 switch (endian) {
958 case G_BIG_ENDIAN:
959 val_native = GUINT64_FROM_BE(val_hw);
960 break;
961 case G_LITTLE_ENDIAN:
962 val_native = GUINT64_FROM_LE(val_hw);
963 break;
964 default:
965 g_assert_not_reached ();
966 }
967 return val_native;
968}
969
970/**
Richard Hughes73bf2332018-08-28 09:38:09 +0100971 * fu_common_strtoull:
972 * @str: A string, e.g. "0x1234"
973 *
974 * Converts a string value to an integer. Values are assumed base 10, unless
975 * prefixed with "0x" where they are parsed as base 16.
976 *
977 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600978 *
979 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100980 **/
981guint64
982fu_common_strtoull (const gchar *str)
983{
984 guint base = 10;
985 if (str == NULL)
986 return 0x0;
987 if (g_str_has_prefix (str, "0x")) {
988 str += 2;
989 base = 16;
990 }
991 return g_ascii_strtoull (str, NULL, base);
992}
993
Richard Hughesa574a752018-08-31 13:31:03 +0100994/**
995 * fu_common_strstrip:
996 * @str: A string, e.g. " test "
997 *
998 * Removes leading and trailing whitespace from a constant string.
999 *
1000 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -06001001 *
1002 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +01001003 **/
1004gchar *
1005fu_common_strstrip (const gchar *str)
1006{
1007 guint head = G_MAXUINT;
1008 guint tail = 0;
1009
1010 g_return_val_if_fail (str != NULL, NULL);
1011
1012 /* find first non-space char */
1013 for (guint i = 0; str[i] != '\0'; i++) {
1014 if (str[i] != ' ') {
1015 head = i;
1016 break;
1017 }
1018 }
1019 if (head == G_MAXUINT)
1020 return g_strdup ("");
1021
1022 /* find last non-space char */
1023 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -05001024 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +01001025 tail = i;
1026 }
1027 return g_strndup (str + head, tail - head + 1);
1028}
1029
Richard Hughese82eef32018-05-20 10:41:26 +01001030static const GError *
1031fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
1032{
1033 for (guint j = 0; j < errors->len; j++) {
1034 const GError *error = g_ptr_array_index (errors, j);
1035 if (g_error_matches (error, FWUPD_ERROR, error_code))
1036 return error;
1037 }
1038 return NULL;
1039}
1040
1041static guint
1042fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
1043{
1044 guint cnt = 0;
1045 for (guint j = 0; j < errors->len; j++) {
1046 const GError *error = g_ptr_array_index (errors, j);
1047 if (g_error_matches (error, FWUPD_ERROR, error_code))
1048 cnt++;
1049 }
1050 return cnt;
1051}
1052
1053static gboolean
1054fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
1055{
1056 for (guint j = 0; j < errors->len; j++) {
1057 const GError *error = g_ptr_array_index (errors, j);
1058 gboolean matches_any = FALSE;
1059 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
1060 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
1061 matches_any = TRUE;
1062 break;
1063 }
1064 }
1065 if (!matches_any)
1066 return FALSE;
1067 }
1068 return TRUE;
1069}
1070
1071/**
1072 * fu_common_error_array_get_best:
1073 * @errors: (element-type GError): array of errors
1074 *
1075 * Finds the 'best' error to show the user from a array of errors, creating a
1076 * completely bespoke error where required.
1077 *
1078 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -06001079 *
1080 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +01001081 **/
1082GError *
1083fu_common_error_array_get_best (GPtrArray *errors)
1084{
1085 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
1086 FWUPD_ERROR_VERSION_SAME,
1087 FWUPD_ERROR_VERSION_NEWER,
1088 FWUPD_ERROR_NOT_SUPPORTED,
1089 FWUPD_ERROR_INTERNAL,
1090 FWUPD_ERROR_NOT_FOUND,
1091 FWUPD_ERROR_LAST };
1092 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
1093 FWUPD_ERROR_NOT_FOUND,
1094 FWUPD_ERROR_NOT_SUPPORTED,
1095 FWUPD_ERROR_LAST };
1096 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
1097 FWUPD_ERROR_VERSION_SAME,
1098 FWUPD_ERROR_NOT_FOUND,
1099 FWUPD_ERROR_NOT_SUPPORTED,
1100 FWUPD_ERROR_LAST };
1101
1102 /* are all the errors either GUID-not-matched or version-same? */
1103 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
1104 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
1105 return g_error_new (FWUPD_ERROR,
1106 FWUPD_ERROR_NOTHING_TO_DO,
1107 "All updatable firmware is already installed");
1108 }
1109
1110 /* are all the errors either GUID-not-matched or version same or newer? */
1111 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
1112 fu_common_error_array_matches_any (errors, err_all_newer)) {
1113 return g_error_new (FWUPD_ERROR,
1114 FWUPD_ERROR_NOTHING_TO_DO,
1115 "All updatable devices already have newer versions");
1116 }
1117
1118 /* get the most important single error */
1119 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1120 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1121 if (error_tmp != NULL)
1122 return g_error_copy (error_tmp);
1123 }
1124
1125 /* fall back to something */
1126 return g_error_new (FWUPD_ERROR,
1127 FWUPD_ERROR_NOT_FOUND,
1128 "No supported devices found");
1129}
Richard Hughes4be17d12018-05-30 20:36:29 +01001130
1131/**
1132 * fu_common_get_path:
1133 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1134 *
1135 * Gets a fwupd-specific system path. These can be overridden with various
1136 * environment variables, for instance %FWUPD_DATADIR.
1137 *
1138 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001139 *
1140 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001141 **/
1142gchar *
1143fu_common_get_path (FuPathKind path_kind)
1144{
1145 const gchar *tmp;
1146 g_autofree gchar *basedir = NULL;
1147
1148 switch (path_kind) {
1149 /* /var */
1150 case FU_PATH_KIND_LOCALSTATEDIR:
1151 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1152 if (tmp != NULL)
1153 return g_strdup (tmp);
1154 tmp = g_getenv ("SNAP_USER_DATA");
1155 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001156 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1157 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001158 /* /proc */
1159 case FU_PATH_KIND_PROCFS:
1160 tmp = g_getenv ("FWUPD_PROCFS");
1161 if (tmp != NULL)
1162 return g_strdup (tmp);
1163 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001164 /* /sys/firmware */
1165 case FU_PATH_KIND_SYSFSDIR_FW:
1166 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1167 if (tmp != NULL)
1168 return g_strdup (tmp);
1169 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001170 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001171 case FU_PATH_KIND_SYSFSDIR_TPM:
1172 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1173 if (tmp != NULL)
1174 return g_strdup (tmp);
1175 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001176 /* /sys/bus/platform/drivers */
1177 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1178 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1179 if (tmp != NULL)
1180 return g_strdup (tmp);
1181 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001182 /* /sys/kernel/security */
1183 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1184 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1185 if (tmp != NULL)
1186 return g_strdup (tmp);
1187 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001188 /* /sys/firmware/acpi/tables */
1189 case FU_PATH_KIND_ACPI_TABLES:
1190 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1191 if (tmp != NULL)
1192 return g_strdup (tmp);
1193 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001194 /* /etc */
1195 case FU_PATH_KIND_SYSCONFDIR:
1196 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1197 if (tmp != NULL)
1198 return g_strdup (tmp);
1199 tmp = g_getenv ("SNAP_USER_DATA");
1200 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001201 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1202 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001203 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1204 case FU_PATH_KIND_PLUGINDIR_PKG:
1205 tmp = g_getenv ("FWUPD_PLUGINDIR");
1206 if (tmp != NULL)
1207 return g_strdup (tmp);
1208 tmp = g_getenv ("SNAP");
1209 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001210 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1211 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001212 /* /usr/share/fwupd */
1213 case FU_PATH_KIND_DATADIR_PKG:
1214 tmp = g_getenv ("FWUPD_DATADIR");
1215 if (tmp != NULL)
1216 return g_strdup (tmp);
1217 tmp = g_getenv ("SNAP");
1218 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001219 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1220 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001221 /* /usr/libexec/fwupd/efi */
1222 case FU_PATH_KIND_EFIAPPDIR:
1223 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1224 if (tmp != NULL)
1225 return g_strdup (tmp);
1226#ifdef EFI_APP_LOCATION
1227 tmp = g_getenv ("SNAP");
1228 if (tmp != NULL)
1229 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1230 return g_strdup (EFI_APP_LOCATION);
1231#else
1232 return NULL;
1233#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001234 /* /etc/fwupd */
1235 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001236 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001237 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001238 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001239 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1240 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1241 /* /var/lib/fwupd */
1242 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001243 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001244 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001245 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001246 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1247 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1248 /* /var/cache/fwupd */
1249 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001250 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001251 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001252 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001253 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1254 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001255 case FU_PATH_KIND_OFFLINE_TRIGGER:
1256 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1257 if (tmp != NULL)
1258 return g_strdup (tmp);
1259 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001260 case FU_PATH_KIND_POLKIT_ACTIONS:
1261#ifdef POLKIT_ACTIONDIR
1262 return g_strdup (POLKIT_ACTIONDIR);
1263#else
1264 return NULL;
1265#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001266 /* this shouldn't happen */
1267 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001268 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001269 }
1270
1271 return NULL;
1272}
Richard Hughes83e56c12018-10-10 20:24:41 +01001273
1274/**
1275 * fu_common_string_replace:
1276 * @string: The #GString to operate on
1277 * @search: The text to search for
1278 * @replace: The text to use for substitutions
1279 *
1280 * Performs multiple search and replace operations on the given string.
1281 *
1282 * Returns: the number of replacements done, or 0 if @search is not found.
1283 *
1284 * Since: 1.2.0
1285 **/
1286guint
1287fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1288{
1289 gchar *tmp;
1290 guint count = 0;
1291 gsize search_idx = 0;
1292 gsize replace_len;
1293 gsize search_len;
1294
1295 g_return_val_if_fail (string != NULL, 0);
1296 g_return_val_if_fail (search != NULL, 0);
1297 g_return_val_if_fail (replace != NULL, 0);
1298
1299 /* nothing to do */
1300 if (string->len == 0)
1301 return 0;
1302
1303 search_len = strlen (search);
1304 replace_len = strlen (replace);
1305
1306 do {
1307 tmp = g_strstr_len (string->str + search_idx, -1, search);
1308 if (tmp == NULL)
1309 break;
1310
1311 /* advance the counter in case @replace contains @search */
1312 search_idx = (gsize) (tmp - string->str);
1313
1314 /* reallocate the string if required */
1315 if (search_len > replace_len) {
1316 g_string_erase (string,
1317 (gssize) search_idx,
1318 (gssize) (search_len - replace_len));
1319 memcpy (tmp, replace, replace_len);
1320 } else if (search_len < replace_len) {
1321 g_string_insert_len (string,
1322 (gssize) search_idx,
1323 replace,
1324 (gssize) (replace_len - search_len));
1325 /* we have to treat this specially as it could have
1326 * been reallocated when the insertion happened */
1327 memcpy (string->str + search_idx, replace, replace_len);
1328 } else {
1329 /* just memcmp in the new string */
1330 memcpy (tmp, replace, replace_len);
1331 }
1332 search_idx += replace_len;
1333 count++;
1334 } while (TRUE);
1335
1336 return count;
1337}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001338
Richard Hughesae96a1f2019-09-23 11:16:36 +01001339/**
1340 * fu_common_strwidth:
1341 * @text: The string to operate on
1342 *
1343 * Returns the width of the string in displayed characters on the console.
1344 *
1345 * Returns: width of text
1346 *
1347 * Since: 1.3.2
1348 **/
1349gsize
1350fu_common_strwidth (const gchar *text)
1351{
1352 const gchar *p = text;
1353 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001354
1355 g_return_val_if_fail (text != NULL, 0);
1356
Richard Hughesae96a1f2019-09-23 11:16:36 +01001357 while (*p) {
1358 gunichar c = g_utf8_get_char (p);
1359 if (g_unichar_iswide (c))
1360 width += 2;
1361 else if (!g_unichar_iszerowidth (c))
1362 width += 1;
1363 p = g_utf8_next_char (p);
1364 }
1365 return width;
1366}
1367
Mario Limonciello1a680f32019-11-25 19:44:53 -06001368/**
1369 * fu_common_string_append_kv:
1370 * @str: A #GString
1371 * @idt: The indent
1372 * @key: A string to append
1373 * @value: a string to append
1374 *
1375 * Appends a key and string value to a string
1376 *
1377 * Since: 1.2.4
1378 */
Richard Hughescea28de2019-08-09 11:16:40 +01001379void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001380fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001381{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001382 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001383 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001384
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001385 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001386
1387 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001388 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001389 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001390 for (gsize i = 0; i < idt; i++)
1391 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001392 if (key[0] != '\0') {
1393 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001394 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001395 } else {
1396 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001397 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001398 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001399 g_auto(GStrv) split = NULL;
1400 split = g_strsplit (value, "\n", -1);
1401 for (guint i = 0; split[i] != NULL; i++) {
1402 if (i == 0) {
1403 for (gsize j = keysz; j < align; j++)
1404 g_string_append (str, " ");
1405 } else {
1406 for (gsize j = 0; j < idt; j++)
1407 g_string_append (str, " ");
1408 }
1409 g_string_append (str, split[i]);
1410 g_string_append (str, "\n");
1411 }
1412 } else {
1413 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001414 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001415}
1416
Mario Limonciello1a680f32019-11-25 19:44:53 -06001417/**
1418 * fu_common_string_append_ku:
1419 * @str: A #GString
1420 * @idt: The indent
1421 * @key: A string to append
1422 * @value: guint64
1423 *
1424 * Appends a key and unsigned integer to a string
1425 *
1426 * Since: 1.2.4
1427 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001428void
1429fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1430{
1431 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1432 fu_common_string_append_kv (str, idt, key, tmp);
1433}
1434
Mario Limonciello1a680f32019-11-25 19:44:53 -06001435/**
1436 * fu_common_string_append_kx:
1437 * @str: A #GString
1438 * @idt: The indent
1439 * @key: A string to append
1440 * @value: guint64
1441 *
1442 * Appends a key and hex integer to a string
1443 *
1444 * Since: 1.2.4
1445 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001446void
1447fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1448{
1449 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1450 fu_common_string_append_kv (str, idt, key, tmp);
1451}
1452
Mario Limonciello1a680f32019-11-25 19:44:53 -06001453/**
1454 * fu_common_string_append_kb:
1455 * @str: A #GString
1456 * @idt: The indent
1457 * @key: A string to append
1458 * @value: Boolean
1459 *
1460 * Appends a key and boolean value to a string
1461 *
1462 * Since: 1.2.4
1463 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001464void
1465fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1466{
1467 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001468}
1469
Richard Hughese59cb9a2018-12-05 14:37:40 +00001470/**
Richard Hughes35481862019-01-06 12:01:58 +00001471 * fu_common_dump_full:
1472 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1473 * @title: prefix title, or %NULL
1474 * @data: buffer to print
1475 * @len: the size of @data
1476 * @columns: break new lines after this many bytes
1477 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1478 *
1479 * Dumps a raw buffer to the screen.
1480 *
1481 * Since: 1.2.4
1482 **/
1483void
1484fu_common_dump_full (const gchar *log_domain,
1485 const gchar *title,
1486 const guint8 *data,
1487 gsize len,
1488 guint columns,
1489 FuDumpFlags flags)
1490{
1491 g_autoptr(GString) str = g_string_new (NULL);
1492
1493 /* optional */
1494 if (title != NULL)
1495 g_string_append_printf (str, "%s:", title);
1496
1497 /* if more than can fit on one line then start afresh */
1498 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1499 g_string_append (str, "\n");
1500 } else {
1501 for (gsize i = str->len; i < 16; i++)
1502 g_string_append (str, " ");
1503 }
1504
1505 /* offset line */
1506 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1507 g_string_append (str, " │ ");
1508 for (gsize i = 0; i < columns; i++)
1509 g_string_append_printf (str, "%02x ", (guint) i);
1510 g_string_append (str, "\n───────┼");
1511 for (gsize i = 0; i < columns; i++)
1512 g_string_append (str, "───");
1513 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1514 }
1515
1516 /* print each row */
1517 for (gsize i = 0; i < len; i++) {
1518 g_string_append_printf (str, "%02x ", data[i]);
1519
1520 /* optionally print ASCII char */
1521 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1522 if (g_ascii_isprint (data[i]))
1523 g_string_append_printf (str, "[%c] ", data[i]);
1524 else
1525 g_string_append (str, "[?] ");
1526 }
1527
1528 /* new row required */
1529 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1530 g_string_append (str, "\n");
1531 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1532 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1533 }
1534 }
1535 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1536}
1537
1538/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001539 * fu_common_dump_raw:
1540 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1541 * @title: prefix title, or %NULL
1542 * @data: buffer to print
1543 * @len: the size of @data
1544 *
1545 * Dumps a raw buffer to the screen.
1546 *
1547 * Since: 1.2.2
1548 **/
1549void
1550fu_common_dump_raw (const gchar *log_domain,
1551 const gchar *title,
1552 const guint8 *data,
1553 gsize len)
1554{
Richard Hughes35481862019-01-06 12:01:58 +00001555 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1556 if (len > 64)
1557 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1558 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001559}
1560
1561/**
Mario Limonciello39602652019-04-29 21:08:58 -05001562 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001563 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1564 * @title: prefix title, or %NULL
1565 * @bytes: a #GBytes
1566 *
1567 * Dumps a byte buffer to the screen.
1568 *
1569 * Since: 1.2.2
1570 **/
1571void
1572fu_common_dump_bytes (const gchar *log_domain,
1573 const gchar *title,
1574 GBytes *bytes)
1575{
1576 gsize len = 0;
1577 const guint8 *data = g_bytes_get_data (bytes, &len);
1578 fu_common_dump_raw (log_domain, title, data, len);
1579}
Richard Hughesfc90f392019-01-15 21:21:16 +00001580
1581/**
1582 * fu_common_bytes_align:
1583 * @bytes: a #GBytes
1584 * @blksz: block size in bytes
1585 * @padval: the byte used to pad the byte buffer
1586 *
1587 * Aligns a block of memory to @blksize using the @padval value; if
1588 * the block is already aligned then the original @bytes is returned.
1589 *
1590 * Returns: (transfer full): a #GBytes, possibly @bytes
1591 *
1592 * Since: 1.2.4
1593 **/
1594GBytes *
1595fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1596{
1597 const guint8 *data;
1598 gsize sz;
1599
1600 g_return_val_if_fail (bytes != NULL, NULL);
1601 g_return_val_if_fail (blksz > 0, NULL);
1602
1603 /* pad */
1604 data = g_bytes_get_data (bytes, &sz);
1605 if (sz % blksz != 0) {
1606 gsize sz_align = ((sz / blksz) + 1) * blksz;
1607 guint8 *data_align = g_malloc (sz_align);
1608 memcpy (data_align, data, sz);
1609 memset (data_align + sz, padval, sz_align - sz);
1610 g_debug ("aligning 0x%x bytes to 0x%x",
1611 (guint) sz, (guint) sz_align);
1612 return g_bytes_new_take (data_align, sz_align);
1613 }
1614
1615 /* perfectly aligned */
1616 return g_bytes_ref (bytes);
1617}
Richard Hughes36999462019-03-19 20:23:29 +00001618
1619/**
1620 * fu_common_bytes_is_empty:
1621 * @bytes: a #GBytes
1622 *
1623 * Checks if a byte array are just empty (0xff) bytes.
1624 *
1625 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001626 *
1627 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001628 **/
1629gboolean
1630fu_common_bytes_is_empty (GBytes *bytes)
1631{
1632 gsize sz = 0;
1633 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1634 for (gsize i = 0; i < sz; i++) {
1635 if (buf[i] != 0xff)
1636 return FALSE;
1637 }
1638 return TRUE;
1639}
Richard Hughes2aad1042019-03-21 09:03:32 +00001640
1641/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001642 * fu_common_bytes_compare_raw:
1643 * @buf1: a buffer
1644 * @bufsz1: sizeof @buf1
1645 * @buf2: another buffer
1646 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001647 * @error: A #GError or %NULL
1648 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001649 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001650 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001651 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001652 *
1653 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001654 **/
1655gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001656fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1657 const guint8 *buf2, gsize bufsz2,
1658 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001659{
Richard Hughes38245ff2019-09-18 14:46:09 +01001660 g_return_val_if_fail (buf1 != NULL, FALSE);
1661 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001662 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1663
1664 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001665 if (bufsz1 != bufsz2) {
1666 g_set_error (error,
1667 G_IO_ERROR,
1668 G_IO_ERROR_INVALID_DATA,
1669 "got %" G_GSIZE_FORMAT " bytes, expected "
1670 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1671 return FALSE;
1672 }
1673
1674 /* check matches */
1675 for (guint i = 0x0; i < bufsz1; i++) {
1676 if (buf1[i] != buf2[i]) {
1677 g_set_error (error,
1678 G_IO_ERROR,
1679 G_IO_ERROR_INVALID_DATA,
1680 "got 0x%02x, expected 0x%02x @ 0x%04x",
1681 buf1[i], buf2[i], i);
1682 return FALSE;
1683 }
1684 }
1685
1686 /* success */
1687 return TRUE;
1688}
Richard Hughes484ee292019-03-22 16:10:50 +00001689
1690/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001691 * fu_common_bytes_compare:
1692 * @bytes1: a #GBytes
1693 * @bytes2: another #GBytes
1694 * @error: A #GError or %NULL
1695 *
1696 * Compares the buffers for equality.
1697 *
1698 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001699 *
1700 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001701 **/
1702gboolean
1703fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1704{
1705 const guint8 *buf1;
1706 const guint8 *buf2;
1707 gsize bufsz1;
1708 gsize bufsz2;
1709
1710 g_return_val_if_fail (bytes1 != NULL, FALSE);
1711 g_return_val_if_fail (bytes2 != NULL, FALSE);
1712 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1713
1714 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1715 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1716 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1717}
1718
1719/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001720 * fu_common_bytes_pad:
1721 * @bytes: a #GBytes
1722 * @sz: the desired size in bytes
1723 *
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001724 * Pads a GBytes to a minimum @sz with `0xff`.
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001725 *
1726 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001727 *
1728 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001729 **/
1730GBytes *
1731fu_common_bytes_pad (GBytes *bytes, gsize sz)
1732{
1733 gsize bytes_sz;
1734
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001735 g_return_val_if_fail (bytes != NULL, NULL);
1736 g_return_val_if_fail (sz != 0, NULL);
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001737
1738 /* pad */
1739 bytes_sz = g_bytes_get_size (bytes);
1740 if (bytes_sz < sz) {
1741 const guint8 *data = g_bytes_get_data (bytes, NULL);
1742 guint8 *data_new = g_malloc (sz);
1743 memcpy (data_new, data, bytes_sz);
1744 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1745 return g_bytes_new_take (data_new, sz);
1746 }
1747
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001748 /* not required */
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001749 return g_bytes_ref (bytes);
1750}
1751
1752/**
Richard Hughes05e33772020-12-08 18:37:02 +00001753 * fu_common_bytes_new_offset:
1754 * @bytes: a #GBytes
1755 * @offset: where subsection starts at
1756 * @length: length of subsection
1757 * @error: A #GError or %NULL
1758 *
1759 * Creates a #GBytes which is a subsection of another #GBytes.
1760 *
1761 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1762 *
1763 * Since: 1.5.4
1764 **/
1765GBytes *
1766fu_common_bytes_new_offset (GBytes *bytes,
1767 gsize offset,
1768 gsize length,
1769 GError **error)
1770{
1771 g_return_val_if_fail (bytes != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001772 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes05e33772020-12-08 18:37:02 +00001773
1774 /* sanity check */
1775 if (offset + length > g_bytes_get_size (bytes)) {
1776 g_set_error (error,
1777 G_IO_ERROR,
1778 G_IO_ERROR_INVALID_DATA,
1779 "cannot create bytes @0x%02x for 0x%02x "
1780 "as buffer only 0x%04x bytes in size",
1781 (guint) offset,
1782 (guint) length,
1783 (guint) g_bytes_get_size (bytes));
1784 return NULL;
1785 }
1786 return g_bytes_new_from_bytes (bytes, offset, length);
1787}
1788
1789/**
Richard Hughes484ee292019-03-22 16:10:50 +00001790 * fu_common_realpath:
1791 * @filename: a filename
1792 * @error: A #GError or %NULL
1793 *
1794 * Finds the canonicalized absolute filename for a path.
1795 *
1796 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001797 *
1798 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001799 **/
1800gchar *
1801fu_common_realpath (const gchar *filename, GError **error)
1802{
1803 char full_tmp[PATH_MAX];
1804
1805 g_return_val_if_fail (filename != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001806 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes484ee292019-03-22 16:10:50 +00001807
Richard Hughes8694dee2019-11-22 09:16:34 +00001808#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001809 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001810#else
1811 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1812#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001813 g_set_error (error,
1814 G_IO_ERROR,
1815 G_IO_ERROR_INVALID_DATA,
1816 "cannot resolve path: %s",
1817 strerror (errno));
1818 return NULL;
1819 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001820 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1821 g_set_error (error,
1822 G_IO_ERROR,
1823 G_IO_ERROR_INVALID_DATA,
1824 "cannot find path: %s",
1825 full_tmp);
1826 return NULL;
1827 }
Richard Hughes484ee292019-03-22 16:10:50 +00001828 return g_strdup (full_tmp);
1829}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001830
1831/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001832 * fu_common_fnmatch:
1833 * @pattern: a glob pattern, e.g. `*foo*`
1834 * @str: a string to match against the pattern, e.g. `bazfoobar`
1835 *
1836 * Matches a string against a glob pattern.
1837 *
1838 * Return value: %TRUE if the string matched
1839 *
1840 * Since: 1.3.5
1841 **/
1842gboolean
1843fu_common_fnmatch (const gchar *pattern, const gchar *str)
1844{
1845 g_return_val_if_fail (pattern != NULL, FALSE);
1846 g_return_val_if_fail (str != NULL, FALSE);
1847#ifdef HAVE_FNMATCH_H
1848 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001849#elif _WIN32
1850 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1851 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1852 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001853#else
1854 return g_strcmp0 (pattern, str) == 0;
1855#endif
1856}
1857
Richard Hughesa84d7a72020-05-06 12:11:51 +01001858static gint
1859fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1860{
1861 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1862}
1863
1864/**
1865 * fu_common_filename_glob:
1866 * @directory: a directory path
1867 * @pattern: a glob pattern, e.g. `*foo*`
1868 * @error: A #GError or %NULL
1869 *
1870 * Returns all the filenames that match a specific glob pattern.
1871 * Any results are sorted. No matching files will set @error.
1872 *
1873 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1874 *
1875 * Since: 1.5.0
1876 **/
1877GPtrArray *
1878fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1879{
1880 const gchar *basename;
Richard Hughes6a489a92020-12-22 10:32:06 +00001881 g_autoptr(GDir) dir = NULL;
Richard Hughesa84d7a72020-05-06 12:11:51 +01001882 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +00001883
1884 g_return_val_if_fail (directory != NULL, NULL);
1885 g_return_val_if_fail (pattern != NULL, NULL);
1886 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1887
1888 dir = g_dir_open (directory, 0, error);
Richard Hughesa84d7a72020-05-06 12:11:51 +01001889 if (dir == NULL)
1890 return NULL;
1891 while ((basename = g_dir_read_name (dir)) != NULL) {
1892 if (!fu_common_fnmatch (pattern, basename))
1893 continue;
1894 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1895 }
1896 if (files->len == 0) {
1897 g_set_error_literal (error,
1898 G_IO_ERROR,
1899 G_IO_ERROR_NOT_FOUND,
1900 "no files matched pattern");
1901 return NULL;
1902 }
1903 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1904 return g_steal_pointer (&files);
1905}
1906
Richard Hughes5c508de2019-11-22 09:57:34 +00001907/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001908 * fu_common_strnsplit:
1909 * @str: a string to split
1910 * @sz: size of @str
1911 * @delimiter: a string which specifies the places at which to split the string
1912 * @max_tokens: the maximum number of pieces to split @str into
1913 *
1914 * Splits a string into a maximum of @max_tokens pieces, using the given
1915 * delimiter. If @max_tokens is reached, the remainder of string is appended
1916 * to the last token.
1917 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001918 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001919 *
1920 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001921 **/
1922gchar **
1923fu_common_strnsplit (const gchar *str, gsize sz,
1924 const gchar *delimiter, gint max_tokens)
1925{
1926 if (str[sz - 1] != '\0') {
1927 g_autofree gchar *str2 = g_strndup (str, sz);
1928 return g_strsplit (str2, delimiter, max_tokens);
1929 }
1930 return g_strsplit (str, delimiter, max_tokens);
1931}
Richard Hughes5308ea42019-08-09 12:25:13 +01001932
1933/**
Richard Hughes364e2682021-01-05 14:38:15 +00001934 * fu_common_strsafe:
1935 * @str: (nullable): a string to make safe for printing
1936 * @maxsz: maximum size of returned string
1937 *
1938 * Converts a string into something that can be safely printed.
1939 *
1940 * Return value: (transfer full): safe string, or %NULL if there was nothing valid
1941 *
1942 * Since: 1.5.5
1943 **/
1944gchar *
1945fu_common_strsafe (const gchar *str, gsize maxsz)
1946{
1947 gboolean valid = FALSE;
1948 g_autoptr(GString) tmp = NULL;
1949
Richard Hughes364e2682021-01-05 14:38:15 +00001950 /* sanity check */
Richard Hughesc123bee2021-02-08 16:47:05 +00001951 if (str == NULL || maxsz == 0)
Richard Hughes364e2682021-01-05 14:38:15 +00001952 return NULL;
1953
1954 /* replace non-printable chars with '.' */
Richard Hughesc123bee2021-02-08 16:47:05 +00001955 tmp = g_string_sized_new (maxsz);
Richard Hughesa4e0de42021-02-14 20:33:47 +00001956 for (gsize i = 0; i < maxsz && str[i] != '\0'; i++) {
Richard Hughes364e2682021-01-05 14:38:15 +00001957 if (!g_ascii_isprint (str[i])) {
1958 g_string_append_c (tmp, '.');
1959 continue;
1960 }
1961 g_string_append_c (tmp, str[i]);
Richard Hughesffbb1172021-02-14 20:34:31 +00001962 if (!g_ascii_isspace (str[i]))
1963 valid = TRUE;
Richard Hughes364e2682021-01-05 14:38:15 +00001964 }
1965
1966 /* if just junk, don't return 'all dots' */
1967 if (tmp->len == 0 || !valid)
1968 return NULL;
1969 return g_string_free (g_steal_pointer (&tmp), FALSE);
1970}
1971
Richard Hughese52e1b42021-01-26 09:59:15 +00001972
1973/**
1974 * fu_common_strjoin_array:
1975 * @separator: (nullable): string to insert between each of the strings, or %NULL
1976 * @array: (element-type utf8): A #GPtrArray
1977 *
1978 * Joins an array of strings together to form one long string, with the optional
1979 * separator inserted between each of them.
1980 *
1981 * If @array has no items, the return value will be an empty string.
1982 * If @array contains a single item, separator will not appear in the resulting
1983 * string.
1984 *
1985 * Returns: a string
1986 *
1987 * Since: 1.5.6
1988 **/
1989gchar *
1990fu_common_strjoin_array (const gchar *separator, GPtrArray *array)
1991{
1992 g_autofree const gchar **strv = NULL;
1993
1994 g_return_val_if_fail (array != NULL, NULL);
1995
1996 strv = g_new0 (const gchar *, array->len + 1);
1997 for (guint i = 0; i < array->len; i++)
1998 strv[i] = g_ptr_array_index (array, i);
1999 return g_strjoinv (separator, (gchar **) strv);
2000}
2001
Richard Hughes364e2682021-01-05 14:38:15 +00002002/**
Richard Hughes5308ea42019-08-09 12:25:13 +01002003 * fu_memcpy_safe:
2004 * @dst: destination buffer
2005 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
2006 * @dst_offset: offset in bytes into @dst to copy to
2007 * @src: source buffer
2008 * @src_sz: maximum size of @dst, typically `sizeof(src)`
2009 * @src_offset: offset in bytes into @src to copy from
2010 * @n: number of bytes to copy from @src+@offset from
2011 * @error: A #GError or %NULL
2012 *
2013 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
2014 * of both the destination and the source allows us to check for buffer overflow.
2015 *
2016 * Providing the buffer offsets also allows us to check reading past the end of
2017 * the source buffer. For this reason the caller should NEVER add an offset to
2018 * @src or @dst.
2019 *
2020 * You don't need to use this function in "obviously correct" cases, nor should
2021 * you use it when performance is a concern. Only us it when you're not sure if
2022 * malicious data from a device or firmware could cause memory corruption.
2023 *
2024 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002025 *
2026 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01002027 **/
2028gboolean
2029fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
2030 const guint8 *src, gsize src_sz, gsize src_offset,
2031 gsize n, GError **error)
2032{
Richard Hughes6a489a92020-12-22 10:32:06 +00002033 g_return_val_if_fail (dst != NULL, FALSE);
2034 g_return_val_if_fail (src != NULL, FALSE);
2035 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2036
Richard Hughes5308ea42019-08-09 12:25:13 +01002037 if (n == 0)
2038 return TRUE;
2039
2040 if (n > src_sz) {
2041 g_set_error (error,
2042 FWUPD_ERROR,
2043 FWUPD_ERROR_READ,
2044 "attempted to read 0x%02x bytes from buffer of 0x%02x",
2045 (guint) n, (guint) src_sz);
2046 return FALSE;
2047 }
2048 if (n + src_offset > src_sz) {
2049 g_set_error (error,
2050 FWUPD_ERROR,
2051 FWUPD_ERROR_READ,
2052 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
2053 (guint) n, (guint) src_offset, (guint) src_sz);
2054 return FALSE;
2055 }
2056 if (n > dst_sz) {
2057 g_set_error (error,
2058 FWUPD_ERROR,
2059 FWUPD_ERROR_WRITE,
2060 "attempted to write 0x%02x bytes to buffer of 0x%02x",
2061 (guint) n, (guint) dst_sz);
2062 return FALSE;
2063 }
2064 if (n + dst_offset > dst_sz) {
2065 g_set_error (error,
2066 FWUPD_ERROR,
2067 FWUPD_ERROR_WRITE,
2068 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
2069 (guint) n, (guint) dst_offset, (guint) dst_sz);
2070 return FALSE;
2071 }
2072
2073 /* phew! */
2074 memcpy (dst + dst_offset, src + src_offset, n);
2075 return TRUE;
2076}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002077
Richard Hughes80768f52019-10-22 07:19:14 +01002078/**
Richard Hughes9b11af92021-02-04 20:28:49 +00002079 * fu_memdup_safe:
2080 * @src: source buffer
2081 * @n: number of bytes to copy from @src
2082 * @error: A #GError or %NULL
2083 *
2084 * Duplicates some memory using memdup in a safe way.
2085 *
2086 * You don't need to use this function in "obviously correct" cases, nor should
2087 * you use it when performance is a concern. Only us it when you're not sure if
2088 * malicious data from a device or firmware could cause memory corruption.
2089 *
2090 * NOTE: This function intentionally limits allocation size to 1GB.
2091 *
2092 * Return value: (transfer full): block of allocated memory, or %NULL for an error.
2093 *
2094 * Since: 1.5.6
2095 **/
2096guint8 *
2097fu_memdup_safe (const guint8 *src, gsize n, GError **error)
2098{
Richard Hughes9b11af92021-02-04 20:28:49 +00002099 /* sanity check */
2100 if (n > 0x40000000) {
2101 g_set_error (error,
2102 G_IO_ERROR,
2103 G_IO_ERROR_NOT_SUPPORTED,
2104 "cannot allocate %uGB of memory",
2105 (guint) (n / 0x40000000));
2106 return NULL;
2107 }
2108
Richard Hughes5294d0c2021-02-05 09:50:02 +00002109#if GLIB_CHECK_VERSION(2,67,3)
Richard Hughes9b11af92021-02-04 20:28:49 +00002110 /* linear block of memory */
Richard Hughes5294d0c2021-02-05 09:50:02 +00002111 return g_memdup2 (src, n);
2112#else
2113 return g_memdup (src, (guint) n);
2114#endif
Richard Hughes9b11af92021-02-04 20:28:49 +00002115}
2116
2117/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01002118 * fu_common_read_uint8_safe:
2119 * @buf: source buffer
2120 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2121 * @offset: offset in bytes into @buf to copy from
2122 * @value: (out) (allow-none): the parsed value
2123 * @error: A #GError or %NULL
2124 *
2125 * Read a value from a buffer in a safe way.
2126 *
2127 * You don't need to use this function in "obviously correct" cases, nor should
2128 * you use it when performance is a concern. Only us it when you're not sure if
2129 * malicious data from a device or firmware could cause memory corruption.
2130 *
2131 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002132 *
2133 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01002134 **/
2135gboolean
2136fu_common_read_uint8_safe (const guint8 *buf,
2137 gsize bufsz,
2138 gsize offset,
2139 guint8 *value,
2140 GError **error)
2141{
2142 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00002143
2144 g_return_val_if_fail (buf != NULL, FALSE);
2145 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2146
Richard Hughesc21a0b92019-10-24 12:24:37 +01002147 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
2148 buf, bufsz, offset, /* src */
2149 sizeof(tmp), error))
2150 return FALSE;
2151 if (value != NULL)
2152 *value = tmp;
2153 return TRUE;
2154}
2155
2156/**
Richard Hughes80768f52019-10-22 07:19:14 +01002157 * fu_common_read_uint16_safe:
2158 * @buf: source buffer
2159 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2160 * @offset: offset in bytes into @buf to copy from
2161 * @value: (out) (allow-none): the parsed value
2162 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2163 * @error: A #GError or %NULL
2164 *
2165 * Read a value from a buffer using a specified endian in a safe way.
2166 *
2167 * You don't need to use this function in "obviously correct" cases, nor should
2168 * you use it when performance is a concern. Only us it when you're not sure if
2169 * malicious data from a device or firmware could cause memory corruption.
2170 *
2171 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002172 *
2173 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002174 **/
2175gboolean
2176fu_common_read_uint16_safe (const guint8 *buf,
2177 gsize bufsz,
2178 gsize offset,
2179 guint16 *value,
2180 FuEndianType endian,
2181 GError **error)
2182{
2183 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002184
2185 g_return_val_if_fail (buf != NULL, FALSE);
2186 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2187
Richard Hughes7d01ac92019-10-23 14:31:46 +01002188 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002189 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002190 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002191 return FALSE;
2192 if (value != NULL)
2193 *value = fu_common_read_uint16 (dst, endian);
2194 return TRUE;
2195}
2196
2197/**
2198 * fu_common_read_uint32_safe:
2199 * @buf: source buffer
2200 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2201 * @offset: offset in bytes into @buf to copy from
2202 * @value: (out) (allow-none): the parsed value
2203 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2204 * @error: A #GError or %NULL
2205 *
2206 * Read a value from a buffer using a specified endian in a safe way.
2207 *
2208 * You don't need to use this function in "obviously correct" cases, nor should
2209 * you use it when performance is a concern. Only us it when you're not sure if
2210 * malicious data from a device or firmware could cause memory corruption.
2211 *
2212 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002213 *
2214 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002215 **/
2216gboolean
2217fu_common_read_uint32_safe (const guint8 *buf,
2218 gsize bufsz,
2219 gsize offset,
2220 guint32 *value,
2221 FuEndianType endian,
2222 GError **error)
2223{
2224 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002225
2226 g_return_val_if_fail (buf != NULL, FALSE);
2227 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2228
Richard Hughes7d01ac92019-10-23 14:31:46 +01002229 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002230 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002231 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002232 return FALSE;
2233 if (value != NULL)
2234 *value = fu_common_read_uint32 (dst, endian);
2235 return TRUE;
2236}
2237
Mario Limonciello1a680f32019-11-25 19:44:53 -06002238/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002239 * fu_common_read_uint64_safe:
2240 * @buf: source buffer
2241 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2242 * @offset: offset in bytes into @buf to copy from
2243 * @value: (out) (allow-none): the parsed value
2244 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2245 * @error: A #GError or %NULL
2246 *
2247 * Read a value from a buffer using a specified endian in a safe way.
2248 *
2249 * You don't need to use this function in "obviously correct" cases, nor should
2250 * you use it when performance is a concern. Only us it when you're not sure if
2251 * malicious data from a device or firmware could cause memory corruption.
2252 *
2253 * Return value: %TRUE if @value was set, %FALSE otherwise
2254 *
2255 * Since: 1.5.8
2256 **/
2257gboolean
2258fu_common_read_uint64_safe (const guint8 *buf,
2259 gsize bufsz,
2260 gsize offset,
2261 guint64 *value,
2262 FuEndianType endian,
2263 GError **error)
2264{
2265 guint8 dst[8] = { 0x0 };
2266
2267 g_return_val_if_fail (buf != NULL, FALSE);
2268 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2269
2270 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
2271 buf, bufsz, offset, /* src */
2272 sizeof(dst), error))
2273 return FALSE;
2274 if (value != NULL)
2275 *value = fu_common_read_uint64 (dst, endian);
2276 return TRUE;
2277}
2278
2279/**
Richard Hughes32ffdb22021-03-04 17:38:12 +00002280 * fu_common_write_uint8_safe:
2281 * @buf: source buffer
2282 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2283 * @offset: offset in bytes into @buf to write to
2284 * @value: the value to write
2285 * @error: A #GError or %NULL
2286 *
2287 * Write a value to a buffer in a safe way.
2288 *
2289 * You don't need to use this function in "obviously correct" cases, nor should
2290 * you use it when performance is a concern. Only us it when you're not sure if
2291 * malicious data from a device or firmware could cause memory corruption.
2292 *
2293 * Return value: %TRUE if @value was written, %FALSE otherwise
2294 *
2295 * Since: 1.5.8
2296 **/
2297gboolean
2298fu_common_write_uint8_safe (guint8 *buf,
2299 gsize bufsz,
2300 gsize offset,
2301 guint8 value,
2302 GError **error)
2303{
2304 g_return_val_if_fail (buf != NULL, FALSE);
2305 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2306
2307 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2308 &value, sizeof(value), 0x0, /* src */
2309 sizeof(value), error);
2310}
2311
2312/**
2313 * fu_common_write_uint16_safe:
2314 * @buf: source buffer
2315 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2316 * @offset: offset in bytes into @buf to write to
2317 * @value: the value to write
2318 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2319 * @error: A #GError or %NULL
2320 *
2321 * Write a value to a buffer using a specified endian in a safe way.
2322 *
2323 * You don't need to use this function in "obviously correct" cases, nor should
2324 * you use it when performance is a concern. Only us it when you're not sure if
2325 * malicious data from a device or firmware could cause memory corruption.
2326 *
2327 * Return value: %TRUE if @value was written, %FALSE otherwise
2328 *
2329 * Since: 1.5.8
2330 **/
2331gboolean
2332fu_common_write_uint16_safe (guint8 *buf,
2333 gsize bufsz,
2334 gsize offset,
2335 guint16 value,
2336 FuEndianType endian,
2337 GError **error)
2338{
2339 guint8 tmp[2] = { 0x0 };
2340
2341 g_return_val_if_fail (buf != NULL, FALSE);
2342 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2343
2344 fu_common_write_uint16 (tmp, value, endian);
2345 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2346 tmp, sizeof(tmp), 0x0, /* src */
2347 sizeof(tmp), error);
2348}
2349
2350/**
2351 * fu_common_write_uint32_safe:
2352 * @buf: source buffer
2353 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2354 * @offset: offset in bytes into @buf to write to
2355 * @value: the value to write
2356 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2357 * @error: A #GError or %NULL
2358 *
2359 * Write a value to a buffer using a specified endian in a safe way.
2360 *
2361 * You don't need to use this function in "obviously correct" cases, nor should
2362 * you use it when performance is a concern. Only us it when you're not sure if
2363 * malicious data from a device or firmware could cause memory corruption.
2364 *
2365 * Return value: %TRUE if @value was written, %FALSE otherwise
2366 *
2367 * Since: 1.5.8
2368 **/
2369gboolean
2370fu_common_write_uint32_safe (guint8 *buf,
2371 gsize bufsz,
2372 gsize offset,
2373 guint32 value,
2374 FuEndianType endian,
2375 GError **error)
2376{
2377 guint8 tmp[4] = { 0x0 };
2378
2379 g_return_val_if_fail (buf != NULL, FALSE);
2380 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2381
2382 fu_common_write_uint32 (tmp, value, endian);
2383 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2384 tmp, sizeof(tmp), 0x0, /* src */
2385 sizeof(tmp), error);
2386}
2387
2388/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002389 * fu_common_write_uint64_safe:
2390 * @buf: source buffer
2391 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2392 * @offset: offset in bytes into @buf to write to
2393 * @value: the value to write
2394 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2395 * @error: A #GError or %NULL
2396 *
2397 * Write a value to a buffer using a specified endian in a safe way.
2398 *
2399 * You don't need to use this function in "obviously correct" cases, nor should
2400 * you use it when performance is a concern. Only us it when you're not sure if
2401 * malicious data from a device or firmware could cause memory corruption.
2402 *
2403 * Return value: %TRUE if @value was written, %FALSE otherwise
2404 *
2405 * Since: 1.5.8
2406 **/
2407gboolean
2408fu_common_write_uint64_safe (guint8 *buf,
2409 gsize bufsz,
2410 gsize offset,
2411 guint64 value,
2412 FuEndianType endian,
2413 GError **error)
2414{
2415 guint8 tmp[8] = { 0x0 };
2416
2417 g_return_val_if_fail (buf != NULL, FALSE);
2418 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2419
2420 fu_common_write_uint64 (tmp, value, endian);
2421 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2422 tmp, sizeof(tmp), 0x0, /* src */
2423 sizeof(tmp), error);
2424}
2425
2426/**
Mario Limonciello1a680f32019-11-25 19:44:53 -06002427 * fu_byte_array_append_uint8:
2428 * @array: A #GByteArray
2429 * @data: #guint8
2430 *
2431 * Adds a 8 bit integer to a byte array
2432 *
2433 * Since: 1.3.1
2434 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002435void
2436fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2437{
2438 g_byte_array_append (array, &data, sizeof(data));
2439}
2440
Mario Limonciello1a680f32019-11-25 19:44:53 -06002441/**
2442 * fu_byte_array_append_uint16:
2443 * @array: A #GByteArray
2444 * @data: #guint16
2445 * @endian: #FuEndianType
2446 *
2447 * Adds a 16 bit integer to a byte array
2448 *
2449 * Since: 1.3.1
2450 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002451void
2452fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2453{
2454 guint8 buf[2];
2455 fu_common_write_uint16 (buf, data, endian);
2456 g_byte_array_append (array, buf, sizeof(buf));
2457}
2458
Mario Limonciello1a680f32019-11-25 19:44:53 -06002459/**
2460 * fu_byte_array_append_uint32:
2461 * @array: A #GByteArray
2462 * @data: #guint32
2463 * @endian: #FuEndianType
2464 *
2465 * Adds a 32 bit integer to a byte array
2466 *
2467 * Since: 1.3.1
2468 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002469void
2470fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2471{
2472 guint8 buf[4];
2473 fu_common_write_uint32 (buf, data, endian);
2474 g_byte_array_append (array, buf, sizeof(buf));
2475}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002476
2477/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002478 * fu_byte_array_append_uint64:
2479 * @array: A #GByteArray
2480 * @data: #guint64
2481 * @endian: #FuEndianType
2482 *
2483 * Adds a 64 bit integer to a byte array
2484 *
2485 * Since: 1.5.8
2486 **/
2487void
2488fu_byte_array_append_uint64 (GByteArray *array, guint64 data, FuEndianType endian)
2489{
2490 guint8 buf[8];
2491 fu_common_write_uint64 (buf, data, endian);
2492 g_byte_array_append (array, buf, sizeof(buf));
2493}
2494
2495/**
Richard Hughese9664e82021-03-10 13:38:06 +00002496 * fu_byte_array_append_bytes:
2497 * @array: A #GByteArray
2498 * @bytes: A #GBytes
2499 *
2500 * Adds the contents of a GBytes to a byte array
2501 *
2502 * Since: 1.5.8
2503 **/
2504void
2505fu_byte_array_append_bytes (GByteArray *array, GBytes *bytes)
2506{
2507 g_byte_array_append (array,
2508 g_bytes_get_data (bytes, NULL),
2509 g_bytes_get_size (bytes));
2510}
2511
2512/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002513 * fu_byte_array_set_size:
2514 * @array: a #GByteArray
2515 * @length: the new size of the GByteArray
2516 *
2517 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2518 *
2519 * Since: 1.5.0
2520 **/
2521void
2522fu_byte_array_set_size (GByteArray *array, guint length)
2523{
2524 guint oldlength = array->len;
2525 g_byte_array_set_size (array, length);
2526 if (length > oldlength)
2527 memset (array->data + oldlength, 0x0, length - oldlength);
2528}
2529
2530/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002531 * fu_common_kernel_locked_down:
2532 *
2533 * Determines if kernel lockdown in effect
2534 *
2535 * Since: 1.3.8
2536 **/
2537gboolean
2538fu_common_kernel_locked_down (void)
2539{
2540#ifndef _WIN32
2541 gsize len = 0;
2542 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2543 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2544 g_autofree gchar *data = NULL;
2545 g_auto(GStrv) options = NULL;
2546
2547 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2548 return FALSE;
2549 if (!g_file_get_contents (fname, &data, &len, NULL))
2550 return FALSE;
2551 if (len < 1)
2552 return FALSE;
2553 options = g_strsplit (data, " ", -1);
2554 for (guint i = 0; options[i] != NULL; i++) {
2555 if (g_strcmp0 (options[i], "[none]") == 0)
2556 return FALSE;
2557 }
2558 return TRUE;
2559#else
2560 return FALSE;
2561#endif
2562}
Richard Hughes9223c892020-05-09 20:32:08 +01002563
2564/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002565 * fu_common_cpuid:
2566 * @leaf: The CPUID level, now called the 'leaf' by Intel
2567 * @eax: (out) (nullable): EAX register
2568 * @ebx: (out) (nullable): EBX register
2569 * @ecx: (out) (nullable): ECX register
2570 * @edx: (out) (nullable): EDX register
2571 * @error: A #GError or NULL
2572 *
2573 * Calls CPUID and returns the registers for the given leaf.
2574 *
2575 * Return value: %TRUE if the registers are set.
2576 *
2577 * Since: 1.5.0
2578 **/
2579gboolean
2580fu_common_cpuid (guint32 leaf,
2581 guint32 *eax,
2582 guint32 *ebx,
2583 guint32 *ecx,
2584 guint32 *edx,
2585 GError **error)
2586{
2587#ifdef HAVE_CPUID_H
2588 guint eax_tmp = 0;
2589 guint ebx_tmp = 0;
2590 guint ecx_tmp = 0;
2591 guint edx_tmp = 0;
2592
Richard Hughes6a489a92020-12-22 10:32:06 +00002593 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2594
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002595 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002596 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002597 if (eax != NULL)
2598 *eax = eax_tmp;
2599 if (ebx != NULL)
2600 *ebx = ebx_tmp;
2601 if (ecx != NULL)
2602 *ecx = ecx_tmp;
2603 if (edx != NULL)
2604 *edx = edx_tmp;
2605 return TRUE;
2606#else
2607 g_set_error_literal (error,
2608 G_IO_ERROR,
2609 G_IO_ERROR_NOT_SUPPORTED,
2610 "no <cpuid.h> support");
2611 return FALSE;
2612#endif
2613}
2614
2615/**
Richard Hughesb63cfa92021-01-05 22:57:12 +00002616 * fu_common_get_cpu_vendor:
2617 *
2618 * Uses CPUID to discover the CPU vendor.
2619 *
2620 * Return value: a #FuCpuVendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD.
2621 *
2622 * Since: 1.5.5
2623 **/
2624FuCpuVendor
2625fu_common_get_cpu_vendor (void)
2626{
2627#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002628 guint ebx = 0;
2629 guint ecx = 0;
2630 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002631
Richard Hughesb63cfa92021-01-05 22:57:12 +00002632 if (fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL)) {
2633 if (ebx == signature_INTEL_ebx &&
2634 edx == signature_INTEL_edx &&
2635 ecx == signature_INTEL_ecx) {
2636 return FU_CPU_VENDOR_INTEL;
2637 }
2638 if (ebx == signature_AMD_ebx &&
2639 edx == signature_AMD_edx &&
2640 ecx == signature_AMD_ecx) {
2641 return FU_CPU_VENDOR_AMD;
2642 }
Richard Hughes9223c892020-05-09 20:32:08 +01002643 }
Richard Hughesbd444322020-05-21 12:05:03 +01002644#endif
Richard Hughesb63cfa92021-01-05 22:57:12 +00002645
2646 /* failed */
2647 return FU_CPU_VENDOR_UNKNOWN;
Richard Hughes9223c892020-05-09 20:32:08 +01002648}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002649
Richard Hughes36111472020-08-12 15:04:24 +01002650/**
2651 * fu_common_is_live_media:
2652 *
2653 * Checks if the user is running from a live media using various heuristics.
2654 *
2655 * Returns: %TRUE if live
2656 *
2657 * Since: 1.4.6
2658 **/
2659gboolean
2660fu_common_is_live_media (void)
2661{
2662 gsize bufsz = 0;
2663 g_autofree gchar *buf = NULL;
2664 g_auto(GStrv) tokens = NULL;
2665 const gchar *args[] = {
2666 "rd.live.image",
2667 "boot=live",
2668 NULL, /* last entry */
2669 };
2670 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2671 return TRUE;
2672 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2673 return FALSE;
2674 if (bufsz == 0)
2675 return FALSE;
2676 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2677 for (guint i = 0; args[i] != NULL; i++) {
2678 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2679 return TRUE;
2680 }
2681 return FALSE;
2682}
2683
Richard Hughes68175e92021-01-14 09:43:33 +00002684/**
2685 * fu_common_get_memory_size:
2686 *
2687 * Returns the size of physical memory.
2688 *
2689 * Returns: bytes
2690 *
2691 * Since: 1.5.6
2692 **/
2693guint64
2694fu_common_get_memory_size (void)
2695{
2696#ifdef _WIN32
2697 MEMORYSTATUSEX status;
2698 status.dwLength = sizeof(status);
2699 GlobalMemoryStatusEx (&status);
2700 return (guint64) status.ullTotalPhys;
2701#else
2702 return sysconf (_SC_PHYS_PAGES) * sysconf (_SC_PAGE_SIZE);
2703#endif
2704}
2705
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002706static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002707fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002708{
2709 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002710 const gchar *obj;
2711 g_autoptr(GVariant) output = NULL;
2712 g_autoptr(GDBusProxy) proxy = NULL;
2713 g_autoptr(GPtrArray) devices = NULL;
2714 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002715 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002716
Richard Hughes43417b22020-10-30 14:46:16 +00002717 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2718 if (connection == NULL) {
2719 g_prefix_error (error, "failed to get system bus: ");
2720 return NULL;
2721 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002722 proxy = g_dbus_proxy_new_sync (connection,
2723 G_DBUS_PROXY_FLAGS_NONE, NULL,
2724 UDISKS_DBUS_SERVICE,
2725 UDISKS_DBUS_PATH,
2726 UDISKS_DBUS_MANAGER_INTERFACE,
2727 NULL, error);
2728 if (proxy == NULL) {
2729 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2730 return NULL;
2731 }
2732 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002733 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002734 "GetBlockDevices",
2735 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002736 G_DBUS_CALL_FLAGS_NONE,
2737 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002738 if (output == NULL) {
2739 if (error != NULL)
2740 g_dbus_error_strip_remote_error (*error);
2741 g_prefix_error (error, "failed to call %s.%s(): ",
2742 UDISKS_DBUS_SERVICE,
2743 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002744 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002745 }
Richard Hughes43417b22020-10-30 14:46:16 +00002746 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002747 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002748 while (g_variant_iter_next (iter, "&o", &obj)) {
2749 g_autoptr(GDBusProxy) proxy_blk = NULL;
2750 proxy_blk = g_dbus_proxy_new_sync (connection,
2751 G_DBUS_PROXY_FLAGS_NONE, NULL,
2752 UDISKS_DBUS_SERVICE,
2753 obj,
2754 UDISKS_DBUS_INTERFACE_BLOCK,
2755 NULL, error);
2756 if (proxy_blk == NULL) {
2757 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2758 return NULL;
2759 }
2760 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2761 }
2762
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002763
2764 return g_steal_pointer (&devices);
2765}
2766
Richard Hughesad3cfc02021-02-22 10:02:54 +00002767static const gchar *
2768fu_common_convert_to_gpt_type (const gchar *type)
2769{
2770 struct {
2771 const gchar *mbr;
2772 const gchar *gpt;
2773 } typeguids[] = {
2774 { "0xef", "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" }, /* esp */
2775 { "0x0b", "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" }, /* fat32 */
2776 { NULL, NULL }
2777 };
2778 for (guint i = 0; typeguids[i].mbr != NULL; i++) {
2779 if (g_strcmp0 (type, typeguids[i].mbr) == 0)
2780 return typeguids[i].gpt;
2781 }
2782 return type;
2783}
2784
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002785/**
2786 * fu_common_get_volumes_by_kind:
2787 * @kind: A volume kind, typically a GUID
2788 * @error: A #GError or NULL
2789 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002790 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002791 *
2792 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2793 *
2794 * Since: 1.4.6
2795 **/
2796GPtrArray *
2797fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2798{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002799 g_autoptr(GPtrArray) devices = NULL;
2800 g_autoptr(GPtrArray) volumes = NULL;
2801
Richard Hughes6a489a92020-12-22 10:32:06 +00002802 g_return_val_if_fail (kind != NULL, NULL);
2803 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2804
Richard Hughes43417b22020-10-30 14:46:16 +00002805 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002806 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002807 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002808 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2809 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002810 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002811 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002812 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002813 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002814 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002815 g_autoptr(GVariant) val = NULL;
2816
Richard Hughes43417b22020-10-30 14:46:16 +00002817 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002818 G_DBUS_PROXY_FLAGS_NONE, NULL,
2819 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002820 g_dbus_proxy_get_object_path (proxy_blk),
2821 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002822 NULL, error);
2823 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002824 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2825 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002826 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002827 }
2828 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2829 if (val == NULL)
2830 continue;
2831
Richard Hughesdb344d52020-09-09 19:42:27 +01002832 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002833 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2834 G_DBUS_PROXY_FLAGS_NONE, NULL,
2835 UDISKS_DBUS_SERVICE,
2836 g_dbus_proxy_get_object_path (proxy_blk),
2837 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2838 NULL, error);
2839 if (proxy_fs == NULL) {
2840 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2841 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002842 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002843 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002844 vol = g_object_new (FU_TYPE_VOLUME,
2845 "proxy-block", proxy_blk,
2846 "proxy-filesystem", proxy_fs,
2847 NULL);
Richard Hughesad3cfc02021-02-22 10:02:54 +00002848
2849 /* convert MBR type to GPT type */
2850 type_str = fu_common_convert_to_gpt_type (type_str);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002851 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2852 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2853 fu_volume_is_internal (vol),
2854 fu_volume_get_id_type (vol));
2855 if (g_strcmp0 (type_str, kind) != 0)
2856 continue;
2857 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002858 }
2859 if (volumes->len == 0) {
2860 g_set_error (error,
2861 G_IO_ERROR,
2862 G_IO_ERROR_NOT_FOUND,
2863 "no volumes of type %s", kind);
2864 return NULL;
2865 }
2866 return g_steal_pointer (&volumes);
2867}
2868
2869/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002870 * fu_common_get_volume_by_device:
2871 * @device: A device string, typcically starting with `/dev/`
2872 * @error: A #GError or NULL
2873 *
2874 * Finds the first volume from the specified device.
2875 *
2876 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2877 *
2878 * Since: 1.5.1
2879 **/
2880FuVolume *
2881fu_common_get_volume_by_device (const gchar *device, GError **error)
2882{
2883 g_autoptr(GPtrArray) devices = NULL;
2884
Richard Hughes6a489a92020-12-22 10:32:06 +00002885 g_return_val_if_fail (device != NULL, NULL);
2886 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2887
Richard Hughes0bdf5612020-10-30 14:56:22 +00002888 /* find matching block device */
2889 devices = fu_common_get_block_devices (error);
2890 if (devices == NULL)
2891 return NULL;
2892 for (guint i = 0; i < devices->len; i++) {
2893 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2894 g_autoptr(GVariant) val = NULL;
2895 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2896 if (val == NULL)
2897 continue;
2898 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2899 return g_object_new (FU_TYPE_VOLUME,
2900 "proxy-block", proxy_blk,
2901 NULL);
2902 }
2903 }
2904
2905 /* failed */
2906 g_set_error (error,
2907 G_IO_ERROR,
2908 G_IO_ERROR_NOT_FOUND,
2909 "no volumes for device %s",
2910 device);
2911 return NULL;
2912}
2913
2914/**
2915 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002916 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002917 * @error: A #GError or NULL
2918 *
2919 * Finds the first volume from the specified device.
2920 *
2921 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2922 *
2923 * Since: 1.5.1
2924 **/
2925FuVolume *
2926fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2927{
2928 g_autoptr(GPtrArray) devices = NULL;
2929
Richard Hughes6a489a92020-12-22 10:32:06 +00002930 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2931
Richard Hughes0bdf5612020-10-30 14:56:22 +00002932 /* find matching block device */
2933 devices = fu_common_get_block_devices (error);
2934 if (devices == NULL)
2935 return NULL;
2936 for (guint i = 0; i < devices->len; i++) {
2937 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2938 g_autoptr(GVariant) val = NULL;
2939 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2940 if (val == NULL)
2941 continue;
2942 if (devnum == g_variant_get_uint64 (val)) {
2943 return g_object_new (FU_TYPE_VOLUME,
2944 "proxy-block", proxy_blk,
2945 NULL);
2946 }
2947 }
2948
2949 /* failed */
2950 g_set_error (error,
2951 G_IO_ERROR,
2952 G_IO_ERROR_NOT_FOUND,
2953 "no volumes for devnum %u",
2954 devnum);
2955 return NULL;
2956}
2957
2958/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002959 * fu_common_get_esp_default:
2960 * @error: A #GError or NULL
2961 *
2962 * Gets the platform default ESP
2963 *
2964 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2965 *
2966 * Since: 1.4.6
2967 **/
2968FuVolume *
2969fu_common_get_esp_default (GError **error)
2970{
2971 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002972 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002973 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2974 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002975 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002976 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002977 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002978
Richard Hughes6a489a92020-12-22 10:32:06 +00002979 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2980
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002981 /* for the test suite use local directory for ESP */
2982 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2983 if (path_tmp != NULL)
2984 return fu_volume_new_from_mount_path (path_tmp);
2985
Mario Limonciello56d816a2020-11-11 16:59:30 -06002986 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2987 if (volumes == NULL) {
2988 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2989 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2990 if (volumes == NULL) {
2991 g_prefix_error (error, "%s: ", error_local->message);
2992 return NULL;
2993 }
2994 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002995
2996 /* are there _any_ internal vfat partitions?
2997 * remember HintSystem is just that -- a hint! */
2998 for (guint i = 0; i < volumes->len; i++) {
2999 FuVolume *vol = g_ptr_array_index (volumes, i);
3000 g_autofree gchar *type = fu_volume_get_id_type (vol);
3001 if (g_strcmp0 (type, "vfat") == 0 &&
3002 fu_volume_is_internal (vol)) {
3003 has_internal = TRUE;
3004 break;
3005 }
3006 }
3007
3008 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003009 for (guint i = 0; i < volumes->len; i++) {
3010 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06003011 g_autofree gchar *type = fu_volume_get_id_type (vol);
3012 if (type == NULL)
3013 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00003014 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06003015 continue;
3016 if (g_strcmp0 (type, "vfat") == 0)
3017 g_ptr_array_add (volumes_vfat, vol);
3018 }
3019 if (volumes_vfat->len == 0) {
3020 g_set_error (error,
3021 G_IO_ERROR,
3022 G_IO_ERROR_INVALID_FILENAME,
3023 "No ESP found");
3024 return NULL;
3025 }
3026 for (guint i = 0; i < volumes_vfat->len; i++) {
3027 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003028 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
3029 }
3030 if (volumes_mtab->len == 1) {
3031 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
3032 return g_object_ref (vol);
3033 }
3034 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
3035 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
3036 return g_object_ref (vol);
3037 }
3038 g_set_error (error,
3039 G_IO_ERROR,
3040 G_IO_ERROR_INVALID_FILENAME,
3041 "More than one available ESP");
3042 return NULL;
3043}
3044
3045/**
3046 * fu_common_get_esp_for_path:
3047 * @esp_path: A path to the ESP
3048 * @error: A #GError or NULL
3049 *
3050 * Gets the platform ESP using a UNIX or UDisks path
3051 *
3052 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
3053 *
3054 * Since: 1.4.6
3055 **/
3056FuVolume *
3057fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
3058{
Richard Hughes6a489a92020-12-22 10:32:06 +00003059 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003060 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciellobe220a42021-02-10 10:39:58 -06003061 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003062
Richard Hughes6a489a92020-12-22 10:32:06 +00003063 g_return_val_if_fail (esp_path != NULL, NULL);
3064 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3065
Mario Limonciellobe220a42021-02-10 10:39:58 -06003066 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
3067 if (volumes == NULL) {
3068 /* check if it's a valid directory already */
3069 if (g_file_test (esp_path, G_FILE_TEST_IS_DIR))
3070 return fu_volume_new_from_mount_path (esp_path);
3071 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughesb81140d2020-08-17 14:47:17 +01003072 return NULL;
Mario Limonciellobe220a42021-02-10 10:39:58 -06003073 }
Richard Hughes6a489a92020-12-22 10:32:06 +00003074 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003075 for (guint i = 0; i < volumes->len; i++) {
3076 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05003077 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003078 if (g_strcmp0 (basename, vol_basename) == 0)
3079 return g_object_ref (vol);
3080 }
3081 g_set_error (error,
3082 G_IO_ERROR,
3083 G_IO_ERROR_INVALID_FILENAME,
3084 "No ESP with path %s",
3085 esp_path);
3086 return NULL;
3087}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01003088
3089/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01003090 * fu_common_crc8:
3091 * @buf: memory buffer
3092 * @bufsz: sizeof buf
3093 *
3094 * Returns the cyclic redundancy check value for the given memory buffer.
3095 *
3096 * Returns: CRC value
3097 *
3098 * Since: 1.5.0
3099 **/
3100guint8
3101fu_common_crc8 (const guint8 *buf, gsize bufsz)
3102{
3103 guint32 crc = 0;
3104 for (gsize j = bufsz; j > 0; j--) {
3105 crc ^= (*(buf++) << 8);
3106 for (guint32 i = 8; i; i--) {
3107 if (crc & 0x8000)
3108 crc ^= (0x1070 << 3);
3109 crc <<= 1;
3110 }
3111 }
3112 return ~((guint8) (crc >> 8));
3113}
3114
3115/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01003116 * fu_common_crc16:
3117 * @buf: memory buffer
3118 * @bufsz: sizeof buf
3119 *
3120 * Returns the cyclic redundancy check value for the given memory buffer.
3121 *
3122 * Returns: CRC value
3123 *
3124 * Since: 1.5.0
3125 **/
3126guint16
3127fu_common_crc16 (const guint8 *buf, gsize bufsz)
3128{
3129 guint16 crc = 0xffff;
3130 for (gsize len = bufsz; len > 0; len--) {
3131 crc = (guint16) (crc ^ (*buf++));
3132 for (guint8 i = 0; i < 8; i++) {
3133 if (crc & 0x1) {
3134 crc = (crc >> 1) ^ 0xa001;
3135 } else {
3136 crc >>= 1;
3137 }
3138 }
3139 }
3140 return ~crc;
3141}
3142
3143/**
3144 * fu_common_crc32_full:
3145 * @buf: memory buffer
3146 * @bufsz: sizeof buf
3147 * @crc: initial CRC value, typically 0xFFFFFFFF
3148 * @polynomial: CRC polynomial, typically 0xEDB88320
3149 *
3150 * Returns the cyclic redundancy check value for the given memory buffer.
3151 *
3152 * Returns: CRC value
3153 *
3154 * Since: 1.5.0
3155 **/
3156guint32
3157fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
3158{
3159 for (guint32 idx = 0; idx < bufsz; idx++) {
3160 guint8 data = *buf++;
3161 crc = crc ^ data;
3162 for (guint32 bit = 0; bit < 8; bit++) {
3163 guint32 mask = -(crc & 1);
3164 crc = (crc >> 1) ^ (polynomial & mask);
3165 }
3166 }
3167 return ~crc;
3168}
3169
3170/**
3171 * fu_common_crc32:
3172 * @buf: memory buffer
3173 * @bufsz: sizeof buf
3174 *
3175 * Returns the cyclic redundancy check value for the given memory buffer.
3176 *
3177 * Returns: CRC value
3178 *
3179 * Since: 1.5.0
3180 **/
3181guint32
3182fu_common_crc32 (const guint8 *buf, gsize bufsz)
3183{
3184 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
3185}
Richard Hughesc7546672021-01-28 10:53:30 +00003186
3187/**
3188 * fu_common_uri_get_scheme:
3189 * @uri: valid URI, e.g. `https://foo.bar/baz`
3190 *
3191 * Returns the USI scheme for the given URI.
3192 *
3193 * Returns: scheme value, or %NULL if invalid, e.g. `https`
3194 *
3195 * Since: 1.5.6
3196 **/
3197gchar *
3198fu_common_uri_get_scheme (const gchar *uri)
3199{
3200 gchar *tmp;
3201
3202 g_return_val_if_fail (uri != NULL, NULL);
3203
3204 tmp = g_strstr_len (uri, -1, ":");
3205 if (tmp == NULL || tmp[0] == '\0')
3206 return NULL;
3207 return g_utf8_strdown (uri, tmp - uri);
3208}
Richard Hughes58d52ed2021-03-10 13:42:59 +00003209
3210/**
3211 * fu_common_align_up:
3212 * @value: value to align
3213 * @alignment: align to this power of 2
3214 *
3215 * Align a value to a power of 2 boundary, where @alignment is the bit position
3216 * to align to. If @alignment is zero then @value is always returned unchanged.
3217 *
3218 * Returns: aligned value, which will be the same as @value if already aligned,
3219 * or %G_MAXSIZE if the value would overflow
3220 *
3221 * Since: 1.6.0
3222 **/
3223gsize
3224fu_common_align_up (gsize value, guint8 alignment)
3225{
3226 gsize value_new;
3227 guint32 mask = 1 << alignment;
3228
3229 /* no alignment required */
3230 if ((value & (mask - 1)) == 0)
3231 return value;
3232
3233 /* increment up to the next alignment value */
3234 value_new = value + mask;
3235 value_new &= ~(mask - 1);
3236
3237 /* overflow */
3238 if (value_new < value)
3239 return G_MAXSIZE;
3240
3241 /* success */
3242 return value_new;
3243}