From 547b9bf0b594b6bf1fdb4735992cb23f6905075f Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 17 Aug 2023 11:14:28 +0200 Subject: [PATCH 01/19] src: utils: Add function to get the stdout output of a command Add a new function pu_spwan_command_line_sync_result() which returns the output of stdout. Also, add a corresponding unit test. Signed-off-by: Leonard Anderweit --- src/pu-utils.c | 20 +++++++++++++++----- src/pu-utils.h | 3 +++ tests/utils.c | 12 ++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/pu-utils.c b/src/pu-utils.c index 6a96fde4..314c6b24 100644 --- a/src/pu-utils.c +++ b/src/pu-utils.c @@ -21,8 +21,9 @@ #define UDEVADM_SETTLE_TIMEOUT 10 gboolean -pu_spawn_command_line_sync(const gchar *command_line, - GError **error) +pu_spawn_command_line_sync_result(const gchar *command_line, + gchar **result, + GError **error) { GSpawnFlags spawn_flags; gchar **argv = NULL; @@ -36,9 +37,11 @@ pu_spawn_command_line_sync(const gchar *command_line, if (!g_shell_parse_argv(command_line, NULL, &argv, error)) return FALSE; - spawn_flags = G_SPAWN_SEARCH_PATH | - G_SPAWN_STDOUT_TO_DEV_NULL; - if (!g_spawn_sync(NULL, argv, NULL, spawn_flags, NULL, NULL, NULL, &errmsg, + spawn_flags = G_SPAWN_SEARCH_PATH; + if (!result) + spawn_flags |= G_SPAWN_STDOUT_TO_DEV_NULL; + + if (!g_spawn_sync(NULL, argv, NULL, spawn_flags, NULL, NULL, result, &errmsg, &wait_status, error)) { g_prefix_error(error, "Failed spawning process: "); g_strfreev(argv); @@ -56,6 +59,13 @@ pu_spawn_command_line_sync(const gchar *command_line, return TRUE; } +gboolean +pu_spawn_command_line_sync(const gchar *command_line, + GError **error) +{ + return pu_spawn_command_line_sync_result(command_line, NULL, error); +} + gboolean pu_file_copy(const gchar *src, const gchar *dest, diff --git a/src/pu-utils.h b/src/pu-utils.h index 89731abd..67399973 100644 --- a/src/pu-utils.h +++ b/src/pu-utils.h @@ -9,6 +9,9 @@ #include #include +gboolean pu_spawn_command_line_sync_result(const gchar *command_line, + gchar **result, + GError **error); gboolean pu_spawn_command_line_sync(const gchar *command_line, GError **error); gboolean pu_file_copy(const gchar *src, diff --git a/tests/utils.c b/tests/utils.c index 1e89a201..33e87f87 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -243,6 +243,17 @@ test_device_get_partition_pattern(void) g_assert_false(g_regex_match_simple(pattern, "/dev/sdb1", 0, 0)); } +static void +test_spawn_command_line_sync_result(void) +{ + g_autoptr(GError) error = NULL; + g_autofree gchar *result = NULL; + + g_assert_true(pu_spawn_command_line_sync_result("echo 123", &result, &error)); + g_assert_no_error(error); + g_assert_nonnull(strstr(result, "123")); +} + int main(int argc, char *argv[]) @@ -276,6 +287,7 @@ main(int argc, test_get_file_size); g_test_add_func("/utils/str_pre_remove", test_str_pre_remove); g_test_add_func("/utils/device_get_partition_pattern", test_device_get_partition_pattern); + g_test_add_func("/utils/spawn_command_line_sync_result", test_spawn_command_line_sync_result); return g_test_run(); } From 4d7e856ced8ce58b208c2f4d955d451f6fb5c585 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 28 Sep 2023 10:04:47 +0200 Subject: [PATCH 02/19] src: utils: Allow copying to file destinations Only append the source filename in case the destination is a directory. Signed-off-by: Leonard Anderweit --- src/pu-utils.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pu-utils.c b/src/pu-utils.c index 314c6b24..a660e2e0 100644 --- a/src/pu-utils.c +++ b/src/pu-utils.c @@ -81,8 +81,12 @@ pu_file_copy(const gchar *src, g_debug("Copying '%s' to '%s'", src, dest); + if (g_file_test(dest, G_FILE_TEST_IS_DIR)) + out_path = g_build_filename(dest, g_path_get_basename(src), NULL); + else + out_path = g_strdup(dest); + in = g_file_new_for_path(src); - out_path = g_build_filename(dest, g_path_get_basename(src), NULL); out = g_file_new_for_path(out_path); return g_file_copy(in, out, G_FILE_COPY_NONE, NULL, NULL, NULL, error); From 75c241dfc6acc09b4bb167ab340520a0926d9b7b Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 28 Sep 2023 09:30:34 +0200 Subject: [PATCH 03/19] src: Add loopdev functions Add functions to create and remove loop devices. Signed-off-by: Leonard Anderweit --- meson.build | 1 + src/pu-loopdev.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pu-loopdev.h | 30 +++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/pu-loopdev.c create mode 100644 src/pu-loopdev.h diff --git a/meson.build b/meson.build index e37590f4..becf1554 100644 --- a/meson.build +++ b/meson.build @@ -40,6 +40,7 @@ src = [ 'src/pu-glib-compat.c', 'src/pu-hashtable.c', 'src/pu-log.c', + 'src/pu-loopdev.c', 'src/pu-mount.c', 'src/pu-package.c', 'src/pu-utils.c' diff --git a/src/pu-loopdev.c b/src/pu-loopdev.c new file mode 100644 index 00000000..665560cf --- /dev/null +++ b/src/pu-loopdev.c @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#define G_LOG_DOMAIN "partup-loopdev" + +#include +#include "pu-error.h" +#include "pu-loopdev.h" +#include "pu-utils.h" + +PuLoopdev * +pu_loopdev_new(gsize size, + GError **error) +{ + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_autofree gchar *buffer = NULL; + g_autoptr(GFileIOStream) iostream = NULL; + + PuLoopdev *loopdev = g_new0(PuLoopdev, 1); + loopdev->file = g_file_new_tmp("partup-loopdev-XXXXXX", &iostream, error); + if (!loopdev->file) + return NULL; + + buffer = g_new0(gchar, size); + if (!buffer) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Failed creating a new loopdev"); + return NULL; + } + + if (!g_file_replace_contents(loopdev->file, buffer, size, NULL, FALSE, + 0, NULL, NULL, error)) + return NULL; + + return loopdev; +} + +gboolean +pu_loopdev_attach(PuLoopdev *loopdev, + GError **error) +{ + g_return_val_if_fail(loopdev, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_autofree gchar *cmd = NULL; + g_autofree gchar *path = NULL; + + cmd = g_strdup_printf("losetup -f --show -P %s", g_file_get_path(loopdev->file)); + if (!pu_spawn_command_line_sync_result(cmd, &path, error)) { + return FALSE; + } + + loopdev->device = g_strdup(g_strstrip(path)); + return TRUE; +} + +gboolean +pu_loopdev_detach(PuLoopdev *loopdev, + GError **error) +{ + g_return_val_if_fail(loopdev, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_autofree gchar *cmd = NULL; + + cmd = g_strdup_printf("losetup -d %s", loopdev->device); + if (!pu_spawn_command_line_sync(cmd, error)) + return FALSE; + + g_free(loopdev->device); + return TRUE; +} + +void +pu_loopdev_free(PuLoopdev *loopdev) +{ + if (!loopdev) + return; + + g_file_delete(loopdev->file, NULL, NULL); + g_object_unref(loopdev->file); + g_free(loopdev); +} diff --git a/src/pu-loopdev.h b/src/pu-loopdev.h new file mode 100644 index 00000000..2d96b6e2 --- /dev/null +++ b/src/pu-loopdev.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#ifndef PARTUP_LOOPDEV_H +#define PARTUP_LOOPDEV_H + +#define PU_LOOPDEV_ERROR (pu_loopdev_error_quark()) + +GQuark pu_loopdev_error_quark(void); + +typedef enum { + PU_LOOPDEV_ERROR_FAILED +} PutLoopdevError; + +typedef struct { + GFile *file; + gchar *device; +} PuLoopdev; + +PuLoopdev * pu_loopdev_new(gsize size, + GError **error); +gboolean pu_loopdev_attach(PuLoopdev *loopdev, + GError **error); +gboolean pu_loopdev_detach(PuLoopdev *loopdev, + GError **error); +void pu_loopdev_free(PuLoopdev *loopdev); + +#endif /* PARTUP_LOOPDEV_H */ From ec7a97ba13c82a7cc9cd88fb04ebf0b1461c9fc9 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 24 Oct 2023 17:26:31 +0200 Subject: [PATCH 04/19] tests: Add test for loopdev Signed-off-by: Leonard Anderweit --- tests/loopdev-root.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 52 insertions(+) create mode 100644 tests/loopdev-root.c diff --git a/tests/loopdev-root.c b/tests/loopdev-root.c new file mode 100644 index 00000000..8f9213be --- /dev/null +++ b/tests/loopdev-root.c @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#include +#include +#include +#include "pu-error.h" +#include "pu-loopdev.h" + +#define LOOPDEV_SIZE 1 * 1024 * 1024 + +static void +test_loopdev(void) +{ + g_autoptr(GError) error = NULL; + PuLoopdev *loopdev; + + loopdev = pu_loopdev_new(LOOPDEV_SIZE, &error); + g_assert_no_error(error); + g_assert_nonnull(loopdev); + g_assert_true(g_file_test(g_file_get_path(loopdev->file), G_FILE_TEST_IS_REGULAR)); + + g_assert_true(pu_loopdev_attach(loopdev, &error)); + g_assert_no_error(error); + + g_assert_true(pu_loopdev_detach(loopdev, &error)); + g_assert_no_error(error); + + pu_loopdev_free(loopdev); +} + +int +main(int argc, + char *argv[]) +{ + /* Skip tests when not run as root */ + if (getuid() != 0) + return 77; + + g_test_init(&argc, &argv, NULL); + +#ifdef PARTUP_TEST_SRCDIR + g_chdir(PARTUP_TEST_SRCDIR); +#endif + + g_test_add_func("/loopdev/loopdev", test_loopdev); + + return g_test_run(); +} diff --git a/tests/meson.build b/tests/meson.build index 06cf46dc..93310543 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -8,6 +8,7 @@ tests = [ ] tests_root = [ + 'loopdev-root', 'mount-root', 'package-root', 'utils-root' From 8fd03eaa27dc4093507d2ec05c773d5d13b6f4e5 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 25 Jul 2023 15:43:26 +0200 Subject: [PATCH 05/19] src: Make input files independent from emmc Separate input files from flash specific information as they only contain general information like filename, checksums and size on target. Signed-off-by: Leonard Anderweit --- meson.build | 1 + src/pu-emmc.c | 23 ++++------- src/pu-input.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ src/pu-input.h | 35 +++++++++++++++++ src/pu-log.c | 2 +- 5 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 src/pu-input.c create mode 100644 src/pu-input.h diff --git a/meson.build b/meson.build index becf1554..22d20e89 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ src = [ 'src/pu-flash.c', 'src/pu-glib-compat.c', 'src/pu-hashtable.c', + 'src/pu-input.c', 'src/pu-log.c', 'src/pu-loopdev.c', 'src/pu-mount.c', diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 1384b5a9..e46ebbce 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -13,18 +13,11 @@ #include "pu-mount.h" #include "pu-utils.h" #include "pu-emmc.h" +#include "pu-input.h" #define PARTITION_TABLE_SIZE_MSDOS 1 #define PARTITION_TABLE_SIZE_GPT 34 -typedef struct _PuEmmcInput { - gchar *filename; - gchar *md5sum; - gchar *sha256sum; - - /* Internal members */ - gsize _size; -} PuEmmcInput; typedef struct _PuEmmcPartition { gchar *label; gchar *partuuid; @@ -41,7 +34,7 @@ typedef struct _PuEmmcPartition { typedef struct _PuEmmcBinary { PedSector input_offset; PedSector output_offset; - PuEmmcInput *input; + PuInput *input; } PuEmmcBinary; typedef struct _PuEmmcBootPartitions { guint enable; @@ -307,7 +300,7 @@ pu_emmc_write_data(PuFlash *flash, return FALSE; for (GList *i = part->input; i != NULL; i = i->next) { - PuEmmcInput *input = i->data; + PuInput *input = i->data; g_autofree gchar *path = NULL; path = pu_path_from_filename(input->filename, prefix, error); @@ -374,7 +367,7 @@ pu_emmc_write_data(PuFlash *flash, for (GList *b = self->raw; b != NULL; b = b->next) { PuEmmcBinary *bin = b->data; - PuEmmcInput *input = bin->input; + PuInput *input = bin->input; g_autofree gchar *path = NULL; path = pu_path_from_filename(input->filename, prefix, error); @@ -486,7 +479,7 @@ pu_emmc_class_finalize(GObject *object) g_free(part->mkfs_extra_args); g_list_free(g_steal_pointer(&part->flags)); for (GList *i = part->input; i != NULL; i = i->next) { - PuEmmcInput *in = i->data; + PuInput *in = i->data; g_free(in->filename); g_free(in->md5sum); g_free(in->sha256sum); @@ -629,7 +622,7 @@ pu_emmc_parse_mmc_controls(PuEmmc *emmc, return FALSE; } - PuEmmcInput *input = g_new0(PuEmmcInput, 1); + PuInput *input = g_new0(PuInput, 1); input->filename = pu_hash_table_lookup_string(value_input->data.mapping, "filename", ""); input->md5sum = pu_hash_table_lookup_string(value_input->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(value_input->data.mapping, "sha256sum", ""); @@ -735,7 +728,7 @@ pu_emmc_parse_raw(PuEmmc *emmc, "'input' of binary does not contain a mapping"); return FALSE; } - PuEmmcInput *input = g_new0(PuEmmcInput, 1); + PuInput *input = g_new0(PuInput, 1); input->filename = pu_hash_table_lookup_string(value_input->data.mapping, "filename", ""); input->md5sum = pu_hash_table_lookup_string(value_input->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(value_input->data.mapping, "sha256sum", ""); @@ -881,7 +874,7 @@ pu_emmc_parse_partitions(PuEmmc *emmc, return FALSE; } - PuEmmcInput *input = g_new0(PuEmmcInput, 1); + PuInput *input = g_new0(PuInput, 1); input->filename = pu_hash_table_lookup_string(iv->data.mapping, "filename", ""); input->md5sum = pu_hash_table_lookup_string(iv->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(iv->data.mapping, "sha256sum", ""); diff --git a/src/pu-input.c b/src/pu-input.c new file mode 100644 index 00000000..bd5cc6a3 --- /dev/null +++ b/src/pu-input.c @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#define G_LOG_DOMAIN "partup-input" + +#include "pu-checksum.h" +#include "pu-glib-compat.h" +#include "pu-input.h" +#include "pu-utils.h" + +G_DEFINE_QUARK(pu-input-context-error-quark, pu_input_error) + +gboolean +pu_input_validate_file(PuInput *input, + GError **error) +{ + gboolean validated = FALSE; + gchar *path; + + g_return_val_if_fail(input != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + path = input->filename; + + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) { + g_set_error(error, PU_INPUT_ERROR, PU_INPUT_ERROR_FILE_NOT_FOUND, + "Input file '%s' does not exist", path); + return FALSE; + } + + if (!g_str_equal(input->md5sum, "")) { + g_debug("Checking MD5 sum of input file '%s'", path); + if (!pu_checksum_verify_file(path, input->md5sum, G_CHECKSUM_MD5, error)) + return FALSE; + validated = TRUE; + } + + if (!g_str_equal(input->sha256sum, "")) { + g_debug("Checking SHA256 sum of input file '%s'", path); + if (!pu_checksum_verify_file(path, input->sha256sum, G_CHECKSUM_SHA256, error)) + return FALSE; + validated = TRUE; + } + + if (!validated) { + g_set_error(error, PU_INPUT_ERROR, PU_INPUT_ERROR_NO_CHECKSUM, + "No checksum provided for '%s'", path); + return FALSE; + } + + return TRUE; +} + +gboolean +pu_input_prefix_filename(PuInput *input, + const gchar *prefix, + GError **error) +{ + g_autofree gchar *path = NULL; + + g_return_val_if_fail(input != NULL, FALSE); + g_return_val_if_fail(prefix != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + path = pu_path_from_filename(input->filename, prefix, error); + if (path == NULL) { + g_prefix_error(error, "Failed parsing input filename: "); + return FALSE; + } + + g_free(input->filename); + input->filename = g_steal_pointer(&path); + + return TRUE; +} + +gboolean +pu_input_get_size(PuInput *input, + GError **error) +{ + g_autofree gchar *cmd = NULL; + g_autofree gchar *out = NULL; + + g_return_val_if_fail(input != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (g_regex_match_simple(".tar", input->filename, G_REGEX_CASELESS, 0)) { + cmd = g_strdup_printf("sh -c 'tar -xf %s -O | wc -c'", input->filename); + if (!pu_spawn_command_line_sync_result(cmd, &out, error)) + return FALSE; + input->_size = (gsize) g_ascii_strtoull(out, NULL, 10); + } else { + input->_size = pu_get_file_size(input->filename, error); + if (!input->_size) + return FALSE; + } + + return TRUE; +} diff --git a/src/pu-input.h b/src/pu-input.h new file mode 100644 index 00000000..facf77f2 --- /dev/null +++ b/src/pu-input.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#ifndef PARTUP_INPUT_H +#define PARTUP_INPUT_H + +#define PU_INPUT_ERROR (pu_input_error_quark()) + +GQuark pu_input_error_quark(void); + +typedef enum { + PU_INPUT_ERROR_NO_CHECKSUM, + PU_INPUT_ERROR_FILE_NOT_FOUND +} PuInputError; + +typedef struct { + gchar *filename; + gchar *md5sum; + gchar *sha256sum; + + /* Internal members */ + gsize _size; +} PuInput; + +gboolean pu_input_validate_file(PuInput *input, + GError **error); +gboolean pu_input_prefix_filename(PuInput *input, + const gchar *prefix, + GError **error); +gboolean pu_input_get_size(PuInput *input, + GError **error); + +#endif /* PARTUP_INPUT_H */ diff --git a/src/pu-log.c b/src/pu-log.c index 4f3beb94..f6284d70 100644 --- a/src/pu-log.c +++ b/src/pu-log.c @@ -7,7 +7,7 @@ #include #include "pu-log.h" -#define PU_LOG_DOMAINS "partup partup-config partup-emmc partup-mount partup-package partup-utils" +#define PU_LOG_DOMAINS "partup partup-config partup-emmc partup-mount partup-package partup-utils partup-input" GLogLevelFlags log_output_level = G_LOG_LEVEL_INFO; From b381b1deb47e704d4ac13ed2394aae4667c12515 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 27 Jul 2023 14:06:26 +0200 Subject: [PATCH 06/19] tests: Add unit test for pu-input Signed-off-by: Leonard Anderweit --- tests/input.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 102 insertions(+) create mode 100644 tests/input.c diff --git a/tests/input.c b/tests/input.c new file mode 100644 index 00000000..e28b639e --- /dev/null +++ b/tests/input.c @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright (c) 2023 PHYTEC Messtechnik GmbH + */ + +#include +#include +#include + +#define LOREM_TXT_SHA256SUM "25623b53e0984428da972f4c635706d32d01ec92dcd2ab39066082e0b9488c9d" +#define LOREM_TXT_MD5SUM "3bc34a45d26784b5bea8529db533ae84" +#define LOREM_TXT_FILENAME "lorem.txt" +#define LOREM_TAR_FILENAME "lorem.tar" +#define DATA_DIR "data" +#define LOREM_TXT_PATH DATA_DIR "/" LOREM_TXT_FILENAME +#define LOREM_TAR_PATH DATA_DIR "/" LOREM_TAR_FILENAME +#define LOREM_TXT_SIZE 12 + +static void +test_prefix_filename(void) +{ + g_autoptr(GError) error = NULL; + PuInput input; + input.filename = g_strdup(LOREM_TXT_FILENAME); + g_assert_true(pu_input_prefix_filename(&input, DATA_DIR, &error)); + g_assert_no_error(error); + g_assert_cmpstr(input.filename, ==, LOREM_TXT_PATH); +} + +static void +test_validate_file(void) +{ + g_autoptr(GError) error = NULL; + PuInput input; + input.filename = g_strdup(LOREM_TXT_PATH); + input.sha256sum = LOREM_TXT_SHA256SUM; + input.md5sum = LOREM_TXT_MD5SUM; + + g_assert_true(pu_input_validate_file(&input, &error)); + g_assert_no_error(error); +} + +static void +test_validate_file_no_checksum(void) +{ + g_autoptr(GError) error = NULL; + PuInput input; + input.filename = g_strdup(LOREM_TXT_PATH); + input.sha256sum = ""; + input.md5sum = ""; + + g_assert_false(pu_input_validate_file(&input, &error)); + g_assert_error(error, PU_INPUT_ERROR, PU_INPUT_ERROR_NO_CHECKSUM); +} + +static void +test_input_get_size(void) +{ + g_autoptr(GError) error = NULL; + PuInput input; + input.filename = g_strdup(LOREM_TXT_PATH); + input.sha256sum = ""; + input.md5sum = ""; + + g_assert_true(pu_input_get_size(&input, &error)); + g_assert_no_error(error); + g_assert_cmpuint(input._size, ==, LOREM_TXT_SIZE); +} + +static void +test_input_get_size_tar(void) +{ + g_autoptr(GError) error = NULL; + PuInput input; + input.filename = g_strdup(LOREM_TAR_PATH); + input.sha256sum = ""; + input.md5sum = ""; + + g_assert_true(pu_input_get_size(&input, &error)); + g_assert_no_error(error); + g_assert_cmpuint(input._size, ==, LOREM_TXT_SIZE); +} + +int +main(int argc, + char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + +#ifdef PARTUP_TEST_SRCDIR + g_chdir(PARTUP_TEST_SRCDIR); +#endif + + g_test_add_func("/input/prefix_filename", test_prefix_filename); + g_test_add_func("/input/validate_file", test_validate_file); + g_test_add_func("/input/validate_file_no_checksum", test_validate_file_no_checksum); + g_test_add_func("/input/input_get_size", test_input_get_size); + g_test_add_func("/input/input_get_size_tar", test_input_get_size_tar); + + return g_test_run(); +} diff --git a/tests/meson.build b/tests/meson.build index 93310543..7923d7cb 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -3,6 +3,7 @@ tests = [ 'command', 'config', 'emmc', + 'input', 'package', 'utils' ] From f202e49771a0d98279584d0da1cdc428915ef5a9 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 1 Aug 2023 12:02:18 +0200 Subject: [PATCH 07/19] src: emmc: Get absolute path and file size for all input files centrally Add a list for all input files regardless of their destination. Use it to get the absolute path and size of all input files at once after config parsing instead of during writing. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 58 +++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index e46ebbce..9d89fe8f 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -60,6 +60,7 @@ struct _PuEmmc { PedDisk *disk; PedDiskType *disktype; + GList *input_files; GList *partitions; GList *clean; GList *raw; @@ -248,13 +249,12 @@ pu_emmc_write_data(PuFlash *flash, gboolean skip_checksums = FALSE; g_autofree gchar *part_path = NULL; g_autofree gchar *part_mount = NULL; - g_autofree gchar *prefix = NULL; + gchar *path; g_return_val_if_fail(flash != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); g_object_get(flash, - "prefix", &prefix, "skip-checksums", &skip_checksums, NULL); @@ -301,13 +301,7 @@ pu_emmc_write_data(PuFlash *flash, for (GList *i = part->input; i != NULL; i = i->next) { PuInput *input = i->data; - g_autofree gchar *path = NULL; - - path = pu_path_from_filename(input->filename, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input filename for partition: "); - return FALSE; - } + path = input->filename; if (!g_str_equal(input->md5sum, "") && !skip_checksums) { g_debug("Checking MD5 sum of input file '%s'", path); @@ -368,13 +362,7 @@ pu_emmc_write_data(PuFlash *flash, for (GList *b = self->raw; b != NULL; b = b->next) { PuEmmcBinary *bin = b->data; PuInput *input = bin->input; - g_autofree gchar *path = NULL; - - path = pu_path_from_filename(input->filename, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input filename for binary: "); - return FALSE; - } + path = input->filename; if (g_str_equal(path, "")) { g_warning("No input specified for binary"); @@ -415,13 +403,7 @@ pu_emmc_write_data(PuFlash *flash, for (GList *i = input; i != NULL; i = i->next) { PuEmmcBinary *bin = i->data; - g_autofree gchar *path = NULL; - - path = pu_path_from_filename(bin->input->filename, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input filename for eMMC boot partition: "); - return FALSE; - } + path = bin->input->filename; if (g_str_equal(path, "")) { g_set_error(error, PU_ERROR, PU_ERROR_FLASH_DATA, @@ -518,6 +500,8 @@ pu_emmc_class_finalize(GObject *object) g_free(emmc->mmc_controls); } + g_list_free(g_steal_pointer(&emmc->input_files)); + if (emmc->disk) ped_disk_destroy(emmc->disk); if (emmc->device) @@ -687,17 +671,11 @@ pu_emmc_parse_raw(PuEmmc *emmc, { PuConfigValue *value_raw = g_hash_table_lookup(root, "raw"); GList *raw; - g_autofree gchar *path = NULL; - g_autofree gchar *prefix = NULL; g_return_val_if_fail(emmc != NULL, FALSE); g_return_val_if_fail(root != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - g_object_get(emmc, - "prefix", &prefix, - NULL); - if (!value_raw) { g_debug("No entry 'raw' found. Skipping..."); return TRUE; @@ -733,14 +711,6 @@ pu_emmc_parse_raw(PuEmmc *emmc, input->md5sum = pu_hash_table_lookup_string(value_input->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(value_input->data.mapping, "sha256sum", ""); - path = pu_path_from_filename(input->filename, prefix, error); - if (path == NULL) - return FALSE; - - input->_size = pu_get_file_size(path, error); - if (!input->_size) - return FALSE; - bin->input = input; g_debug("Parsed raw input: filename=%s md5sum=%s sha256sum=%s", input->filename, input->md5sum, input->sha256sum); @@ -755,6 +725,7 @@ pu_emmc_parse_raw(PuEmmc *emmc, } emmc->raw = g_list_prepend(emmc->raw, bin); + emmc->input_files = g_list_prepend(emmc->input_files, input); } emmc->raw = g_list_reverse(emmc->raw); @@ -879,6 +850,7 @@ pu_emmc_parse_partitions(PuEmmc *emmc, input->md5sum = pu_hash_table_lookup_string(iv->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(iv->data.mapping, "sha256sum", ""); part->input = g_list_prepend(part->input, input); + emmc->input_files = g_list_prepend(emmc->input_files, input); g_debug("Parsed partition input: filename=%s md5sum=%s sha256sum=%s", input->filename, input->md5sum, input->sha256sum); @@ -1021,6 +993,18 @@ pu_emmc_new(const gchar *device_path, if (!pu_emmc_parse_clean(self, root, error)) return NULL; + for (GList *i = self->input_files; i != NULL; i = i->next) { + PuInput *input = i->data; + if (!pu_input_prefix_filename(input, prefix, error)) + return NULL; + } + + for (GList *i = self->input_files; i != NULL; i = i->next) { + PuInput *input = i->data; + if (!pu_input_get_size(input, error)) + return FALSE; + } + if (!pu_emmc_check_raw_overwrite(self, error)) return NULL; From 4dbcb76da7494bd8a702210a0b1279b1ebb13b6d Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 25 Jul 2023 16:23:20 +0200 Subject: [PATCH 08/19] src: Add config validation function Add a new function for config validation. For now only verifies checksums. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 26 ++++++++++++++++++++++++++ src/pu-flash.c | 18 ++++++++++++++++++ src/pu-flash.h | 24 ++++++++++++++++++++---- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 9d89fe8f..078c6265 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -163,6 +163,31 @@ emmc_create_partition(PuEmmc *self, return TRUE; } +static gboolean +pu_emmc_validate_config(PuFlash *flash, + GError **error) +{ + PuEmmc *self = PU_EMMC(flash); + gboolean skip_checksums = FALSE; + + g_return_val_if_fail(flash != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + g_object_get(flash, + "skip-checksums", &skip_checksums, + NULL); + + if (!skip_checksums) { + for (GList *i = self->input_files; i != NULL; i = i->next) { + PuInput *input = i->data; + if (!pu_input_validate_file(input, error)) + return FALSE; + } + } + + return TRUE; +} + static gboolean pu_emmc_init_device(PuFlash *flash, GError **error) @@ -516,6 +541,7 @@ pu_emmc_class_init(PuEmmcClass *class) PuFlashClass *flash_class = PU_FLASH_CLASS(class); GObjectClass *object_class = G_OBJECT_CLASS(class); + flash_class->validate_config = pu_emmc_validate_config; flash_class->init_device = pu_emmc_init_device; flash_class->setup_layout = pu_emmc_setup_layout; flash_class->write_data = pu_emmc_write_data; diff --git a/src/pu-flash.c b/src/pu-flash.c index 1eefff2d..6d54a65e 100644 --- a/src/pu-flash.c +++ b/src/pu-flash.c @@ -27,6 +27,16 @@ static GParamSpec *props[NUM_PROPS] = { NULL }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PuFlash, pu_flash, G_TYPE_OBJECT) +static gboolean +pu_flash_default_validate_config(PuFlash *self, + G_GNUC_UNUSED GError **error) +{ + g_critical("Flash of type '%s' does not implement PuFlash::validate_config", + G_OBJECT_TYPE_NAME(self)); + + return FALSE; +} + static gboolean pu_flash_default_init_device(PuFlash *self, G_GNUC_UNUSED GError **error) @@ -120,6 +130,7 @@ pu_flash_class_init(PuFlashClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); + class->validate_config = pu_flash_default_validate_config; class->init_device = pu_flash_default_init_device; class->setup_layout = pu_flash_default_setup_layout; class->write_data = pu_flash_default_write_data; @@ -159,6 +170,13 @@ pu_flash_init(G_GNUC_UNUSED PuFlash *self) { } +gboolean +pu_flash_validate_config(PuFlash *self, + GError **error) +{ + return PU_FLASH_GET_CLASS(self)->validate_config(self, error); +} + gboolean pu_flash_init_device(PuFlash *self, GError **error) diff --git a/src/pu-flash.h b/src/pu-flash.h index 8d688c1d..ae843610 100644 --- a/src/pu-flash.h +++ b/src/pu-flash.h @@ -21,16 +21,18 @@ * PuFlash is the base object used for specific implementations of flash * devices. An example can be found for eMMC flash with PuEmmc. * - * Implementations of PuFlash use three different functions representing the - * different stages of initializing, formatting and writing a flash device. The - * implementation of these functions are flash device specific and may vary - * depending on its type. + * Implementations of PuFlash use four different functions representing the + * different stages of validating, initializing, formatting and writing a flash + * device. The implementation of these functions are flash device specific and + * may vary depending on its type. */ G_DECLARE_DERIVABLE_TYPE(PuFlash, pu_flash, PU, FLASH, GObject) struct _PuFlashClass { GObjectClass parent_class; + gboolean (*validate_config)(PuFlash *self, + GError **error); gboolean (*init_device)(PuFlash *self, GError **error); gboolean (*setup_layout)(PuFlash *self, @@ -41,6 +43,20 @@ struct _PuFlashClass { gpointer padding[8]; }; +/** + * Validate the config for the flash device. + * + * Validate the config, e.g. check if input files exist and the checksum is + * correct. + * + * @param self the PuFlash instance. + * @param error a GError used for error handling. + * + * @return TRUE on success or FALSE if an error occurred. + */ +gboolean pu_flash_validate_config(PuFlash *self, + GError **error); + /** * Initialize the flash device. * From c2b6c9f07cc64c2fab4916f5757c11da199a7a7c Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 25 Jul 2023 16:41:59 +0200 Subject: [PATCH 09/19] src: Remove checksum verification from install command Checksum verification is done with the validate command. So remove it from the install command to save time when writing to a device. Update the docs accordingly. Signed-off-by: Leonard Anderweit --- doc/usage.rst | 2 -- src/pu-emmc.c | 55 --------------------------------------------------- src/pu-main.c | 5 +---- 3 files changed, 1 insertion(+), 61 deletions(-) diff --git a/doc/usage.rst b/doc/usage.rst index a53cbe6e..69dfcc69 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -37,8 +37,6 @@ Commands install [OPTION…] *PACKAGE* *DEVICE* Install a partup PACKAGE to DEVICE - -s, --skip-checksums Skip checksum verification for all input files - package [OPTION…] *PACKAGE* *FILES…* Create a partup PACKAGE with the contents FILES diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 078c6265..89a50311 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -271,7 +271,6 @@ pu_emmc_write_data(PuFlash *flash, PuEmmc *self = PU_EMMC(flash); guint i = 0; gboolean first_logical_part = FALSE; - gboolean skip_checksums = FALSE; g_autofree gchar *part_path = NULL; g_autofree gchar *part_mount = NULL; gchar *path; @@ -279,10 +278,6 @@ pu_emmc_write_data(PuFlash *flash, g_return_val_if_fail(flash != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - g_object_get(flash, - "skip-checksums", &skip_checksums, - NULL); - g_message("Writing data to MMC"); for (GList *p = self->partitions; p != NULL; p = p->next) { @@ -328,19 +323,6 @@ pu_emmc_write_data(PuFlash *flash, PuInput *input = i->data; path = input->filename; - if (!g_str_equal(input->md5sum, "") && !skip_checksums) { - g_debug("Checking MD5 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, input->md5sum, - G_CHECKSUM_MD5, error)) - return FALSE; - } - if (!g_str_equal(input->sha256sum, "") && !skip_checksums) { - g_debug("Checking SHA256 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, input->sha256sum, - G_CHECKSUM_SHA256, error)) - return FALSE; - } - if (g_regex_match_simple(".tar", path, G_REGEX_CASELESS, 0)) { if (!pu_mount(part_path, part_mount, NULL, NULL, error)) return FALSE; @@ -389,24 +371,6 @@ pu_emmc_write_data(PuFlash *flash, PuInput *input = bin->input; path = input->filename; - if (g_str_equal(path, "")) { - g_warning("No input specified for binary"); - continue; - } - - if (!g_str_equal(input->md5sum, "") && !skip_checksums) { - g_debug("Checking MD5 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, input->md5sum, - G_CHECKSUM_MD5, error)) - return FALSE; - } - if (!g_str_equal(input->sha256sum, "") && !skip_checksums) { - g_debug("Checking SHA256 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, input->sha256sum, - G_CHECKSUM_SHA256, error)) - return FALSE; - } - g_debug("Writing raw data: filename=%s input_offset=%lld output_offset=%lld", input->filename, bin->input_offset, bin->output_offset); @@ -430,25 +394,6 @@ pu_emmc_write_data(PuFlash *flash, PuEmmcBinary *bin = i->data; path = bin->input->filename; - if (g_str_equal(path, "")) { - g_set_error(error, PU_ERROR, PU_ERROR_FLASH_DATA, - "No input specified for eMMC boot partition"); - return FALSE; - } - - if (!g_str_equal(bin->input->md5sum, "") && !skip_checksums) { - g_debug("Checking MD5 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, bin->input->md5sum, - G_CHECKSUM_MD5, error)) - return FALSE; - } - if (!g_str_equal(bin->input->sha256sum, "") && !skip_checksums) { - g_debug("Checking SHA256 sum of input file '%s'", path); - if (!pu_checksum_verify_file(path, bin->input->sha256sum, - G_CHECKSUM_SHA256, error)) - return FALSE; - } - g_debug("Writing eMMC boot partitions: filename=%s input_offset=%lld output_offset=%lld", bin->input->filename, bin->input_offset, bin->output_offset); diff --git a/src/pu-main.c b/src/pu-main.c index b63dd778..a546c7f7 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -24,7 +24,6 @@ static gboolean arg_debug = FALSE; static gchar *arg_debug_domains = NULL; static gboolean arg_quiet = FALSE; -static gboolean arg_install_skip_checksums = FALSE; static gchar *arg_package_directory = NULL; static gboolean arg_package_force = FALSE; static gboolean arg_show_size = FALSE; @@ -101,7 +100,7 @@ cmd_install(PuCommandContext *context, return error_out(mount_path); emmc = pu_emmc_new(device_path, config, mount_path, - arg_install_skip_checksums, error); + TRUE, error); if (emmc == NULL) { g_prefix_error(error, "Failed parsing eMMC info from config: "); return error_out(mount_path); @@ -183,8 +182,6 @@ static GOptionEntry option_entries_main[] = { }; static GOptionEntry option_entries_install[] = { - { "skip-checksums", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, - &arg_install_skip_checksums, "Skip checksum verification for all input files", NULL }, { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY, &arg_remaining, NULL, "install PACKAGE DEVICE" }, { NULL } From c1dc6b8defeec57d30ea465986d0fd88d79b7a7b Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 25 Jul 2023 17:03:59 +0200 Subject: [PATCH 10/19] Check file size and raw overlap only in validate These steps are not necessary for package installation so execute them only when validating a package. Also, update the tests accordingly. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 116 +++++++++++++++++++++++++------------------------- tests/emmc.c | 21 ++++++--- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 89a50311..83839c8d 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -163,6 +163,55 @@ emmc_create_partition(PuEmmc *self, return TRUE; } +static gboolean +pu_emmc_check_raw_overwrite(PuEmmc *emmc, + GError **error) +{ + g_return_val_if_fail(emmc != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (emmc->partitions && emmc->raw) { + PuEmmcPartition *part = emmc->partitions->data; + gsize part_start = part->offset * emmc->device->sector_size; + gsize raw_start; + gsize raw_end; + + for (GList *b = emmc->raw; b != NULL; b = b->next) { + PuEmmcBinary *bin = b->data; + + raw_start = bin->output_offset * emmc->device->sector_size; + raw_end = bin->input->_size; + raw_end += raw_start; + raw_end -= bin->input_offset * emmc->device->sector_size; + + if (raw_end > part_start) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Raw binary overlaps with first partition"); + return FALSE; + } + + /* Check against all binaries after the current one */ + for (GList *i = b->next; i != NULL; i = i->next) { + PuEmmcBinary *bin2 = i->data; + + gsize raw2_start = bin2->output_offset * emmc->device->sector_size; + gsize raw2_end; + raw2_end = bin2->input->_size; + raw2_end += raw2_start; + raw2_end -= bin2->input_offset * emmc->device->sector_size; + + if (!(raw_end < raw2_start || raw_start > raw2_end)) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Raw binary overlaps with other raw binary"); + return FALSE; + } + } + } + } + + return TRUE; +} + static gboolean pu_emmc_validate_config(PuFlash *flash, GError **error) @@ -185,6 +234,15 @@ pu_emmc_validate_config(PuFlash *flash, } } + for (GList *i = self->input_files; i != NULL; i = i->next) { + PuInput *input = i->data; + if (!pu_input_get_size(input, error)) + return FALSE; + } + + if (!pu_emmc_check_raw_overwrite(self, error)) + return FALSE; + return TRUE; } @@ -865,55 +923,6 @@ pu_emmc_parse_partitions(PuEmmc *emmc, return TRUE; } -static gboolean -pu_emmc_check_raw_overwrite(PuEmmc *emmc, - GError **error) -{ - g_return_val_if_fail(emmc != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - if (emmc->partitions && emmc->raw) { - PuEmmcPartition *part = emmc->partitions->data; - gsize part_start = part->offset * emmc->device->sector_size; - gsize raw_start; - gsize raw_end; - - for (GList *b = emmc->raw; b != NULL; b = b->next) { - PuEmmcBinary *bin = b->data; - - raw_start = bin->output_offset * emmc->device->sector_size; - raw_end = bin->input->_size; - raw_end += raw_start; - raw_end -= bin->input_offset * emmc->device->sector_size; - - if (raw_end > part_start) { - g_set_error(error, PU_ERROR, PU_ERROR_FAILED, - "Raw binary overlaps with first partition"); - return FALSE; - } - - /* Check against all binaries after the current one */ - for (GList *i = b->next; i != NULL; i = i->next) { - PuEmmcBinary *bin2 = i->data; - - gsize raw2_start = bin2->output_offset * emmc->device->sector_size; - gsize raw2_end; - raw2_end = bin2->input->_size; - raw2_end += raw2_start; - raw2_end -= bin2->input_offset * emmc->device->sector_size; - - if (!(raw_end < raw2_start || raw_start > raw2_end)) { - g_set_error(error, PU_ERROR, PU_ERROR_FAILED, - "Raw binary overlaps with other raw binary"); - return FALSE; - } - } - } - } - - return TRUE; -} - PuEmmc * pu_emmc_new(const gchar *device_path, PuConfig *config, @@ -970,14 +979,5 @@ pu_emmc_new(const gchar *device_path, return NULL; } - for (GList *i = self->input_files; i != NULL; i = i->next) { - PuInput *input = i->data; - if (!pu_input_get_size(input, error)) - return FALSE; - } - - if (!pu_emmc_check_raw_overwrite(self, error)) - return NULL; - return g_steal_pointer(&self); } diff --git a/tests/emmc.c b/tests/emmc.c index c52a6264..7748491c 100644 --- a/tests/emmc.c +++ b/tests/emmc.c @@ -53,10 +53,13 @@ test_raw_overwrite_pass(EmptyFileFixture *fixture, g_assert_no_error(fixture->error); g_assert_nonnull(config); - emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", FALSE, + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", TRUE, &fixture->error); g_assert_no_error(fixture->error); g_assert_nonnull(emmc); + + g_assert_true(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); + g_assert_no_error(fixture->error); } static void @@ -71,7 +74,7 @@ test_raw_overwrite_fail_partition_table(EmptyFileFixture *fixture, g_assert_no_error(fixture->error); g_assert_nonnull(config); - emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", FALSE, + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", TRUE, &fixture->error); g_assert_error(fixture->error, PU_ERROR, PU_ERROR_EMMC_PARSE); g_assert_null(emmc); @@ -91,10 +94,13 @@ test_raw_overwrite_fail_partition(EmptyFileFixture *fixture, g_assert_no_error(fixture->error); g_assert_nonnull(config); - emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", FALSE, + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", TRUE, &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(emmc); + + g_assert_false(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); g_assert_error(fixture->error, PU_ERROR, PU_ERROR_FAILED); - g_assert_null(emmc); g_clear_error(&fixture->error); } @@ -110,10 +116,13 @@ test_raw_overwrite_fail_raw(EmptyFileFixture *fixture, g_assert_no_error(fixture->error); g_assert_nonnull(config); - emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", FALSE, + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", TRUE, &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(emmc); + + g_assert_false(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); g_assert_error(fixture->error, PU_ERROR, PU_ERROR_FAILED); - g_assert_null(emmc); g_clear_error(&fixture->error); } From d46f7c8b726da2d9c8543cdb9d39d4991b57e17b Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 27 Jul 2023 15:55:37 +0200 Subject: [PATCH 11/19] src: emmc: Check if input files fit into partition Add an additional validation step to check if the input files of each partition fit into the corresponding partition. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 83839c8d..eb3c6556 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -243,6 +243,30 @@ pu_emmc_validate_config(PuFlash *flash, if (!pu_emmc_check_raw_overwrite(self, error)) return FALSE; + // check if input files fit in partition + for (GList *p = self->partitions; p != NULL; p = p->next) { + PuEmmcPartition *part = p->data; + gsize input_size = 0; + gsize part_size = 0; + + if (part->expand) + part_size = self->expanded_part_size * self->device->sector_size; + else + part_size = part->size * self->device->sector_size; + + for (GList *i = part->input; i != NULL; i = i->next) { + PuInput *input = i->data; + input_size += input->_size; + } + g_debug("input size: %ld part size: %ld", input_size, part_size); + + if (input_size > part_size) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Input files are larger than partition"); + return FALSE; + } + } + return TRUE; } From b5d6aa753ce50fe386654b3d10f2cd5fc3698764 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 15 Aug 2023 11:11:27 +0200 Subject: [PATCH 12/19] src: emmc: Check if partitions fit into device Check if the fixed size partitions fit into the device. This does not check expanding partitions. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 6 ++++++ tests/config/raw-non-overlap.yaml | 2 +- tests/config/raw-overlap-partition-table.yaml | 2 +- tests/config/raw-overlap-partition.yaml | 2 +- tests/config/raw-overlap-raw.yaml | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index eb3c6556..d954d795 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -930,6 +930,12 @@ pu_emmc_parse_partitions(PuEmmc *emmc, emmc->partitions = g_list_reverse(emmc->partitions); + if (fixed_parts_size > emmc->device->length) { + g_set_error(error, PU_ERROR, PU_ERROR_EMMC_PARSE, + "Combined partition size is larger than device"); + return FALSE; + } + if (emmc->num_expanded_parts > 0) { emmc->expanded_part_size = emmc->device->length - fixed_parts_size - 2 * emmc->num_logical_parts; diff --git a/tests/config/raw-non-overlap.yaml b/tests/config/raw-non-overlap.yaml index 5289500c..c72814c1 100644 --- a/tests/config/raw-non-overlap.yaml +++ b/tests/config/raw-non-overlap.yaml @@ -14,5 +14,5 @@ raw: partitions: - type: primary filesystem: fat32 - size: 128MiB + size: 64MiB offset: 16MiB diff --git a/tests/config/raw-overlap-partition-table.yaml b/tests/config/raw-overlap-partition-table.yaml index 4a712f2e..4d8fd82f 100644 --- a/tests/config/raw-overlap-partition-table.yaml +++ b/tests/config/raw-overlap-partition-table.yaml @@ -14,5 +14,5 @@ raw: partitions: - type: primary filesystem: fat32 - size: 128MiB + size: 64MiB offset: 16MiB diff --git a/tests/config/raw-overlap-partition.yaml b/tests/config/raw-overlap-partition.yaml index 71352a61..f869f5cb 100644 --- a/tests/config/raw-overlap-partition.yaml +++ b/tests/config/raw-overlap-partition.yaml @@ -14,5 +14,5 @@ raw: partitions: - type: primary filesystem: fat32 - size: 128MiB + size: 64MiB offset: 16MiB diff --git a/tests/config/raw-overlap-raw.yaml b/tests/config/raw-overlap-raw.yaml index c5f013ec..b97ef770 100644 --- a/tests/config/raw-overlap-raw.yaml +++ b/tests/config/raw-overlap-raw.yaml @@ -14,5 +14,5 @@ raw: partitions: - type: primary filesystem: fat32 - size: 128MiB + size: 64MiB offset: 16MiB From 860574db85eccc0dfd657d5e27e70561ebed18e5 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Wed, 30 Aug 2023 10:10:37 +0200 Subject: [PATCH 13/19] tests: emmc: Add unit tests for validation Signed-off-by: Leonard Anderweit --- tests/config/checksums.yaml | 25 +++++++++ tests/config/partition-too-small.yaml | 11 ++++ tests/emmc.c | 76 +++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 tests/config/checksums.yaml create mode 100644 tests/config/partition-too-small.yaml diff --git a/tests/config/checksums.yaml b/tests/config/checksums.yaml new file mode 100644 index 00000000..a7400f3e --- /dev/null +++ b/tests/config/checksums.yaml @@ -0,0 +1,25 @@ +api-version: 0 +disklabel: gpt + +raw: + - input-offset: 0 + output-offset: 33KiB + input: + filename: random.bin + md5sum: 5b41daa7a946650a4e4ee1a24b5f1f78 + +partitions: + - type: primary + filesystem: fat32 + size: 16MiB + offset: 16MiB + input: + - filename: lorem.txt + sha256sum: 25623b53e0984428da972f4c635706d32d01ec92dcd2ab39066082e0b9488c9d + - type: primary + filesystem: NULL + size: 32MiB + input: + - filename: root.ext4 + md5sum: 75866bf838812780c94706ce27ce67f8 + sha256sum: bcd0b814381a47095a18f302a8ec1b76fcdb9161a0af8c2aea56fc8d67d9a489 diff --git a/tests/config/partition-too-small.yaml b/tests/config/partition-too-small.yaml new file mode 100644 index 00000000..bc231ff7 --- /dev/null +++ b/tests/config/partition-too-small.yaml @@ -0,0 +1,11 @@ +api-version: 0 +disklabel: gpt + +partitions: + - type: primary + filesystem: fat32 + size: 10KB + offset: 1MiB + input: + - filename: lorem.txt + - filename: random.bin diff --git a/tests/emmc.c b/tests/emmc.c index 7748491c..18a126bb 100644 --- a/tests/emmc.c +++ b/tests/emmc.c @@ -10,6 +10,7 @@ #include "helper.h" #include "pu-emmc.h" #include "pu-error.h" +#include "pu-input.h" static void emmc_simple(void) @@ -127,6 +128,72 @@ test_raw_overwrite_fail_raw(EmptyFileFixture *fixture, g_clear_error(&fixture->error); } +static void +test_no_checksums(EmptyFileFixture *fixture, + G_GNUC_UNUSED gconstpointer user_data) +{ + g_autoptr(PuConfig) config = NULL; + g_autoptr(PuEmmc) emmc = NULL; + + config = pu_config_new_from_file("config/raw-non-overlap.yaml", &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(config); + + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", false, + &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(emmc); + + g_assert_false(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); + g_assert_error(fixture->error, PU_INPUT_ERROR, PU_INPUT_ERROR_NO_CHECKSUM); + + g_clear_error(&fixture->error); +} + +static void +test_checksums(EmptyFileFixture *fixture, + G_GNUC_UNUSED gconstpointer user_data) +{ + g_autoptr(PuConfig) config = NULL; + g_autoptr(PuEmmc) emmc = NULL; + + config = pu_config_new_from_file("config/checksums.yaml", &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(config); + + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", false, + &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(emmc); + + g_assert_true(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); + g_assert_no_error(fixture->error); + + g_clear_error(&fixture->error); +} + +static void +test_partition_too_small(EmptyFileFixture *fixture, + G_GNUC_UNUSED gconstpointer user_data) +{ + g_autoptr(PuConfig) config = NULL; + g_autoptr(PuEmmc) emmc = NULL; + + config = pu_config_new_from_file("config/partition-too-small.yaml", &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(config); + + emmc = pu_emmc_new(g_file_get_path(fixture->file), config, "data", true, + &fixture->error); + g_assert_no_error(fixture->error); + g_assert_nonnull(emmc); + + g_assert_false(pu_flash_validate_config(PU_FLASH(emmc), &fixture->error)); + g_assert_error(fixture->error, PU_ERROR, PU_ERROR_FAILED); + + g_clear_error(&fixture->error); +} + int main(int argc, char *argv[]) @@ -149,6 +216,15 @@ main(int argc, g_test_add("/emmc/raw_overwrite_fail_raw", EmptyFileFixture, "mmcblk0", empty_file_set_up, test_raw_overwrite_fail_raw, empty_file_tear_down); + g_test_add("/emmc/no_checksums", EmptyFileFixture, "mmcblk0", + empty_file_set_up, test_no_checksums, + empty_file_tear_down); + g_test_add("/emmc/checksums", EmptyFileFixture, "mmcblk0", + empty_file_set_up, test_checksums, + empty_file_tear_down); + g_test_add("/emmc/partition_too_small", EmptyFileFixture, "mmcblk0", + empty_file_set_up, test_partition_too_small, + empty_file_tear_down); return g_test_run(); } From bb8ec175dd2de5ae0c51d26ab70c7f5b883d2c13 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Mon, 14 Aug 2023 14:32:04 +0200 Subject: [PATCH 14/19] src: utils: Add function to parse size strings Add a new function pu_parse_size() which accepts a size string and returns the size in bytes. Accepts strings with the same format as ped_parse_unit, but only with absolute units. Also, add a corresponding unit test. Signed-off-by: Leonard Anderweit --- src/pu-utils.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pu-utils.h | 3 +++ tests/utils.c | 20 ++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/src/pu-utils.c b/src/pu-utils.c index a660e2e0..b5e7d5bf 100644 --- a/src/pu-utils.c +++ b/src/pu-utils.c @@ -556,3 +556,66 @@ pu_str_pre_remove(gchar *string, return string; } + +gboolean +pu_parse_size(const gchar *string, + gsize *size, + GError **error) +{ + gchar *unit; + gsize unit_factor = 0; + + g_return_val_if_fail(g_strcmp0(string, "") > 0, FALSE); + g_return_val_if_fail(size, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + *size = g_ascii_strtoull(string, &unit, 10); + + if (strlen(unit) > 1 && g_ascii_tolower(unit[1]) == 'i') { + switch (g_ascii_tolower(unit[0])) { + case 'k': + unit_factor = PED_KIBIBYTE_SIZE; + break; + case 'm': + unit_factor = PED_MEBIBYTE_SIZE; + break; + case 'g': + unit_factor = PED_GIBIBYTE_SIZE; + break; + case 't': + unit_factor = PED_TEBIBYTE_SIZE; + } + } else if (strlen(unit) > 0) { + switch (g_ascii_tolower(unit[0])) { + case 's': + unit_factor = PED_SECTOR_SIZE_DEFAULT; + break; + case 'b': + unit_factor = 1; + break; + case 'k': + unit_factor = PED_KILOBYTE_SIZE; + break; + case 'm': + unit_factor = PED_MEGABYTE_SIZE; + break; + case 'g': + unit_factor = PED_GIGABYTE_SIZE; + break; + case 't': + unit_factor = PED_TERABYTE_SIZE; + } + } else { + unit_factor = 1; + } + + if (unit_factor == 0) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Size '%s' could not be parsed, unit '%s' is unknown", + string, unit); + return FALSE; + } + + *size *= unit_factor; + return TRUE; +} diff --git a/src/pu-utils.h b/src/pu-utils.h index 67399973..dac7a126 100644 --- a/src/pu-utils.h +++ b/src/pu-utils.h @@ -66,5 +66,8 @@ gchar * pu_device_get_partition_pattern(const gchar *device, GError **error); gchar * pu_str_pre_remove(gchar *string, guint n); +gboolean pu_parse_size(const gchar *string, + gsize *size, + GError **error); #endif /* PARTUP_UTILS_H */ diff --git a/tests/utils.c b/tests/utils.c index 33e87f87..f0272ead 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "helper.h" #include "pu-glib-compat.h" #include "pu-utils.h" @@ -254,6 +255,24 @@ test_spawn_command_line_sync_result(void) g_assert_nonnull(strstr(result, "123")); } +static void +test_parse_size(void) +{ + g_autoptr(GError) error = NULL; + gsize size; + + g_assert_true(pu_parse_size("1M", &size, &error)); + g_assert_no_error(error); + g_assert_cmpuint(size, ==, PED_MEGABYTE_SIZE); + + g_assert_true(pu_parse_size("128KiB", &size, &error)); + g_assert_no_error(error); + g_assert_cmpuint(size, ==, 128 * PED_KIBIBYTE_SIZE); + + g_assert_false(pu_parse_size("1x", &size, &error)); + g_assert_error(error, PU_ERROR, PU_ERROR_FAILED); +} + int main(int argc, char *argv[]) @@ -288,6 +307,7 @@ main(int argc, g_test_add_func("/utils/str_pre_remove", test_str_pre_remove); g_test_add_func("/utils/device_get_partition_pattern", test_device_get_partition_pattern); g_test_add_func("/utils/spawn_command_line_sync_result", test_spawn_command_line_sync_result); + g_test_add_func("/utils/parse_size", test_parse_size); return g_test_run(); } From 4512cfabfcd16be2b9601dd1b00f2f3f7b30e41f Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 28 Sep 2023 10:48:08 +0200 Subject: [PATCH 15/19] src: main: Add validate command The new command 'validate' validates a package either against a provided device or a new loop device. Signed-off-by: Leonard Anderweit --- src/pu-main.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/pu-main.c b/src/pu-main.c index a546c7f7..e50da74e 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include "pu-error.h" #include "pu-flash.h" #include "pu-log.h" +#include "pu-loopdev.h" #include "pu-mount.h" #include "pu-package.h" #include "pu-utils.h" @@ -27,6 +29,9 @@ static gboolean arg_quiet = FALSE; static gchar *arg_package_directory = NULL; static gboolean arg_package_force = FALSE; static gboolean arg_show_size = FALSE; +static gboolean arg_validate_skip_checksums = FALSE; +static gchar *arg_validate_file_size = "1GB"; +static gchar *arg_validate_device = NULL; static gchar **arg_remaining = NULL; static inline gboolean @@ -44,6 +49,14 @@ error_out(const gchar *mountpoint) return FALSE; } +static inline gboolean +error_out_loopdev(PuLoopdev *loopdev, + const gchar *mountpoint) +{ + pu_loopdev_free(loopdev); + return error_out(mountpoint); +} + static gboolean cmd_install(PuCommandContext *context, GError **error) @@ -161,6 +174,82 @@ cmd_show(PuCommandContext *context, return pu_package_show_contents(args[0], arg_show_size, error); } +static gboolean +cmd_validate(PuCommandContext *context, + GError **error) +{ + g_autoptr(PuConfig) config = NULL; + g_autoptr(PuEmmc) emmc = NULL; + g_autofree gchar *mount_path = NULL; + g_autofree gchar *config_path = NULL; + gchar *device; + gchar **args; + gsize device_size; + PuLoopdev *loopdev; + + if (getuid() != 0) + return error_not_root(error); + + args = pu_command_context_get_args(context); + + if (!pu_package_mount(args[0], &mount_path, &config_path, error)) + return FALSE; + + config = pu_config_new_from_file(config_path, error); + if (config == NULL) { + g_prefix_error(error, "Failed creating configuration object for file '%s': ", + config_path); + return error_out(mount_path); + } + if (!pu_config_is_version_compatible(config, PARTUP_VERSION_MAJOR, error)) + return error_out(mount_path); + + if (arg_validate_device) { + device = arg_validate_device; + } else { + /* No device provided, use loopdev instead */ + if (!pu_parse_size(arg_validate_file_size, &device_size, error)) + return error_out(mount_path); + + if (device_size < 128 * PED_MEBIBYTE_SIZE) { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "File size '%s' is too small", arg_validate_file_size); + return error_out(mount_path); + } + + loopdev = pu_loopdev_new(device_size, error); + if (!loopdev) + return error_out(mount_path); + + if (!pu_loopdev_attach(loopdev, error)) { + return error_out_loopdev(loopdev, mount_path); + } + + device = loopdev->device; + } + + emmc = pu_emmc_new(device, config, mount_path, + arg_validate_skip_checksums, error); + if (emmc == NULL) { + g_prefix_error(error, "Failed parsing eMMC info from config: "); + return error_out_loopdev(loopdev, mount_path); + } + + if (!pu_flash_validate_config(PU_FLASH(emmc), error)) { + g_prefix_error(error, "Failed validating package: "); + return error_out_loopdev(loopdev, mount_path); + } + + if (!arg_validate_device) { + if (!pu_loopdev_detach(loopdev, error)) + return error_out_loopdev(loopdev, mount_path); + + pu_loopdev_free(loopdev); + } + + return pu_package_umount(mount_path, error); +} + static gboolean cmd_version(G_GNUC_UNUSED PuCommandContext *context, G_GNUC_UNUSED GError **error) @@ -211,6 +300,19 @@ static GOptionEntry option_entries_version[] = { { NULL } }; +static GOptionEntry option_entries_validate[] = { + { "skip-checksums", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, + &arg_validate_skip_checksums, "Skip checksum verification for all input files", NULL }, + { "size", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, + &arg_validate_file_size, "Size of virtual device. Default is 1G", "SIZE" }, + { "device", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, + &arg_validate_device, "Validate against provided device", + "DEVICE" }, + { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY, + &arg_remaining, NULL, "validate PACKAGE" }, + { NULL } +}; + static PuCommandEntry command_entries[] = { { "install", PU_COMMAND_ARG_FILENAME_ARRAY, cmd_install, "Install a package to a device", option_entries_install }, @@ -218,6 +320,9 @@ static PuCommandEntry command_entries[] = { "Create a package from files", option_entries_package }, { "show", PU_COMMAND_ARG_FILENAME, cmd_show, "List the contents of a package", option_entries_show }, + { "validate", PU_COMMAND_ARG_FILENAME, cmd_validate, + "Validate a package against a virtual or physical device", + option_entries_validate }, { "version", PU_COMMAND_ARG_NONE, cmd_version, "Print the program version", option_entries_version }, PU_COMMAND_ENTRY_NULL From 6a5da15c31f58f74eb546d54dad6d4ab4a00b028 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 28 Sep 2023 10:51:24 +0200 Subject: [PATCH 16/19] src: main: Add option to write package to loop device Writing the package to the loop device is an additional validation step. This is similar to installing the package to a real device. Signed-off-by: Leonard Anderweit --- src/pu-main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/pu-main.c b/src/pu-main.c index e50da74e..aa4e6328 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -30,6 +30,7 @@ static gchar *arg_package_directory = NULL; static gboolean arg_package_force = FALSE; static gboolean arg_show_size = FALSE; static gboolean arg_validate_skip_checksums = FALSE; +static gboolean arg_validate_write_image = FALSE; static gchar *arg_validate_file_size = "1GB"; static gchar *arg_validate_device = NULL; static gchar **arg_remaining = NULL; @@ -241,6 +242,23 @@ cmd_validate(PuCommandContext *context, } if (!arg_validate_device) { + if (arg_validate_write_image) { + /* write data to loop device */ + if (!pu_flash_init_device(PU_FLASH(emmc), error)) { + g_prefix_error(error, "Failed initializing device: "); + return error_out_loopdev(loopdev, mount_path); + } + if (!pu_flash_setup_layout(PU_FLASH(emmc), error)) { + g_prefix_error(error, "Failed setting up layout on device: "); + return error_out_loopdev(loopdev, mount_path); + } + if (!pu_flash_write_data(PU_FLASH(emmc), error)) { + g_prefix_error(error, "Failed writing data to device: "); + pu_umount_all(device, NULL); + return error_out_loopdev(loopdev, mount_path); + } + } + if (!pu_loopdev_detach(loopdev, error)) return error_out_loopdev(loopdev, mount_path); @@ -305,6 +323,10 @@ static GOptionEntry option_entries_validate[] = { &arg_validate_skip_checksums, "Skip checksum verification for all input files", NULL }, { "size", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &arg_validate_file_size, "Size of virtual device. Default is 1G", "SIZE" }, + { "write-image", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, + &arg_validate_write_image, + "Install package to virtual device as an additional validation step", + NULL }, { "device", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &arg_validate_device, "Validate against provided device", "DEVICE" }, From d70fff19da34fe36dce123212afa6b9973ea6e16 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Thu, 28 Sep 2023 10:55:41 +0200 Subject: [PATCH 17/19] src: main: Add option to build flashable image from package The generated image can be written with dd. Signed-off-by: Leonard Anderweit --- src/pu-main.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/pu-main.c b/src/pu-main.c index aa4e6328..f1eded44 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -32,6 +32,7 @@ static gboolean arg_show_size = FALSE; static gboolean arg_validate_skip_checksums = FALSE; static gboolean arg_validate_write_image = FALSE; static gchar *arg_validate_file_size = "1GB"; +static gchar *arg_validate_build_image = NULL; static gchar *arg_validate_device = NULL; static gchar **arg_remaining = NULL; @@ -242,7 +243,7 @@ cmd_validate(PuCommandContext *context, } if (!arg_validate_device) { - if (arg_validate_write_image) { + if (arg_validate_write_image || arg_validate_build_image) { /* write data to loop device */ if (!pu_flash_init_device(PU_FLASH(emmc), error)) { g_prefix_error(error, "Failed initializing device: "); @@ -262,6 +263,15 @@ cmd_validate(PuCommandContext *context, if (!pu_loopdev_detach(loopdev, error)) return error_out_loopdev(loopdev, mount_path); + if (arg_validate_build_image) { + if (g_file_test(arg_validate_build_image, G_FILE_TEST_IS_REGULAR)) + g_remove(arg_validate_build_image); + + if (!pu_file_copy(g_file_get_path(loopdev->file), + arg_validate_build_image, error)) + return error_out_loopdev(loopdev, mount_path); + } + pu_loopdev_free(loopdev); } @@ -327,6 +337,9 @@ static GOptionEntry option_entries_validate[] = { &arg_validate_write_image, "Install package to virtual device as an additional validation step", NULL }, + { "build-image", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, + &arg_validate_build_image, "Build a bootable image from the package. Implies --write-image", + "FILENAME" }, { "device", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &arg_validate_device, "Validate against provided device", "DEVICE" }, From e2ca74e50c80b168bb9e8a18802aad5ef1f5150d Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Mon, 14 Aug 2023 16:42:47 +0200 Subject: [PATCH 18/19] doc: usage: Add validate command Signed-off-by: Leonard Anderweit --- doc/usage.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/usage.rst b/doc/usage.rst index 69dfcc69..fb78ff5f 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -48,6 +48,17 @@ show [OPTION…] *PACKAGE* -s, --size Print the size of each file +validate [OPTION…] *PACKAGE* + Validate a package against a virtual or physical device + + -s, --skip-checksums Skip checksum verification for all input files + --size=SIZE Size of virtual device. Default is 1GB + -w, --write-image Install package to virtual device as an additional validation step + -b, --build-image=FILENAME Build a bootable image from the package. Implies --write-image + --device=DEVICE Validate against provided device + + Available since: :ref:`release-2.0.0` + version Print the program version From b056f7ff1d81befb823e4570bf9dfa7de07eedc6 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Wed, 2 Aug 2023 10:10:54 +0200 Subject: [PATCH 19/19] github: workflows: system-tests: Add package validation step Signed-off-by: Leonard Anderweit --- .github/workflows/system-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/system-tests.yml b/.github/workflows/system-tests.yml index 27779d12..650e368c 100644 --- a/.github/workflows/system-tests.yml +++ b/.github/workflows/system-tests.yml @@ -36,6 +36,8 @@ jobs: layout.yaml random.bin lorem.txt lorem.tar root.ext4 echo "Show package content" sudo G_DEBUG=fatal-warnings partup -s show pkg.partup + echo "Validate package" + sudo G_DEBUG=fatal-warnings partup -s --size=128MiB validate pkg.partup echo "Install package to loop device" sudo G_DEBUG=fatal-warnings partup -d install pkg.partup ${{ env.loop_dev }} echo "Show loop device info"