提交 145d6cb0 编写于 作者: M Matthias Bolte

esx: Improve VMX file name parsing and formatting

For parsing try to match by datastore mount path first, if that
fails fallback to /vmfs/volumes/<datastore>/<path> parsing. This
also fixes problems with GSX on Windows. Because GSX on Windows
doesn't use /vmfs/volumes/ style file names.

For formatting use the datastore mount path too, instead of using
/vmfs/volumes/<datastore>/<path> as fixed format.
上级 6f42d325
...@@ -24,8 +24,6 @@ ...@@ -24,8 +24,6 @@
#include <config.h> #include <config.h>
#include <netdb.h>
#include "internal.h" #include "internal.h"
#include "domain_conf.h" #include "domain_conf.h"
#include "authhelper.h" #include "authhelper.h"
...@@ -60,190 +58,284 @@ struct _esxVMX_Data { ...@@ -60,190 +58,284 @@ struct _esxVMX_Data {
/*
* Parse a file name from a .vmx file and convert it to datastore path format.
* A .vmx file can contain file names in various formats:
*
* - A single name referencing a file in the same directory as the .vmx file:
*
* test1.vmdk
*
* - An absolute file name referencing a file in a datastore that is mounted at
* /vmfs/volumes/<datastore>:
*
* /vmfs/volumes/b24b7a78-9d82b4f5/test1/test1.vmdk
* /vmfs/volumes/datastore1/test1/test1.vmdk
*
* The actual mount directory is /vmfs/volumes/b24b7a78-9d82b4f5, the second
* form is a symlink to it using the datastore name. This is the typical
* setup on an ESX(i) server.
*
* - With GSX installed on Windows there are also Windows style file names
* including UNC file names:
*
* C:\Virtual Machines\test1\test1.vmdk
* \\nas1\storage1\test1\test1.vmdk
*
* The datastore path format typically looks like this:
*
* [datastore1] test1/test1.vmdk
*
* Firstly this functions checks if the given file name contains a separator.
* If it doesn't then the referenced file is in the same directory as the .vmx
* file. The datastore name and directory of the .vmx file are passed to this
* function via the opaque paramater by the caller of esxVMX_ParseConfig.
*
* Otherwise query for all known datastores and their mount directories. Then
* try to find a datastore with a mount directory that is a prefix to the given
* file name. This mechanism covers the Windows style file names too.
*
* The symlinks using the datastore name (/vmfs/volumes/datastore1) are an
* exception and need special handling. Parse the datastore name and use it
* to lookup the datastore by name to verify that it exists.
*/
static char * static char *
esxAbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath) esxParseVMXFileName(const char *fileName, void *opaque)
{ {
bool success = false; char *datastorePath = NULL;
char *copyOfAbsolutePath = NULL; esxVMX_Data *data = opaque;
char *tmp = NULL;
char *saveptr = NULL;
esxVI_String *propertyNameList = NULL; esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *datastoreList = NULL;
esxVI_ObjectContent *datastore = NULL; esxVI_ObjectContent *datastore = NULL;
esxVI_DatastoreHostMount *hostMount = NULL;
char *datastoreName;
char *tmp;
char *saveptr;
char *strippedFileName = NULL;
char *copyOfFileName = NULL;
char *directoryAndFileName;
char *datastorePath = NULL; if (strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) {
char *preliminaryDatastoreName = NULL; /* Plain file name, use same directory as for the .vmx file */
char *directoryAndFileName = NULL; if (virAsprintf(&datastorePath, "[%s] %s/%s", data->datastoreName,
char *datastoreName = NULL; data->directoryName, fileName) < 0) {
virReportOOMError();
if (esxVI_String_DeepCopyValue(&copyOfAbsolutePath, absolutePath) < 0) { goto cleanup;
return NULL; }
} } else {
if (esxVI_String_AppendValueToList(&propertyNameList,
"summary.name") < 0 ||
esxVI_LookupDatastoreList(data->ctx, propertyNameList,
&datastoreList) < 0) {
return NULL;
}
/* Expected format: '/vmfs/volumes/<datastore>/<path>' */ /* Search for datastore by mount path */
if ((tmp = STRSKIP(copyOfAbsolutePath, "/vmfs/volumes/")) == NULL || for (datastore = datastoreList; datastore != NULL;
(preliminaryDatastoreName = strtok_r(tmp, "/", &saveptr)) == NULL || datastore = datastore->_next) {
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) { esxVI_DatastoreHostMount_Free(&hostMount);
ESX_ERROR(VIR_ERR_INTERNAL_ERROR, datastoreName = NULL;
_("Absolute path '%s' doesn't have expected format "
"'/vmfs/volumes/<datastore>/<path>'"), absolutePath);
goto cleanup;
}
if (esxVI_String_AppendValueToList(&propertyNameList, if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
"summary.name") < 0 || &hostMount) < 0 ||
esxVI_LookupDatastoreByAbsolutePath(ctx, absolutePath, esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
propertyNameList, &datastore, esxVI_Occurrence_RequiredItem) < 0) {
esxVI_Occurrence_OptionalItem) < 0) { goto cleanup;
goto cleanup; }
}
if (datastore == NULL) { tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path);
if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName,
propertyNameList, &datastore,
esxVI_Occurrence_OptionalItem) < 0) {
goto cleanup;
}
}
if (datastore != NULL) { if (tmp == NULL) {
if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName, continue;
esxVI_Occurrence_RequiredItem)) { }
goto cleanup;
}
}
if (datastoreName == NULL) { /* Found a match. Strip leading separators */
VIR_WARN("Could not retrieve datastore name for absolute " while (*tmp == '/' || *tmp == '\\') {
"path '%s', falling back to preliminary name '%s'", ++tmp;
absolutePath, preliminaryDatastoreName); }
datastoreName = preliminaryDatastoreName; if (esxVI_String_DeepCopyValue(&strippedFileName, tmp) < 0) {
} goto cleanup;
}
if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, tmp = strippedFileName;
directoryAndFileName) < 0) {
virReportOOMError();
goto cleanup;
}
/* FIXME: Check if referenced path/file really exists */ /* Convert \ to / */
while (*tmp != '\0') {
if (*tmp == '\\') {
*tmp = '/';
}
success = true; ++tmp;
}
cleanup: if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
if (! success) { strippedFileName) < 0) {
VIR_FREE(datastorePath); virReportOOMError();
} goto cleanup;
}
VIR_FREE(copyOfAbsolutePath); break;
esxVI_String_Free(&propertyNameList); }
esxVI_ObjectContent_Free(&datastore);
return datastorePath; /* Fallback to direct datastore name match */
} if (datastorePath == NULL && STRPREFIX(fileName, "/vmfs/volumes/")) {
if (esxVI_String_DeepCopyValue(&copyOfFileName, fileName) < 0) {
goto cleanup;
}
/* Expected format: '/vmfs/volumes/<datastore>/<path>' */
if ((tmp = STRSKIP(copyOfFileName, "/vmfs/volumes/")) == NULL ||
(datastoreName = strtok_r(tmp, "/", &saveptr)) == NULL ||
(directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("File name '%s' doesn't have expected format "
"'/vmfs/volumes/<datastore>/<path>'"), fileName);
goto cleanup;
}
esxVI_ObjectContent_Free(&datastoreList);
static char * if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
esxParseVMXFileName(const char *fileName, void *opaque) NULL, &datastoreList,
{ esxVI_Occurrence_OptionalItem) < 0) {
char *src = NULL; goto cleanup;
esxVMX_Data *data = opaque; }
if (STRPREFIX(fileName, "/vmfs/volumes/")) { if (datastoreList == NULL) {
/* Found absolute path referencing a file inside a datastore */ ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
return esxAbsolutePathToDatastorePath(data->ctx, fileName); _("File name '%s' refers to non-existing datastore '%s'"),
} else if (STRPREFIX(fileName, "/")) { fileName, datastoreName);
/* Found absolute path referencing a file outside a datastore */ goto cleanup;
src = strdup(fileName); }
if (src == NULL) { if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
virReportOOMError(); directoryAndFileName) < 0) {
return NULL; virReportOOMError();
goto cleanup;
}
} }
/* FIXME: Check if referenced path/file really exists */ if (datastorePath == NULL) {
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
return src; _("Could not find datastore for '%s'"), fileName);
} else if (strchr(fileName, '/') != NULL) { goto cleanup;
/* Found relative path, this is not supported */
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Found relative path '%s' in VMX file, this is not "
"supported"), fileName);
return NULL;
} else {
/* Found single file name referencing a file inside a datastore */
if (virAsprintf(&src, "[%s] %s/%s", data->datastoreName,
data->directoryName, fileName) < 0) {
virReportOOMError();
return NULL;
} }
}
/* FIXME: Check if referenced path/file really exists */ cleanup:
esxVI_String_Free(&propertyNameList);
esxVI_ObjectContent_Free(&datastoreList);
esxVI_DatastoreHostMount_Free(&hostMount);
VIR_FREE(strippedFileName);
VIR_FREE(copyOfFileName);
return src; return datastorePath;
}
} }
/*
* This function does the inverse of esxParseVMXFileName. It takes an file name
* in datastore path format and converts it to a file name that can be used in
* a .vmx file.
*
* The datastore path format and the formats found in a .vmx file are described
* in the documentation of esxParseVMXFileName.
*
* Firstly parse the datastore path. Then use the datastore name to lookup the
* datastore and it's mount path. Finally concatenate the mount path, directory
* and file name to an absolute path and return it. Detect the seperator type
* based on the mount path.
*/
static char * static char *
esxFormatVMXFileName(const char *src, void *opaque ATTRIBUTE_UNUSED) esxFormatVMXFileName(const char *datastorePath, void *opaque)
{ {
bool success = false; bool success = false;
esxVMX_Data *data = opaque;
char *datastoreName = NULL; char *datastoreName = NULL;
char *directoryName = NULL; char *directoryName = NULL;
char *fileName = NULL; char *fileName = NULL;
esxVI_ObjectContent *datastore = NULL;
esxVI_DatastoreHostMount *hostMount = NULL;
char separator = '/';
virBuffer buffer = VIR_BUFFER_INITIALIZER;
char *tmp;
int length;
char *absolutePath = NULL; char *absolutePath = NULL;
if (STRPREFIX(src, "[")) { /* Parse datastore path and lookup datastore */
/* Found potential datastore path */ if (esxUtil_ParseDatastorePath(datastorePath, &datastoreName,
if (esxUtil_ParseDatastorePath(src, &datastoreName, &directoryName, &directoryName, &fileName) < 0) {
&fileName) < 0) { goto cleanup;
goto cleanup; }
}
if (directoryName == NULL) { if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s", NULL, &datastore,
datastoreName, fileName) < 0) { esxVI_Occurrence_RequiredItem) < 0 ||
virReportOOMError(); esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
goto cleanup; &hostMount) < 0) {
} goto cleanup;
} else { }
if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s/%s",
datastoreName, directoryName, fileName) < 0) { /* Detect separator type */
virReportOOMError(); if (strchr(hostMount->mountInfo->path, '\\') != NULL) {
goto cleanup; separator = '\\';
}
/* Strip trailing separators */
length = strlen(hostMount->mountInfo->path);
while (length > 0 && hostMount->mountInfo->path[length - 1] == separator) {
--length;
}
/* Format as <mount>[/<directory>]/<file> */
virBufferAdd(&buffer, hostMount->mountInfo->path, length);
if (directoryName != NULL) {
/* Convert / to \ when necessary */
if (separator != '/') {
tmp = directoryName;
while (*tmp != '\0') {
if (*tmp == '/') {
*tmp = separator;
}
++tmp;
} }
} }
} else if (STRPREFIX(src, "/")) {
/* Found absolute path */
absolutePath = strdup(src);
if (absolutePath == NULL) { virBufferAddChar(&buffer, separator);
virReportOOMError(); virBufferAdd(&buffer, directoryName, -1);
goto cleanup; }
}
} else { virBufferAddChar(&buffer, separator);
/* Found relative path, this is not supported */ virBufferAdd(&buffer, fileName, -1);
ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
_("Found relative path '%s' in domain XML, this is not " if (virBufferError(&buffer)) {
"supported"), src); virReportOOMError();
goto cleanup; goto cleanup;
} }
absolutePath = virBufferContentAndReset(&buffer);
/* FIXME: Check if referenced path/file really exists */ /* FIXME: Check if referenced path/file really exists */
success = true; success = true;
cleanup: cleanup:
if (! success) { if (! success) {
virBufferFreeAndReset(&buffer);
VIR_FREE(absolutePath); VIR_FREE(absolutePath);
} }
VIR_FREE(datastoreName); VIR_FREE(datastoreName);
VIR_FREE(directoryName); VIR_FREE(directoryName);
VIR_FREE(fileName); VIR_FREE(fileName);
esxVI_ObjectContent_Free(&datastore);
esxVI_DatastoreHostMount_Free(&hostMount);
return absolutePath; return absolutePath;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册