blob: 19acec92f19b868e0d808a7206d2fbabf63a6f5e [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 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000298 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100299 *
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 Hughesc3689582020-05-06 12:35:20 +01001029 /* /proc */
1030 case FU_PATH_KIND_PROCFS:
1031 tmp = g_getenv ("FWUPD_PROCFS");
1032 if (tmp != NULL)
1033 return g_strdup (tmp);
1034 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001035 /* /sys/firmware */
1036 case FU_PATH_KIND_SYSFSDIR_FW:
1037 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1038 if (tmp != NULL)
1039 return g_strdup (tmp);
1040 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001041 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001042 case FU_PATH_KIND_SYSFSDIR_TPM:
1043 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1044 if (tmp != NULL)
1045 return g_strdup (tmp);
1046 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001047 /* /sys/bus/platform/drivers */
1048 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1049 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1050 if (tmp != NULL)
1051 return g_strdup (tmp);
1052 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001053 /* /sys/kernel/security */
1054 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1055 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1056 if (tmp != NULL)
1057 return g_strdup (tmp);
1058 return g_strdup ("/sys/kernel/security");
Richard Hughes4be17d12018-05-30 20:36:29 +01001059 /* /etc */
1060 case FU_PATH_KIND_SYSCONFDIR:
1061 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1062 if (tmp != NULL)
1063 return g_strdup (tmp);
1064 tmp = g_getenv ("SNAP_USER_DATA");
1065 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001066 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1067 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001068 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1069 case FU_PATH_KIND_PLUGINDIR_PKG:
1070 tmp = g_getenv ("FWUPD_PLUGINDIR");
1071 if (tmp != NULL)
1072 return g_strdup (tmp);
1073 tmp = g_getenv ("SNAP");
1074 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001075 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1076 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001077 /* /usr/share/fwupd */
1078 case FU_PATH_KIND_DATADIR_PKG:
1079 tmp = g_getenv ("FWUPD_DATADIR");
1080 if (tmp != NULL)
1081 return g_strdup (tmp);
1082 tmp = g_getenv ("SNAP");
1083 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001084 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1085 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001086 /* /usr/libexec/fwupd/efi */
1087 case FU_PATH_KIND_EFIAPPDIR:
1088 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1089 if (tmp != NULL)
1090 return g_strdup (tmp);
1091#ifdef EFI_APP_LOCATION
1092 tmp = g_getenv ("SNAP");
1093 if (tmp != NULL)
1094 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1095 return g_strdup (EFI_APP_LOCATION);
1096#else
1097 return NULL;
1098#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001099 /* /etc/fwupd */
1100 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001101 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001102 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001103 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001104 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1105 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1106 /* /var/lib/fwupd */
1107 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001108 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001109 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001110 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001111 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1112 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1113 /* /var/cache/fwupd */
1114 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001115 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001116 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001117 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001118 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1119 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001120 case FU_PATH_KIND_OFFLINE_TRIGGER:
1121 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1122 if (tmp != NULL)
1123 return g_strdup (tmp);
1124 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001125 case FU_PATH_KIND_POLKIT_ACTIONS:
1126#ifdef POLKIT_ACTIONDIR
1127 return g_strdup (POLKIT_ACTIONDIR);
1128#else
1129 return NULL;
1130#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001131 /* this shouldn't happen */
1132 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001133 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001134 }
1135
1136 return NULL;
1137}
Richard Hughes83e56c12018-10-10 20:24:41 +01001138
1139/**
1140 * fu_common_string_replace:
1141 * @string: The #GString to operate on
1142 * @search: The text to search for
1143 * @replace: The text to use for substitutions
1144 *
1145 * Performs multiple search and replace operations on the given string.
1146 *
1147 * Returns: the number of replacements done, or 0 if @search is not found.
1148 *
1149 * Since: 1.2.0
1150 **/
1151guint
1152fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1153{
1154 gchar *tmp;
1155 guint count = 0;
1156 gsize search_idx = 0;
1157 gsize replace_len;
1158 gsize search_len;
1159
1160 g_return_val_if_fail (string != NULL, 0);
1161 g_return_val_if_fail (search != NULL, 0);
1162 g_return_val_if_fail (replace != NULL, 0);
1163
1164 /* nothing to do */
1165 if (string->len == 0)
1166 return 0;
1167
1168 search_len = strlen (search);
1169 replace_len = strlen (replace);
1170
1171 do {
1172 tmp = g_strstr_len (string->str + search_idx, -1, search);
1173 if (tmp == NULL)
1174 break;
1175
1176 /* advance the counter in case @replace contains @search */
1177 search_idx = (gsize) (tmp - string->str);
1178
1179 /* reallocate the string if required */
1180 if (search_len > replace_len) {
1181 g_string_erase (string,
1182 (gssize) search_idx,
1183 (gssize) (search_len - replace_len));
1184 memcpy (tmp, replace, replace_len);
1185 } else if (search_len < replace_len) {
1186 g_string_insert_len (string,
1187 (gssize) search_idx,
1188 replace,
1189 (gssize) (replace_len - search_len));
1190 /* we have to treat this specially as it could have
1191 * been reallocated when the insertion happened */
1192 memcpy (string->str + search_idx, replace, replace_len);
1193 } else {
1194 /* just memcmp in the new string */
1195 memcpy (tmp, replace, replace_len);
1196 }
1197 search_idx += replace_len;
1198 count++;
1199 } while (TRUE);
1200
1201 return count;
1202}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001203
Richard Hughesae96a1f2019-09-23 11:16:36 +01001204/**
1205 * fu_common_strwidth:
1206 * @text: The string to operate on
1207 *
1208 * Returns the width of the string in displayed characters on the console.
1209 *
1210 * Returns: width of text
1211 *
1212 * Since: 1.3.2
1213 **/
1214gsize
1215fu_common_strwidth (const gchar *text)
1216{
1217 const gchar *p = text;
1218 gsize width = 0;
1219 while (*p) {
1220 gunichar c = g_utf8_get_char (p);
1221 if (g_unichar_iswide (c))
1222 width += 2;
1223 else if (!g_unichar_iszerowidth (c))
1224 width += 1;
1225 p = g_utf8_next_char (p);
1226 }
1227 return width;
1228}
1229
Mario Limonciello1a680f32019-11-25 19:44:53 -06001230/**
1231 * fu_common_string_append_kv:
1232 * @str: A #GString
1233 * @idt: The indent
1234 * @key: A string to append
1235 * @value: a string to append
1236 *
1237 * Appends a key and string value to a string
1238 *
1239 * Since: 1.2.4
1240 */
Richard Hughescea28de2019-08-09 11:16:40 +01001241void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001242fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001243{
Richard Hughes847cae82019-08-27 11:22:23 +01001244 const guint align = 25;
1245 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001246
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001247 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001248
1249 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001250 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001251 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001252 for (gsize i = 0; i < idt; i++)
1253 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001254 if (key[0] != '\0') {
1255 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001256 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001257 } else {
1258 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001259 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001260 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001261 g_auto(GStrv) split = NULL;
1262 split = g_strsplit (value, "\n", -1);
1263 for (guint i = 0; split[i] != NULL; i++) {
1264 if (i == 0) {
1265 for (gsize j = keysz; j < align; j++)
1266 g_string_append (str, " ");
1267 } else {
1268 for (gsize j = 0; j < idt; j++)
1269 g_string_append (str, " ");
1270 }
1271 g_string_append (str, split[i]);
1272 g_string_append (str, "\n");
1273 }
1274 } else {
1275 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001276 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001277}
1278
Mario Limonciello1a680f32019-11-25 19:44:53 -06001279/**
1280 * fu_common_string_append_ku:
1281 * @str: A #GString
1282 * @idt: The indent
1283 * @key: A string to append
1284 * @value: guint64
1285 *
1286 * Appends a key and unsigned integer to a string
1287 *
1288 * Since: 1.2.4
1289 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001290void
1291fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1292{
1293 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1294 fu_common_string_append_kv (str, idt, key, tmp);
1295}
1296
Mario Limonciello1a680f32019-11-25 19:44:53 -06001297/**
1298 * fu_common_string_append_kx:
1299 * @str: A #GString
1300 * @idt: The indent
1301 * @key: A string to append
1302 * @value: guint64
1303 *
1304 * Appends a key and hex integer to a string
1305 *
1306 * Since: 1.2.4
1307 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001308void
1309fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1310{
1311 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1312 fu_common_string_append_kv (str, idt, key, tmp);
1313}
1314
Mario Limonciello1a680f32019-11-25 19:44:53 -06001315/**
1316 * fu_common_string_append_kb:
1317 * @str: A #GString
1318 * @idt: The indent
1319 * @key: A string to append
1320 * @value: Boolean
1321 *
1322 * Appends a key and boolean value to a string
1323 *
1324 * Since: 1.2.4
1325 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001326void
1327fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1328{
1329 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001330}
1331
Richard Hughese59cb9a2018-12-05 14:37:40 +00001332/**
Richard Hughes35481862019-01-06 12:01:58 +00001333 * fu_common_dump_full:
1334 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1335 * @title: prefix title, or %NULL
1336 * @data: buffer to print
1337 * @len: the size of @data
1338 * @columns: break new lines after this many bytes
1339 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1340 *
1341 * Dumps a raw buffer to the screen.
1342 *
1343 * Since: 1.2.4
1344 **/
1345void
1346fu_common_dump_full (const gchar *log_domain,
1347 const gchar *title,
1348 const guint8 *data,
1349 gsize len,
1350 guint columns,
1351 FuDumpFlags flags)
1352{
1353 g_autoptr(GString) str = g_string_new (NULL);
1354
1355 /* optional */
1356 if (title != NULL)
1357 g_string_append_printf (str, "%s:", title);
1358
1359 /* if more than can fit on one line then start afresh */
1360 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1361 g_string_append (str, "\n");
1362 } else {
1363 for (gsize i = str->len; i < 16; i++)
1364 g_string_append (str, " ");
1365 }
1366
1367 /* offset line */
1368 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1369 g_string_append (str, " │ ");
1370 for (gsize i = 0; i < columns; i++)
1371 g_string_append_printf (str, "%02x ", (guint) i);
1372 g_string_append (str, "\n───────┼");
1373 for (gsize i = 0; i < columns; i++)
1374 g_string_append (str, "───");
1375 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1376 }
1377
1378 /* print each row */
1379 for (gsize i = 0; i < len; i++) {
1380 g_string_append_printf (str, "%02x ", data[i]);
1381
1382 /* optionally print ASCII char */
1383 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1384 if (g_ascii_isprint (data[i]))
1385 g_string_append_printf (str, "[%c] ", data[i]);
1386 else
1387 g_string_append (str, "[?] ");
1388 }
1389
1390 /* new row required */
1391 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1392 g_string_append (str, "\n");
1393 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1394 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1395 }
1396 }
1397 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1398}
1399
1400/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001401 * fu_common_dump_raw:
1402 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1403 * @title: prefix title, or %NULL
1404 * @data: buffer to print
1405 * @len: the size of @data
1406 *
1407 * Dumps a raw buffer to the screen.
1408 *
1409 * Since: 1.2.2
1410 **/
1411void
1412fu_common_dump_raw (const gchar *log_domain,
1413 const gchar *title,
1414 const guint8 *data,
1415 gsize len)
1416{
Richard Hughes35481862019-01-06 12:01:58 +00001417 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1418 if (len > 64)
1419 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1420 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001421}
1422
1423/**
Mario Limonciello39602652019-04-29 21:08:58 -05001424 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001425 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1426 * @title: prefix title, or %NULL
1427 * @bytes: a #GBytes
1428 *
1429 * Dumps a byte buffer to the screen.
1430 *
1431 * Since: 1.2.2
1432 **/
1433void
1434fu_common_dump_bytes (const gchar *log_domain,
1435 const gchar *title,
1436 GBytes *bytes)
1437{
1438 gsize len = 0;
1439 const guint8 *data = g_bytes_get_data (bytes, &len);
1440 fu_common_dump_raw (log_domain, title, data, len);
1441}
Richard Hughesfc90f392019-01-15 21:21:16 +00001442
1443/**
1444 * fu_common_bytes_align:
1445 * @bytes: a #GBytes
1446 * @blksz: block size in bytes
1447 * @padval: the byte used to pad the byte buffer
1448 *
1449 * Aligns a block of memory to @blksize using the @padval value; if
1450 * the block is already aligned then the original @bytes is returned.
1451 *
1452 * Returns: (transfer full): a #GBytes, possibly @bytes
1453 *
1454 * Since: 1.2.4
1455 **/
1456GBytes *
1457fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1458{
1459 const guint8 *data;
1460 gsize sz;
1461
1462 g_return_val_if_fail (bytes != NULL, NULL);
1463 g_return_val_if_fail (blksz > 0, NULL);
1464
1465 /* pad */
1466 data = g_bytes_get_data (bytes, &sz);
1467 if (sz % blksz != 0) {
1468 gsize sz_align = ((sz / blksz) + 1) * blksz;
1469 guint8 *data_align = g_malloc (sz_align);
1470 memcpy (data_align, data, sz);
1471 memset (data_align + sz, padval, sz_align - sz);
1472 g_debug ("aligning 0x%x bytes to 0x%x",
1473 (guint) sz, (guint) sz_align);
1474 return g_bytes_new_take (data_align, sz_align);
1475 }
1476
1477 /* perfectly aligned */
1478 return g_bytes_ref (bytes);
1479}
Richard Hughes36999462019-03-19 20:23:29 +00001480
1481/**
1482 * fu_common_bytes_is_empty:
1483 * @bytes: a #GBytes
1484 *
1485 * Checks if a byte array are just empty (0xff) bytes.
1486 *
1487 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001488 *
1489 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001490 **/
1491gboolean
1492fu_common_bytes_is_empty (GBytes *bytes)
1493{
1494 gsize sz = 0;
1495 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1496 for (gsize i = 0; i < sz; i++) {
1497 if (buf[i] != 0xff)
1498 return FALSE;
1499 }
1500 return TRUE;
1501}
Richard Hughes2aad1042019-03-21 09:03:32 +00001502
1503/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001504 * fu_common_bytes_compare_raw:
1505 * @buf1: a buffer
1506 * @bufsz1: sizeof @buf1
1507 * @buf2: another buffer
1508 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001509 * @error: A #GError or %NULL
1510 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001511 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001512 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001513 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001514 *
1515 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001516 **/
1517gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001518fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1519 const guint8 *buf2, gsize bufsz2,
1520 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001521{
Richard Hughes38245ff2019-09-18 14:46:09 +01001522 g_return_val_if_fail (buf1 != NULL, FALSE);
1523 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001524 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1525
1526 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001527 if (bufsz1 != bufsz2) {
1528 g_set_error (error,
1529 G_IO_ERROR,
1530 G_IO_ERROR_INVALID_DATA,
1531 "got %" G_GSIZE_FORMAT " bytes, expected "
1532 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1533 return FALSE;
1534 }
1535
1536 /* check matches */
1537 for (guint i = 0x0; i < bufsz1; i++) {
1538 if (buf1[i] != buf2[i]) {
1539 g_set_error (error,
1540 G_IO_ERROR,
1541 G_IO_ERROR_INVALID_DATA,
1542 "got 0x%02x, expected 0x%02x @ 0x%04x",
1543 buf1[i], buf2[i], i);
1544 return FALSE;
1545 }
1546 }
1547
1548 /* success */
1549 return TRUE;
1550}
Richard Hughes484ee292019-03-22 16:10:50 +00001551
1552/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001553 * fu_common_bytes_compare:
1554 * @bytes1: a #GBytes
1555 * @bytes2: another #GBytes
1556 * @error: A #GError or %NULL
1557 *
1558 * Compares the buffers for equality.
1559 *
1560 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001561 *
1562 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001563 **/
1564gboolean
1565fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1566{
1567 const guint8 *buf1;
1568 const guint8 *buf2;
1569 gsize bufsz1;
1570 gsize bufsz2;
1571
1572 g_return_val_if_fail (bytes1 != NULL, FALSE);
1573 g_return_val_if_fail (bytes2 != NULL, FALSE);
1574 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1575
1576 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1577 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1578 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1579}
1580
1581/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001582 * fu_common_bytes_pad:
1583 * @bytes: a #GBytes
1584 * @sz: the desired size in bytes
1585 *
1586 * Pads a GBytes to a given @sz with `0xff`.
1587 *
1588 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001589 *
1590 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001591 **/
1592GBytes *
1593fu_common_bytes_pad (GBytes *bytes, gsize sz)
1594{
1595 gsize bytes_sz;
1596
1597 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1598
1599 /* pad */
1600 bytes_sz = g_bytes_get_size (bytes);
1601 if (bytes_sz < sz) {
1602 const guint8 *data = g_bytes_get_data (bytes, NULL);
1603 guint8 *data_new = g_malloc (sz);
1604 memcpy (data_new, data, bytes_sz);
1605 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1606 return g_bytes_new_take (data_new, sz);
1607 }
1608
1609 /* exactly right */
1610 return g_bytes_ref (bytes);
1611}
1612
1613/**
Richard Hughes484ee292019-03-22 16:10:50 +00001614 * fu_common_realpath:
1615 * @filename: a filename
1616 * @error: A #GError or %NULL
1617 *
1618 * Finds the canonicalized absolute filename for a path.
1619 *
1620 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001621 *
1622 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001623 **/
1624gchar *
1625fu_common_realpath (const gchar *filename, GError **error)
1626{
1627 char full_tmp[PATH_MAX];
1628
1629 g_return_val_if_fail (filename != NULL, NULL);
1630
Richard Hughes8694dee2019-11-22 09:16:34 +00001631#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001632 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001633#else
1634 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1635#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001636 g_set_error (error,
1637 G_IO_ERROR,
1638 G_IO_ERROR_INVALID_DATA,
1639 "cannot resolve path: %s",
1640 strerror (errno));
1641 return NULL;
1642 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001643 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1644 g_set_error (error,
1645 G_IO_ERROR,
1646 G_IO_ERROR_INVALID_DATA,
1647 "cannot find path: %s",
1648 full_tmp);
1649 return NULL;
1650 }
Richard Hughes484ee292019-03-22 16:10:50 +00001651 return g_strdup (full_tmp);
1652}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001653
1654/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001655 * fu_common_fnmatch:
1656 * @pattern: a glob pattern, e.g. `*foo*`
1657 * @str: a string to match against the pattern, e.g. `bazfoobar`
1658 *
1659 * Matches a string against a glob pattern.
1660 *
1661 * Return value: %TRUE if the string matched
1662 *
1663 * Since: 1.3.5
1664 **/
1665gboolean
1666fu_common_fnmatch (const gchar *pattern, const gchar *str)
1667{
1668 g_return_val_if_fail (pattern != NULL, FALSE);
1669 g_return_val_if_fail (str != NULL, FALSE);
1670#ifdef HAVE_FNMATCH_H
1671 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001672#elif _WIN32
1673 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1674 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1675 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001676#else
1677 return g_strcmp0 (pattern, str) == 0;
1678#endif
1679}
1680
1681/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001682 * fu_common_strnsplit:
1683 * @str: a string to split
1684 * @sz: size of @str
1685 * @delimiter: a string which specifies the places at which to split the string
1686 * @max_tokens: the maximum number of pieces to split @str into
1687 *
1688 * Splits a string into a maximum of @max_tokens pieces, using the given
1689 * delimiter. If @max_tokens is reached, the remainder of string is appended
1690 * to the last token.
1691 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001692 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001693 *
1694 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001695 **/
1696gchar **
1697fu_common_strnsplit (const gchar *str, gsize sz,
1698 const gchar *delimiter, gint max_tokens)
1699{
1700 if (str[sz - 1] != '\0') {
1701 g_autofree gchar *str2 = g_strndup (str, sz);
1702 return g_strsplit (str2, delimiter, max_tokens);
1703 }
1704 return g_strsplit (str, delimiter, max_tokens);
1705}
Richard Hughes5308ea42019-08-09 12:25:13 +01001706
1707/**
1708 * fu_memcpy_safe:
1709 * @dst: destination buffer
1710 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1711 * @dst_offset: offset in bytes into @dst to copy to
1712 * @src: source buffer
1713 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1714 * @src_offset: offset in bytes into @src to copy from
1715 * @n: number of bytes to copy from @src+@offset from
1716 * @error: A #GError or %NULL
1717 *
1718 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1719 * of both the destination and the source allows us to check for buffer overflow.
1720 *
1721 * Providing the buffer offsets also allows us to check reading past the end of
1722 * the source buffer. For this reason the caller should NEVER add an offset to
1723 * @src or @dst.
1724 *
1725 * You don't need to use this function in "obviously correct" cases, nor should
1726 * you use it when performance is a concern. Only us it when you're not sure if
1727 * malicious data from a device or firmware could cause memory corruption.
1728 *
1729 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001730 *
1731 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001732 **/
1733gboolean
1734fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1735 const guint8 *src, gsize src_sz, gsize src_offset,
1736 gsize n, GError **error)
1737{
1738 if (n == 0)
1739 return TRUE;
1740
1741 if (n > src_sz) {
1742 g_set_error (error,
1743 FWUPD_ERROR,
1744 FWUPD_ERROR_READ,
1745 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1746 (guint) n, (guint) src_sz);
1747 return FALSE;
1748 }
1749 if (n + src_offset > src_sz) {
1750 g_set_error (error,
1751 FWUPD_ERROR,
1752 FWUPD_ERROR_READ,
1753 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1754 (guint) n, (guint) src_offset, (guint) src_sz);
1755 return FALSE;
1756 }
1757 if (n > dst_sz) {
1758 g_set_error (error,
1759 FWUPD_ERROR,
1760 FWUPD_ERROR_WRITE,
1761 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1762 (guint) n, (guint) dst_sz);
1763 return FALSE;
1764 }
1765 if (n + dst_offset > dst_sz) {
1766 g_set_error (error,
1767 FWUPD_ERROR,
1768 FWUPD_ERROR_WRITE,
1769 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1770 (guint) n, (guint) dst_offset, (guint) dst_sz);
1771 return FALSE;
1772 }
1773
1774 /* phew! */
1775 memcpy (dst + dst_offset, src + src_offset, n);
1776 return TRUE;
1777}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001778
Richard Hughes80768f52019-10-22 07:19:14 +01001779/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001780 * fu_common_read_uint8_safe:
1781 * @buf: source buffer
1782 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1783 * @offset: offset in bytes into @buf to copy from
1784 * @value: (out) (allow-none): the parsed value
1785 * @error: A #GError or %NULL
1786 *
1787 * Read a value from a buffer in a safe way.
1788 *
1789 * You don't need to use this function in "obviously correct" cases, nor should
1790 * you use it when performance is a concern. Only us it when you're not sure if
1791 * malicious data from a device or firmware could cause memory corruption.
1792 *
1793 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001794 *
1795 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001796 **/
1797gboolean
1798fu_common_read_uint8_safe (const guint8 *buf,
1799 gsize bufsz,
1800 gsize offset,
1801 guint8 *value,
1802 GError **error)
1803{
1804 guint8 tmp;
1805 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1806 buf, bufsz, offset, /* src */
1807 sizeof(tmp), error))
1808 return FALSE;
1809 if (value != NULL)
1810 *value = tmp;
1811 return TRUE;
1812}
1813
1814/**
Richard Hughes80768f52019-10-22 07:19:14 +01001815 * fu_common_read_uint16_safe:
1816 * @buf: source buffer
1817 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1818 * @offset: offset in bytes into @buf to copy from
1819 * @value: (out) (allow-none): the parsed value
1820 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1821 * @error: A #GError or %NULL
1822 *
1823 * Read a value from a buffer using a specified endian in a safe way.
1824 *
1825 * You don't need to use this function in "obviously correct" cases, nor should
1826 * you use it when performance is a concern. Only us it when you're not sure if
1827 * malicious data from a device or firmware could cause memory corruption.
1828 *
1829 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001830 *
1831 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001832 **/
1833gboolean
1834fu_common_read_uint16_safe (const guint8 *buf,
1835 gsize bufsz,
1836 gsize offset,
1837 guint16 *value,
1838 FuEndianType endian,
1839 GError **error)
1840{
1841 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001842 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001843 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001844 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001845 return FALSE;
1846 if (value != NULL)
1847 *value = fu_common_read_uint16 (dst, endian);
1848 return TRUE;
1849}
1850
1851/**
1852 * fu_common_read_uint32_safe:
1853 * @buf: source buffer
1854 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1855 * @offset: offset in bytes into @buf to copy from
1856 * @value: (out) (allow-none): the parsed value
1857 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1858 * @error: A #GError or %NULL
1859 *
1860 * Read a value from a buffer using a specified endian in a safe way.
1861 *
1862 * You don't need to use this function in "obviously correct" cases, nor should
1863 * you use it when performance is a concern. Only us it when you're not sure if
1864 * malicious data from a device or firmware could cause memory corruption.
1865 *
1866 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001867 *
1868 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001869 **/
1870gboolean
1871fu_common_read_uint32_safe (const guint8 *buf,
1872 gsize bufsz,
1873 gsize offset,
1874 guint32 *value,
1875 FuEndianType endian,
1876 GError **error)
1877{
1878 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001879 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001880 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001881 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001882 return FALSE;
1883 if (value != NULL)
1884 *value = fu_common_read_uint32 (dst, endian);
1885 return TRUE;
1886}
1887
Mario Limonciello1a680f32019-11-25 19:44:53 -06001888/**
1889 * fu_byte_array_append_uint8:
1890 * @array: A #GByteArray
1891 * @data: #guint8
1892 *
1893 * Adds a 8 bit integer to a byte array
1894 *
1895 * Since: 1.3.1
1896 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001897void
1898fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1899{
1900 g_byte_array_append (array, &data, sizeof(data));
1901}
1902
Mario Limonciello1a680f32019-11-25 19:44:53 -06001903/**
1904 * fu_byte_array_append_uint16:
1905 * @array: A #GByteArray
1906 * @data: #guint16
1907 * @endian: #FuEndianType
1908 *
1909 * Adds a 16 bit integer to a byte array
1910 *
1911 * Since: 1.3.1
1912 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001913void
1914fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1915{
1916 guint8 buf[2];
1917 fu_common_write_uint16 (buf, data, endian);
1918 g_byte_array_append (array, buf, sizeof(buf));
1919}
1920
Mario Limonciello1a680f32019-11-25 19:44:53 -06001921/**
1922 * fu_byte_array_append_uint32:
1923 * @array: A #GByteArray
1924 * @data: #guint32
1925 * @endian: #FuEndianType
1926 *
1927 * Adds a 32 bit integer to a byte array
1928 *
1929 * Since: 1.3.1
1930 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001931void
1932fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1933{
1934 guint8 buf[4];
1935 fu_common_write_uint32 (buf, data, endian);
1936 g_byte_array_append (array, buf, sizeof(buf));
1937}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001938
1939/**
1940 * fu_common_kernel_locked_down:
1941 *
1942 * Determines if kernel lockdown in effect
1943 *
1944 * Since: 1.3.8
1945 **/
1946gboolean
1947fu_common_kernel_locked_down (void)
1948{
1949#ifndef _WIN32
1950 gsize len = 0;
1951 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
1952 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
1953 g_autofree gchar *data = NULL;
1954 g_auto(GStrv) options = NULL;
1955
1956 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
1957 return FALSE;
1958 if (!g_file_get_contents (fname, &data, &len, NULL))
1959 return FALSE;
1960 if (len < 1)
1961 return FALSE;
1962 options = g_strsplit (data, " ", -1);
1963 for (guint i = 0; options[i] != NULL; i++) {
1964 if (g_strcmp0 (options[i], "[none]") == 0)
1965 return FALSE;
1966 }
1967 return TRUE;
1968#else
1969 return FALSE;
1970#endif
1971}