blob: d0592ef32c1a12da523f4683a9841606fccac655 [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 Hughes94f939a2017-08-08 12:21:39 +010022#include <archive_entry.h>
23#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010024#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000025#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000026#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000027#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010028
29#include "fwupd-error.h"
30
31#include "fu-common.h"
32
33/**
Richard Hughes4eada342017-10-03 21:20:32 +010034 * SECTION:fu-common
35 * @short_description: common functionality for plugins to use
36 *
37 * Helper functions that can be used by the daemon and plugins.
38 *
39 * See also: #FuPlugin
40 */
41
42/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010043 * fu_common_rmtree:
44 * @directory: a directory name
45 * @error: A #GError or %NULL
46 *
47 * Recursively removes a directory.
48 *
49 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060050 *
51 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010052 **/
53gboolean
54fu_common_rmtree (const gchar *directory, GError **error)
55{
56 const gchar *filename;
57 g_autoptr(GDir) dir = NULL;
58
59 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010060 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010061 dir = g_dir_open (directory, 0, error);
62 if (dir == NULL)
63 return FALSE;
64
65 /* find each */
66 while ((filename = g_dir_read_name (dir))) {
67 g_autofree gchar *src = NULL;
68 src = g_build_filename (directory, filename, NULL);
69 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
70 if (!fu_common_rmtree (src, error))
71 return FALSE;
72 } else {
73 if (g_unlink (src) != 0) {
74 g_set_error (error,
75 FWUPD_ERROR,
76 FWUPD_ERROR_INTERNAL,
77 "Failed to delete: %s", src);
78 return FALSE;
79 }
80 }
81 }
82 if (g_remove (directory) != 0) {
83 g_set_error (error,
84 FWUPD_ERROR,
85 FWUPD_ERROR_INTERNAL,
86 "Failed to delete: %s", directory);
87 return FALSE;
88 }
89 return TRUE;
90}
91
Richard Hughes89e968b2018-03-07 10:01:08 +000092static gboolean
93fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
94{
95 const gchar *filename;
96 g_autoptr(GDir) dir = NULL;
97
98 /* try to open */
99 dir = g_dir_open (directory, 0, error);
100 if (dir == NULL)
101 return FALSE;
102
103 /* find each */
104 while ((filename = g_dir_read_name (dir))) {
105 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
106 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
107 if (!fu_common_get_file_list_internal (files, src, error))
108 return FALSE;
109 } else {
110 g_ptr_array_add (files, g_steal_pointer (&src));
111 }
112 }
113 return TRUE;
114
115}
116
117/**
118 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100119 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000120 * @error: A #GError or %NULL
121 *
122 * Returns every file found under @directory, and any subdirectory.
123 * If any path under @directory cannot be accessed due to permissions an error
124 * will be returned.
125 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000126 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600127 *
128 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000129 **/
130GPtrArray *
131fu_common_get_files_recursive (const gchar *path, GError **error)
132{
133 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
134 if (!fu_common_get_file_list_internal (files, path, error))
135 return NULL;
136 return g_steal_pointer (&files);
137}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100138/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100139 * fu_common_mkdir_parent:
140 * @filename: A full pathname
141 * @error: A #GError, or %NULL
142 *
143 * Creates any required directories, including any parent directories.
144 *
145 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600146 *
147 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100148 **/
149gboolean
150fu_common_mkdir_parent (const gchar *filename, GError **error)
151{
152 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100153
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100154 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100155 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100156 if (g_mkdir_with_parents (parent, 0755) == -1) {
157 g_set_error (error,
158 FWUPD_ERROR,
159 FWUPD_ERROR_INTERNAL,
160 "Failed to create '%s': %s",
161 parent, g_strerror (errno));
162 return FALSE;
163 }
164 return TRUE;
165}
166
167/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100168 * fu_common_set_contents_bytes:
169 * @filename: A filename
170 * @bytes: The data to write
171 * @error: A #GError, or %NULL
172 *
173 * Writes a blob of data to a filename, creating the parent directories as
174 * required.
175 *
176 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600177 *
178 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100179 **/
180gboolean
181fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
182{
183 const gchar *data;
184 gsize size;
185 g_autoptr(GFile) file = NULL;
186 g_autoptr(GFile) file_parent = NULL;
187
188 file = g_file_new_for_path (filename);
189 file_parent = g_file_get_parent (file);
190 if (!g_file_query_exists (file_parent, NULL)) {
191 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
192 return FALSE;
193 }
194 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100195 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100196 return g_file_set_contents (filename, data, size, error);
197}
198
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100199/**
200 * fu_common_get_contents_bytes:
201 * @filename: A filename
202 * @error: A #GError, or %NULL
203 *
204 * Reads a blob of data from a file.
205 *
206 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600207 *
208 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100209 **/
210GBytes *
211fu_common_get_contents_bytes (const gchar *filename, GError **error)
212{
213 gchar *data = NULL;
214 gsize len = 0;
215 if (!g_file_get_contents (filename, &data, &len, error))
216 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100217 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100218 return g_bytes_new_take (data, len);
219}
Richard Hughes943d2c92017-06-21 09:04:39 +0100220
221/**
222 * fu_common_get_contents_fd:
223 * @fd: A file descriptor
224 * @count: The maximum number of bytes to read
225 * @error: A #GError, or %NULL
226 *
227 * Reads a blob from a specific file descriptor.
228 *
229 * Note: this will close the fd when done
230 *
Richard Hughes4eada342017-10-03 21:20:32 +0100231 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600232 *
233 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100234 **/
235GBytes *
236fu_common_get_contents_fd (gint fd, gsize count, GError **error)
237{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000238#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100239 g_autoptr(GBytes) blob = NULL;
240 g_autoptr(GError) error_local = NULL;
241 g_autoptr(GInputStream) stream = NULL;
242
243 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100244 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
245
Richard Hughes919f8ab2018-02-14 10:24:56 +0000246 /* this is invalid */
247 if (count == 0) {
248 g_set_error_literal (error,
249 FWUPD_ERROR,
250 FWUPD_ERROR_NOT_SUPPORTED,
251 "A maximum read size must be specified");
252 return NULL;
253 }
254
Richard Hughes943d2c92017-06-21 09:04:39 +0100255 /* read the entire fd to a data blob */
256 stream = g_unix_input_stream_new (fd, TRUE);
257 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
258 if (blob == NULL) {
259 g_set_error_literal (error,
260 FWUPD_ERROR,
261 FWUPD_ERROR_INVALID_FILE,
262 error_local->message);
263 return NULL;
264 }
265 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000266#else
267 g_set_error_literal (error,
268 FWUPD_ERROR,
269 FWUPD_ERROR_NOT_SUPPORTED,
270 "Not supported as <glib-unix.h> is unavailable");
271 return NULL;
272#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100273}
Richard Hughes94f939a2017-08-08 12:21:39 +0100274
275static gboolean
276fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
277{
278 const gchar *tmp;
279 g_autofree gchar *buf = NULL;
280
281 /* no output file */
282 if (archive_entry_pathname (entry) == NULL)
283 return FALSE;
284
285 /* update output path */
286 tmp = archive_entry_pathname (entry);
287 buf = g_build_filename (dir, tmp, NULL);
288 archive_entry_update_pathname_utf8 (entry, buf);
289 return TRUE;
290}
291
292/**
293 * fu_common_extract_archive:
294 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100295 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100296 * @error: A #GError, or %NULL
297 *
298 * Extracts an achive to a directory.
299 *
300 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600301 *
302 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100303 **/
304gboolean
305fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
306{
307 gboolean ret = TRUE;
308 int r;
309 struct archive *arch = NULL;
310 struct archive_entry *entry;
311
312 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100313 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100314 arch = archive_read_new ();
315 archive_read_support_format_all (arch);
316 archive_read_support_filter_all (arch);
317 r = archive_read_open_memory (arch,
318 (void *) g_bytes_get_data (blob, NULL),
319 (size_t) g_bytes_get_size (blob));
320 if (r != 0) {
321 ret = FALSE;
322 g_set_error (error,
323 FWUPD_ERROR,
324 FWUPD_ERROR_INTERNAL,
325 "Cannot open: %s",
326 archive_error_string (arch));
327 goto out;
328 }
329 for (;;) {
330 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100331 r = archive_read_next_header (arch, &entry);
332 if (r == ARCHIVE_EOF)
333 break;
334 if (r != ARCHIVE_OK) {
335 ret = FALSE;
336 g_set_error (error,
337 FWUPD_ERROR,
338 FWUPD_ERROR_INTERNAL,
339 "Cannot read header: %s",
340 archive_error_string (arch));
341 goto out;
342 }
343
344 /* only extract if valid */
345 valid = fu_common_extract_archive_entry (entry, dir);
346 if (!valid)
347 continue;
348 r = archive_read_extract (arch, entry, 0);
349 if (r != ARCHIVE_OK) {
350 ret = FALSE;
351 g_set_error (error,
352 FWUPD_ERROR,
353 FWUPD_ERROR_INTERNAL,
354 "Cannot extract: %s",
355 archive_error_string (arch));
356 goto out;
357 }
358 }
359out:
360 if (arch != NULL) {
361 archive_read_close (arch);
362 archive_read_free (arch);
363 }
364 return ret;
365}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100366
367static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300368fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
369
370static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100371fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
372{
373 va_list args;
374 g_autofree gchar *tmp = NULL;
375 g_auto(GStrv) split = NULL;
376
377 va_start (args, fmt);
378 tmp = g_strdup_vprintf (fmt, args);
379 va_end (args);
380
381 split = g_strsplit (tmp, " ", -1);
382 for (guint i = 0; split[i] != NULL; i++)
383 g_ptr_array_add (argv, g_strdup (split[i]));
384}
385
Mario Limonciello1a680f32019-11-25 19:44:53 -0600386/**
387 * fu_common_find_program_in_path:
388 * @basename: The program to search
389 * @error: A #GError, or %NULL
390 *
391 * Looks for a program in the PATH variable
392 *
393 * Returns: a new #gchar, or %NULL for error
394 *
395 * Since: 1.1.2
396 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100397gchar *
398fu_common_find_program_in_path (const gchar *basename, GError **error)
399{
400 gchar *fn = g_find_program_in_path (basename);
401 if (fn == NULL) {
402 g_set_error (error,
403 FWUPD_ERROR,
404 FWUPD_ERROR_NOT_SUPPORTED,
405 "missing executable %s in PATH",
406 basename);
407 return NULL;
408 }
409 return fn;
410}
411
412static gboolean
413fu_common_test_namespace_support (GError **error)
414{
415 /* test if CONFIG_USER_NS is valid */
416 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
417 g_set_error (error,
418 FWUPD_ERROR,
419 FWUPD_ERROR_NOT_SUPPORTED,
420 "missing CONFIG_USER_NS in kernel");
421 return FALSE;
422 }
423 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
424 g_autofree gchar *clone = NULL;
425 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
426 return FALSE;
427 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
428 g_set_error (error,
429 FWUPD_ERROR,
430 FWUPD_ERROR_NOT_SUPPORTED,
431 "unprivileged user namespace clones disabled by distro");
432 return FALSE;
433 }
434 }
435 return TRUE;
436}
437
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100438/**
439 * fu_common_firmware_builder:
440 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100441 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
442 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100443 * @error: A #GError, or %NULL
444 *
445 * Builds a firmware file using tools from the host session in a bubblewrap
446 * jail. Several things happen during build:
447 *
448 * 1. The @bytes data is untarred to a temporary location
449 * 2. A bubblewrap container is set up
450 * 3. The startup.sh script is run inside the container
451 * 4. The firmware.bin is extracted from the container
452 * 5. The temporary location is deleted
453 *
454 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600455 *
456 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100457 **/
458GBytes *
459fu_common_firmware_builder (GBytes *bytes,
460 const gchar *script_fn,
461 const gchar *output_fn,
462 GError **error)
463{
464 gint rc = 0;
465 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500466 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100467 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100468 g_autofree gchar *localstatedir = NULL;
469 g_autofree gchar *output2_fn = NULL;
470 g_autofree gchar *standard_error = NULL;
471 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100472 g_autofree gchar *tmpdir = NULL;
473 g_autoptr(GBytes) firmware_blob = NULL;
474 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
475
476 g_return_val_if_fail (bytes != NULL, NULL);
477 g_return_val_if_fail (script_fn != NULL, NULL);
478 g_return_val_if_fail (output_fn != NULL, NULL);
479 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
480
Mario Limonciello37b59582018-08-13 08:38:01 -0500481 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100482 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
483 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100484 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500485
486 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100487 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100488 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500489
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100490 /* untar file to temp location */
491 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
492 if (tmpdir == NULL)
493 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100494 if (!fu_common_extract_archive (bytes, tmpdir, error))
495 return NULL;
496
497 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100498 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
499 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100500
501 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500502 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100503 fu_common_add_argv (argv, "--die-with-parent");
504 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500505 fu_common_add_argv (argv, "--ro-bind /lib /lib");
506 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
507 fu_common_add_argv (argv, "--ro-bind /bin /bin");
508 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100509 fu_common_add_argv (argv, "--dir /tmp");
510 fu_common_add_argv (argv, "--dir /var");
511 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100512 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
513 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100514 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100515 fu_common_add_argv (argv, "--chdir /tmp");
516 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100517 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100518 g_ptr_array_add (argv, NULL);
519 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
520 g_debug ("running '%s' in %s", argv_str, tmpdir);
521 if (!g_spawn_sync ("/tmp",
522 (gchar **) argv->pdata,
523 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100524 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100525 NULL, NULL, /* child_setup */
526 &standard_output,
527 &standard_error,
528 &rc,
529 error)) {
530 g_prefix_error (error, "failed to run '%s': ", argv_str);
531 return NULL;
532 }
533 if (standard_output != NULL && standard_output[0] != '\0')
534 g_debug ("console output was: %s", standard_output);
535 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500536 FwupdError code = FWUPD_ERROR_INTERNAL;
537 if (errno == ENOTTY)
538 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100539 g_set_error (error,
540 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500541 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100542 "failed to build firmware: %s",
543 standard_error);
544 return NULL;
545 }
546
547 /* get generated file */
548 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
549 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
550 if (firmware_blob == NULL)
551 return NULL;
552
553 /* cleanup temp directory */
554 if (!fu_common_rmtree (tmpdir, error))
555 return NULL;
556
557 /* success */
558 return g_steal_pointer (&firmware_blob);
559}
Richard Hughes049ccc82017-08-09 15:26:56 +0100560
561typedef struct {
562 FuOutputHandler handler_cb;
563 gpointer handler_user_data;
564 GMainLoop *loop;
565 GSource *source;
566 GInputStream *stream;
567 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000568 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100569} FuCommonSpawnHelper;
570
571static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
572
573static gboolean
574fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
575{
576 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
577 gchar buffer[1024];
578 gssize sz;
579 g_auto(GStrv) split = NULL;
580 g_autoptr(GError) error = NULL;
581
582 /* read from stream */
583 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
584 buffer,
585 sizeof(buffer) - 1,
586 NULL,
587 &error);
588 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100589 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
590 g_warning ("failed to get read from nonblocking fd: %s",
591 error->message);
592 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100593 return G_SOURCE_REMOVE;
594 }
595
596 /* no read possible */
597 if (sz == 0)
598 g_main_loop_quit (helper->loop);
599
600 /* emit lines */
601 if (helper->handler_cb != NULL) {
602 buffer[sz] = '\0';
603 split = g_strsplit (buffer, "\n", -1);
604 for (guint i = 0; split[i] != NULL; i++) {
605 if (split[i][0] == '\0')
606 continue;
607 helper->handler_cb (split[i], helper->handler_user_data);
608 }
609 }
610
611 /* set up the source for the next read */
612 fu_common_spawn_create_pollable_source (helper);
613 return G_SOURCE_REMOVE;
614}
615
616static void
617fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
618{
619 if (helper->source != NULL)
620 g_source_destroy (helper->source);
621 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
622 helper->cancellable);
623 g_source_attach (helper->source, NULL);
624 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
625}
626
627static void
628fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
629{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000630 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100631 if (helper->stream != NULL)
632 g_object_unref (helper->stream);
633 if (helper->source != NULL)
634 g_source_destroy (helper->source);
635 if (helper->loop != NULL)
636 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000637 if (helper->timeout_id != 0)
638 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100639 g_free (helper);
640}
641
Mario Limoncielloa98df552018-04-16 12:15:51 -0500642#pragma clang diagnostic push
643#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100644G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500645#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100646
Richard Hughesb768e4d2019-02-26 13:55:18 +0000647static gboolean
648fu_common_spawn_timeout_cb (gpointer user_data)
649{
650 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
651 g_cancellable_cancel (helper->cancellable);
652 g_main_loop_quit (helper->loop);
653 helper->timeout_id = 0;
654 return G_SOURCE_REMOVE;
655}
656
657static void
658fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
659{
660 /* just propagate */
661 g_cancellable_cancel (helper->cancellable);
662}
663
Richard Hughes049ccc82017-08-09 15:26:56 +0100664/**
665 * fu_common_spawn_sync:
666 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100667 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
668 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000669 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100670 * @cancellable: a #GCancellable, or %NULL
671 * @error: A #GError or %NULL
672 *
673 * Runs a subprocess and waits for it to exit. Any output on standard out or
674 * standard error will be forwarded to @handler_cb as whole lines.
675 *
676 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600677 *
678 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100679 **/
680gboolean
681fu_common_spawn_sync (const gchar * const * argv,
682 FuOutputHandler handler_cb,
683 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000684 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100685 GCancellable *cancellable, GError **error)
686{
687 g_autoptr(FuCommonSpawnHelper) helper = NULL;
688 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100689 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000690 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100691
692 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100693 argv_str = g_strjoinv (" ", (gchar **) argv);
694 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100695 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
696 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
697 if (subprocess == NULL)
698 return FALSE;
699
700 /* watch for process to exit */
701 helper = g_new0 (FuCommonSpawnHelper, 1);
702 helper->handler_cb = handler_cb;
703 helper->handler_user_data = handler_user_data;
704 helper->loop = g_main_loop_new (NULL, FALSE);
705 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000706
707 /* always create a cancellable, and connect up the parent */
708 helper->cancellable = g_cancellable_new ();
709 if (cancellable != NULL) {
710 cancellable_id = g_cancellable_connect (cancellable,
711 G_CALLBACK (fu_common_spawn_cancelled_cb),
712 helper, NULL);
713 }
714
715 /* allow timeout */
716 if (timeout_ms > 0) {
717 helper->timeout_id = g_timeout_add (timeout_ms,
718 fu_common_spawn_timeout_cb,
719 helper);
720 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100721 fu_common_spawn_create_pollable_source (helper);
722 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000723 g_cancellable_disconnect (cancellable, cancellable_id);
724 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
725 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100726 return g_subprocess_wait_check (subprocess, cancellable, error);
727}
Richard Hughesae252cd2017-12-08 10:48:15 +0000728
729/**
730 * fu_common_write_uint16:
731 * @buf: A writable buffer
732 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100733 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000734 *
735 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600736 *
737 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000738 **/
739void
740fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
741{
742 guint16 val_hw;
743 switch (endian) {
744 case G_BIG_ENDIAN:
745 val_hw = GUINT16_TO_BE(val_native);
746 break;
747 case G_LITTLE_ENDIAN:
748 val_hw = GUINT16_TO_LE(val_native);
749 break;
750 default:
751 g_assert_not_reached ();
752 }
753 memcpy (buf, &val_hw, sizeof(val_hw));
754}
755
756/**
757 * fu_common_write_uint32:
758 * @buf: A writable buffer
759 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100760 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000761 *
762 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600763 *
764 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000765 **/
766void
767fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
768{
769 guint32 val_hw;
770 switch (endian) {
771 case G_BIG_ENDIAN:
772 val_hw = GUINT32_TO_BE(val_native);
773 break;
774 case G_LITTLE_ENDIAN:
775 val_hw = GUINT32_TO_LE(val_native);
776 break;
777 default:
778 g_assert_not_reached ();
779 }
780 memcpy (buf, &val_hw, sizeof(val_hw));
781}
782
783/**
784 * fu_common_read_uint16:
785 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100786 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000787 *
788 * Read a value from a buffer using a specified endian.
789 *
790 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600791 *
792 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000793 **/
794guint16
795fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
796{
797 guint16 val_hw, val_native;
798 memcpy (&val_hw, buf, sizeof(val_hw));
799 switch (endian) {
800 case G_BIG_ENDIAN:
801 val_native = GUINT16_FROM_BE(val_hw);
802 break;
803 case G_LITTLE_ENDIAN:
804 val_native = GUINT16_FROM_LE(val_hw);
805 break;
806 default:
807 g_assert_not_reached ();
808 }
809 return val_native;
810}
811
812/**
813 * fu_common_read_uint32:
814 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100815 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000816 *
817 * Read a value from a buffer using a specified endian.
818 *
819 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600820 *
821 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000822 **/
823guint32
824fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
825{
826 guint32 val_hw, val_native;
827 memcpy (&val_hw, buf, sizeof(val_hw));
828 switch (endian) {
829 case G_BIG_ENDIAN:
830 val_native = GUINT32_FROM_BE(val_hw);
831 break;
832 case G_LITTLE_ENDIAN:
833 val_native = GUINT32_FROM_LE(val_hw);
834 break;
835 default:
836 g_assert_not_reached ();
837 }
838 return val_native;
839}
Richard Hughese82eef32018-05-20 10:41:26 +0100840
Richard Hughes73bf2332018-08-28 09:38:09 +0100841/**
842 * fu_common_strtoull:
843 * @str: A string, e.g. "0x1234"
844 *
845 * Converts a string value to an integer. Values are assumed base 10, unless
846 * prefixed with "0x" where they are parsed as base 16.
847 *
848 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600849 *
850 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100851 **/
852guint64
853fu_common_strtoull (const gchar *str)
854{
855 guint base = 10;
856 if (str == NULL)
857 return 0x0;
858 if (g_str_has_prefix (str, "0x")) {
859 str += 2;
860 base = 16;
861 }
862 return g_ascii_strtoull (str, NULL, base);
863}
864
Richard Hughesa574a752018-08-31 13:31:03 +0100865/**
866 * fu_common_strstrip:
867 * @str: A string, e.g. " test "
868 *
869 * Removes leading and trailing whitespace from a constant string.
870 *
871 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600872 *
873 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100874 **/
875gchar *
876fu_common_strstrip (const gchar *str)
877{
878 guint head = G_MAXUINT;
879 guint tail = 0;
880
881 g_return_val_if_fail (str != NULL, NULL);
882
883 /* find first non-space char */
884 for (guint i = 0; str[i] != '\0'; i++) {
885 if (str[i] != ' ') {
886 head = i;
887 break;
888 }
889 }
890 if (head == G_MAXUINT)
891 return g_strdup ("");
892
893 /* find last non-space char */
894 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500895 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100896 tail = i;
897 }
898 return g_strndup (str + head, tail - head + 1);
899}
900
Richard Hughese82eef32018-05-20 10:41:26 +0100901static const GError *
902fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
903{
904 for (guint j = 0; j < errors->len; j++) {
905 const GError *error = g_ptr_array_index (errors, j);
906 if (g_error_matches (error, FWUPD_ERROR, error_code))
907 return error;
908 }
909 return NULL;
910}
911
912static guint
913fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
914{
915 guint cnt = 0;
916 for (guint j = 0; j < errors->len; j++) {
917 const GError *error = g_ptr_array_index (errors, j);
918 if (g_error_matches (error, FWUPD_ERROR, error_code))
919 cnt++;
920 }
921 return cnt;
922}
923
924static gboolean
925fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
926{
927 for (guint j = 0; j < errors->len; j++) {
928 const GError *error = g_ptr_array_index (errors, j);
929 gboolean matches_any = FALSE;
930 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
931 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
932 matches_any = TRUE;
933 break;
934 }
935 }
936 if (!matches_any)
937 return FALSE;
938 }
939 return TRUE;
940}
941
942/**
943 * fu_common_error_array_get_best:
944 * @errors: (element-type GError): array of errors
945 *
946 * Finds the 'best' error to show the user from a array of errors, creating a
947 * completely bespoke error where required.
948 *
949 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600950 *
951 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100952 **/
953GError *
954fu_common_error_array_get_best (GPtrArray *errors)
955{
956 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
957 FWUPD_ERROR_VERSION_SAME,
958 FWUPD_ERROR_VERSION_NEWER,
959 FWUPD_ERROR_NOT_SUPPORTED,
960 FWUPD_ERROR_INTERNAL,
961 FWUPD_ERROR_NOT_FOUND,
962 FWUPD_ERROR_LAST };
963 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
964 FWUPD_ERROR_NOT_FOUND,
965 FWUPD_ERROR_NOT_SUPPORTED,
966 FWUPD_ERROR_LAST };
967 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
968 FWUPD_ERROR_VERSION_SAME,
969 FWUPD_ERROR_NOT_FOUND,
970 FWUPD_ERROR_NOT_SUPPORTED,
971 FWUPD_ERROR_LAST };
972
973 /* are all the errors either GUID-not-matched or version-same? */
974 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
975 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
976 return g_error_new (FWUPD_ERROR,
977 FWUPD_ERROR_NOTHING_TO_DO,
978 "All updatable firmware is already installed");
979 }
980
981 /* are all the errors either GUID-not-matched or version same or newer? */
982 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
983 fu_common_error_array_matches_any (errors, err_all_newer)) {
984 return g_error_new (FWUPD_ERROR,
985 FWUPD_ERROR_NOTHING_TO_DO,
986 "All updatable devices already have newer versions");
987 }
988
989 /* get the most important single error */
990 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
991 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
992 if (error_tmp != NULL)
993 return g_error_copy (error_tmp);
994 }
995
996 /* fall back to something */
997 return g_error_new (FWUPD_ERROR,
998 FWUPD_ERROR_NOT_FOUND,
999 "No supported devices found");
1000}
Richard Hughes4be17d12018-05-30 20:36:29 +01001001
1002/**
1003 * fu_common_get_path:
1004 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1005 *
1006 * Gets a fwupd-specific system path. These can be overridden with various
1007 * environment variables, for instance %FWUPD_DATADIR.
1008 *
1009 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001010 *
1011 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001012 **/
1013gchar *
1014fu_common_get_path (FuPathKind path_kind)
1015{
1016 const gchar *tmp;
1017 g_autofree gchar *basedir = NULL;
1018
1019 switch (path_kind) {
1020 /* /var */
1021 case FU_PATH_KIND_LOCALSTATEDIR:
1022 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1023 if (tmp != NULL)
1024 return g_strdup (tmp);
1025 tmp = g_getenv ("SNAP_USER_DATA");
1026 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001027 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1028 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughes282b10d2018-06-22 14:48:00 +01001029 /* /sys/firmware */
1030 case FU_PATH_KIND_SYSFSDIR_FW:
1031 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1032 if (tmp != NULL)
1033 return g_strdup (tmp);
1034 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001035 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001036 case FU_PATH_KIND_SYSFSDIR_TPM:
1037 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1038 if (tmp != NULL)
1039 return g_strdup (tmp);
1040 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001041 /* /sys/bus/platform/drivers */
1042 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1043 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1044 if (tmp != NULL)
1045 return g_strdup (tmp);
1046 return g_strdup ("/sys/bus/platform/drivers");
Richard Hughes4be17d12018-05-30 20:36:29 +01001047 /* /etc */
1048 case FU_PATH_KIND_SYSCONFDIR:
1049 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1050 if (tmp != NULL)
1051 return g_strdup (tmp);
1052 tmp = g_getenv ("SNAP_USER_DATA");
1053 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001054 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1055 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001056 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1057 case FU_PATH_KIND_PLUGINDIR_PKG:
1058 tmp = g_getenv ("FWUPD_PLUGINDIR");
1059 if (tmp != NULL)
1060 return g_strdup (tmp);
1061 tmp = g_getenv ("SNAP");
1062 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001063 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1064 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001065 /* /usr/share/fwupd */
1066 case FU_PATH_KIND_DATADIR_PKG:
1067 tmp = g_getenv ("FWUPD_DATADIR");
1068 if (tmp != NULL)
1069 return g_strdup (tmp);
1070 tmp = g_getenv ("SNAP");
1071 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001072 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1073 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001074 /* /usr/libexec/fwupd/efi */
1075 case FU_PATH_KIND_EFIAPPDIR:
1076 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1077 if (tmp != NULL)
1078 return g_strdup (tmp);
1079#ifdef EFI_APP_LOCATION
1080 tmp = g_getenv ("SNAP");
1081 if (tmp != NULL)
1082 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1083 return g_strdup (EFI_APP_LOCATION);
1084#else
1085 return NULL;
1086#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001087 /* /etc/fwupd */
1088 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001089 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
1090 if (tmp != NULL)
1091 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001092 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1093 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1094 /* /var/lib/fwupd */
1095 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001096 tmp = g_getenv ("STATE_DIRECTORY");
1097 if (tmp != NULL)
1098 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001099 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1100 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1101 /* /var/cache/fwupd */
1102 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001103 tmp = g_getenv ("CACHE_DIRECTORY");
1104 if (tmp != NULL)
1105 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001106 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1107 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001108 case FU_PATH_KIND_OFFLINE_TRIGGER:
1109 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1110 if (tmp != NULL)
1111 return g_strdup (tmp);
1112 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001113 case FU_PATH_KIND_POLKIT_ACTIONS:
1114#ifdef POLKIT_ACTIONDIR
1115 return g_strdup (POLKIT_ACTIONDIR);
1116#else
1117 return NULL;
1118#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001119 /* this shouldn't happen */
1120 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001121 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001122 }
1123
1124 return NULL;
1125}
Richard Hughes83e56c12018-10-10 20:24:41 +01001126
1127/**
1128 * fu_common_string_replace:
1129 * @string: The #GString to operate on
1130 * @search: The text to search for
1131 * @replace: The text to use for substitutions
1132 *
1133 * Performs multiple search and replace operations on the given string.
1134 *
1135 * Returns: the number of replacements done, or 0 if @search is not found.
1136 *
1137 * Since: 1.2.0
1138 **/
1139guint
1140fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1141{
1142 gchar *tmp;
1143 guint count = 0;
1144 gsize search_idx = 0;
1145 gsize replace_len;
1146 gsize search_len;
1147
1148 g_return_val_if_fail (string != NULL, 0);
1149 g_return_val_if_fail (search != NULL, 0);
1150 g_return_val_if_fail (replace != NULL, 0);
1151
1152 /* nothing to do */
1153 if (string->len == 0)
1154 return 0;
1155
1156 search_len = strlen (search);
1157 replace_len = strlen (replace);
1158
1159 do {
1160 tmp = g_strstr_len (string->str + search_idx, -1, search);
1161 if (tmp == NULL)
1162 break;
1163
1164 /* advance the counter in case @replace contains @search */
1165 search_idx = (gsize) (tmp - string->str);
1166
1167 /* reallocate the string if required */
1168 if (search_len > replace_len) {
1169 g_string_erase (string,
1170 (gssize) search_idx,
1171 (gssize) (search_len - replace_len));
1172 memcpy (tmp, replace, replace_len);
1173 } else if (search_len < replace_len) {
1174 g_string_insert_len (string,
1175 (gssize) search_idx,
1176 replace,
1177 (gssize) (replace_len - search_len));
1178 /* we have to treat this specially as it could have
1179 * been reallocated when the insertion happened */
1180 memcpy (string->str + search_idx, replace, replace_len);
1181 } else {
1182 /* just memcmp in the new string */
1183 memcpy (tmp, replace, replace_len);
1184 }
1185 search_idx += replace_len;
1186 count++;
1187 } while (TRUE);
1188
1189 return count;
1190}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001191
Richard Hughesae96a1f2019-09-23 11:16:36 +01001192/**
1193 * fu_common_strwidth:
1194 * @text: The string to operate on
1195 *
1196 * Returns the width of the string in displayed characters on the console.
1197 *
1198 * Returns: width of text
1199 *
1200 * Since: 1.3.2
1201 **/
1202gsize
1203fu_common_strwidth (const gchar *text)
1204{
1205 const gchar *p = text;
1206 gsize width = 0;
1207 while (*p) {
1208 gunichar c = g_utf8_get_char (p);
1209 if (g_unichar_iswide (c))
1210 width += 2;
1211 else if (!g_unichar_iszerowidth (c))
1212 width += 1;
1213 p = g_utf8_next_char (p);
1214 }
1215 return width;
1216}
1217
Mario Limonciello1a680f32019-11-25 19:44:53 -06001218/**
1219 * fu_common_string_append_kv:
1220 * @str: A #GString
1221 * @idt: The indent
1222 * @key: A string to append
1223 * @value: a string to append
1224 *
1225 * Appends a key and string value to a string
1226 *
1227 * Since: 1.2.4
1228 */
Richard Hughescea28de2019-08-09 11:16:40 +01001229void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001230fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001231{
Richard Hughes847cae82019-08-27 11:22:23 +01001232 const guint align = 25;
1233 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001234
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001235 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001236
1237 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001238 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001239 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001240 for (gsize i = 0; i < idt; i++)
1241 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001242 if (key[0] != '\0') {
1243 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001244 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001245 } else {
1246 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001247 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001248 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001249 g_auto(GStrv) split = NULL;
1250 split = g_strsplit (value, "\n", -1);
1251 for (guint i = 0; split[i] != NULL; i++) {
1252 if (i == 0) {
1253 for (gsize j = keysz; j < align; j++)
1254 g_string_append (str, " ");
1255 } else {
1256 for (gsize j = 0; j < idt; j++)
1257 g_string_append (str, " ");
1258 }
1259 g_string_append (str, split[i]);
1260 g_string_append (str, "\n");
1261 }
1262 } else {
1263 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001264 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001265}
1266
Mario Limonciello1a680f32019-11-25 19:44:53 -06001267/**
1268 * fu_common_string_append_ku:
1269 * @str: A #GString
1270 * @idt: The indent
1271 * @key: A string to append
1272 * @value: guint64
1273 *
1274 * Appends a key and unsigned integer to a string
1275 *
1276 * Since: 1.2.4
1277 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001278void
1279fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1280{
1281 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1282 fu_common_string_append_kv (str, idt, key, tmp);
1283}
1284
Mario Limonciello1a680f32019-11-25 19:44:53 -06001285/**
1286 * fu_common_string_append_kx:
1287 * @str: A #GString
1288 * @idt: The indent
1289 * @key: A string to append
1290 * @value: guint64
1291 *
1292 * Appends a key and hex integer to a string
1293 *
1294 * Since: 1.2.4
1295 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001296void
1297fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1298{
1299 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1300 fu_common_string_append_kv (str, idt, key, tmp);
1301}
1302
Mario Limonciello1a680f32019-11-25 19:44:53 -06001303/**
1304 * fu_common_string_append_kb:
1305 * @str: A #GString
1306 * @idt: The indent
1307 * @key: A string to append
1308 * @value: Boolean
1309 *
1310 * Appends a key and boolean value to a string
1311 *
1312 * Since: 1.2.4
1313 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001314void
1315fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1316{
1317 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001318}
1319
Richard Hughese59cb9a2018-12-05 14:37:40 +00001320/**
Richard Hughes35481862019-01-06 12:01:58 +00001321 * fu_common_dump_full:
1322 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1323 * @title: prefix title, or %NULL
1324 * @data: buffer to print
1325 * @len: the size of @data
1326 * @columns: break new lines after this many bytes
1327 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1328 *
1329 * Dumps a raw buffer to the screen.
1330 *
1331 * Since: 1.2.4
1332 **/
1333void
1334fu_common_dump_full (const gchar *log_domain,
1335 const gchar *title,
1336 const guint8 *data,
1337 gsize len,
1338 guint columns,
1339 FuDumpFlags flags)
1340{
1341 g_autoptr(GString) str = g_string_new (NULL);
1342
1343 /* optional */
1344 if (title != NULL)
1345 g_string_append_printf (str, "%s:", title);
1346
1347 /* if more than can fit on one line then start afresh */
1348 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1349 g_string_append (str, "\n");
1350 } else {
1351 for (gsize i = str->len; i < 16; i++)
1352 g_string_append (str, " ");
1353 }
1354
1355 /* offset line */
1356 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1357 g_string_append (str, " │ ");
1358 for (gsize i = 0; i < columns; i++)
1359 g_string_append_printf (str, "%02x ", (guint) i);
1360 g_string_append (str, "\n───────┼");
1361 for (gsize i = 0; i < columns; i++)
1362 g_string_append (str, "───");
1363 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1364 }
1365
1366 /* print each row */
1367 for (gsize i = 0; i < len; i++) {
1368 g_string_append_printf (str, "%02x ", data[i]);
1369
1370 /* optionally print ASCII char */
1371 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1372 if (g_ascii_isprint (data[i]))
1373 g_string_append_printf (str, "[%c] ", data[i]);
1374 else
1375 g_string_append (str, "[?] ");
1376 }
1377
1378 /* new row required */
1379 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1380 g_string_append (str, "\n");
1381 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1382 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1383 }
1384 }
1385 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1386}
1387
1388/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001389 * fu_common_dump_raw:
1390 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1391 * @title: prefix title, or %NULL
1392 * @data: buffer to print
1393 * @len: the size of @data
1394 *
1395 * Dumps a raw buffer to the screen.
1396 *
1397 * Since: 1.2.2
1398 **/
1399void
1400fu_common_dump_raw (const gchar *log_domain,
1401 const gchar *title,
1402 const guint8 *data,
1403 gsize len)
1404{
Richard Hughes35481862019-01-06 12:01:58 +00001405 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1406 if (len > 64)
1407 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1408 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001409}
1410
1411/**
Mario Limonciello39602652019-04-29 21:08:58 -05001412 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001413 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1414 * @title: prefix title, or %NULL
1415 * @bytes: a #GBytes
1416 *
1417 * Dumps a byte buffer to the screen.
1418 *
1419 * Since: 1.2.2
1420 **/
1421void
1422fu_common_dump_bytes (const gchar *log_domain,
1423 const gchar *title,
1424 GBytes *bytes)
1425{
1426 gsize len = 0;
1427 const guint8 *data = g_bytes_get_data (bytes, &len);
1428 fu_common_dump_raw (log_domain, title, data, len);
1429}
Richard Hughesfc90f392019-01-15 21:21:16 +00001430
1431/**
1432 * fu_common_bytes_align:
1433 * @bytes: a #GBytes
1434 * @blksz: block size in bytes
1435 * @padval: the byte used to pad the byte buffer
1436 *
1437 * Aligns a block of memory to @blksize using the @padval value; if
1438 * the block is already aligned then the original @bytes is returned.
1439 *
1440 * Returns: (transfer full): a #GBytes, possibly @bytes
1441 *
1442 * Since: 1.2.4
1443 **/
1444GBytes *
1445fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1446{
1447 const guint8 *data;
1448 gsize sz;
1449
1450 g_return_val_if_fail (bytes != NULL, NULL);
1451 g_return_val_if_fail (blksz > 0, NULL);
1452
1453 /* pad */
1454 data = g_bytes_get_data (bytes, &sz);
1455 if (sz % blksz != 0) {
1456 gsize sz_align = ((sz / blksz) + 1) * blksz;
1457 guint8 *data_align = g_malloc (sz_align);
1458 memcpy (data_align, data, sz);
1459 memset (data_align + sz, padval, sz_align - sz);
1460 g_debug ("aligning 0x%x bytes to 0x%x",
1461 (guint) sz, (guint) sz_align);
1462 return g_bytes_new_take (data_align, sz_align);
1463 }
1464
1465 /* perfectly aligned */
1466 return g_bytes_ref (bytes);
1467}
Richard Hughes36999462019-03-19 20:23:29 +00001468
1469/**
1470 * fu_common_bytes_is_empty:
1471 * @bytes: a #GBytes
1472 *
1473 * Checks if a byte array are just empty (0xff) bytes.
1474 *
1475 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001476 *
1477 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001478 **/
1479gboolean
1480fu_common_bytes_is_empty (GBytes *bytes)
1481{
1482 gsize sz = 0;
1483 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1484 for (gsize i = 0; i < sz; i++) {
1485 if (buf[i] != 0xff)
1486 return FALSE;
1487 }
1488 return TRUE;
1489}
Richard Hughes2aad1042019-03-21 09:03:32 +00001490
1491/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001492 * fu_common_bytes_compare_raw:
1493 * @buf1: a buffer
1494 * @bufsz1: sizeof @buf1
1495 * @buf2: another buffer
1496 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001497 * @error: A #GError or %NULL
1498 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001499 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001500 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001501 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001502 *
1503 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001504 **/
1505gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001506fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1507 const guint8 *buf2, gsize bufsz2,
1508 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001509{
Richard Hughes38245ff2019-09-18 14:46:09 +01001510 g_return_val_if_fail (buf1 != NULL, FALSE);
1511 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001512 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1513
1514 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001515 if (bufsz1 != bufsz2) {
1516 g_set_error (error,
1517 G_IO_ERROR,
1518 G_IO_ERROR_INVALID_DATA,
1519 "got %" G_GSIZE_FORMAT " bytes, expected "
1520 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1521 return FALSE;
1522 }
1523
1524 /* check matches */
1525 for (guint i = 0x0; i < bufsz1; i++) {
1526 if (buf1[i] != buf2[i]) {
1527 g_set_error (error,
1528 G_IO_ERROR,
1529 G_IO_ERROR_INVALID_DATA,
1530 "got 0x%02x, expected 0x%02x @ 0x%04x",
1531 buf1[i], buf2[i], i);
1532 return FALSE;
1533 }
1534 }
1535
1536 /* success */
1537 return TRUE;
1538}
Richard Hughes484ee292019-03-22 16:10:50 +00001539
1540/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001541 * fu_common_bytes_compare:
1542 * @bytes1: a #GBytes
1543 * @bytes2: another #GBytes
1544 * @error: A #GError or %NULL
1545 *
1546 * Compares the buffers for equality.
1547 *
1548 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001549 *
1550 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001551 **/
1552gboolean
1553fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1554{
1555 const guint8 *buf1;
1556 const guint8 *buf2;
1557 gsize bufsz1;
1558 gsize bufsz2;
1559
1560 g_return_val_if_fail (bytes1 != NULL, FALSE);
1561 g_return_val_if_fail (bytes2 != NULL, FALSE);
1562 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1563
1564 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1565 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1566 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1567}
1568
1569/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001570 * fu_common_bytes_pad:
1571 * @bytes: a #GBytes
1572 * @sz: the desired size in bytes
1573 *
1574 * Pads a GBytes to a given @sz with `0xff`.
1575 *
1576 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001577 *
1578 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001579 **/
1580GBytes *
1581fu_common_bytes_pad (GBytes *bytes, gsize sz)
1582{
1583 gsize bytes_sz;
1584
1585 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1586
1587 /* pad */
1588 bytes_sz = g_bytes_get_size (bytes);
1589 if (bytes_sz < sz) {
1590 const guint8 *data = g_bytes_get_data (bytes, NULL);
1591 guint8 *data_new = g_malloc (sz);
1592 memcpy (data_new, data, bytes_sz);
1593 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1594 return g_bytes_new_take (data_new, sz);
1595 }
1596
1597 /* exactly right */
1598 return g_bytes_ref (bytes);
1599}
1600
1601/**
Richard Hughes484ee292019-03-22 16:10:50 +00001602 * fu_common_realpath:
1603 * @filename: a filename
1604 * @error: A #GError or %NULL
1605 *
1606 * Finds the canonicalized absolute filename for a path.
1607 *
1608 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001609 *
1610 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001611 **/
1612gchar *
1613fu_common_realpath (const gchar *filename, GError **error)
1614{
1615 char full_tmp[PATH_MAX];
1616
1617 g_return_val_if_fail (filename != NULL, NULL);
1618
Richard Hughes8694dee2019-11-22 09:16:34 +00001619#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001620 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001621#else
1622 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1623#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001624 g_set_error (error,
1625 G_IO_ERROR,
1626 G_IO_ERROR_INVALID_DATA,
1627 "cannot resolve path: %s",
1628 strerror (errno));
1629 return NULL;
1630 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001631 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1632 g_set_error (error,
1633 G_IO_ERROR,
1634 G_IO_ERROR_INVALID_DATA,
1635 "cannot find path: %s",
1636 full_tmp);
1637 return NULL;
1638 }
Richard Hughes484ee292019-03-22 16:10:50 +00001639 return g_strdup (full_tmp);
1640}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001641
1642/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001643 * fu_common_fnmatch:
1644 * @pattern: a glob pattern, e.g. `*foo*`
1645 * @str: a string to match against the pattern, e.g. `bazfoobar`
1646 *
1647 * Matches a string against a glob pattern.
1648 *
1649 * Return value: %TRUE if the string matched
1650 *
1651 * Since: 1.3.5
1652 **/
1653gboolean
1654fu_common_fnmatch (const gchar *pattern, const gchar *str)
1655{
1656 g_return_val_if_fail (pattern != NULL, FALSE);
1657 g_return_val_if_fail (str != NULL, FALSE);
1658#ifdef HAVE_FNMATCH_H
1659 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001660#elif _WIN32
1661 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1662 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1663 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001664#else
1665 return g_strcmp0 (pattern, str) == 0;
1666#endif
1667}
1668
1669/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001670 * fu_common_strnsplit:
1671 * @str: a string to split
1672 * @sz: size of @str
1673 * @delimiter: a string which specifies the places at which to split the string
1674 * @max_tokens: the maximum number of pieces to split @str into
1675 *
1676 * Splits a string into a maximum of @max_tokens pieces, using the given
1677 * delimiter. If @max_tokens is reached, the remainder of string is appended
1678 * to the last token.
1679 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001680 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001681 *
1682 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001683 **/
1684gchar **
1685fu_common_strnsplit (const gchar *str, gsize sz,
1686 const gchar *delimiter, gint max_tokens)
1687{
1688 if (str[sz - 1] != '\0') {
1689 g_autofree gchar *str2 = g_strndup (str, sz);
1690 return g_strsplit (str2, delimiter, max_tokens);
1691 }
1692 return g_strsplit (str, delimiter, max_tokens);
1693}
Richard Hughes5308ea42019-08-09 12:25:13 +01001694
1695/**
1696 * fu_memcpy_safe:
1697 * @dst: destination buffer
1698 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1699 * @dst_offset: offset in bytes into @dst to copy to
1700 * @src: source buffer
1701 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1702 * @src_offset: offset in bytes into @src to copy from
1703 * @n: number of bytes to copy from @src+@offset from
1704 * @error: A #GError or %NULL
1705 *
1706 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1707 * of both the destination and the source allows us to check for buffer overflow.
1708 *
1709 * Providing the buffer offsets also allows us to check reading past the end of
1710 * the source buffer. For this reason the caller should NEVER add an offset to
1711 * @src or @dst.
1712 *
1713 * You don't need to use this function in "obviously correct" cases, nor should
1714 * you use it when performance is a concern. Only us it when you're not sure if
1715 * malicious data from a device or firmware could cause memory corruption.
1716 *
1717 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001718 *
1719 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001720 **/
1721gboolean
1722fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1723 const guint8 *src, gsize src_sz, gsize src_offset,
1724 gsize n, GError **error)
1725{
1726 if (n == 0)
1727 return TRUE;
1728
1729 if (n > src_sz) {
1730 g_set_error (error,
1731 FWUPD_ERROR,
1732 FWUPD_ERROR_READ,
1733 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1734 (guint) n, (guint) src_sz);
1735 return FALSE;
1736 }
1737 if (n + src_offset > src_sz) {
1738 g_set_error (error,
1739 FWUPD_ERROR,
1740 FWUPD_ERROR_READ,
1741 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1742 (guint) n, (guint) src_offset, (guint) src_sz);
1743 return FALSE;
1744 }
1745 if (n > dst_sz) {
1746 g_set_error (error,
1747 FWUPD_ERROR,
1748 FWUPD_ERROR_WRITE,
1749 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1750 (guint) n, (guint) dst_sz);
1751 return FALSE;
1752 }
1753 if (n + dst_offset > dst_sz) {
1754 g_set_error (error,
1755 FWUPD_ERROR,
1756 FWUPD_ERROR_WRITE,
1757 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1758 (guint) n, (guint) dst_offset, (guint) dst_sz);
1759 return FALSE;
1760 }
1761
1762 /* phew! */
1763 memcpy (dst + dst_offset, src + src_offset, n);
1764 return TRUE;
1765}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001766
Richard Hughes80768f52019-10-22 07:19:14 +01001767/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001768 * fu_common_read_uint8_safe:
1769 * @buf: source buffer
1770 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1771 * @offset: offset in bytes into @buf to copy from
1772 * @value: (out) (allow-none): the parsed value
1773 * @error: A #GError or %NULL
1774 *
1775 * Read a value from a buffer in a safe way.
1776 *
1777 * You don't need to use this function in "obviously correct" cases, nor should
1778 * you use it when performance is a concern. Only us it when you're not sure if
1779 * malicious data from a device or firmware could cause memory corruption.
1780 *
1781 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001782 *
1783 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001784 **/
1785gboolean
1786fu_common_read_uint8_safe (const guint8 *buf,
1787 gsize bufsz,
1788 gsize offset,
1789 guint8 *value,
1790 GError **error)
1791{
1792 guint8 tmp;
1793 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1794 buf, bufsz, offset, /* src */
1795 sizeof(tmp), error))
1796 return FALSE;
1797 if (value != NULL)
1798 *value = tmp;
1799 return TRUE;
1800}
1801
1802/**
Richard Hughes80768f52019-10-22 07:19:14 +01001803 * fu_common_read_uint16_safe:
1804 * @buf: source buffer
1805 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1806 * @offset: offset in bytes into @buf to copy from
1807 * @value: (out) (allow-none): the parsed value
1808 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1809 * @error: A #GError or %NULL
1810 *
1811 * Read a value from a buffer using a specified endian in a safe way.
1812 *
1813 * You don't need to use this function in "obviously correct" cases, nor should
1814 * you use it when performance is a concern. Only us it when you're not sure if
1815 * malicious data from a device or firmware could cause memory corruption.
1816 *
1817 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001818 *
1819 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001820 **/
1821gboolean
1822fu_common_read_uint16_safe (const guint8 *buf,
1823 gsize bufsz,
1824 gsize offset,
1825 guint16 *value,
1826 FuEndianType endian,
1827 GError **error)
1828{
1829 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001830 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001831 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001832 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001833 return FALSE;
1834 if (value != NULL)
1835 *value = fu_common_read_uint16 (dst, endian);
1836 return TRUE;
1837}
1838
1839/**
1840 * fu_common_read_uint32_safe:
1841 * @buf: source buffer
1842 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1843 * @offset: offset in bytes into @buf to copy from
1844 * @value: (out) (allow-none): the parsed value
1845 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1846 * @error: A #GError or %NULL
1847 *
1848 * Read a value from a buffer using a specified endian in a safe way.
1849 *
1850 * You don't need to use this function in "obviously correct" cases, nor should
1851 * you use it when performance is a concern. Only us it when you're not sure if
1852 * malicious data from a device or firmware could cause memory corruption.
1853 *
1854 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001855 *
1856 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001857 **/
1858gboolean
1859fu_common_read_uint32_safe (const guint8 *buf,
1860 gsize bufsz,
1861 gsize offset,
1862 guint32 *value,
1863 FuEndianType endian,
1864 GError **error)
1865{
1866 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001867 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001868 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001869 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001870 return FALSE;
1871 if (value != NULL)
1872 *value = fu_common_read_uint32 (dst, endian);
1873 return TRUE;
1874}
1875
Mario Limonciello1a680f32019-11-25 19:44:53 -06001876/**
1877 * fu_byte_array_append_uint8:
1878 * @array: A #GByteArray
1879 * @data: #guint8
1880 *
1881 * Adds a 8 bit integer to a byte array
1882 *
1883 * Since: 1.3.1
1884 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001885void
1886fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1887{
1888 g_byte_array_append (array, &data, sizeof(data));
1889}
1890
Mario Limonciello1a680f32019-11-25 19:44:53 -06001891/**
1892 * fu_byte_array_append_uint16:
1893 * @array: A #GByteArray
1894 * @data: #guint16
1895 * @endian: #FuEndianType
1896 *
1897 * Adds a 16 bit integer to a byte array
1898 *
1899 * Since: 1.3.1
1900 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001901void
1902fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1903{
1904 guint8 buf[2];
1905 fu_common_write_uint16 (buf, data, endian);
1906 g_byte_array_append (array, buf, sizeof(buf));
1907}
1908
Mario Limonciello1a680f32019-11-25 19:44:53 -06001909/**
1910 * fu_byte_array_append_uint32:
1911 * @array: A #GByteArray
1912 * @data: #guint32
1913 * @endian: #FuEndianType
1914 *
1915 * Adds a 32 bit integer to a byte array
1916 *
1917 * Since: 1.3.1
1918 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001919void
1920fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1921{
1922 guint8 buf[4];
1923 fu_common_write_uint32 (buf, data, endian);
1924 g_byte_array_append (array, buf, sizeof(buf));
1925}