blob: 2556bd03c0186378d41acb3aa28c94c98168e0c6 [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
11#include <gio/gunixinputstream.h>
Richard Hughes954dd9f2017-08-08 13:36:25 +010012#include <glib/gstdio.h>
13
Richard Hughes5c508de2019-11-22 09:57:34 +000014#ifdef HAVE_FNMATCH_H
15#include <fnmatch.h>
16#endif
17
Richard Hughes94f939a2017-08-08 12:21:39 +010018#include <archive_entry.h>
19#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010020#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000021#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000022#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000023#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010024
25#include "fwupd-error.h"
26
27#include "fu-common.h"
28
29/**
Richard Hughes4eada342017-10-03 21:20:32 +010030 * SECTION:fu-common
31 * @short_description: common functionality for plugins to use
32 *
33 * Helper functions that can be used by the daemon and plugins.
34 *
35 * See also: #FuPlugin
36 */
37
38/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010039 * fu_common_rmtree:
40 * @directory: a directory name
41 * @error: A #GError or %NULL
42 *
43 * Recursively removes a directory.
44 *
45 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060046 *
47 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010048 **/
49gboolean
50fu_common_rmtree (const gchar *directory, GError **error)
51{
52 const gchar *filename;
53 g_autoptr(GDir) dir = NULL;
54
55 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010056 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010057 dir = g_dir_open (directory, 0, error);
58 if (dir == NULL)
59 return FALSE;
60
61 /* find each */
62 while ((filename = g_dir_read_name (dir))) {
63 g_autofree gchar *src = NULL;
64 src = g_build_filename (directory, filename, NULL);
65 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
66 if (!fu_common_rmtree (src, error))
67 return FALSE;
68 } else {
69 if (g_unlink (src) != 0) {
70 g_set_error (error,
71 FWUPD_ERROR,
72 FWUPD_ERROR_INTERNAL,
73 "Failed to delete: %s", src);
74 return FALSE;
75 }
76 }
77 }
78 if (g_remove (directory) != 0) {
79 g_set_error (error,
80 FWUPD_ERROR,
81 FWUPD_ERROR_INTERNAL,
82 "Failed to delete: %s", directory);
83 return FALSE;
84 }
85 return TRUE;
86}
87
Richard Hughes89e968b2018-03-07 10:01:08 +000088static gboolean
89fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
90{
91 const gchar *filename;
92 g_autoptr(GDir) dir = NULL;
93
94 /* try to open */
95 dir = g_dir_open (directory, 0, error);
96 if (dir == NULL)
97 return FALSE;
98
99 /* find each */
100 while ((filename = g_dir_read_name (dir))) {
101 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
102 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
103 if (!fu_common_get_file_list_internal (files, src, error))
104 return FALSE;
105 } else {
106 g_ptr_array_add (files, g_steal_pointer (&src));
107 }
108 }
109 return TRUE;
110
111}
112
113/**
114 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100115 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000116 * @error: A #GError or %NULL
117 *
118 * Returns every file found under @directory, and any subdirectory.
119 * If any path under @directory cannot be accessed due to permissions an error
120 * will be returned.
121 *
Mario Limonciello1a680f32019-11-25 19:44:53 -0600122 * Returns: (transfer container): array of files, or %NULL for error
123 *
124 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000125 **/
126GPtrArray *
127fu_common_get_files_recursive (const gchar *path, GError **error)
128{
129 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
130 if (!fu_common_get_file_list_internal (files, path, error))
131 return NULL;
132 return g_steal_pointer (&files);
133}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100134/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100135 * fu_common_mkdir_parent:
136 * @filename: A full pathname
137 * @error: A #GError, or %NULL
138 *
139 * Creates any required directories, including any parent directories.
140 *
141 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600142 *
143 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100144 **/
145gboolean
146fu_common_mkdir_parent (const gchar *filename, GError **error)
147{
148 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100149
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100150 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100151 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100152 if (g_mkdir_with_parents (parent, 0755) == -1) {
153 g_set_error (error,
154 FWUPD_ERROR,
155 FWUPD_ERROR_INTERNAL,
156 "Failed to create '%s': %s",
157 parent, g_strerror (errno));
158 return FALSE;
159 }
160 return TRUE;
161}
162
163/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100164 * fu_common_set_contents_bytes:
165 * @filename: A filename
166 * @bytes: The data to write
167 * @error: A #GError, or %NULL
168 *
169 * Writes a blob of data to a filename, creating the parent directories as
170 * required.
171 *
172 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600173 *
174 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100175 **/
176gboolean
177fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
178{
179 const gchar *data;
180 gsize size;
181 g_autoptr(GFile) file = NULL;
182 g_autoptr(GFile) file_parent = NULL;
183
184 file = g_file_new_for_path (filename);
185 file_parent = g_file_get_parent (file);
186 if (!g_file_query_exists (file_parent, NULL)) {
187 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
188 return FALSE;
189 }
190 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100191 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100192 return g_file_set_contents (filename, data, size, error);
193}
194
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100195/**
196 * fu_common_get_contents_bytes:
197 * @filename: A filename
198 * @error: A #GError, or %NULL
199 *
200 * Reads a blob of data from a file.
201 *
202 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600203 *
204 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100205 **/
206GBytes *
207fu_common_get_contents_bytes (const gchar *filename, GError **error)
208{
209 gchar *data = NULL;
210 gsize len = 0;
211 if (!g_file_get_contents (filename, &data, &len, error))
212 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100213 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100214 return g_bytes_new_take (data, len);
215}
Richard Hughes943d2c92017-06-21 09:04:39 +0100216
217/**
218 * fu_common_get_contents_fd:
219 * @fd: A file descriptor
220 * @count: The maximum number of bytes to read
221 * @error: A #GError, or %NULL
222 *
223 * Reads a blob from a specific file descriptor.
224 *
225 * Note: this will close the fd when done
226 *
Richard Hughes4eada342017-10-03 21:20:32 +0100227 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600228 *
229 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100230 **/
231GBytes *
232fu_common_get_contents_fd (gint fd, gsize count, GError **error)
233{
234 g_autoptr(GBytes) blob = NULL;
235 g_autoptr(GError) error_local = NULL;
236 g_autoptr(GInputStream) stream = NULL;
237
238 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100239 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
240
Richard Hughes919f8ab2018-02-14 10:24:56 +0000241 /* this is invalid */
242 if (count == 0) {
243 g_set_error_literal (error,
244 FWUPD_ERROR,
245 FWUPD_ERROR_NOT_SUPPORTED,
246 "A maximum read size must be specified");
247 return NULL;
248 }
249
Richard Hughes943d2c92017-06-21 09:04:39 +0100250 /* read the entire fd to a data blob */
251 stream = g_unix_input_stream_new (fd, TRUE);
252 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
253 if (blob == NULL) {
254 g_set_error_literal (error,
255 FWUPD_ERROR,
256 FWUPD_ERROR_INVALID_FILE,
257 error_local->message);
258 return NULL;
259 }
260 return g_steal_pointer (&blob);
261}
Richard Hughes94f939a2017-08-08 12:21:39 +0100262
263static gboolean
264fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
265{
266 const gchar *tmp;
267 g_autofree gchar *buf = NULL;
268
269 /* no output file */
270 if (archive_entry_pathname (entry) == NULL)
271 return FALSE;
272
273 /* update output path */
274 tmp = archive_entry_pathname (entry);
275 buf = g_build_filename (dir, tmp, NULL);
276 archive_entry_update_pathname_utf8 (entry, buf);
277 return TRUE;
278}
279
280/**
281 * fu_common_extract_archive:
282 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100283 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100284 * @error: A #GError, or %NULL
285 *
286 * Extracts an achive to a directory.
287 *
288 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600289 *
290 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100291 **/
292gboolean
293fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
294{
295 gboolean ret = TRUE;
296 int r;
297 struct archive *arch = NULL;
298 struct archive_entry *entry;
299
300 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100301 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100302 arch = archive_read_new ();
303 archive_read_support_format_all (arch);
304 archive_read_support_filter_all (arch);
305 r = archive_read_open_memory (arch,
306 (void *) g_bytes_get_data (blob, NULL),
307 (size_t) g_bytes_get_size (blob));
308 if (r != 0) {
309 ret = FALSE;
310 g_set_error (error,
311 FWUPD_ERROR,
312 FWUPD_ERROR_INTERNAL,
313 "Cannot open: %s",
314 archive_error_string (arch));
315 goto out;
316 }
317 for (;;) {
318 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100319 r = archive_read_next_header (arch, &entry);
320 if (r == ARCHIVE_EOF)
321 break;
322 if (r != ARCHIVE_OK) {
323 ret = FALSE;
324 g_set_error (error,
325 FWUPD_ERROR,
326 FWUPD_ERROR_INTERNAL,
327 "Cannot read header: %s",
328 archive_error_string (arch));
329 goto out;
330 }
331
332 /* only extract if valid */
333 valid = fu_common_extract_archive_entry (entry, dir);
334 if (!valid)
335 continue;
336 r = archive_read_extract (arch, entry, 0);
337 if (r != ARCHIVE_OK) {
338 ret = FALSE;
339 g_set_error (error,
340 FWUPD_ERROR,
341 FWUPD_ERROR_INTERNAL,
342 "Cannot extract: %s",
343 archive_error_string (arch));
344 goto out;
345 }
346 }
347out:
348 if (arch != NULL) {
349 archive_read_close (arch);
350 archive_read_free (arch);
351 }
352 return ret;
353}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100354
355static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300356fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
357
358static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100359fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
360{
361 va_list args;
362 g_autofree gchar *tmp = NULL;
363 g_auto(GStrv) split = NULL;
364
365 va_start (args, fmt);
366 tmp = g_strdup_vprintf (fmt, args);
367 va_end (args);
368
369 split = g_strsplit (tmp, " ", -1);
370 for (guint i = 0; split[i] != NULL; i++)
371 g_ptr_array_add (argv, g_strdup (split[i]));
372}
373
Mario Limonciello1a680f32019-11-25 19:44:53 -0600374/**
375 * fu_common_find_program_in_path:
376 * @basename: The program to search
377 * @error: A #GError, or %NULL
378 *
379 * Looks for a program in the PATH variable
380 *
381 * Returns: a new #gchar, or %NULL for error
382 *
383 * Since: 1.1.2
384 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100385gchar *
386fu_common_find_program_in_path (const gchar *basename, GError **error)
387{
388 gchar *fn = g_find_program_in_path (basename);
389 if (fn == NULL) {
390 g_set_error (error,
391 FWUPD_ERROR,
392 FWUPD_ERROR_NOT_SUPPORTED,
393 "missing executable %s in PATH",
394 basename);
395 return NULL;
396 }
397 return fn;
398}
399
400static gboolean
401fu_common_test_namespace_support (GError **error)
402{
403 /* test if CONFIG_USER_NS is valid */
404 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
405 g_set_error (error,
406 FWUPD_ERROR,
407 FWUPD_ERROR_NOT_SUPPORTED,
408 "missing CONFIG_USER_NS in kernel");
409 return FALSE;
410 }
411 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
412 g_autofree gchar *clone = NULL;
413 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
414 return FALSE;
415 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
416 g_set_error (error,
417 FWUPD_ERROR,
418 FWUPD_ERROR_NOT_SUPPORTED,
419 "unprivileged user namespace clones disabled by distro");
420 return FALSE;
421 }
422 }
423 return TRUE;
424}
425
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100426/**
427 * fu_common_firmware_builder:
428 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100429 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
430 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100431 * @error: A #GError, or %NULL
432 *
433 * Builds a firmware file using tools from the host session in a bubblewrap
434 * jail. Several things happen during build:
435 *
436 * 1. The @bytes data is untarred to a temporary location
437 * 2. A bubblewrap container is set up
438 * 3. The startup.sh script is run inside the container
439 * 4. The firmware.bin is extracted from the container
440 * 5. The temporary location is deleted
441 *
442 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600443 *
444 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100445 **/
446GBytes *
447fu_common_firmware_builder (GBytes *bytes,
448 const gchar *script_fn,
449 const gchar *output_fn,
450 GError **error)
451{
452 gint rc = 0;
453 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500454 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100455 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100456 g_autofree gchar *localstatedir = NULL;
457 g_autofree gchar *output2_fn = NULL;
458 g_autofree gchar *standard_error = NULL;
459 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100460 g_autofree gchar *tmpdir = NULL;
461 g_autoptr(GBytes) firmware_blob = NULL;
462 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
463
464 g_return_val_if_fail (bytes != NULL, NULL);
465 g_return_val_if_fail (script_fn != NULL, NULL);
466 g_return_val_if_fail (output_fn != NULL, NULL);
467 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
468
Mario Limonciello37b59582018-08-13 08:38:01 -0500469 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100470 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
471 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100472 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500473
474 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100475 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100476 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500477
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100478 /* untar file to temp location */
479 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
480 if (tmpdir == NULL)
481 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100482 if (!fu_common_extract_archive (bytes, tmpdir, error))
483 return NULL;
484
485 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100486 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
487 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100488
489 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500490 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100491 fu_common_add_argv (argv, "--die-with-parent");
492 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500493 fu_common_add_argv (argv, "--ro-bind /lib /lib");
494 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
495 fu_common_add_argv (argv, "--ro-bind /bin /bin");
496 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100497 fu_common_add_argv (argv, "--dir /tmp");
498 fu_common_add_argv (argv, "--dir /var");
499 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100500 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
501 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100502 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100503 fu_common_add_argv (argv, "--chdir /tmp");
504 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100505 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100506 g_ptr_array_add (argv, NULL);
507 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
508 g_debug ("running '%s' in %s", argv_str, tmpdir);
509 if (!g_spawn_sync ("/tmp",
510 (gchar **) argv->pdata,
511 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100512 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100513 NULL, NULL, /* child_setup */
514 &standard_output,
515 &standard_error,
516 &rc,
517 error)) {
518 g_prefix_error (error, "failed to run '%s': ", argv_str);
519 return NULL;
520 }
521 if (standard_output != NULL && standard_output[0] != '\0')
522 g_debug ("console output was: %s", standard_output);
523 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500524 FwupdError code = FWUPD_ERROR_INTERNAL;
525 if (errno == ENOTTY)
526 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100527 g_set_error (error,
528 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500529 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100530 "failed to build firmware: %s",
531 standard_error);
532 return NULL;
533 }
534
535 /* get generated file */
536 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
537 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
538 if (firmware_blob == NULL)
539 return NULL;
540
541 /* cleanup temp directory */
542 if (!fu_common_rmtree (tmpdir, error))
543 return NULL;
544
545 /* success */
546 return g_steal_pointer (&firmware_blob);
547}
Richard Hughes049ccc82017-08-09 15:26:56 +0100548
549typedef struct {
550 FuOutputHandler handler_cb;
551 gpointer handler_user_data;
552 GMainLoop *loop;
553 GSource *source;
554 GInputStream *stream;
555 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000556 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100557} FuCommonSpawnHelper;
558
559static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
560
561static gboolean
562fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
563{
564 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
565 gchar buffer[1024];
566 gssize sz;
567 g_auto(GStrv) split = NULL;
568 g_autoptr(GError) error = NULL;
569
570 /* read from stream */
571 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
572 buffer,
573 sizeof(buffer) - 1,
574 NULL,
575 &error);
576 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100577 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
578 g_warning ("failed to get read from nonblocking fd: %s",
579 error->message);
580 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100581 return G_SOURCE_REMOVE;
582 }
583
584 /* no read possible */
585 if (sz == 0)
586 g_main_loop_quit (helper->loop);
587
588 /* emit lines */
589 if (helper->handler_cb != NULL) {
590 buffer[sz] = '\0';
591 split = g_strsplit (buffer, "\n", -1);
592 for (guint i = 0; split[i] != NULL; i++) {
593 if (split[i][0] == '\0')
594 continue;
595 helper->handler_cb (split[i], helper->handler_user_data);
596 }
597 }
598
599 /* set up the source for the next read */
600 fu_common_spawn_create_pollable_source (helper);
601 return G_SOURCE_REMOVE;
602}
603
604static void
605fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
606{
607 if (helper->source != NULL)
608 g_source_destroy (helper->source);
609 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
610 helper->cancellable);
611 g_source_attach (helper->source, NULL);
612 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
613}
614
615static void
616fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
617{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000618 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100619 if (helper->stream != NULL)
620 g_object_unref (helper->stream);
621 if (helper->source != NULL)
622 g_source_destroy (helper->source);
623 if (helper->loop != NULL)
624 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000625 if (helper->timeout_id != 0)
626 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100627 g_free (helper);
628}
629
Mario Limoncielloa98df552018-04-16 12:15:51 -0500630#pragma clang diagnostic push
631#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100632G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500633#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100634
Richard Hughesb768e4d2019-02-26 13:55:18 +0000635static gboolean
636fu_common_spawn_timeout_cb (gpointer user_data)
637{
638 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
639 g_cancellable_cancel (helper->cancellable);
640 g_main_loop_quit (helper->loop);
641 helper->timeout_id = 0;
642 return G_SOURCE_REMOVE;
643}
644
645static void
646fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
647{
648 /* just propagate */
649 g_cancellable_cancel (helper->cancellable);
650}
651
Richard Hughes049ccc82017-08-09 15:26:56 +0100652/**
653 * fu_common_spawn_sync:
654 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100655 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
656 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000657 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100658 * @cancellable: a #GCancellable, or %NULL
659 * @error: A #GError or %NULL
660 *
661 * Runs a subprocess and waits for it to exit. Any output on standard out or
662 * standard error will be forwarded to @handler_cb as whole lines.
663 *
664 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600665 *
666 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100667 **/
668gboolean
669fu_common_spawn_sync (const gchar * const * argv,
670 FuOutputHandler handler_cb,
671 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000672 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100673 GCancellable *cancellable, GError **error)
674{
675 g_autoptr(FuCommonSpawnHelper) helper = NULL;
676 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100677 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000678 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100679
680 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100681 argv_str = g_strjoinv (" ", (gchar **) argv);
682 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100683 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
684 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
685 if (subprocess == NULL)
686 return FALSE;
687
688 /* watch for process to exit */
689 helper = g_new0 (FuCommonSpawnHelper, 1);
690 helper->handler_cb = handler_cb;
691 helper->handler_user_data = handler_user_data;
692 helper->loop = g_main_loop_new (NULL, FALSE);
693 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000694
695 /* always create a cancellable, and connect up the parent */
696 helper->cancellable = g_cancellable_new ();
697 if (cancellable != NULL) {
698 cancellable_id = g_cancellable_connect (cancellable,
699 G_CALLBACK (fu_common_spawn_cancelled_cb),
700 helper, NULL);
701 }
702
703 /* allow timeout */
704 if (timeout_ms > 0) {
705 helper->timeout_id = g_timeout_add (timeout_ms,
706 fu_common_spawn_timeout_cb,
707 helper);
708 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100709 fu_common_spawn_create_pollable_source (helper);
710 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000711 g_cancellable_disconnect (cancellable, cancellable_id);
712 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
713 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100714 return g_subprocess_wait_check (subprocess, cancellable, error);
715}
Richard Hughesae252cd2017-12-08 10:48:15 +0000716
717/**
718 * fu_common_write_uint16:
719 * @buf: A writable buffer
720 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100721 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000722 *
723 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600724 *
725 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000726 **/
727void
728fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
729{
730 guint16 val_hw;
731 switch (endian) {
732 case G_BIG_ENDIAN:
733 val_hw = GUINT16_TO_BE(val_native);
734 break;
735 case G_LITTLE_ENDIAN:
736 val_hw = GUINT16_TO_LE(val_native);
737 break;
738 default:
739 g_assert_not_reached ();
740 }
741 memcpy (buf, &val_hw, sizeof(val_hw));
742}
743
744/**
745 * fu_common_write_uint32:
746 * @buf: A writable buffer
747 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100748 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000749 *
750 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600751 *
752 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000753 **/
754void
755fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
756{
757 guint32 val_hw;
758 switch (endian) {
759 case G_BIG_ENDIAN:
760 val_hw = GUINT32_TO_BE(val_native);
761 break;
762 case G_LITTLE_ENDIAN:
763 val_hw = GUINT32_TO_LE(val_native);
764 break;
765 default:
766 g_assert_not_reached ();
767 }
768 memcpy (buf, &val_hw, sizeof(val_hw));
769}
770
771/**
772 * fu_common_read_uint16:
773 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100774 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000775 *
776 * Read a value from a buffer using a specified endian.
777 *
778 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600779 *
780 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000781 **/
782guint16
783fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
784{
785 guint16 val_hw, val_native;
786 memcpy (&val_hw, buf, sizeof(val_hw));
787 switch (endian) {
788 case G_BIG_ENDIAN:
789 val_native = GUINT16_FROM_BE(val_hw);
790 break;
791 case G_LITTLE_ENDIAN:
792 val_native = GUINT16_FROM_LE(val_hw);
793 break;
794 default:
795 g_assert_not_reached ();
796 }
797 return val_native;
798}
799
800/**
801 * fu_common_read_uint32:
802 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100803 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000804 *
805 * Read a value from a buffer using a specified endian.
806 *
807 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600808 *
809 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000810 **/
811guint32
812fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
813{
814 guint32 val_hw, val_native;
815 memcpy (&val_hw, buf, sizeof(val_hw));
816 switch (endian) {
817 case G_BIG_ENDIAN:
818 val_native = GUINT32_FROM_BE(val_hw);
819 break;
820 case G_LITTLE_ENDIAN:
821 val_native = GUINT32_FROM_LE(val_hw);
822 break;
823 default:
824 g_assert_not_reached ();
825 }
826 return val_native;
827}
Richard Hughese82eef32018-05-20 10:41:26 +0100828
Richard Hughes73bf2332018-08-28 09:38:09 +0100829/**
830 * fu_common_strtoull:
831 * @str: A string, e.g. "0x1234"
832 *
833 * Converts a string value to an integer. Values are assumed base 10, unless
834 * prefixed with "0x" where they are parsed as base 16.
835 *
836 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600837 *
838 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100839 **/
840guint64
841fu_common_strtoull (const gchar *str)
842{
843 guint base = 10;
844 if (str == NULL)
845 return 0x0;
846 if (g_str_has_prefix (str, "0x")) {
847 str += 2;
848 base = 16;
849 }
850 return g_ascii_strtoull (str, NULL, base);
851}
852
Richard Hughesa574a752018-08-31 13:31:03 +0100853/**
854 * fu_common_strstrip:
855 * @str: A string, e.g. " test "
856 *
857 * Removes leading and trailing whitespace from a constant string.
858 *
859 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600860 *
861 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100862 **/
863gchar *
864fu_common_strstrip (const gchar *str)
865{
866 guint head = G_MAXUINT;
867 guint tail = 0;
868
869 g_return_val_if_fail (str != NULL, NULL);
870
871 /* find first non-space char */
872 for (guint i = 0; str[i] != '\0'; i++) {
873 if (str[i] != ' ') {
874 head = i;
875 break;
876 }
877 }
878 if (head == G_MAXUINT)
879 return g_strdup ("");
880
881 /* find last non-space char */
882 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500883 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100884 tail = i;
885 }
886 return g_strndup (str + head, tail - head + 1);
887}
888
Richard Hughese82eef32018-05-20 10:41:26 +0100889static const GError *
890fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
891{
892 for (guint j = 0; j < errors->len; j++) {
893 const GError *error = g_ptr_array_index (errors, j);
894 if (g_error_matches (error, FWUPD_ERROR, error_code))
895 return error;
896 }
897 return NULL;
898}
899
900static guint
901fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
902{
903 guint cnt = 0;
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 cnt++;
908 }
909 return cnt;
910}
911
912static gboolean
913fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
914{
915 for (guint j = 0; j < errors->len; j++) {
916 const GError *error = g_ptr_array_index (errors, j);
917 gboolean matches_any = FALSE;
918 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
919 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
920 matches_any = TRUE;
921 break;
922 }
923 }
924 if (!matches_any)
925 return FALSE;
926 }
927 return TRUE;
928}
929
930/**
931 * fu_common_error_array_get_best:
932 * @errors: (element-type GError): array of errors
933 *
934 * Finds the 'best' error to show the user from a array of errors, creating a
935 * completely bespoke error where required.
936 *
937 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600938 *
939 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100940 **/
941GError *
942fu_common_error_array_get_best (GPtrArray *errors)
943{
944 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
945 FWUPD_ERROR_VERSION_SAME,
946 FWUPD_ERROR_VERSION_NEWER,
947 FWUPD_ERROR_NOT_SUPPORTED,
948 FWUPD_ERROR_INTERNAL,
949 FWUPD_ERROR_NOT_FOUND,
950 FWUPD_ERROR_LAST };
951 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
952 FWUPD_ERROR_NOT_FOUND,
953 FWUPD_ERROR_NOT_SUPPORTED,
954 FWUPD_ERROR_LAST };
955 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
956 FWUPD_ERROR_VERSION_SAME,
957 FWUPD_ERROR_NOT_FOUND,
958 FWUPD_ERROR_NOT_SUPPORTED,
959 FWUPD_ERROR_LAST };
960
961 /* are all the errors either GUID-not-matched or version-same? */
962 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
963 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
964 return g_error_new (FWUPD_ERROR,
965 FWUPD_ERROR_NOTHING_TO_DO,
966 "All updatable firmware is already installed");
967 }
968
969 /* are all the errors either GUID-not-matched or version same or newer? */
970 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
971 fu_common_error_array_matches_any (errors, err_all_newer)) {
972 return g_error_new (FWUPD_ERROR,
973 FWUPD_ERROR_NOTHING_TO_DO,
974 "All updatable devices already have newer versions");
975 }
976
977 /* get the most important single error */
978 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
979 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
980 if (error_tmp != NULL)
981 return g_error_copy (error_tmp);
982 }
983
984 /* fall back to something */
985 return g_error_new (FWUPD_ERROR,
986 FWUPD_ERROR_NOT_FOUND,
987 "No supported devices found");
988}
Richard Hughes4be17d12018-05-30 20:36:29 +0100989
990/**
991 * fu_common_get_path:
992 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
993 *
994 * Gets a fwupd-specific system path. These can be overridden with various
995 * environment variables, for instance %FWUPD_DATADIR.
996 *
997 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -0600998 *
999 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001000 **/
1001gchar *
1002fu_common_get_path (FuPathKind path_kind)
1003{
1004 const gchar *tmp;
1005 g_autofree gchar *basedir = NULL;
1006
1007 switch (path_kind) {
1008 /* /var */
1009 case FU_PATH_KIND_LOCALSTATEDIR:
1010 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1011 if (tmp != NULL)
1012 return g_strdup (tmp);
1013 tmp = g_getenv ("SNAP_USER_DATA");
1014 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001015 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1016 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughes282b10d2018-06-22 14:48:00 +01001017 /* /sys/firmware */
1018 case FU_PATH_KIND_SYSFSDIR_FW:
1019 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1020 if (tmp != NULL)
1021 return g_strdup (tmp);
1022 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001023 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001024 case FU_PATH_KIND_SYSFSDIR_TPM:
1025 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1026 if (tmp != NULL)
1027 return g_strdup (tmp);
1028 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001029 /* /sys/bus/platform/drivers */
1030 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1031 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1032 if (tmp != NULL)
1033 return g_strdup (tmp);
1034 return g_strdup ("/sys/bus/platform/drivers");
Richard Hughes4be17d12018-05-30 20:36:29 +01001035 /* /etc */
1036 case FU_PATH_KIND_SYSCONFDIR:
1037 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1038 if (tmp != NULL)
1039 return g_strdup (tmp);
1040 tmp = g_getenv ("SNAP_USER_DATA");
1041 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001042 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1043 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001044 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1045 case FU_PATH_KIND_PLUGINDIR_PKG:
1046 tmp = g_getenv ("FWUPD_PLUGINDIR");
1047 if (tmp != NULL)
1048 return g_strdup (tmp);
1049 tmp = g_getenv ("SNAP");
1050 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001051 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1052 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001053 /* /usr/share/fwupd */
1054 case FU_PATH_KIND_DATADIR_PKG:
1055 tmp = g_getenv ("FWUPD_DATADIR");
1056 if (tmp != NULL)
1057 return g_strdup (tmp);
1058 tmp = g_getenv ("SNAP");
1059 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001060 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1061 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001062 /* /usr/libexec/fwupd/efi */
1063 case FU_PATH_KIND_EFIAPPDIR:
1064 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1065 if (tmp != NULL)
1066 return g_strdup (tmp);
1067#ifdef EFI_APP_LOCATION
1068 tmp = g_getenv ("SNAP");
1069 if (tmp != NULL)
1070 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1071 return g_strdup (EFI_APP_LOCATION);
1072#else
1073 return NULL;
1074#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001075 /* /etc/fwupd */
1076 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001077 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
1078 if (tmp != NULL)
1079 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001080 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1081 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1082 /* /var/lib/fwupd */
1083 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001084 tmp = g_getenv ("STATE_DIRECTORY");
1085 if (tmp != NULL)
1086 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001087 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1088 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1089 /* /var/cache/fwupd */
1090 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001091 tmp = g_getenv ("CACHE_DIRECTORY");
1092 if (tmp != NULL)
1093 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001094 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1095 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001096 case FU_PATH_KIND_OFFLINE_TRIGGER:
1097 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1098 if (tmp != NULL)
1099 return g_strdup (tmp);
1100 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001101 case FU_PATH_KIND_POLKIT_ACTIONS:
1102#ifdef POLKIT_ACTIONDIR
1103 return g_strdup (POLKIT_ACTIONDIR);
1104#else
1105 return NULL;
1106#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001107 /* this shouldn't happen */
1108 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001109 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001110 }
1111
1112 return NULL;
1113}
Richard Hughes83e56c12018-10-10 20:24:41 +01001114
1115/**
1116 * fu_common_string_replace:
1117 * @string: The #GString to operate on
1118 * @search: The text to search for
1119 * @replace: The text to use for substitutions
1120 *
1121 * Performs multiple search and replace operations on the given string.
1122 *
1123 * Returns: the number of replacements done, or 0 if @search is not found.
1124 *
1125 * Since: 1.2.0
1126 **/
1127guint
1128fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1129{
1130 gchar *tmp;
1131 guint count = 0;
1132 gsize search_idx = 0;
1133 gsize replace_len;
1134 gsize search_len;
1135
1136 g_return_val_if_fail (string != NULL, 0);
1137 g_return_val_if_fail (search != NULL, 0);
1138 g_return_val_if_fail (replace != NULL, 0);
1139
1140 /* nothing to do */
1141 if (string->len == 0)
1142 return 0;
1143
1144 search_len = strlen (search);
1145 replace_len = strlen (replace);
1146
1147 do {
1148 tmp = g_strstr_len (string->str + search_idx, -1, search);
1149 if (tmp == NULL)
1150 break;
1151
1152 /* advance the counter in case @replace contains @search */
1153 search_idx = (gsize) (tmp - string->str);
1154
1155 /* reallocate the string if required */
1156 if (search_len > replace_len) {
1157 g_string_erase (string,
1158 (gssize) search_idx,
1159 (gssize) (search_len - replace_len));
1160 memcpy (tmp, replace, replace_len);
1161 } else if (search_len < replace_len) {
1162 g_string_insert_len (string,
1163 (gssize) search_idx,
1164 replace,
1165 (gssize) (replace_len - search_len));
1166 /* we have to treat this specially as it could have
1167 * been reallocated when the insertion happened */
1168 memcpy (string->str + search_idx, replace, replace_len);
1169 } else {
1170 /* just memcmp in the new string */
1171 memcpy (tmp, replace, replace_len);
1172 }
1173 search_idx += replace_len;
1174 count++;
1175 } while (TRUE);
1176
1177 return count;
1178}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001179
Richard Hughesae96a1f2019-09-23 11:16:36 +01001180/**
1181 * fu_common_strwidth:
1182 * @text: The string to operate on
1183 *
1184 * Returns the width of the string in displayed characters on the console.
1185 *
1186 * Returns: width of text
1187 *
1188 * Since: 1.3.2
1189 **/
1190gsize
1191fu_common_strwidth (const gchar *text)
1192{
1193 const gchar *p = text;
1194 gsize width = 0;
1195 while (*p) {
1196 gunichar c = g_utf8_get_char (p);
1197 if (g_unichar_iswide (c))
1198 width += 2;
1199 else if (!g_unichar_iszerowidth (c))
1200 width += 1;
1201 p = g_utf8_next_char (p);
1202 }
1203 return width;
1204}
1205
Mario Limonciello1a680f32019-11-25 19:44:53 -06001206/**
1207 * fu_common_string_append_kv:
1208 * @str: A #GString
1209 * @idt: The indent
1210 * @key: A string to append
1211 * @value: a string to append
1212 *
1213 * Appends a key and string value to a string
1214 *
1215 * Since: 1.2.4
1216 */
Richard Hughescea28de2019-08-09 11:16:40 +01001217void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001218fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001219{
Richard Hughes847cae82019-08-27 11:22:23 +01001220 const guint align = 25;
1221 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001222
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001223 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001224
1225 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001226 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001227 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001228 for (gsize i = 0; i < idt; i++)
1229 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001230 if (key[0] != '\0') {
1231 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001232 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001233 } else {
1234 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001235 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001236 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001237 g_auto(GStrv) split = NULL;
1238 split = g_strsplit (value, "\n", -1);
1239 for (guint i = 0; split[i] != NULL; i++) {
1240 if (i == 0) {
1241 for (gsize j = keysz; j < align; j++)
1242 g_string_append (str, " ");
1243 } else {
1244 for (gsize j = 0; j < idt; j++)
1245 g_string_append (str, " ");
1246 }
1247 g_string_append (str, split[i]);
1248 g_string_append (str, "\n");
1249 }
1250 } else {
1251 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001252 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001253}
1254
Mario Limonciello1a680f32019-11-25 19:44:53 -06001255/**
1256 * fu_common_string_append_ku:
1257 * @str: A #GString
1258 * @idt: The indent
1259 * @key: A string to append
1260 * @value: guint64
1261 *
1262 * Appends a key and unsigned integer to a string
1263 *
1264 * Since: 1.2.4
1265 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001266void
1267fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1268{
1269 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1270 fu_common_string_append_kv (str, idt, key, tmp);
1271}
1272
Mario Limonciello1a680f32019-11-25 19:44:53 -06001273/**
1274 * fu_common_string_append_kx:
1275 * @str: A #GString
1276 * @idt: The indent
1277 * @key: A string to append
1278 * @value: guint64
1279 *
1280 * Appends a key and hex integer to a string
1281 *
1282 * Since: 1.2.4
1283 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001284void
1285fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1286{
1287 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1288 fu_common_string_append_kv (str, idt, key, tmp);
1289}
1290
Mario Limonciello1a680f32019-11-25 19:44:53 -06001291/**
1292 * fu_common_string_append_kb:
1293 * @str: A #GString
1294 * @idt: The indent
1295 * @key: A string to append
1296 * @value: Boolean
1297 *
1298 * Appends a key and boolean value to a string
1299 *
1300 * Since: 1.2.4
1301 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001302void
1303fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1304{
1305 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001306}
1307
Richard Hughese59cb9a2018-12-05 14:37:40 +00001308/**
Richard Hughes35481862019-01-06 12:01:58 +00001309 * fu_common_dump_full:
1310 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1311 * @title: prefix title, or %NULL
1312 * @data: buffer to print
1313 * @len: the size of @data
1314 * @columns: break new lines after this many bytes
1315 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1316 *
1317 * Dumps a raw buffer to the screen.
1318 *
1319 * Since: 1.2.4
1320 **/
1321void
1322fu_common_dump_full (const gchar *log_domain,
1323 const gchar *title,
1324 const guint8 *data,
1325 gsize len,
1326 guint columns,
1327 FuDumpFlags flags)
1328{
1329 g_autoptr(GString) str = g_string_new (NULL);
1330
1331 /* optional */
1332 if (title != NULL)
1333 g_string_append_printf (str, "%s:", title);
1334
1335 /* if more than can fit on one line then start afresh */
1336 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1337 g_string_append (str, "\n");
1338 } else {
1339 for (gsize i = str->len; i < 16; i++)
1340 g_string_append (str, " ");
1341 }
1342
1343 /* offset line */
1344 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1345 g_string_append (str, " │ ");
1346 for (gsize i = 0; i < columns; i++)
1347 g_string_append_printf (str, "%02x ", (guint) i);
1348 g_string_append (str, "\n───────┼");
1349 for (gsize i = 0; i < columns; i++)
1350 g_string_append (str, "───");
1351 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1352 }
1353
1354 /* print each row */
1355 for (gsize i = 0; i < len; i++) {
1356 g_string_append_printf (str, "%02x ", data[i]);
1357
1358 /* optionally print ASCII char */
1359 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1360 if (g_ascii_isprint (data[i]))
1361 g_string_append_printf (str, "[%c] ", data[i]);
1362 else
1363 g_string_append (str, "[?] ");
1364 }
1365
1366 /* new row required */
1367 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1368 g_string_append (str, "\n");
1369 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1370 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1371 }
1372 }
1373 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1374}
1375
1376/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001377 * fu_common_dump_raw:
1378 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1379 * @title: prefix title, or %NULL
1380 * @data: buffer to print
1381 * @len: the size of @data
1382 *
1383 * Dumps a raw buffer to the screen.
1384 *
1385 * Since: 1.2.2
1386 **/
1387void
1388fu_common_dump_raw (const gchar *log_domain,
1389 const gchar *title,
1390 const guint8 *data,
1391 gsize len)
1392{
Richard Hughes35481862019-01-06 12:01:58 +00001393 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1394 if (len > 64)
1395 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1396 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001397}
1398
1399/**
Mario Limonciello39602652019-04-29 21:08:58 -05001400 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001401 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1402 * @title: prefix title, or %NULL
1403 * @bytes: a #GBytes
1404 *
1405 * Dumps a byte buffer to the screen.
1406 *
1407 * Since: 1.2.2
1408 **/
1409void
1410fu_common_dump_bytes (const gchar *log_domain,
1411 const gchar *title,
1412 GBytes *bytes)
1413{
1414 gsize len = 0;
1415 const guint8 *data = g_bytes_get_data (bytes, &len);
1416 fu_common_dump_raw (log_domain, title, data, len);
1417}
Richard Hughesfc90f392019-01-15 21:21:16 +00001418
1419/**
1420 * fu_common_bytes_align:
1421 * @bytes: a #GBytes
1422 * @blksz: block size in bytes
1423 * @padval: the byte used to pad the byte buffer
1424 *
1425 * Aligns a block of memory to @blksize using the @padval value; if
1426 * the block is already aligned then the original @bytes is returned.
1427 *
1428 * Returns: (transfer full): a #GBytes, possibly @bytes
1429 *
1430 * Since: 1.2.4
1431 **/
1432GBytes *
1433fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1434{
1435 const guint8 *data;
1436 gsize sz;
1437
1438 g_return_val_if_fail (bytes != NULL, NULL);
1439 g_return_val_if_fail (blksz > 0, NULL);
1440
1441 /* pad */
1442 data = g_bytes_get_data (bytes, &sz);
1443 if (sz % blksz != 0) {
1444 gsize sz_align = ((sz / blksz) + 1) * blksz;
1445 guint8 *data_align = g_malloc (sz_align);
1446 memcpy (data_align, data, sz);
1447 memset (data_align + sz, padval, sz_align - sz);
1448 g_debug ("aligning 0x%x bytes to 0x%x",
1449 (guint) sz, (guint) sz_align);
1450 return g_bytes_new_take (data_align, sz_align);
1451 }
1452
1453 /* perfectly aligned */
1454 return g_bytes_ref (bytes);
1455}
Richard Hughes36999462019-03-19 20:23:29 +00001456
1457/**
1458 * fu_common_bytes_is_empty:
1459 * @bytes: a #GBytes
1460 *
1461 * Checks if a byte array are just empty (0xff) bytes.
1462 *
1463 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001464 *
1465 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001466 **/
1467gboolean
1468fu_common_bytes_is_empty (GBytes *bytes)
1469{
1470 gsize sz = 0;
1471 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1472 for (gsize i = 0; i < sz; i++) {
1473 if (buf[i] != 0xff)
1474 return FALSE;
1475 }
1476 return TRUE;
1477}
Richard Hughes2aad1042019-03-21 09:03:32 +00001478
1479/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001480 * fu_common_bytes_compare_raw:
1481 * @buf1: a buffer
1482 * @bufsz1: sizeof @buf1
1483 * @buf2: another buffer
1484 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001485 * @error: A #GError or %NULL
1486 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001487 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001488 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001489 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001490 *
1491 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001492 **/
1493gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001494fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1495 const guint8 *buf2, gsize bufsz2,
1496 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001497{
Richard Hughes38245ff2019-09-18 14:46:09 +01001498 g_return_val_if_fail (buf1 != NULL, FALSE);
1499 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001500 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1501
1502 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001503 if (bufsz1 != bufsz2) {
1504 g_set_error (error,
1505 G_IO_ERROR,
1506 G_IO_ERROR_INVALID_DATA,
1507 "got %" G_GSIZE_FORMAT " bytes, expected "
1508 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1509 return FALSE;
1510 }
1511
1512 /* check matches */
1513 for (guint i = 0x0; i < bufsz1; i++) {
1514 if (buf1[i] != buf2[i]) {
1515 g_set_error (error,
1516 G_IO_ERROR,
1517 G_IO_ERROR_INVALID_DATA,
1518 "got 0x%02x, expected 0x%02x @ 0x%04x",
1519 buf1[i], buf2[i], i);
1520 return FALSE;
1521 }
1522 }
1523
1524 /* success */
1525 return TRUE;
1526}
Richard Hughes484ee292019-03-22 16:10:50 +00001527
1528/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001529 * fu_common_bytes_compare:
1530 * @bytes1: a #GBytes
1531 * @bytes2: another #GBytes
1532 * @error: A #GError or %NULL
1533 *
1534 * Compares the buffers for equality.
1535 *
1536 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001537 *
1538 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001539 **/
1540gboolean
1541fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1542{
1543 const guint8 *buf1;
1544 const guint8 *buf2;
1545 gsize bufsz1;
1546 gsize bufsz2;
1547
1548 g_return_val_if_fail (bytes1 != NULL, FALSE);
1549 g_return_val_if_fail (bytes2 != NULL, FALSE);
1550 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1551
1552 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1553 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1554 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1555}
1556
1557/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001558 * fu_common_bytes_pad:
1559 * @bytes: a #GBytes
1560 * @sz: the desired size in bytes
1561 *
1562 * Pads a GBytes to a given @sz with `0xff`.
1563 *
1564 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001565 *
1566 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001567 **/
1568GBytes *
1569fu_common_bytes_pad (GBytes *bytes, gsize sz)
1570{
1571 gsize bytes_sz;
1572
1573 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1574
1575 /* pad */
1576 bytes_sz = g_bytes_get_size (bytes);
1577 if (bytes_sz < sz) {
1578 const guint8 *data = g_bytes_get_data (bytes, NULL);
1579 guint8 *data_new = g_malloc (sz);
1580 memcpy (data_new, data, bytes_sz);
1581 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1582 return g_bytes_new_take (data_new, sz);
1583 }
1584
1585 /* exactly right */
1586 return g_bytes_ref (bytes);
1587}
1588
1589/**
Richard Hughes484ee292019-03-22 16:10:50 +00001590 * fu_common_realpath:
1591 * @filename: a filename
1592 * @error: A #GError or %NULL
1593 *
1594 * Finds the canonicalized absolute filename for a path.
1595 *
1596 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001597 *
1598 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001599 **/
1600gchar *
1601fu_common_realpath (const gchar *filename, GError **error)
1602{
1603 char full_tmp[PATH_MAX];
1604
1605 g_return_val_if_fail (filename != NULL, NULL);
1606
Richard Hughes8694dee2019-11-22 09:16:34 +00001607#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001608 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001609#else
1610 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1611#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001612 g_set_error (error,
1613 G_IO_ERROR,
1614 G_IO_ERROR_INVALID_DATA,
1615 "cannot resolve path: %s",
1616 strerror (errno));
1617 return NULL;
1618 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001619 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1620 g_set_error (error,
1621 G_IO_ERROR,
1622 G_IO_ERROR_INVALID_DATA,
1623 "cannot find path: %s",
1624 full_tmp);
1625 return NULL;
1626 }
Richard Hughes484ee292019-03-22 16:10:50 +00001627 return g_strdup (full_tmp);
1628}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001629
1630/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001631 * fu_common_fnmatch:
1632 * @pattern: a glob pattern, e.g. `*foo*`
1633 * @str: a string to match against the pattern, e.g. `bazfoobar`
1634 *
1635 * Matches a string against a glob pattern.
1636 *
1637 * Return value: %TRUE if the string matched
1638 *
1639 * Since: 1.3.5
1640 **/
1641gboolean
1642fu_common_fnmatch (const gchar *pattern, const gchar *str)
1643{
1644 g_return_val_if_fail (pattern != NULL, FALSE);
1645 g_return_val_if_fail (str != NULL, FALSE);
1646#ifdef HAVE_FNMATCH_H
1647 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
1648#else
1649 return g_strcmp0 (pattern, str) == 0;
1650#endif
1651}
1652
1653/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001654 * fu_common_strnsplit:
1655 * @str: a string to split
1656 * @sz: size of @str
1657 * @delimiter: a string which specifies the places at which to split the string
1658 * @max_tokens: the maximum number of pieces to split @str into
1659 *
1660 * Splits a string into a maximum of @max_tokens pieces, using the given
1661 * delimiter. If @max_tokens is reached, the remainder of string is appended
1662 * to the last token.
1663 *
1664 * Return value: a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001665 *
1666 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001667 **/
1668gchar **
1669fu_common_strnsplit (const gchar *str, gsize sz,
1670 const gchar *delimiter, gint max_tokens)
1671{
1672 if (str[sz - 1] != '\0') {
1673 g_autofree gchar *str2 = g_strndup (str, sz);
1674 return g_strsplit (str2, delimiter, max_tokens);
1675 }
1676 return g_strsplit (str, delimiter, max_tokens);
1677}
Richard Hughes5308ea42019-08-09 12:25:13 +01001678
1679/**
1680 * fu_memcpy_safe:
1681 * @dst: destination buffer
1682 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1683 * @dst_offset: offset in bytes into @dst to copy to
1684 * @src: source buffer
1685 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1686 * @src_offset: offset in bytes into @src to copy from
1687 * @n: number of bytes to copy from @src+@offset from
1688 * @error: A #GError or %NULL
1689 *
1690 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1691 * of both the destination and the source allows us to check for buffer overflow.
1692 *
1693 * Providing the buffer offsets also allows us to check reading past the end of
1694 * the source buffer. For this reason the caller should NEVER add an offset to
1695 * @src or @dst.
1696 *
1697 * You don't need to use this function in "obviously correct" cases, nor should
1698 * you use it when performance is a concern. Only us it when you're not sure if
1699 * malicious data from a device or firmware could cause memory corruption.
1700 *
1701 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001702 *
1703 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001704 **/
1705gboolean
1706fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1707 const guint8 *src, gsize src_sz, gsize src_offset,
1708 gsize n, GError **error)
1709{
1710 if (n == 0)
1711 return TRUE;
1712
1713 if (n > src_sz) {
1714 g_set_error (error,
1715 FWUPD_ERROR,
1716 FWUPD_ERROR_READ,
1717 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1718 (guint) n, (guint) src_sz);
1719 return FALSE;
1720 }
1721 if (n + src_offset > src_sz) {
1722 g_set_error (error,
1723 FWUPD_ERROR,
1724 FWUPD_ERROR_READ,
1725 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1726 (guint) n, (guint) src_offset, (guint) src_sz);
1727 return FALSE;
1728 }
1729 if (n > dst_sz) {
1730 g_set_error (error,
1731 FWUPD_ERROR,
1732 FWUPD_ERROR_WRITE,
1733 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1734 (guint) n, (guint) dst_sz);
1735 return FALSE;
1736 }
1737 if (n + dst_offset > dst_sz) {
1738 g_set_error (error,
1739 FWUPD_ERROR,
1740 FWUPD_ERROR_WRITE,
1741 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1742 (guint) n, (guint) dst_offset, (guint) dst_sz);
1743 return FALSE;
1744 }
1745
1746 /* phew! */
1747 memcpy (dst + dst_offset, src + src_offset, n);
1748 return TRUE;
1749}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001750
Richard Hughes80768f52019-10-22 07:19:14 +01001751/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001752 * fu_common_read_uint8_safe:
1753 * @buf: source buffer
1754 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1755 * @offset: offset in bytes into @buf to copy from
1756 * @value: (out) (allow-none): the parsed value
1757 * @error: A #GError or %NULL
1758 *
1759 * Read a value from a buffer in a safe way.
1760 *
1761 * You don't need to use this function in "obviously correct" cases, nor should
1762 * you use it when performance is a concern. Only us it when you're not sure if
1763 * malicious data from a device or firmware could cause memory corruption.
1764 *
1765 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001766 *
1767 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001768 **/
1769gboolean
1770fu_common_read_uint8_safe (const guint8 *buf,
1771 gsize bufsz,
1772 gsize offset,
1773 guint8 *value,
1774 GError **error)
1775{
1776 guint8 tmp;
1777 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1778 buf, bufsz, offset, /* src */
1779 sizeof(tmp), error))
1780 return FALSE;
1781 if (value != NULL)
1782 *value = tmp;
1783 return TRUE;
1784}
1785
1786/**
Richard Hughes80768f52019-10-22 07:19:14 +01001787 * fu_common_read_uint16_safe:
1788 * @buf: source buffer
1789 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1790 * @offset: offset in bytes into @buf to copy from
1791 * @value: (out) (allow-none): the parsed value
1792 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1793 * @error: A #GError or %NULL
1794 *
1795 * Read a value from a buffer using a specified endian in a safe way.
1796 *
1797 * You don't need to use this function in "obviously correct" cases, nor should
1798 * you use it when performance is a concern. Only us it when you're not sure if
1799 * malicious data from a device or firmware could cause memory corruption.
1800 *
1801 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001802 *
1803 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001804 **/
1805gboolean
1806fu_common_read_uint16_safe (const guint8 *buf,
1807 gsize bufsz,
1808 gsize offset,
1809 guint16 *value,
1810 FuEndianType endian,
1811 GError **error)
1812{
1813 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001814 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001815 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001816 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001817 return FALSE;
1818 if (value != NULL)
1819 *value = fu_common_read_uint16 (dst, endian);
1820 return TRUE;
1821}
1822
1823/**
1824 * fu_common_read_uint32_safe:
1825 * @buf: source buffer
1826 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1827 * @offset: offset in bytes into @buf to copy from
1828 * @value: (out) (allow-none): the parsed value
1829 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1830 * @error: A #GError or %NULL
1831 *
1832 * Read a value from a buffer using a specified endian in a safe way.
1833 *
1834 * You don't need to use this function in "obviously correct" cases, nor should
1835 * you use it when performance is a concern. Only us it when you're not sure if
1836 * malicious data from a device or firmware could cause memory corruption.
1837 *
1838 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001839 *
1840 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001841 **/
1842gboolean
1843fu_common_read_uint32_safe (const guint8 *buf,
1844 gsize bufsz,
1845 gsize offset,
1846 guint32 *value,
1847 FuEndianType endian,
1848 GError **error)
1849{
1850 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001851 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001852 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001853 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001854 return FALSE;
1855 if (value != NULL)
1856 *value = fu_common_read_uint32 (dst, endian);
1857 return TRUE;
1858}
1859
Mario Limonciello1a680f32019-11-25 19:44:53 -06001860/**
1861 * fu_byte_array_append_uint8:
1862 * @array: A #GByteArray
1863 * @data: #guint8
1864 *
1865 * Adds a 8 bit integer to a byte array
1866 *
1867 * Since: 1.3.1
1868 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001869void
1870fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1871{
1872 g_byte_array_append (array, &data, sizeof(data));
1873}
1874
Mario Limonciello1a680f32019-11-25 19:44:53 -06001875/**
1876 * fu_byte_array_append_uint16:
1877 * @array: A #GByteArray
1878 * @data: #guint16
1879 * @endian: #FuEndianType
1880 *
1881 * Adds a 16 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_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1887{
1888 guint8 buf[2];
1889 fu_common_write_uint16 (buf, data, endian);
1890 g_byte_array_append (array, buf, sizeof(buf));
1891}
1892
Mario Limonciello1a680f32019-11-25 19:44:53 -06001893/**
1894 * fu_byte_array_append_uint32:
1895 * @array: A #GByteArray
1896 * @data: #guint32
1897 * @endian: #FuEndianType
1898 *
1899 * Adds a 32 bit integer to a byte array
1900 *
1901 * Since: 1.3.1
1902 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001903void
1904fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1905{
1906 guint8 buf[4];
1907 fu_common_write_uint32 (buf, data, endian);
1908 g_byte_array_append (array, buf, sizeof(buf));
1909}