Update contrib.
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
25 #include "sl_leafdir_cache.h"
26 #include "sl_dir_cache.h"
27 #include "sl_scandrv.h"
30 TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively);
33 //-----------------------------------------------------------------------------------------
35 TFatVolParam::TFatVolParam()
37 Mem::FillZ(this, sizeof(TFatVolParam));
41 populate the object with the values from the boot sector.
42 @param aBootSector a reference to the valid boots sector
44 void TFatVolParam::Populate(const TFatBootSector& aBootSector)
46 ASSERT(aBootSector.IsValid());
48 iSectorsPerCluster = aBootSector.SectorsPerCluster();
49 iSectorSizeLog2 = Log2(aBootSector.BytesPerSector());
50 iClusterSizeLog2 = iSectorSizeLog2+Log2(iSectorsPerCluster);
51 iFirstFatSector = aBootSector.FirstFatSector();
52 iNumberOfFats = aBootSector.NumberOfFats();
53 iFatSizeInBytes = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector();
54 iTotalSectors = aBootSector.VolumeTotalSectorNumber();
55 iRootClusterNum = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16
57 iRootDirectorySector = aBootSector.RootDirStartSector();
58 iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32
60 //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16
61 iFSInfoSectorNum = aBootSector.FSInfoSectorNum();
62 iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector
65 TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const
67 ASSERT(&aRhs != this);
69 return ETrue; //-- comparing with itself
71 return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0);
75 //-----------------------------------------------------------------------------------------
78 CFatMountCB::CFatMountCB()
80 __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName);
85 DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only
88 CFatMountCB::~CFatMountCB()
90 __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this);
101 //-----------------------------------------------------------------------------------------
103 CFatMountCB* CFatMountCB::NewL()
105 CFatMountCB* pSelf = new(ELeave) CFatMountCB;
107 CleanupStack::PushL(pSelf);
109 CleanupStack::Pop(pSelf);
114 // second-stage constructor
115 void CFatMountCB::ConstructL()
118 iNotifier = CAsyncNotifier::New();
122 User::Leave(KErrNoMemory);
125 iNotifier->SetMount(this);
128 //-------------------------------------------------------------------------------------------------------------------
131 Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount.
132 @return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready.
133 @see CMountCB::FileSystemClusterSize()
135 TInt CFatMountCB::ClusterSize() const
137 if (ClusterSizeLog2())
138 return (1 << ClusterSizeLog2());
143 //-------------------------------------------------------------------------------------------------------------------
146 @leave KErrAccessDenied if the mount is read-only
148 void CFatMountCB::CheckWritableL() const
152 __PRINT(_L("CFatMountCB is RO!"));
153 User::Leave(KErrAccessDenied);
158 @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory operations can not be performed
160 void CFatMountCB::CheckStateConsistentL() const
162 if(!ConsistentState())
164 __PRINT(_L("CFatMountCB state is inconsistent !"));
165 User::Leave(KErrCorrupt);
170 //-------------------------------------------------------------------------------------------------------------------
172 Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
173 @return ETrue if volume parameters remained same.
175 TBool CFatMountCB::CheckVolumeTheSame()
177 //-- initialise local drive
178 TInt nRes =InitLocalDrive();
182 //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it.
183 TFatBootSector bootSector;
184 nRes = ReadBootSector(bootSector, iRamDrive);
188 //-- 1. check volume Uid
189 if(iUniqueID != bootSector.UniqueID())
192 //-- check volume parameters, they must remain the same
193 TFatVolParam volParam;
194 volParam.Populate(bootSector);
196 if(!(volParam == iVolParam))
203 //-------------------------------------------------------------------------------------------------------------------
206 Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
207 If they are, re-initialises the mount.
209 void CFatMountCB::DoReMountL()
212 if(!CheckVolumeTheSame())
213 User::Leave(KErrGeneral);
215 //-- get drive capabilities
216 TLocalDriveCapsV2Buf capsBuf;
217 User::LeaveIfError(LocalDrive()->Caps(capsBuf));
219 //-- the volume is the same as it was on original MountL()
220 //-- we need to re-initialize for the case when the media was removed, FAT or directory structure changed on other device and the media returned back.
225 InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting.
229 //-------------------------------------------------------------------------------------------------------------------
232 Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and
233 if they are, re-initialises the mount. This includes resetting all caches.
234 ! Do not call this method from TDriveInterface methods, like citical and non-critical notifiers ! This can lead to the
235 recursive loops and undefined behaviour.
237 @return KErrNone if the remount was OK
238 system-wide error code otherwise
240 TInt CFatMountCB::ReMount()
242 __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State());
244 const TFatMntState currState = State();
246 //-- analyse the mount state and find out if we can remount at all.
250 __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!"));
255 //-- correct state, proceed to remount
260 //-- there are 2 options here:
261 //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it)
262 //-- in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards
263 //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.)
264 //-- in this case we may need to fake the success. This can only happen on forced mount by CFormatCB
266 TRAP(nRes, DoReMountL());
270 //-- note that the mount may be here left in inconsistent state (EMounting)
271 //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and
272 //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this.
273 __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes);
275 //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted.
276 //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and
277 //-- it is used for performing a format. If the format has finished, but RFormat isn't closed yet and we try to access the volume,
278 //-- we will get here, because all members of the constructed mount will be zeroes.
279 if(currState == EInit_Forced)
281 __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!"));
289 __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber());
294 //-------------------------------------------------------------------------------------------------------------------
297 Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is
301 void CFatMountCB::InvalidateLeafDirCache()
305 iLeafDirCache->Reset();
309 User::Free(iLastLeafDir);
314 //-------------------------------------------------------------------------------------------------------------------
317 Delete mount's caches
318 Moves CFatMountCB into ENotMounted state immediately.
320 void CFatMountCB::DoDismount()
322 __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber());
324 //-- try to flush and destroy FAT cache
327 if(!ConsistentState() || ReadOnly())
328 {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either.
329 iFatTable->Dismount(ETrue);
332 {//-- Try to flush the FAT - if this fails there's not much we can do
333 TRAPD(r, iFatTable->FlushL());
334 iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache
338 //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting
339 //-- see CFatMountCB::InitializeL()
340 delete iLeafDirCache;
341 iLeafDirCache = NULL;
343 //-- destroy directory cache, this cache will be re-created while mounting or re-mounting
344 //-- see CFatMountCB::InitializeL()
348 //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive,
349 //-- i.e. iFatTable & iRawDisk are valid
350 SetState(EDismounted);
353 //-----------------------------------------------------------------------------------------
355 /** old implementation */
356 void CFatMountCB::FinaliseMountL()
358 FinaliseMountL(RFs::EFinal_RW);
361 //-----------------------------------------------------------------------------------------
363 Dismount the CFatMountCB and the drive.
364 called from TDrive::Dismount().
366 void CFatMountCB::Dismounted()
368 __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber());
370 //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media.
371 //-- this method may be called after the media change occured from the TDrive::Dismount(). It means
372 //-- that if we try to write some data here, they could be written into a different medium, if it had been
373 //-- physically changed.
375 const TFatMntState prevState = State();
377 DoDismount(); //-- it will change mount state to EDismounted
378 DismountedLocalDrive();
380 //-- check if the previous state was EInit_Forced, which means that this method was called
381 //-- on the mount that might not be alive (no valid iFatTable & iRawDisk).
382 //-- This can happen only during format operation on non-mounted previously volume.
383 //-- this EInit_Forced state must be processed separately, see ::Remount()
384 if(prevState == EInit_Forced)
385 SetState(EInit_Forced);
390 //-------------------------------------------------------------------------------------------------------------------
393 Find out if the mount is finalised.
394 @param aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise.
395 @return standard error codes.
397 TInt CFatMountCB::IsFinalised(TBool& aFinalised)
401 case EFinalised: //-- already explicitly finalised
405 case EInit_W: //-- the volume had been written
409 default: //-- it depends on the state
413 //-- find out if the volume is _physically_ finalised.
414 //-- It can be in the state EInit_R, but finalised before mounting
415 if(!VolCleanFlagSupported())
416 return KErrNotSupported;
418 TInt nRes = KErrNone;
419 TRAP(nRes, aFinalised = VolumeCleanL());
424 //-------------------------------------------------------------------------------------------------------------------
427 @return ETrue if the mount is in consistent state i.e. normally mounted.
428 See TFatMntState enum for more detail.
430 TBool CFatMountCB::ConsistentState() const
432 return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised);
435 //-------------------------------------------------------------------------------------------------------------------
438 Open CFatMountCB for write. I.e. perform some actions on the first write attempt.
439 This is a callback from TDriveInterface.
440 @return System wide error code.
442 TInt CFatMountCB::OpenMountForWrite()
444 if(State() == EInit_W)
445 return KErrNone; //-- nothing to do, the mount is already opened for write
447 __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber());
449 ASSERT(State() == EInit_R || State() == EFinalised);
451 //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and
452 //-- can not call TDriveInterface methods that call this method etc.
453 ASSERT(iCBRecFlag == 0);
454 DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag
456 //-- do here some "opening" work, like marking volme as dirty
457 //-- be careful here, as soon as this is a callback from TDriveInterface, writing via TDriveInterface may cause some unwanted recursion.
459 //-- mark the volume as dirty
461 TRAP(nRes, SetVolumeCleanL(EFalse));
467 DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag
473 //-------------------------------------------------------------------------------------------------------------------
476 Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily.
478 void CFatMountCB::UnFinaliseMountL()
484 SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty"
489 return; //-- nothing to do
492 //-- other mount states are inconsistent; can't perform this operation
493 User::Leave(KErrAbort);
500 //-------------------------------------------------------------------------------------------------------------------
505 @param aOperation describes finalisation operation ,see RFs::TFinaliseDrvMode
506 @param aParam1 not used, for future expansion
507 @param aParam2 not used, for future expansion
509 @leave System wide error code. particular cases:
510 KErrArgument invalid arguments
511 KErrInUse if the volume has opened objects (files, directories etc)
512 KErrCorrupt if the volume is corrupt
515 void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/)
517 __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber());
525 case RFs::EForceUnfinalise:
530 __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation);
532 User::Leave(KErrArgument);
536 //-- mount finalisation work
538 ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO);
540 if(State() == EFinalised)
541 {//-- the mount is already finalised. All we can do is to set it to RO mode
542 if(ReadOnly() && aOperation == RFs::EFinal_RW)
544 User::Leave(KErrAccessDenied); //-- can't override RO flag
547 (void)LocalDrive()->Finalise(ETrue);
549 if(aOperation == RFs::EFinal_RO)
558 if(LockStatus() != 0)
559 {//-- can't finalise the volume if it has opened objects and not in the consistent state.
560 //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present,
561 //-- it's impossible to detect such situation.
562 User::Leave(KErrInUse);
565 if(State() != EInit_R && State() != EInit_W)
566 {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt.
567 __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State());
568 User::Leave(KErrCorrupt);
574 //-- for FAT32 we may need to update data in FSInfo sectors
577 if(FAT().ConsistentState())
578 {//-- FAT table state is consistent and the number of free clusters is known.
579 //-- Do it disregarding the mount state, it may help in the situation when 2 copies of the FSInfo are different for some reason.
580 DoUpdateFSInfoSectorsL(EFalse);
583 {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working.
584 //-- in this case we can't put corect values into the FSInfo.
585 if(State() == EInit_W)
586 {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time.
587 //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo.
588 __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo"));
589 DoUpdateFSInfoSectorsL(ETrue);
592 {//-- no changes on the volume, just do not update FSInfo
593 __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated"));
596 }//if(FAT().ConsistentState())
602 //-- mark the volume as clean
603 SetVolumeCleanL(ETrue);
605 //-- finally, put the volume into RO mode if required
606 if(aOperation == RFs::EFinal_RO)
609 SetState(EFinalised);
613 //-------------------------------------------------------------------------------------------------------------------
616 @return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12
618 TBool CFatMountCB::VolCleanFlagSupported() const
620 const TFatType fatType=FatType();
622 ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32);
623 return (fatType != EFat12);
626 //-----------------------------------------------------------------------------------------
630 Obtain the volume information.
631 All information except iSize and iFree has been added by TDrive::Volume().
633 @param aVolume on return will contain iSize & iFree fields filled with actual data.
635 void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const
638 //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running.
639 //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field.
640 //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters
641 //-- will be returned.
642 const TBool bSyncOp = !aVolume.iVolSizeAsync;
643 aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side
645 __PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp);
646 const TDriveInfo& drvInfo=aVolume.iDrive;
648 #if defined(__EPOC32__)
649 // if RAM drive, cap size according to HAL.
650 if (drvInfo.iType==EMediaRam)
652 TLocalDriveCapsV2Buf caps;
653 LocalDrive()->Caps(caps);
655 const TInt max_drive_size=TInt(caps().iEraseBlockSize);
656 const TInt cur_drive_size=I64INT(caps().iSize);
658 aVolume.iSize=max_drive_size;
659 aVolume.iFree=max_drive_size-cur_drive_size;
661 aVolume.iSize=aVolume.iFree+iSize;
664 if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone)
666 // iSize will never grow beyond maxRam because of a check in medint.
667 // d <= f; (s{f} + f) - m <= f; s{f} <= m
668 __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid));
669 if (aVolume.iSize > maxSize)
671 TInt64 d = aVolume.iSize - maxSize;
672 __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid));
678 aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
679 aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<<ClusterSizeLog2(); //-- round down to cluster size
682 }//if (drvInfo.iType==EMediaRam)
687 const TUint32 freeClusters = FAT().NumberOfFreeClusters(bSyncOp);
689 __PRINT1(_L("CFatMountCB::VolumeL() free clusters:%d"), freeClusters);
691 aVolume.iFree = (TInt64)freeClusters << ClusterSizeLog2();
693 if (drvInfo.iType==EMediaRam)
694 aVolume.iSize=aVolume.iFree+iSize;
696 aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
697 aVolume.iSize=(aVolume.iSize >> ClusterSizeLog2()) << ClusterSizeLog2(); //-- round down to cluster size
702 //-----------------------------------------------------------------------------------------
705 // Set the volume label (write aVolume label into BPB & Volume Label File)
706 // aName string may be zero length but is assumed to contain no illegal characters or NULLs.
708 void CFatMountCB::SetVolumeL(TDes& aName)
711 __PRINT(_L("CFatMountCB::SetVolumeL"));
713 CheckStateConsistentL();
716 __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName));
718 TBuf8<KVolumeLabelSize> buf8(KVolumeLabelSize);
720 LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave);
722 LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before)
724 const TInt lengthOfBuf8=buf8.Length();
725 // Pad to end with spaces if not empty.
726 if (lengthOfBuf8>0 && lengthOfBuf8<KVolumeLabelSize)
728 buf8.SetLength(KVolumeLabelSize);
729 Mem::Fill(&buf8[lengthOfBuf8],KVolumeLabelSize-lengthOfBuf8,' ');
732 // Write a volume label file
733 WriteVolumeLabelFileL( buf8 );
735 // Write the boot sector volume label
736 // Always pad to full length with spaces
739 buf8.Fill(' ',KVolumeLabelSize);
742 WriteVolumeLabelL(buf8);
745 //-----------------------------------------------------------------------------------------
749 @param aName full path to the directory to create. Name validity is checked by file server.
750 all trailing dots from the name will be removed
752 void CFatMountCB::MkDirL(const TDesC& aName)
754 __PRINT2(_L("CFatMountCB::MkDirL, drv:%d, %S"), DriveNumber(), &aName);
756 CheckStateConsistentL();
759 TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
761 TInt namePos=dirName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
762 TPtrC name=dirName.Mid(namePos);
763 TLeafDirData leafDir;
764 const TEntryPos dirPos(FindLeafDirL(dirName.Left(namePos), leafDir),0);
765 TEntryPos dumPos=dirPos;
766 TFatDirEntry dumEntry;
768 TBool isOriginalNameLegal = IsLegalDosName(name,EFalse,EFalse,EFalse,EFalse,ETrue);
769 iFileCreationHelper.InitialiseL(name);
772 TFatDirEntry startEntry;
774 TRAPD(ret,DoFindL(name,KEntryAttMaskSupported,
775 startPos,startEntry,dumPos,dumEntry,
776 fileName,KErrNotFound,
777 &iFileCreationHelper,
780 if (ret!=KErrNotFound && ret!=KErrNone)
782 if (ret!=KErrNotFound)
784 if (dumEntry.Attributes()&KEntryAttDir)
785 User::Leave(KErrAlreadyExists);
787 User::Leave(KErrAccessDenied);
789 TShortName shortName;
791 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
793 GenerateShortNameL(dirPos.iCluster,name,shortName,ETrue);
797 if (isOriginalNameLegal==EFalse)
798 numEntries=NumberOfVFatEntries(name.Length());
801 if (iFileCreationHelper.IsNewEntryPosFound())
803 dumPos = iFileCreationHelper.EntryAddingPos();
806 AddDirEntryL(dumPos,numEntries); // Directory entry in leaf directory
810 //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
811 startCluster=FAT().AllocateSingleClusterL(dumPos.iCluster ? dumPos.iCluster : FAT().FreeClusterHint());
814 TRAPD(r, InitializeFirstDirClusterL(startCluster,dirPos.iCluster));
819 FAT().MarkAsBadClusterL(startCluster);
821 TFatDirEntry fatDirEntry;
822 fatDirEntry.SetName(shortName);
823 fatDirEntry.SetAttributes(KEntryAttDir);
826 fatDirEntry.SetTime(now, TimeOffset());
827 fatDirEntry.SetCreateTime(now, TimeOffset());
828 fatDirEntry.SetStartCluster(startCluster);
829 fatDirEntry.SetSize(0);
830 if (isOriginalNameLegal)
831 WriteDirEntryL(dumPos,fatDirEntry);
833 WriteDirEntryL(dumPos,fatDirEntry,name);
835 iFileCreationHelper.Close();
838 //-----------------------------------------------------------------------------------------
841 Setup 1st cluster of the new directory
843 @param aStartCluster this entry start cluster number
844 @param aParentCluster parent entry start cluster number
846 void CFatMountCB::InitializeFirstDirClusterL(TInt aStartCluster,TInt aParentCluster)
848 const TUint32 KClusterSz= 1<<ClusterSizeLog2();
849 const TUint32 KMaxBufSz = KClusterSz; //-- max. nuffer size is a cluster
850 const TUint32 KMinBufSz = 1<<SectorSizeLog2(); //-- min. buffer size is 1 sector (for OOM case)
852 //-- allocate a buffer for directory file 1st cluster initialisation
854 CleanupClosePushL(buf);
856 if(buf.CreateMax(KMaxBufSz) != KErrNone)
857 buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
861 //-- copy "." directory entry to the buffer
863 //-- "." directory entry
867 entry.SetTime(now, TimeOffset() );
868 entry.SetAttributes(KEntryAttDir);
869 entry.SetCurrentDirectory();
870 entry.SetStartCluster(aStartCluster);
871 Mem::Copy(&buf[0],&entry,KSizeOfFatDirEntry);
873 //-- append ".." directory entry
874 entry.SetParentDirectory();
875 entry.SetStartCluster(aParentCluster==RootIndicator() ? 0 : aParentCluster);
876 Mem::Copy(&buf[0]+KSizeOfFatDirEntry,&entry,KSizeOfFatDirEntry);
878 TEntryPos entryPos(aStartCluster,0);
880 //-- write buffer to the beginning of the directory file.
881 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
883 //-- fill in the rest of the cluster if we used a small buffer
884 if((TUint32)buf.Size() < KClusterSz) //-- use special interface to access FAT directory file
887 const TInt restCnt = SectorsPerCluster() - 1;
890 for(TInt i=0; i<restCnt; ++i)
892 entryPos.iPos += KMinBufSz;
893 DirWriteL(entryPos, buf); //-- use directory cache when dealing with directories
898 CleanupStack::PopAndDestroy(&buf);
902 //-----------------------------------------------------------------------------------------
906 @param aName directory name
907 all trailing dots from the name will be removed
909 void CFatMountCB::RmDirL(const TDesC& aName)
911 __PRINT2(_L("CFatMountCB::RmDirL, drv:%d, %S"), DriveNumber(), &aName);
913 CheckStateConsistentL();
916 TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
918 TFatDirEntry dirEntry;
919 TEntryPos dirEntryPos(RootIndicator(),0); // Already checked entry is a directory
920 FindEntryStartL(dirName,KEntryAttMatchMask|KEntryAttMatchExclusive,dirEntry,dirEntryPos);
921 TEntryPos dosEntryPos=dirEntryPos;
922 TFatDirEntry dosEntry=dirEntry;
923 MoveToDosEntryL(dosEntryPos,dosEntry);
924 if (!IsDirectoryEmptyL(StartCluster(dosEntry)))
925 User::Leave(KErrInUse);
926 // Remove the directory from cache before erasing
927 if(iLeafDirCache && iLeafDirCache->CacheCount() > 0)
929 iLeafDirCache->RemoveDirL(StartCluster(dosEntry));
932 EraseDirEntryL(dirEntryPos,dirEntry);
933 FAT().FreeClusterListL(StartCluster(dosEntry));
937 //-----------------------------------------------------------------------------------------
941 @param aName file name
942 all trailing dots from the name will be removed
944 void CFatMountCB::DeleteL(const TDesC& aName)
946 __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName);
948 CheckStateConsistentL();
951 TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
953 TFatDirEntry firstEntry;
954 TEntryPos firstEntryPos(RootIndicator(),0);
955 FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
956 TEntryPos dosEntryPos=firstEntryPos;
957 TFatDirEntry dosEntry=firstEntry;
958 MoveToDosEntryL(dosEntryPos,dosEntry);
959 if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir))
960 User::Leave(KErrAccessDenied);
961 // Can not delete a file if it is clamped
962 CMountCB* basePtr=(CMountCB*)this;
963 TInt startCluster=StartCluster(dosEntry);
964 if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0)
965 User::Leave(KErrInUse);
966 EraseDirEntryL(firstEntryPos,firstEntry);
967 FAT().FreeClusterListL(StartCluster(dosEntry));
971 //-----------------------------------------------------------------------------------------
975 Rename or replace a directory entry.
976 Assumes all files are closed and replace is only passed files.
977 Assumes rename target does not exist or is the source file.
979 --------------- operating mode --------------------------------------------
983 aOldName exists | aNewName exists | result
984 N N leave KErrNotFound
985 N Y leave KErrNotFound
986 Y N rename aOldName -> aNewName
987 Y Y leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing
991 N N leave KErrNotFound
992 N Y leave KErrNotFound
993 Y N rename aOldName -> aNewName
994 Y Y contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then.
997 @param aOldName entry name to be renamed or replaced
998 @param aNewName a new entry name
999 @param aMode specifies renaming / replacing
1000 @param aNewDosEntryPos on exit contains new entry Pos.
1002 void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos)
1004 __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName);
1006 const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive.
1007 const TBool renameMode = (aMode == EModeRename);
1008 const TBool replaceMode = !renameMode;
1011 if(namesAreIdentical && replaceMode)
1012 return; //-- nothing to do, replacing file with itself
1014 //---------------------------------------------------------------------------------------------------------------------------
1015 //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName'
1017 TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset
1018 TEntryPos oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set
1020 FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos);
1022 TFatDirEntry oldName_DosEntry = oldName_FirstEntry; //-- "aOldName" entry set DOS entry
1023 TEntryPos oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry
1025 MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry);
1027 const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry
1029 //-- check if the file "aOldName" is clamped. In this case it can't be replaced.
1030 if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0))
1031 User::Leave(KErrInUse);
1033 //---------------------------------------------------------------------------------------------------------------------------
1034 //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not
1036 //-- extract new file name from the full path
1038 TPtrC ptrNewNameParentDir;
1039 const TInt delimPos = aNewName.LocateReverse(KPathDelimiter);
1040 ptrNewName.Set(aNewName.Mid(delimPos+1));
1041 ptrNewNameParentDir.Set(aNewName.Left(delimPos+1));
1043 //-- find the parent directory of the "aNewName" and create iterator for it
1044 TLeafDirData leafDir;
1045 const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator
1046 aNewName_DosEntryPos = aNewName_ParentDirPos;
1048 TEntryPos newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set
1049 TFatDirEntry newName_DosEntry;
1052 iFileCreationHelper.InitialiseL(ptrNewName);
1053 TFatDirEntry startEntry;
1055 TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported,
1056 newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry,
1057 fileName, KErrNotFound,
1058 &iFileCreationHelper,
1061 if (nRes!=KErrNone && nRes!=KErrNotFound)
1064 const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists.
1065 const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue);
1067 if(renameMode && newFileExists)
1069 if(!namesAreIdentical)
1071 if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir))
1073 User::Leave(KErrAccessDenied); //-- leave with KErrAccessDenied if it is trying to rename a file
1074 // to a dir or vice versa.
1076 User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists
1080 if(!bNewNameIsVFAT && !bOldNameIsVFAT)
1081 return; //-- renaming DOS name to itself
1083 //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File"
1086 //---------------------------------------------------------------------------------------------------------------------------
1088 if(replaceMode && newFileExists)
1090 //---------------------------------------------------------------------------------------------------------------------------
1091 //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries.
1093 //-- check if we are still trying to replace the file with itself, probably using short name alias
1094 if(aNewName_DosEntryPos == oldName_DosEntryPos)
1095 return; //-- nothing to do, it's the same file
1097 const TInt oldNameStartCluster = StartCluster(oldName_DosEntry);
1098 const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked
1100 newName_DosEntry.SetStartCluster(oldNameStartCluster);
1101 newName_DosEntry.SetSize(oldName_DosEntry.Size());
1102 newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0));
1103 newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes());
1108 //-- set a special Id in reserved section for old and new entries.
1109 //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain.
1110 //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry.
1111 //-- note that SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID.
1112 //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy.
1113 //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980.
1115 newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
1116 oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
1117 WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
1120 //-- write 'aNewName' DOS dir. entry data back
1121 WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry);
1123 //-- erase "oldName" entryset.
1124 EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
1126 //-- free 'aNewName' cluster list
1127 FAT().FreeClusterListL(newNameStartCluster);
1133 else //if(replaceMode && newFileExists)
1135 //---------------------------------------------------------------------------------------------------------------------------
1136 //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset
1138 TFatDirEntry newDosEntry = oldName_DosEntry;
1139 //-- generate short name for the 'aNewName' entryset and make new DOS entry
1141 {//-- need to generate a short name for VFAT entryset DOS entry
1142 TShortName shortName;
1144 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
1146 GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue);
1149 newDosEntry.SetName(shortName);
1152 {//-- just use 'aNewName' as DOS name.
1153 TBuf8<KFatDirNameSize+1> tmp; //-- the name may be "XXXXXXXX.YYY"
1154 tmp.Copy(ptrNewName);
1155 newDosEntry.SetName(DosNameToStdFormat(tmp));
1159 {//-- the the note(1) above
1160 newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
1161 oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
1162 WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
1165 //-- add new entryset to the directory
1166 aNewName_DosEntryPos.iPos = 0;
1167 aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster();
1169 if (iFileCreationHelper.IsNewEntryPosFound())
1171 aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos();
1176 const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length());
1177 AddDirEntryL(aNewName_DosEntryPos, numEntries);
1178 WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName);
1181 {//-- new name is one DOS entry only
1182 AddDirEntryL(aNewName_DosEntryPos, 1);
1183 WriteDirEntryL(aNewName_DosEntryPos, newDosEntry);
1186 //-- erase old entryset.
1187 EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
1189 //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry)
1190 if((newDosEntry.Attributes() & KEntryAttDir))
1192 TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry);
1194 TFatDirEntry chFatEnt;
1195 ReadDirEntryL(parentPtrEntPos, chFatEnt);
1197 const TUint parentDirStartCluster_Old = StartCluster(chFatEnt);
1198 TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster();
1200 if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0)
1201 {//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir.
1202 //-- must have starting cluster 0
1203 parentDirStartCluster_New = 0;
1206 if(parentDirStartCluster_Old != parentDirStartCluster_New)
1208 chFatEnt.SetStartCluster(parentDirStartCluster_New);
1209 WriteDirEntryL(parentPtrEntPos, chFatEnt);
1211 // Invalidate leaf dir cache as it is hard to track the dir structure changes now
1214 iLeafDirCache->Reset();
1217 }//else if(replaceMode && newFileExists)
1219 iFileCreationHelper.Close();
1222 //-----------------------------------------------------------------------------------------
1225 Rename 'aOldName' file/directory to 'aNewName'
1226 all trailing dots from the names will be removed
1228 @param aOldName existing object name
1229 @param aNewName new object name
1231 void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName)
1233 __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
1235 CheckStateConsistentL();
1238 TEntryPos newEntryPos;
1239 DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos);
1245 //-----------------------------------------------------------------------------------------
1248 Replace contents of the 'aNewName' with the contents of 'aOldName'
1249 all trailing dots from the names will be removed
1251 @param aOldName existing object name
1252 @param aNewName new object name
1254 void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName)
1257 __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
1259 CheckStateConsistentL();
1262 TEntryPos newEntryPos;
1263 DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos);
1268 //-----------------------------------------------------------------------------------------
1272 Try to find a directory entry by the given name and path.
1273 This method _must_ leave if the entry is not found. See the caller.
1275 @param aName path to the directory object. all trailing dots from the name will be removed.
1276 @param anEntry on return will contain the entry data
1278 @leave KErrPathNotFound if there is no path to the aName
1279 KErrNotFound if the entry corresponding to the aName is not found
1280 system-wide erorr code of media read failure.
1282 void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const
1284 __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName);
1286 CheckStateConsistentL();
1288 TEntryPos entryPos(RootIndicator(),0);
1290 TPtr fileName(anEntry.iName.Des());
1292 TPtrC fullName = RemoveTrailingDots(aName);
1293 TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
1294 TLeafDirData leafDir;
1295 entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir);
1298 TFatDirEntry startEntry;
1300 DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported,
1301 startPos,startEntry,entryPos,entry,
1302 fileName,KErrNotFound,
1307 anEntry.iAtt=entry.Attributes();
1308 anEntry.iSize=entry.Size();
1309 anEntry.iModified=entry.Time(TimeOffset());
1311 if (fileName.Length()==0)
1313 TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name()));
1314 LocaleUtils::ConvertToUnicodeL(fileName,dosName);
1316 if ((TUint)anEntry.iSize>=sizeof(TCheckedUid))
1317 ReadUidL(StartCluster(entry),anEntry);
1320 //-----------------------------------------------------------------------------------------
1323 Set directory entry details.
1324 @param aName entry name; all trailing dots from the name will be removed
1325 @param aTime entry modification time (and last access as well)
1326 @param aSetAttMask entry attributes OR mask
1327 @param aClearAttMask entry attributes AND mask
1330 void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
1332 __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName);
1334 CheckStateConsistentL();
1337 TEntryPos firstEntryPos(RootIndicator(),0);
1338 TFatDirEntry firstEntry;
1339 FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos);
1340 MoveToDosEntryL(firstEntryPos,firstEntry);
1341 TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
1342 if (setAttMask|aClearAttMask)
1344 TInt att=firstEntry.Attributes();
1346 att&=(~aClearAttMask);
1347 firstEntry.SetAttributes(att);
1349 if (aSetAttMask&KEntryAttModified)
1351 firstEntry.SetTime(aTime,TimeOffset());
1353 WriteDirEntryL(firstEntryPos,firstEntry);
1356 //-----------------------------------------------------------------------------------------
1358 void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const
1360 // Check one fat cluster for loops.
1364 if (aCluster==aPreviousCluster)
1365 User::Leave(KErrCorrupt); // Found loop
1368 if (aCount==aChangePreviousCluster)
1371 aChangePreviousCluster<<=1;
1372 aPreviousCluster=aCluster;
1376 //-----------------------------------------------------------------------------------------
1378 void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const
1384 TInt cluster=StartCluster(anEntry);
1385 if (cluster==0 && anEntry.Size()==0)
1388 TInt previousCluster=cluster;
1389 TInt changePreviousCluster=1;
1395 if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber()))
1396 User::Leave(KErrCorrupt);
1398 if(!FAT().GetNextClusterL(cluster))
1401 DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count);
1406 //-----------------------------------------------------------------------------------------
1409 Open/Create/Replace a file on the current mount.
1411 @param aName file name; all trailing dots from the name will be removed
1412 @param aMode File open mode, See TFileMode
1413 @param anOpen specifies action: open, create or replace the file
1414 @param aFile pointer to the CFileCB object to populate
1417 void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile)
1419 __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName);
1421 CheckStateConsistentL();
1423 TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
1425 TFatDirEntry firstEntry;
1426 TEntryPos firstEntryPos(RootIndicator(),0);
1427 TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
1428 TPtrC name(fullName.Mid(nPos));
1429 TInt ret = KErrNone;
1431 iFileCreationHelper.Close();
1432 if (anOpen == EFileCreate || anOpen == EFileReplace)
1434 iFileCreationHelper.InitialiseL(name);
1435 TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper));
1439 TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos));
1442 if (ret!=KErrNone && ret!=KErrNotFound)
1447 MoveToDosEntryL(firstEntryPos,firstEntry);
1448 if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume))
1449 User::Leave(KErrAccessDenied);
1450 if (anOpen==EFileCreate)
1451 User::Leave(KErrAlreadyExists);
1452 if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite)
1453 User::Leave(KErrAccessDenied);
1454 if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0))
1455 User::Leave(KErrInUse);
1456 CheckFatForLoopsL(firstEntry);
1460 if (anOpen==EFileOpen)
1461 User::Leave(KErrNotFound);
1463 //-- here we try to either create or replace file
1466 TLeafDirData leafDir;
1468 TInt numEntries = iFileCreationHelper.NumOfAddingEntries();
1469 TShortName shortName;
1470 if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
1472 firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
1473 GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue);
1476 if (iFileCreationHelper.IsNewEntryPosFound())
1478 firstEntryPos = iFileCreationHelper.EntryAddingPos();
1482 firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
1483 firstEntryPos.iPos=0;
1486 AddDirEntryL(firstEntryPos,numEntries);
1488 firstEntry.SetName(shortName);
1489 firstEntry.SetStartCluster(0);
1492 now.UniversalTime();
1493 firstEntry.SetCreateTime(now, TimeOffset() );
1495 if (iFileCreationHelper.IsTrgNameLegalDosName())
1496 WriteDirEntryL(firstEntryPos,firstEntry);
1498 WriteDirEntryL(firstEntryPos,firstEntry,name);
1501 CFatFileCB& file=(*((CFatFileCB*)aFile));
1502 file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos);
1503 if (anOpen==EFileReplace && file.Size())
1508 if (file.IsSeekIndex()==EFalse)
1509 file.CreateSeekIndex();
1510 if (anOpen==EFileReplace || anOpen==EFileCreate)
1511 file.SetArchiveAttribute();
1516 iFileCreationHelper.Close();
1519 //-----------------------------------------------------------------------------------------
1523 Open a directory on the current mount.
1525 @param aName path to the object in the directory we want to open; all trailing dots from the name will be removed
1526 @param aDir dir. CB to be filled in.
1528 If there is no such a path, this method must leave with KErrPathNotFound
1530 @leave KErrPathNotFound if thereis no such path
1531 @leave error code on media read fault
1533 void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir)
1535 __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName);
1537 CheckStateConsistentL();
1539 const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
1541 TInt namePos=dirName.LocateReverse(KPathDelimiter);
1543 TFatDirEntry dosEntry;
1544 TEntryPos dosEntryPos(RootIndicator(),0);
1546 InitializeRootEntry(dosEntry);
1549 TPtrC dirPath=dirName.Left(namePos);
1550 TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1;
1551 TLeafDirData leafDir;
1552 dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf
1557 TFatDirEntry startEntry;
1558 DoFindL(dirPath.Mid(dirPos),
1559 KEntryAttMatchMask|KEntryAttMatchExclusive,
1560 startPos, startEntry, dosEntryPos, dosEntry,
1561 fileName, KErrPathNotFound,
1568 TPtrC matchName(dirName.Mid(namePos+1));
1569 if (matchName.Length()==0)
1570 matchName.Set(_L("*"));
1572 ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName);
1576 //-----------------------------------------------------------------------------------------
1578 TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster)
1580 // Check aCluster contains no directory entries other than . and ..
1584 __PRINT(_L("CFatMountCB::IsDirectoryEmptyL"));
1585 TEntryPos dirEntryPos(aCluster,0);
1586 TFatDirEntry dirEntry;
1589 ReadDirEntryL(dirEntryPos,dirEntry);
1590 MoveToDosEntryL(dirEntryPos,dirEntry);
1591 if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory())
1593 if (dirEntry.IsEndOfDirectory())
1595 if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd()))
1596 return ETrue; // Root Directory has no end of directory marker
1597 if (!dirEntry.IsErased())
1600 MoveToNextEntryL(dirEntryPos);
1604 //-----------------------------------------------------------------------------------------
1607 Overwrite as many contiguous file clusters as possible.
1609 void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster)
1612 __PRINT(_L("CFatMountCB::DoWriteToClusterListL"));
1613 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1617 const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
1618 const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
1619 const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
1620 const TInt writeLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
1621 TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
1623 TRAPD(r, iRawDisk->WriteL(dataStart,writeLength,aSrc,aMessage,anOffset));
1625 if(r == KErrNone) // Write succeded
1627 aPos.iPos+=writeLength;
1628 aPos.iCluster=endCluster;
1632 if(r != KErrCorrupt) // failure not due to corruption so propogate up
1635 TErrorInfoBuf errinf;
1636 r = iRawDisk->GetLastErrorInfo(errinf);
1638 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
1641 const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
1642 TInt goodcluster = FAT().AllocateSingleClusterL(badcluster);
1644 //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster.
1645 TInt cluster = aPos.iCluster;
1646 if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<<ClusterSizeLog2())))
1649 if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
1650 { //Copy the contents already present in this cluster to new cluster allocated.
1651 const TInt sizeToRead = aPos.iPos - ((aPos.iPos >> ClusterSizeLog2()) << ClusterSizeLog2());
1652 dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead));
1655 //-- Allocate the buffer required to copy the contents from bad cluster
1657 CleanupClosePushL(clustBuf);
1658 if(clustBuf.CreateMax(sizeToRead) != KErrNone)
1660 FAT().FreeClusterListL(goodcluster);
1661 User::Leave(KErrNoMemory);
1664 r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer
1665 if(r != KErrNone) //If read fails dont do anything not even marking bad cluster.
1667 FAT().FreeClusterListL(goodcluster);
1671 //Copy the bad and good cluster,required to adjust the start cluster number.
1672 if(aBadcluster == 0)
1673 aBadcluster = badcluster;
1675 aGoodcluster = goodcluster;
1679 //Calculate and copy the contents to new cluster.
1680 aPos.iCluster = goodcluster;
1681 dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead);
1683 r = LocalDrive()->Write(dataStart, clustBuf);
1685 { // Copied contents to new cluster so fix up the chain and mark the cluster as bad.
1686 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1687 FAT().MarkAsBadClusterL(badcluster);
1688 aGoodcluster = goodcluster;
1689 CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer
1692 else if(r == KErrCorrupt)
1694 r = LocalDrive()->GetLastErrorInfo(errinf);
1695 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector)
1696 { //Allocate new cluster and adjust the cluster list.
1697 goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster);
1698 FAT().MarkAsBadClusterL(aPos.iCluster);
1703 //Not able to write successfully so dont alter the original list.
1704 aBadcluster = aGoodcluster = 0;
1705 FAT().FreeClusterListL(goodcluster);
1709 }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
1711 if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist
1713 // return bad and good clusters for CFatFileCB to fix up
1714 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1715 aBadcluster = badcluster;
1716 aGoodcluster = goodcluster;
1717 aPos.iCluster = goodcluster;
1721 FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
1722 if(badcluster > aPos.iCluster) //bad cluster not first in this contiguous list
1723 FAT().WriteL(badcluster-1, goodcluster);
1724 else //first cluster of this contigous list bad so update last cluster of previous contiguous list
1725 FAT().WriteL(aLastcluster, goodcluster);
1728 FAT().MarkAsBadClusterL(badcluster);
1733 User::Leave(KErrCorrupt);
1736 //-----------------------------------------------------------------------------------------
1738 void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster)
1740 // Overwrite cluster list.
1744 __PRINT(_L("CFatMountCB::WriteToClusterListL"));
1745 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1747 const TUint startPos=aPos.iPos;
1748 const TUint temp=startPos>>ClusterSizeLog2();
1749 const TUint length = (TUint)aLength;
1751 if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
1753 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1757 TInt previouscluster=0;
1760 DoWriteToClusterListL(aPos,length-offset,aSrc,aMessage,anOffset+offset, previouscluster, aBadcluster, aGoodcluster);
1761 if (offset == (aPos.iPos-startPos))
1763 offset=aPos.iPos-startPos;
1764 __ASSERT_ALWAYS(aPos.iPos>startPos,User::Leave(KErrCorrupt));
1765 previouscluster=aPos.iCluster;
1767 {__ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));}
1773 //-----------------------------------------------------------------------------------------
1775 void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
1777 // Read from as many contiguous file clusters as possible
1781 __PRINT(_L("CFatMountCB::DoReadFromClusterListL"));
1785 const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
1786 const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
1787 const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
1788 const TInt readLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
1789 const TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
1791 TRAPD(r, iRawDisk->ReadL(dataStart,readLength,aTrg,aMessage,anOffset));
1793 if(r == KErrNone) // Read succeded
1795 aPos.iPos+=readLength;
1796 aPos.iCluster=endCluster;
1799 if(r != KErrCorrupt) // failure not due to corruption so propogate up
1802 TErrorInfoBuf errinf;
1803 r = iRawDisk->GetLastErrorInfo(errinf);
1805 if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
1807 TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
1808 FAT().MarkAsBadClusterL(badcluster);
1811 User::Leave(KErrCorrupt);
1814 //-----------------------------------------------------------------------------------------
1816 void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
1818 // Read from cluster list
1822 __PRINT(_L("CFatMountCB::ReadFromClusterListL"));
1823 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
1825 const TInt startPos=aPos.iPos;
1826 const TInt temp=startPos>>ClusterSizeLog2();
1828 if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
1830 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1836 DoReadFromClusterListL(aPos,aLength-offset,aTrg,aMessage,anOffset+offset);
1837 offset=aPos.iPos-startPos;
1838 if ((offset<aLength))
1840 __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
1842 if (offset>=aLength)
1847 //-----------------------------------------------------------------------------------------
1849 TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const
1851 // Navigate the path to find the leaf directory.
1852 // Returns the startcluster of data for the directory found.
1856 __PRINT(_L("CFatMountCB::FindLeafDirL"));
1860 TEntryPos entryPos(RootIndicator(),0);
1862 if (iLeafDirCache == NULL)
1864 TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0;
1865 TInt namelen=aName.Length();
1866 if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen))
1868 if (leaflen==namelen)
1869 return(iLastLeafDirCluster);
1871 entryPos.iCluster=iLastLeafDirCluster;
1876 // Skip root directory
1877 if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1)
1879 TInt err = iLeafDirCache->FindInCache(aName, aLeafDir);
1880 if (err == KErrNone)
1882 ASSERT(aLeafDir.iClusterNum > 0);
1883 return aLeafDir.iClusterNum;
1885 else if (err != KErrNotFound)
1887 User::LeaveIfError(err);
1894 lex.Inc(); // Skip path delimiter
1896 r=lex.Remainder().Locate(KPathDelimiter);
1897 if (r==KErrNotFound)
1898 r=lex.Remainder().Length();
1899 if (r==0) // End of the path
1901 lex.Inc(r); // Set the token length
1906 TFatDirEntry startEntry;
1907 DoFindL(lex.MarkedToken(),
1908 KEntryAttMatchMask|KEntryAttMatchExclusive,
1909 startPos, startEntry, entryPos, entry,
1910 fileName, KErrPathNotFound,
1915 entryPos.iCluster=StartCluster(entry);
1919 if (iLeafDirCache == NULL)
1921 AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName);
1922 ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster;
1926 if (aName.Length() > 1)
1928 aLeafDir = TLeafDirData(entryPos.iCluster);
1929 iLeafDirCache->AddToCacheL(aName, aLeafDir);
1933 return entryPos.iCluster;
1936 //-----------------------------------------------------------------------------------------
1939 Search for a specified name winthin directory cache
1940 Works similary to TBool CFatMountCB::DoFindL()
1942 @param anAtt attributes of the object to find
1943 @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
1944 @param aStartEntry on return will contain first VFAT dir entry
1945 @param aDosEntryPos the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
1946 @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case)
1947 @param aFileName in the case of VFAT entry and on success here will be returned a long filename
1948 @param aAuxParam some parameters package
1949 @param aFileCreationHelper a helper package for file creations
1951 @return ETrue if the specified name is found in the cache. In this case aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName will contain valid values
1953 TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos,
1954 TFatDirEntry& aStartEntry, TEntryPos& aDosEntryPos,
1955 TFatDirEntry& aDosEntry, TDes& aFileName,
1956 const TFindHelper& aAuxParam,
1957 XFileCreationHelper* aFileCreationHelper,
1958 const TLeafDirData& aLeafDir) const
1960 TBool bCacheMatchFound = EFalse;
1962 //-- get an interface to the Dir. cache
1963 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
1968 //-- save original values in order to restore them in the case of negative search results
1969 TEntryPos StartEntryPos1(aStartEntryPos);
1970 TEntryPos DosEntryPos1(aDosEntryPos);
1971 TFatDirEntry StartEntry1(aStartEntry);
1972 TFatDirEntry DosEntry1(aDosEntry);
1974 TInt64 nCachedLinPos;
1976 const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size
1977 const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes
1978 const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2; //-- maximal number of dir entries that can be in the cache
1980 const TUint pageSzLog2 = pDirCache->PageSizeInBytesLog2();
1981 TBool ScanMRUPageFirst = EFalse;
1982 TBool MRUPageScanned = EFalse;
1984 // if MRU pos is availale, start with MRU page
1985 if (aLeafDir.iMRUPos.Cluster())
1987 ScanMRUPageFirst = ETrue;
1988 DosEntryPos1 = aLeafDir.iMRUPos;
1992 TEntryPos startPos = DosEntryPos1;
1993 TInt clusterNum = DosEntryPos1.iCluster;
1995 for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt)
1996 {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries
1997 //-- that can be cached. Helps to avoid problems with infinite (looped) directories
1999 if (IsEndOfClusterCh(DosEntryPos1.iCluster))
2001 // refer back to the last stored cluster position
2002 // note aFileCreationHelper may not be initialised for file opening operations
2003 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster)
2005 TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry);
2006 aFileCreationHelper->SetEntryAddingPos(dummyPos);
2007 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2010 if (ScanMRUPageFirst && !MRUPageScanned)
2012 DosEntryPos1 = aDosEntryPos;
2013 MRUPageScanned = ETrue;
2016 break; //-- this was the last cluster in this directory
2019 const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
2020 DosEntryPos1.iPos = pageStartPos;
2021 TBool PassedPageBoundary = EFalse;
2023 const TInt64 entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory
2024 const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached
2026 {//-- current page is in the directory cache
2027 //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos);
2029 //-- search to the end of the cached page.
2030 // Note GetDirEntry() will read data beyond cache page boundary
2031 const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2;
2034 //-- extract dir entries from the cached page and see if they match given name (aName)
2035 /// until it reaches the next page
2038 StartEntryPos1 = DosEntryPos1;
2039 TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it
2041 //-- get directory entry from the cache. We know that the DosEntryPos1 is cached.
2042 nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName);
2043 if(nErr != KErrNone)
2046 if(DosEntry1.IsEndOfDirectory())
2048 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2050 // note it is impossible to be at the end of the cluster chain here.
2051 aFileCreationHelper->SetEntryAddingPos(DosEntryPos1);
2052 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2055 if (ScanMRUPageFirst && !MRUPageScanned)
2060 // if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned)
2061 goto Exit; //-- this was the last entry in this directory, no reason to look further
2064 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2066 if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage())
2074 startPos = DosEntryPos1;
2077 if (numFound == aFileCreationHelper->NumOfAddingEntries())
2079 aFileCreationHelper->SetEntryAddingPos(startPos);
2080 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2084 if(MatchEntryAtt(DosEntry1.Attributes(),anAtt))
2085 {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then.
2087 if(StartEntry1.IsVFatEntry())
2088 {//-- extracted entry is VFAT one, name can be in UNICODE
2090 // we only check short name candidates for long file names with VFAT entries,
2091 // if it is a valid dos name, it will be checked by default
2092 // note here target name is always fully specified
2093 if (aFileCreationHelper && aFileCreationHelper->IsInitialised())
2095 aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr());
2098 TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
2100 if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound)
2101 {//-- found match in cache
2102 bCacheMatchFound = ETrue;
2105 else if(aAuxParam.TrgtNameIsLegalDos())
2107 if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
2109 bCacheMatchFound = ETrue;
2113 }//if(StartEntry1.IsVFatEntry())
2114 else if(aAuxParam.TrgtNameIsLegalDos())
2115 {//-- this is an old DOS FAT entry
2117 if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
2119 //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves
2120 //-- between 2 clusters (or/and cache pages). I.e. 1st part of this long entry belongs to one cluster and even more might not be cached,
2121 //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached.
2122 //-- In this case if we search for short file name, we find it, but the aStartEntryPos will be incorrect, which leads to the directory corruption.
2123 //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare.
2124 if(StartEntryPos1.iPos == 0)
2125 {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above.
2126 __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!"));
2130 bCacheMatchFound = ETrue;
2135 }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt))
2137 // check boundaries after GetDirEntry()
2138 // if we have cross the cluster boundary, break the for loop
2139 if(DosEntryPos1.iCluster != clSave)
2140 {//-- GetDirEntry() has decided to move to the next cluster.
2141 DosEntryPos1.iCluster = clSave;
2145 // if we are still in the same cluster, check the page boundary by
2146 /// exam how many entries we have scanned within the cluster
2147 const TUint entriesLooked = ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2;
2148 if(entriesLooked > nEntries)
2150 PassedPageBoundary = ETrue;
2155 // move to next entry before scanning next file
2156 TRAP(nErr,MoveToNextEntryL(DosEntryPos1));
2157 if(nErr != KErrNone)
2160 // check boundaries after MoveToNextEntryL()
2161 if(DosEntryPos1.iCluster != clSave)
2163 DosEntryPos1.iCluster = clSave;
2167 if (entriesLooked + 1 > nEntries)
2169 PassedPageBoundary = ETrue;
2175 } //if(iRawDisk->PosCached(...))
2177 // scanning did not happen because the page is not cached,
2179 // scanning finished in last page and file is not found
2181 // if MRU page is not cached or
2182 // we scan MRU page first and it is not scanned yet, then this must be the MRU page,
2183 // we now start to scan from the beginning
2184 if (ScanMRUPageFirst && !MRUPageScanned)
2186 MRUPageScanned = ETrue;
2187 DosEntryPos1 = aDosEntryPos;
2188 DosEntryPos1.iPos = 0;
2192 // if we just finished scanning a page and still in the same cluster, then we crossed page
2193 // boundary, continue with next page.
2194 // note: although we are in the 'next page' already, this page might not be cached, so we need to
2195 // check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly.
2196 if (PassedPageBoundary)
2198 DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
2199 PassedPageBoundary = EFalse;
2203 //-- try to move to the next cluster of the directory file
2205 if(DosEntryPos1.Cluster() < KFatFirstSearchCluster) //-- small trick to get rid of TRAPping GetNextClusterL()
2208 // record previous cluster number before move on
2209 clusterNum = DosEntryPos1.iCluster;
2211 if(! FAT().GetNextClusterL(DosEntryPos1.iCluster))
2215 } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt)
2217 //---------------------------------
2220 if(bCacheMatchFound)
2222 //-- if the position of the found in cache object is less than given, pretend that we haven't found anything
2223 //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to
2224 //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir.
2225 TBool bFallBack=EFalse;
2227 if(DosEntryPos1.iCluster == aDosEntryPos.iCluster)
2229 if(DosEntryPos1.iPos < aDosEntryPos.iPos)
2234 if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos))
2243 //-- Update parameters with new values
2244 aStartEntryPos= StartEntryPos1;
2245 aDosEntryPos = DosEntryPos1;
2246 aStartEntry = StartEntry1;
2247 aDosEntry = DosEntry1;
2249 const TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
2251 pDirCache->MakePageMRU(mruPos);
2253 //-- if the corresponding leaf directory name is cached, associate the last search positionin this directory.
2254 //-- the next search in this dir. will start from this position (and will wrap around over the dir. beginning).
2255 //-- the "last search position" will is the position of current VFAT entryset start.
2256 if (aLeafDir.iClusterNum)
2258 iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos));
2261 return bCacheMatchFound;
2264 //-----------------------------------------------------------------------------------------
2267 initialise find helper with the target file name.
2268 This is a quite expensive operation and initialisation is done only once. After this we know if the name is a legal dos one
2269 and also have the corresponding generated DOS name for it.
2271 @param aTargetName target file name we are looking for in ::DoFindL()
2273 void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName)
2280 iTargetName.Set(aTargetName);
2281 isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse);
2284 {//-- iShortName will contain generated short DOS name by long filename
2285 iShortName = DoGenerateShortNameL(aTargetName, count, ETrue);
2288 isInitialised = ETrue;
2292 Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise().
2293 @param apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format
2294 @return ETrue if the apDosEntryName is the same as generated iShortName
2296 TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const
2298 ASSERT(isInitialised);
2303 return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0);
2306 //-----------------------------------------------------------------------------------------
2307 const TInt KShortNameCandidatesNum = 4;
2309 Constructor of XFileCreationHelper class
2311 CFatMountCB::XFileCreationHelper::XFileCreationHelper()
2313 isInitialised = EFalse;
2317 Destructor of XFileCreationHelper class
2319 CFatMountCB::XFileCreationHelper::~XFileCreationHelper()
2325 Initialises a TFileCreationHelper object, generate a short name candidate pool.
2327 @param aTargetName Target file name for the potential new file.
2328 @post TFileCreationHelper is fully initialised.
2330 void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName)
2332 // close before use, to avoid memory leak
2335 iTargetName.Set(aTargetName);
2336 // generates short name candidate(s)
2338 while (count <= KShortNameCandidatesNum)
2340 TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue);
2341 TInt err = iShortNameCandidates.Append(shortNameCandidate);
2342 User::LeaveIfError(err);
2344 if (count == -1) // No tilde and number is needed
2352 // calculate number of new entries needed
2353 iNumOfAddingEntries = 1;
2354 isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue);
2355 if (!isTrgNameLegalDosName)
2356 iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length());
2358 isNewEntryPosFound = EFalse;
2359 isInitialised = ETrue;
2363 Close function of XFileCreationHelper class
2365 void CFatMountCB::XFileCreationHelper::Close()
2367 iShortNameCandidates.Close();
2368 isInitialised = EFalse;
2372 Validates short name candidates. If the input dos entry name is found in the short name
2373 candidate pool, the corresponding short name candidate will be removed from the pool.
2375 @param apDosEntryName An existing short name, to compare with the candidates.
2376 @pre Object should be initialised
2378 void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName)
2380 ASSERT(isInitialised);
2384 if (iShortNameCandidates.Count() > 0)
2386 for (TInt i = 0; i < iShortNameCandidates.Count(); i++)
2388 if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0)
2390 iShortNameCandidates.Remove(i);
2398 Gets a validated short name from the short name candidate pool.
2400 @param aShortName On return, contains a validated short name if found, otherwise zeroed.
2401 @return TInt Returns KErrNone if a validated short name found successfully,
2402 else KErrNotFound is returned.
2403 Returns KErrNotReady if object is not initialised.
2404 @pre Object should be initialised
2406 TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const
2410 ASSERT(isInitialised);
2412 return KErrNotReady;
2414 if (iShortNameCandidates.Count() > 0)
2416 aShortName.Copy(iShortNameCandidates[0]);
2420 return KErrNotFound;
2423 //-----------------------------------------------------------------------------------------
2427 Scan a directory looking for aName.
2429 @param aTrgtName a name of an object we are looking up in directory
2430 @param anAtt attributes of this object
2431 @param aStartEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2432 @param aStartEntry on return will contain first VFAT dir entry
2433 @param aDosEntryPos the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
2434 @param aDosEntry on return will contain DOS dir entry (the last one for VFAT case)
2435 @param aFileName in the case of VFAT entry and on success here will be returned a long filename
2436 @param anError This function might leave with this given error code
2437 @param aFileCreationHelper a helper package for file creations
2439 @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one
2440 @leave can leave with anError code on error or if the search has reached the end of directory (!)
2442 TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt,
2443 TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry,
2444 TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,
2445 TDes& aFileName,TInt anError,
2446 XFileCreationHelper* aFileCreationHelper,
2447 const TLeafDirData& aLeafDirData) const
2449 // check that the entry position to be read next is not past the end of the
2450 // root directory. If this is the case then when GetDirEntryL(..) is called
2451 // this will lead to MakeLinAddr(..) leaving with KErrDirFull.
2453 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
2454 User::Leave(anError);//Allows maximum number of entries in root directory
2456 __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName);
2458 TInt previousCluster=aDosEntryPos.iCluster;
2459 TUint previousPosition=aDosEntryPos.iPos;
2460 TInt changePreviousCluster=1;
2463 TBool trgNameIsWildCard = EFalse; //-- ETrue if the name we are looking for is a wildcard
2464 TBool trgNameFullySpecified = ETrue; //-- ETrue if the name we are looking for doesn't contain wildcards
2468 //-- find out if the name we are looking for is a wildcard ("*" or "*.*")
2469 const TInt len = aTrgtName.Length();
2472 trgNameIsWildCard = (aTrgtName[0] == '*');
2475 _LIT(KAllFiles, "*.*");
2476 trgNameIsWildCard = (aTrgtName==KAllFiles);
2479 //-- find out if the name we are looking for contains wildcharacters: "*" or "?"
2480 if(trgNameIsWildCard)
2481 trgNameFullySpecified = EFalse;
2484 for(TInt i=0; i<len; ++i)
2486 const TChar ch = aTrgtName[i];
2487 if(ch == (TChar)'*' || ch == (TChar)'?')
2489 trgNameFullySpecified = EFalse;
2497 TPtrC trgtNameNoDot(aTrgtName);
2499 TFindHelper findHelper;
2500 //---------------------------------------------------
2501 //-- if we have fully specified name and directory cache is present, try to
2502 //-- locate the name in the cache first to avoid reading from media
2503 //-- if the entry belongs to the root directory (for FAT12,16) skip the lookup, because root directory isn't aligned by cluster size boundary,
2504 //-- while directory cache pages are. For FAT32 it doesn't matter, because root dir is a usual file.
2506 //-- the "rummage dir. cache" can be swithed off. This is not affecting the functionality, only the performance.
2507 #ifdef USE_DIR_CACHE_RUMMAGE
2509 if(iRawDisk->DirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper)
2510 {//-- aName is fully specified, i.e doesn't contain wildcards
2512 findHelper.InitialiseL(trgtNameNoDot);
2514 const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData);
2517 return(aStartEntry.IsVFatEntry());
2522 //---------------------------------------------------
2524 // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided
2525 TBool scanAhead = EFalse;
2526 // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&&
2527 // we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&&
2528 // if we don't have a starting cluster number, we draw back to original scanning algorithm
2529 if (!IsRootDir(aDosEntryPos) // we don't do forward scanning for root dir &
2530 && aLeafDirData.iClusterNum != 0 // if we have a starting cluster number &
2531 && aLeafDirData.iMRUPos.Cluster() != 0) // if we have a starting cluster number &
2534 aDosEntryPos = aLeafDirData.iMRUPos;
2538 TEntryPos startPos = aDosEntryPos;
2539 TInt clustNum = aDosEntryPos.Cluster();
2541 for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
2543 // if we are not scanning ahead, we don't need this outer for loop
2547 TBool found = EFalse;
2549 FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end
2551 //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT)
2552 //-- aDosEntry will contain a DOS entry of the directory entry we have read.
2553 aStartEntryPos=aDosEntryPos;
2554 User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName));
2556 if (aDosEntry.IsEndOfDirectory())
2557 {//-- the end of directory reached.
2559 // if new entry position for adding has not been found yet.
2560 // note aFileCreationHelper may not be initialised for pure file opening operations
2561 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2563 // if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain,
2564 // we pass the last scanned entry position to AddDirEntryL
2565 if (IsEndOfClusterCh(aDosEntryPos.iCluster))
2567 TInt clusterSize=1<<ClusterSizeLog2();
2568 TEntryPos dummyPos(clustNum, clusterSize - KSizeOfFatDirEntry);
2569 aFileCreationHelper->SetEntryAddingPos(dummyPos);
2570 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2572 // or we reached the end of the directory.
2575 aFileCreationHelper->SetEntryAddingPos(aDosEntryPos);
2576 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2580 // if we are scanning ahead and this is the first scanning, we break out to restart scanning
2581 if (scanAhead && scanCnt == 1)
2583 break; // from FOREVER, restart scanning
2586 // if (!scanAhead || scanAhead && scanCnt == 2)
2587 User::Leave(anError);
2591 // entry space searching for potential new file/directory creation
2592 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
2594 if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage())
2602 startPos = aDosEntryPos;
2605 if (numFound == aFileCreationHelper->NumOfAddingEntries())
2607 aFileCreationHelper->SetEntryAddingPos(startPos);
2608 aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
2614 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
2615 if (aDosEntry.IsErased())
2617 User::Leave(anError);//Allows maximum number of entries in root directory
2621 const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage();
2623 if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt))
2624 {//-- we have read a filename entry and entry's attributes match required; compare names then.
2626 if (trgNameIsWildCard)
2629 break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name.
2633 if (aStartEntry.IsVFatEntry())
2634 {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name.
2635 //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries
2638 // we only check short name candidates for long file names with VFAT entries,
2639 // if it is a valid dos name, it will be checked by default
2640 // note, for file creation cases, target name will be always fully specified
2641 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified)
2643 aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr());
2646 //-- discard trailing dots from aFileName if present
2647 TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
2649 if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound)
2652 break; //-- OK, found a match.
2654 else if (trgNameFullySpecified)
2656 //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified,
2657 //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK.
2658 findHelper.InitialiseL(trgtNameNoDot);
2660 if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
2663 break; //-- DOS entries match, success.
2667 else if (!trgNameFullySpecified)
2668 {//-- target name contains wildcards, we need to use MatchF with dos name
2669 TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
2671 LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
2672 if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
2681 else //if (aStartEntry.IsVFatEntry())
2682 {//-- we've read a legacy FAT entry, so compare DOS entries
2683 findHelper.InitialiseL(trgtNameNoDot);
2685 if(findHelper.TrgtNameIsLegalDos())
2686 {//-- we are looking for a legal DOS name
2687 if(trgNameFullySpecified)
2688 {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries
2689 if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
2696 {//-- target name contains wildcards, we neeed to use MatchF
2697 TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
2699 LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
2700 if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
2707 } //if(findHelper.TrgtNameIsLegalDos())
2709 } //else if (aStartEntry.IsVFatEntry())
2711 } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt))
2714 // record previous cluster number
2715 clustNum = aDosEntryPos.iCluster;
2717 // this is the 2nd scanning and we have just passed the pos we started.
2718 if (scanAhead && scanCnt == 2)
2720 if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster()
2721 && aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos())
2723 User::Leave(anError);
2728 MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory
2730 if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
2732 User::Leave(anError);//Allows maximum number of entries in root directory
2736 if (!scanAhead || scanCnt == 2)
2738 if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
2739 DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count);
2741 previousPosition=aDosEntryPos.iPos;
2743 } // FOREVER -- the actual scanning is done inside this loop
2752 // if we have not found in the first scanning and we are doing scanning ahead,
2753 // we need to go back to the starting pos of this dir and scan from start until
2754 // we reach lastscannedPos
2755 if (scanAhead && scanCnt == 1)
2757 aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0);
2762 // there are only two exits: either found or reached end of dir in the 1st scanning
2766 } // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
2768 //---------------------------------------------------
2769 if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster())
2771 TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
2772 iRawDisk->DirCacheInterface()->MakePageMRU(mruPos);
2774 //-- if the corresponding leaf directory name is cached, associate the last search positionin this directory.
2775 //-- the next search in this dir. will start from this position (and will wrap around over the dir. beginning).
2776 //-- the "last search position" will is the position of current VFAT entryset start.
2777 if(aLeafDirData.iClusterNum)
2779 iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aStartEntryPos));
2783 return (aStartEntry.IsVFatEntry());
2786 //-----------------------------------------------------------------------------------------
2788 Locate an directory entry entry from its full path name.
2790 @param aName a name of an object we are looking for
2791 @param anAtt attributes of this object
2792 @param anEntry on return will contain first VFAT dir entry
2793 @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2795 @leave can leave with KErrNotFound if the search has reached the end of directory
2797 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const
2799 __PRINT(_L("CFatMountCB::FindEntryStartL()"));
2800 TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
2802 TLeafDirData leafDir;
2803 TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
2804 TFatDirEntry dosEntry;
2806 DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir);
2810 //-----------------------------------------------------------------------------------------
2813 Locate an directory entry entry from its full path name.
2815 @param aName a name of an object we are looking for
2816 @param anAtt attributes of this object
2817 @param anEntry on return will contain first VFAT dir entry
2818 @param anEntryPos on return in case of VFAT entry will contain start position of the VFAT dir. entry
2820 @leave can leave with KErrNotFound if the search has reached the end of directory
2822 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const
2824 __PRINT(_L("CFatMountCB::FindEntryStartL()"));
2825 TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
2827 TLeafDirData leafDir;
2828 TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
2829 TFatDirEntry dosEntry;
2830 DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir);
2833 //-----------------------------------------------------------------------------------------
2834 void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const
2836 // Scan a directory looking for aName.
2837 // aCluster and anEntryAddr give the location of the entry.
2841 __PRINT(_L("CFatMountCB::FindDosNameL()"));
2843 TFatDirEntry startEntry;
2845 TLeafDirData leafDir; // leaf dir data is zero initialized, no scannig ahead
2846 DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir);
2848 //-----------------------------------------------------------------------------------------
2850 void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries)
2852 // Find space for a new directory entry. Leave KErrEof if no space
2856 __PRINT(_L("CFatMountCB::AddDirEntryL"));
2859 TEntryPos startPos(RootIndicator(),0);
2860 TInt clusterNum=aPos.iCluster;
2863 ReadDirEntryL(aPos,entry);
2864 if (entry.IsEndOfDirectory())
2866 if (!entry.IsErased() && !entry.IsGarbage())
2873 if (numFound==aNumOfEntries)
2879 clusterNum=aPos.iCluster;
2880 MoveToNextEntryL(aPos);
2881 if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd()))
2882 // No end of directory marker at end of root directory
2883 User::Leave(KErrDirFull);
2886 TUint clusterSize=1<<ClusterSizeLog2();
2887 if (IsEndOfClusterCh(aPos.iCluster))
2888 { // End of last cluster in directory
2889 aPos.iCluster=clusterNum;
2890 aPos.iPos=clusterSize;
2893 TEntryPos eofPos(aPos.iCluster,aPos.iPos+KSizeOfFatDirEntry*aNumOfEntries);
2895 if (IsRootDir(aPos))
2896 { // Special case of root directory
2897 if (eofPos.iPos+StartOfRootDirInBytes()>RootDirEnd())
2898 User::Leave(KErrDirFull);
2903 if (eofPos.iPos==clusterSize)
2904 return; // No need to allocate
2905 if (eofPos.iPos>clusterSize)
2907 TInt numNeeded=eofPos.iPos>>ClusterSizeLog2();
2910 ExtendClusterListZeroedL(numNeeded,eofPos.iCluster);
2914 FAT().ExtendClusterListL(numNeeded,eofPos.iCluster);
2915 ZeroDirClusterL(eofPos.iCluster);
2918 eofPos.iPos-=numNeeded<<ClusterSizeLog2();
2919 if(aPos.iPos==clusterSize)
2921 if (!FAT().GetNextClusterL(aPos.iCluster))
2923 __PRINT(_L("CFatMountCB::AddDirEntryL corrupt#1"))
2924 User::Leave(KErrCorrupt);
2929 else if(Drive().IsRemovable())
2931 // check if entry is already zeroed
2932 ReadDirEntryL(eofPos,entry);
2933 if(!entry.IsEndOfDirectory())
2935 // some removable media may not have directory zeroed
2936 entry.SetEndOfDirectory();
2937 WriteDirEntryL(eofPos,entry);
2944 @param aCluster cluster number to zero-fill
2946 void CFatMountCB::ZeroDirClusterL(TInt aCluster)
2949 __PRINT1(_L("CFatMountCB::ZeroDirClusterL %d"),aCluster);
2951 const TUint32 KClusterSz= 1<<ClusterSizeLog2();
2952 const TUint32 KMaxBufSz = KClusterSz; //-- max. nuffer size is a cluster
2953 const TUint32 KMinBufSz = 1<<SectorSizeLog2(); //-- min. buffer size is 1 sector (for OOM case)
2955 //-- allocate a buffer for zero-filling a cluster
2957 CleanupClosePushL(buf);
2959 if(buf.CreateMax(KMaxBufSz) != KErrNone)
2960 buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
2964 TEntryPos entryPos(aCluster,0);
2966 //-- write buffer to the beginning of the directory file.
2967 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
2969 //-- fill in the rest of the cluster if we used a small buffer
2970 if((TUint32)buf.Size() < KClusterSz) //-- KMaxBufSz may == KMinBufSz if we have 1 sector per cluster
2972 const TInt restCnt = SectorsPerCluster() - 1;
2973 ASSERT(restCnt >=1);
2975 for(TInt i=0; i<restCnt; ++i)
2977 entryPos.iPos += KMinBufSz;
2978 DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
2983 CleanupStack::PopAndDestroy(&buf);
2988 Internal method. Retrieves directory entry from given position.
2990 @param aPos on enter shall contain start position, from where the entry will be read. On return contains position of the DOS entry (the last one for object name for the VFAT case)
2991 @param aDosEntry On return contains DOS entry for the VFAT case
2992 @param aStartEntry On return contains start entry of the directory object for the VFAT case
2993 @param aLongFileName On return contains VFAT or long filename
2995 @return ETrue if whole FAT entry is OK: only 1 entry for DOS name or _ALL_ entries for a long name
2996 EFalse if there was an error in assembling entries to the long file name. In this case this entry shall be ignored by upper level.
2998 can leave because of ReadDirEntryL() and MoveToNextEntryL() [end of dir].
3000 TBool CFatMountCB::DoGetDirEntryL(TEntryPos& aPos, TFatDirEntry& aDosEntry, TFatDirEntry& aStartEntry, TDes& aLongFileName) const
3003 // __PRINT3(_L("CFatMountCB::GetDirEntryL() drv:%d, pos:%d:%d"), Drive().DriveNumber(), aPos.iCluster, aPos.iPos);
3005 ReadDirEntryL(aPos,aStartEntry);
3006 aDosEntry=aStartEntry;
3007 if (!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || aDosEntry.IsGarbage())
3008 {//-- This is either a 8.3 FAT entry or garbage
3009 aLongFileName.SetLength(0);
3013 //-- process VFAT entries
3015 if(!aDosEntry.IsLongNameStart())
3016 return EFalse; //-- wrong counter in the 1st VFat entry, consider it as orphaned
3019 TInt count = aDosEntry.NumFollowing(); //-- count of the following VFat entries
3021 TBuf16<KMaxVFatEntryName> vBuf(KMaxVFatEntryName);
3022 aDosEntry.ReadVFatEntry(vBuf);
3024 TInt vLength=vBuf.Locate('\0');
3025 if (vLength==KErrNotFound)
3026 vLength=KMaxVFatEntryName;
3028 vBuf.SetLength(vLength);
3030 const TInt nameLen = vLength+KMaxVFatEntryName*(count-1);
3031 if(nameLen <= 0 || nameLen > KMaxFileName)
3032 return EFalse; //-- wrong long file name length, consider VFAT entry as orphaned
3034 aLongFileName.SetLength(nameLen);
3036 const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry
3040 TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count);
3041 fileNamePtr.Copy(vBuf);
3043 break; //-- all VFat entries read, only DOS entry remained
3045 MoveToNextEntryL(aPos);
3046 ReadDirEntryL(aPos,aDosEntry);
3048 //-- check if it is correct VFat entry.
3049 //-- if not, this is the "orphaned" entry and will be ignored
3050 if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count)
3051 return EFalse; //-- bad VFAT entry
3053 aDosEntry.ReadVFatEntry(vBuf);
3056 if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3057 return ETrue;//Allows maximum number of entries in root directory
3059 //-- read the last, DOS FAT entry
3060 MoveToNextEntryL(aPos);
3061 ReadDirEntryL(aPos,aDosEntry);
3063 //-- check if it is corect
3064 if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry())
3065 return EFalse; //-- Bad DOS entry
3067 //-- verify ChechSum here if it is incorrect, use DOS name only
3068 const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name());
3069 if(calcNameChkSum != entryCheckSum)
3071 aLongFileName.SetLength(0);//-- don't use long filename
3072 __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum);
3080 Read a number of VFAT entries from the directory file.
3081 for parameters see DoGetDirEntryL()
3083 @return KErrNone if everything is OK, system wide error code otherwise
3086 TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const
3089 TBool bEntryOK=ETrue;
3090 TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName));
3096 {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged.
3097 //-- consider it as an "orphaned" entry and skip
3098 aDosEntry.iData[0] = 0xFF; // Mark entry as garbage
3099 aLongFileName.SetLength(0); // No long filename
3105 void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const
3107 // If anEntry is at the end of the cluster, and we are not the root dir,
3108 // move it to the next in the list.
3112 // __PRINT(_L("CFatMountCB::MoveToNextEntryL"));
3113 if (IsEndOfClusterCh(aPos.iCluster))
3115 const TUint temp = 1<<ClusterSizeLog2();
3116 if (aPos.iPos+KSizeOfFatDirEntry!=temp || IsRootDir(aPos))
3118 aPos.iPos+=KSizeOfFatDirEntry;
3122 if (FAT().GetNextClusterL(aPos.iCluster)==EFalse)
3124 SetEndOfClusterCh(aPos.iCluster);
3130 //-----------------------------------------------------------------------------------------
3133 Starting from a VFat entry walk down the directory until the associated dos entry is found
3135 @param aPos in: VFAT entry position. out: if this is a VFAT entry set, it will be DOS entry position. otherwise not changed
3136 @param anEntry on return will contain DOS dir. entry contents (if aPos points to the VFAT entry)
3138 void CFatMountCB::MoveToDosEntryL(TEntryPos& aPos,TFatDirEntry& anEntry) const
3141 //__PRINT(_L("CFatMountCB::MoveToDosEntryL"));
3142 if (anEntry.IsVFatEntry()==EFalse)
3146 MoveToNextEntryL(aPos);
3147 ReadDirEntryL(aPos,anEntry);
3148 if (anEntry.IsVFatEntry()==EFalse)
3150 if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3151 break; // Allows maximum number of entries in root directory
3155 //-----------------------------------------------------------------------------------------
3157 /** Read the Uid of the entry starting at aCluster */
3158 void CFatMountCB::ReadUidL(TInt aCluster,TEntry& anEntry) const
3161 __PRINT1(_L("CFatMountCB::ReadUidL(%d)"), aCluster);
3163 if((TUint)aCluster < KFatFirstSearchCluster || (TUint)aCluster >= UsableClusters()+KFatFirstSearchCluster)
3164 User::Leave(KErrCorrupt);
3166 TBuf8<sizeof(TCheckedUid)> uidBuf;
3167 iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf);
3168 __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed));
3169 TCheckedUid uid(uidBuf);
3170 anEntry.iType=uid.UidType();
3173 //-----------------------------------------------------------------------------------------
3176 Read file section without opening this file on a file server side.
3178 @param aName file name; all trailing dots from the name will be removed
3179 @param aFilePos start read position within a file
3180 @param aLength how many bytes to read; on return will be how many bytes actually read
3181 @param aDes local buffer desctriptor
3182 @param aMessage from file server, used to write data to the buffer in different address space.
3184 @leave on media read error
3186 void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage)
3188 __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName);
3190 CheckStateConsistentL();
3192 TEntryPos dosEntryPos(RootIndicator(),0);
3193 TFatDirEntry dosEntry;
3197 TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
3198 TLeafDirData leafDir;
3199 dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir);
3202 TFatDirEntry startEntry;
3203 DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported,
3204 startPos,startEntry,dosEntryPos,dosEntry,
3205 fileName,KErrNotFound,
3209 // Check that reading from aPos for aLength lies within the file
3210 // if aPos is within the file, and aLength is too long, read up to EOF
3211 // If aPos is beyond the end of the file, return a zero length descriptor
3213 TUint32 fileSize = dosEntry.Size();
3214 if ((TUint)aPos>=fileSize)
3215 User::Leave(KErrEof);
3217 if ((TUint)(aPos+aLength)>fileSize)
3218 aLength=fileSize-aPos;
3220 TInt cluster=StartCluster(dosEntry);
3224 TInt clusterSize=1<<ClusterSizeLog2(); // Size of file clusters
3227 // Total number of clusters in file
3228 TInt maxClusters=((fileSize+clusterSize-1)>>ClusterSizeLog2());
3233 // Get the maximum number of clusters that can be read contiguously
3234 TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
3235 __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));
3237 // If start position within this block, then read some data
3238 if (pos<(clusterListLen<<ClusterSizeLog2()))
3240 // Read the remaining length or the entire cluster block whichever is smaller
3241 TInt readLength = Min(aLength-readTotal,(clusterListLen<<ClusterSizeLog2())-pos);
3242 __ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
3243 TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
3244 iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
3245 readTotal += readLength;
3247 if (readTotal == aLength)
3253 // Get the next cluster in file
3254 pos-=(clusterListLen<<ClusterSizeLog2());
3256 TBool remainingClusters=
3258 ((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
3259 __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
3265 //-----------------------------------------------------------------------------------------
3267 void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const
3269 // Read aLength of data from disk directly to thread relative descriptor
3272 iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset);
3275 //-----------------------------------------------------------------------------------------
3277 void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage)
3279 // Write aLength of data from thread relative descriptor to disk
3284 //-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case.
3285 FAT().RequestRawWriteAccess(aPos, aLength);
3287 iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset);
3288 //-- Note: FAT directory cache will be invalidated in MountL()
3291 //-----------------------------------------------------------------------------------------
3293 This method must be used when writing to the FAT directory file.
3294 If FAT directory cache is present on this drive, it will be used.
3295 @param aPos entry media position
3296 @param aDes data descriptor
3298 void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes)
3301 const TInt64 posAddr=MakeLinAddrL(aPos);
3303 if(!iRawDisk->DirCacheInterface())
3305 iRawDisk->WriteCachedL(posAddr,aDes);
3308 {//-- if there is an interface to the FAT directory cache, use it
3309 iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes);
3313 //-----------------------------------------------------------------------------------------
3316 This method must be used when reading from the FAT directory file.
3317 If FAT directory cache is present on this drive, it will be used.
3319 @param aPos entry media position
3320 @param aLength how many bytes to read
3321 @param aDes input data descriptor
3323 void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const
3325 const TInt64 posAddr=MakeLinAddrL(aPos);
3327 if(!iRawDisk->DirCacheInterface())
3329 iRawDisk->ReadCachedL(posAddr,aLength,aDes);
3332 {//-- if there is an interface to the FAT directory cache, use it
3333 iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes);
3337 //-----------------------------------------------------------------------------------------
3339 void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry)
3341 // Write a FAT directory entry to disk.
3342 // Assumes sufficient space has been created for it by AddDirEntry.
3346 __PRINT(_L("CFatMountCB::WriteDirEntryL"));
3348 //-- use special interface to access FAT directory file
3349 DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry));
3352 //-----------------------------------------------------------------------------------------
3354 void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos)
3356 // Mark a dir entry as erased
3360 __PRINT(_L("CFatMountCB::EraseDirEntryL"));
3361 if(!iLeafDirCache && iLastLeafDir)
3362 iLastLeafDir->Des().SetLength(0);
3364 //-- use special interface to access FAT directory file
3365 DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8)));
3368 //-----------------------------------------------------------------------------------------
3370 void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const
3372 // Read a FAT directory entry to disk
3376 // __PRINT(_L("CFatMountCB::ReadDirEntryL"));
3377 if (IsEndOfClusterCh(aPos.iCluster))
3382 TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry);
3384 //-- use special interface to access FAT directory file
3385 DirReadL(aPos,KSizeOfFatDirEntry,buf);
3388 //-----------------------------------------------------------------------------------------
3391 Enlarge the disk's size.
3392 This method can be called only for variable size media, i.e. RAM drive
3394 @param aSize size increment (bytes)
3396 void CFatMountCB::EnlargeL(TInt aSize)
3398 __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize);
3403 if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize)
3404 User::Leave(KErrDiskFull);
3405 User::LeaveIfError(LocalDrive()->Enlarge(aSize));
3410 FAT().InitializeL();
3415 RawDisk().InitializeL();
3420 //-----------------------------------------------------------------------------------------
3422 void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength)
3424 // Reduce the disk's size
3428 __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength);
3429 User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength));
3433 //-----------------------------------------------------------------------------------------
3435 TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const
3437 // Convert cluster/position into linear address
3441 //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos);
3442 if (!IsRootDir(aPos))
3444 TInt relPos=ClusterRelativePos(aPos.iPos);
3445 return FAT().DataPositionInBytes(aPos.iCluster)+relPos;
3447 if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())
3448 User::Leave(KErrDirFull); // Past last root dir entry
3449 return StartOfRootDirInBytes()+aPos.iPos;
3452 //-----------------------------------------------------------------------------------------
3454 void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName)
3456 // Get the short name associated with a long file name
3459 __PRINT(_L("CFatMountCB::GetShortNameL"));
3460 TEntryPos firstEntryPos(RootIndicator(),0);
3461 TFatDirEntry firstEntry;
3462 FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
3463 MoveToDosEntryL(firstEntryPos,firstEntry);
3464 TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name()));
3465 LocaleUtils::ConvertToUnicodeL(aShortName, dosName);
3468 //-----------------------------------------------------------------------------------------
3470 void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName)
3472 // Get the long name associated with a short file name
3475 __PRINT(_L("CFatMountCB::GetLongNameL"));
3476 TEntryPos pos(RootIndicator(),0);
3478 const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
3479 const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos));
3480 __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName));
3482 TLeafDirData leafDir;
3483 pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir);
3486 TFatDirEntry startEntry;
3487 User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName));
3488 if (entry.IsEndOfDirectory())
3489 User::Leave(KErrNotFound);
3490 TBool entryIsVFat=EFalse;
3491 if (startEntry.IsVFatEntry())
3493 if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased())
3495 TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name()));
3496 TBuf<0x20> entryName;
3497 LocaleUtils::ConvertToUnicodeL(entryName, entryName8);
3498 if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound)
3500 if (entryIsVFat==EFalse)
3501 aLongName=shortNameWithoutPathDelimiter;
3505 MoveToNextEntryL(pos);
3511 //-----------------------------------------------------------------------------------------
3514 Extend a file or directory, zeroing cluster chain and flushing after every write to FAT.
3515 This method is called for rugged FAT only.
3516 for parameters see CFatTable::ExtendClusterListL
3518 void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster)
3520 __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL"));
3521 __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
3523 while(aNumber && FAT().GetNextClusterL(aCluster))
3526 //-- request aNumber free clusters from the FAT, this request may wait until FAT scan thread counted enough free clusters if it is running.
3527 if(!FAT().RequestFreeClusters(aNumber))
3529 __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull"));
3530 User::Leave(KErrDiskFull);
3534 TInt freeCluster=FAT().AllocateSingleClusterL(aCluster);
3536 ZeroDirClusterL(freeCluster);
3537 FAT().WriteL(aCluster,freeCluster);
3539 aCluster=freeCluster;
3543 //-----------------------------------------------------------------------------------------
3546 TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
3551 if(aCommand>=EExtCustom)
3554 return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2);
3556 return KErrNotSupported;
3560 case ECriticalWriteFailOn:
3564 TPtr8 des((TUint8*)args,4,4);
3565 TRAP(r,aMessage.ReadL(2,des,0));
3568 SetWriteFail(ETrue);
3569 SetWriteFailCount(args[0]);
3570 SetWriteFailError(args[1]);
3573 case ECriticalWriteFailOff:SetWriteFail(EFalse);break;
3574 case ERuggedFSysOn: SetRuggedFSys(ETrue);break;
3575 case ERuggedFSysOff: SetRuggedFSys(EFalse);break;
3579 TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true
3580 TPtr8 pVal(&val,1,1);
3581 TRAP(r,aMessage.WriteL(2,pVal,0));
3586 case ELocalTimeForRemovableMediaOn:
3588 FatFileSystem().SetUseLocalTime(ETrue);
3591 case ELocalTimeForRemovableMediaOff:
3593 FatFileSystem().SetUseLocalTime(EFalse);
3596 case ELocalTimeUsedOnRemovableMedia:
3598 TBool flag = FatFileSystem().GetUseLocalTime();
3599 TPckgC<TBool> flagPckg(flag);
3600 TInt r = aMessage.Write(2, flagPckg);
3607 CheckStateConsistentL();
3609 TEntryPos firstEntryPos(RootIndicator(),0);
3610 TFatDirEntry firstEntry;
3611 //RFs::ControlIO restricts you to use narrow descriptors
3612 //so convert narrow back to wide.
3613 TBuf8<KMaxPath> fileNameNarrow;
3614 aMessage.Read(2, fileNameNarrow);
3616 TFileName fileNameWide;
3617 fileNameWide.Copy(fileNameNarrow);
3619 //find the long file name entry
3620 TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) );
3623 //Find the corresponding 8.3 short name entry, for metadata
3624 MoveToDosEntryL(firstEntryPos,firstEntry);
3625 TTime creationTime=0;
3626 TPckg<TTime> timePckg(creationTime);
3627 SFatDirEntry* sEntry = reinterpret_cast<SFatDirEntry*>(firstEntry.iData);
3628 creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC);
3629 r = aMessage.Write(3, timePckg);
3634 case EDisableFATDirCache:
3636 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3637 TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache;
3638 pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL);
3641 case EDumpFATDirCache:
3643 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3644 TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache;
3645 pDirCache->Control(KEDumpFATDirCache, 0, NULL);
3648 case EFATDirCacheInfo:
3650 MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
3651 TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo;
3652 pDirCache->Control(KEFATDirCacheInfo, 0, NULL);
3657 default: return(KErrNotSupported);
3662 TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
3663 {return(KErrNotSupported);}
3667 //-----------------------------------------------------------------------------------------
3669 TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore)
3671 // lock media device
3674 __PRINT(_L("CFatMountCB::Lock"));
3675 TInt r=CreateDrive(Drive().DriveNumber());
3679 TBusLocalDrive* local;
3680 r=LocalDrive()->GetLocalDrive(local);
3684 #ifdef _LOCKABLE_MEDIA
3685 if(local->Status()==KErrLocked)
3686 local->Status() = KErrNotReady;
3688 r=local->SetPassword(aOld,aNew,aStore);
3689 if(r==KErrNone&&aStore)
3690 WritePasswordData();
3694 //-----------------------------------------------------------------------------------------
3696 TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore)
3698 // Unlock media device
3701 __PRINT(_L("CFatMountCB::Unlock"));
3702 TInt r=CreateDrive(Drive().DriveNumber());
3706 TBusLocalDrive* local;
3707 r=LocalDrive()->GetLocalDrive(local);
3711 #ifdef _LOCKABLE_MEDIA
3712 if(local->Status()==KErrLocked)
3713 local->Status() = KErrNotReady;
3715 r=local->Unlock(aPassword,aStore);
3716 if(r==KErrNone&&aStore)
3717 WritePasswordData();
3721 //-----------------------------------------------------------------------------------------
3723 TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword)
3725 // Clear password from media device
3728 __PRINT(_L("CFatMountCB::ClearPassword"));
3729 TInt r=CreateDrive(Drive().DriveNumber());
3733 TBusLocalDrive* local;
3734 r=LocalDrive()->GetLocalDrive(local);
3738 #ifdef _LOCKABLE_MEDIA
3739 if(local->Status()==KErrLocked)
3740 local->Status() = KErrNotReady;
3742 r=local->Clear(aPassword);
3744 WritePasswordData();
3748 //-----------------------------------------------------------------------------------------
3750 TInt CFatMountCB::ErasePassword()
3752 // Forcibly erase the password from a media device
3755 __PRINT(_L("CFatMountCB::ErasePassword"));
3757 TInt r=CreateDrive(Drive().DriveNumber());
3761 TBusLocalDrive* local;
3762 r=LocalDrive()->GetLocalDrive(local);
3766 #ifdef _LOCKABLE_MEDIA
3767 if(local->Status()==KErrLocked)
3768 local->Status() = KErrNotReady;
3770 r=local->ErasePassword();
3773 // ...media change to ensure a fresh remount the drive
3774 r = local->ForceRemount(0);
3775 local->Status() = KErrNotReady;
3776 WritePasswordData();
3781 //-----------------------------------------------------------------------------------------
3783 TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags)
3785 // Force a remount of the drive
3788 __PRINT(_L("CFatMountCB::ForceRemountDrive"));
3789 TInt r=CreateDrive(Drive().DriveNumber());
3791 r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle);
3793 r=LocalDrive()->ForceRemount(aFlags);
3797 //-----------------------------------------------------------------------------------------
3799 void CFatMountCB::WritePasswordData()
3801 // Write store password data to disk
3804 __PRINT(_L("CFatMountCB::WritePasswordData"));
3805 TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
3806 mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
3807 __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile);
3808 TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
3809 TInt length=local.PasswordStoreLengthInBytes();
3812 WriteToDisk(mediaPWrdFile,_L8(""));
3815 HBufC8* hDes=HBufC8::New(length);
3818 TPtr8 pDes=hDes->Des();
3819 TInt r=local.ReadPasswordData(pDes);
3821 WriteToDisk(mediaPWrdFile,pDes);
3825 //-----------------------------------------------------------------------------------------
3828 Trim trailing spaces of volume label descriptor and adjust its length
3830 void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const
3832 // Locate first '\0'
3833 TInt nullPos = aLabel.Locate('\0');
3834 if (nullPos == KErrNotFound)
3835 nullPos = KVolumeLabelSize;
3837 // Trim trailing spaces
3839 for (i=nullPos-1; i>=0; --i)
3840 if (aLabel[i] != 0x20)
3842 aLabel.SetLength(i+1);
3845 //-----------------------------------------------------------------------------------------
3848 Searches for the volume label file
3850 @param aLabel The name of the volume label file returned upon successful search
3851 @return KErrNone if it finds the volume label file, otherwise KErrNotFound
3853 TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel)
3855 __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile"));
3856 TEntryPos pos(RootIndicator(),0);
3858 TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry));
3859 __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r);
3863 //-----------------------------------------------------------------------------------------
3866 Creates or updates the volume label file with name aNewName
3868 @param aNewName The new name for the volume label file
3870 void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName)
3872 __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName);
3873 TEntryPos pos(RootIndicator(),0);
3876 TBuf8<KVolumeLabelSize> oldName;
3877 TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry));
3881 // Found existing volume label file, rename or delete
3882 if(oldName == aNewName)
3884 __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match"));
3889 if(aNewName.Length() == 0)
3891 // delete the volume label file
3892 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete"));
3893 EraseDirEntryL(pos, entry);
3897 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace"));
3898 entry.SetName(aNewName);
3899 WriteDirEntryL(pos, entry);
3904 else if( KErrNotFound == r )
3906 // Not found, need to create if aNewName is not empty
3907 // Windows allows a volume label file to have the same name as
3908 // an existing file or directory
3909 if(aNewName.Length() > 0)
3911 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create"));
3912 TEntryPos dirPos(RootIndicator(),0);
3913 AddDirEntryL(dirPos,1);
3914 TFatDirEntry fatDirEntry;
3915 fatDirEntry.SetName(aNewName);
3916 fatDirEntry.SetAttributes(KEntryAttVolume);
3919 now.UniversalTime();
3920 fatDirEntry.SetTime(now, TimeOffset() );
3921 fatDirEntry.SetStartCluster(0);
3922 fatDirEntry.SetSize(0);
3923 WriteDirEntryL(dirPos, fatDirEntry);
3935 //-----------------------------------------------------------------------------------------
3938 Scans the root directory for a volume label file. Leaves with an error if not found
3940 @param aLabel Name of the volume label file upon successful search
3941 @param aDosEntryPos Pointer to position of the volume label file upon successful search
3942 @param aDosEntry Contains the entry for the volume label file upon successful search
3944 void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
3946 __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL"));
3948 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
3950 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root"));
3951 User::Leave(KErrNotFound); // Allows maximum number of entries in root directory
3954 TInt previousCluster= aDosEntryPos.iCluster;
3955 TUint previousPosition= aDosEntryPos.iPos;
3956 TInt changePreviousCluster=1;
3959 TFatDirEntry startEntry;
3960 TFileName dummyLongName;
3965 const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName);
3966 __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e);
3967 User::LeaveIfError(e);
3969 User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName));
3971 if(aDosEntry.IsEndOfDirectory())
3973 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir"));
3974 User::Leave(KErrNotFound);
3976 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
3978 if(aDosEntry.IsErased())
3980 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root"));
3981 User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
3984 if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage())
3986 if(aDosEntry.Attributes() & KEntryAttVolume)
3988 aLabel = aDosEntry.Name();
3990 dummyLongName.Copy(aLabel);
3991 __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName);
3996 MoveToNextEntryL(aDosEntryPos);
3997 if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
3999 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root"));
4000 User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
4002 if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
4004 DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count);
4006 previousPosition=aDosEntryPos.iPos;
4010 //-----------------------------------------------------------------------------------------
4013 Read volume label from disk and trim trailing 0x20 & 0x00 characters
4015 void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector)
4017 // Read volume label as 8 bit descriptor
4018 TBuf8<KVolumeLabelSize> volName8;
4019 TInt r = ReadVolumeLabelFile(volName8);
4020 if(r != KErrNone) // No volume label file in root directory
4021 volName8 = aBootSector.VolumeLabel();
4022 TrimVolumeLabel(volName8);
4024 TBuf16<KVolumeLabelSize> volName16;
4025 LocaleUtils::ConvertToUnicodeL(volName16, volName8);
4026 SetVolumeName(volName16.AllocL());
4030 //-----------------------------------------------------------------------------------------
4033 Populates iMap member of aInfo with contiguous block group maps.
4035 @param aPos Start position for a desired section of the file.
4036 @param sLength Length of the desired data to produce the block map for.
4037 @param aInfo A structure describing a group of block maps.
4039 void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo)
4041 __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL"));
4042 __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
4043 TBlockMapEntry blockMapEntry;
4046 TInt clusterRelativePos;
4049 TInt clusterListLen;
4053 TLocalDriveCapsBuf caps;
4055 TInt64 realPosition = 0;
4059 currentPos = aPos.iPos;
4060 temp = currentPos>>ClusterSizeLog2();
4061 if ( (currentPos) && ( (currentPos) == (temp<<ClusterSizeLog2()) ) )
4063 if (!FAT().GetNextClusterL(aPos.iCluster))
4065 __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL corrupt#1"))
4066 User::Leave(KErrCorrupt);
4069 clusterRelativePos = ClusterRelativePos( aPos.iPos );
4070 maxClusters = ((aLength + clusterRelativePos - 1)>>ClusterSizeLog2())+1;
4071 clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters);
4072 readLength = Min( aLength, (clusterListLen<<ClusterSizeLog2()) - clusterRelativePos);
4074 blockMapEntry.SetNumberOfBlocks( clusterListLen );
4075 if (aPos.iCluster < 2)
4076 User::Leave(KErrCorrupt);
4077 r = LocalDrive()->Caps(caps);
4078 if ( r != KErrNone )
4079 User::LeaveIfError(r);
4080 if ( caps().iType&EMediaRam )
4082 realPosition = FAT().DataPositionInBytes( aPos.iCluster );
4083 aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2());
4084 blockMapEntry.SetStartBlock( aPos.iCluster );
4087 blockMapEntry.SetStartBlock( aPos.iCluster - 2);
4088 aInfo.iMap.Append(TPckgC<TBlockMapEntry>(blockMapEntry));
4089 aPos.iPos += readLength;
4090 aPos.iCluster = endCluster;
4091 aLength -= readLength;
4093 while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) );
4097 //-----------------------------------------------------------------------------------------
4099 TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
4101 TFatDirEntry firstEntry;
4102 TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory
4103 FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
4105 aDosEntryPos=firstEntryPos;
4106 aDosEntry=firstEntry;
4107 MoveToDosEntryL(aDosEntryPos,aDosEntry);
4112 //-----------------------------------------------------------------------------------------
4114 TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId)
4116 // Get first cluster of file
4117 TEntryPos dosEntryPos(RootIndicator(),0);
4118 TFatDirEntry dosEntry;
4119 InitializeRootEntry(dosEntry); // Nugatory initialisation to placate warnings
4120 TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry));
4124 TInt startCluster=StartCluster(dosEntry);
4125 // Empty files will return a cluster of zero
4129 aUniqueId=MAKE_TINT64(0,startCluster);
4132 //-----------------------------------------------------------------------------------------
4135 TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4137 return KErrNotSupported;
4140 TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4142 return KErrNotSupported;
4145 TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
4147 return KErrNotSupported;
4150 //-----------------------------------------------------------------------------------------
4153 Check file system for errors.
4154 @return KErrNone if no errors found, otherwise a error code hopefully describing the problem found.
4156 TInt CFatMountCB::CheckDisk()
4159 __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber());
4161 if(!ConsistentState())
4164 //-- create a bit representation of the FAT
4165 const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
4166 if (MaxClusters == 0)
4169 //-- used for measuring time
4172 timeStart.UniversalTime(); //-- take start time
4176 CScanDrive* pScnDrv = NULL;
4177 TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
4178 if(nRes != KErrNone)
4181 //-- start ScanDrive in "checkdisk" mode
4182 TRAPD(nScnDrvRes, pScnDrv->StartL(CScanDrive::ECheckDisk));
4184 timeEnd.UniversalTime(); //-- take end time
4185 const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
4187 __PRINT1(_L("#@@@ CheckDisk() time taken:%d ms"), msScanTime);
4189 CScanDrive::TGenericError chkDskRes = pScnDrv->ProblemsDiscovered();
4190 const TBool bProblemsFound = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
4192 if(bProblemsFound && chkDskRes == CScanDrive::ENoErrors)
4193 {//-- ScanDrive in this mode can leave unexpectedly without setting an error code that is returned by ProblemsDiscovered();
4194 //-- leave itself means a problem
4195 chkDskRes = CScanDrive::EUnknownError;
4200 if(chkDskRes != KErrNone)
4202 __PRINT2(_L("CFatMountCB::CheckDisk() drv:%d, result:%d"), DriveNumber(), chkDskRes);
4210 //-------------------------------------------------------------------------------------------------------------------
4213 Creates a scan drive object and starts the scan.
4215 TInt CFatMountCB::DoRunScanDrive()
4219 CScanDrive* pScnDrv = NULL;
4220 TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
4221 if(nRes != KErrNone)
4224 TRAPD(nScnDrvRes, pScnDrv->StartL(CScanDrive::EScanAndFix));
4226 const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
4231 {//-- ScanDrive found and probably fixed some errors.
4232 // ensure cached fat and free cluster count are updated
4233 DoDismount(); //-- dismount
4234 TRAP(nRes, MountL(EFalse)); //-- mount again
4237 if(nScnDrvRes != KErrNone)
4241 //-- if ScanDrive hasn't found anything wrong or has fixed recoverable errors, mark the volume clean
4242 if(VolCleanFlagSupported())
4244 //-- if there is a background FAT scanning thread, we need to wait until it finishes its work.
4245 //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting.
4246 (void)FAT().NumberOfFreeClusters(ETrue);
4247 TRAP(nRes, FinaliseMountL());
4248 ASSERT(nRes == KErrNone);
4254 //-------------------------------------------------------------------------------------------------------------------
4257 Run scan drive on the given volume.
4258 The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly.
4261 @return Either KErrCorrupt if an error was found that is not caused by write failure due to power removal.
4262 KErrNone if no error was found. One of four positive codes explaining what type of error was rectified
4264 TInt CFatMountCB::ScanDrive()
4266 __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber());
4268 if(!ConsistentState())
4275 __PRINT(_L("CFatMountCB::ScanDrive() locked!\n"));
4280 {//-- Do not check internal RAM drive
4281 __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive."));
4285 //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt
4286 if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume())
4288 TBool bVolClean = EFalse;
4289 TRAP(nRes, bVolClean = VolumeCleanL());
4291 if(nRes == KErrNone && bVolClean)
4293 __PRINT(_L("Skipping ScanDrive on finalised volume!"));
4294 return KErrNone; //-- skip ScanDrive on a clean volume
4300 if(nRes != KErrNone)
4303 nRes = DoRunScanDrive();
4307 __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes);
4313 //-----------------------------------------------------------------------------------------
4315 Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset
4316 if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not
4317 a null offset is returned.
4319 @return The offset in seconds that timestamps on the filesystem have, relative to UTC.
4321 TTimeIntervalSeconds CFatMountCB::TimeOffset() const
4323 if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() )
4325 return User::UTCOffset();
4329 return TTimeIntervalSeconds(0);
4336 //-----------------------------------------------------------------------------------------
4338 Check is this file system can be mounted on the drive at all.
4339 Just read and validate boot region, no real mounting overhead.
4341 @return KErrNone boot region is OK, the file system can be mounted.
4342 KErrLocked the media is locked on a physical level.
4343 other error codes depending on the implementation
4346 TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable()
4350 const TInt driveNo = Drive().DriveNumber();
4351 __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo);
4353 nRes = CreateDrive(driveNo);
4354 if(nRes != KErrNone)
4356 __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes);
4360 //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive()
4361 TFatBootSector bootSector;
4362 nRes = ReadBootSector(bootSector);
4364 DismountedLocalDrive();
4369 //-----------------------------------------------------------------------------------------
4371 Internal helper method.
4372 @param aFatType FAT type
4373 @return End Of Cluster Chain code that depend on FAT type, 0xff8 for FAT12, 0xfff8 for FAT16, and 0xffffff8 for FAT32
4375 TUint32 EocCodeByFatType(TFatType aFatType)
4380 return EOF_32Bit-7; //-- 0xffffff8
4383 return EOF_16Bit-7; //-- 0xfff8
4386 return EOF_12Bit-7; //-- 0xff8
4389 ASSERT(aFatType == EInvalid);
4395 //-----------------------------------------------------------------------------------------
4397 Set FAT type that this object of CFatMountCB will be dealing with.
4399 void CFatMountCB::SetFatType(TFatType aFatType)
4401 ASSERT(State() == ENotMounted || State() == EDismounted || State() == EMounting) ;
4403 iFatType = aFatType;
4404 iFatEocCode = EocCodeByFatType(aFatType);