VirtualBox

Changeset 66676 in vbox for trunk


Ignore:
Timestamp:
Apr 26, 2017 12:00:54 PM (8 years ago)
Author:
vboxsync
Message:

fatvfs.cpp: Implemented file creation and opening of long filenames.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/formats/fat.h

    r66669 r66676  
    520520    /** The directory entry name.
    521521     * First character serves as a flag to indicate deleted or not. */
    522     char            achName[8+3];
     522    uint8_t         achName[8+3];
    523523    /** Attributes (FAT_ATTR_XXX). */
    524524    uint8_t         fAttrib;
     
    588588/** @} */
    589589
    590 /** @name FATDIRENTRY_CASE_F_XXX - FATDIRENTRY::fCase flags.
     590/** @name FATDIRENTRY_CH0_XXX - FATDIRENTRY::achName[0]
    591591 * @{ */
    592592/** Deleted entry. */
     
    636636typedef FATDIRNAMESLOT const *PCFATDIRNAMESLOT;
    637637
     638/** Slot ID flag indicating that it's the first slot. */
     639#define FATDIRNAMESLOT_FIRST_SLOT_FLAG  UINT8_C(0x40)
     640/** Highest slot ID recognized.  This allows for 260 characters, however many
     641 * implementation limits it to 255 or 250. */
     642#define FATDIRNAMESLOT_HIGHEST_SLOT_ID  UINT8_C(0x14)
     643/** Max number of slots recognized.  (This is the same as the higest slot ID
     644 * because the 0 isn't a valid ID.) */
     645#define FATDIRNAMESLOT_MAX_SLOTS        FATDIRNAMESLOT_HIGHEST_SLOT_ID
     646/** Number of UTF-16 units per slot. */
     647#define FATDIRNAMESLOT_CHARS_PER_SLOT   (5 + 6 + 2)
     648
     649
    638650
    639651/**
  • trunk/src/VBox/Runtime/common/filesystem/fatvfs.cpp

    r66674 r66676  
    5151*   Defined Constants And Macros                                                                                                 *
    5252*********************************************************************************************************************************/
    53 /** Gets the cluster from a directory entry.
     53/**
     54 * Gets the cluster from a directory entry.
     55 *
    5456 * @param   a_pDirEntry Pointer to the directory entry.
    5557 * @param   a_pVol      Pointer to the volume.
     
    5961      ? RT_MAKE_U32((a_pDirEntry)->idxCluster, (a_pDirEntry)->u.idxClusterHigh) \
    6062      : (a_pDirEntry)->idxCluster )
     63
     64/**
     65 * Rotates a unsigned 8-bit value one bit to the right.
     66 *
     67 * @returns Rotated 8-bit value.
     68 * @param   a_bValue        The value to rotate.
     69 */
     70#define RTFSFAT_ROT_R1_U8(a_bValue) (((a_bValue) >> 1) | (uint8_t)((a_bValue) << 7))
    6171
    6272
     
    478488    pChain->cClusters           = 0;
    479489    RTListInit(&pChain->ListParts);
     490}
     491
     492
     493/**
     494 * Deletes a chain, freeing it's resources.
     495 *
     496 * @param   pChain              The chain.
     497 */
     498static void rtFsFatChain_Delete(PRTFSFATCHAIN pChain)
     499{
     500    Assert(RT_IS_POWER_OF_TWO(pChain->cbCluster));
     501    Assert(RT_BIT_32(pChain->cClusterByteShift) == pChain->cbCluster);
     502
     503    PRTFSFATCHAINPART pPart = RTListRemoveLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
     504    while (pPart)
     505    {
     506        RTMemFree(pPart);
     507        pPart = RTListRemoveLast(&pChain->ListParts, RTFSFATCHAINPART, ListEntry);
     508    }
     509
     510    pChain->cbChain   = 0;
     511    pChain->cClusters = 0;
    480512}
    481513
     
    12811313
    12821314/**
     1315 * Allocates clusters.
     1316 *
     1317 * Will free the clusters if it fails to allocate all of them.
     1318 *
     1319 * @returns IPRT status code.
     1320 * @param   pThis           The FAT volume instance.
     1321 * @param   pChain          The chain.
     1322 * @param   cClusters       Number of clusters to add to the chain.
     1323 */
     1324static int rtFsFatClusterMap_AllocateMoreClusters(PRTFSFATVOL pThis, PRTFSFATCHAIN pChain, uint32_t cClusters)
     1325{
     1326    int             rc                  = VINF_SUCCESS;
     1327    uint32_t const  cOldClustersInChain = pChain->cClusters;
     1328    uint32_t const  idxOldLastCluster   = rtFsFatChain_GetLastCluster(pChain);
     1329    uint32_t        idxPrevCluster      = idxOldLastCluster;
     1330    uint32_t        iCluster            = 0;
     1331    while (iCluster < cClusters)
     1332    {
     1333        uint32_t idxCluster;
     1334        rc = rtFsFatClusterMap_AllocateCluster(pThis, idxPrevCluster, &idxCluster);
     1335        if (RT_SUCCESS(rc))
     1336        {
     1337            rc = rtFsFatChain_Append(pChain, idxCluster);
     1338            if (RT_SUCCESS(rc))
     1339            {
     1340                /* next */
     1341                iCluster++;
     1342                continue;
     1343            }
     1344
     1345            /* Bail out, freeing any clusters we've managed to allocate by now. */
     1346            rtFsFatClusterMap_FreeCluster(pThis, idxCluster);
     1347        }
     1348        if (idxOldLastCluster != UINT32_MAX)
     1349            rtFsFatClusterMap_SetEndOfChain(pThis, idxOldLastCluster);
     1350        while (iCluster-- > 0)
     1351            rtFsFatClusterMap_FreeCluster(pThis, rtFsFatChain_GetClusterByIndex(pChain, cOldClustersInChain + iCluster));
     1352        rtFsFatChain_Shrink(pChain, iCluster);
     1353        break;
     1354    }
     1355    return rc;
     1356}
     1357
     1358
     1359
     1360/**
    12831361 * Converts a FAT timestamp into an IPRT timesspec.
    12841362 *
     
    12891367 * @param   pVol            The volume.
    12901368 */
    1291 static void rtFsFatDosDateTimeToSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime,
     1369static void rtFsFatDateTime2TimeSpec(PRTTIMESPEC pTimeSpec, uint16_t uDate, uint16_t uTime,
    12921370                                     uint8_t cCentiseconds, PCRTFSFATVOL pVol)
    12931371{
     
    13151393
    13161394    RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
    1317     RTTimeSpecAddNano(pTimeSpec, pVol->offNanoUTC);
     1395    RTTimeSpecSubNano(pTimeSpec, pVol->offNanoUTC);
     1396}
     1397
     1398
     1399/**
     1400 * Converts an IPRT timespec to a FAT timestamp.
     1401 *
     1402 * @returns The centiseconds part.
     1403 * @param   pVol        The volume.
     1404 * @param   pTimeSpec   The IPRT timespec to convert (UTC).
     1405 * @param   puDate      Where to return the date part of the FAT timestamp.
     1406 * @param   puTime      Where to return the time part of the FAT timestamp.
     1407 */
     1408static uint8_t rtFsFatTimeSpec2FatDateTime(PCRTFSFATVOL pVol, PCRTTIMESPEC pTimeSpec, uint16_t *puDate, uint16_t *puTime)
     1409{
     1410    RTTIMESPEC  TimeSpec = *pTimeSpec;
     1411    RTTIME      Time;
     1412    RTTimeExplode(&Time, RTTimeSpecSubNano(&TimeSpec, pVol->offNanoUTC));
     1413
     1414    if (puDate)
     1415        *puDate = ((RT_MIN(Time.i32Year, 1980) - 1980) << 9)
     1416                | (Time.u8Month << 5)
     1417                | Time.u8MonthDay;
     1418    if (puTime)
     1419        *puTime = (Time.u8Hour   << 11)
     1420                | (Time.u8Minute << 5)
     1421                | (Time.u8Second >> 1);
     1422    return (Time.u8Second & 1) * 100 + Time.u32Nanosecond / 10000000;
     1423
     1424}
     1425
     1426
     1427/**
     1428 * Gets the current FAT timestamp.
     1429 *
     1430 * @returns The centiseconds part.
     1431 * @param   pVol     The volume.
     1432 * @param   puDate   Where to return the date part of the FAT timestamp.
     1433 * @param   puTime   Where to return the time part of the FAT timestamp.
     1434 */
     1435static uint8_t rtFsFatCurrentFatDateTime(PCRTFSFATVOL pVol, uint16_t *puDate, uint16_t *puTime)
     1436{
     1437    RTTIMESPEC TimeSpec;
     1438    return rtFsFatTimeSpec2FatDateTime(pVol, RTTimeNow(&TimeSpec), puDate, puTime);
    13181439}
    13191440
     
    13401461    pObj->fMaybeDirtyFat    = false;
    13411462    pObj->fMaybeDirtyDirEnt = false;
    1342     rtFsFatDosDateTimeToSpec(&pObj->ModificationTime, pDirEntry->uModifyDate, pDirEntry->uModifyTime, 0, pVol);
    1343     rtFsFatDosDateTimeToSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds, pVol);
    1344     rtFsFatDosDateTimeToSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0, pVol);
     1463    rtFsFatDateTime2TimeSpec(&pObj->ModificationTime, pDirEntry->uModifyDate, pDirEntry->uModifyTime, 0, pVol);
     1464    rtFsFatDateTime2TimeSpec(&pObj->BirthTime, pDirEntry->uBirthDate, pDirEntry->uBirthTime, pDirEntry->uBirthCentiseconds, pVol);
     1465    rtFsFatDateTime2TimeSpec(&pObj->AccessTime, pDirEntry->uAccessDate, 0, 0, pVol);
    13451466}
    13461467
     
    14111532    if (pObj->pParentDir)
    14121533        rtFsFatDir_RemoveOpenChild(pObj->pParentDir, pObj);
     1534    rtFsFatChain_Delete(&pObj->Clusters);
    14131535    return rc;
    14141536}
     
    15021624    uint32_t cbFileLeft = pThis->Core.cbObject - (uint32_t)off;
    15031625    uint32_t cbRead     = 0;
    1504     uint32_t cbLeft     = pSgBuf->paSegs[0].cbSeg;
     1626    size_t  cbLeft     = pSgBuf->paSegs[0].cbSeg;
    15051627    uint8_t *pbDst      = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
    15061628    while (cbLeft > 0)
     
    15131635                uint32_t cbToRead = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1));
    15141636                if (cbToRead > cbLeft)
    1515                     cbToRead = cbLeft;
     1637                    cbToRead = (uint32_t)cbLeft;
    15161638                if (cbToRead > cbFileLeft)
    15171639                    cbToRead = cbFileLeft;
     
    16491771    int            rc         = VINF_SUCCESS;
    16501772    uint32_t       cbWritten  = 0;
    1651     uint32_t       cbLeft     = pSgBuf->paSegs[0].cbSeg;
     1773    size_t         cbLeft     = pSgBuf->paSegs[0].cbSeg;
    16521774    uint8_t const *pbSrc      = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
    16531775    while (cbLeft > 0)
     
    16561778        uint32_t cbToWrite = pThis->Core.Clusters.cbCluster - ((uint32_t)off & (pThis->Core.Clusters.cbCluster - 1));
    16571779        if (cbToWrite > cbLeft)
    1658             cbToWrite = cbLeft;
     1780            cbToWrite = (uint32_t)cbLeft;
    16591781        uint64_t offNew = (uint64_t)off + cbToWrite;
    16601782        if (offNew < _4G)
     
    21492271
    21502272
     2273/**
     2274 * Release a directory buffer after done reading from it.
     2275 *
     2276 * This is currently just a placeholder.
     2277 *
     2278 * @param   pThis           The directory.
     2279 * @param   uBufferReadLock The buffer lock.
     2280 */
    21512281static void rtFsFatDir_ReleaseBufferAfterReading(PRTFSFATDIR pThis, uint32_t uBufferReadLock)
    21522282{
     
    22902420
    22912421/**
     2422 * Calculates the checksum of a directory entry.
     2423 * @returns Checksum.
     2424 * @param   pDirEntry           The directory entry to checksum.
     2425 */
     2426static uint8_t rtFsFatDir_CalcChecksum(PCFATDIRENTRY pDirEntry)
     2427{
     2428    uint8_t bChecksum = pDirEntry->achName[0];
     2429    for (uint8_t off = 1; off < RT_ELEMENTS(pDirEntry->achName); off++)
     2430    {
     2431        bChecksum = RTFSFAT_ROT_R1_U8(bChecksum);
     2432        bChecksum += pDirEntry->achName[off];
     2433    }
     2434    return bChecksum;
     2435}
     2436
     2437
     2438/**
    22922439 * Locates a directory entry in a directory.
    22932440 *
     
    23102457     * Turn pszEntry into a 8.3 filename, if possible.
    23112458     */
    2312     char szName8Dot3[12+1];
    2313     bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3(szName8Dot3, pszEntry);
     2459    char    szName8Dot3[12+1];
     2460    bool    fIs8Dot3Name = rtFsFatDir_StringTo8Dot3(szName8Dot3, pszEntry);
    23142461
    23152462    /*
    23162463     * Scan the directory buffer by buffer.
    23172464     */
     2465    RTUTF16             wszName[260+1];
     2466    uint8_t             bChecksum       = UINT8_MAX;
     2467    uint8_t             idNextSlot      = UINT8_MAX;
     2468    size_t              cwcName         = 0;
    23182469    uint32_t            offEntryInDir   = 0;
    23192470    uint32_t const      cbDir           = pThis->Core.cbObject;
    23202471    Assert(RT_ALIGN_32(cbDir, sizeof(*pDirEntry)) == cbDir);
     2472    AssertCompile(FATDIRNAMESLOT_MAX_SLOTS * FATDIRNAMESLOT_CHARS_PER_SLOT < RT_ELEMENTS(wszName));
     2473    wszName[260] = '\0';
    23212474
    23222475    while (offEntryInDir < cbDir)
     
    23402493                    break;
    23412494                case FATDIRENTRY_CH0_DELETED:
     2495                    cwcName = 0;
    23422496                    continue;
    23432497                case FATDIRENTRY_CH0_END_OF_DIR:
     
    23472501                        return VERR_FILE_NOT_FOUND;
    23482502                    }
     2503                    cwcName = 0;
    23492504                    break; /* Technically a valid entry before DOS 2.0, or so some claim. */
    23502505            }
     2506
     2507            /*
     2508             * Check for long filename slot.
     2509             */
    23512510            if (   paEntries[iEntry].Slot.fAttrib == FAT_ATTR_NAME_SLOT
    2352                 && (paEntries[iEntry].Slot.idSlot -  (uint8_t)'A') <= 20
    23532511                && paEntries[iEntry].Slot.idxZero == 0
    2354                 && paEntries[iEntry].Slot.fZero   == 0)
     2512                && paEntries[iEntry].Slot.fZero   == 0
     2513                && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) <= FATDIRNAMESLOT_HIGHEST_SLOT_ID
     2514                && (paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG) != 0)
    23552515            {
    2356                 /** @todo long filenames.   */
     2516                /* New slot? */
     2517                if (paEntries[iEntry].Slot.idSlot & FATDIRNAMESLOT_FIRST_SLOT_FLAG)
     2518                {
     2519                    idNextSlot = paEntries[iEntry].Slot.idSlot & ~FATDIRNAMESLOT_FIRST_SLOT_FLAG;
     2520                    bChecksum  = paEntries[iEntry].Slot.bChecksum;
     2521                    cwcName    = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
     2522                    wszName[cwcName] = '\0';
     2523                }
     2524                /* Is valid next entry? */
     2525                else if (   paEntries[iEntry].Slot.idSlot    == idNextSlot
     2526                         && paEntries[iEntry].Slot.bChecksum == bChecksum)
     2527                { /* likely */ }
     2528                else
     2529                    cwcName = 0;
     2530                if (cwcName)
     2531                {
     2532                    idNextSlot--;
     2533                    size_t offName = idNextSlot * FATDIRNAMESLOT_CHARS_PER_SLOT;
     2534                    memcpy(&wszName[offName],         paEntries[iEntry].Slot.awcName0, sizeof(paEntries[iEntry].Slot.awcName0));
     2535                    memcpy(&wszName[offName + 5],     paEntries[iEntry].Slot.awcName1, sizeof(paEntries[iEntry].Slot.awcName1));
     2536                    memcpy(&wszName[offName + 5 + 6], paEntries[iEntry].Slot.awcName2, sizeof(paEntries[iEntry].Slot.awcName2));
     2537                }
    23572538            }
     2539            /*
     2540             * Regular directory entry. Do the matching, first 8.3 then long name.
     2541             */
    23582542            else if (   fIs8Dot3Name
    23592543                     && memcmp(paEntries[iEntry].Entry.achName, szName8Dot3, sizeof(paEntries[iEntry].Entry.achName)) == 0)
     
    23652549                return VINF_SUCCESS;
    23662550            }
     2551            else if (   cwcName != 0
     2552                     && idNextSlot == 0
     2553                     && rtFsFatDir_CalcChecksum(&paEntries[iEntry].Entry) == bChecksum
     2554                     && RTUtf16ICmpUtf8(wszName, pszEntry) == 0)
     2555            {
     2556                *poffEntryInDir = offEntryInDir;
     2557                *pDirEntry      = paEntries[iEntry].Entry;
     2558                *pfLong         = true;
     2559                rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     2560                return VINF_SUCCESS;
     2561            }
     2562            else
     2563                cwcName = 0;
    23672564        }
    23682565        rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     
    23722569}
    23732570
     2571
     2572/**
     2573 * Calculates the FATDIRENTRY::fCase flags for the given name.
     2574 *
     2575 * ASSUMES that the name is a 8.3 name.
     2576 *
     2577 * @returns Case flag mask.
     2578 * @param   pszName             The name.
     2579 */
     2580static uint8_t rtFsFatDir_CalcCaseFlags(const char *pszName)
     2581{
     2582    uint8_t bRet      = FATDIRENTRY_CASE_F_LOWER_BASE | FATDIRENTRY_CASE_F_LOWER_EXT;
     2583    uint8_t bCurrent  = FATDIRENTRY_CASE_F_LOWER_BASE;
     2584    for (;;)
     2585    {
     2586        RTUNICP uc;
     2587        int rc = RTStrGetCpEx(&pszName, &uc);
     2588        if (RT_SUCCESS(rc))
     2589        {
     2590            if (uc != 0)
     2591            {
     2592                if (uc != '.')
     2593                {
     2594                    if (RTUniCpIsUpper(uc))
     2595                    {
     2596                        bRet &= ~bCurrent;
     2597                        if (!bRet)
     2598                            return 0;
     2599                    }
     2600                }
     2601                else
     2602                    bCurrent = FATDIRENTRY_CASE_F_LOWER_EXT;
     2603            }
     2604            else if (bCurrent == FATDIRENTRY_CASE_F_LOWER_BASE)
     2605                return bRet & ~FATDIRENTRY_CASE_F_LOWER_EXT;
     2606            else
     2607                return bRet;
     2608        }
     2609        else
     2610            return 0;
     2611    }
     2612}
     2613
     2614
     2615/**
     2616 * Considers whether we need to create a long name or not.
     2617 *
     2618 * If a long name is needed and the name wasn't 8-dot-3 compatible, a 8-dot-3
     2619 * name will be generated and stored in *pDirEntry.
     2620 *
     2621 * @returns IPRT status code
     2622 * @param   pThis           The directory.
     2623 * @param   pszEntry        The name.
     2624 * @param   fIs8Dot3Name    Whether we have a 8-dot-3 name already.
     2625 * @param   pDirEntry       Where to return the generated 8-dot-3 name.
     2626 * @param   paSlots         Where to return the long name entries.  The array
     2627 *                          can hold at least FATDIRNAMESLOT_MAX_SLOTS entries.
     2628 * @param   pcSlots         Where to return the actual number of slots used.
     2629 */
     2630static int rtFsFatDir_MaybeCreateLongNameAndShortAlias(PRTFSFATDIR pThis, const char *pszEntry, bool fIs8Dot3Name,
     2631                                                       PFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t *pcSlots)
     2632{
     2633    RT_NOREF(pThis, pDirEntry, paSlots, pszEntry);
     2634    if (fIs8Dot3Name)
     2635    {
     2636        *pcSlots = 0;
     2637        return VINF_SUCCESS;
     2638    }
     2639    *pcSlots = UINT32_MAX;
     2640    return VERR_INVALID_NAME;
     2641}
     2642
     2643
     2644/**
     2645 * Searches the directory for a given number of free directory entries.
     2646 *
     2647 * The free entries must be consecutive of course.
     2648 *
     2649 * @returns IPRT status code.
     2650 * @retval  VERR_DISK_FULL if no space was found, *pcFreeTail set.
     2651 * @param   pThis           The directory to search.
     2652 * @param   cEntriesNeeded  How many entries we need.
     2653 * @param   poffEntryInDir  Where to return the offset of the first entry we
     2654 *                          found.
     2655 * @param   pcFreeTail      Where to return the number of free entries at the
     2656 *                          end of the directory when VERR_DISK_FULL is
     2657 *                          returned.
     2658 */
     2659static int rtFsFatChain_FindFreeEntries(PRTFSFATDIR pThis, uint32_t cEntriesNeeded,
     2660                                        uint32_t *poffEntryInDir, uint32_t *pcFreeTail)
     2661{
     2662    uint32_t            offStartFreeEntries = UINT32_MAX;
     2663    uint32_t            cFreeEntries        = 0;
     2664    uint32_t            offEntryInDir       = 0;
     2665    uint32_t const      cbDir               = pThis->Core.cbObject;
     2666    Assert(RT_ALIGN_32(cbDir, sizeof(FATDIRENTRY)) == cbDir);
     2667    while (offEntryInDir < cbDir)
     2668    {
     2669        /* Get chunk of entries starting at offEntryInDir. */
     2670        uint32_t            uBufferLock = UINT32_MAX;
     2671        uint32_t            cEntries    = 0;
     2672        PCFATDIRENTRYUNION  paEntries   = NULL;
     2673        int rc = rtFsFatDir_GetEntriesAt(pThis, offEntryInDir, &paEntries, &cEntries, &uBufferLock);
     2674        if (RT_FAILURE(rc))
     2675            return rc;
     2676
     2677        /*
     2678         * Now work thru each of the entries.
     2679         */
     2680        for (uint32_t iEntry = 0; iEntry < cEntries; iEntry++, offEntryInDir += sizeof(FATDIRENTRY))
     2681        {
     2682            uint8_t const bFirst = paEntries[iEntry].Entry.achName[0];
     2683            if (   bFirst == FATDIRENTRY_CH0_DELETED
     2684                || bFirst == FATDIRENTRY_CH0_END_OF_DIR)
     2685            {
     2686                if (offStartFreeEntries != UINT32_MAX)
     2687                    cFreeEntries++;
     2688                else
     2689                {
     2690                    offStartFreeEntries = offEntryInDir;
     2691                    cFreeEntries        = 1;
     2692                }
     2693                if (cFreeEntries >= cEntriesNeeded)
     2694                {
     2695                    *pcFreeTail     = cEntriesNeeded;
     2696                    *poffEntryInDir = offStartFreeEntries;
     2697                    rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     2698                    return VINF_SUCCESS;
     2699                }
     2700
     2701                if (bFirst == FATDIRENTRY_CH0_END_OF_DIR)
     2702                {
     2703                    if (pThis->Core.pVol->enmBpbVersion >= RTFSFATBPBVER_DOS_2_0)
     2704                    {
     2705                        rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     2706                        *pcFreeTail = cFreeEntries = (cbDir - offStartFreeEntries) / sizeof(FATDIRENTRY);
     2707                        if (cFreeEntries >= cEntriesNeeded)
     2708                        {
     2709                            *poffEntryInDir = offStartFreeEntries;
     2710                            rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     2711                            return VINF_SUCCESS;
     2712                        }
     2713                        return VERR_DISK_FULL;
     2714                    }
     2715                }
     2716            }
     2717            else if (offStartFreeEntries != UINT32_MAX)
     2718            {
     2719                offStartFreeEntries = UINT32_MAX;
     2720                cFreeEntries        = 0;
     2721            }
     2722        }
     2723        rtFsFatDir_ReleaseBufferAfterReading(pThis, uBufferLock);
     2724    }
     2725
     2726    *pcFreeTail     = cFreeEntries;
     2727    *poffEntryInDir = UINT32_MAX;
     2728    return VERR_DISK_FULL;
     2729}
     2730
     2731
     2732/**
     2733 * Try grow the directory.
     2734 *
     2735 * This is not called on the root directory.
     2736 *
     2737 * @returns IPRT status code.
     2738 * @retval  VERR_DISK_FULL if we failed to allocated new space.
     2739 * @param   pThis           The directory to grow.
     2740 * @param   cMinNewEntries  The minimum number of new entries to allocated.
     2741 */
     2742static int rtFsFatChain_GrowDirectory(PRTFSFATDIR pThis, uint32_t cMinNewEntries)
     2743{
     2744    RT_NOREF(pThis, cMinNewEntries);
     2745    return VERR_DISK_FULL;
     2746}
     2747
     2748
     2749/**
     2750 * Inserts a directory with zero of more long name slots preceeding it.
     2751 *
     2752 * @returns IPRT status code.
     2753 * @param   pThis           The directory.
     2754 * @param   pDirEntry       The directory entry.
     2755 * @param   paSlots         The long name slots.
     2756 * @param   cSlots          The number of long name slots.
     2757 * @param   poffEntryInDir  Where to return the directory offset.
     2758 */
     2759static int rtFsFatChain_InsertEntries(PRTFSFATDIR pThis, PCFATDIRENTRY pDirEntry, PFATDIRNAMESLOT paSlots, uint32_t cSlots,
     2760                                      uint32_t *poffEntryInDir)
     2761{
     2762    uint32_t const cTotalEntries = cSlots + 1;
     2763
     2764    /*
     2765     * Find somewhere to put the entries.  Try extend the directory if we're
     2766     * not successful at first.
     2767     */
     2768    uint32_t cFreeTailEntries;
     2769    uint32_t offFirstInDir;
     2770    int rc = rtFsFatChain_FindFreeEntries(pThis, cTotalEntries, &offFirstInDir, &cFreeTailEntries);
     2771    if (rc == VERR_DISK_FULL)
     2772    {
     2773        Assert(cFreeTailEntries < cTotalEntries);
     2774
     2775        /* Try grow it and use the newly allocated space. */
     2776        if (   pThis->Core.pParentDir
     2777            && pThis->cEntries < _64K /* Don't grow beyond 64K entries */)
     2778        {
     2779            offFirstInDir = pThis->Core.cbObject - cFreeTailEntries * sizeof(FATDIRENTRY);
     2780            rc = rtFsFatChain_GrowDirectory(pThis, cTotalEntries - cFreeTailEntries);
     2781        }
     2782
     2783        if (rc == VERR_DISK_FULL)
     2784        {
     2785            /** @todo Try compact the directory if we couldn't grow it. */
     2786        }
     2787    }
     2788    if (RT_SUCCESS(rc))
     2789    {
     2790        /*
     2791         * Update the directory.
     2792         */
     2793        uint32_t      offCurrent = offFirstInDir;
     2794        for (uint32_t iSrcSlot   = 0; iSrcSlot < cTotalEntries; iSrcSlot++, offCurrent += sizeof(FATDIRENTRY))
     2795        {
     2796            uint32_t        uBufferLock;
     2797            PFATDIRENTRY    pDstEntry;
     2798            rc = rtFsFatDir_GetEntryForUpdate(pThis, offCurrent, &pDstEntry, &uBufferLock);
     2799            if (RT_SUCCESS(rc))
     2800            {
     2801                if (iSrcSlot < cSlots)
     2802                    memcpy(pDstEntry, &paSlots[iSrcSlot], sizeof(*pDstEntry));
     2803                else
     2804                    memcpy(pDstEntry, pDirEntry,          sizeof(*pDstEntry));
     2805                rc = rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
     2806                if (RT_SUCCESS(rc))
     2807                    continue;
     2808
     2809                /*
     2810                 * Bail out: Try mark any edited entries as deleted.
     2811                 */
     2812                iSrcSlot++;
     2813            }
     2814            while (iSrcSlot-- > 0)
     2815            {
     2816                int rc2 = rtFsFatDir_GetEntryForUpdate(pThis, offFirstInDir + iSrcSlot * sizeof(FATDIRENTRY),
     2817                                                       &pDstEntry, &uBufferLock);
     2818                if (RT_SUCCESS(rc2))
     2819                {
     2820                    pDstEntry->achName[0] = FATDIRENTRY_CH0_DELETED;
     2821                    rtFsFatDir_PutEntryAfterUpdate(pThis, pDstEntry, uBufferLock);
     2822                }
     2823            }
     2824            *poffEntryInDir = UINT32_MAX;
     2825            return rc;
     2826        }
     2827        AssertRC(rc);
     2828
     2829        /*
     2830         * Successfully inserted all.
     2831         */
     2832        *poffEntryInDir = offFirstInDir + cSlots * sizeof(FATDIRENTRY);
     2833        return VINF_SUCCESS;
     2834    }
     2835
     2836    *poffEntryInDir = UINT32_MAX;
     2837    return rc;
     2838}
     2839
     2840
     2841
     2842/**
     2843 * Creates a new directory entry.
     2844 *
     2845 * @returns IPRT status code
     2846 * @param   pThis           The directory.
     2847 * @param   pszEntry        The name of the new entry.
     2848 * @param   fAttrib         The attributes.
     2849 * @param   cbInitial       The initialize size.
     2850 * @param   poffEntryInDir  Where to return the offset of the directory entry.
     2851 * @param   pDirEntry       Where to return a copy of the directory entry.
     2852 *
     2853 * @remarks ASSUMES caller has already called rtFsFatDir_FindEntry to make sure
     2854 *          the entry doesn't exist.
     2855 */
     2856static int rtFsFatDir_CreateEntry(PRTFSFATDIR pThis, const char *pszEntry, uint8_t fAttrib, uint32_t cbInitial,
     2857                                  uint32_t *poffEntryInDir, PFATDIRENTRY pDirEntry)
     2858{
     2859    PRTFSFATVOL pVol = pThis->Core.pVol;
     2860    *poffEntryInDir = UINT32_MAX;
     2861
     2862    /*
     2863     * Create the directory entries on the stack.
     2864     */
     2865    bool fIs8Dot3Name = rtFsFatDir_StringTo8Dot3((char *)pDirEntry->achName, pszEntry);
     2866    pDirEntry->fAttrib            = fAttrib;
     2867    pDirEntry->fCase              = fIs8Dot3Name ? rtFsFatDir_CalcCaseFlags(pszEntry) : 0;
     2868    pDirEntry->uBirthCentiseconds = rtFsFatCurrentFatDateTime(pVol, &pDirEntry->uBirthDate, &pDirEntry->uBirthTime);
     2869    pDirEntry->uAccessDate        = pDirEntry->uBirthDate;
     2870    pDirEntry->uModifyDate        = pDirEntry->uBirthDate;
     2871    pDirEntry->uModifyTime        = pDirEntry->uBirthTime;
     2872    pDirEntry->idxCluster         = 0;    /* Will fill this in later if cbInitial is non-zero. */
     2873    pDirEntry->u.idxClusterHigh   = 0;
     2874    pDirEntry->cbFile             = cbInitial;
     2875
     2876    /*
     2877     * Create long filename slots if necessary.
     2878     */
     2879    uint32_t        cSlots = UINT32_MAX;
     2880    FATDIRNAMESLOT  aSlots[FATDIRNAMESLOT_MAX_SLOTS];
     2881    int rc = rtFsFatDir_MaybeCreateLongNameAndShortAlias(pThis, pszEntry, fIs8Dot3Name, pDirEntry, aSlots, &cSlots);
     2882    if (RT_SUCCESS(rc))
     2883    {
     2884        Assert(cSlots <= FATDIRNAMESLOT_MAX_SLOTS);
     2885
     2886        /*
     2887         * Allocate initial clusters if requested.
     2888         */
     2889        RTFSFATCHAIN Clusters;
     2890        rtFsFatChain_InitEmpty(&Clusters, pVol);
     2891        if (cbInitial > 0)
     2892        {
     2893            rc = rtFsFatClusterMap_AllocateMoreClusters(pVol, &Clusters,
     2894                                                        (cbInitial + Clusters.cbCluster - 1) >> Clusters.cClusterByteShift);
     2895            if (RT_SUCCESS(rc))
     2896            {
     2897                uint32_t idxFirstCluster = rtFsFatChain_GetFirstCluster(&Clusters);
     2898                pDirEntry->idxCluster = (uint16_t)idxFirstCluster;
     2899                if (pVol->enmFatType >= RTFSFATTYPE_FAT32)
     2900                    pDirEntry->u.idxClusterHigh = (uint16_t)(idxFirstCluster >> 16);
     2901            }
     2902        }
     2903        if (RT_SUCCESS(rc))
     2904        {
     2905            /*
     2906             * Insert the directory entry and name slots.
     2907             */
     2908            rc = rtFsFatChain_InsertEntries(pThis, pDirEntry, aSlots, cSlots, poffEntryInDir);
     2909            if (RT_SUCCESS(rc))
     2910            {
     2911                rtFsFatChain_Delete(&Clusters);
     2912                return VINF_SUCCESS;
     2913            }
     2914
     2915            for (uint32_t iClusterToFree = 0; iClusterToFree < Clusters.cClusters; iClusterToFree++)
     2916                rtFsFatClusterMap_FreeCluster(pVol, rtFsFatChain_GetClusterByIndex(&Clusters, iClusterToFree));
     2917            rtFsFatChain_Delete(&Clusters);
     2918        }
     2919    }
     2920    else
     2921        rc = VERR_NET_NOT_UNIQUE_NAME;
     2922    return rc;
     2923}
    23742924
    23752925
     
    24553005{
    24563006    PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
     3007
     3008    /*
     3009     * Try open existing file.
     3010     */
    24573011    uint32_t    offEntryInDir;
    24583012    bool        fLong;
     
    24663020                if (   !(DirEntry.fAttrib & FAT_ATTR_READONLY)
    24673021                    || !(fOpen & RTFILE_O_WRITE))
    2468                     rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
     3022                {
     3023                    if (   (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
     3024                        || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
     3025                        || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
     3026                        rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
     3027                    else
     3028                        rc = VERR_ALREADY_EXISTS;
     3029                }
    24693030                else
    24703031                    rc = VERR_ACCESS_DENIED;
     
    24783039                break;
    24793040        }
     3041    }
     3042    /*
     3043     * Create the file?
     3044     */
     3045    else if (   rc == VERR_FILE_NOT_FOUND
     3046             && (   (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
     3047                 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
     3048                 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE) )
     3049    {
     3050        rc = rtFsFatDir_CreateEntry(pThis, pszFilename, FAT_ATTR_ARCHIVE, 0 /*cbInitial*/, &offEntryInDir, &DirEntry);
     3051        if (RT_SUCCESS(rc))
     3052            rc = rtFsFatFile_New(pThis->Core.pVol, pThis, &DirEntry, offEntryInDir, fOpen, phVfsFile);
    24803053    }
    24813054    return rc;
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