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:
Matthias Bolte 2010-10-12 19:37:39 +02:00
parent 4cfcde2d83
commit 2dd86bbe5a
9 changed files with 413 additions and 102 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
} }
} }

View File

@ -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;

View File

@ -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;
} }

View File

@ -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"