First public contribution.
1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // Common CFatMountCB code for both EFAT.FSY and EFAT32.fsy
23 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
24 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
26 //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
28 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
29 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
34 #include "sl_leafdir_cache.h"
35 #include "sl_dir_cache.h"
36 #include "sl_scandrv.h"
39 TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively);
41 static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster);
42 static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster);
43 static TInt NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster);
45 //-----------------------------------------------------------------------------------------
47 TFatVolParam::TFatVolParam()
49 Mem::FillZ(this, sizeof(TFatVolParam));
53 populate the object with the values from the boot sector.
54 @param aBootSector a reference to the valid boots sector
56 void TFatVolParam::Populate(const TFatBootSector& aBootSector)
58 ASSERT(aBootSector.IsValid());
60 iSectorsPerCluster = aBootSector.SectorsPerCluster();
61 iSectorSizeLog2 = Log2(aBootSector.BytesPerSector());
62 iClusterSizeLog2 = iSectorSizeLog2+Log2(iSectorsPerCluster);
63 iFirstFatSector = aBootSector.FirstFatSector();
64 iNumberOfFats = aBootSector.NumberOfFats();
65 iFatSizeInBytes = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector();
66 iTotalSectors = aBootSector.VolumeTotalSectorNumber();
67 iRootClusterNum = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16
69 iRootDirectorySector = aBootSector.RootDirStartSector();
70 iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32
72 //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16
73 iFSInfoSectorNum = aBootSector.FSInfoSectorNum();
74 iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector
77 TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const
79 ASSERT(&aRhs != this);
81 return ETrue; //-- comparing with itself
83 return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0);
87 //-----------------------------------------------------------------------------------------
90 CFatMountCB::CFatMountCB()
92 __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName);
95 DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only
98 CFatMountCB::~CFatMountCB()
100 __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this);
107 delete iLeafDirCache;
111 //-----------------------------------------------------------------------------------------
113 CFatMountCB* CFatMountCB::NewL()
115 CFatMountCB* pSelf = new(ELeave) CFatMountCB;
117 CleanupStack::PushL(pSelf);
119 CleanupStack::Pop(pSelf);
124 // second-stage constructor
125 void CFatMountCB::ConstructL()
128 iNotifier = CAsyncNotifier::New();
132 User::Leave(KErrNoMemory);
135 iNotifier->SetMount(this);
138 //-------------------------------------------------------------------------------------------------------------------
141 Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount.
142 @return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready.
143 @see CMountCB::FileSystemClusterSize()
145 TInt CFatMountCB::ClusterSize() const
147 if (ClusterSizeLog2())
148 return (1 << ClusterSizeLog2());
153 //-------------------------------------------------------------------------------------------------------------------
156 @leave KErrAccessDenied if the mount is read-only
158 void CFatMountCB::CheckWritableL() const
162 __PRINT(_L("CFatMountCB is RO!"));
163 User::Leave(KErrAccessDenied);
168 @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory operations can not be performed
170 void CFatMountCB::CheckStateConsistentL() const
172 if(!ConsistentState())
174 __PRINT(_L("CFatMountCB state is inconsistent !"));
175 User::Leave(KErrCorrupt);
180 //-------------------------------------------------------------------------------------------------------------------
182 Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
183 @return ETrue if volume parameters remained same.
185 TBool CFatMountCB::CheckVolumeTheSame()
187 //-- initialise local drive
188 TInt nRes =InitLocalDrive();
192 //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it.
193 TFatBootSector bootSector;
194 nRes = ReadBootSector(bootSector, iRamDrive);
198 //-- 1. check volume Uid
199 if(iUniqueID != bootSector.UniqueID())
202 //-- check volume parameters, they must remain the same
203 TFatVolParam volParam;
204 volParam.Populate(bootSector);
206 if(!(volParam == iVolParam))
213 //-------------------------------------------------------------------------------------------------------------------
216 Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
217 If they are, re-initialises the mount.
219 void CFatMountCB::DoReMountL()
222 if(!CheckVolumeTheSame())
223 User::Leave(KErrGeneral);
225 //-- get drive capabilities
226 TLocalDriveCapsV2Buf capsBuf;
227 User::LeaveIfError(LocalDrive()->Caps(capsBuf));
229 //-- the volume is the same as it was on original MountL()
230 //-- we need to re-initialize for the case when the media was removed, FAT or directory structure changed on other device and the media returned back.
235 InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting.
239 //-------------------------------------------------------------------------------------------------------------------
242 Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and
243 if they are, re-initialises the mount. This includes resetting all caches.
244 ! Do not call this method from TFatDriveInterface methods, like citical and non-critical notifiers ! This can lead to the
245 recursive loops and undefined behaviour.
247 @return KErrNone if the remount was OK
248 system-wide error code otherwise
250 TInt CFatMountCB::ReMount()
252 __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State());
254 const TFatMntState currState = State();
256 //-- analyse the mount state and find out if we can remount at all.
260 __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!"));
265 //-- correct state, proceed to remount
270 //-- there are 2 options here:
271 //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it)
272 //-- in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards
273 //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.)
274 //-- in this case we may need to fake the success. This can only happen on forced mount by CFormatCB
276 TRAP(nRes, DoReMountL());
280 //-- note that the mount may be here left in inconsistent state (EMounting)
281 //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and
282 //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this.
283 __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes);
285 //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted.
286 //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and
287 //-- it is used for performing a format. If the format has finished, but RFormat isn't closed yet and we try to access the volume,
288 //-- we will get here, because all members of the constructed mount will be zeroes.
289 if(currState == EInit_Forced)
291 __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!"));
299 __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber());
304 //-------------------------------------------------------------------------------------------------------------------
307 Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is
311 void CFatMountCB::InvalidateLeafDirCache()
315 iLeafDirCache->Reset();
319 User::Free(iLastLeafDir);
324 //-------------------------------------------------------------------------------------------------------------------
327 Delete mount's caches
328 Moves CFatMountCB into ENotMounted state immediately.
330 void CFatMountCB::DoDismount()
332 __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber());
334 //-- try to flush and destroy FAT cache
337 if(!ConsistentState() || ReadOnly())
338 {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either.
339 iFatTable->Dismount(ETrue);
342 {//-- Try to flush the FAT - if this fails there's not much we can do
343 TRAPD(r, iFatTable->FlushL());
344 iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache
348 //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting
349 //-- see CFatMountCB::InitializeL()
350 delete iLeafDirCache;
351 iLeafDirCache = NULL;
353 //-- destroy directory cache, this cache will be re-created while mounting or re-mounting
354 //-- see CFatMountCB::InitializeL()
358 //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive,
359 //-- i.e. iFatTable & iRawDisk are valid
360 SetState(EDismounted);
363 //-----------------------------------------------------------------------------------------
365 /** old implementation */
366 void CFatMountCB::FinaliseMountL()
368 FinaliseMountL(RFs::EFinal_RW);
371 //-----------------------------------------------------------------------------------------
373 Dismount the CFatMountCB and the drive.
374 called from TDrive::Dismount().
376 void CFatMountCB::Dismounted()
378 __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber());
380 //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media.
381 //-- this method may be called after the media change occured from the TDrive::Dismount(). It means
382 //-- that if we try to write some data here, they could be written into a different medium, if it had been
383 //-- physically changed.
385 const TFatMntState prevState = State();
387 DoDismount(); //-- it will change mount state to EDismounted
388 DismountedLocalDrive();
390 //-- check if the previous state was EInit_Forced, which means that this method was called
391 //-- on the mount that might not be alive (no valid iFatTable & iRawDisk).
392 //-- This can happen only during format operation on non-mounted previously volume.
393 //-- this EInit_Forced state must be processed separately, see ::Remount()
394 if(prevState == EInit_Forced)
395 SetState(EInit_Forced);
400 //-------------------------------------------------------------------------------------------------------------------
403 Find out if the mount is finalised.
404 @param aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise.
405 @return standard error codes.
407 TInt CFatMountCB::IsFinalised(TBool& aFinalised)
411 case EFinalised: //-- already explicitly finalised
415 case EInit_W: //-- the volume had been written
419 default: //-- it depends on the state
423 //-- find out if the volume is _physically_ finalised.
424 //-- It can be in the state EInit_R, but finalised before mounting
425 if(!VolCleanFlagSupported())
426 return KErrNotSupported;
428 TInt nRes = KErrNone;
429 TRAP(nRes, aFinalised = VolumeCleanL());
434 //-------------------------------------------------------------------------------------------------------------------
437 @return ETrue if the mount is in consistent state i.e. normally mounted.
438 See TFatMntState enum for more detail.
440 TBool CFatMountCB::ConsistentState() const
442 return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised);
445 //-------------------------------------------------------------------------------------------------------------------
448 Open CFatMountCB for write. I.e. perform some actions on the first write attempt.
449 This is a callback from TFatDriveInterface.
450 @return System wide error code.
452 TInt CFatMountCB::OpenMountForWrite()
454 if(State() == EInit_W)
455 return KErrNone; //-- nothing to do, the mount is already opened for write
457 __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber());
459 ASSERT(State() == EInit_R || State() == EFinalised);
461 //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and
462 //-- can not call TFatDriveInterface methods that call this method etc.
463 ASSERT(iCBRecFlag == 0);
464 DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag
466 //-- do here some "opening" work, like marking volme as dirty
467 //-- be careful here, as soon as this is a callback from TFatDriveInterface, writing via TFatDriveInterface may cause some unwanted recursion.
469 //-- mark the volume as dirty
471 TRAP(nRes, SetVolumeCleanL(EFalse));
477 DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag
483 //-------------------------------------------------------------------------------------------------------------------
486 Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily.
488 void CFatMountCB::UnFinaliseMountL()
494 SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty"
499 return; //-- nothing to do
502 //-- other mount states are inconsistent; can't perform this operation
503 User::Leave(KErrAbort);
510 //-------------------------------------------------------------------------------------------------------------------
515 @param aOperation describes finalisation operation ,see RFs::TFinaliseDrvMode
516 @param aParam1 not used, for future expansion
517 @param aParam2 not used, for future expansion
519 @leave System wide error code. particular cases:
520 KErrArgument invalid arguments
521 KErrInUse if the volume has opened objects (files, directories etc)
522 KErrCorrupt if the volume is corrupt
525 void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/)
527 __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber());
535 case RFs::EForceUnfinalise:
540 __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation);
542 User::Leave(KErrArgument);
546 //-- mount finalisation work
548 ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO);
550 if(State() == EFinalised)
551 {//-- the mount is already finalised. All we can do is to set it to RO mode
552 if(ReadOnly() && aOperation == RFs::EFinal_RW)
554 User::Leave(KErrAccessDenied); //-- can't override RO flag
557 (void)LocalDrive()->Finalise(ETrue);
559 if(aOperation == RFs::EFinal_RO)
568 if(LockStatus() != 0)
569 {//-- can't finalise the volume if it has opened objects and not in the consistent state.
570 //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present,
571 //-- it's impossible to detect such situation.
572 User::Leave(KErrInUse);
575 if(State() != EInit_R && State() != EInit_W)
576 {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt.
577 __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State());
578 User::Leave(KErrCorrupt);
584 //-- for FAT32 we may need to update data in FSInfo sectors
587 if(FAT().ConsistentState())
588 {//-- FAT table state is consistent and the number of free clusters is known.
589 //-- Do it disregarding the mount state, it may help in the situation when 2 copies of the FSInfo are different for some reason.
590 DoUpdateFSInfoSectorsL(EFalse);
593 {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working.
594 //-- in this case we can't put corect values into the FSInfo.
595 if(State() == EInit_W)
596 {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time.
597 //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo.
598 __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo"));
599 DoUpdateFSInfoSectorsL(ETrue);
602 {//-- no changes on the volume, just do not update FSInfo
603 __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated"));
606 }//if(FAT().ConsistentState())
612 //-- mark the volume as clean
613 SetVolumeCleanL(ETrue);
615 //-- finally, put the volume into RO mode if required
616 if(aOperation == RFs::EFinal_RO)
619 SetState(EFinalised);
623 //-------------------------------------------------------------------------------------------------------------------
626 @return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12
628 TBool CFatMountCB::VolCleanFlagSupported() const
630 const TFatType fatType=FatType();
632 ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32);
633 return (fatType != EFat12);
636 //-----------------------------------------------------------------------------------------
640 Obtain the volume information.
641 All information except iSize and iFree has been added by TDrive::Volume().
643 @param aVolume on return will contain iSize & iFree fields filled with actual data.
645 void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const
648 //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running.
649 //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field.
650 //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters
651 //-- will be returned.
652 const TBool bSyncOp = !aVolume.iVolSizeAsync;
653 aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side
655 __PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp);
656 const TDriveInfo& drvInfo=aVolume.iDrive;
658 #if defined(__EPOC32__)
659 // if RAM drive, cap size according to HAL.
660 if (drvInfo.iType==EMediaRam)
662 TLocalDriveCapsV2Buf caps;
663 LocalDrive()->Caps(caps);
665 const TInt max_drive_size=TInt(caps().iEraseBlockSize);
666 const TInt cur_drive_size=I64INT(caps().iSize);
668 aVolume.iSize=max_drive_size;
669 aVolume.iFree=max_drive_size-cur_drive_size;
671 aVolume.iSize=aVolume.iFree+iSize;
674 if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone)
676 // iSize will never grow beyond maxRam because of a check in medint.
677 // d <= f; (s{f} + f) - m <= f; s{f} <= m
678 __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid));
679 if (aVolume.iSize > maxSize)
681 TInt64 d = aVolume.iSize - maxSize;
682 __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid));
688 aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
689 aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<<ClusterSizeLog2(); //-- round down to cluster size
692 }//if (drvInfo.iType==EMediaRam)
697 const TUint32 freeClusters = FAT().NumberOfFreeClusters(bSyncOp);
699 __PRINT1(_L("CFatMountCB::VolumeL() free clusters:%d"), freeClusters);
701 aVolume.iFree = (TInt64)freeClusters << ClusterSizeLog2();
703 if (drvInfo.iType==EMediaRam)
704 aVolume.iSize=aVolume.iFree+iSize;
706 aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
707 aVolume.iSize=(aVolume.iSize >> ClusterSizeLog2()) << ClusterSizeLog2(); //-- round down to cluster size
712 //-----------------------------------------------------------------------------------------
715 // Set the volume label (write aVolume label into BPB & Volume Label File)
716 // aName string may be zero length but is assumed to contain no illegal characters or NULLs.
718 void CFatMountCB::SetVolumeL(TDes& aName)
721 __PRINT(_L("CFatMountCB::SetVolumeL"));
723 CheckStateConsistentL();
726 __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName));
728 TBuf8<KVolumeLabelSize> buf8(KVolumeLabelSize);
730 LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave);
732 LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before)
734 const TInt lengthOfBuf8=buf8.Length();
735 // Pad to end with spaces if not empty.
736 if (lengthOfBuf8>0 && lengthOfBuf8<KVolumeLabelSize)
738 buf8.SetLength(KVolumeLabelSize);
739 Mem::Fill(&buf8[lengthOfBuf8],KVolumeLabelSize-lengthOfBuf8,' ');
742 // Write a volume label file
743 WriteVolumeLabelFileL( buf8 );
745 // Write the boot sector volume label
746 // Always pad to full length with spaces
749 buf8.Fill(' ',KVolumeLabelSize);
752 WriteVolumeLabelL(buf8);
755 //-----------------------------------------------------------------------------------------
759 @param aName full path to the directory to create. Name validity is checked by file server.
760 all trailing dots from the name will be removed
762 void CFatMountCB::MkDirL(const TDesC& aName)
764 __PRINT2(_L("CFatMountCB::MkDirL, drv:%d, %S"), DriveNumber(), &aName);
766 CheckStateConsistentL();
769 TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
771 TInt namePos=dirName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
772 TPtrC name=dirName.Mid(namePos);
773 TLeafDirData leafDir;
774 const TEntryPos dirPos(FindLeafDirL(dirName.Left(namePos), leafDir),0);
775 TEntryPos dumPos=dirPos;
776 TFatDirEntry dumEntry;
778 TBool isOriginalNameLegal = IsLegalDosName(name,EFalse,EFalse,EFalse,EFalse,ETrue);
779 iFileCreationHelper.InitialiseL(name);
782 TFatDirEntry startEntry;
784 TRAPD(ret,DoFindL(name,KEntryAttMaskSupported,
785 startPos,startEntry,dumPos,dumEntry,
786 fileName,KErrNotFound,
787 &iFileCreationHelper,
790 if (ret!=KErrNotFound && ret!=KErrNone)
792 if (ret!=KErrNotFound)
794 if (dumEntry.Attributes()&KEntryAttDir)
795 User::Leave(KErrAlreadyExists);
797 User::Leave(KErrAccessDenied);
799 TShortName shortName;
801 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
803 GenerateShortNameL(dirPos.iCluster,name,shortName,ETrue);
807 if (isOriginalNameLegal==EFalse)
808 numEntries=NumberOfVFatEntries(name.Length());
811 if (iFileCreationHelper.IsNewEntryPosFound())
813 dumPos = iFileCreationHelper.EntryAddingPos();
816 AddDirEntryL(dumPos,numEntries); // Directory entry in leaf directory
820 //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
821 startCluster=FAT().AllocateSingleClusterL(dumPos.iCluster ? dumPos.iCluster : FAT().FreeClusterHint());
824 TRAPD(r, InitializeFirstDirClusterL(startCluster,dirPos.iCluster));
829 FAT().MarkAsBadClusterL(startCluster);
831 TFatDirEntry fatDirEntry;
832 fatDirEntry.SetName(shortName);
833 fatDirEntry.SetAttributes(KEntryAttDir);
836 fatDirEntry.SetTime(now, TimeOffset());
837 fatDirEntry.SetCreateTime(now, TimeOffset());
838 fatDirEntry.SetStartCluster(startCluster);
839 fatDirEntry.SetSize(0);
840 if (isOriginalNameLegal)
841 WriteDirEntryL(dumPos,fatDirEntry);
843 WriteDirEntryL(dumPos,fatDirEntry,name);
845 iFileCreationHelper.Close();
848 //-----------------------------------------------------------------------------------------
851 Setup 1st cluster of the new directory
853 @param aStartCluster this entry start cluster number
854 @param aParentCluster parent entry start cluster number
856 void CFatMountCB::InitializeFirstDirClusterL(TInt aStartCluster,TInt aParentCluster)
858 const TUint32 KClusterSz= 1<<ClusterSizeLog2();
859 const TUint32 KMaxBufSz = KClusterSz; //-- max. nuffer size is a cluster
860 const TUint32 KMinBufSz = 1<<SectorSizeLog2(); //-- min. buffer size is 1 sector (for OOM case)
862 //-- allocate a buffer for directory file 1st cluster initialisation
864 CleanupClosePushL(buf);
866 if(buf.CreateMax(KMaxBufSz) != KErrNone)
867 buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
871 //-- copy "." directory entry to the buffer
873 //-- "." directory entry
877 entry.SetTime(now, TimeOffset() );
878 entry.SetAttributes(KEntryAttDir);
879 entry.SetCurrentDirectory();
880 entry.SetStartCluster(aStartCluster);
881 Mem::Copy(&buf[0],&entry,KSizeOfFatDirEntry);
883 //-- append ".." directory entry
884 entry.SetParentDirectory();
885 entry.SetStartCluster(aParentCluster==RootIndicator() ? 0 : aParentCluster);
886 Mem::Copy(&buf[0]+KSizeOfFatDirEntry,&entry,KSizeOfFatDirEntry);
888 TEntryPos entryPos(aStartCluster,0);
890 //-- write buffer to the beginning of the directory file.
891 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
893 //-- fill in the rest of the cluster if we used a small buffer
894 if((TUint32)buf.Size() < KClusterSz) //-- use special interface to access FAT directory file
897 const TInt restCnt = SectorsPerCluster() - 1;
900 for(TInt i=0; i<restCnt; ++i)
902 entryPos.iPos += KMinBufSz;
903 DirWriteL(entryPos, buf); //-- use directory cache when dealing with directories
908 CleanupStack::PopAndDestroy(&buf);
912 //-----------------------------------------------------------------------------------------
916 @param aName directory name
917 all trailing dots from the name will be removed
919 void CFatMountCB::RmDirL(const TDesC& aName)
921 __PRINT2(_L("CFatMountCB::RmDirL, drv:%d, %S"), DriveNumber(), &aName);
923 CheckStateConsistentL();
926 TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
928 TFatDirEntry dirEntry;
929 TEntryPos dirEntryPos(RootIndicator(),0); // Already checked entry is a directory
930 FindEntryStartL(dirName,KEntryAttMatchMask|KEntryAttMatchExclusive,dirEntry,dirEntryPos);
931 TEntryPos dosEntryPos=dirEntryPos;
932 TFatDirEntry dosEntry=dirEntry;
933 MoveToDosEntryL(dosEntryPos,dosEntry);
934 if (!IsDirectoryEmptyL(StartCluster(dosEntry)))
935 User::Leave(KErrInUse);
936 // Remove the directory from cache before erasing
937 if(iLeafDirCache && iLeafDirCache->CacheCount() > 0)
939 iLeafDirCache->RemoveDirL(StartCluster(dosEntry));
942 EraseDirEntryL(dirEntryPos,dirEntry);
943 FAT().FreeClusterListL(StartCluster(dosEntry));
947 //-----------------------------------------------------------------------------------------
951 @param aName file name
952 all trailing dots from the name will be removed
954 void CFatMountCB::DeleteL(const TDesC& aName)
956 __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName);
958 CheckStateConsistentL();
961 TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
963 TFatDirEntry firstEntry;
964 TEntryPos firstEntryPos(RootIndicator(),0);
965 FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
966 TEntryPos dosEntryPos=firstEntryPos;
967 TFatDirEntry dosEntry=firstEntry;
968 MoveToDosEntryL(dosEntryPos,dosEntry);
969 if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir))
970 User::Leave(KErrAccessDenied);
971 // Can not delete a file if it is clamped
972 CMountCB* basePtr=(CMountCB*)this;
973 TInt startCluster=StartCluster(dosEntry);
974 if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0)
975 User::Leave(KErrInUse);
976 EraseDirEntryL(firstEntryPos,firstEntry);
977 FAT().FreeClusterListL(StartCluster(dosEntry));
981 //-----------------------------------------------------------------------------------------
985 Rename or replace a directory entry.
986 Assumes all files are closed and replace is only passed files.
987 Assumes rename target does not exist or is the source file.
989 --------------- operating mode --------------------------------------------
993 aOldName exists | aNewName exists | result
994 N N leave KErrNotFound
995 N Y leave KErrNotFound
996 Y N rename aOldName -> aNewName
997 Y Y leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing
1001 N N leave KErrNotFound
1002 N Y leave KErrNotFound
1003 Y N rename aOldName -> aNewName
1004 Y Y contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then.
1007 @param aOldName entry name to be renamed or replaced
1008 @param aNewName a new entry name
1009 @param aMode specifies renaming / replacing
1010 @param aNewDosEntryPos on exit contains new entry Pos.
1012 void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos)
1014 __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName);
1016 const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive.
1017 const TBool renameMode = (aMode == EModeRename);
1018 const TBool replaceMode = !renameMode;
1021 if(namesAreIdentical && replaceMode)
1022 return; //-- nothing to do, replacing file with itself
1024 //---------------------------------------------------------------------------------------------------------------------------
1025 //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName'
1027 TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset
1028 TEntryPos oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set
1030 FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos);
1032 TFatDirEntry oldName_DosEntry = oldName_FirstEntry; //-- "aOldName" entry set DOS entry
1033 TEntryPos oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry
1035 MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry);
1037 const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry
1039 //-- check if the file "aOldName" is clamped. In this case it can't be replaced.
1040 if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0))
1041 User::Leave(KErrInUse);
1043 //---------------------------------------------------------------------------------------------------------------------------
1044 //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not
1046 //-- extract new file name from the full path
1048 TPtrC ptrNewNameParentDir;
1049 const TInt delimPos = aNewName.LocateReverse(KPathDelimiter);
1050 ptrNewName.Set(aNewName.Mid(delimPos+1));
1051 ptrNewNameParentDir.Set(aNewName.Left(delimPos+1));
1053 //-- find the parent directory of the "aNewName" and create iterator for it
1054 TLeafDirData leafDir;
1055 const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator
1056 aNewName_DosEntryPos = aNewName_ParentDirPos;
1058 TEntryPos newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set
1059 TFatDirEntry newName_DosEntry;
1062 iFileCreationHelper.InitialiseL(ptrNewName);
1063 TFatDirEntry startEntry;
1065 TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported,
1066 newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry,
1067 fileName, KErrNotFound,
1068 &iFileCreationHelper,
1071 if (nRes!=KErrNone && nRes!=KErrNotFound)
1074 const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists.
1075 const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue);
1077 if(renameMode && newFileExists)
1079 if(!namesAreIdentical)
1081 if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir))
1083 User::Leave(KErrAccessDenied); //-- leave with KErrAccessDenied if it is trying to rename a file
1084 // to a dir or vice versa.
1086 User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists
1090 if(!bNewNameIsVFAT && !bOldNameIsVFAT)
1091 return; //-- renaming DOS name to itself
1093 //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File"
1096 //---------------------------------------------------------------------------------------------------------------------------
1098 if(replaceMode && newFileExists)
1100 //---------------------------------------------------------------------------------------------------------------------------
1101 //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries.
1103 //-- check if we are still trying to replace the file with itself, probably using short name alias
1104 if(aNewName_DosEntryPos == oldName_DosEntryPos)
1105 return; //-- nothing to do, it's the same file
1107 const TInt oldNameStartCluster = StartCluster(oldName_DosEntry);
1108 const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked
1110 newName_DosEntry.SetStartCluster(oldNameStartCluster);
1111 newName_DosEntry.SetSize(oldName_DosEntry.Size());
1112 newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0));
1113 newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes());
1118 //-- set a special Id in reserved section for old and new entries.
1119 //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain.
1120 //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry.
1121 //-- note that SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID.
1122 //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy.
1123 //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980.
1125 newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
1126 oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
1127 WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
1130 //-- write 'aNewName' DOS dir. entry data back
1131 WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry);
1133 //-- erase "oldName" entryset.
1134 EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
1136 //-- free 'aNewName' cluster list
1137 FAT().FreeClusterListL(newNameStartCluster);
1143 else //if(replaceMode && newFileExists)
1145 //---------------------------------------------------------------------------------------------------------------------------
1146 //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset
1148 TFatDirEntry newDosEntry = oldName_DosEntry;
1149 //-- generate short name for the 'aNewName' entryset and make new DOS entry
1151 {//-- need to generate a short name for VFAT entryset DOS entry
1152 TShortName shortName;
1154 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
1156 GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue);
1159 newDosEntry.SetName(shortName);
1162 {//-- just use 'aNewName' as DOS name.
1163 TBuf8<KFatDirNameSize+1> tmp; //-- the name may be "XXXXXXXX.YYY"
1164 tmp.Copy(ptrNewName);
1165 newDosEntry.SetName(DosNameToStdFormat(tmp));
1169 {//-- the the note(1) above
1170 newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
1171 oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
1172 WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
1175 //-- add new entryset to the directory
1176 aNewName_DosEntryPos.iPos = 0;
1177 aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster();
1179 if (iFileCreationHelper.IsNewEntryPosFound())
1181 aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos();
1186 const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length());
1187 AddDirEntryL(aNewName_DosEntryPos, numEntries);
1188 WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName);
1191 {//-- new name is one DOS entry only
1192 AddDirEntryL(aNewName_DosEntryPos, 1);
1193 WriteDirEntryL(aNewName_DosEntryPos, newDosEntry);
1196 //-- erase old entryset.
1197 EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
1199 //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry)
1200 if((newDosEntry.Attributes() & KEntryAttDir))
1202 TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry);
1204 TFatDirEntry chFatEnt;
1205 ReadDirEntryL(parentPtrEntPos, chFatEnt);
1207 const TUint parentDirStartCluster_Old = StartCluster(chFatEnt);
1208 TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster();
1210 if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0)
1211 {//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir.
1212 //-- must have starting cluster 0
1213 parentDirStartCluster_New = 0;
1216 if(parentDirStartCluster_Old != parentDirStartCluster_New)
1218 chFatEnt.SetStartCluster(parentDirStartCluster_New);
1219 WriteDirEntryL(parentPtrEntPos, chFatEnt);
1221 // Invalidate leaf dir cache as it is hard to track the dir structure changes now
1224 iLeafDirCache->Reset();
1227 }//else if(replaceMode && newFileExists)
1229 iFileCreationHelper.Close();
1232 //-----------------------------------------------------------------------------------------
1235 Rename 'aOldName' file/directory to 'aNewName'
1236 all trailing dots from the names will be removed
1238 @param aOldName existing object name
1239 @param aNewName new object name
1241 void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName)
1243 __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
1245 CheckStateConsistentL();
1248 TEntryPos newEntryPos;
1249 DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos);
1255 //-----------------------------------------------------------------------------------------
1258 Replace contents of the 'aNewName' with the contents of 'aOldName'
1259 all trailing dots from the names will be removed
1261 @param aOldName existing object name
1262 @param aNewName new object name
1264 void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName)
1267 __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
1269 CheckStateConsistentL();
1272 TEntryPos newEntryPos;
1273 DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos);
1278 //-----------------------------------------------------------------------------------------
1282 Try to find a directory entry by the given name and path.
1283 This method _must_ leave if the entry is not found. See the caller.
1285 @param aName path to the directory object. all trailing dots from the name will be removed.
1286 @param anEntry on return will contain the entry data
1288 @leave KErrPathNotFound if there is no path to the aName
1289 KErrNotFound if the entry corresponding to the aName is not found
1290 system-wide erorr code of media read failure.
1292 void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const
1294 __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName);
1296 CheckStateConsistentL();
1298 TEntryPos entryPos(RootIndicator(),0);
1300 TPtr fileName(anEntry.iName.Des());
1302 TPtrC fullName = RemoveTrailingDots(aName);
1303 TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
1304 TLeafDirData leafDir;
1305 entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir);
1308 TFatDirEntry startEntry;
1310 DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported,
1311 startPos,startEntry,entryPos,entry,
1312 fileName,KErrNotFound,
1317 anEntry.iAtt=entry.Attributes();
1318 anEntry.iSize=entry.Size();
1319 anEntry.iModified=entry.Time(TimeOffset());
1321 if (fileName.Length()==0)
1323 TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name()));
1324 LocaleUtils::ConvertToUnicodeL(fileName,dosName);
1326 if ((TUint)anEntry.iSize>=sizeof(TCheckedUid))
1327 ReadUidL(StartCluster(entry),anEntry);
1330 //-----------------------------------------------------------------------------------------
1333 Set directory entry details.
1334 @param aName entry name; all trailing dots from the name will be removed
1335 @param aTime entry modification time (and last access as well)
1336 @param aSetAttMask entry attributes OR mask
1337 @param aClearAttMask entry attributes AND mask
1340 void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
1342 __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName);
1344 CheckStateConsistentL();
1347 TEntryPos firstEntryPos(RootIndicator(),0);
1348 TFatDirEntry firstEntry;
1349 FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos);
1350 MoveToDosEntryL(firstEntryPos,firstEntry);
1351 TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
1352 if (setAttMask|aClearAttMask)
1354 TInt att=firstEntry.Attributes();
1356 att&=(~aClearAttMask);
1357 firstEntry.SetAttributes(att);
1359 if (aSetAttMask&KEntryAttModified)
1361 firstEntry.SetTime(aTime,TimeOffset());
1363 WriteDirEntryL(firstEntryPos,firstEntry);
1366 //-----------------------------------------------------------------------------------------
1368 void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const
1370 // Check one fat cluster for loops.
1374 if (aCluster==aPreviousCluster)
1375 User::Leave(KErrCorrupt); // Found loop
1378 if (aCount==aChangePreviousCluster)
1381 aChangePreviousCluster<<=1;
1382 aPreviousCluster=aCluster;
1386 //-----------------------------------------------------------------------------------------
1388 void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const
1394 TInt cluster=StartCluster(anEntry);
1395 if (cluster==0 && anEntry.Size()==0)
1398 TInt previousCluster=cluster;
1399 TInt changePreviousCluster=1;
1405 if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber()))
1406 User::Leave(KErrCorrupt);
1408 if(!FAT().GetNextClusterL(cluster))
1411 DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count);
1416 //-----------------------------------------------------------------------------------------
1419 Open/Create/Replace a file on the current mount.
1421 @param aName file name; all trailing dots from the name will be removed
1422 @param aMode File open mode, See TFileMode
1423 @param anOpen specifies action: open, create or replace the file
1424 @param aFile pointer to the CFileCB object to populate
1427 void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile)
1429 __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName);
1431 CheckStateConsistentL();
1433 TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
1435 TFatDirEntry firstEntry;
1436 TEntryPos firstEntryPos(RootIndicator(),0);
1437 TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
1438 TPtrC name(fullName.Mid(nPos));
1439 TInt ret = KErrNone;
1441 iFileCreationHelper.Close();
1442 if (anOpen == EFileCreate || anOpen == EFileReplace)
1444 iFileCreationHelper.InitialiseL(name);
1445 TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper));
1449 TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos));
1452 if (ret!=KErrNone && ret!=KErrNotFound)
1457 MoveToDosEntryL(firstEntryPos,firstEntry);
1458 if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume))
1459 User::Leave(KErrAccessDenied);
1460 if (anOpen==EFileCreate)
1461 User::Leave(KErrAlreadyExists);
1462 if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite)
1463 User::Leave(KErrAccessDenied);
1464 if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0))
1465 User::Leave(KErrInUse);
1466 CheckFatForLoopsL(firstEntry);
1470 if (anOpen==EFileOpen)
1471 User::Leave(KErrNotFound);
1473 //-- here we try to either create or replace file
1476 TLeafDirData leafDir;
1478 TInt numEntries = iFileCreationHelper.NumOfAddingEntries();
1479 TShortName shortName;
1480 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
1482 firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
1483 GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue);
1486 if (iFileCreationHelper.IsNewEntryPosFound())
1488 firstEntryPos = iFileCreationHelper.EntryAddingPos();
1492 firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
1493 firstEntryPos.iPos=0;
1496 AddDirEntryL(firstEntryPos,numEntries);
1498 firstEntry.SetName(shortName);
1499 firstEntry.SetStartCluster(0);
1502 now.UniversalTime();
1503 firstEntry.SetCreateTime(now, TimeOffset() );
1505 if (iFileCreationHelper.IsTrgNameLegalDosName())
1506 WriteDirEntryL(firstEntryPos,firstEntry);
1508 WriteDirEntryL(firstEntryPos,firstEntry,name);
1511 CFatFileCB& file=(*((CFatFileCB*)aFile));
1512 file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos);
1513 if (anOpen==EFileReplace && file.Size())
1518 if (file.IsSeekIndex()==EFalse)
1519 file.CreateSeekIndex();
1520 if (anOpen==EFileReplace || anOpen==EFileCreate)
1521 file.SetArchiveAttribute();
1526 iFileCreationHelper.Close();
1529 //-----------------------------------------------------------------------------------------
1533 Open a directory on the current mount.
1535 @param aName path to the object in the directory we want to open; all trailing dots from the name will be removed
1536 @param aDir dir. CB to be filled in.
1538 If there is no such a path, this method must leave with KErrPathNotFound
1540 @leave KErrPathNotFound if thereis no such path
1541 @leave error code on media read fault
1543 void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir)
1545 __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName);
1547 CheckStateConsistentL();
1549 const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
1551 TInt namePos=dirName.LocateReverse(KPathDelimiter);
1553 TFatDirEntry dosEntry;
1554 TEntryPos dosEntryPos(RootIndicator(),0);
1556 InitializeRootEntry(dosEntry);
1559 TPtrC dirPath=dirName.Left(namePos);
1560 TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1;
1561 TLeafDirData leafDir;
1562 dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf
1567 TFatDirEntry startEntry;
1568 DoFindL(dirPath.Mid(dirPos),
1569 KEntryAttMatchMask|KEntryAttMatchExclusive,
1570 startPos, startEntry, dosEntryPos, dosEntry,
1571 fileName, KErrPathNotFound,
1578 TPtrC matchName(dirName.Mid(namePos+1));
1579 if (matchName.Length()==0)
1580 matchName.Set(_L("*"));
1582 ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName);
1586 //-----------------------------------------------------------------------------------------
1588 TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster)
1590 // Check aCluster contains no directory entries other than . and ..
1594 __PRINT(_L("CFatMountCB::IsDirectoryEmptyL"));
1595 TEntryPos dirEntryPos(aCluster,0);
1596 TFatDirEntry dirEntry;
1599 ReadDirEntryL(dirEntryPos,dirEntry);
1600 MoveToDosEntryL(dirEntryPos,dirEntry);
1601 if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory())
1603 if (dirEntry.IsEndOfDirectory())
1605 if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd()))
1606 return ETrue; // Root Directory has no end of directory marker
1607 if (!dirEntry.IsErased())
1610 MoveToNextEntryL(dirEntryPos);
1614 //-----------------------------------------------------------------------------------------
1617 Overwrite as many contiguous file clusters as possible.
1619 void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster)
1622 __PRINT(_L("CFatMountCB::DoWriteToClusterListL"));
1623 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1627 const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
1628 const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
1629 const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
1630 const TInt writeLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
1631 TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
1633 TRAPD(r, iRawDisk->WriteL(dataStart,writeLength,aSrc,aMessage,anOffset));
1635 if(r == KErrNone) // Write succeded
1637 aPos.iPos+=writeLength;
1638 aPos.iCluster=endCluster;
1642 if(r != KErrCorrupt) // failure not due to corruption so propogate up
1645 TErrorInfoBuf errinf;
1646 r = iRawDisk->GetLastErrorInfo(errinf);
1648 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
1651 const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
1652 TInt goodcluster = FAT().AllocateSingleClusterL(badcluster);
1654 //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster.
1655 TInt cluster = aPos.iCluster;
1656 if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<<ClusterSizeLog2())))
1659 if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
1660 { //Copy the contents already present in this cluster to new cluster allocated.
1661 const TInt sizeToRead = aPos.iPos - ((aPos.iPos >> ClusterSizeLog2()) << ClusterSizeLog2());
1662 dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead));
1665 //-- Allocate the buffer required to copy the contents from bad cluster
1667 CleanupClosePushL(clustBuf);
1668 if(clustBuf.CreateMax(sizeToRead) != KErrNone)
1670 FAT().FreeClusterListL(goodcluster);
1671 User::Leave(KErrNoMemory);
1674 r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer
1675 if(r != KErrNone) //If read fails dont do anything not even marking bad cluster.
1677 FAT().FreeClusterListL(goodcluster);
1681 //Copy the bad and good cluster,required to adjust the start cluster number.
1682 if(aBadcluster == 0)
1683 aBadcluster = badcluster;
1685 aGoodcluster = goodcluster;
1689 //Calculate and copy the contents to new cluster.
1690 aPos.iCluster = goodcluster;
1691 dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead);
1693 r = LocalDrive()->Write(dataStart, clustBuf);
1695 { // Copied contents to new cluster so fix up the chain and mark the cluster as bad.
1696 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1697 FAT().MarkAsBadClusterL(badcluster);
1698 aGoodcluster = goodcluster;
1699 CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer
1702 else if(r == KErrCorrupt)
1704 r = LocalDrive()->GetLastErrorInfo(errinf);
1705 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector)
1706 { //Allocate new cluster and adjust the cluster list.
1707 goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster);
1708 FAT().MarkAsBadClusterL(aPos.iCluster);
1713 //Not able to write successfully so dont alter the original list.
1714 aBadcluster = aGoodcluster = 0;
1715 FAT().FreeClusterListL(goodcluster);
1719 }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
1721 if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist
1723 // return bad and good clusters for CFatFileCB to fix up
1724 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1725 aBadcluster = badcluster;
1726 aGoodcluster = goodcluster;
1727 aPos.iCluster = goodcluster;
1731 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1732 if(badcluster > aPos.iCluster) //bad cluster not first in this contiguous list
1733 FAT().WriteL(badcluster-1, goodcluster);
1734 else //first cluster of this contigous list bad so update last cluster of previous contiguous list
1735 FAT().WriteL(aLastcluster, goodcluster);
1738 FAT().MarkAsBadClusterL(badcluster);
1743 User::Leave(KErrCorrupt);
1746 //-----------------------------------------------------------------------------------------
1748 void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster)
1750 // Overwrite cluster list.
1754 __PRINT(_L("CFatMountCB::WriteToClusterListL"));
1755 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1757 const TUint startPos=aPos.iPos;
1758 const TUint temp=startPos>>ClusterSizeLog2();
1759 const TUint length = (TUint)aLength;
1761 if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
1763 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1767 TInt previouscluster=0;
1770 DoWriteToClusterListL(aPos,length-offset,aSrc,aMessage,anOffset+offset, previouscluster, aBadcluster, aGoodcluster);
1771 if (offset == (aPos.iPos-startPos))
1773 offset=aPos.iPos-startPos;
1774 __ASSERT_ALWAYS(aPos.iPos>startPos,User::Leave(KErrCorrupt));
1775 previouscluster=aPos.iCluster;
1777 {__ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));}
1783 //-----------------------------------------------------------------------------------------
1785 void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
1787 // Read from as many contiguous file clusters as possible
1791 __PRINT(_L("CFatMountCB::DoReadFromClusterListL"));
1795 const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
1796 const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
1797 const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
1798 const TInt readLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
1799 const TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
1801 TRAPD(r, iRawDisk->ReadL(dataStart,readLength,aTrg,aMessage,anOffset));
1803 if(r == KErrNone) // Read succeded
1805 aPos.iPos+=readLength;
1806 aPos.iCluster=endCluster;
1809 if(r != KErrCorrupt) // failure not due to corruption so propogate up
1812 TErrorInfoBuf errinf;
1813 r = iRawDisk->GetLastErrorInfo(errinf);
1815 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
1817 TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
1818 FAT().MarkAsBadClusterL(badcluster);
1821 User::Leave(KErrCorrupt);
1824 //-----------------------------------------------------------------------------------------
1826 void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
1828 // Read from cluster list
1832 __PRINT(_L("CFatMountCB::ReadFromClusterListL"));
1833 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1835 const TInt startPos=aPos.iPos;
1836 const TInt temp=startPos>>ClusterSizeLog2();
1838 if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
1840 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1846 DoReadFromClusterListL(aPos,aLength-offset,aTrg,aMessage,anOffset+offset);
1847 offset=aPos.iPos-startPos;
1848 if ((offset<aLength))
1850 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1852 if (offset>=aLength)
1857 //-----------------------------------------------------------------------------------------
1859 TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const
1861 // Navigate the path to find the leaf directory.
1862 // Returns the startcluster of data for the directory found.
1866 __PRINT(_L("CFatMountCB::FindLeafDirL"));
1870 TEntryPos entryPos(RootIndicator(),0);
1872 if (iLeafDirCache == NULL)
1874 TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0;
1875 TInt namelen=aName.Length();
1876 if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen))
1878 if (leaflen==namelen)
1879 return(iLastLeafDirCluster);
1881 entryPos.iCluster=iLastLeafDirCluster;
1886 // Skip root directory
1887 if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1)
1889 TInt err = iLeafDirCache->FindInCache(aName, aLeafDir);
1890 if (err == KErrNone)
1892 ASSERT(aLeafDir.iClusterNum > 0);
1893 return aLeafDir.iClusterNum;
1895 else if (err != KErrNotFound)
1897 User::LeaveIfError(err);
1904 lex.Inc(); // Skip path delimiter
1906 r=lex.Remainder().Locate(KPathDelimiter);
1907 if (r==KErrNotFound)
1908 r=lex.Remainder().Length();
1909 if (r==0) // End of the path
1911 lex.Inc(r); // Set the token length
1916 TFatDirEntry startEntry;
1917 DoFindL(lex.MarkedToken(),
1918 KEntryAttMatchMask|KEntryAttMatchExclusive,
1919 startPos, startEntry, entryPos, entry,
1920 fileName, KErrPathNotFound,
1925 entryPos.iCluster=StartCluster(entry);
1929 if (iLeafDirCache == NULL)
1931 AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName);
1932 ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster;
1936 if (aName.Length() > 1)
1938 aLeafDir = TLeafDirData(entryPos.iCluster);
1939 iLeafDirCache->AddToCacheL(aName, aLeafDir);
1943 return entryPos.iCluster;
1946 //-----------------------------------------------------------------------------------------
1949 Search for a specified name winthin directory cache
1950 Works similary to TBool CFatMountCB::DoFindL()
1952 @param anAtt attributes of the object to find
1953 @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
1954 @param aStartEntry on return will contain first VFAT dir entry
1955 @param aDosEntryPos the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
1956 @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case)
1957 @param aFileName in the case of VFAT entry and on success here will be returned a long filename
1958 @param aAuxParam some parameters package
1959 @param aFileCreationHelper a helper package for file creations
1961 @return ETrue if the specified name is found in the cache. In this case aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName will contain valid values
1963 TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos,
1964 TFatDirEntry& aStartEntry, TEntryPos& aDosEntryPos,
1965 TFatDirEntry& aDosEntry, TDes& aFileName,
1966 const TFindHelper& aAuxParam,
1967 XFileCreationHelper* aFileCreationHelper,
1968 const TLeafDirData& aLeafDir) const
1970 TBool bCacheMatchFound = EFalse;
1972 //-- get an interface to the Dir. cache
1973 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
1978 //-- save original values in order to restore them in the case of negative search results
1979 TEntryPos StartEntryPos1(aStartEntryPos);
1980 TEntryPos DosEntryPos1(aDosEntryPos);
1981 TFatDirEntry StartEntry1(aStartEntry);
1982 TFatDirEntry DosEntry1(aDosEntry);
1984 TInt64 nCachedLinPos;
1986 const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size
1987 const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes
1988 const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2; //-- maximal number of dir entries that can be in the cache
1990 const TUint pageSzLog2 = pDirCache->PageSizeInBytesLog2();
1991 TBool ScanMRUPageFirst = EFalse;
1992 TBool MRUPageScanned = EFalse;
1994 // if MRU pos is availale, start with MRU page
1995 if (aLeafDir.iMRUPos.Cluster())
1997 ScanMRUPageFirst = ETrue;
1998 DosEntryPos1 = aLeafDir.iMRUPos;
2002 TEntryPos startPos = DosEntryPos1;
2003 TInt clusterNum = DosEntryPos1.iCluster;
2005 for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt)
2006 {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries
2007 //-- that can be cached. Helps to avoid problems with infinite (looped) directories
2009 if (IsEndOfClusterCh(DosEntryPos1.iCluster))
2011 // refer back to the last stored cluster position
2012 // note aFileCreationHelper may not be initialised for file opening operations
2013 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster)
2015 TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry);
2016 aFileCreationHelper->SetEntryAddingPos(dummyPos);
2017 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2020 if (ScanMRUPageFirst && !MRUPageScanned)
2022 DosEntryPos1 = aDosEntryPos;
2023 MRUPageScanned = ETrue;
2026 break; //-- this was the last cluster in this directory
2029 const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
2030 DosEntryPos1.iPos = pageStartPos;
2031 TBool PassedPageBoundary = EFalse;
2033 const TInt64 entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory
2034 const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached
2036 {//-- current page is in the directory cache
2037 //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos);
2039 //-- search to the end of the cached page.
2040 // Note GetDirEntry() will read data beyond cache page boundary
2041 const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2;
2044 //-- extract dir entries from the cached page and see if they match given name (aName)
2045 /// until it reaches the next page
2048 StartEntryPos1 = DosEntryPos1;
2049 TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it
2051 //-- get directory entry from the cache. We know that the DosEntryPos1 is cached.
2052 nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName);
2053 if(nErr != KErrNone)
2056 if(DosEntry1.IsEndOfDirectory())
2058 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2060 // note it is impossible to be at the end of the cluster chain here.
2061 aFileCreationHelper->SetEntryAddingPos(DosEntryPos1);
2062 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2065 if (ScanMRUPageFirst && !MRUPageScanned)
2070 // if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned)
2071 goto Exit; //-- this was the last entry in this directory, no reason to look further
2074 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2076 if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage())
2084 startPos = DosEntryPos1;
2087 if (numFound == aFileCreationHelper->NumOfAddingEntries())
2089 aFileCreationHelper->SetEntryAddingPos(startPos);
2090 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2094 if(MatchEntryAtt(DosEntry1.Attributes(),anAtt))
2095 {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then.
2097 if(StartEntry1.IsVFatEntry())
2098 {//-- extracted entry is VFAT one, name can be in UNICODE
2100 // we only check short name candidates for long file names with VFAT entries,
2101 // if it is a valid dos name, it will be checked by default
2102 // note here target name is always fully specified
2103 if (aFileCreationHelper && aFileCreationHelper->IsInitialised())
2105 aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr());
2108 TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
2110 if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound)
2111 {//-- found match in cache
2112 bCacheMatchFound = ETrue;
2115 else if(aAuxParam.TrgtNameIsLegalDos())
2117 if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
2119 bCacheMatchFound = ETrue;
2123 }//if(StartEntry1.IsVFatEntry())
2124 else if(aAuxParam.TrgtNameIsLegalDos())
2125 {//-- this is an old DOS FAT entry
2127 if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
2129 //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves
2130 //-- between 2 clusters (or/and cache pages). I.e. 1st part of this long entry belongs to one cluster and even more might not be cached,
2131 //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached.
2132 //-- In this case if we search for short file name, we find it, but the aStartEntryPos will be incorrect, which leads to the directory corruption.
2133 //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare.
2134 if(StartEntryPos1.iPos == 0)
2135 {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above.
2136 __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!"));
2140 bCacheMatchFound = ETrue;
2145 }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt))
2147 // check boundaries after GetDirEntry()
2148 // if we have cross the cluster boundary, break the for loop
2149 if(DosEntryPos1.iCluster != clSave)
2150 {//-- GetDirEntry() has decided to move to the next cluster.
2151 DosEntryPos1.iCluster = clSave;
2155 // if we are still in the same cluster, check the page boundary by
2156 /// exam how many entries we have scanned within the cluster
2157 const TUint entriesLooked = ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2;
2158 if(entriesLooked > nEntries)
2160 PassedPageBoundary = ETrue;
2165 // move to next entry before scanning next file
2166 TRAP(nErr,MoveToNextEntryL(DosEntryPos1));
2167 if(nErr != KErrNone)
2170 // check boundaries after MoveToNextEntryL()
2171 if(DosEntryPos1.iCluster != clSave)
2173 DosEntryPos1.iCluster = clSave;
2177 if (entriesLooked + 1 > nEntries)
2179 PassedPageBoundary = ETrue;
2185 } //if(iRawDisk->PosCached(...))
2187 // scanning did not happen because the page is not cached,
2189 // scanning finished in last page and file is not found
2191 // if MRU page is not cached or
2192 // we scan MRU page first and it is not scanned yet, then this must be the MRU page,
2193 // we now start to scan from the beginning
2194 if (ScanMRUPageFirst && !MRUPageScanned)
2196 MRUPageScanned = ETrue;
2197 DosEntryPos1 = aDosEntryPos;
2198 DosEntryPos1.iPos = 0;
2202 // if we just finished scanning a page and still in the same cluster, then we crossed page
2203 // boundary, continue with next page.
2204 // note: although we are in the 'next page' already, this page might not be cached, so we need to
2205 // check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly.
2206 if (PassedPageBoundary)
2208 DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
2209 PassedPageBoundary = EFalse;
2213 //-- try to move to the next cluster of the directory file
2215 if(DosEntryPos1.Cluster() < KFatFirstSearchCluster) //-- small trick to get rid of TRAPping GetNextClusterL()
2218 // record previous cluster number before move on
2219 clusterNum = DosEntryPos1.iCluster;
2221 if(! FAT().GetNextClusterL(DosEntryPos1.iCluster))
2225 } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt)
2227 //---------------------------------
2230 if(bCacheMatchFound)
2232 //-- if the position of the found in cache object is less than given, pretend that we haven't found anything
2233 //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to
2234 //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir.
2235 TBool bFallBack=EFalse;
2237 if(DosEntryPos1.iCluster == aDosEntryPos.iCluster)
2239 if(DosEntryPos1.iPos < aDosEntryPos.iPos)
2244 if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos))
2253 //-- Update parameters with new values
2254 aStartEntryPos= StartEntryPos1;
2255 aDosEntryPos = DosEntryPos1;
2256 aStartEntry = StartEntry1;
2257 aDosEntry = DosEntry1;
2259 const TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
2261 pDirCache->MakePageMRU(mruPos);
2263 // only update the leaf dir cache when the original cache index is provided
2264 if (aLeafDir.iClusterNum)
2266 iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos));
2269 return bCacheMatchFound;
2272 //-----------------------------------------------------------------------------------------
2275 initialise find helper with the target file name.
2276 This is a quite expensive operation and initialisation is done only once. After this we know if the name is a legal dos one
2277 and also have the corresponding generated DOS name for it.
2279 @param aTargetName target file name we are looking for in ::DoFindL()
2281 void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName)
2288 iTargetName.Set(aTargetName);
2289 isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse);
2292 {//-- iShortName will contain generated short DOS name by long filename
2293 iShortName = DoGenerateShortNameL(aTargetName, count, ETrue);
2296 isInitialised = ETrue;
2300 Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise().
2301 @param apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format
2302 @return ETrue if the apDosEntryName is the same as generated iShortName
2304 TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const
2306 ASSERT(isInitialised);
2311 return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0);
2314 //-----------------------------------------------------------------------------------------
2315 const TInt KShortNameCandidatesNum = 4;
2317 Constructor of XFileCreationHelper class
2319 CFatMountCB::XFileCreationHelper::XFileCreationHelper()
2321 isInitialised = EFalse;
2325 Destructor of XFileCreationHelper class
2327 CFatMountCB::XFileCreationHelper::~XFileCreationHelper()
2333 Initialises a TFileCreationHelper object, generate a short name candidate pool.
2335 @param aTargetName Target file name for the potential new file.
2336 @post TFileCreationHelper is fully initialised.
2338 void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName)
2340 // close before use, to avoid memory leak
2343 iTargetName.Set(aTargetName);
2344 // generates short name candidate(s)
2346 while (count <= KShortNameCandidatesNum)
2348 TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue);
2349 TInt err = iShortNameCandidates.Append(shortNameCandidate);
2350 User::LeaveIfError(err);
2352 if (count == -1) // No tilde and number is needed
2360 // calculate number of new entries needed
2361 iNumOfAddingEntries = 1;
2362 isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue);
2363 if (!isTrgNameLegalDosName)
2364 iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length());
2366 isNewEntryPosFound = EFalse;
2367 isInitialised = ETrue;
2371 Close function of XFileCreationHelper class
2373 void CFatMountCB::XFileCreationHelper::Close()
2375 iShortNameCandidates.Close();
2376 isInitialised = EFalse;
2380 Validates short name candidates. If the input dos entry name is found in the short name
2381 candidate pool, the corresponding short name candidate will be removed from the pool.
2383 @param apDosEntryName An existing short name, to compare with the candidates.
2384 @pre Object should be initialised
2386 void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName)
2388 ASSERT(isInitialised);
2392 if (iShortNameCandidates.Count() > 0)
2394 for (TInt i = 0; i < iShortNameCandidates.Count(); i++)
2396 if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0)
2398 iShortNameCandidates.Remove(i);
2406 Gets a validated short name from the short name candidate pool.
2408 @param aShortName On return, contains a validated short name if found, otherwise zeroed.
2409 @return TInt Returns KErrNone if a validated short name found successfully,
2410 else KErrNotFound is returned.
2411 Returns KErrNotReady if object is not initialised.
2412 @pre Object should be initialised
2414 TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const
2418 ASSERT(isInitialised);
2420 return KErrNotReady;
2422 if (iShortNameCandidates.Count() > 0)
2424 aShortName.Copy(iShortNameCandidates[0]);
2428 return KErrNotFound;
2431 //-----------------------------------------------------------------------------------------
2435 Scan a directory looking for aName.
2437 @param aTrgtName a name of an object we are looking up in directory
2438 @param anAtt attributes of this object
2439 @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2440 @param aStartEntry on return will contain first VFAT dir entry
2441 @param aDosEntryPos the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
2442 @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case)
2443 @param aFileName in the case of VFAT entry and on success here will be returned a long filename
2444 @param anError This function might leave with this given error code
2445 @param aFileCreationHelper a helper package for file creations
2447 @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one
2448 @leave can leave with anError code on error or if the search has reached the end of directory (!)
2450 TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt,
2451 TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry,
2452 TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,
2453 TDes& aFileName,TInt anError,
2454 XFileCreationHelper* aFileCreationHelper,
2455 const TLeafDirData& aLeafDirData) const
2457 // check that the entry position to be read next is not past the end of the
2458 // root directory. If this is the case then when GetDirEntryL(..) is called
2459 // this will lead to MakeLinAddr(..) leaving with KErrDirFull.
2461 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
2462 User::Leave(anError);//Allows maximum number of entries in root directory
2464 __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName);
2466 TInt previousCluster=aDosEntryPos.iCluster;
2467 TUint previousPosition=aDosEntryPos.iPos;
2468 TInt changePreviousCluster=1;
2471 TBool trgNameIsWildCard = EFalse; //-- ETrue if the name we are looking for is a wildcard
2472 TBool trgNameFullySpecified = ETrue; //-- ETrue if the name we are looking for doesn't contain wildcards
2476 //-- find out if the name we are looking for is a wildcard ("*" or "*.*")
2477 const TInt len = aTrgtName.Length();
2480 trgNameIsWildCard = (aTrgtName[0] == '*');
2483 _LIT(KAllFiles, "*.*");
2484 trgNameIsWildCard = (aTrgtName==KAllFiles);
2487 //-- find out if the name we are looking for contains wildcharacters: "*" or "?"
2488 if(trgNameIsWildCard)
2489 trgNameFullySpecified = EFalse;
2492 for(TInt i=0; i<len; ++i)
2494 const TChar ch = aTrgtName[i];
2495 if(ch == (TChar)'*' || ch == (TChar)'?')
2497 trgNameFullySpecified = EFalse;
2505 TPtrC trgtNameNoDot(aTrgtName);
2507 TFindHelper findHelper;
2508 //---------------------------------------------------
2509 //-- if we have fully specified name and directory cache is present, try to
2510 //-- locate the name in the cache first to avoid reading from media
2511 //-- if the entry belongs to the root directory (for FAT12,16) skip the lookup, because root directory isn't aligned by cluster size boundary,
2512 //-- while directory cache pages are. For FAT32 it doesn't matter, because root dir is a usual file.
2513 if(iRawDisk->DirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper)
2514 {//-- aName is fully specified, i.e doesn't contain wildcards
2516 findHelper.InitialiseL(trgtNameNoDot);
2518 const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData);
2521 return(aStartEntry.IsVFatEntry());
2524 //---------------------------------------------------
2526 // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided
2527 TBool scanAhead = EFalse;
2528 // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&&
2529 // we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&&
2530 // if we don't have a starting cluster number, we draw back to original scanning algorithm
2531 if (!IsRootDir(aDosEntryPos) // we don't do forward scanning for root dir &
2532 && aLeafDirData.iClusterNum != 0 // if we have a starting cluster number &
2533 && aLeafDirData.iMRUPos.Cluster() != 0) // if we have a starting cluster number &
2536 aDosEntryPos = aLeafDirData.iMRUPos;
2540 TEntryPos startPos = aDosEntryPos;
2541 TInt clustNum = aDosEntryPos.Cluster();
2543 for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
2545 // if we are not scanning ahead, we don't need this outer for loop
2549 TBool found = EFalse;
2551 FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end
2553 //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT)
2554 //-- aDosEntry will contain a DOS entry of the directory entry we have read.
2555 aStartEntryPos=aDosEntryPos;
2556 User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName));
2558 if (aDosEntry.IsEndOfDirectory())
2559 {//-- the end of directory reached.
2561 // if new entry position for adding has not been found yet.
2562 // note aFileCreationHelper may not be initialised for pure file opening operations
2563 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2565 // if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain,
2566 // we pass the last scanned entry position to AddDirEntryL
2567 if (IsEndOfClusterCh(aDosEntryPos.iCluster))
2569 TInt clusterSize=1<<ClusterSizeLog2();
2570 TEntryPos dummyPos(clustNum, clusterSize - KSizeOfFatDirEntry);
2571 aFileCreationHelper->SetEntryAddingPos(dummyPos);
2572 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2574 // or we reached the end of the directory.
2577 aFileCreationHelper->SetEntryAddingPos(aDosEntryPos);
2578 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2582 // if we are scanning ahead and this is the first scanning, we break out to restart scanning
2583 if (scanAhead && scanCnt == 1)
2585 break; // from FOREVER, restart scanning
2588 // if (!scanAhead || scanAhead && scanCnt == 2)
2589 User::Leave(anError);
2593 // entry space searching for potential new file/directory creation
2594 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2596 if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage())
2604 startPos = aDosEntryPos;
2607 if (numFound == aFileCreationHelper->NumOfAddingEntries())
2609 aFileCreationHelper->SetEntryAddingPos(startPos);
2610 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2616 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
2617 if (aDosEntry.IsErased())
2619 User::Leave(anError);//Allows maximum number of entries in root directory
2623 const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage();
2625 if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt))
2626 {//-- we have read a filename entry and entry's attributes match required; compare names then.
2628 if (trgNameIsWildCard)
2631 break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name.
2635 if (aStartEntry.IsVFatEntry())
2636 {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name.
2637 //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries
2640 // we only check short name candidates for long file names with VFAT entries,
2641 // if it is a valid dos name, it will be checked by default
2642 // note, for file creation cases, target name will be always fully specified
2643 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified)
2645 aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr());
2648 //-- discard trailing dots from aFileName if present
2649 TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
2651 if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound)
2654 break; //-- OK, found a match.
2656 else if (trgNameFullySpecified)
2658 //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified,
2659 //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK.
2660 findHelper.InitialiseL(trgtNameNoDot);
2662 if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
2665 break; //-- DOS entries match, success.
2668 else if (!trgNameFullySpecified)
2669 {//-- target name contains wildcards, we need to use MatchF with dos name
2670 TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
2672 LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
2673 if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
2682 else //if (aStartEntry.IsVFatEntry())
2683 {//-- we've read a legacy FAT entry, so compare DOS entries
2684 findHelper.InitialiseL(trgtNameNoDot);
2686 if(findHelper.TrgtNameIsLegalDos())
2687 {//-- we are looking for a legal DOS name
2688 if(trgNameFullySpecified)
2689 {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries
2690 if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
2697 {//-- target name contains wildcards, we neeed to use MatchF
2698 TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
2700 LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
2701 if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
2708 } //if(findHelper.TrgtNameIsLegalDos())
2710 } //else if (aStartEntry.IsVFatEntry())
2712 } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt))
2715 // record previous cluster number
2716 clustNum = aDosEntryPos.iCluster;
2718 // this is the 2nd scanning and we have just passed the pos we started.
2719 if (scanAhead && scanCnt == 2)
2721 if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster()
2722 && aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos())
2724 User::Leave(anError);
2729 MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory
2731 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
2733 User::Leave(anError);//Allows maximum number of entries in root directory
2737 if (!scanAhead || scanCnt == 2)
2739 if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
2740 DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count);
2742 previousPosition=aDosEntryPos.iPos;
2744 } // FOREVER -- the actual scanning is done inside this loop
2753 // if we have not found in the first scanning and we are doing scanning ahead,
2754 // we need to go back to the starting pos of this dir and scan from start until
2755 // we reach lastscannedPos
2756 if (scanAhead && scanCnt == 1)
2758 aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0);
2763 // there are only two exits: either found or reached end of dir in the 1st scanning
2767 } // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
2769 //---------------------------------------------------
2770 if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster())
2772 TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
2773 iRawDisk->DirCacheInterface()->MakePageMRU(mruPos);
2775 // only update the leaf dir cache when the original cache index is provided
2776 if (aLeafDirData.iClusterNum)
2778 iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aDosEntryPos));
2782 return (aStartEntry.IsVFatEntry());
2785 //-----------------------------------------------------------------------------------------
2787 Locate an directory entry entry from its full path name.
2789 @param aName a name of an object we are looking for
2790 @param anAtt attributes of this object
2791 @param anEntry on return will contain first VFAT dir entry
2792 @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2794 @leave can leave with KErrNotFound if the search has reached the end of directory
2796 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const
2798 __PRINT(_L("CFatMountCB::FindEntryStartL()"));
2799 TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
2801 TLeafDirData leafDir;
2802 TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
2803 TFatDirEntry dosEntry;
2805 DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir);
2809 //-----------------------------------------------------------------------------------------
2812 Locate an directory entry entry from its full path name.
2814 @param aName a name of an object we are looking for
2815 @param anAtt attributes of this object
2816 @param anEntry on return will contain first VFAT dir entry
2817 @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2819 @leave can leave with KErrNotFound if the search has reached the end of directory
2821 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const
2823 __PRINT(_L("CFatMountCB::FindEntryStartL()"));
2824 TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
2826 TLeafDirData leafDir;
2827 TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
2828 TFatDirEntry dosEntry;
2829 DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir);
2832 //-----------------------------------------------------------------------------------------
2833 void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const
2835 // Scan a directory looking for aName.
2836 // aCluster and anEntryAddr give the location of the entry.
2840 __PRINT(_L("CFatMountCB::FindDosNameL()"));
2842 TFatDirEntry startEntry;
2844 TLeafDirData leafDir; // leaf dir data is zero initialized, no scannig ahead
2845 DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir);
2847 //-----------------------------------------------------------------------------------------
2849 void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries)
2851 // Find space for a new directory entry. Leave KErrEof if no space
2855 __PRINT(_L("CFatMountCB::AddDirEntryL"));
2858 TEntryPos startPos(RootIndicator(),0);
2859 TInt clusterNum=aPos.iCluster;
2862 ReadDirEntryL(aPos,entry);
2863 if (entry.IsEndOfDirectory())
2865 if (!entry.IsErased() && !entry.IsGarbage())
2872 if (numFound==aNumOfEntries)
2878 clusterNum=aPos.iCluster;
2879 MoveToNextEntryL(aPos);
2880 if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd()))
2881 // No end of directory marker at end of root directory
2882 User::Leave(KErrDirFull);
2885 TUint clusterSize=1<<ClusterSizeLog2();
2886 if (IsEndOfClusterCh(aPos.iCluster))
2887 { // End of last cluster in directory
2888 aPos.iCluster=clusterNum;
2889 aPos.iPos=clusterSize;
2892 TEntryPos eofPos(aPos.iCluster,aPos.iPos+KSizeOfFatDirEntry*aNumOfEntries);
2894 if (IsRootDir(aPos))
2895 { // Special case of root directory
2896 if (eofPos.iPos+StartOfRootDirInBytes()>RootDirEnd())
2897 User::Leave(KErrDirFull);
2902 if (eofPos.iPos==clusterSize)
2903 return; // No need to allocate
2904 if (eofPos.iPos>clusterSize)
2906 TInt numNeeded=eofPos.iPos>>ClusterSizeLog2();
2909 ExtendClusterListZeroedL(numNeeded,eofPos.iCluster);
2913 FAT().ExtendClusterListL(numNeeded,eofPos.iCluster);
2914 ZeroDirClusterL(eofPos.iCluster);
2917 eofPos.iPos-=numNeeded<<ClusterSizeLog2();
2918 if(aPos.iPos==clusterSize)
2920 if (!FAT().GetNextClusterL(aPos.iCluster))
2922 __PRINT(_L("CFatMountCB::AddDirEntryL corrupt#1"))
2923 User::Leave(KErrCorrupt);
2928 else if(Drive().IsRemovable())
2930 // check if entry is already zeroed
2931 ReadDirEntryL(eofPos,entry);
2932 if(!entry.IsEndOfDirectory())
2934 // some removable media may not have directory zeroed
2935 entry.SetEndOfDirectory();
2936 WriteDirEntryL(eofPos,entry);
2943 @param aCluster cluster number to zero-fill
2945 void CFatMountCB::ZeroDirClusterL(TInt aCluster)
2948 __PRINT1(_L("CFatMountCB::ZeroDirClusterL %d"),aCluster);
2950 const TUint32 KClusterSz= 1<<ClusterSizeLog2();
2951 const TUint32 KMaxBufSz = KClusterSz; //-- max. nuffer size is a cluster
2952 const TUint32 KMinBufSz = 1<<SectorSizeLog2(); //-- min. buffer size is 1 sector (for OOM case)
2954 //-- allocate a buffer for zero-filling a cluster
2956 CleanupClosePushL(buf);
2958 if(buf.CreateMax(KMaxBufSz) != KErrNone)
2959 buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
2963 TEntryPos entryPos(aCluster,0);
2965 //-- write buffer to the beginning of the directory file.
2966 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
2968 //-- fill in the rest of the cluster if we used a small buffer
2969 if((TUint32)buf.Size() < KClusterSz) //-- KMaxBufSz may == KMinBufSz if we have 1 sector per cluster
2971 const TInt restCnt = SectorsPerCluster() - 1;
2972 ASSERT(restCnt >=1);
2974 for(TInt i=0; i<restCnt; ++i)
2976 entryPos.iPos += KMinBufSz;
2977 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
2982 CleanupStack::PopAndDestroy(&buf);
2987 Internal method. Retrieves directory entry from given position.
2989 @param aPos on enter shall contain start position, from where the entry will be read. On return contains position of the DOS entry (the last one for object name for the VFAT case)
2990 @param aDosEntry On return contains DOS entry for the VFAT case
2991 @param aStartEntry On return contains start entry of the directory object for the VFAT case
2992 @param aLongFileName On return contains VFAT or long filename
2994 @return ETrue if whole FAT entry is OK: only 1 entry for DOS name or _ALL_ entries for a long name
2995 EFalse if there was an error in assembling entries to the long file name. In this case this entry shall be ignored by upper level.
2997 can leave because of ReadDirEntryL() and MoveToNextEntryL() [end of dir].
2999 TBool CFatMountCB::DoGetDirEntryL(TEntryPos& aPos, TFatDirEntry& aDosEntry, TFatDirEntry& aStartEntry, TDes& aLongFileName) const
3002 // __PRINT3(_L("CFatMountCB::GetDirEntryL() drv:%d, pos:%d:%d"), Drive().DriveNumber(), aPos.iCluster, aPos.iPos);
3004 ReadDirEntryL(aPos,aStartEntry);
3005 aDosEntry=aStartEntry;
3006 if (!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || aDosEntry.IsGarbage())
3007 {//-- This is either a 8.3 FAT entry or garbage
3008 aLongFileName.SetLength(0);
3012 //-- process VFAT entries
3014 if(!aDosEntry.IsLongNameStart())
3015 return EFalse; //-- wrong counter in the 1st VFat entry, consider it as orphaned
3018 TInt count = aDosEntry.NumFollowing(); //-- count of the following VFat entries
3020 TBuf16<KMaxVFatEntryName> vBuf(KMaxVFatEntryName);
3021 aDosEntry.ReadVFatEntry(vBuf);
3023 TInt vLength=vBuf.Locate('\0');
3024 if (vLength==KErrNotFound)
3025 vLength=KMaxVFatEntryName;
3027 vBuf.SetLength(vLength);
3029 const TInt nameLen = vLength+KMaxVFatEntryName*(count-1);
3030 if(nameLen <= 0 || nameLen > KMaxFileName)
3031 return EFalse; //-- wrong long file name length, consider VFAT entry as orphaned
3033 aLongFileName.SetLength(nameLen);
3035 const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry
3039 TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count);
3040 fileNamePtr.Copy(vBuf);
3042 break; //-- all VFat entries read, only DOS entry remained
3044 MoveToNextEntryL(aPos);
3045 ReadDirEntryL(aPos,aDosEntry);
3047 //-- check if it is correct VFat entry.
3048 //-- if not, this is the "orphaned" entry and will be ignored
3049 if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count)
3050 return EFalse; //-- bad VFAT entry
3052 aDosEntry.ReadVFatEntry(vBuf);
3055 if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3056 return ETrue;//Allows maximum number of entries in root directory
3058 //-- read the last, DOS FAT entry
3059 MoveToNextEntryL(aPos);
3060 ReadDirEntryL(aPos,aDosEntry);
3062 //-- check if it is corect
3063 if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry())
3064 return EFalse; //-- Bad DOS entry
3066 //-- verify ChechSum here if it is incorrect, use DOS name only
3067 const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name());
3068 if(calcNameChkSum != entryCheckSum)
3070 aLongFileName.SetLength(0);//-- don't use long filename
3071 __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum);
3079 Read a number of VFAT entries from the directory file.
3080 for parameters see DoGetDirEntryL()
3082 @return KErrNone if everything is OK, system wide error code otherwise
3085 TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const
3088 TBool bEntryOK=ETrue;
3089 TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName));
3095 {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged.
3096 //-- consider it as an "orphaned" entry and skip
3097 aDosEntry.iData[0] = 0xFF; // Mark entry as garbage
3098 aLongFileName.SetLength(0); // No long filename
3104 void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const
3106 // If anEntry is at the end of the cluster, and we are not the root dir,
3107 // move it to the next in the list.
3111 // __PRINT(_L("CFatMountCB::MoveToNextEntryL"));
3112 if (IsEndOfClusterCh(aPos.iCluster))
3114 const TUint temp = 1<<ClusterSizeLog2();
3115 if (aPos.iPos+KSizeOfFatDirEntry!=temp || IsRootDir(aPos))
3117 aPos.iPos+=KSizeOfFatDirEntry;
3121 if (FAT().GetNextClusterL(aPos.iCluster)==EFalse)
3123 SetEndOfClusterCh(aPos.iCluster);
3129 //-----------------------------------------------------------------------------------------
3132 Starting from a VFat entry walk down the directory until the associated dos entry is found
3134 @param aPos in: VFAT entry position. out: if this is a VFAT entry set, it will be DOS entry position. otherwise not changed
3135 @param anEntry on return will contain DOS dir. entry contents (if aPos points to the VFAT entry)
3137 void CFatMountCB::MoveToDosEntryL(TEntryPos& aPos,TFatDirEntry& anEntry) const
3140 //__PRINT(_L("CFatMountCB::MoveToDosEntryL"));
3141 if (anEntry.IsVFatEntry()==EFalse)
3145 MoveToNextEntryL(aPos);
3146 ReadDirEntryL(aPos,anEntry);
3147 if (anEntry.IsVFatEntry()==EFalse)
3149 if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3150 break; // Allows maximum number of entries in root directory
3154 //-----------------------------------------------------------------------------------------
3156 /** Read the Uid of the entry starting at aCluster */
3157 void CFatMountCB::ReadUidL(TInt aCluster,TEntry& anEntry) const
3160 __PRINT1(_L("CFatMountCB::ReadUidL(%d)"), aCluster);
3162 if((TUint)aCluster < KFatFirstSearchCluster || (TUint)aCluster >= UsableClusters()+KFatFirstSearchCluster)
3163 User::Leave(KErrCorrupt);
3165 TBuf8<sizeof(TCheckedUid)> uidBuf;
3166 iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf);
3167 __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed));
3168 TCheckedUid uid(uidBuf);
3169 anEntry.iType=uid.UidType();
3172 //-----------------------------------------------------------------------------------------
3175 Read file section without opening this file on a file server side.
3177 @param aName file name; all trailing dots from the name will be removed
3178 @param aFilePos start read position within a file
3179 @param aLength how many bytes to read; on return will be how many bytes actually read
3180 @param aDes local buffer desctriptor
3181 @param aMessage from file server, used to write data to the buffer in different address space.
3183 @leave on media read error
3185 void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage)
3187 __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName);
3189 CheckStateConsistentL();
3191 TEntryPos dosEntryPos(RootIndicator(),0);
3192 TFatDirEntry dosEntry;
3196 TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
3197 TLeafDirData leafDir;
3198 dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir);
3201 TFatDirEntry startEntry;
3202 DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported,
3203 startPos,startEntry,dosEntryPos,dosEntry,
3204 fileName,KErrNotFound,
3208 // Check that reading from aPos for aLength lies within the file
3209 // if aPos is within the file, and aLength is too long, read up to EOF
3210 // If aPos is beyond the end of the file, return a zero length descriptor
3212 TUint32 fileSize = dosEntry.Size();
3213 if ((TUint)aPos>=fileSize)
3214 User::Leave(KErrEof);
3216 if ((TUint)(aPos+aLength)>fileSize)
3217 aLength=fileSize-aPos;
3219 TInt cluster=StartCluster(dosEntry);
3223 TInt clusterSize=1<<ClusterSizeLog2(); // Size of file clusters
3226 // Total number of clusters in file
3227 TInt maxClusters=((fileSize+clusterSize-1)>>ClusterSizeLog2());
3232 // Get the maximum number of clusters that can be read contiguously
3233 TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
3234 __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));
3236 // If start position within this block, then read some data
3237 if (pos<(clusterListLen<<ClusterSizeLog2()))
3239 // Read the remaining length or the entire cluster block whichever is smaller
3240 TInt readLength = Min(aLength-readTotal,(clusterListLen<<ClusterSizeLog2())-pos);
3241 __ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
3242 TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
3243 iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
3244 readTotal += readLength;
3246 if (readTotal == aLength)
3252 // Get the next cluster in file
3253 pos-=(clusterListLen<<ClusterSizeLog2());
3255 TBool remainingClusters=
3257 ((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
3258 __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
3264 //-----------------------------------------------------------------------------------------
3266 void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const
3268 // Read aLength of data from disk directly to thread relative descriptor
3271 iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset);
3274 //-----------------------------------------------------------------------------------------
3276 void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage)
3278 // Write aLength of data from thread relative descriptor to disk
3283 //-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case.
3284 FAT().RequestRawWriteAccess(aPos, aLength);
3286 iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset);
3287 //-- Note: FAT directory cache will be invalidated in MountL()
3290 //-----------------------------------------------------------------------------------------
3292 This method must be used when writing to the FAT directory file.
3293 If FAT directory cache is present on this drive, it will be used.
3294 @param aPos entry media position
3295 @param aDes data descriptor
3297 void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes)
3300 const TInt64 posAddr=MakeLinAddrL(aPos);
3302 if(!iRawDisk->DirCacheInterface())
3304 iRawDisk->WriteCachedL(posAddr,aDes);
3307 {//-- if there is an interface to the FAT directory cache, use it
3308 iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes);
3312 //-----------------------------------------------------------------------------------------
3315 This method must be used when reading from the FAT directory file.
3316 If FAT directory cache is present on this drive, it will be used.
3318 @param aPos entry media position
3319 @param aLength how many bytes to read
3320 @param aDes input data descriptor
3322 void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const
3324 const TInt64 posAddr=MakeLinAddrL(aPos);
3326 if(!iRawDisk->DirCacheInterface())
3328 iRawDisk->ReadCachedL(posAddr,aLength,aDes);
3331 {//-- if there is an interface to the FAT directory cache, use it
3332 iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes);
3336 //-----------------------------------------------------------------------------------------
3338 void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry)
3340 // Write a FAT directory entry to disk.
3341 // Assumes sufficient space has been created for it by AddDirEntry.
3345 __PRINT(_L("CFatMountCB::WriteDirEntryL"));
3347 //-- use special interface to access FAT directory file
3348 DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry));
3351 //-----------------------------------------------------------------------------------------
3353 void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos)
3355 // Mark a dir entry as erased
3359 __PRINT(_L("CFatMountCB::EraseDirEntryL"));
3360 if(!iLeafDirCache && iLastLeafDir)
3361 iLastLeafDir->Des().SetLength(0);
3363 //-- use special interface to access FAT directory file
3364 DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8)));
3367 //-----------------------------------------------------------------------------------------
3369 void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const
3371 // Read a FAT directory entry to disk
3375 // __PRINT(_L("CFatMountCB::ReadDirEntryL"));
3376 if (IsEndOfClusterCh(aPos.iCluster))
3381 TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry);
3383 //-- use special interface to access FAT directory file
3384 DirReadL(aPos,KSizeOfFatDirEntry,buf);
3387 //-----------------------------------------------------------------------------------------
3390 Enlarge the disk's size.
3391 This method can be called only for variable size media, i.e. RAM drive
3393 @param aSize size increment (bytes)
3395 void CFatMountCB::EnlargeL(TInt aSize)
3397 __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize);
3402 if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize)
3403 User::Leave(KErrDiskFull);
3404 User::LeaveIfError(LocalDrive()->Enlarge(aSize));
3409 FAT().InitializeL();
3414 RawDisk().InitializeL();
3419 //-----------------------------------------------------------------------------------------
3421 void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength)
3423 // Reduce the disk's size
3427 __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength);
3428 User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength));
3432 //-----------------------------------------------------------------------------------------
3434 TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const
3436 // Convert cluster/position into linear address
3440 //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos);
3441 if (!IsRootDir(aPos))
3443 TInt relPos=ClusterRelativePos(aPos.iPos);
3444 return FAT().DataPositionInBytes(aPos.iCluster)+relPos;
3446 if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())
3447 User::Leave(KErrDirFull); // Past last root dir entry
3448 return StartOfRootDirInBytes()+aPos.iPos;
3451 //-----------------------------------------------------------------------------------------
3453 void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName)
3455 // Get the short name associated with a long file name
3458 __PRINT(_L("CFatMountCB::GetShortNameL"));
3459 TEntryPos firstEntryPos(RootIndicator(),0);
3460 TFatDirEntry firstEntry;
3461 FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
3462 MoveToDosEntryL(firstEntryPos,firstEntry);
3463 TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name()));
3464 LocaleUtils::ConvertToUnicodeL(aShortName, dosName);
3467 //-----------------------------------------------------------------------------------------
3469 void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName)
3471 // Get the long name associated with a short file name
3474 __PRINT(_L("CFatMountCB::GetLongNameL"));
3475 TEntryPos pos(RootIndicator(),0);
3477 const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
3478 const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos));
3479 __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName));
3481 TLeafDirData leafDir;
3482 pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir);
3485 TFatDirEntry startEntry;
3486 User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName));
3487 if (entry.IsEndOfDirectory())
3488 User::Leave(KErrNotFound);
3489 TBool entryIsVFat=EFalse;
3490 if (startEntry.IsVFatEntry())
3492 if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased())
3494 TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name()));
3495 TBuf<0x20> entryName;
3496 LocaleUtils::ConvertToUnicodeL(entryName, entryName8);
3497 if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound)
3499 if (entryIsVFat==EFalse)
3500 aLongName=shortNameWithoutPathDelimiter;
3504 MoveToNextEntryL(pos);
3510 //-----------------------------------------------------------------------------------------
3513 Extend a file or directory, zeroing cluster chain and flushing after every write to FAT.
3514 This method is called for rugged FAT only.
3515 for parameters see CFatTable::ExtendClusterListL
3517 void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster)
3519 __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL"));
3520 __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
3522 while(aNumber && FAT().GetNextClusterL(aCluster))
3525 //-- request aNumber free clusters from the FAT, this request may wait until FAT scan thread counted enough free clusters if it is running.
3526 if(!FAT().RequestFreeClusters(aNumber))
3528 __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull"));
3529 User::Leave(KErrDiskFull);
3533 TInt freeCluster=FAT().AllocateSingleClusterL(aCluster);
3535 ZeroDirClusterL(freeCluster);
3536 FAT().WriteL(aCluster,freeCluster);
3538 aCluster=freeCluster;
3542 //-----------------------------------------------------------------------------------------
3545 TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
3550 if(aCommand>=EExtCustom)
3553 return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2);
3555 return KErrNotSupported;
3559 case ECriticalWriteFailOn:
3563 TPtr8 des((TUint8*)args,4,4);
3564 TRAP(r,aMessage.ReadL(2,des,0));
3567 SetWriteFail(ETrue);
3568 SetWriteFailCount(args[0]);
3569 SetWriteFailError(args[1]);
3572 case ECriticalWriteFailOff:SetWriteFail(EFalse);break;
3573 case ERuggedFSysOn: SetRuggedFSys(ETrue);break;
3574 case ERuggedFSysOff: SetRuggedFSys(EFalse);break;
3578 TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true
3579 TPtr8 pVal(&val,1,1);
3580 TRAP(r,aMessage.WriteL(2,pVal,0));
3585 case ELocalTimeForRemovableMediaOn:
3587 FatFileSystem().SetUseLocalTime(ETrue);
3590 case ELocalTimeForRemovableMediaOff:
3592 FatFileSystem().SetUseLocalTime(EFalse);
3595 case ELocalTimeUsedOnRemovableMedia:
3597 TBool flag = FatFileSystem().GetUseLocalTime();
3598 TPckgC<TBool> flagPckg(flag);
3599 TInt r = aMessage.Write(2, flagPckg);
3606 CheckStateConsistentL();
3608 TEntryPos firstEntryPos(RootIndicator(),0);
3609 TFatDirEntry firstEntry;
3610 //RFs::ControlIO restricts you to use narrow descriptors
3611 //so convert narrow back to wide.
3612 TBuf8<KMaxPath> fileNameNarrow;
3613 aMessage.Read(2, fileNameNarrow);
3615 TFileName fileNameWide;
3616 fileNameWide.Copy(fileNameNarrow);
3618 //find the long file name entry
3619 TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) );
3622 //Find the corresponding 8.3 short name entry, for metadata
3623 MoveToDosEntryL(firstEntryPos,firstEntry);
3624 TTime creationTime=0;
3625 TPckg<TTime> timePckg(creationTime);
3626 SFatDirEntry* sEntry = reinterpret_cast<SFatDirEntry*>(firstEntry.iData);
3627 creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC);
3628 r = aMessage.Write(3, timePckg);
3633 case EDisableFATDirCache:
3635 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3636 TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache;
3637 pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL);
3640 case EDumpFATDirCache:
3642 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3643 TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache;
3644 pDirCache->Control(KEDumpFATDirCache, 0, NULL);
3647 case EFATDirCacheInfo:
3649 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3650 TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo;
3651 pDirCache->Control(KEFATDirCacheInfo, 0, NULL);
3656 default: return(KErrNotSupported);
3661 TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
3662 {return(KErrNotSupported);}
3666 //-----------------------------------------------------------------------------------------
3668 TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore)
3670 // lock media device
3673 __PRINT(_L("CFatMountCB::Lock"));
3674 TInt r=CreateDrive(Drive().DriveNumber());
3678 TBusLocalDrive* local;
3679 r=LocalDrive()->GetLocalDrive(local);
3683 #ifdef _LOCKABLE_MEDIA
3684 if(local->Status()==KErrLocked)
3685 local->Status() = KErrNotReady;
3687 r=local->SetPassword(aOld,aNew,aStore);
3688 if(r==KErrNone&&aStore)
3689 WritePasswordData();
3693 //-----------------------------------------------------------------------------------------
3695 TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore)
3697 // Unlock media device
3700 __PRINT(_L("CFatMountCB::Unlock"));
3701 TInt r=CreateDrive(Drive().DriveNumber());
3705 TBusLocalDrive* local;
3706 r=LocalDrive()->GetLocalDrive(local);
3710 #ifdef _LOCKABLE_MEDIA
3711 if(local->Status()==KErrLocked)
3712 local->Status() = KErrNotReady;
3714 r=local->Unlock(aPassword,aStore);
3715 if(r==KErrNone&&aStore)
3716 WritePasswordData();
3720 //-----------------------------------------------------------------------------------------
3722 TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword)
3724 // Clear password from media device
3727 __PRINT(_L("CFatMountCB::ClearPassword"));
3728 TInt r=CreateDrive(Drive().DriveNumber());
3732 TBusLocalDrive* local;
3733 r=LocalDrive()->GetLocalDrive(local);
3737 #ifdef _LOCKABLE_MEDIA
3738 if(local->Status()==KErrLocked)
3739 local->Status() = KErrNotReady;
3741 r=local->Clear(aPassword);
3743 WritePasswordData();
3747 //-----------------------------------------------------------------------------------------
3749 TInt CFatMountCB::ErasePassword()
3751 // Forcibly erase the password from a media device
3754 __PRINT(_L("CFatMountCB::ErasePassword"));
3756 TInt r=CreateDrive(Drive().DriveNumber());
3760 TBusLocalDrive* local;
3761 r=LocalDrive()->GetLocalDrive(local);
3765 #ifdef _LOCKABLE_MEDIA
3766 if(local->Status()==KErrLocked)
3767 local->Status() = KErrNotReady;
3769 r=local->ErasePassword();
3772 // ...media change to ensure a fresh remount the drive
3773 r = local->ForceRemount(0);
3774 local->Status() = KErrNotReady;
3775 WritePasswordData();
3780 //-----------------------------------------------------------------------------------------
3782 TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags)
3784 // Force a remount of the drive
3787 __PRINT(_L("CFatMountCB::ForceRemountDrive"));
3788 TInt r=CreateDrive(Drive().DriveNumber());
3790 r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle);
3792 r=LocalDrive()->ForceRemount(aFlags);
3796 //-----------------------------------------------------------------------------------------
3798 void CFatMountCB::WritePasswordData()
3800 // Write store password data to disk
3803 __PRINT(_L("CFatMountCB::WritePasswordData"));
3804 TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
3805 mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
3806 __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile);
3807 TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
3808 TInt length=local.PasswordStoreLengthInBytes();
3811 WriteToDisk(mediaPWrdFile,_L8(""));
3814 HBufC8* hDes=HBufC8::New(length);
3817 TPtr8 pDes=hDes->Des();
3818 TInt r=local.ReadPasswordData(pDes);
3820 WriteToDisk(mediaPWrdFile,pDes);
3824 //-----------------------------------------------------------------------------------------
3827 Trim trailing spaces of volume label descriptor and adjust its length
3829 void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const
3831 // Locate first '\0'
3832 TInt nullPos = aLabel.Locate('\0');
3833 if (nullPos == KErrNotFound)
3834 nullPos = KVolumeLabelSize;
3836 // Trim trailing spaces
3838 for (i=nullPos-1; i>=0; --i)
3839 if (aLabel[i] != 0x20)
3841 aLabel.SetLength(i+1);
3844 //-----------------------------------------------------------------------------------------
3847 Searches for the volume label file
3849 @param aLabel The name of the volume label file returned upon successful search
3850 @return KErrNone if it finds the volume label file, otherwise KErrNotFound
3852 TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel)
3854 __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile"));
3855 TEntryPos pos(RootIndicator(),0);
3857 TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry));
3858 __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r);
3862 //-----------------------------------------------------------------------------------------
3865 Creates or updates the volume label file with name aNewName
3867 @param aNewName The new name for the volume label file
3869 void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName)
3871 __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName);
3872 TEntryPos pos(RootIndicator(),0);
3875 TBuf8<KVolumeLabelSize> oldName;
3876 TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry));
3880 // Found existing volume label file, rename or delete
3881 if(oldName == aNewName)
3883 __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match"));
3888 if(aNewName.Length() == 0)
3890 // delete the volume label file
3891 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete"));
3892 EraseDirEntryL(pos, entry);
3896 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace"));
3897 entry.SetName(aNewName);
3898 WriteDirEntryL(pos, entry);
3903 else if( KErrNotFound == r )
3905 // Not found, need to create if aNewName is not empty
3906 // Windows allows a volume label file to have the same name as
3907 // an existing file or directory
3908 if(aNewName.Length() > 0)
3910 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create"));
3911 TEntryPos dirPos(RootIndicator(),0);
3912 AddDirEntryL(dirPos,1);
3913 TFatDirEntry fatDirEntry;
3914 fatDirEntry.SetName(aNewName);
3915 fatDirEntry.SetAttributes(KEntryAttVolume);
3918 now.UniversalTime();
3919 fatDirEntry.SetTime(now, TimeOffset() );
3920 fatDirEntry.SetStartCluster(0);
3921 fatDirEntry.SetSize(0);
3922 WriteDirEntryL(dirPos, fatDirEntry);
3934 //-----------------------------------------------------------------------------------------
3937 Scans the root directory for a volume label file. Leaves with an error if not found
3939 @param aLabel Name of the volume label file upon successful search
3940 @param aDosEntryPos Pointer to position of the volume label file upon successful search
3941 @param aDosEntry Contains the entry for the volume label file upon successful search
3943 void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
3945 __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL"));
3947 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
3949 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root"));
3950 User::Leave(KErrNotFound); // Allows maximum number of entries in root directory
3953 TInt previousCluster= aDosEntryPos.iCluster;
3954 TUint previousPosition= aDosEntryPos.iPos;
3955 TInt changePreviousCluster=1;
3958 TFatDirEntry startEntry;
3959 TFileName dummyLongName;
3964 const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName);
3965 __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e);
3966 User::LeaveIfError(e);
3968 User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName));
3970 if(aDosEntry.IsEndOfDirectory())
3972 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir"));
3973 User::Leave(KErrNotFound);
3975 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3977 if(aDosEntry.IsErased())
3979 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root"));
3980 User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
3983 if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage())
3985 if(aDosEntry.Attributes() & KEntryAttVolume)
3987 aLabel = aDosEntry.Name();
3989 dummyLongName.Copy(aLabel);
3990 __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName);
3995 MoveToNextEntryL(aDosEntryPos);
3996 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
3998 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root"));
3999 User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
4001 if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
4003 DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count);
4005 previousPosition=aDosEntryPos.iPos;
4009 //-----------------------------------------------------------------------------------------
4012 Read volume label from disk and trim trailing 0x20 & 0x00 characters
4014 void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector)
4016 // Read volume label as 8 bit descriptor
4017 TBuf8<KVolumeLabelSize> volName8;
4018 TInt r = ReadVolumeLabelFile(volName8);
4019 if(r != KErrNone) // No volume label file in root directory
4020 volName8 = aBootSector.VolumeLabel();
4021 TrimVolumeLabel(volName8);
4023 TBuf16<KVolumeLabelSize> volName16;
4024 LocaleUtils::ConvertToUnicodeL(volName16, volName8);
4025 SetVolumeName(volName16.AllocL());
4029 //-----------------------------------------------------------------------------------------
4032 Populates iMap member of aInfo with contiguous block group maps.
4034 @param aPos Start position for a desired section of the file.
4035 @param sLength Length of the desired data to produce the block map for.
4036 @param aInfo A structure describing a group of block maps.
4038 void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo)
4040 __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL"));
4041 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
4042 TBlockMapEntry blockMapEntry;
4045 TInt clusterRelativePos;
4048 TInt clusterListLen;
4052 TLocalDriveCapsBuf caps;
4054 TInt64 realPosition = 0;
4058 currentPos = aPos.iPos;
4059 temp = currentPos>>ClusterSizeLog2();
4060 if ( (currentPos) && ( (currentPos) == (temp<<ClusterSizeLog2()) ) )
4062 if (!FAT().GetNextClusterL(aPos.iCluster))
4064 __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL corrupt#1"))
4065 User::Leave(KErrCorrupt);
4068 clusterRelativePos = ClusterRelativePos( aPos.iPos );
4069 maxClusters = ((aLength + clusterRelativePos - 1)>>ClusterSizeLog2())+1;
4070 clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters);
4071 readLength = Min( aLength, (clusterListLen<<ClusterSizeLog2()) - clusterRelativePos);
4073 blockMapEntry.SetNumberOfBlocks( clusterListLen );
4074 if (aPos.iCluster < 2)
4075 User::Leave(KErrCorrupt);
4076 r = LocalDrive()->Caps(caps);
4077 if ( r != KErrNone )
4078 User::LeaveIfError(r);
4079 if ( caps().iType&EMediaRam )
4081 realPosition = FAT().DataPositionInBytes( aPos.iCluster );
4082 aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2());
4083 blockMapEntry.SetStartBlock( aPos.iCluster );
4086 blockMapEntry.SetStartBlock( aPos.iCluster - 2);
4087 aInfo.iMap.Append(TPckgC<TBlockMapEntry>(blockMapEntry));
4088 aPos.iPos += readLength;
4089 aPos.iCluster = endCluster;
4090 aLength -= readLength;
4092 while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) );
4096 //-----------------------------------------------------------------------------------------
4098 TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
4100 TFatDirEntry firstEntry;
4101 TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory
4102 FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
4104 aDosEntryPos=firstEntryPos;
4105 aDosEntry=firstEntry;
4106 MoveToDosEntryL(aDosEntryPos,aDosEntry);
4111 //-----------------------------------------------------------------------------------------
4113 TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId)
4115 // Get first cluster of file
4116 TEntryPos dosEntryPos(RootIndicator(),0);
4117 TFatDirEntry dosEntry;
4118 InitializeRootEntry(dosEntry); // Nugatory initialisation to placate warnings
4119 TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry));
4122 TInt startCluster=StartCluster(dosEntry);
4123 // Empty files will return a cluster of zero
4126 aUniqueId=MAKE_TINT64(0,startCluster);
4129 //-----------------------------------------------------------------------------------------
4132 TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4134 return KErrNotSupported;
4137 TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4139 return KErrNotSupported;
4142 TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4144 return KErrNotSupported;
4147 //-----------------------------------------------------------------------------------------
4149 //-- maximal level of recursion for the CheckDisk. i.e. the max. number of folded directories to check.
4150 const TInt KCheckDskMaxRecursionLevel = 50;
4153 // Walks a directory cluster list then checks all the entries.
4155 void CFatMountCB::ChkDirL(RBitVector& aFatBitVec, TInt aDirCluster)
4157 //__PRINT1(_L("### CFatMountCB::ChkDirL() level:%d"),iChkDiscRecLevel);
4159 //-- check if we have reached the recursion limit. on hardware the stack is very limited
4160 //-- and its overflow will lead to crash.
4161 if(iChkDiscRecLevel++ >= KCheckDskMaxRecursionLevel)
4163 __PRINT1(_L("CFatMountCB::ChkDirL() max recursion level(%d) reached. Leaving!"),iChkDiscRecLevel);
4164 User::Leave(KErrTooBig);
4167 if(/*Is32BitFat() &&*/aDirCluster != 0 && (aDirCluster == RootIndicator()))//the bit in comments maybe required
4168 WalkClusterListL(aFatBitVec, RootIndicator());
4170 TEntryPos entryPos(aDirCluster,0);
4174 ReadDirEntryL(entryPos,entry);
4175 MoveToDosEntryL(entryPos,entry);
4176 if (entry.IsEndOfDirectory())
4178 if (IsRootDir(entryPos)&&(StartOfRootDirInBytes()+entryPos.iPos==(RootDirEnd()-KSizeOfFatDirEntry)))
4180 if(!entry.IsErased())
4181 ChkEntryL(aFatBitVec, entry);
4182 break; // Allows maximum number of entries in root directory
4184 MoveToNextEntryL(entryPos);
4185 if (entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased())
4187 ChkEntryL(aFatBitVec, entry);
4193 //-----------------------------------------------------------------------------------------
4196 // Check FAT is valid for anEntry
4198 void CFatMountCB::ChkEntryL(RBitVector& aFatBitVec, const TFatDirEntry& anEntry)
4202 if ((anEntry.Attributes()&(KEntryAttDir)) || anEntry.Size())
4203 listLength=WalkClusterListL(aFatBitVec, StartCluster(anEntry));
4204 else if (anEntry.StartCluster() != 0) // zero length file
4205 User::Leave(EFatChkDskInvalidEntrySize); // shouldn't have clusters
4207 if (anEntry.Attributes()&KEntryAttDir)
4208 ChkDirL(aFatBitVec, StartCluster(anEntry));
4210 // Check that the correct number of clusters have been allocated for the size of the file.
4212 else if ((anEntry.Attributes()&KEntryAttVolume)==0)
4214 TInt clustersForFileSize;
4215 TInt clusterSize=1<<ClusterSizeLog2();
4216 clustersForFileSize = (TInt) ( (TInt64(anEntry.Size()) + TInt64(clusterSize-1)) >> ClusterSizeLog2() );
4218 if (listLength!=clustersForFileSize)
4219 User::Leave(EFatChkDskInvalidEntrySize);
4223 //-----------------------------------------------------------------------------------------
4226 // Walks cluster list from aCluster to EOF
4227 // Reports an error if an invalid cluster is found before EOF or
4228 // a cluster has been visited before.
4230 TInt CFatMountCB::WalkClusterListL(RBitVector& aFatBitVec, TInt aCluster)
4236 if (!ValidClusterNumber(aCluster))
4238 __PRINT1(_L("Bad Cluster number %d"),aCluster);
4239 User::Leave(EFatChkDskIllegalClusterNumber);
4242 if (IsClusterVisited(aFatBitVec, aCluster))
4244 __PRINT1(_L("Cluster already in use %d"),aCluster);
4245 User::Leave(EFatChkDskClusterAlreadyInUse);
4248 MarkClusterVisited(aFatBitVec, aCluster);
4250 } while (FAT().GetNextClusterL(aCluster));
4255 //-----------------------------------------------------------------------------------------
4258 // Checks that all unvisited clusters are marked as free in the FAT
4260 void CFatMountCB::CheckUnvisitedClustersL(const RBitVector& aFatBitVec) const
4264 TInt maxCluster=cluster + UsableClusters();
4265 while (cluster<maxCluster)
4267 cluster=NextUnvisitedCluster(aFatBitVec, cluster); //-- move to the next unvisited cluster
4268 if(cluster < 0 || cluster >= maxCluster)
4271 TInt clusterVal=FAT().ReadL(cluster);
4272 if (clusterVal!=0 && IsEndOfClusterCh(clusterVal)==EFalse && !IsBadCluster(clusterVal))
4274 __PRINT1(_L("\n*****Bad cluster Num = %d"),cluster);
4275 User::Leave(EFatChkDskBadCluster);
4280 //-----------------------------------------------------------------------------------------
4283 @param aCluster cluster number to check for validity
4284 @returns ETrue if aCluster is a valid cluster number
4286 TBool CFatMountCB::ValidClusterNumber(TUint32 aCluster) const
4288 return (aCluster>=KFatFirstSearchCluster && aCluster<=MaxClusterNumber());
4291 //-----------------------------------------------------------------------------------------
4294 // Walk the FAT, returns error if find an unterminated list or
4295 // lists that merge.
4297 TInt CFatMountCB::CheckDisk()
4300 __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber());
4302 if(!ConsistentState())
4305 //-- create a bit representation of the FAT
4306 const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
4309 // cluster count may be zero if boot sector failed to be read (e.g. if the media is locked)
4310 // or if TDrive::MountMedia(ETrue) has been called (in which case the boot sector may
4311 if (MaxClusters == 0)
4314 RBitVector bitVec; //-- each bit in this vector represents a FAT cluster
4316 TInt nRes = bitVec.Create(MaxClusters);
4317 if(nRes != KErrNone)
4319 ASSERT(nRes == KErrNoMemory); //-- the only one possible reason.
4320 return KErrNoMemory;
4323 iChkDiscRecLevel = 0; //-- reset CheckDisk recursion counter
4324 TRAPD(r,ChkDirL(bitVec, RootIndicator())); // Check from root directory
4327 TRAP(r,CheckUnvisitedClustersL(bitVec));
4339 case EFatChkDskIllegalClusterNumber:
4342 case EFatChkDskClusterAlreadyInUse:
4345 case EFatChkDskBadCluster:
4348 case EFatChkDskInvalidEntrySize:
4359 //-----------------------------------------------------------------------------------------
4360 // Helper functions for Check Disk functionality
4361 //-----------------------------------------------------------------------------------------
4364 Find the next unvisited cluster number in the bit array.
4366 @param aBitList bit array, where '0' bits represent unvisited clusters.
4367 @param aCluster cluster number to start search with.
4369 @return positive integer indicating next unvisited cluster number
4370 KErrNotFound (-1) if there are no unvisited clusters
4373 static TInt NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster)
4375 __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
4377 TUint32 searchPos = aCluster; //-- bit number to start search with
4379 //-- look for the unvisited cluster (bit '0') in the bit array from the searchPos to the right
4380 if(aFatBitVec.Find(searchPos, 0, RBitVector::ERight))
4383 return KErrNotFound;
4388 Check if we have visited cluster aCluster
4390 @param aFatBitVec bit array, where '0' bits represent unvisited clusters.
4391 @param aCluster cluster number to check
4392 @return ETrue if aCluster has been visited.
4394 static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster)
4396 __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
4398 return aFatBitVec[aCluster];
4402 Mark aCluster as visited
4403 @param aFatBitVec bit array, where '0' bits represent unvisited clusters.
4404 @param aCluster cluster number to mark
4406 static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster)
4408 __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved
4410 aFatBitVec.SetBit(aCluster); //-- '1' bit in a bit array means that the corresponding cluster is visited
4415 //-------------------------------------------------------------------------------------------------------------------
4418 Creates a scan drive object and starts the scan.
4420 TInt CFatMountCB::DoRunScanDrive()
4424 CScanDrive* pScnDrv = NULL;
4425 TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
4426 if(nRes != KErrNone)
4429 TRAPD(nScnDrvRes, pScnDrv->StartL());
4431 const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
4436 {//-- ScanDrive found and probably fixed some errors.
4437 // ensure cached fat and free cluster count are updated
4438 DoDismount(); //-- dismount
4439 TRAP(nRes, MountL(EFalse)); //-- mount again
4442 if(nScnDrvRes != KErrNone)
4446 //-- if ScanDrive hasn't found anything wrong or has fixed recoverable erros, mark the volume clean
4447 if(VolCleanFlagSupported())
4449 //-- if there is a background FAT scanning thread, we need to wait until it finishes its work.
4450 //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting.
4451 (void)FAT().NumberOfFreeClusters(ETrue);
4452 TRAP(nRes, FinaliseMountL());
4453 ASSERT(nRes == KErrNone);
4459 //-------------------------------------------------------------------------------------------------------------------
4462 Run scan drive on the given volume.
4463 The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly.
4466 @return Either KErrCorrupt if an error was found that is not caused by write failure due to power removal.
4467 KErrNone if no error was found. One of four positive codes explaining what type of error was rectified
4469 TInt CFatMountCB::ScanDrive()
4471 __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber());
4473 if(!ConsistentState())
4480 __PRINT(_L("CFatMountCB::ScanDrive() locked!\n"));
4485 {//-- Do not check internal RAM drive
4486 __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive."));
4490 //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt
4491 if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume())
4493 TBool bVolClean = EFalse;
4494 TRAP(nRes, bVolClean = VolumeCleanL());
4496 if(nRes == KErrNone && bVolClean)
4498 __PRINT(_L("Skipping ScanDrive on finalised volume!"));
4499 return KErrNone; //-- skip ScanDrive on a clean volume
4505 if(nRes != KErrNone)
4508 nRes = DoRunScanDrive();
4512 __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes);
4518 //-----------------------------------------------------------------------------------------
4520 Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset
4521 if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not
4522 a null offset is returned.
4524 @return The offset in seconds that timestamps on the filesystem have, relative to UTC.
4526 TTimeIntervalSeconds CFatMountCB::TimeOffset() const
4528 if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() )
4530 return User::UTCOffset();
4534 return TTimeIntervalSeconds(0);
4541 //-----------------------------------------------------------------------------------------
4543 Check is this file system can be mounted on the drive at all.
4544 Just read and validate boot region, no real mounting overhead.
4546 @return KErrNone boot region is OK, the file system can be mounted.
4547 KErrLocked the media is locked on a physical level.
4548 other error codes depending on the implementation
4551 TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable()
4555 const TInt driveNo = Drive().DriveNumber();
4556 __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo);
4558 nRes = CreateDrive(driveNo);
4559 if(nRes != KErrNone)
4561 __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes);
4565 //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive()
4566 TFatBootSector bootSector;
4567 nRes = ReadBootSector(bootSector);
4569 DismountedLocalDrive();