@@ -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
@@ -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;
@@ -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);
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 <ehabkost@redhat.com> --- Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: "Daniel P. Berrangé" <berrange@redhat.com> Cc: Eduardo Habkost <ehabkost@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(+)