Index: /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp	(revision 32881)
+++ /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp	(revision 32882)
@@ -314,4 +314,8 @@
     /** Starting sector of the decompressed grain buffer. */
     uint32_t    uGrainSector;
+    /** Size of compressed grain buffer for streamOptimized extents. */
+    size_t      cbCompGrain;
+    /** Compressed grain buffer for streamOptimized extents, with marker. */
+    void        *pvCompGrain;
     /** Decompressed grain buffer for streamOptimized extents. */
     void        *pvGrain;
@@ -474,12 +478,10 @@
     /* Image this operation relates to. */
     PVMDKIMAGE pImage;
-    /* File where the data is stored. */
-    PVMDKFILE pFile;
-    /* Total size of the data to read. */
-    size_t cbSize;
-    /* Offset in the file to read. */
-    uint64_t uFileOffset;
     /* Current read position. */
     ssize_t iOffset;
+    /* Size of the compressed grain buffer (available data). */
+    size_t cbCompGrain;
+    /* Pointer to the compressed grain buffer. */
+    void *pvCompGrain;
 } VMDKINFLATESTATE;
 
@@ -489,10 +491,10 @@
     /* Image this operation relates to. */
     PVMDKIMAGE pImage;
-    /* File where the data is to be stored. */
-    PVMDKFILE pFile;
-    /* Offset in the file to write at. */
-    uint64_t uFileOffset;
     /* Current write position. */
     ssize_t iOffset;
+    /* Size of the compressed grain buffer. */
+    size_t cbCompGrain;
+    /* Pointer to the compressed grain buffer. */
+    void *pvCompGrain;
 } VMDKDEFLATESTATE;
 
@@ -851,4 +853,5 @@
 {
     VMDKINFLATESTATE *pInflateState = (VMDKINFLATESTATE *)pvUser;
+    size_t cbInjected = 0;
 
     Assert(cbBuf);
@@ -856,19 +859,22 @@
     {
         *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
+        pvBuf = (uint8_t *)pvBuf + 1;
+        cbBuf--;
+        cbInjected = 1;
+        pInflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
+    }
+    if (!cbBuf)
+    {
         if (pcbBuf)
-            *pcbBuf = 1;
-        pInflateState->iOffset = 0;
+            *pcbBuf = cbInjected;
         return VINF_SUCCESS;
     }
-    cbBuf = RT_MIN(cbBuf, pInflateState->cbSize);
-    int rc = vmdkFileReadSync(pInflateState->pImage, pInflateState->pFile,
-                              pInflateState->uFileOffset, pvBuf, cbBuf, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    pInflateState->uFileOffset += cbBuf;
+    cbBuf = RT_MIN(cbBuf, pInflateState->cbCompGrain - pInflateState->iOffset);
+    memcpy(pvBuf,
+           (uint8_t *)pInflateState->pvCompGrain + pInflateState->iOffset,
+           cbBuf);
     pInflateState->iOffset += cbBuf;
-    pInflateState->cbSize -= cbBuf;
     Assert(pcbBuf);
-    *pcbBuf = cbBuf;
+    *pcbBuf = cbBuf + cbInjected;
     return VINF_SUCCESS;
 }
@@ -878,10 +884,10 @@
  * distinguishing between async and normal operation
  */
-DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
                                     uint64_t uOffset, void *pvBuf,
-                                    size_t cbToRead, unsigned uMarker,
-                                    uint64_t *puLBA, uint32_t *pcbMarkerData)
-{
-    if (pVmdkFile->fAsyncIO)
+                                    size_t cbToRead, uint64_t *puLBA,
+                                    uint32_t *pcbMarkerData)
+{
+    if (pExtent->pFile->fAsyncIO)
     {
         AssertMsgFailed(("TODO\n"));
@@ -892,46 +898,45 @@
         int rc;
         PRTZIPDECOMP pZip = NULL;
-        VMDKMARKER Marker;
-        uint64_t uCompOffset, cbComp;
-        VMDKINFLATESTATE InflateState;
-        size_t cbActuallyRead;
-        size_t cbMarker = sizeof(Marker);
-
-        if (uMarker == VMDK_MARKER_IGNORE)
-            cbMarker -= sizeof(Marker.uType);
-        rc = vmdkFileReadSync(pImage, pVmdkFile, uOffset, &Marker, cbMarker, NULL);
+        VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+        size_t cbCompSize, cbActuallyRead;
+
+        rc = vmdkFileReadSync(pImage, pExtent->pFile, uOffset, pMarker,
+                              RT_OFFSETOF(VMDKMARKER, uType), NULL);
         if (RT_FAILURE(rc))
             return rc;
-        Marker.uSector = RT_LE2H_U64(Marker.uSector);
-        Marker.cbSize = RT_LE2H_U32(Marker.cbSize);
-        if (    uMarker != VMDK_MARKER_IGNORE
-            &&  (   RT_LE2H_U32(Marker.uType) != uMarker
-                 || Marker.cbSize != 0))
+        cbCompSize = RT_LE2H_U32(pMarker->cbSize);
+        if (cbCompSize == 0)
+        {
+            AssertMsgFailed(("VMDK: corrupted marker\n"));
             return VERR_VD_VMDK_INVALID_FORMAT;
-        if (Marker.cbSize != 0)
-        {
-            /* Compressed grain marker. Data follows immediately. */
-            uCompOffset = uOffset + 12;
-            cbComp = Marker.cbSize;
-            if (puLBA)
-                *puLBA = Marker.uSector;
-            if (pcbMarkerData)
-                *pcbMarkerData = cbComp + 12;
-        }
-        else
-        {
-            Marker.uType = RT_LE2H_U32(Marker.uType);
-            AssertMsgFailed(("VMDK: unexpected marker type %u\n", Marker.uType));
+        }
+
+        /* Sanity check - the expansion ratio should be much less than 2. */
+        Assert(cbCompSize < 2 * cbToRead);
+        if (cbCompSize >= 2 * cbToRead)
             return VERR_VD_VMDK_INVALID_FORMAT;
-        }
+
+        /* Compressed grain marker. Data follows immediately. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              uOffset + RT_OFFSETOF(VMDKMARKER, uType),
+                                (uint8_t *)pExtent->pvCompGrain
+                              + RT_OFFSETOF(VMDKMARKER, uType),
+                                RT_ALIGN_Z(  cbCompSize
+                                           + RT_OFFSETOF(VMDKMARKER, uType),
+                                           512)
+                              - RT_OFFSETOF(VMDKMARKER, uType), NULL);
+
+        if (puLBA)
+            *puLBA = RT_LE2H_U64(pMarker->uSector);
+        if (pcbMarkerData)
+            *pcbMarkerData = RT_ALIGN(  cbCompSize
+                                      + RT_OFFSETOF(VMDKMARKER, uType),
+                                      512);
+
+        VMDKINFLATESTATE InflateState;
         InflateState.pImage = pImage;
-        InflateState.pFile = pVmdkFile;
-        InflateState.cbSize = cbComp;
-        InflateState.uFileOffset = uCompOffset;
         InflateState.iOffset = -1;
-        /* Sanity check - the expansion ratio should be much less than 2. */
-        Assert(cbComp < 2 * cbToRead);
-        if (cbComp >= 2 * cbToRead)
-            return VERR_VD_VMDK_INVALID_FORMAT;
+        InflateState.cbCompGrain = cbCompSize + RT_OFFSETOF(VMDKMARKER, uType);
+        InflateState.pvCompGrain = pExtent->pvCompGrain;
 
         rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
@@ -957,13 +962,12 @@
         pvBuf = (const uint8_t *)pvBuf + 1;
         cbBuf--;
-        pDeflateState->iOffset = 0;
+        pDeflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
     }
     if (!cbBuf)
         return VINF_SUCCESS;
-    int rc = vmdkFileWriteSync(pDeflateState->pImage, pDeflateState->pFile,
-                               pDeflateState->uFileOffset, pvBuf, cbBuf, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    pDeflateState->uFileOffset += cbBuf;
+    if (pDeflateState->iOffset + cbBuf > pDeflateState->cbCompGrain)
+        return VERR_BUFFER_OVERFLOW;
+    memcpy((uint8_t *)pDeflateState->pvCompGrain + pDeflateState->iOffset,
+           pvBuf, cbBuf);
     pDeflateState->iOffset += cbBuf;
     return VINF_SUCCESS;
@@ -974,10 +978,10 @@
  * distinguishing between async and normal operation
  */
-DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
                                     uint64_t uOffset, const void *pvBuf,
-                                    size_t cbToWrite, unsigned uMarker,
-                                    uint64_t uLBA, uint32_t *pcbMarkerData)
-{
-    if (pVmdkFile->fAsyncIO)
+                                    size_t cbToWrite, uint64_t uLBA,
+                                    uint32_t *pcbMarkerData)
+{
+    if (pExtent->pFile->fAsyncIO)
     {
         AssertMsgFailed(("TODO\n"));
@@ -988,30 +992,15 @@
         int rc;
         PRTZIPCOMP pZip = NULL;
-        VMDKMARKER Marker;
-        uint64_t uCompOffset, cbDecomp;
         VMDKDEFLATESTATE DeflateState;
 
-        Marker.uSector = RT_H2LE_U64(uLBA);
-        Marker.cbSize = RT_H2LE_U32((uint32_t)cbToWrite);
-        if (uMarker == VMDK_MARKER_IGNORE)
-        {
-            /* Compressed grain marker. Data follows immediately. */
-            uCompOffset = uOffset + 12;
-            cbDecomp = cbToWrite;
-        }
-        else
-        {
-            /* The other markers don't contain compressed data. */
-            return VERR_NOT_IMPLEMENTED;
-        }
         DeflateState.pImage = pImage;
-        DeflateState.pFile = pVmdkFile;
-        DeflateState.uFileOffset = uCompOffset;
         DeflateState.iOffset = -1;
+        DeflateState.cbCompGrain = pExtent->cbCompGrain;
+        DeflateState.pvCompGrain = pExtent->pvCompGrain;
 
         rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper, RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
         if (RT_FAILURE(rc))
             return rc;
-        rc = RTZipCompress(pZip, pvBuf, cbDecomp);
+        rc = RTZipCompress(pZip, pvBuf, cbToWrite);
         if (RT_SUCCESS(rc))
             rc = RTZipCompFinish(pZip);
@@ -1019,37 +1008,42 @@
         if (RT_SUCCESS(rc))
         {
+            Assert(   DeflateState.iOffset > 0
+                   && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
+
+            /* pad with zeroes to get to a full sector size */
+            uint32_t uSize = DeflateState.iOffset;
+            if (uSize % 512)
+            {
+                uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
+                memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
+                       uSizeAlign - uSize);
+                uSize = uSizeAlign;
+            }
+
             if (pcbMarkerData)
-                *pcbMarkerData = 12 + DeflateState.iOffset;
+                *pcbMarkerData = uSize;
+
+            /* Compressed grain marker. Data follows immediately. */
+            VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+            pMarker->uSector = RT_H2LE_U64(uLBA);
+            pMarker->cbSize = RT_H2LE_U32(  DeflateState.iOffset
+                                          - RT_OFFSETOF(VMDKMARKER, uType));
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOffset, pMarker,
+                                   uSize, NULL);
+            if (RT_FAILURE(rc))
+                return rc;
+
             /* Set the file size to remove old garbage in case the block is
              * rewritten. Cannot cause data loss as the code calling this
-             * guarantees that data gets only appended. */
-            Assert(DeflateState.uFileOffset > uCompOffset);
-
-            /*
-             * Change the file size only if the size really changed,
-             * because this is very expensive on some filesystems
-             * like XFS.
-             */
+             * guarantees that data gets only appended. Change the file size
+             * only if the size really changed, because this is very expensive
+             * on some filesystems such as XFS. */
             uint64_t cbOld;
-            rc = vmdkFileGetSize(pImage, pVmdkFile, &cbOld);
+            rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbOld);
             if (RT_FAILURE(rc))
                 return rc;
 
-            if (cbOld != DeflateState.uFileOffset)
-                rc = vmdkFileSetSize(pImage, pVmdkFile, DeflateState.uFileOffset);
-
-            if (uMarker == VMDK_MARKER_IGNORE)
-            {
-                /* Compressed grain marker. */
-                Marker.cbSize = RT_H2LE_U32(DeflateState.iOffset);
-                rc = vmdkFileWriteSync(pImage, pVmdkFile, uOffset, &Marker, 12, NULL);
-                if (RT_FAILURE(rc))
-                    return rc;
-            }
-            else
-            {
-                /* The other markers don't contain compressed data. */
-                return VERR_NOT_IMPLEMENTED;
-            }
+            if (cbOld != uOffset + uSize)
+                rc = vmdkFileSetSize(pImage, pExtent->pFile, uOffset + uSize);
         }
         return rc;
@@ -1349,5 +1343,17 @@
         RTMemTmpFree(pTmpGT);
 
-        /* streamOptimized extents need a grain decompress buffer. */
+        /* streamOptimized extents need a compressed grain buffer, which must
+         * be big enough to hold uncompressible data (which needs ~8 bytes
+         * more than the uncompressed data), the marker and padding. */
+        pExtent->cbCompGrain = RT_ALIGN_Z(  VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
+                                          + 8 + sizeof(VMDKMARKER), 512);
+        pExtent->pvCompGrain = RTMemAlloc(pExtent->cbCompGrain);
+        if (!pExtent->pvCompGrain)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        /* streamOptimized extents need a decompressed grain buffer. */
         pExtent->pvGrain = RTMemAlloc(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
         if (!pExtent->pvGrain)
@@ -1361,6 +1367,9 @@
             uint64_t uLBA = 0;
             uint32_t cbMarker = 0;
-            rc = vmdkFileInflateSync(pImage, pExtent->pFile, VMDK_SECTOR2BYTE(uLastGrainSector),
-                                     pExtent->pvGrain, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), VMDK_MARKER_IGNORE, &uLBA, &cbMarker);
+            rc = vmdkFileInflateSync(pImage, pExtent,
+                                     VMDK_SECTOR2BYTE(uLastGrainSector),
+                                     pExtent->pvGrain,
+                                     VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                     &uLBA, &cbMarker);
             if (RT_FAILURE(rc))
                 goto out;
@@ -1368,5 +1377,5 @@
             Assert(uLBA == uLastGrainWritten * pExtent->cSectorsPerGrain);
             pExtent->uGrainSector = uLastGrainSector;
-            pExtent->cbLastGrainWritten = RT_ALIGN(cbMarker, 512);
+            pExtent->cbLastGrainWritten = cbMarker;
         }
         pExtent->uLastGrainWritten = uLastGrainWritten;
@@ -1425,7 +1434,5 @@
             cbOverhead = RT_ALIGN_64(cbOverhead,
                                      VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
-            if (pExtent->fFooter)
-                rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead);
-            else
+            if (!pExtent->fFooter)
                 rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead + 512);
         }
@@ -1499,4 +1506,16 @@
     }
     pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);
+
+    /* streamOptimized extents need a compressed grain buffer, which must
+     * be big enough to hold uncompressible data (which needs ~8 bytes
+     * more than the uncompressed data), the marker and padding. */
+    pExtent->cbCompGrain = RT_ALIGN_Z(  VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
+                                      + 8 + sizeof(VMDKMARKER), 512);
+    pExtent->pvCompGrain = RTMemAlloc(pExtent->cbCompGrain);
+    if (!pExtent->pvCompGrain)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
 
     /* streamOptimized extents need a grain decompress buffer. */
@@ -4500,4 +4519,8 @@
     }
 
+    /* Skip over the overhead area. */
+    rc = vmdkFileSetSize(pImage, pExtent->pFile,
+                         VMDK_SECTOR2BYTE(pExtent->cOverheadSectors));
+
     if (pfnProgress)
         pfnProgress(pvUser, uPercentStart + uPercentSpan * 70 / 100);
@@ -5236,6 +5259,6 @@
         Assert(cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
         uint32_t cbGrain = 0;
-        rc = vmdkFileDeflateSync(pImage, pExtent->pFile, cbExtentSize,
-                                 pvBuf, cbWrite, VMDK_MARKER_IGNORE, uSector, &cbGrain);
+        rc = vmdkFileDeflateSync(pImage, pExtent, cbExtentSize,
+                                 pvBuf, cbWrite, uSector, &cbGrain);
         if (RT_FAILURE(rc))
         {
@@ -5245,5 +5268,4 @@
             return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
         }
-        cbGrain = RT_ALIGN(cbGrain, 512);
         pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(cbExtentSize);
         pExtent->uLastGrainWritten = uSector / pExtent->cSectorsPerGrain;
@@ -5954,7 +5976,7 @@
         pData = pExtent->pvGrain;
     }
-    rc = vmdkFileDeflateSync(pImage, pExtent->pFile, uFileOffset,
-                             pData, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
-                             VMDK_MARKER_IGNORE, uSector, &cbGrain);
+    rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
+                             VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                             uSector, &cbGrain);
     if (RT_FAILURE(rc))
     {
@@ -5964,5 +5986,4 @@
         return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
     }
-    cbGrain = RT_ALIGN(cbGrain, 512);
     pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(uFileOffset);
     pExtent->uLastGrainWritten = uGrain;
@@ -6703,6 +6724,9 @@
                     if (pExtent->uGrainSector != uSectorExtentAbs)
                     {
-                        rc = vmdkFileInflateSync(pImage, pExtent->pFile, VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                                 pExtent->pvGrain, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), VMDK_MARKER_IGNORE, &uLBA, NULL);
+                        rc = vmdkFileInflateSync(pImage, pExtent,
+                                                 VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                                 pExtent->pvGrain,
+                                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                                 &uLBA, NULL);
                         if (RT_FAILURE(rc))
                         {
@@ -6844,6 +6868,9 @@
                         ||  pExtent->uGrainSector != pExtent->uLastGrainSector)
                     {
-                        rc = vmdkFileInflateSync(pImage, pExtent->pFile, VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                                 pExtent->pvGrain, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), VMDK_MARKER_IGNORE, &uLBA, NULL);
+                        rc = vmdkFileInflateSync(pImage, pExtent,
+                                                 VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                                 pExtent->pvGrain,
+                                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                                 &uLBA, NULL);
                         if (RT_FAILURE(rc))
                         {
@@ -6859,8 +6886,9 @@
                     memcpy((uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), pvBuf, cbToWrite);
                     uint32_t cbGrain = 0;
-                    rc = vmdkFileDeflateSync(pImage, pExtent->pFile,
+                    rc = vmdkFileDeflateSync(pImage, pExtent,
                                              VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                             pExtent->pvGrain, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
-                                             VMDK_MARKER_IGNORE, uLBA, &cbGrain);
+                                             pExtent->pvGrain,
+                                             VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                             uLBA, &cbGrain);
                     if (RT_FAILURE(rc))
                     {
@@ -6870,5 +6898,4 @@
                         return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
                     }
-                    cbGrain = RT_ALIGN(cbGrain, 512);
                     pExtent->uLastGrainSector = uSectorExtentAbs;
                     pExtent->uLastGrainWritten = uSectorExtentRel / pExtent->cSectorsPerGrain;
