blob: 4a5e1a81ebf0b1852515f3dab8b82109fe69aeea [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 Hughes484ee292019-03-22 16:10:50 +00001636 * fu_common_realpath:
1637 * @filename: a filename
1638 * @error: A #GError or %NULL
1639 *
1640 * Finds the canonicalized absolute filename for a path.
1641 *
1642 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001643 *
1644 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001645 **/
1646gchar *
1647fu_common_realpath (const gchar *filename, GError **error)
1648{
1649 char full_tmp[PATH_MAX];
1650
1651 g_return_val_if_fail (filename != NULL, NULL);
1652
Richard Hughes8694dee2019-11-22 09:16:34 +00001653#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001654 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001655#else
1656 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1657#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001658 g_set_error (error,
1659 G_IO_ERROR,
1660 G_IO_ERROR_INVALID_DATA,
1661 "cannot resolve path: %s",
1662 strerror (errno));
1663 return NULL;
1664 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001665 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1666 g_set_error (error,
1667 G_IO_ERROR,
1668 G_IO_ERROR_INVALID_DATA,
1669 "cannot find path: %s",
1670 full_tmp);
1671 return NULL;
1672 }
Richard Hughes484ee292019-03-22 16:10:50 +00001673 return g_strdup (full_tmp);
1674}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001675
1676/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001677 * fu_common_fnmatch:
1678 * @pattern: a glob pattern, e.g. `*foo*`
1679 * @str: a string to match against the pattern, e.g. `bazfoobar`
1680 *
1681 * Matches a string against a glob pattern.
1682 *
1683 * Return value: %TRUE if the string matched
1684 *
1685 * Since: 1.3.5
1686 **/
1687gboolean
1688fu_common_fnmatch (const gchar *pattern, const gchar *str)
1689{
1690 g_return_val_if_fail (pattern != NULL, FALSE);
1691 g_return_val_if_fail (str != NULL, FALSE);
1692#ifdef HAVE_FNMATCH_H
1693 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001694#elif _WIN32
1695 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1696 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1697 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001698#else
1699 return g_strcmp0 (pattern, str) == 0;
1700#endif
1701}
1702
Richard Hughesa84d7a72020-05-06 12:11:51 +01001703static gint
1704fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1705{
1706 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1707}
1708
1709/**
1710 * fu_common_filename_glob:
1711 * @directory: a directory path
1712 * @pattern: a glob pattern, e.g. `*foo*`
1713 * @error: A #GError or %NULL
1714 *
1715 * Returns all the filenames that match a specific glob pattern.
1716 * Any results are sorted. No matching files will set @error.
1717 *
1718 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1719 *
1720 * Since: 1.5.0
1721 **/
1722GPtrArray *
1723fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1724{
1725 const gchar *basename;
1726 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1727 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1728 if (dir == NULL)
1729 return NULL;
1730 while ((basename = g_dir_read_name (dir)) != NULL) {
1731 if (!fu_common_fnmatch (pattern, basename))
1732 continue;
1733 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1734 }
1735 if (files->len == 0) {
1736 g_set_error_literal (error,
1737 G_IO_ERROR,
1738 G_IO_ERROR_NOT_FOUND,
1739 "no files matched pattern");
1740 return NULL;
1741 }
1742 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1743 return g_steal_pointer (&files);
1744}
1745
Richard Hughes5c508de2019-11-22 09:57:34 +00001746/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001747 * fu_common_strnsplit:
1748 * @str: a string to split
1749 * @sz: size of @str
1750 * @delimiter: a string which specifies the places at which to split the string
1751 * @max_tokens: the maximum number of pieces to split @str into
1752 *
1753 * Splits a string into a maximum of @max_tokens pieces, using the given
1754 * delimiter. If @max_tokens is reached, the remainder of string is appended
1755 * to the last token.
1756 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001757 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001758 *
1759 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001760 **/
1761gchar **
1762fu_common_strnsplit (const gchar *str, gsize sz,
1763 const gchar *delimiter, gint max_tokens)
1764{
1765 if (str[sz - 1] != '\0') {
1766 g_autofree gchar *str2 = g_strndup (str, sz);
1767 return g_strsplit (str2, delimiter, max_tokens);
1768 }
1769 return g_strsplit (str, delimiter, max_tokens);
1770}
Richard Hughes5308ea42019-08-09 12:25:13 +01001771
1772/**
1773 * fu_memcpy_safe:
1774 * @dst: destination buffer
1775 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1776 * @dst_offset: offset in bytes into @dst to copy to
1777 * @src: source buffer
1778 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1779 * @src_offset: offset in bytes into @src to copy from
1780 * @n: number of bytes to copy from @src+@offset from
1781 * @error: A #GError or %NULL
1782 *
1783 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1784 * of both the destination and the source allows us to check for buffer overflow.
1785 *
1786 * Providing the buffer offsets also allows us to check reading past the end of
1787 * the source buffer. For this reason the caller should NEVER add an offset to
1788 * @src or @dst.
1789 *
1790 * You don't need to use this function in "obviously correct" cases, nor should
1791 * you use it when performance is a concern. Only us it when you're not sure if
1792 * malicious data from a device or firmware could cause memory corruption.
1793 *
1794 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001795 *
1796 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001797 **/
1798gboolean
1799fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1800 const guint8 *src, gsize src_sz, gsize src_offset,
1801 gsize n, GError **error)
1802{
1803 if (n == 0)
1804 return TRUE;
1805
1806 if (n > src_sz) {
1807 g_set_error (error,
1808 FWUPD_ERROR,
1809 FWUPD_ERROR_READ,
1810 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1811 (guint) n, (guint) src_sz);
1812 return FALSE;
1813 }
1814 if (n + src_offset > src_sz) {
1815 g_set_error (error,
1816 FWUPD_ERROR,
1817 FWUPD_ERROR_READ,
1818 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1819 (guint) n, (guint) src_offset, (guint) src_sz);
1820 return FALSE;
1821 }
1822 if (n > dst_sz) {
1823 g_set_error (error,
1824 FWUPD_ERROR,
1825 FWUPD_ERROR_WRITE,
1826 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1827 (guint) n, (guint) dst_sz);
1828 return FALSE;
1829 }
1830 if (n + dst_offset > dst_sz) {
1831 g_set_error (error,
1832 FWUPD_ERROR,
1833 FWUPD_ERROR_WRITE,
1834 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1835 (guint) n, (guint) dst_offset, (guint) dst_sz);
1836 return FALSE;
1837 }
1838
1839 /* phew! */
1840 memcpy (dst + dst_offset, src + src_offset, n);
1841 return TRUE;
1842}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001843
Richard Hughes80768f52019-10-22 07:19:14 +01001844/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001845 * fu_common_read_uint8_safe:
1846 * @buf: source buffer
1847 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1848 * @offset: offset in bytes into @buf to copy from
1849 * @value: (out) (allow-none): the parsed value
1850 * @error: A #GError or %NULL
1851 *
1852 * Read a value from a buffer in a safe way.
1853 *
1854 * You don't need to use this function in "obviously correct" cases, nor should
1855 * you use it when performance is a concern. Only us it when you're not sure if
1856 * malicious data from a device or firmware could cause memory corruption.
1857 *
1858 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001859 *
1860 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001861 **/
1862gboolean
1863fu_common_read_uint8_safe (const guint8 *buf,
1864 gsize bufsz,
1865 gsize offset,
1866 guint8 *value,
1867 GError **error)
1868{
1869 guint8 tmp;
1870 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1871 buf, bufsz, offset, /* src */
1872 sizeof(tmp), error))
1873 return FALSE;
1874 if (value != NULL)
1875 *value = tmp;
1876 return TRUE;
1877}
1878
1879/**
Richard Hughes80768f52019-10-22 07:19:14 +01001880 * fu_common_read_uint16_safe:
1881 * @buf: source buffer
1882 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1883 * @offset: offset in bytes into @buf to copy from
1884 * @value: (out) (allow-none): the parsed value
1885 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1886 * @error: A #GError or %NULL
1887 *
1888 * Read a value from a buffer using a specified endian 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 Hughes80768f52019-10-22 07:19:14 +01001897 **/
1898gboolean
1899fu_common_read_uint16_safe (const guint8 *buf,
1900 gsize bufsz,
1901 gsize offset,
1902 guint16 *value,
1903 FuEndianType endian,
1904 GError **error)
1905{
1906 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001907 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001908 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001909 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001910 return FALSE;
1911 if (value != NULL)
1912 *value = fu_common_read_uint16 (dst, endian);
1913 return TRUE;
1914}
1915
1916/**
1917 * fu_common_read_uint32_safe:
1918 * @buf: source buffer
1919 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1920 * @offset: offset in bytes into @buf to copy from
1921 * @value: (out) (allow-none): the parsed value
1922 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1923 * @error: A #GError or %NULL
1924 *
1925 * Read a value from a buffer using a specified endian in a safe way.
1926 *
1927 * You don't need to use this function in "obviously correct" cases, nor should
1928 * you use it when performance is a concern. Only us it when you're not sure if
1929 * malicious data from a device or firmware could cause memory corruption.
1930 *
1931 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001932 *
1933 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001934 **/
1935gboolean
1936fu_common_read_uint32_safe (const guint8 *buf,
1937 gsize bufsz,
1938 gsize offset,
1939 guint32 *value,
1940 FuEndianType endian,
1941 GError **error)
1942{
1943 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001944 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001945 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001946 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001947 return FALSE;
1948 if (value != NULL)
1949 *value = fu_common_read_uint32 (dst, endian);
1950 return TRUE;
1951}
1952
Mario Limonciello1a680f32019-11-25 19:44:53 -06001953/**
1954 * fu_byte_array_append_uint8:
1955 * @array: A #GByteArray
1956 * @data: #guint8
1957 *
1958 * Adds a 8 bit integer to a byte array
1959 *
1960 * Since: 1.3.1
1961 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001962void
1963fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1964{
1965 g_byte_array_append (array, &data, sizeof(data));
1966}
1967
Mario Limonciello1a680f32019-11-25 19:44:53 -06001968/**
1969 * fu_byte_array_append_uint16:
1970 * @array: A #GByteArray
1971 * @data: #guint16
1972 * @endian: #FuEndianType
1973 *
1974 * Adds a 16 bit integer to a byte array
1975 *
1976 * Since: 1.3.1
1977 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001978void
1979fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1980{
1981 guint8 buf[2];
1982 fu_common_write_uint16 (buf, data, endian);
1983 g_byte_array_append (array, buf, sizeof(buf));
1984}
1985
Mario Limonciello1a680f32019-11-25 19:44:53 -06001986/**
1987 * fu_byte_array_append_uint32:
1988 * @array: A #GByteArray
1989 * @data: #guint32
1990 * @endian: #FuEndianType
1991 *
1992 * Adds a 32 bit integer to a byte array
1993 *
1994 * Since: 1.3.1
1995 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001996void
1997fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1998{
1999 guint8 buf[4];
2000 fu_common_write_uint32 (buf, data, endian);
2001 g_byte_array_append (array, buf, sizeof(buf));
2002}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002003
2004/**
Richard Hughesa2a8f8e2020-10-20 09:27:26 +01002005 * fu_byte_array_set_size:
2006 * @array: a #GByteArray
2007 * @length: the new size of the GByteArray
2008 *
2009 * Sets the size of the GByteArray, expanding it with NULs if necessary.
2010 *
2011 * Since: 1.5.0
2012 **/
2013void
2014fu_byte_array_set_size (GByteArray *array, guint length)
2015{
2016 guint oldlength = array->len;
2017 g_byte_array_set_size (array, length);
2018 if (length > oldlength)
2019 memset (array->data + oldlength, 0x0, length - oldlength);
2020}
2021
2022/**
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002023 * fu_common_kernel_locked_down:
2024 *
2025 * Determines if kernel lockdown in effect
2026 *
2027 * Since: 1.3.8
2028 **/
2029gboolean
2030fu_common_kernel_locked_down (void)
2031{
2032#ifndef _WIN32
2033 gsize len = 0;
2034 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2035 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2036 g_autofree gchar *data = NULL;
2037 g_auto(GStrv) options = NULL;
2038
2039 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2040 return FALSE;
2041 if (!g_file_get_contents (fname, &data, &len, NULL))
2042 return FALSE;
2043 if (len < 1)
2044 return FALSE;
2045 options = g_strsplit (data, " ", -1);
2046 for (guint i = 0; options[i] != NULL; i++) {
2047 if (g_strcmp0 (options[i], "[none]") == 0)
2048 return FALSE;
2049 }
2050 return TRUE;
2051#else
2052 return FALSE;
2053#endif
2054}
Richard Hughes9223c892020-05-09 20:32:08 +01002055
2056/**
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002057 * fu_common_cpuid:
2058 * @leaf: The CPUID level, now called the 'leaf' by Intel
2059 * @eax: (out) (nullable): EAX register
2060 * @ebx: (out) (nullable): EBX register
2061 * @ecx: (out) (nullable): ECX register
2062 * @edx: (out) (nullable): EDX register
2063 * @error: A #GError or NULL
2064 *
2065 * Calls CPUID and returns the registers for the given leaf.
2066 *
2067 * Return value: %TRUE if the registers are set.
2068 *
2069 * Since: 1.5.0
2070 **/
2071gboolean
2072fu_common_cpuid (guint32 leaf,
2073 guint32 *eax,
2074 guint32 *ebx,
2075 guint32 *ecx,
2076 guint32 *edx,
2077 GError **error)
2078{
2079#ifdef HAVE_CPUID_H
2080 guint eax_tmp = 0;
2081 guint ebx_tmp = 0;
2082 guint ecx_tmp = 0;
2083 guint edx_tmp = 0;
2084
2085 /* get vendor */
Richard Hughes7c4a64b2020-08-23 21:20:58 +01002086 __get_cpuid_count (leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp);
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002087 if (eax != NULL)
2088 *eax = eax_tmp;
2089 if (ebx != NULL)
2090 *ebx = ebx_tmp;
2091 if (ecx != NULL)
2092 *ecx = ecx_tmp;
2093 if (edx != NULL)
2094 *edx = edx_tmp;
2095 return TRUE;
2096#else
2097 g_set_error_literal (error,
2098 G_IO_ERROR,
2099 G_IO_ERROR_NOT_SUPPORTED,
2100 "no <cpuid.h> support");
2101 return FALSE;
2102#endif
2103}
2104
2105/**
Richard Hughes9223c892020-05-09 20:32:08 +01002106 * fu_common_is_cpu_intel:
2107 *
2108 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2109 *
2110 * Return value: %TRUE if the vendor was Intel.
2111 *
2112 * Since: 1.5.0
2113 **/
2114gboolean
2115fu_common_is_cpu_intel (void)
2116{
Richard Hughes9223c892020-05-09 20:32:08 +01002117 guint ebx = 0;
2118 guint ecx = 0;
2119 guint edx = 0;
Richard Hughes9223c892020-05-09 20:32:08 +01002120
Richard Hughesbd1dc2a2020-08-20 15:35:28 +01002121 if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, NULL))
2122 return FALSE;
2123#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002124 if (ebx == signature_INTEL_ebx &&
2125 edx == signature_INTEL_edx &&
2126 ecx == signature_INTEL_ecx) {
2127 return TRUE;
2128 }
Richard Hughesbd444322020-05-21 12:05:03 +01002129#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002130 return FALSE;
2131}
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002132
Richard Hughes36111472020-08-12 15:04:24 +01002133/**
2134 * fu_common_is_live_media:
2135 *
2136 * Checks if the user is running from a live media using various heuristics.
2137 *
2138 * Returns: %TRUE if live
2139 *
2140 * Since: 1.4.6
2141 **/
2142gboolean
2143fu_common_is_live_media (void)
2144{
2145 gsize bufsz = 0;
2146 g_autofree gchar *buf = NULL;
2147 g_auto(GStrv) tokens = NULL;
2148 const gchar *args[] = {
2149 "rd.live.image",
2150 "boot=live",
2151 NULL, /* last entry */
2152 };
2153 if (g_file_test ("/cdrom/.disk/info", G_FILE_TEST_EXISTS))
2154 return TRUE;
2155 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, NULL))
2156 return FALSE;
2157 if (bufsz == 0)
2158 return FALSE;
2159 tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
2160 for (guint i = 0; args[i] != NULL; i++) {
2161 if (g_strv_contains ((const gchar * const *) tokens, args[i]))
2162 return TRUE;
2163 }
2164 return FALSE;
2165}
2166
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002167static GPtrArray *
Richard Hughes43417b22020-10-30 14:46:16 +00002168fu_common_get_block_devices (GError **error)
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002169{
2170 GVariantBuilder builder;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002171 const gchar *obj;
2172 g_autoptr(GVariant) output = NULL;
2173 g_autoptr(GDBusProxy) proxy = NULL;
2174 g_autoptr(GPtrArray) devices = NULL;
2175 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002176 g_autoptr(GDBusConnection) connection = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002177
Richard Hughes43417b22020-10-30 14:46:16 +00002178 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
2179 if (connection == NULL) {
2180 g_prefix_error (error, "failed to get system bus: ");
2181 return NULL;
2182 }
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002183 proxy = g_dbus_proxy_new_sync (connection,
2184 G_DBUS_PROXY_FLAGS_NONE, NULL,
2185 UDISKS_DBUS_SERVICE,
2186 UDISKS_DBUS_PATH,
2187 UDISKS_DBUS_MANAGER_INTERFACE,
2188 NULL, error);
2189 if (proxy == NULL) {
2190 g_prefix_error (error, "failed to find %s: ", UDISKS_DBUS_SERVICE);
2191 return NULL;
2192 }
2193 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002194 output = g_dbus_proxy_call_sync (proxy,
Richard Hughesdb344d52020-09-09 19:42:27 +01002195 "GetBlockDevices",
2196 g_variant_new ("(a{sv})", &builder),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002197 G_DBUS_CALL_FLAGS_NONE,
2198 -1, NULL, error);
Richard Hughes0bdf5612020-10-30 14:56:22 +00002199 if (output == NULL) {
2200 if (error != NULL)
2201 g_dbus_error_strip_remote_error (*error);
2202 g_prefix_error (error, "failed to call %s.%s(): ",
2203 UDISKS_DBUS_SERVICE,
2204 "GetBlockDevices");
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002205 return NULL;
Richard Hughes0bdf5612020-10-30 14:56:22 +00002206 }
Richard Hughes43417b22020-10-30 14:46:16 +00002207 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002208 g_variant_get (output, "(ao)", &iter);
Richard Hughes43417b22020-10-30 14:46:16 +00002209 while (g_variant_iter_next (iter, "&o", &obj)) {
2210 g_autoptr(GDBusProxy) proxy_blk = NULL;
2211 proxy_blk = g_dbus_proxy_new_sync (connection,
2212 G_DBUS_PROXY_FLAGS_NONE, NULL,
2213 UDISKS_DBUS_SERVICE,
2214 obj,
2215 UDISKS_DBUS_INTERFACE_BLOCK,
2216 NULL, error);
2217 if (proxy_blk == NULL) {
2218 g_prefix_error (error, "failed to initialize d-bus proxy for %s: ", obj);
2219 return NULL;
2220 }
2221 g_ptr_array_add (devices, g_steal_pointer (&proxy_blk));
2222 }
2223
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002224
2225 return g_steal_pointer (&devices);
2226}
2227
2228/**
2229 * fu_common_get_volumes_by_kind:
2230 * @kind: A volume kind, typically a GUID
2231 * @error: A #GError or NULL
2232 *
Richard Hughesc57a8f52020-10-30 14:42:42 +00002233 * Finds all volumes of a specific partition type
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002234 *
2235 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if the kind was not found
2236 *
2237 * Since: 1.4.6
2238 **/
2239GPtrArray *
2240fu_common_get_volumes_by_kind (const gchar *kind, GError **error)
2241{
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002242 g_autoptr(GPtrArray) devices = NULL;
2243 g_autoptr(GPtrArray) volumes = NULL;
2244
Richard Hughes43417b22020-10-30 14:46:16 +00002245 devices = fu_common_get_block_devices (error);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002246 if (devices == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002247 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002248 volumes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2249 for (guint i = 0; i < devices->len; i++) {
Richard Hughes43417b22020-10-30 14:46:16 +00002250 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002251 const gchar *type_str;
2252 g_autoptr(GDBusProxy) proxy_part = NULL;
Richard Hughes43417b22020-10-30 14:46:16 +00002253 g_autoptr(GDBusProxy) proxy_fs = NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002254 g_autoptr(GVariant) val = NULL;
2255
Richard Hughes43417b22020-10-30 14:46:16 +00002256 proxy_part = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002257 G_DBUS_PROXY_FLAGS_NONE, NULL,
2258 UDISKS_DBUS_SERVICE,
Richard Hughes43417b22020-10-30 14:46:16 +00002259 g_dbus_proxy_get_object_path (proxy_blk),
2260 UDISKS_DBUS_INTERFACE_PARTITION,
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002261 NULL, error);
2262 if (proxy_part == NULL) {
Richard Hughes43417b22020-10-30 14:46:16 +00002263 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2264 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002265 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002266 }
2267 val = g_dbus_proxy_get_cached_property (proxy_part, "Type");
2268 if (val == NULL)
2269 continue;
2270
Richard Hughesdb344d52020-09-09 19:42:27 +01002271 g_variant_get (val, "&s", &type_str);
Richard Hughes43417b22020-10-30 14:46:16 +00002272 g_debug ("device %s, type: %s",
2273 g_dbus_proxy_get_object_path (proxy_blk), type_str);
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002274 if (g_strcmp0 (type_str, kind) != 0)
2275 continue;
Richard Hughes43417b22020-10-30 14:46:16 +00002276 proxy_fs = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy_blk),
2277 G_DBUS_PROXY_FLAGS_NONE, NULL,
2278 UDISKS_DBUS_SERVICE,
2279 g_dbus_proxy_get_object_path (proxy_blk),
2280 UDISKS_DBUS_INTERFACE_FILESYSTEM,
2281 NULL, error);
2282 if (proxy_fs == NULL) {
2283 g_prefix_error (error, "failed to initialize d-bus proxy %s: ",
2284 g_dbus_proxy_get_object_path (proxy_blk));
Richard Hughesb81140d2020-08-17 14:47:17 +01002285 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002286 }
Richard Hughesf3993a62020-10-30 14:49:52 +00002287 g_ptr_array_add (volumes,
2288 g_object_new (FU_TYPE_VOLUME,
Richard Hughes2b188c82020-10-30 14:52:36 +00002289 "proxy-block", proxy_blk,
Richard Hughesf3993a62020-10-30 14:49:52 +00002290 "proxy-filesystem", proxy_fs,
2291 NULL));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002292 }
2293 if (volumes->len == 0) {
2294 g_set_error (error,
2295 G_IO_ERROR,
2296 G_IO_ERROR_NOT_FOUND,
2297 "no volumes of type %s", kind);
2298 return NULL;
2299 }
2300 return g_steal_pointer (&volumes);
2301}
2302
2303/**
Richard Hughes0bdf5612020-10-30 14:56:22 +00002304 * fu_common_get_volume_by_device:
2305 * @device: A device string, typcically starting with `/dev/`
2306 * @error: A #GError or NULL
2307 *
2308 * Finds the first volume from the specified device.
2309 *
2310 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2311 *
2312 * Since: 1.5.1
2313 **/
2314FuVolume *
2315fu_common_get_volume_by_device (const gchar *device, GError **error)
2316{
2317 g_autoptr(GPtrArray) devices = NULL;
2318
2319 /* find matching block device */
2320 devices = fu_common_get_block_devices (error);
2321 if (devices == NULL)
2322 return NULL;
2323 for (guint i = 0; i < devices->len; i++) {
2324 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2325 g_autoptr(GVariant) val = NULL;
2326 val = g_dbus_proxy_get_cached_property (proxy_blk, "Device");
2327 if (val == NULL)
2328 continue;
2329 if (g_strcmp0 (g_variant_get_bytestring (val), device) == 0) {
2330 return g_object_new (FU_TYPE_VOLUME,
2331 "proxy-block", proxy_blk,
2332 NULL);
2333 }
2334 }
2335
2336 /* failed */
2337 g_set_error (error,
2338 G_IO_ERROR,
2339 G_IO_ERROR_NOT_FOUND,
2340 "no volumes for device %s",
2341 device);
2342 return NULL;
2343}
2344
2345/**
2346 * fu_common_get_volume_by_devnum:
Richard Hughese0f92072020-11-06 09:50:29 +00002347 * @devnum: A device number
Richard Hughes0bdf5612020-10-30 14:56:22 +00002348 * @error: A #GError or NULL
2349 *
2350 * Finds the first volume from the specified device.
2351 *
2352 * Returns: (transfer full): a #GPtrArray, or %NULL if the kind was not found
2353 *
2354 * Since: 1.5.1
2355 **/
2356FuVolume *
2357fu_common_get_volume_by_devnum (guint32 devnum, GError **error)
2358{
2359 g_autoptr(GPtrArray) devices = NULL;
2360
2361 /* find matching block device */
2362 devices = fu_common_get_block_devices (error);
2363 if (devices == NULL)
2364 return NULL;
2365 for (guint i = 0; i < devices->len; i++) {
2366 GDBusProxy *proxy_blk = g_ptr_array_index (devices, i);
2367 g_autoptr(GVariant) val = NULL;
2368 val = g_dbus_proxy_get_cached_property (proxy_blk, "DeviceNumber");
2369 if (val == NULL)
2370 continue;
2371 if (devnum == g_variant_get_uint64 (val)) {
2372 return g_object_new (FU_TYPE_VOLUME,
2373 "proxy-block", proxy_blk,
2374 NULL);
2375 }
2376 }
2377
2378 /* failed */
2379 g_set_error (error,
2380 G_IO_ERROR,
2381 G_IO_ERROR_NOT_FOUND,
2382 "no volumes for devnum %u",
2383 devnum);
2384 return NULL;
2385}
2386
2387/**
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002388 * fu_common_get_esp_default:
2389 * @error: A #GError or NULL
2390 *
2391 * Gets the platform default ESP
2392 *
2393 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2394 *
2395 * Since: 1.4.6
2396 **/
2397FuVolume *
2398fu_common_get_esp_default (GError **error)
2399{
2400 const gchar *path_tmp;
2401 g_autoptr(GPtrArray) volumes_fstab = g_ptr_array_new ();
2402 g_autoptr(GPtrArray) volumes_mtab = g_ptr_array_new ();
2403 g_autoptr(GPtrArray) volumes = NULL;
2404
2405 /* for the test suite use local directory for ESP */
2406 path_tmp = g_getenv ("FWUPD_UEFI_ESP_PATH");
2407 if (path_tmp != NULL)
2408 return fu_volume_new_from_mount_path (path_tmp);
2409
2410 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2411 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002412 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002413 for (guint i = 0; i < volumes->len; i++) {
2414 FuVolume *vol = g_ptr_array_index (volumes, i);
2415 g_ptr_array_add (fu_volume_is_mounted (vol) ? volumes_mtab : volumes_fstab, vol);
2416 }
2417 if (volumes_mtab->len == 1) {
2418 FuVolume *vol = g_ptr_array_index (volumes_mtab, 0);
2419 return g_object_ref (vol);
2420 }
2421 if (volumes_mtab->len == 0 && volumes_fstab->len == 1) {
2422 FuVolume *vol = g_ptr_array_index (volumes_fstab, 0);
2423 return g_object_ref (vol);
2424 }
2425 g_set_error (error,
2426 G_IO_ERROR,
2427 G_IO_ERROR_INVALID_FILENAME,
2428 "More than one available ESP");
2429 return NULL;
2430}
2431
2432/**
2433 * fu_common_get_esp_for_path:
2434 * @esp_path: A path to the ESP
2435 * @error: A #GError or NULL
2436 *
2437 * Gets the platform ESP using a UNIX or UDisks path
2438 *
2439 * Returns: (transfer full): a #FuVolume, or %NULL if the ESP was not found
2440 *
2441 * Since: 1.4.6
2442 **/
2443FuVolume *
2444fu_common_get_esp_for_path (const gchar *esp_path, GError **error)
2445{
2446 g_autofree gchar *basename = g_path_get_basename (esp_path);
2447 g_autoptr(GPtrArray) volumes = NULL;
2448
2449 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
2450 if (volumes == NULL)
Richard Hughesb81140d2020-08-17 14:47:17 +01002451 return NULL;
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002452 for (guint i = 0; i < volumes->len; i++) {
2453 FuVolume *vol = g_ptr_array_index (volumes, i);
Mario Limonciello5a835632020-10-07 14:22:08 -05002454 g_autofree gchar *vol_basename = g_path_get_basename (fu_volume_get_mount_point (vol));
Richard Hughes8f0b2d12020-08-12 12:41:53 +01002455 if (g_strcmp0 (basename, vol_basename) == 0)
2456 return g_object_ref (vol);
2457 }
2458 g_set_error (error,
2459 G_IO_ERROR,
2460 G_IO_ERROR_INVALID_FILENAME,
2461 "No ESP with path %s",
2462 esp_path);
2463 return NULL;
2464}
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002465
2466/**
Richard Hughes44ae2a72020-09-25 18:00:21 +01002467 * fu_common_crc8:
2468 * @buf: memory buffer
2469 * @bufsz: sizeof buf
2470 *
2471 * Returns the cyclic redundancy check value for the given memory buffer.
2472 *
2473 * Returns: CRC value
2474 *
2475 * Since: 1.5.0
2476 **/
2477guint8
2478fu_common_crc8 (const guint8 *buf, gsize bufsz)
2479{
2480 guint32 crc = 0;
2481 for (gsize j = bufsz; j > 0; j--) {
2482 crc ^= (*(buf++) << 8);
2483 for (guint32 i = 8; i; i--) {
2484 if (crc & 0x8000)
2485 crc ^= (0x1070 << 3);
2486 crc <<= 1;
2487 }
2488 }
2489 return ~((guint8) (crc >> 8));
2490}
2491
2492/**
Richard Hughes6f5e35a2020-09-25 14:14:52 +01002493 * fu_common_crc16:
2494 * @buf: memory buffer
2495 * @bufsz: sizeof buf
2496 *
2497 * Returns the cyclic redundancy check value for the given memory buffer.
2498 *
2499 * Returns: CRC value
2500 *
2501 * Since: 1.5.0
2502 **/
2503guint16
2504fu_common_crc16 (const guint8 *buf, gsize bufsz)
2505{
2506 guint16 crc = 0xffff;
2507 for (gsize len = bufsz; len > 0; len--) {
2508 crc = (guint16) (crc ^ (*buf++));
2509 for (guint8 i = 0; i < 8; i++) {
2510 if (crc & 0x1) {
2511 crc = (crc >> 1) ^ 0xa001;
2512 } else {
2513 crc >>= 1;
2514 }
2515 }
2516 }
2517 return ~crc;
2518}
2519
2520/**
2521 * fu_common_crc32_full:
2522 * @buf: memory buffer
2523 * @bufsz: sizeof buf
2524 * @crc: initial CRC value, typically 0xFFFFFFFF
2525 * @polynomial: CRC polynomial, typically 0xEDB88320
2526 *
2527 * Returns the cyclic redundancy check value for the given memory buffer.
2528 *
2529 * Returns: CRC value
2530 *
2531 * Since: 1.5.0
2532 **/
2533guint32
2534fu_common_crc32_full (const guint8 *buf, gsize bufsz, guint32 crc, guint32 polynomial)
2535{
2536 for (guint32 idx = 0; idx < bufsz; idx++) {
2537 guint8 data = *buf++;
2538 crc = crc ^ data;
2539 for (guint32 bit = 0; bit < 8; bit++) {
2540 guint32 mask = -(crc & 1);
2541 crc = (crc >> 1) ^ (polynomial & mask);
2542 }
2543 }
2544 return ~crc;
2545}
2546
2547/**
2548 * fu_common_crc32:
2549 * @buf: memory buffer
2550 * @bufsz: sizeof buf
2551 *
2552 * Returns the cyclic redundancy check value for the given memory buffer.
2553 *
2554 * Returns: CRC value
2555 *
2556 * Since: 1.5.0
2557 **/
2558guint32
2559fu_common_crc32 (const guint8 *buf, gsize bufsz)
2560{
2561 return fu_common_crc32_full (buf, bufsz, 0xFFFFFFFF, 0xEDB88320);
2562}