blob: 12e177d41ea3c3eaf3dc52c71ee4dd31b62db35c [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);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500166 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
167 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100168 if (g_mkdir_with_parents (parent, 0755) == -1) {
169 g_set_error (error,
170 FWUPD_ERROR,
171 FWUPD_ERROR_INTERNAL,
172 "Failed to create '%s': %s",
173 parent, g_strerror (errno));
174 return FALSE;
175 }
176 return TRUE;
177}
178
179/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100180 * fu_common_set_contents_bytes:
181 * @filename: A filename
182 * @bytes: The data to write
183 * @error: A #GError, or %NULL
184 *
185 * Writes a blob of data to a filename, creating the parent directories as
186 * required.
187 *
188 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600189 *
190 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100191 **/
192gboolean
193fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
194{
195 const gchar *data;
196 gsize size;
197 g_autoptr(GFile) file = NULL;
198 g_autoptr(GFile) file_parent = NULL;
199
200 file = g_file_new_for_path (filename);
201 file_parent = g_file_get_parent (file);
202 if (!g_file_query_exists (file_parent, NULL)) {
203 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
204 return FALSE;
205 }
206 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100207 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100208 return g_file_set_contents (filename, data, size, error);
209}
210
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100211/**
212 * fu_common_get_contents_bytes:
213 * @filename: A filename
214 * @error: A #GError, or %NULL
215 *
216 * Reads a blob of data from a file.
217 *
218 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600219 *
220 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100221 **/
222GBytes *
223fu_common_get_contents_bytes (const gchar *filename, GError **error)
224{
225 gchar *data = NULL;
226 gsize len = 0;
227 if (!g_file_get_contents (filename, &data, &len, error))
228 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100229 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100230 return g_bytes_new_take (data, len);
231}
Richard Hughes943d2c92017-06-21 09:04:39 +0100232
233/**
234 * fu_common_get_contents_fd:
235 * @fd: A file descriptor
236 * @count: The maximum number of bytes to read
237 * @error: A #GError, or %NULL
238 *
239 * Reads a blob from a specific file descriptor.
240 *
241 * Note: this will close the fd when done
242 *
Richard Hughes4eada342017-10-03 21:20:32 +0100243 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600244 *
245 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100246 **/
247GBytes *
248fu_common_get_contents_fd (gint fd, gsize count, GError **error)
249{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000250#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100251 g_autoptr(GBytes) blob = NULL;
252 g_autoptr(GError) error_local = NULL;
253 g_autoptr(GInputStream) stream = NULL;
254
255 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100256 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
257
Richard Hughes919f8ab2018-02-14 10:24:56 +0000258 /* this is invalid */
259 if (count == 0) {
260 g_set_error_literal (error,
261 FWUPD_ERROR,
262 FWUPD_ERROR_NOT_SUPPORTED,
263 "A maximum read size must be specified");
264 return NULL;
265 }
266
Richard Hughes943d2c92017-06-21 09:04:39 +0100267 /* read the entire fd to a data blob */
268 stream = g_unix_input_stream_new (fd, TRUE);
269 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
270 if (blob == NULL) {
271 g_set_error_literal (error,
272 FWUPD_ERROR,
273 FWUPD_ERROR_INVALID_FILE,
274 error_local->message);
275 return NULL;
276 }
277 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000278#else
279 g_set_error_literal (error,
280 FWUPD_ERROR,
281 FWUPD_ERROR_NOT_SUPPORTED,
282 "Not supported as <glib-unix.h> is unavailable");
283 return NULL;
284#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100285}
Richard Hughes94f939a2017-08-08 12:21:39 +0100286
287static gboolean
288fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
289{
290 const gchar *tmp;
291 g_autofree gchar *buf = NULL;
292
293 /* no output file */
294 if (archive_entry_pathname (entry) == NULL)
295 return FALSE;
296
297 /* update output path */
298 tmp = archive_entry_pathname (entry);
299 buf = g_build_filename (dir, tmp, NULL);
300 archive_entry_update_pathname_utf8 (entry, buf);
301 return TRUE;
302}
303
304/**
305 * fu_common_extract_archive:
306 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100307 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100308 * @error: A #GError, or %NULL
309 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000310 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100311 *
312 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600313 *
314 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100315 **/
316gboolean
317fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
318{
319 gboolean ret = TRUE;
320 int r;
321 struct archive *arch = NULL;
322 struct archive_entry *entry;
323
324 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100325 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100326 arch = archive_read_new ();
327 archive_read_support_format_all (arch);
328 archive_read_support_filter_all (arch);
329 r = archive_read_open_memory (arch,
330 (void *) g_bytes_get_data (blob, NULL),
331 (size_t) g_bytes_get_size (blob));
332 if (r != 0) {
333 ret = FALSE;
334 g_set_error (error,
335 FWUPD_ERROR,
336 FWUPD_ERROR_INTERNAL,
337 "Cannot open: %s",
338 archive_error_string (arch));
339 goto out;
340 }
341 for (;;) {
342 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100343 r = archive_read_next_header (arch, &entry);
344 if (r == ARCHIVE_EOF)
345 break;
346 if (r != ARCHIVE_OK) {
347 ret = FALSE;
348 g_set_error (error,
349 FWUPD_ERROR,
350 FWUPD_ERROR_INTERNAL,
351 "Cannot read header: %s",
352 archive_error_string (arch));
353 goto out;
354 }
355
356 /* only extract if valid */
357 valid = fu_common_extract_archive_entry (entry, dir);
358 if (!valid)
359 continue;
360 r = archive_read_extract (arch, entry, 0);
361 if (r != ARCHIVE_OK) {
362 ret = FALSE;
363 g_set_error (error,
364 FWUPD_ERROR,
365 FWUPD_ERROR_INTERNAL,
366 "Cannot extract: %s",
367 archive_error_string (arch));
368 goto out;
369 }
370 }
371out:
372 if (arch != NULL) {
373 archive_read_close (arch);
374 archive_read_free (arch);
375 }
376 return ret;
377}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100378
379static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300380fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
381
382static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100383fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
384{
385 va_list args;
386 g_autofree gchar *tmp = NULL;
387 g_auto(GStrv) split = NULL;
388
389 va_start (args, fmt);
390 tmp = g_strdup_vprintf (fmt, args);
391 va_end (args);
392
393 split = g_strsplit (tmp, " ", -1);
394 for (guint i = 0; split[i] != NULL; i++)
395 g_ptr_array_add (argv, g_strdup (split[i]));
396}
397
Mario Limonciello1a680f32019-11-25 19:44:53 -0600398/**
399 * fu_common_find_program_in_path:
400 * @basename: The program to search
401 * @error: A #GError, or %NULL
402 *
403 * Looks for a program in the PATH variable
404 *
405 * Returns: a new #gchar, or %NULL for error
406 *
407 * Since: 1.1.2
408 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100409gchar *
410fu_common_find_program_in_path (const gchar *basename, GError **error)
411{
412 gchar *fn = g_find_program_in_path (basename);
413 if (fn == NULL) {
414 g_set_error (error,
415 FWUPD_ERROR,
416 FWUPD_ERROR_NOT_SUPPORTED,
417 "missing executable %s in PATH",
418 basename);
419 return NULL;
420 }
421 return fn;
422}
423
424static gboolean
425fu_common_test_namespace_support (GError **error)
426{
427 /* test if CONFIG_USER_NS is valid */
428 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
429 g_set_error (error,
430 FWUPD_ERROR,
431 FWUPD_ERROR_NOT_SUPPORTED,
432 "missing CONFIG_USER_NS in kernel");
433 return FALSE;
434 }
435 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
436 g_autofree gchar *clone = NULL;
437 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
438 return FALSE;
439 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
440 g_set_error (error,
441 FWUPD_ERROR,
442 FWUPD_ERROR_NOT_SUPPORTED,
443 "unprivileged user namespace clones disabled by distro");
444 return FALSE;
445 }
446 }
447 return TRUE;
448}
449
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100450/**
451 * fu_common_firmware_builder:
452 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100453 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
454 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100455 * @error: A #GError, or %NULL
456 *
457 * Builds a firmware file using tools from the host session in a bubblewrap
458 * jail. Several things happen during build:
459 *
460 * 1. The @bytes data is untarred to a temporary location
461 * 2. A bubblewrap container is set up
462 * 3. The startup.sh script is run inside the container
463 * 4. The firmware.bin is extracted from the container
464 * 5. The temporary location is deleted
465 *
466 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600467 *
468 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100469 **/
470GBytes *
471fu_common_firmware_builder (GBytes *bytes,
472 const gchar *script_fn,
473 const gchar *output_fn,
474 GError **error)
475{
476 gint rc = 0;
477 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500478 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100479 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100480 g_autofree gchar *localstatedir = NULL;
481 g_autofree gchar *output2_fn = NULL;
482 g_autofree gchar *standard_error = NULL;
483 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100484 g_autofree gchar *tmpdir = NULL;
485 g_autoptr(GBytes) firmware_blob = NULL;
486 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
487
488 g_return_val_if_fail (bytes != NULL, NULL);
489 g_return_val_if_fail (script_fn != NULL, NULL);
490 g_return_val_if_fail (output_fn != NULL, NULL);
491 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
492
Mario Limonciello37b59582018-08-13 08:38:01 -0500493 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100494 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
495 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100496 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500497
498 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100499 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100500 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500501
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100502 /* untar file to temp location */
503 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
504 if (tmpdir == NULL)
505 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100506 if (!fu_common_extract_archive (bytes, tmpdir, error))
507 return NULL;
508
509 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100510 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
511 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100512
513 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500514 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100515 fu_common_add_argv (argv, "--die-with-parent");
516 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500517 fu_common_add_argv (argv, "--ro-bind /lib /lib");
518 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
519 fu_common_add_argv (argv, "--ro-bind /bin /bin");
520 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100521 fu_common_add_argv (argv, "--dir /tmp");
522 fu_common_add_argv (argv, "--dir /var");
523 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100524 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
525 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100526 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100527 fu_common_add_argv (argv, "--chdir /tmp");
528 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100529 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100530 g_ptr_array_add (argv, NULL);
531 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
532 g_debug ("running '%s' in %s", argv_str, tmpdir);
533 if (!g_spawn_sync ("/tmp",
534 (gchar **) argv->pdata,
535 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100536 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100537 NULL, NULL, /* child_setup */
538 &standard_output,
539 &standard_error,
540 &rc,
541 error)) {
542 g_prefix_error (error, "failed to run '%s': ", argv_str);
543 return NULL;
544 }
545 if (standard_output != NULL && standard_output[0] != '\0')
546 g_debug ("console output was: %s", standard_output);
547 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500548 FwupdError code = FWUPD_ERROR_INTERNAL;
549 if (errno == ENOTTY)
550 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100551 g_set_error (error,
552 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500553 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100554 "failed to build firmware: %s",
555 standard_error);
556 return NULL;
557 }
558
559 /* get generated file */
560 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
561 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
562 if (firmware_blob == NULL)
563 return NULL;
564
565 /* cleanup temp directory */
566 if (!fu_common_rmtree (tmpdir, error))
567 return NULL;
568
569 /* success */
570 return g_steal_pointer (&firmware_blob);
571}
Richard Hughes049ccc82017-08-09 15:26:56 +0100572
573typedef struct {
574 FuOutputHandler handler_cb;
575 gpointer handler_user_data;
576 GMainLoop *loop;
577 GSource *source;
578 GInputStream *stream;
579 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000580 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100581} FuCommonSpawnHelper;
582
583static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
584
585static gboolean
586fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
587{
588 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
589 gchar buffer[1024];
590 gssize sz;
591 g_auto(GStrv) split = NULL;
592 g_autoptr(GError) error = NULL;
593
594 /* read from stream */
595 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
596 buffer,
597 sizeof(buffer) - 1,
598 NULL,
599 &error);
600 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100601 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
602 g_warning ("failed to get read from nonblocking fd: %s",
603 error->message);
604 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100605 return G_SOURCE_REMOVE;
606 }
607
608 /* no read possible */
609 if (sz == 0)
610 g_main_loop_quit (helper->loop);
611
612 /* emit lines */
613 if (helper->handler_cb != NULL) {
614 buffer[sz] = '\0';
615 split = g_strsplit (buffer, "\n", -1);
616 for (guint i = 0; split[i] != NULL; i++) {
617 if (split[i][0] == '\0')
618 continue;
619 helper->handler_cb (split[i], helper->handler_user_data);
620 }
621 }
622
623 /* set up the source for the next read */
624 fu_common_spawn_create_pollable_source (helper);
625 return G_SOURCE_REMOVE;
626}
627
628static void
629fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
630{
631 if (helper->source != NULL)
632 g_source_destroy (helper->source);
633 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
634 helper->cancellable);
635 g_source_attach (helper->source, NULL);
636 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
637}
638
639static void
640fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
641{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000642 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100643 if (helper->stream != NULL)
644 g_object_unref (helper->stream);
645 if (helper->source != NULL)
646 g_source_destroy (helper->source);
647 if (helper->loop != NULL)
648 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000649 if (helper->timeout_id != 0)
650 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100651 g_free (helper);
652}
653
Mario Limoncielloa98df552018-04-16 12:15:51 -0500654#pragma clang diagnostic push
655#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100656G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500657#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100658
Richard Hughesb768e4d2019-02-26 13:55:18 +0000659static gboolean
660fu_common_spawn_timeout_cb (gpointer user_data)
661{
662 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
663 g_cancellable_cancel (helper->cancellable);
664 g_main_loop_quit (helper->loop);
665 helper->timeout_id = 0;
666 return G_SOURCE_REMOVE;
667}
668
669static void
670fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
671{
672 /* just propagate */
673 g_cancellable_cancel (helper->cancellable);
674}
675
Richard Hughes049ccc82017-08-09 15:26:56 +0100676/**
677 * fu_common_spawn_sync:
678 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100679 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
680 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000681 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100682 * @cancellable: a #GCancellable, or %NULL
683 * @error: A #GError or %NULL
684 *
685 * Runs a subprocess and waits for it to exit. Any output on standard out or
686 * standard error will be forwarded to @handler_cb as whole lines.
687 *
688 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600689 *
690 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100691 **/
692gboolean
693fu_common_spawn_sync (const gchar * const * argv,
694 FuOutputHandler handler_cb,
695 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000696 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100697 GCancellable *cancellable, GError **error)
698{
699 g_autoptr(FuCommonSpawnHelper) helper = NULL;
700 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100701 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000702 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100703
704 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100705 argv_str = g_strjoinv (" ", (gchar **) argv);
706 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100707 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
708 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
709 if (subprocess == NULL)
710 return FALSE;
711
712 /* watch for process to exit */
713 helper = g_new0 (FuCommonSpawnHelper, 1);
714 helper->handler_cb = handler_cb;
715 helper->handler_user_data = handler_user_data;
716 helper->loop = g_main_loop_new (NULL, FALSE);
717 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000718
719 /* always create a cancellable, and connect up the parent */
720 helper->cancellable = g_cancellable_new ();
721 if (cancellable != NULL) {
722 cancellable_id = g_cancellable_connect (cancellable,
723 G_CALLBACK (fu_common_spawn_cancelled_cb),
724 helper, NULL);
725 }
726
727 /* allow timeout */
728 if (timeout_ms > 0) {
729 helper->timeout_id = g_timeout_add (timeout_ms,
730 fu_common_spawn_timeout_cb,
731 helper);
732 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100733 fu_common_spawn_create_pollable_source (helper);
734 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000735 g_cancellable_disconnect (cancellable, cancellable_id);
736 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
737 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100738 return g_subprocess_wait_check (subprocess, cancellable, error);
739}
Richard Hughesae252cd2017-12-08 10:48:15 +0000740
741/**
742 * fu_common_write_uint16:
743 * @buf: A writable buffer
744 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100745 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000746 *
747 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600748 *
749 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000750 **/
751void
752fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
753{
754 guint16 val_hw;
755 switch (endian) {
756 case G_BIG_ENDIAN:
757 val_hw = GUINT16_TO_BE(val_native);
758 break;
759 case G_LITTLE_ENDIAN:
760 val_hw = GUINT16_TO_LE(val_native);
761 break;
762 default:
763 g_assert_not_reached ();
764 }
765 memcpy (buf, &val_hw, sizeof(val_hw));
766}
767
768/**
769 * fu_common_write_uint32:
770 * @buf: A writable buffer
771 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100772 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000773 *
774 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600775 *
776 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000777 **/
778void
779fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
780{
781 guint32 val_hw;
782 switch (endian) {
783 case G_BIG_ENDIAN:
784 val_hw = GUINT32_TO_BE(val_native);
785 break;
786 case G_LITTLE_ENDIAN:
787 val_hw = GUINT32_TO_LE(val_native);
788 break;
789 default:
790 g_assert_not_reached ();
791 }
792 memcpy (buf, &val_hw, sizeof(val_hw));
793}
794
795/**
796 * fu_common_read_uint16:
797 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100798 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000799 *
800 * Read a value from a buffer using a specified endian.
801 *
802 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600803 *
804 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000805 **/
806guint16
807fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
808{
809 guint16 val_hw, val_native;
810 memcpy (&val_hw, buf, sizeof(val_hw));
811 switch (endian) {
812 case G_BIG_ENDIAN:
813 val_native = GUINT16_FROM_BE(val_hw);
814 break;
815 case G_LITTLE_ENDIAN:
816 val_native = GUINT16_FROM_LE(val_hw);
817 break;
818 default:
819 g_assert_not_reached ();
820 }
821 return val_native;
822}
823
824/**
825 * fu_common_read_uint32:
826 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100827 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000828 *
829 * Read a value from a buffer using a specified endian.
830 *
831 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600832 *
833 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000834 **/
835guint32
836fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
837{
838 guint32 val_hw, val_native;
839 memcpy (&val_hw, buf, sizeof(val_hw));
840 switch (endian) {
841 case G_BIG_ENDIAN:
842 val_native = GUINT32_FROM_BE(val_hw);
843 break;
844 case G_LITTLE_ENDIAN:
845 val_native = GUINT32_FROM_LE(val_hw);
846 break;
847 default:
848 g_assert_not_reached ();
849 }
850 return val_native;
851}
Richard Hughese82eef32018-05-20 10:41:26 +0100852
Richard Hughes73bf2332018-08-28 09:38:09 +0100853/**
854 * fu_common_strtoull:
855 * @str: A string, e.g. "0x1234"
856 *
857 * Converts a string value to an integer. Values are assumed base 10, unless
858 * prefixed with "0x" where they are parsed as base 16.
859 *
860 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600861 *
862 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100863 **/
864guint64
865fu_common_strtoull (const gchar *str)
866{
867 guint base = 10;
868 if (str == NULL)
869 return 0x0;
870 if (g_str_has_prefix (str, "0x")) {
871 str += 2;
872 base = 16;
873 }
874 return g_ascii_strtoull (str, NULL, base);
875}
876
Richard Hughesa574a752018-08-31 13:31:03 +0100877/**
878 * fu_common_strstrip:
879 * @str: A string, e.g. " test "
880 *
881 * Removes leading and trailing whitespace from a constant string.
882 *
883 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600884 *
885 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100886 **/
887gchar *
888fu_common_strstrip (const gchar *str)
889{
890 guint head = G_MAXUINT;
891 guint tail = 0;
892
893 g_return_val_if_fail (str != NULL, NULL);
894
895 /* find first non-space char */
896 for (guint i = 0; str[i] != '\0'; i++) {
897 if (str[i] != ' ') {
898 head = i;
899 break;
900 }
901 }
902 if (head == G_MAXUINT)
903 return g_strdup ("");
904
905 /* find last non-space char */
906 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500907 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100908 tail = i;
909 }
910 return g_strndup (str + head, tail - head + 1);
911}
912
Richard Hughese82eef32018-05-20 10:41:26 +0100913static const GError *
914fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
915{
916 for (guint j = 0; j < errors->len; j++) {
917 const GError *error = g_ptr_array_index (errors, j);
918 if (g_error_matches (error, FWUPD_ERROR, error_code))
919 return error;
920 }
921 return NULL;
922}
923
924static guint
925fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
926{
927 guint cnt = 0;
928 for (guint j = 0; j < errors->len; j++) {
929 const GError *error = g_ptr_array_index (errors, j);
930 if (g_error_matches (error, FWUPD_ERROR, error_code))
931 cnt++;
932 }
933 return cnt;
934}
935
936static gboolean
937fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
938{
939 for (guint j = 0; j < errors->len; j++) {
940 const GError *error = g_ptr_array_index (errors, j);
941 gboolean matches_any = FALSE;
942 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
943 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
944 matches_any = TRUE;
945 break;
946 }
947 }
948 if (!matches_any)
949 return FALSE;
950 }
951 return TRUE;
952}
953
954/**
955 * fu_common_error_array_get_best:
956 * @errors: (element-type GError): array of errors
957 *
958 * Finds the 'best' error to show the user from a array of errors, creating a
959 * completely bespoke error where required.
960 *
961 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600962 *
963 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100964 **/
965GError *
966fu_common_error_array_get_best (GPtrArray *errors)
967{
968 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
969 FWUPD_ERROR_VERSION_SAME,
970 FWUPD_ERROR_VERSION_NEWER,
971 FWUPD_ERROR_NOT_SUPPORTED,
972 FWUPD_ERROR_INTERNAL,
973 FWUPD_ERROR_NOT_FOUND,
974 FWUPD_ERROR_LAST };
975 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
976 FWUPD_ERROR_NOT_FOUND,
977 FWUPD_ERROR_NOT_SUPPORTED,
978 FWUPD_ERROR_LAST };
979 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
980 FWUPD_ERROR_VERSION_SAME,
981 FWUPD_ERROR_NOT_FOUND,
982 FWUPD_ERROR_NOT_SUPPORTED,
983 FWUPD_ERROR_LAST };
984
985 /* are all the errors either GUID-not-matched or version-same? */
986 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
987 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
988 return g_error_new (FWUPD_ERROR,
989 FWUPD_ERROR_NOTHING_TO_DO,
990 "All updatable firmware is already installed");
991 }
992
993 /* are all the errors either GUID-not-matched or version same or newer? */
994 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
995 fu_common_error_array_matches_any (errors, err_all_newer)) {
996 return g_error_new (FWUPD_ERROR,
997 FWUPD_ERROR_NOTHING_TO_DO,
998 "All updatable devices already have newer versions");
999 }
1000
1001 /* get the most important single error */
1002 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1003 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1004 if (error_tmp != NULL)
1005 return g_error_copy (error_tmp);
1006 }
1007
1008 /* fall back to something */
1009 return g_error_new (FWUPD_ERROR,
1010 FWUPD_ERROR_NOT_FOUND,
1011 "No supported devices found");
1012}
Richard Hughes4be17d12018-05-30 20:36:29 +01001013
1014/**
1015 * fu_common_get_path:
1016 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1017 *
1018 * Gets a fwupd-specific system path. These can be overridden with various
1019 * environment variables, for instance %FWUPD_DATADIR.
1020 *
1021 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001022 *
1023 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001024 **/
1025gchar *
1026fu_common_get_path (FuPathKind path_kind)
1027{
1028 const gchar *tmp;
1029 g_autofree gchar *basedir = NULL;
1030
1031 switch (path_kind) {
1032 /* /var */
1033 case FU_PATH_KIND_LOCALSTATEDIR:
1034 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1035 if (tmp != NULL)
1036 return g_strdup (tmp);
1037 tmp = g_getenv ("SNAP_USER_DATA");
1038 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001039 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1040 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001041 /* /proc */
1042 case FU_PATH_KIND_PROCFS:
1043 tmp = g_getenv ("FWUPD_PROCFS");
1044 if (tmp != NULL)
1045 return g_strdup (tmp);
1046 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001047 /* /sys/firmware */
1048 case FU_PATH_KIND_SYSFSDIR_FW:
1049 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1050 if (tmp != NULL)
1051 return g_strdup (tmp);
1052 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001053 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001054 case FU_PATH_KIND_SYSFSDIR_TPM:
1055 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1056 if (tmp != NULL)
1057 return g_strdup (tmp);
1058 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001059 /* /sys/bus/platform/drivers */
1060 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1061 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1062 if (tmp != NULL)
1063 return g_strdup (tmp);
1064 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001065 /* /sys/kernel/security */
1066 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1067 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1068 if (tmp != NULL)
1069 return g_strdup (tmp);
1070 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001071 /* /sys/firmware/acpi/tables */
1072 case FU_PATH_KIND_ACPI_TABLES:
1073 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1074 if (tmp != NULL)
1075 return g_strdup (tmp);
1076 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001077 /* /etc */
1078 case FU_PATH_KIND_SYSCONFDIR:
1079 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1080 if (tmp != NULL)
1081 return g_strdup (tmp);
1082 tmp = g_getenv ("SNAP_USER_DATA");
1083 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001084 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1085 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001086 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1087 case FU_PATH_KIND_PLUGINDIR_PKG:
1088 tmp = g_getenv ("FWUPD_PLUGINDIR");
1089 if (tmp != NULL)
1090 return g_strdup (tmp);
1091 tmp = g_getenv ("SNAP");
1092 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001093 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1094 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001095 /* /usr/share/fwupd */
1096 case FU_PATH_KIND_DATADIR_PKG:
1097 tmp = g_getenv ("FWUPD_DATADIR");
1098 if (tmp != NULL)
1099 return g_strdup (tmp);
1100 tmp = g_getenv ("SNAP");
1101 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001102 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1103 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001104 /* /usr/libexec/fwupd/efi */
1105 case FU_PATH_KIND_EFIAPPDIR:
1106 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1107 if (tmp != NULL)
1108 return g_strdup (tmp);
1109#ifdef EFI_APP_LOCATION
1110 tmp = g_getenv ("SNAP");
1111 if (tmp != NULL)
1112 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1113 return g_strdup (EFI_APP_LOCATION);
1114#else
1115 return NULL;
1116#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001117 /* /etc/fwupd */
1118 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001119 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001120 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001121 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001122 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1123 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1124 /* /var/lib/fwupd */
1125 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001126 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001127 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001128 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001129 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1130 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1131 /* /var/cache/fwupd */
1132 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001133 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001134 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001135 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001136 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1137 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001138 case FU_PATH_KIND_OFFLINE_TRIGGER:
1139 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1140 if (tmp != NULL)
1141 return g_strdup (tmp);
1142 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001143 case FU_PATH_KIND_POLKIT_ACTIONS:
1144#ifdef POLKIT_ACTIONDIR
1145 return g_strdup (POLKIT_ACTIONDIR);
1146#else
1147 return NULL;
1148#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001149 /* this shouldn't happen */
1150 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001151 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001152 }
1153
1154 return NULL;
1155}
Richard Hughes83e56c12018-10-10 20:24:41 +01001156
1157/**
1158 * fu_common_string_replace:
1159 * @string: The #GString to operate on
1160 * @search: The text to search for
1161 * @replace: The text to use for substitutions
1162 *
1163 * Performs multiple search and replace operations on the given string.
1164 *
1165 * Returns: the number of replacements done, or 0 if @search is not found.
1166 *
1167 * Since: 1.2.0
1168 **/
1169guint
1170fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1171{
1172 gchar *tmp;
1173 guint count = 0;
1174 gsize search_idx = 0;
1175 gsize replace_len;
1176 gsize search_len;
1177
1178 g_return_val_if_fail (string != NULL, 0);
1179 g_return_val_if_fail (search != NULL, 0);
1180 g_return_val_if_fail (replace != NULL, 0);
1181
1182 /* nothing to do */
1183 if (string->len == 0)
1184 return 0;
1185
1186 search_len = strlen (search);
1187 replace_len = strlen (replace);
1188
1189 do {
1190 tmp = g_strstr_len (string->str + search_idx, -1, search);
1191 if (tmp == NULL)
1192 break;
1193
1194 /* advance the counter in case @replace contains @search */
1195 search_idx = (gsize) (tmp - string->str);
1196
1197 /* reallocate the string if required */
1198 if (search_len > replace_len) {
1199 g_string_erase (string,
1200 (gssize) search_idx,
1201 (gssize) (search_len - replace_len));
1202 memcpy (tmp, replace, replace_len);
1203 } else if (search_len < replace_len) {
1204 g_string_insert_len (string,
1205 (gssize) search_idx,
1206 replace,
1207 (gssize) (replace_len - search_len));
1208 /* we have to treat this specially as it could have
1209 * been reallocated when the insertion happened */
1210 memcpy (string->str + search_idx, replace, replace_len);
1211 } else {
1212 /* just memcmp in the new string */
1213 memcpy (tmp, replace, replace_len);
1214 }
1215 search_idx += replace_len;
1216 count++;
1217 } while (TRUE);
1218
1219 return count;
1220}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001221
Richard Hughesae96a1f2019-09-23 11:16:36 +01001222/**
1223 * fu_common_strwidth:
1224 * @text: The string to operate on
1225 *
1226 * Returns the width of the string in displayed characters on the console.
1227 *
1228 * Returns: width of text
1229 *
1230 * Since: 1.3.2
1231 **/
1232gsize
1233fu_common_strwidth (const gchar *text)
1234{
1235 const gchar *p = text;
1236 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001237
1238 g_return_val_if_fail (text != NULL, 0);
1239
Richard Hughesae96a1f2019-09-23 11:16:36 +01001240 while (*p) {
1241 gunichar c = g_utf8_get_char (p);
1242 if (g_unichar_iswide (c))
1243 width += 2;
1244 else if (!g_unichar_iszerowidth (c))
1245 width += 1;
1246 p = g_utf8_next_char (p);
1247 }
1248 return width;
1249}
1250
Mario Limonciello1a680f32019-11-25 19:44:53 -06001251/**
1252 * fu_common_string_append_kv:
1253 * @str: A #GString
1254 * @idt: The indent
1255 * @key: A string to append
1256 * @value: a string to append
1257 *
1258 * Appends a key and string value to a string
1259 *
1260 * Since: 1.2.4
1261 */
Richard Hughescea28de2019-08-09 11:16:40 +01001262void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001263fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001264{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001265 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001266 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001267
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001268 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001269
1270 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001271 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001272 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001273 for (gsize i = 0; i < idt; i++)
1274 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001275 if (key[0] != '\0') {
1276 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001277 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001278 } else {
1279 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001280 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001281 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001282 g_auto(GStrv) split = NULL;
1283 split = g_strsplit (value, "\n", -1);
1284 for (guint i = 0; split[i] != NULL; i++) {
1285 if (i == 0) {
1286 for (gsize j = keysz; j < align; j++)
1287 g_string_append (str, " ");
1288 } else {
1289 for (gsize j = 0; j < idt; j++)
1290 g_string_append (str, " ");
1291 }
1292 g_string_append (str, split[i]);
1293 g_string_append (str, "\n");
1294 }
1295 } else {
1296 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001297 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001298}
1299
Mario Limonciello1a680f32019-11-25 19:44:53 -06001300/**
1301 * fu_common_string_append_ku:
1302 * @str: A #GString
1303 * @idt: The indent
1304 * @key: A string to append
1305 * @value: guint64
1306 *
1307 * Appends a key and unsigned integer to a string
1308 *
1309 * Since: 1.2.4
1310 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001311void
1312fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1313{
1314 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1315 fu_common_string_append_kv (str, idt, key, tmp);
1316}
1317
Mario Limonciello1a680f32019-11-25 19:44:53 -06001318/**
1319 * fu_common_string_append_kx:
1320 * @str: A #GString
1321 * @idt: The indent
1322 * @key: A string to append
1323 * @value: guint64
1324 *
1325 * Appends a key and hex integer to a string
1326 *
1327 * Since: 1.2.4
1328 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001329void
1330fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1331{
1332 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1333 fu_common_string_append_kv (str, idt, key, tmp);
1334}
1335
Mario Limonciello1a680f32019-11-25 19:44:53 -06001336/**
1337 * fu_common_string_append_kb:
1338 * @str: A #GString
1339 * @idt: The indent
1340 * @key: A string to append
1341 * @value: Boolean
1342 *
1343 * Appends a key and boolean value to a string
1344 *
1345 * Since: 1.2.4
1346 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001347void
1348fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1349{
1350 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001351}
1352
Richard Hughese59cb9a2018-12-05 14:37:40 +00001353/**
Richard Hughes35481862019-01-06 12:01:58 +00001354 * fu_common_dump_full:
1355 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1356 * @title: prefix title, or %NULL
1357 * @data: buffer to print
1358 * @len: the size of @data
1359 * @columns: break new lines after this many bytes
1360 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1361 *
1362 * Dumps a raw buffer to the screen.
1363 *
1364 * Since: 1.2.4
1365 **/
1366void
1367fu_common_dump_full (const gchar *log_domain,
1368 const gchar *title,
1369 const guint8 *data,
1370 gsize len,
1371 guint columns,
1372 FuDumpFlags flags)
1373{
1374 g_autoptr(GString) str = g_string_new (NULL);
1375
1376 /* optional */
1377 if (title != NULL)
1378 g_string_append_printf (str, "%s:", title);
1379
1380 /* if more than can fit on one line then start afresh */
1381 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1382 g_string_append (str, "\n");
1383 } else {
1384 for (gsize i = str->len; i < 16; i++)
1385 g_string_append (str, " ");
1386 }
1387
1388 /* offset line */
1389 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1390 g_string_append (str, " │ ");
1391 for (gsize i = 0; i < columns; i++)
1392 g_string_append_printf (str, "%02x ", (guint) i);
1393 g_string_append (str, "\n───────┼");
1394 for (gsize i = 0; i < columns; i++)
1395 g_string_append (str, "───");
1396 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1397 }
1398
1399 /* print each row */
1400 for (gsize i = 0; i < len; i++) {
1401 g_string_append_printf (str, "%02x ", data[i]);
1402
1403 /* optionally print ASCII char */
1404 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1405 if (g_ascii_isprint (data[i]))
1406 g_string_append_printf (str, "[%c] ", data[i]);
1407 else
1408 g_string_append (str, "[?] ");
1409 }
1410
1411 /* new row required */
1412 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1413 g_string_append (str, "\n");
1414 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1415 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1416 }
1417 }
1418 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1419}
1420
1421/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001422 * fu_common_dump_raw:
1423 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1424 * @title: prefix title, or %NULL
1425 * @data: buffer to print
1426 * @len: the size of @data
1427 *
1428 * Dumps a raw buffer to the screen.
1429 *
1430 * Since: 1.2.2
1431 **/
1432void
1433fu_common_dump_raw (const gchar *log_domain,
1434 const gchar *title,
1435 const guint8 *data,
1436 gsize len)
1437{
Richard Hughes35481862019-01-06 12:01:58 +00001438 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1439 if (len > 64)
1440 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1441 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001442}
1443
1444/**
Mario Limonciello39602652019-04-29 21:08:58 -05001445 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001446 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1447 * @title: prefix title, or %NULL
1448 * @bytes: a #GBytes
1449 *
1450 * Dumps a byte buffer to the screen.
1451 *
1452 * Since: 1.2.2
1453 **/
1454void
1455fu_common_dump_bytes (const gchar *log_domain,
1456 const gchar *title,
1457 GBytes *bytes)
1458{
1459 gsize len = 0;
1460 const guint8 *data = g_bytes_get_data (bytes, &len);
1461 fu_common_dump_raw (log_domain, title, data, len);
1462}
Richard Hughesfc90f392019-01-15 21:21:16 +00001463
1464/**
1465 * fu_common_bytes_align:
1466 * @bytes: a #GBytes
1467 * @blksz: block size in bytes
1468 * @padval: the byte used to pad the byte buffer
1469 *
1470 * Aligns a block of memory to @blksize using the @padval value; if
1471 * the block is already aligned then the original @bytes is returned.
1472 *
1473 * Returns: (transfer full): a #GBytes, possibly @bytes
1474 *
1475 * Since: 1.2.4
1476 **/
1477GBytes *
1478fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1479{
1480 const guint8 *data;
1481 gsize sz;
1482
1483 g_return_val_if_fail (bytes != NULL, NULL);
1484 g_return_val_if_fail (blksz > 0, NULL);
1485
1486 /* pad */
1487 data = g_bytes_get_data (bytes, &sz);
1488 if (sz % blksz != 0) {
1489 gsize sz_align = ((sz / blksz) + 1) * blksz;
1490 guint8 *data_align = g_malloc (sz_align);
1491 memcpy (data_align, data, sz);
1492 memset (data_align + sz, padval, sz_align - sz);
1493 g_debug ("aligning 0x%x bytes to 0x%x",
1494 (guint) sz, (guint) sz_align);
1495 return g_bytes_new_take (data_align, sz_align);
1496 }
1497
1498 /* perfectly aligned */
1499 return g_bytes_ref (bytes);
1500}
Richard Hughes36999462019-03-19 20:23:29 +00001501
1502/**
1503 * fu_common_bytes_is_empty:
1504 * @bytes: a #GBytes
1505 *
1506 * Checks if a byte array are just empty (0xff) bytes.
1507 *
1508 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001509 *
1510 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001511 **/
1512gboolean
1513fu_common_bytes_is_empty (GBytes *bytes)
1514{
1515 gsize sz = 0;
1516 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1517 for (gsize i = 0; i < sz; i++) {
1518 if (buf[i] != 0xff)
1519 return FALSE;
1520 }
1521 return TRUE;
1522}
Richard Hughes2aad1042019-03-21 09:03:32 +00001523
1524/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001525 * fu_common_bytes_compare_raw:
1526 * @buf1: a buffer
1527 * @bufsz1: sizeof @buf1
1528 * @buf2: another buffer
1529 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001530 * @error: A #GError or %NULL
1531 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001532 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001533 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001534 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001535 *
1536 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001537 **/
1538gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001539fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1540 const guint8 *buf2, gsize bufsz2,
1541 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001542{
Richard Hughes38245ff2019-09-18 14:46:09 +01001543 g_return_val_if_fail (buf1 != NULL, FALSE);
1544 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001545 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1546
1547 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001548 if (bufsz1 != bufsz2) {
1549 g_set_error (error,
1550 G_IO_ERROR,
1551 G_IO_ERROR_INVALID_DATA,
1552 "got %" G_GSIZE_FORMAT " bytes, expected "
1553 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1554 return FALSE;
1555 }
1556
1557 /* check matches */
1558 for (guint i = 0x0; i < bufsz1; i++) {
1559 if (buf1[i] != buf2[i]) {
1560 g_set_error (error,
1561 G_IO_ERROR,
1562 G_IO_ERROR_INVALID_DATA,
1563 "got 0x%02x, expected 0x%02x @ 0x%04x",
1564 buf1[i], buf2[i], i);
1565 return FALSE;
1566 }
1567 }
1568
1569 /* success */
1570 return TRUE;
1571}
Richard Hughes484ee292019-03-22 16:10:50 +00001572
1573/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001574 * fu_common_bytes_compare:
1575 * @bytes1: a #GBytes
1576 * @bytes2: another #GBytes
1577 * @error: A #GError or %NULL
1578 *
1579 * Compares the buffers for equality.
1580 *
1581 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001582 *
1583 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001584 **/
1585gboolean
1586fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1587{
1588 const guint8 *buf1;
1589 const guint8 *buf2;
1590 gsize bufsz1;
1591 gsize bufsz2;
1592
1593 g_return_val_if_fail (bytes1 != NULL, FALSE);
1594 g_return_val_if_fail (bytes2 != NULL, FALSE);
1595 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1596
1597 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1598 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1599 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1600}
1601
1602/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001603 * fu_common_bytes_pad:
1604 * @bytes: a #GBytes
1605 * @sz: the desired size in bytes
1606 *
1607 * Pads a GBytes to a given @sz with `0xff`.
1608 *
1609 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001610 *
1611 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001612 **/
1613GBytes *
1614fu_common_bytes_pad (GBytes *bytes, gsize sz)
1615{
1616 gsize bytes_sz;
1617
1618 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1619
1620 /* pad */
1621 bytes_sz = g_bytes_get_size (bytes);
1622 if (bytes_sz < sz) {
1623 const guint8 *data = g_bytes_get_data (bytes, NULL);
1624 guint8 *data_new = g_malloc (sz);
1625 memcpy (data_new, data, bytes_sz);
1626 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1627 return g_bytes_new_take (data_new, sz);
1628 }
1629
1630 /* exactly right */
1631 return g_bytes_ref (bytes);
1632}
1633
1634/**
Richard Hughes484ee292019-03-22 16:10:50 +00001635 * fu_common_realpath:
1636 * @filename: a filename
1637 * @error: A #GError or %NULL
1638 *
1639 * Finds the canonicalized absolute filename for a path.
1640 *
1641 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001642 *
1643 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001644 **/
1645gchar *
1646fu_common_realpath (const gchar *filename, GError **error)
1647{
1648 char full_tmp[PATH_MAX];
1649
1650 g_return_val_if_fail (filename != NULL, NULL);
1651
Richard Hughes8694dee2019-11-22 09:16:34 +00001652#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001653 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001654#else
1655 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1656#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001657 g_set_error (error,
1658 G_IO_ERROR,
1659 G_IO_ERROR_INVALID_DATA,
1660 "cannot resolve path: %s",
1661 strerror (errno));
1662 return NULL;
1663 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001664 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1665 g_set_error (error,
1666 G_IO_ERROR,
1667 G_IO_ERROR_INVALID_DATA,
1668 "cannot find path: %s",
1669 full_tmp);
1670 return NULL;
1671 }
Richard Hughes484ee292019-03-22 16:10:50 +00001672 return g_strdup (full_tmp);
1673}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001674
1675/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001676 * fu_common_fnmatch:
1677 * @pattern: a glob pattern, e.g. `*foo*`
1678 * @str: a string to match against the pattern, e.g. `bazfoobar`
1679 *
1680 * Matches a string against a glob pattern.
1681 *
1682 * Return value: %TRUE if the string matched
1683 *
1684 * Since: 1.3.5
1685 **/
1686gboolean
1687fu_common_fnmatch (const gchar *pattern, const gchar *str)
1688{
1689 g_return_val_if_fail (pattern != NULL, FALSE);
1690 g_return_val_if_fail (str != NULL, FALSE);
1691#ifdef HAVE_FNMATCH_H
1692 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001693#elif _WIN32
1694 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1695 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1696 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001697#else
1698 return g_strcmp0 (pattern, str) == 0;
1699#endif
1700}
1701
Richard Hughesa84d7a72020-05-06 12:11:51 +01001702static gint
1703fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1704{
1705 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1706}
1707
1708/**
1709 * fu_common_filename_glob:
1710 * @directory: a directory path
1711 * @pattern: a glob pattern, e.g. `*foo*`
1712 * @error: A #GError or %NULL
1713 *
1714 * Returns all the filenames that match a specific glob pattern.
1715 * Any results are sorted. No matching files will set @error.
1716 *
1717 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1718 *
1719 * Since: 1.5.0
1720 **/
1721GPtrArray *
1722fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1723{
1724 const gchar *basename;
1725 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1726 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1727 if (dir == NULL)
1728 return NULL;
1729 while ((basename = g_dir_read_name (dir)) != NULL) {
1730 if (!fu_common_fnmatch (pattern, basename))
1731 continue;
1732 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1733 }
1734 if (files->len == 0) {
1735 g_set_error_literal (error,
1736 G_IO_ERROR,
1737 G_IO_ERROR_NOT_FOUND,
1738 "no files matched pattern");
1739 return NULL;
1740 }
1741 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1742 return g_steal_pointer (&files);
1743}
1744
Richard Hughes5c508de2019-11-22 09:57:34 +00001745/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001746 * fu_common_strnsplit:
1747 * @str: a string to split
1748 * @sz: size of @str
1749 * @delimiter: a string which specifies the places at which to split the string
1750 * @max_tokens: the maximum number of pieces to split @str into
1751 *
1752 * Splits a string into a maximum of @max_tokens pieces, using the given
1753 * delimiter. If @max_tokens is reached, the remainder of string is appended
1754 * to the last token.
1755 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001756 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001757 *
1758 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001759 **/
1760gchar **
1761fu_common_strnsplit (const gchar *str, gsize sz,
1762 const gchar *delimiter, gint max_tokens)
1763{
1764 if (str[sz - 1] != '\0') {
1765 g_autofree gchar *str2 = g_strndup (str, sz);
1766 return g_strsplit (str2, delimiter, max_tokens);
1767 }
1768 return g_strsplit (str, delimiter, max_tokens);
1769}
Richard Hughes5308ea42019-08-09 12:25:13 +01001770
1771/**
1772 * fu_memcpy_safe:
1773 * @dst: destination buffer
1774 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1775 * @dst_offset: offset in bytes into @dst to copy to
1776 * @src: source buffer
1777 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1778 * @src_offset: offset in bytes into @src to copy from
1779 * @n: number of bytes to copy from @src+@offset from
1780 * @error: A #GError or %NULL
1781 *
1782 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1783 * of both the destination and the source allows us to check for buffer overflow.
1784 *
1785 * Providing the buffer offsets also allows us to check reading past the end of
1786 * the source buffer. For this reason the caller should NEVER add an offset to
1787 * @src or @dst.
1788 *
1789 * You don't need to use this function in "obviously correct" cases, nor should
1790 * you use it when performance is a concern. Only us it when you're not sure if
1791 * malicious data from a device or firmware could cause memory corruption.
1792 *
1793 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001794 *
1795 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001796 **/
1797gboolean
1798fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1799 const guint8 *src, gsize src_sz, gsize src_offset,
1800 gsize n, GError **error)
1801{
1802 if (n == 0)
1803 return TRUE;
1804
1805 if (n > src_sz) {
1806 g_set_error (error,
1807 FWUPD_ERROR,
1808 FWUPD_ERROR_READ,
1809 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1810 (guint) n, (guint) src_sz);
1811 return FALSE;
1812 }
1813 if (n + src_offset > src_sz) {
1814 g_set_error (error,
1815 FWUPD_ERROR,
1816 FWUPD_ERROR_READ,
1817 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1818 (guint) n, (guint) src_offset, (guint) src_sz);
1819 return FALSE;
1820 }
1821 if (n > dst_sz) {
1822 g_set_error (error,
1823 FWUPD_ERROR,
1824 FWUPD_ERROR_WRITE,
1825 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1826 (guint) n, (guint) dst_sz);
1827 return FALSE;
1828 }
1829 if (n + dst_offset > dst_sz) {
1830 g_set_error (error,
1831 FWUPD_ERROR,
1832 FWUPD_ERROR_WRITE,
1833 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1834 (guint) n, (guint) dst_offset, (guint) dst_sz);
1835 return FALSE;
1836 }
1837
1838 /* phew! */
1839 memcpy (dst + dst_offset, src + src_offset, n);
1840 return TRUE;
1841}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001842
Richard Hughes80768f52019-10-22 07:19:14 +01001843/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001844 * fu_common_read_uint8_safe:
1845 * @buf: source buffer
1846 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1847 * @offset: offset in bytes into @buf to copy from
1848 * @value: (out) (allow-none): the parsed value
1849 * @error: A #GError or %NULL
1850 *
1851 * Read a value from a buffer in a safe way.
1852 *
1853 * You don't need to use this function in "obviously correct" cases, nor should
1854 * you use it when performance is a concern. Only us it when you're not sure if
1855 * malicious data from a device or firmware could cause memory corruption.
1856 *
1857 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001858 *
1859 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001860 **/
1861gboolean
1862fu_common_read_uint8_safe (const guint8 *buf,
1863 gsize bufsz,
1864 gsize offset,
1865 guint8 *value,
1866 GError **error)
1867{
1868 guint8 tmp;
1869 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1870 buf, bufsz, offset, /* src */
1871 sizeof(tmp), error))
1872 return FALSE;
1873 if (value != NULL)
1874 *value = tmp;
1875 return TRUE;
1876}
1877
1878/**
Richard Hughes80768f52019-10-22 07:19:14 +01001879 * fu_common_read_uint16_safe:
1880 * @buf: source buffer
1881 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1882 * @offset: offset in bytes into @buf to copy from
1883 * @value: (out) (allow-none): the parsed value
1884 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1885 * @error: A #GError or %NULL
1886 *
1887 * Read a value from a buffer using a specified endian in a safe way.
1888 *
1889 * You don't need to use this function in "obviously correct" cases, nor should
1890 * you use it when performance is a concern. Only us it when you're not sure if
1891 * malicious data from a device or firmware could cause memory corruption.
1892 *
1893 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001894 *
1895 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001896 **/
1897gboolean
1898fu_common_read_uint16_safe (const guint8 *buf,
1899 gsize bufsz,
1900 gsize offset,
1901 guint16 *value,
1902 FuEndianType endian,
1903 GError **error)
1904{
1905 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001906 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001907 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001908 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001909 return FALSE;
1910 if (value != NULL)
1911 *value = fu_common_read_uint16 (dst, endian);
1912 return TRUE;
1913}
1914
1915/**
1916 * fu_common_read_uint32_safe:
1917 * @buf: source buffer
1918 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1919 * @offset: offset in bytes into @buf to copy from
1920 * @value: (out) (allow-none): the parsed value
1921 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1922 * @error: A #GError or %NULL
1923 *
1924 * Read a value from a buffer using a specified endian in a safe way.
1925 *
1926 * You don't need to use this function in "obviously correct" cases, nor should
1927 * you use it when performance is a concern. Only us it when you're not sure if
1928 * malicious data from a device or firmware could cause memory corruption.
1929 *
1930 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001931 *
1932 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001933 **/
1934gboolean
1935fu_common_read_uint32_safe (const guint8 *buf,
1936 gsize bufsz,
1937 gsize offset,
1938 guint32 *value,
1939 FuEndianType endian,
1940 GError **error)
1941{
1942 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001943 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001944 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001945 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001946 return FALSE;
1947 if (value != NULL)
1948 *value = fu_common_read_uint32 (dst, endian);
1949 return TRUE;
1950}
1951
Mario Limonciello1a680f32019-11-25 19:44:53 -06001952/**
1953 * fu_byte_array_append_uint8:
1954 * @array: A #GByteArray
1955 * @data: #guint8
1956 *
1957 * Adds a 8 bit integer to a byte array
1958 *
1959 * Since: 1.3.1
1960 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001961void
1962fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1963{
1964 g_byte_array_append (array, &data, sizeof(data));
1965}
1966
Mario Limonciello1a680f32019-11-25 19:44:53 -06001967/**
1968 * fu_byte_array_append_uint16:
1969 * @array: A #GByteArray
1970 * @data: #guint16
1971 * @endian: #FuEndianType
1972 *
1973 * Adds a 16 bit integer to a byte array
1974 *
1975 * Since: 1.3.1
1976 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001977void
1978fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1979{
1980 guint8 buf[2];
1981 fu_common_write_uint16 (buf, data, endian);
1982 g_byte_array_append (array, buf, sizeof(buf));
1983}
1984
Mario Limonciello1a680f32019-11-25 19:44:53 -06001985/**
1986 * fu_byte_array_append_uint32:
1987 * @array: A #GByteArray
1988 * @data: #guint32
1989 * @endian: #FuEndianType
1990 *
1991 * Adds a 32 bit integer to a byte array
1992 *
1993 * Since: 1.3.1
1994 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001995void
1996fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1997{
1998 guint8 buf[4];
1999 fu_common_write_uint32 (buf, data, endian);
2000 g_byte_array_append (array, buf, sizeof(buf));
2001}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002002
2003/**
2004 * fu_common_kernel_locked_down:
2005 *
2006 * Determines if kernel lockdown in effect
2007 *
2008 * Since: 1.3.8
2009 **/
2010gboolean
2011fu_common_kernel_locked_down (void)
2012{
2013#ifndef _WIN32
2014 gsize len = 0;
2015 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2016 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2017 g_autofree gchar *data = NULL;
2018 g_auto(GStrv) options = NULL;
2019
2020 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2021 return FALSE;
2022 if (!g_file_get_contents (fname, &data, &len, NULL))
2023 return FALSE;
2024 if (len < 1)
2025 return FALSE;
2026 options = g_strsplit (data, " ", -1);
2027 for (guint i = 0; options[i] != NULL; i++) {
2028 if (g_strcmp0 (options[i], "[none]") == 0)
2029 return FALSE;
2030 }
2031 return TRUE;
2032#else
2033 return FALSE;
2034#endif
2035}
Richard Hughes9223c892020-05-09 20:32:08 +01002036
2037/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002038 * fu_common_cpuid:
2039 * @leaf: The CPUID level, now called the 'leaf' by Intel
2040 * @eax: (out) (nullable): EAX register
2041 * @ebx: (out) (nullable): EBX register
2042 * @ecx: (out) (nullable): ECX register
2043 * @edx: (out) (nullable): EDX register
2044 * @error: A #GError or NULL
2045 *
2046 * Calls CPUID and returns the registers for the given leaf.
2047 *
2048 * Return value: %TRUE if the registers are set.
2049 *
2050 * Since: 1.5.0
2051 **/
2052gboolean
2053fu_common_cpuid (guint32 leaf,
2054 guint32 *eax,
2055 guint32 *ebx,
2056 guint32 *ecx,
2057 guint32 *edx,
2058 GError **error)
2059{
2060#ifdef HAVE_CPUID_H
2061 guint eax_tmp = 0;
2062 guint ebx_tmp = 0;
2063 guint ecx_tmp = 0;
2064 guint edx_tmp = 0;
2065
2066 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002067 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002068 if (eax != NULL)
2069 *eax = eax_tmp;
2070 if (ebx != NULL)
2071 *ebx = ebx_tmp;
2072 if (ecx != NULL)
2073 *ecx = ecx_tmp;
2074 if (edx != NULL)
2075 *edx = edx_tmp;
2076 return TRUE;
2077#else
2078 g_set_error_literal (error,
2079 G_IO_ERROR,
2080 G_IO_ERROR_NOT_SUPPORTED,
2081 "no <cpuid.h> support");
2082 return FALSE;
2083#endif
2084}
2085
2086/**
Richard Hughes9223c892020-05-09 20:32:08 +01002087 * fu_common_is_cpu_intel:
2088 *
2089 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2090 *
2091 * Return value: %TRUE if the vendor was Intel.
2092 *
2093 * Since: 1.5.0
2094 **/
2095gboolean
2096fu_common_is_cpu_intel (void)
2097{
Richard Hughes9223c892020-05-09 20:32:08 +01002098 guint ebx = 0;
2099 guint ecx = 0;
2100 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002101
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002102 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2103 return FALSE;
2104#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002105 if (ebx == signature_INTEL_ebx &&
2106 edx == signature_INTEL_edx &&
2107 ecx == signature_INTEL_ecx) {
2108 return TRUE;
2109 }
Richard Hughesbd444322020-05-21 12:05:03 +01002110#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002111 return FALSE;
2112}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002113
Richard Hughes36111472020-08-12 15:04:24 +01002114/**
2115 * fu_common_is_live_media:
2116 *
2117 * Checks if the user is running from a live media using various heuristics.
2118 *
2119 * Returns: %TRUE if live
2120 *
2121 * Since: 1.4.6
2122 **/
2123gboolean
2124fu_common_is_live_media (void)
2125{
2126 gsize bufsz = 0;
2127 g_autofree gchar *buf = NULL;
2128 g_auto(GStrv) tokens = NULL;
2129 const gchar *args[] = {
2130 "rd.live.image",
2131 "boot=live",
2132 NULL, /* last entry */
2133 };
2134 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2135 return TRUE;
2136 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2137 return FALSE;
2138 if (bufsz == 0)
2139 return FALSE;
2140 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2141 for (guint i = 0; args[i] != NULL; i++) {
2142 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2143 return TRUE;
2144 }
2145 return FALSE;
2146}
2147
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002148static GPtrArray *
2149fu_common_get_block_devices (GDBusConnection *connection, GError **error)
2150{
2151 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002152 const gchar *obj;
2153 g_autoptr(GVariant) output = NULL;
2154 g_autoptr(GDBusProxy) proxy = NULL;
2155 g_autoptr(GPtrArray) devices = NULL;
2156 g_autoptr(GVariantIter) iter = NULL;
2157
2158 proxy = g_dbus_proxy_new_sync (connection,
2159 G_DBUS_PROXY_FLAGS_NONE, NULL,
2160 UDISKS_DBUS_SERVICE,
2161 UDISKS_DBUS_PATH,
2162 UDISKS_DBUS_MANAGER_INTERFACE,
2163 NULL, error);
2164 if (proxy == NULL) {
2165 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2166 return NULL;
2167 }
2168 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002169 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002170 "GetBlockDevices",
2171 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002172 G_DBUS_CALL_FLAGS_NONE,
2173 -1, NULL, error);
2174 if (output == NULL)
2175 return NULL;
2176 devices = g_ptr_array_new_with_free_func (g_free);
2177 g_variant_get (output, "(ao)", &iter);
Richard Hughesdb344d52020-09-09 19:42:27 +01002178 while (g_variant_iter_next (iter, "&o", &obj))
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002179 g_ptr_array_add (devices, g_strdup (obj));
2180
2181 return g_steal_pointer (&devices);
2182}
2183
2184/**
2185 * fu_common_get_volumes_by_kind:
2186 * @kind: A volume kind, typically a GUID
2187 * @error: A #GError or NULL
2188 *
2189 * Call into the plugin's get results routine
2190 *
2191 * Finds all volumes of a specific type
2192 *
2193 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2194 *
2195 * Since: 1.4.6
2196 **/
2197GPtrArray *
2198fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2199{
2200 g_autoptr(GDBusConnection) connection = NULL;
2201 g_autoptr(GPtrArray) devices = NULL;
2202 g_autoptr(GPtrArray) volumes = NULL;
2203
2204 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2205 if (connection == NULL) {
2206 g_prefix_error (error, "failed to get system bus: ");
2207 return NULL;
2208 }
2209 devices = fu_common_get_block_devices (connection, error);
2210 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002211 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002212 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2213 for (guint i = 0; i < devices->len; i++) {
2214 const gchar *obj = g_ptr_array_index (devices, i);
2215 const gchar *type_str;
2216 g_autoptr(GDBusProxy) proxy_part = NULL;
2217 g_autoptr(GDBusProxy) proxy_file = NULL;
2218 g_autoptr(GError) error_local = NULL;
2219 g_autoptr(GVariant) val = NULL;
2220
2221 proxy_part = g_dbus_proxy_new_sync (connection,
2222 G_DBUS_PROXY_FLAGS_NONE, NULL,
2223 UDISKS_DBUS_SERVICE,
2224 obj,
2225 UDISKS_DBUS_PART_INTERFACE,
2226 NULL, error);
2227 if (proxy_part == NULL) {
2228 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
Richard Hughesb81140d2020-08-17 14:47:17 +01002229 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002230 }
2231 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2232 if (val == NULL)
2233 continue;
2234
Richard Hughesdb344d52020-09-09 19:42:27 +01002235 g_variant_get (val, "&s", &type_str);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002236 g_debug ("device %s, type: %s", obj, type_str);
2237 if (g_strcmp0 (type_str, kind) != 0)
2238 continue;
2239 proxy_file = g_dbus_proxy_new_sync (connection,
2240 G_DBUS_PROXY_FLAGS_NONE, NULL,
2241 UDISKS_DBUS_SERVICE,
2242 obj,
2243 UDISKS_DBUS_FILE_INTERFACE,
2244 NULL, error);
2245 if (proxy_file == NULL) {
2246 g_prefix_error (error, "failed to initialize d-bus proxy %s: ", obj);
Richard Hughesb81140d2020-08-17 14:47:17 +01002247 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002248 }
2249 g_ptr_array_add (volumes, fu_volume_new_from_proxy (proxy_file));
2250 }
2251 if (volumes->len == 0) {
2252 g_set_error (error,
2253 G_IO_ERROR,
2254 G_IO_ERROR_NOT_FOUND,
2255 "no volumes of type %s", kind);
2256 return NULL;
2257 }
2258 return g_steal_pointer (&volumes);
2259}
2260
2261/**
2262 * fu_common_get_esp_default:
2263 * @error: A #GError or NULL
2264 *
2265 * Gets the platform default ESP
2266 *
2267 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2268 *
2269 * Since: 1.4.6
2270 **/
2271FuVolume *
2272fu_common_get_esp_default (GError **error)
2273{
2274 const gchar *path_tmp;
2275 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2276 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
2277 g_autoptr(GPtrArray) volumes = NULL;
2278
2279 /* for the test suite use local directory for ESP */
2280 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2281 if (path_tmp != NULL)
2282 return fu_volume_new_from_mount_path (path_tmp);
2283
2284 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2285 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002286 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002287 for (guint i = 0; i < volumes->len; i++) {
2288 FuVolume *vol = g_ptr_array_index (volumes, i);
2289 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2290 }
2291 if (volumes_mtab->len == 1) {
2292 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2293 return g_object_ref (vol);
2294 }
2295 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2296 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2297 return g_object_ref (vol);
2298 }
2299 g_set_error (error,
2300 G_IO_ERROR,
2301 G_IO_ERROR_INVALID_FILENAME,
2302 "More than one available ESP");
2303 return NULL;
2304}
2305
2306/**
2307 * fu_common_get_esp_for_path:
2308 * @esp_path: A path to the ESP
2309 * @error: A #GError or NULL
2310 *
2311 * Gets the platform ESP using a UNIX or UDisks path
2312 *
2313 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2314 *
2315 * Since: 1.4.6
2316 **/
2317FuVolume *
2318fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2319{
2320 g_autofree gchar *basename = g_path_get_basename (esp_path);
2321 g_autoptr(GPtrArray) volumes = NULL;
2322
2323 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2324 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002325 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002326 for (guint i = 0; i < volumes->len; i++) {
2327 FuVolume *vol = g_ptr_array_index (volumes, i);
2328 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_id (vol));
2329 if (g_strcmp0 (basename, vol_basename) == 0)
2330 return g_object_ref (vol);
2331 }
2332 g_set_error (error,
2333 G_IO_ERROR,
2334 G_IO_ERROR_INVALID_FILENAME,
2335 "No ESP with path %s",
2336 esp_path);
2337 return NULL;
2338}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002339
2340/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002341 * fu_common_crc8:
2342 * @buf: memory buffer
2343 * @bufsz: sizeof buf
2344 *
2345 * Returns the cyclic redundancy check value for the given memory buffer.
2346 *
2347 * Returns: CRC value
2348 *
2349 * Since: 1.5.0
2350 **/
2351guint8
2352fu_common_crc8 (const guint8 *buf, gsize bufsz)
2353{
2354 guint32 crc = 0;
2355 for (gsize j = bufsz; j > 0; j--) {
2356 crc ^= (*(buf++) << 8);
2357 for (guint32 i = 8; i; i--) {
2358 if (crc & 0x8000)
2359 crc ^= (0x1070 << 3);
2360 crc <<= 1;
2361 }
2362 }
2363 return ~((guint8) (crc >> 8));
2364}
2365
2366/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002367 * fu_common_crc16:
2368 * @buf: memory buffer
2369 * @bufsz: sizeof buf
2370 *
2371 * Returns the cyclic redundancy check value for the given memory buffer.
2372 *
2373 * Returns: CRC value
2374 *
2375 * Since: 1.5.0
2376 **/
2377guint16
2378fu_common_crc16 (const guint8 *buf, gsize bufsz)
2379{
2380 guint16 crc = 0xffff;
2381 for (gsize len = bufsz; len > 0; len--) {
2382 crc = (guint16) (crc ^ (*buf++));
2383 for (guint8 i = 0; i < 8; i++) {
2384 if (crc & 0x1) {
2385 crc = (crc >> 1) ^ 0xa001;
2386 } else {
2387 crc >>= 1;
2388 }
2389 }
2390 }
2391 return ~crc;
2392}
2393
2394/**
2395 * fu_common_crc32_full:
2396 * @buf: memory buffer
2397 * @bufsz: sizeof buf
2398 * @crc: initial CRC value, typically 0xFFFFFFFF
2399 * @polynomial: CRC polynomial, typically 0xEDB88320
2400 *
2401 * Returns the cyclic redundancy check value for the given memory buffer.
2402 *
2403 * Returns: CRC value
2404 *
2405 * Since: 1.5.0
2406 **/
2407guint32
2408fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2409{
2410 for (guint32 idx = 0; idx < bufsz; idx++) {
2411 guint8 data = *buf++;
2412 crc = crc ^ data;
2413 for (guint32 bit = 0; bit < 8; bit++) {
2414 guint32 mask = -(crc & 1);
2415 crc = (crc >> 1) ^ (polynomial & mask);
2416 }
2417 }
2418 return ~crc;
2419}
2420
2421/**
2422 * fu_common_crc32:
2423 * @buf: memory buffer
2424 * @bufsz: sizeof buf
2425 *
2426 * Returns the cyclic redundancy check value for the given memory buffer.
2427 *
2428 * Returns: CRC value
2429 *
2430 * Since: 1.5.0
2431 **/
2432guint32
2433fu_common_crc32 (const guint8 *buf, gsize bufsz)
2434{
2435 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2436}