From 145d6cb05c45f4c0b5b5cee50edff50e518505f1 Mon Sep 17 00:00:00 2001 From: Matthias Bolte Date: Sat, 7 Aug 2010 19:58:29 +0200 Subject: [PATCH] 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// 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// as fixed format. --- src/esx/esx_driver.c | 348 +++++++++++++++++++++++++++---------------- 1 file changed, 220 insertions(+), 128 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index b0ef70407e..4fb357bf74 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -24,8 +24,6 @@ #include -#include - #include "internal.h" #include "domain_conf.h" #include "authhelper.h" @@ -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/: + * + * /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 * -esxAbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath) +esxParseVMXFileName(const char *fileName, void *opaque) { - bool success = false; - char *copyOfAbsolutePath = NULL; - char *tmp = NULL; - char *saveptr = NULL; + char *datastorePath = NULL; + esxVMX_Data *data = opaque; esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *datastoreList = 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; - char *preliminaryDatastoreName = NULL; - char *directoryAndFileName = NULL; - char *datastoreName = NULL; - - if (esxVI_String_DeepCopyValue(©OfAbsolutePath, absolutePath) < 0) { - return NULL; - } + if (strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) { + /* Plain file name, use same directory as for the .vmx file */ + if (virAsprintf(&datastorePath, "[%s] %s/%s", data->datastoreName, + data->directoryName, fileName) < 0) { + virReportOOMError(); + goto cleanup; + } + } else { + if (esxVI_String_AppendValueToList(&propertyNameList, + "summary.name") < 0 || + esxVI_LookupDatastoreList(data->ctx, propertyNameList, + &datastoreList) < 0) { + return NULL; + } - /* Expected format: '/vmfs/volumes//' */ - if ((tmp = STRSKIP(copyOfAbsolutePath, "/vmfs/volumes/")) == NULL || - (preliminaryDatastoreName = strtok_r(tmp, "/", &saveptr)) == NULL || - (directoryAndFileName = strtok_r(NULL, "", &saveptr)) == NULL) { - ESX_ERROR(VIR_ERR_INTERNAL_ERROR, - _("Absolute path '%s' doesn't have expected format " - "'/vmfs/volumes//'"), absolutePath); - goto cleanup; - } + /* Search for datastore by mount path */ + for (datastore = datastoreList; datastore != NULL; + datastore = datastore->_next) { + esxVI_DatastoreHostMount_Free(&hostMount); + datastoreName = NULL; - if (esxVI_String_AppendValueToList(&propertyNameList, - "summary.name") < 0 || - esxVI_LookupDatastoreByAbsolutePath(ctx, absolutePath, - propertyNameList, &datastore, - esxVI_Occurrence_OptionalItem) < 0) { - goto cleanup; - } + if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj, + &hostMount) < 0 || + esxVI_GetStringValue(datastore, "summary.name", &datastoreName, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } - if (datastore == NULL) { - if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName, - propertyNameList, &datastore, - esxVI_Occurrence_OptionalItem) < 0) { - goto cleanup; - } - } + tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path); - if (datastore != NULL) { - if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName, - esxVI_Occurrence_RequiredItem)) { - goto cleanup; - } - } + if (tmp == NULL) { + continue; + } - if (datastoreName == NULL) { - VIR_WARN("Could not retrieve datastore name for absolute " - "path '%s', falling back to preliminary name '%s'", - absolutePath, preliminaryDatastoreName); + /* Found a match. Strip leading separators */ + while (*tmp == '/' || *tmp == '\\') { + ++tmp; + } - datastoreName = preliminaryDatastoreName; - } + if (esxVI_String_DeepCopyValue(&strippedFileName, tmp) < 0) { + goto cleanup; + } - if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, - directoryAndFileName) < 0) { - virReportOOMError(); - goto cleanup; - } + tmp = strippedFileName; - /* FIXME: Check if referenced path/file really exists */ + /* Convert \ to / */ + while (*tmp != '\0') { + if (*tmp == '\\') { + *tmp = '/'; + } - success = true; + ++tmp; + } - cleanup: - if (! success) { - VIR_FREE(datastorePath); - } + if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, + strippedFileName) < 0) { + virReportOOMError(); + goto cleanup; + } - VIR_FREE(copyOfAbsolutePath); - esxVI_String_Free(&propertyNameList); - esxVI_ObjectContent_Free(&datastore); + break; + } - return datastorePath; -} + /* Fallback to direct datastore name match */ + if (datastorePath == NULL && STRPREFIX(fileName, "/vmfs/volumes/")) { + if (esxVI_String_DeepCopyValue(©OfFileName, fileName) < 0) { + goto cleanup; + } + /* Expected format: '/vmfs/volumes//' */ + 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//'"), fileName); + goto cleanup; + } + esxVI_ObjectContent_Free(&datastoreList); -static char * -esxParseVMXFileName(const char *fileName, void *opaque) -{ - char *src = NULL; - esxVMX_Data *data = opaque; + if (esxVI_LookupDatastoreByName(data->ctx, datastoreName, + NULL, &datastoreList, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } - if (STRPREFIX(fileName, "/vmfs/volumes/")) { - /* Found absolute path referencing a file inside a datastore */ - return esxAbsolutePathToDatastorePath(data->ctx, fileName); - } else if (STRPREFIX(fileName, "/")) { - /* Found absolute path referencing a file outside a datastore */ - src = strdup(fileName); + if (datastoreList == NULL) { + ESX_ERROR(VIR_ERR_INTERNAL_ERROR, + _("File name '%s' refers to non-existing datastore '%s'"), + fileName, datastoreName); + goto cleanup; + } - if (src == NULL) { - virReportOOMError(); - return NULL; + if (virAsprintf(&datastorePath, "[%s] %s", datastoreName, + directoryAndFileName) < 0) { + virReportOOMError(); + goto cleanup; + } } - /* FIXME: Check if referenced path/file really exists */ - - return src; - } else if (strchr(fileName, '/') != NULL) { - /* 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; + if (datastorePath == NULL) { + ESX_ERROR(VIR_ERR_INTERNAL_ERROR, + _("Could not find datastore for '%s'"), fileName); + goto cleanup; } + } - /* 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 * -esxFormatVMXFileName(const char *src, void *opaque ATTRIBUTE_UNUSED) +esxFormatVMXFileName(const char *datastorePath, void *opaque) { bool success = false; + esxVMX_Data *data = opaque; char *datastoreName = NULL; char *directoryName = 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; - if (STRPREFIX(src, "[")) { - /* Found potential datastore path */ - if (esxUtil_ParseDatastorePath(src, &datastoreName, &directoryName, - &fileName) < 0) { - goto cleanup; - } + /* Parse datastore path and lookup datastore */ + if (esxUtil_ParseDatastorePath(datastorePath, &datastoreName, + &directoryName, &fileName) < 0) { + goto cleanup; + } - if (directoryName == NULL) { - if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s", - datastoreName, fileName) < 0) { - virReportOOMError(); - goto cleanup; - } - } else { - if (virAsprintf(&absolutePath, "/vmfs/volumes/%s/%s/%s", - datastoreName, directoryName, fileName) < 0) { - virReportOOMError(); - goto cleanup; + if (esxVI_LookupDatastoreByName(data->ctx, datastoreName, + NULL, &datastore, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj, + &hostMount) < 0) { + goto cleanup; + } + + /* Detect separator type */ + if (strchr(hostMount->mountInfo->path, '\\') != NULL) { + separator = '\\'; + } + + /* Strip trailing separators */ + length = strlen(hostMount->mountInfo->path); + + while (length > 0 && hostMount->mountInfo->path[length - 1] == separator) { + --length; + } + + /* Format as [/]/ */ + 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) { - virReportOOMError(); - goto cleanup; - } - } else { - /* Found relative path, this is not supported */ - ESX_ERROR(VIR_ERR_INTERNAL_ERROR, - _("Found relative path '%s' in domain XML, this is not " - "supported"), src); + virBufferAddChar(&buffer, separator); + virBufferAdd(&buffer, directoryName, -1); + } + + virBufferAddChar(&buffer, separator); + virBufferAdd(&buffer, fileName, -1); + + if (virBufferError(&buffer)) { + virReportOOMError(); goto cleanup; } + absolutePath = virBufferContentAndReset(&buffer); + /* FIXME: Check if referenced path/file really exists */ success = true; cleanup: if (! success) { + virBufferFreeAndReset(&buffer); VIR_FREE(absolutePath); } VIR_FREE(datastoreName); VIR_FREE(directoryName); VIR_FREE(fileName); + esxVI_ObjectContent_Free(&datastore); + esxVI_DatastoreHostMount_Free(&hostMount); return absolutePath; } -- GitLab