--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfat32/sl_mnt32.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1119 @@
+// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfat32\sl_mnt32.cpp
+// CFatMountCB code, specific to the EFAT32.FSY
+//
+//
+
+/**
+ @file
+*/
+
+#include "sl_std.h"
+#include "sl_cache.h"
+#include "sl_leafdir_cache.h"
+
+//-------------------------------------------------------------------------------------------------------------------
+
+
+/**
+ Write aligned members of TFatBootSector to media
+
+ @param aMediaPos media position the data will be written to
+ @param aBootSector data to write
+ @return Media write error code
+*/
+TInt CFatMountCB::DoWriteBootSector(TInt64 aMediaPos, const TFatBootSector& aBootSector) const
+ {
+ __PRINT2(_L("#- CFatMountCB::DoWriteBootSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);
+
+ ASSERT(aMediaPos>=0);
+
+ TBuf8<KDefaultSectorSize> bootSecBuf(KDefaultSectorSize);
+ bootSecBuf.FillZ();
+
+ //-- externalize boot sector to the data buffer
+ aBootSector.Externalize(bootSecBuf);
+
+ //-- put a boot sector signature to the last 2 bytes
+ bootSecBuf[KDefaultSectorSize-2] = 0x55;
+ bootSecBuf[KDefaultSectorSize-1] = 0xaa;
+
+ //-- write boot sector to the media
+ TInt r=LocalDrive()->Write(aMediaPos, bootSecBuf);
+ if (r!=KErrNone)
+ {//-- write failure
+ __PRINT2(_L("CFatMountCB::DoWriteBootSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
+ }
+
+ return r;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+ Read non aligned boot data from media into TFatBootSector structure
+
+ @param aMediaPos media position the data will be read from
+ @param aBootSector refrence to TFatBootSector populate
+ @return Media read error code
+*/
+TInt CFatMountCB::DoReadBootSector(TInt64 aMediaPos, TFatBootSector& aBootSector) const
+ {
+ __PRINT2(_L("#- CFatMountCB::DoReadBootSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);
+
+ ASSERT(aMediaPos>=0);
+
+ TBuf8<KSizeOfFatBootSector> bootSecBuf(KSizeOfFatBootSector);
+
+ //-- read boot sector from the media
+ TInt r=LocalDrive()->Read(aMediaPos, KSizeOfFatBootSector, bootSecBuf);
+
+ if (r != KErrNone)
+ {
+ __PRINT2(_L("CFatMountCB::DoReadBootSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
+
+ //-- fiddling with the error code; taken from MountL()
+ if (r==KErrNotSupported)
+ return KErrNotReady;
+ #if defined(_LOCKABLE_MEDIA)
+ else if(r==KErrLocked)
+ return KErrLocked;
+ #endif
+ else if (r!=KErrNoMemory && r!=KErrNotReady && r!=KErrCorrupt && r!=KErrUnknown)
+ return KErrCorrupt;
+
+ return r;
+ }
+
+ ASSERT(r==KErrNone);
+
+ //-- initialise TFatBootSector object
+ aBootSector.Internalize(bootSecBuf);
+
+ //-- Validate the partition size, and fix up if the out of bounds
+ TLocalDriveCapsV2Buf localDriveCaps;
+ r = LocalDrive()->Caps(localDriveCaps);
+ if (r != KErrNone)
+ {
+ //-- fiddling with the error code; taken from MountL()
+ if (r!=KErrNoMemory && r!=KErrNotReady && r!=KErrCorrupt && r!=KErrUnknown)
+ return KErrCorrupt;
+ else
+ return r;
+ }
+
+ if(!(localDriveCaps().iMediaAtt & KMediaAttVariableSize))
+ {//-- this is not a RAM drive.
+ const TUint32 maxSectors = I64LOW(localDriveCaps().iSize >> KDefSectorSzLog2);
+
+ if(aBootSector.TotalSectors())
+ aBootSector.SetTotalSectors(Min(aBootSector.TotalSectors(), maxSectors));
+ else
+ aBootSector.SetHugeSectors(Min(aBootSector.HugeSectors(), maxSectors));
+ }
+
+ return KErrNone;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+ Read and validate the boot sector.
+ If there is an error in reading the main boot sector (sec:0) or it is invalid, tries to read backup boot sector from sec:6.
+ Flag iMainBootSecValid indicates the validity of the main boot sector. if it is false, but ret. value is KErrNone, it means that
+ the backup boot sector was used and it is valid.
+
+ @param aBootSector reference to the boot sector object to be read.
+ @param aDoNotReadBkBootSec if true, there won't be an attempt to read backup sector
+ @return standard error code.
+
+*/
+TInt CFatMountCB::ReadBootSector(TFatBootSector& aBootSector, TBool aDoNotReadBkBootSec/*=EFalse*/)
+ {
+ iMainBootSecValid = EFalse;
+
+ //-- read main boot sector from the sector 0
+ TInt nRes = DoReadBootSector(KBootSectorNum << KDefSectorSzLog2, aBootSector);
+ if(nRes == KErrNone)
+ {
+ if(aBootSector.IsValid())
+ {
+ iMainBootSecValid = ETrue; //-- main boot sector is valid, everything is OK
+ return KErrNone;
+ }
+ else
+ {
+ __PRINT(_L("MainBoot Sector is invalid! dump:\n"));
+ aBootSector.PrintDebugInfo();
+ nRes = KErrCorrupt;
+ }
+ }
+
+ ASSERT(nRes!= KErrNone && !iMainBootSecValid);
+
+ if(aDoNotReadBkBootSec)
+ return nRes;
+
+ //-- main boot sector is invalid, try backup one (it might not present at all)
+ __PRINT(_L("Using backup boot sector...\n"));
+ nRes=DoReadBootSector(KBkBootSectorNum << KDefSectorSzLog2, aBootSector);
+ if(nRes == KErrNone )
+ {
+ if(aBootSector.IsValid())
+ return KErrNone; //-- main boot sector is bad, but backup one is OK
+ else
+ {//-- backup boot sector is invalid
+ __PRINT(_L("Backup Sector is invalid! dump:\n"));
+ aBootSector.PrintDebugInfo();
+ nRes = KErrCorrupt;
+ }
+ }
+
+ //-- can't read boot sectors, or both are invalid
+ return nRes;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+Write a new volume label to BPB in media
+
+@param aVolumeLabel Descriptor containing the new volume label
+@leave
+*/
+void CFatMountCB::WriteVolumeLabelL(const TDesC8& aVolumeLabel) const
+ {
+ if(aVolumeLabel.Length() > KVolumeLabelSize)
+ User::Leave(KErrArgument);
+
+ const TUint32 posVolLabel = Is32BitFat() ? KFat32VolumeLabelPos : KFat16VolumeLabelPos;
+ User::LeaveIfError(LocalDrive()->Write(posVolLabel, aVolumeLabel));
+
+ }
+
+
+//-------------------------------------------------------------------------------------------------------------------
+
+const TUint32 KFat32CleanShutDownMask = 0x08000000; ///< Mask used to indicate test clean/dirty bit for Fat32
+const TUint16 KFat16CleanShutDownMask = 0x08000; ///< Mask used to indicate test clean/dirty bit for Fat16
+
+/**
+Set or reset "VolumeClean" (ClnShutBitmask) flag.
+
+@param aClean if ETrue, marks the volume as clean, otherwise as dirty.
+@leave if write error occured.
+*/
+void CFatMountCB::SetVolumeCleanL(TBool aClean)
+ {
+
+ //-- The volume can't be set clean if there are objects opened on it. This precondition must be checked before calling this function
+ if(aClean && LockStatus()!=0)
+ {
+ __PRINT1(_L("#- CFatMountCB::SetVolumeCleanL drive:%d isn't free!"),DriveNumber());
+ ASSERT(0);
+ User::Leave(KErrInUse);
+ return;
+ }
+
+ if(FatType() == EFat12)
+ {//-- Fat12 doesn't support this feature; do nothing other than notify the underlying drive (ignoring any error for now as there's nothing we can do with it)
+ (void)LocalDrive()->Finalise(aClean);
+ return;
+ }
+
+ //-- further read and write will be directly from the CProxyDrive, bypassing FAT cache.
+ //-- this is because CFatTable doesn't allow access to FAT[0] & FAT[1]
+ //-- We also need to write data through CProxyDrive, because TFatDriveInterface has a call back that can call this method
+
+ if(Is32BitFat())
+ {//-- Fat32
+ __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL, drive:%d, param:%d, FAT32"),DriveNumber(), aClean);
+
+ TFat32Entry fatEntry;
+ const TInt KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
+ TPtr8 ptrFatEntry((TUint8*)&fatEntry,KFatEntrySize);
+
+ User::LeaveIfError(LocalDrive()->Read(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT32[1] entry
+
+ const TFat32Entry tmp = fatEntry;
+
+ if(aClean)
+ fatEntry |= KFat32CleanShutDownMask; //-- set ClnShutBit flag
+ else
+ fatEntry &= ~KFat32CleanShutDownMask; //-- reset ClnShutBit flag
+
+ if(tmp != fatEntry)
+ {//-- write FAT[1] entry to all available FATs
+ for(TInt i=0; i<NumberOfFats(); ++i)
+ {
+ const TInt64 pos = StartOfFatInBytes()+KFatEntrySize+(FatSizeInBytes()*i);
+ User::LeaveIfError(LocalDrive()->Write(pos, ptrFatEntry)); //write FAT32[1] entry
+ }
+ }
+
+ __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL() entry: %x->%x"), tmp, fatEntry);
+ }
+ else
+ if(Is16BitFat())
+ {//-- Fat16.
+ __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL, drive:%d, param:%d, FAT16"),DriveNumber(), aClean);
+
+ if(FatConfig().FAT16_UseCleanShutDownBit())
+ {
+ TFat16Entry fatEntry;
+ const TInt KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
+ TPtr8 ptrFatEntry((TUint8*)&fatEntry,KFatEntrySize);
+
+ User::LeaveIfError(LocalDrive()->Read(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT16[1] entry
+
+ const TFat16Entry tmp = fatEntry;
+
+ if(aClean)
+ fatEntry |= KFat16CleanShutDownMask; //-- set ClnShutBit flag
+ else
+ fatEntry &= ~KFat16CleanShutDownMask; //-- reset ClnShutBit flag
+
+ if(tmp != fatEntry)
+ {//-- write FAT[1] entry to all available FATs
+ for(TInt i=0; i<NumberOfFats(); ++i)
+ {
+ const TInt64 pos = StartOfFatInBytes()+KFatEntrySize+(FatSizeInBytes()*i);
+ User::LeaveIfError(LocalDrive()->Write(pos, ptrFatEntry)); //write FAT16[1] entry
+ }
+ }
+
+ __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL() entry: %x->%x"), tmp, fatEntry);
+ }
+ else
+ {
+ __PRINT(_L("#- changing FAT16[1] is disabled in config!"));
+ }
+ }
+ else
+ {//-- must never get here
+ ASSERT(0);
+ }
+
+ //-- Notify the underlying media that the mount is consistent (ignoring any error for now as there's nothing we can do with it)
+ (void)LocalDrive()->Finalise(aClean);
+
+
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+Determine whether "VolumeClean" (ClnShutBitmask) flag is set.
+
+@return ETrue if the volume is marked as clean and EFalse otherwise.
+@leave if is called for FAT12 or if read error occured.
+*/
+TBool CFatMountCB::VolumeCleanL()
+ {
+
+ //-- read access to the FAT is through TFatDriveInterface, because CFatTable doesn't allow access to FAT[1]
+ TFatDriveInterface& drive =DriveInterface();
+
+ if(Is32BitFat())
+ {//-- Fat32
+ TFat32Entry fatEntry;
+ const TInt KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
+ TPtr8 ptrFatEntry((TUint8*)&fatEntry, KFatEntrySize);
+
+ User::LeaveIfError(drive.ReadNonCritical(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT32[1] entry
+ return (fatEntry & KFat32CleanShutDownMask);
+ }
+ else
+ if(Is16BitFat())
+ {//-- Fat16
+ TFat16Entry fatEntry;
+ const TInt KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
+ TPtr8 ptrFatEntry((TUint8*)&fatEntry, KFatEntrySize);
+
+ User::LeaveIfError(drive.ReadNonCritical(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT16[1] entry
+ return (fatEntry & KFat16CleanShutDownMask);
+ }
+ else
+ {//-- Fat12 doesn't support this feature, shan't get here, actually
+ ASSERT(0);
+ User::Leave(KErrNotSupported);
+ return ETrue; //-- to satisfy the compiler
+ }
+ }
+
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+Mount a Fat volume.
+
+@param aForceMount Flag to indicate whether mount should be forced to succeed if an error occurs
+@leave KErrNoMemory,KErrNotReady,KErrCorrupt,KErrUnknown.
+*/
+void CFatMountCB::MountL(TBool aForceMount)
+ {
+
+ const TInt driveNo = Drive().DriveNumber();
+
+ __PRINT2(_L("CFatMountCB::MountL() drv:%d, forceMount=%d\n"),driveNo,aForceMount);
+
+ ASSERT(State() == ENotMounted || State() == EDismounted);
+ SetState(EMounting);
+ SetReadOnly(EFalse);
+
+ User::LeaveIfError(CreateDrive(driveNo));
+
+
+ //-- read FAT configuration parameters from estart.txt
+ iFatConfig.ReadConfig(driveNo);
+
+ //-- initialise interface to the low-level drive access
+ if(!iDriverInterface.Init(this))
+ User::LeaveIfError(KErrNoMemory);
+
+ //-- get drive capabilities
+ TLocalDriveCapsV2Buf capsBuf;
+ User::LeaveIfError(LocalDrive()->Caps(capsBuf));
+
+
+ iSize=capsBuf().iSize;
+ iRamDrive = EFalse;
+
+ if(capsBuf().iMediaAtt & KMediaAttVariableSize)
+ {//-- this is a RAM drive
+ UserSvr::UnlockRamDrive();
+ iRamDrive = ETrue;
+ }
+
+ if(aForceMount)
+ {//-- the state is "forcedly mounted", special case. This is an inconsistent state.
+ SetState(EInit_Forced);
+ return;
+ }
+
+ //-- read boot sector. If main is damaged, try to use backup one instead if this is not a RAM drive.
+ TFatBootSector bootSector;
+ User::LeaveIfError(ReadBootSector(bootSector, iRamDrive));
+
+
+ //-- print out boot sector debug information
+ bootSector.PrintDebugInfo();
+
+ //-- determine FAT type by data from boot sector. This is done by counting number of clusters, not by BPB_RootEntCnt
+ iFatType = bootSector.FatType();
+ ASSERT(iFatType != EInvalid); //-- this shall be checked in ReadBootSector()
+
+
+ if(bootSector.RootDirEntries() == 0 && !Is32BitFat())
+ {//-- FAT types mismatch. BPB_RootEntCnt is 0, which can be only for FAT32, but the number of clusters is less
+ //-- than required for FAT32. Probably this is incorrectly FAT32 formatted media. Put the drive into ReadOnly mode, assuming
+ //-- that is FAT32.
+ __PRINT(_L("FAT type mismatch! Setting drive to ReadOnly mode for FAT32. \n"));
+ iFatType = EFat32; //-- force FAT type to be FAT32
+ SetReadOnly(ETrue);
+ }
+
+ //-- store volume UID, it can be checked on Remount
+ iUniqueID = bootSector.UniqueID();
+
+ //-- populate volume parameters with the values from boot sector. They had been validated in TFatBootSector::IsValid()
+ iVolParam.Populate(bootSector);
+
+ //-- initialize the volume
+ InitializeL(capsBuf());
+ ASSERT(State()==EInit_R);
+
+ GetVolumeLabelFromDiskL(bootSector);
+
+ __PRINT2(_L("CFatMountCB::MountL() Completed, drv: %d, state:%d"), DriveNumber(), State());
+ }
+
+
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+Initialize the FAT cache and disk access
+
+@param aLocDrvCaps local drive capabilities
+@param aIgnoreFSInfo if ETrue, FSInfo sector shall be ignored. Used on volume remount to force FAT free clusters counting.
+
+@leave KErrNoMemory,KErrNotReady,KErrCorrupt,KErrUnknown.
+*/
+void CFatMountCB::InitializeL(const TLocalDriveCaps& aLocDrvCaps, TBool aIgnoreFSInfo/*=EFalse*/)
+ {
+ __PRINT1(_L("CFatMountCB::InitializeL() drv:%d"), DriveNumber());
+
+ ASSERT(State() == EMounting); //-- we must get here only from MountL()
+
+ //========== Find out number of clusters on the volume
+ if(iRamDrive && SectorsPerCluster()!=1)
+ {// Align iFirstFreeByte to cluster boundary if internal ram drive
+ const TInt sectorsPerClusterLog2=ClusterSizeLog2()-SectorSizeLog2();
+ const TInt rootDirEndSector=RootDirEnd()>>SectorSizeLog2();
+ const TInt alignedSector=((rootDirEndSector+SectorsPerCluster()-1)>>sectorsPerClusterLog2)<<sectorsPerClusterLog2;
+ iFirstFreeByte=alignedSector<<SectorSizeLog2();
+ }
+ else
+ {
+ if(Is32BitFat())
+ iFirstFreeByte=(NumberOfFats() * FatSizeInBytes()) + (FirstFatSector() << SectorSizeLog2());
+ else
+ iFirstFreeByte=RootDirEnd();
+ }
+
+
+ {//-- check if volume geometry looks valid
+ const TInt usableSectors=TotalSectors()-(iFirstFreeByte>>SectorSizeLog2());
+ iUsableClusters=usableSectors>>(ClusterSizeLog2()-SectorSizeLog2());
+
+ const TUint32 KMinClusters = 32; //-- absolute minimum number of clusters on the volume
+ const TUint32 KMaxClusters=(TotalSectors()-FirstFatSector()-NumberOfFats()*(FatSizeInBytes()>>SectorSizeLog2())) >> (ClusterSizeLog2()-SectorSizeLog2());
+
+ if(usableSectors <=0 || iUsableClusters < KMinClusters || iUsableClusters > KMaxClusters)
+ {
+ __PRINT(_L("CFatMountCB::InitializeL() Wrong number of usable cluster/sectors on the volume!"));
+ User::Leave(KErrCorrupt);
+ }
+ }
+
+ //========== initialise RawDisk interface
+ //-- CFatMountCB parameters might have changed, e.g. after formatting. Reconstruct directory cache with new parameters
+
+ delete iRawDisk;
+ iRawDisk=CRawDisk::NewL(*this, aLocDrvCaps);
+ iRawDisk->InitializeL();
+
+
+ //========== Try to read FSInfo and deduct number of free clusters and other information from there
+ TBool bUseDataFromFsInfo = !aIgnoreFSInfo && Is32BitFat(); //-- if ETrue, we are going to use data from FSInfo sector (applicable for FAT32 only)
+
+ //-- main boot sector shall be valid, otherwise we can't trust data from FSInfo
+ bUseDataFromFsInfo = bUseDataFromFsInfo && iMainBootSecValid;
+
+ //-- 1. check if using FSInfo is disabled in config
+ if(bUseDataFromFsInfo && !FatConfig().FAT32_UseFSInfoOnMount())
+ {
+ __PRINT(_L("#- FSInfo using is disabled in config!"));
+ bUseDataFromFsInfo = EFalse;
+ }
+
+#ifdef _DEBUG
+ //-- 2. check if FSInfo is disabled by test interface (special debug property). This property is defined and set by the test application.
+ TInt nMntDebugFlags;
+ if(bUseDataFromFsInfo && RProperty::Get(KSID_Test1, DriveNumber(), nMntDebugFlags) == KErrNone)
+ {//-- test property for this drive is defined
+ if(nMntDebugFlags & KMntDisable_FsInfo)
+ {
+ __PRINT(_L("#- FSInfo using is disabled by debug interface."));
+ bUseDataFromFsInfo = EFalse;
+ }
+ }
+#endif
+
+ //-- 3. try to read FSInfoSector and its copy if the volume had been properly shut down before (is now clean)
+ CFatTable::TMountParams fatMntParams;
+ bUseDataFromFsInfo = bUseDataFromFsInfo && VolumeCleanL();
+ if(bUseDataFromFsInfo)
+ {
+ bUseDataFromFsInfo = ProcessFSInfoSectors(fatMntParams);
+ if(!bUseDataFromFsInfo)
+ {
+ __PRINT1(_L("#- CFatMountCB::ProcessFSInfoSectors() failed. drv:%d"), DriveNumber());
+ }
+ }
+
+ //========== create and initialise FAT table
+
+ delete iFatTable;
+ iFatTable=CFatTable::NewL(*this, aLocDrvCaps);
+
+ //-- mount the FAT table. Depending on mount parameters and configuration this method
+ //-- can do various things, like counting free clusters synchronously if data from FSInfo isn't valid,
+ //-- or setting up a FAT backround thread and return immediately etc.
+ iFatTable->MountL(fatMntParams);
+
+ SetState(EInit_R); //-- the state is "Initialized, but not writen"
+
+ //-- make a callback, telling FileServer about free space discovered.
+ const TInt64 freeSpace = ((TInt64)FAT().NumberOfFreeClusters()) << ClusterSizeLog2();
+ SetDiskSpaceChange(freeSpace);
+ //========== create and setup leaf direcotry cache if cache limit is set bigger than one
+
+ const TUint32 cacheLimit = iFatConfig.LeafDirCacheSize();
+ if (cacheLimit > 1)
+ {
+ // destroy the old leaf dir cache to avoid memory leak.
+ delete iLeafDirCache;
+ iLeafDirCache = CLeafDirCache::NewL(cacheLimit);
+ }
+ else
+ {
+ iLeafDirCache = NULL;
+ }
+
+ __PRINT3(_L("#- CFatMountCB::InitializeL() done. drv:%d, Free clusters:%d, 1st Free cluster:%d"),DriveNumber(), FAT().NumberOfFreeClusters(), FAT().FreeClusterHint());
+
+
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+ Write FSInfo sectors to the media
+
+ @param aMediaPos Media position to write FSInfo sector to
+ @param aFSInfo FSInfo structure to write
+ @return System wide error code
+*/
+TInt CFatMountCB::WriteFSInfoSector(TInt64 aMediaPos, const TFSInfo& aFSInfo) const
+ {
+ __PRINT2(_L("#- CFatMountCB::WriteFSInfoSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);
+
+ ASSERT(aMediaPos >= 0 && aMediaPos < FirstFatSector()<<SectorSizeLog2());
+ ASSERT(Is32BitFat());
+
+ TBuf8<KSizeOfFSInfo> fsInfoSecBuf;
+
+ //-- put data to the sector buffer
+ aFSInfo.Externalize(fsInfoSecBuf);
+
+ TInt r=LocalDrive()->Write(aMediaPos, fsInfoSecBuf);
+ if (r!=KErrNone)
+ {
+ __PRINT2(_L("CFatMountCB::WriteFSInfoSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
+ }
+
+ return r;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+/**
+ Read the FSInfo structure from media
+
+ @param aMediaPos Media position to read FSInfo sector from
+ @param aFSInfo data read from FSInfo structure
+ @return System wide error code
+*/
+TInt CFatMountCB::ReadFSInfoSector(TInt64 aMediaPos, TFSInfo& aFSInfo) const
+ {
+ __PRINT2(_L("#- CFatMountCB::ReadFSInfoSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);
+
+ ASSERT(aMediaPos >= 0 && aMediaPos < FirstFatSector()<<SectorSizeLog2());
+ ASSERT(Is32BitFat());
+
+ TBuf8<KSizeOfFSInfo> fsInfoSecBuf;
+
+ TInt r=LocalDrive()->Read(aMediaPos, KSizeOfFSInfo, fsInfoSecBuf);
+ if (r!=KErrNone)
+ {
+ __PRINT2(_L("CFatMountCB::ReadFSInfoSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
+ return r;
+ }
+
+ //-- take FSInfo data from the buffer
+ aFSInfo.Internalize(fsInfoSecBuf);
+
+ return KErrNone;
+ }
+
+/**
+Checks for end of file for all Fat types
+
+@param aCluster Cluster to check
+@return Result of test
+*/
+TBool CFatMountCB::IsEndOfClusterCh(TInt aCluster) const
+ {
+ if(Is32BitFat())
+ return(aCluster>=(TInt)0x0FFFFFF8 && aCluster<=(TInt)0x0FFFFFFF);
+ else if(Is16BitFat())
+ return(aCluster>=0xFFF8 && aCluster<=0xFFFF);
+ else
+ return(aCluster>=0xFF8 && aCluster<=0xFFF);
+ }
+
+/**
+Set a cluster to the end of cluster chain marker
+
+@param aCluster cluster to set to end of chain marker
+*/
+void CFatMountCB::SetEndOfClusterCh(TInt &aCluster) const
+ {
+ if(Is32BitFat())
+ aCluster=EOF_32Bit;
+ else if(Is16BitFat())
+ aCluster=EOF_16Bit;
+ else
+ aCluster=EOF_12Bit;
+ }
+
+/**
+Initialize data to represent the root directory
+
+@param anEntry Entry to initialise
+*/
+void CFatMountCB::InitializeRootEntry(TFatDirEntry & anEntry) const
+ {
+ anEntry.SetName(_L8("ROOT"));
+ anEntry.SetAttributes(KEntryAttDir);
+ anEntry.SetStartCluster(RootClusterNum()); //--iRootClusterNum is 0 for FAT12/16
+ }
+
+
+
+/**
+Implementation of CMountCB::FileSystemSubType(). Retrieves the sub type of Fat file system
+and returns the name as a descriptor.
+
+@param aName Name of the sub type of Fat file system
+@return KErrNone if successful; KErrArgument if aName is not long enough; KErrNotReady if
+ the mount is not ready.
+
+@see CMountCB::FileSystemSubType()
+*/
+TInt CFatMountCB::SubType(TDes& aName) const
+ {
+ if(aName.MaxLength() < 5)
+ return KErrArgument;
+
+ switch (iFatType)
+ {
+ case EFat12:
+ {
+ aName = KFSSubType_FAT12;
+ return KErrNone;
+ }
+ case EFat16:
+ {
+ aName = KFSSubType_FAT16;
+ return KErrNone;
+ }
+ case EFat32:
+ {
+ aName = KFSSubType_FAT32;
+ return KErrNone;
+ }
+ default:
+ // case EInvalidFatType
+ return KErrNotReady;
+ }
+ }
+
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+ Try to extract useful information from the FSInfo sectors.
+ The information from FSInfo sectors will be trusted only if there are main and backup sectors,
+ they are both valid and the same.
+
+ @param aFatInitParams on success will contain the number of free clusters on the volume and 1st free cluster number from the FSInfo
+ @return ETrue on success.
+*/
+TBool CFatMountCB::ProcessFSInfoSectors(CFatTable::TMountParams& aFatInitParams) const
+ {
+
+ aFatInitParams.iFreeClusters = 0;
+ aFatInitParams.iFirstFreeCluster = 0;
+ aFatInitParams.iFsInfoValid = EFalse;
+
+ const TUint32 currFsInfoSec = iVolParam.FSInfoSectorNum(); //-- values from the boot sector
+ const TUint32 currFsInfoBkSec = iVolParam.BkFSInfoSectorNum();
+
+ if(!Is32BitFat() || currFsInfoSec < KFSInfoSectorNum || currFsInfoSec >= FirstFatSector())
+ {
+ ASSERT(0); //-- main FSInfo sector must have sensible location
+ return EFalse;
+ }
+
+ if(currFsInfoBkSec < KFSInfoSectorNum || currFsInfoBkSec >= FirstFatSector() || currFsInfoBkSec <= currFsInfoSec)
+ return EFalse; //-- something is wrong with backup copy location
+
+ TFSInfo fsInfo;
+ TInt nRes;
+
+ //-- 1. read and validate main FS Info sector
+ nRes = ReadFSInfoSector(currFsInfoSec << SectorSizeLog2(), fsInfo);
+ if(nRes != KErrNone)
+ return EFalse;
+
+ fsInfo.PrintDebugInfo();
+
+ if(!fsInfo.IsValid())
+ return EFalse;
+
+ const TUint32 freeCount=fsInfo.FreeClusterCount(); // last known free cluster count
+ const TUint32 nextFree =fsInfo.NextFreeCluster(); // hint to file system as to where to start looking for free clusters
+
+ //-- 2. read and check backup FS Info sector, it must be the same as the main one
+ nRes = ReadFSInfoSector(currFsInfoBkSec << SectorSizeLog2(), fsInfo);
+
+ if(nRes != KErrNone || !fsInfo.IsValid() || freeCount != fsInfo.FreeClusterCount() || nextFree != fsInfo.NextFreeCluster())
+ {
+ __PRINT(_L("#- CFatMountCB::ProcessFSInfoSectors(): copies of FSInfo are different!"));
+ return EFalse;
+ }
+
+ if(freeCount < 1 || freeCount > UsableClusters())
+ return EFalse; //-- looks like invalid value
+
+ if(nextFree < KFatFirstSearchCluster || nextFree >= UsableClusters()+KFatFirstSearchCluster)
+ return EFalse; //-- looks like invalid value
+
+ //-- success
+ aFatInitParams.iFreeClusters = freeCount;
+ aFatInitParams.iFirstFreeCluster = nextFree;
+ aFatInitParams.iFsInfoValid = ETrue;
+
+ return ETrue;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+/**
+ Internal helper method. Writes FSInfo sector and its backup copy to the volume if necessary.
+
+ @param aInvalidateFSInfo if ETrue, FSInfo data (free clusters count) will be invalidated.
+ otherwise, data from the CFatTable object will be used
+
+ @leave if disk opertion fails
+*/
+void CFatMountCB::DoUpdateFSInfoSectorsL(TBool aInvalidateFSInfo)
+ {
+ __PRINT2(_L("#- CFatMountCB::DoUpdateFSInfoSectorsL(%d) drv:%d"),aInvalidateFSInfo, Drive().DriveNumber());
+
+ ASSERT(Is32BitFat());
+
+ if(!aInvalidateFSInfo)
+ {
+ ASSERT(FAT().ConsistentState());
+ }
+
+ //-- 1. check that FSInfoSector numbers are valid
+ TBool bCanWriteFSInfo_Main = EFalse;//-- if ETrue, it's OK to write main FSInfo sector
+ TBool bCanWriteFSInfo_Bk = EFalse; //-- if ETrue, it's OK to write backup FSInfo sector
+
+ const TUint32 currFsInfoSec = iVolParam.FSInfoSectorNum(); //-- values from the boot sector
+ const TUint32 currFsInfoBkSec = iVolParam.BkFSInfoSectorNum();
+
+ if(currFsInfoSec > 0 && currFsInfoSec < FirstFatSector())
+ {//-- seems to be OK
+ bCanWriteFSInfo_Main = ETrue;
+ }
+ else
+ {
+ __PRINT1(_L("#- DoUpdateFSInfoSectorsL() iFSInfoSectorNum is wrong!: sec:%d"), currFsInfoSec);
+ }
+
+ if(currFsInfoBkSec > 0 && currFsInfoBkSec < FirstFatSector() && currFsInfoBkSec > currFsInfoSec)
+ {//-- seems to be OK
+ bCanWriteFSInfo_Bk = bCanWriteFSInfo_Main;
+ }
+ else
+ {
+ __PRINT1(_L("#- DoUpdateFSInfoSectorsL() iBkFSInfoSectorNum is wrong!: sec:%d"),currFsInfoBkSec);
+ }
+
+ if(!bCanWriteFSInfo_Main && !bCanWriteFSInfo_Bk)
+ return; //-- nothing to do
+
+
+ const TUint32 KFsInfoSecPos_Main = currFsInfoSec << SectorSizeLog2(); //-- main FSInfo sector media position
+ const TUint32 KFsInfoSecPos_Bk = currFsInfoBkSec << SectorSizeLog2(); //-- backup FSInfo sector media position
+
+ TFSInfo fsInfoSector;
+
+ TBool bNeedWriteFSInfo_Main = EFalse;
+ TBool bNeedWriteFSInfo_Bk = EFalse;
+
+
+ const TUint32 KInvalidVal = 0xFFFFFFFF; //-- invalid value for FSInfo fields, see FAT specs
+
+ //-- we need here _exact_ number of free clusters, so make FAT().NumberOfFreeClusters() operation synchronous
+ const TUint32 KFreeClusters = aInvalidateFSInfo ? KInvalidVal : FAT().NumberOfFreeClusters(ETrue);
+ const TUint32 KNextFreeCluster = aInvalidateFSInfo ? KInvalidVal : FAT().FreeClusterHint();
+
+ //-- check if the main FSInfo sector differs from the FAT information
+ if(bCanWriteFSInfo_Main)
+ {
+ User::LeaveIfError(ReadFSInfoSector(KFsInfoSecPos_Main, fsInfoSector));
+ bNeedWriteFSInfo_Main = !fsInfoSector.IsValid() ||
+ fsInfoSector.FreeClusterCount() != KFreeClusters ||
+ fsInfoSector.NextFreeCluster() != KNextFreeCluster;
+ }
+
+ //-- check if the backup FSInfo sector differs from the FAT information
+ if(bCanWriteFSInfo_Bk)
+ {
+ User::LeaveIfError(ReadFSInfoSector(KFsInfoSecPos_Bk, fsInfoSector));
+ bNeedWriteFSInfo_Bk = !fsInfoSector.IsValid() ||
+ fsInfoSector.FreeClusterCount() != KFreeClusters ||
+ fsInfoSector.NextFreeCluster() != KNextFreeCluster;
+ }
+
+ //-- setup data in FSInfo sector to write
+ fsInfoSector.Initialise();
+ fsInfoSector.SetFreeClusterCount(KFreeClusters);
+ fsInfoSector.SetNextFreeCluster(KNextFreeCluster);
+
+ if(!bNeedWriteFSInfo_Main && !bNeedWriteFSInfo_Bk)
+ return; //-- nothing to do
+
+ SetVolumeCleanL(EFalse); //-- mark volume dirty, just in case something will happen on FSInfo sectors write
+
+ if(bNeedWriteFSInfo_Main)
+ User::LeaveIfError(WriteFSInfoSector(KFsInfoSecPos_Main, fsInfoSector));
+
+ if(bNeedWriteFSInfo_Bk)
+ User::LeaveIfError(WriteFSInfoSector(KFsInfoSecPos_Bk, fsInfoSector));
+
+ }
+
+//-------------------------------------------------------------------------------------------------------------------
+
+/**
+ CFatMountCB control method.
+ @param aLevel specifies the operation to perfrom on the mount
+ @param aOption specific option for the given operation
+ @param aParam pointer to generic parameter, its meaning depends on aLevel and aOption
+
+ @return standard error code.
+*/
+TInt CFatMountCB::MountControl(TInt aLevel, TInt aOption, TAny* aParam)
+ {
+ __PRINT3(_L("CFatMountCB::MountControl() drv:%d, level:%d, opt:%d"),Drive().DriveNumber(), aLevel, aOption);
+
+ TInt nRes = KErrNotSupported;
+
+ if(aLevel == ECheckFsMountable)
+ {
+ return MntCtl_DoCheckFileSystemMountable();
+ }
+
+ //-- todo: move these functions code into separate methods ??
+
+ //-- mount state query: check if is in finalised state
+ if(aLevel == EMountStateQuery && aOption == ESQ_IsMountFinalised)
+ {
+ TBool bFinalised;
+ nRes = IsFinalised(bFinalised);
+ if(nRes == KErrNone)
+ {
+ *((TBool*)aParam) = bFinalised;
+ }
+
+ return nRes;
+ }
+
+ //-- mount-specific volume parameters queries that might not be handled by CFatMountCB::VolumeL
+ if(aLevel == EMountVolParamQuery)
+ {
+ ASSERT(ConsistentState()); //-- volume state shall be consistent, otherwise its parameters do not make sense
+ switch(aOption)
+ {
+ //-- Request a certain amount of free space on the volume.
+ case ESQ_RequestFreeSpace:
+ {
+ TUint64* pVal = (TUint64*)aParam; //-- in: number of free bytes on the volume required, out: resulted amount of free space.
+ const TUint32 KClustersRequired = (TUint32)((*pVal + ClusterSize() - 1) >> ClusterSizeLog2());
+ __PRINT2(_L("MountControl() ReqFreeSpace:%LU, clusters:%d"), *pVal, KClustersRequired);
+
+ if(KClustersRequired)
+ {//-- actually, this doesn't guarantee that it will finally be KClustersRequired available.
+ (void)FAT().RequestFreeClusters(KClustersRequired);
+ }
+
+ const TUint32 freeClusters = FAT().NumberOfFreeClusters(EFalse); //-- _current_ amount of free clusters
+ *pVal = (TInt64)freeClusters << ClusterSizeLog2();
+
+ return KErrNone;
+ }
+
+ //-- A request to obtain the _current_ amount of free space on the volume asynchronously, without blocking.
+ case ESQ_GetCurrentFreeSpace:
+ {
+ TUint64* pVal = (TUint64*)aParam; //-- out: resulted amount of free space.
+
+ const TUint32 freeClusters = FAT().NumberOfFreeClusters(EFalse); //-- _current_ amount of free clusters
+ *pVal = (TInt64)freeClusters << ClusterSizeLog2();
+ __PRINT1(_L("MountControl() Asynch. request; curent amount of free clusters: %d"), freeClusters);
+
+ return KErrNone;
+ }
+
+ //-- A request to obtain size of the mounted volume without blocking (CMountCB::VolumeL() can block).
+ case ESQ_MountedVolumeSize:
+ {
+ if(iRamDrive)
+ return KErrNotSupported; //-- it requires knowledge of free space on the volume
+
+ TUint64* pVal = (TUint64*)aParam;
+ *pVal = iSize; //-- physical drive size
+
+ //-- take into account space occupied by FAT table, etc.
+ *pVal -= ClusterBasePosition();
+ *pVal=(*pVal >> ClusterSizeLog2()) << ClusterSizeLog2(); //-- round down to cluster size
+
+ __PRINT1(_L("MountControl() MountedVolumeSize:%LU"), *pVal);
+ return KErrNone;
+ }
+
+ default:
+ __PRINT1(_L("MountControl() unsupported opt:%d"), aOption);
+ ASSERT(0);
+ break;
+ };
+
+ }
+
+ //-- File System - specific queries
+ if(aLevel == EMountFsParamQuery && aOption == ESQ_GetMaxSupportedFileSize)
+ {//-- this is a query to provide the max. supported file size; aParam is a pointer to TUint64 to return the value
+ *(TUint64*)aParam = KMaxSupportedFatFileSize;
+ return KErrNone;
+ }
+
+
+
+
+ return KErrNotSupported;
+ }
+
+//-----------------------------------------------------------------------------------------
+
+/**
+Reports whether the specified interface is supported - if it is,
+the supplied interface object is modified to it
+
+@param aInterfaceId The interface of interest
+@param aInterface The interface object
+@return KErrNone if the interface is supported, otherwise KErrNotFound
+
+@see CMountCB::GetInterface()
+*/
+TInt CFatMountCB::GetInterface(TInt aInterfaceId, TAny*& aInterface,TAny* aInput)
+ {
+ switch(aInterfaceId)
+ {
+ case (CMountCB::EFileAccessor):
+ ((CMountCB::MFileAccessor*&) aInterface) = this;
+ return KErrNone;
+
+ case (CMountCB::EGetFileSystemSubType):
+ aInterface = (MFileSystemSubType*) (this);
+ return KErrNone;
+
+ case (CMountCB::EGetClusterSize):
+ aInterface = (MFileSystemClusterSize*) (this);
+ return KErrNone;
+
+ case CMountCB::ELocalBufferSupport:
+ // RAM drives doesn't support local buffers (this results in file caching being disabled)
+ if (iRamDrive)
+ return KErrNotSupported;
+ else
+ return LocalDrive()->LocalBufferSupport();
+
+ case EGetProxyDrive:
+ ((CProxyDrive*&)aInterface) = LocalDrive();
+ return KErrNone;
+
+ case CMountCB::EFileExtendedInterface:
+ // For supporting large file ReadFileSection
+ ((CMountCB::MFileExtendedInterface*&) aInterface) = this;
+ return KErrNone;
+
+ default:
+ return(CMountCB::GetInterface(aInterfaceId, aInterface, aInput));
+ }
+ }
+
+//-----------------------------------------------------------------------------------------
+void CFatMountCB::ReadSection64L(const TDesC& aName, TInt64 aPos, TAny* aTrg, TInt aLength, const RMessagePtr2& aMessage)
+// From CMountCB::MFileExtendedInterface
+ {
+ __PRINT(_L("CFatMountCB::ReadSection64L"));
+
+ CheckStateConsistentL();
+ TEntryPos dosEntryPos(RootIndicator(),0);
+ TFatDirEntry dosEntry;
+ TFileName fileName;
+
+ TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
+ TLeafDirData leafDir(0);
+ dosEntryPos.iCluster=FindLeafDirL(aName.Left(namePos), leafDir);
+ dosEntryPos.iPos=0;
+ TEntryPos firstEntryPos;
+ TFatDirEntry firstEntry;
+ DoFindL(aName.Mid(namePos),KEntryAttMaskSupported,
+ firstEntryPos,firstEntry,dosEntryPos,dosEntry,
+ fileName,KErrNotFound,
+ NULL,
+ leafDir);
+
+// Check that reading from aPos for aLength lies within the file
+// if aPos is within the file, and aLength is too long, read up to EOF
+// If aPos is beyond the end of the file, return a zero length descriptor
+
+ TInt64 fileSize = MAKE_TINT64(0,dosEntry.Size());
+ if (aPos>=fileSize)
+ User::Leave(KErrEof);
+
+ if (aPos+aLength>fileSize)
+ aLength=(TInt)(fileSize-aPos);
+
+ TInt cluster=StartCluster(dosEntry);
+ TInt64 pos = aPos;
+
+ TInt endCluster;
+ TInt clusterSize=1<<ClusterSizeLog2(); // Size of file clusters
+ TInt readTotal = 0;
+
+ // Total number of clusters in file
+ TInt maxClusters=(TInt)((fileSize+clusterSize-1)>>ClusterSizeLog2());
+
+ // Read data
+ FOREVER
+ {
+ // Get the maximum number of clusters that can be read contiguously
+ TInt64 clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
+ __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));
+
+ // If start position within this block, then read some data
+ if (pos<(clusterListLen<<ClusterSizeLog2()))
+ {
+ // Read the remaining length or the entire cluster block whichever is smaller
+ TInt readLength = (TInt)Min((TInt64)(aLength-readTotal),(clusterListLen<<ClusterSizeLog2())-pos);
+ __ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
+ TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
+ iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
+ readTotal += readLength;
+
+ if (readTotal == aLength)
+ return;
+
+ pos += readLength;
+ }
+
+ // Get the next cluster in file
+ pos-=(clusterListLen<<ClusterSizeLog2());
+#if defined(_DEBUG)
+ TBool remainingClusters=
+#endif
+ ((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
+ __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
+ cluster=endCluster;
+ }
+ }