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