mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-07-06 09:55:46 +00:00
esx: Handle name escaping properly
VMware uses a mix of percent-, pipe- and base64-encoding in different combinations in different places. Add a testcase for this.
This commit is contained in:
parent
4cfcde2d83
commit
2dd86bbe5a
@ -86,3 +86,28 @@ It tries to cancel the blocked task, but this may not be possible, because
|
|||||||
there are task like the power-on task that is marked as non-cancelable. So the
|
there are task like the power-on task that is marked as non-cancelable. So the
|
||||||
driver may leave blocked tasks behind if automatic question handling is
|
driver may leave blocked tasks behind if automatic question handling is
|
||||||
disabled.
|
disabled.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Different escaping schemes used in different places
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
A domain name in the vSphere API has [%/\] escaped as %XX (percent-encoding),
|
||||||
|
where XX is the ASCII code of the escaped char in hex.
|
||||||
|
|
||||||
|
A domainName entry in a VMX config file is percent-encoded and has [|"] escaped
|
||||||
|
as |XX (pipe-encoding).
|
||||||
|
|
||||||
|
A annotation entry in a VMX config file is pipe-encoded.
|
||||||
|
|
||||||
|
A datastore item name has the special Windows path characters ["*<>:|?]
|
||||||
|
replaced by underscores (_). The result is escaped using percent-encoding and
|
||||||
|
base64-encoding. This isn't a bijective encoding. Therefore, escaped datastore
|
||||||
|
item names cannot be unescaped completely.
|
||||||
|
|
||||||
|
For base64-encoding sequences of chars that don't match [a-zA-Z0-9'(),. _-]
|
||||||
|
are replaced by their base64 form (the padding is omitted). An encoded sequence
|
||||||
|
begins with a plus (+), ends with a minus (-) and can contain a plus (+). The
|
||||||
|
minus (-) is omitted if the string ends in a base64-encoded sequence. VMware
|
||||||
|
uses the comma (,) instead of the slash (/) in the base64 alphabet to avoid
|
||||||
|
conflicts with the slash as path separator.
|
||||||
|
@ -2708,7 +2708,6 @@ esxListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
|
|||||||
esxVI_String *propertyNameList = NULL;
|
esxVI_String *propertyNameList = NULL;
|
||||||
esxVI_ObjectContent *virtualMachineList = NULL;
|
esxVI_ObjectContent *virtualMachineList = NULL;
|
||||||
esxVI_ObjectContent *virtualMachine = NULL;
|
esxVI_ObjectContent *virtualMachine = NULL;
|
||||||
esxVI_DynamicProperty *dynamicProperty = NULL;
|
|
||||||
esxVI_VirtualMachinePowerState powerState;
|
esxVI_VirtualMachinePowerState powerState;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int i;
|
int i;
|
||||||
@ -2745,27 +2744,15 @@ esxListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (dynamicProperty = virtualMachine->propSet;
|
names[count] = NULL;
|
||||||
dynamicProperty != NULL;
|
|
||||||
dynamicProperty = dynamicProperty->_next) {
|
|
||||||
if (STREQ(dynamicProperty->name, "name")) {
|
|
||||||
if (esxVI_AnyType_ExpectType(dynamicProperty->val,
|
|
||||||
esxVI_Type_String) < 0) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
names[count] = strdup(dynamicProperty->val->string);
|
if (esxVI_GetVirtualMachineIdentity(virtualMachine, NULL, &names[count],
|
||||||
|
NULL) < 0) {
|
||||||
if (names[count] == NULL) {
|
goto cleanup;
|
||||||
virReportOOMError();
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++count;
|
||||||
|
|
||||||
if (count >= maxnames) {
|
if (count >= maxnames) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2864,14 +2851,18 @@ esxDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
esxDomainCreate(virDomainPtr domain)
|
esxDomainCreate(virDomainPtr domain)
|
||||||
{
|
{
|
||||||
return esxDomainCreateWithFlags(domain, 0);
|
return esxDomainCreateWithFlags(domain, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static virDomainPtr
|
static virDomainPtr
|
||||||
esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
esxDomainDefineXML(virConnectPtr conn, const char *xml)
|
||||||
{
|
{
|
||||||
esxPrivate *priv = conn->privateData;
|
esxPrivate *priv = conn->privateData;
|
||||||
virDomainDefPtr def = NULL;
|
virDomainDefPtr def = NULL;
|
||||||
@ -2883,6 +2874,7 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
esxVMX_Data data;
|
esxVMX_Data data;
|
||||||
char *datastoreName = NULL;
|
char *datastoreName = NULL;
|
||||||
char *directoryName = NULL;
|
char *directoryName = NULL;
|
||||||
|
char *escapedName = NULL;
|
||||||
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
||||||
char *url = NULL;
|
char *url = NULL;
|
||||||
char *datastoreRelatedPath = NULL;
|
char *datastoreRelatedPath = NULL;
|
||||||
@ -2912,6 +2904,13 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (virtualMachine == NULL &&
|
||||||
|
esxVI_LookupVirtualMachineByName(priv->primary, def->name, NULL,
|
||||||
|
&virtualMachine,
|
||||||
|
esxVI_Occurrence_OptionalItem) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
if (virtualMachine != NULL) {
|
if (virtualMachine != NULL) {
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
@ -2992,7 +2991,13 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
virBufferAddChar(&buffer, '/');
|
virBufferAddChar(&buffer, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
virBufferURIEncodeString(&buffer, def->name);
|
escapedName = esxUtil_EscapeDatastoreItem(def->name);
|
||||||
|
|
||||||
|
if (escapedName == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferURIEncodeString(&buffer, escapedName);
|
||||||
virBufferAddLit(&buffer, ".vmx?dcPath=");
|
virBufferAddLit(&buffer, ".vmx?dcPath=");
|
||||||
virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
|
virBufferURIEncodeString(&buffer, priv->primary->datacenter->name);
|
||||||
virBufferAddLit(&buffer, "&dsName=");
|
virBufferAddLit(&buffer, "&dsName=");
|
||||||
@ -3005,29 +3010,31 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
|
|
||||||
url = virBufferContentAndReset(&buffer);
|
url = virBufferContentAndReset(&buffer);
|
||||||
|
|
||||||
if (directoryName != NULL) {
|
|
||||||
if (virAsprintf(&datastoreRelatedPath, "[%s] %s/%s.vmx", datastoreName,
|
|
||||||
directoryName, def->name) < 0) {
|
|
||||||
virReportOOMError();
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (virAsprintf(&datastoreRelatedPath, "[%s] %s.vmx", datastoreName,
|
|
||||||
def->name) < 0) {
|
|
||||||
virReportOOMError();
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check, if VMX file already exists */
|
/* Check, if VMX file already exists */
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
|
|
||||||
/* Upload VMX file */
|
/* Upload VMX file */
|
||||||
|
VIR_DEBUG("Uploading .vmx config, url='%s' vmx='%s'", url, vmx);
|
||||||
|
|
||||||
if (esxVI_Context_UploadFile(priv->primary, url, vmx) < 0) {
|
if (esxVI_Context_UploadFile(priv->primary, url, vmx) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register the domain */
|
/* Register the domain */
|
||||||
|
if (directoryName != NULL) {
|
||||||
|
if (virAsprintf(&datastoreRelatedPath, "[%s] %s/%s.vmx", datastoreName,
|
||||||
|
directoryName, escapedName) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (virAsprintf(&datastoreRelatedPath, "[%s] %s.vmx", datastoreName,
|
||||||
|
escapedName) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (esxVI_RegisterVM_Task(priv->primary, priv->primary->datacenter->vmFolder,
|
if (esxVI_RegisterVM_Task(priv->primary, priv->primary->datacenter->vmFolder,
|
||||||
datastoreRelatedPath, NULL, esxVI_Boolean_False,
|
datastoreRelatedPath, NULL, esxVI_Boolean_False,
|
||||||
priv->primary->computeResource->resourcePool,
|
priv->primary->computeResource->resourcePool,
|
||||||
@ -3061,6 +3068,7 @@ esxDomainDefineXML(virConnectPtr conn, const char *xml ATTRIBUTE_UNUSED)
|
|||||||
VIR_FREE(vmx);
|
VIR_FREE(vmx);
|
||||||
VIR_FREE(datastoreName);
|
VIR_FREE(datastoreName);
|
||||||
VIR_FREE(directoryName);
|
VIR_FREE(directoryName);
|
||||||
|
VIR_FREE(escapedName);
|
||||||
VIR_FREE(url);
|
VIR_FREE(url);
|
||||||
VIR_FREE(datastoreRelatedPath);
|
VIR_FREE(datastoreRelatedPath);
|
||||||
esxVI_ObjectContent_Free(&virtualMachine);
|
esxVI_ObjectContent_Free(&virtualMachine);
|
||||||
|
@ -915,9 +915,13 @@ esxStorageVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc,
|
|||||||
virStoragePoolDef poolDef;
|
virStoragePoolDef poolDef;
|
||||||
virStorageVolDefPtr def = NULL;
|
virStorageVolDefPtr def = NULL;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
char *datastorePath = NULL;
|
char *unescapedDatastorePath = NULL;
|
||||||
|
char *unescapedDirectoryName = NULL;
|
||||||
|
char *unescapedDirectoryAndFileName = NULL;
|
||||||
char *directoryName = NULL;
|
char *directoryName = NULL;
|
||||||
|
char *fileName = NULL;
|
||||||
char *datastorePathWithoutFileName = NULL;
|
char *datastorePathWithoutFileName = NULL;
|
||||||
|
char *datastorePath = NULL;
|
||||||
esxVI_FileInfo *fileInfo = NULL;
|
esxVI_FileInfo *fileInfo = NULL;
|
||||||
esxVI_FileBackedVirtualDiskSpec *virtualDiskSpec = NULL;
|
esxVI_FileBackedVirtualDiskSpec *virtualDiskSpec = NULL;
|
||||||
esxVI_ManagedObjectReference *task = NULL;
|
esxVI_ManagedObjectReference *task = NULL;
|
||||||
@ -995,15 +999,30 @@ esxStorageVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virAsprintf(&datastorePath, "[%s] %s", pool->name, def->name) < 0) {
|
if (virAsprintf(&unescapedDatastorePath, "[%s] %s", pool->name,
|
||||||
|
def->name) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->target.format == VIR_STORAGE_FILE_VMDK) {
|
if (def->target.format == VIR_STORAGE_FILE_VMDK) {
|
||||||
/* Create directory, if it doesn't exist yet */
|
/* Parse and escape datastore path */
|
||||||
if (esxUtil_ParseDatastorePath(datastorePath, NULL, &directoryName,
|
if (esxUtil_ParseDatastorePath(unescapedDatastorePath, NULL,
|
||||||
NULL) < 0) {
|
&unescapedDirectoryName,
|
||||||
|
&unescapedDirectoryAndFileName) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryName = esxUtil_EscapeDatastoreItem(unescapedDirectoryName);
|
||||||
|
|
||||||
|
if (directoryName == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName = esxUtil_EscapeDatastoreItem(unescapedDirectoryAndFileName +
|
||||||
|
strlen(unescapedDirectoryName) + 1);
|
||||||
|
|
||||||
|
if (fileName == NULL) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1013,6 +1032,13 @@ esxStorageVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (virAsprintf(&datastorePath, "[%s] %s/%s", pool->name, directoryName,
|
||||||
|
fileName) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create directory, if it doesn't exist yet */
|
||||||
if (esxVI_LookupFileInfoByDatastorePath
|
if (esxVI_LookupFileInfoByDatastorePath
|
||||||
(priv->primary, datastorePathWithoutFileName, true, &fileInfo,
|
(priv->primary, datastorePathWithoutFileName, true, &fileInfo,
|
||||||
esxVI_Occurrence_OptionalItem) < 0) {
|
esxVI_Occurrence_OptionalItem) < 0) {
|
||||||
@ -1115,9 +1141,13 @@ esxStorageVolumeCreateXML(virStoragePoolPtr pool, const char *xmldesc,
|
|||||||
esxVI_ObjectContent_Free(&datastore);
|
esxVI_ObjectContent_Free(&datastore);
|
||||||
esxVI_DatastoreInfo_Free(&datastoreInfo);
|
esxVI_DatastoreInfo_Free(&datastoreInfo);
|
||||||
virStorageVolDefFree(def);
|
virStorageVolDefFree(def);
|
||||||
VIR_FREE(datastorePath);
|
VIR_FREE(unescapedDatastorePath);
|
||||||
|
VIR_FREE(unescapedDirectoryName);
|
||||||
|
VIR_FREE(unescapedDirectoryAndFileName);
|
||||||
VIR_FREE(directoryName);
|
VIR_FREE(directoryName);
|
||||||
|
VIR_FREE(fileName);
|
||||||
VIR_FREE(datastorePathWithoutFileName);
|
VIR_FREE(datastorePathWithoutFileName);
|
||||||
|
VIR_FREE(datastorePath);
|
||||||
esxVI_FileInfo_Free(&fileInfo);
|
esxVI_FileInfo_Free(&fileInfo);
|
||||||
esxVI_FileBackedVirtualDiskSpec_Free(&virtualDiskSpec);
|
esxVI_FileBackedVirtualDiskSpec_Free(&virtualDiskSpec);
|
||||||
esxVI_ManagedObjectReference_Free(&task);
|
esxVI_ManagedObjectReference_Free(&task);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <c-ctype.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
@ -621,3 +622,200 @@ esxUtil_ReformatUuid(const char *input, char *output)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
esxUtil_EscapeHex(const char *string, char escape, const char *special)
|
||||||
|
{
|
||||||
|
char *escaped = NULL;
|
||||||
|
size_t length = 1; /* 1 byte for termination */
|
||||||
|
const char *tmp1 = string;
|
||||||
|
char *tmp2;
|
||||||
|
|
||||||
|
/* Calculate length of escaped string */
|
||||||
|
while (*tmp1 != '\0') {
|
||||||
|
if (*tmp1 == escape || strspn(tmp1, special) > 0) {
|
||||||
|
length += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
++tmp1;
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VIR_ALLOC_N(escaped, length) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp1 = string; /* reading from this one */
|
||||||
|
tmp2 = escaped; /* writing to this one */
|
||||||
|
|
||||||
|
/* Escape to 'cXX' where c is the escape char and X is a hex digit */
|
||||||
|
while (*tmp1 != '\0') {
|
||||||
|
if (*tmp1 == escape || strspn(tmp1, special) > 0) {
|
||||||
|
*tmp2++ = escape;
|
||||||
|
|
||||||
|
snprintf(tmp2, 3, "%02x", (unsigned int)*tmp1);
|
||||||
|
|
||||||
|
tmp2 += 2;
|
||||||
|
} else {
|
||||||
|
*tmp2++ = *tmp1;
|
||||||
|
}
|
||||||
|
|
||||||
|
++tmp1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmp2 = '\0';
|
||||||
|
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
esxUtil_UnescapeHex(char *string, char escape)
|
||||||
|
{
|
||||||
|
char *tmp1 = string; /* reading from this one */
|
||||||
|
char *tmp2 = string; /* writing to this one */
|
||||||
|
|
||||||
|
/* Unescape from 'cXX' where c is the escape char and X is a hex digit */
|
||||||
|
while (*tmp1 != '\0') {
|
||||||
|
if (*tmp1 == escape) {
|
||||||
|
if (!c_isxdigit(tmp1[1]) || !c_isxdigit(tmp1[2])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmp2++ = virHexToBin(tmp1[1]) * 16 + virHexToBin(tmp1[2]);
|
||||||
|
tmp1 += 3;
|
||||||
|
} else {
|
||||||
|
*tmp2++ = *tmp1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmp2 = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
esxUtil_EscapeBase64(const char *string)
|
||||||
|
{
|
||||||
|
/* 'normal' characters don't get base64 encoded */
|
||||||
|
static const char *normal =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),. _-";
|
||||||
|
|
||||||
|
/* VMware uses ',' instead of the path separator '/' in the base64 alphabet */
|
||||||
|
static const char *base64 =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
|
||||||
|
|
||||||
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
||||||
|
const char *tmp1 = string;
|
||||||
|
size_t length;
|
||||||
|
unsigned char c1, c2, c3;
|
||||||
|
|
||||||
|
/* Escape sequences of non-'normal' characters as base64 without padding */
|
||||||
|
while (*tmp1 != '\0') {
|
||||||
|
length = strspn(tmp1, normal);
|
||||||
|
|
||||||
|
if (length > 0) {
|
||||||
|
virBufferAdd(&buffer, tmp1, length);
|
||||||
|
|
||||||
|
tmp1 += length;
|
||||||
|
} else {
|
||||||
|
length = strcspn(tmp1, normal);
|
||||||
|
|
||||||
|
virBufferAddChar(&buffer, '+');
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
c1 = *tmp1++;
|
||||||
|
c2 = length > 1 ? *tmp1++ : 0;
|
||||||
|
c3 = length > 2 ? *tmp1++ : 0;
|
||||||
|
|
||||||
|
virBufferAddChar(&buffer, base64[(c1 >> 2) & 0x3f]);
|
||||||
|
virBufferAddChar(&buffer, base64[((c1 << 4) + (c2 >> 4)) & 0x3f]);
|
||||||
|
|
||||||
|
if (length > 1) {
|
||||||
|
virBufferAddChar(&buffer, base64[((c2 << 2) + (c3 >> 6)) & 0x3f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > 2) {
|
||||||
|
virBufferAddChar(&buffer, base64[c3 & 0x3f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
length -= length > 3 ? 3 : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*tmp1 != '\0') {
|
||||||
|
virBufferAddChar(&buffer, '-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virBufferError(&buffer)) {
|
||||||
|
virReportOOMError();
|
||||||
|
virBufferFreeAndReset(&buffer);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return virBufferContentAndReset(&buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
esxUtil_ReplaceSpecialWindowsPathChars(char *string)
|
||||||
|
{
|
||||||
|
/* '/' and '\\' are missing on purpose */
|
||||||
|
static const char *specials = "\"*<>:|?";
|
||||||
|
|
||||||
|
char *tmp = string;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
while (*tmp != '\0') {
|
||||||
|
length = strspn(tmp, specials);
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
*tmp++ = '_';
|
||||||
|
--length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*tmp != '\0') {
|
||||||
|
++tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
esxUtil_EscapeDatastoreItem(const char *string)
|
||||||
|
{
|
||||||
|
char *replaced = strdup(string);
|
||||||
|
char *escaped1;
|
||||||
|
char *escaped2 = NULL;
|
||||||
|
|
||||||
|
if (replaced == NULL) {
|
||||||
|
virReportOOMError();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esxUtil_ReplaceSpecialWindowsPathChars(replaced);
|
||||||
|
|
||||||
|
escaped1 = esxUtil_EscapeHexPercent(replaced);
|
||||||
|
|
||||||
|
if (escaped1 == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
escaped2 = esxUtil_EscapeBase64(escaped1);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
VIR_FREE(replaced);
|
||||||
|
VIR_FREE(escaped1);
|
||||||
|
|
||||||
|
return escaped2;
|
||||||
|
}
|
||||||
|
@ -71,4 +71,22 @@ int esxUtil_GetConfigBoolean(virConfPtr conf, const char *name, bool *boolean_,
|
|||||||
|
|
||||||
int esxUtil_ReformatUuid(const char *input, char *output);
|
int esxUtil_ReformatUuid(const char *input, char *output);
|
||||||
|
|
||||||
|
char *esxUtil_EscapeHex(const char *string, char escape, const char *special);
|
||||||
|
|
||||||
|
# define esxUtil_EscapeHexPipe(_string) esxUtil_EscapeHex(_string, '|', "\"")
|
||||||
|
|
||||||
|
# define esxUtil_EscapeHexPercent(_string) esxUtil_EscapeHex(_string, '%', "/\\")
|
||||||
|
|
||||||
|
int esxUtil_UnescapeHex(char *string, char escape);
|
||||||
|
|
||||||
|
# define esxUtil_UnescapeHexPipe(_string) esxUtil_UnescapeHex(_string, '|')
|
||||||
|
|
||||||
|
# define esxUtil_UnescapeHexPercent(_string) esxUtil_UnescapeHex(_string, '%')
|
||||||
|
|
||||||
|
char *esxUtil_EscapeBase64(const char *string);
|
||||||
|
|
||||||
|
void esxUtil_ReplaceSpecialWindowsPathChars(char *string);
|
||||||
|
|
||||||
|
char *esxUtil_EscapeDatastoreItem(const char *string);
|
||||||
|
|
||||||
#endif /* __ESX_UTIL_H__ */
|
#endif /* __ESX_UTIL_H__ */
|
||||||
|
@ -2014,6 +2014,12 @@ esxVI_GetVirtualMachineIdentity(esxVI_ObjectContent *virtualMachine,
|
|||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esxUtil_UnescapeHexPercent(*name) < 0) {
|
||||||
|
ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("Domain name contains invalid escape sequence"));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -876,8 +876,6 @@ esxVMX_ParseConfig(esxVMX_Context *ctx, virCapsPtr caps, const char *vmx,
|
|||||||
long long numvcpus = 0;
|
long long numvcpus = 0;
|
||||||
char *sched_cpu_affinity = NULL;
|
char *sched_cpu_affinity = NULL;
|
||||||
char *guestOS = NULL;
|
char *guestOS = NULL;
|
||||||
char *tmp1;
|
|
||||||
char *tmp2;
|
|
||||||
int controller;
|
int controller;
|
||||||
int bus;
|
int bus;
|
||||||
int port;
|
int port;
|
||||||
@ -982,34 +980,28 @@ esxVMX_ParseConfig(esxVMX_Context *ctx, virCapsPtr caps, const char *vmx,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (def->name != NULL) {
|
||||||
|
if (esxUtil_UnescapeHexPercent(def->name) < 0 ||
|
||||||
|
esxUtil_UnescapeHexPipe(def->name) < 0) {
|
||||||
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("VMX entry 'name' contains invalid escape sequence"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* vmx:annotation -> def:description */
|
/* vmx:annotation -> def:description */
|
||||||
if (esxUtil_GetConfigString(conf, "annotation", &def->description,
|
if (esxUtil_GetConfigString(conf, "annotation", &def->description,
|
||||||
true) < 0) {
|
true) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unescape '|XX' where X is a hex digit */
|
|
||||||
if (def->description != NULL) {
|
if (def->description != NULL) {
|
||||||
tmp1 = def->description; /* reading from this one */
|
if (esxUtil_UnescapeHexPipe(def->description) < 0) {
|
||||||
tmp2 = def->description; /* writing to this one */
|
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("VMX entry 'annotation' contains invalid escape "
|
||||||
while (*tmp1 != '\0') {
|
"sequence"));
|
||||||
if (*tmp1 == '|') {
|
goto cleanup;
|
||||||
if (!c_isxdigit(tmp1[1]) || !c_isxdigit(tmp1[2])) {
|
|
||||||
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("VMX entry 'annotation' contains invalid "
|
|
||||||
"escape sequence"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
*tmp2++ = virHexToBin(tmp1[1]) * 16 + virHexToBin(tmp1[2]);
|
|
||||||
tmp1 += 3;
|
|
||||||
} else {
|
|
||||||
*tmp2++ = *tmp1++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*tmp2 = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vmx:memsize -> def:mem.max_balloon */
|
/* vmx:memsize -> def:mem.max_balloon */
|
||||||
@ -2460,9 +2452,8 @@ esxVMX_FormatConfig(esxVMX_Context *ctx, virCapsPtr caps, virDomainDefPtr def,
|
|||||||
int sched_cpu_affinity_length;
|
int sched_cpu_affinity_length;
|
||||||
unsigned char zero[VIR_UUID_BUFLEN];
|
unsigned char zero[VIR_UUID_BUFLEN];
|
||||||
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
virBuffer buffer = VIR_BUFFER_INITIALIZER;
|
||||||
size_t length;
|
char *preliminaryDisplayName = NULL;
|
||||||
char *tmp1;
|
char *displayName = NULL;
|
||||||
char *tmp2;
|
|
||||||
char *annotation = NULL;
|
char *annotation = NULL;
|
||||||
bool scsi_present[4] = { false, false, false, false };
|
bool scsi_present[4] = { false, false, false, false };
|
||||||
int scsi_virtualDev[4] = { -1, -1, -1, -1 };
|
int scsi_virtualDev[4] = { -1, -1, -1, -1 };
|
||||||
@ -2536,44 +2527,23 @@ esxVMX_FormatConfig(esxVMX_Context *ctx, virCapsPtr caps, virDomainDefPtr def,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* def:name -> vmx:displayName */
|
/* def:name -> vmx:displayName */
|
||||||
virBufferVSprintf(&buffer, "displayName = \"%s\"\n", def->name);
|
preliminaryDisplayName = esxUtil_EscapeHexPipe(def->name);
|
||||||
|
|
||||||
|
if (preliminaryDisplayName == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName = esxUtil_EscapeHexPercent(preliminaryDisplayName);
|
||||||
|
|
||||||
|
if (displayName == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferVSprintf(&buffer, "displayName = \"%s\"\n", displayName);
|
||||||
|
|
||||||
/* def:description -> vmx:annotation */
|
/* def:description -> vmx:annotation */
|
||||||
if (def->description != NULL) {
|
if (def->description != NULL) {
|
||||||
/* Escape '"' as '|22' and '|' as '|7C' */
|
annotation = esxUtil_EscapeHexPipe(def->description);
|
||||||
length = 1; /* 1 byte for termination */
|
|
||||||
tmp1 = def->description;
|
|
||||||
|
|
||||||
while (*tmp1 != '\0') {
|
|
||||||
if (*tmp1 == '"' || *tmp1 == '|') {
|
|
||||||
length += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
++tmp1;
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VIR_ALLOC_N(annotation, length) < 0) {
|
|
||||||
virReportOOMError();
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp1 = def->description; /* reading from this one */
|
|
||||||
tmp2 = annotation; /* writing to this one */
|
|
||||||
|
|
||||||
while (*tmp1 != '\0') {
|
|
||||||
if (*tmp1 == '"') {
|
|
||||||
*tmp2++ = '|'; *tmp2++ = '2'; *tmp2++ = '2';
|
|
||||||
} else if (*tmp1 == '|') {
|
|
||||||
*tmp2++ = '|'; *tmp2++ = '7'; *tmp2++ = 'C';
|
|
||||||
} else {
|
|
||||||
*tmp2++ = *tmp1;
|
|
||||||
}
|
|
||||||
|
|
||||||
++tmp1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*tmp2 = '\0';
|
|
||||||
|
|
||||||
virBufferVSprintf(&buffer, "annotation = \"%s\"\n", annotation);
|
virBufferVSprintf(&buffer, "annotation = \"%s\"\n", annotation);
|
||||||
}
|
}
|
||||||
@ -2780,6 +2750,8 @@ esxVMX_FormatConfig(esxVMX_Context *ctx, virCapsPtr caps, virDomainDefPtr def,
|
|||||||
virBufferFreeAndReset(&buffer);
|
virBufferFreeAndReset(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VIR_FREE(preliminaryDisplayName);
|
||||||
|
VIR_FREE(displayName);
|
||||||
VIR_FREE(annotation);
|
VIR_FREE(annotation);
|
||||||
|
|
||||||
return vmx;
|
return vmx;
|
||||||
|
@ -227,6 +227,59 @@ testConvertDateTimeToCalendarTime(const void *data ATTRIBUTE_UNUSED)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct testDatastoreItem {
|
||||||
|
const char *string;
|
||||||
|
const char *escaped;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct testDatastoreItem datastoreItems[] = {
|
||||||
|
{ "normal", "normal" },
|
||||||
|
{ /* "Aä1ö2ü3ß4#5~6!7§8/9%Z" */
|
||||||
|
"A\303\2441\303\2662\303\2743\303\2374#5~6!7\302\2478/9%Z",
|
||||||
|
"A+w6Q-1+w7Y-2+w7w-3+w58-4+Iw-5+fg-6+IQ-7+wqc-8+JQ-2f9+JQ-25Z" },
|
||||||
|
{ /* "Z~6!7§8/9%0#1\"2'3`4&A" */ "Z~6!7\302\2478/9%0#1\"2'3`4&A",
|
||||||
|
"Z+fg-6+IQ-7+wqc-8+JQ-2f9+JQ-250+Iw-1_2'3+YA-4+Jg-A" },
|
||||||
|
{ /* "標準語" */ "\346\250\231\346\272\226\350\252\236", "+5qiZ5rqW6Kqe" },
|
||||||
|
{ "!\"#$%&'()*+,-./0123456789:;<=>?",
|
||||||
|
"+IQ-_+IyQl-25+Jg-'()_+Kw-,-.+JQ-2f0123456789_+Ow-_+PQ-__" },
|
||||||
|
{ "A Z[\\]^_B", "A Z+WyU-5c+XV4-_B" },
|
||||||
|
{ "A`B@{|}~DEL", "A+YA-B+QHs-_+fX4-DEL" },
|
||||||
|
{ /* "hÀÁÂÃÄÅH" */ "h\303\200\303\201\303\202\303\203\303\204\303\205H",
|
||||||
|
"h+w4DDgcOCw4PDhMOF-H" },
|
||||||
|
{ /* "A쿀Z" */ "A\354\277\200Z", "A+7L+A-Z" },
|
||||||
|
{ /* "!쿀A" */ "!\354\277\200A", "+Iey,gA-A" },
|
||||||
|
{ "~~~", "+fn5+" },
|
||||||
|
{ "~~~A", "+fn5+-A" },
|
||||||
|
{ "K%U/H\\Z", "K+JQ-25U+JQ-2fH+JQ-5cZ" },
|
||||||
|
{ "vvv<A\"B\"C>zzz", "vvv_A_B_C_zzz" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
testEscapeDatastoreItem(const void *data ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *escaped = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_CARDINALITY(datastoreItems); ++i) {
|
||||||
|
VIR_FREE(escaped);
|
||||||
|
|
||||||
|
escaped = esxUtil_EscapeDatastoreItem(datastoreItems[i].string);
|
||||||
|
|
||||||
|
if (escaped == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (STRNEQ(datastoreItems[i].escaped, escaped)) {
|
||||||
|
VIR_FREE(escaped);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mymain(int argc, char **argv)
|
mymain(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -258,6 +311,7 @@ mymain(int argc, char **argv)
|
|||||||
DO_TEST(DiskNameToIndex);
|
DO_TEST(DiskNameToIndex);
|
||||||
DO_TEST(ParseDatastorePath);
|
DO_TEST(ParseDatastorePath);
|
||||||
DO_TEST(ConvertDateTimeToCalendarTime);
|
DO_TEST(ConvertDateTimeToCalendarTime);
|
||||||
|
DO_TEST(EscapeDatastoreItem);
|
||||||
|
|
||||||
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ virtualHW.version = "4"
|
|||||||
guestOS = "other"
|
guestOS = "other"
|
||||||
uuid.bios = "56 4d 9b ef ac d9 b4 e0-c8 f0 ae a8 b9 10 35 15"
|
uuid.bios = "56 4d 9b ef ac d9 b4 e0-c8 f0 ae a8 b9 10 35 15"
|
||||||
displayName = "annotation"
|
displayName = "annotation"
|
||||||
annotation = "Some |7Ctext|7C to test the |22escaping|22: |7C|7C|22|22|7C|7C|22|7C Unescaped!"
|
annotation = "Some |7ctext|7c to test the |22escaping|22: |7c|7c|22|22|7c|7c|22|7c Unescaped!"
|
||||||
memsize = "4"
|
memsize = "4"
|
||||||
numvcpus = "1"
|
numvcpus = "1"
|
||||||
floppy0.present = "false"
|
floppy0.present = "false"
|
||||||
|
Loading…
Reference in New Issue
Block a user