blob: 13d97e1e9feffe47c025eac4181aad078eee3686 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes943d2c92017-06-21 09:04:39 +01002 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes943d2c92017-06-21 09:04:39 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuCommon"
8
Richard Hughes943d2c92017-06-21 09:04:39 +01009#include <config.h>
10
Richard Hughes9e5675e2019-11-22 09:35:03 +000011#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +010012#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#endif
Richard Hughes954dd9f2017-08-08 13:36:25 +010014#include <glib/gstdio.h>
15
Richard Hughes5c508de2019-11-22 09:57:34 +000016#ifdef HAVE_FNMATCH_H
17#include <fnmatch.h>
Richard Hughes45a00732019-11-22 16:57:14 +000018#elif _WIN32
19#include <shlwapi.h>
Richard Hughes5c508de2019-11-22 09:57:34 +000020#endif
21
Richard Hughesbd444322020-05-21 12:05:03 +010022#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010023#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010024#endif
Richard Hughes9223c892020-05-09 20:32:08 +010025
Richard Hughes94f939a2017-08-08 12:21:39 +010026#include <archive_entry.h>
27#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010028#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000029#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010032
33#include "fwupd-error.h"
34
35#include "fu-common.h"
Richard Hughes8f0b2d12020-08-12 12:41:53 +010036#include "fu-volume-private.h"
37
Richard Hughes43417b22020-10-30 14:46:16 +000038#define UDISKS_DBUS_SERVICE "org.freedesktop.UDisks2"
39#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager"
40#define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
41#define UDISKS_DBUS_INTERFACE_PARTITION "org.freedesktop.UDisks2.Partition"
42#define UDISKS_DBUS_INTERFACE_FILESYSTEM "org.freedesktop.UDisks2.Filesystem"
43#define UDISKS_DBUS_INTERFACE_BLOCK "org.freedesktop.UDisks2.Block"
Richard Hughes943d2c92017-06-21 09:04:39 +010044
45/**
Richard Hughes4eada342017-10-03 21:20:32 +010046 * SECTION:fu-common
47 * @short_description: common functionality for plugins to use
48 *
49 * Helper functions that can be used by the daemon and plugins.
50 *
51 * See also: #FuPlugin
52 */
53
54/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010055 * fu_common_rmtree:
56 * @directory: a directory name
57 * @error: A #GError or %NULL
58 *
59 * Recursively removes a directory.
60 *
61 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060062 *
63 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010064 **/
65gboolean
66fu_common_rmtree (const gchar *directory, GError **error)
67{
68 const gchar *filename;
69 g_autoptr(GDir) dir = NULL;
70
71 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010072 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010073 dir = g_dir_open (directory, 0, error);
74 if (dir == NULL)
75 return FALSE;
76
77 /* find each */
78 while ((filename = g_dir_read_name (dir))) {
79 g_autofree gchar *src = NULL;
80 src = g_build_filename (directory, filename, NULL);
81 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
82 if (!fu_common_rmtree (src, error))
83 return FALSE;
84 } else {
85 if (g_unlink (src) != 0) {
86 g_set_error (error,
87 FWUPD_ERROR,
88 FWUPD_ERROR_INTERNAL,
89 "Failed to delete: %s", src);
90 return FALSE;
91 }
92 }
93 }
94 if (g_remove (directory) != 0) {
95 g_set_error (error,
96 FWUPD_ERROR,
97 FWUPD_ERROR_INTERNAL,
98 "Failed to delete: %s", directory);
99 return FALSE;
100 }
101 return TRUE;
102}
103
Richard Hughes89e968b2018-03-07 10:01:08 +0000104static gboolean
105fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
106{
107 const gchar *filename;
108 g_autoptr(GDir) dir = NULL;
109
110 /* try to open */
111 dir = g_dir_open (directory, 0, error);
112 if (dir == NULL)
113 return FALSE;
114
115 /* find each */
116 while ((filename = g_dir_read_name (dir))) {
117 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
118 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
119 if (!fu_common_get_file_list_internal (files, src, error))
120 return FALSE;
121 } else {
122 g_ptr_array_add (files, g_steal_pointer (&src));
123 }
124 }
125 return TRUE;
126
127}
128
129/**
130 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100131 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000132 * @error: A #GError or %NULL
133 *
134 * Returns every file found under @directory, and any subdirectory.
135 * If any path under @directory cannot be accessed due to permissions an error
136 * will be returned.
137 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000138 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600139 *
140 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000141 **/
142GPtrArray *
143fu_common_get_files_recursive (const gchar *path, GError **error)
144{
145 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
146 if (!fu_common_get_file_list_internal (files, path, error))
147 return NULL;
148 return g_steal_pointer (&files);
149}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100150/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100151 * fu_common_mkdir_parent:
152 * @filename: A full pathname
153 * @error: A #GError, or %NULL
154 *
155 * Creates any required directories, including any parent directories.
156 *
157 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600158 *
159 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100160 **/
161gboolean
162fu_common_mkdir_parent (const gchar *filename, GError **error)
163{
164 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100165
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100166 parent = g_path_get_dirname (filename);
Mario Limonciellod4155ff2020-09-28 15:20:07 -0500167 if (!g_file_test (parent, G_FILE_TEST_IS_DIR))
168 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100169 if (g_mkdir_with_parents (parent, 0755) == -1) {
170 g_set_error (error,
171 FWUPD_ERROR,
172 FWUPD_ERROR_INTERNAL,
173 "Failed to create '%s': %s",
174 parent, g_strerror (errno));
175 return FALSE;
176 }
177 return TRUE;
178}
179
180/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100181 * fu_common_set_contents_bytes:
182 * @filename: A filename
183 * @bytes: The data to write
184 * @error: A #GError, or %NULL
185 *
186 * Writes a blob of data to a filename, creating the parent directories as
187 * required.
188 *
189 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600190 *
191 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100192 **/
193gboolean
194fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
195{
196 const gchar *data;
197 gsize size;
198 g_autoptr(GFile) file = NULL;
199 g_autoptr(GFile) file_parent = NULL;
200
201 file = g_file_new_for_path (filename);
202 file_parent = g_file_get_parent (file);
203 if (!g_file_query_exists (file_parent, NULL)) {
204 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
205 return FALSE;
206 }
207 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100208 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100209 return g_file_set_contents (filename, data, size, error);
210}
211
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100212/**
213 * fu_common_get_contents_bytes:
214 * @filename: A filename
215 * @error: A #GError, or %NULL
216 *
217 * Reads a blob of data from a file.
218 *
219 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600220 *
221 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100222 **/
223GBytes *
224fu_common_get_contents_bytes (const gchar *filename, GError **error)
225{
226 gchar *data = NULL;
227 gsize len = 0;
228 if (!g_file_get_contents (filename, &data, &len, error))
229 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100230 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100231 return g_bytes_new_take (data, len);
232}
Richard Hughes943d2c92017-06-21 09:04:39 +0100233
234/**
235 * fu_common_get_contents_fd:
236 * @fd: A file descriptor
237 * @count: The maximum number of bytes to read
238 * @error: A #GError, or %NULL
239 *
240 * Reads a blob from a specific file descriptor.
241 *
242 * Note: this will close the fd when done
243 *
Richard Hughes4eada342017-10-03 21:20:32 +0100244 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600245 *
246 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100247 **/
248GBytes *
249fu_common_get_contents_fd (gint fd, gsize count, GError **error)
250{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000251#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100252 g_autoptr(GBytes) blob = NULL;
253 g_autoptr(GError) error_local = NULL;
254 g_autoptr(GInputStream) stream = NULL;
255
256 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100257 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
258
Richard Hughes919f8ab2018-02-14 10:24:56 +0000259 /* this is invalid */
260 if (count == 0) {
261 g_set_error_literal (error,
262 FWUPD_ERROR,
263 FWUPD_ERROR_NOT_SUPPORTED,
264 "A maximum read size must be specified");
265 return NULL;
266 }
267
Richard Hughes943d2c92017-06-21 09:04:39 +0100268 /* read the entire fd to a data blob */
269 stream = g_unix_input_stream_new (fd, TRUE);
270 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
271 if (blob == NULL) {
272 g_set_error_literal (error,
273 FWUPD_ERROR,
274 FWUPD_ERROR_INVALID_FILE,
275 error_local->message);
276 return NULL;
277 }
278 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000279#else
280 g_set_error_literal (error,
281 FWUPD_ERROR,
282 FWUPD_ERROR_NOT_SUPPORTED,
283 "Not supported as <glib-unix.h> is unavailable");
284 return NULL;
285#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100286}
Richard Hughes94f939a2017-08-08 12:21:39 +0100287
288static gboolean
289fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
290{
291 const gchar *tmp;
292 g_autofree gchar *buf = NULL;
293
294 /* no output file */
295 if (archive_entry_pathname (entry) == NULL)
296 return FALSE;
297
298 /* update output path */
299 tmp = archive_entry_pathname (entry);
300 buf = g_build_filename (dir, tmp, NULL);
301 archive_entry_update_pathname_utf8 (entry, buf);
302 return TRUE;
303}
304
305/**
306 * fu_common_extract_archive:
307 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100308 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100309 * @error: A #GError, or %NULL
310 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000311 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100312 *
313 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600314 *
315 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100316 **/
317gboolean
318fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
319{
320 gboolean ret = TRUE;
321 int r;
322 struct archive *arch = NULL;
323 struct archive_entry *entry;
324
325 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100326 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100327 arch = archive_read_new ();
328 archive_read_support_format_all (arch);
329 archive_read_support_filter_all (arch);
330 r = archive_read_open_memory (arch,
331 (void *) g_bytes_get_data (blob, NULL),
332 (size_t) g_bytes_get_size (blob));
333 if (r != 0) {
334 ret = FALSE;
335 g_set_error (error,
336 FWUPD_ERROR,
337 FWUPD_ERROR_INTERNAL,
338 "Cannot open: %s",
339 archive_error_string (arch));
340 goto out;
341 }
342 for (;;) {
343 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100344 r = archive_read_next_header (arch, &entry);
345 if (r == ARCHIVE_EOF)
346 break;
347 if (r != ARCHIVE_OK) {
348 ret = FALSE;
349 g_set_error (error,
350 FWUPD_ERROR,
351 FWUPD_ERROR_INTERNAL,
352 "Cannot read header: %s",
353 archive_error_string (arch));
354 goto out;
355 }
356
357 /* only extract if valid */
358 valid = fu_common_extract_archive_entry (entry, dir);
359 if (!valid)
360 continue;
361 r = archive_read_extract (arch, entry, 0);
362 if (r != ARCHIVE_OK) {
363 ret = FALSE;
364 g_set_error (error,
365 FWUPD_ERROR,
366 FWUPD_ERROR_INTERNAL,
367 "Cannot extract: %s",
368 archive_error_string (arch));
369 goto out;
370 }
371 }
372out:
373 if (arch != NULL) {
374 archive_read_close (arch);
375 archive_read_free (arch);
376 }
377 return ret;
378}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100379
380static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300381fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
382
383static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100384fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
385{
386 va_list args;
387 g_autofree gchar *tmp = NULL;
388 g_auto(GStrv) split = NULL;
389
390 va_start (args, fmt);
391 tmp = g_strdup_vprintf (fmt, args);
392 va_end (args);
393
394 split = g_strsplit (tmp, " ", -1);
395 for (guint i = 0; split[i] != NULL; i++)
396 g_ptr_array_add (argv, g_strdup (split[i]));
397}
398
Mario Limonciello1a680f32019-11-25 19:44:53 -0600399/**
400 * fu_common_find_program_in_path:
401 * @basename: The program to search
402 * @error: A #GError, or %NULL
403 *
404 * Looks for a program in the PATH variable
405 *
406 * Returns: a new #gchar, or %NULL for error
407 *
408 * Since: 1.1.2
409 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100410gchar *
411fu_common_find_program_in_path (const gchar *basename, GError **error)
412{
413 gchar *fn = g_find_program_in_path (basename);
414 if (fn == NULL) {
415 g_set_error (error,
416 FWUPD_ERROR,
417 FWUPD_ERROR_NOT_SUPPORTED,
418 "missing executable %s in PATH",
419 basename);
420 return NULL;
421 }
422 return fn;
423}
424
425static gboolean
426fu_common_test_namespace_support (GError **error)
427{
428 /* test if CONFIG_USER_NS is valid */
429 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
430 g_set_error (error,
431 FWUPD_ERROR,
432 FWUPD_ERROR_NOT_SUPPORTED,
433 "missing CONFIG_USER_NS in kernel");
434 return FALSE;
435 }
436 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
437 g_autofree gchar *clone = NULL;
438 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
439 return FALSE;
440 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
441 g_set_error (error,
442 FWUPD_ERROR,
443 FWUPD_ERROR_NOT_SUPPORTED,
444 "unprivileged user namespace clones disabled by distro");
445 return FALSE;
446 }
447 }
448 return TRUE;
449}
450
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100451/**
452 * fu_common_firmware_builder:
453 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100454 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
455 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100456 * @error: A #GError, or %NULL
457 *
458 * Builds a firmware file using tools from the host session in a bubblewrap
459 * jail. Several things happen during build:
460 *
461 * 1. The @bytes data is untarred to a temporary location
462 * 2. A bubblewrap container is set up
463 * 3. The startup.sh script is run inside the container
464 * 4. The firmware.bin is extracted from the container
465 * 5. The temporary location is deleted
466 *
467 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600468 *
469 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100470 **/
471GBytes *
472fu_common_firmware_builder (GBytes *bytes,
473 const gchar *script_fn,
474 const gchar *output_fn,
475 GError **error)
476{
477 gint rc = 0;
478 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500479 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100480 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100481 g_autofree gchar *localstatedir = NULL;
482 g_autofree gchar *output2_fn = NULL;
483 g_autofree gchar *standard_error = NULL;
484 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100485 g_autofree gchar *tmpdir = NULL;
486 g_autoptr(GBytes) firmware_blob = NULL;
487 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
488
489 g_return_val_if_fail (bytes != NULL, NULL);
490 g_return_val_if_fail (script_fn != NULL, NULL);
491 g_return_val_if_fail (output_fn != NULL, NULL);
492 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
493
Mario Limonciello37b59582018-08-13 08:38:01 -0500494 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100495 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
496 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100497 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500498
499 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100500 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100501 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500502
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100503 /* untar file to temp location */
504 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
505 if (tmpdir == NULL)
506 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507 if (!fu_common_extract_archive (bytes, tmpdir, error))
508 return NULL;
509
510 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100511 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
512 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100513
514 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500515 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100516 fu_common_add_argv (argv, "--die-with-parent");
517 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500518 fu_common_add_argv (argv, "--ro-bind /lib /lib");
519 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
520 fu_common_add_argv (argv, "--ro-bind /bin /bin");
521 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100522 fu_common_add_argv (argv, "--dir /tmp");
523 fu_common_add_argv (argv, "--dir /var");
524 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100525 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
526 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100527 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100528 fu_common_add_argv (argv, "--chdir /tmp");
529 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100530 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100531 g_ptr_array_add (argv, NULL);
532 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
533 g_debug ("running '%s' in %s", argv_str, tmpdir);
534 if (!g_spawn_sync ("/tmp",
535 (gchar **) argv->pdata,
536 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100537 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100538 NULL, NULL, /* child_setup */
539 &standard_output,
540 &standard_error,
541 &rc,
542 error)) {
543 g_prefix_error (error, "failed to run '%s': ", argv_str);
544 return NULL;
545 }
546 if (standard_output != NULL && standard_output[0] != '\0')
547 g_debug ("console output was: %s", standard_output);
548 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500549 FwupdError code = FWUPD_ERROR_INTERNAL;
550 if (errno == ENOTTY)
551 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100552 g_set_error (error,
553 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500554 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100555 "failed to build firmware: %s",
556 standard_error);
557 return NULL;
558 }
559
560 /* get generated file */
561 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
562 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
563 if (firmware_blob == NULL)
564 return NULL;
565
566 /* cleanup temp directory */
567 if (!fu_common_rmtree (tmpdir, error))
568 return NULL;
569
570 /* success */
571 return g_steal_pointer (&firmware_blob);
572}
Richard Hughes049ccc82017-08-09 15:26:56 +0100573
574typedef struct {
575 FuOutputHandler handler_cb;
576 gpointer handler_user_data;
577 GMainLoop *loop;
578 GSource *source;
579 GInputStream *stream;
580 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000581 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100582} FuCommonSpawnHelper;
583
584static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
585
586static gboolean
587fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
588{
589 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
590 gchar buffer[1024];
591 gssize sz;
592 g_auto(GStrv) split = NULL;
593 g_autoptr(GError) error = NULL;
594
595 /* read from stream */
596 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
597 buffer,
598 sizeof(buffer) - 1,
599 NULL,
600 &error);
601 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100602 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
603 g_warning ("failed to get read from nonblocking fd: %s",
604 error->message);
605 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100606 return G_SOURCE_REMOVE;
607 }
608
609 /* no read possible */
610 if (sz == 0)
611 g_main_loop_quit (helper->loop);
612
613 /* emit lines */
614 if (helper->handler_cb != NULL) {
615 buffer[sz] = '\0';
616 split = g_strsplit (buffer, "\n", -1);
617 for (guint i = 0; split[i] != NULL; i++) {
618 if (split[i][0] == '\0')
619 continue;
620 helper->handler_cb (split[i], helper->handler_user_data);
621 }
622 }
623
624 /* set up the source for the next read */
625 fu_common_spawn_create_pollable_source (helper);
626 return G_SOURCE_REMOVE;
627}
628
629static void
630fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
631{
632 if (helper->source != NULL)
633 g_source_destroy (helper->source);
634 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
635 helper->cancellable);
636 g_source_attach (helper->source, NULL);
637 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
638}
639
640static void
641fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
642{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000643 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100644 if (helper->stream != NULL)
645 g_object_unref (helper->stream);
646 if (helper->source != NULL)
647 g_source_destroy (helper->source);
648 if (helper->loop != NULL)
649 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000650 if (helper->timeout_id != 0)
651 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100652 g_free (helper);
653}
654
Mario Limoncielloa98df552018-04-16 12:15:51 -0500655#pragma clang diagnostic push
656#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100657G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500658#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100659
Richard Hughesb768e4d2019-02-26 13:55:18 +0000660static gboolean
661fu_common_spawn_timeout_cb (gpointer user_data)
662{
663 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
664 g_cancellable_cancel (helper->cancellable);
665 g_main_loop_quit (helper->loop);
666 helper->timeout_id = 0;
667 return G_SOURCE_REMOVE;
668}
669
670static void
671fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
672{
673 /* just propagate */
674 g_cancellable_cancel (helper->cancellable);
675}
676
Richard Hughes049ccc82017-08-09 15:26:56 +0100677/**
678 * fu_common_spawn_sync:
679 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100680 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
681 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000682 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100683 * @cancellable: a #GCancellable, or %NULL
684 * @error: A #GError or %NULL
685 *
686 * Runs a subprocess and waits for it to exit. Any output on standard out or
687 * standard error will be forwarded to @handler_cb as whole lines.
688 *
689 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600690 *
691 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100692 **/
693gboolean
694fu_common_spawn_sync (const gchar * const * argv,
695 FuOutputHandler handler_cb,
696 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000697 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100698 GCancellable *cancellable, GError **error)
699{
700 g_autoptr(FuCommonSpawnHelper) helper = NULL;
701 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100702 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000703 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100704
705 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100706 argv_str = g_strjoinv (" ", (gchar **) argv);
707 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100708 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
709 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
710 if (subprocess == NULL)
711 return FALSE;
712
713 /* watch for process to exit */
714 helper = g_new0 (FuCommonSpawnHelper, 1);
715 helper->handler_cb = handler_cb;
716 helper->handler_user_data = handler_user_data;
717 helper->loop = g_main_loop_new (NULL, FALSE);
718 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000719
720 /* always create a cancellable, and connect up the parent */
721 helper->cancellable = g_cancellable_new ();
722 if (cancellable != NULL) {
723 cancellable_id = g_cancellable_connect (cancellable,
724 G_CALLBACK (fu_common_spawn_cancelled_cb),
725 helper, NULL);
726 }
727
728 /* allow timeout */
729 if (timeout_ms > 0) {
730 helper->timeout_id = g_timeout_add (timeout_ms,
731 fu_common_spawn_timeout_cb,
732 helper);
733 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100734 fu_common_spawn_create_pollable_source (helper);
735 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000736 g_cancellable_disconnect (cancellable, cancellable_id);
737 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
738 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100739 return g_subprocess_wait_check (subprocess, cancellable, error);
740}
Richard Hughesae252cd2017-12-08 10:48:15 +0000741
742/**
743 * fu_common_write_uint16:
744 * @buf: A writable buffer
745 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100746 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000747 *
748 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600749 *
750 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000751 **/
752void
753fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
754{
755 guint16 val_hw;
756 switch (endian) {
757 case G_BIG_ENDIAN:
758 val_hw = GUINT16_TO_BE(val_native);
759 break;
760 case G_LITTLE_ENDIAN:
761 val_hw = GUINT16_TO_LE(val_native);
762 break;
763 default:
764 g_assert_not_reached ();
765 }
766 memcpy (buf, &val_hw, sizeof(val_hw));
767}
768
769/**
770 * fu_common_write_uint32:
771 * @buf: A writable buffer
772 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100773 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000774 *
775 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600776 *
777 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000778 **/
779void
780fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
781{
782 guint32 val_hw;
783 switch (endian) {
784 case G_BIG_ENDIAN:
785 val_hw = GUINT32_TO_BE(val_native);
786 break;
787 case G_LITTLE_ENDIAN:
788 val_hw = GUINT32_TO_LE(val_native);
789 break;
790 default:
791 g_assert_not_reached ();
792 }
793 memcpy (buf, &val_hw, sizeof(val_hw));
794}
795
796/**
797 * fu_common_read_uint16:
798 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100799 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000800 *
801 * Read a value from a buffer using a specified endian.
802 *
803 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600804 *
805 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000806 **/
807guint16
808fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
809{
810 guint16 val_hw, val_native;
811 memcpy (&val_hw, buf, sizeof(val_hw));
812 switch (endian) {
813 case G_BIG_ENDIAN:
814 val_native = GUINT16_FROM_BE(val_hw);
815 break;
816 case G_LITTLE_ENDIAN:
817 val_native = GUINT16_FROM_LE(val_hw);
818 break;
819 default:
820 g_assert_not_reached ();
821 }
822 return val_native;
823}
824
825/**
826 * fu_common_read_uint32:
827 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100828 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000829 *
830 * Read a value from a buffer using a specified endian.
831 *
832 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600833 *
834 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000835 **/
836guint32
837fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
838{
839 guint32 val_hw, val_native;
840 memcpy (&val_hw, buf, sizeof(val_hw));
841 switch (endian) {
842 case G_BIG_ENDIAN:
843 val_native = GUINT32_FROM_BE(val_hw);
844 break;
845 case G_LITTLE_ENDIAN:
846 val_native = GUINT32_FROM_LE(val_hw);
847 break;
848 default:
849 g_assert_not_reached ();
850 }
851 return val_native;
852}
Richard Hughese82eef32018-05-20 10:41:26 +0100853
Richard Hughes73bf2332018-08-28 09:38:09 +0100854/**
855 * fu_common_strtoull:
856 * @str: A string, e.g. "0x1234"
857 *
858 * Converts a string value to an integer. Values are assumed base 10, unless
859 * prefixed with "0x" where they are parsed as base 16.
860 *
861 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600862 *
863 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100864 **/
865guint64
866fu_common_strtoull (const gchar *str)
867{
868 guint base = 10;
869 if (str == NULL)
870 return 0x0;
871 if (g_str_has_prefix (str, "0x")) {
872 str += 2;
873 base = 16;
874 }
875 return g_ascii_strtoull (str, NULL, base);
876}
877
Richard Hughesa574a752018-08-31 13:31:03 +0100878/**
879 * fu_common_strstrip:
880 * @str: A string, e.g. " test "
881 *
882 * Removes leading and trailing whitespace from a constant string.
883 *
884 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600885 *
886 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100887 **/
888gchar *
889fu_common_strstrip (const gchar *str)
890{
891 guint head = G_MAXUINT;
892 guint tail = 0;
893
894 g_return_val_if_fail (str != NULL, NULL);
895
896 /* find first non-space char */
897 for (guint i = 0; str[i] != '\0'; i++) {
898 if (str[i] != ' ') {
899 head = i;
900 break;
901 }
902 }
903 if (head == G_MAXUINT)
904 return g_strdup ("");
905
906 /* find last non-space char */
907 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500908 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100909 tail = i;
910 }
911 return g_strndup (str + head, tail - head + 1);
912}
913
Richard Hughese82eef32018-05-20 10:41:26 +0100914static const GError *
915fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
916{
917 for (guint j = 0; j < errors->len; j++) {
918 const GError *error = g_ptr_array_index (errors, j);
919 if (g_error_matches (error, FWUPD_ERROR, error_code))
920 return error;
921 }
922 return NULL;
923}
924
925static guint
926fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
927{
928 guint cnt = 0;
929 for (guint j = 0; j < errors->len; j++) {
930 const GError *error = g_ptr_array_index (errors, j);
931 if (g_error_matches (error, FWUPD_ERROR, error_code))
932 cnt++;
933 }
934 return cnt;
935}
936
937static gboolean
938fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
939{
940 for (guint j = 0; j < errors->len; j++) {
941 const GError *error = g_ptr_array_index (errors, j);
942 gboolean matches_any = FALSE;
943 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
944 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
945 matches_any = TRUE;
946 break;
947 }
948 }
949 if (!matches_any)
950 return FALSE;
951 }
952 return TRUE;
953}
954
955/**
956 * fu_common_error_array_get_best:
957 * @errors: (element-type GError): array of errors
958 *
959 * Finds the 'best' error to show the user from a array of errors, creating a
960 * completely bespoke error where required.
961 *
962 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600963 *
964 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100965 **/
966GError *
967fu_common_error_array_get_best (GPtrArray *errors)
968{
969 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
970 FWUPD_ERROR_VERSION_SAME,
971 FWUPD_ERROR_VERSION_NEWER,
972 FWUPD_ERROR_NOT_SUPPORTED,
973 FWUPD_ERROR_INTERNAL,
974 FWUPD_ERROR_NOT_FOUND,
975 FWUPD_ERROR_LAST };
976 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
977 FWUPD_ERROR_NOT_FOUND,
978 FWUPD_ERROR_NOT_SUPPORTED,
979 FWUPD_ERROR_LAST };
980 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
981 FWUPD_ERROR_VERSION_SAME,
982 FWUPD_ERROR_NOT_FOUND,
983 FWUPD_ERROR_NOT_SUPPORTED,
984 FWUPD_ERROR_LAST };
985
986 /* are all the errors either GUID-not-matched or version-same? */
987 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
988 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
989 return g_error_new (FWUPD_ERROR,
990 FWUPD_ERROR_NOTHING_TO_DO,
991 "All updatable firmware is already installed");
992 }
993
994 /* are all the errors either GUID-not-matched or version same or newer? */
995 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
996 fu_common_error_array_matches_any (errors, err_all_newer)) {
997 return g_error_new (FWUPD_ERROR,
998 FWUPD_ERROR_NOTHING_TO_DO,
999 "All updatable devices already have newer versions");
1000 }
1001
1002 /* get the most important single error */
1003 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
1004 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
1005 if (error_tmp != NULL)
1006 return g_error_copy (error_tmp);
1007 }
1008
1009 /* fall back to something */
1010 return g_error_new (FWUPD_ERROR,
1011 FWUPD_ERROR_NOT_FOUND,
1012 "No supported devices found");
1013}
Richard Hughes4be17d12018-05-30 20:36:29 +01001014
1015/**
1016 * fu_common_get_path:
1017 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1018 *
1019 * Gets a fwupd-specific system path. These can be overridden with various
1020 * environment variables, for instance %FWUPD_DATADIR.
1021 *
1022 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001023 *
1024 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001025 **/
1026gchar *
1027fu_common_get_path (FuPathKind path_kind)
1028{
1029 const gchar *tmp;
1030 g_autofree gchar *basedir = NULL;
1031
1032 switch (path_kind) {
1033 /* /var */
1034 case FU_PATH_KIND_LOCALSTATEDIR:
1035 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1036 if (tmp != NULL)
1037 return g_strdup (tmp);
1038 tmp = g_getenv ("SNAP_USER_DATA");
1039 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001040 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1041 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001042 /* /proc */
1043 case FU_PATH_KIND_PROCFS:
1044 tmp = g_getenv ("FWUPD_PROCFS");
1045 if (tmp != NULL)
1046 return g_strdup (tmp);
1047 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001048 /* /sys/firmware */
1049 case FU_PATH_KIND_SYSFSDIR_FW:
1050 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1051 if (tmp != NULL)
1052 return g_strdup (tmp);
1053 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001054 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001055 case FU_PATH_KIND_SYSFSDIR_TPM:
1056 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1057 if (tmp != NULL)
1058 return g_strdup (tmp);
1059 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001060 /* /sys/bus/platform/drivers */
1061 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1062 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1063 if (tmp != NULL)
1064 return g_strdup (tmp);
1065 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001066 /* /sys/kernel/security */
1067 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1068 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1069 if (tmp != NULL)
1070 return g_strdup (tmp);
1071 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001072 /* /sys/firmware/acpi/tables */
1073 case FU_PATH_KIND_ACPI_TABLES:
1074 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1075 if (tmp != NULL)
1076 return g_strdup (tmp);
1077 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001078 /* /etc */
1079 case FU_PATH_KIND_SYSCONFDIR:
1080 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1081 if (tmp != NULL)
1082 return g_strdup (tmp);
1083 tmp = g_getenv ("SNAP_USER_DATA");
1084 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001085 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1086 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001087 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1088 case FU_PATH_KIND_PLUGINDIR_PKG:
1089 tmp = g_getenv ("FWUPD_PLUGINDIR");
1090 if (tmp != NULL)
1091 return g_strdup (tmp);
1092 tmp = g_getenv ("SNAP");
1093 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001094 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1095 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001096 /* /usr/share/fwupd */
1097 case FU_PATH_KIND_DATADIR_PKG:
1098 tmp = g_getenv ("FWUPD_DATADIR");
1099 if (tmp != NULL)
1100 return g_strdup (tmp);
1101 tmp = g_getenv ("SNAP");
1102 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001103 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1104 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001105 /* /usr/libexec/fwupd/efi */
1106 case FU_PATH_KIND_EFIAPPDIR:
1107 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1108 if (tmp != NULL)
1109 return g_strdup (tmp);
1110#ifdef EFI_APP_LOCATION
1111 tmp = g_getenv ("SNAP");
1112 if (tmp != NULL)
1113 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1114 return g_strdup (EFI_APP_LOCATION);
1115#else
1116 return NULL;
1117#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001118 /* /etc/fwupd */
1119 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001120 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001121 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001122 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001123 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1124 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1125 /* /var/lib/fwupd */
1126 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001127 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001128 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001129 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001130 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1131 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1132 /* /var/cache/fwupd */
1133 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001134 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001135 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001136 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001137 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1138 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001139 case FU_PATH_KIND_OFFLINE_TRIGGER:
1140 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1141 if (tmp != NULL)
1142 return g_strdup (tmp);
1143 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001144 case FU_PATH_KIND_POLKIT_ACTIONS:
1145#ifdef POLKIT_ACTIONDIR
1146 return g_strdup (POLKIT_ACTIONDIR);
1147#else
1148 return NULL;
1149#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001150 /* this shouldn't happen */
1151 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001152 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001153 }
1154
1155 return NULL;
1156}
Richard Hughes83e56c12018-10-10 20:24:41 +01001157
1158/**
1159 * fu_common_string_replace:
1160 * @string: The #GString to operate on
1161 * @search: The text to search for
1162 * @replace: The text to use for substitutions
1163 *
1164 * Performs multiple search and replace operations on the given string.
1165 *
1166 * Returns: the number of replacements done, or 0 if @search is not found.
1167 *
1168 * Since: 1.2.0
1169 **/
1170guint
1171fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1172{
1173 gchar *tmp;
1174 guint count = 0;
1175 gsize search_idx = 0;
1176 gsize replace_len;
1177 gsize search_len;
1178
1179 g_return_val_if_fail (string != NULL, 0);
1180 g_return_val_if_fail (search != NULL, 0);
1181 g_return_val_if_fail (replace != NULL, 0);
1182
1183 /* nothing to do */
1184 if (string->len == 0)
1185 return 0;
1186
1187 search_len = strlen (search);
1188 replace_len = strlen (replace);
1189
1190 do {
1191 tmp = g_strstr_len (string->str + search_idx, -1, search);
1192 if (tmp == NULL)
1193 break;
1194
1195 /* advance the counter in case @replace contains @search */
1196 search_idx = (gsize) (tmp - string->str);
1197
1198 /* reallocate the string if required */
1199 if (search_len > replace_len) {
1200 g_string_erase (string,
1201 (gssize) search_idx,
1202 (gssize) (search_len - replace_len));
1203 memcpy (tmp, replace, replace_len);
1204 } else if (search_len < replace_len) {
1205 g_string_insert_len (string,
1206 (gssize) search_idx,
1207 replace,
1208 (gssize) (replace_len - search_len));
1209 /* we have to treat this specially as it could have
1210 * been reallocated when the insertion happened */
1211 memcpy (string->str + search_idx, replace, replace_len);
1212 } else {
1213 /* just memcmp in the new string */
1214 memcpy (tmp, replace, replace_len);
1215 }
1216 search_idx += replace_len;
1217 count++;
1218 } while (TRUE);
1219
1220 return count;
1221}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001222
Richard Hughesae96a1f2019-09-23 11:16:36 +01001223/**
1224 * fu_common_strwidth:
1225 * @text: The string to operate on
1226 *
1227 * Returns the width of the string in displayed characters on the console.
1228 *
1229 * Returns: width of text
1230 *
1231 * Since: 1.3.2
1232 **/
1233gsize
1234fu_common_strwidth (const gchar *text)
1235{
1236 const gchar *p = text;
1237 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001238
1239 g_return_val_if_fail (text != NULL, 0);
1240
Richard Hughesae96a1f2019-09-23 11:16:36 +01001241 while (*p) {
1242 gunichar c = g_utf8_get_char (p);
1243 if (g_unichar_iswide (c))
1244 width += 2;
1245 else if (!g_unichar_iszerowidth (c))
1246 width += 1;
1247 p = g_utf8_next_char (p);
1248 }
1249 return width;
1250}
1251
Mario Limonciello1a680f32019-11-25 19:44:53 -06001252/**
1253 * fu_common_string_append_kv:
1254 * @str: A #GString
1255 * @idt: The indent
1256 * @key: A string to append
1257 * @value: a string to append
1258 *
1259 * Appends a key and string value to a string
1260 *
1261 * Since: 1.2.4
1262 */
Richard Hughescea28de2019-08-09 11:16:40 +01001263void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001264fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001265{
Richard Hughes2506dbf2020-09-03 10:04:19 +01001266 const guint align = 24;
Richard Hughes847cae82019-08-27 11:22:23 +01001267 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001268
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001269 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001270
1271 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001272 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001273 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001274 for (gsize i = 0; i < idt; i++)
1275 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001276 if (key[0] != '\0') {
1277 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001278 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001279 } else {
1280 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001281 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001282 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001283 g_auto(GStrv) split = NULL;
1284 split = g_strsplit (value, "\n", -1);
1285 for (guint i = 0; split[i] != NULL; i++) {
1286 if (i == 0) {
1287 for (gsize j = keysz; j < align; j++)
1288 g_string_append (str, " ");
1289 } else {
1290 for (gsize j = 0; j < idt; j++)
1291 g_string_append (str, " ");
1292 }
1293 g_string_append (str, split[i]);
1294 g_string_append (str, "\n");
1295 }
1296 } else {
1297 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001298 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001299}
1300
Mario Limonciello1a680f32019-11-25 19:44:53 -06001301/**
1302 * fu_common_string_append_ku:
1303 * @str: A #GString
1304 * @idt: The indent
1305 * @key: A string to append
1306 * @value: guint64
1307 *
1308 * Appends a key and unsigned integer to a string
1309 *
1310 * Since: 1.2.4
1311 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001312void
1313fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1314{
1315 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1316 fu_common_string_append_kv (str, idt, key, tmp);
1317}
1318
Mario Limonciello1a680f32019-11-25 19:44:53 -06001319/**
1320 * fu_common_string_append_kx:
1321 * @str: A #GString
1322 * @idt: The indent
1323 * @key: A string to append
1324 * @value: guint64
1325 *
1326 * Appends a key and hex integer to a string
1327 *
1328 * Since: 1.2.4
1329 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001330void
1331fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1332{
1333 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1334 fu_common_string_append_kv (str, idt, key, tmp);
1335}
1336
Mario Limonciello1a680f32019-11-25 19:44:53 -06001337/**
1338 * fu_common_string_append_kb:
1339 * @str: A #GString
1340 * @idt: The indent
1341 * @key: A string to append
1342 * @value: Boolean
1343 *
1344 * Appends a key and boolean value to a string
1345 *
1346 * Since: 1.2.4
1347 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001348void
1349fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1350{
1351 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001352}
1353
Richard Hughese59cb9a2018-12-05 14:37:40 +00001354/**
Richard Hughes35481862019-01-06 12:01:58 +00001355 * fu_common_dump_full:
1356 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1357 * @title: prefix title, or %NULL
1358 * @data: buffer to print
1359 * @len: the size of @data
1360 * @columns: break new lines after this many bytes
1361 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1362 *
1363 * Dumps a raw buffer to the screen.
1364 *
1365 * Since: 1.2.4
1366 **/
1367void
1368fu_common_dump_full (const gchar *log_domain,
1369 const gchar *title,
1370 const guint8 *data,
1371 gsize len,
1372 guint columns,
1373 FuDumpFlags flags)
1374{
1375 g_autoptr(GString) str = g_string_new (NULL);
1376
1377 /* optional */
1378 if (title != NULL)
1379 g_string_append_printf (str, "%s:", title);
1380
1381 /* if more than can fit on one line then start afresh */
1382 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1383 g_string_append (str, "\n");
1384 } else {
1385 for (gsize i = str->len; i < 16; i++)
1386 g_string_append (str, " ");
1387 }
1388
1389 /* offset line */
1390 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1391 g_string_append (str, " │ ");
1392 for (gsize i = 0; i < columns; i++)
1393 g_string_append_printf (str, "%02x ", (guint) i);
1394 g_string_append (str, "\n───────┼");
1395 for (gsize i = 0; i < columns; i++)
1396 g_string_append (str, "───");
1397 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1398 }
1399
1400 /* print each row */
1401 for (gsize i = 0; i < len; i++) {
1402 g_string_append_printf (str, "%02x ", data[i]);
1403
1404 /* optionally print ASCII char */
1405 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1406 if (g_ascii_isprint (data[i]))
1407 g_string_append_printf (str, "[%c] ", data[i]);
1408 else
1409 g_string_append (str, "[?] ");
1410 }
1411
1412 /* new row required */
1413 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1414 g_string_append (str, "\n");
1415 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1416 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1417 }
1418 }
1419 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1420}
1421
1422/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001423 * fu_common_dump_raw:
1424 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1425 * @title: prefix title, or %NULL
1426 * @data: buffer to print
1427 * @len: the size of @data
1428 *
1429 * Dumps a raw buffer to the screen.
1430 *
1431 * Since: 1.2.2
1432 **/
1433void
1434fu_common_dump_raw (const gchar *log_domain,
1435 const gchar *title,
1436 const guint8 *data,
1437 gsize len)
1438{
Richard Hughes35481862019-01-06 12:01:58 +00001439 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1440 if (len > 64)
1441 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1442 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001443}
1444
1445/**
Mario Limonciello39602652019-04-29 21:08:58 -05001446 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001447 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1448 * @title: prefix title, or %NULL
1449 * @bytes: a #GBytes
1450 *
1451 * Dumps a byte buffer to the screen.
1452 *
1453 * Since: 1.2.2
1454 **/
1455void
1456fu_common_dump_bytes (const gchar *log_domain,
1457 const gchar *title,
1458 GBytes *bytes)
1459{
1460 gsize len = 0;
1461 const guint8 *data = g_bytes_get_data (bytes, &len);
1462 fu_common_dump_raw (log_domain, title, data, len);
1463}
Richard Hughesfc90f392019-01-15 21:21:16 +00001464
1465/**
1466 * fu_common_bytes_align:
1467 * @bytes: a #GBytes
1468 * @blksz: block size in bytes
1469 * @padval: the byte used to pad the byte buffer
1470 *
1471 * Aligns a block of memory to @blksize using the @padval value; if
1472 * the block is already aligned then the original @bytes is returned.
1473 *
1474 * Returns: (transfer full): a #GBytes, possibly @bytes
1475 *
1476 * Since: 1.2.4
1477 **/
1478GBytes *
1479fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1480{
1481 const guint8 *data;
1482 gsize sz;
1483
1484 g_return_val_if_fail (bytes != NULL, NULL);
1485 g_return_val_if_fail (blksz > 0, NULL);
1486
1487 /* pad */
1488 data = g_bytes_get_data (bytes, &sz);
1489 if (sz % blksz != 0) {
1490 gsize sz_align = ((sz / blksz) + 1) * blksz;
1491 guint8 *data_align = g_malloc (sz_align);
1492 memcpy (data_align, data, sz);
1493 memset (data_align + sz, padval, sz_align - sz);
1494 g_debug ("aligning 0x%x bytes to 0x%x",
1495 (guint) sz, (guint) sz_align);
1496 return g_bytes_new_take (data_align, sz_align);
1497 }
1498
1499 /* perfectly aligned */
1500 return g_bytes_ref (bytes);
1501}
Richard Hughes36999462019-03-19 20:23:29 +00001502
1503/**
1504 * fu_common_bytes_is_empty:
1505 * @bytes: a #GBytes
1506 *
1507 * Checks if a byte array are just empty (0xff) bytes.
1508 *
1509 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001510 *
1511 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001512 **/
1513gboolean
1514fu_common_bytes_is_empty (GBytes *bytes)
1515{
1516 gsize sz = 0;
1517 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1518 for (gsize i = 0; i < sz; i++) {
1519 if (buf[i] != 0xff)
1520 return FALSE;
1521 }
1522 return TRUE;
1523}
Richard Hughes2aad1042019-03-21 09:03:32 +00001524
1525/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001526 * fu_common_bytes_compare_raw:
1527 * @buf1: a buffer
1528 * @bufsz1: sizeof @buf1
1529 * @buf2: another buffer
1530 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001531 * @error: A #GError or %NULL
1532 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001533 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001534 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001535 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001536 *
1537 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001538 **/
1539gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001540fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1541 const guint8 *buf2, gsize bufsz2,
1542 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001543{
Richard Hughes38245ff2019-09-18 14:46:09 +01001544 g_return_val_if_fail (buf1 != NULL, FALSE);
1545 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001546 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1547
1548 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001549 if (bufsz1 != bufsz2) {
1550 g_set_error (error,
1551 G_IO_ERROR,
1552 G_IO_ERROR_INVALID_DATA,
1553 "got %" G_GSIZE_FORMAT " bytes, expected "
1554 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1555 return FALSE;
1556 }
1557
1558 /* check matches */
1559 for (guint i = 0x0; i < bufsz1; i++) {
1560 if (buf1[i] != buf2[i]) {
1561 g_set_error (error,
1562 G_IO_ERROR,
1563 G_IO_ERROR_INVALID_DATA,
1564 "got 0x%02x, expected 0x%02x @ 0x%04x",
1565 buf1[i], buf2[i], i);
1566 return FALSE;
1567 }
1568 }
1569
1570 /* success */
1571 return TRUE;
1572}
Richard Hughes484ee292019-03-22 16:10:50 +00001573
1574/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001575 * fu_common_bytes_compare:
1576 * @bytes1: a #GBytes
1577 * @bytes2: another #GBytes
1578 * @error: A #GError or %NULL
1579 *
1580 * Compares the buffers for equality.
1581 *
1582 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001583 *
1584 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001585 **/
1586gboolean
1587fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1588{
1589 const guint8 *buf1;
1590 const guint8 *buf2;
1591 gsize bufsz1;
1592 gsize bufsz2;
1593
1594 g_return_val_if_fail (bytes1 != NULL, FALSE);
1595 g_return_val_if_fail (bytes2 != NULL, FALSE);
1596 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1597
1598 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1599 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1600 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1601}
1602
1603/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001604 * fu_common_bytes_pad:
1605 * @bytes: a #GBytes
1606 * @sz: the desired size in bytes
1607 *
1608 * Pads a GBytes to a given @sz with `0xff`.
1609 *
1610 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001611 *
1612 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001613 **/
1614GBytes *
1615fu_common_bytes_pad (GBytes *bytes, gsize sz)
1616{
1617 gsize bytes_sz;
1618
1619 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1620
1621 /* pad */
1622 bytes_sz = g_bytes_get_size (bytes);
1623 if (bytes_sz < sz) {
1624 const guint8 *data = g_bytes_get_data (bytes, NULL);
1625 guint8 *data_new = g_malloc (sz);
1626 memcpy (data_new, data, bytes_sz);
1627 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1628 return g_bytes_new_take (data_new, sz);
1629 }
1630
1631 /* exactly right */
1632 return g_bytes_ref (bytes);
1633}
1634
1635/**
Richard Hughes05e33772020-12-08 18:37:02 +00001636 * fu_common_bytes_new_offset:
1637 * @bytes: a #GBytes
1638 * @offset: where subsection starts at
1639 * @length: length of subsection
1640 * @error: A #GError or %NULL
1641 *
1642 * Creates a #GBytes which is a subsection of another #GBytes.
1643 *
1644 * Return value: (transfer full): a #GBytes, or #NULL if range is invalid
1645 *
1646 * Since: 1.5.4
1647 **/
1648GBytes *
1649fu_common_bytes_new_offset (GBytes *bytes,
1650 gsize offset,
1651 gsize length,
1652 GError **error)
1653{
1654 g_return_val_if_fail (bytes != NULL, NULL);
1655
1656 /* sanity check */
1657 if (offset + length > g_bytes_get_size (bytes)) {
1658 g_set_error (error,
1659 G_IO_ERROR,
1660 G_IO_ERROR_INVALID_DATA,
1661 "cannot create bytes @0x%02x for 0x%02x "
1662 "as buffer only 0x%04x bytes in size",
1663 (guint) offset,
1664 (guint) length,
1665 (guint) g_bytes_get_size (bytes));
1666 return NULL;
1667 }
1668 return g_bytes_new_from_bytes (bytes, offset, length);
1669}
1670
1671/**
Richard Hughes484ee292019-03-22 16:10:50 +00001672 * fu_common_realpath:
1673 * @filename: a filename
1674 * @error: A #GError or %NULL
1675 *
1676 * Finds the canonicalized absolute filename for a path.
1677 *
1678 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001679 *
1680 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001681 **/
1682gchar *
1683fu_common_realpath (const gchar *filename, GError **error)
1684{
1685 char full_tmp[PATH_MAX];
1686
1687 g_return_val_if_fail (filename != NULL, NULL);
1688
Richard Hughes8694dee2019-11-22 09:16:34 +00001689#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001690 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001691#else
1692 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1693#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001694 g_set_error (error,
1695 G_IO_ERROR,
1696 G_IO_ERROR_INVALID_DATA,
1697 "cannot resolve path: %s",
1698 strerror (errno));
1699 return NULL;
1700 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001701 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1702 g_set_error (error,
1703 G_IO_ERROR,
1704 G_IO_ERROR_INVALID_DATA,
1705 "cannot find path: %s",
1706 full_tmp);
1707 return NULL;
1708 }
Richard Hughes484ee292019-03-22 16:10:50 +00001709 return g_strdup (full_tmp);
1710}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001711
1712/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001713 * fu_common_fnmatch:
1714 * @pattern: a glob pattern, e.g. `*foo*`
1715 * @str: a string to match against the pattern, e.g. `bazfoobar`
1716 *
1717 * Matches a string against a glob pattern.
1718 *
1719 * Return value: %TRUE if the string matched
1720 *
1721 * Since: 1.3.5
1722 **/
1723gboolean
1724fu_common_fnmatch (const gchar *pattern, const gchar *str)
1725{
1726 g_return_val_if_fail (pattern != NULL, FALSE);
1727 g_return_val_if_fail (str != NULL, FALSE);
1728#ifdef HAVE_FNMATCH_H
1729 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001730#elif _WIN32
1731 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1732 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1733 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001734#else
1735 return g_strcmp0 (pattern, str) == 0;
1736#endif
1737}
1738
Richard Hughesa84d7a72020-05-06 12:11:51 +01001739static gint
1740fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1741{
1742 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1743}
1744
1745/**
1746 * fu_common_filename_glob:
1747 * @directory: a directory path
1748 * @pattern: a glob pattern, e.g. `*foo*`
1749 * @error: A #GError or %NULL
1750 *
1751 * Returns all the filenames that match a specific glob pattern.
1752 * Any results are sorted. No matching files will set @error.
1753 *
1754 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1755 *
1756 * Since: 1.5.0
1757 **/
1758GPtrArray *
1759fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1760{
1761 const gchar *basename;
1762 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1763 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1764 if (dir == NULL)
1765 return NULL;
1766 while ((basename = g_dir_read_name (dir)) != NULL) {
1767 if (!fu_common_fnmatch (pattern, basename))
1768 continue;
1769 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1770 }
1771 if (files->len == 0) {
1772 g_set_error_literal (error,
1773 G_IO_ERROR,
1774 G_IO_ERROR_NOT_FOUND,
1775 "no files matched pattern");
1776 return NULL;
1777 }
1778 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1779 return g_steal_pointer (&files);
1780}
1781
Richard Hughes5c508de2019-11-22 09:57:34 +00001782/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001783 * fu_common_strnsplit:
1784 * @str: a string to split
1785 * @sz: size of @str
1786 * @delimiter: a string which specifies the places at which to split the string
1787 * @max_tokens: the maximum number of pieces to split @str into
1788 *
1789 * Splits a string into a maximum of @max_tokens pieces, using the given
1790 * delimiter. If @max_tokens is reached, the remainder of string is appended
1791 * to the last token.
1792 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001793 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001794 *
1795 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001796 **/
1797gchar **
1798fu_common_strnsplit (const gchar *str, gsize sz,
1799 const gchar *delimiter, gint max_tokens)
1800{
1801 if (str[sz - 1] != '\0') {
1802 g_autofree gchar *str2 = g_strndup (str, sz);
1803 return g_strsplit (str2, delimiter, max_tokens);
1804 }
1805 return g_strsplit (str, delimiter, max_tokens);
1806}
Richard Hughes5308ea42019-08-09 12:25:13 +01001807
1808/**
1809 * fu_memcpy_safe:
1810 * @dst: destination buffer
1811 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1812 * @dst_offset: offset in bytes into @dst to copy to
1813 * @src: source buffer
1814 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1815 * @src_offset: offset in bytes into @src to copy from
1816 * @n: number of bytes to copy from @src+@offset from
1817 * @error: A #GError or %NULL
1818 *
1819 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1820 * of both the destination and the source allows us to check for buffer overflow.
1821 *
1822 * Providing the buffer offsets also allows us to check reading past the end of
1823 * the source buffer. For this reason the caller should NEVER add an offset to
1824 * @src or @dst.
1825 *
1826 * You don't need to use this function in "obviously correct" cases, nor should
1827 * you use it when performance is a concern. Only us it when you're not sure if
1828 * malicious data from a device or firmware could cause memory corruption.
1829 *
1830 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001831 *
1832 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001833 **/
1834gboolean
1835fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1836 const guint8 *src, gsize src_sz, gsize src_offset,
1837 gsize n, GError **error)
1838{
1839 if (n == 0)
1840 return TRUE;
1841
1842 if (n > src_sz) {
1843 g_set_error (error,
1844 FWUPD_ERROR,
1845 FWUPD_ERROR_READ,
1846 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1847 (guint) n, (guint) src_sz);
1848 return FALSE;
1849 }
1850 if (n + src_offset > src_sz) {
1851 g_set_error (error,
1852 FWUPD_ERROR,
1853 FWUPD_ERROR_READ,
1854 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1855 (guint) n, (guint) src_offset, (guint) src_sz);
1856 return FALSE;
1857 }
1858 if (n > dst_sz) {
1859 g_set_error (error,
1860 FWUPD_ERROR,
1861 FWUPD_ERROR_WRITE,
1862 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1863 (guint) n, (guint) dst_sz);
1864 return FALSE;
1865 }
1866 if (n + dst_offset > dst_sz) {
1867 g_set_error (error,
1868 FWUPD_ERROR,
1869 FWUPD_ERROR_WRITE,
1870 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1871 (guint) n, (guint) dst_offset, (guint) dst_sz);
1872 return FALSE;
1873 }
1874
1875 /* phew! */
1876 memcpy (dst + dst_offset, src + src_offset, n);
1877 return TRUE;
1878}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001879
Richard Hughes80768f52019-10-22 07:19:14 +01001880/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001881 * fu_common_read_uint8_safe:
1882 * @buf: source buffer
1883 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1884 * @offset: offset in bytes into @buf to copy from
1885 * @value: (out) (allow-none): the parsed value
1886 * @error: A #GError or %NULL
1887 *
1888 * Read a value from a buffer in a safe way.
1889 *
1890 * You don't need to use this function in "obviously correct" cases, nor should
1891 * you use it when performance is a concern. Only us it when you're not sure if
1892 * malicious data from a device or firmware could cause memory corruption.
1893 *
1894 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001895 *
1896 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001897 **/
1898gboolean
1899fu_common_read_uint8_safe (const guint8 *buf,
1900 gsize bufsz,
1901 gsize offset,
1902 guint8 *value,
1903 GError **error)
1904{
1905 guint8 tmp;
1906 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1907 buf, bufsz, offset, /* src */
1908 sizeof(tmp), error))
1909 return FALSE;
1910 if (value != NULL)
1911 *value = tmp;
1912 return TRUE;
1913}
1914
1915/**
Richard Hughes80768f52019-10-22 07:19:14 +01001916 * fu_common_read_uint16_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_uint16_safe (const guint8 *buf,
1936 gsize bufsz,
1937 gsize offset,
1938 guint16 *value,
1939 FuEndianType endian,
1940 GError **error)
1941{
1942 guint8 dst[2] = { 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_uint16 (dst, endian);
1949 return TRUE;
1950}
1951
1952/**
1953 * fu_common_read_uint32_safe:
1954 * @buf: source buffer
1955 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1956 * @offset: offset in bytes into @buf to copy from
1957 * @value: (out) (allow-none): the parsed value
1958 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1959 * @error: A #GError or %NULL
1960 *
1961 * Read a value from a buffer using a specified endian in a safe way.
1962 *
1963 * You don't need to use this function in "obviously correct" cases, nor should
1964 * you use it when performance is a concern. Only us it when you're not sure if
1965 * malicious data from a device or firmware could cause memory corruption.
1966 *
1967 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001968 *
1969 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001970 **/
1971gboolean
1972fu_common_read_uint32_safe (const guint8 *buf,
1973 gsize bufsz,
1974 gsize offset,
1975 guint32 *value,
1976 FuEndianType endian,
1977 GError **error)
1978{
1979 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001980 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001981 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001982 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001983 return FALSE;
1984 if (value != NULL)
1985 *value = fu_common_read_uint32 (dst, endian);
1986 return TRUE;
1987}
1988
Mario Limonciello1a680f32019-11-25 19:44:53 -06001989/**
1990 * fu_byte_array_append_uint8:
1991 * @array: A #GByteArray
1992 * @data: #guint8
1993 *
1994 * Adds a 8 bit integer to a byte array
1995 *
1996 * Since: 1.3.1
1997 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001998void
1999fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
2000{
2001 g_byte_array_append (array, &data, sizeof(data));
2002}
2003
Mario Limonciello1a680f32019-11-25 19:44:53 -06002004/**
2005 * fu_byte_array_append_uint16:
2006 * @array: A #GByteArray
2007 * @data: #guint16
2008 * @endian: #FuEndianType
2009 *
2010 * Adds a 16 bit integer to a byte array
2011 *
2012 * Since: 1.3.1
2013 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002014void
2015fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
2016{
2017 guint8 buf[2];
2018 fu_common_write_uint16 (buf, data, endian);
2019 g_byte_array_append (array, buf, sizeof(buf));
2020}
2021
Mario Limonciello1a680f32019-11-25 19:44:53 -06002022/**
2023 * fu_byte_array_append_uint32:
2024 * @array: A #GByteArray
2025 * @data: #guint32
2026 * @endian: #FuEndianType
2027 *
2028 * Adds a 32 bit integer to a byte array
2029 *
2030 * Since: 1.3.1
2031 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002032void
2033fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2034{
2035 guint8 buf[4];
2036 fu_common_write_uint32 (buf, data, endian);
2037 g_byte_array_append (array, buf, sizeof(buf));
2038}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002039
2040/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002041 * fu_byte_array_set_size:
2042 * @array: a #GByteArray
2043 * @length: the new size of the GByteArray
2044 *
2045 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2046 *
2047 * Since: 1.5.0
2048 **/
2049void
2050fu_byte_array_set_size (GByteArray *array, guint length)
2051{
2052 guint oldlength = array->len;
2053 g_byte_array_set_size (array, length);
2054 if (length > oldlength)
2055 memset (array->data + oldlength, 0x0, length - oldlength);
2056}
2057
2058/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002059 * fu_common_kernel_locked_down:
2060 *
2061 * Determines if kernel lockdown in effect
2062 *
2063 * Since: 1.3.8
2064 **/
2065gboolean
2066fu_common_kernel_locked_down (void)
2067{
2068#ifndef _WIN32
2069 gsize len = 0;
2070 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2071 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2072 g_autofree gchar *data = NULL;
2073 g_auto(GStrv) options = NULL;
2074
2075 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2076 return FALSE;
2077 if (!g_file_get_contents (fname, &data, &len, NULL))
2078 return FALSE;
2079 if (len < 1)
2080 return FALSE;
2081 options = g_strsplit (data, " ", -1);
2082 for (guint i = 0; options[i] != NULL; i++) {
2083 if (g_strcmp0 (options[i], "[none]") == 0)
2084 return FALSE;
2085 }
2086 return TRUE;
2087#else
2088 return FALSE;
2089#endif
2090}
Richard Hughes9223c892020-05-09 20:32:08 +01002091
2092/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002093 * fu_common_cpuid:
2094 * @leaf: The CPUID level, now called the 'leaf' by Intel
2095 * @eax: (out) (nullable): EAX register
2096 * @ebx: (out) (nullable): EBX register
2097 * @ecx: (out) (nullable): ECX register
2098 * @edx: (out) (nullable): EDX register
2099 * @error: A #GError or NULL
2100 *
2101 * Calls CPUID and returns the registers for the given leaf.
2102 *
2103 * Return value: %TRUE if the registers are set.
2104 *
2105 * Since: 1.5.0
2106 **/
2107gboolean
2108fu_common_cpuid (guint32 leaf,
2109 guint32 *eax,
2110 guint32 *ebx,
2111 guint32 *ecx,
2112 guint32 *edx,
2113 GError **error)
2114{
2115#ifdef HAVE_CPUID_H
2116 guint eax_tmp = 0;
2117 guint ebx_tmp = 0;
2118 guint ecx_tmp = 0;
2119 guint edx_tmp = 0;
2120
2121 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002122 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002123 if (eax != NULL)
2124 *eax = eax_tmp;
2125 if (ebx != NULL)
2126 *ebx = ebx_tmp;
2127 if (ecx != NULL)
2128 *ecx = ecx_tmp;
2129 if (edx != NULL)
2130 *edx = edx_tmp;
2131 return TRUE;
2132#else
2133 g_set_error_literal (error,
2134 G_IO_ERROR,
2135 G_IO_ERROR_NOT_SUPPORTED,
2136 "no <cpuid.h> support");
2137 return FALSE;
2138#endif
2139}
2140
2141/**
Richard Hughes9223c892020-05-09 20:32:08 +01002142 * fu_common_is_cpu_intel:
2143 *
2144 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2145 *
2146 * Return value: %TRUE if the vendor was Intel.
2147 *
2148 * Since: 1.5.0
2149 **/
2150gboolean
2151fu_common_is_cpu_intel (void)
2152{
Richard Hughes9223c892020-05-09 20:32:08 +01002153 guint ebx = 0;
2154 guint ecx = 0;
2155 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002156
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002157 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2158 return FALSE;
2159#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002160 if (ebx == signature_INTEL_ebx &&
2161 edx == signature_INTEL_edx &&
2162 ecx == signature_INTEL_ecx) {
2163 return TRUE;
2164 }
Richard Hughesbd444322020-05-21 12:05:03 +01002165#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002166 return FALSE;
2167}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002168
Richard Hughes36111472020-08-12 15:04:24 +01002169/**
2170 * fu_common_is_live_media:
2171 *
2172 * Checks if the user is running from a live media using various heuristics.
2173 *
2174 * Returns: %TRUE if live
2175 *
2176 * Since: 1.4.6
2177 **/
2178gboolean
2179fu_common_is_live_media (void)
2180{
2181 gsize bufsz = 0;
2182 g_autofree gchar *buf = NULL;
2183 g_auto(GStrv) tokens = NULL;
2184 const gchar *args[] = {
2185 "rd.live.image",
2186 "boot=live",
2187 NULL, /* last entry */
2188 };
2189 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2190 return TRUE;
2191 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2192 return FALSE;
2193 if (bufsz == 0)
2194 return FALSE;
2195 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2196 for (guint i = 0; args[i] != NULL; i++) {
2197 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2198 return TRUE;
2199 }
2200 return FALSE;
2201}
2202
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002203static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002204fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002205{
2206 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002207 const gchar *obj;
2208 g_autoptr(GVariant) output = NULL;
2209 g_autoptr(GDBusProxy) proxy = NULL;
2210 g_autoptr(GPtrArray) devices = NULL;
2211 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002212 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002213
Richard Hughes43417b22020-10-30 14:46:16 +00002214 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2215 if (connection == NULL) {
2216 g_prefix_error (error, "failed to get system bus: ");
2217 return NULL;
2218 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002219 proxy = g_dbus_proxy_new_sync (connection,
2220 G_DBUS_PROXY_FLAGS_NONE, NULL,
2221 UDISKS_DBUS_SERVICE,
2222 UDISKS_DBUS_PATH,
2223 UDISKS_DBUS_MANAGER_INTERFACE,
2224 NULL, error);
2225 if (proxy == NULL) {
2226 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2227 return NULL;
2228 }
2229 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002230 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002231 "GetBlockDevices",
2232 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002233 G_DBUS_CALL_FLAGS_NONE,
2234 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002235 if (output == NULL) {
2236 if (error != NULL)
2237 g_dbus_error_strip_remote_error (*error);
2238 g_prefix_error (error, "failed to call %s.%s(): ",
2239 UDISKS_DBUS_SERVICE,
2240 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002241 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002242 }
Richard Hughes43417b22020-10-30 14:46:16 +00002243 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002244 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002245 while (g_variant_iter_next (iter, "&o", &obj)) {
2246 g_autoptr(GDBusProxy) proxy_blk = NULL;
2247 proxy_blk = g_dbus_proxy_new_sync (connection,
2248 G_DBUS_PROXY_FLAGS_NONE, NULL,
2249 UDISKS_DBUS_SERVICE,
2250 obj,
2251 UDISKS_DBUS_INTERFACE_BLOCK,
2252 NULL, error);
2253 if (proxy_blk == NULL) {
2254 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2255 return NULL;
2256 }
2257 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2258 }
2259
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002260
2261 return g_steal_pointer (&devices);
2262}
2263
2264/**
2265 * fu_common_get_volumes_by_kind:
2266 * @kind: A volume kind, typically a GUID
2267 * @error: A #GError or NULL
2268 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002269 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002270 *
2271 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2272 *
2273 * Since: 1.4.6
2274 **/
2275GPtrArray *
2276fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2277{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002278 g_autoptr(GPtrArray) devices = NULL;
2279 g_autoptr(GPtrArray) volumes = NULL;
2280
Richard Hughes43417b22020-10-30 14:46:16 +00002281 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002282 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002283 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002284 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2285 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002286 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002287 const gchar *type_str;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002288 g_autoptr(FuVolume) vol = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002289 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002290 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002291 g_autoptr(GVariant) val = NULL;
2292
Richard Hughes43417b22020-10-30 14:46:16 +00002293 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002294 G_DBUS_PROXY_FLAGS_NONE, NULL,
2295 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002296 g_dbus_proxy_get_object_path (proxy_blk),
2297 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002298 NULL, error);
2299 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002300 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2301 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002302 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002303 }
2304 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2305 if (val == NULL)
2306 continue;
2307
Richard Hughesdb344d52020-09-09 19:42:27 +01002308 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002309 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2310 G_DBUS_PROXY_FLAGS_NONE, NULL,
2311 UDISKS_DBUS_SERVICE,
2312 g_dbus_proxy_get_object_path (proxy_blk),
2313 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2314 NULL, error);
2315 if (proxy_fs == NULL) {
2316 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2317 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002318 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002319 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002320 vol = g_object_new (FU_TYPE_VOLUME,
2321 "proxy-block", proxy_blk,
2322 "proxy-filesystem", proxy_fs,
2323 NULL);
2324 g_debug ("device %s, type: %s, internal: %d, fs: %s",
2325 g_dbus_proxy_get_object_path (proxy_blk), type_str,
2326 fu_volume_is_internal (vol),
2327 fu_volume_get_id_type (vol));
2328 if (g_strcmp0 (type_str, kind) != 0)
2329 continue;
2330 g_ptr_array_add (volumes, g_steal_pointer (&vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002331 }
2332 if (volumes->len == 0) {
2333 g_set_error (error,
2334 G_IO_ERROR,
2335 G_IO_ERROR_NOT_FOUND,
2336 "no volumes of type %s", kind);
2337 return NULL;
2338 }
2339 return g_steal_pointer (&volumes);
2340}
2341
2342/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002343 * fu_common_get_volume_by_device:
2344 * @device: A device string, typcically starting with `/dev/`
2345 * @error: A #GError or NULL
2346 *
2347 * Finds the first volume from the specified device.
2348 *
2349 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2350 *
2351 * Since: 1.5.1
2352 **/
2353FuVolume *
2354fu_common_get_volume_by_device (const gchar *device, GError **error)
2355{
2356 g_autoptr(GPtrArray) devices = NULL;
2357
2358 /* find matching block device */
2359 devices = fu_common_get_block_devices (error);
2360 if (devices == NULL)
2361 return NULL;
2362 for (guint i = 0; i < devices->len; i++) {
2363 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2364 g_autoptr(GVariant) val = NULL;
2365 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2366 if (val == NULL)
2367 continue;
2368 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2369 return g_object_new (FU_TYPE_VOLUME,
2370 "proxy-block", proxy_blk,
2371 NULL);
2372 }
2373 }
2374
2375 /* failed */
2376 g_set_error (error,
2377 G_IO_ERROR,
2378 G_IO_ERROR_NOT_FOUND,
2379 "no volumes for device %s",
2380 device);
2381 return NULL;
2382}
2383
2384/**
2385 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002386 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002387 * @error: A #GError or NULL
2388 *
2389 * Finds the first volume from the specified device.
2390 *
2391 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2392 *
2393 * Since: 1.5.1
2394 **/
2395FuVolume *
2396fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2397{
2398 g_autoptr(GPtrArray) devices = NULL;
2399
2400 /* find matching block device */
2401 devices = fu_common_get_block_devices (error);
2402 if (devices == NULL)
2403 return NULL;
2404 for (guint i = 0; i < devices->len; i++) {
2405 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2406 g_autoptr(GVariant) val = NULL;
2407 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2408 if (val == NULL)
2409 continue;
2410 if (devnum == g_variant_get_uint64 (val)) {
2411 return g_object_new (FU_TYPE_VOLUME,
2412 "proxy-block", proxy_blk,
2413 NULL);
2414 }
2415 }
2416
2417 /* failed */
2418 g_set_error (error,
2419 G_IO_ERROR,
2420 G_IO_ERROR_NOT_FOUND,
2421 "no volumes for devnum %u",
2422 devnum);
2423 return NULL;
2424}
2425
2426/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002427 * fu_common_get_esp_default:
2428 * @error: A #GError or NULL
2429 *
2430 * Gets the platform default ESP
2431 *
2432 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2433 *
2434 * Since: 1.4.6
2435 **/
2436FuVolume *
2437fu_common_get_esp_default (GError **error)
2438{
2439 const gchar *path_tmp;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002440 gboolean has_internal = FALSE;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002441 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2442 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
Mario Limonciello56d816a2020-11-11 16:59:30 -06002443 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002444 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002445 g_autoptr(GError) error_local = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002446
2447 /* for the test suite use local directory for ESP */
2448 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2449 if (path_tmp != NULL)
2450 return fu_volume_new_from_mount_path (path_tmp);
2451
Mario Limonciello56d816a2020-11-11 16:59:30 -06002452 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2453 if (volumes == NULL) {
2454 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2455 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2456 if (volumes == NULL) {
2457 g_prefix_error (error, "%s: ", error_local->message);
2458 return NULL;
2459 }
2460 }
Richard Hughes9d20bf92020-12-14 09:36:46 +00002461
2462 /* are there _any_ internal vfat partitions?
2463 * remember HintSystem is just that -- a hint! */
2464 for (guint i = 0; i < volumes->len; i++) {
2465 FuVolume *vol = g_ptr_array_index (volumes, i);
2466 g_autofree gchar *type = fu_volume_get_id_type (vol);
2467 if (g_strcmp0 (type, "vfat") == 0 &&
2468 fu_volume_is_internal (vol)) {
2469 has_internal = TRUE;
2470 break;
2471 }
2472 }
2473
2474 /* filter to vfat partitions */
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002475 for (guint i = 0; i < volumes->len; i++) {
2476 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello56d816a2020-11-11 16:59:30 -06002477 g_autofree gchar *type = fu_volume_get_id_type (vol);
2478 if (type == NULL)
2479 continue;
Richard Hughes9d20bf92020-12-14 09:36:46 +00002480 if (has_internal && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002481 continue;
2482 if (g_strcmp0 (type, "vfat") == 0)
2483 g_ptr_array_add (volumes_vfat, vol);
2484 }
2485 if (volumes_vfat->len == 0) {
2486 g_set_error (error,
2487 G_IO_ERROR,
2488 G_IO_ERROR_INVALID_FILENAME,
2489 "No ESP found");
2490 return NULL;
2491 }
2492 for (guint i = 0; i < volumes_vfat->len; i++) {
2493 FuVolume *vol = g_ptr_array_index (volumes_vfat, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002494 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2495 }
2496 if (volumes_mtab->len == 1) {
2497 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2498 return g_object_ref (vol);
2499 }
2500 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2501 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2502 return g_object_ref (vol);
2503 }
2504 g_set_error (error,
2505 G_IO_ERROR,
2506 G_IO_ERROR_INVALID_FILENAME,
2507 "More than one available ESP");
2508 return NULL;
2509}
2510
2511/**
2512 * fu_common_get_esp_for_path:
2513 * @esp_path: A path to the ESP
2514 * @error: A #GError or NULL
2515 *
2516 * Gets the platform ESP using a UNIX or UDisks path
2517 *
2518 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2519 *
2520 * Since: 1.4.6
2521 **/
2522FuVolume *
2523fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2524{
2525 g_autofree gchar *basename = g_path_get_basename (esp_path);
2526 g_autoptr(GPtrArray) volumes = NULL;
2527
2528 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2529 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002530 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002531 for (guint i = 0; i < volumes->len; i++) {
2532 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002533 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002534 if (g_strcmp0 (basename, vol_basename) == 0)
2535 return g_object_ref (vol);
2536 }
2537 g_set_error (error,
2538 G_IO_ERROR,
2539 G_IO_ERROR_INVALID_FILENAME,
2540 "No ESP with path %s",
2541 esp_path);
2542 return NULL;
2543}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002544
2545/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002546 * fu_common_crc8:
2547 * @buf: memory buffer
2548 * @bufsz: sizeof buf
2549 *
2550 * Returns the cyclic redundancy check value for the given memory buffer.
2551 *
2552 * Returns: CRC value
2553 *
2554 * Since: 1.5.0
2555 **/
2556guint8
2557fu_common_crc8 (const guint8 *buf, gsize bufsz)
2558{
2559 guint32 crc = 0;
2560 for (gsize j = bufsz; j > 0; j--) {
2561 crc ^= (*(buf++) << 8);
2562 for (guint32 i = 8; i; i--) {
2563 if (crc & 0x8000)
2564 crc ^= (0x1070 << 3);
2565 crc <<= 1;
2566 }
2567 }
2568 return ~((guint8) (crc >> 8));
2569}
2570
2571/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002572 * fu_common_crc16:
2573 * @buf: memory buffer
2574 * @bufsz: sizeof buf
2575 *
2576 * Returns the cyclic redundancy check value for the given memory buffer.
2577 *
2578 * Returns: CRC value
2579 *
2580 * Since: 1.5.0
2581 **/
2582guint16
2583fu_common_crc16 (const guint8 *buf, gsize bufsz)
2584{
2585 guint16 crc = 0xffff;
2586 for (gsize len = bufsz; len > 0; len--) {
2587 crc = (guint16) (crc ^ (*buf++));
2588 for (guint8 i = 0; i < 8; i++) {
2589 if (crc & 0x1) {
2590 crc = (crc >> 1) ^ 0xa001;
2591 } else {
2592 crc >>= 1;
2593 }
2594 }
2595 }
2596 return ~crc;
2597}
2598
2599/**
2600 * fu_common_crc32_full:
2601 * @buf: memory buffer
2602 * @bufsz: sizeof buf
2603 * @crc: initial CRC value, typically 0xFFFFFFFF
2604 * @polynomial: CRC polynomial, typically 0xEDB88320
2605 *
2606 * Returns the cyclic redundancy check value for the given memory buffer.
2607 *
2608 * Returns: CRC value
2609 *
2610 * Since: 1.5.0
2611 **/
2612guint32
2613fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2614{
2615 for (guint32 idx = 0; idx < bufsz; idx++) {
2616 guint8 data = *buf++;
2617 crc = crc ^ data;
2618 for (guint32 bit = 0; bit < 8; bit++) {
2619 guint32 mask = -(crc & 1);
2620 crc = (crc >> 1) ^ (polynomial & mask);
2621 }
2622 }
2623 return ~crc;
2624}
2625
2626/**
2627 * fu_common_crc32:
2628 * @buf: memory buffer
2629 * @bufsz: sizeof buf
2630 *
2631 * Returns the cyclic redundancy check value for the given memory buffer.
2632 *
2633 * Returns: CRC value
2634 *
2635 * Since: 1.5.0
2636 **/
2637guint32
2638fu_common_crc32 (const guint8 *buf, gsize bufsz)
2639{
2640 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2641}