blob: 7225cdcfac6336d8005202d23c3cdd72fa4db1c6 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes943d2c92017-06-21 09:04:39 +01002 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes943d2c92017-06-21 09:04:39 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuCommon"
8
Richard Hughes943d2c92017-06-21 09:04:39 +01009#include <config.h>
10
Richard Hughes9e5675e2019-11-22 09:35:03 +000011#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +010012#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#endif
Richard Hughes954dd9f2017-08-08 13:36:25 +010014#include <glib/gstdio.h>
15
Richard Hughes5c508de2019-11-22 09:57:34 +000016#ifdef HAVE_FNMATCH_H
17#include <fnmatch.h>
Richard Hughes45a00732019-11-22 16:57:14 +000018#elif _WIN32
19#include <shlwapi.h>
Richard Hughes5c508de2019-11-22 09:57:34 +000020#endif
21
Richard Hughesbd444322020-05-21 12:05:03 +010022#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010023#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010024#endif
Richard Hughes9223c892020-05-09 20:32:08 +010025
Richard Hughes94f939a2017-08-08 12:21:39 +010026#include <archive_entry.h>
27#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010028#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000029#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010032
33#include "fwupd-error.h"
34
35#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010036#include "fu-volume-private.h"
37
38#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
39#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
40#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
41#define UDISKS_DBUS_PART_INTERFACE "org.freedesktop.UDisks2.Partition"
42#define UDISKS_DBUS_FILE_INTERFACE "org.freedesktop.UDisks2.Filesystem"
Richard Hughes943d2c92017-06-21 09:04:39 +010043
44/**
Richard Hughes4eada342017-10-03 21:20:32 +010045 * SECTION:fu-common
46 * @short_description: common functionality for plugins to use
47 *
48 * Helper functions that can be used by the daemon and plugins.
49 *
50 * See also: #FuPlugin
51 */
52
53/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010054 * fu_common_rmtree:
55 * @directory: a directory name
56 * @error: A #GError or %NULL
57 *
58 * Recursively removes a directory.
59 *
60 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060061 *
62 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010063 **/
64gboolean
65fu_common_rmtree (const gchar *directory, GError **error)
66{
67 const gchar *filename;
68 g_autoptr(GDir) dir = NULL;
69
70 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010071 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010072 dir = g_dir_open (directory, 0, error);
73 if (dir == NULL)
74 return FALSE;
75
76 /* find each */
77 while ((filename = g_dir_read_name (dir))) {
78 g_autofree gchar *src = NULL;
79 src = g_build_filename (directory, filename, NULL);
80 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
81 if (!fu_common_rmtree (src, error))
82 return FALSE;
83 } else {
84 if (g_unlink (src) != 0) {
85 g_set_error (error,
86 FWUPD_ERROR,
87 FWUPD_ERROR_INTERNAL,
88 "Failed to delete: %s", src);
89 return FALSE;
90 }
91 }
92 }
93 if (g_remove (directory) != 0) {
94 g_set_error (error,
95 FWUPD_ERROR,
96 FWUPD_ERROR_INTERNAL,
97 "Failed to delete: %s", directory);
98 return FALSE;
99 }
100 return TRUE;
101}
102
Richard Hughes89e968b2018-03-07 10:01:08 +0000103static gboolean
104fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
105{
106 const gchar *filename;
107 g_autoptr(GDir) dir = NULL;
108
109 /* try to open */
110 dir = g_dir_open (directory, 0, error);
111 if (dir == NULL)
112 return FALSE;
113
114 /* find each */
115 while ((filename = g_dir_read_name (dir))) {
116 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
117 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
118 if (!fu_common_get_file_list_internal (files, src, error))
119 return FALSE;
120 } else {
121 g_ptr_array_add (files, g_steal_pointer (&src));
122 }
123 }
124 return TRUE;
125
126}
127
128/**
129 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100130 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000131 * @error: A #GError or %NULL
132 *
133 * Returns every file found under @directory, and any subdirectory.
134 * If any path under @directory cannot be accessed due to permissions an error
135 * will be returned.
136 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000137 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600138 *
139 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000140 **/
141GPtrArray *
142fu_common_get_files_recursive (const gchar *path, GError **error)
143{
144 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
145 if (!fu_common_get_file_list_internal (files, path, error))
146 return NULL;
147 return g_steal_pointer (&files);
148}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100149/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100150 * fu_common_mkdir_parent:
151 * @filename: A full pathname
152 * @error: A #GError, or %NULL
153 *
154 * Creates any required directories, including any parent directories.
155 *
156 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600157 *
158 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100159 **/
160gboolean
161fu_common_mkdir_parent (const gchar *filename, GError **error)
162{
163 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100164
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100165 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100166 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100167 if (g_mkdir_with_parents (parent, 0755) == -1) {
168 g_set_error (error,
169 FWUPD_ERROR,
170 FWUPD_ERROR_INTERNAL,
171 "Failed to create '%s': %s",
172 parent, g_strerror (errno));
173 return FALSE;
174 }
175 return TRUE;
176}
177
178/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100179 * fu_common_set_contents_bytes:
180 * @filename: A filename
181 * @bytes: The data to write
182 * @error: A #GError, or %NULL
183 *
184 * Writes a blob of data to a filename, creating the parent directories as
185 * required.
186 *
187 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600188 *
189 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100190 **/
191gboolean
192fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
193{
194 const gchar *data;
195 gsize size;
196 g_autoptr(GFile) file = NULL;
197 g_autoptr(GFile) file_parent = NULL;
198
199 file = g_file_new_for_path (filename);
200 file_parent = g_file_get_parent (file);
201 if (!g_file_query_exists (file_parent, NULL)) {
202 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
203 return FALSE;
204 }
205 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100206 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100207 return g_file_set_contents (filename, data, size, error);
208}
209
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100210/**
211 * fu_common_get_contents_bytes:
212 * @filename: A filename
213 * @error: A #GError, or %NULL
214 *
215 * Reads a blob of data from a file.
216 *
217 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600218 *
219 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100220 **/
221GBytes *
222fu_common_get_contents_bytes (const gchar *filename, GError **error)
223{
224 gchar *data = NULL;
225 gsize len = 0;
226 if (!g_file_get_contents (filename, &data, &len, error))
227 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100228 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100229 return g_bytes_new_take (data, len);
230}
Richard Hughes943d2c92017-06-21 09:04:39 +0100231
232/**
233 * fu_common_get_contents_fd:
234 * @fd: A file descriptor
235 * @count: The maximum number of bytes to read
236 * @error: A #GError, or %NULL
237 *
238 * Reads a blob from a specific file descriptor.
239 *
240 * Note: this will close the fd when done
241 *
Richard Hughes4eada342017-10-03 21:20:32 +0100242 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600243 *
244 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100245 **/
246GBytes *
247fu_common_get_contents_fd (gint fd, gsize count, GError **error)
248{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000249#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100250 g_autoptr(GBytes) blob = NULL;
251 g_autoptr(GError) error_local = NULL;
252 g_autoptr(GInputStream) stream = NULL;
253
254 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100255 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
256
Richard Hughes919f8ab2018-02-14 10:24:56 +0000257 /* this is invalid */
258 if (count == 0) {
259 g_set_error_literal (error,
260 FWUPD_ERROR,
261 FWUPD_ERROR_NOT_SUPPORTED,
262 "A maximum read size must be specified");
263 return NULL;
264 }
265
Richard Hughes943d2c92017-06-21 09:04:39 +0100266 /* read the entire fd to a data blob */
267 stream = g_unix_input_stream_new (fd, TRUE);
268 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
269 if (blob == NULL) {
270 g_set_error_literal (error,
271 FWUPD_ERROR,
272 FWUPD_ERROR_INVALID_FILE,
273 error_local->message);
274 return NULL;
275 }
276 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000277#else
278 g_set_error_literal (error,
279 FWUPD_ERROR,
280 FWUPD_ERROR_NOT_SUPPORTED,
281 "Not supported as <glib-unix.h> is unavailable");
282 return NULL;
283#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100284}
Richard Hughes94f939a2017-08-08 12:21:39 +0100285
286static gboolean
287fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
288{
289 const gchar *tmp;
290 g_autofree gchar *buf = NULL;
291
292 /* no output file */
293 if (archive_entry_pathname (entry) == NULL)
294 return FALSE;
295
296 /* update output path */
297 tmp = archive_entry_pathname (entry);
298 buf = g_build_filename (dir, tmp, NULL);
299 archive_entry_update_pathname_utf8 (entry, buf);
300 return TRUE;
301}
302
303/**
304 * fu_common_extract_archive:
305 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100306 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100307 * @error: A #GError, or %NULL
308 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000309 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100310 *
311 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600312 *
313 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100314 **/
315gboolean
316fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
317{
318 gboolean ret = TRUE;
319 int r;
320 struct archive *arch = NULL;
321 struct archive_entry *entry;
322
323 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100324 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100325 arch = archive_read_new ();
326 archive_read_support_format_all (arch);
327 archive_read_support_filter_all (arch);
328 r = archive_read_open_memory (arch,
329 (void *) g_bytes_get_data (blob, NULL),
330 (size_t) g_bytes_get_size (blob));
331 if (r != 0) {
332 ret = FALSE;
333 g_set_error (error,
334 FWUPD_ERROR,
335 FWUPD_ERROR_INTERNAL,
336 "Cannot open: %s",
337 archive_error_string (arch));
338 goto out;
339 }
340 for (;;) {
341 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100342 r = archive_read_next_header (arch, &entry);
343 if (r == ARCHIVE_EOF)
344 break;
345 if (r != ARCHIVE_OK) {
346 ret = FALSE;
347 g_set_error (error,
348 FWUPD_ERROR,
349 FWUPD_ERROR_INTERNAL,
350 "Cannot read header: %s",
351 archive_error_string (arch));
352 goto out;
353 }
354
355 /* only extract if valid */
356 valid = fu_common_extract_archive_entry (entry, dir);
357 if (!valid)
358 continue;
359 r = archive_read_extract (arch, entry, 0);
360 if (r != ARCHIVE_OK) {
361 ret = FALSE;
362 g_set_error (error,
363 FWUPD_ERROR,
364 FWUPD_ERROR_INTERNAL,
365 "Cannot extract: %s",
366 archive_error_string (arch));
367 goto out;
368 }
369 }
370out:
371 if (arch != NULL) {
372 archive_read_close (arch);
373 archive_read_free (arch);
374 }
375 return ret;
376}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100377
378static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300379fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
380
381static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100382fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
383{
384 va_list args;
385 g_autofree gchar *tmp = NULL;
386 g_auto(GStrv) split = NULL;
387
388 va_start (args, fmt);
389 tmp = g_strdup_vprintf (fmt, args);
390 va_end (args);
391
392 split = g_strsplit (tmp, " ", -1);
393 for (guint i = 0; split[i] != NULL; i++)
394 g_ptr_array_add (argv, g_strdup (split[i]));
395}
396
Mario Limonciello1a680f32019-11-25 19:44:53 -0600397/**
398 * fu_common_find_program_in_path:
399 * @basename: The program to search
400 * @error: A #GError, or %NULL
401 *
402 * Looks for a program in the PATH variable
403 *
404 * Returns: a new #gchar, or %NULL for error
405 *
406 * Since: 1.1.2
407 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100408gchar *
409fu_common_find_program_in_path (const gchar *basename, GError **error)
410{
411 gchar *fn = g_find_program_in_path (basename);
412 if (fn == NULL) {
413 g_set_error (error,
414 FWUPD_ERROR,
415 FWUPD_ERROR_NOT_SUPPORTED,
416 "missing executable %s in PATH",
417 basename);
418 return NULL;
419 }
420 return fn;
421}
422
423static gboolean
424fu_common_test_namespace_support (GError **error)
425{
426 /* test if CONFIG_USER_NS is valid */
427 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
428 g_set_error (error,
429 FWUPD_ERROR,
430 FWUPD_ERROR_NOT_SUPPORTED,
431 "missing CONFIG_USER_NS in kernel");
432 return FALSE;
433 }
434 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
435 g_autofree gchar *clone = NULL;
436 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
437 return FALSE;
438 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
439 g_set_error (error,
440 FWUPD_ERROR,
441 FWUPD_ERROR_NOT_SUPPORTED,
442 "unprivileged user namespace clones disabled by distro");
443 return FALSE;
444 }
445 }
446 return TRUE;
447}
448
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100449/**
450 * fu_common_firmware_builder:
451 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100452 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
453 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100454 * @error: A #GError, or %NULL
455 *
456 * Builds a firmware file using tools from the host session in a bubblewrap
457 * jail. Several things happen during build:
458 *
459 * 1. The @bytes data is untarred to a temporary location
460 * 2. A bubblewrap container is set up
461 * 3. The startup.sh script is run inside the container
462 * 4. The firmware.bin is extracted from the container
463 * 5. The temporary location is deleted
464 *
465 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600466 *
467 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100468 **/
469GBytes *
470fu_common_firmware_builder (GBytes *bytes,
471 const gchar *script_fn,
472 const gchar *output_fn,
473 GError **error)
474{
475 gint rc = 0;
476 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500477 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100478 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100479 g_autofree gchar *localstatedir = NULL;
480 g_autofree gchar *output2_fn = NULL;
481 g_autofree gchar *standard_error = NULL;
482 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100483 g_autofree gchar *tmpdir = NULL;
484 g_autoptr(GBytes) firmware_blob = NULL;
485 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
486
487 g_return_val_if_fail (bytes != NULL, NULL);
488 g_return_val_if_fail (script_fn != NULL, NULL);
489 g_return_val_if_fail (output_fn != NULL, NULL);
490 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
491
Mario Limonciello37b59582018-08-13 08:38:01 -0500492 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100493 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
494 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100495 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500496
497 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100498 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100499 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500500
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100501 /* untar file to temp location */
502 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
503 if (tmpdir == NULL)
504 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100505 if (!fu_common_extract_archive (bytes, tmpdir, error))
506 return NULL;
507
508 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100509 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
510 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100511
512 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500513 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100514 fu_common_add_argv (argv, "--die-with-parent");
515 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500516 fu_common_add_argv (argv, "--ro-bind /lib /lib");
517 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
518 fu_common_add_argv (argv, "--ro-bind /bin /bin");
519 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100520 fu_common_add_argv (argv, "--dir /tmp");
521 fu_common_add_argv (argv, "--dir /var");
522 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100523 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
524 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100525 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100526 fu_common_add_argv (argv, "--chdir /tmp");
527 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100528 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100529 g_ptr_array_add (argv, NULL);
530 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
531 g_debug ("running '%s' in %s", argv_str, tmpdir);
532 if (!g_spawn_sync ("/tmp",
533 (gchar **) argv->pdata,
534 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100535 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100536 NULL, NULL, /* child_setup */
537 &standard_output,
538 &standard_error,
539 &rc,
540 error)) {
541 g_prefix_error (error, "failed to run '%s': ", argv_str);
542 return NULL;
543 }
544 if (standard_output != NULL && standard_output[0] != '\0')
545 g_debug ("console output was: %s", standard_output);
546 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500547 FwupdError code = FWUPD_ERROR_INTERNAL;
548 if (errno == ENOTTY)
549 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100550 g_set_error (error,
551 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500552 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100553 "failed to build firmware: %s",
554 standard_error);
555 return NULL;
556 }
557
558 /* get generated file */
559 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
560 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
561 if (firmware_blob == NULL)
562 return NULL;
563
564 /* cleanup temp directory */
565 if (!fu_common_rmtree (tmpdir, error))
566 return NULL;
567
568 /* success */
569 return g_steal_pointer (&firmware_blob);
570}
Richard Hughes049ccc82017-08-09 15:26:56 +0100571
572typedef struct {
573 FuOutputHandler handler_cb;
574 gpointer handler_user_data;
575 GMainLoop *loop;
576 GSource *source;
577 GInputStream *stream;
578 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000579 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100580} FuCommonSpawnHelper;
581
582static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
583
584static gboolean
585fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
586{
587 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
588 gchar buffer[1024];
589 gssize sz;
590 g_auto(GStrv) split = NULL;
591 g_autoptr(GError) error = NULL;
592
593 /* read from stream */
594 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
595 buffer,
596 sizeof(buffer) - 1,
597 NULL,
598 &error);
599 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100600 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
601 g_warning ("failed to get read from nonblocking fd: %s",
602 error->message);
603 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100604 return G_SOURCE_REMOVE;
605 }
606
607 /* no read possible */
608 if (sz == 0)
609 g_main_loop_quit (helper->loop);
610
611 /* emit lines */
612 if (helper->handler_cb != NULL) {
613 buffer[sz] = '\0';
614 split = g_strsplit (buffer, "\n", -1);
615 for (guint i = 0; split[i] != NULL; i++) {
616 if (split[i][0] == '\0')
617 continue;
618 helper->handler_cb (split[i], helper->handler_user_data);
619 }
620 }
621
622 /* set up the source for the next read */
623 fu_common_spawn_create_pollable_source (helper);
624 return G_SOURCE_REMOVE;
625}
626
627static void
628fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
629{
630 if (helper->source != NULL)
631 g_source_destroy (helper->source);
632 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
633 helper->cancellable);
634 g_source_attach (helper->source, NULL);
635 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
636}
637
638static void
639fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
640{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000641 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100642 if (helper->stream != NULL)
643 g_object_unref (helper->stream);
644 if (helper->source != NULL)
645 g_source_destroy (helper->source);
646 if (helper->loop != NULL)
647 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000648 if (helper->timeout_id != 0)
649 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100650 g_free (helper);
651}
652
Mario Limoncielloa98df552018-04-16 12:15:51 -0500653#pragma clang diagnostic push
654#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100655G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500656#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100657
Richard Hughesb768e4d2019-02-26 13:55:18 +0000658static gboolean
659fu_common_spawn_timeout_cb (gpointer user_data)
660{
661 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
662 g_cancellable_cancel (helper->cancellable);
663 g_main_loop_quit (helper->loop);
664 helper->timeout_id = 0;
665 return G_SOURCE_REMOVE;
666}
667
668static void
669fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
670{
671 /* just propagate */
672 g_cancellable_cancel (helper->cancellable);
673}
674
Richard Hughes049ccc82017-08-09 15:26:56 +0100675/**
676 * fu_common_spawn_sync:
677 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100678 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
679 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000680 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100681 * @cancellable: a #GCancellable, or %NULL
682 * @error: A #GError or %NULL
683 *
684 * Runs a subprocess and waits for it to exit. Any output on standard out or
685 * standard error will be forwarded to @handler_cb as whole lines.
686 *
687 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600688 *
689 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100690 **/
691gboolean
692fu_common_spawn_sync (const gchar * const * argv,
693 FuOutputHandler handler_cb,
694 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000695 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100696 GCancellable *cancellable, GError **error)
697{
698 g_autoptr(FuCommonSpawnHelper) helper = NULL;
699 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100700 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000701 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100702
703 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100704 argv_str = g_strjoinv (" ", (gchar **) argv);
705 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100706 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
707 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
708 if (subprocess == NULL)
709 return FALSE;
710
711 /* watch for process to exit */
712 helper = g_new0 (FuCommonSpawnHelper, 1);
713 helper->handler_cb = handler_cb;
714 helper->handler_user_data = handler_user_data;
715 helper->loop = g_main_loop_new (NULL, FALSE);
716 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000717
718 /* always create a cancellable, and connect up the parent */
719 helper->cancellable = g_cancellable_new ();
720 if (cancellable != NULL) {
721 cancellable_id = g_cancellable_connect (cancellable,
722 G_CALLBACK (fu_common_spawn_cancelled_cb),
723 helper, NULL);
724 }
725
726 /* allow timeout */
727 if (timeout_ms > 0) {
728 helper->timeout_id = g_timeout_add (timeout_ms,
729 fu_common_spawn_timeout_cb,
730 helper);
731 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100732 fu_common_spawn_create_pollable_source (helper);
733 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000734 g_cancellable_disconnect (cancellable, cancellable_id);
735 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
736 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100737 return g_subprocess_wait_check (subprocess, cancellable, error);
738}
Richard Hughesae252cd2017-12-08 10:48:15 +0000739
740/**
741 * fu_common_write_uint16:
742 * @buf: A writable buffer
743 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100744 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000745 *
746 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600747 *
748 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000749 **/
750void
751fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
752{
753 guint16 val_hw;
754 switch (endian) {
755 case G_BIG_ENDIAN:
756 val_hw = GUINT16_TO_BE(val_native);
757 break;
758 case G_LITTLE_ENDIAN:
759 val_hw = GUINT16_TO_LE(val_native);
760 break;
761 default:
762 g_assert_not_reached ();
763 }
764 memcpy (buf, &val_hw, sizeof(val_hw));
765}
766
767/**
768 * fu_common_write_uint32:
769 * @buf: A writable buffer
770 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100771 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000772 *
773 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600774 *
775 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000776 **/
777void
778fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
779{
780 guint32 val_hw;
781 switch (endian) {
782 case G_BIG_ENDIAN:
783 val_hw = GUINT32_TO_BE(val_native);
784 break;
785 case G_LITTLE_ENDIAN:
786 val_hw = GUINT32_TO_LE(val_native);
787 break;
788 default:
789 g_assert_not_reached ();
790 }
791 memcpy (buf, &val_hw, sizeof(val_hw));
792}
793
794/**
795 * fu_common_read_uint16:
796 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100797 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000798 *
799 * Read a value from a buffer using a specified endian.
800 *
801 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600802 *
803 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000804 **/
805guint16
806fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
807{
808 guint16 val_hw, val_native;
809 memcpy (&val_hw, buf, sizeof(val_hw));
810 switch (endian) {
811 case G_BIG_ENDIAN:
812 val_native = GUINT16_FROM_BE(val_hw);
813 break;
814 case G_LITTLE_ENDIAN:
815 val_native = GUINT16_FROM_LE(val_hw);
816 break;
817 default:
818 g_assert_not_reached ();
819 }
820 return val_native;
821}
822
823/**
824 * fu_common_read_uint32:
825 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100826 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000827 *
828 * Read a value from a buffer using a specified endian.
829 *
830 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600831 *
832 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000833 **/
834guint32
835fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
836{
837 guint32 val_hw, val_native;
838 memcpy (&val_hw, buf, sizeof(val_hw));
839 switch (endian) {
840 case G_BIG_ENDIAN:
841 val_native = GUINT32_FROM_BE(val_hw);
842 break;
843 case G_LITTLE_ENDIAN:
844 val_native = GUINT32_FROM_LE(val_hw);
845 break;
846 default:
847 g_assert_not_reached ();
848 }
849 return val_native;
850}
Richard Hughese82eef32018-05-20 10:41:26 +0100851
Richard Hughes73bf2332018-08-28 09:38:09 +0100852/**
853 * fu_common_strtoull:
854 * @str: A string, e.g. "0x1234"
855 *
856 * Converts a string value to an integer. Values are assumed base 10, unless
857 * prefixed with "0x" where they are parsed as base 16.
858 *
859 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600860 *
861 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100862 **/
863guint64
864fu_common_strtoull (const gchar *str)
865{
866 guint base = 10;
867 if (str == NULL)
868 return 0x0;
869 if (g_str_has_prefix (str, "0x")) {
870 str += 2;
871 base = 16;
872 }
873 return g_ascii_strtoull (str, NULL, base);
874}
875
Richard Hughesa574a752018-08-31 13:31:03 +0100876/**
877 * fu_common_strstrip:
878 * @str: A string, e.g. " test "
879 *
880 * Removes leading and trailing whitespace from a constant string.
881 *
882 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600883 *
884 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100885 **/
886gchar *
887fu_common_strstrip (const gchar *str)
888{
889 guint head = G_MAXUINT;
890 guint tail = 0;
891
892 g_return_val_if_fail (str != NULL, NULL);
893
894 /* find first non-space char */
895 for (guint i = 0; str[i] != '\0'; i++) {
896 if (str[i] != ' ') {
897 head = i;
898 break;
899 }
900 }
901 if (head == G_MAXUINT)
902 return g_strdup ("");
903
904 /* find last non-space char */
905 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500906 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100907 tail = i;
908 }
909 return g_strndup (str + head, tail - head + 1);
910}
911
Richard Hughese82eef32018-05-20 10:41:26 +0100912static const GError *
913fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
914{
915 for (guint j = 0; j < errors->len; j++) {
916 const GError *error = g_ptr_array_index (errors, j);
917 if (g_error_matches (error, FWUPD_ERROR, error_code))
918 return error;
919 }
920 return NULL;
921}
922
923static guint
924fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
925{
926 guint cnt = 0;
927 for (guint j = 0; j < errors->len; j++) {
928 const GError *error = g_ptr_array_index (errors, j);
929 if (g_error_matches (error, FWUPD_ERROR, error_code))
930 cnt++;
931 }
932 return cnt;
933}
934
935static gboolean
936fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
937{
938 for (guint j = 0; j < errors->len; j++) {
939 const GError *error = g_ptr_array_index (errors, j);
940 gboolean matches_any = FALSE;
941 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
942 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
943 matches_any = TRUE;
944 break;
945 }
946 }
947 if (!matches_any)
948 return FALSE;
949 }
950 return TRUE;
951}
952
953/**
954 * fu_common_error_array_get_best:
955 * @errors: (element-type GError): array of errors
956 *
957 * Finds the 'best' error to show the user from a array of errors, creating a
958 * completely bespoke error where required.
959 *
960 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600961 *
962 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100963 **/
964GError *
965fu_common_error_array_get_best (GPtrArray *errors)
966{
967 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
968 FWUPD_ERROR_VERSION_SAME,
969 FWUPD_ERROR_VERSION_NEWER,
970 FWUPD_ERROR_NOT_SUPPORTED,
971 FWUPD_ERROR_INTERNAL,
972 FWUPD_ERROR_NOT_FOUND,
973 FWUPD_ERROR_LAST };
974 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
975 FWUPD_ERROR_NOT_FOUND,
976 FWUPD_ERROR_NOT_SUPPORTED,
977 FWUPD_ERROR_LAST };
978 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
979 FWUPD_ERROR_VERSION_SAME,
980 FWUPD_ERROR_NOT_FOUND,
981 FWUPD_ERROR_NOT_SUPPORTED,
982 FWUPD_ERROR_LAST };
983
984 /* are all the errors either GUID-not-matched or version-same? */
985 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
986 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
987 return g_error_new (FWUPD_ERROR,
988 FWUPD_ERROR_NOTHING_TO_DO,
989 "All updatable firmware is already installed");
990 }
991
992 /* are all the errors either GUID-not-matched or version same or newer? */
993 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
994 fu_common_error_array_matches_any (errors, err_all_newer)) {
995 return g_error_new (FWUPD_ERROR,
996 FWUPD_ERROR_NOTHING_TO_DO,
997 "All updatable devices already have newer versions");
998 }
999
1000 /* get the most important single error */
1001 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1002 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1003 if (error_tmp != NULL)
1004 return g_error_copy (error_tmp);
1005 }
1006
1007 /* fall back to something */
1008 return g_error_new (FWUPD_ERROR,
1009 FWUPD_ERROR_NOT_FOUND,
1010 "No supported devices found");
1011}
Richard Hughes4be17d12018-05-30 20:36:29 +01001012
1013/**
1014 * fu_common_get_path:
1015 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1016 *
1017 * Gets a fwupd-specific system path. These can be overridden with various
1018 * environment variables, for instance %FWUPD_DATADIR.
1019 *
1020 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001021 *
1022 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001023 **/
1024gchar *
1025fu_common_get_path (FuPathKind path_kind)
1026{
1027 const gchar *tmp;
1028 g_autofree gchar *basedir = NULL;
1029
1030 switch (path_kind) {
1031 /* /var */
1032 case FU_PATH_KIND_LOCALSTATEDIR:
1033 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1034 if (tmp != NULL)
1035 return g_strdup (tmp);
1036 tmp = g_getenv ("SNAP_USER_DATA");
1037 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001038 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1039 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001040 /* /proc */
1041 case FU_PATH_KIND_PROCFS:
1042 tmp = g_getenv ("FWUPD_PROCFS");
1043 if (tmp != NULL)
1044 return g_strdup (tmp);
1045 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001046 /* /sys/firmware */
1047 case FU_PATH_KIND_SYSFSDIR_FW:
1048 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1049 if (tmp != NULL)
1050 return g_strdup (tmp);
1051 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001052 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001053 case FU_PATH_KIND_SYSFSDIR_TPM:
1054 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1055 if (tmp != NULL)
1056 return g_strdup (tmp);
1057 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001058 /* /sys/bus/platform/drivers */
1059 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1060 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1061 if (tmp != NULL)
1062 return g_strdup (tmp);
1063 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001064 /* /sys/kernel/security */
1065 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1066 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1067 if (tmp != NULL)
1068 return g_strdup (tmp);
1069 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001070 /* /sys/firmware/acpi/tables */
1071 case FU_PATH_KIND_ACPI_TABLES:
1072 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1073 if (tmp != NULL)
1074 return g_strdup (tmp);
1075 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001076 /* /etc */
1077 case FU_PATH_KIND_SYSCONFDIR:
1078 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1079 if (tmp != NULL)
1080 return g_strdup (tmp);
1081 tmp = g_getenv ("SNAP_USER_DATA");
1082 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001083 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1084 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001085 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1086 case FU_PATH_KIND_PLUGINDIR_PKG:
1087 tmp = g_getenv ("FWUPD_PLUGINDIR");
1088 if (tmp != NULL)
1089 return g_strdup (tmp);
1090 tmp = g_getenv ("SNAP");
1091 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001092 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1093 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001094 /* /usr/share/fwupd */
1095 case FU_PATH_KIND_DATADIR_PKG:
1096 tmp = g_getenv ("FWUPD_DATADIR");
1097 if (tmp != NULL)
1098 return g_strdup (tmp);
1099 tmp = g_getenv ("SNAP");
1100 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001101 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1102 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001103 /* /usr/libexec/fwupd/efi */
1104 case FU_PATH_KIND_EFIAPPDIR:
1105 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1106 if (tmp != NULL)
1107 return g_strdup (tmp);
1108#ifdef EFI_APP_LOCATION
1109 tmp = g_getenv ("SNAP");
1110 if (tmp != NULL)
1111 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1112 return g_strdup (EFI_APP_LOCATION);
1113#else
1114 return NULL;
1115#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001116 /* /etc/fwupd */
1117 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001118 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001119 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001120 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001121 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1122 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1123 /* /var/lib/fwupd */
1124 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001125 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001126 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001127 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001128 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1129 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1130 /* /var/cache/fwupd */
1131 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001132 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001133 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001134 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001135 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1136 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001137 case FU_PATH_KIND_OFFLINE_TRIGGER:
1138 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1139 if (tmp != NULL)
1140 return g_strdup (tmp);
1141 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001142 case FU_PATH_KIND_POLKIT_ACTIONS:
1143#ifdef POLKIT_ACTIONDIR
1144 return g_strdup (POLKIT_ACTIONDIR);
1145#else
1146 return NULL;
1147#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001148 /* this shouldn't happen */
1149 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001150 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001151 }
1152
1153 return NULL;
1154}
Richard Hughes83e56c12018-10-10 20:24:41 +01001155
1156/**
1157 * fu_common_string_replace:
1158 * @string: The #GString to operate on
1159 * @search: The text to search for
1160 * @replace: The text to use for substitutions
1161 *
1162 * Performs multiple search and replace operations on the given string.
1163 *
1164 * Returns: the number of replacements done, or 0 if @search is not found.
1165 *
1166 * Since: 1.2.0
1167 **/
1168guint
1169fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1170{
1171 gchar *tmp;
1172 guint count = 0;
1173 gsize search_idx = 0;
1174 gsize replace_len;
1175 gsize search_len;
1176
1177 g_return_val_if_fail (string != NULL, 0);
1178 g_return_val_if_fail (search != NULL, 0);
1179 g_return_val_if_fail (replace != NULL, 0);
1180
1181 /* nothing to do */
1182 if (string->len == 0)
1183 return 0;
1184
1185 search_len = strlen (search);
1186 replace_len = strlen (replace);
1187
1188 do {
1189 tmp = g_strstr_len (string->str + search_idx, -1, search);
1190 if (tmp == NULL)
1191 break;
1192
1193 /* advance the counter in case @replace contains @search */
1194 search_idx = (gsize) (tmp - string->str);
1195
1196 /* reallocate the string if required */
1197 if (search_len > replace_len) {
1198 g_string_erase (string,
1199 (gssize) search_idx,
1200 (gssize) (search_len - replace_len));
1201 memcpy (tmp, replace, replace_len);
1202 } else if (search_len < replace_len) {
1203 g_string_insert_len (string,
1204 (gssize) search_idx,
1205 replace,
1206 (gssize) (replace_len - search_len));
1207 /* we have to treat this specially as it could have
1208 * been reallocated when the insertion happened */
1209 memcpy (string->str + search_idx, replace, replace_len);
1210 } else {
1211 /* just memcmp in the new string */
1212 memcpy (tmp, replace, replace_len);
1213 }
1214 search_idx += replace_len;
1215 count++;
1216 } while (TRUE);
1217
1218 return count;
1219}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001220
Richard Hughesae96a1f2019-09-23 11:16:36 +01001221/**
1222 * fu_common_strwidth:
1223 * @text: The string to operate on
1224 *
1225 * Returns the width of the string in displayed characters on the console.
1226 *
1227 * Returns: width of text
1228 *
1229 * Since: 1.3.2
1230 **/
1231gsize
1232fu_common_strwidth (const gchar *text)
1233{
1234 const gchar *p = text;
1235 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001236
1237 g_return_val_if_fail (text != NULL, 0);
1238
Richard Hughesae96a1f2019-09-23 11:16:36 +01001239 while (*p) {
1240 gunichar c = g_utf8_get_char (p);
1241 if (g_unichar_iswide (c))
1242 width += 2;
1243 else if (!g_unichar_iszerowidth (c))
1244 width += 1;
1245 p = g_utf8_next_char (p);
1246 }
1247 return width;
1248}
1249
Mario Limonciello1a680f32019-11-25 19:44:53 -06001250/**
1251 * fu_common_string_append_kv:
1252 * @str: A #GString
1253 * @idt: The indent
1254 * @key: A string to append
1255 * @value: a string to append
1256 *
1257 * Appends a key and string value to a string
1258 *
1259 * Since: 1.2.4
1260 */
Richard Hughescea28de2019-08-09 11:16:40 +01001261void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001262fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001263{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001264 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001265 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001266
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001267 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001268
1269 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001270 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001271 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001272 for (gsize i = 0; i < idt; i++)
1273 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001274 if (key[0] != '\0') {
1275 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001276 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001277 } else {
1278 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001279 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001280 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001281 g_auto(GStrv) split = NULL;
1282 split = g_strsplit (value, "\n", -1);
1283 for (guint i = 0; split[i] != NULL; i++) {
1284 if (i == 0) {
1285 for (gsize j = keysz; j < align; j++)
1286 g_string_append (str, " ");
1287 } else {
1288 for (gsize j = 0; j < idt; j++)
1289 g_string_append (str, " ");
1290 }
1291 g_string_append (str, split[i]);
1292 g_string_append (str, "\n");
1293 }
1294 } else {
1295 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001296 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001297}
1298
Mario Limonciello1a680f32019-11-25 19:44:53 -06001299/**
1300 * fu_common_string_append_ku:
1301 * @str: A #GString
1302 * @idt: The indent
1303 * @key: A string to append
1304 * @value: guint64
1305 *
1306 * Appends a key and unsigned integer to a string
1307 *
1308 * Since: 1.2.4
1309 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001310void
1311fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1312{
1313 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1314 fu_common_string_append_kv (str, idt, key, tmp);
1315}
1316
Mario Limonciello1a680f32019-11-25 19:44:53 -06001317/**
1318 * fu_common_string_append_kx:
1319 * @str: A #GString
1320 * @idt: The indent
1321 * @key: A string to append
1322 * @value: guint64
1323 *
1324 * Appends a key and hex integer to a string
1325 *
1326 * Since: 1.2.4
1327 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001328void
1329fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1330{
1331 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1332 fu_common_string_append_kv (str, idt, key, tmp);
1333}
1334
Mario Limonciello1a680f32019-11-25 19:44:53 -06001335/**
1336 * fu_common_string_append_kb:
1337 * @str: A #GString
1338 * @idt: The indent
1339 * @key: A string to append
1340 * @value: Boolean
1341 *
1342 * Appends a key and boolean value to a string
1343 *
1344 * Since: 1.2.4
1345 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001346void
1347fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1348{
1349 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001350}
1351
Richard Hughese59cb9a2018-12-05 14:37:40 +00001352/**
Richard Hughes35481862019-01-06 12:01:58 +00001353 * fu_common_dump_full:
1354 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1355 * @title: prefix title, or %NULL
1356 * @data: buffer to print
1357 * @len: the size of @data
1358 * @columns: break new lines after this many bytes
1359 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1360 *
1361 * Dumps a raw buffer to the screen.
1362 *
1363 * Since: 1.2.4
1364 **/
1365void
1366fu_common_dump_full (const gchar *log_domain,
1367 const gchar *title,
1368 const guint8 *data,
1369 gsize len,
1370 guint columns,
1371 FuDumpFlags flags)
1372{
1373 g_autoptr(GString) str = g_string_new (NULL);
1374
1375 /* optional */
1376 if (title != NULL)
1377 g_string_append_printf (str, "%s:", title);
1378
1379 /* if more than can fit on one line then start afresh */
1380 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1381 g_string_append (str, "\n");
1382 } else {
1383 for (gsize i = str->len; i < 16; i++)
1384 g_string_append (str, " ");
1385 }
1386
1387 /* offset line */
1388 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1389 g_string_append (str, " │ ");
1390 for (gsize i = 0; i < columns; i++)
1391 g_string_append_printf (str, "%02x ", (guint) i);
1392 g_string_append (str, "\n───────┼");
1393 for (gsize i = 0; i < columns; i++)
1394 g_string_append (str, "───");
1395 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1396 }
1397
1398 /* print each row */
1399 for (gsize i = 0; i < len; i++) {
1400 g_string_append_printf (str, "%02x ", data[i]);
1401
1402 /* optionally print ASCII char */
1403 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1404 if (g_ascii_isprint (data[i]))
1405 g_string_append_printf (str, "[%c] ", data[i]);
1406 else
1407 g_string_append (str, "[?] ");
1408 }
1409
1410 /* new row required */
1411 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1412 g_string_append (str, "\n");
1413 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1414 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1415 }
1416 }
1417 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1418}
1419
1420/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001421 * fu_common_dump_raw:
1422 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1423 * @title: prefix title, or %NULL
1424 * @data: buffer to print
1425 * @len: the size of @data
1426 *
1427 * Dumps a raw buffer to the screen.
1428 *
1429 * Since: 1.2.2
1430 **/
1431void
1432fu_common_dump_raw (const gchar *log_domain,
1433 const gchar *title,
1434 const guint8 *data,
1435 gsize len)
1436{
Richard Hughes35481862019-01-06 12:01:58 +00001437 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1438 if (len > 64)
1439 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1440 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001441}
1442
1443/**
Mario Limonciello39602652019-04-29 21:08:58 -05001444 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001445 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1446 * @title: prefix title, or %NULL
1447 * @bytes: a #GBytes
1448 *
1449 * Dumps a byte buffer to the screen.
1450 *
1451 * Since: 1.2.2
1452 **/
1453void
1454fu_common_dump_bytes (const gchar *log_domain,
1455 const gchar *title,
1456 GBytes *bytes)
1457{
1458 gsize len = 0;
1459 const guint8 *data = g_bytes_get_data (bytes, &len);
1460 fu_common_dump_raw (log_domain, title, data, len);
1461}
Richard Hughesfc90f392019-01-15 21:21:16 +00001462
1463/**
1464 * fu_common_bytes_align:
1465 * @bytes: a #GBytes
1466 * @blksz: block size in bytes
1467 * @padval: the byte used to pad the byte buffer
1468 *
1469 * Aligns a block of memory to @blksize using the @padval value; if
1470 * the block is already aligned then the original @bytes is returned.
1471 *
1472 * Returns: (transfer full): a #GBytes, possibly @bytes
1473 *
1474 * Since: 1.2.4
1475 **/
1476GBytes *
1477fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1478{
1479 const guint8 *data;
1480 gsize sz;
1481
1482 g_return_val_if_fail (bytes != NULL, NULL);
1483 g_return_val_if_fail (blksz > 0, NULL);
1484
1485 /* pad */
1486 data = g_bytes_get_data (bytes, &sz);
1487 if (sz % blksz != 0) {
1488 gsize sz_align = ((sz / blksz) + 1) * blksz;
1489 guint8 *data_align = g_malloc (sz_align);
1490 memcpy (data_align, data, sz);
1491 memset (data_align + sz, padval, sz_align - sz);
1492 g_debug ("aligning 0x%x bytes to 0x%x",
1493 (guint) sz, (guint) sz_align);
1494 return g_bytes_new_take (data_align, sz_align);
1495 }
1496
1497 /* perfectly aligned */
1498 return g_bytes_ref (bytes);
1499}
Richard Hughes36999462019-03-19 20:23:29 +00001500
1501/**
1502 * fu_common_bytes_is_empty:
1503 * @bytes: a #GBytes
1504 *
1505 * Checks if a byte array are just empty (0xff) bytes.
1506 *
1507 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001508 *
1509 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001510 **/
1511gboolean
1512fu_common_bytes_is_empty (GBytes *bytes)
1513{
1514 gsize sz = 0;
1515 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1516 for (gsize i = 0; i < sz; i++) {
1517 if (buf[i] != 0xff)
1518 return FALSE;
1519 }
1520 return TRUE;
1521}
Richard Hughes2aad1042019-03-21 09:03:32 +00001522
1523/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001524 * fu_common_bytes_compare_raw:
1525 * @buf1: a buffer
1526 * @bufsz1: sizeof @buf1
1527 * @buf2: another buffer
1528 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001529 * @error: A #GError or %NULL
1530 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001531 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001532 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001533 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001534 *
1535 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001536 **/
1537gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001538fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1539 const guint8 *buf2, gsize bufsz2,
1540 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001541{
Richard Hughes38245ff2019-09-18 14:46:09 +01001542 g_return_val_if_fail (buf1 != NULL, FALSE);
1543 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001544 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1545
1546 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001547 if (bufsz1 != bufsz2) {
1548 g_set_error (error,
1549 G_IO_ERROR,
1550 G_IO_ERROR_INVALID_DATA,
1551 "got %" G_GSIZE_FORMAT " bytes, expected "
1552 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1553 return FALSE;
1554 }
1555
1556 /* check matches */
1557 for (guint i = 0x0; i < bufsz1; i++) {
1558 if (buf1[i] != buf2[i]) {
1559 g_set_error (error,
1560 G_IO_ERROR,
1561 G_IO_ERROR_INVALID_DATA,
1562 "got 0x%02x, expected 0x%02x @ 0x%04x",
1563 buf1[i], buf2[i], i);
1564 return FALSE;
1565 }
1566 }
1567
1568 /* success */
1569 return TRUE;
1570}
Richard Hughes484ee292019-03-22 16:10:50 +00001571
1572/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001573 * fu_common_bytes_compare:
1574 * @bytes1: a #GBytes
1575 * @bytes2: another #GBytes
1576 * @error: A #GError or %NULL
1577 *
1578 * Compares the buffers for equality.
1579 *
1580 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001581 *
1582 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001583 **/
1584gboolean
1585fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1586{
1587 const guint8 *buf1;
1588 const guint8 *buf2;
1589 gsize bufsz1;
1590 gsize bufsz2;
1591
1592 g_return_val_if_fail (bytes1 != NULL, FALSE);
1593 g_return_val_if_fail (bytes2 != NULL, FALSE);
1594 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1595
1596 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1597 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1598 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1599}
1600
1601/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001602 * fu_common_bytes_pad:
1603 * @bytes: a #GBytes
1604 * @sz: the desired size in bytes
1605 *
1606 * Pads a GBytes to a given @sz with `0xff`.
1607 *
1608 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001609 *
1610 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001611 **/
1612GBytes *
1613fu_common_bytes_pad (GBytes *bytes, gsize sz)
1614{
1615 gsize bytes_sz;
1616
1617 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1618
1619 /* pad */
1620 bytes_sz = g_bytes_get_size (bytes);
1621 if (bytes_sz < sz) {
1622 const guint8 *data = g_bytes_get_data (bytes, NULL);
1623 guint8 *data_new = g_malloc (sz);
1624 memcpy (data_new, data, bytes_sz);
1625 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1626 return g_bytes_new_take (data_new, sz);
1627 }
1628
1629 /* exactly right */
1630 return g_bytes_ref (bytes);
1631}
1632
1633/**
Richard Hughes484ee292019-03-22 16:10:50 +00001634 * fu_common_realpath:
1635 * @filename: a filename
1636 * @error: A #GError or %NULL
1637 *
1638 * Finds the canonicalized absolute filename for a path.
1639 *
1640 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001641 *
1642 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001643 **/
1644gchar *
1645fu_common_realpath (const gchar *filename, GError **error)
1646{
1647 char full_tmp[PATH_MAX];
1648
1649 g_return_val_if_fail (filename != NULL, NULL);
1650
Richard Hughes8694dee2019-11-22 09:16:34 +00001651#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001652 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001653#else
1654 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1655#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001656 g_set_error (error,
1657 G_IO_ERROR,
1658 G_IO_ERROR_INVALID_DATA,
1659 "cannot resolve path: %s",
1660 strerror (errno));
1661 return NULL;
1662 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001663 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1664 g_set_error (error,
1665 G_IO_ERROR,
1666 G_IO_ERROR_INVALID_DATA,
1667 "cannot find path: %s",
1668 full_tmp);
1669 return NULL;
1670 }
Richard Hughes484ee292019-03-22 16:10:50 +00001671 return g_strdup (full_tmp);
1672}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001673
1674/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001675 * fu_common_fnmatch:
1676 * @pattern: a glob pattern, e.g. `*foo*`
1677 * @str: a string to match against the pattern, e.g. `bazfoobar`
1678 *
1679 * Matches a string against a glob pattern.
1680 *
1681 * Return value: %TRUE if the string matched
1682 *
1683 * Since: 1.3.5
1684 **/
1685gboolean
1686fu_common_fnmatch (const gchar *pattern, const gchar *str)
1687{
1688 g_return_val_if_fail (pattern != NULL, FALSE);
1689 g_return_val_if_fail (str != NULL, FALSE);
1690#ifdef HAVE_FNMATCH_H
1691 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001692#elif _WIN32
1693 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1694 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1695 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001696#else
1697 return g_strcmp0 (pattern, str) == 0;
1698#endif
1699}
1700
Richard Hughesa84d7a72020-05-06 12:11:51 +01001701static gint
1702fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1703{
1704 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1705}
1706
1707/**
1708 * fu_common_filename_glob:
1709 * @directory: a directory path
1710 * @pattern: a glob pattern, e.g. `*foo*`
1711 * @error: A #GError or %NULL
1712 *
1713 * Returns all the filenames that match a specific glob pattern.
1714 * Any results are sorted. No matching files will set @error.
1715 *
1716 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1717 *
1718 * Since: 1.5.0
1719 **/
1720GPtrArray *
1721fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1722{
1723 const gchar *basename;
1724 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1725 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1726 if (dir == NULL)
1727 return NULL;
1728 while ((basename = g_dir_read_name (dir)) != NULL) {
1729 if (!fu_common_fnmatch (pattern, basename))
1730 continue;
1731 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1732 }
1733 if (files->len == 0) {
1734 g_set_error_literal (error,
1735 G_IO_ERROR,
1736 G_IO_ERROR_NOT_FOUND,
1737 "no files matched pattern");
1738 return NULL;
1739 }
1740 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1741 return g_steal_pointer (&files);
1742}
1743
Richard Hughes5c508de2019-11-22 09:57:34 +00001744/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001745 * fu_common_strnsplit:
1746 * @str: a string to split
1747 * @sz: size of @str
1748 * @delimiter: a string which specifies the places at which to split the string
1749 * @max_tokens: the maximum number of pieces to split @str into
1750 *
1751 * Splits a string into a maximum of @max_tokens pieces, using the given
1752 * delimiter. If @max_tokens is reached, the remainder of string is appended
1753 * to the last token.
1754 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001755 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001756 *
1757 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001758 **/
1759gchar **
1760fu_common_strnsplit (const gchar *str, gsize sz,
1761 const gchar *delimiter, gint max_tokens)
1762{
1763 if (str[sz - 1] != '\0') {
1764 g_autofree gchar *str2 = g_strndup (str, sz);
1765 return g_strsplit (str2, delimiter, max_tokens);
1766 }
1767 return g_strsplit (str, delimiter, max_tokens);
1768}
Richard Hughes5308ea42019-08-09 12:25:13 +01001769
1770/**
1771 * fu_memcpy_safe:
1772 * @dst: destination buffer
1773 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1774 * @dst_offset: offset in bytes into @dst to copy to
1775 * @src: source buffer
1776 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1777 * @src_offset: offset in bytes into @src to copy from
1778 * @n: number of bytes to copy from @src+@offset from
1779 * @error: A #GError or %NULL
1780 *
1781 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1782 * of both the destination and the source allows us to check for buffer overflow.
1783 *
1784 * Providing the buffer offsets also allows us to check reading past the end of
1785 * the source buffer. For this reason the caller should NEVER add an offset to
1786 * @src or @dst.
1787 *
1788 * You don't need to use this function in "obviously correct" cases, nor should
1789 * you use it when performance is a concern. Only us it when you're not sure if
1790 * malicious data from a device or firmware could cause memory corruption.
1791 *
1792 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001793 *
1794 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001795 **/
1796gboolean
1797fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1798 const guint8 *src, gsize src_sz, gsize src_offset,
1799 gsize n, GError **error)
1800{
1801 if (n == 0)
1802 return TRUE;
1803
1804 if (n > src_sz) {
1805 g_set_error (error,
1806 FWUPD_ERROR,
1807 FWUPD_ERROR_READ,
1808 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1809 (guint) n, (guint) src_sz);
1810 return FALSE;
1811 }
1812 if (n + src_offset > src_sz) {
1813 g_set_error (error,
1814 FWUPD_ERROR,
1815 FWUPD_ERROR_READ,
1816 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1817 (guint) n, (guint) src_offset, (guint) src_sz);
1818 return FALSE;
1819 }
1820 if (n > dst_sz) {
1821 g_set_error (error,
1822 FWUPD_ERROR,
1823 FWUPD_ERROR_WRITE,
1824 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1825 (guint) n, (guint) dst_sz);
1826 return FALSE;
1827 }
1828 if (n + dst_offset > dst_sz) {
1829 g_set_error (error,
1830 FWUPD_ERROR,
1831 FWUPD_ERROR_WRITE,
1832 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1833 (guint) n, (guint) dst_offset, (guint) dst_sz);
1834 return FALSE;
1835 }
1836
1837 /* phew! */
1838 memcpy (dst + dst_offset, src + src_offset, n);
1839 return TRUE;
1840}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001841
Richard Hughes80768f52019-10-22 07:19:14 +01001842/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001843 * fu_common_read_uint8_safe:
1844 * @buf: source buffer
1845 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1846 * @offset: offset in bytes into @buf to copy from
1847 * @value: (out) (allow-none): the parsed value
1848 * @error: A #GError or %NULL
1849 *
1850 * Read a value from a buffer in a safe way.
1851 *
1852 * You don't need to use this function in "obviously correct" cases, nor should
1853 * you use it when performance is a concern. Only us it when you're not sure if
1854 * malicious data from a device or firmware could cause memory corruption.
1855 *
1856 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001857 *
1858 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001859 **/
1860gboolean
1861fu_common_read_uint8_safe (const guint8 *buf,
1862 gsize bufsz,
1863 gsize offset,
1864 guint8 *value,
1865 GError **error)
1866{
1867 guint8 tmp;
1868 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1869 buf, bufsz, offset, /* src */
1870 sizeof(tmp), error))
1871 return FALSE;
1872 if (value != NULL)
1873 *value = tmp;
1874 return TRUE;
1875}
1876
1877/**
Richard Hughes80768f52019-10-22 07:19:14 +01001878 * fu_common_read_uint16_safe:
1879 * @buf: source buffer
1880 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1881 * @offset: offset in bytes into @buf to copy from
1882 * @value: (out) (allow-none): the parsed value
1883 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1884 * @error: A #GError or %NULL
1885 *
1886 * Read a value from a buffer using a specified endian in a safe way.
1887 *
1888 * You don't need to use this function in "obviously correct" cases, nor should
1889 * you use it when performance is a concern. Only us it when you're not sure if
1890 * malicious data from a device or firmware could cause memory corruption.
1891 *
1892 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001893 *
1894 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001895 **/
1896gboolean
1897fu_common_read_uint16_safe (const guint8 *buf,
1898 gsize bufsz,
1899 gsize offset,
1900 guint16 *value,
1901 FuEndianType endian,
1902 GError **error)
1903{
1904 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001905 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001906 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001907 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001908 return FALSE;
1909 if (value != NULL)
1910 *value = fu_common_read_uint16 (dst, endian);
1911 return TRUE;
1912}
1913
1914/**
1915 * fu_common_read_uint32_safe:
1916 * @buf: source buffer
1917 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1918 * @offset: offset in bytes into @buf to copy from
1919 * @value: (out) (allow-none): the parsed value
1920 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1921 * @error: A #GError or %NULL
1922 *
1923 * Read a value from a buffer using a specified endian in a safe way.
1924 *
1925 * You don't need to use this function in "obviously correct" cases, nor should
1926 * you use it when performance is a concern. Only us it when you're not sure if
1927 * malicious data from a device or firmware could cause memory corruption.
1928 *
1929 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001930 *
1931 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001932 **/
1933gboolean
1934fu_common_read_uint32_safe (const guint8 *buf,
1935 gsize bufsz,
1936 gsize offset,
1937 guint32 *value,
1938 FuEndianType endian,
1939 GError **error)
1940{
1941 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001942 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001943 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001944 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001945 return FALSE;
1946 if (value != NULL)
1947 *value = fu_common_read_uint32 (dst, endian);
1948 return TRUE;
1949}
1950
Mario Limonciello1a680f32019-11-25 19:44:53 -06001951/**
1952 * fu_byte_array_append_uint8:
1953 * @array: A #GByteArray
1954 * @data: #guint8
1955 *
1956 * Adds a 8 bit integer to a byte array
1957 *
1958 * Since: 1.3.1
1959 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001960void
1961fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1962{
1963 g_byte_array_append (array, &data, sizeof(data));
1964}
1965
Mario Limonciello1a680f32019-11-25 19:44:53 -06001966/**
1967 * fu_byte_array_append_uint16:
1968 * @array: A #GByteArray
1969 * @data: #guint16
1970 * @endian: #FuEndianType
1971 *
1972 * Adds a 16 bit integer to a byte array
1973 *
1974 * Since: 1.3.1
1975 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001976void
1977fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1978{
1979 guint8 buf[2];
1980 fu_common_write_uint16 (buf, data, endian);
1981 g_byte_array_append (array, buf, sizeof(buf));
1982}
1983
Mario Limonciello1a680f32019-11-25 19:44:53 -06001984/**
1985 * fu_byte_array_append_uint32:
1986 * @array: A #GByteArray
1987 * @data: #guint32
1988 * @endian: #FuEndianType
1989 *
1990 * Adds a 32 bit integer to a byte array
1991 *
1992 * Since: 1.3.1
1993 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001994void
1995fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1996{
1997 guint8 buf[4];
1998 fu_common_write_uint32 (buf, data, endian);
1999 g_byte_array_append (array, buf, sizeof(buf));
2000}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002001
2002/**
2003 * fu_common_kernel_locked_down:
2004 *
2005 * Determines if kernel lockdown in effect
2006 *
2007 * Since: 1.3.8
2008 **/
2009gboolean
2010fu_common_kernel_locked_down (void)
2011{
2012#ifndef _WIN32
2013 gsize len = 0;
2014 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2015 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2016 g_autofree gchar *data = NULL;
2017 g_auto(GStrv) options = NULL;
2018
2019 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2020 return FALSE;
2021 if (!g_file_get_contents (fname, &data, &len, NULL))
2022 return FALSE;
2023 if (len < 1)
2024 return FALSE;
2025 options = g_strsplit (data, " ", -1);
2026 for (guint i = 0; options[i] != NULL; i++) {
2027 if (g_strcmp0 (options[i], "[none]") == 0)
2028 return FALSE;
2029 }
2030 return TRUE;
2031#else
2032 return FALSE;
2033#endif
2034}
Richard Hughes9223c892020-05-09 20:32:08 +01002035
2036/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002037 * fu_common_cpuid:
2038 * @leaf: The CPUID level, now called the 'leaf' by Intel
2039 * @eax: (out) (nullable): EAX register
2040 * @ebx: (out) (nullable): EBX register
2041 * @ecx: (out) (nullable): ECX register
2042 * @edx: (out) (nullable): EDX register
2043 * @error: A #GError or NULL
2044 *
2045 * Calls CPUID and returns the registers for the given leaf.
2046 *
2047 * Return value: %TRUE if the registers are set.
2048 *
2049 * Since: 1.5.0
2050 **/
2051gboolean
2052fu_common_cpuid (guint32 leaf,
2053 guint32 *eax,
2054 guint32 *ebx,
2055 guint32 *ecx,
2056 guint32 *edx,
2057 GError **error)
2058{
2059#ifdef HAVE_CPUID_H
2060 guint eax_tmp = 0;
2061 guint ebx_tmp = 0;
2062 guint ecx_tmp = 0;
2063 guint edx_tmp = 0;
2064
2065 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002066 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002067 if (eax != NULL)
2068 *eax = eax_tmp;
2069 if (ebx != NULL)
2070 *ebx = ebx_tmp;
2071 if (ecx != NULL)
2072 *ecx = ecx_tmp;
2073 if (edx != NULL)
2074 *edx = edx_tmp;
2075 return TRUE;
2076#else
2077 g_set_error_literal (error,
2078 G_IO_ERROR,
2079 G_IO_ERROR_NOT_SUPPORTED,
2080 "no <cpuid.h> support");
2081 return FALSE;
2082#endif
2083}
2084
2085/**
Richard Hughes9223c892020-05-09 20:32:08 +01002086 * fu_common_is_cpu_intel:
2087 *
2088 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2089 *
2090 * Return value: %TRUE if the vendor was Intel.
2091 *
2092 * Since: 1.5.0
2093 **/
2094gboolean
2095fu_common_is_cpu_intel (void)
2096{
Richard Hughes9223c892020-05-09 20:32:08 +01002097 guint ebx = 0;
2098 guint ecx = 0;
2099 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002100
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002101 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2102 return FALSE;
2103#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002104 if (ebx == signature_INTEL_ebx &&
2105 edx == signature_INTEL_edx &&
2106 ecx == signature_INTEL_ecx) {
2107 return TRUE;
2108 }
Richard Hughesbd444322020-05-21 12:05:03 +01002109#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002110 return FALSE;
2111}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002112
Richard Hughes36111472020-08-12 15:04:24 +01002113/**
2114 * fu_common_is_live_media:
2115 *
2116 * Checks if the user is running from a live media using various heuristics.
2117 *
2118 * Returns: %TRUE if live
2119 *
2120 * Since: 1.4.6
2121 **/
2122gboolean
2123fu_common_is_live_media (void)
2124{
2125 gsize bufsz = 0;
2126 g_autofree gchar *buf = NULL;
2127 g_auto(GStrv) tokens = NULL;
2128 const gchar *args[] = {
2129 "rd.live.image",
2130 "boot=live",
2131 NULL, /* last entry */
2132 };
2133 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2134 return TRUE;
2135 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2136 return FALSE;
2137 if (bufsz == 0)
2138 return FALSE;
2139 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2140 for (guint i = 0; args[i] != NULL; i++) {
2141 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2142 return TRUE;
2143 }
2144 return FALSE;
2145}
2146
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002147static GPtrArray *
2148fu_common_get_block_devices (GDBusConnection *connection, GError **error)
2149{
2150 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002151 const gchar *obj;
2152 g_autoptr(GVariant) output = NULL;
2153 g_autoptr(GDBusProxy) proxy = NULL;
2154 g_autoptr(GPtrArray) devices = NULL;
2155 g_autoptr(GVariantIter) iter = NULL;
2156
2157 proxy = g_dbus_proxy_new_sync (connection,
2158 G_DBUS_PROXY_FLAGS_NONE, NULL,
2159 UDISKS_DBUS_SERVICE,
2160 UDISKS_DBUS_PATH,
2161 UDISKS_DBUS_MANAGER_INTERFACE,
2162 NULL, error);
2163 if (proxy == NULL) {
2164 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2165 return NULL;
2166 }
2167 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002168 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002169 "GetBlockDevices",
2170 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002171 G_DBUS_CALL_FLAGS_NONE,
2172 -1, NULL, error);
2173 if (output == NULL)
2174 return NULL;
2175 devices = g_ptr_array_new_with_free_func (g_free);
2176 g_variant_get (output, "(ao)", &iter);
Richard Hughesdb344d52020-09-09 19:42:27 +01002177 while (g_variant_iter_next (iter, "&o", &obj))
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002178 g_ptr_array_add (devices, g_strdup (obj));
2179
2180 return g_steal_pointer (&devices);
2181}
2182
2183/**
2184 * fu_common_get_volumes_by_kind:
2185 * @kind: A volume kind, typically a GUID
2186 * @error: A #GError or NULL
2187 *
2188 * Call into the plugin's get results routine
2189 *
2190 * Finds all volumes of a specific type
2191 *
2192 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2193 *
2194 * Since: 1.4.6
2195 **/
2196GPtrArray *
2197fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2198{
2199 g_autoptr(GDBusConnection) connection = NULL;
2200 g_autoptr(GPtrArray) devices = NULL;
2201 g_autoptr(GPtrArray) volumes = NULL;
2202
2203 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2204 if (connection == NULL) {
2205 g_prefix_error (error, "failed to get system bus: ");
2206 return NULL;
2207 }
2208 devices = fu_common_get_block_devices (connection, error);
2209 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002210 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002211 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2212 for (guint i = 0; i < devices->len; i++) {
2213 const gchar *obj = g_ptr_array_index (devices, i);
2214 const gchar *type_str;
2215 g_autoptr(GDBusProxy) proxy_part = NULL;
2216 g_autoptr(GDBusProxy) proxy_file = NULL;
2217 g_autoptr(GError) error_local = NULL;
2218 g_autoptr(GVariant) val = NULL;
2219
2220 proxy_part = g_dbus_proxy_new_sync (connection,
2221 G_DBUS_PROXY_FLAGS_NONE, NULL,
2222 UDISKS_DBUS_SERVICE,
2223 obj,
2224 UDISKS_DBUS_PART_INTERFACE,
2225 NULL, error);
2226 if (proxy_part == NULL) {
2227 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
Richard Hughesb81140d2020-08-17 14:47:17 +01002228 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002229 }
2230 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2231 if (val == NULL)
2232 continue;
2233
Richard Hughesdb344d52020-09-09 19:42:27 +01002234 g_variant_get (val, "&s", &type_str);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002235 g_debug ("device %s, type: %s", obj, type_str);
2236 if (g_strcmp0 (type_str, kind) != 0)
2237 continue;
2238 proxy_file = g_dbus_proxy_new_sync (connection,
2239 G_DBUS_PROXY_FLAGS_NONE, NULL,
2240 UDISKS_DBUS_SERVICE,
2241 obj,
2242 UDISKS_DBUS_FILE_INTERFACE,
2243 NULL, error);
2244 if (proxy_file == NULL) {
2245 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
Richard Hughesb81140d2020-08-17 14:47:17 +01002246 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002247 }
2248 g_ptr_array_add (volumes, fu_volume_new_from_proxy (proxy_file));
2249 }
2250 if (volumes->len == 0) {
2251 g_set_error (error,
2252 G_IO_ERROR,
2253 G_IO_ERROR_NOT_FOUND,
2254 "no volumes of type %s", kind);
2255 return NULL;
2256 }
2257 return g_steal_pointer (&volumes);
2258}
2259
2260/**
2261 * fu_common_get_esp_default:
2262 * @error: A #GError or NULL
2263 *
2264 * Gets the platform default ESP
2265 *
2266 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2267 *
2268 * Since: 1.4.6
2269 **/
2270FuVolume *
2271fu_common_get_esp_default (GError **error)
2272{
2273 const gchar *path_tmp;
2274 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2275 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
2276 g_autoptr(GPtrArray) volumes = NULL;
2277
2278 /* for the test suite use local directory for ESP */
2279 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2280 if (path_tmp != NULL)
2281 return fu_volume_new_from_mount_path (path_tmp);
2282
2283 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2284 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002285 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002286 for (guint i = 0; i < volumes->len; i++) {
2287 FuVolume *vol = g_ptr_array_index (volumes, i);
2288 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2289 }
2290 if (volumes_mtab->len == 1) {
2291 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2292 return g_object_ref (vol);
2293 }
2294 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2295 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2296 return g_object_ref (vol);
2297 }
2298 g_set_error (error,
2299 G_IO_ERROR,
2300 G_IO_ERROR_INVALID_FILENAME,
2301 "More than one available ESP");
2302 return NULL;
2303}
2304
2305/**
2306 * fu_common_get_esp_for_path:
2307 * @esp_path: A path to the ESP
2308 * @error: A #GError or NULL
2309 *
2310 * Gets the platform ESP using a UNIX or UDisks path
2311 *
2312 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2313 *
2314 * Since: 1.4.6
2315 **/
2316FuVolume *
2317fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2318{
2319 g_autofree gchar *basename = g_path_get_basename (esp_path);
2320 g_autoptr(GPtrArray) volumes = NULL;
2321
2322 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2323 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002324 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002325 for (guint i = 0; i < volumes->len; i++) {
2326 FuVolume *vol = g_ptr_array_index (volumes, i);
2327 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_id (vol));
2328 if (g_strcmp0 (basename, vol_basename) == 0)
2329 return g_object_ref (vol);
2330 }
2331 g_set_error (error,
2332 G_IO_ERROR,
2333 G_IO_ERROR_INVALID_FILENAME,
2334 "No ESP with path %s",
2335 esp_path);
2336 return NULL;
2337}