From 81ae69118d153815178fa92caf07761d1c4173c7 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 14:52:41 +0100 Subject: [PATCH 1/6] src: emmc: Move file validation to separate function Add new function validate_file(), which gets the absolute file path, checks for file existence and validates the file with checksums. Use this new function before writing the file to the device. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 106 +++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 62 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 46b91e3d..60be4d37 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -159,6 +159,44 @@ emmc_create_partition(PuEmmc *self, return TRUE; } +static gboolean +validate_file(PuEmmcInput *input, + gchar *prefix, + gboolean skip_checksums, + GError **error) +{ + g_autofree gchar *path = NULL; + + path = pu_path_from_uri(input->uri, prefix, error); + if (path == NULL) { + g_prefix_error(error, "Failed parsing input URI for partition: "); + return FALSE; + } + + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) { + g_set_error(error, PU_ERROR, PU_ERROR_FLASH_DATA, + "Input file at '%s' does not exist", path); + return FALSE; + } + + 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_free(input->uri); + input->uri = g_steal_pointer(&path); + + return TRUE; +} + static gboolean pu_emmc_init_device(PuFlash *flash, GError **error) @@ -297,26 +335,11 @@ pu_emmc_write_data(PuFlash *flash, for (GList *i = part->input; i != NULL; i = i->next) { PuEmmcInput *input = i->data; - g_autofree gchar *path = NULL; - path = pu_path_from_uri(input->uri, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input URI for partition: "); + if (!validate_file(input, prefix, skip_checksums, error)) return FALSE; - } - 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; - } + gchar *path = input->uri; if (g_regex_match_simple(".tar", path, G_REGEX_CASELESS, 0)) { g_debug("Extracting '%s' to '%s'", path, part_mount); @@ -362,31 +385,11 @@ pu_emmc_write_data(PuFlash *flash, for (GList *b = self->raw; b != NULL; b = b->next) { PuEmmcBinary *bin = b->data; PuEmmcInput *input = bin->input; - g_autofree gchar *path = NULL; - path = pu_path_from_uri(input->uri, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input URI for binary: "); + if (!validate_file(input, prefix, skip_checksums, error)) return FALSE; - } - 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; - } + gchar *path = input->uri; if (!pu_write_raw(path, self->device->path, self->device, bin->input_offset, bin->output_offset, 0, error)) @@ -395,32 +398,11 @@ pu_emmc_write_data(PuFlash *flash, if (self->emmc_boot_partitions) { PuEmmcInput *input = self->emmc_boot_partitions->input; - g_autofree gchar *path = NULL; - - path = pu_path_from_uri(input->uri, prefix, error); - if (path == NULL) { - g_prefix_error(error, "Failed parsing input URI for eMMC boot partition: "); - return FALSE; - } - if (g_str_equal(path, "")) { - g_set_error(error, PU_ERROR, PU_ERROR_FLASH_DATA, - "No input specified for eMMC boot partition"); + if (!validate_file(input, prefix, skip_checksums, error)) return FALSE; - } - 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; - } + gchar *path = input->uri; if (!pu_write_raw_bootpart(path, self->device, 0, self->emmc_boot_partitions->input_offset, From 6a5e92173aa306cd87b87c566180056aba8d5c8f Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 15:08:34 +0100 Subject: [PATCH 2/6] src: flash: Add function for config validation Add function pu_flash_validate_config(). Signed-off-by: Leonard Anderweit --- src/pu-flash.c | 18 ++++++++++++++++++ src/pu-flash.h | 24 ++++++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/pu-flash.c b/src/pu-flash.c index 1c1fdb64..a55aa687 100644 --- a/src/pu-flash.c +++ b/src/pu-flash.c @@ -25,6 +25,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) @@ -118,6 +128,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; @@ -157,6 +168,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 21291c25ff6b66c3debc317997809903776d8d2c Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 15:19:10 +0100 Subject: [PATCH 3/6] src: Implement pu_flash_validate config for eMMC Validate the parsed config before initializing the device. The validation currently includes checking for file existence and checksum validation. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 62 +++++++++++++++++++++++++++++++++++---------------- src/pu-main.c | 4 ++++ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 60be4d37..e8a9ec7c 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -197,6 +197,48 @@ validate_file(PuEmmcInput *input, return TRUE; } +static gboolean +pu_emmc_validate_config(PuFlash *flash, + GError **error) +{ + PuEmmc *self = PU_EMMC(flash); + g_autofree gchar *prefix = NULL; + 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, + "prefix", &prefix, + "skip-checksums", &skip_checksums, + NULL); + + for (GList *p = self->partitions; p != NULL; p = p->next) { + PuEmmcPartition *part = p->data; + + for (GList *i = part->input; i != NULL; i = i->next) { + PuEmmcInput *input = i->data; + if (!validate_file(input, prefix, skip_checksums, error)) + return FALSE; + } + } + + for (GList *b = self->raw; b != NULL; b = b->next) { + PuEmmcBinary *bin = b->data; + PuEmmcInput *input = bin->input; + if (!validate_file(input, prefix, skip_checksums, error)) + return FALSE; + } + + if (self->emmc_boot_partitions) { + PuEmmcInput *input = self->emmc_boot_partitions->input; + if (!validate_file(input, prefix, skip_checksums, error)) + return FALSE; + } + + return TRUE; +} + static gboolean pu_emmc_init_device(PuFlash *flash, GError **error) @@ -282,21 +324,14 @@ 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; - g_autofree gchar *prefix = NULL; g_debug(G_STRFUNC); 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); - for (GList *p = self->partitions; p != NULL; p = p->next) { PuEmmcPartition *part = p->data; if (part->type == PED_PARTITION_LOGICAL && first_logical_part == FALSE) { @@ -335,10 +370,6 @@ pu_emmc_write_data(PuFlash *flash, for (GList *i = part->input; i != NULL; i = i->next) { PuEmmcInput *input = i->data; - - if (!validate_file(input, prefix, skip_checksums, error)) - return FALSE; - gchar *path = input->uri; if (g_regex_match_simple(".tar", path, G_REGEX_CASELESS, 0)) { @@ -385,10 +416,6 @@ pu_emmc_write_data(PuFlash *flash, for (GList *b = self->raw; b != NULL; b = b->next) { PuEmmcBinary *bin = b->data; PuEmmcInput *input = bin->input; - - if (!validate_file(input, prefix, skip_checksums, error)) - return FALSE; - gchar *path = input->uri; if (!pu_write_raw(path, self->device->path, self->device, @@ -398,10 +425,6 @@ pu_emmc_write_data(PuFlash *flash, if (self->emmc_boot_partitions) { PuEmmcInput *input = self->emmc_boot_partitions->input; - - if (!validate_file(input, prefix, skip_checksums, error)) - return FALSE; - gchar *path = input->uri; if (!pu_write_raw_bootpart(path, self->device, 0, @@ -482,6 +505,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-main.c b/src/pu-main.c index ab49fbbf..229aaba4 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -106,6 +106,10 @@ cmd_install(PuCommandContext *context, g_prefix_error(error, "Failed parsing eMMC info from config: "); return error_out(mount_path); } + if (!pu_flash_validate_config(PU_FLASH(emmc), error)) { + g_prefix_error(error, "Failed validating config:"); + return 1; + } if (!pu_flash_init_device(PU_FLASH(emmc), error)) { g_prefix_error(error, "Failed initializing device: "); return error_out(mount_path); From 5cfe7e794e8626eaa5bb77dfd7e12ed4b2f8ce68 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 15:50:25 +0100 Subject: [PATCH 4/6] src: flash: Add property for dry run The property for dry run, which indicates that no changes are made on the device. Signed-off-by: Leonard Anderweit --- src/pu-flash.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pu-flash.c b/src/pu-flash.c index a55aa687..2da00642 100644 --- a/src/pu-flash.c +++ b/src/pu-flash.c @@ -11,6 +11,7 @@ typedef struct { PuConfig *config; gchar *prefix; gboolean skip_checksums; + gboolean dry_run; } PuFlashPrivate; enum { @@ -19,6 +20,7 @@ enum { PROP_CONFIG, PROP_PREFIX, PROP_SKIP_CHECKSUMS, + PROP_DRY_RUN, NUM_PROPS }; static GParamSpec *props[NUM_PROPS] = { NULL }; @@ -89,6 +91,9 @@ pu_flash_set_property(GObject *object, case PROP_SKIP_CHECKSUMS: priv->skip_checksums = g_value_get_boolean(value); break; + case PROP_DRY_RUN: + priv->dry_run = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -117,6 +122,9 @@ pu_flash_get_property(GObject *object, case PROP_SKIP_CHECKSUMS: g_value_set_boolean(value, priv->skip_checksums); break; + case PROP_DRY_RUN: + g_value_set_boolean(value, priv->dry_run); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -159,6 +167,12 @@ pu_flash_class_init(PuFlashClass *class) "Modifier to skip checksum verification for all files when writing", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + props[PROP_DRY_RUN] = + g_param_spec_boolean("dry-run", + "Do not write to device", + "Do not write to device, only validate layout configuration and input files", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties(object_class, NUM_PROPS, props); } From 64af255c9e72d60c544c18bd0562f3687ad679de Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 16:01:37 +0100 Subject: [PATCH 5/6] src: emmc.c: Implement dry run Dry run disables writing to the device. This is done by creating the partition table and the partitions only in memory and not writing them to the device. The input files are only validated but not written. Signed-off-by: Leonard Anderweit --- src/pu-emmc.c | 27 +++++++++++++++++++++++++++ src/pu-emmc.h | 1 + src/pu-main.c | 3 ++- tests/emmc.c | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/pu-emmc.c b/src/pu-emmc.c index e8a9ec7c..d8311e3a 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -245,12 +245,17 @@ pu_emmc_init_device(PuFlash *flash, { PuEmmc *self = PU_EMMC(flash); PedDisk *newdisk; + gboolean dry_run = FALSE; g_debug(G_STRFUNC); g_return_val_if_fail(flash != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_object_get(flash, + "dry-run", &dry_run, + NULL); + newdisk = ped_disk_new_fresh(self->device, self->disktype); if (newdisk == NULL) { g_set_error(error, PU_ERROR, PU_ERROR_FLASH_INIT, @@ -262,6 +267,10 @@ pu_emmc_init_device(PuFlash *flash, ped_disk_destroy(self->disk); } self->disk = newdisk; + + if (dry_run) + return TRUE; + ped_disk_commit(self->disk); g_debug("%s: Finished", G_STRFUNC); @@ -275,12 +284,17 @@ pu_emmc_setup_layout(PuFlash *flash, { PuEmmc *self = PU_EMMC(flash); PedSector part_start = 0; + gboolean dry_run = FALSE; g_debug(G_STRFUNC); g_return_val_if_fail(flash != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + g_object_get(flash, + "dry-run", &dry_run, + NULL); + for (GList *p = self->partitions; p != NULL; p = p->next) { PuEmmcPartition *part = p->data; @@ -309,6 +323,9 @@ pu_emmc_setup_layout(PuFlash *flash, part_start += part->size + part->offset; } + if (dry_run) + return TRUE; + ped_disk_commit(self->disk); if (!pu_wait_for_partitions(error)) @@ -324,6 +341,7 @@ pu_emmc_write_data(PuFlash *flash, PuEmmc *self = PU_EMMC(flash); guint i = 0; gboolean first_logical_part = FALSE; + gboolean dry_run = FALSE; g_autofree gchar *part_path = NULL; g_autofree gchar *part_mount = NULL; @@ -332,6 +350,13 @@ 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, + "dry-run", &dry_run, + NULL); + + if (dry_run) + return TRUE; + for (GList *p = self->partitions; p != NULL; p = p->next) { PuEmmcPartition *part = p->data; if (part->type == PED_PARTITION_LOGICAL && first_logical_part == FALSE) { @@ -842,6 +867,7 @@ pu_emmc_new(const gchar *device_path, PuConfig *config, const gchar *prefix, gboolean skip_checksums, + gboolean dry_run, GError **error) { PuEmmc *self; @@ -858,6 +884,7 @@ pu_emmc_new(const gchar *device_path, "config", config, "prefix", prefix, "skip-checksums", skip_checksums, + "dry-run", dry_run, NULL); root = pu_config_get_root(config); diff --git a/src/pu-emmc.h b/src/pu-emmc.h index 76d03e11..19ee7555 100644 --- a/src/pu-emmc.h +++ b/src/pu-emmc.h @@ -17,6 +17,7 @@ PuEmmc * pu_emmc_new(const gchar *device_path, PuConfig *config, const gchar *prefix, gboolean skip_checksums, + gboolean dry_run, GError **error); #endif /* PARTUP_EMMC_H */ diff --git a/src/pu-main.c b/src/pu-main.c index 229aaba4..1ae2c56f 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -19,6 +19,7 @@ #include "pu-version.h" static gboolean arg_debug = FALSE; +static gboolean arg_install_dry_run = FALSE; static gboolean arg_install_skip_checksums = FALSE; static gchar *arg_package_directory = NULL; static gboolean arg_package_force = FALSE; @@ -101,7 +102,7 @@ cmd_install(PuCommandContext *context, } emmc = pu_emmc_new(device_path, config, mount_path, - arg_install_skip_checksums, error); + arg_install_skip_checksums, arg_install_dry_run, error); if (emmc == NULL) { g_prefix_error(error, "Failed parsing eMMC info from config: "); return error_out(mount_path); diff --git a/tests/emmc.c b/tests/emmc.c index 7c147df1..852bdd23 100644 --- a/tests/emmc.c +++ b/tests/emmc.c @@ -31,7 +31,7 @@ emmc_simple(void) mmcblk0p1 = create_tmp_file("mmcblk0p1", path, 32 * PED_MEBIBYTE_SIZE, &error); mmcblk0p2 = create_tmp_file("mmcblk0p2", path, 64 * PED_MEBIBYTE_SIZE, &error); - emmc = pu_emmc_new(g_file_get_path(mmcblk0), config, NULL, FALSE, &error); + emmc = pu_emmc_new(g_file_get_path(mmcblk0), config, NULL, FALSE, FALSE, &error); g_assert_nonnull(emmc); g_assert_true(pu_flash_init_device(PU_FLASH(emmc), &error)); g_assert_true(pu_flash_setup_layout(PU_FLASH(emmc), &error)); From 985355a3520dcdb4f5caf3203e4625a35b7f3140 Mon Sep 17 00:00:00 2001 From: Leonard Anderweit Date: Tue, 7 Mar 2023 16:35:40 +0100 Subject: [PATCH 6/6] src: main: Add CLI option --dry-run Passing this parameter enables dry run mode which disables all changes to the device. Signed-off-by: Leonard Anderweit --- doc/usage.rst | 1 + src/pu-main.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/doc/usage.rst b/doc/usage.rst index 68390977..5b9ea1f5 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -35,6 +35,7 @@ install [OPTION…] *PACKAGE* *DEVICE* Install a partup PACKAGE to DEVICE -s, --skip-checksums Skip checksum verification for all input files + --dry-run Do not write to device package [OPTION…] *PACKAGE* *FILES…* Create a partup PACKAGE with the contents FILES diff --git a/src/pu-main.c b/src/pu-main.c index 1ae2c56f..bd33f4f6 100644 --- a/src/pu-main.c +++ b/src/pu-main.c @@ -185,6 +185,8 @@ 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 }, + { "dry-run", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, + &arg_install_dry_run, "Do not write to device", NULL }, { NULL } };