blob: 064ce62b8b34fc89750aee151f6e9a9b8a46e32b [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 Hughesb9640a22020-05-05 20:42:47 +01001099 /* /usr/share/fwupd/dbx */
1100 case FU_PATH_KIND_EFIDBXDIR:
1101 tmp = g_getenv ("FWUPD_EFIDBXDIR");
1102 if (tmp != NULL)
1103 return g_strdup (tmp);
1104#ifdef FWUPD_EFI_DBXDIR
1105 tmp = g_getenv ("SNAP");
1106 if (tmp != NULL)
1107 return g_build_filename (tmp, FWUPD_EFI_DBXDIR, NULL);
1108 return g_strdup (FWUPD_EFI_DBXDIR);
1109#else
1110 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1111 return g_build_filename (basedir, "dbx", NULL);
1112#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001113 /* /etc/fwupd */
1114 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001115 tmp = g_getenv ("CONFIGURATION_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_SYSCONFDIR);
1119 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1120 /* /var/lib/fwupd */
1121 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001122 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001123 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001124 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001125 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1126 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1127 /* /var/cache/fwupd */
1128 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001129 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001130 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001131 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001132 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1133 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001134 case FU_PATH_KIND_OFFLINE_TRIGGER:
1135 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1136 if (tmp != NULL)
1137 return g_strdup (tmp);
1138 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001139 case FU_PATH_KIND_POLKIT_ACTIONS:
1140#ifdef POLKIT_ACTIONDIR
1141 return g_strdup (POLKIT_ACTIONDIR);
1142#else
1143 return NULL;
1144#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001145 /* this shouldn't happen */
1146 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001147 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001148 }
1149
1150 return NULL;
1151}
Richard Hughes83e56c12018-10-10 20:24:41 +01001152
1153/**
1154 * fu_common_string_replace:
1155 * @string: The #GString to operate on
1156 * @search: The text to search for
1157 * @replace: The text to use for substitutions
1158 *
1159 * Performs multiple search and replace operations on the given string.
1160 *
1161 * Returns: the number of replacements done, or 0 if @search is not found.
1162 *
1163 * Since: 1.2.0
1164 **/
1165guint
1166fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1167{
1168 gchar *tmp;
1169 guint count = 0;
1170 gsize search_idx = 0;
1171 gsize replace_len;
1172 gsize search_len;
1173
1174 g_return_val_if_fail (string != NULL, 0);
1175 g_return_val_if_fail (search != NULL, 0);
1176 g_return_val_if_fail (replace != NULL, 0);
1177
1178 /* nothing to do */
1179 if (string->len == 0)
1180 return 0;
1181
1182 search_len = strlen (search);
1183 replace_len = strlen (replace);
1184
1185 do {
1186 tmp = g_strstr_len (string->str + search_idx, -1, search);
1187 if (tmp == NULL)
1188 break;
1189
1190 /* advance the counter in case @replace contains @search */
1191 search_idx = (gsize) (tmp - string->str);
1192
1193 /* reallocate the string if required */
1194 if (search_len > replace_len) {
1195 g_string_erase (string,
1196 (gssize) search_idx,
1197 (gssize) (search_len - replace_len));
1198 memcpy (tmp, replace, replace_len);
1199 } else if (search_len < replace_len) {
1200 g_string_insert_len (string,
1201 (gssize) search_idx,
1202 replace,
1203 (gssize) (replace_len - search_len));
1204 /* we have to treat this specially as it could have
1205 * been reallocated when the insertion happened */
1206 memcpy (string->str + search_idx, replace, replace_len);
1207 } else {
1208 /* just memcmp in the new string */
1209 memcpy (tmp, replace, replace_len);
1210 }
1211 search_idx += replace_len;
1212 count++;
1213 } while (TRUE);
1214
1215 return count;
1216}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001217
Richard Hughesae96a1f2019-09-23 11:16:36 +01001218/**
1219 * fu_common_strwidth:
1220 * @text: The string to operate on
1221 *
1222 * Returns the width of the string in displayed characters on the console.
1223 *
1224 * Returns: width of text
1225 *
1226 * Since: 1.3.2
1227 **/
1228gsize
1229fu_common_strwidth (const gchar *text)
1230{
1231 const gchar *p = text;
1232 gsize width = 0;
1233 while (*p) {
1234 gunichar c = g_utf8_get_char (p);
1235 if (g_unichar_iswide (c))
1236 width += 2;
1237 else if (!g_unichar_iszerowidth (c))
1238 width += 1;
1239 p = g_utf8_next_char (p);
1240 }
1241 return width;
1242}
1243
Mario Limonciello1a680f32019-11-25 19:44:53 -06001244/**
1245 * fu_common_string_append_kv:
1246 * @str: A #GString
1247 * @idt: The indent
1248 * @key: A string to append
1249 * @value: a string to append
1250 *
1251 * Appends a key and string value to a string
1252 *
1253 * Since: 1.2.4
1254 */
Richard Hughescea28de2019-08-09 11:16:40 +01001255void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001256fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001257{
Richard Hughes847cae82019-08-27 11:22:23 +01001258 const guint align = 25;
1259 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001260
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001261 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001262
1263 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001264 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001265 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001266 for (gsize i = 0; i < idt; i++)
1267 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001268 if (key[0] != '\0') {
1269 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001270 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001271 } else {
1272 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001273 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001274 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001275 g_auto(GStrv) split = NULL;
1276 split = g_strsplit (value, "\n", -1);
1277 for (guint i = 0; split[i] != NULL; i++) {
1278 if (i == 0) {
1279 for (gsize j = keysz; j < align; j++)
1280 g_string_append (str, " ");
1281 } else {
1282 for (gsize j = 0; j < idt; j++)
1283 g_string_append (str, " ");
1284 }
1285 g_string_append (str, split[i]);
1286 g_string_append (str, "\n");
1287 }
1288 } else {
1289 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001290 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001291}
1292
Mario Limonciello1a680f32019-11-25 19:44:53 -06001293/**
1294 * fu_common_string_append_ku:
1295 * @str: A #GString
1296 * @idt: The indent
1297 * @key: A string to append
1298 * @value: guint64
1299 *
1300 * Appends a key and unsigned integer to a string
1301 *
1302 * Since: 1.2.4
1303 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001304void
1305fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1306{
1307 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1308 fu_common_string_append_kv (str, idt, key, tmp);
1309}
1310
Mario Limonciello1a680f32019-11-25 19:44:53 -06001311/**
1312 * fu_common_string_append_kx:
1313 * @str: A #GString
1314 * @idt: The indent
1315 * @key: A string to append
1316 * @value: guint64
1317 *
1318 * Appends a key and hex integer to a string
1319 *
1320 * Since: 1.2.4
1321 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001322void
1323fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1324{
1325 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1326 fu_common_string_append_kv (str, idt, key, tmp);
1327}
1328
Mario Limonciello1a680f32019-11-25 19:44:53 -06001329/**
1330 * fu_common_string_append_kb:
1331 * @str: A #GString
1332 * @idt: The indent
1333 * @key: A string to append
1334 * @value: Boolean
1335 *
1336 * Appends a key and boolean value to a string
1337 *
1338 * Since: 1.2.4
1339 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001340void
1341fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1342{
1343 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001344}
1345
Richard Hughese59cb9a2018-12-05 14:37:40 +00001346/**
Richard Hughes35481862019-01-06 12:01:58 +00001347 * fu_common_dump_full:
1348 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1349 * @title: prefix title, or %NULL
1350 * @data: buffer to print
1351 * @len: the size of @data
1352 * @columns: break new lines after this many bytes
1353 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1354 *
1355 * Dumps a raw buffer to the screen.
1356 *
1357 * Since: 1.2.4
1358 **/
1359void
1360fu_common_dump_full (const gchar *log_domain,
1361 const gchar *title,
1362 const guint8 *data,
1363 gsize len,
1364 guint columns,
1365 FuDumpFlags flags)
1366{
1367 g_autoptr(GString) str = g_string_new (NULL);
1368
1369 /* optional */
1370 if (title != NULL)
1371 g_string_append_printf (str, "%s:", title);
1372
1373 /* if more than can fit on one line then start afresh */
1374 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1375 g_string_append (str, "\n");
1376 } else {
1377 for (gsize i = str->len; i < 16; i++)
1378 g_string_append (str, " ");
1379 }
1380
1381 /* offset line */
1382 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1383 g_string_append (str, " │ ");
1384 for (gsize i = 0; i < columns; i++)
1385 g_string_append_printf (str, "%02x ", (guint) i);
1386 g_string_append (str, "\n───────┼");
1387 for (gsize i = 0; i < columns; i++)
1388 g_string_append (str, "───");
1389 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1390 }
1391
1392 /* print each row */
1393 for (gsize i = 0; i < len; i++) {
1394 g_string_append_printf (str, "%02x ", data[i]);
1395
1396 /* optionally print ASCII char */
1397 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1398 if (g_ascii_isprint (data[i]))
1399 g_string_append_printf (str, "[%c] ", data[i]);
1400 else
1401 g_string_append (str, "[?] ");
1402 }
1403
1404 /* new row required */
1405 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1406 g_string_append (str, "\n");
1407 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1408 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1409 }
1410 }
1411 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1412}
1413
1414/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001415 * fu_common_dump_raw:
1416 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1417 * @title: prefix title, or %NULL
1418 * @data: buffer to print
1419 * @len: the size of @data
1420 *
1421 * Dumps a raw buffer to the screen.
1422 *
1423 * Since: 1.2.2
1424 **/
1425void
1426fu_common_dump_raw (const gchar *log_domain,
1427 const gchar *title,
1428 const guint8 *data,
1429 gsize len)
1430{
Richard Hughes35481862019-01-06 12:01:58 +00001431 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1432 if (len > 64)
1433 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1434 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001435}
1436
1437/**
Mario Limonciello39602652019-04-29 21:08:58 -05001438 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001439 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1440 * @title: prefix title, or %NULL
1441 * @bytes: a #GBytes
1442 *
1443 * Dumps a byte buffer to the screen.
1444 *
1445 * Since: 1.2.2
1446 **/
1447void
1448fu_common_dump_bytes (const gchar *log_domain,
1449 const gchar *title,
1450 GBytes *bytes)
1451{
1452 gsize len = 0;
1453 const guint8 *data = g_bytes_get_data (bytes, &len);
1454 fu_common_dump_raw (log_domain, title, data, len);
1455}
Richard Hughesfc90f392019-01-15 21:21:16 +00001456
1457/**
1458 * fu_common_bytes_align:
1459 * @bytes: a #GBytes
1460 * @blksz: block size in bytes
1461 * @padval: the byte used to pad the byte buffer
1462 *
1463 * Aligns a block of memory to @blksize using the @padval value; if
1464 * the block is already aligned then the original @bytes is returned.
1465 *
1466 * Returns: (transfer full): a #GBytes, possibly @bytes
1467 *
1468 * Since: 1.2.4
1469 **/
1470GBytes *
1471fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1472{
1473 const guint8 *data;
1474 gsize sz;
1475
1476 g_return_val_if_fail (bytes != NULL, NULL);
1477 g_return_val_if_fail (blksz > 0, NULL);
1478
1479 /* pad */
1480 data = g_bytes_get_data (bytes, &sz);
1481 if (sz % blksz != 0) {
1482 gsize sz_align = ((sz / blksz) + 1) * blksz;
1483 guint8 *data_align = g_malloc (sz_align);
1484 memcpy (data_align, data, sz);
1485 memset (data_align + sz, padval, sz_align - sz);
1486 g_debug ("aligning 0x%x bytes to 0x%x",
1487 (guint) sz, (guint) sz_align);
1488 return g_bytes_new_take (data_align, sz_align);
1489 }
1490
1491 /* perfectly aligned */
1492 return g_bytes_ref (bytes);
1493}
Richard Hughes36999462019-03-19 20:23:29 +00001494
1495/**
1496 * fu_common_bytes_is_empty:
1497 * @bytes: a #GBytes
1498 *
1499 * Checks if a byte array are just empty (0xff) bytes.
1500 *
1501 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001502 *
1503 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001504 **/
1505gboolean
1506fu_common_bytes_is_empty (GBytes *bytes)
1507{
1508 gsize sz = 0;
1509 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1510 for (gsize i = 0; i < sz; i++) {
1511 if (buf[i] != 0xff)
1512 return FALSE;
1513 }
1514 return TRUE;
1515}
Richard Hughes2aad1042019-03-21 09:03:32 +00001516
1517/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001518 * fu_common_bytes_compare_raw:
1519 * @buf1: a buffer
1520 * @bufsz1: sizeof @buf1
1521 * @buf2: another buffer
1522 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001523 * @error: A #GError or %NULL
1524 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001525 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001526 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001527 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001528 *
1529 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001530 **/
1531gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001532fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1533 const guint8 *buf2, gsize bufsz2,
1534 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001535{
Richard Hughes38245ff2019-09-18 14:46:09 +01001536 g_return_val_if_fail (buf1 != NULL, FALSE);
1537 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001538 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1539
1540 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001541 if (bufsz1 != bufsz2) {
1542 g_set_error (error,
1543 G_IO_ERROR,
1544 G_IO_ERROR_INVALID_DATA,
1545 "got %" G_GSIZE_FORMAT " bytes, expected "
1546 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1547 return FALSE;
1548 }
1549
1550 /* check matches */
1551 for (guint i = 0x0; i < bufsz1; i++) {
1552 if (buf1[i] != buf2[i]) {
1553 g_set_error (error,
1554 G_IO_ERROR,
1555 G_IO_ERROR_INVALID_DATA,
1556 "got 0x%02x, expected 0x%02x @ 0x%04x",
1557 buf1[i], buf2[i], i);
1558 return FALSE;
1559 }
1560 }
1561
1562 /* success */
1563 return TRUE;
1564}
Richard Hughes484ee292019-03-22 16:10:50 +00001565
1566/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001567 * fu_common_bytes_compare:
1568 * @bytes1: a #GBytes
1569 * @bytes2: another #GBytes
1570 * @error: A #GError or %NULL
1571 *
1572 * Compares the buffers for equality.
1573 *
1574 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001575 *
1576 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001577 **/
1578gboolean
1579fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1580{
1581 const guint8 *buf1;
1582 const guint8 *buf2;
1583 gsize bufsz1;
1584 gsize bufsz2;
1585
1586 g_return_val_if_fail (bytes1 != NULL, FALSE);
1587 g_return_val_if_fail (bytes2 != NULL, FALSE);
1588 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1589
1590 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1591 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1592 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1593}
1594
1595/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001596 * fu_common_bytes_pad:
1597 * @bytes: a #GBytes
1598 * @sz: the desired size in bytes
1599 *
1600 * Pads a GBytes to a given @sz with `0xff`.
1601 *
1602 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001603 *
1604 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001605 **/
1606GBytes *
1607fu_common_bytes_pad (GBytes *bytes, gsize sz)
1608{
1609 gsize bytes_sz;
1610
1611 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1612
1613 /* pad */
1614 bytes_sz = g_bytes_get_size (bytes);
1615 if (bytes_sz < sz) {
1616 const guint8 *data = g_bytes_get_data (bytes, NULL);
1617 guint8 *data_new = g_malloc (sz);
1618 memcpy (data_new, data, bytes_sz);
1619 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1620 return g_bytes_new_take (data_new, sz);
1621 }
1622
1623 /* exactly right */
1624 return g_bytes_ref (bytes);
1625}
1626
1627/**
Richard Hughes484ee292019-03-22 16:10:50 +00001628 * fu_common_realpath:
1629 * @filename: a filename
1630 * @error: A #GError or %NULL
1631 *
1632 * Finds the canonicalized absolute filename for a path.
1633 *
1634 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001635 *
1636 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001637 **/
1638gchar *
1639fu_common_realpath (const gchar *filename, GError **error)
1640{
1641 char full_tmp[PATH_MAX];
1642
1643 g_return_val_if_fail (filename != NULL, NULL);
1644
Richard Hughes8694dee2019-11-22 09:16:34 +00001645#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001646 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001647#else
1648 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1649#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001650 g_set_error (error,
1651 G_IO_ERROR,
1652 G_IO_ERROR_INVALID_DATA,
1653 "cannot resolve path: %s",
1654 strerror (errno));
1655 return NULL;
1656 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001657 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1658 g_set_error (error,
1659 G_IO_ERROR,
1660 G_IO_ERROR_INVALID_DATA,
1661 "cannot find path: %s",
1662 full_tmp);
1663 return NULL;
1664 }
Richard Hughes484ee292019-03-22 16:10:50 +00001665 return g_strdup (full_tmp);
1666}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001667
1668/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001669 * fu_common_fnmatch:
1670 * @pattern: a glob pattern, e.g. `*foo*`
1671 * @str: a string to match against the pattern, e.g. `bazfoobar`
1672 *
1673 * Matches a string against a glob pattern.
1674 *
1675 * Return value: %TRUE if the string matched
1676 *
1677 * Since: 1.3.5
1678 **/
1679gboolean
1680fu_common_fnmatch (const gchar *pattern, const gchar *str)
1681{
1682 g_return_val_if_fail (pattern != NULL, FALSE);
1683 g_return_val_if_fail (str != NULL, FALSE);
1684#ifdef HAVE_FNMATCH_H
1685 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001686#elif _WIN32
1687 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1688 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1689 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001690#else
1691 return g_strcmp0 (pattern, str) == 0;
1692#endif
1693}
1694
Richard Hughesa84d7a72020-05-06 12:11:51 +01001695static gint
1696fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1697{
1698 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1699}
1700
1701/**
1702 * fu_common_filename_glob:
1703 * @directory: a directory path
1704 * @pattern: a glob pattern, e.g. `*foo*`
1705 * @error: A #GError or %NULL
1706 *
1707 * Returns all the filenames that match a specific glob pattern.
1708 * Any results are sorted. No matching files will set @error.
1709 *
1710 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1711 *
1712 * Since: 1.5.0
1713 **/
1714GPtrArray *
1715fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1716{
1717 const gchar *basename;
1718 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1719 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1720 if (dir == NULL)
1721 return NULL;
1722 while ((basename = g_dir_read_name (dir)) != NULL) {
1723 if (!fu_common_fnmatch (pattern, basename))
1724 continue;
1725 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1726 }
1727 if (files->len == 0) {
1728 g_set_error_literal (error,
1729 G_IO_ERROR,
1730 G_IO_ERROR_NOT_FOUND,
1731 "no files matched pattern");
1732 return NULL;
1733 }
1734 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1735 return g_steal_pointer (&files);
1736}
1737
Richard Hughes5c508de2019-11-22 09:57:34 +00001738/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001739 * fu_common_strnsplit:
1740 * @str: a string to split
1741 * @sz: size of @str
1742 * @delimiter: a string which specifies the places at which to split the string
1743 * @max_tokens: the maximum number of pieces to split @str into
1744 *
1745 * Splits a string into a maximum of @max_tokens pieces, using the given
1746 * delimiter. If @max_tokens is reached, the remainder of string is appended
1747 * to the last token.
1748 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001749 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001750 *
1751 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001752 **/
1753gchar **
1754fu_common_strnsplit (const gchar *str, gsize sz,
1755 const gchar *delimiter, gint max_tokens)
1756{
1757 if (str[sz - 1] != '\0') {
1758 g_autofree gchar *str2 = g_strndup (str, sz);
1759 return g_strsplit (str2, delimiter, max_tokens);
1760 }
1761 return g_strsplit (str, delimiter, max_tokens);
1762}
Richard Hughes5308ea42019-08-09 12:25:13 +01001763
1764/**
1765 * fu_memcpy_safe:
1766 * @dst: destination buffer
1767 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1768 * @dst_offset: offset in bytes into @dst to copy to
1769 * @src: source buffer
1770 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1771 * @src_offset: offset in bytes into @src to copy from
1772 * @n: number of bytes to copy from @src+@offset from
1773 * @error: A #GError or %NULL
1774 *
1775 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1776 * of both the destination and the source allows us to check for buffer overflow.
1777 *
1778 * Providing the buffer offsets also allows us to check reading past the end of
1779 * the source buffer. For this reason the caller should NEVER add an offset to
1780 * @src or @dst.
1781 *
1782 * You don't need to use this function in "obviously correct" cases, nor should
1783 * you use it when performance is a concern. Only us it when you're not sure if
1784 * malicious data from a device or firmware could cause memory corruption.
1785 *
1786 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001787 *
1788 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001789 **/
1790gboolean
1791fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1792 const guint8 *src, gsize src_sz, gsize src_offset,
1793 gsize n, GError **error)
1794{
1795 if (n == 0)
1796 return TRUE;
1797
1798 if (n > src_sz) {
1799 g_set_error (error,
1800 FWUPD_ERROR,
1801 FWUPD_ERROR_READ,
1802 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1803 (guint) n, (guint) src_sz);
1804 return FALSE;
1805 }
1806 if (n + src_offset > src_sz) {
1807 g_set_error (error,
1808 FWUPD_ERROR,
1809 FWUPD_ERROR_READ,
1810 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1811 (guint) n, (guint) src_offset, (guint) src_sz);
1812 return FALSE;
1813 }
1814 if (n > dst_sz) {
1815 g_set_error (error,
1816 FWUPD_ERROR,
1817 FWUPD_ERROR_WRITE,
1818 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1819 (guint) n, (guint) dst_sz);
1820 return FALSE;
1821 }
1822 if (n + dst_offset > dst_sz) {
1823 g_set_error (error,
1824 FWUPD_ERROR,
1825 FWUPD_ERROR_WRITE,
1826 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1827 (guint) n, (guint) dst_offset, (guint) dst_sz);
1828 return FALSE;
1829 }
1830
1831 /* phew! */
1832 memcpy (dst + dst_offset, src + src_offset, n);
1833 return TRUE;
1834}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001835
Richard Hughes80768f52019-10-22 07:19:14 +01001836/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001837 * fu_common_read_uint8_safe:
1838 * @buf: source buffer
1839 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1840 * @offset: offset in bytes into @buf to copy from
1841 * @value: (out) (allow-none): the parsed value
1842 * @error: A #GError or %NULL
1843 *
1844 * Read a value from a buffer in a safe way.
1845 *
1846 * You don't need to use this function in "obviously correct" cases, nor should
1847 * you use it when performance is a concern. Only us it when you're not sure if
1848 * malicious data from a device or firmware could cause memory corruption.
1849 *
1850 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001851 *
1852 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001853 **/
1854gboolean
1855fu_common_read_uint8_safe (const guint8 *buf,
1856 gsize bufsz,
1857 gsize offset,
1858 guint8 *value,
1859 GError **error)
1860{
1861 guint8 tmp;
1862 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1863 buf, bufsz, offset, /* src */
1864 sizeof(tmp), error))
1865 return FALSE;
1866 if (value != NULL)
1867 *value = tmp;
1868 return TRUE;
1869}
1870
1871/**
Richard Hughes80768f52019-10-22 07:19:14 +01001872 * fu_common_read_uint16_safe:
1873 * @buf: source buffer
1874 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1875 * @offset: offset in bytes into @buf to copy from
1876 * @value: (out) (allow-none): the parsed value
1877 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1878 * @error: A #GError or %NULL
1879 *
1880 * Read a value from a buffer using a specified endian in a safe way.
1881 *
1882 * You don't need to use this function in "obviously correct" cases, nor should
1883 * you use it when performance is a concern. Only us it when you're not sure if
1884 * malicious data from a device or firmware could cause memory corruption.
1885 *
1886 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001887 *
1888 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001889 **/
1890gboolean
1891fu_common_read_uint16_safe (const guint8 *buf,
1892 gsize bufsz,
1893 gsize offset,
1894 guint16 *value,
1895 FuEndianType endian,
1896 GError **error)
1897{
1898 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001899 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001900 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001901 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001902 return FALSE;
1903 if (value != NULL)
1904 *value = fu_common_read_uint16 (dst, endian);
1905 return TRUE;
1906}
1907
1908/**
1909 * fu_common_read_uint32_safe:
1910 * @buf: source buffer
1911 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1912 * @offset: offset in bytes into @buf to copy from
1913 * @value: (out) (allow-none): the parsed value
1914 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1915 * @error: A #GError or %NULL
1916 *
1917 * Read a value from a buffer using a specified endian in a safe way.
1918 *
1919 * You don't need to use this function in "obviously correct" cases, nor should
1920 * you use it when performance is a concern. Only us it when you're not sure if
1921 * malicious data from a device or firmware could cause memory corruption.
1922 *
1923 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001924 *
1925 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001926 **/
1927gboolean
1928fu_common_read_uint32_safe (const guint8 *buf,
1929 gsize bufsz,
1930 gsize offset,
1931 guint32 *value,
1932 FuEndianType endian,
1933 GError **error)
1934{
1935 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001936 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001937 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001938 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001939 return FALSE;
1940 if (value != NULL)
1941 *value = fu_common_read_uint32 (dst, endian);
1942 return TRUE;
1943}
1944
Mario Limonciello1a680f32019-11-25 19:44:53 -06001945/**
1946 * fu_byte_array_append_uint8:
1947 * @array: A #GByteArray
1948 * @data: #guint8
1949 *
1950 * Adds a 8 bit integer to a byte array
1951 *
1952 * Since: 1.3.1
1953 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001954void
1955fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1956{
1957 g_byte_array_append (array, &data, sizeof(data));
1958}
1959
Mario Limonciello1a680f32019-11-25 19:44:53 -06001960/**
1961 * fu_byte_array_append_uint16:
1962 * @array: A #GByteArray
1963 * @data: #guint16
1964 * @endian: #FuEndianType
1965 *
1966 * Adds a 16 bit integer to a byte array
1967 *
1968 * Since: 1.3.1
1969 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001970void
1971fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1972{
1973 guint8 buf[2];
1974 fu_common_write_uint16 (buf, data, endian);
1975 g_byte_array_append (array, buf, sizeof(buf));
1976}
1977
Mario Limonciello1a680f32019-11-25 19:44:53 -06001978/**
1979 * fu_byte_array_append_uint32:
1980 * @array: A #GByteArray
1981 * @data: #guint32
1982 * @endian: #FuEndianType
1983 *
1984 * Adds a 32 bit integer to a byte array
1985 *
1986 * Since: 1.3.1
1987 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001988void
1989fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1990{
1991 guint8 buf[4];
1992 fu_common_write_uint32 (buf, data, endian);
1993 g_byte_array_append (array, buf, sizeof(buf));
1994}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001995
1996/**
1997 * fu_common_kernel_locked_down:
1998 *
1999 * Determines if kernel lockdown in effect
2000 *
2001 * Since: 1.3.8
2002 **/
2003gboolean
2004fu_common_kernel_locked_down (void)
2005{
2006#ifndef _WIN32
2007 gsize len = 0;
2008 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2009 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2010 g_autofree gchar *data = NULL;
2011 g_auto(GStrv) options = NULL;
2012
2013 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2014 return FALSE;
2015 if (!g_file_get_contents (fname, &data, &len, NULL))
2016 return FALSE;
2017 if (len < 1)
2018 return FALSE;
2019 options = g_strsplit (data, " ", -1);
2020 for (guint i = 0; options[i] != NULL; i++) {
2021 if (g_strcmp0 (options[i], "[none]") == 0)
2022 return FALSE;
2023 }
2024 return TRUE;
2025#else
2026 return FALSE;
2027#endif
2028}