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-emmc.c b/src/pu-emmc.c index 46b91e3d..d8311e3a 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -159,18 +159,103 @@ 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_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) { 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, @@ -182,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); @@ -195,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; @@ -229,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)) @@ -244,10 +341,9 @@ pu_emmc_write_data(PuFlash *flash, PuEmmc *self = PU_EMMC(flash); guint i = 0; gboolean first_logical_part = FALSE; - gboolean skip_checksums = FALSE; + gboolean dry_run = FALSE; g_autofree gchar *part_path = NULL; g_autofree gchar *part_mount = NULL; - g_autofree gchar *prefix = NULL; g_debug(G_STRFUNC); @@ -255,10 +351,12 @@ pu_emmc_write_data(PuFlash *flash, g_return_val_if_fail(error == NULL || *error == NULL, FALSE); g_object_get(flash, - "prefix", &prefix, - "skip-checksums", &skip_checksums, + "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) { @@ -297,26 +395,7 @@ 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: "); - 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 +441,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; - 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: "); - 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 +450,7 @@ 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"); - 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, @@ -500,6 +530,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; @@ -836,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; @@ -852,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-flash.c b/src/pu-flash.c index 1c1fdb64..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,12 +20,23 @@ enum { PROP_CONFIG, PROP_PREFIX, PROP_SKIP_CHECKSUMS, + PROP_DRY_RUN, NUM_PROPS }; 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) @@ -79,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; @@ -107,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; @@ -118,6 +136,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; @@ -148,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); } @@ -157,6 +182,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. * diff --git a/src/pu-main.c b/src/pu-main.c index ab49fbbf..bd33f4f6 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,11 +102,15 @@ 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); } + 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); @@ -180,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 } }; 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));