VirtualBox

Changeset 66615 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Apr 19, 2017 7:29:36 PM (8 years ago)
Author:
vboxsync
Message:

IPRT: More FAT fun (couldn't stop myself).

Location:
trunk/src/VBox/Runtime/common
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp

    r66602 r66615  
    3030*********************************************************************************************************************************/
    3131#include "internal/iprt.h"
    32 //#include <iprt/???.h>
     32#include <iprt/fs.h>
    3333
    3434#include <iprt/asm.h>
     
    3737#include <iprt/file.h>
    3838#include <iprt/err.h>
     39#include <iprt/mem.h>
    3940#include <iprt/poll.h>
    4041#include <iprt/string.h>
     42#include <iprt/sg.h>
    4143#include <iprt/thread.h>
    4244#include <iprt/vfs.h>
    4345#include <iprt/vfslowlevel.h>
     46#include <iprt/formats/fat.h>
    4447
    4548
     
    7982} RTFSFATCHAIN;
    8083typedef RTFSFATCHAIN *PRTFSFATCHAIN;
     84typedef RTFSFATCHAIN const *PCRTFSFATCHAIN;
     85
    8186
    8287/**
     
    8590typedef struct RTFSFATOBJ
    8691{
     92    /** The parent directory keeps a list of open objects (RTFSFATOBJ). */
     93    RTLISTNODE          Entry;
    8794    /** The byte offset of the directory entry.
    8895     *  This is set to UINT64_MAX if special FAT12/16 root directory. */
     
    103110    struct RTFSFATVOL  *pVol;
    104111} RTFSFATOBJ;
     112typedef RTFSFATOBJ *PRTFSFATOBJ;
    105113
    106114typedef struct RTFSFATFILE
     
    113121typedef RTFSFATFILE *PRTFSFATFILE;
    114122
     123
    115124/**
    116125 * FAT directory.
     126 *
     127 * We work directories in one of two buffering modes.  If there are few entries
     128 * or if it's the FAT12/16 root directory, we map the whole thing into memory.
     129 * If it's too large, we use an inefficient sector buffer for now.
     130 *
     131 * Directory entry updates happens exclusively via the directory, so any open
     132 * files or subdirs have a parent reference for doing that.  The parent OTOH,
     133 * keeps a list of open children.
    117134 */
    118135typedef struct RTFSFATDIR
    119136{
    120137    /** Core FAT object info.  */
    121     RTFSFATOBJ      Core;
     138    RTFSFATOBJ          Core;
     139    /** Open child objects (RTFSFATOBJ). */
     140    RTLISTNODE          OpenChildren;
     141
     142    /** Number of directory entries. */
     143    uint32_t            cEntries;
     144
     145    /** If fully buffered. */
     146    bool                fFullyBuffered;
     147    /** Set if this is a linear root directory. */
     148    bool                fIsFlatRootDir;
     149
     150    union
     151    {
     152        /** Data for the full buffered mode.
     153         * No need to messing around with clusters here, as we only uses this for
     154         * directories with a contiguous mapping on the disk.
     155         * So, if we grow a directory in a non-contiguous manner, we have to switch
     156         * to sector buffering on the fly. */
     157        struct
     158        {
     159            /** Directory offset. */
     160            uint64_t            offDir;
     161            /** Number of sectors mapped by paEntries and pbDirtySectors. */
     162            uint32_t            cSectors;
     163            /** Number of dirty sectors. */
     164            uint32_t            cDirtySectors;
     165            /** Pointer to the linear mapping of the directory entries. */
     166            PFATDIRENTRYUNION   paEntries;
     167            /** Dirty sector map. */
     168            uint8_t            *pbDirtySectors;
     169        } Full;
     170        /** The simple sector buffering.
     171         * This only works for clusters, so no FAT12/16 root directory fun. */
     172        struct
     173        {
     174            /** The disk offset of the current sector, UINT64_MAX if invalid. */
     175            uint64_t            offOnDisk;
     176            /** The directory offset, UINT32_MAX if invalid. */
     177            uint32_t            offInDir;
     178            uint32_t            u32Reserved; /**< Puts pbSector and paEntries at the same location */
     179            /** Sector buffer. */
     180            PFATDIRENTRYUNION  *paEntries;
     181            /** Dirty flag. */
     182            bool                fDirty;
     183        } Simple;
     184    } u;
    122185} RTFSFATDIR;
     186/** Pointer to a FAT directory instance. */
    123187typedef RTFSFATDIR *PRTFSFATDIR;
     188
     189
     190/**
     191 * File allocation table cache entry.
     192 */
     193typedef struct RTFSFATCLUSTERMAPENTRY
     194{
     195    /** The byte offset into the fat, UINT32_MAX if invalid entry. */
     196    uint32_t                offFat;
     197    /** Pointer to the data. */
     198    uint8_t                *pbData;
     199    /** Dirty bitmap.  Indexed by byte offset right shifted by
     200     * RTFSFATCLUSTERMAPCACHE::cDirtyShift. */
     201    uint64_t                bmDirty;
     202} RTFSFATCLUSTERMAPENTRY;
     203/** Pointer to a file allocation table cache entry.  */
     204typedef RTFSFATCLUSTERMAPENTRY *PRTFSFATCLUSTERMAPENTRY;
     205
     206/**
     207 * File allocation table cache.
     208 */
     209typedef struct RTFSFATCLUSTERMAPCACHE
     210{
     211    /** Number of cache entries. */
     212    uint32_t                cEntries;
     213    /** The max size of data in a cache entry. */
     214    uint32_t                cbEntry;
     215    /** Dirty bitmap shift count. */
     216    uint32_t                cDirtyShift;
     217    /** The dirty cache line size (multiple of two). */
     218    uint32_t                cbDirtyLine;
     219    /** The cache name. */
     220    const char             *pszName;
     221    /** Cache entries. */
     222    RTFSFATCLUSTERMAPENTRY  aEntries[RT_FLEXIBLE_ARRAY];
     223} RTFSFATCLUSTERMAPCACHE;
     224/** Pointer to a FAT linear metadata cache. */
     225typedef RTFSFATCLUSTERMAPCACHE *PRTFSFATCLUSTERMAPCACHE;
    124226
    125227
     
    141243typedef struct RTFSFATVOL
    142244{
     245    /** Handle to itself. */
     246    RTVFS           hVfsSelf;
     247    /** The file, partition, or whatever backing the FAT volume. */
     248    RTVFSFILE       hVfsBacking;
     249    /** The size of the backing thingy. */
     250    uint64_t        cbBacking;
     251    /** Byte offset of the bootsector relative to the start of the file. */
     252    uint64_t        offBootSector;
    143253    /** Set if read-only mode. */
    144     bool        fReadOnly;
    145 
     254    bool            fReadOnly;
     255    /** Media byte. */
     256    uint8_t         bMedia;
     257    /** Reserved sectors. */
     258    uint32_t        cReservedSectors;
     259
     260    /** Logical sector size. */
     261    uint32_t        cbSector;
    146262    /** The cluster size in bytes. */
    147     uint32_t    cbCluster;
    148     /** The number of data clusters. */
    149     uint32_t    cClusters;
     263    uint32_t        cbCluster;
     264    /** The number of data clusters, including the two reserved ones. */
     265    uint32_t        cClusters;
    150266    /** The offset of the first cluster. */
    151     uint64_t    offFirstCluster;
     267    uint64_t        offFirstCluster;
     268    /** The total size from the BPB, in bytes. */
     269    uint64_t        cbTotalSize;
    152270
    153271    /** The FAT type. */
    154     RTFSFATTYPE enmFatType;
     272    RTFSFATTYPE     enmFatType;
     273
    155274    /** Number of FAT entries (clusters). */
    156     uint32_t    cFatEntries;
     275    uint32_t        cFatEntries;
     276    /** The size of a FAT, in bytes. */
     277    uint32_t        cbFat;
    157278    /** Number of FATs. */
    158     uint32_t    cFats;
     279    uint32_t        cFats;
     280    /** The end of chain marker used by the formatter (FAT entry \#2). */
     281    uint32_t        idxEndOfChain;
     282    /** The maximum last cluster supported by the FAT format. */
     283    uint32_t        idxMaxLastCluster;
    159284    /** FAT byte offsets.  */
    160     uint64_t    aoffFats[8];
     285    uint64_t        aoffFats[8];
     286    /** Pointer to the FAT (cluster map) cache. */
     287    PRTFSFATCLUSTERMAPCACHE pFatCache;
    161288
    162289    /** The root directory byte offset. */
    163     uint64_t    offRootDir;
    164 
     290    uint64_t        offRootDir;
     291    /** Root directory cluster, UINT32_MAX if not FAT32. */
     292    uint32_t        idxRootDirCluster;
     293    /** Number of root directory entries, if fixed.  UINT32_MAX for FAT32. */
     294    uint32_t        cRootDirEntries;
     295    /** The size of the root directory, rounded up to the nearest sector size. */
     296    uint32_t        cbRootDir;
     297    /** The root directory handle. */
     298    RTVFSDIR        hVfsRootDir;
     299    /** The root directory instance data. */
     300    PRTFSFATDIR     pRootDir;
     301
     302    /** Serial number. */
     303    uint32_t        uSerialNo;
     304    /** The stripped volume label, if included in EBPB. */
     305    char            szLabel[12];
     306    /** The file system type from the EBPB (also stripped).  */
     307    char            szType[9];
     308    /** Number of FAT32 boot sector copies.   */
     309    uint8_t         cBootSectorCopies;
     310    /** FAT32 flags. */
     311    uint16_t        fFat32Flags;
     312    /** Offset of the FAT32 boot sector copies, UINT64_MAX if none. */
     313    uint64_t        offBootSectorCopies;
     314
     315    /** The FAT32 info sector byte offset, UINT64_MAX if not present. */
     316    uint64_t        offFat32InfoSector;
     317    /** The FAT32 info sector if offFat32InfoSector isn't UINT64_MAX. */
     318    FAT32INFOSECTOR Fat32InfoSector;
    165319} RTFSFATVOL;
     320/** Pointer to a FAT volume (VFS instance data). */
     321typedef RTFSFATVOL *PRTFSFATVOL;
     322
     323
     324
     325/**
     326 * Checks if the cluster chain is contiguous on the disk.
     327 *
     328 * @returns true / false.
     329 * @param   pChain              The chain.
     330 */
     331static bool rtFsFatChain_IsContiguous(PCRTFSFATCHAIN pChain)
     332{
     333    if (pChain->cClusters <= 1)
     334        return true;
     335
     336    PRTFSFATCHAINPART   pPart   = RTListGetFirst(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
     337    uint32_t            idxNext = pPart->aEntries[0];
     338    uint32_t            cLeft   = pChain->cClusters;
     339    for (;;)
     340    {
     341        uint32_t const cInPart = RT_MIN(cLeft, RT_ELEMENTS(pPart->aEntries));
     342        for (uint32_t iPart = 0; iPart < cInPart; iPart++)
     343            if (pPart->aEntries[iPart] == idxNext)
     344                idxNext++;
     345            else
     346                return false;
     347        cLeft -= cInPart;
     348        if (!cLeft)
     349            return true;
     350        pPart = RTListGetNext(&pChain->ListParts, pPart, RTFSFATCHAINPART, ListEntry);
     351    }
     352}
     353
     354
     355
     356/**
     357 * Creates a cache for the file allocation table (cluster map).
     358 *
     359 * @returns Pointer to the cache.
     360 * @param   pThis               The FAT volume instance.
     361 * @param   pbFirst512FatBytes  The first 512 bytes of the first FAT.
     362 */
     363static int rtFsFatClusterMap_Create(PRTFSFATVOL pThis, uint8_t const *pbFirst512FatBytes, PRTERRINFO pErrInfo)
     364{
     365    Assert(RT_ALIGN_32(pThis->cbFat, pThis->cbSector) == pThis->cbFat);
     366    Assert(pThis->cbFat != 0);
     367
     368    /*
     369     * Figure the cache size.  Keeping it _very_ simple for now as we just need
     370     * something that works, not anything the performs like crazy.
     371     */
     372    uint32_t cEntries;
     373    uint32_t cbEntry = pThis->cbFat;
     374    if (cbEntry <= _512K)
     375        cEntries = 1;
     376    else
     377    {
     378        Assert(pThis->cbSector < _512K / 8);
     379        cEntries = 8;
     380        cbEntry  = pThis->cbSector;
     381    }
     382
     383    /*
     384     * Allocate and initialize it all.
     385     */
     386    PRTFSFATCLUSTERMAPCACHE pCache;
     387    pThis->pFatCache = pCache = (PRTFSFATCLUSTERMAPCACHE)RTMemAllocZ(RT_OFFSETOF(RTFSFATCLUSTERMAPCACHE, aEntries[cEntries]));
     388    if (!pCache)
     389        return RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache");
     390    pCache->cEntries  = cEntries;
     391    pCache->cbEntry   = cbEntry;
     392
     393    unsigned i = cEntries;
     394    while (i-- > 0)
     395    {
     396        pCache->aEntries[i].pbData = (uint8_t *)RTMemAlloc(cbEntry);
     397        if (pCache->aEntries[i].pbData == NULL)
     398        {
     399            for (i++; i < cEntries; i++)
     400                RTMemFree(pCache->aEntries[i].pbData);
     401            RTMemFree(pCache);
     402            return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "Failed to allocate FAT cache entry (%#x bytes)", cbEntry);
     403        }
     404
     405        pCache->aEntries[i].offFat  = UINT32_MAX;
     406        pCache->aEntries[i].bmDirty = 0;
     407    }
     408
     409    /*
     410     * Calc the dirty shift factor.
     411     */
     412    cbEntry /= 64;
     413    if (cbEntry < pThis->cbSector)
     414        cbEntry = pThis->cbSector;
     415
     416    pCache->cDirtyShift = 1;
     417    pCache->cbDirtyLine = 1;
     418    while (pCache->cbDirtyLine < cbEntry)
     419    {
     420        pCache->cDirtyShift++;
     421        pCache->cbDirtyLine <<= 1;
     422    }
     423
     424    /*
     425     * Fill the cache if single entry or entry size is 512.
     426     */
     427    if (pCache->cEntries == 1 || pCache->cbEntry == 512)
     428    {
     429        memcpy(pCache->aEntries[0].pbData, pbFirst512FatBytes, RT_MIN(512, pCache->cbEntry));
     430        if (pCache->cbEntry > 512)
     431        {
     432            int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0] + 512,
     433                                     &pCache->aEntries[0].pbData[512], pCache->cbEntry - 512, NULL);
     434            if (RT_FAILURE(rc))
     435                return RTErrInfoSet(pErrInfo, rc, "Error reading FAT into memory");
     436        }
     437        pCache->aEntries[0].offFat  = 0;
     438        pCache->aEntries[0].bmDirty = 0;
     439    }
     440
     441    return VINF_SUCCESS;
     442}
     443
     444
     445/**
     446 * Worker for rtFsFatClusterMap_Flush and rtFsFatClusterMap_FlushEntry.
     447 *
     448 * @returns IPRT status code.  On failure, we're currently kind of screwed.
     449 * @param   pThis       The FAT volume instance.
     450 * @param   iFirstEntry Entry to start flushing at.
     451 * @param   iLastEntry  Last entry to flush.
     452 */
     453static int rtFsFatClusterMap_FlushWorker(PRTFSFATVOL pThis, uint32_t const iFirstEntry, uint32_t const iLastEntry)
     454{
     455    PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
     456
     457    /*
     458     * Walk the cache entries, accumulating segments to flush.
     459     */
     460    int      rc      = VINF_SUCCESS;
     461    uint64_t off     = UINT64_MAX;
     462    uint64_t offEdge = UINT64_MAX;
     463    RTSGSEG  aSgSegs[8];
     464    RTSGBUF  SgBuf;
     465    RTSgBufInit(&SgBuf, aSgSegs, RT_ELEMENTS(aSgSegs));
     466    SgBuf.cSegs = 0; /** @todo RTSgBuf API is stupid, make it smarter. */
     467
     468    for (uint32_t iFatCopy = 0; iFatCopy < pThis->cFats; iFatCopy++)
     469    {
     470        for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
     471        {
     472            uint64_t bmDirty = pCache->aEntries[iEntry].bmDirty;
     473            if (   bmDirty != 0
     474                && pCache->aEntries[iEntry].offFat != UINT32_MAX)
     475            {
     476                uint32_t offEntry   = 0;
     477                uint64_t iDirtyLine = 1;
     478                while (offEntry < pCache->cbEntry)
     479                {
     480                    if (pCache->aEntries[iEntry].bmDirty & iDirtyLine)
     481                    {
     482                        /*
     483                         * Found dirty cache line.
     484                         */
     485                        uint64_t offDirtyLine = pThis->aoffFats[iFatCopy] + pCache->aEntries[iEntry].offFat + offEntry;
     486
     487                        /* Can we simply extend the last segment? */
     488                        if (   offDirtyLine == offEdge
     489                            && offEntry)
     490                        {
     491                            Assert(   (uintptr_t)aSgSegs[SgBuf.cSegs - 1].pvSeg + aSgSegs[SgBuf.cSegs - 1].cbSeg
     492                                   == (uintptr_t)&pCache->aEntries[iEntry].pbData[offEntry]);
     493                            aSgSegs[SgBuf.cSegs - 1].cbSeg += pCache->cbDirtyLine;
     494                            offEdge += pCache->cbDirtyLine;
     495                        }
     496                        else
     497                        {
     498                            /* flush if not adjacent or if we're out of segments. */
     499                            if (   offDirtyLine != offEdge
     500                                || SgBuf.cSegs >= RT_ELEMENTS(aSgSegs))
     501                            {
     502                                int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
     503                                if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
     504                                    rc = rc2;
     505                                RTSgBufReset(&SgBuf);
     506                                SgBuf.cSegs = 0;
     507                            }
     508
     509                            /* Append segment. */
     510                            aSgSegs[SgBuf.cSegs].cbSeg = pCache->cbDirtyLine;
     511                            aSgSegs[SgBuf.cSegs].pvSeg = &pCache->aEntries[iEntry].pbData[offEntry];
     512                            SgBuf.cSegs++;
     513                            offEdge = offDirtyLine + pCache->cbDirtyLine;
     514                        }
     515
     516                        bmDirty &= ~iDirtyLine;
     517                        if (!bmDirty)
     518                            break;
     519                    }
     520                }
     521                iDirtyLine++;
     522                offEntry += pCache->cbDirtyLine;
     523            }
     524        }
     525    }
     526
     527    /*
     528     * Final flush job.
     529     */
     530    if (SgBuf.cSegs > 0)
     531    {
     532        int rc2 = RTVfsFileSgWrite(pThis->hVfsBacking, off, &SgBuf, true /*fBlocking*/, NULL);
     533        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
     534            rc = rc2;
     535    }
     536
     537    /*
     538     * Clear the dirty flags on success.
     539     */
     540    if (RT_SUCCESS(rc))
     541        for (uint32_t iEntry = iFirstEntry; iEntry <= iLastEntry; iEntry++)
     542            pCache->aEntries[iEntry].bmDirty = 0;
     543
     544    return rc;
     545}
     546
     547
     548/**
     549 * Flushes out all dirty lines in the entire file allocation table cache.
     550 *
     551 * @returns IPRT status code.  On failure, we're currently kind of screwed.
     552 * @param   pThis       The FAT volume instance.
     553 */
     554static int rtFsFatClusterMap_Flush(PRTFSFATVOL pThis)
     555{
     556    return rtFsFatClusterMap_FlushWorker(pThis, 0, pThis->pFatCache->cEntries - 1);
     557}
     558
     559
     560/**
     561 * Flushes out all dirty lines in the file allocation table (cluster map) cache.
     562 *
     563 * This is typically called prior to reusing the cache entry.
     564 *
     565 * @returns IPRT status code.  On failure, we're currently kind of screwed.
     566 * @param   pThis       The FAT volume instance.
     567 * @param   iEntry      The cache entry to flush.
     568 */
     569static int rtFsFatClusterMap_FlushEntry(PRTFSFATVOL pThis, uint32_t iEntry)
     570{
     571    return rtFsFatClusterMap_FlushWorker(pThis, iEntry, iEntry);
     572}
     573
     574
     575/**
     576 * Destroys the file allcation table cache, first flushing any dirty lines.
     577 *
     578 * @returns IRPT status code from flush (we've destroyed it regardless of the
     579 *          status code).
     580 * @param   pThis       The FAT volume instance which cluster map shall be
     581 *                      destroyed.
     582 */
     583static int rtFsFatClusterMap_Destroy(PRTFSFATVOL pThis)
     584{
     585    int                     rc     = VINF_SUCCESS;
     586    PRTFSFATCLUSTERMAPCACHE pCache = pThis->pFatCache;
     587    if (pCache)
     588    {
     589        /* flush stuff. */
     590        rc = rtFsFatClusterMap_Flush(pThis);
     591
     592        /* free everything. */
     593        uint32_t i = pCache->cEntries;
     594        while (i-- > 0)
     595        {
     596            RTMemFree(pCache->aEntries[i].pbData);
     597            pCache->aEntries[i].pbData = NULL;
     598        }
     599        pCache->cEntries = 0;
     600        RTMemFree(pCache);
     601
     602        pThis->pFatCache = NULL;
     603    }
     604
     605    return rc;
     606}
     607
     608
     609/**
     610 * Reads a cluster chain into memory
     611 *
     612 * @returns IPRT status code.
     613 * @param   pThis           The FAT volume instance.
     614 * @param   idxFirstCluster The first cluster.
     615 * @param   pChain          The chain element to read into (and thereby
     616 *                          initialize).
     617 */
     618static int rtFsFatClusterMap_ReadClusterChain(PRTFSFATVOL pThis, uint32_t idxFirstCluster, PRTFSFATCHAIN pChain)
     619{
     620    pChain->cClusters = 0;
     621    pChain->cbChain   = 0;
     622    RTListInit(&pChain->ListParts);
     623
     624    if (   idxFirstCluster >= pThis->cClusters
     625        && idxFirstCluster >= FAT_FIRST_DATA_CLUSTER)
     626        return VERR_VFS_BOGUS_OFFSET;
     627
     628    return VERR_NOT_IMPLEMENTED;
     629}
     630
     631
     632static void rtFsFatDosDateTimeToSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime, uint8_t cCentiseconds)
     633{
     634    RTTIME Time;
     635    Time.i32Year    = 1980 + (uDate >> 9);
     636    Time.u8Month    = ((uDate >> 5) & 0xf);
     637    Time.u8WeekDay  = UINT8_MAX;
     638    Time.u16YearDay = UINT16_MAX;
     639    Time.u8MonthDay = RT_MAX(uDate & 0x1f, 1);
     640    Time.u8Hour     = uTime >> 11;
     641    Time.u8Minute   = (uTime >> 5) & 0x3f;
     642    Time.u8Second   = (uTime & 0x1f) << 1;
     643    if (cCentiseconds > 0 && cCentiseconds < 200) /* screw complicated stuff for now. */
     644    {
     645        if (cCentiseconds >= 100)
     646        {
     647            cCentiseconds -= 100;
     648            Time.u8Second++;
     649        }
     650        Time.u32Nanosecond = cCentiseconds * UINT64_C(100000000);
     651    }
     652
     653    RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
     654}
     655
     656
     657static void rtFsFatObj_InitFromDirEntry(PRTFSFATOBJ pObj, PCFATDIRENTRY pDirEntry, uint64_t offDirEntry, PRTFSFATVOL pThis)
     658{
     659    RTListInit(&pObj->Entry);
     660    pObj->pVol          = pThis;
     661    pObj->offDirEntry   = offDirEntry;
     662    pObj->fAttrib       = ((RTFMODE)pDirEntry->fAttrib << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
     663    pObj->cbObject      = pDirEntry->cbFile;
     664    rtFsFatDosDateTimeToSpec(&pObj->ModificationTime, pDirEntry->uAccessDate, pDirEntry->uModifyTime, 0);
     665    rtFsFatDosDateTimeToSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds);
     666    rtFsFatDosDateTimeToSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0);
     667}
     668
     669
     670static void rtFsFatObj_InitDummy(PRTFSFATOBJ pObj, uint64_t offDirEntry, uint32_t cbObject, RTFMODE fAttrib, PRTFSFATVOL pThis)
     671{
     672    RTListInit(&pObj->Entry);
     673    pObj->pVol          = pThis;
     674    pObj->offDirEntry   = offDirEntry;
     675    pObj->fAttrib       = fAttrib;
     676    pObj->cbObject      = cbObject;
     677    RTTimeSpecSetDosSeconds(&pObj->AccessTime, 0);
     678    RTTimeSpecSetDosSeconds(&pObj->ModificationTime, 0);
     679    RTTimeSpecSetDosSeconds(&pObj->BirthTime, 0);
     680}
    166681
    167682
     
    5691084
    5701085/**
    571  * @interface_method_impl{RTVFSOPS,pfnDestroy}
    572  */
    573 static DECLCALLBACK(void) rtFsFatVol_Destroy(void *pvThis)
    574 {
    575     RT_NOREF(pvThis);
     1086 * Instantiates a new directory.
     1087 *
     1088 * @returns IPRT status code.
     1089 * @param   pThis       The FAT volume instance.
     1090 * @param   pParentDir  The parent directory.  This is NULL for the root
     1091 *                      directory.
     1092 * @param   pDirEntry   The parent directory entry. This is NULL for the root
     1093 *                      directory.
     1094 * @param   offDirEntry The byte offset of the directory entry.  UINT64_MAX if
     1095 *                      root directory.
     1096 * @param   idxCluster  The cluster where the directory content is to be found.
     1097 *                      This can be UINT32_MAX if a root FAT12/16 directory.
     1098 * @param   offDisk     The disk byte offset of the FAT12/16 root directory.
     1099 *                      This is UINT64_MAX if idxCluster is given.
     1100 * @param   cbDir       The size of the directory.
     1101 * @param   phVfsDir    Where to return the directory handle.
     1102 * @param   ppDir       Where to return the FAT directory instance data.
     1103 */
     1104static int rtFsFatDir_New(PRTFSFATVOL pThis, PRTFSFATDIR pParentDir, PCFATDIRENTRY pDirEntry, uint64_t offDirEntry,
     1105                          uint32_t idxCluster, uint64_t offDisk, uint32_t cbDir, PRTVFSDIR phVfsDir, PRTFSFATDIR *ppDir)
     1106{
     1107    Assert((idxCluster == UINT32_MAX) != (offDisk == UINT64_MAX));
     1108    *ppDir = NULL;
     1109
     1110    PRTFSFATDIR pNewDir;
     1111    int rc = RTVfsNewDir(&g_rtFsFatDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
     1112                         NIL_RTVFSLOCK, phVfsDir, (void **)&pNewDir);
     1113    if (RT_SUCCESS(rc))
     1114    {
     1115        /*
     1116         * Initialize it all so rtFsFatDir_Close doesn't trip up in anyway.
     1117         */
     1118        RTListInit(&pNewDir->OpenChildren);
     1119        if (pDirEntry)
     1120            rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offDirEntry, pThis);
     1121        else
     1122            rtFsFatObj_InitDummy(&pNewDir->Core, offDirEntry, cbDir, RTFS_DOS_DIRECTORY, pThis);
     1123
     1124        pNewDir->cEntries       = cbDir / sizeof(FATDIRENTRY);
     1125        pNewDir->fIsFlatRootDir = idxCluster == UINT32_MAX;
     1126        pNewDir->fFullyBuffered = pNewDir->fIsFlatRootDir;
     1127        if (pNewDir->fFullyBuffered)
     1128        {
     1129            pNewDir->u.Full.offDir          = UINT64_MAX;
     1130            pNewDir->u.Full.cSectors        = 0;
     1131            pNewDir->u.Full.cDirtySectors   = 0;
     1132            pNewDir->u.Full.paEntries       = NULL;
     1133            pNewDir->u.Full.pbDirtySectors  = NULL;
     1134        }
     1135        else
     1136        {
     1137            pNewDir->u.Simple.offOnDisk     = UINT64_MAX;
     1138            pNewDir->u.Simple.offInDir      = UINT32_MAX;
     1139            pNewDir->u.Simple.u32Reserved   = 0;
     1140            pNewDir->u.Simple.paEntries     = NULL;
     1141            pNewDir->u.Simple.fDirty        = false;
     1142        }
     1143
     1144        /*
     1145         * If clustered backing, read the chain and see if we cannot still do the full buffering.
     1146         */
     1147        if (idxCluster != UINT32_MAX)
     1148        {
     1149            rc = rtFsFatClusterMap_ReadClusterChain(pThis, idxCluster, &pNewDir->Core.Clusters);
     1150            if (RT_SUCCESS(rc))
     1151            {
     1152                if (   pNewDir->Core.Clusters.cClusters >= 1
     1153                    && pNewDir->Core.Clusters.cbChain   <= _64K
     1154                    && rtFsFatChain_IsContiguous(&pNewDir->Core.Clusters))
     1155                    pNewDir->fFullyBuffered = true;
     1156            }
     1157        }
     1158
     1159        if (RT_SUCCESS(rc))
     1160        {
     1161            RT_NOREF(pParentDir);
     1162        }
     1163
     1164        RTVfsDirRelease(*phVfsDir);
     1165    }
     1166    *phVfsDir = NIL_RTVFSDIR;
     1167    *ppDir    = NULL;
     1168    return rc;
     1169}
     1170
     1171
     1172
     1173
     1174
     1175/**
     1176 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
     1177 */
     1178static DECLCALLBACK(int) rtFsFatVol_Close(void *pvThis)
     1179{
     1180    PRTFSFATVOL pThis = (PRTFSFATVOL)pvThis;
     1181    int rc = rtFsFatClusterMap_Destroy(pThis);
     1182
     1183    if (pThis->hVfsRootDir != NIL_RTVFSDIR)
     1184    {
     1185        Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
     1186        uint32_t cRefs = RTVfsDirRelease(pThis->hVfsRootDir);
     1187        Assert(cRefs == 0);
     1188        pThis->hVfsRootDir = NIL_RTVFSDIR;
     1189        pThis->pRootDir    = NULL;
     1190    }
     1191
     1192    RTVfsFileRelease(pThis->hVfsBacking);
     1193    pThis->hVfsBacking = NIL_RTVFSFILE;
     1194
     1195    return rc;
     1196}
     1197
     1198
     1199/**
     1200 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
     1201 */
     1202static DECLCALLBACK(int) rtFsFatVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
     1203{
     1204    RT_NOREF(pvThis, pObjInfo, enmAddAttr);
     1205    return VERR_WRONG_TYPE;
    5761206}
    5771207
     
    5991229DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsFatVolOps =
    6001230{
     1231    { /* Obj */
     1232        RTVFSOBJOPS_VERSION,
     1233        RTVFSOBJTYPE_VFS,
     1234        "FatVol",
     1235        rtFsFatVol_Close,
     1236        rtFsFatVol_QueryInfo,
     1237        RTVFSOBJOPS_VERSION
     1238    },
    6011239    RTVFSOPS_VERSION,
    6021240    0 /* fFeatures */,
    603     "FatVol",
    604     rtFsFatVol_Destroy,
    6051241    rtFsFatVol_OpenRoot,
    6061242    rtFsFatVol_IsRangeInUse,
     
    6091245
    6101246
    611 
    612 RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, PRTVFS phVfs, PRTERRINFO pErrInfo)
    613 {
    614     RT_NOREF(hVfsFileIn, fReadOnly, pErrInfo, phVfs);
    615     return VERR_NOT_IMPLEMENTED;
     1247/**
     1248 * Tries to detect a DOS 1.x formatted image and fills in the BPB fields.
     1249 *
     1250 * There is no BPB here, but fortunately, there isn't much variety.
     1251 *
     1252 * @returns IPRT status code.
     1253 * @param   pThis       The FAT volume instance, BPB derived fields are filled
     1254 *                      in on success.
     1255 * @param   pBootSector The boot sector.
     1256 * @param   pbFatSector Points to the FAT sector, or whatever is 512 bytes after
     1257 *                      the boot sector.
     1258 * @param   pErrInfo    Where to return additional error information.
     1259 */
     1260static int rtFsFatVolTryInitDos1x(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t const *pbFatSector,
     1261                                  PRTERRINFO pErrInfo)
     1262{
     1263    /*
     1264     * PC-DOS 1.0 does a 2fh byte short jump w/o any NOP following it.
     1265     * Instead the following are three words and a 9 byte build date
     1266     * string.  The remaining space is zero filled.
     1267     *
     1268     * Note! No idea how this would look like for 8" floppies, only got 5"1/4'.
     1269     *
     1270     * ASSUME all non-BPB disks are using this format.
     1271     */
     1272    if (   pBootSector->abJmp[0] != 0xeb /* jmp rel8 */
     1273        || pBootSector->abJmp[1] <  0x2f
     1274        || pBootSector->abJmp[1] >= 0x80
     1275        || pBootSector->abJmp[2] == 0x90 /* nop */)
     1276        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1277                             "No DOS v1.0 bootsector either - invalid jmp: %.3Rhxs", pBootSector->abJmp);
     1278    uint32_t const offJump      = 2 + pBootSector->abJmp[1];
     1279    uint32_t const offFirstZero = 2 /*jmp */ + 3 * 2 /* words */ + 9 /* date string */;
     1280    Assert(offFirstZero >= RT_OFFSETOF(FATBOOTSECTOR, Bpb));
     1281    uint32_t const cbZeroPad    = RT_MIN(offJump - offFirstZero,
     1282                                         sizeof(pBootSector->Bpb.Bpb20) - (offFirstZero - RT_OFFSETOF(FATBOOTSECTOR, Bpb)));
     1283
     1284    if (!ASMMemIsAllU8((uint8_t const *)pBootSector + offFirstZero, cbZeroPad, 0))
     1285        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1286                             "No DOS v1.0 bootsector either - expected zero padding %#x LB %#x: %.*Rhxs",
     1287                             offFirstZero, cbZeroPad, cbZeroPad, (uint8_t const *)pBootSector + offFirstZero);
     1288
     1289    /*
     1290     * Check the FAT ID so we can tell if this is double or single sided,
     1291     * as well as being a valid FAT12 start.
     1292     */
     1293    if (   (pbFatSector[0] != 0xfe && pbFatSector[0] != 0xff)
     1294        || pbFatSector[1] != 0xff
     1295        || pbFatSector[2] != 0xff)
     1296        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1297                             "No DOS v1.0 bootsector either - unexpected start of FAT: %.3Rhxs", pbFatSector);
     1298
     1299    /*
     1300     * Fixed DOS 1.0 config.
     1301     */
     1302    pThis->enmFatType       = RTFSFATTYPE_FAT12;
     1303    pThis->bMedia           = pbFatSector[0];
     1304    pThis->cReservedSectors = 1;
     1305    pThis->cbSector         = 512;
     1306    pThis->cbCluster        = pThis->bMedia == 0xfe ? 1024 : 512;
     1307    pThis->cFats            = 2;
     1308    pThis->cbFat            = 512;
     1309    pThis->aoffFats[0]      = pThis->offBootSector + pThis->cReservedSectors * 512;
     1310    pThis->aoffFats[1]      = pThis->aoffFats[0] + pThis->cbFat;
     1311    pThis->offRootDir       = pThis->aoffFats[1] + pThis->cbFat;
     1312    pThis->cRootDirEntries  = 512;
     1313    pThis->offFirstCluster  = pThis->offRootDir + RT_ALIGN_32(pThis->cRootDirEntries * sizeof(FATDIRENTRY),
     1314                                                              pThis->cbSector);
     1315    pThis->cbTotalSize      = pThis->bMedia == 0xfe ? 8 * 1 * 40 * 512 : 8 * 2 * 40 * 512;
     1316    pThis->cClusters        = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
     1317    return VINF_SUCCESS;
     1318}
     1319
     1320
     1321/**
     1322 * Worker for rtFsFatVolTryInitDos2Plus that handles remaining BPB fields.
     1323 *
     1324 * @returns IPRT status code.
     1325 * @param   pThis       The FAT volume instance, BPB derived fields are filled
     1326 *                      in on success.
     1327 * @param   pBootSector The boot sector.
     1328 * @param   fMaybe331   Set if it could be a DOS v3.31 BPB.
     1329 * @param   pErrInfo    Where to return additional error information.
     1330 */
     1331static int rtFsFatVolTryInitDos2PlusBpb(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, bool fMaybe331, PRTERRINFO pErrInfo)
     1332{
     1333    /*
     1334     * Figure total sector count.  Could both be zero, in which case we have to
     1335     * fall back on the size of the backing stuff.
     1336     */
     1337    if (pBootSector->Bpb.Bpb20.cTotalSectors16 != 0)
     1338        pThis->cbTotalSize = pBootSector->Bpb.Bpb20.cTotalSectors16 * pThis->cbSector;
     1339    else if (   pBootSector->Bpb.Bpb331.cTotalSectors32 != 0
     1340             && fMaybe331)
     1341        pThis->cbTotalSize = pBootSector->Bpb.Bpb331.cTotalSectors32 * (uint64_t)pThis->cbSector;
     1342    else
     1343        pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
     1344    if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
     1345        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1346                             "Bogus FAT12/16 total or reserved sector count: %#x vs %#x",
     1347                             pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
     1348
     1349    /*
     1350     * The fat size.  Complete FAT offsets.
     1351     */
     1352    if (   pBootSector->Bpb.Bpb20.cSectorsPerFat == 0
     1353        || ((uint32_t)pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cFats + 1) * pThis->cbSector > pThis->cbTotalSize)
     1354        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT12/16 sectors per FAT: %#x (total sectors %#RX64)",
     1355                             pBootSector->Bpb.Bpb20.cSectorsPerFat, pThis->cbTotalSize / pThis->cbSector);
     1356    pThis->cbFat = pBootSector->Bpb.Bpb20.cSectorsPerFat * pThis->cbSector;
     1357
     1358    AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
     1359    for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
     1360        pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
     1361
     1362    /*
     1363     * Do root directory calculations.
     1364     */
     1365    pThis->idxRootDirCluster = UINT32_MAX;
     1366    pThis->offRootDir        = pThis->aoffFats[pThis->cFats];
     1367    if (pThis->cRootDirEntries == 0)
     1368        return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT,  "Zero FAT12/16 root directory size");
     1369    pThis->cbRootDir         = pThis->cRootDirEntries * sizeof(FATDIRENTRY);
     1370    pThis->cbRootDir         = RT_ALIGN_32(pThis->cbRootDir, pThis->cbSector);
     1371
     1372    /*
     1373     * First cluster and cluster count checks and calcs.  Determin FAT type.
     1374     */
     1375    pThis->offFirstCluster = pThis->offRootDir + pThis->cbRootDir;
     1376    uint64_t cbSystemStuff = pThis->offFirstCluster - pThis->offBootSector;
     1377    if (cbSystemStuff >= pThis->cbTotalSize)
     1378        return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT,  "Bogus FAT12/16 total size, root dir, or fat size");
     1379    pThis->cClusters = (pThis->cbTotalSize - cbSystemStuff) / pThis->cbCluster;
     1380
     1381    if (pThis->cClusters >= FAT_LAST_FAT16_DATA_CLUSTER)
     1382    {
     1383        pThis->cClusters  = FAT_LAST_FAT16_DATA_CLUSTER + 1;
     1384        pThis->enmFatType = RTFSFATTYPE_FAT16;
     1385    }
     1386    else if (pThis->cClusters > FAT_LAST_FAT12_DATA_CLUSTER)
     1387        pThis->enmFatType = RTFSFATTYPE_FAT16;
     1388    else
     1389        pThis->enmFatType = RTFSFATTYPE_FAT12; /** @todo Not sure if this is entirely the right way to go about it... */
     1390
     1391    uint32_t cClustersPerFat;
     1392    if (pThis->enmFatType == RTFSFATTYPE_FAT16)
     1393        cClustersPerFat = pThis->cbFat / 2;
     1394    else
     1395        cClustersPerFat = pThis->cbFat * 2 / 3;
     1396    if (pThis->cClusters > cClustersPerFat)
     1397        pThis->cClusters = cClustersPerFat;
     1398
     1399    return VINF_SUCCESS;
     1400}
     1401
     1402
     1403/**
     1404 * Worker for rtFsFatVolTryInitDos2Plus and rtFsFatVolTryInitDos2PlusFat32 that
     1405 * handles common extended BPBs fields.
     1406 *
     1407 * @returns IPRT status code.
     1408 * @param   pThis           The FAT volume instance.
     1409 * @param   bExtSignature   The extended BPB signature.
     1410 * @param   uSerialNumber   The serial number.
     1411 * @param   pachLabel       Pointer to the volume label field.
     1412 * @param   pachType        Pointer to the file system type field.
     1413 */
     1414static void rtFsFatVolInitCommonEbpbBits(PRTFSFATVOL pThis, uint8_t bExtSignature, uint32_t uSerialNumber,
     1415                                         char const *pachLabel, char const *pachType)
     1416{
     1417    pThis->uSerialNo = uSerialNumber;
     1418    if (bExtSignature == FATEBPB_SIGNATURE)
     1419    {
     1420        memcpy(pThis->szLabel, pachLabel, RT_SIZEOFMEMB(FATEBPB, achLabel));
     1421        pThis->szLabel[RT_SIZEOFMEMB(FATEBPB, achLabel)] = '\0';
     1422        RTStrStrip(pThis->szLabel);
     1423
     1424        memcpy(pThis->szType, pachType, RT_SIZEOFMEMB(FATEBPB, achType));
     1425        pThis->szType[RT_SIZEOFMEMB(FATEBPB, achType)] = '\0';
     1426        RTStrStrip(pThis->szType);
     1427    }
     1428    else
     1429    {
     1430        pThis->szLabel[0] = '\0';
     1431        pThis->szType[0] = '\0';
     1432    }
     1433}
     1434
     1435
     1436/**
     1437 * Worker for rtFsFatVolTryInitDos2Plus that deals with FAT32.
     1438 *
     1439 * @returns IPRT status code.
     1440 * @param   pThis       The FAT volume instance, BPB derived fields are filled
     1441 *                      in on success.
     1442 * @param   pBootSector The boot sector.
     1443 * @param   pErrInfo    Where to return additional error information.
     1444 */
     1445static int rtFsFatVolTryInitDos2PlusFat32(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, PRTERRINFO pErrInfo)
     1446{
     1447    pThis->enmFatType  = RTFSFATTYPE_FAT32;
     1448    pThis->fFat32Flags = pBootSector->Bpb.Fat32Ebpb.fFlags;
     1449
     1450    if (pBootSector->Bpb.Fat32Ebpb.uVersion != FAT32EBPB_VERSION_0_0)
     1451        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Unsupported FAT32 version: %d.%d (%#x)",
     1452                             RT_HI_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),  RT_LO_U8(pBootSector->Bpb.Fat32Ebpb.uVersion),
     1453                             pBootSector->Bpb.Fat32Ebpb.uVersion);
     1454
     1455    /*
     1456     * Figure total sector count.  We expected it to be filled in.
     1457     */
     1458    bool fUsing64BitTotalSectorCount = false;
     1459    if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 != 0)
     1460        pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors16 * pThis->cbSector;
     1461    else if (pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 != 0)
     1462        pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.Bpb.cTotalSectors32 * (uint64_t)pThis->cbSector;
     1463    else if (   pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 <= UINT64_MAX / 512
     1464             && pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 > 3
     1465             && pBootSector->Bpb.Fat32Ebpb.bExtSignature != FATEBPB_SIGNATURE_OLD)
     1466    {
     1467        pThis->cbTotalSize = pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64 * pThis->cbSector;
     1468        fUsing64BitTotalSectorCount = true;
     1469    }
     1470    else
     1471        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 total sector count out of range: %#RX64",
     1472                             pBootSector->Bpb.Fat32Ebpb.u.cTotalSectors64);
     1473    if (pThis->cReservedSectors * pThis->cbSector >= pThis->cbTotalSize)
     1474        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1475                             "Bogus FAT32 total or reserved sector count: %#x vs %#x",
     1476                             pThis->cReservedSectors, pThis->cbTotalSize / pThis->cbSector);
     1477
     1478    /*
     1479     * Fat size. We check the 16-bit field even if it probably should be zero all the time.
     1480     */
     1481    if (pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat != 0)
     1482    {
     1483        if (   pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != 0
     1484            && pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 != pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat)
     1485            return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1486                                 "Both 16-bit and 32-bit FAT size fields are set: %#RX16 vs %#RX32",
     1487                                 pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat, pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
     1488        pThis->cbFat = pBootSector->Bpb.Fat32Ebpb.Bpb.cSectorsPerFat * pThis->cbSector;
     1489    }
     1490    else
     1491    {
     1492        uint64_t cbFat = pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32 * (uint64_t)pThis->cbSector;
     1493        if (   cbFat == 0
     1494            || cbFat >= (FAT_LAST_FAT32_DATA_CLUSTER + 1) * 4 + pThis->cbSector * 16)
     1495            return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1496                                 "Bogus 32-bit FAT size: %#RX32", pBootSector->Bpb.Fat32Ebpb.cSectorsPerFat32);
     1497        pThis->cbFat = (uint32_t)cbFat;
     1498    }
     1499
     1500    /*
     1501     * Complete the FAT offsets and first cluster offset, then calculate number
     1502     * of data clusters.
     1503     */
     1504    AssertReturn(pThis->cFats < RT_ELEMENTS(pThis->aoffFats), VERR_VFS_BOGUS_FORMAT);
     1505    for (unsigned iFat = 1; iFat <= pThis->cFats; iFat++)
     1506        pThis->aoffFats[iFat] = pThis->aoffFats[iFat - 1] + pThis->cbFat;
     1507    pThis->offFirstCluster = pThis->aoffFats[pThis->cFats];
     1508
     1509    if (pThis->offFirstCluster - pThis->offBootSector >= pThis->cbTotalSize)
     1510        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1511                             "Bogus 32-bit FAT size or total sector count: cFats=%d cbFat=%#x cbTotalSize=%#x",
     1512                             pThis->cFats, pThis->cbFat, pThis->cbTotalSize);
     1513
     1514    uint64_t cClusters = (pThis->cbTotalSize - (pThis->offFirstCluster - pThis->offBootSector)) / pThis->cbCluster;
     1515    if (cClusters <= FAT_LAST_FAT32_DATA_CLUSTER)
     1516        pThis->cClusters = (uint32_t)cClusters;
     1517    else
     1518        pThis->cClusters = FAT_LAST_FAT32_DATA_CLUSTER + 1;
     1519    if (pThis->cClusters > pThis->cbFat / 4)
     1520        pThis->cClusters = pThis->cbFat / 4;
     1521
     1522    /*
     1523     * Root dir cluster.
     1524     */
     1525    if (   pBootSector->Bpb.Fat32Ebpb.uRootDirCluster < FAT_FIRST_DATA_CLUSTER
     1526        || pBootSector->Bpb.Fat32Ebpb.uRootDirCluster >= pThis->cClusters)
     1527        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1528                             "Bogus FAT32 root directory cluster: %#x", pBootSector->Bpb.Fat32Ebpb.uRootDirCluster);
     1529    pThis->idxRootDirCluster = pBootSector->Bpb.Fat32Ebpb.uRootDirCluster;
     1530    pThis->offRootDir        = pThis->offFirstCluster
     1531                             + (pBootSector->Bpb.Fat32Ebpb.uRootDirCluster - FAT_FIRST_DATA_CLUSTER) * pThis->cbCluster;
     1532
     1533    /*
     1534     * Info sector.
     1535     */
     1536    if (   pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == 0
     1537        || pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo == UINT16_MAX)
     1538        pThis->offFat32InfoSector = UINT64_MAX;
     1539    else if (pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo >= pThis->cReservedSectors)
     1540        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1541                             "Bogus FAT32 info sector number: %#x (reserved sectors %#x)",
     1542                             pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pThis->cReservedSectors);
     1543    else
     1544    {
     1545        pThis->offFat32InfoSector = pThis->cbSector * pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo + pThis->offBootSector;
     1546        int rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->offFat32InfoSector,
     1547                                 &pThis->Fat32InfoSector, sizeof(pThis->Fat32InfoSector), NULL);
     1548        if (RT_FAILURE(rc))
     1549            return RTErrInfoSetF(pErrInfo, rc, "Failed to read FAT32 info sector at offset %#RX64", pThis->offFat32InfoSector);
     1550        if (   pThis->Fat32InfoSector.uSignature1 != FAT32INFOSECTOR_SIGNATURE_1
     1551            || pThis->Fat32InfoSector.uSignature2 != FAT32INFOSECTOR_SIGNATURE_2
     1552            || pThis->Fat32InfoSector.uSignature3 != FAT32INFOSECTOR_SIGNATURE_3)
     1553            return RTErrInfoSetF(pErrInfo, rc, "FAT32 info sector signature mismatch: %#x %#x %#x",
     1554                                 pThis->Fat32InfoSector.uSignature1,  pThis->Fat32InfoSector.uSignature2,
     1555                                 pThis->Fat32InfoSector.uSignature3);
     1556    }
     1557
     1558    /*
     1559     * Boot sector copy.
     1560     */
     1561    if (   pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == 0
     1562        || pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo == UINT16_MAX)
     1563    {
     1564        pThis->cBootSectorCopies   = 0;
     1565        pThis->offBootSectorCopies = UINT64_MAX;
     1566    }
     1567    else if (pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo >= pThis->cReservedSectors)
     1568        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1569                             "Bogus FAT32 info boot sector copy location: %#x (reserved sectors %#x)",
     1570                             pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo, pThis->cReservedSectors);
     1571    else
     1572    {
     1573        /** @todo not sure if cbSector is correct here. */
     1574        pThis->cBootSectorCopies = 3;
     1575        if (  (uint32_t)pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo + pThis->cBootSectorCopies
     1576            > pThis->cReservedSectors)
     1577            pThis->cBootSectorCopies = (uint8_t)(pThis->cReservedSectors - pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
     1578        pThis->offBootSectorCopies = pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo * pThis->cbSector + pThis->offBootSector;
     1579        if (   pThis->offFat32InfoSector != UINT64_MAX
     1580            && pThis->offFat32InfoSector - pThis->offBootSectorCopies < (uint64_t)(pThis->cBootSectorCopies * pThis->cbSector))
     1581            return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "FAT32 info sector and boot sector copies overlap: %#x vs %#x",
     1582                                 pBootSector->Bpb.Fat32Ebpb.uInfoSectorNo, pBootSector->Bpb.Fat32Ebpb.uBootSectorCopySectorNo);
     1583    }
     1584
     1585    /*
     1586     * Serial number, label and type.
     1587     */
     1588    rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Fat32Ebpb.bExtSignature, pBootSector->Bpb.Fat32Ebpb.uSerialNumber,
     1589                                 pBootSector->Bpb.Fat32Ebpb.achLabel,
     1590                                 fUsing64BitTotalSectorCount ? pBootSector->achOemName : pBootSector->Bpb.Fat32Ebpb.achLabel);
     1591    if (pThis->szType[0] == '\0')
     1592        memcpy(pThis->szType, "FAT32", 6);
     1593
     1594    return VINF_SUCCESS;
     1595}
     1596
     1597
     1598/**
     1599 * Tries to detect a DOS 2.0+ formatted image and fills in the BPB fields.
     1600 *
     1601 * We ASSUME BPB here, but need to figure out which version of the BPB it is,
     1602 * which is lots of fun.
     1603 *
     1604 * @returns IPRT status code.
     1605 * @param   pThis       The FAT volume instance, BPB derived fields are filled
     1606 *                      in on success.
     1607 * @param   pBootSector The boot sector.
     1608 * @param   pbFatSector Points to the FAT sector, or whatever is 512 bytes after
     1609 *                      the boot sector.  On successful return it will contain
     1610 *                      the first FAT sector.
     1611 * @param   pErrInfo    Where to return additional error information.
     1612 */
     1613static int rtFsFatVolTryInitDos2Plus(PRTFSFATVOL pThis, PCFATBOOTSECTOR pBootSector, uint8_t *pbFatSector, PRTERRINFO pErrInfo)
     1614{
     1615    /*
     1616     * Check if we've got a known jump instruction first, because that will
     1617     * give us a max (E)BPB size hint.
     1618     */
     1619    uint8_t offJmp = UINT8_MAX;
     1620    if (   pBootSector->abJmp[0] == 0xeb
     1621        && pBootSector->abJmp[1] <= 0x7f)
     1622        offJmp = pBootSector->abJmp[1] + 2;
     1623    else if (   pBootSector->abJmp[0] == 0x90
     1624             && pBootSector->abJmp[1] == 0xeb
     1625             && pBootSector->abJmp[2] <= 0x7f)
     1626        offJmp = pBootSector->abJmp[2] + 3;
     1627    else if (   pBootSector->abJmp[0] == 0xe9
     1628             && pBootSector->abJmp[2] <= 0x7f)
     1629        offJmp = RT_MIN(127, RT_MAKE_U16(pBootSector->abJmp[1], pBootSector->abJmp[2]));
     1630    uint8_t const cbMaxBpb = offJmp - RT_OFFSETOF(FATBOOTSECTOR, Bpb);
     1631
     1632    /*
     1633     * Do the basic DOS v2.0 BPB fields.
     1634     */
     1635    if (cbMaxBpb < sizeof(FATBPB20))
     1636        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1637                             "DOS signature, but jmp too short for any BPB: %#x (max %#x BPB)", offJmp, cbMaxBpb);
     1638
     1639    if (pBootSector->Bpb.Bpb20.cFats == 0)
     1640        return RTErrInfoSet(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, number of FATs is zero, so not FAT file system");
     1641    if (pBootSector->Bpb.Bpb20.cFats > 4)
     1642        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many FATs: %#x", pBootSector->Bpb.Bpb20.cFats);
     1643    pThis->cFats = pBootSector->Bpb.Bpb20.cFats;
     1644
     1645    if (!FATBPB_MEDIA_IS_VALID(pBootSector->Bpb.Bpb20.bMedia))
     1646        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1647                             "DOS signature, invalid media byte: %#x", pBootSector->Bpb.Bpb20.bMedia);
     1648    pThis->bMedia = pBootSector->Bpb.Bpb20.bMedia;
     1649
     1650    if (!RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cbSector))
     1651        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1652                             "DOS signature, sector size not power of two: %#x", pBootSector->Bpb.Bpb20.cbSector);
     1653    if (   pBootSector->Bpb.Bpb20.cbSector != 512
     1654        && pBootSector->Bpb.Bpb20.cbSector != 4096
     1655        && pBootSector->Bpb.Bpb20.cbSector != 1024
     1656        && pBootSector->Bpb.Bpb20.cbSector != 128)
     1657        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
     1658                             "DOS signature, unsupported sector size: %#x", pBootSector->Bpb.Bpb20.cbSector);
     1659    pThis->cbSector = pBootSector->Bpb.Bpb20.cbSector;
     1660
     1661    if (   !RT_IS_POWER_OF_TWO(pBootSector->Bpb.Bpb20.cSectorsPerCluster)
     1662        || !pBootSector->Bpb.Bpb20.cSectorsPerCluster)
     1663        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "DOS signature, cluster size not non-zero power of two: %#x",
     1664                             pBootSector->Bpb.Bpb20.cSectorsPerCluster);
     1665    pThis->cbCluster = pBootSector->Bpb.Bpb20.cSectorsPerCluster * pThis->cbSector;
     1666
     1667    uint64_t const cMaxRoot = (pThis->cbBacking - pThis->offBootSector - 512) / sizeof(FATDIRENTRY); /* we'll check again later. */
     1668    if (pBootSector->Bpb.Bpb20.cMaxRootDirEntries >= cMaxRoot)
     1669        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "DOS signature, too many root entries: %#x (max %#RX64)",
     1670                             pBootSector->Bpb.Bpb20.cSectorsPerCluster, cMaxRoot);
     1671    pThis->cRootDirEntries = pBootSector->Bpb.Bpb20.cMaxRootDirEntries;
     1672
     1673    if (   pBootSector->Bpb.Bpb20.cReservedSectors == 0
     1674        || pBootSector->Bpb.Bpb20.cReservedSectors >= _32K)
     1675        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1676                             "DOS signature, bogus reserved sector count: %#x", pBootSector->Bpb.Bpb20.cReservedSectors);
     1677    pThis->cReservedSectors = pBootSector->Bpb.Bpb20.cReservedSectors;
     1678    pThis->aoffFats[0]      = pThis->offBootSector + pThis->cReservedSectors * pThis->cbSector;
     1679
     1680    /*
     1681     * Jump ahead and check for FAT32 EBPB.
     1682     * If found, we simply ASSUME it's a FAT32 file system.
     1683     */
     1684    int rc;
     1685    if (   (   sizeof(FAT32EBPB) <= cbMaxBpb
     1686            && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE)
     1687        || (   RT_OFFSETOF(FAT32EBPB, achLabel) <= cbMaxBpb
     1688            && pBootSector->Bpb.Fat32Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
     1689    {
     1690        rc = rtFsFatVolTryInitDos2PlusFat32(pThis, pBootSector, pErrInfo);
     1691        if (RT_FAILURE(rc))
     1692            return rc;
     1693    }
     1694    else
     1695    {
     1696        /*
     1697         * Check for extended BPB, otherwise we'll have to make qualified guesses
     1698         * about what kind of BPB we're up against based on jmp offset and zero fields.
     1699         * ASSUMES either FAT16 or FAT12.
     1700         */
     1701        if (   (   sizeof(FATEBPB) <= cbMaxBpb
     1702                && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE)
     1703            || (   RT_OFFSETOF(FATEBPB, achLabel) <= cbMaxBpb
     1704                && pBootSector->Bpb.Ebpb.bExtSignature == FATEBPB_SIGNATURE_OLD) )
     1705        {
     1706            rtFsFatVolInitCommonEbpbBits(pThis, pBootSector->Bpb.Ebpb.bExtSignature, pBootSector->Bpb.Ebpb.uSerialNumber,
     1707                                         pBootSector->Bpb.Ebpb.achLabel, pBootSector->Bpb.Ebpb.achType);
     1708            rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, true /*fMaybe331*/, pErrInfo);
     1709        }
     1710        else
     1711            rc = rtFsFatVolTryInitDos2PlusBpb(pThis, pBootSector, cbMaxBpb >= sizeof(FATBPB331), pErrInfo);
     1712        if (RT_FAILURE(rc))
     1713            return rc;
     1714        if (pThis->szType[0] == '\0')
     1715            memcpy(pThis->szType, pThis->enmFatType == RTFSFATTYPE_FAT12 ? "FAT12" : "FAT16", 6);
     1716    }
     1717
     1718    /*
     1719     * Check the FAT ID. May have to read a bit of the FAT into the buffer.
     1720     */
     1721    if (pThis->aoffFats[0] != pThis->offBootSector + 512)
     1722    {
     1723        rc = RTVfsFileReadAt(pThis->hVfsBacking, pThis->aoffFats[0], pbFatSector, 512, NULL);
     1724        if (RT_FAILURE(rc))
     1725            return RTErrInfoSet(pErrInfo, rc, "error reading first FAT sector");
     1726    }
     1727    if (pbFatSector[0] != pThis->bMedia)
     1728        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
     1729                             "Media byte and FAT ID mismatch: %#x vs %#x (%.7Rhxs)", pbFatSector[0], pThis->bMedia, pbFatSector);
     1730    switch (pThis->enmFatType)
     1731    {
     1732        case RTFSFATTYPE_FAT12:
     1733            if ((pbFatSector[1] & 0xf) != 0xf)
     1734                return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT12): %.3Rhxs", pbFatSector);
     1735            pThis->idxMaxLastCluster = FAT_LAST_FAT12_DATA_CLUSTER;
     1736            pThis->idxEndOfChain     = (pbFatSector[1] >> 4) | ((uint32_t)pbFatSector[2] << 4);
     1737            break;
     1738
     1739        case RTFSFATTYPE_FAT16:
     1740            if (pbFatSector[1] != 0xff)
     1741                return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT16): %.4Rhxs", pbFatSector);
     1742            pThis->idxMaxLastCluster = FAT_LAST_FAT16_DATA_CLUSTER;
     1743            pThis->idxEndOfChain     = RT_MAKE_U16(pbFatSector[2], pbFatSector[3]);
     1744            break;
     1745
     1746        case RTFSFATTYPE_FAT32:
     1747            if (   pbFatSector[1] != 0xff
     1748                || pbFatSector[2] != 0xff
     1749                || (pbFatSector[3] & 0x0f) != 0x0f)
     1750                return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus FAT ID patting (FAT32): %.8Rhxs", pbFatSector);
     1751            pThis->idxMaxLastCluster = FAT_LAST_FAT32_DATA_CLUSTER;
     1752            pThis->idxEndOfChain     = RT_MAKE_U32_FROM_U8(pbFatSector[4], pbFatSector[5], pbFatSector[6], pbFatSector[7]);
     1753            break;
     1754
     1755        default: AssertFailedReturn(VERR_INTERNAL_ERROR_2);
     1756    }
     1757    if (pThis->idxEndOfChain <= pThis->idxMaxLastCluster)
     1758        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Bogus formatter end-of-chain value: %#x, must be above %#x",
     1759                             pThis->idxEndOfChain, pThis->idxMaxLastCluster);
     1760
     1761    RT_NOREF(pbFatSector);
     1762    return VINF_SUCCESS;
     1763}
     1764
     1765
     1766/**
     1767 * Worker for RTFsFatVolOpen.
     1768 *
     1769 * @returns IPRT status code.
     1770 * @param   pThis           The FAT VFS instance to initialize.
     1771 * @param   hVfsSelf        The FAT VFS handle (no reference consumed).
     1772 * @param   hVfsBacking     The file backing the alleged FAT file system.
     1773 *                          Reference is consumed (via rtFsFatVol_Destroy).
     1774 * @param   fReadOnly       Readonly or readwrite mount.
     1775 * @param   offBootSector   The boot sector offset in bytes.
     1776 * @param   pErrInfo        Where to return additional error info.  Can be NULL.
     1777 */
     1778static int rtFsFatVolTryInit(PRTFSFATVOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking,
     1779                             bool fReadOnly, uint64_t offBootSector, PRTERRINFO pErrInfo)
     1780{
     1781    /*
     1782     * First initialize the state so that rtFsFatVol_Destroy won't trip up.
     1783     */
     1784    pThis->hVfsSelf             = hVfsSelf;
     1785    pThis->hVfsBacking          = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsFatVol_Destroy releases it. */
     1786    pThis->cbBacking            = 0;
     1787    pThis->offBootSector        = offBootSector;
     1788    pThis->fReadOnly            = fReadOnly;
     1789    pThis->cReservedSectors     = 1;
     1790
     1791    pThis->cbSector             = 512;
     1792    pThis->cbCluster            = 512;
     1793    pThis->cClusters            = 0;
     1794    pThis->offFirstCluster      = 0;
     1795    pThis->cbTotalSize          = 0;
     1796
     1797    pThis->enmFatType           = RTFSFATTYPE_INVALID;
     1798    pThis->cFatEntries          = 0;
     1799    pThis->cFats                = 0;
     1800    pThis->cbFat                = 0;
     1801    for (unsigned i = 0; i < RT_ELEMENTS(pThis->aoffFats); i++)
     1802        pThis->aoffFats[i]      = UINT64_MAX;
     1803    pThis->pFatCache            = NULL;
     1804
     1805    pThis->offRootDir           = UINT64_MAX;
     1806    pThis->idxRootDirCluster    = UINT32_MAX;
     1807    pThis->cRootDirEntries      = UINT32_MAX;
     1808    pThis->cbRootDir            = 0;
     1809    pThis->hVfsRootDir          = NIL_RTVFSDIR;
     1810    pThis->pRootDir             = NULL;
     1811
     1812    pThis->uSerialNo            = 0;
     1813    pThis->szLabel[0]           = '\0';
     1814    pThis->szType[0]            = '\0';
     1815    pThis->cBootSectorCopies    = 0;
     1816    pThis->fFat32Flags          = 0;
     1817    pThis->offBootSectorCopies  = UINT64_MAX;
     1818    pThis->offFat32InfoSector   = UINT64_MAX;
     1819    RT_ZERO(pThis->Fat32InfoSector);
     1820
     1821    /*
     1822     * Get stuff that may fail.
     1823     */
     1824    int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
     1825    if (RT_FAILURE(rc))
     1826        return rc;
     1827    pThis->cbTotalSize = pThis->cbBacking - pThis->offBootSector;
     1828
     1829    /*
     1830     * Read the boot sector and the following sector (start of the allocation
     1831     * table unless it a FAT32 FS).  We'll then validate the boot sector and
     1832     * start of the FAT, expanding the BPB into the instance data.
     1833     */
     1834    union
     1835    {
     1836        uint8_t             ab[512*2];
     1837        uint16_t            au16[512*2 / 2];
     1838        uint32_t            au32[512*2 / 4];
     1839        FATBOOTSECTOR       BootSector;
     1840        FAT32INFOSECTOR     InfoSector;
     1841    } Buf;
     1842    RT_ZERO(Buf);
     1843
     1844    rc = RTVfsFileReadAt(hVfsBacking, offBootSector, &Buf.BootSector, 512 * 2, NULL);
     1845    if (RT_FAILURE(rc))
     1846        return RTErrInfoSet(pErrInfo, rc, "Unable to read bootsect");
     1847
     1848    /*
     1849     * Extract info from the BPB and validate the two special FAT entries.
     1850     *
     1851     * Check the DOS signature first.  The PC-DOS 1.0 boot floppy does not have
     1852     * a signature and we ASSUME this is the case for all flopies formated by it.
     1853     */
     1854    if (Buf.BootSector.uSignature != FATBOOTSECTOR_SIGNATURE)
     1855    {
     1856        if (Buf.BootSector.uSignature != 0)
     1857            return RTErrInfoSetF(pErrInfo, rc, "No DOS bootsector signature: %#06x", Buf.BootSector.uSignature);
     1858        rc = rtFsFatVolTryInitDos1x(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
     1859    }
     1860    else
     1861        rc = rtFsFatVolTryInitDos2Plus(pThis, &Buf.BootSector, &Buf.ab[512], pErrInfo);
     1862    if (RT_FAILURE(rc))
     1863        return rc;
     1864
     1865    /*
     1866     * Setup the FAT cache.
     1867     */
     1868    rc = rtFsFatClusterMap_Create(pThis, &Buf.ab[512], pErrInfo);
     1869    if (RT_FAILURE(rc))
     1870        return rc;
     1871
     1872    /*
     1873     * Create the root directory fun.
     1874     */
     1875    if (pThis->idxRootDirCluster == UINT32_MAX)
     1876        rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, 0 /*offDirEntry*/,
     1877                            UINT32_MAX, pThis->offRootDir, pThis->cbRootDir,
     1878                            &pThis->hVfsRootDir, &pThis->pRootDir);
     1879    else
     1880        rc = rtFsFatDir_New(pThis, NULL /*pParentDir*/, NULL /*pDirEntry*/, 0 /*offDirEntry*/,
     1881                            pThis->idxRootDirCluster, UINT64_MAX, pThis->cbRootDir,
     1882                            &pThis->hVfsRootDir, &pThis->pRootDir);
     1883    if (RT_FAILURE(rc))
     1884        return rc;
     1885
     1886
     1887    return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED,
     1888                         "cbSector=%#x cbCluster=%#x cReservedSectors=%#x\n"
     1889                         "cFats=%#x cbFat=%#x offFirstFat=%#RX64 offSecondFat=%#RX64\n"
     1890                         "cbRootDir=%#x offRootDir=%#RX64 offFirstCluster=%#RX64",
     1891                         pThis->cbSector, pThis->cbCluster, pThis->cReservedSectors,
     1892                         pThis->cFats, pThis->cbFat, pThis->aoffFats[0], pThis->aoffFats[1],
     1893                         pThis->cbRootDir, pThis->offRootDir, pThis->offFirstCluster);
     1894}
     1895
     1896
     1897RTDECL(int) RTFsFatVolOpen(RTVFSFILE hVfsFileIn, bool fReadOnly, uint64_t offBootSector, PRTVFS phVfs, PRTERRINFO pErrInfo)
     1898{
     1899    /*
     1900     * Quick input validation.
     1901     */
     1902    AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
     1903    *phVfs = NIL_RTVFS;
     1904
     1905    uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
     1906    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
     1907
     1908    /*
     1909     * Create a new FAT VFS instance and try initialize it using the given input file.
     1910     */
     1911    RTVFS hVfs   = NIL_RTVFS;
     1912    void *pvThis = NULL;
     1913    int rc = RTVfsNew(&g_rtFsFatVolOps, sizeof(RTFSFATVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
     1914    if (RT_SUCCESS(rc))
     1915    {
     1916        rc = rtFsFatVolTryInit((PRTFSFATVOL)pvThis, hVfs, hVfsFileIn, fReadOnly, offBootSector, pErrInfo);
     1917        if (RT_SUCCESS(rc))
     1918            *phVfs = hVfs;
     1919        else
     1920            RTVfsRelease(hVfs);
     1921    }
     1922    else
     1923        RTVfsFileRelease(hVfsFileIn);
     1924    return rc;
    6161925}
    6171926
     
    6761985    {
    6771986        RTVFS hVfs;
    678         rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, &hVfs, pErrInfo);
     1987        rc = RTFsFatVolOpen(hVfsFileIn, pElement->uProvider != false, 0, &hVfs, pErrInfo);
    6791988        RTVfsFileRelease(hVfsFileIn);
    6801989        if (RT_SUCCESS(rc))
  • trunk/src/VBox/Runtime/common/filesystem/filesystemext.cpp

    r63561 r66615  
    317317}
    318318
    319 static DECLCALLBACK(void) rtFsExtDestroy(void *pvThis)
     319
     320/**
     321 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
     322 */
     323static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
    320324{
    321325    PRTFILESYSTEMEXT pThis = (PRTFILESYSTEMEXT)pvThis;
     
    323327    if (pThis->pBlkGrpDesc)
    324328        RTMemFree(pThis->pBlkGrpDesc);
    325 }
     329
     330    return VINF_SUCCESS;
     331}
     332
     333
     334/**
     335 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
     336 */
     337static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
     338{
     339    RT_NOREF(pvThis, pObjInfo, enmAddAttr);
     340    return VERR_WRONG_TYPE;
     341}
     342
    326343
    327344static DECLCALLBACK(int) rtFsExtOpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
     
    376393}
    377394
     395
    378396DECL_HIDDEN_CONST(RTFILESYSTEMDESC) const g_rtFsExt =
    379397{
    380     /** cbFs */
    381     sizeof(RTFILESYSTEMEXT),
    382     /** VfsOps */
    383     {
    384         /** uVersion. */
    385         RTVFSOPS_VERSION,
    386         /** fFeatures */
    387         0,
    388         /** pszName */
    389         "ExtVfsOps",
    390         /** pfnDestroy */
    391         rtFsExtDestroy,
    392         /** pfnOpenRoot */
    393         rtFsExtOpenRoot,
    394         /** pfnIsRangeInUse */
    395         rtFsExtIsRangeInUse,
    396         /** uEndMarker */
    397         RTVFSOPS_VERSION
     398    /* .cbFs = */       sizeof(RTFILESYSTEMEXT),
     399    /* .VfsOps = */
     400    {
     401        /* .Obj = */
     402        {
     403            /* .uVersion = */       RTVFSOBJOPS_VERSION,
     404            /* .enmType = */        RTVFSOBJTYPE_VFS,
     405            /* .pszName = */        "ExtVol",
     406            /* .pfnClose = */       rtFsExtVol_Close,
     407            /* .pfnQueryInfo = */   rtFsExtVol_QueryInfo,
     408            /* .uEndMarker = */     RTVFSOBJOPS_VERSION
     409        },
     410        /* .uVersion = */           RTVFSOPS_VERSION,
     411        /* .fFeatures = */          0,
     412        /* .pfnOpenRoot = */        rtFsExtOpenRoot,
     413        /* .pfnIsRangeInUse = */    rtFsExtIsRangeInUse,
     414        /* .uEndMarker = */         RTVFSOPS_VERSION
    398415    },
    399     /** pfnProbe */
    400     rtFsExtProbe,
    401     /** pfnInit */
    402     rtFsExtInit
     416    /* .pfnProbe = */   rtFsExtProbe,
     417    /* .pfnInit = */    rtFsExtInit
    403418};
    404419
  • trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp

    r66601 r66615  
    8080        AssertPtr((a_pSetOps)->pfnSetOwner); \
    8181        Assert((a_pSetOps)->uEndMarker == RTVFSOBJSETOPS_VERSION); \
     82    } while (0)
     83
     84/** Asserts that the VFS directory vtable is valid. */
     85#define RTVFSDIR_ASSERT_OPS(pDirOps, a_enmType) \
     86    do { \
     87        RTVFSOBJ_ASSERT_OPS(&(pDirOps)->Obj, a_enmType); \
     88        RTVFSOBJSET_ASSERT_OPS(&(pDirOps)->ObjSet, RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet)); \
     89        Assert((pDirOps)->uVersion == RTVFSDIROPS_VERSION); \
     90        Assert(!(pDirOps)->fReserved); \
     91        AssertPtr((pDirOps)->pfnTraversalOpen); \
     92        AssertPtr((pDirOps)->pfnOpenFile); \
     93        AssertPtr((pDirOps)->pfnOpenDir); \
     94        AssertPtr((pDirOps)->pfnCreateDir); \
     95        AssertPtr((pDirOps)->pfnOpenSymlink); \
     96        AssertPtr((pDirOps)->pfnCreateSymlink); \
     97        AssertPtr((pDirOps)->pfnUnlinkEntry); \
     98        AssertPtr((pDirOps)->pfnRewindDir); \
     99        AssertPtr((pDirOps)->pfnReadDir); \
     100        Assert((pDirOps)->uEndMarker == RTVFSDIROPS_VERSION); \
    82101    } while (0)
    83102
     
    16341653    AssertReturn(pVfsOps->uVersion   == RTVFSOPS_VERSION, VERR_VERSION_MISMATCH);
    16351654    AssertReturn(pVfsOps->uEndMarker == RTVFSOPS_VERSION, VERR_VERSION_MISMATCH);
     1655    RTVFSOBJ_ASSERT_OPS(&pVfsOps->Obj, RTVFSOBJTYPE_VFS);
    16361656    Assert(cbInstance > 0);
    16371657    AssertPtr(ppvInstance);
     
    16471667        return VERR_NO_MEMORY;
    16481668
    1649     int rc = rtVfsObjInitNewObject(&pThis->Base, NULL, hVfs, hLock,
     1669    int rc = rtVfsObjInitNewObject(&pThis->Base, &pVfsOps->Obj, hVfs, hLock,
    16501670                                   (char *)pThis + RT_ALIGN_Z(sizeof(*pThis), RTVFS_INST_ALIGNMENT));
    16511671    if (RT_FAILURE(rc))
     
    16601680    *phVfs       = pThis;
    16611681    *ppvInstance = pThis->Base.pvThis;
     1682
    16621683    return VINF_SUCCESS;
    16631684}
     
    18321853 *
    18331854 */
     1855
     1856
     1857RTDECL(int) RTVfsNewDir(PCRTVFSDIROPS pDirOps, size_t cbInstance, uint32_t fFlags, RTVFS hVfs, RTVFSLOCK hLock,
     1858                        PRTVFSDIR phVfsDir, void **ppvInstance)
     1859{
     1860    /*
     1861     * Validate the input, be extra strict in strict builds.
     1862     */
     1863    AssertPtr(pDirOps);
     1864    AssertReturn(pDirOps->uVersion   == RTVFSDIROPS_VERSION, VERR_VERSION_MISMATCH);
     1865    AssertReturn(pDirOps->uEndMarker == RTVFSDIROPS_VERSION, VERR_VERSION_MISMATCH);
     1866    Assert(!pDirOps->fReserved);
     1867    RTVFSDIR_ASSERT_OPS(pDirOps, RTVFSOBJTYPE_DIR);
     1868    Assert(cbInstance > 0);
     1869    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
     1870    AssertPtr(ppvInstance);
     1871    AssertPtr(phVfsDir);
     1872    RTVFS_ASSERT_VALID_HANDLE_OR_NIL_RETURN(hVfs, VERR_INVALID_HANDLE);
     1873
     1874    /*
     1875     * Allocate the handle + instance data.
     1876     */
     1877    size_t const cbThis = RT_ALIGN_Z(sizeof(RTVFSDIRINTERNAL), RTVFS_INST_ALIGNMENT)
     1878                        + RT_ALIGN_Z(cbInstance, RTVFS_INST_ALIGNMENT);
     1879    RTVFSDIRINTERNAL *pThis = (RTVFSDIRINTERNAL *)RTMemAllocZ(cbThis);
     1880    if (!pThis)
     1881        return VERR_NO_MEMORY;
     1882
     1883    int rc = rtVfsObjInitNewObject(&pThis->Base, &pDirOps->Obj, hVfs, hLock,
     1884                                   (char *)pThis + RT_ALIGN_Z(sizeof(*pThis), RTVFS_INST_ALIGNMENT));
     1885    if (RT_FAILURE(rc))
     1886    {
     1887        RTMemFree(pThis);
     1888        return rc;
     1889    }
     1890
     1891    pThis->uMagic       = RTVFSDIR_MAGIC;
     1892    pThis->fReserved    = 0;
     1893    pThis->pOps         = pDirOps;
     1894
     1895    *phVfsDir    = pThis;
     1896    *ppvInstance = pThis->Base.pvThis;
     1897    return VINF_SUCCESS;
     1898}
     1899
    18341900
    18351901RTDECL(uint32_t) RTVfsDirRetain(RTVFSDIR hVfsDir)
     
    28012867
    28022868
     2869RTDECL(int) RTVfsFileSgRead(RTVFSFILE hVfsFile, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
     2870{
     2871    AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
     2872    if (pcbRead)
     2873        *pcbRead = 0;
     2874    RTVFSFILEINTERNAL *pThis = hVfsFile;
     2875    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     2876    AssertReturn(pThis->uMagic == RTVFSFILE_MAGIC, VERR_INVALID_HANDLE);
     2877
     2878    return RTVfsIoStrmSgRead(&pThis->Stream, off, pSgBuf, fBlocking, pcbRead);
     2879}
     2880
     2881
     2882RTDECL(int) RTVfsFileSgWrite(RTVFSFILE hVfsFile, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
     2883{
     2884    AssertPtrNullReturn(pcbWritten, VERR_INVALID_POINTER);
     2885    if (pcbWritten)
     2886        *pcbWritten = 0;
     2887    RTVFSFILEINTERNAL *pThis = hVfsFile;
     2888    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     2889    AssertReturn(pThis->uMagic == RTVFSFILE_MAGIC, VERR_INVALID_HANDLE);
     2890
     2891    return RTVfsIoStrmSgWrite(&pThis->Stream, off, pSgBuf, fBlocking, pcbWritten);
     2892}
     2893
     2894
    28032895RTDECL(int) RTVfsFileFlush(RTVFSFILE hVfsFile)
    28042896{
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette