Index: /trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp	(revision 2563)
+++ /trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp	(revision 2564)
@@ -217,4 +217,5 @@
     int rc;
     size_t cbThisRead;
+    PVBOXHDDIMAGEDESC pCurrImage;
 
     /* Loop until all read. */
@@ -226,7 +227,7 @@
         cbThisRead = cbRead;
         rc = VINF_VDI_BLOCK_FREE;
-        for (; pImage != NULL && rc == VINF_VDI_BLOCK_FREE; pImage = pImage->pPrev)
+        for (pCurrImage = pImage; pCurrImage != NULL && rc == VINF_VDI_BLOCK_FREE; pCurrImage = pCurrImage->pPrev)
         {
-            rc = pDisk->Backend->pfnRead(pImage->pvBackendData, uOffset, pvBuf, cbThisRead, &cbThisRead);
+            rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData, uOffset, pvBuf, cbThisRead, &cbThisRead);
         }
 
@@ -428,4 +429,6 @@
             rc = VERR_VDI_INVALID_TYPE;
 
+        /** @todo optionally check UUIDs */
+
         if (VBOX_SUCCESS(rc))
         {
@@ -531,5 +534,50 @@
                                PFNVMPROGRESS pfnProgress, void *pvUser)
 {
-    return VERR_NOT_IMPLEMENTED;
+    int rc = VINF_SUCCESS;
+    LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__, pszFilename, uOpenFlags));
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    /* Check arguments. */
+    if (    !pszFilename
+        ||  *pszFilename == '\0'
+        ||  (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
+        ||  !cbSize
+        ||  (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
+    {
+        AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /* Check state. */
+    if (pDisk->cImages != 0)
+    {
+        AssertMsgFailed(("Create base image cannot be done with other images open\n"));
+        return VERR_VDI_INVALID_STATE;
+    }
+
+    /* Set up image descriptor. */
+    PVBOXHDDIMAGEDESC pImage = (PVBOXHDDIMAGEDESC)RTMemAllocZ(sizeof(VBOXHDDIMAGEDESC));
+    if (!pImage)
+        return VERR_NO_MEMORY;
+    pImage->pszFilename = RTStrDup(pszFilename);
+    if (!pImage->pszFilename)
+        rc = VERR_NO_MEMORY;
+
+    if (VBOX_SUCCESS(rc))
+        rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
+                                       uImageFlags, pszComment, uOpenFlags,
+                                       pfnProgress, pvUser,
+                                       pDisk->pfnError, pDisk->pvErrorUser,
+                                       &pImage->pvBackendData);
+
+    if (VBOX_FAILURE(rc))
+    {
+        RTStrFree(pImage->pszFilename);
+        RTMemFree(pImage);
+    }
+
+    return rc;
 }
 
Index: /trunk/src/VBox/Devices/Storage/VBoxHDD-newInternal.h
===================================================================
--- /trunk/src/VBox/Devices/Storage/VBoxHDD-newInternal.h	(revision 2563)
+++ /trunk/src/VBox/Devices/Storage/VBoxHDD-newInternal.h	(revision 2564)
@@ -45,4 +45,23 @@
 
     /**
+     * Create a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file to create. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     * @param   penmType        Image type. Both base and diff image types are valid.
+     * @param   cbSize          Image size in bytes.
+     * @param   uImageFlags     Flags specifying special image features.
+     * @param   pszComment      Pointer to image comment. NULL is ok.
+     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+     * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
+     * @param   pvUser          User argument for the progress callback.
+     * @param   pfnError        Callback for setting extended error information.
+     * @param   pvErrorUser     Opaque parameter for pfnError.
+     * @param   ppvBackendData  Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCreate, (const char *pszFilename, VDIMAGETYPE penmType, uint64_t cbSize, unsigned uImageFlags, const char *pszComment, unsigned uOpenFlags, PFNVMPROGRESS pfnProgress, void *pvUser, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData));
+
+    /**
      * Close a disk image.
      *
@@ -107,7 +126,7 @@
      * @returns VBox status code.
      * @param   pvBackendData   Opaque state data for this image.
-     * @param   penmImageType   Image type of this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetImageType, (void *pvBackendData, PVDIMAGETYPE penmImageType));
+     * @param   penmType        Image type of this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetImageType, (void *pvBackendData, PVDIMAGETYPE penmType));
 
     /**
Index: /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp	(revision 2563)
+++ /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp	(revision 2564)
@@ -1965,4 +1965,14 @@
 }
 
+static int vmdkCreate(const char *pszFilename, VDIMAGETYPE penmType,
+                      uint64_t cbSize, unsigned uImageFlags,
+                      const char *pszComment, unsigned uOpenFlags,
+                      PFNVMPROGRESS pfnProgress, void *pvUser,
+                      PFNVDERROR pfnError, void *pvErrorUser,
+                      void **ppvBackendData)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
 static int vmdkClose(void *pBackendData)
 {
@@ -1983,6 +1993,6 @@
     PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
     PVMDKEXTENT pExtent;
-    uint64_t uSectorInExtent;
-    uint64_t uSectorOffset;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
     int rc;
 
@@ -1997,5 +2007,5 @@
 
     rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorInExtent);
+                        &pExtent, &uSectorExtentRel);
     if (VBOX_FAILURE(rc))
         goto out;
@@ -2009,5 +2019,5 @@
 
     /* Clip read range to remain in this extent. */
-    cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
+    cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
 
     /* Handle the read according to the current extent type. */
@@ -2018,19 +2028,20 @@
         case VMDKETYPE_ESX_SPARSE:
 #endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorInExtent,
-                               &uSectorOffset);
+            rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
+                               &uSectorExtentAbs);
             if (VBOX_FAILURE(rc))
                 goto out;
             /* Clip read range to at most the rest of the grain. */
-            cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - (uSectorOffset % pExtent->cSectorsPerGrain)));
-            if (uSectorOffset == 0)
+            cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            Assert(!(cbRead % 512));
+            if (uSectorExtentAbs == 0)
                 rc = VINF_VDI_BLOCK_FREE;
             else
                 rc = RTFileReadAt(pExtent->File,
-                                  VMDK_SECTOR2BYTE(uSectorOffset),
+                                  VMDK_SECTOR2BYTE(uSectorExtentAbs),
                                   pvBuf, cbRead, NULL);
             break;
         case VMDKETYPE_FLAT:
-            rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorInExtent),
+            rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel),
                               pvBuf, cbRead, NULL);
             break;
@@ -2049,6 +2060,6 @@
     PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
     PVMDKEXTENT pExtent;
-    uint64_t uSectorInExtent;
-    uint64_t uSectorOffset;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
     int rc;
 
@@ -2070,5 +2081,5 @@
 
     rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorInExtent);
+                        &pExtent, &uSectorExtentRel);
     if (VBOX_FAILURE(rc))
         goto out;
@@ -2080,4 +2091,10 @@
         goto out;
     }
+
+    /** @todo implement suppressing of zero data writes (a bit tricky in this
+     * case, as VMDK has no marker for zero blocks). We somehow need to get the
+     * information whether the information in this area is all zeroes as of the
+     * parent image. Then (based on the assumption that parent images are
+     * immutable) the write can be ignored. */
 
     /* Handle the write according to the current extent type. */
@@ -2088,11 +2105,11 @@
         case VMDKETYPE_ESX_SPARSE:
 #endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorInExtent,
-                               &uSectorOffset);
+            rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
+                               &uSectorExtentAbs);
             if (VBOX_FAILURE(rc))
                 goto out;
             /* Clip write range to at most the rest of the grain. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - (uSectorOffset % pExtent->cSectorsPerGrain)));
-            if (uSectorOffset == 0)
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            if (uSectorExtentAbs == 0)
             {
                 if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
@@ -2101,11 +2118,11 @@
                      * Allocate GT and find out where to store the grain. */
                     rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
-                                        uSectorInExtent, pvBuf, cbWrite);
+                                        uSectorExtentRel, pvBuf, cbWrite);
                 }
                 else
                 {
                     /* Clip write range to remain in this extent. */
-                    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
-                    *pcbPreRead = VMDK_SECTOR2BYTE(uSectorInExtent % pExtent->cSectorsPerGrain);
+                    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
+                    *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
                     *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
                     rc = VINF_VDI_BLOCK_FREE;
@@ -2114,15 +2131,15 @@
             else
                 rc = RTFileWriteAt(pExtent->File,
-                                   VMDK_SECTOR2BYTE(uSectorOffset),
+                                   VMDK_SECTOR2BYTE(uSectorExtentAbs),
                                    pvBuf, cbWrite, NULL);
             break;
         case VMDKETYPE_FLAT:
             /* Clip write range to remain in this extent. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
-            rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorInExtent), pvBuf, cbWrite, NULL);
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
+            rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel), pvBuf, cbWrite, NULL);
             break;
         case VMDKETYPE_ZERO:
             /* Clip write range to remain in this extent. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
             break;
     }
@@ -2448,4 +2465,6 @@
     /* pfnOpen */
     vmdkOpen,
+    /* pfnCreate */
+    vmdkCreate,
     /* pfnClose */
     vmdkClose,
