blob: 1755552b2ffdc79d57af68a60e12bc8852859e55 [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 Hughes94f939a2017-08-08 12:21:39 +010026#include <archive_entry.h>
27#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010028#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000029#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010032
33#include "fwupd-error.h"
34
35#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010036#include "fu-volume-private.h"
37
Richard Hughes43417b22020-10-30 14:46:16 +000038#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
39#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
40#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
41#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
42#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
43#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010044
45/**
Richard Hughes4eada342017-10-03 21:20:32 +010046 * SECTION:fu-common
47 * @short_description: common functionality for plugins to use
48 *
49 * Helper functions that can be used by the daemon and plugins.
50 *
51 * See also: #FuPlugin
52 */
53
54/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010055 * fu_common_rmtree:
56 * @directory: a directory name
57 * @error: A #GError or %NULL
58 *
59 * Recursively removes a directory.
60 *
61 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060062 *
63 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010064 **/
65gboolean
66fu_common_rmtree (const gchar *directory, GError **error)
67{
68 const gchar *filename;
69 g_autoptr(GDir) dir = NULL;
70
Richard Hughes6a489a92020-12-22 10:32:06 +000071 g_return_val_if_fail (directory != NULL, FALSE);
72 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
73
Richard Hughes954dd9f2017-08-08 13:36:25 +010074 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010075 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010076 dir = g_dir_open (directory, 0, error);
77 if (dir == NULL)
78 return FALSE;
79
80 /* find each */
81 while ((filename = g_dir_read_name (dir))) {
82 g_autofree gchar *src = NULL;
83 src = g_build_filename (directory, filename, NULL);
84 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
85 if (!fu_common_rmtree (src, error))
86 return FALSE;
87 } else {
88 if (g_unlink (src) != 0) {
89 g_set_error (error,
90 FWUPD_ERROR,
91 FWUPD_ERROR_INTERNAL,
92 "Failed to delete: %s", src);
93 return FALSE;
94 }
95 }
96 }
97 if (g_remove (directory) != 0) {
98 g_set_error (error,
99 FWUPD_ERROR,
100 FWUPD_ERROR_INTERNAL,
101 "Failed to delete: %s", directory);
102 return FALSE;
103 }
104 return TRUE;
105}
106
Richard Hughes89e968b2018-03-07 10:01:08 +0000107static gboolean
108fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
109{
110 const gchar *filename;
111 g_autoptr(GDir) dir = NULL;
112
113 /* try to open */
114 dir = g_dir_open (directory, 0, error);
115 if (dir == NULL)
116 return FALSE;
117
118 /* find each */
119 while ((filename = g_dir_read_name (dir))) {
120 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
121 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
122 if (!fu_common_get_file_list_internal (files, src, error))
123 return FALSE;
124 } else {
125 g_ptr_array_add (files, g_steal_pointer (&src));
126 }
127 }
128 return TRUE;
129
130}
131
132/**
133 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100134 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000135 * @error: A #GError or %NULL
136 *
137 * Returns every file found under @directory, and any subdirectory.
138 * If any path under @directory cannot be accessed due to permissions an error
139 * will be returned.
140 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000141 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600142 *
143 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000144 **/
145GPtrArray *
146fu_common_get_files_recursive (const gchar *path, GError **error)
147{
148 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +0000149
150 g_return_val_if_fail (path != NULL, NULL);
151 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
152
Richard Hughes89e968b2018-03-07 10:01:08 +0000153 if (!fu_common_get_file_list_internal (files, path, error))
154 return NULL;
155 return g_steal_pointer (&files);
156}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100157/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100158 * fu_common_mkdir_parent:
159 * @filename: A full pathname
160 * @error: A #GError, or %NULL
161 *
162 * Creates any required directories, including any parent directories.
163 *
164 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600165 *
166 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100167 **/
168gboolean
169fu_common_mkdir_parent (const gchar *filename, GError **error)
170{
171 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100172
Richard Hughes6a489a92020-12-22 10:32:06 +0000173 g_return_val_if_fail (filename != NULL, FALSE);
174 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
175
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100176 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500177 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
178 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100179 if (g_mkdir_with_parents (parent, 0755) == -1) {
180 g_set_error (error,
181 FWUPD_ERROR,
182 FWUPD_ERROR_INTERNAL,
183 "Failed to create '%s': %s",
184 parent, g_strerror (errno));
185 return FALSE;
186 }
187 return TRUE;
188}
189
190/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100191 * fu_common_set_contents_bytes:
192 * @filename: A filename
193 * @bytes: The data to write
194 * @error: A #GError, or %NULL
195 *
196 * Writes a blob of data to a filename, creating the parent directories as
197 * required.
198 *
199 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600200 *
201 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100202 **/
203gboolean
204fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
205{
206 const gchar *data;
207 gsize size;
208 g_autoptr(GFile) file = NULL;
209 g_autoptr(GFile) file_parent = NULL;
210
Richard Hughes6a489a92020-12-22 10:32:06 +0000211 g_return_val_if_fail (filename != NULL, FALSE);
212 g_return_val_if_fail (bytes != NULL, FALSE);
213 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
214
Richard Hughes943d2c92017-06-21 09:04:39 +0100215 file = g_file_new_for_path (filename);
216 file_parent = g_file_get_parent (file);
217 if (!g_file_query_exists (file_parent, NULL)) {
218 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
219 return FALSE;
220 }
221 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100222 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100223 return g_file_set_contents (filename, data, size, error);
224}
225
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100226/**
227 * fu_common_get_contents_bytes:
228 * @filename: A filename
229 * @error: A #GError, or %NULL
230 *
231 * Reads a blob of data from a file.
232 *
233 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600234 *
235 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100236 **/
237GBytes *
238fu_common_get_contents_bytes (const gchar *filename, GError **error)
239{
240 gchar *data = NULL;
241 gsize len = 0;
Richard Hughes6a489a92020-12-22 10:32:06 +0000242
243 g_return_val_if_fail (filename != NULL, NULL);
244 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
245
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100246 if (!g_file_get_contents (filename, &data, &len, error))
247 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100248 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100249 return g_bytes_new_take (data, len);
250}
Richard Hughes943d2c92017-06-21 09:04:39 +0100251
252/**
253 * fu_common_get_contents_fd:
254 * @fd: A file descriptor
255 * @count: The maximum number of bytes to read
256 * @error: A #GError, or %NULL
257 *
258 * Reads a blob from a specific file descriptor.
259 *
260 * Note: this will close the fd when done
261 *
Richard Hughes4eada342017-10-03 21:20:32 +0100262 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600263 *
264 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100265 **/
266GBytes *
267fu_common_get_contents_fd (gint fd, gsize count, GError **error)
268{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000269#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100270 g_autoptr(GBytes) blob = NULL;
271 g_autoptr(GError) error_local = NULL;
272 g_autoptr(GInputStream) stream = NULL;
273
274 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100275 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
276
Richard Hughes919f8ab2018-02-14 10:24:56 +0000277 /* this is invalid */
278 if (count == 0) {
279 g_set_error_literal (error,
280 FWUPD_ERROR,
281 FWUPD_ERROR_NOT_SUPPORTED,
282 "A maximum read size must be specified");
283 return NULL;
284 }
285
Richard Hughes943d2c92017-06-21 09:04:39 +0100286 /* read the entire fd to a data blob */
287 stream = g_unix_input_stream_new (fd, TRUE);
288 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
289 if (blob == NULL) {
290 g_set_error_literal (error,
291 FWUPD_ERROR,
292 FWUPD_ERROR_INVALID_FILE,
293 error_local->message);
294 return NULL;
295 }
296 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000297#else
298 g_set_error_literal (error,
299 FWUPD_ERROR,
300 FWUPD_ERROR_NOT_SUPPORTED,
301 "Not supported as <glib-unix.h> is unavailable");
302 return NULL;
303#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100304}
Richard Hughes94f939a2017-08-08 12:21:39 +0100305
306static gboolean
307fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
308{
309 const gchar *tmp;
310 g_autofree gchar *buf = NULL;
311
312 /* no output file */
313 if (archive_entry_pathname (entry) == NULL)
314 return FALSE;
315
316 /* update output path */
317 tmp = archive_entry_pathname (entry);
318 buf = g_build_filename (dir, tmp, NULL);
319 archive_entry_update_pathname_utf8 (entry, buf);
320 return TRUE;
321}
322
323/**
324 * fu_common_extract_archive:
325 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100326 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100327 * @error: A #GError, or %NULL
328 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000329 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100330 *
331 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600332 *
333 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100334 **/
335gboolean
336fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
337{
338 gboolean ret = TRUE;
339 int r;
340 struct archive *arch = NULL;
341 struct archive_entry *entry;
342
Richard Hughes6a489a92020-12-22 10:32:06 +0000343 g_return_val_if_fail (blob != NULL, FALSE);
344 g_return_val_if_fail (dir != NULL, FALSE);
345 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
346
Richard Hughes94f939a2017-08-08 12:21:39 +0100347 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100348 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100349 arch = archive_read_new ();
350 archive_read_support_format_all (arch);
351 archive_read_support_filter_all (arch);
352 r = archive_read_open_memory (arch,
353 (void *) g_bytes_get_data (blob, NULL),
354 (size_t) g_bytes_get_size (blob));
355 if (r != 0) {
356 ret = FALSE;
357 g_set_error (error,
358 FWUPD_ERROR,
359 FWUPD_ERROR_INTERNAL,
360 "Cannot open: %s",
361 archive_error_string (arch));
362 goto out;
363 }
364 for (;;) {
365 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100366 r = archive_read_next_header (arch, &entry);
367 if (r == ARCHIVE_EOF)
368 break;
369 if (r != ARCHIVE_OK) {
370 ret = FALSE;
371 g_set_error (error,
372 FWUPD_ERROR,
373 FWUPD_ERROR_INTERNAL,
374 "Cannot read header: %s",
375 archive_error_string (arch));
376 goto out;
377 }
378
379 /* only extract if valid */
380 valid = fu_common_extract_archive_entry (entry, dir);
381 if (!valid)
382 continue;
383 r = archive_read_extract (arch, entry, 0);
384 if (r != ARCHIVE_OK) {
385 ret = FALSE;
386 g_set_error (error,
387 FWUPD_ERROR,
388 FWUPD_ERROR_INTERNAL,
389 "Cannot extract: %s",
390 archive_error_string (arch));
391 goto out;
392 }
393 }
394out:
395 if (arch != NULL) {
396 archive_read_close (arch);
397 archive_read_free (arch);
398 }
399 return ret;
400}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100401
402static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300403fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
404
405static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100406fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
407{
408 va_list args;
409 g_autofree gchar *tmp = NULL;
410 g_auto(GStrv) split = NULL;
411
412 va_start (args, fmt);
413 tmp = g_strdup_vprintf (fmt, args);
414 va_end (args);
415
416 split = g_strsplit (tmp, " ", -1);
417 for (guint i = 0; split[i] != NULL; i++)
418 g_ptr_array_add (argv, g_strdup (split[i]));
419}
420
Mario Limonciello1a680f32019-11-25 19:44:53 -0600421/**
422 * fu_common_find_program_in_path:
423 * @basename: The program to search
424 * @error: A #GError, or %NULL
425 *
426 * Looks for a program in the PATH variable
427 *
428 * Returns: a new #gchar, or %NULL for error
429 *
430 * Since: 1.1.2
431 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100432gchar *
433fu_common_find_program_in_path (const gchar *basename, GError **error)
434{
435 gchar *fn = g_find_program_in_path (basename);
436 if (fn == NULL) {
437 g_set_error (error,
438 FWUPD_ERROR,
439 FWUPD_ERROR_NOT_SUPPORTED,
440 "missing executable %s in PATH",
441 basename);
442 return NULL;
443 }
444 return fn;
445}
446
447static gboolean
448fu_common_test_namespace_support (GError **error)
449{
450 /* test if CONFIG_USER_NS is valid */
451 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
452 g_set_error (error,
453 FWUPD_ERROR,
454 FWUPD_ERROR_NOT_SUPPORTED,
455 "missing CONFIG_USER_NS in kernel");
456 return FALSE;
457 }
458 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
459 g_autofree gchar *clone = NULL;
460 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
461 return FALSE;
462 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
463 g_set_error (error,
464 FWUPD_ERROR,
465 FWUPD_ERROR_NOT_SUPPORTED,
466 "unprivileged user namespace clones disabled by distro");
467 return FALSE;
468 }
469 }
470 return TRUE;
471}
472
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100473/**
474 * fu_common_firmware_builder:
475 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100476 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
477 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100478 * @error: A #GError, or %NULL
479 *
480 * Builds a firmware file using tools from the host session in a bubblewrap
481 * jail. Several things happen during build:
482 *
483 * 1. The @bytes data is untarred to a temporary location
484 * 2. A bubblewrap container is set up
485 * 3. The startup.sh script is run inside the container
486 * 4. The firmware.bin is extracted from the container
487 * 5. The temporary location is deleted
488 *
489 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600490 *
491 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100492 **/
493GBytes *
494fu_common_firmware_builder (GBytes *bytes,
495 const gchar *script_fn,
496 const gchar *output_fn,
497 GError **error)
498{
499 gint rc = 0;
500 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500501 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100502 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100503 g_autofree gchar *localstatedir = NULL;
504 g_autofree gchar *output2_fn = NULL;
505 g_autofree gchar *standard_error = NULL;
506 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507 g_autofree gchar *tmpdir = NULL;
508 g_autoptr(GBytes) firmware_blob = NULL;
509 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
510
511 g_return_val_if_fail (bytes != NULL, NULL);
512 g_return_val_if_fail (script_fn != NULL, NULL);
513 g_return_val_if_fail (output_fn != NULL, NULL);
514 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
515
Mario Limonciello37b59582018-08-13 08:38:01 -0500516 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100517 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
518 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100519 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500520
521 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100522 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100523 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500524
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100525 /* untar file to temp location */
526 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
527 if (tmpdir == NULL)
528 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100529 if (!fu_common_extract_archive (bytes, tmpdir, error))
530 return NULL;
531
532 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100533 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
534 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100535
536 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500537 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100538 fu_common_add_argv (argv, "--die-with-parent");
539 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500540 fu_common_add_argv (argv, "--ro-bind /lib /lib");
Mario Limoncielloed4e9122020-12-15 20:26:50 -0600541 fu_common_add_argv (argv, "--ro-bind-try /lib64 /lib64");
Mario Limonciellob8215572018-07-13 09:49:55 -0500542 fu_common_add_argv (argv, "--ro-bind /bin /bin");
543 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100544 fu_common_add_argv (argv, "--dir /tmp");
545 fu_common_add_argv (argv, "--dir /var");
546 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100547 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
548 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100549 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100550 fu_common_add_argv (argv, "--chdir /tmp");
551 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100552 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100553 g_ptr_array_add (argv, NULL);
554 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
555 g_debug ("running '%s' in %s", argv_str, tmpdir);
556 if (!g_spawn_sync ("/tmp",
557 (gchar **) argv->pdata,
558 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100559 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100560 NULL, NULL, /* child_setup */
561 &standard_output,
562 &standard_error,
563 &rc,
564 error)) {
565 g_prefix_error (error, "failed to run '%s': ", argv_str);
566 return NULL;
567 }
568 if (standard_output != NULL && standard_output[0] != '\0')
569 g_debug ("console output was: %s", standard_output);
570 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500571 FwupdError code = FWUPD_ERROR_INTERNAL;
572 if (errno == ENOTTY)
573 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100574 g_set_error (error,
575 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500576 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100577 "failed to build firmware: %s",
578 standard_error);
579 return NULL;
580 }
581
582 /* get generated file */
583 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
584 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
585 if (firmware_blob == NULL)
586 return NULL;
587
588 /* cleanup temp directory */
589 if (!fu_common_rmtree (tmpdir, error))
590 return NULL;
591
592 /* success */
593 return g_steal_pointer (&firmware_blob);
594}
Richard Hughes049ccc82017-08-09 15:26:56 +0100595
596typedef struct {
597 FuOutputHandler handler_cb;
598 gpointer handler_user_data;
599 GMainLoop *loop;
600 GSource *source;
601 GInputStream *stream;
602 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000603 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100604} FuCommonSpawnHelper;
605
606static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
607
608static gboolean
609fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
610{
611 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
612 gchar buffer[1024];
613 gssize sz;
614 g_auto(GStrv) split = NULL;
615 g_autoptr(GError) error = NULL;
616
617 /* read from stream */
618 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
619 buffer,
620 sizeof(buffer) - 1,
621 NULL,
622 &error);
623 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100624 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
625 g_warning ("failed to get read from nonblocking fd: %s",
626 error->message);
627 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100628 return G_SOURCE_REMOVE;
629 }
630
631 /* no read possible */
632 if (sz == 0)
633 g_main_loop_quit (helper->loop);
634
635 /* emit lines */
636 if (helper->handler_cb != NULL) {
637 buffer[sz] = '\0';
638 split = g_strsplit (buffer, "\n", -1);
639 for (guint i = 0; split[i] != NULL; i++) {
640 if (split[i][0] == '\0')
641 continue;
642 helper->handler_cb (split[i], helper->handler_user_data);
643 }
644 }
645
646 /* set up the source for the next read */
647 fu_common_spawn_create_pollable_source (helper);
648 return G_SOURCE_REMOVE;
649}
650
651static void
652fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
653{
654 if (helper->source != NULL)
655 g_source_destroy (helper->source);
656 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
657 helper->cancellable);
658 g_source_attach (helper->source, NULL);
659 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
660}
661
662static void
663fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
664{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000665 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100666 if (helper->stream != NULL)
667 g_object_unref (helper->stream);
668 if (helper->source != NULL)
669 g_source_destroy (helper->source);
670 if (helper->loop != NULL)
671 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000672 if (helper->timeout_id != 0)
673 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100674 g_free (helper);
675}
676
Mario Limoncielloa98df552018-04-16 12:15:51 -0500677#pragma clang diagnostic push
678#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100679G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500680#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100681
Richard Hughesb768e4d2019-02-26 13:55:18 +0000682static gboolean
683fu_common_spawn_timeout_cb (gpointer user_data)
684{
685 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
686 g_cancellable_cancel (helper->cancellable);
687 g_main_loop_quit (helper->loop);
688 helper->timeout_id = 0;
689 return G_SOURCE_REMOVE;
690}
691
692static void
693fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
694{
695 /* just propagate */
696 g_cancellable_cancel (helper->cancellable);
697}
698
Richard Hughes049ccc82017-08-09 15:26:56 +0100699/**
700 * fu_common_spawn_sync:
701 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100702 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
703 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000704 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100705 * @cancellable: a #GCancellable, or %NULL
706 * @error: A #GError or %NULL
707 *
708 * Runs a subprocess and waits for it to exit. Any output on standard out or
709 * standard error will be forwarded to @handler_cb as whole lines.
710 *
711 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600712 *
713 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100714 **/
715gboolean
716fu_common_spawn_sync (const gchar * const * argv,
717 FuOutputHandler handler_cb,
718 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000719 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100720 GCancellable *cancellable, GError **error)
721{
722 g_autoptr(FuCommonSpawnHelper) helper = NULL;
723 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100724 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000725 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100726
Richard Hughes6a489a92020-12-22 10:32:06 +0000727 g_return_val_if_fail (argv != NULL, FALSE);
728 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
729 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
730
Richard Hughes049ccc82017-08-09 15:26:56 +0100731 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100732 argv_str = g_strjoinv (" ", (gchar **) argv);
733 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100734 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
735 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
736 if (subprocess == NULL)
737 return FALSE;
738
739 /* watch for process to exit */
740 helper = g_new0 (FuCommonSpawnHelper, 1);
741 helper->handler_cb = handler_cb;
742 helper->handler_user_data = handler_user_data;
743 helper->loop = g_main_loop_new (NULL, FALSE);
744 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000745
746 /* always create a cancellable, and connect up the parent */
747 helper->cancellable = g_cancellable_new ();
748 if (cancellable != NULL) {
749 cancellable_id = g_cancellable_connect (cancellable,
750 G_CALLBACK (fu_common_spawn_cancelled_cb),
751 helper, NULL);
752 }
753
754 /* allow timeout */
755 if (timeout_ms > 0) {
756 helper->timeout_id = g_timeout_add (timeout_ms,
757 fu_common_spawn_timeout_cb,
758 helper);
759 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100760 fu_common_spawn_create_pollable_source (helper);
761 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000762 g_cancellable_disconnect (cancellable, cancellable_id);
763 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
764 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100765 return g_subprocess_wait_check (subprocess, cancellable, error);
766}
Richard Hughesae252cd2017-12-08 10:48:15 +0000767
768/**
769 * fu_common_write_uint16:
770 * @buf: A writable buffer
771 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100772 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000773 *
774 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600775 *
776 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000777 **/
778void
779fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
780{
781 guint16 val_hw;
782 switch (endian) {
783 case G_BIG_ENDIAN:
784 val_hw = GUINT16_TO_BE(val_native);
785 break;
786 case G_LITTLE_ENDIAN:
787 val_hw = GUINT16_TO_LE(val_native);
788 break;
789 default:
790 g_assert_not_reached ();
791 }
792 memcpy (buf, &val_hw, sizeof(val_hw));
793}
794
795/**
796 * fu_common_write_uint32:
797 * @buf: A writable buffer
798 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100799 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000800 *
801 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600802 *
803 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000804 **/
805void
806fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
807{
808 guint32 val_hw;
809 switch (endian) {
810 case G_BIG_ENDIAN:
811 val_hw = GUINT32_TO_BE(val_native);
812 break;
813 case G_LITTLE_ENDIAN:
814 val_hw = GUINT32_TO_LE(val_native);
815 break;
816 default:
817 g_assert_not_reached ();
818 }
819 memcpy (buf, &val_hw, sizeof(val_hw));
820}
821
822/**
823 * fu_common_read_uint16:
824 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100825 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000826 *
827 * Read a value from a buffer using a specified endian.
828 *
829 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600830 *
831 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000832 **/
833guint16
834fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
835{
836 guint16 val_hw, val_native;
837 memcpy (&val_hw, buf, sizeof(val_hw));
838 switch (endian) {
839 case G_BIG_ENDIAN:
840 val_native = GUINT16_FROM_BE(val_hw);
841 break;
842 case G_LITTLE_ENDIAN:
843 val_native = GUINT16_FROM_LE(val_hw);
844 break;
845 default:
846 g_assert_not_reached ();
847 }
848 return val_native;
849}
850
851/**
852 * fu_common_read_uint32:
853 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100854 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000855 *
856 * Read a value from a buffer using a specified endian.
857 *
858 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600859 *
860 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000861 **/
862guint32
863fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
864{
865 guint32 val_hw, val_native;
866 memcpy (&val_hw, buf, sizeof(val_hw));
867 switch (endian) {
868 case G_BIG_ENDIAN:
869 val_native = GUINT32_FROM_BE(val_hw);
870 break;
871 case G_LITTLE_ENDIAN:
872 val_native = GUINT32_FROM_LE(val_hw);
873 break;
874 default:
875 g_assert_not_reached ();
876 }
877 return val_native;
878}
Richard Hughese82eef32018-05-20 10:41:26 +0100879
Richard Hughes73bf2332018-08-28 09:38:09 +0100880/**
881 * fu_common_strtoull:
882 * @str: A string, e.g. "0x1234"
883 *
884 * Converts a string value to an integer. Values are assumed base 10, unless
885 * prefixed with "0x" where they are parsed as base 16.
886 *
887 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600888 *
889 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100890 **/
891guint64
892fu_common_strtoull (const gchar *str)
893{
894 guint base = 10;
895 if (str == NULL)
896 return 0x0;
897 if (g_str_has_prefix (str, "0x")) {
898 str += 2;
899 base = 16;
900 }
901 return g_ascii_strtoull (str, NULL, base);
902}
903
Richard Hughesa574a752018-08-31 13:31:03 +0100904/**
905 * fu_common_strstrip:
906 * @str: A string, e.g. " test "
907 *
908 * Removes leading and trailing whitespace from a constant string.
909 *
910 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600911 *
912 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100913 **/
914gchar *
915fu_common_strstrip (const gchar *str)
916{
917 guint head = G_MAXUINT;
918 guint tail = 0;
919
920 g_return_val_if_fail (str != NULL, NULL);
921
922 /* find first non-space char */
923 for (guint i = 0; str[i] != '\0'; i++) {
924 if (str[i] != ' ') {
925 head = i;
926 break;
927 }
928 }
929 if (head == G_MAXUINT)
930 return g_strdup ("");
931
932 /* find last non-space char */
933 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500934 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100935 tail = i;
936 }
937 return g_strndup (str + head, tail - head + 1);
938}
939
Richard Hughese82eef32018-05-20 10:41:26 +0100940static const GError *
941fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
942{
943 for (guint j = 0; j < errors->len; j++) {
944 const GError *error = g_ptr_array_index (errors, j);
945 if (g_error_matches (error, FWUPD_ERROR, error_code))
946 return error;
947 }
948 return NULL;
949}
950
951static guint
952fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
953{
954 guint cnt = 0;
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 cnt++;
959 }
960 return cnt;
961}
962
963static gboolean
964fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
965{
966 for (guint j = 0; j < errors->len; j++) {
967 const GError *error = g_ptr_array_index (errors, j);
968 gboolean matches_any = FALSE;
969 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
970 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
971 matches_any = TRUE;
972 break;
973 }
974 }
975 if (!matches_any)
976 return FALSE;
977 }
978 return TRUE;
979}
980
981/**
982 * fu_common_error_array_get_best:
983 * @errors: (element-type GError): array of errors
984 *
985 * Finds the 'best' error to show the user from a array of errors, creating a
986 * completely bespoke error where required.
987 *
988 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600989 *
990 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100991 **/
992GError *
993fu_common_error_array_get_best (GPtrArray *errors)
994{
995 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
996 FWUPD_ERROR_VERSION_SAME,
997 FWUPD_ERROR_VERSION_NEWER,
998 FWUPD_ERROR_NOT_SUPPORTED,
999 FWUPD_ERROR_INTERNAL,
1000 FWUPD_ERROR_NOT_FOUND,
1001 FWUPD_ERROR_LAST };
1002 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
1003 FWUPD_ERROR_NOT_FOUND,
1004 FWUPD_ERROR_NOT_SUPPORTED,
1005 FWUPD_ERROR_LAST };
1006 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
1007 FWUPD_ERROR_VERSION_SAME,
1008 FWUPD_ERROR_NOT_FOUND,
1009 FWUPD_ERROR_NOT_SUPPORTED,
1010 FWUPD_ERROR_LAST };
1011
1012 /* are all the errors either GUID-not-matched or version-same? */
1013 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
1014 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
1015 return g_error_new (FWUPD_ERROR,
1016 FWUPD_ERROR_NOTHING_TO_DO,
1017 "All updatable firmware is already installed");
1018 }
1019
1020 /* are all the errors either GUID-not-matched or version same or newer? */
1021 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
1022 fu_common_error_array_matches_any (errors, err_all_newer)) {
1023 return g_error_new (FWUPD_ERROR,
1024 FWUPD_ERROR_NOTHING_TO_DO,
1025 "All updatable devices already have newer versions");
1026 }
1027
1028 /* get the most important single error */
1029 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1030 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1031 if (error_tmp != NULL)
1032 return g_error_copy (error_tmp);
1033 }
1034
1035 /* fall back to something */
1036 return g_error_new (FWUPD_ERROR,
1037 FWUPD_ERROR_NOT_FOUND,
1038 "No supported devices found");
1039}
Richard Hughes4be17d12018-05-30 20:36:29 +01001040
1041/**
1042 * fu_common_get_path:
1043 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1044 *
1045 * Gets a fwupd-specific system path. These can be overridden with various
1046 * environment variables, for instance %FWUPD_DATADIR.
1047 *
1048 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001049 *
1050 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001051 **/
1052gchar *
1053fu_common_get_path (FuPathKind path_kind)
1054{
1055 const gchar *tmp;
1056 g_autofree gchar *basedir = NULL;
1057
1058 switch (path_kind) {
1059 /* /var */
1060 case FU_PATH_KIND_LOCALSTATEDIR:
1061 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1062 if (tmp != NULL)
1063 return g_strdup (tmp);
1064 tmp = g_getenv ("SNAP_USER_DATA");
1065 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001066 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1067 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001068 /* /proc */
1069 case FU_PATH_KIND_PROCFS:
1070 tmp = g_getenv ("FWUPD_PROCFS");
1071 if (tmp != NULL)
1072 return g_strdup (tmp);
1073 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001074 /* /sys/firmware */
1075 case FU_PATH_KIND_SYSFSDIR_FW:
1076 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1077 if (tmp != NULL)
1078 return g_strdup (tmp);
1079 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001080 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001081 case FU_PATH_KIND_SYSFSDIR_TPM:
1082 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1083 if (tmp != NULL)
1084 return g_strdup (tmp);
1085 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001086 /* /sys/bus/platform/drivers */
1087 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1088 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1089 if (tmp != NULL)
1090 return g_strdup (tmp);
1091 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001092 /* /sys/kernel/security */
1093 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1094 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1095 if (tmp != NULL)
1096 return g_strdup (tmp);
1097 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001098 /* /sys/firmware/acpi/tables */
1099 case FU_PATH_KIND_ACPI_TABLES:
1100 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1101 if (tmp != NULL)
1102 return g_strdup (tmp);
1103 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001104 /* /etc */
1105 case FU_PATH_KIND_SYSCONFDIR:
1106 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1107 if (tmp != NULL)
1108 return g_strdup (tmp);
1109 tmp = g_getenv ("SNAP_USER_DATA");
1110 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001111 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1112 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001113 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1114 case FU_PATH_KIND_PLUGINDIR_PKG:
1115 tmp = g_getenv ("FWUPD_PLUGINDIR");
1116 if (tmp != NULL)
1117 return g_strdup (tmp);
1118 tmp = g_getenv ("SNAP");
1119 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001120 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1121 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001122 /* /usr/share/fwupd */
1123 case FU_PATH_KIND_DATADIR_PKG:
1124 tmp = g_getenv ("FWUPD_DATADIR");
1125 if (tmp != NULL)
1126 return g_strdup (tmp);
1127 tmp = g_getenv ("SNAP");
1128 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001129 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1130 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001131 /* /usr/libexec/fwupd/efi */
1132 case FU_PATH_KIND_EFIAPPDIR:
1133 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1134 if (tmp != NULL)
1135 return g_strdup (tmp);
1136#ifdef EFI_APP_LOCATION
1137 tmp = g_getenv ("SNAP");
1138 if (tmp != NULL)
1139 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1140 return g_strdup (EFI_APP_LOCATION);
1141#else
1142 return NULL;
1143#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001144 /* /etc/fwupd */
1145 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001146 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001147 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001148 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001149 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1150 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1151 /* /var/lib/fwupd */
1152 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001153 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001154 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001155 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001156 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1157 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1158 /* /var/cache/fwupd */
1159 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001160 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001161 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001162 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001163 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1164 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001165 case FU_PATH_KIND_OFFLINE_TRIGGER:
1166 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1167 if (tmp != NULL)
1168 return g_strdup (tmp);
1169 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001170 case FU_PATH_KIND_POLKIT_ACTIONS:
1171#ifdef POLKIT_ACTIONDIR
1172 return g_strdup (POLKIT_ACTIONDIR);
1173#else
1174 return NULL;
1175#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001176 /* this shouldn't happen */
1177 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001178 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001179 }
1180
1181 return NULL;
1182}
Richard Hughes83e56c12018-10-10 20:24:41 +01001183
1184/**
1185 * fu_common_string_replace:
1186 * @string: The #GString to operate on
1187 * @search: The text to search for
1188 * @replace: The text to use for substitutions
1189 *
1190 * Performs multiple search and replace operations on the given string.
1191 *
1192 * Returns: the number of replacements done, or 0 if @search is not found.
1193 *
1194 * Since: 1.2.0
1195 **/
1196guint
1197fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1198{
1199 gchar *tmp;
1200 guint count = 0;
1201 gsize search_idx = 0;
1202 gsize replace_len;
1203 gsize search_len;
1204
1205 g_return_val_if_fail (string != NULL, 0);
1206 g_return_val_if_fail (search != NULL, 0);
1207 g_return_val_if_fail (replace != NULL, 0);
1208
1209 /* nothing to do */
1210 if (string->len == 0)
1211 return 0;
1212
1213 search_len = strlen (search);
1214 replace_len = strlen (replace);
1215
1216 do {
1217 tmp = g_strstr_len (string->str + search_idx, -1, search);
1218 if (tmp == NULL)
1219 break;
1220
1221 /* advance the counter in case @replace contains @search */
1222 search_idx = (gsize) (tmp - string->str);
1223
1224 /* reallocate the string if required */
1225 if (search_len > replace_len) {
1226 g_string_erase (string,
1227 (gssize) search_idx,
1228 (gssize) (search_len - replace_len));
1229 memcpy (tmp, replace, replace_len);
1230 } else if (search_len < replace_len) {
1231 g_string_insert_len (string,
1232 (gssize) search_idx,
1233 replace,
1234 (gssize) (replace_len - search_len));
1235 /* we have to treat this specially as it could have
1236 * been reallocated when the insertion happened */
1237 memcpy (string->str + search_idx, replace, replace_len);
1238 } else {
1239 /* just memcmp in the new string */
1240 memcpy (tmp, replace, replace_len);
1241 }
1242 search_idx += replace_len;
1243 count++;
1244 } while (TRUE);
1245
1246 return count;
1247}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001248
Richard Hughesae96a1f2019-09-23 11:16:36 +01001249/**
1250 * fu_common_strwidth:
1251 * @text: The string to operate on
1252 *
1253 * Returns the width of the string in displayed characters on the console.
1254 *
1255 * Returns: width of text
1256 *
1257 * Since: 1.3.2
1258 **/
1259gsize
1260fu_common_strwidth (const gchar *text)
1261{
1262 const gchar *p = text;
1263 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001264
1265 g_return_val_if_fail (text != NULL, 0);
1266
Richard Hughesae96a1f2019-09-23 11:16:36 +01001267 while (*p) {
1268 gunichar c = g_utf8_get_char (p);
1269 if (g_unichar_iswide (c))
1270 width += 2;
1271 else if (!g_unichar_iszerowidth (c))
1272 width += 1;
1273 p = g_utf8_next_char (p);
1274 }
1275 return width;
1276}
1277
Mario Limonciello1a680f32019-11-25 19:44:53 -06001278/**
1279 * fu_common_string_append_kv:
1280 * @str: A #GString
1281 * @idt: The indent
1282 * @key: A string to append
1283 * @value: a string to append
1284 *
1285 * Appends a key and string value to a string
1286 *
1287 * Since: 1.2.4
1288 */
Richard Hughescea28de2019-08-09 11:16:40 +01001289void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001290fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001291{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001292 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001293 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001294
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001295 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001296
1297 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001298 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001299 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001300 for (gsize i = 0; i < idt; i++)
1301 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001302 if (key[0] != '\0') {
1303 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001304 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001305 } else {
1306 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001307 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001308 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001309 g_auto(GStrv) split = NULL;
1310 split = g_strsplit (value, "\n", -1);
1311 for (guint i = 0; split[i] != NULL; i++) {
1312 if (i == 0) {
1313 for (gsize j = keysz; j < align; j++)
1314 g_string_append (str, " ");
1315 } else {
1316 for (gsize j = 0; j < idt; j++)
1317 g_string_append (str, " ");
1318 }
1319 g_string_append (str, split[i]);
1320 g_string_append (str, "\n");
1321 }
1322 } else {
1323 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001324 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001325}
1326
Mario Limonciello1a680f32019-11-25 19:44:53 -06001327/**
1328 * fu_common_string_append_ku:
1329 * @str: A #GString
1330 * @idt: The indent
1331 * @key: A string to append
1332 * @value: guint64
1333 *
1334 * Appends a key and unsigned integer to a string
1335 *
1336 * Since: 1.2.4
1337 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001338void
1339fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1340{
1341 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1342 fu_common_string_append_kv (str, idt, key, tmp);
1343}
1344
Mario Limonciello1a680f32019-11-25 19:44:53 -06001345/**
1346 * fu_common_string_append_kx:
1347 * @str: A #GString
1348 * @idt: The indent
1349 * @key: A string to append
1350 * @value: guint64
1351 *
1352 * Appends a key and hex integer to a string
1353 *
1354 * Since: 1.2.4
1355 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001356void
1357fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1358{
1359 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1360 fu_common_string_append_kv (str, idt, key, tmp);
1361}
1362
Mario Limonciello1a680f32019-11-25 19:44:53 -06001363/**
1364 * fu_common_string_append_kb:
1365 * @str: A #GString
1366 * @idt: The indent
1367 * @key: A string to append
1368 * @value: Boolean
1369 *
1370 * Appends a key and boolean value to a string
1371 *
1372 * Since: 1.2.4
1373 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001374void
1375fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1376{
1377 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001378}
1379
Richard Hughese59cb9a2018-12-05 14:37:40 +00001380/**
Richard Hughes35481862019-01-06 12:01:58 +00001381 * fu_common_dump_full:
1382 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1383 * @title: prefix title, or %NULL
1384 * @data: buffer to print
1385 * @len: the size of @data
1386 * @columns: break new lines after this many bytes
1387 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1388 *
1389 * Dumps a raw buffer to the screen.
1390 *
1391 * Since: 1.2.4
1392 **/
1393void
1394fu_common_dump_full (const gchar *log_domain,
1395 const gchar *title,
1396 const guint8 *data,
1397 gsize len,
1398 guint columns,
1399 FuDumpFlags flags)
1400{
1401 g_autoptr(GString) str = g_string_new (NULL);
1402
1403 /* optional */
1404 if (title != NULL)
1405 g_string_append_printf (str, "%s:", title);
1406
1407 /* if more than can fit on one line then start afresh */
1408 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1409 g_string_append (str, "\n");
1410 } else {
1411 for (gsize i = str->len; i < 16; i++)
1412 g_string_append (str, " ");
1413 }
1414
1415 /* offset line */
1416 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1417 g_string_append (str, " │ ");
1418 for (gsize i = 0; i < columns; i++)
1419 g_string_append_printf (str, "%02x ", (guint) i);
1420 g_string_append (str, "\n───────┼");
1421 for (gsize i = 0; i < columns; i++)
1422 g_string_append (str, "───");
1423 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1424 }
1425
1426 /* print each row */
1427 for (gsize i = 0; i < len; i++) {
1428 g_string_append_printf (str, "%02x ", data[i]);
1429
1430 /* optionally print ASCII char */
1431 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1432 if (g_ascii_isprint (data[i]))
1433 g_string_append_printf (str, "[%c] ", data[i]);
1434 else
1435 g_string_append (str, "[?] ");
1436 }
1437
1438 /* new row required */
1439 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1440 g_string_append (str, "\n");
1441 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1442 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1443 }
1444 }
1445 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1446}
1447
1448/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001449 * fu_common_dump_raw:
1450 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1451 * @title: prefix title, or %NULL
1452 * @data: buffer to print
1453 * @len: the size of @data
1454 *
1455 * Dumps a raw buffer to the screen.
1456 *
1457 * Since: 1.2.2
1458 **/
1459void
1460fu_common_dump_raw (const gchar *log_domain,
1461 const gchar *title,
1462 const guint8 *data,
1463 gsize len)
1464{
Richard Hughes35481862019-01-06 12:01:58 +00001465 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1466 if (len > 64)
1467 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1468 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001469}
1470
1471/**
Mario Limonciello39602652019-04-29 21:08:58 -05001472 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001473 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1474 * @title: prefix title, or %NULL
1475 * @bytes: a #GBytes
1476 *
1477 * Dumps a byte buffer to the screen.
1478 *
1479 * Since: 1.2.2
1480 **/
1481void
1482fu_common_dump_bytes (const gchar *log_domain,
1483 const gchar *title,
1484 GBytes *bytes)
1485{
1486 gsize len = 0;
1487 const guint8 *data = g_bytes_get_data (bytes, &len);
1488 fu_common_dump_raw (log_domain, title, data, len);
1489}
Richard Hughesfc90f392019-01-15 21:21:16 +00001490
1491/**
1492 * fu_common_bytes_align:
1493 * @bytes: a #GBytes
1494 * @blksz: block size in bytes
1495 * @padval: the byte used to pad the byte buffer
1496 *
1497 * Aligns a block of memory to @blksize using the @padval value; if
1498 * the block is already aligned then the original @bytes is returned.
1499 *
1500 * Returns: (transfer full): a #GBytes, possibly @bytes
1501 *
1502 * Since: 1.2.4
1503 **/
1504GBytes *
1505fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1506{
1507 const guint8 *data;
1508 gsize sz;
1509
1510 g_return_val_if_fail (bytes != NULL, NULL);
1511 g_return_val_if_fail (blksz > 0, NULL);
1512
1513 /* pad */
1514 data = g_bytes_get_data (bytes, &sz);
1515 if (sz % blksz != 0) {
1516 gsize sz_align = ((sz / blksz) + 1) * blksz;
1517 guint8 *data_align = g_malloc (sz_align);
1518 memcpy (data_align, data, sz);
1519 memset (data_align + sz, padval, sz_align - sz);
1520 g_debug ("aligning 0x%x bytes to 0x%x",
1521 (guint) sz, (guint) sz_align);
1522 return g_bytes_new_take (data_align, sz_align);
1523 }
1524
1525 /* perfectly aligned */
1526 return g_bytes_ref (bytes);
1527}
Richard Hughes36999462019-03-19 20:23:29 +00001528
1529/**
1530 * fu_common_bytes_is_empty:
1531 * @bytes: a #GBytes
1532 *
1533 * Checks if a byte array are just empty (0xff) bytes.
1534 *
1535 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001536 *
1537 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001538 **/
1539gboolean
1540fu_common_bytes_is_empty (GBytes *bytes)
1541{
1542 gsize sz = 0;
1543 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1544 for (gsize i = 0; i < sz; i++) {
1545 if (buf[i] != 0xff)
1546 return FALSE;
1547 }
1548 return TRUE;
1549}
Richard Hughes2aad1042019-03-21 09:03:32 +00001550
1551/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001552 * fu_common_bytes_compare_raw:
1553 * @buf1: a buffer
1554 * @bufsz1: sizeof @buf1
1555 * @buf2: another buffer
1556 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001557 * @error: A #GError or %NULL
1558 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001559 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001560 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001561 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001562 *
1563 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001564 **/
1565gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001566fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1567 const guint8 *buf2, gsize bufsz2,
1568 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001569{
Richard Hughes38245ff2019-09-18 14:46:09 +01001570 g_return_val_if_fail (buf1 != NULL, FALSE);
1571 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001572 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1573
1574 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001575 if (bufsz1 != bufsz2) {
1576 g_set_error (error,
1577 G_IO_ERROR,
1578 G_IO_ERROR_INVALID_DATA,
1579 "got %" G_GSIZE_FORMAT " bytes, expected "
1580 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1581 return FALSE;
1582 }
1583
1584 /* check matches */
1585 for (guint i = 0x0; i < bufsz1; i++) {
1586 if (buf1[i] != buf2[i]) {
1587 g_set_error (error,
1588 G_IO_ERROR,
1589 G_IO_ERROR_INVALID_DATA,
1590 "got 0x%02x, expected 0x%02x @ 0x%04x",
1591 buf1[i], buf2[i], i);
1592 return FALSE;
1593 }
1594 }
1595
1596 /* success */
1597 return TRUE;
1598}
Richard Hughes484ee292019-03-22 16:10:50 +00001599
1600/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001601 * fu_common_bytes_compare:
1602 * @bytes1: a #GBytes
1603 * @bytes2: another #GBytes
1604 * @error: A #GError or %NULL
1605 *
1606 * Compares the buffers for equality.
1607 *
1608 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001609 *
1610 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001611 **/
1612gboolean
1613fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1614{
1615 const guint8 *buf1;
1616 const guint8 *buf2;
1617 gsize bufsz1;
1618 gsize bufsz2;
1619
1620 g_return_val_if_fail (bytes1 != NULL, FALSE);
1621 g_return_val_if_fail (bytes2 != NULL, FALSE);
1622 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1623
1624 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1625 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1626 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1627}
1628
1629/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001630 * fu_common_bytes_pad:
1631 * @bytes: a #GBytes
1632 * @sz: the desired size in bytes
1633 *
1634 * Pads a GBytes to a given @sz with `0xff`.
1635 *
1636 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001637 *
1638 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001639 **/
1640GBytes *
1641fu_common_bytes_pad (GBytes *bytes, gsize sz)
1642{
1643 gsize bytes_sz;
1644
1645 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1646
1647 /* pad */
1648 bytes_sz = g_bytes_get_size (bytes);
1649 if (bytes_sz < sz) {
1650 const guint8 *data = g_bytes_get_data (bytes, NULL);
1651 guint8 *data_new = g_malloc (sz);
1652 memcpy (data_new, data, bytes_sz);
1653 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1654 return g_bytes_new_take (data_new, sz);
1655 }
1656
1657 /* exactly right */
1658 return g_bytes_ref (bytes);
1659}
1660
1661/**
Richard Hughes05e33772020-12-08 18:37:02 +00001662 * fu_common_bytes_new_offset:
1663 * @bytes: a #GBytes
1664 * @offset: where subsection starts at
1665 * @length: length of subsection
1666 * @error: A #GError or %NULL
1667 *
1668 * Creates a #GBytes which is a subsection of another #GBytes.
1669 *
1670 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1671 *
1672 * Since: 1.5.4
1673 **/
1674GBytes *
1675fu_common_bytes_new_offset (GBytes *bytes,
1676 gsize offset,
1677 gsize length,
1678 GError **error)
1679{
1680 g_return_val_if_fail (bytes != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001681 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes05e33772020-12-08 18:37:02 +00001682
1683 /* sanity check */
1684 if (offset + length > g_bytes_get_size (bytes)) {
1685 g_set_error (error,
1686 G_IO_ERROR,
1687 G_IO_ERROR_INVALID_DATA,
1688 "cannot create bytes @0x%02x for 0x%02x "
1689 "as buffer only 0x%04x bytes in size",
1690 (guint) offset,
1691 (guint) length,
1692 (guint) g_bytes_get_size (bytes));
1693 return NULL;
1694 }
1695 return g_bytes_new_from_bytes (bytes, offset, length);
1696}
1697
1698/**
Richard Hughes484ee292019-03-22 16:10:50 +00001699 * fu_common_realpath:
1700 * @filename: a filename
1701 * @error: A #GError or %NULL
1702 *
1703 * Finds the canonicalized absolute filename for a path.
1704 *
1705 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001706 *
1707 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001708 **/
1709gchar *
1710fu_common_realpath (const gchar *filename, GError **error)
1711{
1712 char full_tmp[PATH_MAX];
1713
1714 g_return_val_if_fail (filename != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001715 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes484ee292019-03-22 16:10:50 +00001716
Richard Hughes8694dee2019-11-22 09:16:34 +00001717#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001718 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001719#else
1720 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1721#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001722 g_set_error (error,
1723 G_IO_ERROR,
1724 G_IO_ERROR_INVALID_DATA,
1725 "cannot resolve path: %s",
1726 strerror (errno));
1727 return NULL;
1728 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001729 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1730 g_set_error (error,
1731 G_IO_ERROR,
1732 G_IO_ERROR_INVALID_DATA,
1733 "cannot find path: %s",
1734 full_tmp);
1735 return NULL;
1736 }
Richard Hughes484ee292019-03-22 16:10:50 +00001737 return g_strdup (full_tmp);
1738}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001739
1740/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001741 * fu_common_fnmatch:
1742 * @pattern: a glob pattern, e.g. `*foo*`
1743 * @str: a string to match against the pattern, e.g. `bazfoobar`
1744 *
1745 * Matches a string against a glob pattern.
1746 *
1747 * Return value: %TRUE if the string matched
1748 *
1749 * Since: 1.3.5
1750 **/
1751gboolean
1752fu_common_fnmatch (const gchar *pattern, const gchar *str)
1753{
1754 g_return_val_if_fail (pattern != NULL, FALSE);
1755 g_return_val_if_fail (str != NULL, FALSE);
1756#ifdef HAVE_FNMATCH_H
1757 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001758#elif _WIN32
1759 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1760 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1761 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001762#else
1763 return g_strcmp0 (pattern, str) == 0;
1764#endif
1765}
1766
Richard Hughesa84d7a72020-05-06 12:11:51 +01001767static gint
1768fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1769{
1770 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1771}
1772
1773/**
1774 * fu_common_filename_glob:
1775 * @directory: a directory path
1776 * @pattern: a glob pattern, e.g. `*foo*`
1777 * @error: A #GError or %NULL
1778 *
1779 * Returns all the filenames that match a specific glob pattern.
1780 * Any results are sorted. No matching files will set @error.
1781 *
1782 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1783 *
1784 * Since: 1.5.0
1785 **/
1786GPtrArray *
1787fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1788{
1789 const gchar *basename;
Richard Hughes6a489a92020-12-22 10:32:06 +00001790 g_autoptr(GDir) dir = NULL;
Richard Hughesa84d7a72020-05-06 12:11:51 +01001791 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +00001792
1793 g_return_val_if_fail (directory != NULL, NULL);
1794 g_return_val_if_fail (pattern != NULL, NULL);
1795 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1796
1797 dir = g_dir_open (directory, 0, error);
Richard Hughesa84d7a72020-05-06 12:11:51 +01001798 if (dir == NULL)
1799 return NULL;
1800 while ((basename = g_dir_read_name (dir)) != NULL) {
1801 if (!fu_common_fnmatch (pattern, basename))
1802 continue;
1803 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1804 }
1805 if (files->len == 0) {
1806 g_set_error_literal (error,
1807 G_IO_ERROR,
1808 G_IO_ERROR_NOT_FOUND,
1809 "no files matched pattern");
1810 return NULL;
1811 }
1812 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1813 return g_steal_pointer (&files);
1814}
1815
Richard Hughes5c508de2019-11-22 09:57:34 +00001816/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001817 * fu_common_strnsplit:
1818 * @str: a string to split
1819 * @sz: size of @str
1820 * @delimiter: a string which specifies the places at which to split the string
1821 * @max_tokens: the maximum number of pieces to split @str into
1822 *
1823 * Splits a string into a maximum of @max_tokens pieces, using the given
1824 * delimiter. If @max_tokens is reached, the remainder of string is appended
1825 * to the last token.
1826 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001827 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001828 *
1829 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001830 **/
1831gchar **
1832fu_common_strnsplit (const gchar *str, gsize sz,
1833 const gchar *delimiter, gint max_tokens)
1834{
1835 if (str[sz - 1] != '\0') {
1836 g_autofree gchar *str2 = g_strndup (str, sz);
1837 return g_strsplit (str2, delimiter, max_tokens);
1838 }
1839 return g_strsplit (str, delimiter, max_tokens);
1840}
Richard Hughes5308ea42019-08-09 12:25:13 +01001841
1842/**
Richard Hughes364e2682021-01-05 14:38:15 +00001843 * fu_common_strsafe:
1844 * @str: (nullable): a string to make safe for printing
1845 * @maxsz: maximum size of returned string
1846 *
1847 * Converts a string into something that can be safely printed.
1848 *
1849 * Return value: (transfer full): safe string, or %NULL if there was nothing valid
1850 *
1851 * Since: 1.5.5
1852 **/
1853gchar *
1854fu_common_strsafe (const gchar *str, gsize maxsz)
1855{
1856 gboolean valid = FALSE;
1857 g_autoptr(GString) tmp = NULL;
1858
1859 g_return_val_if_fail (maxsz > 0, NULL);
1860
1861 /* sanity check */
1862 if (str == NULL)
1863 return NULL;
1864
1865 /* replace non-printable chars with '.' */
1866 tmp = g_string_sized_new (strlen (str));
1867 for (gsize i = 0; str[i] != '\0' && i < maxsz; i++) {
1868 if (!g_ascii_isprint (str[i])) {
1869 g_string_append_c (tmp, '.');
1870 continue;
1871 }
1872 g_string_append_c (tmp, str[i]);
1873 valid = TRUE;
1874 }
1875
1876 /* if just junk, don't return 'all dots' */
1877 if (tmp->len == 0 || !valid)
1878 return NULL;
1879 return g_string_free (g_steal_pointer (&tmp), FALSE);
1880}
1881
1882/**
Richard Hughes5308ea42019-08-09 12:25:13 +01001883 * fu_memcpy_safe:
1884 * @dst: destination buffer
1885 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1886 * @dst_offset: offset in bytes into @dst to copy to
1887 * @src: source buffer
1888 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1889 * @src_offset: offset in bytes into @src to copy from
1890 * @n: number of bytes to copy from @src+@offset from
1891 * @error: A #GError or %NULL
1892 *
1893 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1894 * of both the destination and the source allows us to check for buffer overflow.
1895 *
1896 * Providing the buffer offsets also allows us to check reading past the end of
1897 * the source buffer. For this reason the caller should NEVER add an offset to
1898 * @src or @dst.
1899 *
1900 * You don't need to use this function in "obviously correct" cases, nor should
1901 * you use it when performance is a concern. Only us it when you're not sure if
1902 * malicious data from a device or firmware could cause memory corruption.
1903 *
1904 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001905 *
1906 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001907 **/
1908gboolean
1909fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1910 const guint8 *src, gsize src_sz, gsize src_offset,
1911 gsize n, GError **error)
1912{
Richard Hughes6a489a92020-12-22 10:32:06 +00001913 g_return_val_if_fail (dst != NULL, FALSE);
1914 g_return_val_if_fail (src != NULL, FALSE);
1915 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1916
Richard Hughes5308ea42019-08-09 12:25:13 +01001917 if (n == 0)
1918 return TRUE;
1919
1920 if (n > src_sz) {
1921 g_set_error (error,
1922 FWUPD_ERROR,
1923 FWUPD_ERROR_READ,
1924 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1925 (guint) n, (guint) src_sz);
1926 return FALSE;
1927 }
1928 if (n + src_offset > src_sz) {
1929 g_set_error (error,
1930 FWUPD_ERROR,
1931 FWUPD_ERROR_READ,
1932 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1933 (guint) n, (guint) src_offset, (guint) src_sz);
1934 return FALSE;
1935 }
1936 if (n > dst_sz) {
1937 g_set_error (error,
1938 FWUPD_ERROR,
1939 FWUPD_ERROR_WRITE,
1940 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1941 (guint) n, (guint) dst_sz);
1942 return FALSE;
1943 }
1944 if (n + dst_offset > dst_sz) {
1945 g_set_error (error,
1946 FWUPD_ERROR,
1947 FWUPD_ERROR_WRITE,
1948 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1949 (guint) n, (guint) dst_offset, (guint) dst_sz);
1950 return FALSE;
1951 }
1952
1953 /* phew! */
1954 memcpy (dst + dst_offset, src + src_offset, n);
1955 return TRUE;
1956}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001957
Richard Hughes80768f52019-10-22 07:19:14 +01001958/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001959 * fu_common_read_uint8_safe:
1960 * @buf: source buffer
1961 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1962 * @offset: offset in bytes into @buf to copy from
1963 * @value: (out) (allow-none): the parsed value
1964 * @error: A #GError or %NULL
1965 *
1966 * Read a value from a buffer in a safe way.
1967 *
1968 * You don't need to use this function in "obviously correct" cases, nor should
1969 * you use it when performance is a concern. Only us it when you're not sure if
1970 * malicious data from a device or firmware could cause memory corruption.
1971 *
1972 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001973 *
1974 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001975 **/
1976gboolean
1977fu_common_read_uint8_safe (const guint8 *buf,
1978 gsize bufsz,
1979 gsize offset,
1980 guint8 *value,
1981 GError **error)
1982{
1983 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00001984
1985 g_return_val_if_fail (buf != NULL, FALSE);
1986 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1987
Richard Hughesc21a0b92019-10-24 12:24:37 +01001988 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1989 buf, bufsz, offset, /* src */
1990 sizeof(tmp), error))
1991 return FALSE;
1992 if (value != NULL)
1993 *value = tmp;
1994 return TRUE;
1995}
1996
1997/**
Richard Hughes80768f52019-10-22 07:19:14 +01001998 * fu_common_read_uint16_safe:
1999 * @buf: source buffer
2000 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2001 * @offset: offset in bytes into @buf to copy from
2002 * @value: (out) (allow-none): the parsed value
2003 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2004 * @error: A #GError or %NULL
2005 *
2006 * Read a value from a buffer using a specified endian in a safe way.
2007 *
2008 * You don't need to use this function in "obviously correct" cases, nor should
2009 * you use it when performance is a concern. Only us it when you're not sure if
2010 * malicious data from a device or firmware could cause memory corruption.
2011 *
2012 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002013 *
2014 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002015 **/
2016gboolean
2017fu_common_read_uint16_safe (const guint8 *buf,
2018 gsize bufsz,
2019 gsize offset,
2020 guint16 *value,
2021 FuEndianType endian,
2022 GError **error)
2023{
2024 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002025
2026 g_return_val_if_fail (buf != NULL, FALSE);
2027 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2028
Richard Hughes7d01ac92019-10-23 14:31:46 +01002029 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002030 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002031 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002032 return FALSE;
2033 if (value != NULL)
2034 *value = fu_common_read_uint16 (dst, endian);
2035 return TRUE;
2036}
2037
2038/**
2039 * fu_common_read_uint32_safe:
2040 * @buf: source buffer
2041 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2042 * @offset: offset in bytes into @buf to copy from
2043 * @value: (out) (allow-none): the parsed value
2044 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2045 * @error: A #GError or %NULL
2046 *
2047 * Read a value from a buffer using a specified endian in a safe way.
2048 *
2049 * You don't need to use this function in "obviously correct" cases, nor should
2050 * you use it when performance is a concern. Only us it when you're not sure if
2051 * malicious data from a device or firmware could cause memory corruption.
2052 *
2053 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002054 *
2055 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002056 **/
2057gboolean
2058fu_common_read_uint32_safe (const guint8 *buf,
2059 gsize bufsz,
2060 gsize offset,
2061 guint32 *value,
2062 FuEndianType endian,
2063 GError **error)
2064{
2065 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002066
2067 g_return_val_if_fail (buf != NULL, FALSE);
2068 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2069
Richard Hughes7d01ac92019-10-23 14:31:46 +01002070 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002071 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002072 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002073 return FALSE;
2074 if (value != NULL)
2075 *value = fu_common_read_uint32 (dst, endian);
2076 return TRUE;
2077}
2078
Mario Limonciello1a680f32019-11-25 19:44:53 -06002079/**
2080 * fu_byte_array_append_uint8:
2081 * @array: A #GByteArray
2082 * @data: #guint8
2083 *
2084 * Adds a 8 bit integer to a byte array
2085 *
2086 * Since: 1.3.1
2087 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002088void
2089fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2090{
2091 g_byte_array_append (array, &data, sizeof(data));
2092}
2093
Mario Limonciello1a680f32019-11-25 19:44:53 -06002094/**
2095 * fu_byte_array_append_uint16:
2096 * @array: A #GByteArray
2097 * @data: #guint16
2098 * @endian: #FuEndianType
2099 *
2100 * Adds a 16 bit integer to a byte array
2101 *
2102 * Since: 1.3.1
2103 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002104void
2105fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2106{
2107 guint8 buf[2];
2108 fu_common_write_uint16 (buf, data, endian);
2109 g_byte_array_append (array, buf, sizeof(buf));
2110}
2111
Mario Limonciello1a680f32019-11-25 19:44:53 -06002112/**
2113 * fu_byte_array_append_uint32:
2114 * @array: A #GByteArray
2115 * @data: #guint32
2116 * @endian: #FuEndianType
2117 *
2118 * Adds a 32 bit integer to a byte array
2119 *
2120 * Since: 1.3.1
2121 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002122void
2123fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2124{
2125 guint8 buf[4];
2126 fu_common_write_uint32 (buf, data, endian);
2127 g_byte_array_append (array, buf, sizeof(buf));
2128}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002129
2130/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002131 * fu_byte_array_set_size:
2132 * @array: a #GByteArray
2133 * @length: the new size of the GByteArray
2134 *
2135 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2136 *
2137 * Since: 1.5.0
2138 **/
2139void
2140fu_byte_array_set_size (GByteArray *array, guint length)
2141{
2142 guint oldlength = array->len;
2143 g_byte_array_set_size (array, length);
2144 if (length > oldlength)
2145 memset (array->data + oldlength, 0x0, length - oldlength);
2146}
2147
2148/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002149 * fu_common_kernel_locked_down:
2150 *
2151 * Determines if kernel lockdown in effect
2152 *
2153 * Since: 1.3.8
2154 **/
2155gboolean
2156fu_common_kernel_locked_down (void)
2157{
2158#ifndef _WIN32
2159 gsize len = 0;
2160 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2161 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2162 g_autofree gchar *data = NULL;
2163 g_auto(GStrv) options = NULL;
2164
2165 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2166 return FALSE;
2167 if (!g_file_get_contents (fname, &data, &len, NULL))
2168 return FALSE;
2169 if (len < 1)
2170 return FALSE;
2171 options = g_strsplit (data, " ", -1);
2172 for (guint i = 0; options[i] != NULL; i++) {
2173 if (g_strcmp0 (options[i], "[none]") == 0)
2174 return FALSE;
2175 }
2176 return TRUE;
2177#else
2178 return FALSE;
2179#endif
2180}
Richard Hughes9223c892020-05-09 20:32:08 +01002181
2182/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002183 * fu_common_cpuid:
2184 * @leaf: The CPUID level, now called the 'leaf' by Intel
2185 * @eax: (out) (nullable): EAX register
2186 * @ebx: (out) (nullable): EBX register
2187 * @ecx: (out) (nullable): ECX register
2188 * @edx: (out) (nullable): EDX register
2189 * @error: A #GError or NULL
2190 *
2191 * Calls CPUID and returns the registers for the given leaf.
2192 *
2193 * Return value: %TRUE if the registers are set.
2194 *
2195 * Since: 1.5.0
2196 **/
2197gboolean
2198fu_common_cpuid (guint32 leaf,
2199 guint32 *eax,
2200 guint32 *ebx,
2201 guint32 *ecx,
2202 guint32 *edx,
2203 GError **error)
2204{
2205#ifdef HAVE_CPUID_H
2206 guint eax_tmp = 0;
2207 guint ebx_tmp = 0;
2208 guint ecx_tmp = 0;
2209 guint edx_tmp = 0;
2210
Richard Hughes6a489a92020-12-22 10:32:06 +00002211 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2212
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002213 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002214 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002215 if (eax != NULL)
2216 *eax = eax_tmp;
2217 if (ebx != NULL)
2218 *ebx = ebx_tmp;
2219 if (ecx != NULL)
2220 *ecx = ecx_tmp;
2221 if (edx != NULL)
2222 *edx = edx_tmp;
2223 return TRUE;
2224#else
2225 g_set_error_literal (error,
2226 G_IO_ERROR,
2227 G_IO_ERROR_NOT_SUPPORTED,
2228 "no <cpuid.h> support");
2229 return FALSE;
2230#endif
2231}
2232
2233/**
Richard Hughes9223c892020-05-09 20:32:08 +01002234 * fu_common_is_cpu_intel:
2235 *
2236 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2237 *
2238 * Return value: %TRUE if the vendor was Intel.
2239 *
2240 * Since: 1.5.0
2241 **/
2242gboolean
2243fu_common_is_cpu_intel (void)
2244{
Richard Hughes9223c892020-05-09 20:32:08 +01002245 guint ebx = 0;
2246 guint ecx = 0;
2247 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002248
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002249 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2250 return FALSE;
2251#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002252 if (ebx == signature_INTEL_ebx &&
2253 edx == signature_INTEL_edx &&
2254 ecx == signature_INTEL_ecx) {
2255 return TRUE;
2256 }
Richard Hughesbd444322020-05-21 12:05:03 +01002257#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002258 return FALSE;
2259}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002260
Richard Hughes36111472020-08-12 15:04:24 +01002261/**
2262 * fu_common_is_live_media:
2263 *
2264 * Checks if the user is running from a live media using various heuristics.
2265 *
2266 * Returns: %TRUE if live
2267 *
2268 * Since: 1.4.6
2269 **/
2270gboolean
2271fu_common_is_live_media (void)
2272{
2273 gsize bufsz = 0;
2274 g_autofree gchar *buf = NULL;
2275 g_auto(GStrv) tokens = NULL;
2276 const gchar *args[] = {
2277 "rd.live.image",
2278 "boot=live",
2279 NULL, /* last entry */
2280 };
2281 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2282 return TRUE;
2283 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2284 return FALSE;
2285 if (bufsz == 0)
2286 return FALSE;
2287 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2288 for (guint i = 0; args[i] != NULL; i++) {
2289 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2290 return TRUE;
2291 }
2292 return FALSE;
2293}
2294
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002295static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002296fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002297{
2298 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002299 const gchar *obj;
2300 g_autoptr(GVariant) output = NULL;
2301 g_autoptr(GDBusProxy) proxy = NULL;
2302 g_autoptr(GPtrArray) devices = NULL;
2303 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002304 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002305
Richard Hughes43417b22020-10-30 14:46:16 +00002306 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2307 if (connection == NULL) {
2308 g_prefix_error (error, "failed to get system bus: ");
2309 return NULL;
2310 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002311 proxy = g_dbus_proxy_new_sync (connection,
2312 G_DBUS_PROXY_FLAGS_NONE, NULL,
2313 UDISKS_DBUS_SERVICE,
2314 UDISKS_DBUS_PATH,
2315 UDISKS_DBUS_MANAGER_INTERFACE,
2316 NULL, error);
2317 if (proxy == NULL) {
2318 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2319 return NULL;
2320 }
2321 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002322 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002323 "GetBlockDevices",
2324 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002325 G_DBUS_CALL_FLAGS_NONE,
2326 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002327 if (output == NULL) {
2328 if (error != NULL)
2329 g_dbus_error_strip_remote_error (*error);
2330 g_prefix_error (error, "failed to call %s.%s(): ",
2331 UDISKS_DBUS_SERVICE,
2332 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002333 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002334 }
Richard Hughes43417b22020-10-30 14:46:16 +00002335 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002336 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002337 while (g_variant_iter_next (iter, "&o", &obj)) {
2338 g_autoptr(GDBusProxy) proxy_blk = NULL;
2339 proxy_blk = g_dbus_proxy_new_sync (connection,
2340 G_DBUS_PROXY_FLAGS_NONE, NULL,
2341 UDISKS_DBUS_SERVICE,
2342 obj,
2343 UDISKS_DBUS_INTERFACE_BLOCK,
2344 NULL, error);
2345 if (proxy_blk == NULL) {
2346 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2347 return NULL;
2348 }
2349 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2350 }
2351
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002352
2353 return g_steal_pointer (&devices);
2354}
2355
2356/**
2357 * fu_common_get_volumes_by_kind:
2358 * @kind: A volume kind, typically a GUID
2359 * @error: A #GError or NULL
2360 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002361 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002362 *
2363 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2364 *
2365 * Since: 1.4.6
2366 **/
2367GPtrArray *
2368fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2369{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002370 g_autoptr(GPtrArray) devices = NULL;
2371 g_autoptr(GPtrArray) volumes = NULL;
2372
Richard Hughes6a489a92020-12-22 10:32:06 +00002373 g_return_val_if_fail (kind != NULL, NULL);
2374 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2375
Richard Hughes43417b22020-10-30 14:46:16 +00002376 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002377 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002378 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002379 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2380 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002381 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002382 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002383 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002384 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002385 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002386 g_autoptr(GVariant) val = NULL;
2387
Richard Hughes43417b22020-10-30 14:46:16 +00002388 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002389 G_DBUS_PROXY_FLAGS_NONE, NULL,
2390 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002391 g_dbus_proxy_get_object_path (proxy_blk),
2392 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002393 NULL, error);
2394 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002395 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2396 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002397 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002398 }
2399 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2400 if (val == NULL)
2401 continue;
2402
Richard Hughesdb344d52020-09-09 19:42:27 +01002403 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002404 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2405 G_DBUS_PROXY_FLAGS_NONE, NULL,
2406 UDISKS_DBUS_SERVICE,
2407 g_dbus_proxy_get_object_path (proxy_blk),
2408 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2409 NULL, error);
2410 if (proxy_fs == NULL) {
2411 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2412 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002413 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002414 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002415 vol = g_object_new (FU_TYPE_VOLUME,
2416 "proxy-block", proxy_blk,
2417 "proxy-filesystem", proxy_fs,
2418 NULL);
2419 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2420 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2421 fu_volume_is_internal (vol),
2422 fu_volume_get_id_type (vol));
2423 if (g_strcmp0 (type_str, kind) != 0)
2424 continue;
2425 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002426 }
2427 if (volumes->len == 0) {
2428 g_set_error (error,
2429 G_IO_ERROR,
2430 G_IO_ERROR_NOT_FOUND,
2431 "no volumes of type %s", kind);
2432 return NULL;
2433 }
2434 return g_steal_pointer (&volumes);
2435}
2436
2437/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002438 * fu_common_get_volume_by_device:
2439 * @device: A device string, typcically starting with `/dev/`
2440 * @error: A #GError or NULL
2441 *
2442 * Finds the first volume from the specified device.
2443 *
2444 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2445 *
2446 * Since: 1.5.1
2447 **/
2448FuVolume *
2449fu_common_get_volume_by_device (const gchar *device, GError **error)
2450{
2451 g_autoptr(GPtrArray) devices = NULL;
2452
Richard Hughes6a489a92020-12-22 10:32:06 +00002453 g_return_val_if_fail (device != NULL, NULL);
2454 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2455
Richard Hughes0bdf5612020-10-30 14:56:22 +00002456 /* find matching block device */
2457 devices = fu_common_get_block_devices (error);
2458 if (devices == NULL)
2459 return NULL;
2460 for (guint i = 0; i < devices->len; i++) {
2461 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2462 g_autoptr(GVariant) val = NULL;
2463 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2464 if (val == NULL)
2465 continue;
2466 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2467 return g_object_new (FU_TYPE_VOLUME,
2468 "proxy-block", proxy_blk,
2469 NULL);
2470 }
2471 }
2472
2473 /* failed */
2474 g_set_error (error,
2475 G_IO_ERROR,
2476 G_IO_ERROR_NOT_FOUND,
2477 "no volumes for device %s",
2478 device);
2479 return NULL;
2480}
2481
2482/**
2483 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002484 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002485 * @error: A #GError or NULL
2486 *
2487 * Finds the first volume from the specified device.
2488 *
2489 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2490 *
2491 * Since: 1.5.1
2492 **/
2493FuVolume *
2494fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2495{
2496 g_autoptr(GPtrArray) devices = NULL;
2497
Richard Hughes6a489a92020-12-22 10:32:06 +00002498 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2499
Richard Hughes0bdf5612020-10-30 14:56:22 +00002500 /* find matching block device */
2501 devices = fu_common_get_block_devices (error);
2502 if (devices == NULL)
2503 return NULL;
2504 for (guint i = 0; i < devices->len; i++) {
2505 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2506 g_autoptr(GVariant) val = NULL;
2507 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2508 if (val == NULL)
2509 continue;
2510 if (devnum == g_variant_get_uint64 (val)) {
2511 return g_object_new (FU_TYPE_VOLUME,
2512 "proxy-block", proxy_blk,
2513 NULL);
2514 }
2515 }
2516
2517 /* failed */
2518 g_set_error (error,
2519 G_IO_ERROR,
2520 G_IO_ERROR_NOT_FOUND,
2521 "no volumes for devnum %u",
2522 devnum);
2523 return NULL;
2524}
2525
2526/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002527 * fu_common_get_esp_default:
2528 * @error: A #GError or NULL
2529 *
2530 * Gets the platform default ESP
2531 *
2532 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2533 *
2534 * Since: 1.4.6
2535 **/
2536FuVolume *
2537fu_common_get_esp_default (GError **error)
2538{
2539 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002540 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002541 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2542 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002543 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002544 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002545 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002546
Richard Hughes6a489a92020-12-22 10:32:06 +00002547 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2548
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002549 /* for the test suite use local directory for ESP */
2550 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2551 if (path_tmp != NULL)
2552 return fu_volume_new_from_mount_path (path_tmp);
2553
Mario Limonciello56d816a2020-11-11 16:59:30 -06002554 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2555 if (volumes == NULL) {
2556 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2557 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2558 if (volumes == NULL) {
2559 g_prefix_error (error, "%s: ", error_local->message);
2560 return NULL;
2561 }
2562 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002563
2564 /* are there _any_ internal vfat partitions?
2565 * remember HintSystem is just that -- a hint! */
2566 for (guint i = 0; i < volumes->len; i++) {
2567 FuVolume *vol = g_ptr_array_index (volumes, i);
2568 g_autofree gchar *type = fu_volume_get_id_type (vol);
2569 if (g_strcmp0 (type, "vfat") == 0 &&
2570 fu_volume_is_internal (vol)) {
2571 has_internal = TRUE;
2572 break;
2573 }
2574 }
2575
2576 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002577 for (guint i = 0; i < volumes->len; i++) {
2578 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002579 g_autofree gchar *type = fu_volume_get_id_type (vol);
2580 if (type == NULL)
2581 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002582 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002583 continue;
2584 if (g_strcmp0 (type, "vfat") == 0)
2585 g_ptr_array_add (volumes_vfat, vol);
2586 }
2587 if (volumes_vfat->len == 0) {
2588 g_set_error (error,
2589 G_IO_ERROR,
2590 G_IO_ERROR_INVALID_FILENAME,
2591 "No ESP found");
2592 return NULL;
2593 }
2594 for (guint i = 0; i < volumes_vfat->len; i++) {
2595 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002596 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2597 }
2598 if (volumes_mtab->len == 1) {
2599 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2600 return g_object_ref (vol);
2601 }
2602 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2603 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2604 return g_object_ref (vol);
2605 }
2606 g_set_error (error,
2607 G_IO_ERROR,
2608 G_IO_ERROR_INVALID_FILENAME,
2609 "More than one available ESP");
2610 return NULL;
2611}
2612
2613/**
2614 * fu_common_get_esp_for_path:
2615 * @esp_path: A path to the ESP
2616 * @error: A #GError or NULL
2617 *
2618 * Gets the platform ESP using a UNIX or UDisks path
2619 *
2620 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2621 *
2622 * Since: 1.4.6
2623 **/
2624FuVolume *
2625fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2626{
Richard Hughes6a489a92020-12-22 10:32:06 +00002627 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002628 g_autoptr(GPtrArray) volumes = NULL;
2629
Richard Hughes6a489a92020-12-22 10:32:06 +00002630 g_return_val_if_fail (esp_path != NULL, NULL);
2631 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2632
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002633 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2634 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002635 return NULL;
Richard Hughes6a489a92020-12-22 10:32:06 +00002636 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002637 for (guint i = 0; i < volumes->len; i++) {
2638 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002639 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002640 if (g_strcmp0 (basename, vol_basename) == 0)
2641 return g_object_ref (vol);
2642 }
2643 g_set_error (error,
2644 G_IO_ERROR,
2645 G_IO_ERROR_INVALID_FILENAME,
2646 "No ESP with path %s",
2647 esp_path);
2648 return NULL;
2649}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002650
2651/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002652 * fu_common_crc8:
2653 * @buf: memory buffer
2654 * @bufsz: sizeof buf
2655 *
2656 * Returns the cyclic redundancy check value for the given memory buffer.
2657 *
2658 * Returns: CRC value
2659 *
2660 * Since: 1.5.0
2661 **/
2662guint8
2663fu_common_crc8 (const guint8 *buf, gsize bufsz)
2664{
2665 guint32 crc = 0;
2666 for (gsize j = bufsz; j > 0; j--) {
2667 crc ^= (*(buf++) << 8);
2668 for (guint32 i = 8; i; i--) {
2669 if (crc & 0x8000)
2670 crc ^= (0x1070 << 3);
2671 crc <<= 1;
2672 }
2673 }
2674 return ~((guint8) (crc >> 8));
2675}
2676
2677/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002678 * fu_common_crc16:
2679 * @buf: memory buffer
2680 * @bufsz: sizeof buf
2681 *
2682 * Returns the cyclic redundancy check value for the given memory buffer.
2683 *
2684 * Returns: CRC value
2685 *
2686 * Since: 1.5.0
2687 **/
2688guint16
2689fu_common_crc16 (const guint8 *buf, gsize bufsz)
2690{
2691 guint16 crc = 0xffff;
2692 for (gsize len = bufsz; len > 0; len--) {
2693 crc = (guint16) (crc ^ (*buf++));
2694 for (guint8 i = 0; i < 8; i++) {
2695 if (crc & 0x1) {
2696 crc = (crc >> 1) ^ 0xa001;
2697 } else {
2698 crc >>= 1;
2699 }
2700 }
2701 }
2702 return ~crc;
2703}
2704
2705/**
2706 * fu_common_crc32_full:
2707 * @buf: memory buffer
2708 * @bufsz: sizeof buf
2709 * @crc: initial CRC value, typically 0xFFFFFFFF
2710 * @polynomial: CRC polynomial, typically 0xEDB88320
2711 *
2712 * Returns the cyclic redundancy check value for the given memory buffer.
2713 *
2714 * Returns: CRC value
2715 *
2716 * Since: 1.5.0
2717 **/
2718guint32
2719fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2720{
2721 for (guint32 idx = 0; idx < bufsz; idx++) {
2722 guint8 data = *buf++;
2723 crc = crc ^ data;
2724 for (guint32 bit = 0; bit < 8; bit++) {
2725 guint32 mask = -(crc & 1);
2726 crc = (crc >> 1) ^ (polynomial & mask);
2727 }
2728 }
2729 return ~crc;
2730}
2731
2732/**
2733 * fu_common_crc32:
2734 * @buf: memory buffer
2735 * @bufsz: sizeof buf
2736 *
2737 * Returns the cyclic redundancy check value for the given memory buffer.
2738 *
2739 * Returns: CRC value
2740 *
2741 * Since: 1.5.0
2742 **/
2743guint32
2744fu_common_crc32 (const guint8 *buf, gsize bufsz)
2745{
2746 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2747}