Index: /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66671)
+++ /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66672)
@@ -68,4 +68,7 @@
 
 
+/** The number of entire in a chain part. */
+#define RTFSFATCHAINPART_ENTRIES (256U - 4U)
+
 /**
  * A part of the cluster chain covering up to 252 clusters.
@@ -76,9 +79,10 @@
     RTLISTNODE  ListEntry;
     /** Chain entries. */
-    uint32_t    aEntries[256 - 4];
+    uint32_t    aEntries[RTFSFATCHAINPART_ENTRIES];
 } RTFSFATCHAINPART;
 AssertCompile(sizeof(RTFSFATCHAINPART) <= _1K);
 typedef RTFSFATCHAINPART *PRTFSFATCHAINPART;
 typedef RTFSFATCHAINPART const *PCRTFSFATCHAINPART;
+
 
 /**
@@ -467,5 +471,5 @@
 {
     PRTFSFATCHAINPART pPart;
-    uint32_t idxLast = pChain->cClusters % RT_ELEMENTS(pPart->aEntries);
+    uint32_t idxLast = pChain->cClusters % RTFSFATCHAINPART_ENTRIES;
     if (idxLast != 0)
         pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
@@ -500,7 +504,7 @@
     {
         PRTFSFATCHAINPART pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
-        while (idxCluster >= RT_ELEMENTS(pPart->aEntries))
+        while (idxCluster >= RTFSFATCHAINPART_ENTRIES)
         {
-            idxCluster -= RT_ELEMENTS(pPart->aEntries);
+            idxCluster -= RTFSFATCHAINPART_ENTRIES;
             pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
         }
@@ -529,5 +533,5 @@
     for (;;)
     {
-        uint32_t const cInPart = RT_MIN(cLeft, RT_ELEMENTS(pPart->aEntries));
+        uint32_t const cInPart = RT_MIN(cLeft, RTFSFATCHAINPART_ENTRIES);
         for (uint32_t iPart = 0; iPart < cInPart; iPart++)
             if (pPart->aEntries[iPart] == idxNext)
@@ -543,4 +547,69 @@
 
 
+/**
+ * Gets a cluster array index.
+ *
+ * This works the chain thing as an indexed array.
+ *
+ * @returns The cluster number, UINT32_MAX if out of bounds.
+ * @param   pChain              The chain.
+ * @param   idx                 The index.
+ */
+static uint32_t rtFsFatChain_GetClusterByIndex(PCRTFSFATCHAIN pChain, uint32_t idx)
+{
+    if (idx < pChain->cClusters)
+    {
+        /*
+         * In the first part?
+         */
+        PRTFSFATCHAINPART pPart;
+        if (idx < RTFSFATCHAINPART_ENTRIES)
+        {
+            pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
+            return pPart->aEntries[idx];
+        }
+
+        /*
+         * In the last part?
+         */
+        uint32_t cParts    = (pChain->cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
+        uint32_t idxPart   = idx / RTFSFATCHAINPART_ENTRIES;
+        uint32_t idxInPart = idx % RTFSFATCHAINPART_ENTRIES;
+        if (idxPart + 1 == cParts)
+            pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
+        else
+        {
+            /*
+             * No, do linear search from the start, skipping the first part.
+             */
+            pPart = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
+            while (idxPart-- > 1)
+                pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
+        }
+
+        return pPart->aEntries[idxInPart];
+    }
+    return UINT32_MAX;
+}
+
+#ifdef RT_STRICT
+/**
+ * Assert chain consistency.
+ */
+static bool rtFsFatChain_AssertValid(PCRTFSFATCHAIN pChain)
+{
+    bool                fRc = true;
+    uint32_t            cParts = 0;
+    PRTFSFATCHAINPART   pPart;
+    RTListForEach(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry)
+        cParts++;
+
+    uint32_t cExpected = (pChain->cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
+    AssertMsgStmt(cExpected == cParts, ("cExpected=%#x cParts=%#x\n", cExpected, cParts), fRc = false);
+    AssertMsgStmt(pChain->cbChain == (pChain->cClusters << pChain->cClusterByteShift),
+                  ("cExpected=%#x cParts=%#x\n", cExpected, cParts), fRc = false);
+    return fRc;
+}
+#endif /* RT_STRICT */
 
 /**
@@ -957,5 +1026,6 @@
 {
     AssertReturn(idxCluster >= FAT_FIRST_DATA_CLUSTER, VERR_VFS_BOGUS_OFFSET);
-    AssertReturn(idxCluster < pThis->cClusters, VERR_VFS_BOGUS_OFFSET);
+    AssertMsgReturn(idxCluster < pThis->cClusters, ("idxCluster=%#x cClusters=%#x\n", idxCluster, pThis->cClusters),
+                    VERR_VFS_BOGUS_OFFSET);
     switch (pThis->enmFatType)
     {
@@ -1230,5 +1300,6 @@
 static int rtFsFatObj_SetSize(PRTFSFATOBJ pObj, uint32_t cbFile)
 {
-    AssertReturn((pObj->cbObject >> pObj->Clusters.cClusterByteShift) == pObj->Clusters.cClusters, VERR_INTERNAL_ERROR_3);
+    AssertReturn(   ((pObj->cbObject + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift)
+                 == pObj->Clusters.cClusters, VERR_INTERNAL_ERROR_3);
 
     /*
@@ -1242,5 +1313,5 @@
      * or release clusters.
      */
-    int rc;
+    int rc = VINF_SUCCESS;
     uint32_t const cClustersNew = (cbFile + pObj->Clusters.cbCluster - 1) >> pObj->Clusters.cClusterByteShift;
     AssertReturn(pObj->pParentDir, VERR_INTERNAL_ERROR_2);
@@ -1248,5 +1319,4 @@
     {
         pObj->cbObject = cbFile;
-        rc = VINF_SUCCESS;
     }
     else if (pObj->cbObject < cbFile)
@@ -1257,5 +1327,6 @@
     {
         /* Free clusters we don't need any more. */
-        rc = rtFsFatClusterMap_SetEndOfChain(pObj->pVol, rtFsFatChain_GetCluster(&pObj->Clusters, cClustersNew - 1));
+        if (cClustersNew > 0)
+            rc = rtFsFatClusterMap_SetEndOfChain(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, cClustersNew - 1));
         if (RT_SUCCESS(rc))
         {
@@ -1263,10 +1334,17 @@
             while (iClusterToFree < pObj->Clusters.cClusters && RT_SUCCESS(rc))
             {
-                rc = rtFsFatClusterMap_FreeCluster(pObj->pVol, rtFsFatChain_GetCluster(&pObj->Clusters, iClusterToFree));
+                rc = rtFsFatClusterMap_FreeCluster(pObj->pVol, rtFsFatChain_GetClusterByIndex(&pObj->Clusters, iClusterToFree));
                 iClusterToFree++;
             }
 
             /* Update the chain structure. */
+            uint32_t cOldParts = (pObj->Clusters.cClusters + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
+            uint32_t cNewParts = (cClustersNew             + RTFSFATCHAINPART_ENTRIES - 1) / RTFSFATCHAINPART_ENTRIES;
+            Assert(cOldParts >= cNewParts);
+            while (cOldParts-- > cNewParts)
+                RTMemFree(RTListRemoveLast(&pObj->Clusters.ListParts, RTFSFATCHAINPART, ListEntry));
             pObj->Clusters.cClusters = cClustersNew;
+            pObj->Clusters.cbChain   = pObj->Clusters.cClusters << pObj->Clusters.cClusterByteShift;
+            Assert(rtFsFatChain_AssertValid(&pObj->Clusters));
         }
     }
@@ -1282,4 +1360,12 @@
         {
             pDirEntry->cbFile = cbFile;
+            /** @todo figure out if setting the cluster to 0 is the right way to deal
+             *        with empty files... */
+            if (cClustersNew == 0)
+            {
+                pDirEntry->idxCluster = 0;
+                if (pObj->pVol->enmFatType >= RTFSFATTYPE_FAT32)
+                    pDirEntry->u.idxClusterHigh = 0;
+            }
             rc = rtFsFatDir_PutEntryAfterUpdate(pObj->pParentDir, pDirEntry, uWriteLock);
         }
@@ -2604,5 +2690,5 @@
     uint32_t const offJump      = 2 + pBootSector->abJmp[1];
     uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
-    Assert(offFirstZero >= RT_OFFSETOF(FATBOOTSECTOR, Bpb));
+    Assert(offFirstZero >= RT_UOFFSETOF(FATBOOTSECTOR, Bpb));
     uint32_t const cbZeroPad    = RT_MIN(offJump - offFirstZero,
                                          sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
