blob: bd66931e7bf9fab095c37c2bc150524baacd8570 [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 Hughesb9640a22020-05-05 20:42:47 +01001116 /* /usr/share/fwupd/dbx */
1117 case FU_PATH_KIND_EFIDBXDIR:
1118 tmp = g_getenv ("FWUPD_EFIDBXDIR");
1119 if (tmp != NULL)
1120 return g_strdup (tmp);
1121#ifdef FWUPD_EFI_DBXDIR
1122 tmp = g_getenv ("SNAP");
1123 if (tmp != NULL)
1124 return g_build_filename (tmp, FWUPD_EFI_DBXDIR, NULL);
1125 return g_strdup (FWUPD_EFI_DBXDIR);
1126#else
1127 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1128 return g_build_filename (basedir, "dbx", NULL);
1129#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001130 /* /etc/fwupd */
1131 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001132 tmp = g_getenv ("CONFIGURATION_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_SYSCONFDIR);
1136 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1137 /* /var/lib/fwupd */
1138 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001139 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001140 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001141 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001142 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1143 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1144 /* /var/cache/fwupd */
1145 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001146 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001147 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001148 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001149 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1150 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001151 case FU_PATH_KIND_OFFLINE_TRIGGER:
1152 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1153 if (tmp != NULL)
1154 return g_strdup (tmp);
1155 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001156 case FU_PATH_KIND_POLKIT_ACTIONS:
1157#ifdef POLKIT_ACTIONDIR
1158 return g_strdup (POLKIT_ACTIONDIR);
1159#else
1160 return NULL;
1161#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001162 /* this shouldn't happen */
1163 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001164 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001165 }
1166
1167 return NULL;
1168}
Richard Hughes83e56c12018-10-10 20:24:41 +01001169
1170/**
1171 * fu_common_string_replace:
1172 * @string: The #GString to operate on
1173 * @search: The text to search for
1174 * @replace: The text to use for substitutions
1175 *
1176 * Performs multiple search and replace operations on the given string.
1177 *
1178 * Returns: the number of replacements done, or 0 if @search is not found.
1179 *
1180 * Since: 1.2.0
1181 **/
1182guint
1183fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1184{
1185 gchar *tmp;
1186 guint count = 0;
1187 gsize search_idx = 0;
1188 gsize replace_len;
1189 gsize search_len;
1190
1191 g_return_val_if_fail (string != NULL, 0);
1192 g_return_val_if_fail (search != NULL, 0);
1193 g_return_val_if_fail (replace != NULL, 0);
1194
1195 /* nothing to do */
1196 if (string->len == 0)
1197 return 0;
1198
1199 search_len = strlen (search);
1200 replace_len = strlen (replace);
1201
1202 do {
1203 tmp = g_strstr_len (string->str + search_idx, -1, search);
1204 if (tmp == NULL)
1205 break;
1206
1207 /* advance the counter in case @replace contains @search */
1208 search_idx = (gsize) (tmp - string->str);
1209
1210 /* reallocate the string if required */
1211 if (search_len > replace_len) {
1212 g_string_erase (string,
1213 (gssize) search_idx,
1214 (gssize) (search_len - replace_len));
1215 memcpy (tmp, replace, replace_len);
1216 } else if (search_len < replace_len) {
1217 g_string_insert_len (string,
1218 (gssize) search_idx,
1219 replace,
1220 (gssize) (replace_len - search_len));
1221 /* we have to treat this specially as it could have
1222 * been reallocated when the insertion happened */
1223 memcpy (string->str + search_idx, replace, replace_len);
1224 } else {
1225 /* just memcmp in the new string */
1226 memcpy (tmp, replace, replace_len);
1227 }
1228 search_idx += replace_len;
1229 count++;
1230 } while (TRUE);
1231
1232 return count;
1233}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001234
Richard Hughesae96a1f2019-09-23 11:16:36 +01001235/**
1236 * fu_common_strwidth:
1237 * @text: The string to operate on
1238 *
1239 * Returns the width of the string in displayed characters on the console.
1240 *
1241 * Returns: width of text
1242 *
1243 * Since: 1.3.2
1244 **/
1245gsize
1246fu_common_strwidth (const gchar *text)
1247{
1248 const gchar *p = text;
1249 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001250
1251 g_return_val_if_fail (text != NULL, 0);
1252
Richard Hughesae96a1f2019-09-23 11:16:36 +01001253 while (*p) {
1254 gunichar c = g_utf8_get_char (p);
1255 if (g_unichar_iswide (c))
1256 width += 2;
1257 else if (!g_unichar_iszerowidth (c))
1258 width += 1;
1259 p = g_utf8_next_char (p);
1260 }
1261 return width;
1262}
1263
Mario Limonciello1a680f32019-11-25 19:44:53 -06001264/**
1265 * fu_common_string_append_kv:
1266 * @str: A #GString
1267 * @idt: The indent
1268 * @key: A string to append
1269 * @value: a string to append
1270 *
1271 * Appends a key and string value to a string
1272 *
1273 * Since: 1.2.4
1274 */
Richard Hughescea28de2019-08-09 11:16:40 +01001275void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001276fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001277{
Richard Hughes847cae82019-08-27 11:22:23 +01001278 const guint align = 25;
1279 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001280
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001281 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001282
1283 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001284 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001285 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001286 for (gsize i = 0; i < idt; i++)
1287 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001288 if (key[0] != '\0') {
1289 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001290 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001291 } else {
1292 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001293 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001294 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001295 g_auto(GStrv) split = NULL;
1296 split = g_strsplit (value, "\n", -1);
1297 for (guint i = 0; split[i] != NULL; i++) {
1298 if (i == 0) {
1299 for (gsize j = keysz; j < align; j++)
1300 g_string_append (str, " ");
1301 } else {
1302 for (gsize j = 0; j < idt; j++)
1303 g_string_append (str, " ");
1304 }
1305 g_string_append (str, split[i]);
1306 g_string_append (str, "\n");
1307 }
1308 } else {
1309 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001310 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001311}
1312
Mario Limonciello1a680f32019-11-25 19:44:53 -06001313/**
1314 * fu_common_string_append_ku:
1315 * @str: A #GString
1316 * @idt: The indent
1317 * @key: A string to append
1318 * @value: guint64
1319 *
1320 * Appends a key and unsigned integer to a string
1321 *
1322 * Since: 1.2.4
1323 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001324void
1325fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1326{
1327 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1328 fu_common_string_append_kv (str, idt, key, tmp);
1329}
1330
Mario Limonciello1a680f32019-11-25 19:44:53 -06001331/**
1332 * fu_common_string_append_kx:
1333 * @str: A #GString
1334 * @idt: The indent
1335 * @key: A string to append
1336 * @value: guint64
1337 *
1338 * Appends a key and hex integer to a string
1339 *
1340 * Since: 1.2.4
1341 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001342void
1343fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1344{
1345 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1346 fu_common_string_append_kv (str, idt, key, tmp);
1347}
1348
Mario Limonciello1a680f32019-11-25 19:44:53 -06001349/**
1350 * fu_common_string_append_kb:
1351 * @str: A #GString
1352 * @idt: The indent
1353 * @key: A string to append
1354 * @value: Boolean
1355 *
1356 * Appends a key and boolean value to a string
1357 *
1358 * Since: 1.2.4
1359 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001360void
1361fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1362{
1363 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001364}
1365
Richard Hughese59cb9a2018-12-05 14:37:40 +00001366/**
Richard Hughes35481862019-01-06 12:01:58 +00001367 * fu_common_dump_full:
1368 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1369 * @title: prefix title, or %NULL
1370 * @data: buffer to print
1371 * @len: the size of @data
1372 * @columns: break new lines after this many bytes
1373 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1374 *
1375 * Dumps a raw buffer to the screen.
1376 *
1377 * Since: 1.2.4
1378 **/
1379void
1380fu_common_dump_full (const gchar *log_domain,
1381 const gchar *title,
1382 const guint8 *data,
1383 gsize len,
1384 guint columns,
1385 FuDumpFlags flags)
1386{
1387 g_autoptr(GString) str = g_string_new (NULL);
1388
1389 /* optional */
1390 if (title != NULL)
1391 g_string_append_printf (str, "%s:", title);
1392
1393 /* if more than can fit on one line then start afresh */
1394 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1395 g_string_append (str, "\n");
1396 } else {
1397 for (gsize i = str->len; i < 16; i++)
1398 g_string_append (str, " ");
1399 }
1400
1401 /* offset line */
1402 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1403 g_string_append (str, " │ ");
1404 for (gsize i = 0; i < columns; i++)
1405 g_string_append_printf (str, "%02x ", (guint) i);
1406 g_string_append (str, "\n───────┼");
1407 for (gsize i = 0; i < columns; i++)
1408 g_string_append (str, "───");
1409 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1410 }
1411
1412 /* print each row */
1413 for (gsize i = 0; i < len; i++) {
1414 g_string_append_printf (str, "%02x ", data[i]);
1415
1416 /* optionally print ASCII char */
1417 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1418 if (g_ascii_isprint (data[i]))
1419 g_string_append_printf (str, "[%c] ", data[i]);
1420 else
1421 g_string_append (str, "[?] ");
1422 }
1423
1424 /* new row required */
1425 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1426 g_string_append (str, "\n");
1427 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1428 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1429 }
1430 }
1431 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1432}
1433
1434/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001435 * fu_common_dump_raw:
1436 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1437 * @title: prefix title, or %NULL
1438 * @data: buffer to print
1439 * @len: the size of @data
1440 *
1441 * Dumps a raw buffer to the screen.
1442 *
1443 * Since: 1.2.2
1444 **/
1445void
1446fu_common_dump_raw (const gchar *log_domain,
1447 const gchar *title,
1448 const guint8 *data,
1449 gsize len)
1450{
Richard Hughes35481862019-01-06 12:01:58 +00001451 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1452 if (len > 64)
1453 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1454 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001455}
1456
1457/**
Mario Limonciello39602652019-04-29 21:08:58 -05001458 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001459 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1460 * @title: prefix title, or %NULL
1461 * @bytes: a #GBytes
1462 *
1463 * Dumps a byte buffer to the screen.
1464 *
1465 * Since: 1.2.2
1466 **/
1467void
1468fu_common_dump_bytes (const gchar *log_domain,
1469 const gchar *title,
1470 GBytes *bytes)
1471{
1472 gsize len = 0;
1473 const guint8 *data = g_bytes_get_data (bytes, &len);
1474 fu_common_dump_raw (log_domain, title, data, len);
1475}
Richard Hughesfc90f392019-01-15 21:21:16 +00001476
1477/**
1478 * fu_common_bytes_align:
1479 * @bytes: a #GBytes
1480 * @blksz: block size in bytes
1481 * @padval: the byte used to pad the byte buffer
1482 *
1483 * Aligns a block of memory to @blksize using the @padval value; if
1484 * the block is already aligned then the original @bytes is returned.
1485 *
1486 * Returns: (transfer full): a #GBytes, possibly @bytes
1487 *
1488 * Since: 1.2.4
1489 **/
1490GBytes *
1491fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1492{
1493 const guint8 *data;
1494 gsize sz;
1495
1496 g_return_val_if_fail (bytes != NULL, NULL);
1497 g_return_val_if_fail (blksz > 0, NULL);
1498
1499 /* pad */
1500 data = g_bytes_get_data (bytes, &sz);
1501 if (sz % blksz != 0) {
1502 gsize sz_align = ((sz / blksz) + 1) * blksz;
1503 guint8 *data_align = g_malloc (sz_align);
1504 memcpy (data_align, data, sz);
1505 memset (data_align + sz, padval, sz_align - sz);
1506 g_debug ("aligning 0x%x bytes to 0x%x",
1507 (guint) sz, (guint) sz_align);
1508 return g_bytes_new_take (data_align, sz_align);
1509 }
1510
1511 /* perfectly aligned */
1512 return g_bytes_ref (bytes);
1513}
Richard Hughes36999462019-03-19 20:23:29 +00001514
1515/**
1516 * fu_common_bytes_is_empty:
1517 * @bytes: a #GBytes
1518 *
1519 * Checks if a byte array are just empty (0xff) bytes.
1520 *
1521 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001522 *
1523 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001524 **/
1525gboolean
1526fu_common_bytes_is_empty (GBytes *bytes)
1527{
1528 gsize sz = 0;
1529 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1530 for (gsize i = 0; i < sz; i++) {
1531 if (buf[i] != 0xff)
1532 return FALSE;
1533 }
1534 return TRUE;
1535}
Richard Hughes2aad1042019-03-21 09:03:32 +00001536
1537/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001538 * fu_common_bytes_compare_raw:
1539 * @buf1: a buffer
1540 * @bufsz1: sizeof @buf1
1541 * @buf2: another buffer
1542 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001543 * @error: A #GError or %NULL
1544 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001545 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001546 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001547 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001548 *
1549 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001550 **/
1551gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001552fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1553 const guint8 *buf2, gsize bufsz2,
1554 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001555{
Richard Hughes38245ff2019-09-18 14:46:09 +01001556 g_return_val_if_fail (buf1 != NULL, FALSE);
1557 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001558 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1559
1560 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001561 if (bufsz1 != bufsz2) {
1562 g_set_error (error,
1563 G_IO_ERROR,
1564 G_IO_ERROR_INVALID_DATA,
1565 "got %" G_GSIZE_FORMAT " bytes, expected "
1566 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1567 return FALSE;
1568 }
1569
1570 /* check matches */
1571 for (guint i = 0x0; i < bufsz1; i++) {
1572 if (buf1[i] != buf2[i]) {
1573 g_set_error (error,
1574 G_IO_ERROR,
1575 G_IO_ERROR_INVALID_DATA,
1576 "got 0x%02x, expected 0x%02x @ 0x%04x",
1577 buf1[i], buf2[i], i);
1578 return FALSE;
1579 }
1580 }
1581
1582 /* success */
1583 return TRUE;
1584}
Richard Hughes484ee292019-03-22 16:10:50 +00001585
1586/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001587 * fu_common_bytes_compare:
1588 * @bytes1: a #GBytes
1589 * @bytes2: another #GBytes
1590 * @error: A #GError or %NULL
1591 *
1592 * Compares the buffers for equality.
1593 *
1594 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001595 *
1596 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001597 **/
1598gboolean
1599fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1600{
1601 const guint8 *buf1;
1602 const guint8 *buf2;
1603 gsize bufsz1;
1604 gsize bufsz2;
1605
1606 g_return_val_if_fail (bytes1 != NULL, FALSE);
1607 g_return_val_if_fail (bytes2 != NULL, FALSE);
1608 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1609
1610 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1611 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1612 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1613}
1614
1615/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001616 * fu_common_bytes_pad:
1617 * @bytes: a #GBytes
1618 * @sz: the desired size in bytes
1619 *
1620 * Pads a GBytes to a given @sz with `0xff`.
1621 *
1622 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001623 *
1624 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001625 **/
1626GBytes *
1627fu_common_bytes_pad (GBytes *bytes, gsize sz)
1628{
1629 gsize bytes_sz;
1630
1631 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1632
1633 /* pad */
1634 bytes_sz = g_bytes_get_size (bytes);
1635 if (bytes_sz < sz) {
1636 const guint8 *data = g_bytes_get_data (bytes, NULL);
1637 guint8 *data_new = g_malloc (sz);
1638 memcpy (data_new, data, bytes_sz);
1639 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1640 return g_bytes_new_take (data_new, sz);
1641 }
1642
1643 /* exactly right */
1644 return g_bytes_ref (bytes);
1645}
1646
1647/**
Richard Hughes484ee292019-03-22 16:10:50 +00001648 * fu_common_realpath:
1649 * @filename: a filename
1650 * @error: A #GError or %NULL
1651 *
1652 * Finds the canonicalized absolute filename for a path.
1653 *
1654 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001655 *
1656 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001657 **/
1658gchar *
1659fu_common_realpath (const gchar *filename, GError **error)
1660{
1661 char full_tmp[PATH_MAX];
1662
1663 g_return_val_if_fail (filename != NULL, NULL);
1664
Richard Hughes8694dee2019-11-22 09:16:34 +00001665#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001666 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001667#else
1668 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1669#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001670 g_set_error (error,
1671 G_IO_ERROR,
1672 G_IO_ERROR_INVALID_DATA,
1673 "cannot resolve path: %s",
1674 strerror (errno));
1675 return NULL;
1676 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001677 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1678 g_set_error (error,
1679 G_IO_ERROR,
1680 G_IO_ERROR_INVALID_DATA,
1681 "cannot find path: %s",
1682 full_tmp);
1683 return NULL;
1684 }
Richard Hughes484ee292019-03-22 16:10:50 +00001685 return g_strdup (full_tmp);
1686}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001687
1688/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001689 * fu_common_fnmatch:
1690 * @pattern: a glob pattern, e.g. `*foo*`
1691 * @str: a string to match against the pattern, e.g. `bazfoobar`
1692 *
1693 * Matches a string against a glob pattern.
1694 *
1695 * Return value: %TRUE if the string matched
1696 *
1697 * Since: 1.3.5
1698 **/
1699gboolean
1700fu_common_fnmatch (const gchar *pattern, const gchar *str)
1701{
1702 g_return_val_if_fail (pattern != NULL, FALSE);
1703 g_return_val_if_fail (str != NULL, FALSE);
1704#ifdef HAVE_FNMATCH_H
1705 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001706#elif _WIN32
1707 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1708 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1709 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001710#else
1711 return g_strcmp0 (pattern, str) == 0;
1712#endif
1713}
1714
Richard Hughesa84d7a72020-05-06 12:11:51 +01001715static gint
1716fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1717{
1718 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1719}
1720
1721/**
1722 * fu_common_filename_glob:
1723 * @directory: a directory path
1724 * @pattern: a glob pattern, e.g. `*foo*`
1725 * @error: A #GError or %NULL
1726 *
1727 * Returns all the filenames that match a specific glob pattern.
1728 * Any results are sorted. No matching files will set @error.
1729 *
1730 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1731 *
1732 * Since: 1.5.0
1733 **/
1734GPtrArray *
1735fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1736{
1737 const gchar *basename;
1738 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1739 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1740 if (dir == NULL)
1741 return NULL;
1742 while ((basename = g_dir_read_name (dir)) != NULL) {
1743 if (!fu_common_fnmatch (pattern, basename))
1744 continue;
1745 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1746 }
1747 if (files->len == 0) {
1748 g_set_error_literal (error,
1749 G_IO_ERROR,
1750 G_IO_ERROR_NOT_FOUND,
1751 "no files matched pattern");
1752 return NULL;
1753 }
1754 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1755 return g_steal_pointer (&files);
1756}
1757
Richard Hughes5c508de2019-11-22 09:57:34 +00001758/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001759 * fu_common_strnsplit:
1760 * @str: a string to split
1761 * @sz: size of @str
1762 * @delimiter: a string which specifies the places at which to split the string
1763 * @max_tokens: the maximum number of pieces to split @str into
1764 *
1765 * Splits a string into a maximum of @max_tokens pieces, using the given
1766 * delimiter. If @max_tokens is reached, the remainder of string is appended
1767 * to the last token.
1768 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001769 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001770 *
1771 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001772 **/
1773gchar **
1774fu_common_strnsplit (const gchar *str, gsize sz,
1775 const gchar *delimiter, gint max_tokens)
1776{
1777 if (str[sz - 1] != '\0') {
1778 g_autofree gchar *str2 = g_strndup (str, sz);
1779 return g_strsplit (str2, delimiter, max_tokens);
1780 }
1781 return g_strsplit (str, delimiter, max_tokens);
1782}
Richard Hughes5308ea42019-08-09 12:25:13 +01001783
1784/**
1785 * fu_memcpy_safe:
1786 * @dst: destination buffer
1787 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1788 * @dst_offset: offset in bytes into @dst to copy to
1789 * @src: source buffer
1790 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1791 * @src_offset: offset in bytes into @src to copy from
1792 * @n: number of bytes to copy from @src+@offset from
1793 * @error: A #GError or %NULL
1794 *
1795 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1796 * of both the destination and the source allows us to check for buffer overflow.
1797 *
1798 * Providing the buffer offsets also allows us to check reading past the end of
1799 * the source buffer. For this reason the caller should NEVER add an offset to
1800 * @src or @dst.
1801 *
1802 * You don't need to use this function in "obviously correct" cases, nor should
1803 * you use it when performance is a concern. Only us it when you're not sure if
1804 * malicious data from a device or firmware could cause memory corruption.
1805 *
1806 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001807 *
1808 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001809 **/
1810gboolean
1811fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1812 const guint8 *src, gsize src_sz, gsize src_offset,
1813 gsize n, GError **error)
1814{
1815 if (n == 0)
1816 return TRUE;
1817
1818 if (n > src_sz) {
1819 g_set_error (error,
1820 FWUPD_ERROR,
1821 FWUPD_ERROR_READ,
1822 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1823 (guint) n, (guint) src_sz);
1824 return FALSE;
1825 }
1826 if (n + src_offset > src_sz) {
1827 g_set_error (error,
1828 FWUPD_ERROR,
1829 FWUPD_ERROR_READ,
1830 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1831 (guint) n, (guint) src_offset, (guint) src_sz);
1832 return FALSE;
1833 }
1834 if (n > dst_sz) {
1835 g_set_error (error,
1836 FWUPD_ERROR,
1837 FWUPD_ERROR_WRITE,
1838 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1839 (guint) n, (guint) dst_sz);
1840 return FALSE;
1841 }
1842 if (n + dst_offset > dst_sz) {
1843 g_set_error (error,
1844 FWUPD_ERROR,
1845 FWUPD_ERROR_WRITE,
1846 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1847 (guint) n, (guint) dst_offset, (guint) dst_sz);
1848 return FALSE;
1849 }
1850
1851 /* phew! */
1852 memcpy (dst + dst_offset, src + src_offset, n);
1853 return TRUE;
1854}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001855
Richard Hughes80768f52019-10-22 07:19:14 +01001856/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001857 * fu_common_read_uint8_safe:
1858 * @buf: source buffer
1859 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1860 * @offset: offset in bytes into @buf to copy from
1861 * @value: (out) (allow-none): the parsed value
1862 * @error: A #GError or %NULL
1863 *
1864 * Read a value from a buffer in a safe way.
1865 *
1866 * You don't need to use this function in "obviously correct" cases, nor should
1867 * you use it when performance is a concern. Only us it when you're not sure if
1868 * malicious data from a device or firmware could cause memory corruption.
1869 *
1870 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001871 *
1872 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001873 **/
1874gboolean
1875fu_common_read_uint8_safe (const guint8 *buf,
1876 gsize bufsz,
1877 gsize offset,
1878 guint8 *value,
1879 GError **error)
1880{
1881 guint8 tmp;
1882 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1883 buf, bufsz, offset, /* src */
1884 sizeof(tmp), error))
1885 return FALSE;
1886 if (value != NULL)
1887 *value = tmp;
1888 return TRUE;
1889}
1890
1891/**
Richard Hughes80768f52019-10-22 07:19:14 +01001892 * fu_common_read_uint16_safe:
1893 * @buf: source buffer
1894 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1895 * @offset: offset in bytes into @buf to copy from
1896 * @value: (out) (allow-none): the parsed value
1897 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1898 * @error: A #GError or %NULL
1899 *
1900 * Read a value from a buffer using a specified endian in a safe way.
1901 *
1902 * You don't need to use this function in "obviously correct" cases, nor should
1903 * you use it when performance is a concern. Only us it when you're not sure if
1904 * malicious data from a device or firmware could cause memory corruption.
1905 *
1906 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001907 *
1908 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001909 **/
1910gboolean
1911fu_common_read_uint16_safe (const guint8 *buf,
1912 gsize bufsz,
1913 gsize offset,
1914 guint16 *value,
1915 FuEndianType endian,
1916 GError **error)
1917{
1918 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001919 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001920 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001921 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001922 return FALSE;
1923 if (value != NULL)
1924 *value = fu_common_read_uint16 (dst, endian);
1925 return TRUE;
1926}
1927
1928/**
1929 * fu_common_read_uint32_safe:
1930 * @buf: source buffer
1931 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1932 * @offset: offset in bytes into @buf to copy from
1933 * @value: (out) (allow-none): the parsed value
1934 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1935 * @error: A #GError or %NULL
1936 *
1937 * Read a value from a buffer using a specified endian in a safe way.
1938 *
1939 * You don't need to use this function in "obviously correct" cases, nor should
1940 * you use it when performance is a concern. Only us it when you're not sure if
1941 * malicious data from a device or firmware could cause memory corruption.
1942 *
1943 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001944 *
1945 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001946 **/
1947gboolean
1948fu_common_read_uint32_safe (const guint8 *buf,
1949 gsize bufsz,
1950 gsize offset,
1951 guint32 *value,
1952 FuEndianType endian,
1953 GError **error)
1954{
1955 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001956 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001957 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001958 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001959 return FALSE;
1960 if (value != NULL)
1961 *value = fu_common_read_uint32 (dst, endian);
1962 return TRUE;
1963}
1964
Mario Limonciello1a680f32019-11-25 19:44:53 -06001965/**
1966 * fu_byte_array_append_uint8:
1967 * @array: A #GByteArray
1968 * @data: #guint8
1969 *
1970 * Adds a 8 bit integer to a byte array
1971 *
1972 * Since: 1.3.1
1973 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001974void
1975fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1976{
1977 g_byte_array_append (array, &data, sizeof(data));
1978}
1979
Mario Limonciello1a680f32019-11-25 19:44:53 -06001980/**
1981 * fu_byte_array_append_uint16:
1982 * @array: A #GByteArray
1983 * @data: #guint16
1984 * @endian: #FuEndianType
1985 *
1986 * Adds a 16 bit integer to a byte array
1987 *
1988 * Since: 1.3.1
1989 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001990void
1991fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1992{
1993 guint8 buf[2];
1994 fu_common_write_uint16 (buf, data, endian);
1995 g_byte_array_append (array, buf, sizeof(buf));
1996}
1997
Mario Limonciello1a680f32019-11-25 19:44:53 -06001998/**
1999 * fu_byte_array_append_uint32:
2000 * @array: A #GByteArray
2001 * @data: #guint32
2002 * @endian: #FuEndianType
2003 *
2004 * Adds a 32 bit integer to a byte array
2005 *
2006 * Since: 1.3.1
2007 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002008void
2009fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2010{
2011 guint8 buf[4];
2012 fu_common_write_uint32 (buf, data, endian);
2013 g_byte_array_append (array, buf, sizeof(buf));
2014}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002015
2016/**
2017 * fu_common_kernel_locked_down:
2018 *
2019 * Determines if kernel lockdown in effect
2020 *
2021 * Since: 1.3.8
2022 **/
2023gboolean
2024fu_common_kernel_locked_down (void)
2025{
2026#ifndef _WIN32
2027 gsize len = 0;
2028 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2029 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2030 g_autofree gchar *data = NULL;
2031 g_auto(GStrv) options = NULL;
2032
2033 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2034 return FALSE;
2035 if (!g_file_get_contents (fname, &data, &len, NULL))
2036 return FALSE;
2037 if (len < 1)
2038 return FALSE;
2039 options = g_strsplit (data, " ", -1);
2040 for (guint i = 0; options[i] != NULL; i++) {
2041 if (g_strcmp0 (options[i], "[none]") == 0)
2042 return FALSE;
2043 }
2044 return TRUE;
2045#else
2046 return FALSE;
2047#endif
2048}
Richard Hughes9223c892020-05-09 20:32:08 +01002049
2050/**
2051 * fu_common_is_cpu_intel:
2052 *
2053 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2054 *
2055 * Return value: %TRUE if the vendor was Intel.
2056 *
2057 * Since: 1.5.0
2058 **/
2059gboolean
2060fu_common_is_cpu_intel (void)
2061{
Richard Hughesbd444322020-05-21 12:05:03 +01002062#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002063 guint eax = 0;
2064 guint ebx = 0;
2065 guint ecx = 0;
2066 guint edx = 0;
2067 guint level = 0;
2068
2069 /* get vendor */
2070 __get_cpuid(level, &eax, &ebx, &ecx, &edx);
2071 if (ebx == signature_INTEL_ebx &&
2072 edx == signature_INTEL_edx &&
2073 ecx == signature_INTEL_ecx) {
2074 return TRUE;
2075 }
Richard Hughesbd444322020-05-21 12:05:03 +01002076#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002077 return FALSE;
2078}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002079
Richard Hughes36111472020-08-12 15:04:24 +01002080/**
2081 * fu_common_is_live_media:
2082 *
2083 * Checks if the user is running from a live media using various heuristics.
2084 *
2085 * Returns: %TRUE if live
2086 *
2087 * Since: 1.4.6
2088 **/
2089gboolean
2090fu_common_is_live_media (void)
2091{
2092 gsize bufsz = 0;
2093 g_autofree gchar *buf = NULL;
2094 g_auto(GStrv) tokens = NULL;
2095 const gchar *args[] = {
2096 "rd.live.image",
2097 "boot=live",
2098 NULL, /* last entry */
2099 };
2100 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2101 return TRUE;
2102 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2103 return FALSE;
2104 if (bufsz == 0)
2105 return FALSE;
2106 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2107 for (guint i = 0; args[i] != NULL; i++) {
2108 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2109 return TRUE;
2110 }
2111 return FALSE;
2112}
2113
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002114static GPtrArray *
2115fu_common_get_block_devices (GDBusConnection *connection, GError **error)
2116{
2117 GVariantBuilder builder;
2118 GVariant *input;
2119 const gchar *obj;
2120 g_autoptr(GVariant) output = NULL;
2121 g_autoptr(GDBusProxy) proxy = NULL;
2122 g_autoptr(GPtrArray) devices = NULL;
2123 g_autoptr(GVariantIter) iter = NULL;
2124
2125 proxy = g_dbus_proxy_new_sync (connection,
2126 G_DBUS_PROXY_FLAGS_NONE, NULL,
2127 UDISKS_DBUS_SERVICE,
2128 UDISKS_DBUS_PATH,
2129 UDISKS_DBUS_MANAGER_INTERFACE,
2130 NULL, error);
2131 if (proxy == NULL) {
2132 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2133 return NULL;
2134 }
2135 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
2136 input = g_variant_new ("(a{sv})", &builder);
2137 output = g_dbus_proxy_call_sync (proxy,
2138 "GetBlockDevices", g_variant_ref (input),
2139 G_DBUS_CALL_FLAGS_NONE,
2140 -1, NULL, error);
2141 if (output == NULL)
2142 return NULL;
2143 devices = g_ptr_array_new_with_free_func (g_free);
2144 g_variant_get (output, "(ao)", &iter);
2145 while (g_variant_iter_next (iter, "o", &obj))
2146 g_ptr_array_add (devices, g_strdup (obj));
2147
2148 return g_steal_pointer (&devices);
2149}
2150
2151/**
2152 * fu_common_get_volumes_by_kind:
2153 * @kind: A volume kind, typically a GUID
2154 * @error: A #GError or NULL
2155 *
2156 * Call into the plugin's get results routine
2157 *
2158 * Finds all volumes of a specific type
2159 *
2160 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2161 *
2162 * Since: 1.4.6
2163 **/
2164GPtrArray *
2165fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2166{
2167 g_autoptr(GDBusConnection) connection = NULL;
2168 g_autoptr(GPtrArray) devices = NULL;
2169 g_autoptr(GPtrArray) volumes = NULL;
2170
2171 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2172 if (connection == NULL) {
2173 g_prefix_error (error, "failed to get system bus: ");
2174 return NULL;
2175 }
2176 devices = fu_common_get_block_devices (connection, error);
2177 if (devices == NULL)
2178 return FALSE;
2179 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2180 for (guint i = 0; i < devices->len; i++) {
2181 const gchar *obj = g_ptr_array_index (devices, i);
2182 const gchar *type_str;
2183 g_autoptr(GDBusProxy) proxy_part = NULL;
2184 g_autoptr(GDBusProxy) proxy_file = NULL;
2185 g_autoptr(GError) error_local = NULL;
2186 g_autoptr(GVariant) val = NULL;
2187
2188 proxy_part = g_dbus_proxy_new_sync (connection,
2189 G_DBUS_PROXY_FLAGS_NONE, NULL,
2190 UDISKS_DBUS_SERVICE,
2191 obj,
2192 UDISKS_DBUS_PART_INTERFACE,
2193 NULL, error);
2194 if (proxy_part == NULL) {
2195 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
2196 return FALSE;
2197 }
2198 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2199 if (val == NULL)
2200 continue;
2201
2202 g_variant_get (val, "s", &type_str);
2203 g_debug ("device %s, type: %s", obj, type_str);
2204 if (g_strcmp0 (type_str, kind) != 0)
2205 continue;
2206 proxy_file = g_dbus_proxy_new_sync (connection,
2207 G_DBUS_PROXY_FLAGS_NONE, NULL,
2208 UDISKS_DBUS_SERVICE,
2209 obj,
2210 UDISKS_DBUS_FILE_INTERFACE,
2211 NULL, error);
2212 if (proxy_file == NULL) {
2213 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
2214 return FALSE;
2215 }
2216 g_ptr_array_add (volumes, fu_volume_new_from_proxy (proxy_file));
2217 }
2218 if (volumes->len == 0) {
2219 g_set_error (error,
2220 G_IO_ERROR,
2221 G_IO_ERROR_NOT_FOUND,
2222 "no volumes of type %s", kind);
2223 return NULL;
2224 }
2225 return g_steal_pointer (&volumes);
2226}
2227
2228/**
2229 * fu_common_get_esp_default:
2230 * @error: A #GError or NULL
2231 *
2232 * Gets the platform default ESP
2233 *
2234 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2235 *
2236 * Since: 1.4.6
2237 **/
2238FuVolume *
2239fu_common_get_esp_default (GError **error)
2240{
2241 const gchar *path_tmp;
2242 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2243 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
2244 g_autoptr(GPtrArray) volumes = NULL;
2245
2246 /* for the test suite use local directory for ESP */
2247 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2248 if (path_tmp != NULL)
2249 return fu_volume_new_from_mount_path (path_tmp);
2250
2251 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2252 if (volumes == NULL)
2253 return FALSE;
2254 for (guint i = 0; i < volumes->len; i++) {
2255 FuVolume *vol = g_ptr_array_index (volumes, i);
2256 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2257 }
2258 if (volumes_mtab->len == 1) {
2259 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2260 return g_object_ref (vol);
2261 }
2262 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2263 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2264 return g_object_ref (vol);
2265 }
2266 g_set_error (error,
2267 G_IO_ERROR,
2268 G_IO_ERROR_INVALID_FILENAME,
2269 "More than one available ESP");
2270 return NULL;
2271}
2272
2273/**
2274 * fu_common_get_esp_for_path:
2275 * @esp_path: A path to the ESP
2276 * @error: A #GError or NULL
2277 *
2278 * Gets the platform ESP using a UNIX or UDisks path
2279 *
2280 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2281 *
2282 * Since: 1.4.6
2283 **/
2284FuVolume *
2285fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2286{
2287 g_autofree gchar *basename = g_path_get_basename (esp_path);
2288 g_autoptr(GPtrArray) volumes = NULL;
2289
2290 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2291 if (volumes == NULL)
2292 return FALSE;
2293 for (guint i = 0; i < volumes->len; i++) {
2294 FuVolume *vol = g_ptr_array_index (volumes, i);
2295 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_id (vol));
2296 if (g_strcmp0 (basename, vol_basename) == 0)
2297 return g_object_ref (vol);
2298 }
2299 g_set_error (error,
2300 G_IO_ERROR,
2301 G_IO_ERROR_INVALID_FILENAME,
2302 "No ESP with path %s",
2303 esp_path);
2304 return NULL;
2305}