blob: d8083442393d0ada5389546e5122615ac94f8dac [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>
18#endif
19
Richard Hughes94f939a2017-08-08 12:21:39 +010020#include <archive_entry.h>
21#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010022#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000023#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000024#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000025#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010026
27#include "fwupd-error.h"
28
29#include "fu-common.h"
30
31/**
Richard Hughes4eada342017-10-03 21:20:32 +010032 * SECTION:fu-common
33 * @short_description: common functionality for plugins to use
34 *
35 * Helper functions that can be used by the daemon and plugins.
36 *
37 * See also: #FuPlugin
38 */
39
40/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010041 * fu_common_rmtree:
42 * @directory: a directory name
43 * @error: A #GError or %NULL
44 *
45 * Recursively removes a directory.
46 *
47 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060048 *
49 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010050 **/
51gboolean
52fu_common_rmtree (const gchar *directory, GError **error)
53{
54 const gchar *filename;
55 g_autoptr(GDir) dir = NULL;
56
57 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010058 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010059 dir = g_dir_open (directory, 0, error);
60 if (dir == NULL)
61 return FALSE;
62
63 /* find each */
64 while ((filename = g_dir_read_name (dir))) {
65 g_autofree gchar *src = NULL;
66 src = g_build_filename (directory, filename, NULL);
67 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
68 if (!fu_common_rmtree (src, error))
69 return FALSE;
70 } else {
71 if (g_unlink (src) != 0) {
72 g_set_error (error,
73 FWUPD_ERROR,
74 FWUPD_ERROR_INTERNAL,
75 "Failed to delete: %s", src);
76 return FALSE;
77 }
78 }
79 }
80 if (g_remove (directory) != 0) {
81 g_set_error (error,
82 FWUPD_ERROR,
83 FWUPD_ERROR_INTERNAL,
84 "Failed to delete: %s", directory);
85 return FALSE;
86 }
87 return TRUE;
88}
89
Richard Hughes89e968b2018-03-07 10:01:08 +000090static gboolean
91fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
92{
93 const gchar *filename;
94 g_autoptr(GDir) dir = NULL;
95
96 /* try to open */
97 dir = g_dir_open (directory, 0, error);
98 if (dir == NULL)
99 return FALSE;
100
101 /* find each */
102 while ((filename = g_dir_read_name (dir))) {
103 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
104 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
105 if (!fu_common_get_file_list_internal (files, src, error))
106 return FALSE;
107 } else {
108 g_ptr_array_add (files, g_steal_pointer (&src));
109 }
110 }
111 return TRUE;
112
113}
114
115/**
116 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100117 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000118 * @error: A #GError or %NULL
119 *
120 * Returns every file found under @directory, and any subdirectory.
121 * If any path under @directory cannot be accessed due to permissions an error
122 * will be returned.
123 *
Mario Limonciello1a680f32019-11-25 19:44:53 -0600124 * Returns: (transfer container): array of files, or %NULL for error
125 *
126 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000127 **/
128GPtrArray *
129fu_common_get_files_recursive (const gchar *path, GError **error)
130{
131 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
132 if (!fu_common_get_file_list_internal (files, path, error))
133 return NULL;
134 return g_steal_pointer (&files);
135}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100136/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100137 * fu_common_mkdir_parent:
138 * @filename: A full pathname
139 * @error: A #GError, or %NULL
140 *
141 * Creates any required directories, including any parent directories.
142 *
143 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600144 *
145 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100146 **/
147gboolean
148fu_common_mkdir_parent (const gchar *filename, GError **error)
149{
150 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100151
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100152 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100153 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100154 if (g_mkdir_with_parents (parent, 0755) == -1) {
155 g_set_error (error,
156 FWUPD_ERROR,
157 FWUPD_ERROR_INTERNAL,
158 "Failed to create '%s': %s",
159 parent, g_strerror (errno));
160 return FALSE;
161 }
162 return TRUE;
163}
164
165/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100166 * fu_common_set_contents_bytes:
167 * @filename: A filename
168 * @bytes: The data to write
169 * @error: A #GError, or %NULL
170 *
171 * Writes a blob of data to a filename, creating the parent directories as
172 * required.
173 *
174 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600175 *
176 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100177 **/
178gboolean
179fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
180{
181 const gchar *data;
182 gsize size;
183 g_autoptr(GFile) file = NULL;
184 g_autoptr(GFile) file_parent = NULL;
185
186 file = g_file_new_for_path (filename);
187 file_parent = g_file_get_parent (file);
188 if (!g_file_query_exists (file_parent, NULL)) {
189 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
190 return FALSE;
191 }
192 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100193 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100194 return g_file_set_contents (filename, data, size, error);
195}
196
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100197/**
198 * fu_common_get_contents_bytes:
199 * @filename: A filename
200 * @error: A #GError, or %NULL
201 *
202 * Reads a blob of data from a file.
203 *
204 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600205 *
206 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100207 **/
208GBytes *
209fu_common_get_contents_bytes (const gchar *filename, GError **error)
210{
211 gchar *data = NULL;
212 gsize len = 0;
213 if (!g_file_get_contents (filename, &data, &len, error))
214 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100215 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100216 return g_bytes_new_take (data, len);
217}
Richard Hughes943d2c92017-06-21 09:04:39 +0100218
219/**
220 * fu_common_get_contents_fd:
221 * @fd: A file descriptor
222 * @count: The maximum number of bytes to read
223 * @error: A #GError, or %NULL
224 *
225 * Reads a blob from a specific file descriptor.
226 *
227 * Note: this will close the fd when done
228 *
Richard Hughes4eada342017-10-03 21:20:32 +0100229 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600230 *
231 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100232 **/
233GBytes *
234fu_common_get_contents_fd (gint fd, gsize count, GError **error)
235{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000236#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100237 g_autoptr(GBytes) blob = NULL;
238 g_autoptr(GError) error_local = NULL;
239 g_autoptr(GInputStream) stream = NULL;
240
241 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100242 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
243
Richard Hughes919f8ab2018-02-14 10:24:56 +0000244 /* this is invalid */
245 if (count == 0) {
246 g_set_error_literal (error,
247 FWUPD_ERROR,
248 FWUPD_ERROR_NOT_SUPPORTED,
249 "A maximum read size must be specified");
250 return NULL;
251 }
252
Richard Hughes943d2c92017-06-21 09:04:39 +0100253 /* read the entire fd to a data blob */
254 stream = g_unix_input_stream_new (fd, TRUE);
255 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
256 if (blob == NULL) {
257 g_set_error_literal (error,
258 FWUPD_ERROR,
259 FWUPD_ERROR_INVALID_FILE,
260 error_local->message);
261 return NULL;
262 }
263 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000264#else
265 g_set_error_literal (error,
266 FWUPD_ERROR,
267 FWUPD_ERROR_NOT_SUPPORTED,
268 "Not supported as <glib-unix.h> is unavailable");
269 return NULL;
270#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100271}
Richard Hughes94f939a2017-08-08 12:21:39 +0100272
273static gboolean
274fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
275{
276 const gchar *tmp;
277 g_autofree gchar *buf = NULL;
278
279 /* no output file */
280 if (archive_entry_pathname (entry) == NULL)
281 return FALSE;
282
283 /* update output path */
284 tmp = archive_entry_pathname (entry);
285 buf = g_build_filename (dir, tmp, NULL);
286 archive_entry_update_pathname_utf8 (entry, buf);
287 return TRUE;
288}
289
290/**
291 * fu_common_extract_archive:
292 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100293 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100294 * @error: A #GError, or %NULL
295 *
296 * Extracts an achive to a directory.
297 *
298 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600299 *
300 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100301 **/
302gboolean
303fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
304{
305 gboolean ret = TRUE;
306 int r;
307 struct archive *arch = NULL;
308 struct archive_entry *entry;
309
310 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100311 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100312 arch = archive_read_new ();
313 archive_read_support_format_all (arch);
314 archive_read_support_filter_all (arch);
315 r = archive_read_open_memory (arch,
316 (void *) g_bytes_get_data (blob, NULL),
317 (size_t) g_bytes_get_size (blob));
318 if (r != 0) {
319 ret = FALSE;
320 g_set_error (error,
321 FWUPD_ERROR,
322 FWUPD_ERROR_INTERNAL,
323 "Cannot open: %s",
324 archive_error_string (arch));
325 goto out;
326 }
327 for (;;) {
328 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100329 r = archive_read_next_header (arch, &entry);
330 if (r == ARCHIVE_EOF)
331 break;
332 if (r != ARCHIVE_OK) {
333 ret = FALSE;
334 g_set_error (error,
335 FWUPD_ERROR,
336 FWUPD_ERROR_INTERNAL,
337 "Cannot read header: %s",
338 archive_error_string (arch));
339 goto out;
340 }
341
342 /* only extract if valid */
343 valid = fu_common_extract_archive_entry (entry, dir);
344 if (!valid)
345 continue;
346 r = archive_read_extract (arch, entry, 0);
347 if (r != ARCHIVE_OK) {
348 ret = FALSE;
349 g_set_error (error,
350 FWUPD_ERROR,
351 FWUPD_ERROR_INTERNAL,
352 "Cannot extract: %s",
353 archive_error_string (arch));
354 goto out;
355 }
356 }
357out:
358 if (arch != NULL) {
359 archive_read_close (arch);
360 archive_read_free (arch);
361 }
362 return ret;
363}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100364
365static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300366fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
367
368static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100369fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
370{
371 va_list args;
372 g_autofree gchar *tmp = NULL;
373 g_auto(GStrv) split = NULL;
374
375 va_start (args, fmt);
376 tmp = g_strdup_vprintf (fmt, args);
377 va_end (args);
378
379 split = g_strsplit (tmp, " ", -1);
380 for (guint i = 0; split[i] != NULL; i++)
381 g_ptr_array_add (argv, g_strdup (split[i]));
382}
383
Mario Limonciello1a680f32019-11-25 19:44:53 -0600384/**
385 * fu_common_find_program_in_path:
386 * @basename: The program to search
387 * @error: A #GError, or %NULL
388 *
389 * Looks for a program in the PATH variable
390 *
391 * Returns: a new #gchar, or %NULL for error
392 *
393 * Since: 1.1.2
394 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100395gchar *
396fu_common_find_program_in_path (const gchar *basename, GError **error)
397{
398 gchar *fn = g_find_program_in_path (basename);
399 if (fn == NULL) {
400 g_set_error (error,
401 FWUPD_ERROR,
402 FWUPD_ERROR_NOT_SUPPORTED,
403 "missing executable %s in PATH",
404 basename);
405 return NULL;
406 }
407 return fn;
408}
409
410static gboolean
411fu_common_test_namespace_support (GError **error)
412{
413 /* test if CONFIG_USER_NS is valid */
414 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
415 g_set_error (error,
416 FWUPD_ERROR,
417 FWUPD_ERROR_NOT_SUPPORTED,
418 "missing CONFIG_USER_NS in kernel");
419 return FALSE;
420 }
421 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
422 g_autofree gchar *clone = NULL;
423 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
424 return FALSE;
425 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
426 g_set_error (error,
427 FWUPD_ERROR,
428 FWUPD_ERROR_NOT_SUPPORTED,
429 "unprivileged user namespace clones disabled by distro");
430 return FALSE;
431 }
432 }
433 return TRUE;
434}
435
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100436/**
437 * fu_common_firmware_builder:
438 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100439 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
440 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100441 * @error: A #GError, or %NULL
442 *
443 * Builds a firmware file using tools from the host session in a bubblewrap
444 * jail. Several things happen during build:
445 *
446 * 1. The @bytes data is untarred to a temporary location
447 * 2. A bubblewrap container is set up
448 * 3. The startup.sh script is run inside the container
449 * 4. The firmware.bin is extracted from the container
450 * 5. The temporary location is deleted
451 *
452 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600453 *
454 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100455 **/
456GBytes *
457fu_common_firmware_builder (GBytes *bytes,
458 const gchar *script_fn,
459 const gchar *output_fn,
460 GError **error)
461{
462 gint rc = 0;
463 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500464 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100465 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100466 g_autofree gchar *localstatedir = NULL;
467 g_autofree gchar *output2_fn = NULL;
468 g_autofree gchar *standard_error = NULL;
469 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100470 g_autofree gchar *tmpdir = NULL;
471 g_autoptr(GBytes) firmware_blob = NULL;
472 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
473
474 g_return_val_if_fail (bytes != NULL, NULL);
475 g_return_val_if_fail (script_fn != NULL, NULL);
476 g_return_val_if_fail (output_fn != NULL, NULL);
477 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
478
Mario Limonciello37b59582018-08-13 08:38:01 -0500479 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100480 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
481 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100482 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500483
484 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100485 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100486 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500487
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100488 /* untar file to temp location */
489 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
490 if (tmpdir == NULL)
491 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100492 if (!fu_common_extract_archive (bytes, tmpdir, error))
493 return NULL;
494
495 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100496 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
497 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100498
499 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500500 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100501 fu_common_add_argv (argv, "--die-with-parent");
502 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500503 fu_common_add_argv (argv, "--ro-bind /lib /lib");
504 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
505 fu_common_add_argv (argv, "--ro-bind /bin /bin");
506 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507 fu_common_add_argv (argv, "--dir /tmp");
508 fu_common_add_argv (argv, "--dir /var");
509 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100510 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
511 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100512 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100513 fu_common_add_argv (argv, "--chdir /tmp");
514 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100515 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100516 g_ptr_array_add (argv, NULL);
517 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
518 g_debug ("running '%s' in %s", argv_str, tmpdir);
519 if (!g_spawn_sync ("/tmp",
520 (gchar **) argv->pdata,
521 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100522 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100523 NULL, NULL, /* child_setup */
524 &standard_output,
525 &standard_error,
526 &rc,
527 error)) {
528 g_prefix_error (error, "failed to run '%s': ", argv_str);
529 return NULL;
530 }
531 if (standard_output != NULL && standard_output[0] != '\0')
532 g_debug ("console output was: %s", standard_output);
533 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500534 FwupdError code = FWUPD_ERROR_INTERNAL;
535 if (errno == ENOTTY)
536 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100537 g_set_error (error,
538 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500539 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100540 "failed to build firmware: %s",
541 standard_error);
542 return NULL;
543 }
544
545 /* get generated file */
546 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
547 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
548 if (firmware_blob == NULL)
549 return NULL;
550
551 /* cleanup temp directory */
552 if (!fu_common_rmtree (tmpdir, error))
553 return NULL;
554
555 /* success */
556 return g_steal_pointer (&firmware_blob);
557}
Richard Hughes049ccc82017-08-09 15:26:56 +0100558
559typedef struct {
560 FuOutputHandler handler_cb;
561 gpointer handler_user_data;
562 GMainLoop *loop;
563 GSource *source;
564 GInputStream *stream;
565 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000566 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100567} FuCommonSpawnHelper;
568
569static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
570
571static gboolean
572fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
573{
574 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
575 gchar buffer[1024];
576 gssize sz;
577 g_auto(GStrv) split = NULL;
578 g_autoptr(GError) error = NULL;
579
580 /* read from stream */
581 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
582 buffer,
583 sizeof(buffer) - 1,
584 NULL,
585 &error);
586 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100587 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
588 g_warning ("failed to get read from nonblocking fd: %s",
589 error->message);
590 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100591 return G_SOURCE_REMOVE;
592 }
593
594 /* no read possible */
595 if (sz == 0)
596 g_main_loop_quit (helper->loop);
597
598 /* emit lines */
599 if (helper->handler_cb != NULL) {
600 buffer[sz] = '\0';
601 split = g_strsplit (buffer, "\n", -1);
602 for (guint i = 0; split[i] != NULL; i++) {
603 if (split[i][0] == '\0')
604 continue;
605 helper->handler_cb (split[i], helper->handler_user_data);
606 }
607 }
608
609 /* set up the source for the next read */
610 fu_common_spawn_create_pollable_source (helper);
611 return G_SOURCE_REMOVE;
612}
613
614static void
615fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
616{
617 if (helper->source != NULL)
618 g_source_destroy (helper->source);
619 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
620 helper->cancellable);
621 g_source_attach (helper->source, NULL);
622 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
623}
624
625static void
626fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
627{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000628 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100629 if (helper->stream != NULL)
630 g_object_unref (helper->stream);
631 if (helper->source != NULL)
632 g_source_destroy (helper->source);
633 if (helper->loop != NULL)
634 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000635 if (helper->timeout_id != 0)
636 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100637 g_free (helper);
638}
639
Mario Limoncielloa98df552018-04-16 12:15:51 -0500640#pragma clang diagnostic push
641#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100642G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500643#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100644
Richard Hughesb768e4d2019-02-26 13:55:18 +0000645static gboolean
646fu_common_spawn_timeout_cb (gpointer user_data)
647{
648 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
649 g_cancellable_cancel (helper->cancellable);
650 g_main_loop_quit (helper->loop);
651 helper->timeout_id = 0;
652 return G_SOURCE_REMOVE;
653}
654
655static void
656fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
657{
658 /* just propagate */
659 g_cancellable_cancel (helper->cancellable);
660}
661
Richard Hughes049ccc82017-08-09 15:26:56 +0100662/**
663 * fu_common_spawn_sync:
664 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100665 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
666 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000667 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100668 * @cancellable: a #GCancellable, or %NULL
669 * @error: A #GError or %NULL
670 *
671 * Runs a subprocess and waits for it to exit. Any output on standard out or
672 * standard error will be forwarded to @handler_cb as whole lines.
673 *
674 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600675 *
676 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100677 **/
678gboolean
679fu_common_spawn_sync (const gchar * const * argv,
680 FuOutputHandler handler_cb,
681 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000682 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100683 GCancellable *cancellable, GError **error)
684{
685 g_autoptr(FuCommonSpawnHelper) helper = NULL;
686 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100687 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000688 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100689
690 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100691 argv_str = g_strjoinv (" ", (gchar **) argv);
692 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100693 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
694 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
695 if (subprocess == NULL)
696 return FALSE;
697
698 /* watch for process to exit */
699 helper = g_new0 (FuCommonSpawnHelper, 1);
700 helper->handler_cb = handler_cb;
701 helper->handler_user_data = handler_user_data;
702 helper->loop = g_main_loop_new (NULL, FALSE);
703 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000704
705 /* always create a cancellable, and connect up the parent */
706 helper->cancellable = g_cancellable_new ();
707 if (cancellable != NULL) {
708 cancellable_id = g_cancellable_connect (cancellable,
709 G_CALLBACK (fu_common_spawn_cancelled_cb),
710 helper, NULL);
711 }
712
713 /* allow timeout */
714 if (timeout_ms > 0) {
715 helper->timeout_id = g_timeout_add (timeout_ms,
716 fu_common_spawn_timeout_cb,
717 helper);
718 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100719 fu_common_spawn_create_pollable_source (helper);
720 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000721 g_cancellable_disconnect (cancellable, cancellable_id);
722 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
723 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100724 return g_subprocess_wait_check (subprocess, cancellable, error);
725}
Richard Hughesae252cd2017-12-08 10:48:15 +0000726
727/**
728 * fu_common_write_uint16:
729 * @buf: A writable buffer
730 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100731 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000732 *
733 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600734 *
735 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000736 **/
737void
738fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
739{
740 guint16 val_hw;
741 switch (endian) {
742 case G_BIG_ENDIAN:
743 val_hw = GUINT16_TO_BE(val_native);
744 break;
745 case G_LITTLE_ENDIAN:
746 val_hw = GUINT16_TO_LE(val_native);
747 break;
748 default:
749 g_assert_not_reached ();
750 }
751 memcpy (buf, &val_hw, sizeof(val_hw));
752}
753
754/**
755 * fu_common_write_uint32:
756 * @buf: A writable buffer
757 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100758 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000759 *
760 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600761 *
762 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000763 **/
764void
765fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
766{
767 guint32 val_hw;
768 switch (endian) {
769 case G_BIG_ENDIAN:
770 val_hw = GUINT32_TO_BE(val_native);
771 break;
772 case G_LITTLE_ENDIAN:
773 val_hw = GUINT32_TO_LE(val_native);
774 break;
775 default:
776 g_assert_not_reached ();
777 }
778 memcpy (buf, &val_hw, sizeof(val_hw));
779}
780
781/**
782 * fu_common_read_uint16:
783 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100784 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000785 *
786 * Read a value from a buffer using a specified endian.
787 *
788 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600789 *
790 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000791 **/
792guint16
793fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
794{
795 guint16 val_hw, val_native;
796 memcpy (&val_hw, buf, sizeof(val_hw));
797 switch (endian) {
798 case G_BIG_ENDIAN:
799 val_native = GUINT16_FROM_BE(val_hw);
800 break;
801 case G_LITTLE_ENDIAN:
802 val_native = GUINT16_FROM_LE(val_hw);
803 break;
804 default:
805 g_assert_not_reached ();
806 }
807 return val_native;
808}
809
810/**
811 * fu_common_read_uint32:
812 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100813 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000814 *
815 * Read a value from a buffer using a specified endian.
816 *
817 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600818 *
819 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000820 **/
821guint32
822fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
823{
824 guint32 val_hw, val_native;
825 memcpy (&val_hw, buf, sizeof(val_hw));
826 switch (endian) {
827 case G_BIG_ENDIAN:
828 val_native = GUINT32_FROM_BE(val_hw);
829 break;
830 case G_LITTLE_ENDIAN:
831 val_native = GUINT32_FROM_LE(val_hw);
832 break;
833 default:
834 g_assert_not_reached ();
835 }
836 return val_native;
837}
Richard Hughese82eef32018-05-20 10:41:26 +0100838
Richard Hughes73bf2332018-08-28 09:38:09 +0100839/**
840 * fu_common_strtoull:
841 * @str: A string, e.g. "0x1234"
842 *
843 * Converts a string value to an integer. Values are assumed base 10, unless
844 * prefixed with "0x" where they are parsed as base 16.
845 *
846 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600847 *
848 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100849 **/
850guint64
851fu_common_strtoull (const gchar *str)
852{
853 guint base = 10;
854 if (str == NULL)
855 return 0x0;
856 if (g_str_has_prefix (str, "0x")) {
857 str += 2;
858 base = 16;
859 }
860 return g_ascii_strtoull (str, NULL, base);
861}
862
Richard Hughesa574a752018-08-31 13:31:03 +0100863/**
864 * fu_common_strstrip:
865 * @str: A string, e.g. " test "
866 *
867 * Removes leading and trailing whitespace from a constant string.
868 *
869 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600870 *
871 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100872 **/
873gchar *
874fu_common_strstrip (const gchar *str)
875{
876 guint head = G_MAXUINT;
877 guint tail = 0;
878
879 g_return_val_if_fail (str != NULL, NULL);
880
881 /* find first non-space char */
882 for (guint i = 0; str[i] != '\0'; i++) {
883 if (str[i] != ' ') {
884 head = i;
885 break;
886 }
887 }
888 if (head == G_MAXUINT)
889 return g_strdup ("");
890
891 /* find last non-space char */
892 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500893 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100894 tail = i;
895 }
896 return g_strndup (str + head, tail - head + 1);
897}
898
Richard Hughese82eef32018-05-20 10:41:26 +0100899static const GError *
900fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
901{
902 for (guint j = 0; j < errors->len; j++) {
903 const GError *error = g_ptr_array_index (errors, j);
904 if (g_error_matches (error, FWUPD_ERROR, error_code))
905 return error;
906 }
907 return NULL;
908}
909
910static guint
911fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
912{
913 guint cnt = 0;
914 for (guint j = 0; j < errors->len; j++) {
915 const GError *error = g_ptr_array_index (errors, j);
916 if (g_error_matches (error, FWUPD_ERROR, error_code))
917 cnt++;
918 }
919 return cnt;
920}
921
922static gboolean
923fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
924{
925 for (guint j = 0; j < errors->len; j++) {
926 const GError *error = g_ptr_array_index (errors, j);
927 gboolean matches_any = FALSE;
928 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
929 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
930 matches_any = TRUE;
931 break;
932 }
933 }
934 if (!matches_any)
935 return FALSE;
936 }
937 return TRUE;
938}
939
940/**
941 * fu_common_error_array_get_best:
942 * @errors: (element-type GError): array of errors
943 *
944 * Finds the 'best' error to show the user from a array of errors, creating a
945 * completely bespoke error where required.
946 *
947 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600948 *
949 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100950 **/
951GError *
952fu_common_error_array_get_best (GPtrArray *errors)
953{
954 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
955 FWUPD_ERROR_VERSION_SAME,
956 FWUPD_ERROR_VERSION_NEWER,
957 FWUPD_ERROR_NOT_SUPPORTED,
958 FWUPD_ERROR_INTERNAL,
959 FWUPD_ERROR_NOT_FOUND,
960 FWUPD_ERROR_LAST };
961 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
962 FWUPD_ERROR_NOT_FOUND,
963 FWUPD_ERROR_NOT_SUPPORTED,
964 FWUPD_ERROR_LAST };
965 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
966 FWUPD_ERROR_VERSION_SAME,
967 FWUPD_ERROR_NOT_FOUND,
968 FWUPD_ERROR_NOT_SUPPORTED,
969 FWUPD_ERROR_LAST };
970
971 /* are all the errors either GUID-not-matched or version-same? */
972 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
973 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
974 return g_error_new (FWUPD_ERROR,
975 FWUPD_ERROR_NOTHING_TO_DO,
976 "All updatable firmware is already installed");
977 }
978
979 /* are all the errors either GUID-not-matched or version same or newer? */
980 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
981 fu_common_error_array_matches_any (errors, err_all_newer)) {
982 return g_error_new (FWUPD_ERROR,
983 FWUPD_ERROR_NOTHING_TO_DO,
984 "All updatable devices already have newer versions");
985 }
986
987 /* get the most important single error */
988 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
989 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
990 if (error_tmp != NULL)
991 return g_error_copy (error_tmp);
992 }
993
994 /* fall back to something */
995 return g_error_new (FWUPD_ERROR,
996 FWUPD_ERROR_NOT_FOUND,
997 "No supported devices found");
998}
Richard Hughes4be17d12018-05-30 20:36:29 +0100999
1000/**
1001 * fu_common_get_path:
1002 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1003 *
1004 * Gets a fwupd-specific system path. These can be overridden with various
1005 * environment variables, for instance %FWUPD_DATADIR.
1006 *
1007 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001008 *
1009 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001010 **/
1011gchar *
1012fu_common_get_path (FuPathKind path_kind)
1013{
1014 const gchar *tmp;
1015 g_autofree gchar *basedir = NULL;
1016
1017 switch (path_kind) {
1018 /* /var */
1019 case FU_PATH_KIND_LOCALSTATEDIR:
1020 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1021 if (tmp != NULL)
1022 return g_strdup (tmp);
1023 tmp = g_getenv ("SNAP_USER_DATA");
1024 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001025 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1026 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughes282b10d2018-06-22 14:48:00 +01001027 /* /sys/firmware */
1028 case FU_PATH_KIND_SYSFSDIR_FW:
1029 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1030 if (tmp != NULL)
1031 return g_strdup (tmp);
1032 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001033 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001034 case FU_PATH_KIND_SYSFSDIR_TPM:
1035 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1036 if (tmp != NULL)
1037 return g_strdup (tmp);
1038 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001039 /* /sys/bus/platform/drivers */
1040 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1041 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1042 if (tmp != NULL)
1043 return g_strdup (tmp);
1044 return g_strdup ("/sys/bus/platform/drivers");
Richard Hughes4be17d12018-05-30 20:36:29 +01001045 /* /etc */
1046 case FU_PATH_KIND_SYSCONFDIR:
1047 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1048 if (tmp != NULL)
1049 return g_strdup (tmp);
1050 tmp = g_getenv ("SNAP_USER_DATA");
1051 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001052 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1053 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001054 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1055 case FU_PATH_KIND_PLUGINDIR_PKG:
1056 tmp = g_getenv ("FWUPD_PLUGINDIR");
1057 if (tmp != NULL)
1058 return g_strdup (tmp);
1059 tmp = g_getenv ("SNAP");
1060 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001061 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1062 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001063 /* /usr/share/fwupd */
1064 case FU_PATH_KIND_DATADIR_PKG:
1065 tmp = g_getenv ("FWUPD_DATADIR");
1066 if (tmp != NULL)
1067 return g_strdup (tmp);
1068 tmp = g_getenv ("SNAP");
1069 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001070 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1071 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001072 /* /usr/libexec/fwupd/efi */
1073 case FU_PATH_KIND_EFIAPPDIR:
1074 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1075 if (tmp != NULL)
1076 return g_strdup (tmp);
1077#ifdef EFI_APP_LOCATION
1078 tmp = g_getenv ("SNAP");
1079 if (tmp != NULL)
1080 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1081 return g_strdup (EFI_APP_LOCATION);
1082#else
1083 return NULL;
1084#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001085 /* /etc/fwupd */
1086 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001087 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
1088 if (tmp != NULL)
1089 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001090 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1091 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1092 /* /var/lib/fwupd */
1093 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001094 tmp = g_getenv ("STATE_DIRECTORY");
1095 if (tmp != NULL)
1096 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001097 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1098 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1099 /* /var/cache/fwupd */
1100 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001101 tmp = g_getenv ("CACHE_DIRECTORY");
1102 if (tmp != NULL)
1103 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001104 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1105 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001106 case FU_PATH_KIND_OFFLINE_TRIGGER:
1107 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1108 if (tmp != NULL)
1109 return g_strdup (tmp);
1110 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001111 case FU_PATH_KIND_POLKIT_ACTIONS:
1112#ifdef POLKIT_ACTIONDIR
1113 return g_strdup (POLKIT_ACTIONDIR);
1114#else
1115 return NULL;
1116#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001117 /* this shouldn't happen */
1118 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001119 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001120 }
1121
1122 return NULL;
1123}
Richard Hughes83e56c12018-10-10 20:24:41 +01001124
1125/**
1126 * fu_common_string_replace:
1127 * @string: The #GString to operate on
1128 * @search: The text to search for
1129 * @replace: The text to use for substitutions
1130 *
1131 * Performs multiple search and replace operations on the given string.
1132 *
1133 * Returns: the number of replacements done, or 0 if @search is not found.
1134 *
1135 * Since: 1.2.0
1136 **/
1137guint
1138fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1139{
1140 gchar *tmp;
1141 guint count = 0;
1142 gsize search_idx = 0;
1143 gsize replace_len;
1144 gsize search_len;
1145
1146 g_return_val_if_fail (string != NULL, 0);
1147 g_return_val_if_fail (search != NULL, 0);
1148 g_return_val_if_fail (replace != NULL, 0);
1149
1150 /* nothing to do */
1151 if (string->len == 0)
1152 return 0;
1153
1154 search_len = strlen (search);
1155 replace_len = strlen (replace);
1156
1157 do {
1158 tmp = g_strstr_len (string->str + search_idx, -1, search);
1159 if (tmp == NULL)
1160 break;
1161
1162 /* advance the counter in case @replace contains @search */
1163 search_idx = (gsize) (tmp - string->str);
1164
1165 /* reallocate the string if required */
1166 if (search_len > replace_len) {
1167 g_string_erase (string,
1168 (gssize) search_idx,
1169 (gssize) (search_len - replace_len));
1170 memcpy (tmp, replace, replace_len);
1171 } else if (search_len < replace_len) {
1172 g_string_insert_len (string,
1173 (gssize) search_idx,
1174 replace,
1175 (gssize) (replace_len - search_len));
1176 /* we have to treat this specially as it could have
1177 * been reallocated when the insertion happened */
1178 memcpy (string->str + search_idx, replace, replace_len);
1179 } else {
1180 /* just memcmp in the new string */
1181 memcpy (tmp, replace, replace_len);
1182 }
1183 search_idx += replace_len;
1184 count++;
1185 } while (TRUE);
1186
1187 return count;
1188}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001189
Richard Hughesae96a1f2019-09-23 11:16:36 +01001190/**
1191 * fu_common_strwidth:
1192 * @text: The string to operate on
1193 *
1194 * Returns the width of the string in displayed characters on the console.
1195 *
1196 * Returns: width of text
1197 *
1198 * Since: 1.3.2
1199 **/
1200gsize
1201fu_common_strwidth (const gchar *text)
1202{
1203 const gchar *p = text;
1204 gsize width = 0;
1205 while (*p) {
1206 gunichar c = g_utf8_get_char (p);
1207 if (g_unichar_iswide (c))
1208 width += 2;
1209 else if (!g_unichar_iszerowidth (c))
1210 width += 1;
1211 p = g_utf8_next_char (p);
1212 }
1213 return width;
1214}
1215
Mario Limonciello1a680f32019-11-25 19:44:53 -06001216/**
1217 * fu_common_string_append_kv:
1218 * @str: A #GString
1219 * @idt: The indent
1220 * @key: A string to append
1221 * @value: a string to append
1222 *
1223 * Appends a key and string value to a string
1224 *
1225 * Since: 1.2.4
1226 */
Richard Hughescea28de2019-08-09 11:16:40 +01001227void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001228fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001229{
Richard Hughes847cae82019-08-27 11:22:23 +01001230 const guint align = 25;
1231 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001232
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001233 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001234
1235 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001236 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001237 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001238 for (gsize i = 0; i < idt; i++)
1239 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001240 if (key[0] != '\0') {
1241 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001242 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001243 } else {
1244 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001245 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001246 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001247 g_auto(GStrv) split = NULL;
1248 split = g_strsplit (value, "\n", -1);
1249 for (guint i = 0; split[i] != NULL; i++) {
1250 if (i == 0) {
1251 for (gsize j = keysz; j < align; j++)
1252 g_string_append (str, " ");
1253 } else {
1254 for (gsize j = 0; j < idt; j++)
1255 g_string_append (str, " ");
1256 }
1257 g_string_append (str, split[i]);
1258 g_string_append (str, "\n");
1259 }
1260 } else {
1261 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001262 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001263}
1264
Mario Limonciello1a680f32019-11-25 19:44:53 -06001265/**
1266 * fu_common_string_append_ku:
1267 * @str: A #GString
1268 * @idt: The indent
1269 * @key: A string to append
1270 * @value: guint64
1271 *
1272 * Appends a key and unsigned integer to a string
1273 *
1274 * Since: 1.2.4
1275 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001276void
1277fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1278{
1279 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1280 fu_common_string_append_kv (str, idt, key, tmp);
1281}
1282
Mario Limonciello1a680f32019-11-25 19:44:53 -06001283/**
1284 * fu_common_string_append_kx:
1285 * @str: A #GString
1286 * @idt: The indent
1287 * @key: A string to append
1288 * @value: guint64
1289 *
1290 * Appends a key and hex integer to a string
1291 *
1292 * Since: 1.2.4
1293 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001294void
1295fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1296{
1297 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1298 fu_common_string_append_kv (str, idt, key, tmp);
1299}
1300
Mario Limonciello1a680f32019-11-25 19:44:53 -06001301/**
1302 * fu_common_string_append_kb:
1303 * @str: A #GString
1304 * @idt: The indent
1305 * @key: A string to append
1306 * @value: Boolean
1307 *
1308 * Appends a key and boolean value to a string
1309 *
1310 * Since: 1.2.4
1311 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001312void
1313fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1314{
1315 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001316}
1317
Richard Hughese59cb9a2018-12-05 14:37:40 +00001318/**
Richard Hughes35481862019-01-06 12:01:58 +00001319 * fu_common_dump_full:
1320 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1321 * @title: prefix title, or %NULL
1322 * @data: buffer to print
1323 * @len: the size of @data
1324 * @columns: break new lines after this many bytes
1325 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1326 *
1327 * Dumps a raw buffer to the screen.
1328 *
1329 * Since: 1.2.4
1330 **/
1331void
1332fu_common_dump_full (const gchar *log_domain,
1333 const gchar *title,
1334 const guint8 *data,
1335 gsize len,
1336 guint columns,
1337 FuDumpFlags flags)
1338{
1339 g_autoptr(GString) str = g_string_new (NULL);
1340
1341 /* optional */
1342 if (title != NULL)
1343 g_string_append_printf (str, "%s:", title);
1344
1345 /* if more than can fit on one line then start afresh */
1346 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1347 g_string_append (str, "\n");
1348 } else {
1349 for (gsize i = str->len; i < 16; i++)
1350 g_string_append (str, " ");
1351 }
1352
1353 /* offset line */
1354 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1355 g_string_append (str, " │ ");
1356 for (gsize i = 0; i < columns; i++)
1357 g_string_append_printf (str, "%02x ", (guint) i);
1358 g_string_append (str, "\n───────┼");
1359 for (gsize i = 0; i < columns; i++)
1360 g_string_append (str, "───");
1361 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1362 }
1363
1364 /* print each row */
1365 for (gsize i = 0; i < len; i++) {
1366 g_string_append_printf (str, "%02x ", data[i]);
1367
1368 /* optionally print ASCII char */
1369 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1370 if (g_ascii_isprint (data[i]))
1371 g_string_append_printf (str, "[%c] ", data[i]);
1372 else
1373 g_string_append (str, "[?] ");
1374 }
1375
1376 /* new row required */
1377 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1378 g_string_append (str, "\n");
1379 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1380 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1381 }
1382 }
1383 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1384}
1385
1386/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001387 * fu_common_dump_raw:
1388 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1389 * @title: prefix title, or %NULL
1390 * @data: buffer to print
1391 * @len: the size of @data
1392 *
1393 * Dumps a raw buffer to the screen.
1394 *
1395 * Since: 1.2.2
1396 **/
1397void
1398fu_common_dump_raw (const gchar *log_domain,
1399 const gchar *title,
1400 const guint8 *data,
1401 gsize len)
1402{
Richard Hughes35481862019-01-06 12:01:58 +00001403 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1404 if (len > 64)
1405 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1406 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001407}
1408
1409/**
Mario Limonciello39602652019-04-29 21:08:58 -05001410 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001411 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1412 * @title: prefix title, or %NULL
1413 * @bytes: a #GBytes
1414 *
1415 * Dumps a byte buffer to the screen.
1416 *
1417 * Since: 1.2.2
1418 **/
1419void
1420fu_common_dump_bytes (const gchar *log_domain,
1421 const gchar *title,
1422 GBytes *bytes)
1423{
1424 gsize len = 0;
1425 const guint8 *data = g_bytes_get_data (bytes, &len);
1426 fu_common_dump_raw (log_domain, title, data, len);
1427}
Richard Hughesfc90f392019-01-15 21:21:16 +00001428
1429/**
1430 * fu_common_bytes_align:
1431 * @bytes: a #GBytes
1432 * @blksz: block size in bytes
1433 * @padval: the byte used to pad the byte buffer
1434 *
1435 * Aligns a block of memory to @blksize using the @padval value; if
1436 * the block is already aligned then the original @bytes is returned.
1437 *
1438 * Returns: (transfer full): a #GBytes, possibly @bytes
1439 *
1440 * Since: 1.2.4
1441 **/
1442GBytes *
1443fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1444{
1445 const guint8 *data;
1446 gsize sz;
1447
1448 g_return_val_if_fail (bytes != NULL, NULL);
1449 g_return_val_if_fail (blksz > 0, NULL);
1450
1451 /* pad */
1452 data = g_bytes_get_data (bytes, &sz);
1453 if (sz % blksz != 0) {
1454 gsize sz_align = ((sz / blksz) + 1) * blksz;
1455 guint8 *data_align = g_malloc (sz_align);
1456 memcpy (data_align, data, sz);
1457 memset (data_align + sz, padval, sz_align - sz);
1458 g_debug ("aligning 0x%x bytes to 0x%x",
1459 (guint) sz, (guint) sz_align);
1460 return g_bytes_new_take (data_align, sz_align);
1461 }
1462
1463 /* perfectly aligned */
1464 return g_bytes_ref (bytes);
1465}
Richard Hughes36999462019-03-19 20:23:29 +00001466
1467/**
1468 * fu_common_bytes_is_empty:
1469 * @bytes: a #GBytes
1470 *
1471 * Checks if a byte array are just empty (0xff) bytes.
1472 *
1473 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001474 *
1475 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001476 **/
1477gboolean
1478fu_common_bytes_is_empty (GBytes *bytes)
1479{
1480 gsize sz = 0;
1481 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1482 for (gsize i = 0; i < sz; i++) {
1483 if (buf[i] != 0xff)
1484 return FALSE;
1485 }
1486 return TRUE;
1487}
Richard Hughes2aad1042019-03-21 09:03:32 +00001488
1489/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001490 * fu_common_bytes_compare_raw:
1491 * @buf1: a buffer
1492 * @bufsz1: sizeof @buf1
1493 * @buf2: another buffer
1494 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001495 * @error: A #GError or %NULL
1496 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001497 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001498 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001499 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001500 *
1501 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001502 **/
1503gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001504fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1505 const guint8 *buf2, gsize bufsz2,
1506 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001507{
Richard Hughes38245ff2019-09-18 14:46:09 +01001508 g_return_val_if_fail (buf1 != NULL, FALSE);
1509 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001510 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1511
1512 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001513 if (bufsz1 != bufsz2) {
1514 g_set_error (error,
1515 G_IO_ERROR,
1516 G_IO_ERROR_INVALID_DATA,
1517 "got %" G_GSIZE_FORMAT " bytes, expected "
1518 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1519 return FALSE;
1520 }
1521
1522 /* check matches */
1523 for (guint i = 0x0; i < bufsz1; i++) {
1524 if (buf1[i] != buf2[i]) {
1525 g_set_error (error,
1526 G_IO_ERROR,
1527 G_IO_ERROR_INVALID_DATA,
1528 "got 0x%02x, expected 0x%02x @ 0x%04x",
1529 buf1[i], buf2[i], i);
1530 return FALSE;
1531 }
1532 }
1533
1534 /* success */
1535 return TRUE;
1536}
Richard Hughes484ee292019-03-22 16:10:50 +00001537
1538/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001539 * fu_common_bytes_compare:
1540 * @bytes1: a #GBytes
1541 * @bytes2: another #GBytes
1542 * @error: A #GError or %NULL
1543 *
1544 * Compares the buffers for equality.
1545 *
1546 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001547 *
1548 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001549 **/
1550gboolean
1551fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1552{
1553 const guint8 *buf1;
1554 const guint8 *buf2;
1555 gsize bufsz1;
1556 gsize bufsz2;
1557
1558 g_return_val_if_fail (bytes1 != NULL, FALSE);
1559 g_return_val_if_fail (bytes2 != NULL, FALSE);
1560 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1561
1562 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1563 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1564 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1565}
1566
1567/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001568 * fu_common_bytes_pad:
1569 * @bytes: a #GBytes
1570 * @sz: the desired size in bytes
1571 *
1572 * Pads a GBytes to a given @sz with `0xff`.
1573 *
1574 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001575 *
1576 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001577 **/
1578GBytes *
1579fu_common_bytes_pad (GBytes *bytes, gsize sz)
1580{
1581 gsize bytes_sz;
1582
1583 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1584
1585 /* pad */
1586 bytes_sz = g_bytes_get_size (bytes);
1587 if (bytes_sz < sz) {
1588 const guint8 *data = g_bytes_get_data (bytes, NULL);
1589 guint8 *data_new = g_malloc (sz);
1590 memcpy (data_new, data, bytes_sz);
1591 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1592 return g_bytes_new_take (data_new, sz);
1593 }
1594
1595 /* exactly right */
1596 return g_bytes_ref (bytes);
1597}
1598
1599/**
Richard Hughes484ee292019-03-22 16:10:50 +00001600 * fu_common_realpath:
1601 * @filename: a filename
1602 * @error: A #GError or %NULL
1603 *
1604 * Finds the canonicalized absolute filename for a path.
1605 *
1606 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001607 *
1608 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001609 **/
1610gchar *
1611fu_common_realpath (const gchar *filename, GError **error)
1612{
1613 char full_tmp[PATH_MAX];
1614
1615 g_return_val_if_fail (filename != NULL, NULL);
1616
Richard Hughes8694dee2019-11-22 09:16:34 +00001617#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001618 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001619#else
1620 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1621#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001622 g_set_error (error,
1623 G_IO_ERROR,
1624 G_IO_ERROR_INVALID_DATA,
1625 "cannot resolve path: %s",
1626 strerror (errno));
1627 return NULL;
1628 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001629 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1630 g_set_error (error,
1631 G_IO_ERROR,
1632 G_IO_ERROR_INVALID_DATA,
1633 "cannot find path: %s",
1634 full_tmp);
1635 return NULL;
1636 }
Richard Hughes484ee292019-03-22 16:10:50 +00001637 return g_strdup (full_tmp);
1638}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001639
1640/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001641 * fu_common_fnmatch:
1642 * @pattern: a glob pattern, e.g. `*foo*`
1643 * @str: a string to match against the pattern, e.g. `bazfoobar`
1644 *
1645 * Matches a string against a glob pattern.
1646 *
1647 * Return value: %TRUE if the string matched
1648 *
1649 * Since: 1.3.5
1650 **/
1651gboolean
1652fu_common_fnmatch (const gchar *pattern, const gchar *str)
1653{
1654 g_return_val_if_fail (pattern != NULL, FALSE);
1655 g_return_val_if_fail (str != NULL, FALSE);
1656#ifdef HAVE_FNMATCH_H
1657 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
1658#else
1659 return g_strcmp0 (pattern, str) == 0;
1660#endif
1661}
1662
1663/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001664 * fu_common_strnsplit:
1665 * @str: a string to split
1666 * @sz: size of @str
1667 * @delimiter: a string which specifies the places at which to split the string
1668 * @max_tokens: the maximum number of pieces to split @str into
1669 *
1670 * Splits a string into a maximum of @max_tokens pieces, using the given
1671 * delimiter. If @max_tokens is reached, the remainder of string is appended
1672 * to the last token.
1673 *
1674 * Return value: a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001675 *
1676 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001677 **/
1678gchar **
1679fu_common_strnsplit (const gchar *str, gsize sz,
1680 const gchar *delimiter, gint max_tokens)
1681{
1682 if (str[sz - 1] != '\0') {
1683 g_autofree gchar *str2 = g_strndup (str, sz);
1684 return g_strsplit (str2, delimiter, max_tokens);
1685 }
1686 return g_strsplit (str, delimiter, max_tokens);
1687}
Richard Hughes5308ea42019-08-09 12:25:13 +01001688
1689/**
1690 * fu_memcpy_safe:
1691 * @dst: destination buffer
1692 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1693 * @dst_offset: offset in bytes into @dst to copy to
1694 * @src: source buffer
1695 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1696 * @src_offset: offset in bytes into @src to copy from
1697 * @n: number of bytes to copy from @src+@offset from
1698 * @error: A #GError or %NULL
1699 *
1700 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1701 * of both the destination and the source allows us to check for buffer overflow.
1702 *
1703 * Providing the buffer offsets also allows us to check reading past the end of
1704 * the source buffer. For this reason the caller should NEVER add an offset to
1705 * @src or @dst.
1706 *
1707 * You don't need to use this function in "obviously correct" cases, nor should
1708 * you use it when performance is a concern. Only us it when you're not sure if
1709 * malicious data from a device or firmware could cause memory corruption.
1710 *
1711 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001712 *
1713 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001714 **/
1715gboolean
1716fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1717 const guint8 *src, gsize src_sz, gsize src_offset,
1718 gsize n, GError **error)
1719{
1720 if (n == 0)
1721 return TRUE;
1722
1723 if (n > src_sz) {
1724 g_set_error (error,
1725 FWUPD_ERROR,
1726 FWUPD_ERROR_READ,
1727 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1728 (guint) n, (guint) src_sz);
1729 return FALSE;
1730 }
1731 if (n + src_offset > src_sz) {
1732 g_set_error (error,
1733 FWUPD_ERROR,
1734 FWUPD_ERROR_READ,
1735 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1736 (guint) n, (guint) src_offset, (guint) src_sz);
1737 return FALSE;
1738 }
1739 if (n > dst_sz) {
1740 g_set_error (error,
1741 FWUPD_ERROR,
1742 FWUPD_ERROR_WRITE,
1743 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1744 (guint) n, (guint) dst_sz);
1745 return FALSE;
1746 }
1747 if (n + dst_offset > dst_sz) {
1748 g_set_error (error,
1749 FWUPD_ERROR,
1750 FWUPD_ERROR_WRITE,
1751 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1752 (guint) n, (guint) dst_offset, (guint) dst_sz);
1753 return FALSE;
1754 }
1755
1756 /* phew! */
1757 memcpy (dst + dst_offset, src + src_offset, n);
1758 return TRUE;
1759}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001760
Richard Hughes80768f52019-10-22 07:19:14 +01001761/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001762 * fu_common_read_uint8_safe:
1763 * @buf: source buffer
1764 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1765 * @offset: offset in bytes into @buf to copy from
1766 * @value: (out) (allow-none): the parsed value
1767 * @error: A #GError or %NULL
1768 *
1769 * Read a value from a buffer in a safe way.
1770 *
1771 * You don't need to use this function in "obviously correct" cases, nor should
1772 * you use it when performance is a concern. Only us it when you're not sure if
1773 * malicious data from a device or firmware could cause memory corruption.
1774 *
1775 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001776 *
1777 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001778 **/
1779gboolean
1780fu_common_read_uint8_safe (const guint8 *buf,
1781 gsize bufsz,
1782 gsize offset,
1783 guint8 *value,
1784 GError **error)
1785{
1786 guint8 tmp;
1787 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1788 buf, bufsz, offset, /* src */
1789 sizeof(tmp), error))
1790 return FALSE;
1791 if (value != NULL)
1792 *value = tmp;
1793 return TRUE;
1794}
1795
1796/**
Richard Hughes80768f52019-10-22 07:19:14 +01001797 * fu_common_read_uint16_safe:
1798 * @buf: source buffer
1799 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1800 * @offset: offset in bytes into @buf to copy from
1801 * @value: (out) (allow-none): the parsed value
1802 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1803 * @error: A #GError or %NULL
1804 *
1805 * Read a value from a buffer using a specified endian in a safe way.
1806 *
1807 * You don't need to use this function in "obviously correct" cases, nor should
1808 * you use it when performance is a concern. Only us it when you're not sure if
1809 * malicious data from a device or firmware could cause memory corruption.
1810 *
1811 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001812 *
1813 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001814 **/
1815gboolean
1816fu_common_read_uint16_safe (const guint8 *buf,
1817 gsize bufsz,
1818 gsize offset,
1819 guint16 *value,
1820 FuEndianType endian,
1821 GError **error)
1822{
1823 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001824 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001825 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001826 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001827 return FALSE;
1828 if (value != NULL)
1829 *value = fu_common_read_uint16 (dst, endian);
1830 return TRUE;
1831}
1832
1833/**
1834 * fu_common_read_uint32_safe:
1835 * @buf: source buffer
1836 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1837 * @offset: offset in bytes into @buf to copy from
1838 * @value: (out) (allow-none): the parsed value
1839 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1840 * @error: A #GError or %NULL
1841 *
1842 * Read a value from a buffer using a specified endian in a safe way.
1843 *
1844 * You don't need to use this function in "obviously correct" cases, nor should
1845 * you use it when performance is a concern. Only us it when you're not sure if
1846 * malicious data from a device or firmware could cause memory corruption.
1847 *
1848 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001849 *
1850 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001851 **/
1852gboolean
1853fu_common_read_uint32_safe (const guint8 *buf,
1854 gsize bufsz,
1855 gsize offset,
1856 guint32 *value,
1857 FuEndianType endian,
1858 GError **error)
1859{
1860 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001861 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001862 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001863 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001864 return FALSE;
1865 if (value != NULL)
1866 *value = fu_common_read_uint32 (dst, endian);
1867 return TRUE;
1868}
1869
Mario Limonciello1a680f32019-11-25 19:44:53 -06001870/**
1871 * fu_byte_array_append_uint8:
1872 * @array: A #GByteArray
1873 * @data: #guint8
1874 *
1875 * Adds a 8 bit integer to a byte array
1876 *
1877 * Since: 1.3.1
1878 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001879void
1880fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1881{
1882 g_byte_array_append (array, &data, sizeof(data));
1883}
1884
Mario Limonciello1a680f32019-11-25 19:44:53 -06001885/**
1886 * fu_byte_array_append_uint16:
1887 * @array: A #GByteArray
1888 * @data: #guint16
1889 * @endian: #FuEndianType
1890 *
1891 * Adds a 16 bit integer to a byte array
1892 *
1893 * Since: 1.3.1
1894 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001895void
1896fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1897{
1898 guint8 buf[2];
1899 fu_common_write_uint16 (buf, data, endian);
1900 g_byte_array_append (array, buf, sizeof(buf));
1901}
1902
Mario Limonciello1a680f32019-11-25 19:44:53 -06001903/**
1904 * fu_byte_array_append_uint32:
1905 * @array: A #GByteArray
1906 * @data: #guint32
1907 * @endian: #FuEndianType
1908 *
1909 * Adds a 32 bit integer to a byte array
1910 *
1911 * Since: 1.3.1
1912 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001913void
1914fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1915{
1916 guint8 buf[4];
1917 fu_common_write_uint32 (buf, data, endian);
1918 g_byte_array_append (array, buf, sizeof(buf));
1919}