blob: d2adb14109287e36610c9c169c8cacb6b9d9a617 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes943d2c92017-06-21 09:04:39 +01002 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes943d2c92017-06-21 09:04:39 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuCommon"
8
Richard Hughes943d2c92017-06-21 09:04:39 +01009#include <config.h>
10
Richard Hughes9e5675e2019-11-22 09:35:03 +000011#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +010012#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#endif
Richard Hughes954dd9f2017-08-08 13:36:25 +010014#include <glib/gstdio.h>
15
Richard Hughes5c508de2019-11-22 09:57:34 +000016#ifdef HAVE_FNMATCH_H
17#include <fnmatch.h>
Richard Hughes45a00732019-11-22 16:57:14 +000018#elif _WIN32
19#include <shlwapi.h>
Richard Hughes5c508de2019-11-22 09:57:34 +000020#endif
21
Richard Hughesbd444322020-05-21 12:05:03 +010022#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010023#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010024#endif
Richard Hughes9223c892020-05-09 20:32:08 +010025
Richard Hughes5add3a72021-01-13 19:25:10 +000026#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +010027#include <archive_entry.h>
28#include <archive.h>
Richard Hughes5add3a72021-01-13 19:25:10 +000029#endif
Richard Hughes7ee42fe2017-08-15 14:06:21 +010030#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000032#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000033#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010034
35#include "fwupd-error.h"
36
37#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010038#include "fu-volume-private.h"
39
Richard Hughes43417b22020-10-30 14:46:16 +000040#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
41#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
42#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
43#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
44#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
45#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010046
47/**
Richard Hughes4eada342017-10-03 21:20:32 +010048 * SECTION:fu-common
49 * @short_description: common functionality for plugins to use
50 *
51 * Helper functions that can be used by the daemon and plugins.
52 *
53 * See also: #FuPlugin
54 */
55
56/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010057 * fu_common_rmtree:
58 * @directory: a directory name
59 * @error: A #GError or %NULL
60 *
61 * Recursively removes a directory.
62 *
63 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060064 *
65 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010066 **/
67gboolean
68fu_common_rmtree (const gchar *directory, GError **error)
69{
70 const gchar *filename;
71 g_autoptr(GDir) dir = NULL;
72
Richard Hughes6a489a92020-12-22 10:32:06 +000073 g_return_val_if_fail (directory != NULL, FALSE);
74 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
75
Richard Hughes954dd9f2017-08-08 13:36:25 +010076 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010077 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010078 dir = g_dir_open (directory, 0, error);
79 if (dir == NULL)
80 return FALSE;
81
82 /* find each */
83 while ((filename = g_dir_read_name (dir))) {
84 g_autofree gchar *src = NULL;
85 src = g_build_filename (directory, filename, NULL);
86 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
87 if (!fu_common_rmtree (src, error))
88 return FALSE;
89 } else {
90 if (g_unlink (src) != 0) {
91 g_set_error (error,
92 FWUPD_ERROR,
93 FWUPD_ERROR_INTERNAL,
94 "Failed to delete: %s", src);
95 return FALSE;
96 }
97 }
98 }
99 if (g_remove (directory) != 0) {
100 g_set_error (error,
101 FWUPD_ERROR,
102 FWUPD_ERROR_INTERNAL,
103 "Failed to delete: %s", directory);
104 return FALSE;
105 }
106 return TRUE;
107}
108
Richard Hughes89e968b2018-03-07 10:01:08 +0000109static gboolean
110fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
111{
112 const gchar *filename;
113 g_autoptr(GDir) dir = NULL;
114
115 /* try to open */
116 dir = g_dir_open (directory, 0, error);
117 if (dir == NULL)
118 return FALSE;
119
120 /* find each */
121 while ((filename = g_dir_read_name (dir))) {
122 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
123 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
124 if (!fu_common_get_file_list_internal (files, src, error))
125 return FALSE;
126 } else {
127 g_ptr_array_add (files, g_steal_pointer (&src));
128 }
129 }
130 return TRUE;
131
132}
133
134/**
135 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100136 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000137 * @error: A #GError or %NULL
138 *
139 * Returns every file found under @directory, and any subdirectory.
140 * If any path under @directory cannot be accessed due to permissions an error
141 * will be returned.
142 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000143 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600144 *
145 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000146 **/
147GPtrArray *
148fu_common_get_files_recursive (const gchar *path, GError **error)
149{
150 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +0000151
152 g_return_val_if_fail (path != NULL, NULL);
153 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
154
Richard Hughes89e968b2018-03-07 10:01:08 +0000155 if (!fu_common_get_file_list_internal (files, path, error))
156 return NULL;
157 return g_steal_pointer (&files);
158}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100159/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100160 * fu_common_mkdir_parent:
161 * @filename: A full pathname
162 * @error: A #GError, or %NULL
163 *
164 * Creates any required directories, including any parent directories.
165 *
166 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600167 *
168 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100169 **/
170gboolean
171fu_common_mkdir_parent (const gchar *filename, GError **error)
172{
173 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100174
Richard Hughes6a489a92020-12-22 10:32:06 +0000175 g_return_val_if_fail (filename != NULL, FALSE);
176 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
177
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100178 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500179 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
180 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100181 if (g_mkdir_with_parents (parent, 0755) == -1) {
182 g_set_error (error,
183 FWUPD_ERROR,
184 FWUPD_ERROR_INTERNAL,
185 "Failed to create '%s': %s",
186 parent, g_strerror (errno));
187 return FALSE;
188 }
189 return TRUE;
190}
191
192/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100193 * fu_common_set_contents_bytes:
194 * @filename: A filename
195 * @bytes: The data to write
196 * @error: A #GError, or %NULL
197 *
198 * Writes a blob of data to a filename, creating the parent directories as
199 * required.
200 *
201 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600202 *
203 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100204 **/
205gboolean
206fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
207{
208 const gchar *data;
209 gsize size;
210 g_autoptr(GFile) file = NULL;
211 g_autoptr(GFile) file_parent = NULL;
212
Richard Hughes6a489a92020-12-22 10:32:06 +0000213 g_return_val_if_fail (filename != NULL, FALSE);
214 g_return_val_if_fail (bytes != NULL, FALSE);
215 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
216
Richard Hughes943d2c92017-06-21 09:04:39 +0100217 file = g_file_new_for_path (filename);
218 file_parent = g_file_get_parent (file);
219 if (!g_file_query_exists (file_parent, NULL)) {
220 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
221 return FALSE;
222 }
223 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100224 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100225 return g_file_set_contents (filename, data, size, error);
226}
227
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100228/**
229 * fu_common_get_contents_bytes:
230 * @filename: A filename
231 * @error: A #GError, or %NULL
232 *
233 * Reads a blob of data from a file.
234 *
235 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600236 *
237 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100238 **/
239GBytes *
240fu_common_get_contents_bytes (const gchar *filename, GError **error)
241{
242 gchar *data = NULL;
243 gsize len = 0;
Richard Hughes6a489a92020-12-22 10:32:06 +0000244
245 g_return_val_if_fail (filename != NULL, NULL);
246 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
247
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100248 if (!g_file_get_contents (filename, &data, &len, error))
249 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100250 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100251 return g_bytes_new_take (data, len);
252}
Richard Hughes943d2c92017-06-21 09:04:39 +0100253
254/**
255 * fu_common_get_contents_fd:
256 * @fd: A file descriptor
257 * @count: The maximum number of bytes to read
258 * @error: A #GError, or %NULL
259 *
260 * Reads a blob from a specific file descriptor.
261 *
262 * Note: this will close the fd when done
263 *
Richard Hughes4eada342017-10-03 21:20:32 +0100264 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600265 *
266 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100267 **/
268GBytes *
269fu_common_get_contents_fd (gint fd, gsize count, GError **error)
270{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000271#ifdef HAVE_GIO_UNIX
Richard Hughes97ad3352021-01-14 20:07:47 +0000272 guint8 tmp[0x8000] = { 0x0 };
273 g_autoptr(GByteArray) buf = g_byte_array_new ();
Richard Hughes943d2c92017-06-21 09:04:39 +0100274 g_autoptr(GError) error_local = NULL;
275 g_autoptr(GInputStream) stream = NULL;
276
277 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100278 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
279
Richard Hughes919f8ab2018-02-14 10:24:56 +0000280 /* this is invalid */
281 if (count == 0) {
282 g_set_error_literal (error,
283 FWUPD_ERROR,
284 FWUPD_ERROR_NOT_SUPPORTED,
285 "A maximum read size must be specified");
286 return NULL;
287 }
288
Richard Hughes943d2c92017-06-21 09:04:39 +0100289 /* read the entire fd to a data blob */
290 stream = g_unix_input_stream_new (fd, TRUE);
Richard Hughes97ad3352021-01-14 20:07:47 +0000291
292 /* read from stream in 32kB chunks */
293 while (TRUE) {
294 gssize sz;
295 sz = g_input_stream_read (stream, tmp, sizeof(tmp), NULL, &error_local);
296 if (sz == 0)
297 break;
298 if (sz < 0) {
299 g_set_error_literal (error,
300 FWUPD_ERROR,
301 FWUPD_ERROR_INVALID_FILE,
302 error_local->message);
303 return NULL;
304 }
305 g_byte_array_append (buf, tmp, sz);
306 if (buf->len > count) {
307 g_set_error (error,
Richard Hughes943d2c92017-06-21 09:04:39 +0100308 FWUPD_ERROR,
309 FWUPD_ERROR_INVALID_FILE,
Richard Hughes97ad3352021-01-14 20:07:47 +0000310 "cannot read from fd: 0x%x > 0x%x",
311 buf->len, (guint) count);
312 return NULL;
313 }
Richard Hughes943d2c92017-06-21 09:04:39 +0100314 }
Richard Hughes97ad3352021-01-14 20:07:47 +0000315 return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
Richard Hughes9e5675e2019-11-22 09:35:03 +0000316#else
317 g_set_error_literal (error,
318 FWUPD_ERROR,
319 FWUPD_ERROR_NOT_SUPPORTED,
320 "Not supported as <glib-unix.h> is unavailable");
321 return NULL;
322#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100323}
Richard Hughes94f939a2017-08-08 12:21:39 +0100324
Richard Hughes5add3a72021-01-13 19:25:10 +0000325#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100326static gboolean
327fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
328{
329 const gchar *tmp;
330 g_autofree gchar *buf = NULL;
331
332 /* no output file */
333 if (archive_entry_pathname (entry) == NULL)
334 return FALSE;
335
336 /* update output path */
337 tmp = archive_entry_pathname (entry);
338 buf = g_build_filename (dir, tmp, NULL);
339 archive_entry_update_pathname_utf8 (entry, buf);
340 return TRUE;
341}
Richard Hughes5add3a72021-01-13 19:25:10 +0000342#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100343
344/**
345 * fu_common_extract_archive:
346 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100347 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100348 * @error: A #GError, or %NULL
349 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000350 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100351 *
352 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600353 *
354 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100355 **/
356gboolean
357fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
358{
Richard Hughes5add3a72021-01-13 19:25:10 +0000359#ifdef HAVE_LIBARCHIVE
Richard Hughes94f939a2017-08-08 12:21:39 +0100360 gboolean ret = TRUE;
361 int r;
362 struct archive *arch = NULL;
363 struct archive_entry *entry;
364
Richard Hughes6a489a92020-12-22 10:32:06 +0000365 g_return_val_if_fail (blob != NULL, FALSE);
366 g_return_val_if_fail (dir != NULL, FALSE);
367 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
368
Richard Hughes94f939a2017-08-08 12:21:39 +0100369 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100370 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100371 arch = archive_read_new ();
372 archive_read_support_format_all (arch);
373 archive_read_support_filter_all (arch);
374 r = archive_read_open_memory (arch,
375 (void *) g_bytes_get_data (blob, NULL),
376 (size_t) g_bytes_get_size (blob));
377 if (r != 0) {
378 ret = FALSE;
379 g_set_error (error,
380 FWUPD_ERROR,
381 FWUPD_ERROR_INTERNAL,
382 "Cannot open: %s",
383 archive_error_string (arch));
384 goto out;
385 }
386 for (;;) {
387 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100388 r = archive_read_next_header (arch, &entry);
389 if (r == ARCHIVE_EOF)
390 break;
391 if (r != ARCHIVE_OK) {
392 ret = FALSE;
393 g_set_error (error,
394 FWUPD_ERROR,
395 FWUPD_ERROR_INTERNAL,
396 "Cannot read header: %s",
397 archive_error_string (arch));
398 goto out;
399 }
400
401 /* only extract if valid */
402 valid = fu_common_extract_archive_entry (entry, dir);
403 if (!valid)
404 continue;
405 r = archive_read_extract (arch, entry, 0);
406 if (r != ARCHIVE_OK) {
407 ret = FALSE;
408 g_set_error (error,
409 FWUPD_ERROR,
410 FWUPD_ERROR_INTERNAL,
411 "Cannot extract: %s",
412 archive_error_string (arch));
413 goto out;
414 }
415 }
416out:
417 if (arch != NULL) {
418 archive_read_close (arch);
419 archive_read_free (arch);
420 }
421 return ret;
Richard Hughes5add3a72021-01-13 19:25:10 +0000422#else
423 g_set_error_literal (error,
424 FWUPD_ERROR,
425 FWUPD_ERROR_NOT_SUPPORTED,
426 "missing libarchive support");
427 return FALSE;
428#endif
Richard Hughes94f939a2017-08-08 12:21:39 +0100429}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100430
431static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300432fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
433
434static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100435fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
436{
437 va_list args;
438 g_autofree gchar *tmp = NULL;
439 g_auto(GStrv) split = NULL;
440
441 va_start (args, fmt);
442 tmp = g_strdup_vprintf (fmt, args);
443 va_end (args);
444
445 split = g_strsplit (tmp, " ", -1);
446 for (guint i = 0; split[i] != NULL; i++)
447 g_ptr_array_add (argv, g_strdup (split[i]));
448}
449
Mario Limonciello1a680f32019-11-25 19:44:53 -0600450/**
451 * fu_common_find_program_in_path:
452 * @basename: The program to search
453 * @error: A #GError, or %NULL
454 *
455 * Looks for a program in the PATH variable
456 *
457 * Returns: a new #gchar, or %NULL for error
458 *
459 * Since: 1.1.2
460 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100461gchar *
462fu_common_find_program_in_path (const gchar *basename, GError **error)
463{
464 gchar *fn = g_find_program_in_path (basename);
465 if (fn == NULL) {
466 g_set_error (error,
467 FWUPD_ERROR,
468 FWUPD_ERROR_NOT_SUPPORTED,
469 "missing executable %s in PATH",
470 basename);
471 return NULL;
472 }
473 return fn;
474}
475
476static gboolean
477fu_common_test_namespace_support (GError **error)
478{
479 /* test if CONFIG_USER_NS is valid */
480 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
481 g_set_error (error,
482 FWUPD_ERROR,
483 FWUPD_ERROR_NOT_SUPPORTED,
484 "missing CONFIG_USER_NS in kernel");
485 return FALSE;
486 }
487 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
488 g_autofree gchar *clone = NULL;
489 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
490 return FALSE;
491 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
492 g_set_error (error,
493 FWUPD_ERROR,
494 FWUPD_ERROR_NOT_SUPPORTED,
495 "unprivileged user namespace clones disabled by distro");
496 return FALSE;
497 }
498 }
499 return TRUE;
500}
501
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100502/**
503 * fu_common_firmware_builder:
504 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100505 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
506 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507 * @error: A #GError, or %NULL
508 *
509 * Builds a firmware file using tools from the host session in a bubblewrap
510 * jail. Several things happen during build:
511 *
512 * 1. The @bytes data is untarred to a temporary location
513 * 2. A bubblewrap container is set up
514 * 3. The startup.sh script is run inside the container
515 * 4. The firmware.bin is extracted from the container
516 * 5. The temporary location is deleted
517 *
518 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600519 *
520 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100521 **/
522GBytes *
523fu_common_firmware_builder (GBytes *bytes,
524 const gchar *script_fn,
525 const gchar *output_fn,
526 GError **error)
527{
528 gint rc = 0;
529 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500530 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100531 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100532 g_autofree gchar *localstatedir = NULL;
533 g_autofree gchar *output2_fn = NULL;
534 g_autofree gchar *standard_error = NULL;
535 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100536 g_autofree gchar *tmpdir = NULL;
537 g_autoptr(GBytes) firmware_blob = NULL;
538 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
539
540 g_return_val_if_fail (bytes != NULL, NULL);
541 g_return_val_if_fail (script_fn != NULL, NULL);
542 g_return_val_if_fail (output_fn != NULL, NULL);
543 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
544
Mario Limonciello37b59582018-08-13 08:38:01 -0500545 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100546 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
547 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100548 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500549
550 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100551 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100552 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500553
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100554 /* untar file to temp location */
555 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
556 if (tmpdir == NULL)
557 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100558 if (!fu_common_extract_archive (bytes, tmpdir, error))
559 return NULL;
560
561 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100562 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
563 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100564
565 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500566 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100567 fu_common_add_argv (argv, "--die-with-parent");
568 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500569 fu_common_add_argv (argv, "--ro-bind /lib /lib");
Mario Limoncielloed4e9122020-12-15 20:26:50 -0600570 fu_common_add_argv (argv, "--ro-bind-try /lib64 /lib64");
Mario Limonciellob8215572018-07-13 09:49:55 -0500571 fu_common_add_argv (argv, "--ro-bind /bin /bin");
572 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100573 fu_common_add_argv (argv, "--dir /tmp");
574 fu_common_add_argv (argv, "--dir /var");
575 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100576 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
577 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100578 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100579 fu_common_add_argv (argv, "--chdir /tmp");
580 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100581 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100582 g_ptr_array_add (argv, NULL);
583 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
584 g_debug ("running '%s' in %s", argv_str, tmpdir);
585 if (!g_spawn_sync ("/tmp",
586 (gchar **) argv->pdata,
587 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100588 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100589 NULL, NULL, /* child_setup */
590 &standard_output,
591 &standard_error,
592 &rc,
593 error)) {
594 g_prefix_error (error, "failed to run '%s': ", argv_str);
595 return NULL;
596 }
597 if (standard_output != NULL && standard_output[0] != '\0')
598 g_debug ("console output was: %s", standard_output);
599 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500600 FwupdError code = FWUPD_ERROR_INTERNAL;
601 if (errno == ENOTTY)
602 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100603 g_set_error (error,
604 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500605 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100606 "failed to build firmware: %s",
607 standard_error);
608 return NULL;
609 }
610
611 /* get generated file */
612 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
613 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
614 if (firmware_blob == NULL)
615 return NULL;
616
617 /* cleanup temp directory */
618 if (!fu_common_rmtree (tmpdir, error))
619 return NULL;
620
621 /* success */
622 return g_steal_pointer (&firmware_blob);
623}
Richard Hughes049ccc82017-08-09 15:26:56 +0100624
625typedef struct {
626 FuOutputHandler handler_cb;
627 gpointer handler_user_data;
628 GMainLoop *loop;
629 GSource *source;
630 GInputStream *stream;
631 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000632 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100633} FuCommonSpawnHelper;
634
635static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
636
637static gboolean
638fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
639{
640 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
641 gchar buffer[1024];
642 gssize sz;
643 g_auto(GStrv) split = NULL;
644 g_autoptr(GError) error = NULL;
645
646 /* read from stream */
647 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
648 buffer,
649 sizeof(buffer) - 1,
650 NULL,
651 &error);
652 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100653 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
654 g_warning ("failed to get read from nonblocking fd: %s",
655 error->message);
656 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100657 return G_SOURCE_REMOVE;
658 }
659
660 /* no read possible */
661 if (sz == 0)
662 g_main_loop_quit (helper->loop);
663
664 /* emit lines */
665 if (helper->handler_cb != NULL) {
666 buffer[sz] = '\0';
667 split = g_strsplit (buffer, "\n", -1);
668 for (guint i = 0; split[i] != NULL; i++) {
669 if (split[i][0] == '\0')
670 continue;
671 helper->handler_cb (split[i], helper->handler_user_data);
672 }
673 }
674
675 /* set up the source for the next read */
676 fu_common_spawn_create_pollable_source (helper);
677 return G_SOURCE_REMOVE;
678}
679
680static void
681fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
682{
683 if (helper->source != NULL)
684 g_source_destroy (helper->source);
685 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
686 helper->cancellable);
687 g_source_attach (helper->source, NULL);
688 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
689}
690
691static void
692fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
693{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000694 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100695 if (helper->stream != NULL)
696 g_object_unref (helper->stream);
697 if (helper->source != NULL)
698 g_source_destroy (helper->source);
699 if (helper->loop != NULL)
700 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000701 if (helper->timeout_id != 0)
702 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100703 g_free (helper);
704}
705
Mario Limoncielloa98df552018-04-16 12:15:51 -0500706#pragma clang diagnostic push
707#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100708G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500709#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100710
Richard Hughesb768e4d2019-02-26 13:55:18 +0000711static gboolean
712fu_common_spawn_timeout_cb (gpointer user_data)
713{
714 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
715 g_cancellable_cancel (helper->cancellable);
716 g_main_loop_quit (helper->loop);
717 helper->timeout_id = 0;
718 return G_SOURCE_REMOVE;
719}
720
721static void
722fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
723{
724 /* just propagate */
725 g_cancellable_cancel (helper->cancellable);
726}
727
Richard Hughes049ccc82017-08-09 15:26:56 +0100728/**
729 * fu_common_spawn_sync:
730 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100731 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
732 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000733 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100734 * @cancellable: a #GCancellable, or %NULL
735 * @error: A #GError or %NULL
736 *
737 * Runs a subprocess and waits for it to exit. Any output on standard out or
738 * standard error will be forwarded to @handler_cb as whole lines.
739 *
740 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600741 *
742 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100743 **/
744gboolean
745fu_common_spawn_sync (const gchar * const * argv,
746 FuOutputHandler handler_cb,
747 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000748 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100749 GCancellable *cancellable, GError **error)
750{
751 g_autoptr(FuCommonSpawnHelper) helper = NULL;
752 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100753 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000754 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100755
Richard Hughes6a489a92020-12-22 10:32:06 +0000756 g_return_val_if_fail (argv != NULL, FALSE);
757 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
758 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
759
Richard Hughes049ccc82017-08-09 15:26:56 +0100760 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100761 argv_str = g_strjoinv (" ", (gchar **) argv);
762 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100763 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
764 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
765 if (subprocess == NULL)
766 return FALSE;
767
768 /* watch for process to exit */
769 helper = g_new0 (FuCommonSpawnHelper, 1);
770 helper->handler_cb = handler_cb;
771 helper->handler_user_data = handler_user_data;
772 helper->loop = g_main_loop_new (NULL, FALSE);
773 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000774
775 /* always create a cancellable, and connect up the parent */
776 helper->cancellable = g_cancellable_new ();
777 if (cancellable != NULL) {
778 cancellable_id = g_cancellable_connect (cancellable,
779 G_CALLBACK (fu_common_spawn_cancelled_cb),
780 helper, NULL);
781 }
782
783 /* allow timeout */
784 if (timeout_ms > 0) {
785 helper->timeout_id = g_timeout_add (timeout_ms,
786 fu_common_spawn_timeout_cb,
787 helper);
788 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100789 fu_common_spawn_create_pollable_source (helper);
790 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000791 g_cancellable_disconnect (cancellable, cancellable_id);
792 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
793 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100794 return g_subprocess_wait_check (subprocess, cancellable, error);
795}
Richard Hughesae252cd2017-12-08 10:48:15 +0000796
797/**
798 * fu_common_write_uint16:
799 * @buf: A writable buffer
800 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100801 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000802 *
803 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600804 *
805 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000806 **/
807void
808fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
809{
810 guint16 val_hw;
811 switch (endian) {
812 case G_BIG_ENDIAN:
813 val_hw = GUINT16_TO_BE(val_native);
814 break;
815 case G_LITTLE_ENDIAN:
816 val_hw = GUINT16_TO_LE(val_native);
817 break;
818 default:
819 g_assert_not_reached ();
820 }
821 memcpy (buf, &val_hw, sizeof(val_hw));
822}
823
824/**
825 * fu_common_write_uint32:
826 * @buf: A writable buffer
827 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100828 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000829 *
830 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600831 *
832 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000833 **/
834void
835fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
836{
837 guint32 val_hw;
838 switch (endian) {
839 case G_BIG_ENDIAN:
840 val_hw = GUINT32_TO_BE(val_native);
841 break;
842 case G_LITTLE_ENDIAN:
843 val_hw = GUINT32_TO_LE(val_native);
844 break;
845 default:
846 g_assert_not_reached ();
847 }
848 memcpy (buf, &val_hw, sizeof(val_hw));
849}
850
851/**
852 * fu_common_read_uint16:
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 **/
862guint16
863fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
864{
865 guint16 val_hw, val_native;
866 memcpy (&val_hw, buf, sizeof(val_hw));
867 switch (endian) {
868 case G_BIG_ENDIAN:
869 val_native = GUINT16_FROM_BE(val_hw);
870 break;
871 case G_LITTLE_ENDIAN:
872 val_native = GUINT16_FROM_LE(val_hw);
873 break;
874 default:
875 g_assert_not_reached ();
876 }
877 return val_native;
878}
879
880/**
881 * fu_common_read_uint32:
882 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100883 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000884 *
885 * Read a value from a buffer using a specified endian.
886 *
887 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600888 *
889 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000890 **/
891guint32
892fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
893{
894 guint32 val_hw, val_native;
895 memcpy (&val_hw, buf, sizeof(val_hw));
896 switch (endian) {
897 case G_BIG_ENDIAN:
898 val_native = GUINT32_FROM_BE(val_hw);
899 break;
900 case G_LITTLE_ENDIAN:
901 val_native = GUINT32_FROM_LE(val_hw);
902 break;
903 default:
904 g_assert_not_reached ();
905 }
906 return val_native;
907}
Richard Hughese82eef32018-05-20 10:41:26 +0100908
Richard Hughes73bf2332018-08-28 09:38:09 +0100909/**
910 * fu_common_strtoull:
911 * @str: A string, e.g. "0x1234"
912 *
913 * Converts a string value to an integer. Values are assumed base 10, unless
914 * prefixed with "0x" where they are parsed as base 16.
915 *
916 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600917 *
918 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100919 **/
920guint64
921fu_common_strtoull (const gchar *str)
922{
923 guint base = 10;
924 if (str == NULL)
925 return 0x0;
926 if (g_str_has_prefix (str, "0x")) {
927 str += 2;
928 base = 16;
929 }
930 return g_ascii_strtoull (str, NULL, base);
931}
932
Richard Hughesa574a752018-08-31 13:31:03 +0100933/**
934 * fu_common_strstrip:
935 * @str: A string, e.g. " test "
936 *
937 * Removes leading and trailing whitespace from a constant string.
938 *
939 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600940 *
941 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100942 **/
943gchar *
944fu_common_strstrip (const gchar *str)
945{
946 guint head = G_MAXUINT;
947 guint tail = 0;
948
949 g_return_val_if_fail (str != NULL, NULL);
950
951 /* find first non-space char */
952 for (guint i = 0; str[i] != '\0'; i++) {
953 if (str[i] != ' ') {
954 head = i;
955 break;
956 }
957 }
958 if (head == G_MAXUINT)
959 return g_strdup ("");
960
961 /* find last non-space char */
962 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500963 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100964 tail = i;
965 }
966 return g_strndup (str + head, tail - head + 1);
967}
968
Richard Hughese82eef32018-05-20 10:41:26 +0100969static const GError *
970fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
971{
972 for (guint j = 0; j < errors->len; j++) {
973 const GError *error = g_ptr_array_index (errors, j);
974 if (g_error_matches (error, FWUPD_ERROR, error_code))
975 return error;
976 }
977 return NULL;
978}
979
980static guint
981fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
982{
983 guint cnt = 0;
984 for (guint j = 0; j < errors->len; j++) {
985 const GError *error = g_ptr_array_index (errors, j);
986 if (g_error_matches (error, FWUPD_ERROR, error_code))
987 cnt++;
988 }
989 return cnt;
990}
991
992static gboolean
993fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
994{
995 for (guint j = 0; j < errors->len; j++) {
996 const GError *error = g_ptr_array_index (errors, j);
997 gboolean matches_any = FALSE;
998 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
999 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
1000 matches_any = TRUE;
1001 break;
1002 }
1003 }
1004 if (!matches_any)
1005 return FALSE;
1006 }
1007 return TRUE;
1008}
1009
1010/**
1011 * fu_common_error_array_get_best:
1012 * @errors: (element-type GError): array of errors
1013 *
1014 * Finds the 'best' error to show the user from a array of errors, creating a
1015 * completely bespoke error where required.
1016 *
1017 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -06001018 *
1019 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +01001020 **/
1021GError *
1022fu_common_error_array_get_best (GPtrArray *errors)
1023{
1024 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
1025 FWUPD_ERROR_VERSION_SAME,
1026 FWUPD_ERROR_VERSION_NEWER,
1027 FWUPD_ERROR_NOT_SUPPORTED,
1028 FWUPD_ERROR_INTERNAL,
1029 FWUPD_ERROR_NOT_FOUND,
1030 FWUPD_ERROR_LAST };
1031 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
1032 FWUPD_ERROR_NOT_FOUND,
1033 FWUPD_ERROR_NOT_SUPPORTED,
1034 FWUPD_ERROR_LAST };
1035 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
1036 FWUPD_ERROR_VERSION_SAME,
1037 FWUPD_ERROR_NOT_FOUND,
1038 FWUPD_ERROR_NOT_SUPPORTED,
1039 FWUPD_ERROR_LAST };
1040
1041 /* are all the errors either GUID-not-matched or version-same? */
1042 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
1043 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
1044 return g_error_new (FWUPD_ERROR,
1045 FWUPD_ERROR_NOTHING_TO_DO,
1046 "All updatable firmware is already installed");
1047 }
1048
1049 /* are all the errors either GUID-not-matched or version same or newer? */
1050 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
1051 fu_common_error_array_matches_any (errors, err_all_newer)) {
1052 return g_error_new (FWUPD_ERROR,
1053 FWUPD_ERROR_NOTHING_TO_DO,
1054 "All updatable devices already have newer versions");
1055 }
1056
1057 /* get the most important single error */
1058 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1059 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1060 if (error_tmp != NULL)
1061 return g_error_copy (error_tmp);
1062 }
1063
1064 /* fall back to something */
1065 return g_error_new (FWUPD_ERROR,
1066 FWUPD_ERROR_NOT_FOUND,
1067 "No supported devices found");
1068}
Richard Hughes4be17d12018-05-30 20:36:29 +01001069
1070/**
1071 * fu_common_get_path:
1072 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1073 *
1074 * Gets a fwupd-specific system path. These can be overridden with various
1075 * environment variables, for instance %FWUPD_DATADIR.
1076 *
1077 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001078 *
1079 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001080 **/
1081gchar *
1082fu_common_get_path (FuPathKind path_kind)
1083{
1084 const gchar *tmp;
1085 g_autofree gchar *basedir = NULL;
1086
1087 switch (path_kind) {
1088 /* /var */
1089 case FU_PATH_KIND_LOCALSTATEDIR:
1090 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1091 if (tmp != NULL)
1092 return g_strdup (tmp);
1093 tmp = g_getenv ("SNAP_USER_DATA");
1094 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001095 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1096 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001097 /* /proc */
1098 case FU_PATH_KIND_PROCFS:
1099 tmp = g_getenv ("FWUPD_PROCFS");
1100 if (tmp != NULL)
1101 return g_strdup (tmp);
1102 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001103 /* /sys/firmware */
1104 case FU_PATH_KIND_SYSFSDIR_FW:
1105 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1106 if (tmp != NULL)
1107 return g_strdup (tmp);
1108 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001109 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001110 case FU_PATH_KIND_SYSFSDIR_TPM:
1111 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1112 if (tmp != NULL)
1113 return g_strdup (tmp);
1114 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001115 /* /sys/bus/platform/drivers */
1116 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1117 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1118 if (tmp != NULL)
1119 return g_strdup (tmp);
1120 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001121 /* /sys/kernel/security */
1122 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1123 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1124 if (tmp != NULL)
1125 return g_strdup (tmp);
1126 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001127 /* /sys/firmware/acpi/tables */
1128 case FU_PATH_KIND_ACPI_TABLES:
1129 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1130 if (tmp != NULL)
1131 return g_strdup (tmp);
1132 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001133 /* /etc */
1134 case FU_PATH_KIND_SYSCONFDIR:
1135 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1136 if (tmp != NULL)
1137 return g_strdup (tmp);
1138 tmp = g_getenv ("SNAP_USER_DATA");
1139 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001140 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1141 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001142 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1143 case FU_PATH_KIND_PLUGINDIR_PKG:
1144 tmp = g_getenv ("FWUPD_PLUGINDIR");
1145 if (tmp != NULL)
1146 return g_strdup (tmp);
1147 tmp = g_getenv ("SNAP");
1148 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001149 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1150 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001151 /* /usr/share/fwupd */
1152 case FU_PATH_KIND_DATADIR_PKG:
1153 tmp = g_getenv ("FWUPD_DATADIR");
1154 if (tmp != NULL)
1155 return g_strdup (tmp);
1156 tmp = g_getenv ("SNAP");
1157 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001158 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1159 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001160 /* /usr/libexec/fwupd/efi */
1161 case FU_PATH_KIND_EFIAPPDIR:
1162 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1163 if (tmp != NULL)
1164 return g_strdup (tmp);
1165#ifdef EFI_APP_LOCATION
1166 tmp = g_getenv ("SNAP");
1167 if (tmp != NULL)
1168 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1169 return g_strdup (EFI_APP_LOCATION);
1170#else
1171 return NULL;
1172#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001173 /* /etc/fwupd */
1174 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001175 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001176 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001177 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001178 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1179 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1180 /* /var/lib/fwupd */
1181 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001182 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001183 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001184 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001185 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1186 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1187 /* /var/cache/fwupd */
1188 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001189 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001190 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001191 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001192 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1193 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001194 case FU_PATH_KIND_OFFLINE_TRIGGER:
1195 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1196 if (tmp != NULL)
1197 return g_strdup (tmp);
1198 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001199 case FU_PATH_KIND_POLKIT_ACTIONS:
1200#ifdef POLKIT_ACTIONDIR
1201 return g_strdup (POLKIT_ACTIONDIR);
1202#else
1203 return NULL;
1204#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001205 /* this shouldn't happen */
1206 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001207 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001208 }
1209
1210 return NULL;
1211}
Richard Hughes83e56c12018-10-10 20:24:41 +01001212
1213/**
1214 * fu_common_string_replace:
1215 * @string: The #GString to operate on
1216 * @search: The text to search for
1217 * @replace: The text to use for substitutions
1218 *
1219 * Performs multiple search and replace operations on the given string.
1220 *
1221 * Returns: the number of replacements done, or 0 if @search is not found.
1222 *
1223 * Since: 1.2.0
1224 **/
1225guint
1226fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1227{
1228 gchar *tmp;
1229 guint count = 0;
1230 gsize search_idx = 0;
1231 gsize replace_len;
1232 gsize search_len;
1233
1234 g_return_val_if_fail (string != NULL, 0);
1235 g_return_val_if_fail (search != NULL, 0);
1236 g_return_val_if_fail (replace != NULL, 0);
1237
1238 /* nothing to do */
1239 if (string->len == 0)
1240 return 0;
1241
1242 search_len = strlen (search);
1243 replace_len = strlen (replace);
1244
1245 do {
1246 tmp = g_strstr_len (string->str + search_idx, -1, search);
1247 if (tmp == NULL)
1248 break;
1249
1250 /* advance the counter in case @replace contains @search */
1251 search_idx = (gsize) (tmp - string->str);
1252
1253 /* reallocate the string if required */
1254 if (search_len > replace_len) {
1255 g_string_erase (string,
1256 (gssize) search_idx,
1257 (gssize) (search_len - replace_len));
1258 memcpy (tmp, replace, replace_len);
1259 } else if (search_len < replace_len) {
1260 g_string_insert_len (string,
1261 (gssize) search_idx,
1262 replace,
1263 (gssize) (replace_len - search_len));
1264 /* we have to treat this specially as it could have
1265 * been reallocated when the insertion happened */
1266 memcpy (string->str + search_idx, replace, replace_len);
1267 } else {
1268 /* just memcmp in the new string */
1269 memcpy (tmp, replace, replace_len);
1270 }
1271 search_idx += replace_len;
1272 count++;
1273 } while (TRUE);
1274
1275 return count;
1276}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001277
Richard Hughesae96a1f2019-09-23 11:16:36 +01001278/**
1279 * fu_common_strwidth:
1280 * @text: The string to operate on
1281 *
1282 * Returns the width of the string in displayed characters on the console.
1283 *
1284 * Returns: width of text
1285 *
1286 * Since: 1.3.2
1287 **/
1288gsize
1289fu_common_strwidth (const gchar *text)
1290{
1291 const gchar *p = text;
1292 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001293
1294 g_return_val_if_fail (text != NULL, 0);
1295
Richard Hughesae96a1f2019-09-23 11:16:36 +01001296 while (*p) {
1297 gunichar c = g_utf8_get_char (p);
1298 if (g_unichar_iswide (c))
1299 width += 2;
1300 else if (!g_unichar_iszerowidth (c))
1301 width += 1;
1302 p = g_utf8_next_char (p);
1303 }
1304 return width;
1305}
1306
Mario Limonciello1a680f32019-11-25 19:44:53 -06001307/**
1308 * fu_common_string_append_kv:
1309 * @str: A #GString
1310 * @idt: The indent
1311 * @key: A string to append
1312 * @value: a string to append
1313 *
1314 * Appends a key and string value to a string
1315 *
1316 * Since: 1.2.4
1317 */
Richard Hughescea28de2019-08-09 11:16:40 +01001318void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001319fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001320{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001321 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001322 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001323
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001324 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001325
1326 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001327 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001328 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001329 for (gsize i = 0; i < idt; i++)
1330 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001331 if (key[0] != '\0') {
1332 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001333 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001334 } else {
1335 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001336 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001337 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001338 g_auto(GStrv) split = NULL;
1339 split = g_strsplit (value, "\n", -1);
1340 for (guint i = 0; split[i] != NULL; i++) {
1341 if (i == 0) {
1342 for (gsize j = keysz; j < align; j++)
1343 g_string_append (str, " ");
1344 } else {
1345 for (gsize j = 0; j < idt; j++)
1346 g_string_append (str, " ");
1347 }
1348 g_string_append (str, split[i]);
1349 g_string_append (str, "\n");
1350 }
1351 } else {
1352 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001353 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001354}
1355
Mario Limonciello1a680f32019-11-25 19:44:53 -06001356/**
1357 * fu_common_string_append_ku:
1358 * @str: A #GString
1359 * @idt: The indent
1360 * @key: A string to append
1361 * @value: guint64
1362 *
1363 * Appends a key and unsigned integer to a string
1364 *
1365 * Since: 1.2.4
1366 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001367void
1368fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1369{
1370 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1371 fu_common_string_append_kv (str, idt, key, tmp);
1372}
1373
Mario Limonciello1a680f32019-11-25 19:44:53 -06001374/**
1375 * fu_common_string_append_kx:
1376 * @str: A #GString
1377 * @idt: The indent
1378 * @key: A string to append
1379 * @value: guint64
1380 *
1381 * Appends a key and hex integer to a string
1382 *
1383 * Since: 1.2.4
1384 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001385void
1386fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1387{
1388 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1389 fu_common_string_append_kv (str, idt, key, tmp);
1390}
1391
Mario Limonciello1a680f32019-11-25 19:44:53 -06001392/**
1393 * fu_common_string_append_kb:
1394 * @str: A #GString
1395 * @idt: The indent
1396 * @key: A string to append
1397 * @value: Boolean
1398 *
1399 * Appends a key and boolean value to a string
1400 *
1401 * Since: 1.2.4
1402 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001403void
1404fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1405{
1406 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001407}
1408
Richard Hughese59cb9a2018-12-05 14:37:40 +00001409/**
Richard Hughes35481862019-01-06 12:01:58 +00001410 * fu_common_dump_full:
1411 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1412 * @title: prefix title, or %NULL
1413 * @data: buffer to print
1414 * @len: the size of @data
1415 * @columns: break new lines after this many bytes
1416 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1417 *
1418 * Dumps a raw buffer to the screen.
1419 *
1420 * Since: 1.2.4
1421 **/
1422void
1423fu_common_dump_full (const gchar *log_domain,
1424 const gchar *title,
1425 const guint8 *data,
1426 gsize len,
1427 guint columns,
1428 FuDumpFlags flags)
1429{
1430 g_autoptr(GString) str = g_string_new (NULL);
1431
1432 /* optional */
1433 if (title != NULL)
1434 g_string_append_printf (str, "%s:", title);
1435
1436 /* if more than can fit on one line then start afresh */
1437 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1438 g_string_append (str, "\n");
1439 } else {
1440 for (gsize i = str->len; i < 16; i++)
1441 g_string_append (str, " ");
1442 }
1443
1444 /* offset line */
1445 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1446 g_string_append (str, " │ ");
1447 for (gsize i = 0; i < columns; i++)
1448 g_string_append_printf (str, "%02x ", (guint) i);
1449 g_string_append (str, "\n───────┼");
1450 for (gsize i = 0; i < columns; i++)
1451 g_string_append (str, "───");
1452 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1453 }
1454
1455 /* print each row */
1456 for (gsize i = 0; i < len; i++) {
1457 g_string_append_printf (str, "%02x ", data[i]);
1458
1459 /* optionally print ASCII char */
1460 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1461 if (g_ascii_isprint (data[i]))
1462 g_string_append_printf (str, "[%c] ", data[i]);
1463 else
1464 g_string_append (str, "[?] ");
1465 }
1466
1467 /* new row required */
1468 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1469 g_string_append (str, "\n");
1470 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1471 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1472 }
1473 }
1474 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1475}
1476
1477/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001478 * fu_common_dump_raw:
1479 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1480 * @title: prefix title, or %NULL
1481 * @data: buffer to print
1482 * @len: the size of @data
1483 *
1484 * Dumps a raw buffer to the screen.
1485 *
1486 * Since: 1.2.2
1487 **/
1488void
1489fu_common_dump_raw (const gchar *log_domain,
1490 const gchar *title,
1491 const guint8 *data,
1492 gsize len)
1493{
Richard Hughes35481862019-01-06 12:01:58 +00001494 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1495 if (len > 64)
1496 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1497 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001498}
1499
1500/**
Mario Limonciello39602652019-04-29 21:08:58 -05001501 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001502 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1503 * @title: prefix title, or %NULL
1504 * @bytes: a #GBytes
1505 *
1506 * Dumps a byte buffer to the screen.
1507 *
1508 * Since: 1.2.2
1509 **/
1510void
1511fu_common_dump_bytes (const gchar *log_domain,
1512 const gchar *title,
1513 GBytes *bytes)
1514{
1515 gsize len = 0;
1516 const guint8 *data = g_bytes_get_data (bytes, &len);
1517 fu_common_dump_raw (log_domain, title, data, len);
1518}
Richard Hughesfc90f392019-01-15 21:21:16 +00001519
1520/**
1521 * fu_common_bytes_align:
1522 * @bytes: a #GBytes
1523 * @blksz: block size in bytes
1524 * @padval: the byte used to pad the byte buffer
1525 *
1526 * Aligns a block of memory to @blksize using the @padval value; if
1527 * the block is already aligned then the original @bytes is returned.
1528 *
1529 * Returns: (transfer full): a #GBytes, possibly @bytes
1530 *
1531 * Since: 1.2.4
1532 **/
1533GBytes *
1534fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1535{
1536 const guint8 *data;
1537 gsize sz;
1538
1539 g_return_val_if_fail (bytes != NULL, NULL);
1540 g_return_val_if_fail (blksz > 0, NULL);
1541
1542 /* pad */
1543 data = g_bytes_get_data (bytes, &sz);
1544 if (sz % blksz != 0) {
1545 gsize sz_align = ((sz / blksz) + 1) * blksz;
1546 guint8 *data_align = g_malloc (sz_align);
1547 memcpy (data_align, data, sz);
1548 memset (data_align + sz, padval, sz_align - sz);
1549 g_debug ("aligning 0x%x bytes to 0x%x",
1550 (guint) sz, (guint) sz_align);
1551 return g_bytes_new_take (data_align, sz_align);
1552 }
1553
1554 /* perfectly aligned */
1555 return g_bytes_ref (bytes);
1556}
Richard Hughes36999462019-03-19 20:23:29 +00001557
1558/**
1559 * fu_common_bytes_is_empty:
1560 * @bytes: a #GBytes
1561 *
1562 * Checks if a byte array are just empty (0xff) bytes.
1563 *
1564 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001565 *
1566 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001567 **/
1568gboolean
1569fu_common_bytes_is_empty (GBytes *bytes)
1570{
1571 gsize sz = 0;
1572 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1573 for (gsize i = 0; i < sz; i++) {
1574 if (buf[i] != 0xff)
1575 return FALSE;
1576 }
1577 return TRUE;
1578}
Richard Hughes2aad1042019-03-21 09:03:32 +00001579
1580/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001581 * fu_common_bytes_compare_raw:
1582 * @buf1: a buffer
1583 * @bufsz1: sizeof @buf1
1584 * @buf2: another buffer
1585 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001586 * @error: A #GError or %NULL
1587 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001588 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001589 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001590 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001591 *
1592 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001593 **/
1594gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001595fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1596 const guint8 *buf2, gsize bufsz2,
1597 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001598{
Richard Hughes38245ff2019-09-18 14:46:09 +01001599 g_return_val_if_fail (buf1 != NULL, FALSE);
1600 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001601 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1602
1603 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001604 if (bufsz1 != bufsz2) {
1605 g_set_error (error,
1606 G_IO_ERROR,
1607 G_IO_ERROR_INVALID_DATA,
1608 "got %" G_GSIZE_FORMAT " bytes, expected "
1609 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1610 return FALSE;
1611 }
1612
1613 /* check matches */
1614 for (guint i = 0x0; i < bufsz1; i++) {
1615 if (buf1[i] != buf2[i]) {
1616 g_set_error (error,
1617 G_IO_ERROR,
1618 G_IO_ERROR_INVALID_DATA,
1619 "got 0x%02x, expected 0x%02x @ 0x%04x",
1620 buf1[i], buf2[i], i);
1621 return FALSE;
1622 }
1623 }
1624
1625 /* success */
1626 return TRUE;
1627}
Richard Hughes484ee292019-03-22 16:10:50 +00001628
1629/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001630 * fu_common_bytes_compare:
1631 * @bytes1: a #GBytes
1632 * @bytes2: another #GBytes
1633 * @error: A #GError or %NULL
1634 *
1635 * Compares the buffers for equality.
1636 *
1637 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001638 *
1639 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001640 **/
1641gboolean
1642fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1643{
1644 const guint8 *buf1;
1645 const guint8 *buf2;
1646 gsize bufsz1;
1647 gsize bufsz2;
1648
1649 g_return_val_if_fail (bytes1 != NULL, FALSE);
1650 g_return_val_if_fail (bytes2 != NULL, FALSE);
1651 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1652
1653 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1654 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1655 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1656}
1657
1658/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001659 * fu_common_bytes_pad:
1660 * @bytes: a #GBytes
1661 * @sz: the desired size in bytes
1662 *
1663 * Pads a GBytes to a given @sz with `0xff`.
1664 *
1665 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001666 *
1667 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001668 **/
1669GBytes *
1670fu_common_bytes_pad (GBytes *bytes, gsize sz)
1671{
1672 gsize bytes_sz;
1673
1674 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1675
1676 /* pad */
1677 bytes_sz = g_bytes_get_size (bytes);
1678 if (bytes_sz < sz) {
1679 const guint8 *data = g_bytes_get_data (bytes, NULL);
1680 guint8 *data_new = g_malloc (sz);
1681 memcpy (data_new, data, bytes_sz);
1682 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1683 return g_bytes_new_take (data_new, sz);
1684 }
1685
1686 /* exactly right */
1687 return g_bytes_ref (bytes);
1688}
1689
1690/**
Richard Hughes05e33772020-12-08 18:37:02 +00001691 * fu_common_bytes_new_offset:
1692 * @bytes: a #GBytes
1693 * @offset: where subsection starts at
1694 * @length: length of subsection
1695 * @error: A #GError or %NULL
1696 *
1697 * Creates a #GBytes which is a subsection of another #GBytes.
1698 *
1699 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1700 *
1701 * Since: 1.5.4
1702 **/
1703GBytes *
1704fu_common_bytes_new_offset (GBytes *bytes,
1705 gsize offset,
1706 gsize length,
1707 GError **error)
1708{
1709 g_return_val_if_fail (bytes != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001710 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes05e33772020-12-08 18:37:02 +00001711
1712 /* sanity check */
1713 if (offset + length > g_bytes_get_size (bytes)) {
1714 g_set_error (error,
1715 G_IO_ERROR,
1716 G_IO_ERROR_INVALID_DATA,
1717 "cannot create bytes @0x%02x for 0x%02x "
1718 "as buffer only 0x%04x bytes in size",
1719 (guint) offset,
1720 (guint) length,
1721 (guint) g_bytes_get_size (bytes));
1722 return NULL;
1723 }
1724 return g_bytes_new_from_bytes (bytes, offset, length);
1725}
1726
1727/**
Richard Hughes484ee292019-03-22 16:10:50 +00001728 * fu_common_realpath:
1729 * @filename: a filename
1730 * @error: A #GError or %NULL
1731 *
1732 * Finds the canonicalized absolute filename for a path.
1733 *
1734 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001735 *
1736 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001737 **/
1738gchar *
1739fu_common_realpath (const gchar *filename, GError **error)
1740{
1741 char full_tmp[PATH_MAX];
1742
1743 g_return_val_if_fail (filename != NULL, NULL);
Richard Hughes6a489a92020-12-22 10:32:06 +00001744 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Richard Hughes484ee292019-03-22 16:10:50 +00001745
Richard Hughes8694dee2019-11-22 09:16:34 +00001746#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001747 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001748#else
1749 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1750#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001751 g_set_error (error,
1752 G_IO_ERROR,
1753 G_IO_ERROR_INVALID_DATA,
1754 "cannot resolve path: %s",
1755 strerror (errno));
1756 return NULL;
1757 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001758 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1759 g_set_error (error,
1760 G_IO_ERROR,
1761 G_IO_ERROR_INVALID_DATA,
1762 "cannot find path: %s",
1763 full_tmp);
1764 return NULL;
1765 }
Richard Hughes484ee292019-03-22 16:10:50 +00001766 return g_strdup (full_tmp);
1767}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001768
1769/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001770 * fu_common_fnmatch:
1771 * @pattern: a glob pattern, e.g. `*foo*`
1772 * @str: a string to match against the pattern, e.g. `bazfoobar`
1773 *
1774 * Matches a string against a glob pattern.
1775 *
1776 * Return value: %TRUE if the string matched
1777 *
1778 * Since: 1.3.5
1779 **/
1780gboolean
1781fu_common_fnmatch (const gchar *pattern, const gchar *str)
1782{
1783 g_return_val_if_fail (pattern != NULL, FALSE);
1784 g_return_val_if_fail (str != NULL, FALSE);
1785#ifdef HAVE_FNMATCH_H
1786 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001787#elif _WIN32
1788 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1789 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1790 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001791#else
1792 return g_strcmp0 (pattern, str) == 0;
1793#endif
1794}
1795
Richard Hughesa84d7a72020-05-06 12:11:51 +01001796static gint
1797fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1798{
1799 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1800}
1801
1802/**
1803 * fu_common_filename_glob:
1804 * @directory: a directory path
1805 * @pattern: a glob pattern, e.g. `*foo*`
1806 * @error: A #GError or %NULL
1807 *
1808 * Returns all the filenames that match a specific glob pattern.
1809 * Any results are sorted. No matching files will set @error.
1810 *
1811 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1812 *
1813 * Since: 1.5.0
1814 **/
1815GPtrArray *
1816fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1817{
1818 const gchar *basename;
Richard Hughes6a489a92020-12-22 10:32:06 +00001819 g_autoptr(GDir) dir = NULL;
Richard Hughesa84d7a72020-05-06 12:11:51 +01001820 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
Richard Hughes6a489a92020-12-22 10:32:06 +00001821
1822 g_return_val_if_fail (directory != NULL, NULL);
1823 g_return_val_if_fail (pattern != NULL, NULL);
1824 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1825
1826 dir = g_dir_open (directory, 0, error);
Richard Hughesa84d7a72020-05-06 12:11:51 +01001827 if (dir == NULL)
1828 return NULL;
1829 while ((basename = g_dir_read_name (dir)) != NULL) {
1830 if (!fu_common_fnmatch (pattern, basename))
1831 continue;
1832 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1833 }
1834 if (files->len == 0) {
1835 g_set_error_literal (error,
1836 G_IO_ERROR,
1837 G_IO_ERROR_NOT_FOUND,
1838 "no files matched pattern");
1839 return NULL;
1840 }
1841 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1842 return g_steal_pointer (&files);
1843}
1844
Richard Hughes5c508de2019-11-22 09:57:34 +00001845/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001846 * fu_common_strnsplit:
1847 * @str: a string to split
1848 * @sz: size of @str
1849 * @delimiter: a string which specifies the places at which to split the string
1850 * @max_tokens: the maximum number of pieces to split @str into
1851 *
1852 * Splits a string into a maximum of @max_tokens pieces, using the given
1853 * delimiter. If @max_tokens is reached, the remainder of string is appended
1854 * to the last token.
1855 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001856 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001857 *
1858 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001859 **/
1860gchar **
1861fu_common_strnsplit (const gchar *str, gsize sz,
1862 const gchar *delimiter, gint max_tokens)
1863{
1864 if (str[sz - 1] != '\0') {
1865 g_autofree gchar *str2 = g_strndup (str, sz);
1866 return g_strsplit (str2, delimiter, max_tokens);
1867 }
1868 return g_strsplit (str, delimiter, max_tokens);
1869}
Richard Hughes5308ea42019-08-09 12:25:13 +01001870
1871/**
Richard Hughes364e2682021-01-05 14:38:15 +00001872 * fu_common_strsafe:
1873 * @str: (nullable): a string to make safe for printing
1874 * @maxsz: maximum size of returned string
1875 *
1876 * Converts a string into something that can be safely printed.
1877 *
1878 * Return value: (transfer full): safe string, or %NULL if there was nothing valid
1879 *
1880 * Since: 1.5.5
1881 **/
1882gchar *
1883fu_common_strsafe (const gchar *str, gsize maxsz)
1884{
1885 gboolean valid = FALSE;
1886 g_autoptr(GString) tmp = NULL;
1887
1888 g_return_val_if_fail (maxsz > 0, NULL);
1889
1890 /* sanity check */
1891 if (str == NULL)
1892 return NULL;
1893
1894 /* replace non-printable chars with '.' */
1895 tmp = g_string_sized_new (strlen (str));
1896 for (gsize i = 0; str[i] != '\0' && i < maxsz; i++) {
1897 if (!g_ascii_isprint (str[i])) {
1898 g_string_append_c (tmp, '.');
1899 continue;
1900 }
1901 g_string_append_c (tmp, str[i]);
1902 valid = TRUE;
1903 }
1904
1905 /* if just junk, don't return 'all dots' */
1906 if (tmp->len == 0 || !valid)
1907 return NULL;
1908 return g_string_free (g_steal_pointer (&tmp), FALSE);
1909}
1910
1911/**
Richard Hughes5308ea42019-08-09 12:25:13 +01001912 * fu_memcpy_safe:
1913 * @dst: destination buffer
1914 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1915 * @dst_offset: offset in bytes into @dst to copy to
1916 * @src: source buffer
1917 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1918 * @src_offset: offset in bytes into @src to copy from
1919 * @n: number of bytes to copy from @src+@offset from
1920 * @error: A #GError or %NULL
1921 *
1922 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1923 * of both the destination and the source allows us to check for buffer overflow.
1924 *
1925 * Providing the buffer offsets also allows us to check reading past the end of
1926 * the source buffer. For this reason the caller should NEVER add an offset to
1927 * @src or @dst.
1928 *
1929 * You don't need to use this function in "obviously correct" cases, nor should
1930 * you use it when performance is a concern. Only us it when you're not sure if
1931 * malicious data from a device or firmware could cause memory corruption.
1932 *
1933 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001934 *
1935 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001936 **/
1937gboolean
1938fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1939 const guint8 *src, gsize src_sz, gsize src_offset,
1940 gsize n, GError **error)
1941{
Richard Hughes6a489a92020-12-22 10:32:06 +00001942 g_return_val_if_fail (dst != NULL, FALSE);
1943 g_return_val_if_fail (src != NULL, FALSE);
1944 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1945
Richard Hughes5308ea42019-08-09 12:25:13 +01001946 if (n == 0)
1947 return TRUE;
1948
1949 if (n > src_sz) {
1950 g_set_error (error,
1951 FWUPD_ERROR,
1952 FWUPD_ERROR_READ,
1953 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1954 (guint) n, (guint) src_sz);
1955 return FALSE;
1956 }
1957 if (n + src_offset > src_sz) {
1958 g_set_error (error,
1959 FWUPD_ERROR,
1960 FWUPD_ERROR_READ,
1961 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1962 (guint) n, (guint) src_offset, (guint) src_sz);
1963 return FALSE;
1964 }
1965 if (n > dst_sz) {
1966 g_set_error (error,
1967 FWUPD_ERROR,
1968 FWUPD_ERROR_WRITE,
1969 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1970 (guint) n, (guint) dst_sz);
1971 return FALSE;
1972 }
1973 if (n + dst_offset > dst_sz) {
1974 g_set_error (error,
1975 FWUPD_ERROR,
1976 FWUPD_ERROR_WRITE,
1977 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1978 (guint) n, (guint) dst_offset, (guint) dst_sz);
1979 return FALSE;
1980 }
1981
1982 /* phew! */
1983 memcpy (dst + dst_offset, src + src_offset, n);
1984 return TRUE;
1985}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001986
Richard Hughes80768f52019-10-22 07:19:14 +01001987/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001988 * fu_common_read_uint8_safe:
1989 * @buf: source buffer
1990 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1991 * @offset: offset in bytes into @buf to copy from
1992 * @value: (out) (allow-none): the parsed value
1993 * @error: A #GError or %NULL
1994 *
1995 * Read a value from a buffer in a safe way.
1996 *
1997 * You don't need to use this function in "obviously correct" cases, nor should
1998 * you use it when performance is a concern. Only us it when you're not sure if
1999 * malicious data from a device or firmware could cause memory corruption.
2000 *
2001 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002002 *
2003 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01002004 **/
2005gboolean
2006fu_common_read_uint8_safe (const guint8 *buf,
2007 gsize bufsz,
2008 gsize offset,
2009 guint8 *value,
2010 GError **error)
2011{
2012 guint8 tmp;
Richard Hughes6a489a92020-12-22 10:32:06 +00002013
2014 g_return_val_if_fail (buf != NULL, FALSE);
2015 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2016
Richard Hughesc21a0b92019-10-24 12:24:37 +01002017 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
2018 buf, bufsz, offset, /* src */
2019 sizeof(tmp), error))
2020 return FALSE;
2021 if (value != NULL)
2022 *value = tmp;
2023 return TRUE;
2024}
2025
2026/**
Richard Hughes80768f52019-10-22 07:19:14 +01002027 * fu_common_read_uint16_safe:
2028 * @buf: source buffer
2029 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2030 * @offset: offset in bytes into @buf to copy from
2031 * @value: (out) (allow-none): the parsed value
2032 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2033 * @error: A #GError or %NULL
2034 *
2035 * Read a value from a buffer using a specified endian in a safe way.
2036 *
2037 * You don't need to use this function in "obviously correct" cases, nor should
2038 * you use it when performance is a concern. Only us it when you're not sure if
2039 * malicious data from a device or firmware could cause memory corruption.
2040 *
2041 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002042 *
2043 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002044 **/
2045gboolean
2046fu_common_read_uint16_safe (const guint8 *buf,
2047 gsize bufsz,
2048 gsize offset,
2049 guint16 *value,
2050 FuEndianType endian,
2051 GError **error)
2052{
2053 guint8 dst[2] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002054
2055 g_return_val_if_fail (buf != NULL, FALSE);
2056 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2057
Richard Hughes7d01ac92019-10-23 14:31:46 +01002058 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002059 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002060 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002061 return FALSE;
2062 if (value != NULL)
2063 *value = fu_common_read_uint16 (dst, endian);
2064 return TRUE;
2065}
2066
2067/**
2068 * fu_common_read_uint32_safe:
2069 * @buf: source buffer
2070 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
2071 * @offset: offset in bytes into @buf to copy from
2072 * @value: (out) (allow-none): the parsed value
2073 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
2074 * @error: A #GError or %NULL
2075 *
2076 * Read a value from a buffer using a specified endian in a safe way.
2077 *
2078 * You don't need to use this function in "obviously correct" cases, nor should
2079 * you use it when performance is a concern. Only us it when you're not sure if
2080 * malicious data from a device or firmware could cause memory corruption.
2081 *
2082 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06002083 *
2084 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01002085 **/
2086gboolean
2087fu_common_read_uint32_safe (const guint8 *buf,
2088 gsize bufsz,
2089 gsize offset,
2090 guint32 *value,
2091 FuEndianType endian,
2092 GError **error)
2093{
2094 guint8 dst[4] = { 0x0 };
Richard Hughes6a489a92020-12-22 10:32:06 +00002095
2096 g_return_val_if_fail (buf != NULL, FALSE);
2097 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2098
Richard Hughes7d01ac92019-10-23 14:31:46 +01002099 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01002100 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01002101 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01002102 return FALSE;
2103 if (value != NULL)
2104 *value = fu_common_read_uint32 (dst, endian);
2105 return TRUE;
2106}
2107
Mario Limonciello1a680f32019-11-25 19:44:53 -06002108/**
2109 * fu_byte_array_append_uint8:
2110 * @array: A #GByteArray
2111 * @data: #guint8
2112 *
2113 * Adds a 8 bit integer to a byte array
2114 *
2115 * Since: 1.3.1
2116 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002117void
2118fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2119{
2120 g_byte_array_append (array, &data, sizeof(data));
2121}
2122
Mario Limonciello1a680f32019-11-25 19:44:53 -06002123/**
2124 * fu_byte_array_append_uint16:
2125 * @array: A #GByteArray
2126 * @data: #guint16
2127 * @endian: #FuEndianType
2128 *
2129 * Adds a 16 bit integer to a byte array
2130 *
2131 * Since: 1.3.1
2132 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002133void
2134fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2135{
2136 guint8 buf[2];
2137 fu_common_write_uint16 (buf, data, endian);
2138 g_byte_array_append (array, buf, sizeof(buf));
2139}
2140
Mario Limonciello1a680f32019-11-25 19:44:53 -06002141/**
2142 * fu_byte_array_append_uint32:
2143 * @array: A #GByteArray
2144 * @data: #guint32
2145 * @endian: #FuEndianType
2146 *
2147 * Adds a 32 bit integer to a byte array
2148 *
2149 * Since: 1.3.1
2150 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002151void
2152fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2153{
2154 guint8 buf[4];
2155 fu_common_write_uint32 (buf, data, endian);
2156 g_byte_array_append (array, buf, sizeof(buf));
2157}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002158
2159/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002160 * fu_byte_array_set_size:
2161 * @array: a #GByteArray
2162 * @length: the new size of the GByteArray
2163 *
2164 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2165 *
2166 * Since: 1.5.0
2167 **/
2168void
2169fu_byte_array_set_size (GByteArray *array, guint length)
2170{
2171 guint oldlength = array->len;
2172 g_byte_array_set_size (array, length);
2173 if (length > oldlength)
2174 memset (array->data + oldlength, 0x0, length - oldlength);
2175}
2176
2177/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002178 * fu_common_kernel_locked_down:
2179 *
2180 * Determines if kernel lockdown in effect
2181 *
2182 * Since: 1.3.8
2183 **/
2184gboolean
2185fu_common_kernel_locked_down (void)
2186{
2187#ifndef _WIN32
2188 gsize len = 0;
2189 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2190 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2191 g_autofree gchar *data = NULL;
2192 g_auto(GStrv) options = NULL;
2193
2194 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2195 return FALSE;
2196 if (!g_file_get_contents (fname, &data, &len, NULL))
2197 return FALSE;
2198 if (len < 1)
2199 return FALSE;
2200 options = g_strsplit (data, " ", -1);
2201 for (guint i = 0; options[i] != NULL; i++) {
2202 if (g_strcmp0 (options[i], "[none]") == 0)
2203 return FALSE;
2204 }
2205 return TRUE;
2206#else
2207 return FALSE;
2208#endif
2209}
Richard Hughes9223c892020-05-09 20:32:08 +01002210
2211/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002212 * fu_common_cpuid:
2213 * @leaf: The CPUID level, now called the 'leaf' by Intel
2214 * @eax: (out) (nullable): EAX register
2215 * @ebx: (out) (nullable): EBX register
2216 * @ecx: (out) (nullable): ECX register
2217 * @edx: (out) (nullable): EDX register
2218 * @error: A #GError or NULL
2219 *
2220 * Calls CPUID and returns the registers for the given leaf.
2221 *
2222 * Return value: %TRUE if the registers are set.
2223 *
2224 * Since: 1.5.0
2225 **/
2226gboolean
2227fu_common_cpuid (guint32 leaf,
2228 guint32 *eax,
2229 guint32 *ebx,
2230 guint32 *ecx,
2231 guint32 *edx,
2232 GError **error)
2233{
2234#ifdef HAVE_CPUID_H
2235 guint eax_tmp = 0;
2236 guint ebx_tmp = 0;
2237 guint ecx_tmp = 0;
2238 guint edx_tmp = 0;
2239
Richard Hughes6a489a92020-12-22 10:32:06 +00002240 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2241
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002242 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002243 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002244 if (eax != NULL)
2245 *eax = eax_tmp;
2246 if (ebx != NULL)
2247 *ebx = ebx_tmp;
2248 if (ecx != NULL)
2249 *ecx = ecx_tmp;
2250 if (edx != NULL)
2251 *edx = edx_tmp;
2252 return TRUE;
2253#else
2254 g_set_error_literal (error,
2255 G_IO_ERROR,
2256 G_IO_ERROR_NOT_SUPPORTED,
2257 "no <cpuid.h> support");
2258 return FALSE;
2259#endif
2260}
2261
2262/**
Richard Hughes9223c892020-05-09 20:32:08 +01002263 * fu_common_is_cpu_intel:
2264 *
2265 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2266 *
2267 * Return value: %TRUE if the vendor was Intel.
Richard Hughesb63cfa92021-01-05 22:57:12 +00002268 * Deprecated: 1.5.5: Use fu_common_get_cpu_vendor() instead.
Richard Hughes9223c892020-05-09 20:32:08 +01002269 *
2270 * Since: 1.5.0
2271 **/
2272gboolean
2273fu_common_is_cpu_intel (void)
2274{
Richard Hughesb63cfa92021-01-05 22:57:12 +00002275 return fu_common_get_cpu_vendor () == FU_CPU_VENDOR_INTEL;
2276}
2277
2278/**
2279 * fu_common_get_cpu_vendor:
2280 *
2281 * Uses CPUID to discover the CPU vendor.
2282 *
2283 * Return value: a #FuCpuVendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD.
2284 *
2285 * Since: 1.5.5
2286 **/
2287FuCpuVendor
2288fu_common_get_cpu_vendor (void)
2289{
2290#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002291 guint ebx = 0;
2292 guint ecx = 0;
2293 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002294
Richard Hughesb63cfa92021-01-05 22:57:12 +00002295 if (fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL)) {
2296 if (ebx == signature_INTEL_ebx &&
2297 edx == signature_INTEL_edx &&
2298 ecx == signature_INTEL_ecx) {
2299 return FU_CPU_VENDOR_INTEL;
2300 }
2301 if (ebx == signature_AMD_ebx &&
2302 edx == signature_AMD_edx &&
2303 ecx == signature_AMD_ecx) {
2304 return FU_CPU_VENDOR_AMD;
2305 }
Richard Hughes9223c892020-05-09 20:32:08 +01002306 }
Richard Hughesbd444322020-05-21 12:05:03 +01002307#endif
Richard Hughesb63cfa92021-01-05 22:57:12 +00002308
2309 /* failed */
2310 return FU_CPU_VENDOR_UNKNOWN;
Richard Hughes9223c892020-05-09 20:32:08 +01002311}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002312
Richard Hughes36111472020-08-12 15:04:24 +01002313/**
2314 * fu_common_is_live_media:
2315 *
2316 * Checks if the user is running from a live media using various heuristics.
2317 *
2318 * Returns: %TRUE if live
2319 *
2320 * Since: 1.4.6
2321 **/
2322gboolean
2323fu_common_is_live_media (void)
2324{
2325 gsize bufsz = 0;
2326 g_autofree gchar *buf = NULL;
2327 g_auto(GStrv) tokens = NULL;
2328 const gchar *args[] = {
2329 "rd.live.image",
2330 "boot=live",
2331 NULL, /* last entry */
2332 };
2333 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2334 return TRUE;
2335 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2336 return FALSE;
2337 if (bufsz == 0)
2338 return FALSE;
2339 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2340 for (guint i = 0; args[i] != NULL; i++) {
2341 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2342 return TRUE;
2343 }
2344 return FALSE;
2345}
2346
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002347static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002348fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002349{
2350 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002351 const gchar *obj;
2352 g_autoptr(GVariant) output = NULL;
2353 g_autoptr(GDBusProxy) proxy = NULL;
2354 g_autoptr(GPtrArray) devices = NULL;
2355 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002356 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002357
Richard Hughes43417b22020-10-30 14:46:16 +00002358 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2359 if (connection == NULL) {
2360 g_prefix_error (error, "failed to get system bus: ");
2361 return NULL;
2362 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002363 proxy = g_dbus_proxy_new_sync (connection,
2364 G_DBUS_PROXY_FLAGS_NONE, NULL,
2365 UDISKS_DBUS_SERVICE,
2366 UDISKS_DBUS_PATH,
2367 UDISKS_DBUS_MANAGER_INTERFACE,
2368 NULL, error);
2369 if (proxy == NULL) {
2370 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2371 return NULL;
2372 }
2373 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002374 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002375 "GetBlockDevices",
2376 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002377 G_DBUS_CALL_FLAGS_NONE,
2378 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002379 if (output == NULL) {
2380 if (error != NULL)
2381 g_dbus_error_strip_remote_error (*error);
2382 g_prefix_error (error, "failed to call %s.%s(): ",
2383 UDISKS_DBUS_SERVICE,
2384 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002385 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002386 }
Richard Hughes43417b22020-10-30 14:46:16 +00002387 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002388 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002389 while (g_variant_iter_next (iter, "&o", &obj)) {
2390 g_autoptr(GDBusProxy) proxy_blk = NULL;
2391 proxy_blk = g_dbus_proxy_new_sync (connection,
2392 G_DBUS_PROXY_FLAGS_NONE, NULL,
2393 UDISKS_DBUS_SERVICE,
2394 obj,
2395 UDISKS_DBUS_INTERFACE_BLOCK,
2396 NULL, error);
2397 if (proxy_blk == NULL) {
2398 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2399 return NULL;
2400 }
2401 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2402 }
2403
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002404
2405 return g_steal_pointer (&devices);
2406}
2407
2408/**
2409 * fu_common_get_volumes_by_kind:
2410 * @kind: A volume kind, typically a GUID
2411 * @error: A #GError or NULL
2412 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002413 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002414 *
2415 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2416 *
2417 * Since: 1.4.6
2418 **/
2419GPtrArray *
2420fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2421{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002422 g_autoptr(GPtrArray) devices = NULL;
2423 g_autoptr(GPtrArray) volumes = NULL;
2424
Richard Hughes6a489a92020-12-22 10:32:06 +00002425 g_return_val_if_fail (kind != NULL, NULL);
2426 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2427
Richard Hughes43417b22020-10-30 14:46:16 +00002428 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002429 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002430 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002431 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2432 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002433 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002434 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002435 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002436 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002437 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002438 g_autoptr(GVariant) val = NULL;
2439
Richard Hughes43417b22020-10-30 14:46:16 +00002440 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002441 G_DBUS_PROXY_FLAGS_NONE, NULL,
2442 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002443 g_dbus_proxy_get_object_path (proxy_blk),
2444 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002445 NULL, error);
2446 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002447 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2448 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002449 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002450 }
2451 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2452 if (val == NULL)
2453 continue;
2454
Richard Hughesdb344d52020-09-09 19:42:27 +01002455 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002456 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2457 G_DBUS_PROXY_FLAGS_NONE, NULL,
2458 UDISKS_DBUS_SERVICE,
2459 g_dbus_proxy_get_object_path (proxy_blk),
2460 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2461 NULL, error);
2462 if (proxy_fs == NULL) {
2463 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2464 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002465 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002466 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002467 vol = g_object_new (FU_TYPE_VOLUME,
2468 "proxy-block", proxy_blk,
2469 "proxy-filesystem", proxy_fs,
2470 NULL);
2471 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2472 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2473 fu_volume_is_internal (vol),
2474 fu_volume_get_id_type (vol));
2475 if (g_strcmp0 (type_str, kind) != 0)
2476 continue;
2477 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002478 }
2479 if (volumes->len == 0) {
2480 g_set_error (error,
2481 G_IO_ERROR,
2482 G_IO_ERROR_NOT_FOUND,
2483 "no volumes of type %s", kind);
2484 return NULL;
2485 }
2486 return g_steal_pointer (&volumes);
2487}
2488
2489/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002490 * fu_common_get_volume_by_device:
2491 * @device: A device string, typcically starting with `/dev/`
2492 * @error: A #GError or NULL
2493 *
2494 * Finds the first volume from the specified device.
2495 *
2496 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2497 *
2498 * Since: 1.5.1
2499 **/
2500FuVolume *
2501fu_common_get_volume_by_device (const gchar *device, GError **error)
2502{
2503 g_autoptr(GPtrArray) devices = NULL;
2504
Richard Hughes6a489a92020-12-22 10:32:06 +00002505 g_return_val_if_fail (device != NULL, NULL);
2506 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2507
Richard Hughes0bdf5612020-10-30 14:56:22 +00002508 /* find matching block device */
2509 devices = fu_common_get_block_devices (error);
2510 if (devices == NULL)
2511 return NULL;
2512 for (guint i = 0; i < devices->len; i++) {
2513 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2514 g_autoptr(GVariant) val = NULL;
2515 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2516 if (val == NULL)
2517 continue;
2518 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2519 return g_object_new (FU_TYPE_VOLUME,
2520 "proxy-block", proxy_blk,
2521 NULL);
2522 }
2523 }
2524
2525 /* failed */
2526 g_set_error (error,
2527 G_IO_ERROR,
2528 G_IO_ERROR_NOT_FOUND,
2529 "no volumes for device %s",
2530 device);
2531 return NULL;
2532}
2533
2534/**
2535 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002536 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002537 * @error: A #GError or NULL
2538 *
2539 * Finds the first volume from the specified device.
2540 *
2541 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2542 *
2543 * Since: 1.5.1
2544 **/
2545FuVolume *
2546fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2547{
2548 g_autoptr(GPtrArray) devices = NULL;
2549
Richard Hughes6a489a92020-12-22 10:32:06 +00002550 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2551
Richard Hughes0bdf5612020-10-30 14:56:22 +00002552 /* find matching block device */
2553 devices = fu_common_get_block_devices (error);
2554 if (devices == NULL)
2555 return NULL;
2556 for (guint i = 0; i < devices->len; i++) {
2557 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2558 g_autoptr(GVariant) val = NULL;
2559 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2560 if (val == NULL)
2561 continue;
2562 if (devnum == g_variant_get_uint64 (val)) {
2563 return g_object_new (FU_TYPE_VOLUME,
2564 "proxy-block", proxy_blk,
2565 NULL);
2566 }
2567 }
2568
2569 /* failed */
2570 g_set_error (error,
2571 G_IO_ERROR,
2572 G_IO_ERROR_NOT_FOUND,
2573 "no volumes for devnum %u",
2574 devnum);
2575 return NULL;
2576}
2577
2578/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002579 * fu_common_get_esp_default:
2580 * @error: A #GError or NULL
2581 *
2582 * Gets the platform default ESP
2583 *
2584 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2585 *
2586 * Since: 1.4.6
2587 **/
2588FuVolume *
2589fu_common_get_esp_default (GError **error)
2590{
2591 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002592 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002593 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2594 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002595 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002596 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002597 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002598
Richard Hughes6a489a92020-12-22 10:32:06 +00002599 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2600
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002601 /* for the test suite use local directory for ESP */
2602 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2603 if (path_tmp != NULL)
2604 return fu_volume_new_from_mount_path (path_tmp);
2605
Mario Limonciello56d816a2020-11-11 16:59:30 -06002606 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2607 if (volumes == NULL) {
2608 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2609 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2610 if (volumes == NULL) {
2611 g_prefix_error (error, "%s: ", error_local->message);
2612 return NULL;
2613 }
2614 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002615
2616 /* are there _any_ internal vfat partitions?
2617 * remember HintSystem is just that -- a hint! */
2618 for (guint i = 0; i < volumes->len; i++) {
2619 FuVolume *vol = g_ptr_array_index (volumes, i);
2620 g_autofree gchar *type = fu_volume_get_id_type (vol);
2621 if (g_strcmp0 (type, "vfat") == 0 &&
2622 fu_volume_is_internal (vol)) {
2623 has_internal = TRUE;
2624 break;
2625 }
2626 }
2627
2628 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002629 for (guint i = 0; i < volumes->len; i++) {
2630 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002631 g_autofree gchar *type = fu_volume_get_id_type (vol);
2632 if (type == NULL)
2633 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002634 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002635 continue;
2636 if (g_strcmp0 (type, "vfat") == 0)
2637 g_ptr_array_add (volumes_vfat, vol);
2638 }
2639 if (volumes_vfat->len == 0) {
2640 g_set_error (error,
2641 G_IO_ERROR,
2642 G_IO_ERROR_INVALID_FILENAME,
2643 "No ESP found");
2644 return NULL;
2645 }
2646 for (guint i = 0; i < volumes_vfat->len; i++) {
2647 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002648 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2649 }
2650 if (volumes_mtab->len == 1) {
2651 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2652 return g_object_ref (vol);
2653 }
2654 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2655 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2656 return g_object_ref (vol);
2657 }
2658 g_set_error (error,
2659 G_IO_ERROR,
2660 G_IO_ERROR_INVALID_FILENAME,
2661 "More than one available ESP");
2662 return NULL;
2663}
2664
2665/**
2666 * fu_common_get_esp_for_path:
2667 * @esp_path: A path to the ESP
2668 * @error: A #GError or NULL
2669 *
2670 * Gets the platform ESP using a UNIX or UDisks path
2671 *
2672 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2673 *
2674 * Since: 1.4.6
2675 **/
2676FuVolume *
2677fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2678{
Richard Hughes6a489a92020-12-22 10:32:06 +00002679 g_autofree gchar *basename = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002680 g_autoptr(GPtrArray) volumes = NULL;
2681
Richard Hughes6a489a92020-12-22 10:32:06 +00002682 g_return_val_if_fail (esp_path != NULL, NULL);
2683 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2684
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002685 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2686 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002687 return NULL;
Richard Hughes6a489a92020-12-22 10:32:06 +00002688 basename = g_path_get_basename (esp_path);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002689 for (guint i = 0; i < volumes->len; i++) {
2690 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002691 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002692 if (g_strcmp0 (basename, vol_basename) == 0)
2693 return g_object_ref (vol);
2694 }
2695 g_set_error (error,
2696 G_IO_ERROR,
2697 G_IO_ERROR_INVALID_FILENAME,
2698 "No ESP with path %s",
2699 esp_path);
2700 return NULL;
2701}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002702
2703/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002704 * fu_common_crc8:
2705 * @buf: memory buffer
2706 * @bufsz: sizeof buf
2707 *
2708 * Returns the cyclic redundancy check value for the given memory buffer.
2709 *
2710 * Returns: CRC value
2711 *
2712 * Since: 1.5.0
2713 **/
2714guint8
2715fu_common_crc8 (const guint8 *buf, gsize bufsz)
2716{
2717 guint32 crc = 0;
2718 for (gsize j = bufsz; j > 0; j--) {
2719 crc ^= (*(buf++) << 8);
2720 for (guint32 i = 8; i; i--) {
2721 if (crc & 0x8000)
2722 crc ^= (0x1070 << 3);
2723 crc <<= 1;
2724 }
2725 }
2726 return ~((guint8) (crc >> 8));
2727}
2728
2729/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002730 * fu_common_crc16:
2731 * @buf: memory buffer
2732 * @bufsz: sizeof buf
2733 *
2734 * Returns the cyclic redundancy check value for the given memory buffer.
2735 *
2736 * Returns: CRC value
2737 *
2738 * Since: 1.5.0
2739 **/
2740guint16
2741fu_common_crc16 (const guint8 *buf, gsize bufsz)
2742{
2743 guint16 crc = 0xffff;
2744 for (gsize len = bufsz; len > 0; len--) {
2745 crc = (guint16) (crc ^ (*buf++));
2746 for (guint8 i = 0; i < 8; i++) {
2747 if (crc & 0x1) {
2748 crc = (crc >> 1) ^ 0xa001;
2749 } else {
2750 crc >>= 1;
2751 }
2752 }
2753 }
2754 return ~crc;
2755}
2756
2757/**
2758 * fu_common_crc32_full:
2759 * @buf: memory buffer
2760 * @bufsz: sizeof buf
2761 * @crc: initial CRC value, typically 0xFFFFFFFF
2762 * @polynomial: CRC polynomial, typically 0xEDB88320
2763 *
2764 * Returns the cyclic redundancy check value for the given memory buffer.
2765 *
2766 * Returns: CRC value
2767 *
2768 * Since: 1.5.0
2769 **/
2770guint32
2771fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2772{
2773 for (guint32 idx = 0; idx < bufsz; idx++) {
2774 guint8 data = *buf++;
2775 crc = crc ^ data;
2776 for (guint32 bit = 0; bit < 8; bit++) {
2777 guint32 mask = -(crc & 1);
2778 crc = (crc >> 1) ^ (polynomial & mask);
2779 }
2780 }
2781 return ~crc;
2782}
2783
2784/**
2785 * fu_common_crc32:
2786 * @buf: memory buffer
2787 * @bufsz: sizeof buf
2788 *
2789 * Returns the cyclic redundancy check value for the given memory buffer.
2790 *
2791 * Returns: CRC value
2792 *
2793 * Since: 1.5.0
2794 **/
2795guint32
2796fu_common_crc32 (const guint8 *buf, gsize bufsz)
2797{
2798 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2799}