Add a mechanism to allow QOM types to prevent writable instance properties from being registered. This will be used by types that expose all QOM properties in user-visible interfaces like object-add and device_add, to ensure our external interfaces are not affected by dynamic QOM properties.
Signed-off-by: Eduardo Habkost <ehabk...@redhat.com> --- Cc: Paolo Bonzini <pbonz...@redhat.com> Cc: "Daniel P. Berrangé" <berra...@redhat.com> Cc: Eduardo Habkost <ehabk...@redhat.com> Cc: qemu-devel@nongnu.org --- include/qom/object.h | 17 +++++++++ qom/object.c | 28 ++++++++++++++ tests/test-qdev-global-props.c | 70 ++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/include/qom/object.h b/include/qom/object.h index 1634294e4f..a124cf897d 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -137,6 +137,8 @@ struct ObjectClass ObjectUnparent *unparent; GHashTable *properties; + /* instance properties locked. See object_class_lock_properties() */ + bool properties_locked; }; /** @@ -1867,6 +1869,21 @@ void object_property_set_description(Object *obj, const char *name, void object_class_property_set_description(ObjectClass *klass, const char *name, const char *description); +/** + * object_class_lock_properties: + * @oc: the object class to have properties locked + * + * Prevent all subtypes of @oc from having writeable instance + * properties. If @oc is an interface type, this also affects all + * classes implementing the interface. + * + * This can be used by QOM types that have all QOM properties + * exposed to the external world (e.g. #TYPE_USER_CREATABLE) to + * ensure all user-writable properties are introspectable at the + * class level. + */ +void object_class_lock_properties(ObjectClass *oc); + /** * object_child_foreach: * @obj: the object whose children will be navigated diff --git a/qom/object.c b/qom/object.c index bb32f5d3ad..73f27b8b7e 100644 --- a/qom/object.c +++ b/qom/object.c @@ -498,6 +498,27 @@ static void object_class_property_init_all(Object *obj) } } +void object_class_lock_properties(ObjectClass *oc) +{ + oc->properties_locked = true; +} + +static bool object_class_properties_locked(ObjectClass *oc) +{ + GSList *i = NULL; + + if (oc->properties_locked) { + return true; + } + for (i = oc->interfaces; i; i = i->next) { + ObjectClass *ic = i->data; + if (ic->properties_locked) { + return true; + } + } + return false; +} + static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) { type_initialize(type); @@ -1192,8 +1213,15 @@ object_property_try_add(Object *obj, const char *name, const char *type, void *opaque, Error **errp) { ObjectProperty *prop; + ObjectClass *oc = object_get_class(obj); size_t name_len = strlen(name); + if (set && object_class_properties_locked(oc)) { + error_setg(errp, "writable instance property not allowed for type %s", + object_class_get_name(oc)); + return NULL; + } + if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) { int i; ObjectProperty *ret; diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index c8862cac5f..590c916c4b 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -58,6 +58,9 @@ static void static_prop_class_init(ObjectClass *oc, void *data) dc->realize = NULL; device_class_set_props(dc, static_props); + + /* test_proplist_lock() will check if property locking works */ + object_class_lock_properties(oc); } static const TypeInfo static_prop_type = { @@ -213,6 +216,69 @@ static const TypeInfo nondevice_type = { .parent = TYPE_OBJECT, }; +static void locked_interface_class_base_init(ObjectClass *klass, void *data) +{ + object_class_lock_properties(klass); +} + +#define TYPE_LOCKED_INTERFACE "locked-interface" +static const TypeInfo locked_interface_type = { + .name = TYPE_LOCKED_INTERFACE, + .parent = TYPE_INTERFACE, + .class_base_init = locked_interface_class_base_init, +}; + +#define TYPE_LOCKED_BY_INTERFACE "locked-by-interface" +static const TypeInfo locked_by_interface_type = { + .name = TYPE_LOCKED_BY_INTERFACE, + .parent = TYPE_OBJECT, + .interfaces = (InterfaceInfo[]) { + { TYPE_LOCKED_INTERFACE }, + { }, + }, +}; + +/* Make sure QOM property locking works as expected */ +static void test_proplist_lock(void) +{ + g_autoptr(Object) dynamic_obj = object_new(TYPE_DYNAMIC_PROPS); + g_autoptr(Object) static_obj = object_new(TYPE_STATIC_PROPS); + g_autoptr(Object) locked = object_new(TYPE_LOCKED_BY_INTERFACE); + Error *err = NULL; + + /* read-only property: should always work */ + object_property_try_add(dynamic_obj, "dynamic-prop-ro", "uint32", + prop1_accessor, NULL, + NULL, NULL, &error_abort); + object_property_try_add(static_obj, "dynamic-prop-ro", "uint32", + prop1_accessor, NULL, + NULL, NULL, &error_abort); + object_property_try_add(locked, "dynamic-prop-ro", "uint32", + prop1_accessor, NULL, + NULL, NULL, &error_abort); + + + /* read-write property: */ + + /* TYPE_DYNAMIC_PROPS is not locked */ + object_property_try_add(dynamic_obj, "dynamic-prop-rw", "uint32", + prop1_accessor, prop1_accessor, + NULL, NULL, &error_abort); + + /* TYPE_STATIC_PROPS is locked */ + object_property_try_add(static_obj, "dynamic-prop-rw", "uint32", + prop1_accessor, prop1_accessor, + NULL, NULL, &err); + error_free_or_abort(&err); + + /* TYPE_LOCKED_BY_INTERFACE is locked by interface type */ + object_property_try_add(locked, "dynamic-prop-rw", "uint32", + prop1_accessor, prop1_accessor, + NULL, NULL, &err); + error_free_or_abort(&err); +} + + /* Test setting of dynamic properties using global properties */ static void test_dynamic_globalprop_subprocess(void) { @@ -294,6 +360,10 @@ int main(int argc, char **argv) type_register_static(&hotplug_type); type_register_static(&nohotplug_type); type_register_static(&nondevice_type); + type_register_static(&locked_interface_type); + type_register_static(&locked_by_interface_type); + + g_test_add_func("/qdev/properties/locking", test_proplist_lock); g_test_add_func("/qdev/properties/static/default/subprocess", test_static_prop_subprocess); -- 2.26.2