sl@0: // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Common CFatMountCB code for both EFAT.FSY and EFAT32.fsy sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: @internalTechnology sl@0: */ sl@0: sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: //!! sl@0: //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it sl@0: //!! sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: sl@0: sl@0: #include "sl_std.h" sl@0: #include "sl_cache.h" sl@0: #include "sl_leafdir_cache.h" sl@0: #include "sl_dir_cache.h" sl@0: #include "sl_scandrv.h" sl@0: #include sl@0: sl@0: TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively); sl@0: sl@0: static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster); sl@0: static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster); sl@0: static TInt NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster); sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TFatVolParam::TFatVolParam() sl@0: { sl@0: Mem::FillZ(this, sizeof(TFatVolParam)); sl@0: } sl@0: sl@0: /** sl@0: populate the object with the values from the boot sector. sl@0: @param aBootSector a reference to the valid boots sector sl@0: */ sl@0: void TFatVolParam::Populate(const TFatBootSector& aBootSector) sl@0: { sl@0: ASSERT(aBootSector.IsValid()); sl@0: sl@0: iSectorsPerCluster = aBootSector.SectorsPerCluster(); sl@0: iSectorSizeLog2 = Log2(aBootSector.BytesPerSector()); sl@0: iClusterSizeLog2 = iSectorSizeLog2+Log2(iSectorsPerCluster); sl@0: iFirstFatSector = aBootSector.FirstFatSector(); sl@0: iNumberOfFats = aBootSector.NumberOfFats(); sl@0: iFatSizeInBytes = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector(); sl@0: iTotalSectors = aBootSector.VolumeTotalSectorNumber(); sl@0: iRootClusterNum = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16 sl@0: sl@0: iRootDirectorySector = aBootSector.RootDirStartSector(); sl@0: iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32 sl@0: sl@0: //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16 sl@0: iFSInfoSectorNum = aBootSector.FSInfoSectorNum(); sl@0: iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector sl@0: } sl@0: sl@0: TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const sl@0: { sl@0: ASSERT(&aRhs != this); sl@0: if(&aRhs == this) sl@0: return ETrue; //-- comparing with itself sl@0: sl@0: return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: CFatMountCB::CFatMountCB() sl@0: { sl@0: __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName); sl@0: iFatType = EInvalid; sl@0: iState = ENotMounted; sl@0: DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only sl@0: } sl@0: sl@0: CFatMountCB::~CFatMountCB() sl@0: { sl@0: __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this); sl@0: sl@0: DoDismount(); sl@0: sl@0: delete iNotifier; sl@0: delete iFatTable; sl@0: delete iRawDisk; sl@0: delete iLeafDirCache; sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: CFatMountCB* CFatMountCB::NewL() sl@0: { sl@0: CFatMountCB* pSelf = new(ELeave) CFatMountCB; sl@0: sl@0: CleanupStack::PushL(pSelf); sl@0: pSelf->ConstructL(); sl@0: CleanupStack::Pop(pSelf); sl@0: sl@0: return pSelf; sl@0: } sl@0: sl@0: // second-stage constructor sl@0: void CFatMountCB::ConstructL() sl@0: { sl@0: //-- create Notifier sl@0: iNotifier = CAsyncNotifier::New(); sl@0: if( !iNotifier ) sl@0: { sl@0: Close(); sl@0: User::Leave(KErrNoMemory); sl@0: } sl@0: sl@0: iNotifier->SetMount(this); sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount. sl@0: @return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready. sl@0: @see CMountCB::FileSystemClusterSize() sl@0: */ sl@0: TInt CFatMountCB::ClusterSize() const sl@0: { sl@0: if (ClusterSizeLog2()) sl@0: return (1 << ClusterSizeLog2()); sl@0: sl@0: return KErrNotReady; sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: @leave KErrAccessDenied if the mount is read-only sl@0: */ sl@0: void CFatMountCB::CheckWritableL() const sl@0: { sl@0: if(ReadOnly()) sl@0: { sl@0: __PRINT(_L("CFatMountCB is RO!")); sl@0: User::Leave(KErrAccessDenied); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory operations can not be performed sl@0: */ sl@0: void CFatMountCB::CheckStateConsistentL() const sl@0: { sl@0: if(!ConsistentState()) sl@0: { sl@0: __PRINT(_L("CFatMountCB state is inconsistent !")); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: } sl@0: sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: /** sl@0: Helper Method. Check if the parameters of the volume being remounted are the same as current ones. sl@0: @return ETrue if volume parameters remained same. sl@0: */ sl@0: TBool CFatMountCB::CheckVolumeTheSame() sl@0: { sl@0: //-- initialise local drive sl@0: TInt nRes =InitLocalDrive(); sl@0: if(nRes != KErrNone) sl@0: return EFalse; sl@0: sl@0: //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it. sl@0: TFatBootSector bootSector; sl@0: nRes = ReadBootSector(bootSector, iRamDrive); sl@0: if(nRes != KErrNone) sl@0: return EFalse; sl@0: sl@0: //-- 1. check volume Uid sl@0: if(iUniqueID != bootSector.UniqueID()) sl@0: return EFalse; sl@0: sl@0: //-- check volume parameters, they must remain the same sl@0: TFatVolParam volParam; sl@0: volParam.Populate(bootSector); sl@0: sl@0: if(!(volParam == iVolParam)) sl@0: return EFalse; sl@0: sl@0: sl@0: return ETrue; sl@0: sl@0: } sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Helper Method. Check if the parameters of the volume being remounted are the same as current ones. sl@0: If they are, re-initialises the mount. sl@0: */ sl@0: void CFatMountCB::DoReMountL() sl@0: { sl@0: sl@0: if(!CheckVolumeTheSame()) sl@0: User::Leave(KErrGeneral); sl@0: sl@0: //-- get drive capabilities sl@0: TLocalDriveCapsV2Buf capsBuf; sl@0: User::LeaveIfError(LocalDrive()->Caps(capsBuf)); sl@0: sl@0: //-- the volume is the same as it was on original MountL() sl@0: //-- 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: DoDismount(); sl@0: sl@0: SetState(EMounting); sl@0: sl@0: InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting. sl@0: sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and sl@0: if they are, re-initialises the mount. This includes resetting all caches. sl@0: ! Do not call this method from TFatDriveInterface methods, like citical and non-critical notifiers ! This can lead to the sl@0: recursive loops and undefined behaviour. sl@0: sl@0: @return KErrNone if the remount was OK sl@0: system-wide error code otherwise sl@0: */ sl@0: TInt CFatMountCB::ReMount() sl@0: { sl@0: __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State()); sl@0: sl@0: const TFatMntState currState = State(); sl@0: sl@0: //-- analyse the mount state and find out if we can remount at all. sl@0: switch(currState) sl@0: { sl@0: case ENotMounted: sl@0: __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!")); sl@0: sl@0: ASSERT(0); sl@0: return KErrGeneral; sl@0: sl@0: //-- correct state, proceed to remount sl@0: default: sl@0: break; sl@0: } sl@0: sl@0: //-- there are 2 options here: sl@0: //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it) sl@0: //-- in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards sl@0: //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.) sl@0: //-- in this case we may need to fake the success. This can only happen on forced mount by CFormatCB sl@0: TInt nRes; sl@0: TRAP(nRes, DoReMountL()); sl@0: sl@0: if(nRes != KErrNone) sl@0: { sl@0: //-- note that the mount may be here left in inconsistent state (EMounting) sl@0: //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and sl@0: //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this. sl@0: __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes); sl@0: sl@0: //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted. sl@0: //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and sl@0: //-- 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: //-- we will get here, because all members of the constructed mount will be zeroes. sl@0: if(currState == EInit_Forced) sl@0: { sl@0: __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!")); sl@0: SetState(currState); sl@0: return KErrNone; sl@0: } sl@0: sl@0: return nRes; sl@0: } sl@0: sl@0: __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber()); sl@0: SetState(EInit_R); sl@0: return nRes; sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is sl@0: instantiated. sl@0: */ sl@0: sl@0: void CFatMountCB::InvalidateLeafDirCache() sl@0: { sl@0: if (iLeafDirCache) sl@0: { sl@0: iLeafDirCache->Reset(); sl@0: } sl@0: else sl@0: { sl@0: User::Free(iLastLeafDir); sl@0: iLastLeafDir=NULL; sl@0: } sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Delete mount's caches sl@0: Moves CFatMountCB into ENotMounted state immediately. sl@0: */ sl@0: void CFatMountCB::DoDismount() sl@0: { sl@0: __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber()); sl@0: sl@0: //-- try to flush and destroy FAT cache sl@0: if (iFatTable) sl@0: { sl@0: if(!ConsistentState() || ReadOnly()) sl@0: {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either. sl@0: iFatTable->Dismount(ETrue); sl@0: } sl@0: else sl@0: {//-- Try to flush the FAT - if this fails there's not much we can do sl@0: TRAPD(r, iFatTable->FlushL()); sl@0: iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache sl@0: } sl@0: } sl@0: sl@0: //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting sl@0: //-- see CFatMountCB::InitializeL() sl@0: delete iLeafDirCache; sl@0: iLeafDirCache = NULL; sl@0: sl@0: //-- destroy directory cache, this cache will be re-created while mounting or re-mounting sl@0: //-- see CFatMountCB::InitializeL() sl@0: delete iRawDisk; sl@0: iRawDisk = NULL; sl@0: sl@0: //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive, sl@0: //-- i.e. iFatTable & iRawDisk are valid sl@0: SetState(EDismounted); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** old implementation */ sl@0: void CFatMountCB::FinaliseMountL() sl@0: { sl@0: FinaliseMountL(RFs::EFinal_RW); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: /** sl@0: Dismount the CFatMountCB and the drive. sl@0: called from TDrive::Dismount(). sl@0: */ sl@0: void CFatMountCB::Dismounted() sl@0: { sl@0: __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber()); sl@0: sl@0: //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media. sl@0: //-- this method may be called after the media change occured from the TDrive::Dismount(). It means sl@0: //-- that if we try to write some data here, they could be written into a different medium, if it had been sl@0: //-- physically changed. sl@0: sl@0: const TFatMntState prevState = State(); sl@0: sl@0: DoDismount(); //-- it will change mount state to EDismounted sl@0: DismountedLocalDrive(); sl@0: sl@0: //-- check if the previous state was EInit_Forced, which means that this method was called sl@0: //-- on the mount that might not be alive (no valid iFatTable & iRawDisk). sl@0: //-- This can happen only during format operation on non-mounted previously volume. sl@0: //-- this EInit_Forced state must be processed separately, see ::Remount() sl@0: if(prevState == EInit_Forced) sl@0: SetState(EInit_Forced); sl@0: sl@0: } sl@0: sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Find out if the mount is finalised. sl@0: @param aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise. sl@0: @return standard error codes. sl@0: */ sl@0: TInt CFatMountCB::IsFinalised(TBool& aFinalised) sl@0: { sl@0: switch(State()) sl@0: { sl@0: case EFinalised: //-- already explicitly finalised sl@0: aFinalised = ETrue; sl@0: return KErrNone; sl@0: sl@0: case EInit_W: //-- the volume had been written sl@0: aFinalised = EFalse; sl@0: return KErrNone; sl@0: sl@0: default: //-- it depends on the state sl@0: break; sl@0: } sl@0: sl@0: //-- find out if the volume is _physically_ finalised. sl@0: //-- It can be in the state EInit_R, but finalised before mounting sl@0: if(!VolCleanFlagSupported()) sl@0: return KErrNotSupported; sl@0: sl@0: TInt nRes = KErrNone; sl@0: TRAP(nRes, aFinalised = VolumeCleanL()); sl@0: sl@0: return nRes; sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: @return ETrue if the mount is in consistent state i.e. normally mounted. sl@0: See TFatMntState enum for more detail. sl@0: */ sl@0: TBool CFatMountCB::ConsistentState() const sl@0: { sl@0: return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised); sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Open CFatMountCB for write. I.e. perform some actions on the first write attempt. sl@0: This is a callback from TFatDriveInterface. sl@0: @return System wide error code. sl@0: */ sl@0: TInt CFatMountCB::OpenMountForWrite() sl@0: { sl@0: if(State() == EInit_W) sl@0: return KErrNone; //-- nothing to do, the mount is already opened for write sl@0: sl@0: __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber()); sl@0: sl@0: ASSERT(State() == EInit_R || State() == EFinalised); sl@0: sl@0: //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and sl@0: //-- can not call TFatDriveInterface methods that call this method etc. sl@0: ASSERT(iCBRecFlag == 0); sl@0: DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag sl@0: sl@0: //-- do here some "opening" work, like marking volme as dirty sl@0: //-- be careful here, as soon as this is a callback from TFatDriveInterface, writing via TFatDriveInterface may cause some unwanted recursion. sl@0: sl@0: //-- mark the volume as dirty sl@0: TInt nRes=KErrNone; sl@0: TRAP(nRes, SetVolumeCleanL(EFalse)); sl@0: if(nRes == KErrNone) sl@0: { sl@0: SetState(EInit_W); sl@0: } sl@0: sl@0: DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag sl@0: sl@0: return nRes; sl@0: sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily. sl@0: */ sl@0: void CFatMountCB::UnFinaliseMountL() sl@0: { sl@0: switch(State()) sl@0: { sl@0: case EFinalised: sl@0: case EInit_R: sl@0: SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty" sl@0: SetState(EInit_R); sl@0: return; sl@0: sl@0: case EInit_W: sl@0: return; //-- nothing to do sl@0: sl@0: default: sl@0: //-- other mount states are inconsistent; can't perform this operation sl@0: User::Leave(KErrAbort); sl@0: break; sl@0: sl@0: } sl@0: sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Finalise the mount. sl@0: sl@0: @param aOperation describes finalisation operation ,see RFs::TFinaliseDrvMode sl@0: @param aParam1 not used, for future expansion sl@0: @param aParam2 not used, for future expansion sl@0: sl@0: @leave System wide error code. particular cases: sl@0: KErrArgument invalid arguments sl@0: KErrInUse if the volume has opened objects (files, directories etc) sl@0: KErrCorrupt if the volume is corrupt sl@0: sl@0: */ sl@0: void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/) sl@0: { sl@0: __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber()); sl@0: sl@0: switch(aOperation) sl@0: { sl@0: case RFs::EFinal_RW: sl@0: case RFs::EFinal_RO: sl@0: break; sl@0: sl@0: case RFs::EForceUnfinalise: sl@0: UnFinaliseMountL(); sl@0: return; sl@0: sl@0: default: sl@0: __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation); sl@0: ASSERT(0); sl@0: User::Leave(KErrArgument); sl@0: return; sl@0: } sl@0: sl@0: //-- mount finalisation work sl@0: sl@0: ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO); sl@0: sl@0: if(State() == EFinalised) sl@0: {//-- the mount is already finalised. All we can do is to set it to RO mode sl@0: if(ReadOnly() && aOperation == RFs::EFinal_RW) sl@0: { sl@0: User::Leave(KErrAccessDenied); //-- can't override RO flag sl@0: } sl@0: sl@0: (void)LocalDrive()->Finalise(ETrue); sl@0: sl@0: if(aOperation == RFs::EFinal_RO) sl@0: { sl@0: SetReadOnly(ETrue); sl@0: return; sl@0: } sl@0: sl@0: return; sl@0: } sl@0: sl@0: if(LockStatus() != 0) sl@0: {//-- can't finalise the volume if it has opened objects and not in the consistent state. sl@0: //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present, sl@0: //-- it's impossible to detect such situation. sl@0: User::Leave(KErrInUse); sl@0: } sl@0: sl@0: if(State() != EInit_R && State() != EInit_W) sl@0: {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt. sl@0: __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State()); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: //-- flush FAT cache sl@0: FAT().FlushL(); sl@0: sl@0: //-- for FAT32 we may need to update data in FSInfo sectors sl@0: if(Is32BitFat()) sl@0: { sl@0: if(FAT().ConsistentState()) sl@0: {//-- FAT table state is consistent and the number of free clusters is known. sl@0: //-- 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: DoUpdateFSInfoSectorsL(EFalse); sl@0: } sl@0: else sl@0: {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working. sl@0: //-- in this case we can't put corect values into the FSInfo. sl@0: if(State() == EInit_W) sl@0: {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time. sl@0: //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo. sl@0: __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo")); sl@0: DoUpdateFSInfoSectorsL(ETrue); sl@0: } sl@0: else sl@0: {//-- no changes on the volume, just do not update FSInfo sl@0: __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated")); sl@0: } sl@0: sl@0: }//if(FAT().ConsistentState()) sl@0: sl@0: }//if(Is32BitFat()) sl@0: sl@0: sl@0: sl@0: //-- mark the volume as clean sl@0: SetVolumeCleanL(ETrue); sl@0: sl@0: //-- finally, put the volume into RO mode if required sl@0: if(aOperation == RFs::EFinal_RO) sl@0: SetReadOnly(ETrue); sl@0: sl@0: SetState(EFinalised); sl@0: } sl@0: sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: @return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12 sl@0: */ sl@0: TBool CFatMountCB::VolCleanFlagSupported() const sl@0: { sl@0: const TFatType fatType=FatType(); sl@0: sl@0: ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32); sl@0: return (fatType != EFat12); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: /** sl@0: Obtain the volume information. sl@0: All information except iSize and iFree has been added by TDrive::Volume(). sl@0: sl@0: @param aVolume on return will contain iSize & iFree fields filled with actual data. sl@0: */ sl@0: void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const sl@0: { sl@0: sl@0: //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running. sl@0: //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field. sl@0: //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters sl@0: //-- will be returned. sl@0: const TBool bSyncOp = !aVolume.iVolSizeAsync; sl@0: aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side sl@0: sl@0: __PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp); sl@0: const TDriveInfo& drvInfo=aVolume.iDrive; sl@0: sl@0: #if defined(__EPOC32__) sl@0: // if RAM drive, cap size according to HAL. sl@0: if (drvInfo.iType==EMediaRam) sl@0: { sl@0: TLocalDriveCapsV2Buf caps; sl@0: LocalDrive()->Caps(caps); sl@0: sl@0: const TInt max_drive_size=TInt(caps().iEraseBlockSize); sl@0: const TInt cur_drive_size=I64INT(caps().iSize); sl@0: sl@0: aVolume.iSize=max_drive_size; sl@0: aVolume.iFree=max_drive_size-cur_drive_size; sl@0: sl@0: aVolume.iSize=aVolume.iFree+iSize; sl@0: sl@0: TInt maxSize; sl@0: if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone) sl@0: { sl@0: // iSize will never grow beyond maxRam because of a check in medint. sl@0: // d <= f; (s{f} + f) - m <= f; s{f} <= m sl@0: __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid)); sl@0: if (aVolume.iSize > maxSize) sl@0: { sl@0: TInt64 d = aVolume.iSize - maxSize; sl@0: __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid)); sl@0: aVolume.iSize -= d; sl@0: aVolume.iFree -= d; sl@0: } sl@0: } sl@0: sl@0: aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc sl@0: aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<> ClusterSizeLog2()) << ClusterSizeLog2(); //-- round down to cluster size sl@0: sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: // sl@0: // Set the volume label (write aVolume label into BPB & Volume Label File) sl@0: // aName string may be zero length but is assumed to contain no illegal characters or NULLs. sl@0: // sl@0: void CFatMountCB::SetVolumeL(TDes& aName) sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::SetVolumeL")); sl@0: sl@0: CheckStateConsistentL(); sl@0: CheckWritableL(); sl@0: sl@0: __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName)); sl@0: sl@0: TBuf8 buf8(KVolumeLabelSize); sl@0: buf8.Zero(); sl@0: LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave); sl@0: aName.Zero(); sl@0: LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before) sl@0: sl@0: const TInt lengthOfBuf8=buf8.Length(); sl@0: // Pad to end with spaces if not empty. sl@0: if (lengthOfBuf8>0 && lengthOfBuf8=1); sl@0: sl@0: for(TInt i=0; iCacheCount() > 0) sl@0: { sl@0: iLeafDirCache->RemoveDirL(StartCluster(dosEntry)); sl@0: } sl@0: sl@0: EraseDirEntryL(dirEntryPos,dirEntry); sl@0: FAT().FreeClusterListL(StartCluster(dosEntry)); sl@0: FAT().FlushL(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Delete a file sl@0: @param aName file name sl@0: all trailing dots from the name will be removed sl@0: */ sl@0: void CFatMountCB::DeleteL(const TDesC& aName) sl@0: { sl@0: __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: CheckWritableL(); sl@0: sl@0: TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name sl@0: sl@0: TFatDirEntry firstEntry; sl@0: TEntryPos firstEntryPos(RootIndicator(),0); sl@0: FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos); sl@0: TEntryPos dosEntryPos=firstEntryPos; sl@0: TFatDirEntry dosEntry=firstEntry; sl@0: MoveToDosEntryL(dosEntryPos,dosEntry); sl@0: if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir)) sl@0: User::Leave(KErrAccessDenied); sl@0: // Can not delete a file if it is clamped sl@0: CMountCB* basePtr=(CMountCB*)this; sl@0: TInt startCluster=StartCluster(dosEntry); sl@0: if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0) sl@0: User::Leave(KErrInUse); sl@0: EraseDirEntryL(firstEntryPos,firstEntry); sl@0: FAT().FreeClusterListL(StartCluster(dosEntry)); sl@0: FAT().FlushL(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: sl@0: Rename or replace a directory entry. sl@0: Assumes all files are closed and replace is only passed files. sl@0: Assumes rename target does not exist or is the source file. sl@0: sl@0: --------------- operating mode -------------------------------------------- sl@0: sl@0: * rename mode sl@0: sl@0: aOldName exists | aNewName exists | result sl@0: N N leave KErrNotFound sl@0: N Y leave KErrNotFound sl@0: Y N rename aOldName -> aNewName sl@0: Y Y leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing sl@0: sl@0: * replace mode sl@0: sl@0: N N leave KErrNotFound sl@0: N Y leave KErrNotFound sl@0: Y N rename aOldName -> aNewName sl@0: Y Y contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then. sl@0: sl@0: sl@0: @param aOldName entry name to be renamed or replaced sl@0: @param aNewName a new entry name sl@0: @param aMode specifies renaming / replacing sl@0: @param aNewDosEntryPos on exit contains new entry Pos. sl@0: */ sl@0: void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos) sl@0: { sl@0: __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName); sl@0: sl@0: const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive. sl@0: const TBool renameMode = (aMode == EModeRename); sl@0: const TBool replaceMode = !renameMode; sl@0: TInt nRes; sl@0: sl@0: if(namesAreIdentical && replaceMode) sl@0: return; //-- nothing to do, replacing file with itself sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------- sl@0: //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName' sl@0: sl@0: TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset sl@0: TEntryPos oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set sl@0: sl@0: FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos); sl@0: sl@0: TFatDirEntry oldName_DosEntry = oldName_FirstEntry; //-- "aOldName" entry set DOS entry sl@0: TEntryPos oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry sl@0: sl@0: MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry); sl@0: sl@0: const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry sl@0: sl@0: //-- check if the file "aOldName" is clamped. In this case it can't be replaced. sl@0: if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0)) sl@0: User::Leave(KErrInUse); sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------- sl@0: //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not sl@0: sl@0: //-- extract new file name from the full path sl@0: TPtrC ptrNewName; sl@0: TPtrC ptrNewNameParentDir; sl@0: const TInt delimPos = aNewName.LocateReverse(KPathDelimiter); sl@0: ptrNewName.Set(aNewName.Mid(delimPos+1)); sl@0: ptrNewNameParentDir.Set(aNewName.Left(delimPos+1)); sl@0: sl@0: //-- find the parent directory of the "aNewName" and create iterator for it sl@0: TLeafDirData leafDir; sl@0: const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator sl@0: aNewName_DosEntryPos = aNewName_ParentDirPos; sl@0: sl@0: TEntryPos newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set sl@0: TFatDirEntry newName_DosEntry; sl@0: sl@0: TFileName fileName; sl@0: iFileCreationHelper.InitialiseL(ptrNewName); sl@0: TFatDirEntry startEntry; sl@0: sl@0: TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported, sl@0: newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry, sl@0: fileName, KErrNotFound, sl@0: &iFileCreationHelper, sl@0: leafDir)); sl@0: sl@0: if (nRes!=KErrNone && nRes!=KErrNotFound) sl@0: User::Leave(nRes); sl@0: sl@0: const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists. sl@0: const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue); sl@0: sl@0: if(renameMode && newFileExists) sl@0: { sl@0: if(!namesAreIdentical) sl@0: { sl@0: if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir)) sl@0: { sl@0: User::Leave(KErrAccessDenied); //-- leave with KErrAccessDenied if it is trying to rename a file sl@0: // to a dir or vice versa. sl@0: } sl@0: User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists sl@0: } sl@0: else sl@0: { sl@0: if(!bNewNameIsVFAT && !bOldNameIsVFAT) sl@0: return; //-- renaming DOS name to itself sl@0: } sl@0: //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File" sl@0: } sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: if(replaceMode && newFileExists) sl@0: { sl@0: //--------------------------------------------------------------------------------------------------------------------------- sl@0: //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries. sl@0: sl@0: //-- check if we are still trying to replace the file with itself, probably using short name alias sl@0: if(aNewName_DosEntryPos == oldName_DosEntryPos) sl@0: return; //-- nothing to do, it's the same file sl@0: sl@0: const TInt oldNameStartCluster = StartCluster(oldName_DosEntry); sl@0: const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked sl@0: sl@0: newName_DosEntry.SetStartCluster(oldNameStartCluster); sl@0: newName_DosEntry.SetSize(oldName_DosEntry.Size()); sl@0: newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0)); sl@0: newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes()); sl@0: sl@0: if(IsRuggedFSys()) sl@0: { sl@0: //-- Note 1. sl@0: //-- set a special Id in reserved section for old and new entries. sl@0: //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain. sl@0: //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry. sl@0: //-- note that SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID. sl@0: //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy. sl@0: //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980. sl@0: sl@0: newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry); sl@0: oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry); sl@0: WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry); sl@0: } sl@0: sl@0: //-- write 'aNewName' DOS dir. entry data back sl@0: WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry); sl@0: sl@0: //-- erase "oldName" entryset. sl@0: EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry); sl@0: sl@0: //-- free 'aNewName' cluster list sl@0: FAT().FreeClusterListL(newNameStartCluster); sl@0: sl@0: if(IsRuggedFSys()) sl@0: FAT().FlushL(); sl@0: sl@0: } sl@0: else //if(replaceMode && newFileExists) sl@0: { sl@0: //--------------------------------------------------------------------------------------------------------------------------- sl@0: //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset sl@0: sl@0: TFatDirEntry newDosEntry = oldName_DosEntry; sl@0: //-- generate short name for the 'aNewName' entryset and make new DOS entry sl@0: if(bNewNameIsVFAT) sl@0: {//-- need to generate a short name for VFAT entryset DOS entry sl@0: TShortName shortName; sl@0: sl@0: if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound) sl@0: { sl@0: GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue); sl@0: } sl@0: sl@0: newDosEntry.SetName(shortName); sl@0: } sl@0: else sl@0: {//-- just use 'aNewName' as DOS name. sl@0: TBuf8 tmp; //-- the name may be "XXXXXXXX.YYY" sl@0: tmp.Copy(ptrNewName); sl@0: newDosEntry.SetName(DosNameToStdFormat(tmp)); sl@0: } sl@0: sl@0: if(IsRuggedFSys()) sl@0: {//-- the the note(1) above sl@0: newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry); sl@0: oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry); sl@0: WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry); sl@0: } sl@0: sl@0: //-- add new entryset to the directory sl@0: aNewName_DosEntryPos.iPos = 0; sl@0: aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster(); sl@0: sl@0: if (iFileCreationHelper.IsNewEntryPosFound()) sl@0: { sl@0: aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos(); sl@0: } sl@0: sl@0: if(bNewNameIsVFAT) sl@0: { sl@0: const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length()); sl@0: AddDirEntryL(aNewName_DosEntryPos, numEntries); sl@0: WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName); sl@0: } sl@0: else sl@0: {//-- new name is one DOS entry only sl@0: AddDirEntryL(aNewName_DosEntryPos, 1); sl@0: WriteDirEntryL(aNewName_DosEntryPos, newDosEntry); sl@0: } sl@0: sl@0: //-- erase old entryset. sl@0: EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry); sl@0: sl@0: //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry) sl@0: if((newDosEntry.Attributes() & KEntryAttDir)) sl@0: { sl@0: TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry); sl@0: sl@0: TFatDirEntry chFatEnt; sl@0: ReadDirEntryL(parentPtrEntPos, chFatEnt); sl@0: sl@0: const TUint parentDirStartCluster_Old = StartCluster(chFatEnt); sl@0: TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster(); sl@0: sl@0: if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0) sl@0: {//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir. sl@0: //-- must have starting cluster 0 sl@0: parentDirStartCluster_New = 0; sl@0: } sl@0: sl@0: if(parentDirStartCluster_Old != parentDirStartCluster_New) sl@0: { sl@0: chFatEnt.SetStartCluster(parentDirStartCluster_New); sl@0: WriteDirEntryL(parentPtrEntPos, chFatEnt); sl@0: } sl@0: // Invalidate leaf dir cache as it is hard to track the dir structure changes now sl@0: if (iLeafDirCache) sl@0: { sl@0: iLeafDirCache->Reset(); sl@0: } sl@0: } sl@0: }//else if(replaceMode && newFileExists) sl@0: sl@0: iFileCreationHelper.Close(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Rename 'aOldName' file/directory to 'aNewName' sl@0: all trailing dots from the names will be removed sl@0: sl@0: @param aOldName existing object name sl@0: @param aNewName new object name sl@0: */ sl@0: void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName) sl@0: { sl@0: __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName); sl@0: sl@0: CheckStateConsistentL(); sl@0: CheckWritableL(); sl@0: sl@0: TEntryPos newEntryPos; sl@0: DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos); sl@0: sl@0: if(!IsRuggedFSys()) sl@0: FAT().FlushL(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Replace contents of the 'aNewName' with the contents of 'aOldName' sl@0: all trailing dots from the names will be removed sl@0: sl@0: @param aOldName existing object name sl@0: @param aNewName new object name sl@0: */ sl@0: void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName) sl@0: { sl@0: sl@0: __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName); sl@0: sl@0: CheckStateConsistentL(); sl@0: CheckWritableL(); sl@0: sl@0: TEntryPos newEntryPos; sl@0: DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos); sl@0: if(!IsRuggedFSys()) sl@0: FAT().FlushL(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: /** sl@0: Try to find a directory entry by the given name and path. sl@0: This method _must_ leave if the entry is not found. See the caller. sl@0: sl@0: @param aName path to the directory object. all trailing dots from the name will be removed. sl@0: @param anEntry on return will contain the entry data sl@0: sl@0: @leave KErrPathNotFound if there is no path to the aName sl@0: KErrNotFound if the entry corresponding to the aName is not found sl@0: system-wide erorr code of media read failure. sl@0: */ sl@0: void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const sl@0: { sl@0: __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: sl@0: TEntryPos entryPos(RootIndicator(),0); sl@0: TFatDirEntry entry; sl@0: TPtr fileName(anEntry.iName.Des()); sl@0: sl@0: TPtrC fullName = RemoveTrailingDots(aName); sl@0: TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: TLeafDirData leafDir; sl@0: entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir); sl@0: entryPos.iPos=0; sl@0: TEntryPos startPos; sl@0: TFatDirEntry startEntry; sl@0: sl@0: DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported, sl@0: startPos,startEntry,entryPos,entry, sl@0: fileName,KErrNotFound, sl@0: NULL, sl@0: leafDir); sl@0: sl@0: sl@0: anEntry.iAtt=entry.Attributes(); sl@0: anEntry.iSize=entry.Size(); sl@0: anEntry.iModified=entry.Time(TimeOffset()); sl@0: sl@0: if (fileName.Length()==0) sl@0: { sl@0: TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name())); sl@0: LocaleUtils::ConvertToUnicodeL(fileName,dosName); sl@0: } sl@0: if ((TUint)anEntry.iSize>=sizeof(TCheckedUid)) sl@0: ReadUidL(StartCluster(entry),anEntry); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Set directory entry details. sl@0: @param aName entry name; all trailing dots from the name will be removed sl@0: @param aTime entry modification time (and last access as well) sl@0: @param aSetAttMask entry attributes OR mask sl@0: @param aClearAttMask entry attributes AND mask sl@0: sl@0: */ sl@0: void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask) sl@0: { sl@0: __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: CheckWritableL(); sl@0: sl@0: TEntryPos firstEntryPos(RootIndicator(),0); sl@0: TFatDirEntry firstEntry; sl@0: FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos); sl@0: MoveToDosEntryL(firstEntryPos,firstEntry); sl@0: TUint setAttMask=aSetAttMask&KEntryAttMaskSupported; sl@0: if (setAttMask|aClearAttMask) sl@0: { sl@0: TInt att=firstEntry.Attributes(); sl@0: att|=setAttMask; sl@0: att&=(~aClearAttMask); sl@0: firstEntry.SetAttributes(att); sl@0: } sl@0: if (aSetAttMask&KEntryAttModified) sl@0: { sl@0: firstEntry.SetTime(aTime,TimeOffset()); sl@0: } sl@0: WriteDirEntryL(firstEntryPos,firstEntry); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const sl@0: // sl@0: // Check one fat cluster for loops. sl@0: // sl@0: { sl@0: sl@0: if (aCluster==aPreviousCluster) sl@0: User::Leave(KErrCorrupt); // Found loop sl@0: sl@0: aCount++; sl@0: if (aCount==aChangePreviousCluster) sl@0: { sl@0: aCount=0; sl@0: aChangePreviousCluster<<=1; sl@0: aPreviousCluster=aCluster; sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const sl@0: // sl@0: // Check for loops sl@0: // sl@0: { sl@0: sl@0: TInt cluster=StartCluster(anEntry); sl@0: if (cluster==0 && anEntry.Size()==0) sl@0: return; sl@0: sl@0: TInt previousCluster=cluster; sl@0: TInt changePreviousCluster=1; sl@0: TInt count=0; sl@0: sl@0: sl@0: for(;;) sl@0: { sl@0: if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber())) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: if(!FAT().GetNextClusterL(cluster)) sl@0: break; sl@0: sl@0: DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count); sl@0: } sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Open/Create/Replace a file on the current mount. sl@0: sl@0: @param aName file name; all trailing dots from the name will be removed sl@0: @param aMode File open mode, See TFileMode sl@0: @param anOpen specifies action: open, create or replace the file sl@0: @param aFile pointer to the CFileCB object to populate sl@0: sl@0: */ sl@0: void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile) sl@0: { sl@0: __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: sl@0: TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name sl@0: sl@0: TFatDirEntry firstEntry; sl@0: TEntryPos firstEntryPos(RootIndicator(),0); sl@0: TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: TPtrC name(fullName.Mid(nPos)); sl@0: TInt ret = KErrNone; sl@0: sl@0: iFileCreationHelper.Close(); sl@0: if (anOpen == EFileCreate || anOpen == EFileReplace) sl@0: { sl@0: iFileCreationHelper.InitialiseL(name); sl@0: TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper)); sl@0: } sl@0: else sl@0: { sl@0: TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos)); sl@0: } sl@0: sl@0: if (ret!=KErrNone && ret!=KErrNotFound) sl@0: User::Leave(ret); sl@0: sl@0: if (ret==KErrNone) sl@0: { sl@0: MoveToDosEntryL(firstEntryPos,firstEntry); sl@0: if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume)) sl@0: User::Leave(KErrAccessDenied); sl@0: if (anOpen==EFileCreate) sl@0: User::Leave(KErrAlreadyExists); sl@0: if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite) sl@0: User::Leave(KErrAccessDenied); sl@0: if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0)) sl@0: User::Leave(KErrInUse); sl@0: CheckFatForLoopsL(firstEntry); sl@0: } sl@0: else sl@0: { sl@0: if (anOpen==EFileOpen) sl@0: User::Leave(KErrNotFound); sl@0: sl@0: //-- here we try to either create or replace file sl@0: CheckWritableL(); sl@0: sl@0: TLeafDirData leafDir; sl@0: sl@0: TInt numEntries = iFileCreationHelper.NumOfAddingEntries(); sl@0: TShortName shortName; sl@0: if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound) sl@0: { sl@0: firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir); sl@0: GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue); sl@0: } sl@0: sl@0: if (iFileCreationHelper.IsNewEntryPosFound()) sl@0: { sl@0: firstEntryPos = iFileCreationHelper.EntryAddingPos(); sl@0: } sl@0: else sl@0: { sl@0: firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir); sl@0: firstEntryPos.iPos=0; sl@0: } sl@0: sl@0: AddDirEntryL(firstEntryPos,numEntries); sl@0: firstEntry.InitZ(); sl@0: firstEntry.SetName(shortName); sl@0: firstEntry.SetStartCluster(0); sl@0: sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: firstEntry.SetCreateTime(now, TimeOffset() ); sl@0: sl@0: if (iFileCreationHelper.IsTrgNameLegalDosName()) sl@0: WriteDirEntryL(firstEntryPos,firstEntry); sl@0: else sl@0: WriteDirEntryL(firstEntryPos,firstEntry,name); sl@0: } sl@0: sl@0: CFatFileCB& file=(*((CFatFileCB*)aFile)); sl@0: file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos); sl@0: if (anOpen==EFileReplace && file.Size()) sl@0: { sl@0: file.SetSizeL(0); sl@0: file.SetSize(0); sl@0: } sl@0: if (file.IsSeekIndex()==EFalse) sl@0: file.CreateSeekIndex(); sl@0: if (anOpen==EFileReplace || anOpen==EFileCreate) sl@0: file.SetArchiveAttribute(); sl@0: sl@0: if(!IsRuggedFSys()) sl@0: FAT().FlushL(); sl@0: sl@0: iFileCreationHelper.Close(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: /** sl@0: Open a directory on the current mount. sl@0: sl@0: @param aName path to the object in the directory we want to open; all trailing dots from the name will be removed sl@0: @param aDir dir. CB to be filled in. sl@0: sl@0: If there is no such a path, this method must leave with KErrPathNotFound sl@0: sl@0: @leave KErrPathNotFound if thereis no such path sl@0: @leave error code on media read fault sl@0: */ sl@0: void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir) sl@0: { sl@0: __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: sl@0: const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name sl@0: sl@0: TInt namePos=dirName.LocateReverse(KPathDelimiter); sl@0: sl@0: TFatDirEntry dosEntry; sl@0: TEntryPos dosEntryPos(RootIndicator(),0); sl@0: if (namePos==0) sl@0: InitializeRootEntry(dosEntry); sl@0: else sl@0: { sl@0: TPtrC dirPath=dirName.Left(namePos); sl@0: TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1; sl@0: TLeafDirData leafDir; sl@0: dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf sl@0: dosEntryPos.iPos=0; sl@0: sl@0: TFileName fileName; sl@0: TEntryPos startPos; sl@0: TFatDirEntry startEntry; sl@0: DoFindL(dirPath.Mid(dirPos), sl@0: KEntryAttMatchMask|KEntryAttMatchExclusive, sl@0: startPos, startEntry, dosEntryPos, dosEntry, sl@0: fileName, KErrPathNotFound, sl@0: NULL, sl@0: leafDir); sl@0: sl@0: sl@0: } sl@0: sl@0: TPtrC matchName(dirName.Mid(namePos+1)); sl@0: if (matchName.Length()==0) sl@0: matchName.Set(_L("*")); sl@0: sl@0: ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName); sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster) sl@0: // sl@0: // Check aCluster contains no directory entries other than . and .. sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::IsDirectoryEmptyL")); sl@0: TEntryPos dirEntryPos(aCluster,0); sl@0: TFatDirEntry dirEntry; sl@0: FOREVER sl@0: { sl@0: ReadDirEntryL(dirEntryPos,dirEntry); sl@0: MoveToDosEntryL(dirEntryPos,dirEntry); sl@0: if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory()) sl@0: goto LoopEnd; sl@0: if (dirEntry.IsEndOfDirectory()) sl@0: return ETrue; sl@0: if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd())) sl@0: return ETrue; // Root Directory has no end of directory marker sl@0: if (!dirEntry.IsErased()) sl@0: return EFalse; sl@0: LoopEnd: sl@0: MoveToNextEntryL(dirEntryPos); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Overwrite as many contiguous file clusters as possible. sl@0: */ sl@0: void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster) sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::DoWriteToClusterListL")); sl@0: __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt)); sl@0: sl@0: TInt endCluster=0; sl@0: sl@0: const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos); sl@0: const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1; sl@0: const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters); sl@0: const TInt writeLength=Min(aLength,(clusterListLen<WriteL(dataStart,writeLength,aSrc,aMessage,anOffset)); sl@0: sl@0: if(r == KErrNone) // Write succeded sl@0: { sl@0: aPos.iPos+=writeLength; sl@0: aPos.iCluster=endCluster; sl@0: return; sl@0: } sl@0: sl@0: if(r != KErrCorrupt) // failure not due to corruption so propogate up sl@0: User::Leave(r); sl@0: sl@0: TErrorInfoBuf errinf; sl@0: r = iRawDisk->GetLastErrorInfo(errinf); sl@0: sl@0: if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector sl@0: { sl@0: sl@0: const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster; sl@0: TInt goodcluster = FAT().AllocateSingleClusterL(badcluster); sl@0: sl@0: //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster. sl@0: TInt cluster = aPos.iCluster; sl@0: if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<> ClusterSizeLog2()) << ClusterSizeLog2()); sl@0: dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead)); sl@0: sl@0: sl@0: //-- Allocate the buffer required to copy the contents from bad cluster sl@0: RBuf8 clustBuf; sl@0: CleanupClosePushL(clustBuf); sl@0: if(clustBuf.CreateMax(sizeToRead) != KErrNone) sl@0: { sl@0: FAT().FreeClusterListL(goodcluster); sl@0: User::Leave(KErrNoMemory); sl@0: } sl@0: sl@0: r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer sl@0: if(r != KErrNone) //If read fails dont do anything not even marking bad cluster. sl@0: { sl@0: FAT().FreeClusterListL(goodcluster); sl@0: User::Leave(r); sl@0: } sl@0: sl@0: //Copy the bad and good cluster,required to adjust the start cluster number. sl@0: if(aBadcluster == 0) sl@0: aBadcluster = badcluster; sl@0: sl@0: aGoodcluster = goodcluster; sl@0: sl@0: FOREVER sl@0: { sl@0: //Calculate and copy the contents to new cluster. sl@0: aPos.iCluster = goodcluster; sl@0: dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead); sl@0: sl@0: r = LocalDrive()->Write(dataStart, clustBuf); sl@0: if(r == KErrNone) sl@0: { // Copied contents to new cluster so fix up the chain and mark the cluster as bad. sl@0: FAT().WriteL(goodcluster, FAT().ReadL(badcluster)); sl@0: FAT().MarkAsBadClusterL(badcluster); sl@0: aGoodcluster = goodcluster; sl@0: CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer sl@0: return; sl@0: } sl@0: else if(r == KErrCorrupt) sl@0: { sl@0: r = LocalDrive()->GetLastErrorInfo(errinf); sl@0: if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) sl@0: { //Allocate new cluster and adjust the cluster list. sl@0: goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster); sl@0: FAT().MarkAsBadClusterL(aPos.iCluster); sl@0: continue; sl@0: } sl@0: r = KErrCorrupt; sl@0: } sl@0: //Not able to write successfully so dont alter the original list. sl@0: aBadcluster = aGoodcluster = 0; sl@0: FAT().FreeClusterListL(goodcluster); sl@0: User::Leave(r); sl@0: } sl@0: sl@0: }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster)) sl@0: sl@0: if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist sl@0: { sl@0: // return bad and good clusters for CFatFileCB to fix up sl@0: FAT().WriteL(goodcluster, FAT().ReadL(badcluster)); sl@0: aBadcluster = badcluster; sl@0: aGoodcluster = goodcluster; sl@0: aPos.iCluster = goodcluster; sl@0: } sl@0: else //fix up chain sl@0: { sl@0: FAT().WriteL(goodcluster, FAT().ReadL(badcluster)); sl@0: if(badcluster > aPos.iCluster) //bad cluster not first in this contiguous list sl@0: FAT().WriteL(badcluster-1, goodcluster); sl@0: else //first cluster of this contigous list bad so update last cluster of previous contiguous list sl@0: FAT().WriteL(aLastcluster, goodcluster); sl@0: } sl@0: sl@0: FAT().MarkAsBadClusterL(badcluster); sl@0: sl@0: sl@0: return; sl@0: } sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster) sl@0: // sl@0: // Overwrite cluster list. sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::WriteToClusterListL")); sl@0: __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt)); sl@0: sl@0: const TUint startPos=aPos.iPos; sl@0: const TUint temp=startPos>>ClusterSizeLog2(); sl@0: const TUint length = (TUint)aLength; sl@0: sl@0: if ( (startPos) && ((startPos)==(temp<startPos,User::Leave(KErrCorrupt)); sl@0: previouscluster=aPos.iCluster; sl@0: if (offset=length) sl@0: return; sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const sl@0: // sl@0: // Read from as many contiguous file clusters as possible sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::DoReadFromClusterListL")); sl@0: sl@0: TInt endCluster=0; sl@0: sl@0: const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos); sl@0: const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1; sl@0: const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters); sl@0: const TInt readLength=Min(aLength,(clusterListLen<ReadL(dataStart,readLength,aTrg,aMessage,anOffset)); sl@0: sl@0: if(r == KErrNone) // Read succeded sl@0: { sl@0: aPos.iPos+=readLength; sl@0: aPos.iCluster=endCluster; sl@0: return; sl@0: } sl@0: if(r != KErrCorrupt) // failure not due to corruption so propogate up sl@0: User::Leave(r); sl@0: sl@0: TErrorInfoBuf errinf; sl@0: r = iRawDisk->GetLastErrorInfo(errinf); sl@0: sl@0: if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector sl@0: { sl@0: TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster; sl@0: FAT().MarkAsBadClusterL(badcluster); sl@0: } sl@0: sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const sl@0: // sl@0: // Read from cluster list sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::ReadFromClusterListL")); sl@0: __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt)); sl@0: sl@0: const TInt startPos=aPos.iPos; sl@0: const TInt temp=startPos>>ClusterSizeLog2(); sl@0: sl@0: if ( (startPos) && ((startPos)==(temp<=aLength) sl@0: return; sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const sl@0: // sl@0: // Navigate the path to find the leaf directory. sl@0: // Returns the startcluster of data for the directory found. sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::FindLeafDirL")); sl@0: sl@0: TLex lex(aName); sl@0: TInt r; sl@0: TEntryPos entryPos(RootIndicator(),0); sl@0: sl@0: if (iLeafDirCache == NULL) sl@0: { sl@0: TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0; sl@0: TInt namelen=aName.Length(); sl@0: if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen)) sl@0: { sl@0: if (leaflen==namelen) sl@0: return(iLastLeafDirCluster); sl@0: lex.Inc(leaflen-1); sl@0: entryPos.iCluster=iLastLeafDirCluster; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Skip root directory sl@0: if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1) sl@0: { sl@0: TInt err = iLeafDirCache->FindInCache(aName, aLeafDir); sl@0: if (err == KErrNone) sl@0: { sl@0: ASSERT(aLeafDir.iClusterNum > 0); sl@0: return aLeafDir.iClusterNum; sl@0: } sl@0: else if (err != KErrNotFound) sl@0: { sl@0: User::LeaveIfError(err); sl@0: } sl@0: } sl@0: } sl@0: sl@0: FOREVER sl@0: { sl@0: lex.Inc(); // Skip path delimiter sl@0: lex.Mark(); sl@0: r=lex.Remainder().Locate(KPathDelimiter); sl@0: if (r==KErrNotFound) sl@0: r=lex.Remainder().Length(); sl@0: if (r==0) // End of the path sl@0: break; sl@0: lex.Inc(r); // Set the token length sl@0: TFatDirEntry entry; sl@0: sl@0: TFileName fileName; sl@0: TEntryPos startPos; sl@0: TFatDirEntry startEntry; sl@0: DoFindL(lex.MarkedToken(), sl@0: KEntryAttMatchMask|KEntryAttMatchExclusive, sl@0: startPos, startEntry, entryPos, entry, sl@0: fileName, KErrPathNotFound, sl@0: NULL, sl@0: aLeafDir); sl@0: sl@0: sl@0: entryPos.iCluster=StartCluster(entry); sl@0: entryPos.iPos=0; sl@0: } sl@0: sl@0: if (iLeafDirCache == NULL) sl@0: { sl@0: AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName); sl@0: ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster; sl@0: } sl@0: else sl@0: { sl@0: if (aName.Length() > 1) sl@0: { sl@0: aLeafDir = TLeafDirData(entryPos.iCluster); sl@0: iLeafDirCache->AddToCacheL(aName, aLeafDir); sl@0: } sl@0: } sl@0: sl@0: return entryPos.iCluster; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Search for a specified name winthin directory cache sl@0: Works similary to TBool CFatMountCB::DoFindL() sl@0: sl@0: @param anAtt attributes of the object to find sl@0: @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry sl@0: @param aStartEntry on return will contain first VFAT dir entry sl@0: @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: @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case) sl@0: @param aFileName in the case of VFAT entry and on success here will be returned a long filename sl@0: @param aAuxParam some parameters package sl@0: @param aFileCreationHelper a helper package for file creations sl@0: sl@0: @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: */ sl@0: TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos, sl@0: TFatDirEntry& aStartEntry, TEntryPos& aDosEntryPos, sl@0: TFatDirEntry& aDosEntry, TDes& aFileName, sl@0: const TFindHelper& aAuxParam, sl@0: XFileCreationHelper* aFileCreationHelper, sl@0: const TLeafDirData& aLeafDir) const sl@0: { sl@0: TBool bCacheMatchFound = EFalse; sl@0: sl@0: //-- get an interface to the Dir. cache sl@0: MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface(); sl@0: ASSERT(pDirCache); sl@0: if(!pDirCache) sl@0: return EFalse; sl@0: sl@0: //-- save original values in order to restore them in the case of negative search results sl@0: TEntryPos StartEntryPos1(aStartEntryPos); sl@0: TEntryPos DosEntryPos1(aDosEntryPos); sl@0: TFatDirEntry StartEntry1(aStartEntry); sl@0: TFatDirEntry DosEntry1(aDosEntry); sl@0: sl@0: TInt64 nCachedLinPos; sl@0: sl@0: const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size sl@0: const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes sl@0: const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2; //-- maximal number of dir entries that can be in the cache sl@0: sl@0: const TUint pageSzLog2 = pDirCache->PageSizeInBytesLog2(); sl@0: TBool ScanMRUPageFirst = EFalse; sl@0: TBool MRUPageScanned = EFalse; sl@0: sl@0: // if MRU pos is availale, start with MRU page sl@0: if (aLeafDir.iMRUPos.Cluster()) sl@0: { sl@0: ScanMRUPageFirst = ETrue; sl@0: DosEntryPos1 = aLeafDir.iMRUPos; sl@0: } sl@0: sl@0: TInt numFound = 0; sl@0: TEntryPos startPos = DosEntryPos1; sl@0: TInt clusterNum = DosEntryPos1.iCluster; sl@0: sl@0: for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt) sl@0: {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries sl@0: //-- that can be cached. Helps to avoid problems with infinite (looped) directories sl@0: sl@0: if (IsEndOfClusterCh(DosEntryPos1.iCluster)) sl@0: { sl@0: // refer back to the last stored cluster position sl@0: // note aFileCreationHelper may not be initialised for file opening operations sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster) sl@0: { sl@0: TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry); sl@0: aFileCreationHelper->SetEntryAddingPos(dummyPos); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: sl@0: if (ScanMRUPageFirst && !MRUPageScanned) sl@0: { sl@0: DosEntryPos1 = aDosEntryPos; sl@0: MRUPageScanned = ETrue; sl@0: continue; sl@0: } sl@0: break; //-- this was the last cluster in this directory sl@0: } sl@0: sl@0: const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2); sl@0: DosEntryPos1.iPos = pageStartPos; sl@0: TBool PassedPageBoundary = EFalse; sl@0: sl@0: const TInt64 entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory sl@0: const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached sl@0: if(cachePageSz) sl@0: {//-- current page is in the directory cache sl@0: //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos); sl@0: sl@0: //-- search to the end of the cached page. sl@0: // Note GetDirEntry() will read data beyond cache page boundary sl@0: const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2; sl@0: sl@0: TInt nErr; sl@0: //-- extract dir entries from the cached page and see if they match given name (aName) sl@0: /// until it reaches the next page sl@0: for(;;) sl@0: { sl@0: StartEntryPos1 = DosEntryPos1; sl@0: TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it sl@0: sl@0: //-- get directory entry from the cache. We know that the DosEntryPos1 is cached. sl@0: nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName); sl@0: if(nErr != KErrNone) sl@0: break; sl@0: sl@0: if(DosEntry1.IsEndOfDirectory()) sl@0: { sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound()) sl@0: { sl@0: // note it is impossible to be at the end of the cluster chain here. sl@0: aFileCreationHelper->SetEntryAddingPos(DosEntryPos1); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: sl@0: if (ScanMRUPageFirst && !MRUPageScanned) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: // if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned) sl@0: goto Exit; //-- this was the last entry in this directory, no reason to look further sl@0: } sl@0: sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound()) sl@0: { sl@0: if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage()) sl@0: { sl@0: numFound = 0; sl@0: } sl@0: else sl@0: { sl@0: if (numFound == 0) sl@0: { sl@0: startPos = DosEntryPos1; sl@0: } sl@0: numFound++; sl@0: if (numFound == aFileCreationHelper->NumOfAddingEntries()) sl@0: { sl@0: aFileCreationHelper->SetEntryAddingPos(startPos); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: } sl@0: } sl@0: if(MatchEntryAtt(DosEntry1.Attributes(),anAtt)) sl@0: {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then. sl@0: sl@0: if(StartEntry1.IsVFatEntry()) sl@0: {//-- extracted entry is VFAT one, name can be in UNICODE sl@0: sl@0: // we only check short name candidates for long file names with VFAT entries, sl@0: // if it is a valid dos name, it will be checked by default sl@0: // note here target name is always fully specified sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised()) sl@0: { sl@0: aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr()); sl@0: } sl@0: sl@0: TPtrC ptrAssembledName = RemoveTrailingDots(aFileName); sl@0: sl@0: if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound) sl@0: {//-- found match in cache sl@0: bCacheMatchFound = ETrue; sl@0: goto Exit; sl@0: } sl@0: else if(aAuxParam.TrgtNameIsLegalDos()) sl@0: { sl@0: if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr())) sl@0: { sl@0: bCacheMatchFound = ETrue; sl@0: goto Exit; sl@0: } sl@0: } sl@0: }//if(StartEntry1.IsVFatEntry()) sl@0: else if(aAuxParam.TrgtNameIsLegalDos()) sl@0: {//-- this is an old DOS FAT entry sl@0: sl@0: if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr())) sl@0: { sl@0: //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves sl@0: //-- 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: //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached. sl@0: //-- 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: //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare. sl@0: if(StartEntryPos1.iPos == 0) sl@0: {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above. sl@0: __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!")); sl@0: goto Exit; sl@0: } sl@0: sl@0: bCacheMatchFound = ETrue; sl@0: goto Exit; sl@0: } sl@0: } sl@0: sl@0: }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt)) sl@0: sl@0: // check boundaries after GetDirEntry() sl@0: // if we have cross the cluster boundary, break the for loop sl@0: if(DosEntryPos1.iCluster != clSave) sl@0: {//-- GetDirEntry() has decided to move to the next cluster. sl@0: DosEntryPos1.iCluster = clSave; sl@0: break; sl@0: } sl@0: sl@0: // if we are still in the same cluster, check the page boundary by sl@0: /// exam how many entries we have scanned within the cluster sl@0: const TUint entriesLooked = ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2; sl@0: if(entriesLooked > nEntries) sl@0: { sl@0: PassedPageBoundary = ETrue; sl@0: break; sl@0: } sl@0: sl@0: sl@0: // move to next entry before scanning next file sl@0: TRAP(nErr,MoveToNextEntryL(DosEntryPos1)); sl@0: if(nErr != KErrNone) sl@0: goto Exit; sl@0: sl@0: // check boundaries after MoveToNextEntryL() sl@0: if(DosEntryPos1.iCluster != clSave) sl@0: { sl@0: DosEntryPos1.iCluster = clSave; sl@0: break; sl@0: } sl@0: sl@0: if (entriesLooked + 1 > nEntries) sl@0: { sl@0: PassedPageBoundary = ETrue; sl@0: break; sl@0: } sl@0: sl@0: } //for(;;) sl@0: sl@0: } //if(iRawDisk->PosCached(...)) sl@0: sl@0: // scanning did not happen because the page is not cached, sl@0: // or sl@0: // scanning finished in last page and file is not found sl@0: sl@0: // if MRU page is not cached or sl@0: // we scan MRU page first and it is not scanned yet, then this must be the MRU page, sl@0: // we now start to scan from the beginning sl@0: if (ScanMRUPageFirst && !MRUPageScanned) sl@0: { sl@0: MRUPageScanned = ETrue; sl@0: DosEntryPos1 = aDosEntryPos; sl@0: DosEntryPos1.iPos = 0; sl@0: continue; sl@0: } sl@0: sl@0: // if we just finished scanning a page and still in the same cluster, then we crossed page sl@0: // boundary, continue with next page. sl@0: // note: although we are in the 'next page' already, this page might not be cached, so we need to sl@0: // check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly. sl@0: if (PassedPageBoundary) sl@0: { sl@0: DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2); sl@0: PassedPageBoundary = EFalse; sl@0: continue; sl@0: } sl@0: sl@0: //-- try to move to the next cluster of the directory file sl@0: sl@0: if(DosEntryPos1.Cluster() < KFatFirstSearchCluster) //-- small trick to get rid of TRAPping GetNextClusterL() sl@0: break; sl@0: sl@0: // record previous cluster number before move on sl@0: clusterNum = DosEntryPos1.iCluster; sl@0: sl@0: if(! FAT().GetNextClusterL(DosEntryPos1.iCluster)) sl@0: break; sl@0: sl@0: sl@0: } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt) sl@0: sl@0: //--------------------------------- sl@0: Exit: sl@0: sl@0: if(bCacheMatchFound) sl@0: { sl@0: //-- if the position of the found in cache object is less than given, pretend that we haven't found anything sl@0: //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to sl@0: //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir. sl@0: TBool bFallBack=EFalse; sl@0: sl@0: if(DosEntryPos1.iCluster == aDosEntryPos.iCluster) sl@0: { sl@0: if(DosEntryPos1.iPos < aDosEntryPos.iPos) sl@0: bFallBack = ETrue; sl@0: } sl@0: else sl@0: { sl@0: if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos)) sl@0: bFallBack = ETrue; sl@0: } sl@0: sl@0: if(bFallBack) sl@0: { sl@0: return EFalse; sl@0: } sl@0: sl@0: //-- Update parameters with new values sl@0: aStartEntryPos= StartEntryPos1; sl@0: aDosEntryPos = DosEntryPos1; sl@0: aStartEntry = StartEntry1; sl@0: aDosEntry = DosEntry1; sl@0: sl@0: const TInt64 mruPos = MakeLinAddrL(aDosEntryPos); sl@0: sl@0: pDirCache->MakePageMRU(mruPos); sl@0: sl@0: // only update the leaf dir cache when the original cache index is provided sl@0: if (aLeafDir.iClusterNum) sl@0: { sl@0: iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos)); sl@0: } sl@0: } sl@0: return bCacheMatchFound; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: initialise find helper with the target file name. sl@0: 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: and also have the corresponding generated DOS name for it. sl@0: sl@0: @param aTargetName target file name we are looking for in ::DoFindL() sl@0: */ sl@0: void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName) sl@0: { sl@0: if(isInitialised) sl@0: return; sl@0: sl@0: TInt count = 1; sl@0: sl@0: iTargetName.Set(aTargetName); sl@0: isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse); sl@0: sl@0: if(isLegalDosName) sl@0: {//-- iShortName will contain generated short DOS name by long filename sl@0: iShortName = DoGenerateShortNameL(aTargetName, count, ETrue); sl@0: } sl@0: sl@0: isInitialised = ETrue; sl@0: } sl@0: sl@0: /** sl@0: Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise(). sl@0: @param apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format sl@0: @return ETrue if the apDosEntryName is the same as generated iShortName sl@0: */ sl@0: TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const sl@0: { sl@0: ASSERT(isInitialised); sl@0: sl@0: if(!isLegalDosName) sl@0: return EFalse; sl@0: sl@0: return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: const TInt KShortNameCandidatesNum = 4; sl@0: /** sl@0: Constructor of XFileCreationHelper class sl@0: */ sl@0: CFatMountCB::XFileCreationHelper::XFileCreationHelper() sl@0: { sl@0: isInitialised = EFalse; sl@0: } sl@0: sl@0: /** sl@0: Destructor of XFileCreationHelper class sl@0: */ sl@0: CFatMountCB::XFileCreationHelper::~XFileCreationHelper() sl@0: { sl@0: Close(); sl@0: } sl@0: sl@0: /** sl@0: Initialises a TFileCreationHelper object, generate a short name candidate pool. sl@0: sl@0: @param aTargetName Target file name for the potential new file. sl@0: @post TFileCreationHelper is fully initialised. sl@0: */ sl@0: void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName) sl@0: { sl@0: // close before use, to avoid memory leak sl@0: Close(); sl@0: sl@0: iTargetName.Set(aTargetName); sl@0: // generates short name candidate(s) sl@0: TInt count = 1; sl@0: while (count <= KShortNameCandidatesNum) sl@0: { sl@0: TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue); sl@0: TInt err = iShortNameCandidates.Append(shortNameCandidate); sl@0: User::LeaveIfError(err); sl@0: sl@0: if (count == -1) // No tilde and number is needed sl@0: { sl@0: break; sl@0: } sl@0: else sl@0: count++; sl@0: } sl@0: sl@0: // calculate number of new entries needed sl@0: iNumOfAddingEntries = 1; sl@0: isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue); sl@0: if (!isTrgNameLegalDosName) sl@0: iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length()); sl@0: sl@0: isNewEntryPosFound = EFalse; sl@0: isInitialised = ETrue; sl@0: } sl@0: sl@0: /** sl@0: Close function of XFileCreationHelper class sl@0: */ sl@0: void CFatMountCB::XFileCreationHelper::Close() sl@0: { sl@0: iShortNameCandidates.Close(); sl@0: isInitialised = EFalse; sl@0: } sl@0: sl@0: /** sl@0: Validates short name candidates. If the input dos entry name is found in the short name sl@0: candidate pool, the corresponding short name candidate will be removed from the pool. sl@0: sl@0: @param apDosEntryName An existing short name, to compare with the candidates. sl@0: @pre Object should be initialised sl@0: */ sl@0: void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName) sl@0: { sl@0: ASSERT(isInitialised); sl@0: if (!isInitialised) sl@0: return; sl@0: sl@0: if (iShortNameCandidates.Count() > 0) sl@0: { sl@0: for (TInt i = 0; i < iShortNameCandidates.Count(); i++) sl@0: { sl@0: if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0) sl@0: { sl@0: iShortNameCandidates.Remove(i); sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Gets a validated short name from the short name candidate pool. sl@0: sl@0: @param aShortName On return, contains a validated short name if found, otherwise zeroed. sl@0: @return TInt Returns KErrNone if a validated short name found successfully, sl@0: else KErrNotFound is returned. sl@0: Returns KErrNotReady if object is not initialised. sl@0: @pre Object should be initialised sl@0: */ sl@0: TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const sl@0: { sl@0: aShortName.Zero(); sl@0: sl@0: ASSERT(isInitialised); sl@0: if (!isInitialised) sl@0: return KErrNotReady; sl@0: sl@0: if (iShortNameCandidates.Count() > 0) sl@0: { sl@0: aShortName.Copy(iShortNameCandidates[0]); sl@0: return KErrNone; sl@0: } sl@0: sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: /** sl@0: Scan a directory looking for aName. sl@0: sl@0: @param aTrgtName a name of an object we are looking up in directory sl@0: @param anAtt attributes of this object sl@0: @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry sl@0: @param aStartEntry on return will contain first VFAT dir entry sl@0: @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: @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case) sl@0: @param aFileName in the case of VFAT entry and on success here will be returned a long filename sl@0: @param anError This function might leave with this given error code sl@0: @param aFileCreationHelper a helper package for file creations sl@0: sl@0: @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one sl@0: @leave can leave with anError code on error or if the search has reached the end of directory (!) sl@0: */ sl@0: TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt, sl@0: TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry, sl@0: TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry, sl@0: TDes& aFileName,TInt anError, sl@0: XFileCreationHelper* aFileCreationHelper, sl@0: const TLeafDirData& aLeafDirData) const sl@0: { sl@0: // check that the entry position to be read next is not past the end of the sl@0: // root directory. If this is the case then when GetDirEntryL(..) is called sl@0: // this will lead to MakeLinAddr(..) leaving with KErrDirFull. sl@0: sl@0: if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())) sl@0: User::Leave(anError);//Allows maximum number of entries in root directory sl@0: sl@0: __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName); sl@0: sl@0: TInt previousCluster=aDosEntryPos.iCluster; sl@0: TUint previousPosition=aDosEntryPos.iPos; sl@0: TInt changePreviousCluster=1; sl@0: TInt count=0; sl@0: sl@0: TBool trgNameIsWildCard = EFalse; //-- ETrue if the name we are looking for is a wildcard sl@0: TBool trgNameFullySpecified = ETrue; //-- ETrue if the name we are looking for doesn't contain wildcards sl@0: sl@0: sl@0: { sl@0: //-- find out if the name we are looking for is a wildcard ("*" or "*.*") sl@0: const TInt len = aTrgtName.Length(); sl@0: sl@0: if(len == 1) sl@0: trgNameIsWildCard = (aTrgtName[0] == '*'); sl@0: else if(len == 3) sl@0: { sl@0: _LIT(KAllFiles, "*.*"); sl@0: trgNameIsWildCard = (aTrgtName==KAllFiles); sl@0: } sl@0: sl@0: //-- find out if the name we are looking for contains wildcharacters: "*" or "?" sl@0: if(trgNameIsWildCard) sl@0: trgNameFullySpecified = EFalse; sl@0: else sl@0: { sl@0: for(TInt i=0; iDirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper) sl@0: {//-- aName is fully specified, i.e doesn't contain wildcards sl@0: sl@0: findHelper.InitialiseL(trgtNameNoDot); sl@0: sl@0: const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData); sl@0: if(bMatchFound) sl@0: { sl@0: return(aStartEntry.IsVFatEntry()); sl@0: } sl@0: } sl@0: //--------------------------------------------------- sl@0: sl@0: // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided sl@0: TBool scanAhead = EFalse; sl@0: // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&& sl@0: // we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&& sl@0: // if we don't have a starting cluster number, we draw back to original scanning algorithm sl@0: if (!IsRootDir(aDosEntryPos) // we don't do forward scanning for root dir & sl@0: && aLeafDirData.iClusterNum != 0 // if we have a starting cluster number & sl@0: && aLeafDirData.iMRUPos.Cluster() != 0) // if we have a starting cluster number & sl@0: { sl@0: scanAhead = ETrue; sl@0: aDosEntryPos = aLeafDirData.iMRUPos; sl@0: } sl@0: sl@0: TInt numFound = 0; sl@0: TEntryPos startPos = aDosEntryPos; sl@0: TInt clustNum = aDosEntryPos.Cluster(); sl@0: sl@0: for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt) sl@0: { sl@0: // if we are not scanning ahead, we don't need this outer for loop sl@0: if (!scanAhead) sl@0: scanCnt++; sl@0: sl@0: TBool found = EFalse; sl@0: sl@0: FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end sl@0: { sl@0: //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT) sl@0: //-- aDosEntry will contain a DOS entry of the directory entry we have read. sl@0: aStartEntryPos=aDosEntryPos; sl@0: User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName)); sl@0: sl@0: if (aDosEntry.IsEndOfDirectory()) sl@0: {//-- the end of directory reached. sl@0: sl@0: // if new entry position for adding has not been found yet. sl@0: // note aFileCreationHelper may not be initialised for pure file opening operations sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound()) sl@0: { sl@0: // if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain, sl@0: // we pass the last scanned entry position to AddDirEntryL sl@0: if (IsEndOfClusterCh(aDosEntryPos.iCluster)) sl@0: { sl@0: TInt clusterSize=1<SetEntryAddingPos(dummyPos); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: // or we reached the end of the directory. sl@0: else sl@0: { sl@0: aFileCreationHelper->SetEntryAddingPos(aDosEntryPos); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: } sl@0: sl@0: // if we are scanning ahead and this is the first scanning, we break out to restart scanning sl@0: if (scanAhead && scanCnt == 1) sl@0: { sl@0: break; // from FOREVER, restart scanning sl@0: } sl@0: sl@0: // if (!scanAhead || scanAhead && scanCnt == 2) sl@0: User::Leave(anError); sl@0: } sl@0: sl@0: sl@0: // entry space searching for potential new file/directory creation sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound()) sl@0: { sl@0: if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage()) sl@0: { sl@0: numFound = 0; sl@0: } sl@0: else sl@0: { sl@0: if (numFound == 0) sl@0: { sl@0: startPos = aDosEntryPos; sl@0: } sl@0: numFound++; sl@0: if (numFound == aFileCreationHelper->NumOfAddingEntries()) sl@0: { sl@0: aFileCreationHelper->SetEntryAddingPos(startPos); sl@0: aFileCreationHelper->SetIsNewEntryPosFound(ETrue); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry))) sl@0: if (aDosEntry.IsErased()) sl@0: { sl@0: User::Leave(anError);//Allows maximum number of entries in root directory sl@0: } sl@0: sl@0: sl@0: const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage(); sl@0: sl@0: if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt)) sl@0: {//-- we have read a filename entry and entry's attributes match required; compare names then. sl@0: sl@0: if (trgNameIsWildCard) sl@0: { sl@0: found = ETrue; sl@0: break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name. sl@0: } sl@0: sl@0: sl@0: if (aStartEntry.IsVFatEntry()) sl@0: {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name. sl@0: //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries sl@0: sl@0: sl@0: // we only check short name candidates for long file names with VFAT entries, sl@0: // if it is a valid dos name, it will be checked by default sl@0: // note, for file creation cases, target name will be always fully specified sl@0: if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified) sl@0: { sl@0: aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr()); sl@0: } sl@0: sl@0: //-- discard trailing dots from aFileName if present sl@0: TPtrC ptrAssembledName = RemoveTrailingDots(aFileName); sl@0: sl@0: if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound) sl@0: { sl@0: found = ETrue; sl@0: break; //-- OK, found a match. sl@0: } sl@0: else if (trgNameFullySpecified) sl@0: { sl@0: //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified, sl@0: //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK. sl@0: findHelper.InitialiseL(trgtNameNoDot); sl@0: sl@0: if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr())) sl@0: { sl@0: found = ETrue; sl@0: break; //-- DOS entries match, success. sl@0: } sl@0: } sl@0: else if (!trgNameFullySpecified) sl@0: {//-- target name contains wildcards, we need to use MatchF with dos name sl@0: TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name())); sl@0: TBuf<0x20> dosName; sl@0: LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings) sl@0: if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound) sl@0: { sl@0: found = ETrue; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: sl@0: } sl@0: else //if (aStartEntry.IsVFatEntry()) sl@0: {//-- we've read a legacy FAT entry, so compare DOS entries sl@0: findHelper.InitialiseL(trgtNameNoDot); sl@0: sl@0: if(findHelper.TrgtNameIsLegalDos()) sl@0: {//-- we are looking for a legal DOS name sl@0: if(trgNameFullySpecified) sl@0: {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries sl@0: if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr())) sl@0: { sl@0: found = ETrue; sl@0: break; sl@0: } sl@0: } sl@0: else sl@0: {//-- target name contains wildcards, we neeed to use MatchF sl@0: TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name())); sl@0: TBuf<0x20> dosName; sl@0: LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings) sl@0: if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound) sl@0: { sl@0: found = ETrue; sl@0: break; sl@0: } sl@0: sl@0: } sl@0: } //if(findHelper.TrgtNameIsLegalDos()) sl@0: sl@0: } //else if (aStartEntry.IsVFatEntry()) sl@0: sl@0: } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt)) sl@0: sl@0: sl@0: // record previous cluster number sl@0: clustNum = aDosEntryPos.iCluster; sl@0: sl@0: // this is the 2nd scanning and we have just passed the pos we started. sl@0: if (scanAhead && scanCnt == 2) sl@0: { sl@0: if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster() sl@0: && aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos()) sl@0: { sl@0: User::Leave(anError); sl@0: } sl@0: } sl@0: sl@0: sl@0: MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory sl@0: sl@0: if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())) sl@0: { sl@0: User::Leave(anError);//Allows maximum number of entries in root directory sl@0: } sl@0: sl@0: sl@0: if (!scanAhead || scanCnt == 2) sl@0: { sl@0: if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition)) sl@0: DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count); sl@0: sl@0: previousPosition=aDosEntryPos.iPos; sl@0: } sl@0: } // FOREVER -- the actual scanning is done inside this loop sl@0: sl@0: sl@0: if (found) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: // if not found: sl@0: // if we have not found in the first scanning and we are doing scanning ahead, sl@0: // we need to go back to the starting pos of this dir and scan from start until sl@0: // we reach lastscannedPos sl@0: if (scanAhead && scanCnt == 1) sl@0: { sl@0: aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0); sl@0: continue; sl@0: } sl@0: else sl@0: { sl@0: // there are only two exits: either found or reached end of dir in the 1st scanning sl@0: ASSERT(0); sl@0: break; sl@0: } sl@0: } // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt) sl@0: sl@0: //--------------------------------------------------- sl@0: if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster()) sl@0: { sl@0: TInt64 mruPos = MakeLinAddrL(aDosEntryPos); sl@0: iRawDisk->DirCacheInterface()->MakePageMRU(mruPos); sl@0: sl@0: // only update the leaf dir cache when the original cache index is provided sl@0: if (aLeafDirData.iClusterNum) sl@0: { sl@0: iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aDosEntryPos)); sl@0: } sl@0: } sl@0: sl@0: return (aStartEntry.IsVFatEntry()); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: /** sl@0: Locate an directory entry entry from its full path name. sl@0: sl@0: @param aName a name of an object we are looking for sl@0: @param anAtt attributes of this object sl@0: @param anEntry on return will contain first VFAT dir entry sl@0: @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry sl@0: sl@0: @leave can leave with KErrNotFound if the search has reached the end of directory sl@0: */ sl@0: void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const sl@0: { sl@0: __PRINT(_L("CFatMountCB::FindEntryStartL()")); sl@0: TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: TFileName fileName; sl@0: TLeafDirData leafDir; sl@0: TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0); sl@0: TFatDirEntry dosEntry; sl@0: sl@0: DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Locate an directory entry entry from its full path name. sl@0: sl@0: @param aName a name of an object we are looking for sl@0: @param anAtt attributes of this object sl@0: @param anEntry on return will contain first VFAT dir entry sl@0: @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry sl@0: sl@0: @leave can leave with KErrNotFound if the search has reached the end of directory sl@0: */ sl@0: void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const sl@0: { sl@0: __PRINT(_L("CFatMountCB::FindEntryStartL()")); sl@0: TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: TFileName fileName; sl@0: TLeafDirData leafDir; sl@0: TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0); sl@0: TFatDirEntry dosEntry; sl@0: DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const sl@0: // sl@0: // Scan a directory looking for aName. sl@0: // aCluster and anEntryAddr give the location of the entry. sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::FindDosNameL()")); sl@0: TEntryPos startPos; sl@0: TFatDirEntry startEntry; sl@0: sl@0: TLeafDirData leafDir; // leaf dir data is zero initialized, no scannig ahead sl@0: DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir); sl@0: } sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries) sl@0: // sl@0: // Find space for a new directory entry. Leave KErrEof if no space sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::AddDirEntryL")); sl@0: TInt numFound=0; sl@0: TFatDirEntry entry; sl@0: TEntryPos startPos(RootIndicator(),0); sl@0: TInt clusterNum=aPos.iCluster; sl@0: FOREVER sl@0: { sl@0: ReadDirEntryL(aPos,entry); sl@0: if (entry.IsEndOfDirectory()) sl@0: break; sl@0: if (!entry.IsErased() && !entry.IsGarbage()) sl@0: numFound=0; sl@0: else sl@0: { sl@0: if (numFound==0) sl@0: startPos=aPos; sl@0: numFound++; sl@0: if (numFound==aNumOfEntries) sl@0: { sl@0: aPos=startPos; sl@0: return; sl@0: } sl@0: } sl@0: clusterNum=aPos.iCluster; sl@0: MoveToNextEntryL(aPos); sl@0: if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd())) sl@0: // No end of directory marker at end of root directory sl@0: User::Leave(KErrDirFull); sl@0: } sl@0: sl@0: TUint clusterSize=1<RootDirEnd()) sl@0: User::Leave(KErrDirFull); sl@0: else sl@0: return; sl@0: } sl@0: sl@0: if (eofPos.iPos==clusterSize) sl@0: return; // No need to allocate sl@0: if (eofPos.iPos>clusterSize) sl@0: { sl@0: TInt numNeeded=eofPos.iPos>>ClusterSizeLog2(); sl@0: if(IsRuggedFSys()) sl@0: { sl@0: ExtendClusterListZeroedL(numNeeded,eofPos.iCluster); sl@0: } sl@0: else sl@0: { sl@0: FAT().ExtendClusterListL(numNeeded,eofPos.iCluster); sl@0: ZeroDirClusterL(eofPos.iCluster); sl@0: } sl@0: sl@0: eofPos.iPos-=numNeeded<=1); sl@0: sl@0: for(TInt i=0; i vBuf(KMaxVFatEntryName); sl@0: aDosEntry.ReadVFatEntry(vBuf); sl@0: sl@0: TInt vLength=vBuf.Locate('\0'); sl@0: if (vLength==KErrNotFound) sl@0: vLength=KMaxVFatEntryName; sl@0: sl@0: vBuf.SetLength(vLength); sl@0: sl@0: const TInt nameLen = vLength+KMaxVFatEntryName*(count-1); sl@0: if(nameLen <= 0 || nameLen > KMaxFileName) sl@0: return EFalse; //-- wrong long file name length, consider VFAT entry as orphaned sl@0: sl@0: aLongFileName.SetLength(nameLen); sl@0: sl@0: const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry sl@0: sl@0: while (count--) sl@0: { sl@0: TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count); sl@0: fileNamePtr.Copy(vBuf); sl@0: if (count==0) sl@0: break; //-- all VFat entries read, only DOS entry remained sl@0: sl@0: MoveToNextEntryL(aPos); sl@0: ReadDirEntryL(aPos,aDosEntry); sl@0: sl@0: //-- check if it is correct VFat entry. sl@0: //-- if not, this is the "orphaned" entry and will be ignored sl@0: if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count) sl@0: return EFalse; //-- bad VFAT entry sl@0: sl@0: aDosEntry.ReadVFatEntry(vBuf); sl@0: } sl@0: sl@0: if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry))) sl@0: return ETrue;//Allows maximum number of entries in root directory sl@0: sl@0: //-- read the last, DOS FAT entry sl@0: MoveToNextEntryL(aPos); sl@0: ReadDirEntryL(aPos,aDosEntry); sl@0: sl@0: //-- check if it is corect sl@0: if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry()) sl@0: return EFalse; //-- Bad DOS entry sl@0: sl@0: //-- verify ChechSum here if it is incorrect, use DOS name only sl@0: const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name()); sl@0: if(calcNameChkSum != entryCheckSum) sl@0: { sl@0: aLongFileName.SetLength(0);//-- don't use long filename sl@0: __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum); sl@0: } sl@0: sl@0: return ETrue; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Read a number of VFAT entries from the directory file. sl@0: for parameters see DoGetDirEntryL() sl@0: sl@0: @return KErrNone if everything is OK, system wide error code otherwise sl@0: sl@0: */ sl@0: TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const sl@0: { sl@0: sl@0: TBool bEntryOK=ETrue; sl@0: TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName)); sl@0: sl@0: if(nErr !=KErrNone) sl@0: return nErr; sl@0: sl@0: if(!bEntryOK) sl@0: {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged. sl@0: //-- consider it as an "orphaned" entry and skip sl@0: aDosEntry.iData[0] = 0xFF; // Mark entry as garbage sl@0: aLongFileName.SetLength(0); // No long filename sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const sl@0: // sl@0: // If anEntry is at the end of the cluster, and we are not the root dir, sl@0: // move it to the next in the list. sl@0: // sl@0: { sl@0: sl@0: // __PRINT(_L("CFatMountCB::MoveToNextEntryL")); sl@0: if (IsEndOfClusterCh(aPos.iCluster)) sl@0: return; sl@0: const TUint temp = 1<= UsableClusters()+KFatFirstSearchCluster) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: TBuf8 uidBuf; sl@0: iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf); sl@0: __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed)); sl@0: TCheckedUid uid(uidBuf); sl@0: anEntry.iType=uid.UidType(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Read file section without opening this file on a file server side. sl@0: sl@0: @param aName file name; all trailing dots from the name will be removed sl@0: @param aFilePos start read position within a file sl@0: @param aLength how many bytes to read; on return will be how many bytes actually read sl@0: @param aDes local buffer desctriptor sl@0: @param aMessage from file server, used to write data to the buffer in different address space. sl@0: sl@0: @leave on media read error sl@0: */ sl@0: void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage) sl@0: { sl@0: __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName); sl@0: sl@0: CheckStateConsistentL(); sl@0: sl@0: TEntryPos dosEntryPos(RootIndicator(),0); sl@0: TFatDirEntry dosEntry; sl@0: TFileName fileName; sl@0: sl@0: sl@0: TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: TLeafDirData leafDir; sl@0: dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir); sl@0: dosEntryPos.iPos=0; sl@0: TEntryPos startPos; sl@0: TFatDirEntry startEntry; sl@0: DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported, sl@0: startPos,startEntry,dosEntryPos,dosEntry, sl@0: fileName,KErrNotFound, sl@0: NULL, sl@0: leafDir); sl@0: sl@0: // Check that reading from aPos for aLength lies within the file sl@0: // if aPos is within the file, and aLength is too long, read up to EOF sl@0: // If aPos is beyond the end of the file, return a zero length descriptor sl@0: sl@0: TUint32 fileSize = dosEntry.Size(); sl@0: if ((TUint)aPos>=fileSize) sl@0: User::Leave(KErrEof); sl@0: sl@0: if ((TUint)(aPos+aLength)>fileSize) sl@0: aLength=fileSize-aPos; sl@0: sl@0: TInt cluster=StartCluster(dosEntry); sl@0: TInt pos = aPos; sl@0: sl@0: TInt endCluster; sl@0: TInt clusterSize=1<>ClusterSizeLog2()); sl@0: sl@0: // Read data sl@0: FOREVER sl@0: { sl@0: // Get the maximum number of clusters that can be read contiguously sl@0: TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters); sl@0: __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed)); sl@0: sl@0: // If start position within this block, then read some data sl@0: if (pos<(clusterListLen<0,Fault(EReadFileSectionFailed)); sl@0: TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos; sl@0: iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal); sl@0: readTotal += readLength; sl@0: sl@0: if (readTotal == aLength) sl@0: return; sl@0: sl@0: pos += readLength; sl@0: } sl@0: sl@0: // Get the next cluster in file sl@0: pos-=(clusterListLen<FAT().GetNextClusterL(endCluster); sl@0: __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed)); sl@0: cluster=endCluster; sl@0: } sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const sl@0: // sl@0: // Read aLength of data from disk directly to thread relative descriptor sl@0: // sl@0: { sl@0: iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage) sl@0: // sl@0: // Write aLength of data from thread relative descriptor to disk sl@0: // sl@0: { sl@0: CheckWritableL(); sl@0: sl@0: //-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case. sl@0: FAT().RequestRawWriteAccess(aPos, aLength); sl@0: sl@0: iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset); sl@0: //-- Note: FAT directory cache will be invalidated in MountL() sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: /** sl@0: This method must be used when writing to the FAT directory file. sl@0: If FAT directory cache is present on this drive, it will be used. sl@0: @param aPos entry media position sl@0: @param aDes data descriptor sl@0: */ sl@0: void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes) sl@0: { sl@0: CheckWritableL(); sl@0: const TInt64 posAddr=MakeLinAddrL(aPos); sl@0: sl@0: if(!iRawDisk->DirCacheInterface()) sl@0: { sl@0: iRawDisk->WriteCachedL(posAddr,aDes); sl@0: } sl@0: else sl@0: {//-- if there is an interface to the FAT directory cache, use it sl@0: iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: This method must be used when reading from the FAT directory file. sl@0: If FAT directory cache is present on this drive, it will be used. sl@0: sl@0: @param aPos entry media position sl@0: @param aLength how many bytes to read sl@0: @param aDes input data descriptor sl@0: */ sl@0: void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const sl@0: { sl@0: const TInt64 posAddr=MakeLinAddrL(aPos); sl@0: sl@0: if(!iRawDisk->DirCacheInterface()) sl@0: { sl@0: iRawDisk->ReadCachedL(posAddr,aLength,aDes); sl@0: } sl@0: else sl@0: {//-- if there is an interface to the FAT directory cache, use it sl@0: iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry) sl@0: // sl@0: // Write a FAT directory entry to disk. sl@0: // Assumes sufficient space has been created for it by AddDirEntry. sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::WriteDirEntryL")); sl@0: sl@0: //-- use special interface to access FAT directory file sl@0: DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry)); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos) sl@0: // sl@0: // Mark a dir entry as erased sl@0: // sl@0: { sl@0: sl@0: __PRINT(_L("CFatMountCB::EraseDirEntryL")); sl@0: if(!iLeafDirCache && iLastLeafDir) sl@0: iLastLeafDir->Des().SetLength(0); sl@0: sl@0: //-- use special interface to access FAT directory file sl@0: DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8))); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const sl@0: // sl@0: // Read a FAT directory entry to disk sl@0: // sl@0: { sl@0: sl@0: // __PRINT(_L("CFatMountCB::ReadDirEntryL")); sl@0: if (IsEndOfClusterCh(aPos.iCluster)) sl@0: { sl@0: aDirEntry.InitZ(); sl@0: return; sl@0: } sl@0: TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry); sl@0: sl@0: //-- use special interface to access FAT directory file sl@0: DirReadL(aPos,KSizeOfFatDirEntry,buf); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Enlarge the disk's size. sl@0: This method can be called only for variable size media, i.e. RAM drive sl@0: sl@0: @param aSize size increment (bytes) sl@0: */ sl@0: void CFatMountCB::EnlargeL(TInt aSize) sl@0: { sl@0: __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize); sl@0: sl@0: ASSERT(iRamDrive); sl@0: sl@0: TInt maxSize; sl@0: if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize) sl@0: User::Leave(KErrDiskFull); sl@0: User::LeaveIfError(LocalDrive()->Enlarge(aSize)); sl@0: iSize+=aSize; sl@0: sl@0: if (&FAT()) sl@0: { sl@0: FAT().InitializeL(); sl@0: } sl@0: sl@0: if (&RawDisk()) sl@0: { sl@0: RawDisk().InitializeL(); sl@0: } sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength) sl@0: // sl@0: // Reduce the disk's size sl@0: // sl@0: { sl@0: sl@0: __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength); sl@0: User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength)); sl@0: iSize-=aLength; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const sl@0: // sl@0: // Convert cluster/position into linear address sl@0: // sl@0: { sl@0: sl@0: //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos); sl@0: if (!IsRootDir(aPos)) sl@0: { sl@0: TInt relPos=ClusterRelativePos(aPos.iPos); sl@0: return FAT().DataPositionInBytes(aPos.iCluster)+relPos; sl@0: } sl@0: if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()) sl@0: User::Leave(KErrDirFull); // Past last root dir entry sl@0: return StartOfRootDirInBytes()+aPos.iPos; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName) sl@0: // sl@0: // Get the short name associated with a long file name sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::GetShortNameL")); sl@0: TEntryPos firstEntryPos(RootIndicator(),0); sl@0: TFatDirEntry firstEntry; sl@0: FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos); sl@0: MoveToDosEntryL(firstEntryPos,firstEntry); sl@0: TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name())); sl@0: LocaleUtils::ConvertToUnicodeL(aShortName, dosName); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName) sl@0: // sl@0: // Get the long name associated with a short file name sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::GetLongNameL")); sl@0: TEntryPos pos(RootIndicator(),0); sl@0: TFatDirEntry entry; sl@0: const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter sl@0: const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos)); sl@0: __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName)); sl@0: sl@0: TLeafDirData leafDir; sl@0: pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir); sl@0: FOREVER sl@0: { sl@0: TFatDirEntry startEntry; sl@0: User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName)); sl@0: if (entry.IsEndOfDirectory()) sl@0: User::Leave(KErrNotFound); sl@0: TBool entryIsVFat=EFalse; sl@0: if (startEntry.IsVFatEntry()) sl@0: entryIsVFat=ETrue; sl@0: if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased()) sl@0: { sl@0: TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name())); sl@0: TBuf<0x20> entryName; sl@0: LocaleUtils::ConvertToUnicodeL(entryName, entryName8); sl@0: if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound) sl@0: { sl@0: if (entryIsVFat==EFalse) sl@0: aLongName=shortNameWithoutPathDelimiter; sl@0: return; sl@0: } sl@0: } sl@0: MoveToNextEntryL(pos); sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Extend a file or directory, zeroing cluster chain and flushing after every write to FAT. sl@0: This method is called for rugged FAT only. sl@0: for parameters see CFatTable::ExtendClusterListL sl@0: */ sl@0: void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster) sl@0: { sl@0: __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL")); sl@0: __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter)); sl@0: sl@0: while(aNumber && FAT().GetNextClusterL(aCluster)) sl@0: aNumber--; sl@0: sl@0: //-- 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: if(!FAT().RequestFreeClusters(aNumber)) sl@0: { sl@0: __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: while (aNumber--) sl@0: { sl@0: TInt freeCluster=FAT().AllocateSingleClusterL(aCluster); sl@0: FAT().FlushL(); sl@0: ZeroDirClusterL(freeCluster); sl@0: FAT().WriteL(aCluster,freeCluster); sl@0: FAT().FlushL(); sl@0: aCluster=freeCluster; sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: #if defined(_DEBUG) sl@0: TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2) sl@0: // sl@0: // Debug function sl@0: // sl@0: { sl@0: if(aCommand>=EExtCustom) sl@0: { sl@0: if(LocalDrive()) sl@0: return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2); sl@0: else sl@0: return KErrNotSupported; sl@0: } sl@0: switch(aCommand) sl@0: { sl@0: case ECriticalWriteFailOn: sl@0: { sl@0: TInt r; sl@0: TInt16 args[2]; sl@0: TPtr8 des((TUint8*)args,4,4); sl@0: TRAP(r,aMessage.ReadL(2,des,0)); sl@0: if(r!=KErrNone) sl@0: return(r); sl@0: SetWriteFail(ETrue); sl@0: SetWriteFailCount(args[0]); sl@0: SetWriteFailError(args[1]); sl@0: break; sl@0: } sl@0: case ECriticalWriteFailOff:SetWriteFail(EFalse);break; sl@0: case ERuggedFSysOn: SetRuggedFSys(ETrue);break; sl@0: case ERuggedFSysOff: SetRuggedFSys(EFalse);break; sl@0: case EIsRuggedFSys: sl@0: { sl@0: TInt r; sl@0: TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true sl@0: TPtr8 pVal(&val,1,1); sl@0: TRAP(r,aMessage.WriteL(2,pVal,0)); sl@0: if(r!=KErrNone) sl@0: return(r); sl@0: break; sl@0: } sl@0: case ELocalTimeForRemovableMediaOn: sl@0: { sl@0: FatFileSystem().SetUseLocalTime(ETrue); sl@0: break; sl@0: } sl@0: case ELocalTimeForRemovableMediaOff: sl@0: { sl@0: FatFileSystem().SetUseLocalTime(EFalse); sl@0: break; sl@0: } sl@0: case ELocalTimeUsedOnRemovableMedia: sl@0: { sl@0: TBool flag = FatFileSystem().GetUseLocalTime(); sl@0: TPckgC flagPckg(flag); sl@0: TInt r = aMessage.Write(2, flagPckg); sl@0: if(r!=KErrNone) sl@0: return r; sl@0: break; sl@0: } sl@0: case ECreationTime: sl@0: { sl@0: CheckStateConsistentL(); sl@0: sl@0: TEntryPos firstEntryPos(RootIndicator(),0); sl@0: TFatDirEntry firstEntry; sl@0: //RFs::ControlIO restricts you to use narrow descriptors sl@0: //so convert narrow back to wide. sl@0: TBuf8 fileNameNarrow; sl@0: aMessage.Read(2, fileNameNarrow); sl@0: sl@0: TFileName fileNameWide; sl@0: fileNameWide.Copy(fileNameNarrow); sl@0: sl@0: //find the long file name entry sl@0: TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) ); sl@0: if(r!=KErrNone) sl@0: return(r); sl@0: //Find the corresponding 8.3 short name entry, for metadata sl@0: MoveToDosEntryL(firstEntryPos,firstEntry); sl@0: TTime creationTime=0; sl@0: TPckg timePckg(creationTime); sl@0: SFatDirEntry* sEntry = reinterpret_cast(firstEntry.iData); sl@0: creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC); sl@0: r = aMessage.Write(3, timePckg); sl@0: if(r!=KErrNone) sl@0: return r; sl@0: break; sl@0: } sl@0: case EDisableFATDirCache: sl@0: { sl@0: MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface(); sl@0: TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache; sl@0: pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL); sl@0: break; sl@0: } sl@0: case EDumpFATDirCache: sl@0: { sl@0: MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface(); sl@0: TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache; sl@0: pDirCache->Control(KEDumpFATDirCache, 0, NULL); sl@0: break; sl@0: } sl@0: case EFATDirCacheInfo: sl@0: { sl@0: MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface(); sl@0: TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo; sl@0: pDirCache->Control(KEFATDirCacheInfo, 0, NULL); sl@0: break; sl@0: } sl@0: sl@0: sl@0: default: return(KErrNotSupported); sl@0: } sl@0: return(KErrNone); sl@0: } sl@0: #else sl@0: TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/) sl@0: {return(KErrNotSupported);} sl@0: #endif sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore) sl@0: // sl@0: // lock media device sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::Lock")); sl@0: TInt r=CreateDrive(Drive().DriveNumber()); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: TBusLocalDrive* local; sl@0: r=LocalDrive()->GetLocalDrive(local); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: #ifdef _LOCKABLE_MEDIA sl@0: if(local->Status()==KErrLocked) sl@0: local->Status() = KErrNotReady; sl@0: #endif sl@0: r=local->SetPassword(aOld,aNew,aStore); sl@0: if(r==KErrNone&&aStore) sl@0: WritePasswordData(); sl@0: return(r); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore) sl@0: // sl@0: // Unlock media device sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::Unlock")); sl@0: TInt r=CreateDrive(Drive().DriveNumber()); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: TBusLocalDrive* local; sl@0: r=LocalDrive()->GetLocalDrive(local); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: #ifdef _LOCKABLE_MEDIA sl@0: if(local->Status()==KErrLocked) sl@0: local->Status() = KErrNotReady; sl@0: #endif sl@0: r=local->Unlock(aPassword,aStore); sl@0: if(r==KErrNone&&aStore) sl@0: WritePasswordData(); sl@0: return(r); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword) sl@0: // sl@0: // Clear password from media device sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::ClearPassword")); sl@0: TInt r=CreateDrive(Drive().DriveNumber()); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: TBusLocalDrive* local; sl@0: r=LocalDrive()->GetLocalDrive(local); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: #ifdef _LOCKABLE_MEDIA sl@0: if(local->Status()==KErrLocked) sl@0: local->Status() = KErrNotReady; sl@0: #endif sl@0: r=local->Clear(aPassword); sl@0: if(r==KErrNone) sl@0: WritePasswordData(); sl@0: return(r); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::ErasePassword() sl@0: // sl@0: // Forcibly erase the password from a media device sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::ErasePassword")); sl@0: sl@0: TInt r=CreateDrive(Drive().DriveNumber()); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: TBusLocalDrive* local; sl@0: r=LocalDrive()->GetLocalDrive(local); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: sl@0: #ifdef _LOCKABLE_MEDIA sl@0: if(local->Status()==KErrLocked) sl@0: local->Status() = KErrNotReady; sl@0: #endif sl@0: r=local->ErasePassword(); sl@0: if(r==KErrNone) sl@0: { sl@0: // ...media change to ensure a fresh remount the drive sl@0: r = local->ForceRemount(0); sl@0: local->Status() = KErrNotReady; sl@0: WritePasswordData(); sl@0: } sl@0: return(r); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags) sl@0: // sl@0: // Force a remount of the drive sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::ForceRemountDrive")); sl@0: TInt r=CreateDrive(Drive().DriveNumber()); sl@0: if (r==KErrNone) sl@0: r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle); sl@0: if (r==KErrNone) sl@0: r=LocalDrive()->ForceRemount(aFlags); sl@0: return(r); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: void CFatMountCB::WritePasswordData() sl@0: // sl@0: // Write store password data to disk sl@0: // sl@0: { sl@0: __PRINT(_L("CFatMountCB::WritePasswordData")); sl@0: TBuf mediaPWrdFile(KMediaPWrdFile); sl@0: mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar(); sl@0: __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile); sl@0: TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber()); sl@0: TInt length=local.PasswordStoreLengthInBytes(); sl@0: if(length==0) sl@0: { sl@0: WriteToDisk(mediaPWrdFile,_L8("")); sl@0: return; sl@0: } sl@0: HBufC8* hDes=HBufC8::New(length); sl@0: if(hDes==NULL) sl@0: return; sl@0: TPtr8 pDes=hDes->Des(); sl@0: TInt r=local.ReadPasswordData(pDes); sl@0: if(r==KErrNone) sl@0: WriteToDisk(mediaPWrdFile,pDes); sl@0: delete hDes; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Trim trailing spaces of volume label descriptor and adjust its length sl@0: */ sl@0: void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const sl@0: { sl@0: // Locate first '\0' sl@0: TInt nullPos = aLabel.Locate('\0'); sl@0: if (nullPos == KErrNotFound) sl@0: nullPos = KVolumeLabelSize; sl@0: sl@0: // Trim trailing spaces sl@0: TInt i; sl@0: for (i=nullPos-1; i>=0; --i) sl@0: if (aLabel[i] != 0x20) sl@0: break; sl@0: aLabel.SetLength(i+1); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Searches for the volume label file sl@0: sl@0: @param aLabel The name of the volume label file returned upon successful search sl@0: @return KErrNone if it finds the volume label file, otherwise KErrNotFound sl@0: */ sl@0: TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel) sl@0: { sl@0: __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile")); sl@0: TEntryPos pos(RootIndicator(),0); sl@0: TFatDirEntry entry; sl@0: TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry)); sl@0: __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r); sl@0: return r; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Creates or updates the volume label file with name aNewName sl@0: sl@0: @param aNewName The new name for the volume label file sl@0: */ sl@0: void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName) sl@0: { sl@0: __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName); sl@0: TEntryPos pos(RootIndicator(),0); sl@0: TFatDirEntry entry; sl@0: sl@0: TBuf8 oldName; sl@0: TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry)); sl@0: sl@0: if( KErrNone == r ) sl@0: { sl@0: // Found existing volume label file, rename or delete sl@0: if(oldName == aNewName) sl@0: { sl@0: __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match")); sl@0: return; sl@0: } sl@0: else sl@0: { sl@0: if(aNewName.Length() == 0) sl@0: { sl@0: // delete the volume label file sl@0: __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete")); sl@0: EraseDirEntryL(pos, entry); sl@0: } sl@0: else sl@0: { sl@0: __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace")); sl@0: entry.SetName(aNewName); sl@0: WriteDirEntryL(pos, entry); sl@0: } sl@0: FAT().FlushL(); sl@0: } sl@0: } sl@0: else if( KErrNotFound == r ) sl@0: { sl@0: // Not found, need to create if aNewName is not empty sl@0: // Windows allows a volume label file to have the same name as sl@0: // an existing file or directory sl@0: if(aNewName.Length() > 0) sl@0: { sl@0: __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create")); sl@0: TEntryPos dirPos(RootIndicator(),0); sl@0: AddDirEntryL(dirPos,1); sl@0: TFatDirEntry fatDirEntry; sl@0: fatDirEntry.SetName(aNewName); sl@0: fatDirEntry.SetAttributes(KEntryAttVolume); sl@0: sl@0: TTime now; sl@0: now.UniversalTime(); sl@0: fatDirEntry.SetTime(now, TimeOffset() ); sl@0: fatDirEntry.SetStartCluster(0); sl@0: fatDirEntry.SetSize(0); sl@0: WriteDirEntryL(dirPos, fatDirEntry); sl@0: FAT().FlushL(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Some other error sl@0: User::Leave(r); sl@0: } sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Scans the root directory for a volume label file. Leaves with an error if not found sl@0: sl@0: @param aLabel Name of the volume label file upon successful search sl@0: @param aDosEntryPos Pointer to position of the volume label file upon successful search sl@0: @param aDosEntry Contains the entry for the volume label file upon successful search sl@0: */ sl@0: void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry) sl@0: { sl@0: __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL")); sl@0: sl@0: if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())) sl@0: { sl@0: __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root")); sl@0: User::Leave(KErrNotFound); // Allows maximum number of entries in root directory sl@0: } sl@0: sl@0: TInt previousCluster= aDosEntryPos.iCluster; sl@0: TUint previousPosition= aDosEntryPos.iPos; sl@0: TInt changePreviousCluster=1; sl@0: TInt count=0; sl@0: sl@0: TFatDirEntry startEntry; sl@0: TFileName dummyLongName; sl@0: sl@0: FOREVER sl@0: { sl@0: #ifdef _DEBUG sl@0: const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName); sl@0: __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e); sl@0: User::LeaveIfError(e); sl@0: #else sl@0: User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName)); sl@0: #endif sl@0: if(aDosEntry.IsEndOfDirectory()) sl@0: { sl@0: __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir")); sl@0: User::Leave(KErrNotFound); sl@0: } sl@0: if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry))) sl@0: { sl@0: if(aDosEntry.IsErased()) sl@0: { sl@0: __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root")); sl@0: User::Leave(KErrNotFound); //Allows maximum number of entries in root directory sl@0: } sl@0: } sl@0: if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage()) sl@0: { sl@0: if(aDosEntry.Attributes() & KEntryAttVolume) sl@0: { sl@0: aLabel = aDosEntry.Name(); sl@0: #ifdef _DEBUG sl@0: dummyLongName.Copy(aLabel); sl@0: __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName); sl@0: #endif sl@0: break; sl@0: } sl@0: } sl@0: MoveToNextEntryL(aDosEntryPos); sl@0: if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())) sl@0: { sl@0: __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root")); sl@0: User::Leave(KErrNotFound); //Allows maximum number of entries in root directory sl@0: } sl@0: if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition)) sl@0: { sl@0: DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count); sl@0: } sl@0: previousPosition=aDosEntryPos.iPos; sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Read volume label from disk and trim trailing 0x20 & 0x00 characters sl@0: */ sl@0: void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector) sl@0: { sl@0: // Read volume label as 8 bit descriptor sl@0: TBuf8 volName8; sl@0: TInt r = ReadVolumeLabelFile(volName8); sl@0: if(r != KErrNone) // No volume label file in root directory sl@0: volName8 = aBootSector.VolumeLabel(); sl@0: TrimVolumeLabel(volName8); sl@0: sl@0: TBuf16 volName16; sl@0: LocaleUtils::ConvertToUnicodeL(volName16, volName8); sl@0: SetVolumeName(volName16.AllocL()); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Populates iMap member of aInfo with contiguous block group maps. sl@0: sl@0: @param aPos Start position for a desired section of the file. sl@0: @param sLength Length of the desired data to produce the block map for. sl@0: @param aInfo A structure describing a group of block maps. sl@0: */ sl@0: void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo) sl@0: { sl@0: __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL")); sl@0: __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt)); sl@0: TBlockMapEntry blockMapEntry; sl@0: sl@0: TUint i = 0; sl@0: TInt clusterRelativePos; sl@0: TInt maxClusters; sl@0: TInt endCluster; sl@0: TInt clusterListLen; sl@0: TInt readLength; sl@0: TInt temp; sl@0: TInt currentPos; sl@0: TLocalDriveCapsBuf caps; sl@0: TInt r; sl@0: TInt64 realPosition = 0; sl@0: sl@0: do sl@0: { sl@0: currentPos = aPos.iPos; sl@0: temp = currentPos>>ClusterSizeLog2(); sl@0: if ( (currentPos) && ( (currentPos) == (temp<>ClusterSizeLog2())+1; sl@0: clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters); sl@0: readLength = Min( aLength, (clusterListLen<Caps(caps); sl@0: if ( r != KErrNone ) sl@0: User::LeaveIfError(r); sl@0: if ( caps().iType&EMediaRam ) sl@0: { sl@0: realPosition = FAT().DataPositionInBytes( aPos.iCluster ); sl@0: aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2()); sl@0: blockMapEntry.SetStartBlock( aPos.iCluster ); sl@0: } sl@0: else sl@0: blockMapEntry.SetStartBlock( aPos.iCluster - 2); sl@0: aInfo.iMap.Append(TPckgC(blockMapEntry)); sl@0: aPos.iPos += readLength; sl@0: aPos.iCluster = endCluster; sl@0: aLength -= readLength; sl@0: } sl@0: while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) ); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry) sl@0: { sl@0: TFatDirEntry firstEntry; sl@0: TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory sl@0: FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos); sl@0: sl@0: aDosEntryPos=firstEntryPos; sl@0: aDosEntry=firstEntry; sl@0: MoveToDosEntryL(aDosEntryPos,aDosEntry); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId) sl@0: { sl@0: // Get first cluster of file sl@0: TEntryPos dosEntryPos(RootIndicator(),0); sl@0: TFatDirEntry dosEntry; sl@0: InitializeRootEntry(dosEntry); // Nugatory initialisation to placate warnings sl@0: TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry)); sl@0: if(err!=KErrNone) sl@0: return err; sl@0: TInt startCluster=StartCluster(dosEntry); sl@0: // Empty files will return a cluster of zero sl@0: if(startCluster==0) sl@0: return KErrEof; sl@0: aUniqueId=MAKE_TINT64(0,startCluster); sl@0: return KErrNone; sl@0: } sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) sl@0: { sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: //-- maximal level of recursion for the CheckDisk. i.e. the max. number of folded directories to check. sl@0: const TInt KCheckDskMaxRecursionLevel = 50; sl@0: sl@0: // sl@0: // Walks a directory cluster list then checks all the entries. sl@0: // sl@0: void CFatMountCB::ChkDirL(RBitVector& aFatBitVec, TInt aDirCluster) sl@0: { sl@0: //__PRINT1(_L("### CFatMountCB::ChkDirL() level:%d"),iChkDiscRecLevel); sl@0: sl@0: //-- check if we have reached the recursion limit. on hardware the stack is very limited sl@0: //-- and its overflow will lead to crash. sl@0: if(iChkDiscRecLevel++ >= KCheckDskMaxRecursionLevel) sl@0: { sl@0: __PRINT1(_L("CFatMountCB::ChkDirL() max recursion level(%d) reached. Leaving!"),iChkDiscRecLevel); sl@0: User::Leave(KErrTooBig); sl@0: } sl@0: sl@0: if(/*Is32BitFat() &&*/aDirCluster != 0 && (aDirCluster == RootIndicator()))//the bit in comments maybe required sl@0: WalkClusterListL(aFatBitVec, RootIndicator()); sl@0: sl@0: TEntryPos entryPos(aDirCluster,0); sl@0: FOREVER sl@0: { sl@0: TFatDirEntry entry; sl@0: ReadDirEntryL(entryPos,entry); sl@0: MoveToDosEntryL(entryPos,entry); sl@0: if (entry.IsEndOfDirectory()) sl@0: break; sl@0: if (IsRootDir(entryPos)&&(StartOfRootDirInBytes()+entryPos.iPos==(RootDirEnd()-KSizeOfFatDirEntry))) sl@0: { sl@0: if(!entry.IsErased()) sl@0: ChkEntryL(aFatBitVec, entry); sl@0: break; // Allows maximum number of entries in root directory sl@0: } sl@0: MoveToNextEntryL(entryPos); sl@0: if (entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased()) sl@0: continue; sl@0: ChkEntryL(aFatBitVec, entry); sl@0: } sl@0: sl@0: iChkDiscRecLevel--; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: // sl@0: // Check FAT is valid for anEntry sl@0: // sl@0: void CFatMountCB::ChkEntryL(RBitVector& aFatBitVec, const TFatDirEntry& anEntry) sl@0: { sl@0: TInt listLength=0; sl@0: sl@0: if ((anEntry.Attributes()&(KEntryAttDir)) || anEntry.Size()) sl@0: listLength=WalkClusterListL(aFatBitVec, StartCluster(anEntry)); sl@0: else if (anEntry.StartCluster() != 0) // zero length file sl@0: User::Leave(EFatChkDskInvalidEntrySize); // shouldn't have clusters sl@0: sl@0: if (anEntry.Attributes()&KEntryAttDir) sl@0: ChkDirL(aFatBitVec, StartCluster(anEntry)); sl@0: sl@0: // Check that the correct number of clusters have been allocated for the size of the file. sl@0: sl@0: else if ((anEntry.Attributes()&KEntryAttVolume)==0) sl@0: { sl@0: TInt clustersForFileSize; sl@0: TInt clusterSize=1<> ClusterSizeLog2() ); sl@0: sl@0: if (listLength!=clustersForFileSize) sl@0: User::Leave(EFatChkDskInvalidEntrySize); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: // sl@0: // Walks cluster list from aCluster to EOF sl@0: // Reports an error if an invalid cluster is found before EOF or sl@0: // a cluster has been visited before. sl@0: // sl@0: TInt CFatMountCB::WalkClusterListL(RBitVector& aFatBitVec, TInt aCluster) sl@0: { sl@0: sl@0: TInt i=0; sl@0: do { sl@0: i++; sl@0: if (!ValidClusterNumber(aCluster)) sl@0: { sl@0: __PRINT1(_L("Bad Cluster number %d"),aCluster); sl@0: User::Leave(EFatChkDskIllegalClusterNumber); sl@0: } sl@0: sl@0: if (IsClusterVisited(aFatBitVec, aCluster)) sl@0: { sl@0: __PRINT1(_L("Cluster already in use %d"),aCluster); sl@0: User::Leave(EFatChkDskClusterAlreadyInUse); sl@0: } sl@0: sl@0: MarkClusterVisited(aFatBitVec, aCluster); sl@0: sl@0: } while (FAT().GetNextClusterL(aCluster)); sl@0: sl@0: return(i); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: // sl@0: // Checks that all unvisited clusters are marked as free in the FAT sl@0: // sl@0: void CFatMountCB::CheckUnvisitedClustersL(const RBitVector& aFatBitVec) const sl@0: { sl@0: sl@0: TInt cluster=2; sl@0: TInt maxCluster=cluster + UsableClusters(); sl@0: while (cluster= maxCluster) sl@0: break; sl@0: sl@0: TInt clusterVal=FAT().ReadL(cluster); sl@0: if (clusterVal!=0 && IsEndOfClusterCh(clusterVal)==EFalse && !IsBadCluster(clusterVal)) sl@0: { sl@0: __PRINT1(_L("\n*****Bad cluster Num = %d"),cluster); sl@0: User::Leave(EFatChkDskBadCluster); sl@0: } sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: @param aCluster cluster number to check for validity sl@0: @returns ETrue if aCluster is a valid cluster number sl@0: */ sl@0: TBool CFatMountCB::ValidClusterNumber(TUint32 aCluster) const sl@0: { sl@0: return (aCluster>=KFatFirstSearchCluster && aCluster<=MaxClusterNumber()); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: // sl@0: // Walk the FAT, returns error if find an unterminated list or sl@0: // lists that merge. sl@0: // sl@0: TInt CFatMountCB::CheckDisk() sl@0: { sl@0: sl@0: __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber()); sl@0: sl@0: if(!ConsistentState()) sl@0: return KErrCorrupt; sl@0: sl@0: //-- create a bit representation of the FAT sl@0: const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers sl@0: sl@0: sl@0: // cluster count may be zero if boot sector failed to be read (e.g. if the media is locked) sl@0: // or if TDrive::MountMedia(ETrue) has been called (in which case the boot sector may sl@0: if (MaxClusters == 0) sl@0: return KErrCorrupt; sl@0: sl@0: RBitVector bitVec; //-- each bit in this vector represents a FAT cluster sl@0: sl@0: TInt nRes = bitVec.Create(MaxClusters); sl@0: if(nRes != KErrNone) sl@0: { sl@0: ASSERT(nRes == KErrNoMemory); //-- the only one possible reason. sl@0: return KErrNoMemory; sl@0: } sl@0: sl@0: iChkDiscRecLevel = 0; //-- reset CheckDisk recursion counter sl@0: TRAPD(r,ChkDirL(bitVec, RootIndicator())); // Check from root directory sl@0: if (r==KErrNone) sl@0: { sl@0: TRAP(r,CheckUnvisitedClustersL(bitVec)); sl@0: } sl@0: sl@0: sl@0: bitVec.Close(); sl@0: sl@0: switch(r) sl@0: { sl@0: sl@0: case KErrNone: sl@0: return KErrNone; sl@0: sl@0: case EFatChkDskIllegalClusterNumber: sl@0: return(1); sl@0: sl@0: case EFatChkDskClusterAlreadyInUse: sl@0: return(2); sl@0: sl@0: case EFatChkDskBadCluster: sl@0: return(3); sl@0: sl@0: case EFatChkDskInvalidEntrySize: sl@0: return(4); sl@0: sl@0: default: sl@0: break; sl@0: } sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: // Helper functions for Check Disk functionality sl@0: //----------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Find the next unvisited cluster number in the bit array. sl@0: sl@0: @param aBitList bit array, where '0' bits represent unvisited clusters. sl@0: @param aCluster cluster number to start search with. sl@0: sl@0: @return positive integer indicating next unvisited cluster number sl@0: KErrNotFound (-1) if there are no unvisited clusters sl@0: sl@0: */ sl@0: static TInt NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster) sl@0: { sl@0: __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved sl@0: sl@0: TUint32 searchPos = aCluster; //-- bit number to start search with sl@0: sl@0: //-- look for the unvisited cluster (bit '0') in the bit array from the searchPos to the right sl@0: if(aFatBitVec.Find(searchPos, 0, RBitVector::ERight)) sl@0: return searchPos; sl@0: sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Check if we have visited cluster aCluster sl@0: sl@0: @param aFatBitVec bit array, where '0' bits represent unvisited clusters. sl@0: @param aCluster cluster number to check sl@0: @return ETrue if aCluster has been visited. sl@0: */ sl@0: static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster) sl@0: { sl@0: __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved sl@0: sl@0: return aFatBitVec[aCluster]; sl@0: } sl@0: sl@0: /** sl@0: Mark aCluster as visited sl@0: @param aFatBitVec bit array, where '0' bits represent unvisited clusters. sl@0: @param aCluster cluster number to mark sl@0: */ sl@0: static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster) sl@0: { sl@0: __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved sl@0: sl@0: aFatBitVec.SetBit(aCluster); //-- '1' bit in a bit array means that the corresponding cluster is visited sl@0: } sl@0: sl@0: sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Creates a scan drive object and starts the scan. sl@0: */ sl@0: TInt CFatMountCB::DoRunScanDrive() sl@0: { sl@0: TInt nRes; sl@0: sl@0: CScanDrive* pScnDrv = NULL; sl@0: TRAP(nRes, pScnDrv=CScanDrive::NewL(this)); sl@0: if(nRes != KErrNone) sl@0: return nRes; sl@0: sl@0: TRAPD(nScnDrvRes, pScnDrv->StartL()); sl@0: sl@0: const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered(); sl@0: delete pScnDrv; sl@0: sl@0: sl@0: if(bNeedFatRemount) sl@0: {//-- ScanDrive found and probably fixed some errors. sl@0: // ensure cached fat and free cluster count are updated sl@0: DoDismount(); //-- dismount sl@0: TRAP(nRes, MountL(EFalse)); //-- mount again sl@0: } sl@0: sl@0: if(nScnDrvRes != KErrNone) sl@0: return nScnDrvRes; sl@0: sl@0: sl@0: //-- if ScanDrive hasn't found anything wrong or has fixed recoverable erros, mark the volume clean sl@0: if(VolCleanFlagSupported()) sl@0: { sl@0: //-- if there is a background FAT scanning thread, we need to wait until it finishes its work. sl@0: //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting. sl@0: (void)FAT().NumberOfFreeClusters(ETrue); sl@0: TRAP(nRes, FinaliseMountL()); sl@0: ASSERT(nRes == KErrNone); sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: //------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Run scan drive on the given volume. sl@0: The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly. sl@0: sl@0: sl@0: @return Either KErrCorrupt if an error was found that is not caused by write failure due to power removal. sl@0: KErrNone if no error was found. One of four positive codes explaining what type of error was rectified sl@0: */ sl@0: TInt CFatMountCB::ScanDrive() sl@0: { sl@0: __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber()); sl@0: sl@0: if(!ConsistentState()) sl@0: return KErrCorrupt; sl@0: sl@0: TInt nRes; sl@0: sl@0: if(LockStatus()!=0) sl@0: { sl@0: __PRINT(_L("CFatMountCB::ScanDrive() locked!\n")); sl@0: return KErrInUse; sl@0: } sl@0: sl@0: if(iRamDrive) sl@0: {//-- Do not check internal RAM drive sl@0: __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive.")); sl@0: return KErrNone; sl@0: } sl@0: sl@0: //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt sl@0: if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume()) sl@0: { sl@0: TBool bVolClean = EFalse; sl@0: TRAP(nRes, bVolClean = VolumeCleanL()); sl@0: sl@0: if(nRes == KErrNone && bVolClean) sl@0: { sl@0: __PRINT(_L("Skipping ScanDrive on finalised volume!")); sl@0: return KErrNone; //-- skip ScanDrive on a clean volume sl@0: } sl@0: } sl@0: sl@0: //-- run ScanDrive sl@0: nRes = Open(); sl@0: if(nRes != KErrNone) sl@0: return nRes; sl@0: sl@0: nRes = DoRunScanDrive(); sl@0: sl@0: Close(); sl@0: sl@0: __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes); sl@0: sl@0: return nRes; sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: /** sl@0: Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset sl@0: if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not sl@0: a null offset is returned. sl@0: sl@0: @return The offset in seconds that timestamps on the filesystem have, relative to UTC. sl@0: */ sl@0: TTimeIntervalSeconds CFatMountCB::TimeOffset() const sl@0: { sl@0: if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() ) sl@0: { sl@0: return User::UTCOffset(); sl@0: } sl@0: else sl@0: { sl@0: return TTimeIntervalSeconds(0); sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: //----------------------------------------------------------------------------------------- sl@0: /** sl@0: Check is this file system can be mounted on the drive at all. sl@0: Just read and validate boot region, no real mounting overhead. sl@0: sl@0: @return KErrNone boot region is OK, the file system can be mounted. sl@0: KErrLocked the media is locked on a physical level. sl@0: other error codes depending on the implementation sl@0: sl@0: */ sl@0: TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable() sl@0: { sl@0: TInt nRes; sl@0: sl@0: const TInt driveNo = Drive().DriveNumber(); sl@0: __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo); sl@0: sl@0: nRes = CreateDrive(driveNo); sl@0: if(nRes != KErrNone) sl@0: { sl@0: __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes); sl@0: return nRes; sl@0: } sl@0: sl@0: //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive() sl@0: TFatBootSector bootSector; sl@0: nRes = ReadBootSector(bootSector); sl@0: sl@0: DismountedLocalDrive(); sl@0: sl@0: return nRes; sl@0: } sl@0: sl@0: sl@0: