Index: /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66669)
+++ /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66670)
@@ -137,4 +137,8 @@
     /** The current file offset. */
     uint32_t        offFile;
+    /** Set if we've maybe dirtied the FAT. */
+    bool            fMaybeDirtyFat;
+    /** Set if we've maybe dirtied the directory entry. */
+    bool            fMaybeDirtyDirEnt;
 } RTFSFATFILE;
 typedef RTFSFATFILE *PRTFSFATFILE;
@@ -413,4 +417,5 @@
 static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
 static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
+static int  rtFsFatDir_Flush(PRTFSFATDIR pThis);
 static int  rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir,
                            uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir);
@@ -484,6 +489,7 @@
  * @param   pChain              The chain.
  * @param   offFile             The file offset.
- */
-static uint64_t rtFsFatChain_FileOffsetToDiskOff(PCRTFSFATCHAIN pChain, uint32_t offFile)
+ * @param   pVol                The volume.
+ */
+static uint64_t rtFsFatChain_FileOffsetToDiskOff(PCRTFSFATCHAIN pChain, uint32_t offFile, PCRTFSFATVOL pVol)
 {
     uint32_t idxCluster = offFile >> pChain->cClusterByteShift;
@@ -496,5 +502,7 @@
             pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
         }
-        return pPart->aEntries[idxCluster] + (offFile & ~(pChain->cbCluster - 1));
+        return pVol->offFirstCluster
+             + ((uint64_t)(pPart->aEntries[idxCluster] - FAT_FIRST_DATA_CLUSTER) << pChain->cClusterByteShift)
+             + (offFile & (pChain->cbCluster - 1));
     }
     return UINT64_MAX;
@@ -737,4 +745,5 @@
 
 
+#if 0 /* unused */
 /**
  * Flushes out all dirty lines in the file allocation table (cluster map) cache.
@@ -750,4 +759,5 @@
     return rtFsFatClusterMap_FlushWorker(pThis, iEntry, iEntry);
 }
+#endif
 
 
@@ -802,5 +812,4 @@
     {
         /* Validate the cluster, checking for end of file. */
-RTAssertMsg2("idxCluster=%#x cClusters=%#x\n", idxCluster, pVol->cClusters);
         if (   idxCluster >= pVol->cClusters
             || idxCluster <  FAT_FIRST_DATA_CLUSTER)
@@ -818,5 +827,5 @@
         /* Next cluster. */
         bool     fOdd   = idxCluster & 1;
-        uint32_t offFat = idxCluster * 2 / 3;
+        uint32_t offFat = idxCluster * 3 / 2;
         idxCluster = RT_MAKE_U16(pbFat[offFat], pbFat[offFat + 1]);
         if (fOdd)
@@ -1043,6 +1052,64 @@
 {
     PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
-    RT_NOREF(pThis, off, pSgBuf, fBlocking, pcbRead);
-    return VERR_NOT_IMPLEMENTED;
+    AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
+    RT_NOREF(fBlocking);
+
+    /*
+     * Check for EOF.
+     */
+    if (off == -1)
+        off = pThis->offFile;
+    if ((uint64_t)off >= pThis->Core.cbObject)
+    {
+        if (pcbRead)
+        {
+            *pcbRead = 0;
+            return VINF_EOF;
+        }
+        return VERR_EOF;
+    }
+
+    /*
+     * Do the reading cluster by cluster (converge clusters later).
+     */
+    int      rc         = VINF_SUCCESS;
+    uint32_t cbFileLeft = pThis->Core.cbObject - (uint32_t)off;
+    uint32_t cbRead     = 0;
+    uint32_t cbLeft     = pSgBuf->paSegs[0].cbSeg;
+    uint8_t *pbDst      = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
+    while (cbLeft > 0)
+    {
+        if (cbFileLeft > 0)
+        {
+            uint64_t offDisk = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, (uint32_t)off, pThis->Core.pVol);
+            if (offDisk != UINT64_MAX)
+            {
+                uint32_t cbToRead = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1));
+                if (cbToRead > cbLeft)
+                    cbToRead = cbLeft;
+                if (cbToRead > cbFileLeft)
+                    cbToRead = cbFileLeft;
+                rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, offDisk, pbDst, cbToRead, NULL);
+                if (RT_SUCCESS(rc))
+                {
+                    off         += cbToRead;
+                    pbDst       += cbToRead;
+                    cbRead      += cbToRead;
+                    cbFileLeft  -= cbToRead;
+                    cbLeft      -= cbToRead;
+                    continue;
+                }
+            }
+            else
+                rc = VERR_VFS_BOGUS_OFFSET;
+        }
+        else
+            rc = pcbRead ? VINF_EOF : VERR_EOF;
+        break;
+    }
+    pThis->offFile = off;
+    if (pcbRead)
+        *pcbRead = cbRead;
+    return VINF_SUCCESS;
 }
 
@@ -1065,6 +1132,18 @@
 {
     PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
-    RT_NOREF(pThis);
-    return VERR_NOT_IMPLEMENTED;
+    int rc = VINF_SUCCESS;
+    if (pThis->fMaybeDirtyFat)
+        rc = rtFsFatClusterMap_Flush(pThis->Core.pVol);
+    if (pThis->fMaybeDirtyDirEnt)
+    {
+        int rc2 = rtFsFatDir_Flush(pThis->Core.pParentDir);
+        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    int rc2 = RTVfsFileFlush(pThis->Core.pVol->hVfsBacking);
+    if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+        rc = rc2;
+    return rc;
 }
 
@@ -1215,5 +1294,5 @@
         },
         RTVFSIOSTREAMOPS_VERSION,
-        0,
+        RTVFSIOSTREAMOPS_FEAT_NO_SG,
         rtFsFatFile_Read,
         rtFsFatFile_Write,
@@ -1268,5 +1347,7 @@
          */
         rtFsFatObj_InitFromDirEntry(&pNewFile->Core, pDirEntry, offEntryInDir, pThis);
-        pNewFile->offFile = 0;
+        pNewFile->offFile           = 0;
+        pNewFile->fMaybeDirtyFat    = false;
+        pNewFile->fMaybeDirtyDirEnt = false;
         rc = rtFsFatClusterMap_ReadClusterChain(pThis, RTFSFAT_GET_CLUSTER(pDirEntry, pThis), &pNewFile->Core.Clusters);
         if (RT_SUCCESS(rc))
@@ -1393,5 +1474,6 @@
                     off                      =  offEntryInDir &  (pVol->cbSector - 1);
                     pThis->u.Simple.offInDir = (offEntryInDir & ~(pVol->cbSector - 1));
-                    pThis->offEntriesOnDisk  = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, pThis->u.Simple.offInDir);
+                    pThis->offEntriesOnDisk  = rtFsFatChain_FileOffsetToDiskOff(&pThis->Core.Clusters, pThis->u.Simple.offInDir,
+                                                                                pThis->Core.pVol);
                     rc = RTVfsFileReadAt(pThis->Core.pVol->hVfsBacking, pThis->offEntriesOnDisk,
                                          pThis->paEntries, pVol->cbSector, NULL);
@@ -1515,5 +1597,5 @@
                     pszName8Dot3[offDst] = '\0';
 
-                    if (pszName8Dot3[0] == FATDIRENTRY_CH0_DELETED)
+                    if ((uint8_t)pszName8Dot3[0] == FATDIRENTRY_CH0_DELETED)
                         pszName8Dot3[0] = FATDIRENTRY_CH0_ESC_E5;
                     return true;
@@ -1575,5 +1657,5 @@
         for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
         {
-            switch (paEntries[iEntry].Entry.achName[0])
+            switch ((uint8_t)paEntries[iEntry].Entry.achName[0])
             {
                 default:
