Add support for matching <firmware> requirements on device parents

For composite devices you might want to restrict the child device in respect
to the parent, for instance requiring the parent to have greater than a specific
firmware version number.

The other useful thing to use this for is checking if the parent has a specific
GUID (of any version) which allows us to match against the common VID&PID
instance IDs. This would allow us to restrict a generic child device update to
a specific OEM vendor parent.

This is specified as <firmware depth="1" ...> to match the parent device and
<firmware depth="2" ...> to match the grandparent device.
This commit is contained in:
Richard Hughes 2019-11-14 13:42:52 +00:00
parent e2b8a2797e
commit f7006d2baa
2 changed files with 110 additions and 11 deletions

View File

@ -1079,11 +1079,31 @@ static gboolean
fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
FuDevice *device, GError **error)
{
guint64 depth;
g_autoptr(FuDevice) device_actual = g_object_ref (device);
g_autoptr(GError) error_local = NULL;
/* look at the parent device */
depth = xb_node_get_attr_as_uint (req, "depth");
if (depth != G_MAXUINT64) {
for (guint64 i = 0; i < depth; i++) {
FuDevice *device_tmp = fu_device_get_parent (device_actual);
if (device_actual == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"No parent device for %s "
"(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")",
fu_device_get_name (device_actual), i, depth);
return FALSE;
}
g_set_object (&device_actual, device_tmp);
}
}
/* old firmware version */
if (xb_node_get_text (req) == NULL) {
const gchar *version = fu_device_get_version (device);
const gchar *version = fu_device_get_version (device_actual);
if (!fu_engine_require_vercmp (req, version, &error_local)) {
if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
g_set_error (error,
@ -1105,7 +1125,7 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
/* bootloader version */
if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) {
const gchar *version = fu_device_get_version_bootloader (device);
const gchar *version = fu_device_get_version_bootloader (device_actual);
if (!fu_engine_require_vercmp (req, version, &error_local)) {
if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
g_set_error (error,
@ -1127,7 +1147,7 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
/* vendor ID */
if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) {
const gchar *version = fu_device_get_vendor_id (device);
const gchar *version = fu_device_get_vendor_id (device_actual);
if (!fu_engine_require_vercmp (req, version, &error_local)) {
if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
g_set_error (error,
@ -1149,29 +1169,44 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
/* child version */
if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0)
return fu_engine_check_requirement_not_child (self, req, device, error);
return fu_engine_check_requirement_not_child (self, req, device_actual, error);
/* another device */
if (fwupd_guid_is_valid (xb_node_get_text (req))) {
const gchar *guid = xb_node_get_text (req);
const gchar *version;
g_autoptr(FuDevice) device2 = NULL;
/* find if the other device exists */
device2 = fu_device_list_get_by_guid (self->device_list, guid, error);
if (device2 == NULL)
return FALSE;
if (depth == G_MAXUINT64) {
g_autoptr(FuDevice) device_tmp = NULL;
device_tmp = fu_device_list_get_by_guid (self->device_list, guid, error);
if (device_tmp == NULL)
return FALSE;
g_set_object (&device_actual, device_tmp);
/* verify the parent device has the GUID */
} else {
if (!fu_device_has_guid (device_actual, guid)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"No GUID of %s on parent device %s",
guid, fu_device_get_name (device_actual));
return FALSE;
}
}
/* get the version of the other device */
version = fu_device_get_version (device2);
version = fu_device_get_version (device_actual);
if (version != NULL &&
xb_node_get_attr (req, "compare") != NULL &&
!fu_engine_require_vercmp (req, version, &error_local)) {
if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Not compatible with %s version %s, requires >= %s",
fu_device_get_name (device2),
fu_device_get_name (device_actual),
version,
xb_node_get_attr (req, "version"));
} else {
@ -1179,7 +1214,7 @@ fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"Not compatible with %s: %s",
fu_device_get_name (device2),
fu_device_get_name (device_actual),
error_local->message);
}
return FALSE;

View File

@ -566,6 +566,69 @@ fu_engine_requirements_other_device_func (void)
g_assert (ret);
}
static void
fu_engine_requirements_parent_device_func (void)
{
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new ();
g_autoptr(FuDevice) device2 = fu_device_new ();
g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE);
g_autoptr(FuInstallTask) task = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new ();
const gchar *xml =
"<component>"
" <requires>"
" <firmware depth=\"1\" compare=\"eq\" version=\"1.2.3\"/>"
" <firmware depth=\"1\">12345678-1234-1234-1234-123456789012</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">1ff60ab2-3905-06a1-b476-0371f00c9e9b</firmware>"
" </provides>"
" <releases>"
" <release version=\"4.5.7\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* no metadata in daemon */
fu_engine_set_silo (engine, silo_empty);
/* set up child device */
fu_device_set_id (device2, "child");
fu_device_set_name (device2, "child");
fu_device_set_version (device2, "4.5.6", FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_add_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_guid (device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b");
/* set up a parent device */
fu_device_set_id (device1, "parent");
fu_device_set_name (device1, "parent");
fu_device_set_version (device1, "1.2.3", FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_add_guid (device1, "12345678-1234-1234-1234-123456789012");
fu_device_add_child (device1, device2);
fu_engine_add_device (engine, device1);
/* import firmware metainfo */
silo = xb_silo_new_from_xml (xml, &error);
g_assert_no_error (error);
g_assert_nonnull (silo);
component = xb_silo_query_first (silo, "component", &error);
g_assert_no_error (error);
g_assert_nonnull (component);
/* check this passes */
task = fu_install_task_new (device2, component);
ret = fu_engine_check_requirements (engine, task,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error (error);
g_assert (ret);
}
static void
fu_engine_device_priority_func (void)
{
@ -4168,6 +4231,7 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/engine{downgrade}", fu_engine_downgrade_func);
g_test_add_func ("/fwupd/engine{requirements-success}", fu_engine_requirements_func);
g_test_add_func ("/fwupd/engine{requirements-missing}", fu_engine_requirements_missing_func);
g_test_add_func ("/fwupd/engine{requirements-parent-device}", fu_engine_requirements_parent_device_func);
g_test_add_func ("/fwupd/engine{requirements-not-child}", fu_engine_requirements_child_func);
g_test_add_func ("/fwupd/engine{requirements-not-child-fail}", fu_engine_requirements_child_fail_func);
g_test_add_func ("/fwupd/engine{requirements-unsupported}", fu_engine_requirements_unsupported_func);