Index: /trunk/include/iprt/formats/fat.h
===================================================================
--- /trunk/include/iprt/formats/fat.h	(revision 66668)
+++ /trunk/include/iprt/formats/fat.h	(revision 66669)
@@ -506,4 +506,8 @@
 #define FAT_LAST_FAT16_DATA_CLUSTER     UINT32_C(0x0000fff5)    /**< The last possible data cluster for FAT16. */
 #define FAT_LAST_FAT32_DATA_CLUSTER     UINT32_C(0x0ffffff5)    /**< The last possible data cluster for FAT32. */
+
+#define FAT_FIRST_FAT12_EOC             UINT32_C(0x00000ff8)    /**< The first end-of-file-cluster number for FAT12. */
+#define FAT_FIRST_FAT16_EOC             UINT32_C(0x0000fff8)    /**< The first end-of-file-cluster number for FAT16. */
+#define FAT_FIRST_FAT32_EOC             UINT32_C(0x0ffffff8)    /**< The first end-of-file-cluster number for FAT32. */
 /** @} */
 
@@ -521,11 +525,11 @@
     /** NT case flags (FATDIRENTRY_CASE_F_XXX). */
     uint8_t         fCase;
-    /** Birth milliseconds. */
+    /** Birth milliseconds (DOS 7.0+ w/VFAT). */
     uint8_t         uBirthCentiseconds;
-    /** Birth time. */
+    /** Birth time (DOS 7.0+ w/VFAT). */
     uint16_t        uBirthTime;
-    /** Birth date. */
+    /** Birth date (DOS 7.0+ w/VFAT). */
     uint16_t        uBirthDate;
-    /** Access date. */
+    /** Access date (DOS 7.0+ w/ACCDATA in Config.sys). */
     uint16_t        uAccessDate;
     union
@@ -536,6 +540,8 @@
         uint16_t    idxEAs;
     } u;
-    /** Modify time. */
+    /** Modify time (PC-DOS 1.1+, MS-DOS 1.20+).  */
     uint16_t        uModifyTime;
+    /** Modify date. */
+    uint16_t        uModifyDate;
     /** The data cluster index. */
     uint16_t        idxCluster;
@@ -544,4 +550,15 @@
 } FATDIRENTRY;
 AssertCompileSize(FATDIRENTRY, 0x20);
+AssertCompileMemberOffset(FATDIRENTRY, fAttrib, 0x0b);
+AssertCompileMemberOffset(FATDIRENTRY, fCase, 0x0c);
+AssertCompileMemberOffset(FATDIRENTRY, uBirthCentiseconds, 0x0d);
+AssertCompileMemberOffset(FATDIRENTRY, uBirthTime, 0x0e);
+AssertCompileMemberOffset(FATDIRENTRY, uBirthDate, 0x10);
+AssertCompileMemberOffset(FATDIRENTRY, uAccessDate, 0x12);
+AssertCompileMemberOffset(FATDIRENTRY, u, 0x14);
+AssertCompileMemberOffset(FATDIRENTRY, uModifyTime, 0x16);
+AssertCompileMemberOffset(FATDIRENTRY, uModifyDate, 0x18);
+AssertCompileMemberOffset(FATDIRENTRY, idxCluster, 0x1a);
+AssertCompileMemberOffset(FATDIRENTRY, cbFile, 0x1c);
 /** Pointer to a FAT directory entry. */
 typedef FATDIRENTRY *PFATDIRENTRY;
Index: /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66668)
+++ /trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp	(revision 66669)
@@ -291,4 +291,12 @@
     /** Byte offset of the bootsector relative to the start of the file. */
     uint64_t        offBootSector;
+    /** The UTC offset in nanoseconds to use for this file system (FAT traditionally
+     * stores timestamps in local time).
+     * @remarks This may need improving later. */
+    int64_t         offNanoUTC;
+    /** The UTC offset in minutes to use for this file system (FAT traditionally
+     * stores timestamps in local time).
+     * @remarks This may need improving later. */
+    int32_t         offMinUTC;
     /** Set if read-only mode. */
     bool            fReadOnly;
@@ -366,4 +374,6 @@
 /** Pointer to a FAT volume (VFS instance data). */
 typedef RTFSFATVOL *PRTFSFATVOL;
+/** Pointer to a const FAT volume (VFS instance data). */
+typedef RTFSFATVOL const *PCRTFSFATVOL;
 
 
@@ -402,4 +412,5 @@
 *********************************************************************************************************************************/
 static void rtFsFatDir_AddOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
+static void rtFsFatDir_RemoveOpenChild(PRTFSFATDIR pDir, PRTFSFATOBJ pChild);
 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);
@@ -435,4 +446,31 @@
     pChain->cClusters           = 0;
     RTListInit(&pChain->ListParts);
+}
+
+
+/**
+ * Appends a cluster to a cluster chain.
+ *
+ * @returns IPRT status code.
+ * @param   pChain              The chain.
+ * @param   idxCluster          The cluster to append.
+ */
+static int rtFsFatChain_Append(PRTFSFATCHAIN pChain, uint32_t idxCluster)
+{
+    PRTFSFATCHAINPART pPart;
+    uint32_t idxLast = pChain->cClusters % RT_ELEMENTS(pPart->aEntries);
+    if (idxLast != 0)
+        pPart = RTListGetLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
+    else
+    {
+        pPart = (PRTFSFATCHAINPART)RTMemAllocZ(sizeof(*pPart));
+        if (!pPart)
+            return VERR_NO_MEMORY;
+        RTListAppend(&pChain->ListParts, &pPart->ListEntry);
+    }
+    pPart->aEntries[idxLast] = idxCluster;
+    pChain->cClusters++;
+    pChain->cbChain += pChain->cbCluster;
+    return VINF_SUCCESS;
 }
 
@@ -749,4 +787,68 @@
 
 /**
+ * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT12.
+ */
+static int rtFsFatClusterMap_Fat12_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
+                                                    PRTFSFATCHAIN pChain)
+{
+    /* ASSUME that for FAT12 we cache the whole FAT in a single entry.  That
+       way we don't need to deal with entries in different sectors and whatnot.  */
+    AssertReturn(pFatCache->cEntries == 1, VERR_INTERNAL_ERROR_4);
+    AssertReturn(pFatCache->cbEntry == pVol->cbFat, VERR_INTERNAL_ERROR_4);
+    AssertReturn(pFatCache->aEntries[0].offFat == 0, VERR_INTERNAL_ERROR_4);
+
+    uint8_t const *pbFat = pFatCache->aEntries[0].pbData;
+    for (;;)
+    {
+        /* 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)
+        {
+            if (idxCluster >= FAT_FIRST_FAT12_EOC)
+                return VINF_SUCCESS;
+            return VERR_VFS_BOGUS_OFFSET;
+        }
+
+        /* Add cluster to chain.  */
+        int rc = rtFsFatChain_Append(pChain, idxCluster);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        /* Next cluster. */
+        bool     fOdd   = idxCluster & 1;
+        uint32_t offFat = idxCluster * 2 / 3;
+        idxCluster = RT_MAKE_U16(pbFat[offFat], pbFat[offFat + 1]);
+        if (fOdd)
+            idxCluster >>= 4;
+        else
+            idxCluster &= 0x0fff;
+    }
+}
+
+
+/**
+ * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT16.
+ */
+static int rtFsFatClusterMap_Fat16_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
+                                                    PRTFSFATCHAIN pChain)
+{
+    RT_NOREF(pFatCache, pVol, idxCluster, pChain);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Worker for rtFsFatClusterMap_ReadClusterChain handling FAT32.
+ */
+static int rtFsFatClusterMap_Fat32_ReadClusterChain(PRTFSFATCLUSTERMAPCACHE pFatCache, PRTFSFATVOL pVol, uint32_t idxCluster,
+                                                    PRTFSFATCHAIN pChain)
+{
+    RT_NOREF(pFatCache, pVol, idxCluster, pChain);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
  * Reads a cluster chain into memory
  *
@@ -764,25 +866,39 @@
     pChain->cbChain             = 0;
     RTListInit(&pChain->ListParts);
-
-    if (   idxFirstCluster >= pThis->cClusters
-        && idxFirstCluster >= FAT_FIRST_DATA_CLUSTER)
-        return VERR_VFS_BOGUS_OFFSET;
-
-    return VERR_NOT_IMPLEMENTED;
-}
-
-
-static void rtFsFatDosDateTimeToSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime, uint8_t cCentiseconds)
+    switch (pThis->enmFatType)
+    {
+        case RTFSFATTYPE_FAT12: return rtFsFatClusterMap_Fat12_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
+        case RTFSFATTYPE_FAT16: return rtFsFatClusterMap_Fat16_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
+        case RTFSFATTYPE_FAT32: return rtFsFatClusterMap_Fat32_ReadClusterChain(pThis->pFatCache, pThis, idxFirstCluster, pChain);
+        default:
+            AssertFailedReturn(VERR_INTERNAL_ERROR_2);
+    }
+}
+
+
+/**
+ * Converts a FAT timestamp into an IPRT timesspec.
+ *
+ * @param   pTimeSpec       Where to return the IRPT time.
+ * @param   uDate           The date part of the FAT timestamp.
+ * @param   uTime           The time part of the FAT timestamp.
+ * @param   cCentiseconds   Centiseconds part if applicable (0 otherwise).
+ * @param   pVol            The volume.
+ */
+static void rtFsFatDosDateTimeToSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime,
+                                     uint8_t cCentiseconds, PCRTFSFATVOL pVol)
 {
     RTTIME Time;
-    Time.offUTC     = 0;
-    Time.i32Year    = 1980 + (uDate >> 9);
-    Time.u8Month    = ((uDate >> 5) & 0xf);
-    Time.u8WeekDay  = UINT8_MAX;
-    Time.u16YearDay = UINT16_MAX;
-    Time.u8MonthDay = RT_MAX(uDate & 0x1f, 1);
-    Time.u8Hour     = uTime >> 11;
-    Time.u8Minute   = (uTime >> 5) & 0x3f;
-    Time.u8Second   = (uTime & 0x1f) << 1;
+    Time.fFlags         = RTTIME_FLAGS_TYPE_UTC;
+    Time.offUTC         = 0;
+    Time.i32Year        = 1980 + (uDate >> 9);
+    Time.u8Month        = RT_MAX((uDate >> 5) & 0xf, 1);
+    Time.u8MonthDay     = RT_MAX(uDate & 0x1f, 1);
+    Time.u8WeekDay      = UINT8_MAX;
+    Time.u16YearDay     = 0;
+    Time.u8Hour         = uTime >> 11;
+    Time.u8Minute       = (uTime >> 5) & 0x3f;
+    Time.u8Second       = (uTime & 0x1f) << 1;
+    Time.u32Nanosecond  = 0;
     if (cCentiseconds > 0 && cCentiseconds < 200) /* screw complicated stuff for now. */
     {
@@ -796,27 +912,50 @@
 
     RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
-}
-
-
-static void rtFsFatObj_InitFromDirEntry(PRTFSFATOBJ pObj, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir, PRTFSFATVOL pThis)
+    RTTimeSpecAddNano(pTimeSpec, pVol->offNanoUTC);
+}
+
+
+/**
+ * Initialization of a RTFSFATOBJ structure from a FAT directory entry.
+ *
+ * @note    The RTFSFATOBJ::pParentDir and RTFSFATOBJ::Clusters members are
+ *          properly initialized elsewhere.
+ *
+ * @param   pObj            The structure to initialize.
+ * @param   pParentDir      The parent directory.
+ * @param   offEntryInDir   The offset in the parent directory.
+ * @param   pVol            The volume.
+ */
+static void rtFsFatObj_InitFromDirEntry(PRTFSFATOBJ pObj, PCFATDIRENTRY pDirEntry, uint32_t offEntryInDir, PRTFSFATVOL pVol)
 {
     RTListInit(&pObj->Entry);
     pObj->pParentDir    = NULL;
-    pObj->pVol          = pThis;
+    pObj->pVol          = pVol;
     pObj->offEntryInDir = offEntryInDir;
     pObj->fAttrib       = ((RTFMODE)pDirEntry->fAttrib << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
     pObj->cbObject      = pDirEntry->cbFile;
-    rtFsFatDosDateTimeToSpec(&pObj->ModificationTime, pDirEntry->uAccessDate, pDirEntry->uModifyTime, 0);
-    rtFsFatDosDateTimeToSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds);
-    rtFsFatDosDateTimeToSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0);
-}
-
-
-static void rtFsFatObj_InitDummy(PRTFSFATOBJ pObj, uint32_t offEntryInDir, uint32_t cbObject, RTFMODE fAttrib, PRTFSFATVOL pThis)
+    rtFsFatDosDateTimeToSpec(&pObj->ModificationTime, pDirEntry->uModifyDate, pDirEntry->uModifyTime, 0, pVol);
+    rtFsFatDosDateTimeToSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds, pVol);
+    rtFsFatDosDateTimeToSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0, pVol);
+}
+
+
+/**
+ * Dummy initialization of a RTFSFATOBJ structure.
+ *
+ * @note    The RTFSFATOBJ::pParentDir and RTFSFATOBJ::Clusters members are
+ *          properly initialized elsewhere.
+ *
+ * @param   pObj            The structure to initialize.
+ * @param   cbObject        The object size.
+ * @param   fAttrib         The attributes.
+ * @param   pVol            The volume.
+ */
+static void rtFsFatObj_InitDummy(PRTFSFATOBJ pObj, uint32_t cbObject, RTFMODE fAttrib, PRTFSFATVOL pVol)
 {
     RTListInit(&pObj->Entry);
     pObj->pParentDir    = NULL;
-    pObj->pVol          = pThis;
-    pObj->offEntryInDir = offEntryInDir;
+    pObj->pVol          = pVol;
+    pObj->offEntryInDir = UINT32_MAX;
     pObj->fAttrib       = fAttrib;
     pObj->cbObject      = cbObject;
@@ -828,4 +967,18 @@
 
 /**
+ * Worker for rtFsFatFile_Close and rtFsFatDir_Close that does common work.
+ *
+ * @returns IPRT status code.
+ * @param   pObj            The common object structure.
+ */
+static int rtFsFatObj_Close(PRTFSFATOBJ pObj)
+{
+    if (pObj->pParentDir)
+        rtFsFatDir_RemoveOpenChild(pObj->pParentDir, pObj);
+    return VINF_SUCCESS;
+}
+
+
+/**
  * @interface_method_impl{RTVFSOBJOPS,pfnClose}
  */
@@ -833,6 +986,5 @@
 {
     PRTFSFATFILE pThis = (PRTFSFATFILE)pvThis;
-    RT_NOREF(pThis);
-    return VERR_NOT_IMPLEMENTED;
+    return rtFsFatObj_Close(&pThis->Core);
 }
 
@@ -1469,12 +1621,16 @@
 {
     PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
+    int rc;
     if (pThis->paEntries)
     {
-        /** @todo flush */
-
+        rc = rtFsFatDir_Flush(pThis);
         RTMemFree(pThis->paEntries);
         pThis->paEntries = NULL;
     }
-    return VINF_SUCCESS;
+    else
+        rc = VINF_SUCCESS;
+
+    rtFsFatObj_Close(&pThis->Core);
+    return rc;
 }
 
@@ -1794,5 +1950,5 @@
             rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offEntryInDir, pThis);
         else
-            rtFsFatObj_InitDummy(&pNewDir->Core, offEntryInDir, cbDir, RTFS_DOS_DIRECTORY, pThis);
+            rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_DOS_DIRECTORY, pThis);
 
         pNewDir->hVfsSelf           = *phVfsDir;
@@ -2543,4 +2699,6 @@
     pThis->cbBacking            = 0;
     pThis->offBootSector        = offBootSector;
+    pThis->offNanoUTC           = RTTimeLocalDeltaNano();
+    pThis->offMinUTC            = pThis->offNanoUTC / RT_NS_1MIN;
     pThis->fReadOnly            = fReadOnly;
     pThis->cReservedSectors     = 1;
