diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/sfat32/sl_mnt32.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfat32/sl_mnt32.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -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 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 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; iWrite(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; iWrite(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)<>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()< 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()< 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()); + + // 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<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<FAT().GetNextClusterL(endCluster); + __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed)); + cluster=endCluster; + } + }