Repository: rauc/rauc (C)

Issue:
Support Image Type Option in Manifest
**Is your feature request related to a problem? Please describe.**

So far, RAUC uses only the image's file name extension (like `.ext4`) to determine the *image type*.
The *image type* however is important for the correct update handler selection in RAUC.

Different tools tend to use different or custom file endings for the images they create.
This led to several requests in the past that aimed to add more matching patterns in https://github.com/rauc/rauc/blob/7f74ea372a3afffb495ff929b7a032bca3f042a2/src/update_handler.c#L2583.

For example, see #1009, #1177, #1201, #1453.

The table is already complex and it's impossible to handle all potential file endings that image generation tools would emit.

**Describe the solution you'd like**

The solution brought up in https://github.com/rauc/rauc/pull/1201#issuecomment-1663668792 would be to add an explicit (new) option to the manifest where one can override the auto-determined file type.
The option could simply be named `type` as this is not yet defined in the context of a manifest image.
It must be optional.

An example configuration would then look as follows:

``` ini
[image.rootfs]
filename=root-file-system.ext4.verity
type=img
```

It should be clarified if all image types currently listed in `updatepairs[]` should be supported or if a filtered/explicit list is preferable.



Fix (patch):
diff --git a/docs/integration.rst b/docs/integration.rst
index e81fe695c..119585ab3 100644
--- a/docs/integration.rst
+++ b/docs/integration.rst
@@ -231,8 +231,8 @@ Additionally, there are specific slot types for :ref:`atomic bootloader updates
 <sec-advanced-updating-bootloader>`: ``boot-emmc``, ``boot-mbr-switch``,
 ``boot-gpt-switch``, ``boot-raw-fallback``.
 
-Depending on this slot storage type and the slot's :ref:`image filename <image-filename>`
-extension, RAUC determines how to extract the image content to the target slot.
+Depending on this slot storage type and the slot's :ref:`image type <image-type>`,
+RAUC determines how to extract the image content to the target slot.
 
 While the generic filename extension ``.img`` is supported for all filesystems,
 it is strongly recommended to use explicit extensions (e.g. ``.vfat`` or ``.ext4``)
diff --git a/docs/reference.rst b/docs/reference.rst
index ee6056eb6..61ed2ae23 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -923,11 +923,30 @@ The following fields are supported for image sections:
 ``filename`` (required)
   Name of the image file (relative to bundle content).
 
+  .. note::
+    While RAUC can automatically detect the image type from filename extensions
+    for backward compatibility, it is recommended to explicitly specify the
+    ``type`` field for new projects or in cases where all target systems
+    are running a new enough version.
+
   .. important::
-    RAUC uses the file name extension and the slot type to decide how to
+    If RAUC uses the file name extension and the slot type to decide how to
     extract the image file content to the slot.
     Make sure to only use :ref:`supported file name extensions
-    <sec-ref-supported-image-types>`!
+    <sec-ref-image-types-to-fileext-map>`!
+
+.. _image-type:
+
+``type`` (optional, recommended)
+  Specifies the type of the image content.
+  This determines how RAUC will process the image during installation.
+  If not specified, the image type is automatically detected
+  based on the filename extension.
+
+  This was added in RAUC v1.15.
+
+  Detailed information about the supported types is described in the
+  :ref:`Supported Image Types <sec-ref-supported-image-types>` section.
 
 ``sha256`` (generated)
   sha256 of image file. RAUC determines this value automatically when creating
@@ -1027,36 +1046,69 @@ In future releases, they will be accessible in other hooks, as well.
 
 .. _sec-ref-supported-image-types:
 
-Supported Image Types (Extensions)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-RAUC uses the file name extension of images in combination with the target
-slot's type to select the *update handler*.
-To keep the number of combinations manageable, only a limited amount of file
-name extensions is supported by RAUC.
-
-The ``*.img`` extension is valid for all slot types.
-It should be used when no specific file name extension (and handler) is
-supported.
-
-Supported file system image types/extensions are:
-
-  * ``*.ext4``: ext[234] file system image
-  * ``*.vfat``: vfat/EFI file system image
-  * ``*.ubifs``: UBIFS file system image
-  * ``*.squashfs``: SquashFS image (compressed or uncompressed)
-  * ``*.squashfs-<comp>``:  SquashFS image (compressed, with ``<comp>`` being one of ``lz4``, ``lzo``, ``xz``, ``xst``)
-
-Supported TAR archive types/extensions are:
-
-  * ``*.tar*``: Compressed or uncompressed TAR archive
-
-For casync support, some specific file name extensions are supported (and used
-for casync-mode detection):
-
-  * ``.caidx``: casync directory tree index
-  * ``<extension>.caibx``: casync blob index (where ``<extension>`` is a valid 'standard' extension)
-  * ``.catar``: casync directory tree archive
+Supported Image Types
+~~~~~~~~~~~~~~~~~~~~~
+.. note::
+  RAUC v1.15 or newer support setting an explicit ``type`` field
+  instead of using the filename extensions to determine the image type.
+  For backward compatibility, RAUC can still derive the image type
+  from file name extensions.
+
+RAUC uses the image ``type`` in combination with the slot ``type``
+to determine how to process images during installation.
+When the ``type`` parameter is set, the filename extension is not taken into
+account and can be chosen freely.
+
+Supported file system image types are:
+
+  * ``ext4``: ext[234] file system image
+  * ``vfat``: vfat file system image (sometimes used for the EFI System Partition)
+  * ``ubifs``: UBIFS file system image
+  * ``squashfs``: SquashFS image (uncompressed, or compressed with one of ``lz4``, ``lzo``, ``xz``, ``xst``)
+
+Supported binary image types are:
+
+  * ``image``: Generic image format (raw binary data)
+
+    .. note::
+      The ``image`` type is valid for all slot types and should be used for raw
+      binary data like bootloader images, firmware binaries, etc.
+
+Supported archive types are:
+
+  * ``tar``: TAR archive (compressed or uncompressed)
+
+For casync support, specific image types are available:
+
+  * ``caidx``: casync directory tree index
+  * ``ext4-caibx``: ext4 file system image in casync blob index format
+  * ``vfat-caibx``: vfat file system image in casync blob index format
+  * ``ubifs-caibx``: UBIFS file system image in casync blob index format
+  * ``squashfs-caibx``: SquashFS image in casync blob index format
+  * ``raw-caibx``: Generic image in casync blob index format
+  * ``catar``: casync directory tree archive
+
+.. _sec-ref-image-types-to-fileext-map:
+
+If no ``type`` is given for the image, the following file name extensions are
+mapped automatically by RAUC:
+
+  * ``*.ext4.caibx`` -> ``ext4-caibx``
+  * ``*.vfat.caibx`` -> ``vfat-caibx``
+  * ``*.ubifs.caibx`` -> ``ubifs-caibx``
+  * ``*.img.caibx`` -> ``raw-caibx``
+  * ``*.squashfs.caibx`` -> ``squashfs-caibx``
+  * ``*.squashfs-*.caibx`` -> ``squashfs-caibx``
+  * ``*.catar`` -> ``catar``
+  * ``*.caidx`` -> ``caidx``
+  * ``*.tar*`` -> ``tar``
+  * ``*.tgz`` -> ``tar``
+  * ``*.ext4`` -> ``ext4``
+  * ``*.vfat`` -> ``vfat``
+  * ``*.img`` -> ``raw``
+  * ``*.squashfs-*`` -> ``squashfs``
+  * ``*.squashfs`` -> ``squashfs``
+  * ``*.ubifs`` -> ``ubifs``
 
 .. _sec_ref_formats:
 
diff --git a/include/manifest.h b/include/manifest.h
index 2f64bc19e..cf05e8bea 100644
--- a/include/manifest.h
+++ b/include/manifest.h
@@ -7,12 +7,13 @@
 #define R_MANIFEST_ERROR r_manifest_error_quark()
 GQuark r_manifest_error_quark(void);
 
-#define R_MANIFEST_ERROR_NO_DATA	0
-#define R_MANIFEST_ERROR_CHECKSUM	1
-#define R_MANIFEST_ERROR_COMPATIBLE	2
-#define R_MANIFEST_PARSE_ERROR		3
-#define R_MANIFEST_EMPTY_STRING		4
-#define R_MANIFEST_CHECK_ERROR		5
+#define R_MANIFEST_ERROR_NO_DATA            0
+#define R_MANIFEST_ERROR_CHECKSUM           1
+#define R_MANIFEST_ERROR_COMPATIBLE         2
+#define R_MANIFEST_PARSE_ERROR              3
+#define R_MANIFEST_EMPTY_STRING             4
+#define R_MANIFEST_CHECK_ERROR              5
+#define R_MANIFEST_ERROR_INVALID_IMAGE_TYPE 6
 
 typedef struct {
 	gboolean install_check;
@@ -30,6 +31,9 @@ typedef struct {
 	gchar* variant;
 	RaucChecksum checksum;
 	gchar* filename;
+	gchar* type;
+	/* flag to remember if image type was auto-detected from file extension */
+	gboolean type_from_fileext;
 	SlotHooks hooks;
 	GStrv adaptive;
 	GStrv convert;
diff --git a/include/update_handler.h b/include/update_handler.h
index 0cf8e0201..0d55fead4 100644
--- a/include/update_handler.h
+++ b/include/update_handler.h
@@ -21,6 +21,31 @@ G_GNUC_WARN_UNUSED_RESULT;
 img_to_slot_handler get_update_handler(RaucImage *mfimage, RaucSlot  *dest_slot, GError **error)
 G_GNUC_WARN_UNUSED_RESULT;
 
+/**
+ * Matches the file name extension of the input filename with a corresponding
+ * image type.
+ *
+ * Uses the internal matching 'ext_type_map'.
+ *
+ * @param filename name of image file to inspect
+ *
+ * @return matching image type, or NULL if none found
+ */
+const gchar* derive_image_type_from_filename_pattern(const gchar *filename)
+G_GNUC_WARN_UNUSED_RESULT;
+
+/**
+ * Checks if the provided name is a valid image type.
+ *
+ * Uses the internal 'image_type_map' to check for supported types.
+ *
+ * @param type name of an image type
+ *
+ * @return TRUE if supported, FALSE otherwise
+ */
+gboolean is_image_type_supported(const gchar *type)
+G_GNUC_WARN_UNUSED_RESULT;
+
 struct boot_switch_partition {
 	guint64 start;          /* address in bytes */
 	guint64 size;           /* size in bytes */
diff --git a/src/main.c b/src/main.c
index 61720757a..f8aec226c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -497,6 +497,14 @@ static gboolean write_slot_start(int argc, char **argv)
 	image->checksum.size = g_file_info_get_size(info);
 	image->filename = g_strdup(argv[3]);
 
+	const gchar *derived_type = derive_image_type_from_filename_pattern(image->filename);
+	if (derived_type == NULL) {
+		g_printerr("Unable to map extension of file '%s' to known image type\n", image->filename);
+		r_exit_status = 1;
+		return TRUE;
+	}
+	image->type = g_strdup(derived_type);
+
 	/* retrieve RaucSlot */
 	slot = g_hash_table_lookup(r_context()->config->slots, argv[2]);
 	if (slot == NULL) {
diff --git a/src/manifest.c b/src/manifest.c
index ed951145f..ce5b025d6 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -4,12 +4,15 @@
 #include "config_file.h"
 #include "context.h"
 #include "manifest.h"
+#include "glib.h"
 #include "signature.h"
+#include "update_handler.h"
 #include "utils.h"
 
 #define RAUC_IMAGE_PREFIX	"image"
 
 #define R_MANIFEST_ERROR r_manifest_error_quark()
+
 GQuark r_manifest_error_quark(void)
 {
 	return g_quark_from_static_string("r_manifest_error_quark");
@@ -97,6 +100,35 @@ static gboolean parse_image(GKeyFile *key_file, const gchar *group, RaucImage **
 		}
 	}
 
+	/* Setting the 'type' option for artifacts is not supported */
+	if (!iimage->artifact) {
+		iimage->type_from_fileext = FALSE;
+		iimage->type = key_file_consume_string(key_file, group, "type", &ierror);
+		if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+			/* If no type is set, derive it from filename extension to support manifests without type */
+			g_clear_error(&ierror);
+			if (!iimage->hooks.install) {
+				const gchar *derived_type = derive_image_type_from_filename_pattern(iimage->filename);
+				if (derived_type == NULL) {
+					g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+							"No 'type=' set and unable to map extension of file '%s' to known image type", iimage->filename);
+					return FALSE;
+				}
+				iimage->type_from_fileext = TRUE;
+				iimage->type = g_strdup(derived_type);
+			}
+		} else if (ierror) {
+			g_propagate_error(error, ierror);
+			return FALSE;
+		}
+
+		if (!iimage->hooks.install && !is_image_type_supported(iimage->type)) {
+			g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_ERROR_INVALID_IMAGE_TYPE,
+					"Unsupported image type '%s'", iimage->type);
+			return FALSE;
+		}
+	}
+
 	g_key_file_remove_key(key_file, group, "version", NULL);
 	g_key_file_remove_key(key_file, group, "description", NULL);
 	g_key_file_remove_key(key_file, group, "build", NULL);
@@ -903,6 +935,9 @@ static GKeyFile *prepare_manifest(const RaucManifest *mf)
 		if (image->filename)
 			g_key_file_set_string(key_file, group, "filename", image->filename);
 
+		if (image->type && !image->type_from_fileext)
+			g_key_file_set_string(key_file, group, "type", image->type);
+
 		if (image->hooks.pre_install == TRUE) {
 			g_ptr_array_add(hooklist, g_strdup("pre-install"));
 		}
@@ -1165,6 +1200,7 @@ void r_free_image(gpointer data)
 	g_free(image->variant);
 	g_free(image->checksum.digest);
 	g_free(image->filename);
+	g_free(image->type);
 	g_strfreev(image->adaptive);
 	g_strfreev(image->convert);
 	g_clear_pointer(&image->converted, g_ptr_array_unref);
diff --git a/src/update_handler.c b/src/update_handler.c
index 4c1d044aa..aea93fb98 100644
--- a/src/update_handler.c
+++ b/src/update_handler.c
@@ -2633,85 +2633,148 @@ static gboolean hook_install_handler(RaucImage *image, RaucSlot *dest_slot, cons
 
 	return TRUE;
 }
-
 typedef struct {
-	const gchar *src;
-	const gchar *dest;
+	const gchar *type;
+	const gchar *slottype;
 	img_to_slot_handler handler;
-} RaucUpdatePair;
-
-RaucUpdatePair updatepairs[] = {
-	{"*.ext4.caibx", "ext4", img_to_fs_handler},
-	{"*.ext4.caibx", "raw", img_to_raw_handler},
-	{"*.vfat.caibx", "raw", img_to_raw_handler},
-	{"*.ubifs.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.ubifs.caibx", "ubifs", img_to_ubifs_handler},
-	//{"*.img.caibx", "nand", img_to_nand_handler}, /* unsupported */
-	{"*.img.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.img.caibx", "ubifs", img_to_ubifs_handler},
-	{"*.squashfs.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-lz4.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-lzo.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-xz.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-zst.caibx", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs.caibx", "raw", img_to_raw_handler},
-	{"*.squashfs-lz4.caibx", "raw", img_to_raw_handler},
-	{"*.squashfs-lzo.caibx", "raw", img_to_raw_handler},
-	{"*.squashfs-xz.caibx", "raw", img_to_raw_handler},
-	{"*.squashfs-zst.caibx", "raw", img_to_raw_handler},
-	{"*.caidx", "ext4", archive_to_ext4_handler},
-	{"*.caidx", "ubifs", archive_to_ubifs_handler},
-	{"*.caidx", "vfat", archive_to_vfat_handler},
-	{"*.ext4", "ext4", img_to_fs_handler},
-	{"*.ext4", "raw", img_to_raw_handler},
-	{"*.vfat", "raw", img_to_raw_handler},
-	{"*.squashfs", "raw", img_to_raw_handler},
-	{"*.squashfs-lz4", "raw", img_to_raw_handler},
-	{"*.squashfs-lzo", "raw", img_to_raw_handler},
-	{"*.squashfs-xz", "raw", img_to_raw_handler},
-	{"*.squashfs-zst", "raw", img_to_raw_handler},
-	{"*.vfat", "vfat", img_to_fs_handler},
-	{"*.tar*", "ext4", archive_to_ext4_handler},
-	{"*.catar", "ext4", archive_to_ext4_handler},
-	{"*.tar*", "ubifs", archive_to_ubifs_handler},
-	{"*.tar*", "jffs2", archive_to_jffs2_handler},
-	{"*.tar*", "vfat", archive_to_vfat_handler},
-	{"*.ubifs", "ubivol", img_to_ubivol_handler},
-	{"*.ubifs", "ubifs", img_to_ubifs_handler},
-	{"*.img", "ext4", img_to_fs_handler},
-	{"*.img", "nor", img_to_nor_handler},
-	{"*.img", "nand", img_to_nand_handler},
-	{"*.img", "ubifs", img_to_ubifs_handler},
-	{"*.img", "ubivol", img_to_ubivol_handler},
-	{"*.img", "vfat", img_to_fs_handler},
-	{"*.squashfs", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-lz4", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-lzo", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-xz", "ubivol", img_to_ubivol_handler},
-	{"*.squashfs-zst", "ubivol", img_to_ubivol_handler},
+} RaucImageTypeMap;
+
+/* Image type to handler mapping */
+static RaucImageTypeMap image_type_map[] = {
+	/* casync - caibx */
+	{"ext4-caibx", "ext4", img_to_fs_handler},
+	{"raw-caibx", "ext4", img_to_raw_handler},
+
+	{"ext4-caibx", "raw", img_to_raw_handler},
+	{"vfat-caibx", "raw", img_to_raw_handler},
+	{"raw-caibx", "raw", img_to_raw_handler},
+	{"raw-caibx", "vfat", img_to_raw_handler},
+	{"raw-caibx", "jffs2", img_to_raw_handler},
+	{"raw-caibx", "nor", img_to_raw_handler},
+	{"raw-caibx", "nand", img_to_raw_handler},
+	{"squashfs-caibx", "raw", img_to_raw_handler},
+
+	{"squashfs-caibx", "ubivol", img_to_ubivol_handler},
+	{"ubifs-caibx", "ubivol", img_to_ubivol_handler},
+	{"raw-caibx", "ubivol", img_to_ubivol_handler},
+	{"ubifs-caibx", "ubifs", img_to_ubifs_handler},
+	{"raw-caibx", "ubifs", img_to_ubifs_handler},
+	/* casync - caidx */
+	{"caidx", "ext4", archive_to_ext4_handler},
+	{"caidx", "ubifs", archive_to_ubifs_handler},
+	{"caidx", "vfat", archive_to_vfat_handler},
+	/* casync - catar */
+	{"catar", "ext4", archive_to_ext4_handler},
+	/* file system */
+	{"ext4", "ext4", img_to_fs_handler},
+	{"ext4", "raw", img_to_raw_handler},
+	{"vfat", "vfat", img_to_fs_handler},
+	{"vfat", "raw", img_to_raw_handler},
+	{"squashfs", "ubivol", img_to_ubivol_handler},
+	{"squashfs", "raw", img_to_raw_handler},
+	{"ubifs", "ubivol", img_to_ubivol_handler},
+	{"ubifs", "ubifs", img_to_ubifs_handler},
+	/* raw */
+	{"raw", "ext4", img_to_fs_handler},
+	{"raw", "nor", img_to_nor_handler},
+	{"raw", "nand", img_to_nand_handler},
+	{"raw", "ubifs", img_to_ubifs_handler},
+	{"raw", "ubivol", img_to_ubivol_handler},
+	{"raw", "vfat", img_to_fs_handler},
+	{"raw", "raw", img_to_raw_handler},
+	{"raw", "jffs2", img_to_raw_handler},
+	/* archive */
+	{"tar", "ext4", archive_to_ext4_handler},
+	{"tar", "ubifs", archive_to_ubifs_handler},
+	{"tar", "vfat", archive_to_vfat_handler},
+	{"tar", "jffs2", archive_to_jffs2_handler},
+	/* boot-* slot types */
 #if ENABLE_EMMC_BOOT_SUPPORT == 1
-	{"*.img", "boot-emmc", img_to_boot_emmc_handler},
+	{"raw", "boot-emmc", img_to_boot_emmc_handler},
 #endif
-	{"*", "boot-emmc", NULL},
-	{"*.vfat", "boot-mbr-switch", img_to_boot_mbr_switch_handler},
-	{"*.img", "boot-mbr-switch", img_to_boot_mbr_switch_handler},
-	{"*", "boot-mbr-switch", NULL},
+	{"vfat", "boot-mbr-switch", img_to_boot_mbr_switch_handler},
+	{"raw", "boot-mbr-switch", img_to_boot_mbr_switch_handler},
 #if ENABLE_GPT == 1
-	{"*.vfat", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
-	{"*.ext4", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
-	{"*.img", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
+	{"vfat", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
+	{"ext4", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
+	{"raw", "boot-gpt-switch", img_to_boot_gpt_switch_handler},
 #endif
-	{"*", "boot-gpt-switch", NULL},
-	{"*.img", "boot-raw-fallback", img_to_boot_raw_fallback_handler},
-	{"*", "boot-raw-fallback", NULL},
-	{"*.img.caibx", "*", img_to_raw_handler}, /* fallback */
-	{"*.img", "*", img_to_raw_handler}, /* fallback */
-	{0}
+	{"raw", "boot-raw-fallback", img_to_boot_raw_fallback_handler},
+	{NULL, NULL, NULL}
 };
 
+typedef struct {
+	const gchar *fileext;
+	const gchar *type;
+} RaucFileExtTypeMap;
+
+/* For compatibility reasons, this mapping is needed to map the former file
+ * extension-based approach to the image types.
+ * It must be ordered with more specific patterns first (e.g.
+ * '*.squashfs-*.caibx' before '*.squashfs-*') to avoid selecting the wrong
+ * image type. */
+static RaucFileExtTypeMap ext_type_map[] = {
+	{"*.ext4.caibx", "ext4-caibx"},
+	{"*.vfat.caibx", "vfat-caibx"},
+	{"*.ubifs.caibx", "ubifs-caibx"},
+	{"*.img.caibx", "raw-caibx"},
+	{"*.squashfs.caibx", "squashfs-caibx"},
+	{"*.squashfs-*.caibx", "squashfs-caibx"},
+	{"*.catar", "catar"},
+	{"*.caidx", "caidx"},
+	{"*.tar*", "tar"},
+	{"*.tgz", "tar"},
+	{"*.ext4", "ext4"},
+	{"*.vfat", "vfat"},
+	{"*.img", "raw"},
+	{"*.squashfs-*", "squashfs"},
+	{"*.squashfs", "squashfs"},
+	{"*.ubifs", "ubifs"},
+};
+
+const gchar* derive_image_type_from_filename_pattern(const gchar *filename)
+{
+	g_return_val_if_fail(filename, NULL);
+
+	for (unsigned long i = 0; i < G_N_ELEMENTS(ext_type_map); i++) {
+		if (g_pattern_match_simple(ext_type_map[i].fileext, filename)) {
+			return ext_type_map[i].type;
+		}
+	}
+
+	return NULL;
+}
+
+static img_to_slot_handler get_handler_from_type(const gchar *image_type, const gchar *slot_type)
+{
+	g_return_val_if_fail(image_type, NULL);
+	g_return_val_if_fail(slot_type, NULL);
+
+	for (RaucImageTypeMap *map = image_type_map; map->type != NULL; map++) {
+		if (g_strcmp0(map->type, image_type) == 0 &&
+		    g_strcmp0(map->slottype, slot_type) == 0) {
+			return map->handler;
+		}
+	}
+
+	return NULL;
+}
+
+gboolean is_image_type_supported(const gchar *type)
+{
+	g_return_val_if_fail(type, FALSE);
+
+	for (RaucImageTypeMap *map = image_type_map; map->type != NULL; map++) {
+		if (g_strcmp0(map->type, type) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 img_to_slot_handler get_update_handler(RaucImage *mfimage, RaucSlot *dest_slot, GError **error)
 {
-	const gchar *src = mfimage->filename;
 	const gchar *dest = dest_slot->type;
 	img_to_slot_handler handler = NULL;
 
@@ -2722,21 +2785,13 @@ img_to_slot_handler get_update_handler(RaucImage *mfimage, RaucSlot *dest_slot,
 
 	g_message("Checking image type for slot type: %s", dest);
 
-	for (RaucUpdatePair *updatepair = updatepairs; updatepair->src != NULL; updatepair++) {
-		if (g_pattern_match_simple(updatepair->src, src) &&
-		    g_pattern_match_simple(updatepair->dest, dest)) {
-			g_message("Image detected as type: %s", updatepair->src);
-			handler = updatepair->handler;
-			break;
-		}
-	}
-
-	if (handler == NULL) {
-		g_set_error(error, R_UPDATE_ERROR, R_UPDATE_ERROR_NO_HANDLER, "Unsupported image %s for slot type %s",
-				mfimage->filename, dest);
-		goto out;
+	handler = get_handler_from_type(mfimage->type, dest);
+	if (!handler) {
+		g_set_error(error, R_UPDATE_ERROR, R_UPDATE_ERROR_NO_HANDLER,
+				"Unsupported image type '%s' for slot type '%s'", mfimage->type, dest);
+		return NULL;
 	}
 
-out:
+	g_message("Found handler for image type '%s' and slot type '%s'", mfimage->type, dest);
 	return handler;
 }