[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [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/<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.
---
 src/esx/esx_driver.c |  372 +++++++++++++++++++++++++++++++------------------
 1 files changed, 235 insertions(+), 137 deletions(-)

diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index b0ef704..322c588 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -24,8 +24,6 @@
 
 #include <config.h>
 
-#include <netdb.h>
-
 #include "internal.h"
 #include "domain_conf.h"
 #include "authhelper.h"
@@ -60,190 +58,290 @@ struct _esxVMX_Data {
 
 
 
-static char *
-esxAbsolutePathToDatastorePath(esxVI_Context *ctx, const char *absolutePath)
-{
-    bool success = false;
-    char *copyOfAbsolutePath = NULL;
-    char *tmp = NULL;
-    char *saveptr = NULL;
-    esxVI_String *propertyNameList = NULL;
-    esxVI_ObjectContent *datastore = NULL;
-
-    char *datastorePath = NULL;
-    char *preliminaryDatastoreName = NULL;
-    char *directoryAndFileName = NULL;
-    char *datastoreName = NULL;
-
-    if (esxVI_String_DeepCopyValue(&copyOfAbsolutePath, absolutePath) < 0) {
-        return NULL;
-    }
-
-    /* Expected format: '/vmfs/volumes/<datastore>/<path>' */
-    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/<datastore>/<path>'"), absolutePath);
-        goto cleanup;
-    }
-
-    if (esxVI_String_AppendValueToList(&propertyNameList,
-                                       "summary.name") < 0 ||
-        esxVI_LookupDatastoreByAbsolutePath(ctx, absolutePath,
-                                            propertyNameList, &datastore,
-                                            esxVI_Occurrence_OptionalItem) < 0) {
-        goto cleanup;
-    }
-
-    if (datastore == NULL) {
-        if (esxVI_LookupDatastoreByName(ctx, preliminaryDatastoreName,
-                                        propertyNameList, &datastore,
-                                        esxVI_Occurrence_OptionalItem) < 0) {
-            goto cleanup;
-        }
-    }
-
-    if (datastore != NULL) {
-        if (esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
-                                 esxVI_Occurrence_RequiredItem)) {
-            goto cleanup;
-        }
-    }
-
-    if (datastoreName == NULL) {
-        VIR_WARN("Could not retrieve datastore name for absolute "
-                 "path '%s', falling back to preliminary name '%s'",
-                 absolutePath, preliminaryDatastoreName);
-
-        datastoreName = preliminaryDatastoreName;
-    }
-
-    if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
-                    directoryAndFileName) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    /* FIXME: Check if referenced path/file really exists */
-
-    success = true;
-
-  cleanup:
-    if (! success) {
-        VIR_FREE(datastorePath);
-    }
-
-    VIR_FREE(copyOfAbsolutePath);
-    esxVI_String_Free(&propertyNameList);
-    esxVI_ObjectContent_Free(&datastore);
-
-    return datastorePath;
-}
-
-
-
+/*
+ * 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 *
 esxParseVMXFileName(const char *fileName, void *opaque)
 {
-    char *src = NULL;
+    char *datastorePath = NULL;
     esxVMX_Data *data = opaque;
-
-    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 (src == NULL) {
-            virReportOOMError();
-            return NULL;
-        }
-
-        /* 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,
+    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;
+
+    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;
         }
 
-        /* FIXME: Check if referenced path/file really exists */
+        /* Search for datastore by mount path */
+        for (datastore = datastoreList; datastore != NULL;
+             datastore = datastore->_next) {
+            esxVI_DatastoreHostMount_Free(&hostMount);
+            datastoreName = NULL;
 
-        return src;
+            if (esxVI_LookupDatastoreHostMount(data->ctx, datastore->obj,
+                                               &hostMount) < 0 ||
+                esxVI_GetStringValue(datastore, "summary.name", &datastoreName,
+                                     esxVI_Occurrence_RequiredItem) < 0) {
+                goto cleanup;
+            }
+
+            tmp = (char *)STRSKIP(fileName, hostMount->mountInfo->path);
+
+            if (tmp == NULL) {
+                continue;
+            }
+
+            /* Found a match. Strip leading separators */
+            while (*tmp == '/' || *tmp == '\\') {
+                ++tmp;
+            }
+
+            if (esxVI_String_DeepCopyValue(&strippedFileName, tmp) < 0) {
+                goto cleanup;
+            }
+
+            tmp = strippedFileName;
+
+            /* Convert \ to / */
+            while (*tmp != '\0') {
+                if (*tmp == '\\') {
+                    *tmp = '/';
+                }
+
+                ++tmp;
+            }
+
+            if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
+                            strippedFileName) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+
+            break;
+        }
+
+        /* 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);
+
+            if (esxVI_LookupDatastoreByName(data->ctx, datastoreName,
+                                            NULL, &datastoreList,
+                                            esxVI_Occurrence_OptionalItem) < 0) {
+                goto cleanup;
+            }
+
+            if (datastoreList == NULL) {
+                ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
+                          _("File name '%s' refers to non-existing datastore '%s'"),
+                          fileName, datastoreName);
+                goto cleanup;
+            }
+
+            if (virAsprintf(&datastorePath, "[%s] %s", datastoreName,
+                            directoryAndFileName) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+        }
+
+        if (datastorePath == NULL) {
+            ESX_ERROR(VIR_ERR_INTERNAL_ERROR,
+                      _("Could not find datastore for '%s'"), fileName);
+            goto cleanup;
+        }
     }
+
+  cleanup:
+    esxVI_String_Free(&propertyNameList);
+    esxVI_ObjectContent_Free(&datastoreList);
+    esxVI_DatastoreHostMount_Free(&hostMount);
+    VIR_FREE(strippedFileName);
+    VIR_FREE(copyOfFileName);
+
+    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 (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);
+
+    if (length > 0) {
+        tmp = hostMount->mountInfo->path + length - 1;
+
+        while (*tmp == separator && tmp > hostMount->mountInfo->path) {
+            --tmp;
         }
 
-        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;
+        length = tmp - hostMount->mountInfo->path;
+    }
+
+    /* 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) {
-            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;
 }
-- 
1.7.0.4


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]