提交 b449c270 编写于 作者: J Jiri Denemark

qemu: Pass correct qemuCaps to virDomainDeviceDefPostParse

Since qemuDomainDeviceDefPostParse callback requires qemuCaps, we need
to make sure it gets the capabilities stored in the domain's private
data if the domain is running. Passing NULL may cause QEMU capabilities
probing to be triggered in case QEMU binary changed in the meantime.
When this happens while a running domain object is locked, QMP event
delivered to the domain before QEMU capabilities probing finishes will
deadlock the event loop.

QEMU capabilities lookup (via domainPostParseDataAlloc callback) is
hidden inside virDomainDeviceDefPostParseOne with no way to pass
qemuCaps to virDomainDeviceDef* functions. This patch fixes all
remaining paths leading to virDomainDeviceDefPostParse.
Signed-off-by: NJiri Denemark <jdenemar@redhat.com>
Reviewed-by: NMichal Privoznik <mprivozn@redhat.com>
上级 577a1f98
...@@ -5454,22 +5454,24 @@ virDomainDeviceDefPostParseOne(virDomainDeviceDefPtr dev, ...@@ -5454,22 +5454,24 @@ virDomainDeviceDefPostParseOne(virDomainDeviceDefPtr dev,
const virDomainDef *def, const virDomainDef *def,
virCapsPtr caps, virCapsPtr caps,
unsigned int flags, unsigned int flags,
virDomainXMLOptionPtr xmlopt) virDomainXMLOptionPtr xmlopt,
void *parseOpaque)
{ {
void *parseOpaque = NULL; void *data = NULL;
int ret; int ret;
if (xmlopt->config.domainPostParseDataAlloc) { if (!parseOpaque && xmlopt->config.domainPostParseDataAlloc) {
if (xmlopt->config.domainPostParseDataAlloc(def, caps, flags, if (xmlopt->config.domainPostParseDataAlloc(def, caps, flags,
xmlopt->config.priv, xmlopt->config.priv,
&parseOpaque) < 0) &data) < 0)
return -1; return -1;
parseOpaque = data;
} }
ret = virDomainDeviceDefPostParse(dev, def, caps, flags, xmlopt, parseOpaque); ret = virDomainDeviceDefPostParse(dev, def, caps, flags, xmlopt, parseOpaque);
if (parseOpaque && xmlopt->config.domainPostParseDataFree) if (data && xmlopt->config.domainPostParseDataFree)
xmlopt->config.domainPostParseDataFree(parseOpaque); xmlopt->config.domainPostParseDataFree(data);
return ret; return ret;
} }
...@@ -16279,6 +16281,7 @@ virDomainDeviceDefParse(const char *xmlStr, ...@@ -16279,6 +16281,7 @@ virDomainDeviceDefParse(const char *xmlStr,
const virDomainDef *def, const virDomainDef *def,
virCapsPtr caps, virCapsPtr caps,
virDomainXMLOptionPtr xmlopt, virDomainXMLOptionPtr xmlopt,
void *parseOpaque,
unsigned int flags) unsigned int flags)
{ {
xmlDocPtr xml; xmlDocPtr xml;
...@@ -16441,7 +16444,8 @@ virDomainDeviceDefParse(const char *xmlStr, ...@@ -16441,7 +16444,8 @@ virDomainDeviceDefParse(const char *xmlStr,
} }
/* callback to fill driver specific device aspects */ /* callback to fill driver specific device aspects */
if (virDomainDeviceDefPostParseOne(dev, def, caps, flags, xmlopt) < 0) if (virDomainDeviceDefPostParseOne(dev, def, caps, flags,
xmlopt, parseOpaque) < 0)
goto error; goto error;
/* validate the configuration */ /* validate the configuration */
...@@ -29799,7 +29803,8 @@ virDomainDeviceDefPtr ...@@ -29799,7 +29803,8 @@ virDomainDeviceDefPtr
virDomainDeviceDefCopy(virDomainDeviceDefPtr src, virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
const virDomainDef *def, const virDomainDef *def,
virCapsPtr caps, virCapsPtr caps,
virDomainXMLOptionPtr xmlopt) virDomainXMLOptionPtr xmlopt,
void *parseOpaque)
{ {
VIR_AUTOCLEAN(virBuffer) buf = VIR_BUFFER_INITIALIZER; VIR_AUTOCLEAN(virBuffer) buf = VIR_BUFFER_INITIALIZER;
int flags = VIR_DOMAIN_DEF_FORMAT_INACTIVE | VIR_DOMAIN_DEF_FORMAT_SECURE; int flags = VIR_DOMAIN_DEF_FORMAT_INACTIVE | VIR_DOMAIN_DEF_FORMAT_SECURE;
...@@ -29887,7 +29892,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, ...@@ -29887,7 +29892,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
return NULL; return NULL;
xmlStr = virBufferContentAndReset(&buf); xmlStr = virBufferContentAndReset(&buf);
return virDomainDeviceDefParse(xmlStr, def, caps, xmlopt, return virDomainDeviceDefParse(xmlStr, def, caps, xmlopt, parseOpaque,
VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE); VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
} }
......
...@@ -2866,7 +2866,8 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def); ...@@ -2866,7 +2866,8 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
virDomainDeviceDefPtr virDomainDeviceDefCopy(virDomainDeviceDefPtr src, virDomainDeviceDefPtr virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
const virDomainDef *def, const virDomainDef *def,
virCapsPtr caps, virCapsPtr caps,
virDomainXMLOptionPtr xmlopt); virDomainXMLOptionPtr xmlopt,
void *parseOpaque);
virDomainDeviceInfoPtr virDomainDeviceGetInfo(virDomainDeviceDefPtr device); virDomainDeviceInfoPtr virDomainDeviceGetInfo(virDomainDeviceDefPtr device);
void virDomainDeviceSetData(virDomainDeviceDefPtr device, void virDomainDeviceSetData(virDomainDeviceDefPtr device,
void *devicedata); void *devicedata);
...@@ -2998,6 +2999,7 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(const char *xmlStr, ...@@ -2998,6 +2999,7 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(const char *xmlStr,
const virDomainDef *def, const virDomainDef *def,
virCapsPtr caps, virCapsPtr caps,
virDomainXMLOptionPtr xmlopt, virDomainXMLOptionPtr xmlopt,
void *parseOpaque,
unsigned int flags); unsigned int flags);
virDomainDiskDefPtr virDomainDiskDefParse(const char *xmlStr, virDomainDiskDefPtr virDomainDiskDefParse(const char *xmlStr,
const virDomainDef *def, const virDomainDef *def,
......
...@@ -4120,7 +4120,7 @@ libxlDomainAttachDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4120,7 +4120,7 @@ libxlDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE))) VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto endjob; goto endjob;
...@@ -4137,7 +4137,7 @@ libxlDomainAttachDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4137,7 +4137,7 @@ libxlDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
/* If dev exists it was created to modify the domain config. Free it. */ /* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev); virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE))) VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto endjob; goto endjob;
...@@ -4209,7 +4209,7 @@ libxlDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4209,7 +4209,7 @@ libxlDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
goto endjob; goto endjob;
...@@ -4227,7 +4227,7 @@ libxlDomainDetachDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4227,7 +4227,7 @@ libxlDomainDetachDeviceFlags(virDomainPtr dom, const char *xml,
/* If dev exists it was created to modify the domain config. Free it. */ /* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev); virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
goto endjob; goto endjob;
...@@ -4297,7 +4297,7 @@ libxlDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4297,7 +4297,7 @@ libxlDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE))) VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto cleanup; goto cleanup;
...@@ -4316,7 +4316,7 @@ libxlDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -4316,7 +4316,7 @@ libxlDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
/* If dev exists it was created to modify the domain config. Free it. */ /* If dev exists it was created to modify the domain config. Free it. */
virDomainDeviceDefFree(dev); virDomainDeviceDefFree(dev);
if (!(dev = virDomainDeviceDefParse(xml, vm->def, if (!(dev = virDomainDeviceDefParse(xml, vm->def,
cfg->caps, driver->xmlopt, cfg->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE))) VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto cleanup; goto cleanup;
......
...@@ -4716,7 +4716,7 @@ static int lxcDomainAttachDeviceFlags(virDomainPtr dom, ...@@ -4716,7 +4716,7 @@ static int lxcDomainAttachDeviceFlags(virDomainPtr dom,
goto endjob; goto endjob;
dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, dev = dev_copy = virDomainDeviceDefParse(xml, vm->def,
caps, driver->xmlopt, caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE); VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (dev == NULL) if (dev == NULL)
goto endjob; goto endjob;
...@@ -4728,7 +4728,7 @@ static int lxcDomainAttachDeviceFlags(virDomainPtr dom, ...@@ -4728,7 +4728,7 @@ static int lxcDomainAttachDeviceFlags(virDomainPtr dom,
* to CONFIG takes one instance. * to CONFIG takes one instance.
*/ */
dev_copy = virDomainDeviceDefCopy(dev, vm->def, dev_copy = virDomainDeviceDefCopy(dev, vm->def,
caps, driver->xmlopt); caps, driver->xmlopt, NULL);
if (!dev_copy) if (!dev_copy)
goto endjob; goto endjob;
} }
...@@ -4835,7 +4835,7 @@ static int lxcDomainUpdateDeviceFlags(virDomainPtr dom, ...@@ -4835,7 +4835,7 @@ static int lxcDomainUpdateDeviceFlags(virDomainPtr dom,
if (!(caps = virLXCDriverGetCapabilities(driver, false))) if (!(caps = virLXCDriverGetCapabilities(driver, false)))
goto endjob; goto endjob;
if (!(dev = virDomainDeviceDefParse(xml, vm->def, caps, driver->xmlopt, if (!(dev = virDomainDeviceDefParse(xml, vm->def, caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE))) VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto endjob; goto endjob;
...@@ -4899,7 +4899,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom, ...@@ -4899,7 +4899,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom,
goto endjob; goto endjob;
dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, dev = dev_copy = virDomainDeviceDefParse(xml, vm->def,
caps, driver->xmlopt, caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE); VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
if (dev == NULL) if (dev == NULL)
...@@ -4912,7 +4912,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom, ...@@ -4912,7 +4912,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom,
* to CONFIG takes one instance. * to CONFIG takes one instance.
*/ */
dev_copy = virDomainDeviceDefCopy(dev, vm->def, dev_copy = virDomainDeviceDefCopy(dev, vm->def,
caps, driver->xmlopt); caps, driver->xmlopt, NULL);
if (!dev_copy) if (!dev_copy)
goto endjob; goto endjob;
} }
......
...@@ -1962,7 +1962,7 @@ openvzDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml, ...@@ -1962,7 +1962,7 @@ openvzDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
if (!(def = virDomainObjGetOneDef(vm, flags))) if (!(def = virDomainObjGetOneDef(vm, flags)))
goto cleanup; goto cleanup;
dev = virDomainDeviceDefParse(xml, def, driver->caps, driver->xmlopt, dev = virDomainDeviceDefParse(xml, def, driver->caps, driver->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE); VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (!dev) if (!dev)
goto cleanup; goto cleanup;
......
...@@ -1691,7 +1691,7 @@ phypDomainAttachDeviceFlags(virDomainPtr domain, ...@@ -1691,7 +1691,7 @@ phypDomainAttachDeviceFlags(virDomainPtr domain,
def->os.type = VIR_DOMAIN_OSTYPE_LINUX; def->os.type = VIR_DOMAIN_OSTYPE_LINUX;
dev = virDomainDeviceDefParse(xml, def, phyp_driver->caps, NULL, dev = virDomainDeviceDefParse(xml, def, phyp_driver->caps, NULL, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE); VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (!dev) if (!dev)
goto cleanup; goto cleanup;
......
...@@ -8866,7 +8866,8 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm, ...@@ -8866,7 +8866,8 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
goto cleanup; goto cleanup;
if (!(devConf = virDomainDeviceDefParse(xml, vmdef, caps, if (!(devConf = virDomainDeviceDefParse(xml, vmdef, caps,
driver->xmlopt, parse_flags))) driver->xmlopt, priv->qemuCaps,
parse_flags)))
goto cleanup; goto cleanup;
if (virDomainDeviceValidateAliasForHotplug(vm, devConf, if (virDomainDeviceValidateAliasForHotplug(vm, devConf,
...@@ -8886,7 +8887,8 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm, ...@@ -8886,7 +8887,8 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
if (flags & VIR_DOMAIN_AFFECT_LIVE) { if (flags & VIR_DOMAIN_AFFECT_LIVE) {
if (!(devLive = virDomainDeviceDefParse(xml, vm->def, caps, if (!(devLive = virDomainDeviceDefParse(xml, vm->def, caps,
driver->xmlopt, parse_flags))) driver->xmlopt, priv->qemuCaps,
parse_flags)))
goto cleanup; goto cleanup;
if (virDomainDeviceValidateAliasForHotplug(vm, devLive, if (virDomainDeviceValidateAliasForHotplug(vm, devLive,
...@@ -9017,8 +9019,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, ...@@ -9017,8 +9019,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
!(flags & VIR_DOMAIN_AFFECT_LIVE)) !(flags & VIR_DOMAIN_AFFECT_LIVE))
parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE;
dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, caps,
caps, driver->xmlopt, driver->xmlopt, priv->qemuCaps,
parse_flags); parse_flags);
if (dev == NULL) if (dev == NULL)
goto endjob; goto endjob;
...@@ -9029,7 +9031,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, ...@@ -9029,7 +9031,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
* create a deep copy of device as adding * create a deep copy of device as adding
* to CONFIG takes one instance. * to CONFIG takes one instance.
*/ */
dev_copy = virDomainDeviceDefCopy(dev, vm->def, caps, driver->xmlopt); dev_copy = virDomainDeviceDefCopy(dev, vm->def, caps,
driver->xmlopt, priv->qemuCaps);
if (!dev_copy) if (!dev_copy)
goto endjob; goto endjob;
} }
...@@ -9115,8 +9118,8 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, ...@@ -9115,8 +9118,8 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver,
!(flags & VIR_DOMAIN_AFFECT_LIVE)) !(flags & VIR_DOMAIN_AFFECT_LIVE))
parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE;
dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, caps,
caps, driver->xmlopt, driver->xmlopt, priv->qemuCaps,
parse_flags); parse_flags);
if (dev == NULL) if (dev == NULL)
goto cleanup; goto cleanup;
...@@ -9127,7 +9130,8 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, ...@@ -9127,7 +9130,8 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver,
* create a deep copy of device as adding * create a deep copy of device as adding
* to CONFIG takes one instance. * to CONFIG takes one instance.
*/ */
dev_copy = virDomainDeviceDefCopy(dev, vm->def, caps, driver->xmlopt); dev_copy = virDomainDeviceDefCopy(dev, vm->def, caps,
driver->xmlopt, priv->qemuCaps);
if (!dev_copy) if (!dev_copy)
goto cleanup; goto cleanup;
} }
......
...@@ -4313,7 +4313,7 @@ static int vboxDomainAttachDeviceImpl(virDomainPtr dom, ...@@ -4313,7 +4313,7 @@ static int vboxDomainAttachDeviceImpl(virDomainPtr dom,
def->os.type = VIR_DOMAIN_OSTYPE_HVM; def->os.type = VIR_DOMAIN_OSTYPE_HVM;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt, dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE); VIR_DOMAIN_DEF_PARSE_INACTIVE);
if (dev == NULL) if (dev == NULL)
goto cleanup; goto cleanup;
...@@ -4432,7 +4432,7 @@ static int vboxDomainDetachDevice(virDomainPtr dom, const char *xml) ...@@ -4432,7 +4432,7 @@ static int vboxDomainDetachDevice(virDomainPtr dom, const char *xml)
def->os.type = VIR_DOMAIN_OSTYPE_HVM; def->os.type = VIR_DOMAIN_OSTYPE_HVM;
dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt, dev = virDomainDeviceDefParse(xml, def, data->caps, data->xmlopt, NULL,
VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE); VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
if (dev == NULL) if (dev == NULL)
......
...@@ -274,7 +274,7 @@ testQemuHotplug(const void *data) ...@@ -274,7 +274,7 @@ testQemuHotplug(const void *data)
device_parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE; device_parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
if (!(dev = virDomainDeviceDefParse(device_xml, vm->def, if (!(dev = virDomainDeviceDefParse(device_xml, vm->def,
caps, driver.xmlopt, caps, driver.xmlopt, NULL,
device_parse_flags))) device_parse_flags)))
goto cleanup; goto cleanup;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册