blob: 643192fe2e6433cd1d5db2231190aea0d8e0d112 [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 Hughes119d2602021-03-17 10:07:38 +000043#include "fu-firmware.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010044#include "fu-volume-private.h"
45
Richard Hughes43417b22020-10-30 14:46:16 +000046#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
47#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
48#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
49#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
50#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
51#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010052
53/**
Richard Hughes4eada342017-10-03 21:20:32 +010054 * SECTION:fu-common
55 * @short_description: common functionality for plugins to use
56 *
57 * Helper functions that can be used by the daemon and plugins.
58 *
59 * See also: #FuPlugin
60 */
61
62/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010063 * fu_common_rmtree:
64 * @directory: a directory name
65 * @error: A #GError or %NULL
66 *
67 * Recursively removes a directory.
68 *
69 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060070 *
71 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010072 **/
73gboolean
74fu_common_rmtree (const gchar *directory, GError **error)
75{
76 const gchar *filename;
77 g_autoptr(GDir) dir = NULL;
78
Richard Hughes6a489a92020-12-22 10:32:06 +000079 g_return_val_if_fail (directory != NULL, FALSE);
80 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
81
Richard Hughes954dd9f2017-08-08 13:36:25 +010082 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010083 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010084 dir = g_dir_open (directory, 0, error);
85 if (dir == NULL)
86 return FALSE;
87
88 /* find each */
89 while ((filename = g_dir_read_name (dir))) {
90 g_autofree gchar *src = NULL;
91 src = g_build_filename (directory, filename, NULL);
92 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
93 if (!fu_common_rmtree (src, error))
94 return FALSE;
95 } else {
96 if (g_unlink (src) != 0) {
97 g_set_error (error,
98 FWUPD_ERROR,
99 FWUPD_ERROR_INTERNAL,
100 "Failed to delete: %s", src);
101 return FALSE;
102 }
103 }
104 }
105 if (g_remove (directory) != 0) {
106 g_set_error (error,
107 FWUPD_ERROR,
108 FWUPD_ERROR_INTERNAL,
109 "Failed to delete: %s", directory);
110 return FALSE;
111 }
112 return TRUE;
113}
114
Richard Hughes89e968b2018-03-07 10:01:08 +0000115static gboolean
116fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
117{
118 const gchar *filename;
119 g_autoptr(GDir) dir = NULL;
120
121 /* try to open */
122 dir = g_dir_open (directory, 0, error);
123 if (dir == NULL)
124 return FALSE;
125
126 /* find each */
127 while ((filename = g_dir_read_name (dir))) {
128 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
129 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
130 if (!fu_common_get_file_list_internal (files, src, error))
131 return FALSE;
132 } else {
133 g_ptr_array_add (files, g_steal_pointer (&src));
134 }
135 }
136 return TRUE;
137
138}
139
140/**
141 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100142 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000143 * @error: A #GError or %NULL
144 *
145 * Returns every file found under @directory, and any subdirectory.
146 * If any path under @directory cannot be accessed due to permissions an error
147 * will be returned.
148 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000149 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600150 *
151 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000152 **/
153GPtrArray *
154fu_common_get_files_recursive (const gchar *path, GError **error)
155{
156 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +0000157
158 g_return_val_if_fail (path != NULL, NULL);
159 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
160
Richard Hughes89e968b2018-03-07 10:01:08 +0000161 if (!fu_common_get_file_list_internal (files, path, error))
162 return NULL;
163 return g_steal_pointer (&files);
164}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100165/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100166 * fu_common_mkdir_parent:
167 * @filename: A full pathname
168 * @error: A #GError, or %NULL
169 *
170 * Creates any required directories, including any parent directories.
171 *
172 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600173 *
174 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100175 **/
176gboolean
177fu_common_mkdir_parent (const gchar *filename, GError **error)
178{
179 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100180
Richard Hughes6a489a92020-12-22 10:32:06 +0000181 g_return_val_if_fail (filename != NULL, FALSE);
182 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
183
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100184 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500185 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
186 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100187 if (g_mkdir_with_parents (parent, 0755) == -1) {
188 g_set_error (error,
189 FWUPD_ERROR,
190 FWUPD_ERROR_INTERNAL,
191 "Failed to create '%s': %s",
192 parent, g_strerror (errno));
193 return FALSE;
194 }
195 return TRUE;
196}
197
198/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100199 * fu_common_set_contents_bytes:
200 * @filename: A filename
201 * @bytes: The data to write
202 * @error: A #GError, or %NULL
203 *
204 * Writes a blob of data to a filename, creating the parent directories as
205 * required.
206 *
207 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600208 *
209 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100210 **/
211gboolean
212fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
213{
214 const gchar *data;
215 gsize size;
216 g_autoptr(GFile) file = NULL;
217 g_autoptr(GFile) file_parent = NULL;
218
Richard Hughes6a489a92020-12-22 10:32:06 +0000219 g_return_val_if_fail (filename != NULL, FALSE);
220 g_return_val_if_fail (bytes != NULL, FALSE);
221 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
222
Richard Hughes943d2c92017-06-21 09:04:39 +0100223 file = g_file_new_for_path (filename);
224 file_parent = g_file_get_parent (file);
225 if (!g_file_query_exists (file_parent, NULL)) {
226 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
227 return FALSE;
228 }
229 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100230 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100231 return g_file_set_contents (filename, data, size, error);
232}
233
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100234/**
235 * fu_common_get_contents_bytes:
236 * @filename: A filename
237 * @error: A #GError, or %NULL
238 *
239 * Reads a blob of data from a file.
240 *
241 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600242 *
243 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100244 **/
245GBytes *
246fu_common_get_contents_bytes (const gchar *filename, GError **error)
247{
248 gchar *data = NULL;
249 gsize len = 0;
Richard Hughes6a489a92020-12-22 10:32:06 +0000250
251 g_return_val_if_fail (filename != NULL, NULL);
252 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
253
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100254 if (!g_file_get_contents (filename, &data, &len, error))
255 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100256 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100257 return g_bytes_new_take (data, len);
258}
Richard Hughes943d2c92017-06-21 09:04:39 +0100259
260/**
261 * fu_common_get_contents_fd:
262 * @fd: A file descriptor
263 * @count: The maximum number of bytes to read
264 * @error: A #GError, or %NULL
265 *
266 * Reads a blob from a specific file descriptor.
267 *
268 * Note: this will close the fd when done
269 *
Richard Hughes4eada342017-10-03 21:20:32 +0100270 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600271 *
272 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100273 **/
274GBytes *
275fu_common_get_contents_fd (gint fd, gsize count, GError **error)
276{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000277#ifdef HAVE_GIO_UNIX
Richard Hughes97ad3352021-01-14 20:07:47 +0000278 guint8 tmp[0x8000] = { 0x0 };
279 g_autoptr(GByteArray) buf = g_byte_array_new ();
Richard Hughes943d2c92017-06-21 09:04:39 +0100280 g_autoptr(GError) error_local = NULL;
281 g_autoptr(GInputStream) stream = NULL;
282
283 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100284 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
285
Richard Hughes919f8ab2018-02-14 10:24:56 +0000286 /* this is invalid */
287 if (count == 0) {
288 g_set_error_literal (error,
289 FWUPD_ERROR,
290 FWUPD_ERROR_NOT_SUPPORTED,
291 "A maximum read size must be specified");
292 return NULL;
293 }
294
Richard Hughes943d2c92017-06-21 09:04:39 +0100295 /* read the entire fd to a data blob */
296 stream = g_unix_input_stream_new (fd, TRUE);
Richard Hughes97ad3352021-01-14 20:07:47 +0000297
298 /* read from stream in 32kB chunks */
299 while (TRUE) {
300 gssize sz;
301 sz = g_input_stream_read (stream, tmp, sizeof(tmp), NULL, &error_local);
302 if (sz == 0)
303 break;
304 if (sz < 0) {
305 g_set_error_literal (error,
306 FWUPD_ERROR,
307 FWUPD_ERROR_INVALID_FILE,
308 error_local->message);
309 return NULL;
310 }
311 g_byte_array_append (buf, tmp, sz);
312 if (buf->len > count) {
313 g_set_error (error,
Richard Hughes943d2c92017-06-21 09:04:39 +0100314 FWUPD_ERROR,
315 FWUPD_ERROR_INVALID_FILE,
Richard Hughes97ad3352021-01-14 20:07:47 +0000316 "cannot read from fd: 0x%x > 0x%x",
317 buf->len, (guint) count);
318 return NULL;
319 }
Richard Hughes943d2c92017-06-21 09:04:39 +0100320 }
Richard Hughes97ad3352021-01-14 20:07:47 +0000321 return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
Richard Hughes9e5675e2019-11-22 09:35:03 +0000322#else
323 g_set_error_literal (error,
324 FWUPD_ERROR,
325 FWUPD_ERROR_NOT_SUPPORTED,
326 "Not supported as <glib-unix.h> is unavailable");
327 return NULL;
328#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100329}
Richard Hughes94f939a2017-08-08 12:21:39 +0100330
Richard Hughes5add3a72021-01-13 19:25:10 +0000331#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100332static gboolean
333fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
334{
335 const gchar *tmp;
336 g_autofree gchar *buf = NULL;
337
338 /* no output file */
339 if (archive_entry_pathname (entry) == NULL)
340 return FALSE;
341
342 /* update output path */
343 tmp = archive_entry_pathname (entry);
344 buf = g_build_filename (dir, tmp, NULL);
345 archive_entry_update_pathname_utf8 (entry, buf);
346 return TRUE;
347}
Richard Hughes5add3a72021-01-13 19:25:10 +0000348#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100349
350/**
351 * fu_common_extract_archive:
352 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100353 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100354 * @error: A #GError, or %NULL
355 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000356 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100357 *
358 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600359 *
360 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100361 **/
362gboolean
363fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
364{
Richard Hughes5add3a72021-01-13 19:25:10 +0000365#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100366 gboolean ret = TRUE;
367 int r;
368 struct archive *arch = NULL;
369 struct archive_entry *entry;
370
Richard Hughes6a489a92020-12-22 10:32:06 +0000371 g_return_val_if_fail (blob != NULL, FALSE);
372 g_return_val_if_fail (dir != NULL, FALSE);
373 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
374
Richard Hughes94f939a2017-08-08 12:21:39 +0100375 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100376 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100377 arch = archive_read_new ();
378 archive_read_support_format_all (arch);
379 archive_read_support_filter_all (arch);
380 r = archive_read_open_memory (arch,
381 (void *) g_bytes_get_data (blob, NULL),
382 (size_t) g_bytes_get_size (blob));
383 if (r != 0) {
384 ret = FALSE;
385 g_set_error (error,
386 FWUPD_ERROR,
387 FWUPD_ERROR_INTERNAL,
388 "Cannot open: %s",
389 archive_error_string (arch));
390 goto out;
391 }
392 for (;;) {
393 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100394 r = archive_read_next_header (arch, &entry);
395 if (r == ARCHIVE_EOF)
396 break;
397 if (r != ARCHIVE_OK) {
398 ret = FALSE;
399 g_set_error (error,
400 FWUPD_ERROR,
401 FWUPD_ERROR_INTERNAL,
402 "Cannot read header: %s",
403 archive_error_string (arch));
404 goto out;
405 }
406
407 /* only extract if valid */
408 valid = fu_common_extract_archive_entry (entry, dir);
409 if (!valid)
410 continue;
411 r = archive_read_extract (arch, entry, 0);
412 if (r != ARCHIVE_OK) {
413 ret = FALSE;
414 g_set_error (error,
415 FWUPD_ERROR,
416 FWUPD_ERROR_INTERNAL,
417 "Cannot extract: %s",
418 archive_error_string (arch));
419 goto out;
420 }
421 }
422out:
423 if (arch != NULL) {
424 archive_read_close (arch);
425 archive_read_free (arch);
426 }
427 return ret;
Richard Hughes5add3a72021-01-13 19:25:10 +0000428#else
429 g_set_error_literal (error,
430 FWUPD_ERROR,
431 FWUPD_ERROR_NOT_SUPPORTED,
432 "missing libarchive support");
433 return FALSE;
434#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100435}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100436
437static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300438fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
439
440static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100441fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
442{
443 va_list args;
444 g_autofree gchar *tmp = NULL;
445 g_auto(GStrv) split = NULL;
446
447 va_start (args, fmt);
448 tmp = g_strdup_vprintf (fmt, args);
449 va_end (args);
450
451 split = g_strsplit (tmp, " ", -1);
452 for (guint i = 0; split[i] != NULL; i++)
453 g_ptr_array_add (argv, g_strdup (split[i]));
454}
455
Mario Limonciello1a680f32019-11-25 19:44:53 -0600456/**
457 * fu_common_find_program_in_path:
458 * @basename: The program to search
459 * @error: A #GError, or %NULL
460 *
461 * Looks for a program in the PATH variable
462 *
463 * Returns: a new #gchar, or %NULL for error
464 *
465 * Since: 1.1.2
466 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100467gchar *
468fu_common_find_program_in_path (const gchar *basename, GError **error)
469{
470 gchar *fn = g_find_program_in_path (basename);
471 if (fn == NULL) {
472 g_set_error (error,
473 FWUPD_ERROR,
474 FWUPD_ERROR_NOT_SUPPORTED,
475 "missing executable %s in PATH",
476 basename);
477 return NULL;
478 }
479 return fn;
480}
481
482static gboolean
483fu_common_test_namespace_support (GError **error)
484{
485 /* test if CONFIG_USER_NS is valid */
486 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
487 g_set_error (error,
488 FWUPD_ERROR,
489 FWUPD_ERROR_NOT_SUPPORTED,
490 "missing CONFIG_USER_NS in kernel");
491 return FALSE;
492 }
493 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
494 g_autofree gchar *clone = NULL;
495 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
496 return FALSE;
497 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
498 g_set_error (error,
499 FWUPD_ERROR,
500 FWUPD_ERROR_NOT_SUPPORTED,
501 "unprivileged user namespace clones disabled by distro");
502 return FALSE;
503 }
504 }
505 return TRUE;
506}
507
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100508/**
509 * fu_common_firmware_builder:
510 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100511 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
512 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100513 * @error: A #GError, or %NULL
514 *
515 * Builds a firmware file using tools from the host session in a bubblewrap
516 * jail. Several things happen during build:
517 *
518 * 1. The @bytes data is untarred to a temporary location
519 * 2. A bubblewrap container is set up
520 * 3. The startup.sh script is run inside the container
521 * 4. The firmware.bin is extracted from the container
522 * 5. The temporary location is deleted
523 *
524 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600525 *
526 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100527 **/
528GBytes *
529fu_common_firmware_builder (GBytes *bytes,
530 const gchar *script_fn,
531 const gchar *output_fn,
532 GError **error)
533{
534 gint rc = 0;
535 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500536 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100537 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100538 g_autofree gchar *localstatedir = NULL;
539 g_autofree gchar *output2_fn = NULL;
540 g_autofree gchar *standard_error = NULL;
541 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100542 g_autofree gchar *tmpdir = NULL;
543 g_autoptr(GBytes) firmware_blob = NULL;
544 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
545
546 g_return_val_if_fail (bytes != NULL, NULL);
547 g_return_val_if_fail (script_fn != NULL, NULL);
548 g_return_val_if_fail (output_fn != NULL, NULL);
549 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
550
Mario Limonciello37b59582018-08-13 08:38:01 -0500551 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100552 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
553 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100554 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500555
556 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100557 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100558 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500559
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100560 /* untar file to temp location */
561 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
562 if (tmpdir == NULL)
563 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100564 if (!fu_common_extract_archive (bytes, tmpdir, error))
565 return NULL;
566
567 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100568 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
569 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100570
571 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500572 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100573 fu_common_add_argv (argv, "--die-with-parent");
574 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500575 fu_common_add_argv (argv, "--ro-bind /lib /lib");
Mario Limoncielloed4e9122020-12-15 20:26:50 -0600576 fu_common_add_argv (argv, "--ro-bind-try /lib64 /lib64");
Mario Limonciellob8215572018-07-13 09:49:55 -0500577 fu_common_add_argv (argv, "--ro-bind /bin /bin");
578 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100579 fu_common_add_argv (argv, "--dir /tmp");
580 fu_common_add_argv (argv, "--dir /var");
581 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100582 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
583 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100584 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100585 fu_common_add_argv (argv, "--chdir /tmp");
586 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100587 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100588 g_ptr_array_add (argv, NULL);
589 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
590 g_debug ("running '%s' in %s", argv_str, tmpdir);
591 if (!g_spawn_sync ("/tmp",
592 (gchar **) argv->pdata,
593 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100594 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100595 NULL, NULL, /* child_setup */
596 &standard_output,
597 &standard_error,
598 &rc,
599 error)) {
600 g_prefix_error (error, "failed to run '%s': ", argv_str);
601 return NULL;
602 }
603 if (standard_output != NULL && standard_output[0] != '\0')
604 g_debug ("console output was: %s", standard_output);
605 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500606 FwupdError code = FWUPD_ERROR_INTERNAL;
607 if (errno == ENOTTY)
608 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100609 g_set_error (error,
610 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500611 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100612 "failed to build firmware: %s",
613 standard_error);
614 return NULL;
615 }
616
617 /* get generated file */
618 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
619 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
620 if (firmware_blob == NULL)
621 return NULL;
622
623 /* cleanup temp directory */
624 if (!fu_common_rmtree (tmpdir, error))
625 return NULL;
626
627 /* success */
628 return g_steal_pointer (&firmware_blob);
629}
Richard Hughes049ccc82017-08-09 15:26:56 +0100630
631typedef struct {
632 FuOutputHandler handler_cb;
633 gpointer handler_user_data;
634 GMainLoop *loop;
635 GSource *source;
636 GInputStream *stream;
637 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000638 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100639} FuCommonSpawnHelper;
640
641static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
642
643static gboolean
644fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
645{
646 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
647 gchar buffer[1024];
648 gssize sz;
649 g_auto(GStrv) split = NULL;
650 g_autoptr(GError) error = NULL;
651
652 /* read from stream */
653 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
654 buffer,
655 sizeof(buffer) - 1,
656 NULL,
657 &error);
658 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100659 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
660 g_warning ("failed to get read from nonblocking fd: %s",
661 error->message);
662 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100663 return G_SOURCE_REMOVE;
664 }
665
666 /* no read possible */
667 if (sz == 0)
668 g_main_loop_quit (helper->loop);
669
670 /* emit lines */
671 if (helper->handler_cb != NULL) {
672 buffer[sz] = '\0';
673 split = g_strsplit (buffer, "\n", -1);
674 for (guint i = 0; split[i] != NULL; i++) {
675 if (split[i][0] == '\0')
676 continue;
677 helper->handler_cb (split[i], helper->handler_user_data);
678 }
679 }
680
681 /* set up the source for the next read */
682 fu_common_spawn_create_pollable_source (helper);
683 return G_SOURCE_REMOVE;
684}
685
686static void
687fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
688{
689 if (helper->source != NULL)
690 g_source_destroy (helper->source);
691 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
692 helper->cancellable);
693 g_source_attach (helper->source, NULL);
694 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
695}
696
697static void
698fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
699{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000700 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100701 if (helper->stream != NULL)
702 g_object_unref (helper->stream);
703 if (helper->source != NULL)
704 g_source_destroy (helper->source);
705 if (helper->loop != NULL)
706 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000707 if (helper->timeout_id != 0)
708 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100709 g_free (helper);
710}
711
Mario Limoncielloa98df552018-04-16 12:15:51 -0500712#pragma clang diagnostic push
713#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100714G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500715#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100716
Richard Hughesb768e4d2019-02-26 13:55:18 +0000717static gboolean
718fu_common_spawn_timeout_cb (gpointer user_data)
719{
720 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
721 g_cancellable_cancel (helper->cancellable);
722 g_main_loop_quit (helper->loop);
723 helper->timeout_id = 0;
724 return G_SOURCE_REMOVE;
725}
726
727static void
728fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
729{
730 /* just propagate */
731 g_cancellable_cancel (helper->cancellable);
732}
733
Richard Hughes049ccc82017-08-09 15:26:56 +0100734/**
735 * fu_common_spawn_sync:
736 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100737 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
738 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000739 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100740 * @cancellable: a #GCancellable, or %NULL
741 * @error: A #GError or %NULL
742 *
743 * Runs a subprocess and waits for it to exit. Any output on standard out or
744 * standard error will be forwarded to @handler_cb as whole lines.
745 *
746 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600747 *
748 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100749 **/
750gboolean
751fu_common_spawn_sync (const gchar * const * argv,
752 FuOutputHandler handler_cb,
753 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000754 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100755 GCancellable *cancellable, GError **error)
756{
757 g_autoptr(FuCommonSpawnHelper) helper = NULL;
758 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100759 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000760 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100761
Richard Hughes6a489a92020-12-22 10:32:06 +0000762 g_return_val_if_fail (argv != NULL, FALSE);
763 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
764 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
765
Richard Hughes049ccc82017-08-09 15:26:56 +0100766 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100767 argv_str = g_strjoinv (" ", (gchar **) argv);
768 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100769 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
770 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
771 if (subprocess == NULL)
772 return FALSE;
773
774 /* watch for process to exit */
775 helper = g_new0 (FuCommonSpawnHelper, 1);
776 helper->handler_cb = handler_cb;
777 helper->handler_user_data = handler_user_data;
778 helper->loop = g_main_loop_new (NULL, FALSE);
779 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000780
781 /* always create a cancellable, and connect up the parent */
782 helper->cancellable = g_cancellable_new ();
783 if (cancellable != NULL) {
784 cancellable_id = g_cancellable_connect (cancellable,
785 G_CALLBACK (fu_common_spawn_cancelled_cb),
786 helper, NULL);
787 }
788
789 /* allow timeout */
790 if (timeout_ms > 0) {
791 helper->timeout_id = g_timeout_add (timeout_ms,
792 fu_common_spawn_timeout_cb,
793 helper);
794 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100795 fu_common_spawn_create_pollable_source (helper);
796 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000797 g_cancellable_disconnect (cancellable, cancellable_id);
798 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
799 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100800 return g_subprocess_wait_check (subprocess, cancellable, error);
801}
Richard Hughesae252cd2017-12-08 10:48:15 +0000802
803/**
804 * fu_common_write_uint16:
805 * @buf: A writable buffer
806 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100807 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000808 *
809 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600810 *
811 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000812 **/
813void
814fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
815{
816 guint16 val_hw;
817 switch (endian) {
818 case G_BIG_ENDIAN:
819 val_hw = GUINT16_TO_BE(val_native);
820 break;
821 case G_LITTLE_ENDIAN:
822 val_hw = GUINT16_TO_LE(val_native);
823 break;
824 default:
825 g_assert_not_reached ();
826 }
827 memcpy (buf, &val_hw, sizeof(val_hw));
828}
829
830/**
831 * fu_common_write_uint32:
832 * @buf: A writable buffer
833 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100834 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000835 *
836 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600837 *
838 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000839 **/
840void
841fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
842{
843 guint32 val_hw;
844 switch (endian) {
845 case G_BIG_ENDIAN:
846 val_hw = GUINT32_TO_BE(val_native);
847 break;
848 case G_LITTLE_ENDIAN:
849 val_hw = GUINT32_TO_LE(val_native);
850 break;
851 default:
852 g_assert_not_reached ();
853 }
854 memcpy (buf, &val_hw, sizeof(val_hw));
855}
856
857/**
Richard Hughesf2849d22021-03-05 17:19:17 +0000858 * fu_common_write_uint64:
859 * @buf: A writable buffer
860 * @val_native: a value in host byte-order
861 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
862 *
863 * Writes a value to a buffer using a specified endian.
864 *
865 * Since: 1.5.8
866 **/
867void
868fu_common_write_uint64 (guint8 *buf, guint64 val_native, FuEndianType endian)
869{
870 guint64 val_hw;
871 switch (endian) {
872 case G_BIG_ENDIAN:
873 val_hw = GUINT64_TO_BE(val_native);
874 break;
875 case G_LITTLE_ENDIAN:
876 val_hw = GUINT64_TO_LE(val_native);
877 break;
878 default:
879 g_assert_not_reached ();
880 }
881 memcpy (buf, &val_hw, sizeof(val_hw));
882}
883
884/**
Richard Hughesae252cd2017-12-08 10:48:15 +0000885 * fu_common_read_uint16:
886 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100887 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000888 *
889 * Read a value from a buffer using a specified endian.
890 *
891 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600892 *
893 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000894 **/
895guint16
896fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
897{
898 guint16 val_hw, val_native;
899 memcpy (&val_hw, buf, sizeof(val_hw));
900 switch (endian) {
901 case G_BIG_ENDIAN:
902 val_native = GUINT16_FROM_BE(val_hw);
903 break;
904 case G_LITTLE_ENDIAN:
905 val_native = GUINT16_FROM_LE(val_hw);
906 break;
907 default:
908 g_assert_not_reached ();
909 }
910 return val_native;
911}
912
913/**
914 * fu_common_read_uint32:
915 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100916 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000917 *
918 * Read a value from a buffer using a specified endian.
919 *
920 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600921 *
922 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000923 **/
924guint32
925fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
926{
927 guint32 val_hw, val_native;
928 memcpy (&val_hw, buf, sizeof(val_hw));
929 switch (endian) {
930 case G_BIG_ENDIAN:
931 val_native = GUINT32_FROM_BE(val_hw);
932 break;
933 case G_LITTLE_ENDIAN:
934 val_native = GUINT32_FROM_LE(val_hw);
935 break;
936 default:
937 g_assert_not_reached ();
938 }
939 return val_native;
940}
Richard Hughese82eef32018-05-20 10:41:26 +0100941
Richard Hughes73bf2332018-08-28 09:38:09 +0100942/**
Richard Hughesf2849d22021-03-05 17:19:17 +0000943 * fu_common_read_uint64:
944 * @buf: A readable buffer
945 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
946 *
947 * Read a value from a buffer using a specified endian.
948 *
949 * Returns: a value in host byte-order
950 *
951 * Since: 1.5.8
952 **/
953guint64
954fu_common_read_uint64 (const guint8 *buf, FuEndianType endian)
955{
956 guint64 val_hw, val_native;
957 memcpy (&val_hw, buf, sizeof(val_hw));
958 switch (endian) {
959 case G_BIG_ENDIAN:
960 val_native = GUINT64_FROM_BE(val_hw);
961 break;
962 case G_LITTLE_ENDIAN:
963 val_native = GUINT64_FROM_LE(val_hw);
964 break;
965 default:
966 g_assert_not_reached ();
967 }
968 return val_native;
969}
970
971/**
Richard Hughes73bf2332018-08-28 09:38:09 +0100972 * fu_common_strtoull:
973 * @str: A string, e.g. "0x1234"
974 *
975 * Converts a string value to an integer. Values are assumed base 10, unless
976 * prefixed with "0x" where they are parsed as base 16.
977 *
978 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600979 *
980 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100981 **/
982guint64
983fu_common_strtoull (const gchar *str)
984{
985 guint base = 10;
986 if (str == NULL)
987 return 0x0;
988 if (g_str_has_prefix (str, "0x")) {
989 str += 2;
990 base = 16;
991 }
992 return g_ascii_strtoull (str, NULL, base);
993}
994
Richard Hughesa574a752018-08-31 13:31:03 +0100995/**
996 * fu_common_strstrip:
997 * @str: A string, e.g. " test "
998 *
999 * Removes leading and trailing whitespace from a constant string.
1000 *
1001 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -06001002 *
1003 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +01001004 **/
1005gchar *
1006fu_common_strstrip (const gchar *str)
1007{
1008 guint head = G_MAXUINT;
1009 guint tail = 0;
1010
1011 g_return_val_if_fail (str != NULL, NULL);
1012
1013 /* find first non-space char */
1014 for (guint i = 0; str[i] != '\0'; i++) {
1015 if (str[i] != ' ') {
1016 head = i;
1017 break;
1018 }
1019 }
1020 if (head == G_MAXUINT)
1021 return g_strdup ("");
1022
1023 /* find last non-space char */
1024 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -05001025 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +01001026 tail = i;
1027 }
1028 return g_strndup (str + head, tail - head + 1);
1029}
1030
Richard Hughese82eef32018-05-20 10:41:26 +01001031static const GError *
1032fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
1033{
1034 for (guint j = 0; j < errors->len; j++) {
1035 const GError *error = g_ptr_array_index (errors, j);
1036 if (g_error_matches (error, FWUPD_ERROR, error_code))
1037 return error;
1038 }
1039 return NULL;
1040}
1041
1042static guint
1043fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
1044{
1045 guint cnt = 0;
1046 for (guint j = 0; j < errors->len; j++) {
1047 const GError *error = g_ptr_array_index (errors, j);
1048 if (g_error_matches (error, FWUPD_ERROR, error_code))
1049 cnt++;
1050 }
1051 return cnt;
1052}
1053
1054static gboolean
1055fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
1056{
1057 for (guint j = 0; j < errors->len; j++) {
1058 const GError *error = g_ptr_array_index (errors, j);
1059 gboolean matches_any = FALSE;
1060 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
1061 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
1062 matches_any = TRUE;
1063 break;
1064 }
1065 }
1066 if (!matches_any)
1067 return FALSE;
1068 }
1069 return TRUE;
1070}
1071
1072/**
1073 * fu_common_error_array_get_best:
1074 * @errors: (element-type GError): array of errors
1075 *
1076 * Finds the 'best' error to show the user from a array of errors, creating a
1077 * completely bespoke error where required.
1078 *
1079 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -06001080 *
1081 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +01001082 **/
1083GError *
1084fu_common_error_array_get_best (GPtrArray *errors)
1085{
1086 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
1087 FWUPD_ERROR_VERSION_SAME,
1088 FWUPD_ERROR_VERSION_NEWER,
1089 FWUPD_ERROR_NOT_SUPPORTED,
1090 FWUPD_ERROR_INTERNAL,
1091 FWUPD_ERROR_NOT_FOUND,
1092 FWUPD_ERROR_LAST };
1093 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
1094 FWUPD_ERROR_NOT_FOUND,
1095 FWUPD_ERROR_NOT_SUPPORTED,
1096 FWUPD_ERROR_LAST };
1097 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
1098 FWUPD_ERROR_VERSION_SAME,
1099 FWUPD_ERROR_NOT_FOUND,
1100 FWUPD_ERROR_NOT_SUPPORTED,
1101 FWUPD_ERROR_LAST };
1102
1103 /* are all the errors either GUID-not-matched or version-same? */
1104 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
1105 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
1106 return g_error_new (FWUPD_ERROR,
1107 FWUPD_ERROR_NOTHING_TO_DO,
1108 "All updatable firmware is already installed");
1109 }
1110
1111 /* are all the errors either GUID-not-matched or version same or newer? */
1112 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
1113 fu_common_error_array_matches_any (errors, err_all_newer)) {
1114 return g_error_new (FWUPD_ERROR,
1115 FWUPD_ERROR_NOTHING_TO_DO,
1116 "All updatable devices already have newer versions");
1117 }
1118
1119 /* get the most important single error */
1120 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1121 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1122 if (error_tmp != NULL)
1123 return g_error_copy (error_tmp);
1124 }
1125
1126 /* fall back to something */
1127 return g_error_new (FWUPD_ERROR,
1128 FWUPD_ERROR_NOT_FOUND,
1129 "No supported devices found");
1130}
Richard Hughes4be17d12018-05-30 20:36:29 +01001131
1132/**
1133 * fu_common_get_path:
1134 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1135 *
1136 * Gets a fwupd-specific system path. These can be overridden with various
1137 * environment variables, for instance %FWUPD_DATADIR.
1138 *
1139 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001140 *
1141 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001142 **/
1143gchar *
1144fu_common_get_path (FuPathKind path_kind)
1145{
1146 const gchar *tmp;
1147 g_autofree gchar *basedir = NULL;
1148
1149 switch (path_kind) {
1150 /* /var */
1151 case FU_PATH_KIND_LOCALSTATEDIR:
1152 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1153 if (tmp != NULL)
1154 return g_strdup (tmp);
1155 tmp = g_getenv ("SNAP_USER_DATA");
1156 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001157 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1158 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001159 /* /proc */
1160 case FU_PATH_KIND_PROCFS:
1161 tmp = g_getenv ("FWUPD_PROCFS");
1162 if (tmp != NULL)
1163 return g_strdup (tmp);
1164 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001165 /* /sys/firmware */
1166 case FU_PATH_KIND_SYSFSDIR_FW:
1167 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1168 if (tmp != NULL)
1169 return g_strdup (tmp);
1170 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001171 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001172 case FU_PATH_KIND_SYSFSDIR_TPM:
1173 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1174 if (tmp != NULL)
1175 return g_strdup (tmp);
1176 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001177 /* /sys/bus/platform/drivers */
1178 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1179 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1180 if (tmp != NULL)
1181 return g_strdup (tmp);
1182 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001183 /* /sys/kernel/security */
1184 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1185 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1186 if (tmp != NULL)
1187 return g_strdup (tmp);
1188 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001189 /* /sys/firmware/acpi/tables */
1190 case FU_PATH_KIND_ACPI_TABLES:
1191 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1192 if (tmp != NULL)
1193 return g_strdup (tmp);
1194 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001195 /* /etc */
1196 case FU_PATH_KIND_SYSCONFDIR:
1197 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1198 if (tmp != NULL)
1199 return g_strdup (tmp);
1200 tmp = g_getenv ("SNAP_USER_DATA");
1201 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001202 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1203 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001204 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1205 case FU_PATH_KIND_PLUGINDIR_PKG:
1206 tmp = g_getenv ("FWUPD_PLUGINDIR");
1207 if (tmp != NULL)
1208 return g_strdup (tmp);
1209 tmp = g_getenv ("SNAP");
1210 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001211 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1212 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001213 /* /usr/share/fwupd */
1214 case FU_PATH_KIND_DATADIR_PKG:
1215 tmp = g_getenv ("FWUPD_DATADIR");
1216 if (tmp != NULL)
1217 return g_strdup (tmp);
1218 tmp = g_getenv ("SNAP");
1219 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001220 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1221 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001222 /* /usr/libexec/fwupd/efi */
1223 case FU_PATH_KIND_EFIAPPDIR:
1224 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1225 if (tmp != NULL)
1226 return g_strdup (tmp);
1227#ifdef EFI_APP_LOCATION
1228 tmp = g_getenv ("SNAP");
1229 if (tmp != NULL)
1230 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1231 return g_strdup (EFI_APP_LOCATION);
1232#else
1233 return NULL;
1234#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001235 /* /etc/fwupd */
1236 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001237 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001238 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001239 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001240 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1241 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1242 /* /var/lib/fwupd */
1243 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001244 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001245 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001246 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001247 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1248 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1249 /* /var/cache/fwupd */
1250 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001251 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001252 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001253 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001254 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1255 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Daniel Campelloaf56c3b2021-05-05 15:04:17 +00001256 /* /run/lock */
1257 case FU_PATH_KIND_LOCKDIR:
1258 return g_strdup ("/run/lock");
Richard Hughesafdba372019-11-23 12:57:35 +00001259 case FU_PATH_KIND_OFFLINE_TRIGGER:
1260 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1261 if (tmp != NULL)
1262 return g_strdup (tmp);
1263 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001264 case FU_PATH_KIND_POLKIT_ACTIONS:
1265#ifdef POLKIT_ACTIONDIR
1266 return g_strdup (POLKIT_ACTIONDIR);
1267#else
1268 return NULL;
1269#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001270 /* this shouldn't happen */
1271 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001272 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001273 }
1274
1275 return NULL;
1276}
Richard Hughes83e56c12018-10-10 20:24:41 +01001277
1278/**
1279 * fu_common_string_replace:
1280 * @string: The #GString to operate on
1281 * @search: The text to search for
1282 * @replace: The text to use for substitutions
1283 *
1284 * Performs multiple search and replace operations on the given string.
1285 *
1286 * Returns: the number of replacements done, or 0 if @search is not found.
1287 *
1288 * Since: 1.2.0
1289 **/
1290guint
1291fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1292{
1293 gchar *tmp;
1294 guint count = 0;
1295 gsize search_idx = 0;
1296 gsize replace_len;
1297 gsize search_len;
1298
1299 g_return_val_if_fail (string != NULL, 0);
1300 g_return_val_if_fail (search != NULL, 0);
1301 g_return_val_if_fail (replace != NULL, 0);
1302
1303 /* nothing to do */
1304 if (string->len == 0)
1305 return 0;
1306
1307 search_len = strlen (search);
1308 replace_len = strlen (replace);
1309
1310 do {
1311 tmp = g_strstr_len (string->str + search_idx, -1, search);
1312 if (tmp == NULL)
1313 break;
1314
1315 /* advance the counter in case @replace contains @search */
1316 search_idx = (gsize) (tmp - string->str);
1317
1318 /* reallocate the string if required */
1319 if (search_len > replace_len) {
1320 g_string_erase (string,
1321 (gssize) search_idx,
1322 (gssize) (search_len - replace_len));
1323 memcpy (tmp, replace, replace_len);
1324 } else if (search_len < replace_len) {
1325 g_string_insert_len (string,
1326 (gssize) search_idx,
1327 replace,
1328 (gssize) (replace_len - search_len));
1329 /* we have to treat this specially as it could have
1330 * been reallocated when the insertion happened */
1331 memcpy (string->str + search_idx, replace, replace_len);
1332 } else {
1333 /* just memcmp in the new string */
1334 memcpy (tmp, replace, replace_len);
1335 }
1336 search_idx += replace_len;
1337 count++;
1338 } while (TRUE);
1339
1340 return count;
1341}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001342
Richard Hughesae96a1f2019-09-23 11:16:36 +01001343/**
1344 * fu_common_strwidth:
1345 * @text: The string to operate on
1346 *
1347 * Returns the width of the string in displayed characters on the console.
1348 *
1349 * Returns: width of text
1350 *
1351 * Since: 1.3.2
1352 **/
1353gsize
1354fu_common_strwidth (const gchar *text)
1355{
1356 const gchar *p = text;
1357 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001358
1359 g_return_val_if_fail (text != NULL, 0);
1360
Richard Hughesae96a1f2019-09-23 11:16:36 +01001361 while (*p) {
1362 gunichar c = g_utf8_get_char (p);
1363 if (g_unichar_iswide (c))
1364 width += 2;
1365 else if (!g_unichar_iszerowidth (c))
1366 width += 1;
1367 p = g_utf8_next_char (p);
1368 }
1369 return width;
1370}
1371
Mario Limonciello1a680f32019-11-25 19:44:53 -06001372/**
1373 * fu_common_string_append_kv:
1374 * @str: A #GString
1375 * @idt: The indent
1376 * @key: A string to append
1377 * @value: a string to append
1378 *
1379 * Appends a key and string value to a string
1380 *
1381 * Since: 1.2.4
1382 */
Richard Hughescea28de2019-08-09 11:16:40 +01001383void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001384fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001385{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001386 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001387 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001388
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001389 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001390
1391 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001392 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001393 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001394 for (gsize i = 0; i < idt; i++)
1395 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001396 if (key[0] != '\0') {
1397 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001398 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001399 } else {
1400 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001401 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001402 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001403 g_auto(GStrv) split = NULL;
1404 split = g_strsplit (value, "\n", -1);
1405 for (guint i = 0; split[i] != NULL; i++) {
1406 if (i == 0) {
1407 for (gsize j = keysz; j < align; j++)
1408 g_string_append (str, " ");
1409 } else {
1410 for (gsize j = 0; j < idt; j++)
1411 g_string_append (str, " ");
1412 }
1413 g_string_append (str, split[i]);
1414 g_string_append (str, "\n");
1415 }
1416 } else {
1417 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001418 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001419}
1420
Mario Limonciello1a680f32019-11-25 19:44:53 -06001421/**
1422 * fu_common_string_append_ku:
1423 * @str: A #GString
1424 * @idt: The indent
1425 * @key: A string to append
1426 * @value: guint64
1427 *
1428 * Appends a key and unsigned integer to a string
1429 *
1430 * Since: 1.2.4
1431 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001432void
1433fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1434{
1435 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1436 fu_common_string_append_kv (str, idt, key, tmp);
1437}
1438
Mario Limonciello1a680f32019-11-25 19:44:53 -06001439/**
1440 * fu_common_string_append_kx:
1441 * @str: A #GString
1442 * @idt: The indent
1443 * @key: A string to append
1444 * @value: guint64
1445 *
1446 * Appends a key and hex integer to a string
1447 *
1448 * Since: 1.2.4
1449 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001450void
1451fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1452{
1453 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1454 fu_common_string_append_kv (str, idt, key, tmp);
1455}
1456
Mario Limonciello1a680f32019-11-25 19:44:53 -06001457/**
1458 * fu_common_string_append_kb:
1459 * @str: A #GString
1460 * @idt: The indent
1461 * @key: A string to append
1462 * @value: Boolean
1463 *
1464 * Appends a key and boolean value to a string
1465 *
1466 * Since: 1.2.4
1467 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001468void
1469fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1470{
1471 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001472}
1473
Richard Hughese59cb9a2018-12-05 14:37:40 +00001474/**
Richard Hughes35481862019-01-06 12:01:58 +00001475 * fu_common_dump_full:
1476 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1477 * @title: prefix title, or %NULL
1478 * @data: buffer to print
1479 * @len: the size of @data
1480 * @columns: break new lines after this many bytes
1481 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1482 *
1483 * Dumps a raw buffer to the screen.
1484 *
1485 * Since: 1.2.4
1486 **/
1487void
1488fu_common_dump_full (const gchar *log_domain,
1489 const gchar *title,
1490 const guint8 *data,
1491 gsize len,
1492 guint columns,
1493 FuDumpFlags flags)
1494{
1495 g_autoptr(GString) str = g_string_new (NULL);
1496
1497 /* optional */
1498 if (title != NULL)
1499 g_string_append_printf (str, "%s:", title);
1500
1501 /* if more than can fit on one line then start afresh */
1502 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1503 g_string_append (str, "\n");
1504 } else {
1505 for (gsize i = str->len; i < 16; i++)
1506 g_string_append (str, " ");
1507 }
1508
1509 /* offset line */
1510 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1511 g_string_append (str, " │ ");
1512 for (gsize i = 0; i < columns; i++)
1513 g_string_append_printf (str, "%02x ", (guint) i);
1514 g_string_append (str, "\n───────┼");
1515 for (gsize i = 0; i < columns; i++)
1516 g_string_append (str, "───");
1517 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1518 }
1519
1520 /* print each row */
1521 for (gsize i = 0; i < len; i++) {
1522 g_string_append_printf (str, "%02x ", data[i]);
1523
1524 /* optionally print ASCII char */
1525 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1526 if (g_ascii_isprint (data[i]))
1527 g_string_append_printf (str, "[%c] ", data[i]);
1528 else
1529 g_string_append (str, "[?] ");
1530 }
1531
1532 /* new row required */
1533 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1534 g_string_append (str, "\n");
1535 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1536 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1537 }
1538 }
1539 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1540}
1541
1542/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001543 * fu_common_dump_raw:
1544 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1545 * @title: prefix title, or %NULL
1546 * @data: buffer to print
1547 * @len: the size of @data
1548 *
1549 * Dumps a raw buffer to the screen.
1550 *
1551 * Since: 1.2.2
1552 **/
1553void
1554fu_common_dump_raw (const gchar *log_domain,
1555 const gchar *title,
1556 const guint8 *data,
1557 gsize len)
1558{
Richard Hughes35481862019-01-06 12:01:58 +00001559 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1560 if (len > 64)
1561 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1562 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001563}
1564
1565/**
Mario Limonciello39602652019-04-29 21:08:58 -05001566 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001567 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1568 * @title: prefix title, or %NULL
1569 * @bytes: a #GBytes
1570 *
1571 * Dumps a byte buffer to the screen.
1572 *
1573 * Since: 1.2.2
1574 **/
1575void
1576fu_common_dump_bytes (const gchar *log_domain,
1577 const gchar *title,
1578 GBytes *bytes)
1579{
1580 gsize len = 0;
1581 const guint8 *data = g_bytes_get_data (bytes, &len);
1582 fu_common_dump_raw (log_domain, title, data, len);
1583}
Richard Hughesfc90f392019-01-15 21:21:16 +00001584
1585/**
1586 * fu_common_bytes_align:
1587 * @bytes: a #GBytes
1588 * @blksz: block size in bytes
1589 * @padval: the byte used to pad the byte buffer
1590 *
1591 * Aligns a block of memory to @blksize using the @padval value; if
1592 * the block is already aligned then the original @bytes is returned.
1593 *
1594 * Returns: (transfer full): a #GBytes, possibly @bytes
1595 *
1596 * Since: 1.2.4
1597 **/
1598GBytes *
1599fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1600{
1601 const guint8 *data;
1602 gsize sz;
1603
1604 g_return_val_if_fail (bytes != NULL, NULL);
1605 g_return_val_if_fail (blksz > 0, NULL);
1606
1607 /* pad */
1608 data = g_bytes_get_data (bytes, &sz);
1609 if (sz % blksz != 0) {
1610 gsize sz_align = ((sz / blksz) + 1) * blksz;
1611 guint8 *data_align = g_malloc (sz_align);
1612 memcpy (data_align, data, sz);
1613 memset (data_align + sz, padval, sz_align - sz);
1614 g_debug ("aligning 0x%x bytes to 0x%x",
1615 (guint) sz, (guint) sz_align);
1616 return g_bytes_new_take (data_align, sz_align);
1617 }
1618
1619 /* perfectly aligned */
1620 return g_bytes_ref (bytes);
1621}
Richard Hughes36999462019-03-19 20:23:29 +00001622
1623/**
1624 * fu_common_bytes_is_empty:
1625 * @bytes: a #GBytes
1626 *
1627 * Checks if a byte array are just empty (0xff) bytes.
1628 *
1629 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001630 *
1631 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001632 **/
1633gboolean
1634fu_common_bytes_is_empty (GBytes *bytes)
1635{
1636 gsize sz = 0;
1637 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1638 for (gsize i = 0; i < sz; i++) {
1639 if (buf[i] != 0xff)
1640 return FALSE;
1641 }
1642 return TRUE;
1643}
Richard Hughes2aad1042019-03-21 09:03:32 +00001644
1645/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001646 * fu_common_bytes_compare_raw:
1647 * @buf1: a buffer
1648 * @bufsz1: sizeof @buf1
1649 * @buf2: another buffer
1650 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001651 * @error: A #GError or %NULL
1652 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001653 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001654 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001655 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001656 *
1657 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001658 **/
1659gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001660fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1661 const guint8 *buf2, gsize bufsz2,
1662 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001663{
Richard Hughes38245ff2019-09-18 14:46:09 +01001664 g_return_val_if_fail (buf1 != NULL, FALSE);
1665 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001666 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1667
1668 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001669 if (bufsz1 != bufsz2) {
1670 g_set_error (error,
1671 G_IO_ERROR,
1672 G_IO_ERROR_INVALID_DATA,
1673 "got %" G_GSIZE_FORMAT " bytes, expected "
1674 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1675 return FALSE;
1676 }
1677
1678 /* check matches */
1679 for (guint i = 0x0; i < bufsz1; i++) {
1680 if (buf1[i] != buf2[i]) {
1681 g_set_error (error,
1682 G_IO_ERROR,
1683 G_IO_ERROR_INVALID_DATA,
1684 "got 0x%02x, expected 0x%02x @ 0x%04x",
1685 buf1[i], buf2[i], i);
1686 return FALSE;
1687 }
1688 }
1689
1690 /* success */
1691 return TRUE;
1692}
Richard Hughes484ee292019-03-22 16:10:50 +00001693
1694/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001695 * fu_common_bytes_compare:
1696 * @bytes1: a #GBytes
1697 * @bytes2: another #GBytes
1698 * @error: A #GError or %NULL
1699 *
1700 * Compares the buffers for equality.
1701 *
1702 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001703 *
1704 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001705 **/
1706gboolean
1707fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1708{
1709 const guint8 *buf1;
1710 const guint8 *buf2;
1711 gsize bufsz1;
1712 gsize bufsz2;
1713
1714 g_return_val_if_fail (bytes1 != NULL, FALSE);
1715 g_return_val_if_fail (bytes2 != NULL, FALSE);
1716 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1717
1718 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1719 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1720 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1721}
1722
1723/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001724 * fu_common_bytes_pad:
1725 * @bytes: a #GBytes
1726 * @sz: the desired size in bytes
1727 *
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001728 * Pads a GBytes to a minimum @sz with `0xff`.
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001729 *
1730 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001731 *
1732 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001733 **/
1734GBytes *
1735fu_common_bytes_pad (GBytes *bytes, gsize sz)
1736{
1737 gsize bytes_sz;
1738
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001739 g_return_val_if_fail (bytes != NULL, NULL);
1740 g_return_val_if_fail (sz != 0, NULL);
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001741
1742 /* pad */
1743 bytes_sz = g_bytes_get_size (bytes);
1744 if (bytes_sz < sz) {
1745 const guint8 *data = g_bytes_get_data (bytes, NULL);
1746 guint8 *data_new = g_malloc (sz);
1747 memcpy (data_new, data, bytes_sz);
1748 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1749 return g_bytes_new_take (data_new, sz);
1750 }
1751
Richard Hughes2ad99bb2021-03-03 10:19:13 +00001752 /* not required */
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001753 return g_bytes_ref (bytes);
1754}
1755
1756/**
Richard Hughes05e33772020-12-08 18:37:02 +00001757 * fu_common_bytes_new_offset:
1758 * @bytes: a #GBytes
1759 * @offset: where subsection starts at
1760 * @length: length of subsection
1761 * @error: A #GError or %NULL
1762 *
1763 * Creates a #GBytes which is a subsection of another #GBytes.
1764 *
1765 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1766 *
1767 * Since: 1.5.4
1768 **/
1769GBytes *
1770fu_common_bytes_new_offset (GBytes *bytes,
1771 gsize offset,
1772 gsize length,
1773 GError **error)
1774{
1775 g_return_val_if_fail (bytes != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001776 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes05e33772020-12-08 18:37:02 +00001777
1778 /* sanity check */
1779 if (offset + length > g_bytes_get_size (bytes)) {
1780 g_set_error (error,
1781 G_IO_ERROR,
1782 G_IO_ERROR_INVALID_DATA,
1783 "cannot create bytes @0x%02x for 0x%02x "
1784 "as buffer only 0x%04x bytes in size",
1785 (guint) offset,
1786 (guint) length,
1787 (guint) g_bytes_get_size (bytes));
1788 return NULL;
1789 }
1790 return g_bytes_new_from_bytes (bytes, offset, length);
1791}
1792
1793/**
Richard Hughes484ee292019-03-22 16:10:50 +00001794 * fu_common_realpath:
1795 * @filename: a filename
1796 * @error: A #GError or %NULL
1797 *
1798 * Finds the canonicalized absolute filename for a path.
1799 *
1800 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001801 *
1802 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001803 **/
1804gchar *
1805fu_common_realpath (const gchar *filename, GError **error)
1806{
1807 char full_tmp[PATH_MAX];
1808
1809 g_return_val_if_fail (filename != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001810 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes484ee292019-03-22 16:10:50 +00001811
Richard Hughes8694dee2019-11-22 09:16:34 +00001812#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001813 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001814#else
1815 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1816#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001817 g_set_error (error,
1818 G_IO_ERROR,
1819 G_IO_ERROR_INVALID_DATA,
1820 "cannot resolve path: %s",
1821 strerror (errno));
1822 return NULL;
1823 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001824 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1825 g_set_error (error,
1826 G_IO_ERROR,
1827 G_IO_ERROR_INVALID_DATA,
1828 "cannot find path: %s",
1829 full_tmp);
1830 return NULL;
1831 }
Richard Hughes484ee292019-03-22 16:10:50 +00001832 return g_strdup (full_tmp);
1833}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001834
1835/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001836 * fu_common_fnmatch:
1837 * @pattern: a glob pattern, e.g. `*foo*`
1838 * @str: a string to match against the pattern, e.g. `bazfoobar`
1839 *
1840 * Matches a string against a glob pattern.
1841 *
1842 * Return value: %TRUE if the string matched
1843 *
1844 * Since: 1.3.5
1845 **/
1846gboolean
1847fu_common_fnmatch (const gchar *pattern, const gchar *str)
1848{
1849 g_return_val_if_fail (pattern != NULL, FALSE);
1850 g_return_val_if_fail (str != NULL, FALSE);
1851#ifdef HAVE_FNMATCH_H
1852 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001853#elif _WIN32
1854 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1855 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1856 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001857#else
1858 return g_strcmp0 (pattern, str) == 0;
1859#endif
1860}
1861
Richard Hughesa84d7a72020-05-06 12:11:51 +01001862static gint
1863fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1864{
1865 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1866}
1867
1868/**
1869 * fu_common_filename_glob:
1870 * @directory: a directory path
1871 * @pattern: a glob pattern, e.g. `*foo*`
1872 * @error: A #GError or %NULL
1873 *
1874 * Returns all the filenames that match a specific glob pattern.
1875 * Any results are sorted. No matching files will set @error.
1876 *
1877 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1878 *
1879 * Since: 1.5.0
1880 **/
1881GPtrArray *
1882fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1883{
1884 const gchar *basename;
Richard Hughes6a489a92020-12-22 10:32:06 +00001885 g_autoptr(GDir) dir = NULL;
Richard Hughesa84d7a72020-05-06 12:11:51 +01001886 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +00001887
1888 g_return_val_if_fail (directory != NULL, NULL);
1889 g_return_val_if_fail (pattern != NULL, NULL);
1890 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1891
1892 dir = g_dir_open (directory, 0, error);
Richard Hughesa84d7a72020-05-06 12:11:51 +01001893 if (dir == NULL)
1894 return NULL;
1895 while ((basename = g_dir_read_name (dir)) != NULL) {
1896 if (!fu_common_fnmatch (pattern, basename))
1897 continue;
1898 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1899 }
1900 if (files->len == 0) {
1901 g_set_error_literal (error,
1902 G_IO_ERROR,
1903 G_IO_ERROR_NOT_FOUND,
1904 "no files matched pattern");
1905 return NULL;
1906 }
1907 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1908 return g_steal_pointer (&files);
1909}
1910
Richard Hughes5c508de2019-11-22 09:57:34 +00001911/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001912 * fu_common_strnsplit:
1913 * @str: a string to split
1914 * @sz: size of @str
1915 * @delimiter: a string which specifies the places at which to split the string
1916 * @max_tokens: the maximum number of pieces to split @str into
1917 *
1918 * Splits a string into a maximum of @max_tokens pieces, using the given
1919 * delimiter. If @max_tokens is reached, the remainder of string is appended
1920 * to the last token.
1921 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001922 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001923 *
1924 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001925 **/
1926gchar **
1927fu_common_strnsplit (const gchar *str, gsize sz,
1928 const gchar *delimiter, gint max_tokens)
1929{
1930 if (str[sz - 1] != '\0') {
1931 g_autofree gchar *str2 = g_strndup (str, sz);
1932 return g_strsplit (str2, delimiter, max_tokens);
1933 }
1934 return g_strsplit (str, delimiter, max_tokens);
1935}
Richard Hughes5308ea42019-08-09 12:25:13 +01001936
1937/**
Richard Hughes364e2682021-01-05 14:38:15 +00001938 * fu_common_strsafe:
1939 * @str: (nullable): a string to make safe for printing
1940 * @maxsz: maximum size of returned string
1941 *
1942 * Converts a string into something that can be safely printed.
1943 *
1944 * Return value: (transfer full): safe string, or %NULL if there was nothing valid
1945 *
1946 * Since: 1.5.5
1947 **/
1948gchar *
1949fu_common_strsafe (const gchar *str, gsize maxsz)
1950{
1951 gboolean valid = FALSE;
1952 g_autoptr(GString) tmp = NULL;
1953
Richard Hughes364e2682021-01-05 14:38:15 +00001954 /* sanity check */
Richard Hughesc123bee2021-02-08 16:47:05 +00001955 if (str == NULL || maxsz == 0)
Richard Hughes364e2682021-01-05 14:38:15 +00001956 return NULL;
1957
1958 /* replace non-printable chars with '.' */
Richard Hughesc123bee2021-02-08 16:47:05 +00001959 tmp = g_string_sized_new (maxsz);
Richard Hughesa4e0de42021-02-14 20:33:47 +00001960 for (gsize i = 0; i < maxsz && str[i] != '\0'; i++) {
Richard Hughes364e2682021-01-05 14:38:15 +00001961 if (!g_ascii_isprint (str[i])) {
1962 g_string_append_c (tmp, '.');
1963 continue;
1964 }
1965 g_string_append_c (tmp, str[i]);
Richard Hughesffbb1172021-02-14 20:34:31 +00001966 if (!g_ascii_isspace (str[i]))
1967 valid = TRUE;
Richard Hughes364e2682021-01-05 14:38:15 +00001968 }
1969
1970 /* if just junk, don't return 'all dots' */
1971 if (tmp->len == 0 || !valid)
1972 return NULL;
1973 return g_string_free (g_steal_pointer (&tmp), FALSE);
1974}
1975
Richard Hughese52e1b42021-01-26 09:59:15 +00001976
1977/**
1978 * fu_common_strjoin_array:
1979 * @separator: (nullable): string to insert between each of the strings, or %NULL
1980 * @array: (element-type utf8): A #GPtrArray
1981 *
1982 * Joins an array of strings together to form one long string, with the optional
1983 * separator inserted between each of them.
1984 *
1985 * If @array has no items, the return value will be an empty string.
1986 * If @array contains a single item, separator will not appear in the resulting
1987 * string.
1988 *
1989 * Returns: a string
1990 *
1991 * Since: 1.5.6
1992 **/
1993gchar *
1994fu_common_strjoin_array (const gchar *separator, GPtrArray *array)
1995{
1996 g_autofree const gchar **strv = NULL;
1997
1998 g_return_val_if_fail (array != NULL, NULL);
1999
2000 strv = g_new0 (const gchar *, array->len + 1);
2001 for (guint i = 0; i < array->len; i++)
2002 strv[i] = g_ptr_array_index (array, i);
2003 return g_strjoinv (separator, (gchar **) strv);
2004}
2005
Richard Hughes364e2682021-01-05 14:38:15 +00002006/**
Richard Hughes5308ea42019-08-09 12:25:13 +01002007 * fu_memcpy_safe:
2008 * @dst: destination buffer
2009 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
2010 * @dst_offset: offset in bytes into @dst to copy to
2011 * @src: source buffer
2012 * @src_sz: maximum size of @dst, typically `sizeof(src)`
2013 * @src_offset: offset in bytes into @src to copy from
2014 * @n: number of bytes to copy from @src+@offset from
2015 * @error: A #GError or %NULL
2016 *
2017 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
2018 * of both the destination and the source allows us to check for buffer overflow.
2019 *
2020 * Providing the buffer offsets also allows us to check reading past the end of
2021 * the source buffer. For this reason the caller should NEVER add an offset to
2022 * @src or @dst.
2023 *
2024 * You don't need to use this function in "obviously correct" cases, nor should
2025 * you use it when performance is a concern. Only us it when you're not sure if
2026 * malicious data from a device or firmware could cause memory corruption.
2027 *
2028 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002029 *
2030 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01002031 **/
2032gboolean
2033fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
2034 const guint8 *src, gsize src_sz, gsize src_offset,
2035 gsize n, GError **error)
2036{
Richard Hughes6a489a92020-12-22 10:32:06 +00002037 g_return_val_if_fail (dst != NULL, FALSE);
2038 g_return_val_if_fail (src != NULL, FALSE);
2039 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2040
Richard Hughes5308ea42019-08-09 12:25:13 +01002041 if (n == 0)
2042 return TRUE;
2043
2044 if (n > src_sz) {
2045 g_set_error (error,
2046 FWUPD_ERROR,
2047 FWUPD_ERROR_READ,
2048 "attempted to read 0x%02x bytes from buffer of 0x%02x",
2049 (guint) n, (guint) src_sz);
2050 return FALSE;
2051 }
2052 if (n + src_offset > src_sz) {
2053 g_set_error (error,
2054 FWUPD_ERROR,
2055 FWUPD_ERROR_READ,
2056 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
2057 (guint) n, (guint) src_offset, (guint) src_sz);
2058 return FALSE;
2059 }
2060 if (n > dst_sz) {
2061 g_set_error (error,
2062 FWUPD_ERROR,
2063 FWUPD_ERROR_WRITE,
2064 "attempted to write 0x%02x bytes to buffer of 0x%02x",
2065 (guint) n, (guint) dst_sz);
2066 return FALSE;
2067 }
2068 if (n + dst_offset > dst_sz) {
2069 g_set_error (error,
2070 FWUPD_ERROR,
2071 FWUPD_ERROR_WRITE,
2072 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
2073 (guint) n, (guint) dst_offset, (guint) dst_sz);
2074 return FALSE;
2075 }
2076
2077 /* phew! */
2078 memcpy (dst + dst_offset, src + src_offset, n);
2079 return TRUE;
2080}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002081
Richard Hughes80768f52019-10-22 07:19:14 +01002082/**
Richard Hughes9b11af92021-02-04 20:28:49 +00002083 * fu_memdup_safe:
2084 * @src: source buffer
2085 * @n: number of bytes to copy from @src
2086 * @error: A #GError or %NULL
2087 *
2088 * Duplicates some memory using memdup in a safe way.
2089 *
2090 * You don't need to use this function in "obviously correct" cases, nor should
2091 * you use it when performance is a concern. Only us it when you're not sure if
2092 * malicious data from a device or firmware could cause memory corruption.
2093 *
2094 * NOTE: This function intentionally limits allocation size to 1GB.
2095 *
2096 * Return value: (transfer full): block of allocated memory, or %NULL for an error.
2097 *
2098 * Since: 1.5.6
2099 **/
2100guint8 *
2101fu_memdup_safe (const guint8 *src, gsize n, GError **error)
2102{
Richard Hughes9b11af92021-02-04 20:28:49 +00002103 /* sanity check */
2104 if (n > 0x40000000) {
2105 g_set_error (error,
2106 G_IO_ERROR,
2107 G_IO_ERROR_NOT_SUPPORTED,
2108 "cannot allocate %uGB of memory",
2109 (guint) (n / 0x40000000));
2110 return NULL;
2111 }
2112
Richard Hughes5294d0c2021-02-05 09:50:02 +00002113#if GLIB_CHECK_VERSION(2,67,3)
Richard Hughes9b11af92021-02-04 20:28:49 +00002114 /* linear block of memory */
Richard Hughes5294d0c2021-02-05 09:50:02 +00002115 return g_memdup2 (src, n);
2116#else
2117 return g_memdup (src, (guint) n);
2118#endif
Richard Hughes9b11af92021-02-04 20:28:49 +00002119}
2120
2121/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01002122 * fu_common_read_uint8_safe:
2123 * @buf: source buffer
2124 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2125 * @offset: offset in bytes into @buf to copy from
Richard Hughesd42bd852021-04-19 10:15:48 +01002126 * @value: (out) (nullable): the parsed value
Richard Hughesc21a0b92019-10-24 12:24:37 +01002127 * @error: A #GError or %NULL
2128 *
2129 * Read a value from a buffer in a safe way.
2130 *
2131 * You don't need to use this function in "obviously correct" cases, nor should
2132 * you use it when performance is a concern. Only us it when you're not sure if
2133 * malicious data from a device or firmware could cause memory corruption.
2134 *
2135 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002136 *
2137 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01002138 **/
2139gboolean
2140fu_common_read_uint8_safe (const guint8 *buf,
2141 gsize bufsz,
2142 gsize offset,
2143 guint8 *value,
2144 GError **error)
2145{
2146 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00002147
2148 g_return_val_if_fail (buf != NULL, FALSE);
2149 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2150
Richard Hughesc21a0b92019-10-24 12:24:37 +01002151 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
2152 buf, bufsz, offset, /* src */
2153 sizeof(tmp), error))
2154 return FALSE;
2155 if (value != NULL)
2156 *value = tmp;
2157 return TRUE;
2158}
2159
2160/**
Richard Hughes80768f52019-10-22 07:19:14 +01002161 * fu_common_read_uint16_safe:
2162 * @buf: source buffer
2163 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2164 * @offset: offset in bytes into @buf to copy from
Richard Hughesd42bd852021-04-19 10:15:48 +01002165 * @value: (out) (nullable): the parsed value
Richard Hughes80768f52019-10-22 07:19:14 +01002166 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2167 * @error: A #GError or %NULL
2168 *
2169 * Read a value from a buffer using a specified endian in a safe way.
2170 *
2171 * You don't need to use this function in "obviously correct" cases, nor should
2172 * you use it when performance is a concern. Only us it when you're not sure if
2173 * malicious data from a device or firmware could cause memory corruption.
2174 *
2175 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002176 *
2177 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002178 **/
2179gboolean
2180fu_common_read_uint16_safe (const guint8 *buf,
2181 gsize bufsz,
2182 gsize offset,
2183 guint16 *value,
2184 FuEndianType endian,
2185 GError **error)
2186{
2187 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002188
2189 g_return_val_if_fail (buf != NULL, FALSE);
2190 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2191
Richard Hughes7d01ac92019-10-23 14:31:46 +01002192 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002193 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002194 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002195 return FALSE;
2196 if (value != NULL)
2197 *value = fu_common_read_uint16 (dst, endian);
2198 return TRUE;
2199}
2200
2201/**
2202 * fu_common_read_uint32_safe:
2203 * @buf: source buffer
2204 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2205 * @offset: offset in bytes into @buf to copy from
Richard Hughesd42bd852021-04-19 10:15:48 +01002206 * @value: (out) (nullable): the parsed value
Richard Hughes80768f52019-10-22 07:19:14 +01002207 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2208 * @error: A #GError or %NULL
2209 *
2210 * Read a value from a buffer using a specified endian in a safe way.
2211 *
2212 * You don't need to use this function in "obviously correct" cases, nor should
2213 * you use it when performance is a concern. Only us it when you're not sure if
2214 * malicious data from a device or firmware could cause memory corruption.
2215 *
2216 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002217 *
2218 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002219 **/
2220gboolean
2221fu_common_read_uint32_safe (const guint8 *buf,
2222 gsize bufsz,
2223 gsize offset,
2224 guint32 *value,
2225 FuEndianType endian,
2226 GError **error)
2227{
2228 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002229
2230 g_return_val_if_fail (buf != NULL, FALSE);
2231 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2232
Richard Hughes7d01ac92019-10-23 14:31:46 +01002233 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002234 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002235 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002236 return FALSE;
2237 if (value != NULL)
2238 *value = fu_common_read_uint32 (dst, endian);
2239 return TRUE;
2240}
2241
Mario Limonciello1a680f32019-11-25 19:44:53 -06002242/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002243 * fu_common_read_uint64_safe:
2244 * @buf: source buffer
2245 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2246 * @offset: offset in bytes into @buf to copy from
Richard Hughesd42bd852021-04-19 10:15:48 +01002247 * @value: (out) (nullable): the parsed value
Richard Hughesf2849d22021-03-05 17:19:17 +00002248 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2249 * @error: A #GError or %NULL
2250 *
2251 * Read a value from a buffer using a specified endian in a safe way.
2252 *
2253 * You don't need to use this function in "obviously correct" cases, nor should
2254 * you use it when performance is a concern. Only us it when you're not sure if
2255 * malicious data from a device or firmware could cause memory corruption.
2256 *
2257 * Return value: %TRUE if @value was set, %FALSE otherwise
2258 *
2259 * Since: 1.5.8
2260 **/
2261gboolean
2262fu_common_read_uint64_safe (const guint8 *buf,
2263 gsize bufsz,
2264 gsize offset,
2265 guint64 *value,
2266 FuEndianType endian,
2267 GError **error)
2268{
2269 guint8 dst[8] = { 0x0 };
2270
2271 g_return_val_if_fail (buf != NULL, FALSE);
2272 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2273
2274 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
2275 buf, bufsz, offset, /* src */
2276 sizeof(dst), error))
2277 return FALSE;
2278 if (value != NULL)
2279 *value = fu_common_read_uint64 (dst, endian);
2280 return TRUE;
2281}
2282
2283/**
Richard Hughes32ffdb22021-03-04 17:38:12 +00002284 * fu_common_write_uint8_safe:
2285 * @buf: source buffer
2286 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2287 * @offset: offset in bytes into @buf to write to
2288 * @value: the value to write
2289 * @error: A #GError or %NULL
2290 *
2291 * Write a value to a buffer in a safe way.
2292 *
2293 * You don't need to use this function in "obviously correct" cases, nor should
2294 * you use it when performance is a concern. Only us it when you're not sure if
2295 * malicious data from a device or firmware could cause memory corruption.
2296 *
2297 * Return value: %TRUE if @value was written, %FALSE otherwise
2298 *
2299 * Since: 1.5.8
2300 **/
2301gboolean
2302fu_common_write_uint8_safe (guint8 *buf,
2303 gsize bufsz,
2304 gsize offset,
2305 guint8 value,
2306 GError **error)
2307{
2308 g_return_val_if_fail (buf != NULL, FALSE);
2309 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2310
2311 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2312 &value, sizeof(value), 0x0, /* src */
2313 sizeof(value), error);
2314}
2315
2316/**
2317 * fu_common_write_uint16_safe:
2318 * @buf: source buffer
2319 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2320 * @offset: offset in bytes into @buf to write to
2321 * @value: the value to write
2322 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2323 * @error: A #GError or %NULL
2324 *
2325 * Write a value to a buffer using a specified endian in a safe way.
2326 *
2327 * You don't need to use this function in "obviously correct" cases, nor should
2328 * you use it when performance is a concern. Only us it when you're not sure if
2329 * malicious data from a device or firmware could cause memory corruption.
2330 *
2331 * Return value: %TRUE if @value was written, %FALSE otherwise
2332 *
2333 * Since: 1.5.8
2334 **/
2335gboolean
2336fu_common_write_uint16_safe (guint8 *buf,
2337 gsize bufsz,
2338 gsize offset,
2339 guint16 value,
2340 FuEndianType endian,
2341 GError **error)
2342{
2343 guint8 tmp[2] = { 0x0 };
2344
2345 g_return_val_if_fail (buf != NULL, FALSE);
2346 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2347
2348 fu_common_write_uint16 (tmp, value, endian);
2349 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2350 tmp, sizeof(tmp), 0x0, /* src */
2351 sizeof(tmp), error);
2352}
2353
2354/**
2355 * fu_common_write_uint32_safe:
2356 * @buf: source buffer
2357 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2358 * @offset: offset in bytes into @buf to write to
2359 * @value: the value to write
2360 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2361 * @error: A #GError or %NULL
2362 *
2363 * Write a value to a buffer using a specified endian in a safe way.
2364 *
2365 * You don't need to use this function in "obviously correct" cases, nor should
2366 * you use it when performance is a concern. Only us it when you're not sure if
2367 * malicious data from a device or firmware could cause memory corruption.
2368 *
2369 * Return value: %TRUE if @value was written, %FALSE otherwise
2370 *
2371 * Since: 1.5.8
2372 **/
2373gboolean
2374fu_common_write_uint32_safe (guint8 *buf,
2375 gsize bufsz,
2376 gsize offset,
2377 guint32 value,
2378 FuEndianType endian,
2379 GError **error)
2380{
2381 guint8 tmp[4] = { 0x0 };
2382
2383 g_return_val_if_fail (buf != NULL, FALSE);
2384 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2385
2386 fu_common_write_uint32 (tmp, value, endian);
2387 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2388 tmp, sizeof(tmp), 0x0, /* src */
2389 sizeof(tmp), error);
2390}
2391
2392/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002393 * fu_common_write_uint64_safe:
2394 * @buf: source buffer
2395 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2396 * @offset: offset in bytes into @buf to write to
2397 * @value: the value to write
2398 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2399 * @error: A #GError or %NULL
2400 *
2401 * Write a value to a buffer using a specified endian in a safe way.
2402 *
2403 * You don't need to use this function in "obviously correct" cases, nor should
2404 * you use it when performance is a concern. Only us it when you're not sure if
2405 * malicious data from a device or firmware could cause memory corruption.
2406 *
2407 * Return value: %TRUE if @value was written, %FALSE otherwise
2408 *
2409 * Since: 1.5.8
2410 **/
2411gboolean
2412fu_common_write_uint64_safe (guint8 *buf,
2413 gsize bufsz,
2414 gsize offset,
2415 guint64 value,
2416 FuEndianType endian,
2417 GError **error)
2418{
2419 guint8 tmp[8] = { 0x0 };
2420
2421 g_return_val_if_fail (buf != NULL, FALSE);
2422 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2423
2424 fu_common_write_uint64 (tmp, value, endian);
2425 return fu_memcpy_safe (buf, bufsz, offset, /* dst */
2426 tmp, sizeof(tmp), 0x0, /* src */
2427 sizeof(tmp), error);
2428}
2429
2430/**
Mario Limonciello1a680f32019-11-25 19:44:53 -06002431 * fu_byte_array_append_uint8:
2432 * @array: A #GByteArray
2433 * @data: #guint8
2434 *
2435 * Adds a 8 bit integer to a byte array
2436 *
2437 * Since: 1.3.1
2438 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002439void
2440fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2441{
2442 g_byte_array_append (array, &data, sizeof(data));
2443}
2444
Mario Limonciello1a680f32019-11-25 19:44:53 -06002445/**
2446 * fu_byte_array_append_uint16:
2447 * @array: A #GByteArray
2448 * @data: #guint16
2449 * @endian: #FuEndianType
2450 *
2451 * Adds a 16 bit integer to a byte array
2452 *
2453 * Since: 1.3.1
2454 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002455void
2456fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2457{
2458 guint8 buf[2];
2459 fu_common_write_uint16 (buf, data, endian);
2460 g_byte_array_append (array, buf, sizeof(buf));
2461}
2462
Mario Limonciello1a680f32019-11-25 19:44:53 -06002463/**
2464 * fu_byte_array_append_uint32:
2465 * @array: A #GByteArray
2466 * @data: #guint32
2467 * @endian: #FuEndianType
2468 *
2469 * Adds a 32 bit integer to a byte array
2470 *
2471 * Since: 1.3.1
2472 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002473void
2474fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2475{
2476 guint8 buf[4];
2477 fu_common_write_uint32 (buf, data, endian);
2478 g_byte_array_append (array, buf, sizeof(buf));
2479}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002480
2481/**
Richard Hughesf2849d22021-03-05 17:19:17 +00002482 * fu_byte_array_append_uint64:
2483 * @array: A #GByteArray
2484 * @data: #guint64
2485 * @endian: #FuEndianType
2486 *
2487 * Adds a 64 bit integer to a byte array
2488 *
2489 * Since: 1.5.8
2490 **/
2491void
2492fu_byte_array_append_uint64 (GByteArray *array, guint64 data, FuEndianType endian)
2493{
2494 guint8 buf[8];
2495 fu_common_write_uint64 (buf, data, endian);
2496 g_byte_array_append (array, buf, sizeof(buf));
2497}
2498
2499/**
Richard Hughese9664e82021-03-10 13:38:06 +00002500 * fu_byte_array_append_bytes:
2501 * @array: A #GByteArray
2502 * @bytes: A #GBytes
2503 *
2504 * Adds the contents of a GBytes to a byte array
2505 *
2506 * Since: 1.5.8
2507 **/
2508void
2509fu_byte_array_append_bytes (GByteArray *array, GBytes *bytes)
2510{
2511 g_byte_array_append (array,
2512 g_bytes_get_data (bytes, NULL),
2513 g_bytes_get_size (bytes));
2514}
2515
2516/**
Richard Hughes74db3402021-03-11 13:20:25 +00002517 * fu_byte_array_set_size_full:
2518 * @array: a #GByteArray
2519 * @length: the new size of the GByteArray
2520 * @data: the byte used to pad the array
2521 *
2522 * Sets the size of the GByteArray, expanding with @data as required.
2523 *
2524 * Since: 1.6.0
2525 **/
2526void
2527fu_byte_array_set_size_full (GByteArray *array, guint length, guint8 data)
2528{
2529 guint oldlength = array->len;
2530 g_byte_array_set_size (array, length);
2531 if (length > oldlength)
2532 memset (array->data + oldlength, data, length - oldlength);
2533}
2534
2535/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002536 * fu_byte_array_set_size:
2537 * @array: a #GByteArray
2538 * @length: the new size of the GByteArray
2539 *
2540 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2541 *
2542 * Since: 1.5.0
2543 **/
2544void
2545fu_byte_array_set_size (GByteArray *array, guint length)
2546{
Richard Hughes74db3402021-03-11 13:20:25 +00002547 return fu_byte_array_set_size_full (array, length, 0x0);
2548}
2549
2550/**
2551 * fu_byte_array_align_up:
2552 * @array: a #GByteArray
2553 * @alignment: align to this power of 2
2554 * @data: the byte used to pad the array
2555 *
2556 * Align a byte array length to a power of 2 boundary, where @alignment is the
2557 * bit position to align to. If @alignment is zero then @array is unchanged.
2558 *
2559 * Since: 1.6.0
2560 **/
2561void
2562fu_byte_array_align_up (GByteArray *array, guint8 alignment, guint8 data)
2563{
2564 fu_byte_array_set_size_full (array,
2565 fu_common_align_up (array->len, alignment),
2566 data);
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002567}
2568
2569/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002570 * fu_common_kernel_locked_down:
2571 *
2572 * Determines if kernel lockdown in effect
2573 *
2574 * Since: 1.3.8
2575 **/
2576gboolean
2577fu_common_kernel_locked_down (void)
2578{
Norbert Kamiński76e19932021-03-08 11:38:43 +01002579#ifdef __linux__
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002580 gsize len = 0;
2581 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2582 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2583 g_autofree gchar *data = NULL;
2584 g_auto(GStrv) options = NULL;
2585
2586 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2587 return FALSE;
2588 if (!g_file_get_contents (fname, &data, &len, NULL))
2589 return FALSE;
2590 if (len < 1)
2591 return FALSE;
2592 options = g_strsplit (data, " ", -1);
2593 for (guint i = 0; options[i] != NULL; i++) {
2594 if (g_strcmp0 (options[i], "[none]") == 0)
2595 return FALSE;
2596 }
2597 return TRUE;
2598#else
2599 return FALSE;
2600#endif
2601}
Richard Hughes9223c892020-05-09 20:32:08 +01002602
2603/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002604 * fu_common_cpuid:
2605 * @leaf: The CPUID level, now called the 'leaf' by Intel
2606 * @eax: (out) (nullable): EAX register
2607 * @ebx: (out) (nullable): EBX register
2608 * @ecx: (out) (nullable): ECX register
2609 * @edx: (out) (nullable): EDX register
2610 * @error: A #GError or NULL
2611 *
2612 * Calls CPUID and returns the registers for the given leaf.
2613 *
2614 * Return value: %TRUE if the registers are set.
2615 *
2616 * Since: 1.5.0
2617 **/
2618gboolean
2619fu_common_cpuid (guint32 leaf,
2620 guint32 *eax,
2621 guint32 *ebx,
2622 guint32 *ecx,
2623 guint32 *edx,
2624 GError **error)
2625{
2626#ifdef HAVE_CPUID_H
2627 guint eax_tmp = 0;
2628 guint ebx_tmp = 0;
2629 guint ecx_tmp = 0;
2630 guint edx_tmp = 0;
2631
Richard Hughes6a489a92020-12-22 10:32:06 +00002632 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2633
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002634 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002635 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002636 if (eax != NULL)
2637 *eax = eax_tmp;
2638 if (ebx != NULL)
2639 *ebx = ebx_tmp;
2640 if (ecx != NULL)
2641 *ecx = ecx_tmp;
2642 if (edx != NULL)
2643 *edx = edx_tmp;
2644 return TRUE;
2645#else
2646 g_set_error_literal (error,
2647 G_IO_ERROR,
2648 G_IO_ERROR_NOT_SUPPORTED,
2649 "no <cpuid.h> support");
2650 return FALSE;
2651#endif
2652}
2653
2654/**
Richard Hughesb63cfa92021-01-05 22:57:12 +00002655 * fu_common_get_cpu_vendor:
2656 *
2657 * Uses CPUID to discover the CPU vendor.
2658 *
2659 * Return value: a #FuCpuVendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD.
2660 *
2661 * Since: 1.5.5
2662 **/
2663FuCpuVendor
2664fu_common_get_cpu_vendor (void)
2665{
2666#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002667 guint ebx = 0;
2668 guint ecx = 0;
2669 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002670
Richard Hughesb63cfa92021-01-05 22:57:12 +00002671 if (fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL)) {
2672 if (ebx == signature_INTEL_ebx &&
2673 edx == signature_INTEL_edx &&
2674 ecx == signature_INTEL_ecx) {
2675 return FU_CPU_VENDOR_INTEL;
2676 }
2677 if (ebx == signature_AMD_ebx &&
2678 edx == signature_AMD_edx &&
2679 ecx == signature_AMD_ecx) {
2680 return FU_CPU_VENDOR_AMD;
2681 }
Richard Hughes9223c892020-05-09 20:32:08 +01002682 }
Richard Hughesbd444322020-05-21 12:05:03 +01002683#endif
Richard Hughesb63cfa92021-01-05 22:57:12 +00002684
2685 /* failed */
2686 return FU_CPU_VENDOR_UNKNOWN;
Richard Hughes9223c892020-05-09 20:32:08 +01002687}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002688
Richard Hughes36111472020-08-12 15:04:24 +01002689/**
2690 * fu_common_is_live_media:
2691 *
2692 * Checks if the user is running from a live media using various heuristics.
2693 *
2694 * Returns: %TRUE if live
2695 *
2696 * Since: 1.4.6
2697 **/
2698gboolean
2699fu_common_is_live_media (void)
2700{
2701 gsize bufsz = 0;
2702 g_autofree gchar *buf = NULL;
2703 g_auto(GStrv) tokens = NULL;
2704 const gchar *args[] = {
2705 "rd.live.image",
2706 "boot=live",
2707 NULL, /* last entry */
2708 };
2709 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2710 return TRUE;
2711 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2712 return FALSE;
2713 if (bufsz == 0)
2714 return FALSE;
2715 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2716 for (guint i = 0; args[i] != NULL; i++) {
2717 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2718 return TRUE;
2719 }
2720 return FALSE;
2721}
2722
Richard Hughes68175e92021-01-14 09:43:33 +00002723/**
2724 * fu_common_get_memory_size:
2725 *
2726 * Returns the size of physical memory.
2727 *
2728 * Returns: bytes
2729 *
2730 * Since: 1.5.6
2731 **/
2732guint64
2733fu_common_get_memory_size (void)
2734{
2735#ifdef _WIN32
2736 MEMORYSTATUSEX status;
2737 status.dwLength = sizeof(status);
2738 GlobalMemoryStatusEx (&status);
2739 return (guint64) status.ullTotalPhys;
2740#else
Richard Hughes9c318a52021-03-20 16:11:00 +00002741 return (guint64) sysconf (_SC_PHYS_PAGES) * (guint64) sysconf (_SC_PAGE_SIZE);
Richard Hughes68175e92021-01-14 09:43:33 +00002742#endif
2743}
2744
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002745static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002746fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002747{
2748 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002749 const gchar *obj;
2750 g_autoptr(GVariant) output = NULL;
2751 g_autoptr(GDBusProxy) proxy = NULL;
2752 g_autoptr(GPtrArray) devices = NULL;
2753 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002754 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002755
Richard Hughes43417b22020-10-30 14:46:16 +00002756 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2757 if (connection == NULL) {
2758 g_prefix_error (error, "failed to get system bus: ");
2759 return NULL;
2760 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002761 proxy = g_dbus_proxy_new_sync (connection,
2762 G_DBUS_PROXY_FLAGS_NONE, NULL,
2763 UDISKS_DBUS_SERVICE,
2764 UDISKS_DBUS_PATH,
2765 UDISKS_DBUS_MANAGER_INTERFACE,
2766 NULL, error);
2767 if (proxy == NULL) {
2768 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2769 return NULL;
2770 }
2771 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002772 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002773 "GetBlockDevices",
2774 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002775 G_DBUS_CALL_FLAGS_NONE,
2776 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002777 if (output == NULL) {
2778 if (error != NULL)
2779 g_dbus_error_strip_remote_error (*error);
2780 g_prefix_error (error, "failed to call %s.%s(): ",
2781 UDISKS_DBUS_SERVICE,
2782 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002783 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002784 }
Richard Hughes43417b22020-10-30 14:46:16 +00002785 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002786 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002787 while (g_variant_iter_next (iter, "&o", &obj)) {
2788 g_autoptr(GDBusProxy) proxy_blk = NULL;
2789 proxy_blk = g_dbus_proxy_new_sync (connection,
2790 G_DBUS_PROXY_FLAGS_NONE, NULL,
2791 UDISKS_DBUS_SERVICE,
2792 obj,
2793 UDISKS_DBUS_INTERFACE_BLOCK,
2794 NULL, error);
2795 if (proxy_blk == NULL) {
2796 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2797 return NULL;
2798 }
2799 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2800 }
2801
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002802
2803 return g_steal_pointer (&devices);
2804}
2805
Richard Hughesad3cfc02021-02-22 10:02:54 +00002806static const gchar *
2807fu_common_convert_to_gpt_type (const gchar *type)
2808{
2809 struct {
2810 const gchar *mbr;
2811 const gchar *gpt;
2812 } typeguids[] = {
2813 { "0xef", "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" }, /* esp */
2814 { "0x0b", "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" }, /* fat32 */
2815 { NULL, NULL }
2816 };
2817 for (guint i = 0; typeguids[i].mbr != NULL; i++) {
2818 if (g_strcmp0 (type, typeguids[i].mbr) == 0)
2819 return typeguids[i].gpt;
2820 }
2821 return type;
2822}
2823
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002824/**
2825 * fu_common_get_volumes_by_kind:
2826 * @kind: A volume kind, typically a GUID
2827 * @error: A #GError or NULL
2828 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002829 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002830 *
2831 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2832 *
2833 * Since: 1.4.6
2834 **/
2835GPtrArray *
2836fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2837{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002838 g_autoptr(GPtrArray) devices = NULL;
2839 g_autoptr(GPtrArray) volumes = NULL;
2840
Richard Hughes6a489a92020-12-22 10:32:06 +00002841 g_return_val_if_fail (kind != NULL, NULL);
2842 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2843
Richard Hughes43417b22020-10-30 14:46:16 +00002844 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002845 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002846 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002847 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2848 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002849 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002850 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002851 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002852 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002853 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002854 g_autoptr(GVariant) val = NULL;
2855
Richard Hughes43417b22020-10-30 14:46:16 +00002856 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002857 G_DBUS_PROXY_FLAGS_NONE, NULL,
2858 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002859 g_dbus_proxy_get_object_path (proxy_blk),
2860 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002861 NULL, error);
2862 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002863 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2864 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002865 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002866 }
2867 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2868 if (val == NULL)
2869 continue;
2870
Richard Hughesdb344d52020-09-09 19:42:27 +01002871 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002872 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2873 G_DBUS_PROXY_FLAGS_NONE, NULL,
2874 UDISKS_DBUS_SERVICE,
2875 g_dbus_proxy_get_object_path (proxy_blk),
2876 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2877 NULL, error);
2878 if (proxy_fs == NULL) {
2879 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2880 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002881 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002882 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002883 vol = g_object_new (FU_TYPE_VOLUME,
2884 "proxy-block", proxy_blk,
2885 "proxy-filesystem", proxy_fs,
2886 NULL);
Richard Hughesad3cfc02021-02-22 10:02:54 +00002887
2888 /* convert MBR type to GPT type */
2889 type_str = fu_common_convert_to_gpt_type (type_str);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002890 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2891 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2892 fu_volume_is_internal (vol),
2893 fu_volume_get_id_type (vol));
2894 if (g_strcmp0 (type_str, kind) != 0)
2895 continue;
2896 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002897 }
2898 if (volumes->len == 0) {
2899 g_set_error (error,
2900 G_IO_ERROR,
2901 G_IO_ERROR_NOT_FOUND,
2902 "no volumes of type %s", kind);
2903 return NULL;
2904 }
2905 return g_steal_pointer (&volumes);
2906}
2907
2908/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002909 * fu_common_get_volume_by_device:
2910 * @device: A device string, typcically starting with `/dev/`
2911 * @error: A #GError or NULL
2912 *
2913 * Finds the first volume from the specified device.
2914 *
2915 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2916 *
2917 * Since: 1.5.1
2918 **/
2919FuVolume *
2920fu_common_get_volume_by_device (const gchar *device, GError **error)
2921{
2922 g_autoptr(GPtrArray) devices = NULL;
2923
Richard Hughes6a489a92020-12-22 10:32:06 +00002924 g_return_val_if_fail (device != NULL, NULL);
2925 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2926
Richard Hughes0bdf5612020-10-30 14:56:22 +00002927 /* find matching block device */
2928 devices = fu_common_get_block_devices (error);
2929 if (devices == NULL)
2930 return NULL;
2931 for (guint i = 0; i < devices->len; i++) {
2932 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2933 g_autoptr(GVariant) val = NULL;
2934 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2935 if (val == NULL)
2936 continue;
2937 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2938 return g_object_new (FU_TYPE_VOLUME,
2939 "proxy-block", proxy_blk,
2940 NULL);
2941 }
2942 }
2943
2944 /* failed */
2945 g_set_error (error,
2946 G_IO_ERROR,
2947 G_IO_ERROR_NOT_FOUND,
2948 "no volumes for device %s",
2949 device);
2950 return NULL;
2951}
2952
2953/**
2954 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002955 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002956 * @error: A #GError or NULL
2957 *
2958 * Finds the first volume from the specified device.
2959 *
2960 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2961 *
2962 * Since: 1.5.1
2963 **/
2964FuVolume *
2965fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2966{
2967 g_autoptr(GPtrArray) devices = NULL;
2968
Richard Hughes6a489a92020-12-22 10:32:06 +00002969 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2970
Richard Hughes0bdf5612020-10-30 14:56:22 +00002971 /* find matching block device */
2972 devices = fu_common_get_block_devices (error);
2973 if (devices == NULL)
2974 return NULL;
2975 for (guint i = 0; i < devices->len; i++) {
2976 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2977 g_autoptr(GVariant) val = NULL;
2978 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2979 if (val == NULL)
2980 continue;
2981 if (devnum == g_variant_get_uint64 (val)) {
2982 return g_object_new (FU_TYPE_VOLUME,
2983 "proxy-block", proxy_blk,
2984 NULL);
2985 }
2986 }
2987
2988 /* failed */
2989 g_set_error (error,
2990 G_IO_ERROR,
2991 G_IO_ERROR_NOT_FOUND,
2992 "no volumes for devnum %u",
2993 devnum);
2994 return NULL;
2995}
2996
2997/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002998 * fu_common_get_esp_default:
2999 * @error: A #GError or NULL
3000 *
3001 * Gets the platform default ESP
3002 *
3003 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
3004 *
3005 * Since: 1.4.6
3006 **/
3007FuVolume *
3008fu_common_get_esp_default (GError **error)
3009{
3010 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00003011 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003012 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
3013 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06003014 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003015 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06003016 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003017
Richard Hughes6a489a92020-12-22 10:32:06 +00003018 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3019
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003020 /* for the test suite use local directory for ESP */
3021 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
3022 if (path_tmp != NULL)
3023 return fu_volume_new_from_mount_path (path_tmp);
3024
Mario Limonciello56d816a2020-11-11 16:59:30 -06003025 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
3026 if (volumes == NULL) {
3027 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
3028 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
3029 if (volumes == NULL) {
3030 g_prefix_error (error, "%s: ", error_local->message);
3031 return NULL;
3032 }
3033 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00003034
3035 /* are there _any_ internal vfat partitions?
3036 * remember HintSystem is just that -- a hint! */
3037 for (guint i = 0; i < volumes->len; i++) {
3038 FuVolume *vol = g_ptr_array_index (volumes, i);
3039 g_autofree gchar *type = fu_volume_get_id_type (vol);
3040 if (g_strcmp0 (type, "vfat") == 0 &&
3041 fu_volume_is_internal (vol)) {
3042 has_internal = TRUE;
3043 break;
3044 }
3045 }
3046
3047 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003048 for (guint i = 0; i < volumes->len; i++) {
3049 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06003050 g_autofree gchar *type = fu_volume_get_id_type (vol);
3051 if (type == NULL)
3052 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00003053 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06003054 continue;
3055 if (g_strcmp0 (type, "vfat") == 0)
3056 g_ptr_array_add (volumes_vfat, vol);
3057 }
3058 if (volumes_vfat->len == 0) {
3059 g_set_error (error,
3060 G_IO_ERROR,
3061 G_IO_ERROR_INVALID_FILENAME,
3062 "No ESP found");
3063 return NULL;
3064 }
3065 for (guint i = 0; i < volumes_vfat->len; i++) {
3066 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003067 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
3068 }
3069 if (volumes_mtab->len == 1) {
3070 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
3071 return g_object_ref (vol);
3072 }
3073 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
3074 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
3075 return g_object_ref (vol);
3076 }
3077 g_set_error (error,
3078 G_IO_ERROR,
3079 G_IO_ERROR_INVALID_FILENAME,
3080 "More than one available ESP");
3081 return NULL;
3082}
3083
3084/**
3085 * fu_common_get_esp_for_path:
3086 * @esp_path: A path to the ESP
3087 * @error: A #GError or NULL
3088 *
3089 * Gets the platform ESP using a UNIX or UDisks path
3090 *
3091 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
3092 *
3093 * Since: 1.4.6
3094 **/
3095FuVolume *
3096fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
3097{
Richard Hughes6a489a92020-12-22 10:32:06 +00003098 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003099 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciellobe220a42021-02-10 10:39:58 -06003100 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003101
Richard Hughes6a489a92020-12-22 10:32:06 +00003102 g_return_val_if_fail (esp_path != NULL, NULL);
3103 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3104
Mario Limonciellobe220a42021-02-10 10:39:58 -06003105 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
3106 if (volumes == NULL) {
3107 /* check if it's a valid directory already */
3108 if (g_file_test (esp_path, G_FILE_TEST_IS_DIR))
3109 return fu_volume_new_from_mount_path (esp_path);
3110 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughesb81140d2020-08-17 14:47:17 +01003111 return NULL;
Mario Limonciellobe220a42021-02-10 10:39:58 -06003112 }
Richard Hughes6a489a92020-12-22 10:32:06 +00003113 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003114 for (guint i = 0; i < volumes->len; i++) {
3115 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05003116 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01003117 if (g_strcmp0 (basename, vol_basename) == 0)
3118 return g_object_ref (vol);
3119 }
3120 g_set_error (error,
3121 G_IO_ERROR,
3122 G_IO_ERROR_INVALID_FILENAME,
3123 "No ESP with path %s",
3124 esp_path);
3125 return NULL;
3126}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01003127
3128/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01003129 * fu_common_crc8:
3130 * @buf: memory buffer
3131 * @bufsz: sizeof buf
3132 *
3133 * Returns the cyclic redundancy check value for the given memory buffer.
3134 *
3135 * Returns: CRC value
3136 *
3137 * Since: 1.5.0
3138 **/
3139guint8
3140fu_common_crc8 (const guint8 *buf, gsize bufsz)
3141{
3142 guint32 crc = 0;
3143 for (gsize j = bufsz; j > 0; j--) {
3144 crc ^= (*(buf++) << 8);
3145 for (guint32 i = 8; i; i--) {
3146 if (crc & 0x8000)
3147 crc ^= (0x1070 << 3);
3148 crc <<= 1;
3149 }
3150 }
3151 return ~((guint8) (crc >> 8));
3152}
3153
3154/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01003155 * fu_common_crc16:
3156 * @buf: memory buffer
3157 * @bufsz: sizeof buf
3158 *
3159 * Returns the cyclic redundancy check value for the given memory buffer.
3160 *
3161 * Returns: CRC value
3162 *
3163 * Since: 1.5.0
3164 **/
3165guint16
3166fu_common_crc16 (const guint8 *buf, gsize bufsz)
3167{
3168 guint16 crc = 0xffff;
3169 for (gsize len = bufsz; len > 0; len--) {
3170 crc = (guint16) (crc ^ (*buf++));
3171 for (guint8 i = 0; i < 8; i++) {
3172 if (crc & 0x1) {
3173 crc = (crc >> 1) ^ 0xa001;
3174 } else {
3175 crc >>= 1;
3176 }
3177 }
3178 }
3179 return ~crc;
3180}
3181
3182/**
3183 * fu_common_crc32_full:
3184 * @buf: memory buffer
3185 * @bufsz: sizeof buf
3186 * @crc: initial CRC value, typically 0xFFFFFFFF
3187 * @polynomial: CRC polynomial, typically 0xEDB88320
3188 *
3189 * Returns the cyclic redundancy check value for the given memory buffer.
3190 *
3191 * Returns: CRC value
3192 *
3193 * Since: 1.5.0
3194 **/
3195guint32
3196fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
3197{
3198 for (guint32 idx = 0; idx < bufsz; idx++) {
3199 guint8 data = *buf++;
3200 crc = crc ^ data;
3201 for (guint32 bit = 0; bit < 8; bit++) {
3202 guint32 mask = -(crc & 1);
3203 crc = (crc >> 1) ^ (polynomial & mask);
3204 }
3205 }
3206 return ~crc;
3207}
3208
3209/**
3210 * fu_common_crc32:
3211 * @buf: memory buffer
3212 * @bufsz: sizeof buf
3213 *
3214 * Returns the cyclic redundancy check value for the given memory buffer.
3215 *
3216 * Returns: CRC value
3217 *
3218 * Since: 1.5.0
3219 **/
3220guint32
3221fu_common_crc32 (const guint8 *buf, gsize bufsz)
3222{
3223 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
3224}
Richard Hughesc7546672021-01-28 10:53:30 +00003225
3226/**
3227 * fu_common_uri_get_scheme:
3228 * @uri: valid URI, e.g. `https://foo.bar/baz`
3229 *
3230 * Returns the USI scheme for the given URI.
3231 *
3232 * Returns: scheme value, or %NULL if invalid, e.g. `https`
3233 *
3234 * Since: 1.5.6
3235 **/
3236gchar *
3237fu_common_uri_get_scheme (const gchar *uri)
3238{
3239 gchar *tmp;
3240
3241 g_return_val_if_fail (uri != NULL, NULL);
3242
3243 tmp = g_strstr_len (uri, -1, ":");
3244 if (tmp == NULL || tmp[0] == '\0')
3245 return NULL;
3246 return g_utf8_strdown (uri, tmp - uri);
3247}
Richard Hughes58d52ed2021-03-10 13:42:59 +00003248
3249/**
3250 * fu_common_align_up:
3251 * @value: value to align
Richard Hughes119d2602021-03-17 10:07:38 +00003252 * @alignment: align to this power of 2, where 0x1F is the maximum value of 2GB
Richard Hughes58d52ed2021-03-10 13:42:59 +00003253 *
3254 * Align a value to a power of 2 boundary, where @alignment is the bit position
3255 * to align to. If @alignment is zero then @value is always returned unchanged.
3256 *
3257 * Returns: aligned value, which will be the same as @value if already aligned,
3258 * or %G_MAXSIZE if the value would overflow
3259 *
3260 * Since: 1.6.0
3261 **/
3262gsize
3263fu_common_align_up (gsize value, guint8 alignment)
3264{
3265 gsize value_new;
3266 guint32 mask = 1 << alignment;
3267
Richard Hughes119d2602021-03-17 10:07:38 +00003268 g_return_val_if_fail (alignment <= FU_FIRMWARE_ALIGNMENT_2G, G_MAXSIZE);
3269
Richard Hughes58d52ed2021-03-10 13:42:59 +00003270 /* no alignment required */
3271 if ((value & (mask - 1)) == 0)
3272 return value;
3273
3274 /* increment up to the next alignment value */
3275 value_new = value + mask;
3276 value_new &= ~(mask - 1);
3277
3278 /* overflow */
3279 if (value_new < value)
3280 return G_MAXSIZE;
3281
3282 /* success */
3283 return value_new;
3284}
Richard Hughes52441f22021-03-12 21:21:10 +00003285
3286/**
Richard Hughes4d76d182021-04-06 13:35:15 +01003287 * fu_battery_state_to_string:
3288 * @battery_state: a #FuBatteryState, e.g. %FU_BATTERY_STATE_FULLY_CHARGED
3289 *
3290 * Converts an enumerated type to a string.
3291 *
3292 * Returns: a string, or %NULL for invalid
3293 *
3294 * Since: 1.6.0
3295 **/
3296const gchar *
3297fu_battery_state_to_string (FuBatteryState battery_state)
3298{
3299 if (battery_state == FU_BATTERY_STATE_UNKNOWN)
3300 return "unknown";
3301 if (battery_state == FU_BATTERY_STATE_CHARGING)
3302 return "charging";
3303 if (battery_state == FU_BATTERY_STATE_DISCHARGING)
3304 return "discharging";
3305 if (battery_state == FU_BATTERY_STATE_EMPTY)
3306 return "empty";
3307 if (battery_state == FU_BATTERY_STATE_FULLY_CHARGED)
3308 return "fully-charged";
3309 return NULL;
3310}
3311
3312/**
Richard Hughes7f6f5252021-04-09 17:09:57 +01003313 * fu_bytes_get_data_safe:
3314 * @bytes: a #GBytes
3315 * @bufsz: (out) (optional): location to return size of byte data
3316 * @error: A #GError or %NULL
3317 *
3318 * Get the byte data in the #GBytes. This data should not be modified.
3319 * This function will always return the same pointer for a given #GBytes.
3320 *
3321 * If the size of @bytes is zero, then %NULL is returned and the @error is set,
Richard Hughes1ac05ef2021-04-24 08:52:41 +01003322 * which differs in behavior to that of g_bytes_get_data().
Richard Hughes7f6f5252021-04-09 17:09:57 +01003323 *
3324 * This may be useful when calling g_mapped_file_new() on a zero-length file.
3325 *
3326 * Returns: a pointer to the byte data, or %NULL.
3327 *
3328 * Since: 1.6.0
3329 **/
3330const guint8 *
3331fu_bytes_get_data_safe (GBytes *bytes, gsize *bufsz, GError **error)
3332{
3333 const guint8 *buf = g_bytes_get_data (bytes, bufsz);
3334 if (buf == NULL) {
3335 g_set_error (error,
3336 G_IO_ERROR,
3337 G_IO_ERROR_INVALID_DATA,
3338 "invalid data");
3339 return NULL;
3340 }
3341 return buf;
3342}
3343
3344/**
Richard Hughes52441f22021-03-12 21:21:10 +00003345 * fu_xmlb_builder_insert_kv:
3346 * @bn: #JsonBuilder
3347 * @key: string key
3348 * @value: string value
3349 *
3350 * Convenience function to add an XML node with a string value. If @value is %NULL
3351 * then no member is added.
3352 *
3353 * Since: 1.6.0
3354 **/
3355void
3356fu_xmlb_builder_insert_kv (XbBuilderNode *bn, const gchar *key, const gchar *value)
3357{
3358 if (value == NULL)
3359 return;
3360 xb_builder_node_insert_text (bn, key, value, NULL);
3361}
3362
3363/**
3364 * fu_xmlb_builder_insert_kx:
3365 * @bn: #JsonBuilder
3366 * @key: string key
3367 * @value: integer value
3368 *
3369 * Convenience function to add an XML node with a integer value. If @value is 0
3370 * then no member is added.
3371 *
3372 * Since: 1.6.0
3373 **/
3374void
3375fu_xmlb_builder_insert_kx (XbBuilderNode *bn, const gchar *key, guint64 value)
3376{
3377 g_autofree gchar *value_hex = NULL;
3378 if (value == 0)
3379 return;
3380 value_hex = g_strdup_printf ("0x%x", (guint) value);
3381 xb_builder_node_insert_text (bn, key, value_hex, NULL);
3382}
3383
3384/**
3385 * fu_xmlb_builder_insert_kb:
3386 * @bn: #JsonBuilder
3387 * @key: string key
3388 * @value: boolean value
3389 *
3390 * Convenience function to add an XML node with a boolean value.
3391 *
3392 * Since: 1.6.0
3393 **/
3394void
3395fu_xmlb_builder_insert_kb (XbBuilderNode *bn, const gchar *key, gboolean value)
3396{
3397 xb_builder_node_insert_text (bn, key, value ? "true" : "false", NULL);
3398}