blob: 5ca3164991f351e7ebb72661342a84513d329b27 [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 Hughesbd444322020-05-21 12:05:03 +010022#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010023#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010024#endif
Richard Hughes9223c892020-05-09 20:32:08 +010025
Richard Hughes5add3a72021-01-13 19:25:10 +000026#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +010027#include <archive_entry.h>
28#include <archive.h>
Richard Hughes5add3a72021-01-13 19:25:10 +000029#endif
Richard Hughes7ee42fe2017-08-15 14:06:21 +010030#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000032#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000033#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010034
35#include "fwupd-error.h"
36
37#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010038#include "fu-volume-private.h"
39
Richard Hughes43417b22020-10-30 14:46:16 +000040#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
41#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
42#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
43#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
44#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
45#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010046
47/**
Richard Hughes4eada342017-10-03 21:20:32 +010048 * SECTION:fu-common
49 * @short_description: common functionality for plugins to use
50 *
51 * Helper functions that can be used by the daemon and plugins.
52 *
53 * See also: #FuPlugin
54 */
55
56/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010057 * fu_common_rmtree:
58 * @directory: a directory name
59 * @error: A #GError or %NULL
60 *
61 * Recursively removes a directory.
62 *
63 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060064 *
65 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010066 **/
67gboolean
68fu_common_rmtree (const gchar *directory, GError **error)
69{
70 const gchar *filename;
71 g_autoptr(GDir) dir = NULL;
72
Richard Hughes6a489a92020-12-22 10:32:06 +000073 g_return_val_if_fail (directory != NULL, FALSE);
74 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
75
Richard Hughes954dd9f2017-08-08 13:36:25 +010076 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010077 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010078 dir = g_dir_open (directory, 0, error);
79 if (dir == NULL)
80 return FALSE;
81
82 /* find each */
83 while ((filename = g_dir_read_name (dir))) {
84 g_autofree gchar *src = NULL;
85 src = g_build_filename (directory, filename, NULL);
86 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
87 if (!fu_common_rmtree (src, error))
88 return FALSE;
89 } else {
90 if (g_unlink (src) != 0) {
91 g_set_error (error,
92 FWUPD_ERROR,
93 FWUPD_ERROR_INTERNAL,
94 "Failed to delete: %s", src);
95 return FALSE;
96 }
97 }
98 }
99 if (g_remove (directory) != 0) {
100 g_set_error (error,
101 FWUPD_ERROR,
102 FWUPD_ERROR_INTERNAL,
103 "Failed to delete: %s", directory);
104 return FALSE;
105 }
106 return TRUE;
107}
108
Richard Hughes89e968b2018-03-07 10:01:08 +0000109static gboolean
110fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
111{
112 const gchar *filename;
113 g_autoptr(GDir) dir = NULL;
114
115 /* try to open */
116 dir = g_dir_open (directory, 0, error);
117 if (dir == NULL)
118 return FALSE;
119
120 /* find each */
121 while ((filename = g_dir_read_name (dir))) {
122 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
123 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
124 if (!fu_common_get_file_list_internal (files, src, error))
125 return FALSE;
126 } else {
127 g_ptr_array_add (files, g_steal_pointer (&src));
128 }
129 }
130 return TRUE;
131
132}
133
134/**
135 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100136 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000137 * @error: A #GError or %NULL
138 *
139 * Returns every file found under @directory, and any subdirectory.
140 * If any path under @directory cannot be accessed due to permissions an error
141 * will be returned.
142 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000143 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600144 *
145 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000146 **/
147GPtrArray *
148fu_common_get_files_recursive (const gchar *path, GError **error)
149{
150 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +0000151
152 g_return_val_if_fail (path != NULL, NULL);
153 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
154
Richard Hughes89e968b2018-03-07 10:01:08 +0000155 if (!fu_common_get_file_list_internal (files, path, error))
156 return NULL;
157 return g_steal_pointer (&files);
158}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100159/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100160 * fu_common_mkdir_parent:
161 * @filename: A full pathname
162 * @error: A #GError, or %NULL
163 *
164 * Creates any required directories, including any parent directories.
165 *
166 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600167 *
168 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100169 **/
170gboolean
171fu_common_mkdir_parent (const gchar *filename, GError **error)
172{
173 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100174
Richard Hughes6a489a92020-12-22 10:32:06 +0000175 g_return_val_if_fail (filename != NULL, FALSE);
176 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
177
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100178 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500179 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
180 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100181 if (g_mkdir_with_parents (parent, 0755) == -1) {
182 g_set_error (error,
183 FWUPD_ERROR,
184 FWUPD_ERROR_INTERNAL,
185 "Failed to create '%s': %s",
186 parent, g_strerror (errno));
187 return FALSE;
188 }
189 return TRUE;
190}
191
192/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100193 * fu_common_set_contents_bytes:
194 * @filename: A filename
195 * @bytes: The data to write
196 * @error: A #GError, or %NULL
197 *
198 * Writes a blob of data to a filename, creating the parent directories as
199 * required.
200 *
201 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600202 *
203 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100204 **/
205gboolean
206fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
207{
208 const gchar *data;
209 gsize size;
210 g_autoptr(GFile) file = NULL;
211 g_autoptr(GFile) file_parent = NULL;
212
Richard Hughes6a489a92020-12-22 10:32:06 +0000213 g_return_val_if_fail (filename != NULL, FALSE);
214 g_return_val_if_fail (bytes != NULL, FALSE);
215 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
216
Richard Hughes943d2c92017-06-21 09:04:39 +0100217 file = g_file_new_for_path (filename);
218 file_parent = g_file_get_parent (file);
219 if (!g_file_query_exists (file_parent, NULL)) {
220 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
221 return FALSE;
222 }
223 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100224 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100225 return g_file_set_contents (filename, data, size, error);
226}
227
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100228/**
229 * fu_common_get_contents_bytes:
230 * @filename: A filename
231 * @error: A #GError, or %NULL
232 *
233 * Reads a blob of data from a file.
234 *
235 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600236 *
237 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100238 **/
239GBytes *
240fu_common_get_contents_bytes (const gchar *filename, GError **error)
241{
242 gchar *data = NULL;
243 gsize len = 0;
Richard Hughes6a489a92020-12-22 10:32:06 +0000244
245 g_return_val_if_fail (filename != NULL, NULL);
246 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
247
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100248 if (!g_file_get_contents (filename, &data, &len, error))
249 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100250 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100251 return g_bytes_new_take (data, len);
252}
Richard Hughes943d2c92017-06-21 09:04:39 +0100253
254/**
255 * fu_common_get_contents_fd:
256 * @fd: A file descriptor
257 * @count: The maximum number of bytes to read
258 * @error: A #GError, or %NULL
259 *
260 * Reads a blob from a specific file descriptor.
261 *
262 * Note: this will close the fd when done
263 *
Richard Hughes4eada342017-10-03 21:20:32 +0100264 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600265 *
266 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100267 **/
268GBytes *
269fu_common_get_contents_fd (gint fd, gsize count, GError **error)
270{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000271#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100272 g_autoptr(GBytes) blob = NULL;
273 g_autoptr(GError) error_local = NULL;
274 g_autoptr(GInputStream) stream = NULL;
275
276 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100277 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
278
Richard Hughes919f8ab2018-02-14 10:24:56 +0000279 /* this is invalid */
280 if (count == 0) {
281 g_set_error_literal (error,
282 FWUPD_ERROR,
283 FWUPD_ERROR_NOT_SUPPORTED,
284 "A maximum read size must be specified");
285 return NULL;
286 }
287
Richard Hughes943d2c92017-06-21 09:04:39 +0100288 /* read the entire fd to a data blob */
289 stream = g_unix_input_stream_new (fd, TRUE);
290 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
291 if (blob == NULL) {
292 g_set_error_literal (error,
293 FWUPD_ERROR,
294 FWUPD_ERROR_INVALID_FILE,
295 error_local->message);
296 return NULL;
297 }
298 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000299#else
300 g_set_error_literal (error,
301 FWUPD_ERROR,
302 FWUPD_ERROR_NOT_SUPPORTED,
303 "Not supported as <glib-unix.h> is unavailable");
304 return NULL;
305#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100306}
Richard Hughes94f939a2017-08-08 12:21:39 +0100307
Richard Hughes5add3a72021-01-13 19:25:10 +0000308#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100309static gboolean
310fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
311{
312 const gchar *tmp;
313 g_autofree gchar *buf = NULL;
314
315 /* no output file */
316 if (archive_entry_pathname (entry) == NULL)
317 return FALSE;
318
319 /* update output path */
320 tmp = archive_entry_pathname (entry);
321 buf = g_build_filename (dir, tmp, NULL);
322 archive_entry_update_pathname_utf8 (entry, buf);
323 return TRUE;
324}
Richard Hughes5add3a72021-01-13 19:25:10 +0000325#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100326
327/**
328 * fu_common_extract_archive:
329 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100330 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100331 * @error: A #GError, or %NULL
332 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000333 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100334 *
335 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600336 *
337 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100338 **/
339gboolean
340fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
341{
Richard Hughes5add3a72021-01-13 19:25:10 +0000342#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100343 gboolean ret = TRUE;
344 int r;
345 struct archive *arch = NULL;
346 struct archive_entry *entry;
347
Richard Hughes6a489a92020-12-22 10:32:06 +0000348 g_return_val_if_fail (blob != NULL, FALSE);
349 g_return_val_if_fail (dir != NULL, FALSE);
350 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
351
Richard Hughes94f939a2017-08-08 12:21:39 +0100352 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100353 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100354 arch = archive_read_new ();
355 archive_read_support_format_all (arch);
356 archive_read_support_filter_all (arch);
357 r = archive_read_open_memory (arch,
358 (void *) g_bytes_get_data (blob, NULL),
359 (size_t) g_bytes_get_size (blob));
360 if (r != 0) {
361 ret = FALSE;
362 g_set_error (error,
363 FWUPD_ERROR,
364 FWUPD_ERROR_INTERNAL,
365 "Cannot open: %s",
366 archive_error_string (arch));
367 goto out;
368 }
369 for (;;) {
370 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100371 r = archive_read_next_header (arch, &entry);
372 if (r == ARCHIVE_EOF)
373 break;
374 if (r != ARCHIVE_OK) {
375 ret = FALSE;
376 g_set_error (error,
377 FWUPD_ERROR,
378 FWUPD_ERROR_INTERNAL,
379 "Cannot read header: %s",
380 archive_error_string (arch));
381 goto out;
382 }
383
384 /* only extract if valid */
385 valid = fu_common_extract_archive_entry (entry, dir);
386 if (!valid)
387 continue;
388 r = archive_read_extract (arch, entry, 0);
389 if (r != ARCHIVE_OK) {
390 ret = FALSE;
391 g_set_error (error,
392 FWUPD_ERROR,
393 FWUPD_ERROR_INTERNAL,
394 "Cannot extract: %s",
395 archive_error_string (arch));
396 goto out;
397 }
398 }
399out:
400 if (arch != NULL) {
401 archive_read_close (arch);
402 archive_read_free (arch);
403 }
404 return ret;
Richard Hughes5add3a72021-01-13 19:25:10 +0000405#else
406 g_set_error_literal (error,
407 FWUPD_ERROR,
408 FWUPD_ERROR_NOT_SUPPORTED,
409 "missing libarchive support");
410 return FALSE;
411#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100412}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100413
414static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300415fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
416
417static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100418fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
419{
420 va_list args;
421 g_autofree gchar *tmp = NULL;
422 g_auto(GStrv) split = NULL;
423
424 va_start (args, fmt);
425 tmp = g_strdup_vprintf (fmt, args);
426 va_end (args);
427
428 split = g_strsplit (tmp, " ", -1);
429 for (guint i = 0; split[i] != NULL; i++)
430 g_ptr_array_add (argv, g_strdup (split[i]));
431}
432
Mario Limonciello1a680f32019-11-25 19:44:53 -0600433/**
434 * fu_common_find_program_in_path:
435 * @basename: The program to search
436 * @error: A #GError, or %NULL
437 *
438 * Looks for a program in the PATH variable
439 *
440 * Returns: a new #gchar, or %NULL for error
441 *
442 * Since: 1.1.2
443 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100444gchar *
445fu_common_find_program_in_path (const gchar *basename, GError **error)
446{
447 gchar *fn = g_find_program_in_path (basename);
448 if (fn == NULL) {
449 g_set_error (error,
450 FWUPD_ERROR,
451 FWUPD_ERROR_NOT_SUPPORTED,
452 "missing executable %s in PATH",
453 basename);
454 return NULL;
455 }
456 return fn;
457}
458
459static gboolean
460fu_common_test_namespace_support (GError **error)
461{
462 /* test if CONFIG_USER_NS is valid */
463 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
464 g_set_error (error,
465 FWUPD_ERROR,
466 FWUPD_ERROR_NOT_SUPPORTED,
467 "missing CONFIG_USER_NS in kernel");
468 return FALSE;
469 }
470 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
471 g_autofree gchar *clone = NULL;
472 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
473 return FALSE;
474 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
475 g_set_error (error,
476 FWUPD_ERROR,
477 FWUPD_ERROR_NOT_SUPPORTED,
478 "unprivileged user namespace clones disabled by distro");
479 return FALSE;
480 }
481 }
482 return TRUE;
483}
484
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100485/**
486 * fu_common_firmware_builder:
487 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100488 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
489 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100490 * @error: A #GError, or %NULL
491 *
492 * Builds a firmware file using tools from the host session in a bubblewrap
493 * jail. Several things happen during build:
494 *
495 * 1. The @bytes data is untarred to a temporary location
496 * 2. A bubblewrap container is set up
497 * 3. The startup.sh script is run inside the container
498 * 4. The firmware.bin is extracted from the container
499 * 5. The temporary location is deleted
500 *
501 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600502 *
503 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100504 **/
505GBytes *
506fu_common_firmware_builder (GBytes *bytes,
507 const gchar *script_fn,
508 const gchar *output_fn,
509 GError **error)
510{
511 gint rc = 0;
512 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500513 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100514 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100515 g_autofree gchar *localstatedir = NULL;
516 g_autofree gchar *output2_fn = NULL;
517 g_autofree gchar *standard_error = NULL;
518 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100519 g_autofree gchar *tmpdir = NULL;
520 g_autoptr(GBytes) firmware_blob = NULL;
521 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
522
523 g_return_val_if_fail (bytes != NULL, NULL);
524 g_return_val_if_fail (script_fn != NULL, NULL);
525 g_return_val_if_fail (output_fn != NULL, NULL);
526 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
527
Mario Limonciello37b59582018-08-13 08:38:01 -0500528 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100529 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
530 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100531 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500532
533 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100534 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100535 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500536
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100537 /* untar file to temp location */
538 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
539 if (tmpdir == NULL)
540 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100541 if (!fu_common_extract_archive (bytes, tmpdir, error))
542 return NULL;
543
544 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100545 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
546 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100547
548 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500549 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100550 fu_common_add_argv (argv, "--die-with-parent");
551 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500552 fu_common_add_argv (argv, "--ro-bind /lib /lib");
Mario Limoncielloed4e9122020-12-15 20:26:50 -0600553 fu_common_add_argv (argv, "--ro-bind-try /lib64 /lib64");
Mario Limonciellob8215572018-07-13 09:49:55 -0500554 fu_common_add_argv (argv, "--ro-bind /bin /bin");
555 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100556 fu_common_add_argv (argv, "--dir /tmp");
557 fu_common_add_argv (argv, "--dir /var");
558 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100559 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
560 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100561 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100562 fu_common_add_argv (argv, "--chdir /tmp");
563 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100564 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100565 g_ptr_array_add (argv, NULL);
566 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
567 g_debug ("running '%s' in %s", argv_str, tmpdir);
568 if (!g_spawn_sync ("/tmp",
569 (gchar **) argv->pdata,
570 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100571 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100572 NULL, NULL, /* child_setup */
573 &standard_output,
574 &standard_error,
575 &rc,
576 error)) {
577 g_prefix_error (error, "failed to run '%s': ", argv_str);
578 return NULL;
579 }
580 if (standard_output != NULL && standard_output[0] != '\0')
581 g_debug ("console output was: %s", standard_output);
582 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500583 FwupdError code = FWUPD_ERROR_INTERNAL;
584 if (errno == ENOTTY)
585 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100586 g_set_error (error,
587 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500588 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100589 "failed to build firmware: %s",
590 standard_error);
591 return NULL;
592 }
593
594 /* get generated file */
595 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
596 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
597 if (firmware_blob == NULL)
598 return NULL;
599
600 /* cleanup temp directory */
601 if (!fu_common_rmtree (tmpdir, error))
602 return NULL;
603
604 /* success */
605 return g_steal_pointer (&firmware_blob);
606}
Richard Hughes049ccc82017-08-09 15:26:56 +0100607
608typedef struct {
609 FuOutputHandler handler_cb;
610 gpointer handler_user_data;
611 GMainLoop *loop;
612 GSource *source;
613 GInputStream *stream;
614 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000615 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100616} FuCommonSpawnHelper;
617
618static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
619
620static gboolean
621fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
622{
623 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
624 gchar buffer[1024];
625 gssize sz;
626 g_auto(GStrv) split = NULL;
627 g_autoptr(GError) error = NULL;
628
629 /* read from stream */
630 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
631 buffer,
632 sizeof(buffer) - 1,
633 NULL,
634 &error);
635 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100636 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
637 g_warning ("failed to get read from nonblocking fd: %s",
638 error->message);
639 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100640 return G_SOURCE_REMOVE;
641 }
642
643 /* no read possible */
644 if (sz == 0)
645 g_main_loop_quit (helper->loop);
646
647 /* emit lines */
648 if (helper->handler_cb != NULL) {
649 buffer[sz] = '\0';
650 split = g_strsplit (buffer, "\n", -1);
651 for (guint i = 0; split[i] != NULL; i++) {
652 if (split[i][0] == '\0')
653 continue;
654 helper->handler_cb (split[i], helper->handler_user_data);
655 }
656 }
657
658 /* set up the source for the next read */
659 fu_common_spawn_create_pollable_source (helper);
660 return G_SOURCE_REMOVE;
661}
662
663static void
664fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
665{
666 if (helper->source != NULL)
667 g_source_destroy (helper->source);
668 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
669 helper->cancellable);
670 g_source_attach (helper->source, NULL);
671 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
672}
673
674static void
675fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
676{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000677 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100678 if (helper->stream != NULL)
679 g_object_unref (helper->stream);
680 if (helper->source != NULL)
681 g_source_destroy (helper->source);
682 if (helper->loop != NULL)
683 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000684 if (helper->timeout_id != 0)
685 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100686 g_free (helper);
687}
688
Mario Limoncielloa98df552018-04-16 12:15:51 -0500689#pragma clang diagnostic push
690#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100691G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500692#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100693
Richard Hughesb768e4d2019-02-26 13:55:18 +0000694static gboolean
695fu_common_spawn_timeout_cb (gpointer user_data)
696{
697 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
698 g_cancellable_cancel (helper->cancellable);
699 g_main_loop_quit (helper->loop);
700 helper->timeout_id = 0;
701 return G_SOURCE_REMOVE;
702}
703
704static void
705fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
706{
707 /* just propagate */
708 g_cancellable_cancel (helper->cancellable);
709}
710
Richard Hughes049ccc82017-08-09 15:26:56 +0100711/**
712 * fu_common_spawn_sync:
713 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100714 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
715 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000716 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100717 * @cancellable: a #GCancellable, or %NULL
718 * @error: A #GError or %NULL
719 *
720 * Runs a subprocess and waits for it to exit. Any output on standard out or
721 * standard error will be forwarded to @handler_cb as whole lines.
722 *
723 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600724 *
725 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100726 **/
727gboolean
728fu_common_spawn_sync (const gchar * const * argv,
729 FuOutputHandler handler_cb,
730 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000731 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100732 GCancellable *cancellable, GError **error)
733{
734 g_autoptr(FuCommonSpawnHelper) helper = NULL;
735 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100736 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000737 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100738
Richard Hughes6a489a92020-12-22 10:32:06 +0000739 g_return_val_if_fail (argv != NULL, FALSE);
740 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
741 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
742
Richard Hughes049ccc82017-08-09 15:26:56 +0100743 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100744 argv_str = g_strjoinv (" ", (gchar **) argv);
745 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100746 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
747 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
748 if (subprocess == NULL)
749 return FALSE;
750
751 /* watch for process to exit */
752 helper = g_new0 (FuCommonSpawnHelper, 1);
753 helper->handler_cb = handler_cb;
754 helper->handler_user_data = handler_user_data;
755 helper->loop = g_main_loop_new (NULL, FALSE);
756 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000757
758 /* always create a cancellable, and connect up the parent */
759 helper->cancellable = g_cancellable_new ();
760 if (cancellable != NULL) {
761 cancellable_id = g_cancellable_connect (cancellable,
762 G_CALLBACK (fu_common_spawn_cancelled_cb),
763 helper, NULL);
764 }
765
766 /* allow timeout */
767 if (timeout_ms > 0) {
768 helper->timeout_id = g_timeout_add (timeout_ms,
769 fu_common_spawn_timeout_cb,
770 helper);
771 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100772 fu_common_spawn_create_pollable_source (helper);
773 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000774 g_cancellable_disconnect (cancellable, cancellable_id);
775 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
776 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100777 return g_subprocess_wait_check (subprocess, cancellable, error);
778}
Richard Hughesae252cd2017-12-08 10:48:15 +0000779
780/**
781 * fu_common_write_uint16:
782 * @buf: A writable buffer
783 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100784 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000785 *
786 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600787 *
788 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000789 **/
790void
791fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
792{
793 guint16 val_hw;
794 switch (endian) {
795 case G_BIG_ENDIAN:
796 val_hw = GUINT16_TO_BE(val_native);
797 break;
798 case G_LITTLE_ENDIAN:
799 val_hw = GUINT16_TO_LE(val_native);
800 break;
801 default:
802 g_assert_not_reached ();
803 }
804 memcpy (buf, &val_hw, sizeof(val_hw));
805}
806
807/**
808 * fu_common_write_uint32:
809 * @buf: A writable buffer
810 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100811 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000812 *
813 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600814 *
815 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000816 **/
817void
818fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
819{
820 guint32 val_hw;
821 switch (endian) {
822 case G_BIG_ENDIAN:
823 val_hw = GUINT32_TO_BE(val_native);
824 break;
825 case G_LITTLE_ENDIAN:
826 val_hw = GUINT32_TO_LE(val_native);
827 break;
828 default:
829 g_assert_not_reached ();
830 }
831 memcpy (buf, &val_hw, sizeof(val_hw));
832}
833
834/**
835 * fu_common_read_uint16:
836 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100837 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000838 *
839 * Read a value from a buffer using a specified endian.
840 *
841 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600842 *
843 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000844 **/
845guint16
846fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
847{
848 guint16 val_hw, val_native;
849 memcpy (&val_hw, buf, sizeof(val_hw));
850 switch (endian) {
851 case G_BIG_ENDIAN:
852 val_native = GUINT16_FROM_BE(val_hw);
853 break;
854 case G_LITTLE_ENDIAN:
855 val_native = GUINT16_FROM_LE(val_hw);
856 break;
857 default:
858 g_assert_not_reached ();
859 }
860 return val_native;
861}
862
863/**
864 * fu_common_read_uint32:
865 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100866 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000867 *
868 * Read a value from a buffer using a specified endian.
869 *
870 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600871 *
872 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000873 **/
874guint32
875fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
876{
877 guint32 val_hw, val_native;
878 memcpy (&val_hw, buf, sizeof(val_hw));
879 switch (endian) {
880 case G_BIG_ENDIAN:
881 val_native = GUINT32_FROM_BE(val_hw);
882 break;
883 case G_LITTLE_ENDIAN:
884 val_native = GUINT32_FROM_LE(val_hw);
885 break;
886 default:
887 g_assert_not_reached ();
888 }
889 return val_native;
890}
Richard Hughese82eef32018-05-20 10:41:26 +0100891
Richard Hughes73bf2332018-08-28 09:38:09 +0100892/**
893 * fu_common_strtoull:
894 * @str: A string, e.g. "0x1234"
895 *
896 * Converts a string value to an integer. Values are assumed base 10, unless
897 * prefixed with "0x" where they are parsed as base 16.
898 *
899 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600900 *
901 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100902 **/
903guint64
904fu_common_strtoull (const gchar *str)
905{
906 guint base = 10;
907 if (str == NULL)
908 return 0x0;
909 if (g_str_has_prefix (str, "0x")) {
910 str += 2;
911 base = 16;
912 }
913 return g_ascii_strtoull (str, NULL, base);
914}
915
Richard Hughesa574a752018-08-31 13:31:03 +0100916/**
917 * fu_common_strstrip:
918 * @str: A string, e.g. " test "
919 *
920 * Removes leading and trailing whitespace from a constant string.
921 *
922 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600923 *
924 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100925 **/
926gchar *
927fu_common_strstrip (const gchar *str)
928{
929 guint head = G_MAXUINT;
930 guint tail = 0;
931
932 g_return_val_if_fail (str != NULL, NULL);
933
934 /* find first non-space char */
935 for (guint i = 0; str[i] != '\0'; i++) {
936 if (str[i] != ' ') {
937 head = i;
938 break;
939 }
940 }
941 if (head == G_MAXUINT)
942 return g_strdup ("");
943
944 /* find last non-space char */
945 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500946 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100947 tail = i;
948 }
949 return g_strndup (str + head, tail - head + 1);
950}
951
Richard Hughese82eef32018-05-20 10:41:26 +0100952static const GError *
953fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
954{
955 for (guint j = 0; j < errors->len; j++) {
956 const GError *error = g_ptr_array_index (errors, j);
957 if (g_error_matches (error, FWUPD_ERROR, error_code))
958 return error;
959 }
960 return NULL;
961}
962
963static guint
964fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
965{
966 guint cnt = 0;
967 for (guint j = 0; j < errors->len; j++) {
968 const GError *error = g_ptr_array_index (errors, j);
969 if (g_error_matches (error, FWUPD_ERROR, error_code))
970 cnt++;
971 }
972 return cnt;
973}
974
975static gboolean
976fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
977{
978 for (guint j = 0; j < errors->len; j++) {
979 const GError *error = g_ptr_array_index (errors, j);
980 gboolean matches_any = FALSE;
981 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
982 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
983 matches_any = TRUE;
984 break;
985 }
986 }
987 if (!matches_any)
988 return FALSE;
989 }
990 return TRUE;
991}
992
993/**
994 * fu_common_error_array_get_best:
995 * @errors: (element-type GError): array of errors
996 *
997 * Finds the 'best' error to show the user from a array of errors, creating a
998 * completely bespoke error where required.
999 *
1000 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -06001001 *
1002 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +01001003 **/
1004GError *
1005fu_common_error_array_get_best (GPtrArray *errors)
1006{
1007 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
1008 FWUPD_ERROR_VERSION_SAME,
1009 FWUPD_ERROR_VERSION_NEWER,
1010 FWUPD_ERROR_NOT_SUPPORTED,
1011 FWUPD_ERROR_INTERNAL,
1012 FWUPD_ERROR_NOT_FOUND,
1013 FWUPD_ERROR_LAST };
1014 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
1015 FWUPD_ERROR_NOT_FOUND,
1016 FWUPD_ERROR_NOT_SUPPORTED,
1017 FWUPD_ERROR_LAST };
1018 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
1019 FWUPD_ERROR_VERSION_SAME,
1020 FWUPD_ERROR_NOT_FOUND,
1021 FWUPD_ERROR_NOT_SUPPORTED,
1022 FWUPD_ERROR_LAST };
1023
1024 /* are all the errors either GUID-not-matched or version-same? */
1025 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
1026 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
1027 return g_error_new (FWUPD_ERROR,
1028 FWUPD_ERROR_NOTHING_TO_DO,
1029 "All updatable firmware is already installed");
1030 }
1031
1032 /* are all the errors either GUID-not-matched or version same or newer? */
1033 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
1034 fu_common_error_array_matches_any (errors, err_all_newer)) {
1035 return g_error_new (FWUPD_ERROR,
1036 FWUPD_ERROR_NOTHING_TO_DO,
1037 "All updatable devices already have newer versions");
1038 }
1039
1040 /* get the most important single error */
1041 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1042 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1043 if (error_tmp != NULL)
1044 return g_error_copy (error_tmp);
1045 }
1046
1047 /* fall back to something */
1048 return g_error_new (FWUPD_ERROR,
1049 FWUPD_ERROR_NOT_FOUND,
1050 "No supported devices found");
1051}
Richard Hughes4be17d12018-05-30 20:36:29 +01001052
1053/**
1054 * fu_common_get_path:
1055 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1056 *
1057 * Gets a fwupd-specific system path. These can be overridden with various
1058 * environment variables, for instance %FWUPD_DATADIR.
1059 *
1060 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001061 *
1062 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001063 **/
1064gchar *
1065fu_common_get_path (FuPathKind path_kind)
1066{
1067 const gchar *tmp;
1068 g_autofree gchar *basedir = NULL;
1069
1070 switch (path_kind) {
1071 /* /var */
1072 case FU_PATH_KIND_LOCALSTATEDIR:
1073 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1074 if (tmp != NULL)
1075 return g_strdup (tmp);
1076 tmp = g_getenv ("SNAP_USER_DATA");
1077 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001078 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1079 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001080 /* /proc */
1081 case FU_PATH_KIND_PROCFS:
1082 tmp = g_getenv ("FWUPD_PROCFS");
1083 if (tmp != NULL)
1084 return g_strdup (tmp);
1085 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001086 /* /sys/firmware */
1087 case FU_PATH_KIND_SYSFSDIR_FW:
1088 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1089 if (tmp != NULL)
1090 return g_strdup (tmp);
1091 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001092 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001093 case FU_PATH_KIND_SYSFSDIR_TPM:
1094 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1095 if (tmp != NULL)
1096 return g_strdup (tmp);
1097 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001098 /* /sys/bus/platform/drivers */
1099 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1100 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1101 if (tmp != NULL)
1102 return g_strdup (tmp);
1103 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001104 /* /sys/kernel/security */
1105 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1106 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1107 if (tmp != NULL)
1108 return g_strdup (tmp);
1109 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001110 /* /sys/firmware/acpi/tables */
1111 case FU_PATH_KIND_ACPI_TABLES:
1112 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1113 if (tmp != NULL)
1114 return g_strdup (tmp);
1115 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001116 /* /etc */
1117 case FU_PATH_KIND_SYSCONFDIR:
1118 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1119 if (tmp != NULL)
1120 return g_strdup (tmp);
1121 tmp = g_getenv ("SNAP_USER_DATA");
1122 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001123 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1124 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001125 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1126 case FU_PATH_KIND_PLUGINDIR_PKG:
1127 tmp = g_getenv ("FWUPD_PLUGINDIR");
1128 if (tmp != NULL)
1129 return g_strdup (tmp);
1130 tmp = g_getenv ("SNAP");
1131 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001132 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1133 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001134 /* /usr/share/fwupd */
1135 case FU_PATH_KIND_DATADIR_PKG:
1136 tmp = g_getenv ("FWUPD_DATADIR");
1137 if (tmp != NULL)
1138 return g_strdup (tmp);
1139 tmp = g_getenv ("SNAP");
1140 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001141 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1142 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001143 /* /usr/libexec/fwupd/efi */
1144 case FU_PATH_KIND_EFIAPPDIR:
1145 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1146 if (tmp != NULL)
1147 return g_strdup (tmp);
1148#ifdef EFI_APP_LOCATION
1149 tmp = g_getenv ("SNAP");
1150 if (tmp != NULL)
1151 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1152 return g_strdup (EFI_APP_LOCATION);
1153#else
1154 return NULL;
1155#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001156 /* /etc/fwupd */
1157 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001158 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001159 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001160 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001161 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1162 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1163 /* /var/lib/fwupd */
1164 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001165 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001166 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001167 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001168 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1169 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1170 /* /var/cache/fwupd */
1171 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001172 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001173 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001174 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001175 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1176 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001177 case FU_PATH_KIND_OFFLINE_TRIGGER:
1178 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1179 if (tmp != NULL)
1180 return g_strdup (tmp);
1181 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001182 case FU_PATH_KIND_POLKIT_ACTIONS:
1183#ifdef POLKIT_ACTIONDIR
1184 return g_strdup (POLKIT_ACTIONDIR);
1185#else
1186 return NULL;
1187#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001188 /* this shouldn't happen */
1189 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001190 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001191 }
1192
1193 return NULL;
1194}
Richard Hughes83e56c12018-10-10 20:24:41 +01001195
1196/**
1197 * fu_common_string_replace:
1198 * @string: The #GString to operate on
1199 * @search: The text to search for
1200 * @replace: The text to use for substitutions
1201 *
1202 * Performs multiple search and replace operations on the given string.
1203 *
1204 * Returns: the number of replacements done, or 0 if @search is not found.
1205 *
1206 * Since: 1.2.0
1207 **/
1208guint
1209fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1210{
1211 gchar *tmp;
1212 guint count = 0;
1213 gsize search_idx = 0;
1214 gsize replace_len;
1215 gsize search_len;
1216
1217 g_return_val_if_fail (string != NULL, 0);
1218 g_return_val_if_fail (search != NULL, 0);
1219 g_return_val_if_fail (replace != NULL, 0);
1220
1221 /* nothing to do */
1222 if (string->len == 0)
1223 return 0;
1224
1225 search_len = strlen (search);
1226 replace_len = strlen (replace);
1227
1228 do {
1229 tmp = g_strstr_len (string->str + search_idx, -1, search);
1230 if (tmp == NULL)
1231 break;
1232
1233 /* advance the counter in case @replace contains @search */
1234 search_idx = (gsize) (tmp - string->str);
1235
1236 /* reallocate the string if required */
1237 if (search_len > replace_len) {
1238 g_string_erase (string,
1239 (gssize) search_idx,
1240 (gssize) (search_len - replace_len));
1241 memcpy (tmp, replace, replace_len);
1242 } else if (search_len < replace_len) {
1243 g_string_insert_len (string,
1244 (gssize) search_idx,
1245 replace,
1246 (gssize) (replace_len - search_len));
1247 /* we have to treat this specially as it could have
1248 * been reallocated when the insertion happened */
1249 memcpy (string->str + search_idx, replace, replace_len);
1250 } else {
1251 /* just memcmp in the new string */
1252 memcpy (tmp, replace, replace_len);
1253 }
1254 search_idx += replace_len;
1255 count++;
1256 } while (TRUE);
1257
1258 return count;
1259}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001260
Richard Hughesae96a1f2019-09-23 11:16:36 +01001261/**
1262 * fu_common_strwidth:
1263 * @text: The string to operate on
1264 *
1265 * Returns the width of the string in displayed characters on the console.
1266 *
1267 * Returns: width of text
1268 *
1269 * Since: 1.3.2
1270 **/
1271gsize
1272fu_common_strwidth (const gchar *text)
1273{
1274 const gchar *p = text;
1275 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001276
1277 g_return_val_if_fail (text != NULL, 0);
1278
Richard Hughesae96a1f2019-09-23 11:16:36 +01001279 while (*p) {
1280 gunichar c = g_utf8_get_char (p);
1281 if (g_unichar_iswide (c))
1282 width += 2;
1283 else if (!g_unichar_iszerowidth (c))
1284 width += 1;
1285 p = g_utf8_next_char (p);
1286 }
1287 return width;
1288}
1289
Mario Limonciello1a680f32019-11-25 19:44:53 -06001290/**
1291 * fu_common_string_append_kv:
1292 * @str: A #GString
1293 * @idt: The indent
1294 * @key: A string to append
1295 * @value: a string to append
1296 *
1297 * Appends a key and string value to a string
1298 *
1299 * Since: 1.2.4
1300 */
Richard Hughescea28de2019-08-09 11:16:40 +01001301void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001302fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001303{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001304 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001305 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001306
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001307 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001308
1309 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001310 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001311 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001312 for (gsize i = 0; i < idt; i++)
1313 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001314 if (key[0] != '\0') {
1315 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001316 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001317 } else {
1318 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001319 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001320 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001321 g_auto(GStrv) split = NULL;
1322 split = g_strsplit (value, "\n", -1);
1323 for (guint i = 0; split[i] != NULL; i++) {
1324 if (i == 0) {
1325 for (gsize j = keysz; j < align; j++)
1326 g_string_append (str, " ");
1327 } else {
1328 for (gsize j = 0; j < idt; j++)
1329 g_string_append (str, " ");
1330 }
1331 g_string_append (str, split[i]);
1332 g_string_append (str, "\n");
1333 }
1334 } else {
1335 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001336 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001337}
1338
Mario Limonciello1a680f32019-11-25 19:44:53 -06001339/**
1340 * fu_common_string_append_ku:
1341 * @str: A #GString
1342 * @idt: The indent
1343 * @key: A string to append
1344 * @value: guint64
1345 *
1346 * Appends a key and unsigned integer to a string
1347 *
1348 * Since: 1.2.4
1349 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001350void
1351fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1352{
1353 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1354 fu_common_string_append_kv (str, idt, key, tmp);
1355}
1356
Mario Limonciello1a680f32019-11-25 19:44:53 -06001357/**
1358 * fu_common_string_append_kx:
1359 * @str: A #GString
1360 * @idt: The indent
1361 * @key: A string to append
1362 * @value: guint64
1363 *
1364 * Appends a key and hex integer to a string
1365 *
1366 * Since: 1.2.4
1367 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001368void
1369fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1370{
1371 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1372 fu_common_string_append_kv (str, idt, key, tmp);
1373}
1374
Mario Limonciello1a680f32019-11-25 19:44:53 -06001375/**
1376 * fu_common_string_append_kb:
1377 * @str: A #GString
1378 * @idt: The indent
1379 * @key: A string to append
1380 * @value: Boolean
1381 *
1382 * Appends a key and boolean value to a string
1383 *
1384 * Since: 1.2.4
1385 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001386void
1387fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1388{
1389 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001390}
1391
Richard Hughese59cb9a2018-12-05 14:37:40 +00001392/**
Richard Hughes35481862019-01-06 12:01:58 +00001393 * fu_common_dump_full:
1394 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1395 * @title: prefix title, or %NULL
1396 * @data: buffer to print
1397 * @len: the size of @data
1398 * @columns: break new lines after this many bytes
1399 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1400 *
1401 * Dumps a raw buffer to the screen.
1402 *
1403 * Since: 1.2.4
1404 **/
1405void
1406fu_common_dump_full (const gchar *log_domain,
1407 const gchar *title,
1408 const guint8 *data,
1409 gsize len,
1410 guint columns,
1411 FuDumpFlags flags)
1412{
1413 g_autoptr(GString) str = g_string_new (NULL);
1414
1415 /* optional */
1416 if (title != NULL)
1417 g_string_append_printf (str, "%s:", title);
1418
1419 /* if more than can fit on one line then start afresh */
1420 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1421 g_string_append (str, "\n");
1422 } else {
1423 for (gsize i = str->len; i < 16; i++)
1424 g_string_append (str, " ");
1425 }
1426
1427 /* offset line */
1428 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1429 g_string_append (str, " │ ");
1430 for (gsize i = 0; i < columns; i++)
1431 g_string_append_printf (str, "%02x ", (guint) i);
1432 g_string_append (str, "\n───────┼");
1433 for (gsize i = 0; i < columns; i++)
1434 g_string_append (str, "───");
1435 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1436 }
1437
1438 /* print each row */
1439 for (gsize i = 0; i < len; i++) {
1440 g_string_append_printf (str, "%02x ", data[i]);
1441
1442 /* optionally print ASCII char */
1443 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1444 if (g_ascii_isprint (data[i]))
1445 g_string_append_printf (str, "[%c] ", data[i]);
1446 else
1447 g_string_append (str, "[?] ");
1448 }
1449
1450 /* new row required */
1451 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1452 g_string_append (str, "\n");
1453 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1454 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1455 }
1456 }
1457 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1458}
1459
1460/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001461 * fu_common_dump_raw:
1462 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1463 * @title: prefix title, or %NULL
1464 * @data: buffer to print
1465 * @len: the size of @data
1466 *
1467 * Dumps a raw buffer to the screen.
1468 *
1469 * Since: 1.2.2
1470 **/
1471void
1472fu_common_dump_raw (const gchar *log_domain,
1473 const gchar *title,
1474 const guint8 *data,
1475 gsize len)
1476{
Richard Hughes35481862019-01-06 12:01:58 +00001477 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1478 if (len > 64)
1479 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1480 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001481}
1482
1483/**
Mario Limonciello39602652019-04-29 21:08:58 -05001484 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001485 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1486 * @title: prefix title, or %NULL
1487 * @bytes: a #GBytes
1488 *
1489 * Dumps a byte buffer to the screen.
1490 *
1491 * Since: 1.2.2
1492 **/
1493void
1494fu_common_dump_bytes (const gchar *log_domain,
1495 const gchar *title,
1496 GBytes *bytes)
1497{
1498 gsize len = 0;
1499 const guint8 *data = g_bytes_get_data (bytes, &len);
1500 fu_common_dump_raw (log_domain, title, data, len);
1501}
Richard Hughesfc90f392019-01-15 21:21:16 +00001502
1503/**
1504 * fu_common_bytes_align:
1505 * @bytes: a #GBytes
1506 * @blksz: block size in bytes
1507 * @padval: the byte used to pad the byte buffer
1508 *
1509 * Aligns a block of memory to @blksize using the @padval value; if
1510 * the block is already aligned then the original @bytes is returned.
1511 *
1512 * Returns: (transfer full): a #GBytes, possibly @bytes
1513 *
1514 * Since: 1.2.4
1515 **/
1516GBytes *
1517fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1518{
1519 const guint8 *data;
1520 gsize sz;
1521
1522 g_return_val_if_fail (bytes != NULL, NULL);
1523 g_return_val_if_fail (blksz > 0, NULL);
1524
1525 /* pad */
1526 data = g_bytes_get_data (bytes, &sz);
1527 if (sz % blksz != 0) {
1528 gsize sz_align = ((sz / blksz) + 1) * blksz;
1529 guint8 *data_align = g_malloc (sz_align);
1530 memcpy (data_align, data, sz);
1531 memset (data_align + sz, padval, sz_align - sz);
1532 g_debug ("aligning 0x%x bytes to 0x%x",
1533 (guint) sz, (guint) sz_align);
1534 return g_bytes_new_take (data_align, sz_align);
1535 }
1536
1537 /* perfectly aligned */
1538 return g_bytes_ref (bytes);
1539}
Richard Hughes36999462019-03-19 20:23:29 +00001540
1541/**
1542 * fu_common_bytes_is_empty:
1543 * @bytes: a #GBytes
1544 *
1545 * Checks if a byte array are just empty (0xff) bytes.
1546 *
1547 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001548 *
1549 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001550 **/
1551gboolean
1552fu_common_bytes_is_empty (GBytes *bytes)
1553{
1554 gsize sz = 0;
1555 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1556 for (gsize i = 0; i < sz; i++) {
1557 if (buf[i] != 0xff)
1558 return FALSE;
1559 }
1560 return TRUE;
1561}
Richard Hughes2aad1042019-03-21 09:03:32 +00001562
1563/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001564 * fu_common_bytes_compare_raw:
1565 * @buf1: a buffer
1566 * @bufsz1: sizeof @buf1
1567 * @buf2: another buffer
1568 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001569 * @error: A #GError or %NULL
1570 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001571 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001572 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001573 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001574 *
1575 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001576 **/
1577gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001578fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1579 const guint8 *buf2, gsize bufsz2,
1580 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001581{
Richard Hughes38245ff2019-09-18 14:46:09 +01001582 g_return_val_if_fail (buf1 != NULL, FALSE);
1583 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001584 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1585
1586 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001587 if (bufsz1 != bufsz2) {
1588 g_set_error (error,
1589 G_IO_ERROR,
1590 G_IO_ERROR_INVALID_DATA,
1591 "got %" G_GSIZE_FORMAT " bytes, expected "
1592 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1593 return FALSE;
1594 }
1595
1596 /* check matches */
1597 for (guint i = 0x0; i < bufsz1; i++) {
1598 if (buf1[i] != buf2[i]) {
1599 g_set_error (error,
1600 G_IO_ERROR,
1601 G_IO_ERROR_INVALID_DATA,
1602 "got 0x%02x, expected 0x%02x @ 0x%04x",
1603 buf1[i], buf2[i], i);
1604 return FALSE;
1605 }
1606 }
1607
1608 /* success */
1609 return TRUE;
1610}
Richard Hughes484ee292019-03-22 16:10:50 +00001611
1612/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001613 * fu_common_bytes_compare:
1614 * @bytes1: a #GBytes
1615 * @bytes2: another #GBytes
1616 * @error: A #GError or %NULL
1617 *
1618 * Compares the buffers for equality.
1619 *
1620 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001621 *
1622 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001623 **/
1624gboolean
1625fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1626{
1627 const guint8 *buf1;
1628 const guint8 *buf2;
1629 gsize bufsz1;
1630 gsize bufsz2;
1631
1632 g_return_val_if_fail (bytes1 != NULL, FALSE);
1633 g_return_val_if_fail (bytes2 != NULL, FALSE);
1634 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1635
1636 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1637 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1638 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1639}
1640
1641/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001642 * fu_common_bytes_pad:
1643 * @bytes: a #GBytes
1644 * @sz: the desired size in bytes
1645 *
1646 * Pads a GBytes to a given @sz with `0xff`.
1647 *
1648 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001649 *
1650 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001651 **/
1652GBytes *
1653fu_common_bytes_pad (GBytes *bytes, gsize sz)
1654{
1655 gsize bytes_sz;
1656
1657 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1658
1659 /* pad */
1660 bytes_sz = g_bytes_get_size (bytes);
1661 if (bytes_sz < sz) {
1662 const guint8 *data = g_bytes_get_data (bytes, NULL);
1663 guint8 *data_new = g_malloc (sz);
1664 memcpy (data_new, data, bytes_sz);
1665 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1666 return g_bytes_new_take (data_new, sz);
1667 }
1668
1669 /* exactly right */
1670 return g_bytes_ref (bytes);
1671}
1672
1673/**
Richard Hughes05e33772020-12-08 18:37:02 +00001674 * fu_common_bytes_new_offset:
1675 * @bytes: a #GBytes
1676 * @offset: where subsection starts at
1677 * @length: length of subsection
1678 * @error: A #GError or %NULL
1679 *
1680 * Creates a #GBytes which is a subsection of another #GBytes.
1681 *
1682 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1683 *
1684 * Since: 1.5.4
1685 **/
1686GBytes *
1687fu_common_bytes_new_offset (GBytes *bytes,
1688 gsize offset,
1689 gsize length,
1690 GError **error)
1691{
1692 g_return_val_if_fail (bytes != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001693 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes05e33772020-12-08 18:37:02 +00001694
1695 /* sanity check */
1696 if (offset + length > g_bytes_get_size (bytes)) {
1697 g_set_error (error,
1698 G_IO_ERROR,
1699 G_IO_ERROR_INVALID_DATA,
1700 "cannot create bytes @0x%02x for 0x%02x "
1701 "as buffer only 0x%04x bytes in size",
1702 (guint) offset,
1703 (guint) length,
1704 (guint) g_bytes_get_size (bytes));
1705 return NULL;
1706 }
1707 return g_bytes_new_from_bytes (bytes, offset, length);
1708}
1709
1710/**
Richard Hughes484ee292019-03-22 16:10:50 +00001711 * fu_common_realpath:
1712 * @filename: a filename
1713 * @error: A #GError or %NULL
1714 *
1715 * Finds the canonicalized absolute filename for a path.
1716 *
1717 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001718 *
1719 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001720 **/
1721gchar *
1722fu_common_realpath (const gchar *filename, GError **error)
1723{
1724 char full_tmp[PATH_MAX];
1725
1726 g_return_val_if_fail (filename != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001727 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes484ee292019-03-22 16:10:50 +00001728
Richard Hughes8694dee2019-11-22 09:16:34 +00001729#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001730 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001731#else
1732 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1733#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001734 g_set_error (error,
1735 G_IO_ERROR,
1736 G_IO_ERROR_INVALID_DATA,
1737 "cannot resolve path: %s",
1738 strerror (errno));
1739 return NULL;
1740 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001741 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1742 g_set_error (error,
1743 G_IO_ERROR,
1744 G_IO_ERROR_INVALID_DATA,
1745 "cannot find path: %s",
1746 full_tmp);
1747 return NULL;
1748 }
Richard Hughes484ee292019-03-22 16:10:50 +00001749 return g_strdup (full_tmp);
1750}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001751
1752/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001753 * fu_common_fnmatch:
1754 * @pattern: a glob pattern, e.g. `*foo*`
1755 * @str: a string to match against the pattern, e.g. `bazfoobar`
1756 *
1757 * Matches a string against a glob pattern.
1758 *
1759 * Return value: %TRUE if the string matched
1760 *
1761 * Since: 1.3.5
1762 **/
1763gboolean
1764fu_common_fnmatch (const gchar *pattern, const gchar *str)
1765{
1766 g_return_val_if_fail (pattern != NULL, FALSE);
1767 g_return_val_if_fail (str != NULL, FALSE);
1768#ifdef HAVE_FNMATCH_H
1769 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001770#elif _WIN32
1771 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1772 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1773 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001774#else
1775 return g_strcmp0 (pattern, str) == 0;
1776#endif
1777}
1778
Richard Hughesa84d7a72020-05-06 12:11:51 +01001779static gint
1780fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1781{
1782 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1783}
1784
1785/**
1786 * fu_common_filename_glob:
1787 * @directory: a directory path
1788 * @pattern: a glob pattern, e.g. `*foo*`
1789 * @error: A #GError or %NULL
1790 *
1791 * Returns all the filenames that match a specific glob pattern.
1792 * Any results are sorted. No matching files will set @error.
1793 *
1794 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1795 *
1796 * Since: 1.5.0
1797 **/
1798GPtrArray *
1799fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1800{
1801 const gchar *basename;
Richard Hughes6a489a92020-12-22 10:32:06 +00001802 g_autoptr(GDir) dir = NULL;
Richard Hughesa84d7a72020-05-06 12:11:51 +01001803 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +00001804
1805 g_return_val_if_fail (directory != NULL, NULL);
1806 g_return_val_if_fail (pattern != NULL, NULL);
1807 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1808
1809 dir = g_dir_open (directory, 0, error);
Richard Hughesa84d7a72020-05-06 12:11:51 +01001810 if (dir == NULL)
1811 return NULL;
1812 while ((basename = g_dir_read_name (dir)) != NULL) {
1813 if (!fu_common_fnmatch (pattern, basename))
1814 continue;
1815 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1816 }
1817 if (files->len == 0) {
1818 g_set_error_literal (error,
1819 G_IO_ERROR,
1820 G_IO_ERROR_NOT_FOUND,
1821 "no files matched pattern");
1822 return NULL;
1823 }
1824 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1825 return g_steal_pointer (&files);
1826}
1827
Richard Hughes5c508de2019-11-22 09:57:34 +00001828/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001829 * fu_common_strnsplit:
1830 * @str: a string to split
1831 * @sz: size of @str
1832 * @delimiter: a string which specifies the places at which to split the string
1833 * @max_tokens: the maximum number of pieces to split @str into
1834 *
1835 * Splits a string into a maximum of @max_tokens pieces, using the given
1836 * delimiter. If @max_tokens is reached, the remainder of string is appended
1837 * to the last token.
1838 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001839 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001840 *
1841 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001842 **/
1843gchar **
1844fu_common_strnsplit (const gchar *str, gsize sz,
1845 const gchar *delimiter, gint max_tokens)
1846{
1847 if (str[sz - 1] != '\0') {
1848 g_autofree gchar *str2 = g_strndup (str, sz);
1849 return g_strsplit (str2, delimiter, max_tokens);
1850 }
1851 return g_strsplit (str, delimiter, max_tokens);
1852}
Richard Hughes5308ea42019-08-09 12:25:13 +01001853
1854/**
Richard Hughes364e2682021-01-05 14:38:15 +00001855 * fu_common_strsafe:
1856 * @str: (nullable): a string to make safe for printing
1857 * @maxsz: maximum size of returned string
1858 *
1859 * Converts a string into something that can be safely printed.
1860 *
1861 * Return value: (transfer full): safe string, or %NULL if there was nothing valid
1862 *
1863 * Since: 1.5.5
1864 **/
1865gchar *
1866fu_common_strsafe (const gchar *str, gsize maxsz)
1867{
1868 gboolean valid = FALSE;
1869 g_autoptr(GString) tmp = NULL;
1870
1871 g_return_val_if_fail (maxsz > 0, NULL);
1872
1873 /* sanity check */
1874 if (str == NULL)
1875 return NULL;
1876
1877 /* replace non-printable chars with '.' */
1878 tmp = g_string_sized_new (strlen (str));
1879 for (gsize i = 0; str[i] != '\0' && i < maxsz; i++) {
1880 if (!g_ascii_isprint (str[i])) {
1881 g_string_append_c (tmp, '.');
1882 continue;
1883 }
1884 g_string_append_c (tmp, str[i]);
1885 valid = TRUE;
1886 }
1887
1888 /* if just junk, don't return 'all dots' */
1889 if (tmp->len == 0 || !valid)
1890 return NULL;
1891 return g_string_free (g_steal_pointer (&tmp), FALSE);
1892}
1893
1894/**
Richard Hughes5308ea42019-08-09 12:25:13 +01001895 * fu_memcpy_safe:
1896 * @dst: destination buffer
1897 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1898 * @dst_offset: offset in bytes into @dst to copy to
1899 * @src: source buffer
1900 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1901 * @src_offset: offset in bytes into @src to copy from
1902 * @n: number of bytes to copy from @src+@offset from
1903 * @error: A #GError or %NULL
1904 *
1905 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1906 * of both the destination and the source allows us to check for buffer overflow.
1907 *
1908 * Providing the buffer offsets also allows us to check reading past the end of
1909 * the source buffer. For this reason the caller should NEVER add an offset to
1910 * @src or @dst.
1911 *
1912 * You don't need to use this function in "obviously correct" cases, nor should
1913 * you use it when performance is a concern. Only us it when you're not sure if
1914 * malicious data from a device or firmware could cause memory corruption.
1915 *
1916 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001917 *
1918 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001919 **/
1920gboolean
1921fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1922 const guint8 *src, gsize src_sz, gsize src_offset,
1923 gsize n, GError **error)
1924{
Richard Hughes6a489a92020-12-22 10:32:06 +00001925 g_return_val_if_fail (dst != NULL, FALSE);
1926 g_return_val_if_fail (src != NULL, FALSE);
1927 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1928
Richard Hughes5308ea42019-08-09 12:25:13 +01001929 if (n == 0)
1930 return TRUE;
1931
1932 if (n > src_sz) {
1933 g_set_error (error,
1934 FWUPD_ERROR,
1935 FWUPD_ERROR_READ,
1936 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1937 (guint) n, (guint) src_sz);
1938 return FALSE;
1939 }
1940 if (n + src_offset > src_sz) {
1941 g_set_error (error,
1942 FWUPD_ERROR,
1943 FWUPD_ERROR_READ,
1944 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1945 (guint) n, (guint) src_offset, (guint) src_sz);
1946 return FALSE;
1947 }
1948 if (n > dst_sz) {
1949 g_set_error (error,
1950 FWUPD_ERROR,
1951 FWUPD_ERROR_WRITE,
1952 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1953 (guint) n, (guint) dst_sz);
1954 return FALSE;
1955 }
1956 if (n + dst_offset > dst_sz) {
1957 g_set_error (error,
1958 FWUPD_ERROR,
1959 FWUPD_ERROR_WRITE,
1960 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1961 (guint) n, (guint) dst_offset, (guint) dst_sz);
1962 return FALSE;
1963 }
1964
1965 /* phew! */
1966 memcpy (dst + dst_offset, src + src_offset, n);
1967 return TRUE;
1968}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001969
Richard Hughes80768f52019-10-22 07:19:14 +01001970/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001971 * fu_common_read_uint8_safe:
1972 * @buf: source buffer
1973 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1974 * @offset: offset in bytes into @buf to copy from
1975 * @value: (out) (allow-none): the parsed value
1976 * @error: A #GError or %NULL
1977 *
1978 * Read a value from a buffer in a safe way.
1979 *
1980 * You don't need to use this function in "obviously correct" cases, nor should
1981 * you use it when performance is a concern. Only us it when you're not sure if
1982 * malicious data from a device or firmware could cause memory corruption.
1983 *
1984 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001985 *
1986 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001987 **/
1988gboolean
1989fu_common_read_uint8_safe (const guint8 *buf,
1990 gsize bufsz,
1991 gsize offset,
1992 guint8 *value,
1993 GError **error)
1994{
1995 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00001996
1997 g_return_val_if_fail (buf != NULL, FALSE);
1998 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1999
Richard Hughesc21a0b92019-10-24 12:24:37 +01002000 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
2001 buf, bufsz, offset, /* src */
2002 sizeof(tmp), error))
2003 return FALSE;
2004 if (value != NULL)
2005 *value = tmp;
2006 return TRUE;
2007}
2008
2009/**
Richard Hughes80768f52019-10-22 07:19:14 +01002010 * fu_common_read_uint16_safe:
2011 * @buf: source buffer
2012 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2013 * @offset: offset in bytes into @buf to copy from
2014 * @value: (out) (allow-none): the parsed value
2015 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2016 * @error: A #GError or %NULL
2017 *
2018 * Read a value from a buffer using a specified endian in a safe way.
2019 *
2020 * You don't need to use this function in "obviously correct" cases, nor should
2021 * you use it when performance is a concern. Only us it when you're not sure if
2022 * malicious data from a device or firmware could cause memory corruption.
2023 *
2024 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002025 *
2026 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002027 **/
2028gboolean
2029fu_common_read_uint16_safe (const guint8 *buf,
2030 gsize bufsz,
2031 gsize offset,
2032 guint16 *value,
2033 FuEndianType endian,
2034 GError **error)
2035{
2036 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002037
2038 g_return_val_if_fail (buf != NULL, FALSE);
2039 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2040
Richard Hughes7d01ac92019-10-23 14:31:46 +01002041 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002042 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002043 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002044 return FALSE;
2045 if (value != NULL)
2046 *value = fu_common_read_uint16 (dst, endian);
2047 return TRUE;
2048}
2049
2050/**
2051 * fu_common_read_uint32_safe:
2052 * @buf: source buffer
2053 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2054 * @offset: offset in bytes into @buf to copy from
2055 * @value: (out) (allow-none): the parsed value
2056 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2057 * @error: A #GError or %NULL
2058 *
2059 * Read a value from a buffer using a specified endian in a safe way.
2060 *
2061 * You don't need to use this function in "obviously correct" cases, nor should
2062 * you use it when performance is a concern. Only us it when you're not sure if
2063 * malicious data from a device or firmware could cause memory corruption.
2064 *
2065 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002066 *
2067 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002068 **/
2069gboolean
2070fu_common_read_uint32_safe (const guint8 *buf,
2071 gsize bufsz,
2072 gsize offset,
2073 guint32 *value,
2074 FuEndianType endian,
2075 GError **error)
2076{
2077 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002078
2079 g_return_val_if_fail (buf != NULL, FALSE);
2080 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2081
Richard Hughes7d01ac92019-10-23 14:31:46 +01002082 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002083 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002084 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002085 return FALSE;
2086 if (value != NULL)
2087 *value = fu_common_read_uint32 (dst, endian);
2088 return TRUE;
2089}
2090
Mario Limonciello1a680f32019-11-25 19:44:53 -06002091/**
2092 * fu_byte_array_append_uint8:
2093 * @array: A #GByteArray
2094 * @data: #guint8
2095 *
2096 * Adds a 8 bit integer to a byte array
2097 *
2098 * Since: 1.3.1
2099 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002100void
2101fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2102{
2103 g_byte_array_append (array, &data, sizeof(data));
2104}
2105
Mario Limonciello1a680f32019-11-25 19:44:53 -06002106/**
2107 * fu_byte_array_append_uint16:
2108 * @array: A #GByteArray
2109 * @data: #guint16
2110 * @endian: #FuEndianType
2111 *
2112 * Adds a 16 bit integer to a byte array
2113 *
2114 * Since: 1.3.1
2115 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002116void
2117fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2118{
2119 guint8 buf[2];
2120 fu_common_write_uint16 (buf, data, endian);
2121 g_byte_array_append (array, buf, sizeof(buf));
2122}
2123
Mario Limonciello1a680f32019-11-25 19:44:53 -06002124/**
2125 * fu_byte_array_append_uint32:
2126 * @array: A #GByteArray
2127 * @data: #guint32
2128 * @endian: #FuEndianType
2129 *
2130 * Adds a 32 bit integer to a byte array
2131 *
2132 * Since: 1.3.1
2133 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002134void
2135fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2136{
2137 guint8 buf[4];
2138 fu_common_write_uint32 (buf, data, endian);
2139 g_byte_array_append (array, buf, sizeof(buf));
2140}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002141
2142/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002143 * fu_byte_array_set_size:
2144 * @array: a #GByteArray
2145 * @length: the new size of the GByteArray
2146 *
2147 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2148 *
2149 * Since: 1.5.0
2150 **/
2151void
2152fu_byte_array_set_size (GByteArray *array, guint length)
2153{
2154 guint oldlength = array->len;
2155 g_byte_array_set_size (array, length);
2156 if (length > oldlength)
2157 memset (array->data + oldlength, 0x0, length - oldlength);
2158}
2159
2160/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002161 * fu_common_kernel_locked_down:
2162 *
2163 * Determines if kernel lockdown in effect
2164 *
2165 * Since: 1.3.8
2166 **/
2167gboolean
2168fu_common_kernel_locked_down (void)
2169{
2170#ifndef _WIN32
2171 gsize len = 0;
2172 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2173 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2174 g_autofree gchar *data = NULL;
2175 g_auto(GStrv) options = NULL;
2176
2177 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2178 return FALSE;
2179 if (!g_file_get_contents (fname, &data, &len, NULL))
2180 return FALSE;
2181 if (len < 1)
2182 return FALSE;
2183 options = g_strsplit (data, " ", -1);
2184 for (guint i = 0; options[i] != NULL; i++) {
2185 if (g_strcmp0 (options[i], "[none]") == 0)
2186 return FALSE;
2187 }
2188 return TRUE;
2189#else
2190 return FALSE;
2191#endif
2192}
Richard Hughes9223c892020-05-09 20:32:08 +01002193
2194/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002195 * fu_common_cpuid:
2196 * @leaf: The CPUID level, now called the 'leaf' by Intel
2197 * @eax: (out) (nullable): EAX register
2198 * @ebx: (out) (nullable): EBX register
2199 * @ecx: (out) (nullable): ECX register
2200 * @edx: (out) (nullable): EDX register
2201 * @error: A #GError or NULL
2202 *
2203 * Calls CPUID and returns the registers for the given leaf.
2204 *
2205 * Return value: %TRUE if the registers are set.
2206 *
2207 * Since: 1.5.0
2208 **/
2209gboolean
2210fu_common_cpuid (guint32 leaf,
2211 guint32 *eax,
2212 guint32 *ebx,
2213 guint32 *ecx,
2214 guint32 *edx,
2215 GError **error)
2216{
2217#ifdef HAVE_CPUID_H
2218 guint eax_tmp = 0;
2219 guint ebx_tmp = 0;
2220 guint ecx_tmp = 0;
2221 guint edx_tmp = 0;
2222
Richard Hughes6a489a92020-12-22 10:32:06 +00002223 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2224
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002225 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002226 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002227 if (eax != NULL)
2228 *eax = eax_tmp;
2229 if (ebx != NULL)
2230 *ebx = ebx_tmp;
2231 if (ecx != NULL)
2232 *ecx = ecx_tmp;
2233 if (edx != NULL)
2234 *edx = edx_tmp;
2235 return TRUE;
2236#else
2237 g_set_error_literal (error,
2238 G_IO_ERROR,
2239 G_IO_ERROR_NOT_SUPPORTED,
2240 "no <cpuid.h> support");
2241 return FALSE;
2242#endif
2243}
2244
2245/**
Richard Hughes9223c892020-05-09 20:32:08 +01002246 * fu_common_is_cpu_intel:
2247 *
2248 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2249 *
2250 * Return value: %TRUE if the vendor was Intel.
Richard Hughesb63cfa92021-01-05 22:57:12 +00002251 * Deprecated: 1.5.5: Use fu_common_get_cpu_vendor() instead.
Richard Hughes9223c892020-05-09 20:32:08 +01002252 *
2253 * Since: 1.5.0
2254 **/
2255gboolean
2256fu_common_is_cpu_intel (void)
2257{
Richard Hughesb63cfa92021-01-05 22:57:12 +00002258 return fu_common_get_cpu_vendor () == FU_CPU_VENDOR_INTEL;
2259}
2260
2261/**
2262 * fu_common_get_cpu_vendor:
2263 *
2264 * Uses CPUID to discover the CPU vendor.
2265 *
2266 * Return value: a #FuCpuVendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD.
2267 *
2268 * Since: 1.5.5
2269 **/
2270FuCpuVendor
2271fu_common_get_cpu_vendor (void)
2272{
2273#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002274 guint ebx = 0;
2275 guint ecx = 0;
2276 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002277
Richard Hughesb63cfa92021-01-05 22:57:12 +00002278 if (fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL)) {
2279 if (ebx == signature_INTEL_ebx &&
2280 edx == signature_INTEL_edx &&
2281 ecx == signature_INTEL_ecx) {
2282 return FU_CPU_VENDOR_INTEL;
2283 }
2284 if (ebx == signature_AMD_ebx &&
2285 edx == signature_AMD_edx &&
2286 ecx == signature_AMD_ecx) {
2287 return FU_CPU_VENDOR_AMD;
2288 }
Richard Hughes9223c892020-05-09 20:32:08 +01002289 }
Richard Hughesbd444322020-05-21 12:05:03 +01002290#endif
Richard Hughesb63cfa92021-01-05 22:57:12 +00002291
2292 /* failed */
2293 return FU_CPU_VENDOR_UNKNOWN;
Richard Hughes9223c892020-05-09 20:32:08 +01002294}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002295
Richard Hughes36111472020-08-12 15:04:24 +01002296/**
2297 * fu_common_is_live_media:
2298 *
2299 * Checks if the user is running from a live media using various heuristics.
2300 *
2301 * Returns: %TRUE if live
2302 *
2303 * Since: 1.4.6
2304 **/
2305gboolean
2306fu_common_is_live_media (void)
2307{
2308 gsize bufsz = 0;
2309 g_autofree gchar *buf = NULL;
2310 g_auto(GStrv) tokens = NULL;
2311 const gchar *args[] = {
2312 "rd.live.image",
2313 "boot=live",
2314 NULL, /* last entry */
2315 };
2316 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2317 return TRUE;
2318 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2319 return FALSE;
2320 if (bufsz == 0)
2321 return FALSE;
2322 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2323 for (guint i = 0; args[i] != NULL; i++) {
2324 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2325 return TRUE;
2326 }
2327 return FALSE;
2328}
2329
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002330static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002331fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002332{
2333 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002334 const gchar *obj;
2335 g_autoptr(GVariant) output = NULL;
2336 g_autoptr(GDBusProxy) proxy = NULL;
2337 g_autoptr(GPtrArray) devices = NULL;
2338 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002339 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002340
Richard Hughes43417b22020-10-30 14:46:16 +00002341 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2342 if (connection == NULL) {
2343 g_prefix_error (error, "failed to get system bus: ");
2344 return NULL;
2345 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002346 proxy = g_dbus_proxy_new_sync (connection,
2347 G_DBUS_PROXY_FLAGS_NONE, NULL,
2348 UDISKS_DBUS_SERVICE,
2349 UDISKS_DBUS_PATH,
2350 UDISKS_DBUS_MANAGER_INTERFACE,
2351 NULL, error);
2352 if (proxy == NULL) {
2353 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2354 return NULL;
2355 }
2356 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002357 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002358 "GetBlockDevices",
2359 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002360 G_DBUS_CALL_FLAGS_NONE,
2361 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002362 if (output == NULL) {
2363 if (error != NULL)
2364 g_dbus_error_strip_remote_error (*error);
2365 g_prefix_error (error, "failed to call %s.%s(): ",
2366 UDISKS_DBUS_SERVICE,
2367 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002368 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002369 }
Richard Hughes43417b22020-10-30 14:46:16 +00002370 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002371 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002372 while (g_variant_iter_next (iter, "&o", &obj)) {
2373 g_autoptr(GDBusProxy) proxy_blk = NULL;
2374 proxy_blk = g_dbus_proxy_new_sync (connection,
2375 G_DBUS_PROXY_FLAGS_NONE, NULL,
2376 UDISKS_DBUS_SERVICE,
2377 obj,
2378 UDISKS_DBUS_INTERFACE_BLOCK,
2379 NULL, error);
2380 if (proxy_blk == NULL) {
2381 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2382 return NULL;
2383 }
2384 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2385 }
2386
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002387
2388 return g_steal_pointer (&devices);
2389}
2390
2391/**
2392 * fu_common_get_volumes_by_kind:
2393 * @kind: A volume kind, typically a GUID
2394 * @error: A #GError or NULL
2395 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002396 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002397 *
2398 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2399 *
2400 * Since: 1.4.6
2401 **/
2402GPtrArray *
2403fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2404{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002405 g_autoptr(GPtrArray) devices = NULL;
2406 g_autoptr(GPtrArray) volumes = NULL;
2407
Richard Hughes6a489a92020-12-22 10:32:06 +00002408 g_return_val_if_fail (kind != NULL, NULL);
2409 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2410
Richard Hughes43417b22020-10-30 14:46:16 +00002411 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002412 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002413 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002414 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2415 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002416 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002417 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002418 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002419 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002420 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002421 g_autoptr(GVariant) val = NULL;
2422
Richard Hughes43417b22020-10-30 14:46:16 +00002423 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002424 G_DBUS_PROXY_FLAGS_NONE, NULL,
2425 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002426 g_dbus_proxy_get_object_path (proxy_blk),
2427 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002428 NULL, error);
2429 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002430 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2431 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002432 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002433 }
2434 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2435 if (val == NULL)
2436 continue;
2437
Richard Hughesdb344d52020-09-09 19:42:27 +01002438 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002439 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2440 G_DBUS_PROXY_FLAGS_NONE, NULL,
2441 UDISKS_DBUS_SERVICE,
2442 g_dbus_proxy_get_object_path (proxy_blk),
2443 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2444 NULL, error);
2445 if (proxy_fs == NULL) {
2446 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2447 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002448 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002449 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002450 vol = g_object_new (FU_TYPE_VOLUME,
2451 "proxy-block", proxy_blk,
2452 "proxy-filesystem", proxy_fs,
2453 NULL);
2454 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2455 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2456 fu_volume_is_internal (vol),
2457 fu_volume_get_id_type (vol));
2458 if (g_strcmp0 (type_str, kind) != 0)
2459 continue;
2460 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002461 }
2462 if (volumes->len == 0) {
2463 g_set_error (error,
2464 G_IO_ERROR,
2465 G_IO_ERROR_NOT_FOUND,
2466 "no volumes of type %s", kind);
2467 return NULL;
2468 }
2469 return g_steal_pointer (&volumes);
2470}
2471
2472/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002473 * fu_common_get_volume_by_device:
2474 * @device: A device string, typcically starting with `/dev/`
2475 * @error: A #GError or NULL
2476 *
2477 * Finds the first volume from the specified device.
2478 *
2479 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2480 *
2481 * Since: 1.5.1
2482 **/
2483FuVolume *
2484fu_common_get_volume_by_device (const gchar *device, GError **error)
2485{
2486 g_autoptr(GPtrArray) devices = NULL;
2487
Richard Hughes6a489a92020-12-22 10:32:06 +00002488 g_return_val_if_fail (device != NULL, NULL);
2489 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2490
Richard Hughes0bdf5612020-10-30 14:56:22 +00002491 /* find matching block device */
2492 devices = fu_common_get_block_devices (error);
2493 if (devices == NULL)
2494 return NULL;
2495 for (guint i = 0; i < devices->len; i++) {
2496 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2497 g_autoptr(GVariant) val = NULL;
2498 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2499 if (val == NULL)
2500 continue;
2501 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2502 return g_object_new (FU_TYPE_VOLUME,
2503 "proxy-block", proxy_blk,
2504 NULL);
2505 }
2506 }
2507
2508 /* failed */
2509 g_set_error (error,
2510 G_IO_ERROR,
2511 G_IO_ERROR_NOT_FOUND,
2512 "no volumes for device %s",
2513 device);
2514 return NULL;
2515}
2516
2517/**
2518 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002519 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002520 * @error: A #GError or NULL
2521 *
2522 * Finds the first volume from the specified device.
2523 *
2524 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2525 *
2526 * Since: 1.5.1
2527 **/
2528FuVolume *
2529fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2530{
2531 g_autoptr(GPtrArray) devices = NULL;
2532
Richard Hughes6a489a92020-12-22 10:32:06 +00002533 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2534
Richard Hughes0bdf5612020-10-30 14:56:22 +00002535 /* find matching block device */
2536 devices = fu_common_get_block_devices (error);
2537 if (devices == NULL)
2538 return NULL;
2539 for (guint i = 0; i < devices->len; i++) {
2540 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2541 g_autoptr(GVariant) val = NULL;
2542 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2543 if (val == NULL)
2544 continue;
2545 if (devnum == g_variant_get_uint64 (val)) {
2546 return g_object_new (FU_TYPE_VOLUME,
2547 "proxy-block", proxy_blk,
2548 NULL);
2549 }
2550 }
2551
2552 /* failed */
2553 g_set_error (error,
2554 G_IO_ERROR,
2555 G_IO_ERROR_NOT_FOUND,
2556 "no volumes for devnum %u",
2557 devnum);
2558 return NULL;
2559}
2560
2561/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002562 * fu_common_get_esp_default:
2563 * @error: A #GError or NULL
2564 *
2565 * Gets the platform default ESP
2566 *
2567 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2568 *
2569 * Since: 1.4.6
2570 **/
2571FuVolume *
2572fu_common_get_esp_default (GError **error)
2573{
2574 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002575 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002576 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2577 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002578 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002579 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002580 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002581
Richard Hughes6a489a92020-12-22 10:32:06 +00002582 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2583
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002584 /* for the test suite use local directory for ESP */
2585 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2586 if (path_tmp != NULL)
2587 return fu_volume_new_from_mount_path (path_tmp);
2588
Mario Limonciello56d816a2020-11-11 16:59:30 -06002589 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2590 if (volumes == NULL) {
2591 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2592 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2593 if (volumes == NULL) {
2594 g_prefix_error (error, "%s: ", error_local->message);
2595 return NULL;
2596 }
2597 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002598
2599 /* are there _any_ internal vfat partitions?
2600 * remember HintSystem is just that -- a hint! */
2601 for (guint i = 0; i < volumes->len; i++) {
2602 FuVolume *vol = g_ptr_array_index (volumes, i);
2603 g_autofree gchar *type = fu_volume_get_id_type (vol);
2604 if (g_strcmp0 (type, "vfat") == 0 &&
2605 fu_volume_is_internal (vol)) {
2606 has_internal = TRUE;
2607 break;
2608 }
2609 }
2610
2611 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002612 for (guint i = 0; i < volumes->len; i++) {
2613 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002614 g_autofree gchar *type = fu_volume_get_id_type (vol);
2615 if (type == NULL)
2616 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002617 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002618 continue;
2619 if (g_strcmp0 (type, "vfat") == 0)
2620 g_ptr_array_add (volumes_vfat, vol);
2621 }
2622 if (volumes_vfat->len == 0) {
2623 g_set_error (error,
2624 G_IO_ERROR,
2625 G_IO_ERROR_INVALID_FILENAME,
2626 "No ESP found");
2627 return NULL;
2628 }
2629 for (guint i = 0; i < volumes_vfat->len; i++) {
2630 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002631 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2632 }
2633 if (volumes_mtab->len == 1) {
2634 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2635 return g_object_ref (vol);
2636 }
2637 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2638 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2639 return g_object_ref (vol);
2640 }
2641 g_set_error (error,
2642 G_IO_ERROR,
2643 G_IO_ERROR_INVALID_FILENAME,
2644 "More than one available ESP");
2645 return NULL;
2646}
2647
2648/**
2649 * fu_common_get_esp_for_path:
2650 * @esp_path: A path to the ESP
2651 * @error: A #GError or NULL
2652 *
2653 * Gets the platform ESP using a UNIX or UDisks path
2654 *
2655 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2656 *
2657 * Since: 1.4.6
2658 **/
2659FuVolume *
2660fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2661{
Richard Hughes6a489a92020-12-22 10:32:06 +00002662 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002663 g_autoptr(GPtrArray) volumes = NULL;
2664
Richard Hughes6a489a92020-12-22 10:32:06 +00002665 g_return_val_if_fail (esp_path != NULL, NULL);
2666 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2667
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002668 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2669 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002670 return NULL;
Richard Hughes6a489a92020-12-22 10:32:06 +00002671 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002672 for (guint i = 0; i < volumes->len; i++) {
2673 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002674 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002675 if (g_strcmp0 (basename, vol_basename) == 0)
2676 return g_object_ref (vol);
2677 }
2678 g_set_error (error,
2679 G_IO_ERROR,
2680 G_IO_ERROR_INVALID_FILENAME,
2681 "No ESP with path %s",
2682 esp_path);
2683 return NULL;
2684}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002685
2686/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002687 * fu_common_crc8:
2688 * @buf: memory buffer
2689 * @bufsz: sizeof buf
2690 *
2691 * Returns the cyclic redundancy check value for the given memory buffer.
2692 *
2693 * Returns: CRC value
2694 *
2695 * Since: 1.5.0
2696 **/
2697guint8
2698fu_common_crc8 (const guint8 *buf, gsize bufsz)
2699{
2700 guint32 crc = 0;
2701 for (gsize j = bufsz; j > 0; j--) {
2702 crc ^= (*(buf++) << 8);
2703 for (guint32 i = 8; i; i--) {
2704 if (crc & 0x8000)
2705 crc ^= (0x1070 << 3);
2706 crc <<= 1;
2707 }
2708 }
2709 return ~((guint8) (crc >> 8));
2710}
2711
2712/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002713 * fu_common_crc16:
2714 * @buf: memory buffer
2715 * @bufsz: sizeof buf
2716 *
2717 * Returns the cyclic redundancy check value for the given memory buffer.
2718 *
2719 * Returns: CRC value
2720 *
2721 * Since: 1.5.0
2722 **/
2723guint16
2724fu_common_crc16 (const guint8 *buf, gsize bufsz)
2725{
2726 guint16 crc = 0xffff;
2727 for (gsize len = bufsz; len > 0; len--) {
2728 crc = (guint16) (crc ^ (*buf++));
2729 for (guint8 i = 0; i < 8; i++) {
2730 if (crc & 0x1) {
2731 crc = (crc >> 1) ^ 0xa001;
2732 } else {
2733 crc >>= 1;
2734 }
2735 }
2736 }
2737 return ~crc;
2738}
2739
2740/**
2741 * fu_common_crc32_full:
2742 * @buf: memory buffer
2743 * @bufsz: sizeof buf
2744 * @crc: initial CRC value, typically 0xFFFFFFFF
2745 * @polynomial: CRC polynomial, typically 0xEDB88320
2746 *
2747 * Returns the cyclic redundancy check value for the given memory buffer.
2748 *
2749 * Returns: CRC value
2750 *
2751 * Since: 1.5.0
2752 **/
2753guint32
2754fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2755{
2756 for (guint32 idx = 0; idx < bufsz; idx++) {
2757 guint8 data = *buf++;
2758 crc = crc ^ data;
2759 for (guint32 bit = 0; bit < 8; bit++) {
2760 guint32 mask = -(crc & 1);
2761 crc = (crc >> 1) ^ (polynomial & mask);
2762 }
2763 }
2764 return ~crc;
2765}
2766
2767/**
2768 * fu_common_crc32:
2769 * @buf: memory buffer
2770 * @bufsz: sizeof buf
2771 *
2772 * Returns the cyclic redundancy check value for the given memory buffer.
2773 *
2774 * Returns: CRC value
2775 *
2776 * Since: 1.5.0
2777 **/
2778guint32
2779fu_common_crc32 (const guint8 *buf, gsize bufsz)
2780{
2781 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2782}