os/kernelhwsrv/userlibandfileserver/fileserver/sfat/sl_mnt.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// Common CFatMountCB code for both EFAT.FSY and EFAT32.fsy
sl@0
    15
//
sl@0
    16
//
sl@0
    17
sl@0
    18
/**
sl@0
    19
 @file
sl@0
    20
 @internalTechnology
sl@0
    21
*/
sl@0
    22
sl@0
    23
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    24
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    25
//!!
sl@0
    26
//!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
sl@0
    27
//!!
sl@0
    28
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    29
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    30
sl@0
    31
sl@0
    32
#include "sl_std.h"
sl@0
    33
#include "sl_cache.h"
sl@0
    34
#include "sl_leafdir_cache.h"
sl@0
    35
#include "sl_dir_cache.h"
sl@0
    36
#include "sl_scandrv.h"
sl@0
    37
#include <hal.h>
sl@0
    38
sl@0
    39
TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively);
sl@0
    40
sl@0
    41
static void  MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster);
sl@0
    42
static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster);
sl@0
    43
static TInt  NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster);
sl@0
    44
sl@0
    45
//-----------------------------------------------------------------------------------------
sl@0
    46
sl@0
    47
TFatVolParam::TFatVolParam()
sl@0
    48
    {
sl@0
    49
    Mem::FillZ(this, sizeof(TFatVolParam));
sl@0
    50
    }
sl@0
    51
sl@0
    52
/**
sl@0
    53
    populate the object with the values from the boot sector.
sl@0
    54
    @param aBootSector a reference to the valid boots sector
sl@0
    55
*/
sl@0
    56
void TFatVolParam::Populate(const TFatBootSector& aBootSector)
sl@0
    57
    {
sl@0
    58
    ASSERT(aBootSector.IsValid());
sl@0
    59
sl@0
    60
    iSectorsPerCluster = aBootSector.SectorsPerCluster();
sl@0
    61
    iSectorSizeLog2    = Log2(aBootSector.BytesPerSector());
sl@0
    62
    iClusterSizeLog2   = iSectorSizeLog2+Log2(iSectorsPerCluster);
sl@0
    63
    iFirstFatSector    = aBootSector.FirstFatSector();
sl@0
    64
    iNumberOfFats      = aBootSector.NumberOfFats();
sl@0
    65
    iFatSizeInBytes    = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector();
sl@0
    66
    iTotalSectors      = aBootSector.VolumeTotalSectorNumber();
sl@0
    67
    iRootClusterNum    = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16
sl@0
    68
sl@0
    69
    iRootDirectorySector = aBootSector.RootDirStartSector();
sl@0
    70
    iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32
sl@0
    71
sl@0
    72
    //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16
sl@0
    73
    iFSInfoSectorNum   = aBootSector.FSInfoSectorNum();
sl@0
    74
    iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector
sl@0
    75
    }
sl@0
    76
sl@0
    77
TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const
sl@0
    78
    {
sl@0
    79
    ASSERT(&aRhs != this);
sl@0
    80
    if(&aRhs == this)
sl@0
    81
        return ETrue; //-- comparing with itself
sl@0
    82
sl@0
    83
    return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0);
sl@0
    84
    }
sl@0
    85
sl@0
    86
sl@0
    87
//-----------------------------------------------------------------------------------------
sl@0
    88
sl@0
    89
sl@0
    90
CFatMountCB::CFatMountCB()
sl@0
    91
    {
sl@0
    92
    __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName);
sl@0
    93
    iFatType = EInvalid;
sl@0
    94
    iState   = ENotMounted;
sl@0
    95
    DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only
sl@0
    96
    }
sl@0
    97
sl@0
    98
CFatMountCB::~CFatMountCB()
sl@0
    99
    {
sl@0
   100
    __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this);
sl@0
   101
sl@0
   102
    DoDismount();
sl@0
   103
sl@0
   104
    delete iNotifier;
sl@0
   105
    delete iFatTable;
sl@0
   106
    delete iRawDisk;
sl@0
   107
    delete iLeafDirCache;
sl@0
   108
sl@0
   109
    }
sl@0
   110
sl@0
   111
//-----------------------------------------------------------------------------------------
sl@0
   112
sl@0
   113
CFatMountCB* CFatMountCB::NewL()
sl@0
   114
    {
sl@0
   115
    CFatMountCB* pSelf = new(ELeave) CFatMountCB;
sl@0
   116
sl@0
   117
    CleanupStack::PushL(pSelf);
sl@0
   118
    pSelf->ConstructL();
sl@0
   119
    CleanupStack::Pop(pSelf);
sl@0
   120
sl@0
   121
    return pSelf;
sl@0
   122
    }
sl@0
   123
sl@0
   124
// second-stage constructor
sl@0
   125
void CFatMountCB::ConstructL()
sl@0
   126
{
sl@0
   127
    //-- create Notifier
sl@0
   128
    iNotifier = CAsyncNotifier::New();
sl@0
   129
    if( !iNotifier )
sl@0
   130
        {
sl@0
   131
        Close();
sl@0
   132
        User::Leave(KErrNoMemory);
sl@0
   133
        }
sl@0
   134
sl@0
   135
    iNotifier->SetMount(this);
sl@0
   136
    }
sl@0
   137
sl@0
   138
//-------------------------------------------------------------------------------------------------------------------
sl@0
   139
sl@0
   140
/**
sl@0
   141
Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount.
sl@0
   142
@return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready.
sl@0
   143
@see CMountCB::FileSystemClusterSize()
sl@0
   144
*/
sl@0
   145
TInt CFatMountCB::ClusterSize() const
sl@0
   146
    {
sl@0
   147
    if (ClusterSizeLog2())
sl@0
   148
        return (1 << ClusterSizeLog2());
sl@0
   149
sl@0
   150
    return KErrNotReady;
sl@0
   151
    }
sl@0
   152
sl@0
   153
//-------------------------------------------------------------------------------------------------------------------
sl@0
   154
sl@0
   155
/**
sl@0
   156
    @leave KErrAccessDenied if the mount is read-only
sl@0
   157
*/
sl@0
   158
void CFatMountCB::CheckWritableL() const
sl@0
   159
    {
sl@0
   160
    if(ReadOnly())
sl@0
   161
        {
sl@0
   162
        __PRINT(_L("CFatMountCB is RO!"));
sl@0
   163
        User::Leave(KErrAccessDenied);
sl@0
   164
        }
sl@0
   165
    }
sl@0
   166
sl@0
   167
/**
sl@0
   168
    @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory  operations can not be performed
sl@0
   169
*/
sl@0
   170
void CFatMountCB::CheckStateConsistentL() const
sl@0
   171
    {
sl@0
   172
    if(!ConsistentState())
sl@0
   173
        {
sl@0
   174
        __PRINT(_L("CFatMountCB state is inconsistent !"));
sl@0
   175
        User::Leave(KErrCorrupt);
sl@0
   176
        }
sl@0
   177
    }
sl@0
   178
sl@0
   179
sl@0
   180
//-------------------------------------------------------------------------------------------------------------------
sl@0
   181
/**
sl@0
   182
    Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
sl@0
   183
    @return ETrue if volume parameters remained same.
sl@0
   184
*/
sl@0
   185
TBool CFatMountCB::CheckVolumeTheSame()
sl@0
   186
{
sl@0
   187
    //-- initialise local drive
sl@0
   188
    TInt nRes =InitLocalDrive();
sl@0
   189
    if(nRes != KErrNone)
sl@0
   190
        return EFalse;
sl@0
   191
sl@0
   192
    //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it.
sl@0
   193
    TFatBootSector bootSector;
sl@0
   194
    nRes = ReadBootSector(bootSector, iRamDrive);
sl@0
   195
    if(nRes != KErrNone)
sl@0
   196
        return EFalse;
sl@0
   197
sl@0
   198
    //-- 1. check volume Uid
sl@0
   199
    if(iUniqueID != bootSector.UniqueID())
sl@0
   200
        return EFalse;
sl@0
   201
sl@0
   202
    //-- check volume parameters, they must remain the same
sl@0
   203
    TFatVolParam volParam;
sl@0
   204
    volParam.Populate(bootSector);
sl@0
   205
sl@0
   206
    if(!(volParam == iVolParam))
sl@0
   207
        return EFalse;
sl@0
   208
sl@0
   209
sl@0
   210
    return ETrue;
sl@0
   211
sl@0
   212
}
sl@0
   213
//-------------------------------------------------------------------------------------------------------------------
sl@0
   214
sl@0
   215
/**
sl@0
   216
    Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
sl@0
   217
    If they are, re-initialises the mount.
sl@0
   218
*/
sl@0
   219
void CFatMountCB::DoReMountL()
sl@0
   220
    {
sl@0
   221
sl@0
   222
    if(!CheckVolumeTheSame())
sl@0
   223
        User::Leave(KErrGeneral);
sl@0
   224
sl@0
   225
    //-- get drive capabilities
sl@0
   226
    TLocalDriveCapsV2Buf capsBuf;
sl@0
   227
    User::LeaveIfError(LocalDrive()->Caps(capsBuf));
sl@0
   228
sl@0
   229
    //-- the volume is the same as it was on original MountL()
sl@0
   230
    //-- we need to re-initialize for the case when the media was removed, FAT or directory structure changed on other device and the media returned back.
sl@0
   231
    DoDismount();
sl@0
   232
sl@0
   233
    SetState(EMounting);
sl@0
   234
sl@0
   235
    InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting.
sl@0
   236
sl@0
   237
    }
sl@0
   238
sl@0
   239
//-------------------------------------------------------------------------------------------------------------------
sl@0
   240
sl@0
   241
/**
sl@0
   242
    Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and
sl@0
   243
    if they are, re-initialises the mount. This includes resetting all caches.
sl@0
   244
    ! Do not call this method from TFatDriveInterface methods, like citical and non-critical notifiers ! This can lead to the
sl@0
   245
    recursive loops and undefined behaviour.
sl@0
   246
sl@0
   247
    @return KErrNone if the remount was OK
sl@0
   248
            system-wide error code otherwise
sl@0
   249
*/
sl@0
   250
TInt CFatMountCB::ReMount()
sl@0
   251
    {
sl@0
   252
    __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State());
sl@0
   253
sl@0
   254
    const TFatMntState currState = State();
sl@0
   255
sl@0
   256
    //-- analyse the mount state and find out if we can remount at all.
sl@0
   257
    switch(currState)
sl@0
   258
        {
sl@0
   259
        case ENotMounted:
sl@0
   260
        __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!"));
sl@0
   261
sl@0
   262
        ASSERT(0);
sl@0
   263
        return KErrGeneral;
sl@0
   264
sl@0
   265
        //-- correct state, proceed to remount
sl@0
   266
        default:
sl@0
   267
        break;
sl@0
   268
    }
sl@0
   269
sl@0
   270
    //-- there are 2 options here:
sl@0
   271
    //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it)
sl@0
   272
    //--    in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards
sl@0
   273
    //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.)
sl@0
   274
    //--    in this case we may need to fake the success. This can only happen on forced mount by CFormatCB
sl@0
   275
    TInt nRes;
sl@0
   276
    TRAP(nRes, DoReMountL());
sl@0
   277
sl@0
   278
    if(nRes != KErrNone)
sl@0
   279
        {
sl@0
   280
        //-- note that the mount may be here left in inconsistent state (EMounting)
sl@0
   281
        //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and
sl@0
   282
        //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this.
sl@0
   283
        __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes);
sl@0
   284
sl@0
   285
        //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted.
sl@0
   286
        //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and
sl@0
   287
        //-- it is used for performing a format. If the format has finished, but RFormat isn't closed yet and we try to access the volume,
sl@0
   288
        //-- we will get here, because all members of the constructed mount will be zeroes.
sl@0
   289
        if(currState == EInit_Forced)
sl@0
   290
            {
sl@0
   291
            __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!"));
sl@0
   292
            SetState(currState);
sl@0
   293
            return KErrNone;
sl@0
   294
            }
sl@0
   295
sl@0
   296
        return nRes;
sl@0
   297
        }
sl@0
   298
sl@0
   299
    __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber());
sl@0
   300
    SetState(EInit_R);
sl@0
   301
    return nRes;
sl@0
   302
    }
sl@0
   303
sl@0
   304
//-------------------------------------------------------------------------------------------------------------------
sl@0
   305
sl@0
   306
/**
sl@0
   307
    Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is
sl@0
   308
    instantiated.
sl@0
   309
*/
sl@0
   310
sl@0
   311
void CFatMountCB::InvalidateLeafDirCache()
sl@0
   312
    {
sl@0
   313
    if (iLeafDirCache)
sl@0
   314
        {
sl@0
   315
        iLeafDirCache->Reset();
sl@0
   316
        }
sl@0
   317
    else
sl@0
   318
        {
sl@0
   319
        User::Free(iLastLeafDir);
sl@0
   320
        iLastLeafDir=NULL;
sl@0
   321
        }
sl@0
   322
    }
sl@0
   323
sl@0
   324
//-------------------------------------------------------------------------------------------------------------------
sl@0
   325
sl@0
   326
/**
sl@0
   327
    Delete mount's caches
sl@0
   328
    Moves CFatMountCB into ENotMounted state immediately.
sl@0
   329
*/
sl@0
   330
void CFatMountCB::DoDismount()
sl@0
   331
    {
sl@0
   332
    __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber());
sl@0
   333
sl@0
   334
    //-- try to flush and destroy FAT cache
sl@0
   335
    if (iFatTable)
sl@0
   336
        {
sl@0
   337
        if(!ConsistentState() || ReadOnly())
sl@0
   338
            {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either.
sl@0
   339
            iFatTable->Dismount(ETrue);
sl@0
   340
            }
sl@0
   341
        else
sl@0
   342
            {//-- Try to flush the FAT - if this fails there's not much we can do
sl@0
   343
            TRAPD(r, iFatTable->FlushL());
sl@0
   344
            iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache
sl@0
   345
            }
sl@0
   346
        }
sl@0
   347
sl@0
   348
    //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting
sl@0
   349
    //-- see CFatMountCB::InitializeL()
sl@0
   350
    delete iLeafDirCache;
sl@0
   351
    iLeafDirCache = NULL;
sl@0
   352
sl@0
   353
    //-- destroy directory cache, this cache will be re-created while mounting or re-mounting
sl@0
   354
    //-- see CFatMountCB::InitializeL()
sl@0
   355
    delete iRawDisk;
sl@0
   356
    iRawDisk = NULL;
sl@0
   357
sl@0
   358
    //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive,
sl@0
   359
    //-- i.e. iFatTable & iRawDisk are valid
sl@0
   360
    SetState(EDismounted);
sl@0
   361
    }
sl@0
   362
sl@0
   363
//-----------------------------------------------------------------------------------------
sl@0
   364
sl@0
   365
/** old implementation */
sl@0
   366
void CFatMountCB::FinaliseMountL()
sl@0
   367
    {
sl@0
   368
    FinaliseMountL(RFs::EFinal_RW);
sl@0
   369
    }
sl@0
   370
sl@0
   371
//-----------------------------------------------------------------------------------------
sl@0
   372
/**
sl@0
   373
    Dismount the CFatMountCB and the drive.
sl@0
   374
    called from TDrive::Dismount().
sl@0
   375
*/
sl@0
   376
void CFatMountCB::Dismounted()
sl@0
   377
    {
sl@0
   378
    __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber());
sl@0
   379
sl@0
   380
    //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media.
sl@0
   381
    //-- this method may be called after the media change occured from the TDrive::Dismount(). It means
sl@0
   382
    //-- that if we try to write some data here, they could be written into a different medium, if it had been
sl@0
   383
    //-- physically changed.
sl@0
   384
sl@0
   385
    const TFatMntState prevState = State();
sl@0
   386
sl@0
   387
    DoDismount(); //-- it will change mount state to EDismounted
sl@0
   388
    DismountedLocalDrive();
sl@0
   389
sl@0
   390
    //-- check if the previous state was EInit_Forced, which means that this method was called
sl@0
   391
    //-- on the mount that might not be alive (no valid iFatTable & iRawDisk).
sl@0
   392
    //-- This can happen only during format operation on non-mounted previously volume.
sl@0
   393
    //-- this EInit_Forced state must be processed separately, see ::Remount()
sl@0
   394
    if(prevState == EInit_Forced)
sl@0
   395
        SetState(EInit_Forced);
sl@0
   396
sl@0
   397
    }
sl@0
   398
sl@0
   399
sl@0
   400
//-------------------------------------------------------------------------------------------------------------------
sl@0
   401
sl@0
   402
/**
sl@0
   403
    Find out if the mount is finalised.
sl@0
   404
    @param  aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise.
sl@0
   405
    @return standard error codes.
sl@0
   406
*/
sl@0
   407
TInt CFatMountCB::IsFinalised(TBool& aFinalised)
sl@0
   408
    {
sl@0
   409
    switch(State())
sl@0
   410
        {
sl@0
   411
        case EFinalised: //-- already explicitly finalised
sl@0
   412
            aFinalised = ETrue;
sl@0
   413
        return KErrNone;
sl@0
   414
sl@0
   415
        case EInit_W: //-- the volume had been written
sl@0
   416
            aFinalised = EFalse;
sl@0
   417
        return KErrNone;
sl@0
   418
sl@0
   419
        default: //-- it depends on the state
sl@0
   420
        break;
sl@0
   421
        }
sl@0
   422
sl@0
   423
    //-- find out if the volume is _physically_ finalised.
sl@0
   424
    //-- It can be in the state EInit_R, but finalised before mounting
sl@0
   425
    if(!VolCleanFlagSupported())
sl@0
   426
        return KErrNotSupported;
sl@0
   427
sl@0
   428
    TInt nRes = KErrNone;
sl@0
   429
    TRAP(nRes, aFinalised = VolumeCleanL());
sl@0
   430
sl@0
   431
    return nRes;
sl@0
   432
    }
sl@0
   433
sl@0
   434
//-------------------------------------------------------------------------------------------------------------------
sl@0
   435
sl@0
   436
/**
sl@0
   437
    @return ETrue if the mount is in consistent state i.e. normally mounted.
sl@0
   438
    See TFatMntState enum for more detail.
sl@0
   439
*/
sl@0
   440
TBool CFatMountCB::ConsistentState() const
sl@0
   441
    {
sl@0
   442
    return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised);
sl@0
   443
    }
sl@0
   444
sl@0
   445
//-------------------------------------------------------------------------------------------------------------------
sl@0
   446
sl@0
   447
/**
sl@0
   448
    Open CFatMountCB for write. I.e. perform some actions on the first write attempt.
sl@0
   449
    This is a callback from TFatDriveInterface.
sl@0
   450
    @return System wide error code.
sl@0
   451
*/
sl@0
   452
TInt CFatMountCB::OpenMountForWrite()
sl@0
   453
    {
sl@0
   454
    if(State() == EInit_W)
sl@0
   455
        return KErrNone; //-- nothing to do, the mount is already opened for write
sl@0
   456
sl@0
   457
    __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber());
sl@0
   458
sl@0
   459
    ASSERT(State() == EInit_R || State() == EFinalised);
sl@0
   460
sl@0
   461
    //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and
sl@0
   462
    //-- can not call TFatDriveInterface methods that call this method etc.
sl@0
   463
    ASSERT(iCBRecFlag == 0);
sl@0
   464
    DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag
sl@0
   465
sl@0
   466
    //-- do here some "opening" work, like marking volme as dirty
sl@0
   467
    //-- be careful here, as soon as this is a callback from TFatDriveInterface, writing via TFatDriveInterface may cause some unwanted recursion.
sl@0
   468
sl@0
   469
    //-- mark the volume as dirty
sl@0
   470
    TInt nRes=KErrNone;
sl@0
   471
    TRAP(nRes, SetVolumeCleanL(EFalse));
sl@0
   472
    if(nRes == KErrNone)
sl@0
   473
        {
sl@0
   474
        SetState(EInit_W);
sl@0
   475
        }
sl@0
   476
sl@0
   477
    DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag
sl@0
   478
sl@0
   479
    return nRes;
sl@0
   480
sl@0
   481
    }
sl@0
   482
sl@0
   483
//-------------------------------------------------------------------------------------------------------------------
sl@0
   484
sl@0
   485
/**
sl@0
   486
    Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily.
sl@0
   487
*/
sl@0
   488
void CFatMountCB::UnFinaliseMountL()
sl@0
   489
    {
sl@0
   490
    switch(State())
sl@0
   491
        {
sl@0
   492
        case EFinalised:
sl@0
   493
        case EInit_R:
sl@0
   494
            SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty"
sl@0
   495
            SetState(EInit_R);
sl@0
   496
        return;
sl@0
   497
sl@0
   498
        case EInit_W:
sl@0
   499
        return; //-- nothing to do
sl@0
   500
sl@0
   501
        default:
sl@0
   502
        //-- other mount states are inconsistent; can't perform this operation
sl@0
   503
        User::Leave(KErrAbort);
sl@0
   504
        break;
sl@0
   505
sl@0
   506
        }
sl@0
   507
sl@0
   508
    }
sl@0
   509
sl@0
   510
//-------------------------------------------------------------------------------------------------------------------
sl@0
   511
sl@0
   512
/**
sl@0
   513
    Finalise the mount.
sl@0
   514
sl@0
   515
    @param  aOperation  describes finalisation operation ,see RFs::TFinaliseDrvMode
sl@0
   516
    @param  aParam1     not used, for future expansion
sl@0
   517
    @param  aParam2     not used, for future expansion
sl@0
   518
sl@0
   519
    @leave  System wide error code. particular cases:
sl@0
   520
            KErrArgument invalid arguments
sl@0
   521
            KErrInUse    if the volume has opened objects (files, directories etc)
sl@0
   522
            KErrCorrupt  if the volume is corrupt
sl@0
   523
sl@0
   524
*/
sl@0
   525
void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/)
sl@0
   526
    {
sl@0
   527
    __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber());
sl@0
   528
sl@0
   529
    switch(aOperation)
sl@0
   530
        {
sl@0
   531
        case RFs::EFinal_RW:
sl@0
   532
        case RFs::EFinal_RO:
sl@0
   533
        break;
sl@0
   534
sl@0
   535
        case RFs::EForceUnfinalise:
sl@0
   536
            UnFinaliseMountL();
sl@0
   537
        return;
sl@0
   538
sl@0
   539
        default:
sl@0
   540
            __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation);
sl@0
   541
            ASSERT(0);
sl@0
   542
            User::Leave(KErrArgument);
sl@0
   543
        return;
sl@0
   544
        }
sl@0
   545
sl@0
   546
    //-- mount finalisation work
sl@0
   547
sl@0
   548
    ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO);
sl@0
   549
sl@0
   550
    if(State() == EFinalised)
sl@0
   551
        {//-- the mount is already finalised. All we can do is to set it to RO mode
sl@0
   552
        if(ReadOnly() && aOperation == RFs::EFinal_RW)
sl@0
   553
            {
sl@0
   554
            User::Leave(KErrAccessDenied); //-- can't override RO flag
sl@0
   555
            }
sl@0
   556
sl@0
   557
        (void)LocalDrive()->Finalise(ETrue);
sl@0
   558
sl@0
   559
        if(aOperation == RFs::EFinal_RO)
sl@0
   560
            {
sl@0
   561
            SetReadOnly(ETrue);
sl@0
   562
            return;
sl@0
   563
            }
sl@0
   564
sl@0
   565
        return;
sl@0
   566
        }
sl@0
   567
sl@0
   568
    if(LockStatus() != 0)
sl@0
   569
        {//-- can't finalise the volume if it has opened objects and not in the consistent state.
sl@0
   570
         //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present,
sl@0
   571
         //-- it's impossible to detect such situation.
sl@0
   572
        User::Leave(KErrInUse);
sl@0
   573
        }
sl@0
   574
sl@0
   575
    if(State() != EInit_R && State() != EInit_W)
sl@0
   576
        {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt.
sl@0
   577
        __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State());
sl@0
   578
        User::Leave(KErrCorrupt);
sl@0
   579
        }
sl@0
   580
sl@0
   581
    //-- flush FAT cache
sl@0
   582
    FAT().FlushL();
sl@0
   583
sl@0
   584
    //-- for FAT32 we may need to update data in FSInfo sectors
sl@0
   585
    if(Is32BitFat())
sl@0
   586
        {
sl@0
   587
        if(FAT().ConsistentState())
sl@0
   588
            {//-- FAT table state is consistent and the number of free clusters is known.
sl@0
   589
             //-- Do it disregarding the mount state, it may help in the situation when 2 copies of the FSInfo are different for some reason.
sl@0
   590
            DoUpdateFSInfoSectorsL(EFalse);
sl@0
   591
            }
sl@0
   592
            else
sl@0
   593
            {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working.
sl@0
   594
             //-- in this case we can't put corect values into the FSInfo.
sl@0
   595
            if(State() == EInit_W)
sl@0
   596
                {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time.
sl@0
   597
                 //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo.
sl@0
   598
                __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo"));
sl@0
   599
                DoUpdateFSInfoSectorsL(ETrue);
sl@0
   600
                }
sl@0
   601
             else
sl@0
   602
                {//-- no changes on the volume, just do not update FSInfo
sl@0
   603
                __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated"));
sl@0
   604
                }
sl@0
   605
sl@0
   606
            }//if(FAT().ConsistentState())
sl@0
   607
sl@0
   608
        }//if(Is32BitFat())
sl@0
   609
sl@0
   610
sl@0
   611
sl@0
   612
    //-- mark the volume as clean
sl@0
   613
    SetVolumeCleanL(ETrue);
sl@0
   614
sl@0
   615
    //-- finally, put the volume into RO mode if required
sl@0
   616
    if(aOperation == RFs::EFinal_RO)
sl@0
   617
        SetReadOnly(ETrue);
sl@0
   618
sl@0
   619
    SetState(EFinalised);
sl@0
   620
    }
sl@0
   621
sl@0
   622
sl@0
   623
//-------------------------------------------------------------------------------------------------------------------
sl@0
   624
sl@0
   625
/**
sl@0
   626
@return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12
sl@0
   627
*/
sl@0
   628
TBool CFatMountCB::VolCleanFlagSupported() const
sl@0
   629
    {
sl@0
   630
        const TFatType fatType=FatType();
sl@0
   631
sl@0
   632
        ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32);
sl@0
   633
        return (fatType != EFat12);
sl@0
   634
    }
sl@0
   635
sl@0
   636
//-----------------------------------------------------------------------------------------
sl@0
   637
sl@0
   638
sl@0
   639
/**
sl@0
   640
    Obtain the volume information.
sl@0
   641
    All information except iSize and iFree has been added by TDrive::Volume().
sl@0
   642
sl@0
   643
    @param  aVolume on return will contain iSize & iFree fields filled with actual data.
sl@0
   644
*/
sl@0
   645
void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const
sl@0
   646
    {
sl@0
   647
sl@0
   648
    //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running.
sl@0
   649
    //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field.
sl@0
   650
    //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters
sl@0
   651
    //-- will be returned.
sl@0
   652
    const TBool bSyncOp = !aVolume.iVolSizeAsync;
sl@0
   653
    aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side
sl@0
   654
sl@0
   655
    __PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp);
sl@0
   656
    const TDriveInfo& drvInfo=aVolume.iDrive;
sl@0
   657
sl@0
   658
#if defined(__EPOC32__)
sl@0
   659
    // if RAM drive, cap size according to HAL.
sl@0
   660
    if (drvInfo.iType==EMediaRam)
sl@0
   661
        {
sl@0
   662
        TLocalDriveCapsV2Buf caps;
sl@0
   663
        LocalDrive()->Caps(caps);
sl@0
   664
sl@0
   665
        const TInt max_drive_size=TInt(caps().iEraseBlockSize);
sl@0
   666
        const TInt cur_drive_size=I64INT(caps().iSize);
sl@0
   667
sl@0
   668
        aVolume.iSize=max_drive_size;
sl@0
   669
        aVolume.iFree=max_drive_size-cur_drive_size;
sl@0
   670
sl@0
   671
        aVolume.iSize=aVolume.iFree+iSize;
sl@0
   672
sl@0
   673
        TInt maxSize;
sl@0
   674
        if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone)
sl@0
   675
            {
sl@0
   676
            // iSize will never grow beyond maxRam because of a check in medint.
sl@0
   677
            // d <= f; (s{f} + f) - m <= f; s{f} <= m
sl@0
   678
            __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid));
sl@0
   679
            if (aVolume.iSize > maxSize)
sl@0
   680
                {
sl@0
   681
                TInt64 d = aVolume.iSize - maxSize;
sl@0
   682
                __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid));
sl@0
   683
                aVolume.iSize -= d;
sl@0
   684
                aVolume.iFree -= d;
sl@0
   685
                }
sl@0
   686
            }
sl@0
   687
sl@0
   688
        aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
sl@0
   689
        aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<<ClusterSizeLog2();  //-- round down to cluster size
sl@0
   690
sl@0
   691
        return;
sl@0
   692
        }//if (drvInfo.iType==EMediaRam)
sl@0
   693
sl@0
   694
#endif
sl@0
   695
sl@0
   696
sl@0
   697
    const TUint32 freeClusters = FAT().NumberOfFreeClusters(bSyncOp);
sl@0
   698
sl@0
   699
    __PRINT1(_L("CFatMountCB::VolumeL() free clusters:%d"), freeClusters);
sl@0
   700
sl@0
   701
    aVolume.iFree = (TInt64)freeClusters << ClusterSizeLog2();
sl@0
   702
sl@0
   703
    if (drvInfo.iType==EMediaRam)
sl@0
   704
        aVolume.iSize=aVolume.iFree+iSize;
sl@0
   705
sl@0
   706
    aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
sl@0
   707
    aVolume.iSize=(aVolume.iSize >> ClusterSizeLog2()) << ClusterSizeLog2();  //-- round down to cluster size
sl@0
   708
sl@0
   709
    }
sl@0
   710
sl@0
   711
sl@0
   712
//-----------------------------------------------------------------------------------------
sl@0
   713
sl@0
   714
//
sl@0
   715
//  Set the volume label (write aVolume label into BPB & Volume Label File)
sl@0
   716
//  aName string may be zero length but is assumed to contain no illegal characters or NULLs.
sl@0
   717
//
sl@0
   718
void CFatMountCB::SetVolumeL(TDes& aName)
sl@0
   719
    {
sl@0
   720
sl@0
   721
    __PRINT(_L("CFatMountCB::SetVolumeL"));
sl@0
   722
sl@0
   723
    CheckStateConsistentL();
sl@0
   724
    CheckWritableL();
sl@0
   725
sl@0
   726
    __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName));
sl@0
   727
sl@0
   728
    TBuf8<KVolumeLabelSize> buf8(KVolumeLabelSize);
sl@0
   729
    buf8.Zero();
sl@0
   730
    LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave);
sl@0
   731
    aName.Zero();
sl@0
   732
    LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before)
sl@0
   733
sl@0
   734
    const TInt lengthOfBuf8=buf8.Length();
sl@0
   735
    // Pad to end with spaces if not empty.
sl@0
   736
    if (lengthOfBuf8>0 && lengthOfBuf8<KVolumeLabelSize)
sl@0
   737
        {
sl@0
   738
        buf8.SetLength(KVolumeLabelSize);
sl@0
   739
        Mem::Fill(&buf8[lengthOfBuf8],KVolumeLabelSize-lengthOfBuf8,' ');
sl@0
   740
        }
sl@0
   741
sl@0
   742
    // Write a volume label file
sl@0
   743
    WriteVolumeLabelFileL( buf8 );
sl@0
   744
sl@0
   745
    // Write the boot sector volume label
sl@0
   746
    // Always pad to full length with spaces
sl@0
   747
    if (lengthOfBuf8==0)
sl@0
   748
        {
sl@0
   749
        buf8.Fill(' ',KVolumeLabelSize);
sl@0
   750
        }
sl@0
   751
sl@0
   752
    WriteVolumeLabelL(buf8);
sl@0
   753
    }
sl@0
   754
sl@0
   755
//-----------------------------------------------------------------------------------------
sl@0
   756
sl@0
   757
/**
sl@0
   758
    Make a directory.
sl@0
   759
    @param aName full path to the directory to create. Name validity is checked by file server.
sl@0
   760
    all trailing dots from the name will be removed
sl@0
   761
*/
sl@0
   762
void CFatMountCB::MkDirL(const TDesC& aName)
sl@0
   763
    {
sl@0
   764
    __PRINT2(_L("CFatMountCB::MkDirL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
   765
sl@0
   766
    CheckStateConsistentL();
sl@0
   767
    CheckWritableL();
sl@0
   768
sl@0
   769
    TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
sl@0
   770
sl@0
   771
    TInt namePos=dirName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
   772
    TPtrC name=dirName.Mid(namePos);
sl@0
   773
    TLeafDirData leafDir;
sl@0
   774
    const TEntryPos dirPos(FindLeafDirL(dirName.Left(namePos), leafDir),0);
sl@0
   775
    TEntryPos dumPos=dirPos;
sl@0
   776
    TFatDirEntry dumEntry;
sl@0
   777
sl@0
   778
    TBool isOriginalNameLegal = IsLegalDosName(name,EFalse,EFalse,EFalse,EFalse,ETrue);
sl@0
   779
    iFileCreationHelper.InitialiseL(name);
sl@0
   780
    TFileName fileName;
sl@0
   781
    TEntryPos startPos;
sl@0
   782
    TFatDirEntry startEntry;
sl@0
   783
    
sl@0
   784
    TRAPD(ret,DoFindL(name,KEntryAttMaskSupported,
sl@0
   785
                        startPos,startEntry,dumPos,dumEntry,
sl@0
   786
                        fileName,KErrNotFound,
sl@0
   787
                        &iFileCreationHelper,
sl@0
   788
                        leafDir));
sl@0
   789
sl@0
   790
    if (ret!=KErrNotFound && ret!=KErrNone)
sl@0
   791
        User::Leave(ret);
sl@0
   792
    if (ret!=KErrNotFound)
sl@0
   793
        {
sl@0
   794
        if (dumEntry.Attributes()&KEntryAttDir)
sl@0
   795
            User::Leave(KErrAlreadyExists);
sl@0
   796
        else
sl@0
   797
            User::Leave(KErrAccessDenied);
sl@0
   798
        }
sl@0
   799
    TShortName shortName;
sl@0
   800
sl@0
   801
    if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
sl@0
   802
        {
sl@0
   803
        GenerateShortNameL(dirPos.iCluster,name,shortName,ETrue);
sl@0
   804
        }
sl@0
   805
sl@0
   806
    TInt numEntries=1;
sl@0
   807
    if (isOriginalNameLegal==EFalse)
sl@0
   808
        numEntries=NumberOfVFatEntries(name.Length());
sl@0
   809
    dumPos=dirPos;
sl@0
   810
sl@0
   811
    if (iFileCreationHelper.IsNewEntryPosFound())
sl@0
   812
        {
sl@0
   813
        dumPos = iFileCreationHelper.EntryAddingPos();
sl@0
   814
        }
sl@0
   815
sl@0
   816
    AddDirEntryL(dumPos,numEntries);    //  Directory entry in leaf directory
sl@0
   817
    TInt startCluster;
sl@0
   818
    FOREVER
sl@0
   819
        {
sl@0
   820
        //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
sl@0
   821
        startCluster=FAT().AllocateSingleClusterL(dumPos.iCluster ? dumPos.iCluster : FAT().FreeClusterHint());
sl@0
   822
sl@0
   823
        FAT().FlushL();
sl@0
   824
        TRAPD(r, InitializeFirstDirClusterL(startCluster,dirPos.iCluster));
sl@0
   825
        if(r == KErrNone)
sl@0
   826
            break;
sl@0
   827
        if(r != KErrCorrupt)
sl@0
   828
            User::Leave(r);
sl@0
   829
        FAT().MarkAsBadClusterL(startCluster);
sl@0
   830
        }
sl@0
   831
    TFatDirEntry fatDirEntry;
sl@0
   832
    fatDirEntry.SetName(shortName);
sl@0
   833
    fatDirEntry.SetAttributes(KEntryAttDir);
sl@0
   834
    TTime now;
sl@0
   835
    now.UniversalTime();
sl@0
   836
    fatDirEntry.SetTime(now, TimeOffset());
sl@0
   837
    fatDirEntry.SetCreateTime(now, TimeOffset());
sl@0
   838
    fatDirEntry.SetStartCluster(startCluster);
sl@0
   839
    fatDirEntry.SetSize(0);
sl@0
   840
    if (isOriginalNameLegal)
sl@0
   841
        WriteDirEntryL(dumPos,fatDirEntry);
sl@0
   842
    else
sl@0
   843
        WriteDirEntryL(dumPos,fatDirEntry,name);
sl@0
   844
sl@0
   845
    iFileCreationHelper.Close();
sl@0
   846
    }
sl@0
   847
sl@0
   848
//-----------------------------------------------------------------------------------------
sl@0
   849
sl@0
   850
/**
sl@0
   851
    Setup 1st cluster of the new directory
sl@0
   852
sl@0
   853
    @param  aStartCluster   this entry start cluster number
sl@0
   854
    @param  aParentCluster  parent entry start cluster number
sl@0
   855
*/
sl@0
   856
void CFatMountCB::InitializeFirstDirClusterL(TInt aStartCluster,TInt aParentCluster)
sl@0
   857
    {
sl@0
   858
    const TUint32 KClusterSz= 1<<ClusterSizeLog2();
sl@0
   859
    const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
sl@0
   860
    const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)
sl@0
   861
sl@0
   862
    //-- allocate a buffer for directory file 1st cluster initialisation
sl@0
   863
    RBuf8 buf;
sl@0
   864
    CleanupClosePushL(buf);
sl@0
   865
sl@0
   866
    if(buf.CreateMax(KMaxBufSz) != KErrNone)
sl@0
   867
        buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
sl@0
   868
sl@0
   869
    buf.FillZ();
sl@0
   870
sl@0
   871
    //-- copy "." directory entry to the buffer
sl@0
   872
sl@0
   873
    //-- "." directory entry
sl@0
   874
    TFatDirEntry entry;
sl@0
   875
    TTime now;
sl@0
   876
    now.UniversalTime();
sl@0
   877
    entry.SetTime(now, TimeOffset() );
sl@0
   878
    entry.SetAttributes(KEntryAttDir);
sl@0
   879
    entry.SetCurrentDirectory();
sl@0
   880
    entry.SetStartCluster(aStartCluster);
sl@0
   881
    Mem::Copy(&buf[0],&entry,KSizeOfFatDirEntry);
sl@0
   882
sl@0
   883
    //-- append ".." directory entry
sl@0
   884
    entry.SetParentDirectory();
sl@0
   885
    entry.SetStartCluster(aParentCluster==RootIndicator() ? 0 : aParentCluster);
sl@0
   886
    Mem::Copy(&buf[0]+KSizeOfFatDirEntry,&entry,KSizeOfFatDirEntry);
sl@0
   887
sl@0
   888
    TEntryPos entryPos(aStartCluster,0);
sl@0
   889
sl@0
   890
    //-- write buffer to the beginning of the directory file.
sl@0
   891
    DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
sl@0
   892
sl@0
   893
    //-- fill in the rest of the cluster if we used a small buffer
sl@0
   894
    if((TUint32)buf.Size() < KClusterSz) //-- use special interface to access FAT directory file
sl@0
   895
    {
sl@0
   896
        buf.FillZ();
sl@0
   897
        const TInt restCnt = SectorsPerCluster() - 1;
sl@0
   898
        ASSERT(restCnt >=1);
sl@0
   899
sl@0
   900
        for(TInt i=0; i<restCnt; ++i)
sl@0
   901
        {
sl@0
   902
            entryPos.iPos += KMinBufSz;
sl@0
   903
            DirWriteL(entryPos, buf); //-- use directory cache when dealing with directories
sl@0
   904
        }
sl@0
   905
sl@0
   906
    }
sl@0
   907
sl@0
   908
    CleanupStack::PopAndDestroy(&buf);
sl@0
   909
sl@0
   910
    }
sl@0
   911
sl@0
   912
//-----------------------------------------------------------------------------------------
sl@0
   913
sl@0
   914
/**
sl@0
   915
    Remove a directory.
sl@0
   916
    @param aName directory name
sl@0
   917
    all trailing dots from the name will be removed
sl@0
   918
*/
sl@0
   919
void CFatMountCB::RmDirL(const TDesC& aName)
sl@0
   920
    {
sl@0
   921
    __PRINT2(_L("CFatMountCB::RmDirL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
   922
sl@0
   923
    CheckStateConsistentL();
sl@0
   924
    CheckWritableL();
sl@0
   925
sl@0
   926
    TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
sl@0
   927
sl@0
   928
    TFatDirEntry dirEntry;
sl@0
   929
    TEntryPos dirEntryPos(RootIndicator(),0); // Already checked entry is a directory
sl@0
   930
    FindEntryStartL(dirName,KEntryAttMatchMask|KEntryAttMatchExclusive,dirEntry,dirEntryPos);
sl@0
   931
    TEntryPos dosEntryPos=dirEntryPos;
sl@0
   932
    TFatDirEntry dosEntry=dirEntry;
sl@0
   933
    MoveToDosEntryL(dosEntryPos,dosEntry);
sl@0
   934
    if (!IsDirectoryEmptyL(StartCluster(dosEntry)))
sl@0
   935
        User::Leave(KErrInUse);
sl@0
   936
    // Remove the directory from cache before erasing
sl@0
   937
    if(iLeafDirCache && iLeafDirCache->CacheCount() > 0)
sl@0
   938
        {
sl@0
   939
        iLeafDirCache->RemoveDirL(StartCluster(dosEntry));
sl@0
   940
        }
sl@0
   941
sl@0
   942
    EraseDirEntryL(dirEntryPos,dirEntry);
sl@0
   943
    FAT().FreeClusterListL(StartCluster(dosEntry));
sl@0
   944
    FAT().FlushL();
sl@0
   945
    }
sl@0
   946
sl@0
   947
//-----------------------------------------------------------------------------------------
sl@0
   948
sl@0
   949
/**
sl@0
   950
    Delete a file
sl@0
   951
    @param aName file name
sl@0
   952
    all trailing dots from the name will be removed
sl@0
   953
*/
sl@0
   954
void CFatMountCB::DeleteL(const TDesC& aName)
sl@0
   955
    {
sl@0
   956
    __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
   957
sl@0
   958
    CheckStateConsistentL();
sl@0
   959
    CheckWritableL();
sl@0
   960
sl@0
   961
    TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
sl@0
   962
sl@0
   963
    TFatDirEntry firstEntry;
sl@0
   964
    TEntryPos firstEntryPos(RootIndicator(),0);
sl@0
   965
    FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
sl@0
   966
    TEntryPos dosEntryPos=firstEntryPos;
sl@0
   967
    TFatDirEntry dosEntry=firstEntry;
sl@0
   968
    MoveToDosEntryL(dosEntryPos,dosEntry);
sl@0
   969
    if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir))
sl@0
   970
        User::Leave(KErrAccessDenied);
sl@0
   971
    // Can not delete a file if it is clamped
sl@0
   972
    CMountCB* basePtr=(CMountCB*)this;
sl@0
   973
    TInt startCluster=StartCluster(dosEntry);
sl@0
   974
    if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0)
sl@0
   975
        User::Leave(KErrInUse);
sl@0
   976
    EraseDirEntryL(firstEntryPos,firstEntry);
sl@0
   977
    FAT().FreeClusterListL(StartCluster(dosEntry));
sl@0
   978
    FAT().FlushL();
sl@0
   979
    }
sl@0
   980
sl@0
   981
//-----------------------------------------------------------------------------------------
sl@0
   982
sl@0
   983
/**
sl@0
   984
sl@0
   985
    Rename or replace a directory entry.
sl@0
   986
    Assumes all files are closed and replace is only passed files.
sl@0
   987
    Assumes rename target does not exist or is the source file.
sl@0
   988
sl@0
   989
    --------------- operating mode --------------------------------------------
sl@0
   990
sl@0
   991
    * rename mode
sl@0
   992
sl@0
   993
    aOldName exists  |  aNewName exists |   result
sl@0
   994
        N                    N              leave KErrNotFound
sl@0
   995
        N                    Y              leave KErrNotFound
sl@0
   996
        Y                    N              rename aOldName -> aNewName
sl@0
   997
        Y                    Y              leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing
sl@0
   998
sl@0
   999
    * replace mode
sl@0
  1000
sl@0
  1001
        N                    N              leave KErrNotFound
sl@0
  1002
        N                    Y              leave KErrNotFound
sl@0
  1003
        Y                    N              rename aOldName -> aNewName
sl@0
  1004
        Y                    Y              contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then.
sl@0
  1005
sl@0
  1006
sl@0
  1007
    @param   aOldName           entry name to be renamed or replaced
sl@0
  1008
    @param   aNewName           a new entry name
sl@0
  1009
    @param   aMode              specifies renaming / replacing
sl@0
  1010
    @param   aNewDosEntryPos    on exit contains new entry Pos.
sl@0
  1011
*/
sl@0
  1012
void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos)
sl@0
  1013
    {
sl@0
  1014
    __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName);
sl@0
  1015
sl@0
  1016
    const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive.
sl@0
  1017
    const TBool renameMode = (aMode == EModeRename);
sl@0
  1018
    const TBool replaceMode = !renameMode;
sl@0
  1019
    TInt  nRes;
sl@0
  1020
sl@0
  1021
    if(namesAreIdentical && replaceMode)
sl@0
  1022
        return; //-- nothing to do, replacing file with itself
sl@0
  1023
sl@0
  1024
    //---------------------------------------------------------------------------------------------------------------------------
sl@0
  1025
    //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName'
sl@0
  1026
sl@0
  1027
    TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset
sl@0
  1028
    TEntryPos    oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set
sl@0
  1029
sl@0
  1030
    FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos);
sl@0
  1031
sl@0
  1032
    TFatDirEntry oldName_DosEntry    = oldName_FirstEntry;   //-- "aOldName" entry set DOS entry
sl@0
  1033
    TEntryPos    oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry
sl@0
  1034
sl@0
  1035
    MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry);
sl@0
  1036
sl@0
  1037
    const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry
sl@0
  1038
sl@0
  1039
    //-- check if the file "aOldName" is clamped. In this case it can't be replaced.
sl@0
  1040
    if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0))
sl@0
  1041
        User::Leave(KErrInUse);
sl@0
  1042
sl@0
  1043
    //---------------------------------------------------------------------------------------------------------------------------
sl@0
  1044
    //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not
sl@0
  1045
sl@0
  1046
    //-- extract new file name from the full path
sl@0
  1047
    TPtrC ptrNewName;
sl@0
  1048
    TPtrC ptrNewNameParentDir;
sl@0
  1049
    const TInt delimPos = aNewName.LocateReverse(KPathDelimiter);
sl@0
  1050
    ptrNewName.Set(aNewName.Mid(delimPos+1));
sl@0
  1051
    ptrNewNameParentDir.Set(aNewName.Left(delimPos+1));
sl@0
  1052
sl@0
  1053
    //-- find the parent directory of the "aNewName" and create iterator for it
sl@0
  1054
    TLeafDirData leafDir;
sl@0
  1055
    const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator
sl@0
  1056
    aNewName_DosEntryPos = aNewName_ParentDirPos;
sl@0
  1057
sl@0
  1058
    TEntryPos    newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set
sl@0
  1059
    TFatDirEntry newName_DosEntry;
sl@0
  1060
sl@0
  1061
    TFileName fileName;
sl@0
  1062
    iFileCreationHelper.InitialiseL(ptrNewName);
sl@0
  1063
    TFatDirEntry startEntry;
sl@0
  1064
sl@0
  1065
    TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported,
sl@0
  1066
                newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry,
sl@0
  1067
                fileName, KErrNotFound,
sl@0
  1068
                &iFileCreationHelper,
sl@0
  1069
                leafDir));
sl@0
  1070
sl@0
  1071
    if (nRes!=KErrNone && nRes!=KErrNotFound)
sl@0
  1072
        User::Leave(nRes);
sl@0
  1073
sl@0
  1074
    const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists.
sl@0
  1075
    const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue);
sl@0
  1076
sl@0
  1077
    if(renameMode && newFileExists)
sl@0
  1078
        {
sl@0
  1079
        if(!namesAreIdentical)
sl@0
  1080
        {
sl@0
  1081
        if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir))
sl@0
  1082
            {
sl@0
  1083
            User::Leave(KErrAccessDenied);  //-- leave with KErrAccessDenied if it is trying to rename a file
sl@0
  1084
                                            //      to a dir or vice versa.
sl@0
  1085
            }
sl@0
  1086
        User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists
sl@0
  1087
        }
sl@0
  1088
        else
sl@0
  1089
            {
sl@0
  1090
            if(!bNewNameIsVFAT && !bOldNameIsVFAT)
sl@0
  1091
                return; //-- renaming DOS name to itself
sl@0
  1092
            }
sl@0
  1093
        //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File"
sl@0
  1094
        }
sl@0
  1095
sl@0
  1096
    //---------------------------------------------------------------------------------------------------------------------------
sl@0
  1097
sl@0
  1098
    if(replaceMode && newFileExists)
sl@0
  1099
        {
sl@0
  1100
        //---------------------------------------------------------------------------------------------------------------------------
sl@0
  1101
        //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries.
sl@0
  1102
sl@0
  1103
        //-- check if we are still trying to replace the file with itself, probably using short name alias
sl@0
  1104
        if(aNewName_DosEntryPos == oldName_DosEntryPos)
sl@0
  1105
            return; //-- nothing to do, it's the same file
sl@0
  1106
sl@0
  1107
        const TInt oldNameStartCluster = StartCluster(oldName_DosEntry);
sl@0
  1108
        const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked
sl@0
  1109
sl@0
  1110
        newName_DosEntry.SetStartCluster(oldNameStartCluster);
sl@0
  1111
        newName_DosEntry.SetSize(oldName_DosEntry.Size());
sl@0
  1112
        newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0));
sl@0
  1113
        newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes());
sl@0
  1114
sl@0
  1115
            if(IsRuggedFSys())
sl@0
  1116
                {
sl@0
  1117
                //-- Note 1.
sl@0
  1118
                //-- set a special Id in reserved section for old and new entries.
sl@0
  1119
                //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain.
sl@0
  1120
                //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry.
sl@0
  1121
                //-- note that  SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID.
sl@0
  1122
                //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy.
sl@0
  1123
                //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980.
sl@0
  1124
sl@0
  1125
                newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
sl@0
  1126
                oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
sl@0
  1127
                WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
sl@0
  1128
                }
sl@0
  1129
sl@0
  1130
        //-- write 'aNewName' DOS dir. entry data back
sl@0
  1131
        WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry);
sl@0
  1132
sl@0
  1133
        //-- erase "oldName" entryset.
sl@0
  1134
        EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
sl@0
  1135
sl@0
  1136
        //-- free 'aNewName' cluster list
sl@0
  1137
        FAT().FreeClusterListL(newNameStartCluster);
sl@0
  1138
sl@0
  1139
        if(IsRuggedFSys())
sl@0
  1140
            FAT().FlushL();
sl@0
  1141
sl@0
  1142
        }
sl@0
  1143
    else //if(replaceMode && newFileExists)
sl@0
  1144
        {
sl@0
  1145
        //---------------------------------------------------------------------------------------------------------------------------
sl@0
  1146
        //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset
sl@0
  1147
sl@0
  1148
        TFatDirEntry newDosEntry = oldName_DosEntry;
sl@0
  1149
        //-- generate short name for the 'aNewName' entryset and make new DOS entry
sl@0
  1150
        if(bNewNameIsVFAT)
sl@0
  1151
            {//-- need to generate a short name for VFAT entryset DOS entry
sl@0
  1152
            TShortName shortName;
sl@0
  1153
sl@0
  1154
            if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
sl@0
  1155
                {
sl@0
  1156
                GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue);
sl@0
  1157
                }
sl@0
  1158
sl@0
  1159
            newDosEntry.SetName(shortName);
sl@0
  1160
            }
sl@0
  1161
        else
sl@0
  1162
            {//-- just use 'aNewName' as DOS name.
sl@0
  1163
            TBuf8<KFatDirNameSize+1> tmp; //-- the name may be "XXXXXXXX.YYY"
sl@0
  1164
            tmp.Copy(ptrNewName);
sl@0
  1165
            newDosEntry.SetName(DosNameToStdFormat(tmp));
sl@0
  1166
            }
sl@0
  1167
sl@0
  1168
        if(IsRuggedFSys())
sl@0
  1169
            {//-- the the note(1) above
sl@0
  1170
            newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
sl@0
  1171
            oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
sl@0
  1172
            WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
sl@0
  1173
            }
sl@0
  1174
sl@0
  1175
        //-- add new entryset to the directory
sl@0
  1176
        aNewName_DosEntryPos.iPos = 0;
sl@0
  1177
        aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster();
sl@0
  1178
sl@0
  1179
        if (iFileCreationHelper.IsNewEntryPosFound())
sl@0
  1180
            {
sl@0
  1181
            aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos();
sl@0
  1182
            }
sl@0
  1183
sl@0
  1184
        if(bNewNameIsVFAT)
sl@0
  1185
            {
sl@0
  1186
            const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length());
sl@0
  1187
            AddDirEntryL(aNewName_DosEntryPos, numEntries);
sl@0
  1188
            WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName);
sl@0
  1189
            }
sl@0
  1190
        else
sl@0
  1191
            {//-- new name is one DOS entry only
sl@0
  1192
            AddDirEntryL(aNewName_DosEntryPos, 1);
sl@0
  1193
            WriteDirEntryL(aNewName_DosEntryPos, newDosEntry);
sl@0
  1194
            }
sl@0
  1195
sl@0
  1196
        //-- erase old entryset.
sl@0
  1197
        EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
sl@0
  1198
sl@0
  1199
        //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry)
sl@0
  1200
        if((newDosEntry.Attributes() & KEntryAttDir))
sl@0
  1201
            {
sl@0
  1202
            TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry);
sl@0
  1203
sl@0
  1204
            TFatDirEntry chFatEnt;
sl@0
  1205
            ReadDirEntryL(parentPtrEntPos, chFatEnt);
sl@0
  1206
sl@0
  1207
            const TUint parentDirStartCluster_Old = StartCluster(chFatEnt);
sl@0
  1208
                  TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster();
sl@0
  1209
sl@0
  1210
            if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0)
sl@0
  1211
                {//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir.
sl@0
  1212
                //-- must have starting cluster 0
sl@0
  1213
                parentDirStartCluster_New = 0;
sl@0
  1214
                }
sl@0
  1215
sl@0
  1216
            if(parentDirStartCluster_Old != parentDirStartCluster_New)
sl@0
  1217
                {
sl@0
  1218
                chFatEnt.SetStartCluster(parentDirStartCluster_New);
sl@0
  1219
                WriteDirEntryL(parentPtrEntPos, chFatEnt);
sl@0
  1220
                }
sl@0
  1221
            // Invalidate leaf dir cache as it is hard to track the dir structure changes now
sl@0
  1222
            if (iLeafDirCache)
sl@0
  1223
                {
sl@0
  1224
                iLeafDirCache->Reset();
sl@0
  1225
                }
sl@0
  1226
            }
sl@0
  1227
        }//else if(replaceMode && newFileExists)
sl@0
  1228
sl@0
  1229
    iFileCreationHelper.Close();
sl@0
  1230
    }
sl@0
  1231
sl@0
  1232
//-----------------------------------------------------------------------------------------
sl@0
  1233
sl@0
  1234
/**
sl@0
  1235
    Rename 'aOldName' file/directory to 'aNewName'
sl@0
  1236
    all trailing dots from the names will be removed
sl@0
  1237
sl@0
  1238
    @param  aOldName        existing object name
sl@0
  1239
    @param  aNewName        new object name
sl@0
  1240
*/
sl@0
  1241
void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName)
sl@0
  1242
    {
sl@0
  1243
    __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
sl@0
  1244
sl@0
  1245
    CheckStateConsistentL();
sl@0
  1246
    CheckWritableL();
sl@0
  1247
sl@0
  1248
    TEntryPos newEntryPos;
sl@0
  1249
    DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos);
sl@0
  1250
sl@0
  1251
    if(!IsRuggedFSys())
sl@0
  1252
        FAT().FlushL();
sl@0
  1253
    }
sl@0
  1254
sl@0
  1255
//-----------------------------------------------------------------------------------------
sl@0
  1256
sl@0
  1257
/**
sl@0
  1258
    Replace contents of the 'aNewName' with the contents of 'aOldName'
sl@0
  1259
    all trailing dots from the names will be removed
sl@0
  1260
sl@0
  1261
    @param  aOldName        existing object name
sl@0
  1262
    @param  aNewName        new object name
sl@0
  1263
*/
sl@0
  1264
void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName)
sl@0
  1265
    {
sl@0
  1266
sl@0
  1267
    __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
sl@0
  1268
sl@0
  1269
    CheckStateConsistentL();
sl@0
  1270
    CheckWritableL();
sl@0
  1271
sl@0
  1272
    TEntryPos newEntryPos;
sl@0
  1273
    DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos);
sl@0
  1274
    if(!IsRuggedFSys())
sl@0
  1275
        FAT().FlushL();
sl@0
  1276
    }
sl@0
  1277
sl@0
  1278
//-----------------------------------------------------------------------------------------
sl@0
  1279
sl@0
  1280
sl@0
  1281
/**
sl@0
  1282
    Try to find a directory entry by the given name and path.
sl@0
  1283
    This method _must_ leave if the entry is not found. See the caller.
sl@0
  1284
sl@0
  1285
    @param  aName   path to the directory object. all trailing dots from the name will be removed.
sl@0
  1286
    @param  anEntry on return will contain the entry data
sl@0
  1287
sl@0
  1288
    @leave  KErrPathNotFound if there is no path to the aName
sl@0
  1289
            KErrNotFound     if the entry corresponding to the aName is not found
sl@0
  1290
            system-wide erorr code of media read failure.
sl@0
  1291
*/
sl@0
  1292
void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const
sl@0
  1293
    {
sl@0
  1294
    __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
  1295
sl@0
  1296
    CheckStateConsistentL();
sl@0
  1297
sl@0
  1298
    TEntryPos entryPos(RootIndicator(),0);
sl@0
  1299
    TFatDirEntry entry;
sl@0
  1300
    TPtr fileName(anEntry.iName.Des());
sl@0
  1301
sl@0
  1302
    TPtrC fullName = RemoveTrailingDots(aName);
sl@0
  1303
    TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  1304
    TLeafDirData leafDir;
sl@0
  1305
    entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir);
sl@0
  1306
    entryPos.iPos=0;
sl@0
  1307
    TEntryPos startPos;
sl@0
  1308
    TFatDirEntry startEntry;
sl@0
  1309
sl@0
  1310
    DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported,
sl@0
  1311
            startPos,startEntry,entryPos,entry,
sl@0
  1312
            fileName,KErrNotFound,
sl@0
  1313
            NULL,
sl@0
  1314
            leafDir);
sl@0
  1315
sl@0
  1316
sl@0
  1317
    anEntry.iAtt=entry.Attributes();
sl@0
  1318
    anEntry.iSize=entry.Size();
sl@0
  1319
    anEntry.iModified=entry.Time(TimeOffset());
sl@0
  1320
sl@0
  1321
    if (fileName.Length()==0)
sl@0
  1322
        {
sl@0
  1323
        TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name()));
sl@0
  1324
        LocaleUtils::ConvertToUnicodeL(fileName,dosName);
sl@0
  1325
        }
sl@0
  1326
    if ((TUint)anEntry.iSize>=sizeof(TCheckedUid))
sl@0
  1327
        ReadUidL(StartCluster(entry),anEntry);
sl@0
  1328
    }
sl@0
  1329
sl@0
  1330
//-----------------------------------------------------------------------------------------
sl@0
  1331
sl@0
  1332
/**
sl@0
  1333
    Set directory entry details.
sl@0
  1334
    @param  aName           entry name; all trailing dots from the name will be removed
sl@0
  1335
    @param  aTime           entry modification time (and last access as well)
sl@0
  1336
    @param  aSetAttMask     entry attributes OR mask
sl@0
  1337
    @param  aClearAttMask   entry attributes AND mask
sl@0
  1338
sl@0
  1339
*/
sl@0
  1340
void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
sl@0
  1341
    {
sl@0
  1342
    __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
  1343
sl@0
  1344
    CheckStateConsistentL();
sl@0
  1345
    CheckWritableL();
sl@0
  1346
sl@0
  1347
    TEntryPos firstEntryPos(RootIndicator(),0);
sl@0
  1348
    TFatDirEntry firstEntry;
sl@0
  1349
    FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos);
sl@0
  1350
    MoveToDosEntryL(firstEntryPos,firstEntry);
sl@0
  1351
    TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
sl@0
  1352
    if (setAttMask|aClearAttMask)
sl@0
  1353
        {
sl@0
  1354
        TInt att=firstEntry.Attributes();
sl@0
  1355
        att|=setAttMask;
sl@0
  1356
        att&=(~aClearAttMask);
sl@0
  1357
        firstEntry.SetAttributes(att);
sl@0
  1358
        }
sl@0
  1359
    if (aSetAttMask&KEntryAttModified)
sl@0
  1360
        {
sl@0
  1361
        firstEntry.SetTime(aTime,TimeOffset());
sl@0
  1362
        }
sl@0
  1363
    WriteDirEntryL(firstEntryPos,firstEntry);
sl@0
  1364
    }
sl@0
  1365
sl@0
  1366
//-----------------------------------------------------------------------------------------
sl@0
  1367
sl@0
  1368
void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const
sl@0
  1369
//
sl@0
  1370
// Check one fat cluster for loops.
sl@0
  1371
//
sl@0
  1372
    {
sl@0
  1373
sl@0
  1374
    if (aCluster==aPreviousCluster)
sl@0
  1375
        User::Leave(KErrCorrupt); // Found loop
sl@0
  1376
sl@0
  1377
    aCount++;
sl@0
  1378
    if (aCount==aChangePreviousCluster)
sl@0
  1379
        {
sl@0
  1380
        aCount=0;
sl@0
  1381
        aChangePreviousCluster<<=1;
sl@0
  1382
        aPreviousCluster=aCluster;
sl@0
  1383
        }
sl@0
  1384
    }
sl@0
  1385
sl@0
  1386
//-----------------------------------------------------------------------------------------
sl@0
  1387
sl@0
  1388
void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const
sl@0
  1389
//
sl@0
  1390
// Check for loops
sl@0
  1391
//
sl@0
  1392
    {
sl@0
  1393
sl@0
  1394
    TInt cluster=StartCluster(anEntry);
sl@0
  1395
    if (cluster==0 && anEntry.Size()==0)
sl@0
  1396
        return;
sl@0
  1397
sl@0
  1398
    TInt previousCluster=cluster;
sl@0
  1399
    TInt changePreviousCluster=1;
sl@0
  1400
    TInt count=0;
sl@0
  1401
sl@0
  1402
sl@0
  1403
    for(;;)
sl@0
  1404
        {
sl@0
  1405
        if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber()))
sl@0
  1406
            User::Leave(KErrCorrupt);
sl@0
  1407
sl@0
  1408
         if(!FAT().GetNextClusterL(cluster))
sl@0
  1409
            break;
sl@0
  1410
sl@0
  1411
         DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count);
sl@0
  1412
        }
sl@0
  1413
sl@0
  1414
    }
sl@0
  1415
sl@0
  1416
//-----------------------------------------------------------------------------------------
sl@0
  1417
sl@0
  1418
/**
sl@0
  1419
    Open/Create/Replace a file on the current mount.
sl@0
  1420
sl@0
  1421
    @param  aName   file name; all trailing dots from the name will be removed
sl@0
  1422
    @param  aMode   File open mode, See TFileMode
sl@0
  1423
    @param  anOpen  specifies action: open, create or replace the file
sl@0
  1424
    @param  aFile   pointer to the CFileCB object to populate
sl@0
  1425
sl@0
  1426
*/
sl@0
  1427
void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile)
sl@0
  1428
    {
sl@0
  1429
    __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName);
sl@0
  1430
sl@0
  1431
    CheckStateConsistentL();
sl@0
  1432
sl@0
  1433
    TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
sl@0
  1434
sl@0
  1435
    TFatDirEntry firstEntry;
sl@0
  1436
    TEntryPos firstEntryPos(RootIndicator(),0);
sl@0
  1437
    TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  1438
    TPtrC name(fullName.Mid(nPos));
sl@0
  1439
    TInt ret = KErrNone;
sl@0
  1440
sl@0
  1441
    iFileCreationHelper.Close();
sl@0
  1442
    if (anOpen == EFileCreate || anOpen == EFileReplace)
sl@0
  1443
        {
sl@0
  1444
        iFileCreationHelper.InitialiseL(name);
sl@0
  1445
        TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper));
sl@0
  1446
        }
sl@0
  1447
    else
sl@0
  1448
        {
sl@0
  1449
        TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos));
sl@0
  1450
        }
sl@0
  1451
sl@0
  1452
    if (ret!=KErrNone && ret!=KErrNotFound)
sl@0
  1453
        User::Leave(ret);
sl@0
  1454
sl@0
  1455
    if (ret==KErrNone)
sl@0
  1456
        {
sl@0
  1457
        MoveToDosEntryL(firstEntryPos,firstEntry);
sl@0
  1458
        if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume))
sl@0
  1459
            User::Leave(KErrAccessDenied);
sl@0
  1460
        if (anOpen==EFileCreate)
sl@0
  1461
            User::Leave(KErrAlreadyExists);
sl@0
  1462
        if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite)
sl@0
  1463
            User::Leave(KErrAccessDenied);
sl@0
  1464
        if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0))
sl@0
  1465
            User::Leave(KErrInUse);
sl@0
  1466
        CheckFatForLoopsL(firstEntry);
sl@0
  1467
        }
sl@0
  1468
    else
sl@0
  1469
        {
sl@0
  1470
        if (anOpen==EFileOpen)
sl@0
  1471
            User::Leave(KErrNotFound);
sl@0
  1472
sl@0
  1473
        //-- here we try to either create or replace file
sl@0
  1474
        CheckWritableL();
sl@0
  1475
sl@0
  1476
        TLeafDirData leafDir;
sl@0
  1477
sl@0
  1478
        TInt numEntries = iFileCreationHelper.NumOfAddingEntries();
sl@0
  1479
        TShortName shortName;
sl@0
  1480
        if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
sl@0
  1481
            {
sl@0
  1482
            firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
sl@0
  1483
            GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue);
sl@0
  1484
            }
sl@0
  1485
sl@0
  1486
        if (iFileCreationHelper.IsNewEntryPosFound())
sl@0
  1487
            {
sl@0
  1488
            firstEntryPos = iFileCreationHelper.EntryAddingPos();
sl@0
  1489
            }
sl@0
  1490
        else
sl@0
  1491
            {
sl@0
  1492
            firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
sl@0
  1493
            firstEntryPos.iPos=0;
sl@0
  1494
            }
sl@0
  1495
sl@0
  1496
        AddDirEntryL(firstEntryPos,numEntries);
sl@0
  1497
        firstEntry.InitZ();
sl@0
  1498
        firstEntry.SetName(shortName);
sl@0
  1499
        firstEntry.SetStartCluster(0);
sl@0
  1500
sl@0
  1501
        TTime now;
sl@0
  1502
        now.UniversalTime();
sl@0
  1503
        firstEntry.SetCreateTime(now, TimeOffset() );
sl@0
  1504
sl@0
  1505
        if (iFileCreationHelper.IsTrgNameLegalDosName())
sl@0
  1506
            WriteDirEntryL(firstEntryPos,firstEntry);
sl@0
  1507
        else
sl@0
  1508
            WriteDirEntryL(firstEntryPos,firstEntry,name);
sl@0
  1509
        }
sl@0
  1510
sl@0
  1511
    CFatFileCB& file=(*((CFatFileCB*)aFile));
sl@0
  1512
    file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos);
sl@0
  1513
    if (anOpen==EFileReplace && file.Size())
sl@0
  1514
        {
sl@0
  1515
        file.SetSizeL(0);
sl@0
  1516
        file.SetSize(0);
sl@0
  1517
        }
sl@0
  1518
    if (file.IsSeekIndex()==EFalse)
sl@0
  1519
        file.CreateSeekIndex();
sl@0
  1520
    if (anOpen==EFileReplace || anOpen==EFileCreate)
sl@0
  1521
        file.SetArchiveAttribute();
sl@0
  1522
sl@0
  1523
    if(!IsRuggedFSys())
sl@0
  1524
        FAT().FlushL();
sl@0
  1525
sl@0
  1526
    iFileCreationHelper.Close();
sl@0
  1527
    }
sl@0
  1528
sl@0
  1529
//-----------------------------------------------------------------------------------------
sl@0
  1530
sl@0
  1531
sl@0
  1532
/**
sl@0
  1533
    Open a directory on the current mount.
sl@0
  1534
sl@0
  1535
    @param  aName   path to the object in the directory we want to open; all trailing dots from the name will be removed
sl@0
  1536
    @param  aDir    dir. CB to be filled in.
sl@0
  1537
sl@0
  1538
    If there is no such a path, this method must leave with KErrPathNotFound
sl@0
  1539
sl@0
  1540
    @leave  KErrPathNotFound if thereis no such path
sl@0
  1541
    @leave  error code on media read fault
sl@0
  1542
*/
sl@0
  1543
void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir)
sl@0
  1544
    {
sl@0
  1545
    __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName);
sl@0
  1546
sl@0
  1547
    CheckStateConsistentL();
sl@0
  1548
sl@0
  1549
    const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
sl@0
  1550
sl@0
  1551
    TInt namePos=dirName.LocateReverse(KPathDelimiter);
sl@0
  1552
sl@0
  1553
    TFatDirEntry dosEntry;
sl@0
  1554
    TEntryPos dosEntryPos(RootIndicator(),0);
sl@0
  1555
    if (namePos==0)
sl@0
  1556
        InitializeRootEntry(dosEntry);
sl@0
  1557
    else
sl@0
  1558
        {
sl@0
  1559
        TPtrC dirPath=dirName.Left(namePos);
sl@0
  1560
        TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1;
sl@0
  1561
        TLeafDirData leafDir;
sl@0
  1562
        dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf
sl@0
  1563
        dosEntryPos.iPos=0;
sl@0
  1564
sl@0
  1565
        TFileName fileName;
sl@0
  1566
        TEntryPos startPos;
sl@0
  1567
        TFatDirEntry startEntry;
sl@0
  1568
        DoFindL(dirPath.Mid(dirPos),
sl@0
  1569
                KEntryAttMatchMask|KEntryAttMatchExclusive,
sl@0
  1570
                startPos, startEntry, dosEntryPos, dosEntry,
sl@0
  1571
                fileName, KErrPathNotFound,
sl@0
  1572
                NULL,
sl@0
  1573
                leafDir);
sl@0
  1574
sl@0
  1575
sl@0
  1576
        }
sl@0
  1577
sl@0
  1578
    TPtrC matchName(dirName.Mid(namePos+1));
sl@0
  1579
    if (matchName.Length()==0)
sl@0
  1580
        matchName.Set(_L("*"));
sl@0
  1581
sl@0
  1582
    ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName);
sl@0
  1583
sl@0
  1584
    }
sl@0
  1585
sl@0
  1586
//-----------------------------------------------------------------------------------------
sl@0
  1587
sl@0
  1588
TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster)
sl@0
  1589
//
sl@0
  1590
// Check aCluster contains no directory entries other than . and ..
sl@0
  1591
//
sl@0
  1592
    {
sl@0
  1593
sl@0
  1594
    __PRINT(_L("CFatMountCB::IsDirectoryEmptyL"));
sl@0
  1595
    TEntryPos dirEntryPos(aCluster,0);
sl@0
  1596
    TFatDirEntry dirEntry;
sl@0
  1597
    FOREVER
sl@0
  1598
        {
sl@0
  1599
        ReadDirEntryL(dirEntryPos,dirEntry);
sl@0
  1600
        MoveToDosEntryL(dirEntryPos,dirEntry);
sl@0
  1601
        if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory())
sl@0
  1602
            goto LoopEnd;
sl@0
  1603
        if (dirEntry.IsEndOfDirectory())
sl@0
  1604
            return ETrue;
sl@0
  1605
        if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd()))
sl@0
  1606
            return ETrue;   //  Root Directory has no end of directory marker
sl@0
  1607
        if (!dirEntry.IsErased())
sl@0
  1608
            return EFalse;
sl@0
  1609
LoopEnd:
sl@0
  1610
        MoveToNextEntryL(dirEntryPos);
sl@0
  1611
        }
sl@0
  1612
    }
sl@0
  1613
sl@0
  1614
//-----------------------------------------------------------------------------------------
sl@0
  1615
sl@0
  1616
/**
sl@0
  1617
    Overwrite as many contiguous file clusters as possible.
sl@0
  1618
*/
sl@0
  1619
void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster)
sl@0
  1620
    {
sl@0
  1621
sl@0
  1622
    __PRINT(_L("CFatMountCB::DoWriteToClusterListL"));
sl@0
  1623
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
sl@0
  1624
sl@0
  1625
    TInt endCluster=0;
sl@0
  1626
sl@0
  1627
    const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
sl@0
  1628
    const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
sl@0
  1629
    const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
sl@0
  1630
    const TInt writeLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
sl@0
  1631
    TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
sl@0
  1632
sl@0
  1633
    TRAPD(r, iRawDisk->WriteL(dataStart,writeLength,aSrc,aMessage,anOffset));
sl@0
  1634
sl@0
  1635
    if(r == KErrNone) // Write succeded
sl@0
  1636
        {
sl@0
  1637
        aPos.iPos+=writeLength;
sl@0
  1638
        aPos.iCluster=endCluster;
sl@0
  1639
        return;
sl@0
  1640
        }
sl@0
  1641
sl@0
  1642
    if(r != KErrCorrupt) // failure not due to corruption so propogate up
sl@0
  1643
        User::Leave(r);
sl@0
  1644
sl@0
  1645
    TErrorInfoBuf errinf;
sl@0
  1646
    r = iRawDisk->GetLastErrorInfo(errinf);
sl@0
  1647
sl@0
  1648
    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
sl@0
  1649
        {
sl@0
  1650
sl@0
  1651
        const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
sl@0
  1652
              TInt goodcluster = FAT().AllocateSingleClusterL(badcluster);
sl@0
  1653
sl@0
  1654
        //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster.
sl@0
  1655
        TInt cluster = aPos.iCluster;
sl@0
  1656
        if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<<ClusterSizeLog2())))
sl@0
  1657
            cluster--;
sl@0
  1658
sl@0
  1659
        if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
sl@0
  1660
            { //Copy the contents already present in this cluster to new cluster allocated.
sl@0
  1661
            const TInt sizeToRead = aPos.iPos - ((aPos.iPos >> ClusterSizeLog2()) << ClusterSizeLog2());
sl@0
  1662
            dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead));
sl@0
  1663
sl@0
  1664
sl@0
  1665
            //-- Allocate the buffer required to copy the contents from bad cluster
sl@0
  1666
            RBuf8 clustBuf;
sl@0
  1667
            CleanupClosePushL(clustBuf);
sl@0
  1668
            if(clustBuf.CreateMax(sizeToRead) != KErrNone)
sl@0
  1669
                {
sl@0
  1670
                FAT().FreeClusterListL(goodcluster);
sl@0
  1671
                User::Leave(KErrNoMemory);
sl@0
  1672
                }
sl@0
  1673
sl@0
  1674
            r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer
sl@0
  1675
            if(r != KErrNone) //If read fails dont do anything not even marking bad cluster.
sl@0
  1676
                {
sl@0
  1677
                FAT().FreeClusterListL(goodcluster);
sl@0
  1678
                User::Leave(r);
sl@0
  1679
                }
sl@0
  1680
sl@0
  1681
            //Copy the bad and good cluster,required to adjust the start cluster number.
sl@0
  1682
            if(aBadcluster == 0)
sl@0
  1683
                aBadcluster = badcluster;
sl@0
  1684
sl@0
  1685
            aGoodcluster = goodcluster;
sl@0
  1686
sl@0
  1687
            FOREVER
sl@0
  1688
                {
sl@0
  1689
                //Calculate and copy the contents to new cluster.
sl@0
  1690
                aPos.iCluster = goodcluster;
sl@0
  1691
                dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead);
sl@0
  1692
sl@0
  1693
                r = LocalDrive()->Write(dataStart, clustBuf);
sl@0
  1694
                if(r == KErrNone)
sl@0
  1695
                    { // Copied contents to new cluster so fix up the chain and mark the cluster as bad.
sl@0
  1696
                    FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
sl@0
  1697
                    FAT().MarkAsBadClusterL(badcluster);
sl@0
  1698
                    aGoodcluster = goodcluster;
sl@0
  1699
                    CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer
sl@0
  1700
                    return;
sl@0
  1701
                    }
sl@0
  1702
                else if(r == KErrCorrupt)
sl@0
  1703
                    {
sl@0
  1704
                    r = LocalDrive()->GetLastErrorInfo(errinf);
sl@0
  1705
                    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector)
sl@0
  1706
                        { //Allocate new cluster and adjust the cluster list.
sl@0
  1707
                        goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster);
sl@0
  1708
                        FAT().MarkAsBadClusterL(aPos.iCluster);
sl@0
  1709
                        continue;
sl@0
  1710
                        }
sl@0
  1711
                        r = KErrCorrupt;
sl@0
  1712
                    }
sl@0
  1713
                    //Not able to write successfully so dont alter the original list.
sl@0
  1714
                    aBadcluster = aGoodcluster = 0;
sl@0
  1715
                    FAT().FreeClusterListL(goodcluster);
sl@0
  1716
                    User::Leave(r);
sl@0
  1717
                }
sl@0
  1718
sl@0
  1719
        }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
sl@0
  1720
sl@0
  1721
        if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist
sl@0
  1722
            {
sl@0
  1723
            // return bad and good clusters for CFatFileCB to fix up
sl@0
  1724
            FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
sl@0
  1725
            aBadcluster = badcluster;
sl@0
  1726
            aGoodcluster = goodcluster;
sl@0
  1727
            aPos.iCluster = goodcluster;
sl@0
  1728
            }
sl@0
  1729
        else    //fix up chain
sl@0
  1730
            {
sl@0
  1731
            FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
sl@0
  1732
            if(badcluster > aPos.iCluster)  //bad cluster not first in this contiguous list
sl@0
  1733
                FAT().WriteL(badcluster-1, goodcluster);
sl@0
  1734
            else    //first cluster of this contigous list bad so update last cluster of previous contiguous list
sl@0
  1735
                FAT().WriteL(aLastcluster, goodcluster);
sl@0
  1736
            }
sl@0
  1737
sl@0
  1738
        FAT().MarkAsBadClusterL(badcluster);
sl@0
  1739
sl@0
  1740
sl@0
  1741
        return;
sl@0
  1742
        }
sl@0
  1743
    User::Leave(KErrCorrupt);
sl@0
  1744
    }
sl@0
  1745
sl@0
  1746
//-----------------------------------------------------------------------------------------
sl@0
  1747
sl@0
  1748
void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster)
sl@0
  1749
//
sl@0
  1750
// Overwrite cluster list.
sl@0
  1751
//
sl@0
  1752
    {
sl@0
  1753
sl@0
  1754
    __PRINT(_L("CFatMountCB::WriteToClusterListL"));
sl@0
  1755
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
sl@0
  1756
sl@0
  1757
    const TUint startPos=aPos.iPos;
sl@0
  1758
    const TUint temp=startPos>>ClusterSizeLog2();
sl@0
  1759
    const TUint length = (TUint)aLength;
sl@0
  1760
sl@0
  1761
    if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
sl@0
  1762
        {
sl@0
  1763
        __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
sl@0
  1764
        }
sl@0
  1765
sl@0
  1766
    TUint offset=0;
sl@0
  1767
    TInt previouscluster=0;
sl@0
  1768
    FOREVER
sl@0
  1769
        {
sl@0
  1770
        DoWriteToClusterListL(aPos,length-offset,aSrc,aMessage,anOffset+offset, previouscluster, aBadcluster, aGoodcluster);
sl@0
  1771
        if (offset == (aPos.iPos-startPos))
sl@0
  1772
            continue;
sl@0
  1773
        offset=aPos.iPos-startPos;
sl@0
  1774
        __ASSERT_ALWAYS(aPos.iPos>startPos,User::Leave(KErrCorrupt));
sl@0
  1775
        previouscluster=aPos.iCluster;
sl@0
  1776
        if (offset<length)
sl@0
  1777
            {__ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));}
sl@0
  1778
        if (offset>=length)
sl@0
  1779
            return;
sl@0
  1780
        }
sl@0
  1781
    }
sl@0
  1782
sl@0
  1783
//-----------------------------------------------------------------------------------------
sl@0
  1784
sl@0
  1785
void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
sl@0
  1786
//
sl@0
  1787
// Read from as many contiguous file clusters as possible
sl@0
  1788
//
sl@0
  1789
    {
sl@0
  1790
sl@0
  1791
    __PRINT(_L("CFatMountCB::DoReadFromClusterListL"));
sl@0
  1792
sl@0
  1793
    TInt endCluster=0;
sl@0
  1794
sl@0
  1795
    const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
sl@0
  1796
    const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
sl@0
  1797
    const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
sl@0
  1798
    const TInt readLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
sl@0
  1799
    const TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
sl@0
  1800
sl@0
  1801
    TRAPD(r, iRawDisk->ReadL(dataStart,readLength,aTrg,aMessage,anOffset));
sl@0
  1802
sl@0
  1803
    if(r == KErrNone) // Read succeded
sl@0
  1804
        {
sl@0
  1805
        aPos.iPos+=readLength;
sl@0
  1806
        aPos.iCluster=endCluster;
sl@0
  1807
        return;
sl@0
  1808
        }
sl@0
  1809
    if(r != KErrCorrupt) // failure not due to corruption so propogate up
sl@0
  1810
        User::Leave(r);
sl@0
  1811
sl@0
  1812
    TErrorInfoBuf errinf;
sl@0
  1813
    r = iRawDisk->GetLastErrorInfo(errinf);
sl@0
  1814
sl@0
  1815
    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
sl@0
  1816
        {
sl@0
  1817
        TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
sl@0
  1818
        FAT().MarkAsBadClusterL(badcluster);
sl@0
  1819
        }
sl@0
  1820
sl@0
  1821
    User::Leave(KErrCorrupt);
sl@0
  1822
    }
sl@0
  1823
sl@0
  1824
//-----------------------------------------------------------------------------------------
sl@0
  1825
sl@0
  1826
void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
sl@0
  1827
//
sl@0
  1828
// Read from cluster list
sl@0
  1829
//
sl@0
  1830
    {
sl@0
  1831
sl@0
  1832
    __PRINT(_L("CFatMountCB::ReadFromClusterListL"));
sl@0
  1833
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
sl@0
  1834
sl@0
  1835
    const TInt startPos=aPos.iPos;
sl@0
  1836
    const TInt temp=startPos>>ClusterSizeLog2();
sl@0
  1837
sl@0
  1838
    if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
sl@0
  1839
        {
sl@0
  1840
        __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
sl@0
  1841
        }
sl@0
  1842
sl@0
  1843
    TInt offset=0;
sl@0
  1844
    FOREVER
sl@0
  1845
        {
sl@0
  1846
        DoReadFromClusterListL(aPos,aLength-offset,aTrg,aMessage,anOffset+offset);
sl@0
  1847
        offset=aPos.iPos-startPos;
sl@0
  1848
        if ((offset<aLength))
sl@0
  1849
            {
sl@0
  1850
            __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
sl@0
  1851
            }
sl@0
  1852
        if (offset>=aLength)
sl@0
  1853
            return;
sl@0
  1854
        }
sl@0
  1855
    }
sl@0
  1856
sl@0
  1857
//-----------------------------------------------------------------------------------------
sl@0
  1858
sl@0
  1859
TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const
sl@0
  1860
//
sl@0
  1861
// Navigate the path to find the leaf directory.
sl@0
  1862
// Returns the startcluster of data for the directory found.
sl@0
  1863
//
sl@0
  1864
    {
sl@0
  1865
sl@0
  1866
    __PRINT(_L("CFatMountCB::FindLeafDirL"));
sl@0
  1867
sl@0
  1868
    TLex lex(aName);
sl@0
  1869
    TInt r;
sl@0
  1870
    TEntryPos entryPos(RootIndicator(),0);
sl@0
  1871
sl@0
  1872
    if (iLeafDirCache == NULL)
sl@0
  1873
        {
sl@0
  1874
        TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0;
sl@0
  1875
        TInt namelen=aName.Length();
sl@0
  1876
        if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen))
sl@0
  1877
            {
sl@0
  1878
            if (leaflen==namelen)
sl@0
  1879
                return(iLastLeafDirCluster);
sl@0
  1880
            lex.Inc(leaflen-1);
sl@0
  1881
            entryPos.iCluster=iLastLeafDirCluster;
sl@0
  1882
            }
sl@0
  1883
        }
sl@0
  1884
    else
sl@0
  1885
        {
sl@0
  1886
        // Skip root directory
sl@0
  1887
        if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1)
sl@0
  1888
            {
sl@0
  1889
            TInt err = iLeafDirCache->FindInCache(aName, aLeafDir);
sl@0
  1890
            if (err == KErrNone)
sl@0
  1891
                {
sl@0
  1892
                ASSERT(aLeafDir.iClusterNum > 0);
sl@0
  1893
                return aLeafDir.iClusterNum;
sl@0
  1894
                }
sl@0
  1895
            else if (err != KErrNotFound)
sl@0
  1896
                {
sl@0
  1897
                User::LeaveIfError(err);
sl@0
  1898
                }
sl@0
  1899
            }
sl@0
  1900
        }
sl@0
  1901
sl@0
  1902
    FOREVER
sl@0
  1903
        {
sl@0
  1904
        lex.Inc(); // Skip path delimiter
sl@0
  1905
        lex.Mark();
sl@0
  1906
        r=lex.Remainder().Locate(KPathDelimiter);
sl@0
  1907
        if (r==KErrNotFound)
sl@0
  1908
            r=lex.Remainder().Length();
sl@0
  1909
        if (r==0) // End of the path
sl@0
  1910
            break;
sl@0
  1911
        lex.Inc(r); // Set the token length
sl@0
  1912
        TFatDirEntry entry;
sl@0
  1913
sl@0
  1914
        TFileName fileName;
sl@0
  1915
        TEntryPos startPos;
sl@0
  1916
        TFatDirEntry startEntry;
sl@0
  1917
        DoFindL(lex.MarkedToken(),
sl@0
  1918
                KEntryAttMatchMask|KEntryAttMatchExclusive,
sl@0
  1919
                startPos, startEntry, entryPos, entry,
sl@0
  1920
                fileName, KErrPathNotFound,
sl@0
  1921
                NULL,
sl@0
  1922
                aLeafDir);
sl@0
  1923
sl@0
  1924
sl@0
  1925
        entryPos.iCluster=StartCluster(entry);
sl@0
  1926
        entryPos.iPos=0;
sl@0
  1927
        }
sl@0
  1928
sl@0
  1929
    if (iLeafDirCache == NULL)
sl@0
  1930
        {
sl@0
  1931
        AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName);
sl@0
  1932
        ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster;
sl@0
  1933
        }
sl@0
  1934
    else
sl@0
  1935
        {
sl@0
  1936
        if (aName.Length() > 1)
sl@0
  1937
            {
sl@0
  1938
            aLeafDir = TLeafDirData(entryPos.iCluster);
sl@0
  1939
            iLeafDirCache->AddToCacheL(aName, aLeafDir);
sl@0
  1940
            }
sl@0
  1941
        }
sl@0
  1942
sl@0
  1943
    return entryPos.iCluster;
sl@0
  1944
    }
sl@0
  1945
sl@0
  1946
//-----------------------------------------------------------------------------------------
sl@0
  1947
sl@0
  1948
/**
sl@0
  1949
    Search for a specified name winthin directory cache
sl@0
  1950
    Works similary to TBool CFatMountCB::DoFindL()
sl@0
  1951
sl@0
  1952
    @param  anAtt           attributes of the object to find
sl@0
  1953
    @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
sl@0
  1954
    @param  aStartEntry     on return will contain first VFAT dir entry
sl@0
  1955
    @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
sl@0
  1956
    @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
sl@0
  1957
    @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
sl@0
  1958
    @param  aAuxParam       some parameters package
sl@0
  1959
    @param  aFileCreationHelper       a helper package for file creations
sl@0
  1960
sl@0
  1961
    @return ETrue if the specified name is found in the cache. In this case aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName will contain valid values
sl@0
  1962
*/
sl@0
  1963
TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos,  
sl@0
  1964
                                        TFatDirEntry& aStartEntry,  TEntryPos& aDosEntryPos,
sl@0
  1965
                                        TFatDirEntry& aDosEntry,    TDes& aFileName,
sl@0
  1966
                                        const TFindHelper& aAuxParam,
sl@0
  1967
                                        XFileCreationHelper* aFileCreationHelper,
sl@0
  1968
                                        const TLeafDirData& aLeafDir) const
sl@0
  1969
    {
sl@0
  1970
    TBool bCacheMatchFound = EFalse;
sl@0
  1971
sl@0
  1972
    //-- get an interface to the Dir. cache
sl@0
  1973
    MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
sl@0
  1974
    ASSERT(pDirCache);
sl@0
  1975
    if(!pDirCache)
sl@0
  1976
        return EFalse;
sl@0
  1977
sl@0
  1978
    //-- save original values in order to restore them in the case of negative search results
sl@0
  1979
    TEntryPos       StartEntryPos1(aStartEntryPos);
sl@0
  1980
    TEntryPos       DosEntryPos1(aDosEntryPos);
sl@0
  1981
    TFatDirEntry    StartEntry1(aStartEntry);
sl@0
  1982
    TFatDirEntry    DosEntry1(aDosEntry);
sl@0
  1983
sl@0
  1984
    TInt64          nCachedLinPos;
sl@0
  1985
sl@0
  1986
    const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size
sl@0
  1987
    const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes
sl@0
  1988
    const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2;  //-- maximal number of dir entries that can be in the cache
sl@0
  1989
sl@0
  1990
    const TUint   pageSzLog2 = pDirCache->PageSizeInBytesLog2();
sl@0
  1991
    TBool ScanMRUPageFirst  = EFalse;
sl@0
  1992
    TBool MRUPageScanned    = EFalse;
sl@0
  1993
sl@0
  1994
    // if MRU pos is availale, start with MRU page
sl@0
  1995
    if (aLeafDir.iMRUPos.Cluster())
sl@0
  1996
        {
sl@0
  1997
        ScanMRUPageFirst = ETrue;
sl@0
  1998
        DosEntryPos1 = aLeafDir.iMRUPos;
sl@0
  1999
        }
sl@0
  2000
sl@0
  2001
    TInt numFound = 0;
sl@0
  2002
    TEntryPos startPos = DosEntryPos1;
sl@0
  2003
    TInt clusterNum = DosEntryPos1.iCluster;
sl@0
  2004
sl@0
  2005
    for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt)
sl@0
  2006
        {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries
sl@0
  2007
         //-- that can be cached. Helps to avoid problems with infinite (looped) directories
sl@0
  2008
sl@0
  2009
        if (IsEndOfClusterCh(DosEntryPos1.iCluster))
sl@0
  2010
            {
sl@0
  2011
            // refer back to the last stored cluster position
sl@0
  2012
            //  note aFileCreationHelper may not be initialised for file opening operations
sl@0
  2013
            if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster)
sl@0
  2014
                {
sl@0
  2015
                TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry);
sl@0
  2016
                aFileCreationHelper->SetEntryAddingPos(dummyPos);
sl@0
  2017
                aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2018
                }
sl@0
  2019
sl@0
  2020
            if (ScanMRUPageFirst && !MRUPageScanned)
sl@0
  2021
                {
sl@0
  2022
                DosEntryPos1 = aDosEntryPos;
sl@0
  2023
                MRUPageScanned = ETrue;
sl@0
  2024
                continue;
sl@0
  2025
                }
sl@0
  2026
            break; //-- this was the last cluster in this directory
sl@0
  2027
            }
sl@0
  2028
sl@0
  2029
        const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
sl@0
  2030
        DosEntryPos1.iPos = pageStartPos;
sl@0
  2031
        TBool   PassedPageBoundary = EFalse;
sl@0
  2032
sl@0
  2033
        const TInt64  entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory
sl@0
  2034
        const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached
sl@0
  2035
        if(cachePageSz)
sl@0
  2036
            {//-- current page is in the directory cache
sl@0
  2037
             //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos);
sl@0
  2038
sl@0
  2039
            //-- search to the end of the cached page.
sl@0
  2040
            // Note GetDirEntry() will read data beyond cache page boundary
sl@0
  2041
            const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2;
sl@0
  2042
sl@0
  2043
            TInt nErr;
sl@0
  2044
            //-- extract dir entries from the cached page and see if they match given name (aName)
sl@0
  2045
            /// until it reaches the next page
sl@0
  2046
            for(;;)
sl@0
  2047
                {
sl@0
  2048
                StartEntryPos1 = DosEntryPos1;
sl@0
  2049
                TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it
sl@0
  2050
sl@0
  2051
                //-- get directory entry from the cache. We know that the DosEntryPos1 is cached.
sl@0
  2052
                nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName);
sl@0
  2053
                if(nErr != KErrNone)
sl@0
  2054
                    break;
sl@0
  2055
sl@0
  2056
                if(DosEntry1.IsEndOfDirectory())
sl@0
  2057
                    {
sl@0
  2058
                    if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
sl@0
  2059
                        {
sl@0
  2060
                        // note it is impossible to be at the end of the cluster chain here.
sl@0
  2061
                        aFileCreationHelper->SetEntryAddingPos(DosEntryPos1);
sl@0
  2062
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2063
                        }
sl@0
  2064
sl@0
  2065
                    if (ScanMRUPageFirst && !MRUPageScanned)
sl@0
  2066
                        {
sl@0
  2067
                        break;
sl@0
  2068
                        }
sl@0
  2069
sl@0
  2070
                    // if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned)
sl@0
  2071
                    goto Exit; //-- this was the last entry in this directory, no reason to look further
sl@0
  2072
                    }
sl@0
  2073
sl@0
  2074
                if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
sl@0
  2075
                    {
sl@0
  2076
                    if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage())
sl@0
  2077
                        {
sl@0
  2078
                        numFound = 0;
sl@0
  2079
                        }
sl@0
  2080
                    else
sl@0
  2081
                        {
sl@0
  2082
                        if (numFound == 0)
sl@0
  2083
                            {
sl@0
  2084
                            startPos = DosEntryPos1;
sl@0
  2085
                            }
sl@0
  2086
                        numFound++;
sl@0
  2087
                        if (numFound == aFileCreationHelper->NumOfAddingEntries())
sl@0
  2088
                            {
sl@0
  2089
                            aFileCreationHelper->SetEntryAddingPos(startPos);
sl@0
  2090
                            aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2091
                            }
sl@0
  2092
                        }
sl@0
  2093
                    }
sl@0
  2094
                if(MatchEntryAtt(DosEntry1.Attributes(),anAtt))
sl@0
  2095
                    {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then.
sl@0
  2096
sl@0
  2097
                    if(StartEntry1.IsVFatEntry())
sl@0
  2098
                        {//-- extracted entry is VFAT one, name can be in UNICODE
sl@0
  2099
sl@0
  2100
                        // we only check short name candidates for long file names with VFAT entries,
sl@0
  2101
                        //  if it is a valid dos name, it will be checked by default
sl@0
  2102
                        // note here target name is always fully specified
sl@0
  2103
                        if (aFileCreationHelper && aFileCreationHelper->IsInitialised())
sl@0
  2104
                            {
sl@0
  2105
                            aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr());
sl@0
  2106
                            }
sl@0
  2107
sl@0
  2108
                        TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
sl@0
  2109
sl@0
  2110
                        if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound)
sl@0
  2111
                            {//-- found match in cache
sl@0
  2112
                            bCacheMatchFound = ETrue;
sl@0
  2113
                            goto Exit;
sl@0
  2114
                            }
sl@0
  2115
                        else if(aAuxParam.TrgtNameIsLegalDos())
sl@0
  2116
                            {
sl@0
  2117
                            if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
sl@0
  2118
                                {
sl@0
  2119
                                bCacheMatchFound = ETrue;
sl@0
  2120
                                goto Exit;
sl@0
  2121
                                }
sl@0
  2122
                            }
sl@0
  2123
                        }//if(StartEntry1.IsVFatEntry())
sl@0
  2124
                    else if(aAuxParam.TrgtNameIsLegalDos())
sl@0
  2125
                        {//-- this is an old DOS FAT entry
sl@0
  2126
sl@0
  2127
                          if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
sl@0
  2128
                            {
sl@0
  2129
                            //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves
sl@0
  2130
                            //-- between 2 clusters (or/and cache pages). I.e. 1st part of this long entry belongs to one cluster and even more might not be cached,
sl@0
  2131
                            //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached.
sl@0
  2132
                            //-- In this case if we search for short file name, we find it, but the aStartEntryPos will be incorrect, which leads to the directory corruption.
sl@0
  2133
                            //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare.
sl@0
  2134
                            if(StartEntryPos1.iPos == 0)
sl@0
  2135
                                {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above.
sl@0
  2136
                                __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!"));
sl@0
  2137
                                goto Exit;
sl@0
  2138
                                }
sl@0
  2139
sl@0
  2140
                            bCacheMatchFound = ETrue;
sl@0
  2141
                            goto Exit;
sl@0
  2142
                            }
sl@0
  2143
                        }
sl@0
  2144
sl@0
  2145
                    }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt))
sl@0
  2146
sl@0
  2147
                // check boundaries after GetDirEntry()
sl@0
  2148
                // if we have cross the cluster boundary, break the for loop
sl@0
  2149
                if(DosEntryPos1.iCluster != clSave)
sl@0
  2150
                    {//-- GetDirEntry() has decided to move to the next cluster.
sl@0
  2151
                    DosEntryPos1.iCluster = clSave;
sl@0
  2152
                    break;
sl@0
  2153
                    }
sl@0
  2154
sl@0
  2155
                // if we are still in the same cluster, check the page boundary by
sl@0
  2156
                /// exam how many entries we have scanned within the cluster
sl@0
  2157
                const TUint entriesLooked =  ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2;
sl@0
  2158
                if(entriesLooked > nEntries)
sl@0
  2159
                    {
sl@0
  2160
                    PassedPageBoundary = ETrue;
sl@0
  2161
                    break;
sl@0
  2162
                    }
sl@0
  2163
sl@0
  2164
sl@0
  2165
                // move to next entry before scanning next file
sl@0
  2166
                TRAP(nErr,MoveToNextEntryL(DosEntryPos1));
sl@0
  2167
                if(nErr != KErrNone)
sl@0
  2168
                    goto Exit;
sl@0
  2169
sl@0
  2170
                // check boundaries after MoveToNextEntryL()
sl@0
  2171
                if(DosEntryPos1.iCluster != clSave)
sl@0
  2172
                    {
sl@0
  2173
                    DosEntryPos1.iCluster = clSave;
sl@0
  2174
                    break;
sl@0
  2175
                    }
sl@0
  2176
sl@0
  2177
                if (entriesLooked + 1 > nEntries)
sl@0
  2178
                    {
sl@0
  2179
                    PassedPageBoundary = ETrue;
sl@0
  2180
                    break;
sl@0
  2181
                    }
sl@0
  2182
sl@0
  2183
                } //for(;;)
sl@0
  2184
sl@0
  2185
            } //if(iRawDisk->PosCached(...))
sl@0
  2186
sl@0
  2187
        // scanning did not happen because the page is not cached,
sl@0
  2188
        // or
sl@0
  2189
        // scanning finished in last page and file is not found
sl@0
  2190
sl@0
  2191
        // if MRU page is not cached or
sl@0
  2192
        // we scan MRU page first and it is not scanned yet, then this must be the MRU page,
sl@0
  2193
        //  we now start to scan from the beginning
sl@0
  2194
        if (ScanMRUPageFirst && !MRUPageScanned)
sl@0
  2195
            {
sl@0
  2196
            MRUPageScanned = ETrue;
sl@0
  2197
            DosEntryPos1 = aDosEntryPos;
sl@0
  2198
            DosEntryPos1.iPos = 0;
sl@0
  2199
            continue;
sl@0
  2200
            }
sl@0
  2201
sl@0
  2202
        // if we just finished scanning a page and still in the same cluster, then we crossed page
sl@0
  2203
        //  boundary, continue with next page.
sl@0
  2204
        // note: although we are in the 'next page' already, this page might not be cached, so we need to
sl@0
  2205
        //  check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly.
sl@0
  2206
        if (PassedPageBoundary)
sl@0
  2207
            {
sl@0
  2208
            DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
sl@0
  2209
            PassedPageBoundary = EFalse;
sl@0
  2210
            continue;
sl@0
  2211
            }
sl@0
  2212
sl@0
  2213
        //-- try to move to the next cluster of the directory file
sl@0
  2214
sl@0
  2215
        if(DosEntryPos1.Cluster() < KFatFirstSearchCluster)  //-- small trick to get rid of TRAPping GetNextClusterL()
sl@0
  2216
            break;
sl@0
  2217
sl@0
  2218
        // record previous cluster number before move on
sl@0
  2219
        clusterNum = DosEntryPos1.iCluster;
sl@0
  2220
sl@0
  2221
        if(! FAT().GetNextClusterL(DosEntryPos1.iCluster))
sl@0
  2222
            break;
sl@0
  2223
sl@0
  2224
sl@0
  2225
    } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt)
sl@0
  2226
sl@0
  2227
    //---------------------------------
sl@0
  2228
    Exit:
sl@0
  2229
sl@0
  2230
    if(bCacheMatchFound)
sl@0
  2231
        {
sl@0
  2232
        //-- if the position of the found in cache object is less than given, pretend that we haven't found anything
sl@0
  2233
        //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to
sl@0
  2234
        //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir.
sl@0
  2235
        TBool bFallBack=EFalse;
sl@0
  2236
sl@0
  2237
        if(DosEntryPos1.iCluster == aDosEntryPos.iCluster)
sl@0
  2238
            {
sl@0
  2239
            if(DosEntryPos1.iPos < aDosEntryPos.iPos)
sl@0
  2240
                bFallBack = ETrue;
sl@0
  2241
            }
sl@0
  2242
        else
sl@0
  2243
            {
sl@0
  2244
            if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos))
sl@0
  2245
                bFallBack = ETrue;
sl@0
  2246
            }
sl@0
  2247
sl@0
  2248
        if(bFallBack)
sl@0
  2249
            {
sl@0
  2250
            return EFalse;
sl@0
  2251
            }
sl@0
  2252
sl@0
  2253
        //-- Update parameters with new values
sl@0
  2254
        aStartEntryPos= StartEntryPos1;
sl@0
  2255
        aDosEntryPos  = DosEntryPos1;
sl@0
  2256
        aStartEntry   = StartEntry1;
sl@0
  2257
        aDosEntry     = DosEntry1;
sl@0
  2258
sl@0
  2259
        const TInt64  mruPos = MakeLinAddrL(aDosEntryPos);
sl@0
  2260
        
sl@0
  2261
        pDirCache->MakePageMRU(mruPos);
sl@0
  2262
sl@0
  2263
        // only update the leaf dir cache when the original cache index is provided
sl@0
  2264
        if (aLeafDir.iClusterNum)
sl@0
  2265
            {
sl@0
  2266
            iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos));
sl@0
  2267
            }
sl@0
  2268
        }
sl@0
  2269
    return bCacheMatchFound;
sl@0
  2270
    }
sl@0
  2271
sl@0
  2272
//-----------------------------------------------------------------------------------------
sl@0
  2273
sl@0
  2274
/**
sl@0
  2275
    initialise find helper with the target file name.
sl@0
  2276
    This is a quite expensive operation and initialisation is done only once. After this we know if the name is a legal dos one
sl@0
  2277
    and also have the corresponding generated DOS name for it.
sl@0
  2278
sl@0
  2279
    @param aTargetName target file name we are looking for in ::DoFindL()
sl@0
  2280
*/
sl@0
  2281
void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName)
sl@0
  2282
    {
sl@0
  2283
    if(isInitialised)
sl@0
  2284
        return;
sl@0
  2285
sl@0
  2286
     TInt count = 1;
sl@0
  2287
sl@0
  2288
     iTargetName.Set(aTargetName);
sl@0
  2289
     isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse);
sl@0
  2290
sl@0
  2291
     if(isLegalDosName)
sl@0
  2292
        {//-- iShortName will contain generated short DOS name by long filename
sl@0
  2293
        iShortName = DoGenerateShortNameL(aTargetName, count, ETrue);
sl@0
  2294
        }
sl@0
  2295
sl@0
  2296
     isInitialised = ETrue;
sl@0
  2297
    }
sl@0
  2298
sl@0
  2299
/**
sl@0
  2300
    Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise().
sl@0
  2301
    @param  apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format
sl@0
  2302
    @return ETrue if the apDosEntryName is the same as generated iShortName
sl@0
  2303
*/
sl@0
  2304
TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const
sl@0
  2305
    {
sl@0
  2306
    ASSERT(isInitialised);
sl@0
  2307
sl@0
  2308
    if(!isLegalDosName)
sl@0
  2309
        return EFalse;
sl@0
  2310
sl@0
  2311
    return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0);
sl@0
  2312
    }
sl@0
  2313
sl@0
  2314
//-----------------------------------------------------------------------------------------
sl@0
  2315
const TInt KShortNameCandidatesNum = 4;
sl@0
  2316
/**
sl@0
  2317
Constructor of XFileCreationHelper class
sl@0
  2318
*/
sl@0
  2319
CFatMountCB::XFileCreationHelper::XFileCreationHelper()
sl@0
  2320
    {
sl@0
  2321
    isInitialised = EFalse;
sl@0
  2322
    }
sl@0
  2323
sl@0
  2324
/**
sl@0
  2325
Destructor of XFileCreationHelper class
sl@0
  2326
*/
sl@0
  2327
CFatMountCB::XFileCreationHelper::~XFileCreationHelper()
sl@0
  2328
    {
sl@0
  2329
    Close();
sl@0
  2330
    }
sl@0
  2331
sl@0
  2332
/**
sl@0
  2333
Initialises a TFileCreationHelper object, generate a short name candidate pool.
sl@0
  2334
sl@0
  2335
@param  aTargetName Target file name for the potential new file.
sl@0
  2336
@post   TFileCreationHelper is fully initialised.
sl@0
  2337
*/
sl@0
  2338
void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName)
sl@0
  2339
    {
sl@0
  2340
    // close before use, to avoid memory leak
sl@0
  2341
    Close();
sl@0
  2342
sl@0
  2343
    iTargetName.Set(aTargetName);
sl@0
  2344
    // generates short name candidate(s)
sl@0
  2345
    TInt count = 1;
sl@0
  2346
    while (count <= KShortNameCandidatesNum)
sl@0
  2347
        {
sl@0
  2348
        TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue);
sl@0
  2349
        TInt err = iShortNameCandidates.Append(shortNameCandidate);
sl@0
  2350
        User::LeaveIfError(err);
sl@0
  2351
sl@0
  2352
        if (count == -1)    // No tilde and number is needed
sl@0
  2353
            {
sl@0
  2354
            break;
sl@0
  2355
            }
sl@0
  2356
        else
sl@0
  2357
            count++;
sl@0
  2358
        }
sl@0
  2359
sl@0
  2360
    // calculate number of new entries needed
sl@0
  2361
    iNumOfAddingEntries = 1;
sl@0
  2362
    isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue);
sl@0
  2363
    if (!isTrgNameLegalDosName)
sl@0
  2364
        iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length());
sl@0
  2365
sl@0
  2366
    isNewEntryPosFound = EFalse;
sl@0
  2367
    isInitialised = ETrue;
sl@0
  2368
    }
sl@0
  2369
sl@0
  2370
/**
sl@0
  2371
Close function of XFileCreationHelper class
sl@0
  2372
*/
sl@0
  2373
void CFatMountCB::XFileCreationHelper::Close()
sl@0
  2374
    {
sl@0
  2375
    iShortNameCandidates.Close();
sl@0
  2376
    isInitialised = EFalse;
sl@0
  2377
    }
sl@0
  2378
sl@0
  2379
/**
sl@0
  2380
Validates short name candidates. If the input dos entry name is found in the short name
sl@0
  2381
 candidate pool, the corresponding short name candidate will be removed from the pool.
sl@0
  2382
sl@0
  2383
@param  apDosEntryName  An existing short name, to compare with the candidates.
sl@0
  2384
@pre    Object should be initialised
sl@0
  2385
*/
sl@0
  2386
void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName)
sl@0
  2387
    {
sl@0
  2388
    ASSERT(isInitialised);
sl@0
  2389
    if (!isInitialised)
sl@0
  2390
        return;
sl@0
  2391
sl@0
  2392
    if (iShortNameCandidates.Count() > 0)
sl@0
  2393
        {
sl@0
  2394
        for (TInt i = 0; i < iShortNameCandidates.Count(); i++)
sl@0
  2395
            {
sl@0
  2396
            if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0)
sl@0
  2397
                {
sl@0
  2398
                iShortNameCandidates.Remove(i);
sl@0
  2399
                break;
sl@0
  2400
                }
sl@0
  2401
            }
sl@0
  2402
        }
sl@0
  2403
    }
sl@0
  2404
sl@0
  2405
/**
sl@0
  2406
Gets a validated short name from the short name candidate pool.
sl@0
  2407
sl@0
  2408
@param  aShortName  On return, contains a validated short name if found, otherwise zeroed.
sl@0
  2409
@return TInt        Returns KErrNone if a validated short name found successfully,
sl@0
  2410
                     else KErrNotFound is returned.
sl@0
  2411
                    Returns KErrNotReady if object is not initialised.
sl@0
  2412
@pre    Object should be initialised
sl@0
  2413
*/
sl@0
  2414
TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const
sl@0
  2415
    {
sl@0
  2416
    aShortName.Zero();
sl@0
  2417
sl@0
  2418
    ASSERT(isInitialised);
sl@0
  2419
    if (!isInitialised)
sl@0
  2420
        return KErrNotReady;
sl@0
  2421
sl@0
  2422
    if (iShortNameCandidates.Count() > 0)
sl@0
  2423
        {
sl@0
  2424
        aShortName.Copy(iShortNameCandidates[0]);
sl@0
  2425
        return KErrNone;
sl@0
  2426
        }
sl@0
  2427
sl@0
  2428
    return KErrNotFound;
sl@0
  2429
    }
sl@0
  2430
sl@0
  2431
//-----------------------------------------------------------------------------------------
sl@0
  2432
sl@0
  2433
sl@0
  2434
/**
sl@0
  2435
    Scan a directory looking for aName.
sl@0
  2436
sl@0
  2437
    @param  aTrgtName       a name of an object we are looking up in directory
sl@0
  2438
    @param  anAtt           attributes of this object
sl@0
  2439
    @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
sl@0
  2440
    @param  aStartEntry     on return will contain first VFAT dir entry
sl@0
  2441
    @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
sl@0
  2442
    @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
sl@0
  2443
    @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
sl@0
  2444
    @param  anError         This function might leave with this given error code
sl@0
  2445
    @param  aFileCreationHelper       a helper package for file creations
sl@0
  2446
sl@0
  2447
    @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one
sl@0
  2448
    @leave  can leave with anError code on error or if the search has reached the end of directory (!)
sl@0
  2449
*/
sl@0
  2450
TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt,
sl@0
  2451
                        TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry,
sl@0
  2452
                        TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,
sl@0
  2453
                        TDes& aFileName,TInt anError,
sl@0
  2454
                        XFileCreationHelper* aFileCreationHelper,
sl@0
  2455
                        const TLeafDirData& aLeafDirData) const
sl@0
  2456
    {
sl@0
  2457
    // check that the entry position to be read next is not past the end of the
sl@0
  2458
    // root directory. If this is the case then when GetDirEntryL(..) is called
sl@0
  2459
    // this will lead to MakeLinAddr(..) leaving with KErrDirFull.
sl@0
  2460
sl@0
  2461
    if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
sl@0
  2462
        User::Leave(anError);//Allows maximum number of entries in root directory
sl@0
  2463
sl@0
  2464
    __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName);
sl@0
  2465
sl@0
  2466
    TInt previousCluster=aDosEntryPos.iCluster;
sl@0
  2467
    TUint previousPosition=aDosEntryPos.iPos;
sl@0
  2468
    TInt changePreviousCluster=1;
sl@0
  2469
    TInt count=0;
sl@0
  2470
sl@0
  2471
    TBool trgNameIsWildCard     = EFalse; //-- ETrue if the name we are looking for is a wildcard
sl@0
  2472
    TBool trgNameFullySpecified = ETrue;  //-- ETrue if the name we are looking for doesn't contain wildcards
sl@0
  2473
sl@0
  2474
sl@0
  2475
    {
sl@0
  2476
    //-- find out if the name we are looking for is a wildcard ("*" or "*.*")
sl@0
  2477
    const TInt len = aTrgtName.Length();
sl@0
  2478
sl@0
  2479
    if(len == 1)
sl@0
  2480
        trgNameIsWildCard = (aTrgtName[0] == '*');
sl@0
  2481
    else if(len == 3)
sl@0
  2482
        {
sl@0
  2483
        _LIT(KAllFiles, "*.*");
sl@0
  2484
        trgNameIsWildCard = (aTrgtName==KAllFiles);
sl@0
  2485
        }
sl@0
  2486
sl@0
  2487
    //-- find out if the name we are looking for contains wildcharacters: "*" or "?"
sl@0
  2488
    if(trgNameIsWildCard)
sl@0
  2489
        trgNameFullySpecified = EFalse;
sl@0
  2490
    else
sl@0
  2491
        {
sl@0
  2492
        for(TInt i=0; i<len; ++i)
sl@0
  2493
            {
sl@0
  2494
            const TChar ch = aTrgtName[i];
sl@0
  2495
            if(ch == (TChar)'*' || ch == (TChar)'?')
sl@0
  2496
                {
sl@0
  2497
                trgNameFullySpecified = EFalse;
sl@0
  2498
                break;
sl@0
  2499
                }
sl@0
  2500
            }
sl@0
  2501
        }
sl@0
  2502
    }
sl@0
  2503
sl@0
  2504
sl@0
  2505
    TPtrC trgtNameNoDot(aTrgtName);
sl@0
  2506
sl@0
  2507
    TFindHelper findHelper;
sl@0
  2508
    //---------------------------------------------------
sl@0
  2509
    //-- if we have fully specified name and directory cache is present, try to
sl@0
  2510
    //-- locate the name in the cache first to avoid reading from media
sl@0
  2511
    //-- if the entry belongs to the root directory (for FAT12,16) skip the lookup, because root directory isn't aligned by cluster size boundary,
sl@0
  2512
    //-- while directory cache pages are. For FAT32 it doesn't matter, because root dir is a usual file.
sl@0
  2513
    if(iRawDisk->DirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper)
sl@0
  2514
        {//-- aName is fully specified, i.e doesn't contain wildcards
sl@0
  2515
sl@0
  2516
        findHelper.InitialiseL(trgtNameNoDot);
sl@0
  2517
sl@0
  2518
        const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData);
sl@0
  2519
        if(bMatchFound)
sl@0
  2520
            {
sl@0
  2521
            return(aStartEntry.IsVFatEntry());
sl@0
  2522
            }
sl@0
  2523
        }
sl@0
  2524
    //---------------------------------------------------
sl@0
  2525
sl@0
  2526
    // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided
sl@0
  2527
    TBool scanAhead = EFalse;
sl@0
  2528
    // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&&
sl@0
  2529
    //  we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&&
sl@0
  2530
    // if we don't have a starting cluster number, we draw back to original scanning algorithm
sl@0
  2531
    if (!IsRootDir(aDosEntryPos)            // we don't do forward scanning for root dir &
sl@0
  2532
            && aLeafDirData.iClusterNum != 0    // if we have a starting cluster number &
sl@0
  2533
            && aLeafDirData.iMRUPos.Cluster() != 0)     // if we have a starting cluster number &
sl@0
  2534
        {
sl@0
  2535
        scanAhead = ETrue;
sl@0
  2536
        aDosEntryPos = aLeafDirData.iMRUPos;
sl@0
  2537
        }
sl@0
  2538
sl@0
  2539
    TInt numFound = 0;
sl@0
  2540
    TEntryPos startPos = aDosEntryPos;
sl@0
  2541
    TInt clustNum = aDosEntryPos.Cluster();
sl@0
  2542
sl@0
  2543
    for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
sl@0
  2544
        {
sl@0
  2545
        // if we are not scanning ahead, we don't need this outer for loop
sl@0
  2546
        if (!scanAhead)
sl@0
  2547
            scanCnt++;
sl@0
  2548
sl@0
  2549
        TBool found = EFalse;
sl@0
  2550
sl@0
  2551
        FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end
sl@0
  2552
            {
sl@0
  2553
            //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT)
sl@0
  2554
            //-- aDosEntry will contain a DOS entry of the directory entry we have read.
sl@0
  2555
            aStartEntryPos=aDosEntryPos;
sl@0
  2556
            User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName));
sl@0
  2557
sl@0
  2558
            if (aDosEntry.IsEndOfDirectory())
sl@0
  2559
                {//-- the end of directory reached.
sl@0
  2560
sl@0
  2561
                // if new entry position for adding has not been found yet.
sl@0
  2562
                // note aFileCreationHelper may not be initialised for pure file opening operations
sl@0
  2563
                if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
sl@0
  2564
                    {
sl@0
  2565
                    // if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain,
sl@0
  2566
                    //  we pass the last scanned entry position to AddDirEntryL
sl@0
  2567
                    if (IsEndOfClusterCh(aDosEntryPos.iCluster))
sl@0
  2568
                        {
sl@0
  2569
                        TInt clusterSize=1<<ClusterSizeLog2();
sl@0
  2570
                        TEntryPos dummyPos(clustNum, clusterSize - KSizeOfFatDirEntry);
sl@0
  2571
                        aFileCreationHelper->SetEntryAddingPos(dummyPos);
sl@0
  2572
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2573
                        }
sl@0
  2574
                    // or we reached the end of the directory.
sl@0
  2575
                    else
sl@0
  2576
                        {
sl@0
  2577
                        aFileCreationHelper->SetEntryAddingPos(aDosEntryPos);
sl@0
  2578
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2579
                        }
sl@0
  2580
                    }
sl@0
  2581
sl@0
  2582
                // if we are scanning ahead and this is the first scanning, we break out to restart scanning
sl@0
  2583
                if (scanAhead && scanCnt == 1)
sl@0
  2584
                    {
sl@0
  2585
                    break; // from FOREVER, restart scanning
sl@0
  2586
                    }
sl@0
  2587
sl@0
  2588
                // if (!scanAhead || scanAhead && scanCnt == 2)
sl@0
  2589
                User::Leave(anError);
sl@0
  2590
                }
sl@0
  2591
sl@0
  2592
sl@0
  2593
            // entry space searching for potential new file/directory creation
sl@0
  2594
            if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
sl@0
  2595
                {
sl@0
  2596
                if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage())
sl@0
  2597
                    {
sl@0
  2598
                    numFound = 0;
sl@0
  2599
                    }
sl@0
  2600
                else
sl@0
  2601
                    {
sl@0
  2602
                    if (numFound == 0)
sl@0
  2603
                        {
sl@0
  2604
                        startPos = aDosEntryPos;
sl@0
  2605
                        }
sl@0
  2606
                    numFound++;
sl@0
  2607
                    if (numFound == aFileCreationHelper->NumOfAddingEntries())
sl@0
  2608
                        {
sl@0
  2609
                        aFileCreationHelper->SetEntryAddingPos(startPos);
sl@0
  2610
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
sl@0
  2611
                        }
sl@0
  2612
                    }
sl@0
  2613
                }
sl@0
  2614
sl@0
  2615
sl@0
  2616
            if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
sl@0
  2617
                if (aDosEntry.IsErased())
sl@0
  2618
                    {
sl@0
  2619
                    User::Leave(anError);//Allows maximum number of entries in root directory
sl@0
  2620
                    }
sl@0
  2621
sl@0
  2622
sl@0
  2623
            const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage();
sl@0
  2624
sl@0
  2625
            if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt))
sl@0
  2626
                {//-- we have read a filename entry and entry's attributes match required; compare names then.
sl@0
  2627
sl@0
  2628
                if (trgNameIsWildCard)
sl@0
  2629
                    {
sl@0
  2630
                    found = ETrue;
sl@0
  2631
                    break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name.
sl@0
  2632
                    }
sl@0
  2633
sl@0
  2634
sl@0
  2635
                if (aStartEntry.IsVFatEntry())
sl@0
  2636
                    {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name.
sl@0
  2637
                     //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries
sl@0
  2638
sl@0
  2639
sl@0
  2640
                    // we only check short name candidates for long file names with VFAT entries,
sl@0
  2641
                    //  if it is a valid dos name, it will be checked by default
sl@0
  2642
                    // note, for file creation cases, target name will be always fully specified
sl@0
  2643
                    if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified)
sl@0
  2644
                         {
sl@0
  2645
                         aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr());
sl@0
  2646
                         }
sl@0
  2647
sl@0
  2648
                    //-- discard trailing dots from aFileName if present
sl@0
  2649
                     TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
sl@0
  2650
sl@0
  2651
                     if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound)
sl@0
  2652
                         {
sl@0
  2653
                         found = ETrue;
sl@0
  2654
                         break; //-- OK, found a match.
sl@0
  2655
                         }
sl@0
  2656
                     else if (trgNameFullySpecified)
sl@0
  2657
                         {
sl@0
  2658
                         //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified,
sl@0
  2659
                         //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK.
sl@0
  2660
                         findHelper.InitialiseL(trgtNameNoDot);
sl@0
  2661
sl@0
  2662
                         if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
sl@0
  2663
                             {
sl@0
  2664
                             found = ETrue;
sl@0
  2665
                             break; //-- DOS entries match, success.
sl@0
  2666
                             }
sl@0
  2667
                         }
sl@0
  2668
                     else if (!trgNameFullySpecified)
sl@0
  2669
                         {//-- target name contains wildcards, we need to use MatchF with dos name
sl@0
  2670
                         TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
sl@0
  2671
                         TBuf<0x20>  dosName;
sl@0
  2672
                         LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
sl@0
  2673
                         if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
sl@0
  2674
                             {
sl@0
  2675
                             found = ETrue;
sl@0
  2676
                             break;
sl@0
  2677
                             }
sl@0
  2678
                         }
sl@0
  2679
sl@0
  2680
sl@0
  2681
                    }
sl@0
  2682
                else //if (aStartEntry.IsVFatEntry())
sl@0
  2683
                    {//-- we've read a legacy FAT entry, so compare DOS entries
sl@0
  2684
                    findHelper.InitialiseL(trgtNameNoDot);
sl@0
  2685
sl@0
  2686
                    if(findHelper.TrgtNameIsLegalDos())
sl@0
  2687
                        {//-- we are looking for a legal DOS name
sl@0
  2688
                        if(trgNameFullySpecified)
sl@0
  2689
                            {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries
sl@0
  2690
                            if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
sl@0
  2691
                                {
sl@0
  2692
                                found = ETrue;
sl@0
  2693
                                break;
sl@0
  2694
                                }
sl@0
  2695
                            }
sl@0
  2696
                        else
sl@0
  2697
                            {//-- target name contains wildcards, we neeed to use MatchF
sl@0
  2698
                            TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
sl@0
  2699
                            TBuf<0x20>  dosName;
sl@0
  2700
                            LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
sl@0
  2701
                            if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
sl@0
  2702
                                {
sl@0
  2703
                                found = ETrue;
sl@0
  2704
                                break;
sl@0
  2705
                                }
sl@0
  2706
sl@0
  2707
                            }
sl@0
  2708
                        } //if(findHelper.TrgtNameIsLegalDos())
sl@0
  2709
sl@0
  2710
                    } //else if (aStartEntry.IsVFatEntry())
sl@0
  2711
sl@0
  2712
                } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt))
sl@0
  2713
sl@0
  2714
sl@0
  2715
            // record previous cluster number
sl@0
  2716
            clustNum = aDosEntryPos.iCluster;
sl@0
  2717
sl@0
  2718
            // this is the 2nd scanning and we have just passed the pos we started.
sl@0
  2719
            if (scanAhead && scanCnt == 2)
sl@0
  2720
                {
sl@0
  2721
                if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster()
sl@0
  2722
                        && aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos())
sl@0
  2723
                    {
sl@0
  2724
                    User::Leave(anError);
sl@0
  2725
                    }
sl@0
  2726
                }
sl@0
  2727
sl@0
  2728
sl@0
  2729
            MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory
sl@0
  2730
sl@0
  2731
            if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
sl@0
  2732
                {
sl@0
  2733
                User::Leave(anError);//Allows maximum number of entries in root directory
sl@0
  2734
                }
sl@0
  2735
sl@0
  2736
sl@0
  2737
            if (!scanAhead || scanCnt == 2)
sl@0
  2738
                {
sl@0
  2739
                if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
sl@0
  2740
                    DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count);
sl@0
  2741
sl@0
  2742
                previousPosition=aDosEntryPos.iPos;
sl@0
  2743
                }
sl@0
  2744
            }   // FOREVER -- the actual scanning is done inside this loop
sl@0
  2745
sl@0
  2746
sl@0
  2747
        if (found)
sl@0
  2748
            {
sl@0
  2749
            break;
sl@0
  2750
            }
sl@0
  2751
sl@0
  2752
        // if not found:
sl@0
  2753
        // if we have not found in the first scanning and we are doing scanning ahead,
sl@0
  2754
        //  we need to go back to the starting pos of this dir and scan from start until
sl@0
  2755
        //  we reach lastscannedPos
sl@0
  2756
        if (scanAhead && scanCnt == 1)
sl@0
  2757
            {
sl@0
  2758
            aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0);
sl@0
  2759
            continue;
sl@0
  2760
            }
sl@0
  2761
        else
sl@0
  2762
            {
sl@0
  2763
            // there are only two exits: either found or reached end of dir in the 1st scanning
sl@0
  2764
            ASSERT(0);
sl@0
  2765
            break;
sl@0
  2766
            }
sl@0
  2767
        } // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
sl@0
  2768
sl@0
  2769
    //---------------------------------------------------
sl@0
  2770
    if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster())
sl@0
  2771
        {
sl@0
  2772
        TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
sl@0
  2773
        iRawDisk->DirCacheInterface()->MakePageMRU(mruPos);
sl@0
  2774
sl@0
  2775
        // only update the leaf dir cache when the original cache index is provided
sl@0
  2776
        if (aLeafDirData.iClusterNum)
sl@0
  2777
            {
sl@0
  2778
            iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aDosEntryPos));
sl@0
  2779
            }
sl@0
  2780
        }
sl@0
  2781
sl@0
  2782
    return (aStartEntry.IsVFatEntry());
sl@0
  2783
    }
sl@0
  2784
sl@0
  2785
//-----------------------------------------------------------------------------------------
sl@0
  2786
/**
sl@0
  2787
    Locate an directory entry entry from its full path name.
sl@0
  2788
sl@0
  2789
    @param  aName           a name of an object we are looking for
sl@0
  2790
    @param  anAtt           attributes of this object
sl@0
  2791
    @param  anEntry         on return will contain first VFAT dir entry
sl@0
  2792
    @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry
sl@0
  2793
sl@0
  2794
    @leave  can leave with KErrNotFound if the search has reached the end of directory
sl@0
  2795
*/
sl@0
  2796
void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const
sl@0
  2797
    {
sl@0
  2798
    __PRINT(_L("CFatMountCB::FindEntryStartL()"));
sl@0
  2799
    TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  2800
    TFileName fileName;
sl@0
  2801
    TLeafDirData leafDir;
sl@0
  2802
    TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
sl@0
  2803
    TFatDirEntry dosEntry;
sl@0
  2804
sl@0
  2805
    DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir);
sl@0
  2806
    }
sl@0
  2807
sl@0
  2808
sl@0
  2809
//-----------------------------------------------------------------------------------------
sl@0
  2810
sl@0
  2811
/**
sl@0
  2812
    Locate an directory entry entry from its full path name.
sl@0
  2813
sl@0
  2814
    @param  aName           a name of an object we are looking for
sl@0
  2815
    @param  anAtt           attributes of this object
sl@0
  2816
    @param  anEntry         on return will contain first VFAT dir entry
sl@0
  2817
    @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry
sl@0
  2818
sl@0
  2819
    @leave  can leave with KErrNotFound if the search has reached the end of directory
sl@0
  2820
*/
sl@0
  2821
void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const
sl@0
  2822
    {
sl@0
  2823
    __PRINT(_L("CFatMountCB::FindEntryStartL()"));
sl@0
  2824
    TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  2825
    TFileName fileName;
sl@0
  2826
    TLeafDirData leafDir;
sl@0
  2827
    TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
sl@0
  2828
    TFatDirEntry dosEntry;
sl@0
  2829
    DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir);
sl@0
  2830
    }
sl@0
  2831
sl@0
  2832
//-----------------------------------------------------------------------------------------
sl@0
  2833
void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const
sl@0
  2834
//
sl@0
  2835
// Scan a directory looking for aName.
sl@0
  2836
// aCluster and anEntryAddr give the location of the entry.
sl@0
  2837
//
sl@0
  2838
    {
sl@0
  2839
sl@0
  2840
    __PRINT(_L("CFatMountCB::FindDosNameL()"));
sl@0
  2841
    TEntryPos startPos;
sl@0
  2842
    TFatDirEntry startEntry;
sl@0
  2843
sl@0
  2844
    TLeafDirData leafDir;           // leaf dir data is zero initialized, no scannig ahead
sl@0
  2845
    DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir);
sl@0
  2846
    }
sl@0
  2847
//-----------------------------------------------------------------------------------------
sl@0
  2848
sl@0
  2849
void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries)
sl@0
  2850
//
sl@0
  2851
// Find space for a new directory entry. Leave KErrEof if no space
sl@0
  2852
//
sl@0
  2853
    {
sl@0
  2854
sl@0
  2855
    __PRINT(_L("CFatMountCB::AddDirEntryL"));
sl@0
  2856
    TInt numFound=0;
sl@0
  2857
    TFatDirEntry entry;
sl@0
  2858
    TEntryPos startPos(RootIndicator(),0);
sl@0
  2859
    TInt clusterNum=aPos.iCluster;
sl@0
  2860
    FOREVER
sl@0
  2861
        {
sl@0
  2862
        ReadDirEntryL(aPos,entry);
sl@0
  2863
        if (entry.IsEndOfDirectory())
sl@0
  2864
            break;
sl@0
  2865
        if (!entry.IsErased() && !entry.IsGarbage())
sl@0
  2866
            numFound=0;
sl@0
  2867
        else
sl@0
  2868
            {
sl@0
  2869
            if (numFound==0)
sl@0
  2870
                startPos=aPos;
sl@0
  2871
            numFound++;
sl@0
  2872
            if (numFound==aNumOfEntries)
sl@0
  2873
                {
sl@0
  2874
                aPos=startPos;
sl@0
  2875
                return;
sl@0
  2876
                }
sl@0
  2877
            }
sl@0
  2878
        clusterNum=aPos.iCluster;
sl@0
  2879
        MoveToNextEntryL(aPos);
sl@0
  2880
        if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd()))
sl@0
  2881
    //  No end of directory marker at end of root directory
sl@0
  2882
            User::Leave(KErrDirFull);
sl@0
  2883
        }
sl@0
  2884
sl@0
  2885
    TUint clusterSize=1<<ClusterSizeLog2();
sl@0
  2886
    if (IsEndOfClusterCh(aPos.iCluster))
sl@0
  2887
        { // End of last cluster in directory
sl@0
  2888
        aPos.iCluster=clusterNum;
sl@0
  2889
        aPos.iPos=clusterSize;
sl@0
  2890
        }
sl@0
  2891
sl@0
  2892
    TEntryPos eofPos(aPos.iCluster,aPos.iPos+KSizeOfFatDirEntry*aNumOfEntries);
sl@0
  2893
sl@0
  2894
    if (IsRootDir(aPos))
sl@0
  2895
        { // Special case of root directory
sl@0
  2896
        if (eofPos.iPos+StartOfRootDirInBytes()>RootDirEnd())
sl@0
  2897
            User::Leave(KErrDirFull);
sl@0
  2898
        else
sl@0
  2899
            return;
sl@0
  2900
        }
sl@0
  2901
sl@0
  2902
    if (eofPos.iPos==clusterSize)
sl@0
  2903
        return; // No need to allocate
sl@0
  2904
    if (eofPos.iPos>clusterSize)
sl@0
  2905
        {
sl@0
  2906
        TInt numNeeded=eofPos.iPos>>ClusterSizeLog2();
sl@0
  2907
        if(IsRuggedFSys())
sl@0
  2908
            {
sl@0
  2909
            ExtendClusterListZeroedL(numNeeded,eofPos.iCluster);
sl@0
  2910
            }
sl@0
  2911
        else
sl@0
  2912
            {
sl@0
  2913
            FAT().ExtendClusterListL(numNeeded,eofPos.iCluster);
sl@0
  2914
            ZeroDirClusterL(eofPos.iCluster);
sl@0
  2915
            }
sl@0
  2916
sl@0
  2917
        eofPos.iPos-=numNeeded<<ClusterSizeLog2();
sl@0
  2918
        if(aPos.iPos==clusterSize)
sl@0
  2919
            {
sl@0
  2920
            if (!FAT().GetNextClusterL(aPos.iCluster))
sl@0
  2921
                {
sl@0
  2922
                __PRINT(_L("CFatMountCB::AddDirEntryL corrupt#1"))
sl@0
  2923
                User::Leave(KErrCorrupt);
sl@0
  2924
                }
sl@0
  2925
            aPos.iPos=0;
sl@0
  2926
            }
sl@0
  2927
        }
sl@0
  2928
    else if(Drive().IsRemovable())
sl@0
  2929
        {
sl@0
  2930
        // check if entry is already zeroed
sl@0
  2931
        ReadDirEntryL(eofPos,entry);
sl@0
  2932
        if(!entry.IsEndOfDirectory())
sl@0
  2933
            {
sl@0
  2934
            // some removable media may not have directory zeroed
sl@0
  2935
            entry.SetEndOfDirectory();
sl@0
  2936
            WriteDirEntryL(eofPos,entry);
sl@0
  2937
            }
sl@0
  2938
        }
sl@0
  2939
    }
sl@0
  2940
sl@0
  2941
/**
sl@0
  2942
    Zero fill a cluster
sl@0
  2943
    @param  aCluster cluster number to zero-fill
sl@0
  2944
*/
sl@0
  2945
void CFatMountCB::ZeroDirClusterL(TInt aCluster)
sl@0
  2946
    {
sl@0
  2947
sl@0
  2948
    __PRINT1(_L("CFatMountCB::ZeroDirClusterL %d"),aCluster);
sl@0
  2949
sl@0
  2950
    const TUint32 KClusterSz= 1<<ClusterSizeLog2();
sl@0
  2951
    const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
sl@0
  2952
    const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)
sl@0
  2953
sl@0
  2954
    //-- allocate a buffer for zero-filling a cluster
sl@0
  2955
    RBuf8 buf;
sl@0
  2956
    CleanupClosePushL(buf);
sl@0
  2957
sl@0
  2958
    if(buf.CreateMax(KMaxBufSz) != KErrNone)
sl@0
  2959
        buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
sl@0
  2960
sl@0
  2961
    buf.FillZ();
sl@0
  2962
sl@0
  2963
    TEntryPos entryPos(aCluster,0);
sl@0
  2964
sl@0
  2965
    //-- write buffer to the beginning of the directory file.
sl@0
  2966
    DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
sl@0
  2967
sl@0
  2968
    //-- fill in the rest of the cluster if we used a small buffer
sl@0
  2969
    if((TUint32)buf.Size() < KClusterSz) //--  KMaxBufSz may == KMinBufSz if we have 1 sector per cluster
sl@0
  2970
    {
sl@0
  2971
        const TInt restCnt = SectorsPerCluster() - 1;
sl@0
  2972
        ASSERT(restCnt >=1);
sl@0
  2973
sl@0
  2974
        for(TInt i=0; i<restCnt; ++i)
sl@0
  2975
        {
sl@0
  2976
            entryPos.iPos += KMinBufSz;
sl@0
  2977
            DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
sl@0
  2978
        }
sl@0
  2979
sl@0
  2980
    }
sl@0
  2981
sl@0
  2982
    CleanupStack::PopAndDestroy(&buf);
sl@0
  2983
    }
sl@0
  2984
sl@0
  2985
sl@0
  2986
/**
sl@0
  2987
    Internal method. Retrieves directory entry from given position.
sl@0
  2988
sl@0
  2989
    @param  aPos            on enter shall contain start position, from where the entry will be read. On return contains position of the DOS entry (the last one for object name for the VFAT case)
sl@0
  2990
    @param  aDosEntry       On return contains DOS entry for the VFAT case
sl@0
  2991
    @param  aStartEntry     On return contains start entry of the directory object for the VFAT case
sl@0
  2992
    @param  aLongFileName   On return contains VFAT or long filename
sl@0
  2993
sl@0
  2994
    @return  ETrue  if whole FAT entry is OK: only 1 entry for DOS name or _ALL_ entries for a long name
sl@0
  2995
             EFalse if there was an error in assembling entries to the long file name. In this case this entry shall be ignored by upper level.
sl@0
  2996
sl@0
  2997
    can leave because of ReadDirEntryL() and MoveToNextEntryL() [end of dir].
sl@0
  2998
*/
sl@0
  2999
TBool CFatMountCB::DoGetDirEntryL(TEntryPos& aPos, TFatDirEntry& aDosEntry, TFatDirEntry& aStartEntry, TDes& aLongFileName) const
sl@0
  3000
    {
sl@0
  3001
sl@0
  3002
//    __PRINT3(_L("CFatMountCB::GetDirEntryL() drv:%d, pos:%d:%d"), Drive().DriveNumber(), aPos.iCluster, aPos.iPos);
sl@0
  3003
sl@0
  3004
    ReadDirEntryL(aPos,aStartEntry);
sl@0
  3005
    aDosEntry=aStartEntry;
sl@0
  3006
    if (!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || aDosEntry.IsGarbage())
sl@0
  3007
        {//-- This is either a 8.3 FAT entry or garbage
sl@0
  3008
        aLongFileName.SetLength(0);
sl@0
  3009
        return ETrue;
sl@0
  3010
        }
sl@0
  3011
sl@0
  3012
    //-- process VFAT entries
sl@0
  3013
sl@0
  3014
    if(!aDosEntry.IsLongNameStart())
sl@0
  3015
        return EFalse; //-- wrong counter in the 1st VFat entry, consider it as orphaned
sl@0
  3016
sl@0
  3017
sl@0
  3018
    TInt count = aDosEntry.NumFollowing(); //-- count of the following VFat entries
sl@0
  3019
sl@0
  3020
    TBuf16<KMaxVFatEntryName> vBuf(KMaxVFatEntryName);
sl@0
  3021
    aDosEntry.ReadVFatEntry(vBuf);
sl@0
  3022
sl@0
  3023
    TInt vLength=vBuf.Locate('\0');
sl@0
  3024
    if (vLength==KErrNotFound)
sl@0
  3025
        vLength=KMaxVFatEntryName;
sl@0
  3026
sl@0
  3027
    vBuf.SetLength(vLength);
sl@0
  3028
sl@0
  3029
    const TInt nameLen = vLength+KMaxVFatEntryName*(count-1);
sl@0
  3030
    if(nameLen <= 0 || nameLen > KMaxFileName)
sl@0
  3031
        return EFalse; //--  wrong long file name length, consider VFAT entry as orphaned
sl@0
  3032
sl@0
  3033
    aLongFileName.SetLength(nameLen);
sl@0
  3034
sl@0
  3035
    const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry
sl@0
  3036
sl@0
  3037
    while (count--)
sl@0
  3038
        {
sl@0
  3039
        TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count);
sl@0
  3040
        fileNamePtr.Copy(vBuf);
sl@0
  3041
        if (count==0)
sl@0
  3042
            break; //-- all VFat entries read, only DOS entry remained
sl@0
  3043
sl@0
  3044
        MoveToNextEntryL(aPos);
sl@0
  3045
        ReadDirEntryL(aPos,aDosEntry);
sl@0
  3046
sl@0
  3047
        //-- check if it is correct VFat entry.
sl@0
  3048
        //-- if not, this is the "orphaned" entry and will be ignored
sl@0
  3049
        if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count)
sl@0
  3050
            return EFalse;  //-- bad VFAT entry
sl@0
  3051
sl@0
  3052
        aDosEntry.ReadVFatEntry(vBuf);
sl@0
  3053
        }
sl@0
  3054
sl@0
  3055
    if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
sl@0
  3056
        return ETrue;//Allows maximum number of entries in root directory
sl@0
  3057
sl@0
  3058
    //-- read the last, DOS FAT entry
sl@0
  3059
    MoveToNextEntryL(aPos);
sl@0
  3060
    ReadDirEntryL(aPos,aDosEntry);
sl@0
  3061
sl@0
  3062
    //-- check if it is corect
sl@0
  3063
    if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry())
sl@0
  3064
        return EFalse; //-- Bad DOS entry
sl@0
  3065
sl@0
  3066
    //-- verify ChechSum here if it is incorrect, use DOS name only
sl@0
  3067
    const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name());
sl@0
  3068
    if(calcNameChkSum != entryCheckSum)
sl@0
  3069
        {
sl@0
  3070
        aLongFileName.SetLength(0);//-- don't use long filename
sl@0
  3071
        __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum);
sl@0
  3072
        }
sl@0
  3073
sl@0
  3074
    return ETrue;
sl@0
  3075
    }
sl@0
  3076
sl@0
  3077
sl@0
  3078
/**
sl@0
  3079
    Read a number of VFAT entries from the directory file.
sl@0
  3080
    for parameters see DoGetDirEntryL()
sl@0
  3081
sl@0
  3082
    @return KErrNone if everything is OK, system wide error code otherwise
sl@0
  3083
sl@0
  3084
*/
sl@0
  3085
TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const
sl@0
  3086
    {
sl@0
  3087
sl@0
  3088
    TBool bEntryOK=ETrue;
sl@0
  3089
    TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName));
sl@0
  3090
sl@0
  3091
    if(nErr !=KErrNone)
sl@0
  3092
        return nErr;
sl@0
  3093
sl@0
  3094
    if(!bEntryOK)
sl@0
  3095
        {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged.
sl@0
  3096
         //-- consider it as an "orphaned" entry and skip
sl@0
  3097
        aDosEntry.iData[0] = 0xFF;      // Mark entry as garbage
sl@0
  3098
        aLongFileName.SetLength(0);     // No long filename
sl@0
  3099
        }
sl@0
  3100
sl@0
  3101
    return KErrNone;
sl@0
  3102
    }
sl@0
  3103
sl@0
  3104
void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const
sl@0
  3105
//
sl@0
  3106
// If anEntry is at the end of the cluster, and we are not the root dir,
sl@0
  3107
// move it to the next in the list.
sl@0
  3108
//
sl@0
  3109
    {
sl@0
  3110
sl@0
  3111
//  __PRINT(_L("CFatMountCB::MoveToNextEntryL"));
sl@0
  3112
    if (IsEndOfClusterCh(aPos.iCluster))
sl@0
  3113
        return;
sl@0
  3114
    const TUint temp = 1<<ClusterSizeLog2();
sl@0
  3115
    if (aPos.iPos+KSizeOfFatDirEntry!=temp || IsRootDir(aPos))
sl@0
  3116
        {
sl@0
  3117
        aPos.iPos+=KSizeOfFatDirEntry;
sl@0
  3118
        }
sl@0
  3119
    else
sl@0
  3120
        {
sl@0
  3121
        if (FAT().GetNextClusterL(aPos.iCluster)==EFalse)
sl@0
  3122
            {
sl@0
  3123
            SetEndOfClusterCh(aPos.iCluster);
sl@0
  3124
            }
sl@0
  3125
        aPos.iPos=0;
sl@0
  3126
        }
sl@0
  3127
    }
sl@0
  3128
sl@0
  3129
//-----------------------------------------------------------------------------------------
sl@0
  3130
sl@0
  3131
/**
sl@0
  3132
    Starting from a VFat entry walk down the directory until the associated dos entry is found
sl@0
  3133
sl@0
  3134
    @param aPos     in: VFAT entry position. out: if this is a VFAT entry set, it will be DOS entry position. otherwise not changed
sl@0
  3135
    @param anEntry  on return will contain DOS dir. entry contents (if aPos points to the VFAT entry)
sl@0
  3136
*/
sl@0
  3137
void CFatMountCB::MoveToDosEntryL(TEntryPos& aPos,TFatDirEntry& anEntry) const
sl@0
  3138
    {
sl@0
  3139
sl@0
  3140
    //__PRINT(_L("CFatMountCB::MoveToDosEntryL"));
sl@0
  3141
    if (anEntry.IsVFatEntry()==EFalse)
sl@0
  3142
        return;
sl@0
  3143
    FOREVER
sl@0
  3144
        {
sl@0
  3145
        MoveToNextEntryL(aPos);
sl@0
  3146
        ReadDirEntryL(aPos,anEntry);
sl@0
  3147
        if (anEntry.IsVFatEntry()==EFalse)
sl@0
  3148
            break;
sl@0
  3149
        if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
sl@0
  3150
            break;  //  Allows maximum number of entries in root directory
sl@0
  3151
        }
sl@0
  3152
    }
sl@0
  3153
sl@0
  3154
//-----------------------------------------------------------------------------------------
sl@0
  3155
sl@0
  3156
/** Read the Uid of the entry starting at aCluster */
sl@0
  3157
void CFatMountCB::ReadUidL(TInt aCluster,TEntry& anEntry) const
sl@0
  3158
    {
sl@0
  3159
sl@0
  3160
    __PRINT1(_L("CFatMountCB::ReadUidL(%d)"), aCluster);
sl@0
  3161
sl@0
  3162
    if((TUint)aCluster < KFatFirstSearchCluster || (TUint)aCluster >= UsableClusters()+KFatFirstSearchCluster)
sl@0
  3163
        User::Leave(KErrCorrupt);
sl@0
  3164
sl@0
  3165
    TBuf8<sizeof(TCheckedUid)> uidBuf;
sl@0
  3166
    iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf);
sl@0
  3167
    __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed));
sl@0
  3168
    TCheckedUid uid(uidBuf);
sl@0
  3169
    anEntry.iType=uid.UidType();
sl@0
  3170
    }
sl@0
  3171
sl@0
  3172
//-----------------------------------------------------------------------------------------
sl@0
  3173
sl@0
  3174
/**
sl@0
  3175
    Read file section without opening this file on a file server side.
sl@0
  3176
sl@0
  3177
    @param  aName       file name; all trailing dots from the name will be removed
sl@0
  3178
    @param  aFilePos    start read position within a file
sl@0
  3179
    @param  aLength     how many bytes to read; on return will be how many bytes actually read
sl@0
  3180
    @param  aDes        local buffer desctriptor
sl@0
  3181
    @param  aMessage    from file server, used to write data to the buffer in different address space.
sl@0
  3182
sl@0
  3183
    @leave on media read error
sl@0
  3184
*/
sl@0
  3185
void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage)
sl@0
  3186
    {
sl@0
  3187
    __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName);
sl@0
  3188
sl@0
  3189
    CheckStateConsistentL();
sl@0
  3190
sl@0
  3191
    TEntryPos dosEntryPos(RootIndicator(),0);
sl@0
  3192
    TFatDirEntry dosEntry;
sl@0
  3193
    TFileName fileName;
sl@0
  3194
sl@0
  3195
sl@0
  3196
    TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  3197
    TLeafDirData leafDir;
sl@0
  3198
    dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir);
sl@0
  3199
    dosEntryPos.iPos=0;
sl@0
  3200
    TEntryPos startPos;
sl@0
  3201
    TFatDirEntry startEntry;
sl@0
  3202
    DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported,
sl@0
  3203
            startPos,startEntry,dosEntryPos,dosEntry,
sl@0
  3204
            fileName,KErrNotFound,
sl@0
  3205
            NULL,
sl@0
  3206
            leafDir);
sl@0
  3207
sl@0
  3208
//  Check that reading from aPos for aLength lies within the file
sl@0
  3209
//  if aPos is within the file, and aLength is too long, read up to EOF
sl@0
  3210
//  If aPos is beyond the end of the file, return a zero length descriptor
sl@0
  3211
sl@0
  3212
    TUint32 fileSize = dosEntry.Size();
sl@0
  3213
    if ((TUint)aPos>=fileSize)
sl@0
  3214
        User::Leave(KErrEof);
sl@0
  3215
sl@0
  3216
    if ((TUint)(aPos+aLength)>fileSize)
sl@0
  3217
        aLength=fileSize-aPos;
sl@0
  3218
sl@0
  3219
    TInt cluster=StartCluster(dosEntry);
sl@0
  3220
    TInt pos = aPos;
sl@0
  3221
sl@0
  3222
    TInt endCluster;
sl@0
  3223
    TInt clusterSize=1<<ClusterSizeLog2();      //  Size of file clusters
sl@0
  3224
    TInt readTotal = 0;
sl@0
  3225
sl@0
  3226
    // Total number of clusters in file
sl@0
  3227
    TInt maxClusters=((fileSize+clusterSize-1)>>ClusterSizeLog2());
sl@0
  3228
sl@0
  3229
    // Read data
sl@0
  3230
    FOREVER
sl@0
  3231
        {
sl@0
  3232
        //  Get the maximum number of clusters that can be read contiguously
sl@0
  3233
        TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
sl@0
  3234
        __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));
sl@0
  3235
sl@0
  3236
        //  If start position within this block, then read some data
sl@0
  3237
        if (pos<(clusterListLen<<ClusterSizeLog2()))
sl@0
  3238
            {
sl@0
  3239
            //  Read the remaining length or the entire cluster block whichever is smaller
sl@0
  3240
            TInt readLength = Min(aLength-readTotal,(clusterListLen<<ClusterSizeLog2())-pos);
sl@0
  3241
            __ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
sl@0
  3242
            TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
sl@0
  3243
            iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
sl@0
  3244
            readTotal += readLength;
sl@0
  3245
sl@0
  3246
            if (readTotal == aLength)
sl@0
  3247
                return;
sl@0
  3248
sl@0
  3249
            pos += readLength;
sl@0
  3250
            }
sl@0
  3251
sl@0
  3252
        // Get the next cluster in file
sl@0
  3253
        pos-=(clusterListLen<<ClusterSizeLog2());
sl@0
  3254
#if defined(_DEBUG)
sl@0
  3255
        TBool remainingClusters=
sl@0
  3256
#endif
sl@0
  3257
            ((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
sl@0
  3258
        __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
sl@0
  3259
        cluster=endCluster;
sl@0
  3260
        }
sl@0
  3261
    }
sl@0
  3262
sl@0
  3263
sl@0
  3264
//-----------------------------------------------------------------------------------------
sl@0
  3265
sl@0
  3266
void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const
sl@0
  3267
//
sl@0
  3268
// Read aLength of data from disk directly to thread relative descriptor
sl@0
  3269
//
sl@0
  3270
    {
sl@0
  3271
    iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset);
sl@0
  3272
    }
sl@0
  3273
sl@0
  3274
//-----------------------------------------------------------------------------------------
sl@0
  3275
sl@0
  3276
void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage)
sl@0
  3277
//
sl@0
  3278
// Write aLength of data from thread relative descriptor to disk
sl@0
  3279
//
sl@0
  3280
    {
sl@0
  3281
    CheckWritableL();
sl@0
  3282
sl@0
  3283
    //-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case.
sl@0
  3284
    FAT().RequestRawWriteAccess(aPos, aLength);
sl@0
  3285
sl@0
  3286
    iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset);
sl@0
  3287
    //-- Note: FAT directory cache will be invalidated in MountL()
sl@0
  3288
    }
sl@0
  3289
sl@0
  3290
//-----------------------------------------------------------------------------------------
sl@0
  3291
/**
sl@0
  3292
    This method must be used when writing to the FAT directory file.
sl@0
  3293
    If FAT directory cache is present on this drive, it will be used.
sl@0
  3294
    @param  aPos    entry media position
sl@0
  3295
    @param  aDes    data descriptor
sl@0
  3296
*/
sl@0
  3297
void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes)
sl@0
  3298
    {
sl@0
  3299
        CheckWritableL();
sl@0
  3300
        const TInt64 posAddr=MakeLinAddrL(aPos);
sl@0
  3301
sl@0
  3302
        if(!iRawDisk->DirCacheInterface())
sl@0
  3303
            {
sl@0
  3304
            iRawDisk->WriteCachedL(posAddr,aDes);
sl@0
  3305
            }
sl@0
  3306
        else
sl@0
  3307
            {//-- if there is an interface to the FAT directory cache, use it
sl@0
  3308
            iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes);
sl@0
  3309
            }
sl@0
  3310
    }
sl@0
  3311
sl@0
  3312
//-----------------------------------------------------------------------------------------
sl@0
  3313
sl@0
  3314
/**
sl@0
  3315
    This method must be used when reading from the FAT directory file.
sl@0
  3316
    If FAT directory cache is present on this drive, it will be used.
sl@0
  3317
sl@0
  3318
    @param  aPos    entry media position
sl@0
  3319
    @param  aLength how many bytes to read
sl@0
  3320
    @param  aDes    input data descriptor
sl@0
  3321
*/
sl@0
  3322
void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const
sl@0
  3323
    {
sl@0
  3324
        const TInt64 posAddr=MakeLinAddrL(aPos);
sl@0
  3325
sl@0
  3326
        if(!iRawDisk->DirCacheInterface())
sl@0
  3327
            {
sl@0
  3328
            iRawDisk->ReadCachedL(posAddr,aLength,aDes);
sl@0
  3329
            }
sl@0
  3330
        else
sl@0
  3331
            {//-- if there is an interface to the FAT directory cache, use it
sl@0
  3332
            iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes);
sl@0
  3333
            }
sl@0
  3334
    }
sl@0
  3335
sl@0
  3336
//-----------------------------------------------------------------------------------------
sl@0
  3337
sl@0
  3338
void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry)
sl@0
  3339
//
sl@0
  3340
// Write a FAT directory entry to disk.
sl@0
  3341
// Assumes sufficient space has been created for it by AddDirEntry.
sl@0
  3342
//
sl@0
  3343
    {
sl@0
  3344
sl@0
  3345
    __PRINT(_L("CFatMountCB::WriteDirEntryL"));
sl@0
  3346
sl@0
  3347
    //-- use special interface to access FAT directory file
sl@0
  3348
    DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry));
sl@0
  3349
    }
sl@0
  3350
sl@0
  3351
//-----------------------------------------------------------------------------------------
sl@0
  3352
sl@0
  3353
void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos)
sl@0
  3354
//
sl@0
  3355
// Mark a dir entry as erased
sl@0
  3356
//
sl@0
  3357
    {
sl@0
  3358
sl@0
  3359
    __PRINT(_L("CFatMountCB::EraseDirEntryL"));
sl@0
  3360
    if(!iLeafDirCache && iLastLeafDir)
sl@0
  3361
        iLastLeafDir->Des().SetLength(0);
sl@0
  3362
sl@0
  3363
    //-- use special interface to access FAT directory file
sl@0
  3364
    DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8)));
sl@0
  3365
    }
sl@0
  3366
sl@0
  3367
//-----------------------------------------------------------------------------------------
sl@0
  3368
sl@0
  3369
void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const
sl@0
  3370
//
sl@0
  3371
// Read a FAT directory entry to disk
sl@0
  3372
//
sl@0
  3373
    {
sl@0
  3374
sl@0
  3375
//  __PRINT(_L("CFatMountCB::ReadDirEntryL"));
sl@0
  3376
    if (IsEndOfClusterCh(aPos.iCluster))
sl@0
  3377
        {
sl@0
  3378
        aDirEntry.InitZ();
sl@0
  3379
        return;
sl@0
  3380
        }
sl@0
  3381
    TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry);
sl@0
  3382
sl@0
  3383
    //-- use special interface to access FAT directory file
sl@0
  3384
    DirReadL(aPos,KSizeOfFatDirEntry,buf);
sl@0
  3385
    }
sl@0
  3386
sl@0
  3387
//-----------------------------------------------------------------------------------------
sl@0
  3388
sl@0
  3389
/**
sl@0
  3390
    Enlarge the disk's size.
sl@0
  3391
    This method can be called only for variable size media, i.e. RAM drive
sl@0
  3392
sl@0
  3393
    @param aSize size increment (bytes)
sl@0
  3394
*/
sl@0
  3395
void CFatMountCB::EnlargeL(TInt aSize)
sl@0
  3396
    {
sl@0
  3397
    __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize);
sl@0
  3398
sl@0
  3399
    ASSERT(iRamDrive);
sl@0
  3400
sl@0
  3401
    TInt maxSize;
sl@0
  3402
    if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize)
sl@0
  3403
        User::Leave(KErrDiskFull);
sl@0
  3404
    User::LeaveIfError(LocalDrive()->Enlarge(aSize));
sl@0
  3405
    iSize+=aSize;
sl@0
  3406
sl@0
  3407
    if (&FAT())
sl@0
  3408
        {
sl@0
  3409
        FAT().InitializeL();
sl@0
  3410
        }
sl@0
  3411
sl@0
  3412
    if (&RawDisk())
sl@0
  3413
        {
sl@0
  3414
        RawDisk().InitializeL();
sl@0
  3415
        }
sl@0
  3416
sl@0
  3417
    }
sl@0
  3418
sl@0
  3419
//-----------------------------------------------------------------------------------------
sl@0
  3420
sl@0
  3421
void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength)
sl@0
  3422
//
sl@0
  3423
// Reduce the disk's size
sl@0
  3424
//
sl@0
  3425
    {
sl@0
  3426
sl@0
  3427
    __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength);
sl@0
  3428
    User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength));
sl@0
  3429
    iSize-=aLength;
sl@0
  3430
    }
sl@0
  3431
sl@0
  3432
//-----------------------------------------------------------------------------------------
sl@0
  3433
sl@0
  3434
TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const
sl@0
  3435
//
sl@0
  3436
// Convert cluster/position into linear address
sl@0
  3437
//
sl@0
  3438
    {
sl@0
  3439
sl@0
  3440
    //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos);
sl@0
  3441
    if (!IsRootDir(aPos))
sl@0
  3442
        {
sl@0
  3443
        TInt relPos=ClusterRelativePos(aPos.iPos);
sl@0
  3444
        return FAT().DataPositionInBytes(aPos.iCluster)+relPos;
sl@0
  3445
        }
sl@0
  3446
    if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())
sl@0
  3447
        User::Leave(KErrDirFull); // Past last root dir entry
sl@0
  3448
    return StartOfRootDirInBytes()+aPos.iPos;
sl@0
  3449
    }
sl@0
  3450
sl@0
  3451
//-----------------------------------------------------------------------------------------
sl@0
  3452
sl@0
  3453
void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName)
sl@0
  3454
//
sl@0
  3455
// Get the short name associated with a long file name
sl@0
  3456
//
sl@0
  3457
    {
sl@0
  3458
    __PRINT(_L("CFatMountCB::GetShortNameL"));
sl@0
  3459
    TEntryPos firstEntryPos(RootIndicator(),0);
sl@0
  3460
    TFatDirEntry firstEntry;
sl@0
  3461
    FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
sl@0
  3462
    MoveToDosEntryL(firstEntryPos,firstEntry);
sl@0
  3463
    TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name()));
sl@0
  3464
    LocaleUtils::ConvertToUnicodeL(aShortName, dosName);
sl@0
  3465
    }
sl@0
  3466
sl@0
  3467
//-----------------------------------------------------------------------------------------
sl@0
  3468
sl@0
  3469
void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName)
sl@0
  3470
//
sl@0
  3471
// Get the long name associated with a short file name
sl@0
  3472
//
sl@0
  3473
    {
sl@0
  3474
    __PRINT(_L("CFatMountCB::GetLongNameL"));
sl@0
  3475
    TEntryPos pos(RootIndicator(),0);
sl@0
  3476
    TFatDirEntry entry;
sl@0
  3477
    const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
sl@0
  3478
    const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos));
sl@0
  3479
    __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName));
sl@0
  3480
sl@0
  3481
    TLeafDirData leafDir;
sl@0
  3482
    pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir);
sl@0
  3483
    FOREVER
sl@0
  3484
        {
sl@0
  3485
        TFatDirEntry startEntry;
sl@0
  3486
        User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName));
sl@0
  3487
        if (entry.IsEndOfDirectory())
sl@0
  3488
            User::Leave(KErrNotFound);
sl@0
  3489
        TBool entryIsVFat=EFalse;
sl@0
  3490
        if (startEntry.IsVFatEntry())
sl@0
  3491
            entryIsVFat=ETrue;
sl@0
  3492
        if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased())
sl@0
  3493
            {
sl@0
  3494
            TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name()));
sl@0
  3495
            TBuf<0x20> entryName;
sl@0
  3496
            LocaleUtils::ConvertToUnicodeL(entryName, entryName8);
sl@0
  3497
            if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound)
sl@0
  3498
                {
sl@0
  3499
                if (entryIsVFat==EFalse)
sl@0
  3500
                    aLongName=shortNameWithoutPathDelimiter;
sl@0
  3501
                return;
sl@0
  3502
                }
sl@0
  3503
            }
sl@0
  3504
        MoveToNextEntryL(pos);
sl@0
  3505
        }
sl@0
  3506
    }
sl@0
  3507
sl@0
  3508
sl@0
  3509
sl@0
  3510
//-----------------------------------------------------------------------------------------
sl@0
  3511
sl@0
  3512
/**
sl@0
  3513
    Extend a file or directory, zeroing cluster chain and flushing after every write to FAT.
sl@0
  3514
    This method is called for rugged FAT only.
sl@0
  3515
    for parameters see CFatTable::ExtendClusterListL
sl@0
  3516
*/
sl@0
  3517
void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster)
sl@0
  3518
    {
sl@0
  3519
    __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL"));
sl@0
  3520
    __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
sl@0
  3521
sl@0
  3522
    while(aNumber && FAT().GetNextClusterL(aCluster))
sl@0
  3523
        aNumber--;
sl@0
  3524
sl@0
  3525
    //-- request aNumber free clusters from the FAT, this request may wait until FAT scan thread counted enough free clusters if it is running.
sl@0
  3526
    if(!FAT().RequestFreeClusters(aNumber))
sl@0
  3527
        {
sl@0
  3528
        __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull"));
sl@0
  3529
        User::Leave(KErrDiskFull);
sl@0
  3530
        }
sl@0
  3531
    while (aNumber--)
sl@0
  3532
        {
sl@0
  3533
        TInt freeCluster=FAT().AllocateSingleClusterL(aCluster);
sl@0
  3534
        FAT().FlushL();
sl@0
  3535
        ZeroDirClusterL(freeCluster);
sl@0
  3536
        FAT().WriteL(aCluster,freeCluster);
sl@0
  3537
        FAT().FlushL();
sl@0
  3538
        aCluster=freeCluster;
sl@0
  3539
        }
sl@0
  3540
    }
sl@0
  3541
sl@0
  3542
//-----------------------------------------------------------------------------------------
sl@0
  3543
sl@0
  3544
#if defined(_DEBUG)
sl@0
  3545
TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
sl@0
  3546
//
sl@0
  3547
// Debug function
sl@0
  3548
//
sl@0
  3549
    {
sl@0
  3550
    if(aCommand>=EExtCustom)
sl@0
  3551
        {
sl@0
  3552
        if(LocalDrive())
sl@0
  3553
            return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2);
sl@0
  3554
        else
sl@0
  3555
            return KErrNotSupported;
sl@0
  3556
        }
sl@0
  3557
    switch(aCommand)
sl@0
  3558
        {
sl@0
  3559
        case ECriticalWriteFailOn:
sl@0
  3560
            {
sl@0
  3561
            TInt r;
sl@0
  3562
            TInt16 args[2];
sl@0
  3563
            TPtr8 des((TUint8*)args,4,4);
sl@0
  3564
            TRAP(r,aMessage.ReadL(2,des,0));
sl@0
  3565
            if(r!=KErrNone)
sl@0
  3566
                return(r);
sl@0
  3567
            SetWriteFail(ETrue);
sl@0
  3568
            SetWriteFailCount(args[0]);
sl@0
  3569
            SetWriteFailError(args[1]);
sl@0
  3570
            break;
sl@0
  3571
            }
sl@0
  3572
        case ECriticalWriteFailOff:SetWriteFail(EFalse);break;
sl@0
  3573
        case ERuggedFSysOn: SetRuggedFSys(ETrue);break;
sl@0
  3574
        case ERuggedFSysOff: SetRuggedFSys(EFalse);break;
sl@0
  3575
        case EIsRuggedFSys:
sl@0
  3576
            {
sl@0
  3577
            TInt r;
sl@0
  3578
            TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true
sl@0
  3579
            TPtr8 pVal(&val,1,1);
sl@0
  3580
            TRAP(r,aMessage.WriteL(2,pVal,0));
sl@0
  3581
            if(r!=KErrNone)
sl@0
  3582
                return(r);
sl@0
  3583
            break;
sl@0
  3584
            }
sl@0
  3585
        case ELocalTimeForRemovableMediaOn:
sl@0
  3586
            {
sl@0
  3587
            FatFileSystem().SetUseLocalTime(ETrue);
sl@0
  3588
            break;
sl@0
  3589
            }
sl@0
  3590
        case ELocalTimeForRemovableMediaOff:
sl@0
  3591
            {
sl@0
  3592
            FatFileSystem().SetUseLocalTime(EFalse);
sl@0
  3593
            break;
sl@0
  3594
            }
sl@0
  3595
        case ELocalTimeUsedOnRemovableMedia:
sl@0
  3596
            {
sl@0
  3597
            TBool flag = FatFileSystem().GetUseLocalTime();
sl@0
  3598
            TPckgC<TBool> flagPckg(flag);
sl@0
  3599
            TInt r = aMessage.Write(2, flagPckg);
sl@0
  3600
            if(r!=KErrNone)
sl@0
  3601
                return r;
sl@0
  3602
            break;
sl@0
  3603
            }
sl@0
  3604
        case ECreationTime:
sl@0
  3605
            {
sl@0
  3606
            CheckStateConsistentL();
sl@0
  3607
sl@0
  3608
            TEntryPos firstEntryPos(RootIndicator(),0);
sl@0
  3609
            TFatDirEntry firstEntry;
sl@0
  3610
            //RFs::ControlIO restricts you to use narrow descriptors
sl@0
  3611
            //so convert narrow back to wide.
sl@0
  3612
            TBuf8<KMaxPath> fileNameNarrow;
sl@0
  3613
            aMessage.Read(2, fileNameNarrow);
sl@0
  3614
sl@0
  3615
            TFileName fileNameWide;
sl@0
  3616
            fileNameWide.Copy(fileNameNarrow);
sl@0
  3617
sl@0
  3618
            //find the long file name entry
sl@0
  3619
            TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) );
sl@0
  3620
            if(r!=KErrNone)
sl@0
  3621
              return(r);
sl@0
  3622
            //Find the corresponding 8.3 short name entry, for metadata
sl@0
  3623
            MoveToDosEntryL(firstEntryPos,firstEntry);
sl@0
  3624
            TTime creationTime=0;
sl@0
  3625
            TPckg<TTime> timePckg(creationTime);
sl@0
  3626
            SFatDirEntry* sEntry = reinterpret_cast<SFatDirEntry*>(firstEntry.iData);
sl@0
  3627
            creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC);
sl@0
  3628
            r = aMessage.Write(3, timePckg);
sl@0
  3629
            if(r!=KErrNone)
sl@0
  3630
                return r;
sl@0
  3631
            break;
sl@0
  3632
            }
sl@0
  3633
        case EDisableFATDirCache:
sl@0
  3634
            {
sl@0
  3635
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
sl@0
  3636
            TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache;
sl@0
  3637
            pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL);
sl@0
  3638
            break;
sl@0
  3639
            }
sl@0
  3640
        case EDumpFATDirCache:
sl@0
  3641
            {
sl@0
  3642
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
sl@0
  3643
            TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache;
sl@0
  3644
            pDirCache->Control(KEDumpFATDirCache, 0, NULL);
sl@0
  3645
            break;
sl@0
  3646
            }
sl@0
  3647
        case EFATDirCacheInfo:
sl@0
  3648
            {
sl@0
  3649
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
sl@0
  3650
            TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo;
sl@0
  3651
            pDirCache->Control(KEFATDirCacheInfo, 0, NULL);
sl@0
  3652
            break;
sl@0
  3653
            }
sl@0
  3654
sl@0
  3655
sl@0
  3656
        default: return(KErrNotSupported);
sl@0
  3657
        }
sl@0
  3658
    return(KErrNone);
sl@0
  3659
    }
sl@0
  3660
#else
sl@0
  3661
TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
sl@0
  3662
    {return(KErrNotSupported);}
sl@0
  3663
#endif
sl@0
  3664
sl@0
  3665
sl@0
  3666
//-----------------------------------------------------------------------------------------
sl@0
  3667
sl@0
  3668
TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore)
sl@0
  3669
//
sl@0
  3670
// lock media device
sl@0
  3671
//
sl@0
  3672
    {
sl@0
  3673
    __PRINT(_L("CFatMountCB::Lock"));
sl@0
  3674
    TInt r=CreateDrive(Drive().DriveNumber());
sl@0
  3675
    if (r!=KErrNone)
sl@0
  3676
        return r;
sl@0
  3677
sl@0
  3678
    TBusLocalDrive* local;
sl@0
  3679
    r=LocalDrive()->GetLocalDrive(local);
sl@0
  3680
    if (r!=KErrNone)
sl@0
  3681
        return r;
sl@0
  3682
sl@0
  3683
#ifdef _LOCKABLE_MEDIA
sl@0
  3684
    if(local->Status()==KErrLocked)
sl@0
  3685
        local->Status() = KErrNotReady;
sl@0
  3686
#endif
sl@0
  3687
    r=local->SetPassword(aOld,aNew,aStore);
sl@0
  3688
    if(r==KErrNone&&aStore)
sl@0
  3689
        WritePasswordData();
sl@0
  3690
    return(r);
sl@0
  3691
    }
sl@0
  3692
sl@0
  3693
//-----------------------------------------------------------------------------------------
sl@0
  3694
sl@0
  3695
TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore)
sl@0
  3696
//
sl@0
  3697
// Unlock media device
sl@0
  3698
//
sl@0
  3699
    {
sl@0
  3700
    __PRINT(_L("CFatMountCB::Unlock"));
sl@0
  3701
    TInt r=CreateDrive(Drive().DriveNumber());
sl@0
  3702
    if (r!=KErrNone)
sl@0
  3703
        return r;
sl@0
  3704
sl@0
  3705
    TBusLocalDrive* local;
sl@0
  3706
    r=LocalDrive()->GetLocalDrive(local);
sl@0
  3707
    if (r!=KErrNone)
sl@0
  3708
        return r;
sl@0
  3709
sl@0
  3710
#ifdef _LOCKABLE_MEDIA
sl@0
  3711
    if(local->Status()==KErrLocked)
sl@0
  3712
        local->Status() = KErrNotReady;
sl@0
  3713
#endif
sl@0
  3714
    r=local->Unlock(aPassword,aStore);
sl@0
  3715
    if(r==KErrNone&&aStore)
sl@0
  3716
        WritePasswordData();
sl@0
  3717
    return(r);
sl@0
  3718
    }
sl@0
  3719
sl@0
  3720
//-----------------------------------------------------------------------------------------
sl@0
  3721
sl@0
  3722
TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword)
sl@0
  3723
//
sl@0
  3724
// Clear password from media device
sl@0
  3725
//
sl@0
  3726
    {
sl@0
  3727
    __PRINT(_L("CFatMountCB::ClearPassword"));
sl@0
  3728
    TInt r=CreateDrive(Drive().DriveNumber());
sl@0
  3729
    if (r!=KErrNone)
sl@0
  3730
        return r;
sl@0
  3731
sl@0
  3732
    TBusLocalDrive* local;
sl@0
  3733
    r=LocalDrive()->GetLocalDrive(local);
sl@0
  3734
    if (r!=KErrNone)
sl@0
  3735
        return r;
sl@0
  3736
sl@0
  3737
#ifdef _LOCKABLE_MEDIA
sl@0
  3738
    if(local->Status()==KErrLocked)
sl@0
  3739
        local->Status() = KErrNotReady;
sl@0
  3740
#endif
sl@0
  3741
    r=local->Clear(aPassword);
sl@0
  3742
    if(r==KErrNone)
sl@0
  3743
        WritePasswordData();
sl@0
  3744
    return(r);
sl@0
  3745
    }
sl@0
  3746
sl@0
  3747
//-----------------------------------------------------------------------------------------
sl@0
  3748
sl@0
  3749
TInt CFatMountCB::ErasePassword()
sl@0
  3750
//
sl@0
  3751
// Forcibly erase the password from a media device
sl@0
  3752
//
sl@0
  3753
    {
sl@0
  3754
    __PRINT(_L("CFatMountCB::ErasePassword"));
sl@0
  3755
sl@0
  3756
    TInt r=CreateDrive(Drive().DriveNumber());
sl@0
  3757
    if (r!=KErrNone)
sl@0
  3758
        return r;
sl@0
  3759
sl@0
  3760
    TBusLocalDrive* local;
sl@0
  3761
    r=LocalDrive()->GetLocalDrive(local);
sl@0
  3762
    if (r!=KErrNone)
sl@0
  3763
        return r;
sl@0
  3764
sl@0
  3765
#ifdef _LOCKABLE_MEDIA
sl@0
  3766
    if(local->Status()==KErrLocked)
sl@0
  3767
        local->Status() = KErrNotReady;
sl@0
  3768
#endif
sl@0
  3769
    r=local->ErasePassword();
sl@0
  3770
    if(r==KErrNone)
sl@0
  3771
        {
sl@0
  3772
        // ...media change to ensure a fresh remount the drive
sl@0
  3773
        r = local->ForceRemount(0);
sl@0
  3774
        local->Status() = KErrNotReady;
sl@0
  3775
        WritePasswordData();
sl@0
  3776
        }
sl@0
  3777
    return(r);
sl@0
  3778
    }
sl@0
  3779
sl@0
  3780
//-----------------------------------------------------------------------------------------
sl@0
  3781
sl@0
  3782
TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags)
sl@0
  3783
//
sl@0
  3784
// Force a remount of the drive
sl@0
  3785
//
sl@0
  3786
    {
sl@0
  3787
    __PRINT(_L("CFatMountCB::ForceRemountDrive"));
sl@0
  3788
    TInt r=CreateDrive(Drive().DriveNumber());
sl@0
  3789
    if (r==KErrNone)
sl@0
  3790
        r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle);
sl@0
  3791
    if (r==KErrNone)
sl@0
  3792
        r=LocalDrive()->ForceRemount(aFlags);
sl@0
  3793
    return(r);
sl@0
  3794
    }
sl@0
  3795
sl@0
  3796
//-----------------------------------------------------------------------------------------
sl@0
  3797
sl@0
  3798
void CFatMountCB::WritePasswordData()
sl@0
  3799
//
sl@0
  3800
// Write store password data to disk
sl@0
  3801
//
sl@0
  3802
    {
sl@0
  3803
    __PRINT(_L("CFatMountCB::WritePasswordData"));
sl@0
  3804
    TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
sl@0
  3805
    mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
sl@0
  3806
    __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile);
sl@0
  3807
    TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
sl@0
  3808
    TInt length=local.PasswordStoreLengthInBytes();
sl@0
  3809
    if(length==0)
sl@0
  3810
        {
sl@0
  3811
        WriteToDisk(mediaPWrdFile,_L8(""));
sl@0
  3812
        return;
sl@0
  3813
        }
sl@0
  3814
    HBufC8* hDes=HBufC8::New(length);
sl@0
  3815
    if(hDes==NULL)
sl@0
  3816
        return;
sl@0
  3817
    TPtr8 pDes=hDes->Des();
sl@0
  3818
    TInt r=local.ReadPasswordData(pDes);
sl@0
  3819
    if(r==KErrNone)
sl@0
  3820
        WriteToDisk(mediaPWrdFile,pDes);
sl@0
  3821
    delete hDes;
sl@0
  3822
    }
sl@0
  3823
sl@0
  3824
//-----------------------------------------------------------------------------------------
sl@0
  3825
sl@0
  3826
/**
sl@0
  3827
Trim trailing spaces of volume label descriptor and adjust its length
sl@0
  3828
*/
sl@0
  3829
void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const
sl@0
  3830
    {
sl@0
  3831
    // Locate first '\0'
sl@0
  3832
    TInt nullPos = aLabel.Locate('\0');
sl@0
  3833
    if (nullPos == KErrNotFound)
sl@0
  3834
        nullPos = KVolumeLabelSize;
sl@0
  3835
sl@0
  3836
    // Trim trailing spaces
sl@0
  3837
    TInt i;
sl@0
  3838
    for (i=nullPos-1; i>=0; --i)
sl@0
  3839
        if (aLabel[i] != 0x20)
sl@0
  3840
            break;
sl@0
  3841
    aLabel.SetLength(i+1);
sl@0
  3842
    }
sl@0
  3843
sl@0
  3844
//-----------------------------------------------------------------------------------------
sl@0
  3845
sl@0
  3846
/**
sl@0
  3847
Searches for the volume label file
sl@0
  3848
sl@0
  3849
@param aLabel The name of the volume label file returned upon successful search
sl@0
  3850
@return KErrNone if it finds the volume label file, otherwise KErrNotFound
sl@0
  3851
*/
sl@0
  3852
TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel)
sl@0
  3853
    {
sl@0
  3854
    __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile"));
sl@0
  3855
    TEntryPos pos(RootIndicator(),0);
sl@0
  3856
    TFatDirEntry entry;
sl@0
  3857
    TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry));
sl@0
  3858
    __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r);
sl@0
  3859
    return r;
sl@0
  3860
    }
sl@0
  3861
sl@0
  3862
//-----------------------------------------------------------------------------------------
sl@0
  3863
sl@0
  3864
/**
sl@0
  3865
Creates or updates the volume label file with name aNewName
sl@0
  3866
sl@0
  3867
@param aNewName The new name for the volume label file
sl@0
  3868
*/
sl@0
  3869
void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName)
sl@0
  3870
    {
sl@0
  3871
    __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName);
sl@0
  3872
    TEntryPos pos(RootIndicator(),0);
sl@0
  3873
    TFatDirEntry entry;
sl@0
  3874
sl@0
  3875
    TBuf8<KVolumeLabelSize> oldName;
sl@0
  3876
    TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry));
sl@0
  3877
sl@0
  3878
    if( KErrNone == r )
sl@0
  3879
        {
sl@0
  3880
        // Found existing volume label file, rename or delete
sl@0
  3881
        if(oldName == aNewName)
sl@0
  3882
            {
sl@0
  3883
            __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match"));
sl@0
  3884
            return;
sl@0
  3885
            }
sl@0
  3886
        else
sl@0
  3887
            {
sl@0
  3888
            if(aNewName.Length() == 0)
sl@0
  3889
                {
sl@0
  3890
                // delete the volume label file
sl@0
  3891
                __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete"));
sl@0
  3892
                EraseDirEntryL(pos, entry);
sl@0
  3893
                }
sl@0
  3894
            else
sl@0
  3895
                {
sl@0
  3896
                __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace"));
sl@0
  3897
                entry.SetName(aNewName);
sl@0
  3898
                WriteDirEntryL(pos, entry);
sl@0
  3899
                }
sl@0
  3900
            FAT().FlushL();
sl@0
  3901
            }
sl@0
  3902
        }
sl@0
  3903
    else if( KErrNotFound == r )
sl@0
  3904
        {
sl@0
  3905
        // Not found, need to create if aNewName is not empty
sl@0
  3906
        // Windows allows a volume label file to have the same name as
sl@0
  3907
        // an existing file or directory
sl@0
  3908
        if(aNewName.Length() > 0)
sl@0
  3909
            {
sl@0
  3910
            __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create"));
sl@0
  3911
            TEntryPos dirPos(RootIndicator(),0);
sl@0
  3912
            AddDirEntryL(dirPos,1);
sl@0
  3913
            TFatDirEntry fatDirEntry;
sl@0
  3914
            fatDirEntry.SetName(aNewName);
sl@0
  3915
            fatDirEntry.SetAttributes(KEntryAttVolume);
sl@0
  3916
sl@0
  3917
            TTime now;
sl@0
  3918
            now.UniversalTime();
sl@0
  3919
            fatDirEntry.SetTime(now, TimeOffset() );
sl@0
  3920
            fatDirEntry.SetStartCluster(0);
sl@0
  3921
            fatDirEntry.SetSize(0);
sl@0
  3922
            WriteDirEntryL(dirPos, fatDirEntry);
sl@0
  3923
            FAT().FlushL();
sl@0
  3924
            }
sl@0
  3925
        }
sl@0
  3926
    else
sl@0
  3927
        {
sl@0
  3928
        // Some other error
sl@0
  3929
        User::Leave(r);
sl@0
  3930
        }
sl@0
  3931
    }
sl@0
  3932
sl@0
  3933
sl@0
  3934
//-----------------------------------------------------------------------------------------
sl@0
  3935
sl@0
  3936
/**
sl@0
  3937
Scans the root directory for a volume label file. Leaves with an error if not found
sl@0
  3938
sl@0
  3939
@param aLabel Name of the volume label file upon successful search
sl@0
  3940
@param aDosEntryPos Pointer to position of the volume label file upon successful search
sl@0
  3941
@param aDosEntry Contains the entry for the volume label file upon successful search
sl@0
  3942
*/
sl@0
  3943
void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
sl@0
  3944
    {
sl@0
  3945
    __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL"));
sl@0
  3946
sl@0
  3947
    if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
sl@0
  3948
        {
sl@0
  3949
        __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root"));
sl@0
  3950
        User::Leave(KErrNotFound); // Allows maximum number of entries in root directory
sl@0
  3951
        }
sl@0
  3952
sl@0
  3953
    TInt previousCluster= aDosEntryPos.iCluster;
sl@0
  3954
    TUint previousPosition= aDosEntryPos.iPos;
sl@0
  3955
    TInt changePreviousCluster=1;
sl@0
  3956
    TInt count=0;
sl@0
  3957
sl@0
  3958
    TFatDirEntry startEntry;
sl@0
  3959
    TFileName dummyLongName;
sl@0
  3960
sl@0
  3961
    FOREVER
sl@0
  3962
        {
sl@0
  3963
#ifdef _DEBUG
sl@0
  3964
        const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName);
sl@0
  3965
        __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e);
sl@0
  3966
        User::LeaveIfError(e);
sl@0
  3967
#else
sl@0
  3968
        User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName));
sl@0
  3969
#endif
sl@0
  3970
        if(aDosEntry.IsEndOfDirectory())
sl@0
  3971
            {
sl@0
  3972
            __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir"));
sl@0
  3973
            User::Leave(KErrNotFound);
sl@0
  3974
            }
sl@0
  3975
        if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
sl@0
  3976
            {
sl@0
  3977
            if(aDosEntry.IsErased())
sl@0
  3978
                {
sl@0
  3979
                __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root"));
sl@0
  3980
                User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
sl@0
  3981
                }
sl@0
  3982
            }
sl@0
  3983
        if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage())
sl@0
  3984
            {
sl@0
  3985
            if(aDosEntry.Attributes() & KEntryAttVolume)
sl@0
  3986
                {
sl@0
  3987
                aLabel = aDosEntry.Name();
sl@0
  3988
#ifdef _DEBUG
sl@0
  3989
                dummyLongName.Copy(aLabel);
sl@0
  3990
                __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName);
sl@0
  3991
#endif
sl@0
  3992
                break;
sl@0
  3993
                }
sl@0
  3994
            }
sl@0
  3995
        MoveToNextEntryL(aDosEntryPos);
sl@0
  3996
        if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
sl@0
  3997
            {
sl@0
  3998
            __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root"));
sl@0
  3999
            User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
sl@0
  4000
            }
sl@0
  4001
        if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
sl@0
  4002
            {
sl@0
  4003
            DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count);
sl@0
  4004
            }
sl@0
  4005
        previousPosition=aDosEntryPos.iPos;
sl@0
  4006
        }
sl@0
  4007
    }
sl@0
  4008
sl@0
  4009
//-----------------------------------------------------------------------------------------
sl@0
  4010
sl@0
  4011
/**
sl@0
  4012
Read volume label from disk and trim trailing 0x20 & 0x00 characters
sl@0
  4013
*/
sl@0
  4014
void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector)
sl@0
  4015
    {
sl@0
  4016
    // Read volume label as 8 bit descriptor
sl@0
  4017
    TBuf8<KVolumeLabelSize> volName8;
sl@0
  4018
    TInt r = ReadVolumeLabelFile(volName8);
sl@0
  4019
    if(r != KErrNone)   // No volume label file in root directory
sl@0
  4020
        volName8 = aBootSector.VolumeLabel();
sl@0
  4021
    TrimVolumeLabel(volName8);
sl@0
  4022
sl@0
  4023
    TBuf16<KVolumeLabelSize> volName16;
sl@0
  4024
    LocaleUtils::ConvertToUnicodeL(volName16, volName8);
sl@0
  4025
    SetVolumeName(volName16.AllocL());
sl@0
  4026
    }
sl@0
  4027
sl@0
  4028
sl@0
  4029
//-----------------------------------------------------------------------------------------
sl@0
  4030
sl@0
  4031
/**
sl@0
  4032
Populates iMap member of aInfo with contiguous block group maps.
sl@0
  4033
sl@0
  4034
@param aPos     Start position for a desired section of the file.
sl@0
  4035
@param sLength  Length of the desired data to produce the block map for.
sl@0
  4036
@param aInfo    A structure describing a group of block maps.
sl@0
  4037
*/
sl@0
  4038
void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo)
sl@0
  4039
    {
sl@0
  4040
    __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL"));
sl@0
  4041
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
sl@0
  4042
    TBlockMapEntry blockMapEntry;
sl@0
  4043
sl@0
  4044
    TUint i = 0;
sl@0
  4045
    TInt clusterRelativePos;
sl@0
  4046
    TInt maxClusters;
sl@0
  4047
    TInt endCluster;
sl@0
  4048
    TInt clusterListLen;
sl@0
  4049
    TInt readLength;
sl@0
  4050
    TInt temp;
sl@0
  4051
    TInt currentPos;
sl@0
  4052
    TLocalDriveCapsBuf caps;
sl@0
  4053
    TInt r;
sl@0
  4054
    TInt64 realPosition = 0;
sl@0
  4055
sl@0
  4056
    do
sl@0
  4057
        {
sl@0
  4058
        currentPos = aPos.iPos;
sl@0
  4059
        temp = currentPos>>ClusterSizeLog2();
sl@0
  4060
        if ( (currentPos) && ( (currentPos) == (temp<<ClusterSizeLog2()) ) )
sl@0
  4061
            {
sl@0
  4062
            if (!FAT().GetNextClusterL(aPos.iCluster))
sl@0
  4063
                {
sl@0
  4064
                __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL corrupt#1"))
sl@0
  4065
                User::Leave(KErrCorrupt);
sl@0
  4066
                }
sl@0
  4067
            }
sl@0
  4068
        clusterRelativePos = ClusterRelativePos( aPos.iPos );
sl@0
  4069
        maxClusters = ((aLength + clusterRelativePos - 1)>>ClusterSizeLog2())+1;
sl@0
  4070
        clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters);
sl@0
  4071
        readLength = Min( aLength, (clusterListLen<<ClusterSizeLog2()) - clusterRelativePos);
sl@0
  4072
sl@0
  4073
        blockMapEntry.SetNumberOfBlocks( clusterListLen );
sl@0
  4074
        if (aPos.iCluster < 2)
sl@0
  4075
            User::Leave(KErrCorrupt);
sl@0
  4076
        r = LocalDrive()->Caps(caps);
sl@0
  4077
        if ( r != KErrNone )
sl@0
  4078
            User::LeaveIfError(r);
sl@0
  4079
        if ( caps().iType&EMediaRam )
sl@0
  4080
            {
sl@0
  4081
            realPosition = FAT().DataPositionInBytes( aPos.iCluster );
sl@0
  4082
            aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2());
sl@0
  4083
            blockMapEntry.SetStartBlock( aPos.iCluster );
sl@0
  4084
            }
sl@0
  4085
        else
sl@0
  4086
            blockMapEntry.SetStartBlock( aPos.iCluster - 2);
sl@0
  4087
        aInfo.iMap.Append(TPckgC<TBlockMapEntry>(blockMapEntry));
sl@0
  4088
        aPos.iPos += readLength;
sl@0
  4089
        aPos.iCluster = endCluster;
sl@0
  4090
        aLength -= readLength;
sl@0
  4091
        }
sl@0
  4092
    while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) );
sl@0
  4093
    }
sl@0
  4094
sl@0
  4095
sl@0
  4096
//-----------------------------------------------------------------------------------------
sl@0
  4097
sl@0
  4098
TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
sl@0
  4099
    {
sl@0
  4100
    TFatDirEntry firstEntry;
sl@0
  4101
    TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory
sl@0
  4102
    FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
sl@0
  4103
sl@0
  4104
    aDosEntryPos=firstEntryPos;
sl@0
  4105
    aDosEntry=firstEntry;
sl@0
  4106
    MoveToDosEntryL(aDosEntryPos,aDosEntry);
sl@0
  4107
sl@0
  4108
    return KErrNone;
sl@0
  4109
    }
sl@0
  4110
sl@0
  4111
//-----------------------------------------------------------------------------------------
sl@0
  4112
sl@0
  4113
TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId)
sl@0
  4114
    {
sl@0
  4115
    // Get first cluster of file
sl@0
  4116
    TEntryPos dosEntryPos(RootIndicator(),0);
sl@0
  4117
    TFatDirEntry dosEntry;
sl@0
  4118
    InitializeRootEntry(dosEntry);  // Nugatory initialisation to placate warnings
sl@0
  4119
    TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry));
sl@0
  4120
    if(err!=KErrNone)
sl@0
  4121
        return err;
sl@0
  4122
    TInt startCluster=StartCluster(dosEntry);
sl@0
  4123
    // Empty files will return a cluster of zero
sl@0
  4124
    if(startCluster==0)
sl@0
  4125
        return KErrEof;
sl@0
  4126
    aUniqueId=MAKE_TINT64(0,startCluster);
sl@0
  4127
    return KErrNone;
sl@0
  4128
    }
sl@0
  4129
//-----------------------------------------------------------------------------------------
sl@0
  4130
sl@0
  4131
sl@0
  4132
TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
sl@0
  4133
    {
sl@0
  4134
    return KErrNotSupported;
sl@0
  4135
    }
sl@0
  4136
sl@0
  4137
TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
sl@0
  4138
    {
sl@0
  4139
    return KErrNotSupported;
sl@0
  4140
    }
sl@0
  4141
sl@0
  4142
TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
sl@0
  4143
    {
sl@0
  4144
    return KErrNotSupported;
sl@0
  4145
    }
sl@0
  4146
sl@0
  4147
//-----------------------------------------------------------------------------------------
sl@0
  4148
sl@0
  4149
//-- maximal level of recursion for the CheckDisk. i.e. the max. number of folded directories to check.
sl@0
  4150
const TInt KCheckDskMaxRecursionLevel = 50;
sl@0
  4151
sl@0
  4152
//
sl@0
  4153
// Walks a directory cluster list then checks all the entries.
sl@0
  4154
//
sl@0
  4155
void  CFatMountCB::ChkDirL(RBitVector& aFatBitVec, TInt aDirCluster)
sl@0
  4156
    {
sl@0
  4157
    //__PRINT1(_L("### CFatMountCB::ChkDirL() level:%d"),iChkDiscRecLevel);
sl@0
  4158
sl@0
  4159
    //-- check if we have reached the recursion limit. on hardware the stack is very limited
sl@0
  4160
    //-- and its overflow will lead to crash.
sl@0
  4161
    if(iChkDiscRecLevel++ >= KCheckDskMaxRecursionLevel)
sl@0
  4162
        {
sl@0
  4163
         __PRINT1(_L("CFatMountCB::ChkDirL() max recursion level(%d) reached. Leaving!"),iChkDiscRecLevel);
sl@0
  4164
         User::Leave(KErrTooBig);
sl@0
  4165
        }
sl@0
  4166
sl@0
  4167
    if(/*Is32BitFat() &&*/aDirCluster != 0 && (aDirCluster == RootIndicator()))//the bit in comments maybe required
sl@0
  4168
        WalkClusterListL(aFatBitVec, RootIndicator());
sl@0
  4169
sl@0
  4170
    TEntryPos entryPos(aDirCluster,0);
sl@0
  4171
    FOREVER
sl@0
  4172
        {
sl@0
  4173
        TFatDirEntry entry;
sl@0
  4174
        ReadDirEntryL(entryPos,entry);
sl@0
  4175
        MoveToDosEntryL(entryPos,entry);
sl@0
  4176
        if (entry.IsEndOfDirectory())
sl@0
  4177
            break;
sl@0
  4178
        if (IsRootDir(entryPos)&&(StartOfRootDirInBytes()+entryPos.iPos==(RootDirEnd()-KSizeOfFatDirEntry)))
sl@0
  4179
            {
sl@0
  4180
            if(!entry.IsErased())
sl@0
  4181
                ChkEntryL(aFatBitVec, entry);
sl@0
  4182
            break;  //  Allows maximum number of entries in root directory
sl@0
  4183
            }
sl@0
  4184
        MoveToNextEntryL(entryPos);
sl@0
  4185
        if (entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased())
sl@0
  4186
            continue;
sl@0
  4187
        ChkEntryL(aFatBitVec, entry);
sl@0
  4188
        }
sl@0
  4189
sl@0
  4190
    iChkDiscRecLevel--;
sl@0
  4191
    }
sl@0
  4192
sl@0
  4193
//-----------------------------------------------------------------------------------------
sl@0
  4194
sl@0
  4195
//
sl@0
  4196
// Check FAT is valid for anEntry
sl@0
  4197
//
sl@0
  4198
void  CFatMountCB::ChkEntryL(RBitVector& aFatBitVec, const TFatDirEntry& anEntry)
sl@0
  4199
    {
sl@0
  4200
    TInt listLength=0;
sl@0
  4201
sl@0
  4202
    if ((anEntry.Attributes()&(KEntryAttDir)) || anEntry.Size())
sl@0
  4203
        listLength=WalkClusterListL(aFatBitVec, StartCluster(anEntry));
sl@0
  4204
    else if (anEntry.StartCluster() != 0)        // zero length file
sl@0
  4205
        User::Leave(EFatChkDskInvalidEntrySize); // shouldn't have clusters
sl@0
  4206
sl@0
  4207
    if (anEntry.Attributes()&KEntryAttDir)
sl@0
  4208
        ChkDirL(aFatBitVec, StartCluster(anEntry));
sl@0
  4209
sl@0
  4210
    //  Check that the correct number of clusters have been allocated for the size of the file.
sl@0
  4211
sl@0
  4212
    else if ((anEntry.Attributes()&KEntryAttVolume)==0)
sl@0
  4213
        {
sl@0
  4214
        TInt clustersForFileSize;
sl@0
  4215
        TInt clusterSize=1<<ClusterSizeLog2();
sl@0
  4216
        clustersForFileSize = (TInt) ( (TInt64(anEntry.Size()) + TInt64(clusterSize-1)) >> ClusterSizeLog2() );
sl@0
  4217
sl@0
  4218
        if (listLength!=clustersForFileSize)
sl@0
  4219
            User::Leave(EFatChkDskInvalidEntrySize);
sl@0
  4220
        }
sl@0
  4221
    }
sl@0
  4222
sl@0
  4223
//-----------------------------------------------------------------------------------------
sl@0
  4224
sl@0
  4225
//
sl@0
  4226
// Walks cluster list from aCluster to EOF
sl@0
  4227
// Reports an error if an invalid cluster is found before EOF or
sl@0
  4228
// a cluster has been visited before.
sl@0
  4229
//
sl@0
  4230
TInt  CFatMountCB::WalkClusterListL(RBitVector& aFatBitVec, TInt aCluster)
sl@0
  4231
    {
sl@0
  4232
sl@0
  4233
    TInt i=0;
sl@0
  4234
    do  {
sl@0
  4235
        i++;
sl@0
  4236
        if (!ValidClusterNumber(aCluster))
sl@0
  4237
            {
sl@0
  4238
            __PRINT1(_L("Bad Cluster number %d"),aCluster);
sl@0
  4239
            User::Leave(EFatChkDskIllegalClusterNumber);
sl@0
  4240
            }
sl@0
  4241
sl@0
  4242
        if (IsClusterVisited(aFatBitVec, aCluster))
sl@0
  4243
            {
sl@0
  4244
            __PRINT1(_L("Cluster already in use %d"),aCluster);
sl@0
  4245
            User::Leave(EFatChkDskClusterAlreadyInUse);
sl@0
  4246
            }
sl@0
  4247
sl@0
  4248
        MarkClusterVisited(aFatBitVec, aCluster);
sl@0
  4249
sl@0
  4250
        } while (FAT().GetNextClusterL(aCluster));
sl@0
  4251
sl@0
  4252
    return(i);
sl@0
  4253
    }
sl@0
  4254
sl@0
  4255
//-----------------------------------------------------------------------------------------
sl@0
  4256
sl@0
  4257
//
sl@0
  4258
// Checks that all unvisited clusters are marked as free in the FAT
sl@0
  4259
//
sl@0
  4260
void  CFatMountCB::CheckUnvisitedClustersL(const RBitVector& aFatBitVec) const
sl@0
  4261
    {
sl@0
  4262
sl@0
  4263
    TInt cluster=2;
sl@0
  4264
    TInt maxCluster=cluster + UsableClusters();
sl@0
  4265
    while (cluster<maxCluster)
sl@0
  4266
        {
sl@0
  4267
        cluster=NextUnvisitedCluster(aFatBitVec, cluster); //-- move to the next unvisited cluster
sl@0
  4268
        if(cluster < 0 || cluster >= maxCluster)
sl@0
  4269
            break;
sl@0
  4270
sl@0
  4271
        TInt clusterVal=FAT().ReadL(cluster);
sl@0
  4272
        if (clusterVal!=0 && IsEndOfClusterCh(clusterVal)==EFalse && !IsBadCluster(clusterVal))
sl@0
  4273
            {
sl@0
  4274
            __PRINT1(_L("\n*****Bad cluster Num = %d"),cluster);
sl@0
  4275
            User::Leave(EFatChkDskBadCluster);
sl@0
  4276
            }
sl@0
  4277
        }
sl@0
  4278
    }
sl@0
  4279
sl@0
  4280
//-----------------------------------------------------------------------------------------
sl@0
  4281
sl@0
  4282
/**
sl@0
  4283
@param  aCluster cluster number to check for validity
sl@0
  4284
@returns ETrue if aCluster is a valid cluster number
sl@0
  4285
*/
sl@0
  4286
TBool CFatMountCB::ValidClusterNumber(TUint32 aCluster) const
sl@0
  4287
    {
sl@0
  4288
    return (aCluster>=KFatFirstSearchCluster && aCluster<=MaxClusterNumber());
sl@0
  4289
    }
sl@0
  4290
sl@0
  4291
//-----------------------------------------------------------------------------------------
sl@0
  4292
sl@0
  4293
//
sl@0
  4294
// Walk the FAT, returns error if find an unterminated list or
sl@0
  4295
// lists that merge.
sl@0
  4296
//
sl@0
  4297
TInt CFatMountCB::CheckDisk()
sl@0
  4298
    {
sl@0
  4299
sl@0
  4300
    __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber());
sl@0
  4301
sl@0
  4302
    if(!ConsistentState())
sl@0
  4303
        return KErrCorrupt;
sl@0
  4304
sl@0
  4305
    //-- create a bit representation of the FAT
sl@0
  4306
    const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
sl@0
  4307
sl@0
  4308
sl@0
  4309
    // cluster count may be zero if boot sector failed to be read (e.g. if the media is locked)
sl@0
  4310
    // or if TDrive::MountMedia(ETrue) has been called (in which case the boot sector may
sl@0
  4311
    if (MaxClusters == 0)
sl@0
  4312
        return KErrCorrupt;
sl@0
  4313
sl@0
  4314
    RBitVector bitVec; //-- each bit in this vector represents a FAT cluster
sl@0
  4315
sl@0
  4316
    TInt nRes = bitVec.Create(MaxClusters);
sl@0
  4317
    if(nRes != KErrNone)
sl@0
  4318
        {
sl@0
  4319
        ASSERT(nRes == KErrNoMemory); //-- the only one possible reason.
sl@0
  4320
        return KErrNoMemory;
sl@0
  4321
        }
sl@0
  4322
sl@0
  4323
    iChkDiscRecLevel = 0; //-- reset CheckDisk recursion counter
sl@0
  4324
    TRAPD(r,ChkDirL(bitVec, RootIndicator())); // Check from root directory
sl@0
  4325
    if (r==KErrNone)
sl@0
  4326
        {
sl@0
  4327
        TRAP(r,CheckUnvisitedClustersL(bitVec));
sl@0
  4328
        }
sl@0
  4329
sl@0
  4330
sl@0
  4331
    bitVec.Close();
sl@0
  4332
sl@0
  4333
    switch(r)
sl@0
  4334
        {
sl@0
  4335
sl@0
  4336
    case KErrNone:
sl@0
  4337
        return KErrNone;
sl@0
  4338
sl@0
  4339
    case EFatChkDskIllegalClusterNumber:
sl@0
  4340
        return(1);
sl@0
  4341
sl@0
  4342
    case EFatChkDskClusterAlreadyInUse:
sl@0
  4343
        return(2);
sl@0
  4344
sl@0
  4345
    case EFatChkDskBadCluster:
sl@0
  4346
        return(3);
sl@0
  4347
sl@0
  4348
    case EFatChkDskInvalidEntrySize:
sl@0
  4349
        return(4);
sl@0
  4350
sl@0
  4351
    default:
sl@0
  4352
        break;
sl@0
  4353
        }
sl@0
  4354
sl@0
  4355
    return(r);
sl@0
  4356
    }
sl@0
  4357
sl@0
  4358
sl@0
  4359
//-----------------------------------------------------------------------------------------
sl@0
  4360
//  Helper functions for Check Disk functionality
sl@0
  4361
//-----------------------------------------------------------------------------------------
sl@0
  4362
sl@0
  4363
/**
sl@0
  4364
    Find the next unvisited cluster number in the bit array.
sl@0
  4365
sl@0
  4366
    @param  aBitList    bit array, where '0' bits represent unvisited clusters.
sl@0
  4367
    @param  aCluster    cluster number to start search with.
sl@0
  4368
sl@0
  4369
    @return positive integer indicating next unvisited cluster number
sl@0
  4370
            KErrNotFound (-1) if there are no unvisited clusters
sl@0
  4371
sl@0
  4372
*/
sl@0
  4373
static TInt  NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster)
sl@0
  4374
{
sl@0
  4375
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
sl@0
  4376
sl@0
  4377
    TUint32 searchPos = aCluster; //-- bit number to start search with
sl@0
  4378
sl@0
  4379
    //-- look for the unvisited cluster (bit '0') in the bit array from the searchPos to the right
sl@0
  4380
    if(aFatBitVec.Find(searchPos, 0, RBitVector::ERight))
sl@0
  4381
        return searchPos;
sl@0
  4382
sl@0
  4383
    return KErrNotFound;
sl@0
  4384
}
sl@0
  4385
sl@0
  4386
sl@0
  4387
/**
sl@0
  4388
    Check if we have visited cluster aCluster
sl@0
  4389
sl@0
  4390
    @param  aFatBitVec  bit array, where '0' bits represent unvisited clusters.
sl@0
  4391
    @param  aCluster    cluster number to check
sl@0
  4392
    @return ETrue if aCluster has been visited.
sl@0
  4393
*/
sl@0
  4394
static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster)
sl@0
  4395
{
sl@0
  4396
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
sl@0
  4397
sl@0
  4398
    return aFatBitVec[aCluster];
sl@0
  4399
}
sl@0
  4400
sl@0
  4401
/**
sl@0
  4402
    Mark aCluster as visited
sl@0
  4403
    @param  aFatBitVec  bit array, where '0' bits represent unvisited clusters.
sl@0
  4404
    @param  aCluster    cluster number to mark
sl@0
  4405
*/
sl@0
  4406
static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster)
sl@0
  4407
{
sl@0
  4408
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
sl@0
  4409
sl@0
  4410
    aFatBitVec.SetBit(aCluster); //-- '1' bit in a bit array means that the corresponding cluster is visited
sl@0
  4411
}
sl@0
  4412
sl@0
  4413
sl@0
  4414
sl@0
  4415
//-------------------------------------------------------------------------------------------------------------------
sl@0
  4416
sl@0
  4417
/**
sl@0
  4418
    Creates a scan drive object and starts the scan.
sl@0
  4419
*/
sl@0
  4420
TInt CFatMountCB::DoRunScanDrive()
sl@0
  4421
{
sl@0
  4422
    TInt nRes;
sl@0
  4423
sl@0
  4424
    CScanDrive* pScnDrv = NULL;
sl@0
  4425
    TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
sl@0
  4426
    if(nRes != KErrNone)
sl@0
  4427
        return nRes;
sl@0
  4428
sl@0
  4429
    TRAPD(nScnDrvRes, pScnDrv->StartL());
sl@0
  4430
sl@0
  4431
    const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
sl@0
  4432
    delete pScnDrv;
sl@0
  4433
sl@0
  4434
sl@0
  4435
    if(bNeedFatRemount)
sl@0
  4436
        {//-- ScanDrive found and probably fixed some errors.
sl@0
  4437
        // ensure cached fat and free cluster count are updated
sl@0
  4438
        DoDismount(); //-- dismount
sl@0
  4439
        TRAP(nRes, MountL(EFalse)); //-- mount again
sl@0
  4440
        }
sl@0
  4441
sl@0
  4442
    if(nScnDrvRes != KErrNone)
sl@0
  4443
        return nScnDrvRes;
sl@0
  4444
sl@0
  4445
sl@0
  4446
    //-- if ScanDrive hasn't found anything wrong or has fixed recoverable erros, mark the volume clean
sl@0
  4447
    if(VolCleanFlagSupported())
sl@0
  4448
        {
sl@0
  4449
        //-- if there is a background FAT scanning thread, we need to wait until it finishes its work.
sl@0
  4450
        //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting.
sl@0
  4451
        (void)FAT().NumberOfFreeClusters(ETrue);
sl@0
  4452
        TRAP(nRes, FinaliseMountL());
sl@0
  4453
        ASSERT(nRes == KErrNone);
sl@0
  4454
        }
sl@0
  4455
sl@0
  4456
    return KErrNone;
sl@0
  4457
}
sl@0
  4458
sl@0
  4459
//-------------------------------------------------------------------------------------------------------------------
sl@0
  4460
sl@0
  4461
/**
sl@0
  4462
    Run scan drive on the given volume.
sl@0
  4463
    The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly.
sl@0
  4464
sl@0
  4465
sl@0
  4466
    @return Either  KErrCorrupt if an error was found that is not caused by write failure due to power removal.
sl@0
  4467
                    KErrNone if no error was found. One of four positive codes explaining what type of error was rectified
sl@0
  4468
*/
sl@0
  4469
TInt CFatMountCB::ScanDrive()
sl@0
  4470
{
sl@0
  4471
    __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber());
sl@0
  4472
sl@0
  4473
    if(!ConsistentState())
sl@0
  4474
        return KErrCorrupt;
sl@0
  4475
sl@0
  4476
    TInt nRes;
sl@0
  4477
sl@0
  4478
    if(LockStatus()!=0)
sl@0
  4479
        {
sl@0
  4480
        __PRINT(_L("CFatMountCB::ScanDrive() locked!\n"));
sl@0
  4481
        return KErrInUse;
sl@0
  4482
        }
sl@0
  4483
sl@0
  4484
    if(iRamDrive)
sl@0
  4485
        {//-- Do not check internal RAM drive
sl@0
  4486
        __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive."));
sl@0
  4487
        return KErrNone;
sl@0
  4488
        }
sl@0
  4489
sl@0
  4490
    //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt
sl@0
  4491
    if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume())
sl@0
  4492
        {
sl@0
  4493
        TBool bVolClean = EFalse;
sl@0
  4494
        TRAP(nRes, bVolClean = VolumeCleanL());
sl@0
  4495
sl@0
  4496
        if(nRes == KErrNone && bVolClean)
sl@0
  4497
            {
sl@0
  4498
            __PRINT(_L("Skipping ScanDrive on finalised volume!"));
sl@0
  4499
            return KErrNone; //-- skip ScanDrive on a clean volume
sl@0
  4500
            }
sl@0
  4501
        }
sl@0
  4502
sl@0
  4503
    //-- run ScanDrive
sl@0
  4504
    nRes = Open();
sl@0
  4505
    if(nRes != KErrNone)
sl@0
  4506
        return nRes;
sl@0
  4507
sl@0
  4508
    nRes = DoRunScanDrive();
sl@0
  4509
sl@0
  4510
    Close();
sl@0
  4511
sl@0
  4512
    __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes);
sl@0
  4513
sl@0
  4514
    return nRes;
sl@0
  4515
sl@0
  4516
}
sl@0
  4517
sl@0
  4518
//-----------------------------------------------------------------------------------------
sl@0
  4519
/**
sl@0
  4520
Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset
sl@0
  4521
if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not
sl@0
  4522
a null offset is returned.
sl@0
  4523
sl@0
  4524
@return The offset in seconds that timestamps on the filesystem have, relative to UTC.
sl@0
  4525
*/
sl@0
  4526
TTimeIntervalSeconds CFatMountCB::TimeOffset() const
sl@0
  4527
    {
sl@0
  4528
    if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() )
sl@0
  4529
        {
sl@0
  4530
        return User::UTCOffset();
sl@0
  4531
        }
sl@0
  4532
    else
sl@0
  4533
        {
sl@0
  4534
        return TTimeIntervalSeconds(0);
sl@0
  4535
        }
sl@0
  4536
    }
sl@0
  4537
sl@0
  4538
sl@0
  4539
sl@0
  4540
sl@0
  4541
//-----------------------------------------------------------------------------------------
sl@0
  4542
/** 
sl@0
  4543
    Check is this file system can be mounted on the drive at all.
sl@0
  4544
    Just read and validate boot region, no real mounting overhead. 
sl@0
  4545
    
sl@0
  4546
    @return KErrNone    boot region is OK, the file system can be mounted.
sl@0
  4547
            KErrLocked  the media is locked on a physical level.
sl@0
  4548
            other error codes depending on the implementation
sl@0
  4549
sl@0
  4550
*/
sl@0
  4551
TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable()
sl@0
  4552
    {
sl@0
  4553
    TInt nRes;
sl@0
  4554
    
sl@0
  4555
    const TInt driveNo = Drive().DriveNumber();
sl@0
  4556
    __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo);
sl@0
  4557
sl@0
  4558
    nRes = CreateDrive(driveNo);
sl@0
  4559
    if(nRes != KErrNone)
sl@0
  4560
        {
sl@0
  4561
        __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes);    
sl@0
  4562
        return nRes;
sl@0
  4563
        }
sl@0
  4564
sl@0
  4565
    //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive()
sl@0
  4566
    TFatBootSector bootSector;
sl@0
  4567
    nRes = ReadBootSector(bootSector);
sl@0
  4568
sl@0
  4569
    DismountedLocalDrive();
sl@0
  4570
sl@0
  4571
    return nRes;
sl@0
  4572
    }
sl@0
  4573
sl@0
  4574
sl@0
  4575