blob: fd13f11d97e700ac5417e3a95312eb4b3f898dce [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/**
1843 * fu_memcpy_safe:
1844 * @dst: destination buffer
1845 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1846 * @dst_offset: offset in bytes into @dst to copy to
1847 * @src: source buffer
1848 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1849 * @src_offset: offset in bytes into @src to copy from
1850 * @n: number of bytes to copy from @src+@offset from
1851 * @error: A #GError or %NULL
1852 *
1853 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1854 * of both the destination and the source allows us to check for buffer overflow.
1855 *
1856 * Providing the buffer offsets also allows us to check reading past the end of
1857 * the source buffer. For this reason the caller should NEVER add an offset to
1858 * @src or @dst.
1859 *
1860 * You don't need to use this function in "obviously correct" cases, nor should
1861 * you use it when performance is a concern. Only us it when you're not sure if
1862 * malicious data from a device or firmware could cause memory corruption.
1863 *
1864 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001865 *
1866 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001867 **/
1868gboolean
1869fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1870 const guint8 *src, gsize src_sz, gsize src_offset,
1871 gsize n, GError **error)
1872{
Richard Hughes6a489a92020-12-22 10:32:06 +00001873 g_return_val_if_fail (dst != NULL, FALSE);
1874 g_return_val_if_fail (src != NULL, FALSE);
1875 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1876
Richard Hughes5308ea42019-08-09 12:25:13 +01001877 if (n == 0)
1878 return TRUE;
1879
1880 if (n > src_sz) {
1881 g_set_error (error,
1882 FWUPD_ERROR,
1883 FWUPD_ERROR_READ,
1884 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1885 (guint) n, (guint) src_sz);
1886 return FALSE;
1887 }
1888 if (n + src_offset > src_sz) {
1889 g_set_error (error,
1890 FWUPD_ERROR,
1891 FWUPD_ERROR_READ,
1892 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1893 (guint) n, (guint) src_offset, (guint) src_sz);
1894 return FALSE;
1895 }
1896 if (n > dst_sz) {
1897 g_set_error (error,
1898 FWUPD_ERROR,
1899 FWUPD_ERROR_WRITE,
1900 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1901 (guint) n, (guint) dst_sz);
1902 return FALSE;
1903 }
1904 if (n + dst_offset > dst_sz) {
1905 g_set_error (error,
1906 FWUPD_ERROR,
1907 FWUPD_ERROR_WRITE,
1908 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1909 (guint) n, (guint) dst_offset, (guint) dst_sz);
1910 return FALSE;
1911 }
1912
1913 /* phew! */
1914 memcpy (dst + dst_offset, src + src_offset, n);
1915 return TRUE;
1916}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001917
Richard Hughes80768f52019-10-22 07:19:14 +01001918/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001919 * fu_common_read_uint8_safe:
1920 * @buf: source buffer
1921 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1922 * @offset: offset in bytes into @buf to copy from
1923 * @value: (out) (allow-none): the parsed value
1924 * @error: A #GError or %NULL
1925 *
1926 * Read a value from a buffer in a safe way.
1927 *
1928 * You don't need to use this function in "obviously correct" cases, nor should
1929 * you use it when performance is a concern. Only us it when you're not sure if
1930 * malicious data from a device or firmware could cause memory corruption.
1931 *
1932 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001933 *
1934 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001935 **/
1936gboolean
1937fu_common_read_uint8_safe (const guint8 *buf,
1938 gsize bufsz,
1939 gsize offset,
1940 guint8 *value,
1941 GError **error)
1942{
1943 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00001944
1945 g_return_val_if_fail (buf != NULL, FALSE);
1946 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1947
Richard Hughesc21a0b92019-10-24 12:24:37 +01001948 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1949 buf, bufsz, offset, /* src */
1950 sizeof(tmp), error))
1951 return FALSE;
1952 if (value != NULL)
1953 *value = tmp;
1954 return TRUE;
1955}
1956
1957/**
Richard Hughes80768f52019-10-22 07:19:14 +01001958 * fu_common_read_uint16_safe:
1959 * @buf: source buffer
1960 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1961 * @offset: offset in bytes into @buf to copy from
1962 * @value: (out) (allow-none): the parsed value
1963 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1964 * @error: A #GError or %NULL
1965 *
1966 * Read a value from a buffer using a specified endian 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 Hughes80768f52019-10-22 07:19:14 +01001975 **/
1976gboolean
1977fu_common_read_uint16_safe (const guint8 *buf,
1978 gsize bufsz,
1979 gsize offset,
1980 guint16 *value,
1981 FuEndianType endian,
1982 GError **error)
1983{
1984 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00001985
1986 g_return_val_if_fail (buf != NULL, FALSE);
1987 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1988
Richard Hughes7d01ac92019-10-23 14:31:46 +01001989 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001990 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001991 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001992 return FALSE;
1993 if (value != NULL)
1994 *value = fu_common_read_uint16 (dst, endian);
1995 return TRUE;
1996}
1997
1998/**
1999 * fu_common_read_uint32_safe:
2000 * @buf: source buffer
2001 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2002 * @offset: offset in bytes into @buf to copy from
2003 * @value: (out) (allow-none): the parsed value
2004 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2005 * @error: A #GError or %NULL
2006 *
2007 * Read a value from a buffer using a specified endian in a safe way.
2008 *
2009 * You don't need to use this function in "obviously correct" cases, nor should
2010 * you use it when performance is a concern. Only us it when you're not sure if
2011 * malicious data from a device or firmware could cause memory corruption.
2012 *
2013 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002014 *
2015 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002016 **/
2017gboolean
2018fu_common_read_uint32_safe (const guint8 *buf,
2019 gsize bufsz,
2020 gsize offset,
2021 guint32 *value,
2022 FuEndianType endian,
2023 GError **error)
2024{
2025 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002026
2027 g_return_val_if_fail (buf != NULL, FALSE);
2028 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2029
Richard Hughes7d01ac92019-10-23 14:31:46 +01002030 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002031 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002032 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002033 return FALSE;
2034 if (value != NULL)
2035 *value = fu_common_read_uint32 (dst, endian);
2036 return TRUE;
2037}
2038
Mario Limonciello1a680f32019-11-25 19:44:53 -06002039/**
2040 * fu_byte_array_append_uint8:
2041 * @array: A #GByteArray
2042 * @data: #guint8
2043 *
2044 * Adds a 8 bit integer to a byte array
2045 *
2046 * Since: 1.3.1
2047 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002048void
2049fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2050{
2051 g_byte_array_append (array, &data, sizeof(data));
2052}
2053
Mario Limonciello1a680f32019-11-25 19:44:53 -06002054/**
2055 * fu_byte_array_append_uint16:
2056 * @array: A #GByteArray
2057 * @data: #guint16
2058 * @endian: #FuEndianType
2059 *
2060 * Adds a 16 bit integer to a byte array
2061 *
2062 * Since: 1.3.1
2063 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002064void
2065fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2066{
2067 guint8 buf[2];
2068 fu_common_write_uint16 (buf, data, endian);
2069 g_byte_array_append (array, buf, sizeof(buf));
2070}
2071
Mario Limonciello1a680f32019-11-25 19:44:53 -06002072/**
2073 * fu_byte_array_append_uint32:
2074 * @array: A #GByteArray
2075 * @data: #guint32
2076 * @endian: #FuEndianType
2077 *
2078 * Adds a 32 bit integer to a byte array
2079 *
2080 * Since: 1.3.1
2081 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002082void
2083fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2084{
2085 guint8 buf[4];
2086 fu_common_write_uint32 (buf, data, endian);
2087 g_byte_array_append (array, buf, sizeof(buf));
2088}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002089
2090/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002091 * fu_byte_array_set_size:
2092 * @array: a #GByteArray
2093 * @length: the new size of the GByteArray
2094 *
2095 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2096 *
2097 * Since: 1.5.0
2098 **/
2099void
2100fu_byte_array_set_size (GByteArray *array, guint length)
2101{
2102 guint oldlength = array->len;
2103 g_byte_array_set_size (array, length);
2104 if (length > oldlength)
2105 memset (array->data + oldlength, 0x0, length - oldlength);
2106}
2107
2108/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002109 * fu_common_kernel_locked_down:
2110 *
2111 * Determines if kernel lockdown in effect
2112 *
2113 * Since: 1.3.8
2114 **/
2115gboolean
2116fu_common_kernel_locked_down (void)
2117{
2118#ifndef _WIN32
2119 gsize len = 0;
2120 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2121 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2122 g_autofree gchar *data = NULL;
2123 g_auto(GStrv) options = NULL;
2124
2125 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2126 return FALSE;
2127 if (!g_file_get_contents (fname, &data, &len, NULL))
2128 return FALSE;
2129 if (len < 1)
2130 return FALSE;
2131 options = g_strsplit (data, " ", -1);
2132 for (guint i = 0; options[i] != NULL; i++) {
2133 if (g_strcmp0 (options[i], "[none]") == 0)
2134 return FALSE;
2135 }
2136 return TRUE;
2137#else
2138 return FALSE;
2139#endif
2140}
Richard Hughes9223c892020-05-09 20:32:08 +01002141
2142/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002143 * fu_common_cpuid:
2144 * @leaf: The CPUID level, now called the 'leaf' by Intel
2145 * @eax: (out) (nullable): EAX register
2146 * @ebx: (out) (nullable): EBX register
2147 * @ecx: (out) (nullable): ECX register
2148 * @edx: (out) (nullable): EDX register
2149 * @error: A #GError or NULL
2150 *
2151 * Calls CPUID and returns the registers for the given leaf.
2152 *
2153 * Return value: %TRUE if the registers are set.
2154 *
2155 * Since: 1.5.0
2156 **/
2157gboolean
2158fu_common_cpuid (guint32 leaf,
2159 guint32 *eax,
2160 guint32 *ebx,
2161 guint32 *ecx,
2162 guint32 *edx,
2163 GError **error)
2164{
2165#ifdef HAVE_CPUID_H
2166 guint eax_tmp = 0;
2167 guint ebx_tmp = 0;
2168 guint ecx_tmp = 0;
2169 guint edx_tmp = 0;
2170
Richard Hughes6a489a92020-12-22 10:32:06 +00002171 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2172
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002173 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002174 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002175 if (eax != NULL)
2176 *eax = eax_tmp;
2177 if (ebx != NULL)
2178 *ebx = ebx_tmp;
2179 if (ecx != NULL)
2180 *ecx = ecx_tmp;
2181 if (edx != NULL)
2182 *edx = edx_tmp;
2183 return TRUE;
2184#else
2185 g_set_error_literal (error,
2186 G_IO_ERROR,
2187 G_IO_ERROR_NOT_SUPPORTED,
2188 "no <cpuid.h> support");
2189 return FALSE;
2190#endif
2191}
2192
2193/**
Richard Hughes9223c892020-05-09 20:32:08 +01002194 * fu_common_is_cpu_intel:
2195 *
2196 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2197 *
2198 * Return value: %TRUE if the vendor was Intel.
2199 *
2200 * Since: 1.5.0
2201 **/
2202gboolean
2203fu_common_is_cpu_intel (void)
2204{
Richard Hughes9223c892020-05-09 20:32:08 +01002205 guint ebx = 0;
2206 guint ecx = 0;
2207 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002208
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002209 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2210 return FALSE;
2211#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002212 if (ebx == signature_INTEL_ebx &&
2213 edx == signature_INTEL_edx &&
2214 ecx == signature_INTEL_ecx) {
2215 return TRUE;
2216 }
Richard Hughesbd444322020-05-21 12:05:03 +01002217#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002218 return FALSE;
2219}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002220
Richard Hughes36111472020-08-12 15:04:24 +01002221/**
2222 * fu_common_is_live_media:
2223 *
2224 * Checks if the user is running from a live media using various heuristics.
2225 *
2226 * Returns: %TRUE if live
2227 *
2228 * Since: 1.4.6
2229 **/
2230gboolean
2231fu_common_is_live_media (void)
2232{
2233 gsize bufsz = 0;
2234 g_autofree gchar *buf = NULL;
2235 g_auto(GStrv) tokens = NULL;
2236 const gchar *args[] = {
2237 "rd.live.image",
2238 "boot=live",
2239 NULL, /* last entry */
2240 };
2241 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2242 return TRUE;
2243 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2244 return FALSE;
2245 if (bufsz == 0)
2246 return FALSE;
2247 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2248 for (guint i = 0; args[i] != NULL; i++) {
2249 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2250 return TRUE;
2251 }
2252 return FALSE;
2253}
2254
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002255static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002256fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002257{
2258 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002259 const gchar *obj;
2260 g_autoptr(GVariant) output = NULL;
2261 g_autoptr(GDBusProxy) proxy = NULL;
2262 g_autoptr(GPtrArray) devices = NULL;
2263 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002264 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002265
Richard Hughes43417b22020-10-30 14:46:16 +00002266 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2267 if (connection == NULL) {
2268 g_prefix_error (error, "failed to get system bus: ");
2269 return NULL;
2270 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002271 proxy = g_dbus_proxy_new_sync (connection,
2272 G_DBUS_PROXY_FLAGS_NONE, NULL,
2273 UDISKS_DBUS_SERVICE,
2274 UDISKS_DBUS_PATH,
2275 UDISKS_DBUS_MANAGER_INTERFACE,
2276 NULL, error);
2277 if (proxy == NULL) {
2278 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2279 return NULL;
2280 }
2281 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002282 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002283 "GetBlockDevices",
2284 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002285 G_DBUS_CALL_FLAGS_NONE,
2286 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002287 if (output == NULL) {
2288 if (error != NULL)
2289 g_dbus_error_strip_remote_error (*error);
2290 g_prefix_error (error, "failed to call %s.%s(): ",
2291 UDISKS_DBUS_SERVICE,
2292 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002293 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002294 }
Richard Hughes43417b22020-10-30 14:46:16 +00002295 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002296 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002297 while (g_variant_iter_next (iter, "&o", &obj)) {
2298 g_autoptr(GDBusProxy) proxy_blk = NULL;
2299 proxy_blk = g_dbus_proxy_new_sync (connection,
2300 G_DBUS_PROXY_FLAGS_NONE, NULL,
2301 UDISKS_DBUS_SERVICE,
2302 obj,
2303 UDISKS_DBUS_INTERFACE_BLOCK,
2304 NULL, error);
2305 if (proxy_blk == NULL) {
2306 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2307 return NULL;
2308 }
2309 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2310 }
2311
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002312
2313 return g_steal_pointer (&devices);
2314}
2315
2316/**
2317 * fu_common_get_volumes_by_kind:
2318 * @kind: A volume kind, typically a GUID
2319 * @error: A #GError or NULL
2320 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002321 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002322 *
2323 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2324 *
2325 * Since: 1.4.6
2326 **/
2327GPtrArray *
2328fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2329{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002330 g_autoptr(GPtrArray) devices = NULL;
2331 g_autoptr(GPtrArray) volumes = NULL;
2332
Richard Hughes6a489a92020-12-22 10:32:06 +00002333 g_return_val_if_fail (kind != NULL, NULL);
2334 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2335
Richard Hughes43417b22020-10-30 14:46:16 +00002336 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002337 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002338 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002339 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2340 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002341 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002342 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002343 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002344 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002345 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002346 g_autoptr(GVariant) val = NULL;
2347
Richard Hughes43417b22020-10-30 14:46:16 +00002348 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002349 G_DBUS_PROXY_FLAGS_NONE, NULL,
2350 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002351 g_dbus_proxy_get_object_path (proxy_blk),
2352 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002353 NULL, error);
2354 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002355 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2356 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002357 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002358 }
2359 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2360 if (val == NULL)
2361 continue;
2362
Richard Hughesdb344d52020-09-09 19:42:27 +01002363 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002364 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2365 G_DBUS_PROXY_FLAGS_NONE, NULL,
2366 UDISKS_DBUS_SERVICE,
2367 g_dbus_proxy_get_object_path (proxy_blk),
2368 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2369 NULL, error);
2370 if (proxy_fs == NULL) {
2371 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2372 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002373 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002374 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002375 vol = g_object_new (FU_TYPE_VOLUME,
2376 "proxy-block", proxy_blk,
2377 "proxy-filesystem", proxy_fs,
2378 NULL);
2379 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2380 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2381 fu_volume_is_internal (vol),
2382 fu_volume_get_id_type (vol));
2383 if (g_strcmp0 (type_str, kind) != 0)
2384 continue;
2385 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002386 }
2387 if (volumes->len == 0) {
2388 g_set_error (error,
2389 G_IO_ERROR,
2390 G_IO_ERROR_NOT_FOUND,
2391 "no volumes of type %s", kind);
2392 return NULL;
2393 }
2394 return g_steal_pointer (&volumes);
2395}
2396
2397/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002398 * fu_common_get_volume_by_device:
2399 * @device: A device string, typcically starting with `/dev/`
2400 * @error: A #GError or NULL
2401 *
2402 * Finds the first volume from the specified device.
2403 *
2404 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2405 *
2406 * Since: 1.5.1
2407 **/
2408FuVolume *
2409fu_common_get_volume_by_device (const gchar *device, GError **error)
2410{
2411 g_autoptr(GPtrArray) devices = NULL;
2412
Richard Hughes6a489a92020-12-22 10:32:06 +00002413 g_return_val_if_fail (device != NULL, NULL);
2414 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2415
Richard Hughes0bdf5612020-10-30 14:56:22 +00002416 /* find matching block device */
2417 devices = fu_common_get_block_devices (error);
2418 if (devices == NULL)
2419 return NULL;
2420 for (guint i = 0; i < devices->len; i++) {
2421 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2422 g_autoptr(GVariant) val = NULL;
2423 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2424 if (val == NULL)
2425 continue;
2426 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2427 return g_object_new (FU_TYPE_VOLUME,
2428 "proxy-block", proxy_blk,
2429 NULL);
2430 }
2431 }
2432
2433 /* failed */
2434 g_set_error (error,
2435 G_IO_ERROR,
2436 G_IO_ERROR_NOT_FOUND,
2437 "no volumes for device %s",
2438 device);
2439 return NULL;
2440}
2441
2442/**
2443 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002444 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002445 * @error: A #GError or NULL
2446 *
2447 * Finds the first volume from the specified device.
2448 *
2449 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2450 *
2451 * Since: 1.5.1
2452 **/
2453FuVolume *
2454fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2455{
2456 g_autoptr(GPtrArray) devices = NULL;
2457
Richard Hughes6a489a92020-12-22 10:32:06 +00002458 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2459
Richard Hughes0bdf5612020-10-30 14:56:22 +00002460 /* find matching block device */
2461 devices = fu_common_get_block_devices (error);
2462 if (devices == NULL)
2463 return NULL;
2464 for (guint i = 0; i < devices->len; i++) {
2465 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2466 g_autoptr(GVariant) val = NULL;
2467 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2468 if (val == NULL)
2469 continue;
2470 if (devnum == g_variant_get_uint64 (val)) {
2471 return g_object_new (FU_TYPE_VOLUME,
2472 "proxy-block", proxy_blk,
2473 NULL);
2474 }
2475 }
2476
2477 /* failed */
2478 g_set_error (error,
2479 G_IO_ERROR,
2480 G_IO_ERROR_NOT_FOUND,
2481 "no volumes for devnum %u",
2482 devnum);
2483 return NULL;
2484}
2485
2486/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002487 * fu_common_get_esp_default:
2488 * @error: A #GError or NULL
2489 *
2490 * Gets the platform default ESP
2491 *
2492 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2493 *
2494 * Since: 1.4.6
2495 **/
2496FuVolume *
2497fu_common_get_esp_default (GError **error)
2498{
2499 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002500 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002501 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2502 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002503 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002504 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002505 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002506
Richard Hughes6a489a92020-12-22 10:32:06 +00002507 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2508
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002509 /* for the test suite use local directory for ESP */
2510 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2511 if (path_tmp != NULL)
2512 return fu_volume_new_from_mount_path (path_tmp);
2513
Mario Limonciello56d816a2020-11-11 16:59:30 -06002514 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2515 if (volumes == NULL) {
2516 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2517 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2518 if (volumes == NULL) {
2519 g_prefix_error (error, "%s: ", error_local->message);
2520 return NULL;
2521 }
2522 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002523
2524 /* are there _any_ internal vfat partitions?
2525 * remember HintSystem is just that -- a hint! */
2526 for (guint i = 0; i < volumes->len; i++) {
2527 FuVolume *vol = g_ptr_array_index (volumes, i);
2528 g_autofree gchar *type = fu_volume_get_id_type (vol);
2529 if (g_strcmp0 (type, "vfat") == 0 &&
2530 fu_volume_is_internal (vol)) {
2531 has_internal = TRUE;
2532 break;
2533 }
2534 }
2535
2536 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002537 for (guint i = 0; i < volumes->len; i++) {
2538 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002539 g_autofree gchar *type = fu_volume_get_id_type (vol);
2540 if (type == NULL)
2541 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002542 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002543 continue;
2544 if (g_strcmp0 (type, "vfat") == 0)
2545 g_ptr_array_add (volumes_vfat, vol);
2546 }
2547 if (volumes_vfat->len == 0) {
2548 g_set_error (error,
2549 G_IO_ERROR,
2550 G_IO_ERROR_INVALID_FILENAME,
2551 "No ESP found");
2552 return NULL;
2553 }
2554 for (guint i = 0; i < volumes_vfat->len; i++) {
2555 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002556 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2557 }
2558 if (volumes_mtab->len == 1) {
2559 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2560 return g_object_ref (vol);
2561 }
2562 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2563 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2564 return g_object_ref (vol);
2565 }
2566 g_set_error (error,
2567 G_IO_ERROR,
2568 G_IO_ERROR_INVALID_FILENAME,
2569 "More than one available ESP");
2570 return NULL;
2571}
2572
2573/**
2574 * fu_common_get_esp_for_path:
2575 * @esp_path: A path to the ESP
2576 * @error: A #GError or NULL
2577 *
2578 * Gets the platform ESP using a UNIX or UDisks path
2579 *
2580 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2581 *
2582 * Since: 1.4.6
2583 **/
2584FuVolume *
2585fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2586{
Richard Hughes6a489a92020-12-22 10:32:06 +00002587 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002588 g_autoptr(GPtrArray) volumes = NULL;
2589
Richard Hughes6a489a92020-12-22 10:32:06 +00002590 g_return_val_if_fail (esp_path != NULL, NULL);
2591 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2592
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002593 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2594 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002595 return NULL;
Richard Hughes6a489a92020-12-22 10:32:06 +00002596 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002597 for (guint i = 0; i < volumes->len; i++) {
2598 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002599 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002600 if (g_strcmp0 (basename, vol_basename) == 0)
2601 return g_object_ref (vol);
2602 }
2603 g_set_error (error,
2604 G_IO_ERROR,
2605 G_IO_ERROR_INVALID_FILENAME,
2606 "No ESP with path %s",
2607 esp_path);
2608 return NULL;
2609}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002610
2611/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002612 * fu_common_crc8:
2613 * @buf: memory buffer
2614 * @bufsz: sizeof buf
2615 *
2616 * Returns the cyclic redundancy check value for the given memory buffer.
2617 *
2618 * Returns: CRC value
2619 *
2620 * Since: 1.5.0
2621 **/
2622guint8
2623fu_common_crc8 (const guint8 *buf, gsize bufsz)
2624{
2625 guint32 crc = 0;
2626 for (gsize j = bufsz; j > 0; j--) {
2627 crc ^= (*(buf++) << 8);
2628 for (guint32 i = 8; i; i--) {
2629 if (crc & 0x8000)
2630 crc ^= (0x1070 << 3);
2631 crc <<= 1;
2632 }
2633 }
2634 return ~((guint8) (crc >> 8));
2635}
2636
2637/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002638 * fu_common_crc16:
2639 * @buf: memory buffer
2640 * @bufsz: sizeof buf
2641 *
2642 * Returns the cyclic redundancy check value for the given memory buffer.
2643 *
2644 * Returns: CRC value
2645 *
2646 * Since: 1.5.0
2647 **/
2648guint16
2649fu_common_crc16 (const guint8 *buf, gsize bufsz)
2650{
2651 guint16 crc = 0xffff;
2652 for (gsize len = bufsz; len > 0; len--) {
2653 crc = (guint16) (crc ^ (*buf++));
2654 for (guint8 i = 0; i < 8; i++) {
2655 if (crc & 0x1) {
2656 crc = (crc >> 1) ^ 0xa001;
2657 } else {
2658 crc >>= 1;
2659 }
2660 }
2661 }
2662 return ~crc;
2663}
2664
2665/**
2666 * fu_common_crc32_full:
2667 * @buf: memory buffer
2668 * @bufsz: sizeof buf
2669 * @crc: initial CRC value, typically 0xFFFFFFFF
2670 * @polynomial: CRC polynomial, typically 0xEDB88320
2671 *
2672 * Returns the cyclic redundancy check value for the given memory buffer.
2673 *
2674 * Returns: CRC value
2675 *
2676 * Since: 1.5.0
2677 **/
2678guint32
2679fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2680{
2681 for (guint32 idx = 0; idx < bufsz; idx++) {
2682 guint8 data = *buf++;
2683 crc = crc ^ data;
2684 for (guint32 bit = 0; bit < 8; bit++) {
2685 guint32 mask = -(crc & 1);
2686 crc = (crc >> 1) ^ (polynomial & mask);
2687 }
2688 }
2689 return ~crc;
2690}
2691
2692/**
2693 * fu_common_crc32:
2694 * @buf: memory buffer
2695 * @bufsz: sizeof buf
2696 *
2697 * Returns the cyclic redundancy check value for the given memory buffer.
2698 *
2699 * Returns: CRC value
2700 *
2701 * Since: 1.5.0
2702 **/
2703guint32
2704fu_common_crc32 (const guint8 *buf, gsize bufsz)
2705{
2706 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2707}