/** 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())
void CSVPHostMountCB::DirOpenL(const TDesC& aName ,CDirCB* aDir) { DP(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName); CSVPHostDirCB& dir=(*((CSVPHostDirCB*)aDir)); TBuf<KMaxPath> name; TUint driveNumber = Drive().DriveNumber(); CanonicalizePathname(aName, driveNumber, name); TSVPHostFsDirOpenInfo info(driveNumber, name); TInt err = SVP_HOST_FS_DEVICE().DirOpen(info); User::LeaveIfError(err); dir.SetHandle(info.iHandle); TFileName n(TDriveUnit(Drive().DriveNumber()).Name()); n.Append(aName); dir.SetFullName(n); }
/** Format a disk section, called iteratively to erase whole of media, on last iteration creates an empty volume. If called with quick formatonly erases the Fat leaving the rest of the volume intact. @leave System wide error code */ void CFatFormatCB::DoFormatStepL() { if (iFormatInfo.iFormatIsCurrent==EFalse) { // Only done first time through if (iMode & EForceErase) { TInt r = FatMount().ErasePassword(); User::LeaveIfError(r); // CFatMountCB::ErasePassword() calls TBusLocalDrive::ForceRemount(), // so need to stop a remount from occurring in next call to : // TFsFormatNext::DoRequestL((), TDrive::CheckMount(). FatMount().Drive().SetChanged(EFalse); } RecordOldInfoL(); InitializeFormatDataL(); FatMount().DoDismount(); if (iVariableSize) FatMount().ReduceSizeL(0,I64LOW(FatMount().iSize)); } // // Blank disk if not EQuickFormat // if (!iVariableSize && !(iMode & EQuickFormat) && iCurrentStep) { if (iFormatInfo.iFormatIsCurrent == EFalse) {//-- firstly invalidate sectors 0-6 inclusive, they may contain main boot sector, backup boot sector and FSInfo sector. DoZeroFillMediaL(0, (KBkBootSectorNum+1)*iBytesPerSector); } TInt ret=FatMount().LocalDrive()->Format(iFormatInfo); if (ret!=KErrNone && ret!=KErrEof) // Handle format error ret = HandleCorrupt(ret); if (ret!=KErrNone && ret!=KErrEof) // KErrEof could be set by LocalDrive()->Format() User::Leave(ret); if (ret==KErrNone) { iCurrentStep = I64LOW( 100 - (100 * TInt64(iFormatInfo.i512ByteSectorsFormatted)) / iMaxDiskSectors ); if (iCurrentStep<=0) iCurrentStep=1; return; } } // ReMount since MBR may have been rewritten and partition may have moved / changed size TInt ret = LocalDrive()->ForceRemount(0); if (ret != KErrNone && ret != KErrNotSupported) User::Leave(ret); // MBR may have changed, so need to re-read iHiddenSectors etc.before BPB is written InitializeFormatDataL(); // Translate bad sector number to cluster number which contains that sector // This only happens in full format, in quick format they are already cluster numbers if (!iVariableSize && !(iMode & EQuickFormat)) User::LeaveIfError(BadSectorToCluster()); //Check if root cluster is bad and update as required if(Is32BitFat() && !iVariableSize && (iMode & EQuickFormat)) { if(iBadClusters.Find(iRootClusterNum) != KErrNotFound) { iRootClusterNum++; while(iBadClusters.Find(iRootClusterNum) != KErrNotFound) { iRootClusterNum++; } } } // // Do the rest of the disk in one lump // iCurrentStep=0; //-- zero-fill media from position 0 to the FAT end, i.e main & backup boot sector, FSInfo and its copy and all FATs const TUint32 posFatEnd = ((iSectorsPerFat*iNumberOfFats) + iReservedSectors) * iBytesPerSector; //-- last FAT end position if (iVariableSize) FatMount().EnlargeL(posFatEnd); DoZeroFillMediaL(0, posFatEnd); if(Is32BitFat()) {//create an empty root directory entry here const TUint KFat32EntrySz = 4; //-- FAT32 entry size, bytes const TInt startFAT1 = iReservedSectors; //-- FAT1 start sector const TInt entryOffset = iRootClusterNum*KFat32EntrySz; //-- Root dir entry offset in the FAT, bytes TBuf8<KFat32EntrySz> EOF(KFat32EntrySz); EOF[0]=0xFF; EOF[1]=0xFF; EOF[2]=0xFF; EOF[3]=0x0F; //-- write EOF mark to the every FAT copy for(TInt i=0; i<iNumberOfFats; i++) { const TInt rootDirEntryPos = iBytesPerSector*(startFAT1 + i*iSectorsPerFat) + entryOffset; User::LeaveIfError(LocalDrive()->Write(rootDirEntryPos, EOF)); } //-- zero-fill FAT32 root directory (just 1 cluster) const TInt firstDataSector = iReservedSectors + (iNumberOfFats * iSectorsPerFat); //+RootDirSectors (not required for fat32) const TInt firstSectorOfCluster = ((iRootClusterNum - KFatFirstSearchCluster) * iSectorsPerCluster) + firstDataSector; const TUint32 posRootDirStart = firstSectorOfCluster * iBytesPerSector; const TUint32 posRootDirEnd = posRootDirStart + iSectorsPerCluster*iBytesPerSector; DoZeroFillMediaL(posRootDirStart, posRootDirEnd); } else {//-- FAT12/16 //-- Zero fill root directory const TInt rootDirSector = iReservedSectors + (iNumberOfFats * iSectorsPerFat); const TInt rootDirSize = iRootDirEntries * KSizeOfFatDirEntry; //-- size in bytes const TUint32 posRootDirStart = rootDirSector * iBytesPerSector; const TUint32 posRootDirEnd = posRootDirStart + rootDirSize; const TInt numOfRootSectors=(rootDirSize%iBytesPerSector) ? (rootDirSize/iBytesPerSector+1) : (rootDirSize/iBytesPerSector); if (iVariableSize) FatMount().EnlargeL(iBytesPerSector*numOfRootSectors); DoZeroFillMediaL(posRootDirStart, posRootDirEnd); // Enlarge ram drive to take into account rounding of // data start to cluster boundary if(iVariableSize && iSectorsPerCluster!=1) { const TInt firstFreeSector=rootDirSector+numOfRootSectors; const TInt firstFreeCluster=firstFreeSector%iSectorsPerCluster ? firstFreeSector/iSectorsPerCluster+1 : firstFreeSector/iSectorsPerCluster; const TInt alignedSector=firstFreeCluster*iSectorsPerCluster; if(alignedSector!=firstFreeSector) FatMount().EnlargeL((alignedSector-firstFreeSector)*iBytesPerSector); } } //-- FAT[0] must contain media descriptor in the low byte, FAT[1] for fat16/32 may contain some flags TBuf8<8> startFat(8); startFat.Fill(0xFF); if(Is32BitFat()) //-- FAT32 {//-- FAT32 uses only low 28 bits in FAT entry. startFat[3] = 0x0F; startFat[7] = 0x0F; } else if(iVariableSize||Is16BitFat()) //-- FAT16 or RAM drive which is always FAT16 { startFat.SetLength(4); } else //-- FAT12 { startFat.SetLength(3); } startFat[0]=KBootSectorMediaDescriptor; //-- write FAT[0] and FAT[1] entries to all copies of FAT for(TInt i=0;i<iNumberOfFats;i++) { User::LeaveIfError(LocalDrive()->Write(iBytesPerSector*(iReservedSectors+(iSectorsPerFat*i)),startFat)); } //-- create boot sectors CreateBootSectorL(); //-- create FSInfo sectors if (Is32BitFat()) { CreateReservedBootSectorL(); CreateFSInfoSectorL(); } //-- here we have bad clusters numbers saved by the quick format //-- Interpret old bad cluster number to new cluster number and mark new bad clusters if (!iVariableSize && iBadClusters.Count()>0) { //-- Here we need fully mounted volume, so mount it normally. FatMount().MountL(EFalse); iBadClusters.Sort(); TranslateL(); const TInt mark = FatMount().Is32BitFat() ? KBad_32Bit : (FatMount().Is16BitFat() ? KBad_16Bit : KBad_12Bit); for (TInt i=0; i<iBadClusters.Count(); ++i) FatMount().FAT().WriteL(iBadClusters[i], mark); FatMount().FAT().FlushL(); //-- indicate that the volume is "dirty" in order to the next Mount evend not to use FSInfo, which //-- contains incorrect value of free clusters because we already have bad ones saved. //-- This is a very rare condition. FatMount().SetVolumeCleanL(EFalse); #if defined(_DEBUG) TInt r=FatMount().CheckDisk(); __PRINT1(_L("CFatFormatCB::DoFormatStepL() CheckDisk res: %d"),r); #endif } else { //-- We do not need to perform full mount in this case, the TDrive object will be marked as changed in ~CFormatCB and the //-- mount will be closed. Therefore on the first access to it it will be mounted normally. FatMount().MountL(ETrue); //-- force mount } __PRINT1(_L("CFatFormatCB::DoFormatStepL() Format complete drv:%d"), DriveNumber()); }
/** Create the boot sector on media for the volume. For FAT32 also creates a backup copy of the boot sector. @leave System wide error codes */ void CFatFormatCB::CreateBootSectorL() { __PRINT1(_L("CFatFormatCB::CreateBootSector() drive:%d"),DriveNumber()); _LIT8(KName_Fat12,"FAT12 "); ///< Name in BPB given to a Fat12 volume _LIT8(KName_Fat16,"FAT16 "); ///< Name in BPB given to a Fat16 volume _LIT8(KName_Fat32,"FAT32 "); ///< Name in BPB given to a Fat32 volume _LIT8(KDefaultVendorID, "EPOC"); ///< Vendor Name for BPB for any volume formated using a Symbian OS device const TBool bFat32 = Is32BitFat(); TFatBootSector bootSector; bootSector.SetVendorID(KDefaultVendorID); bootSector.SetBytesPerSector(iBytesPerSector); bootSector.SetSectorsPerCluster(iSectorsPerCluster); bootSector.SetReservedSectors(iReservedSectors); bootSector.SetNumberOfFats(iNumberOfFats); iCountOfClusters=iMaxDiskSectors/iSectorsPerCluster; if (!bFat32) { if (iCountOfClusters>(TInt)KMaxTUint16) User::Leave(KErrTooBig); } bootSector.SetReservedByte(0); TTime timeID; timeID.HomeTime(); bootSector.SetUniqueID(I64LOW(timeID.Int64())); // Generate Volume UniqueID from time bootSector.SetVolumeLabel(_L8("")); //-- set a text string in BPB that corresponds to the FS type switch(FatType()) { case EFat12: bootSector.SetFileSysType(KName_Fat12); break; case EFat16: bootSector.SetFileSysType(KName_Fat16); break; case EFat32: bootSector.SetFileSysType(KName_Fat32); break; default: ASSERT(0); User::Leave(KErrArgument); }; bootSector.SetJumpInstruction(); bootSector.SetMediaDescriptor(KBootSectorMediaDescriptor); bootSector.SetNumberOfHeads(iNumberOfHeads); bootSector.SetHiddenSectors(iHiddenSectors); bootSector.SetSectorsPerTrack(iSectorsPerTrack); bootSector.SetPhysicalDriveNumber(128); bootSector.SetExtendedBootSignature(0x29); if(bFat32) { bootSector.SetFatSectors(0); bootSector.SetFatSectors32(iSectorsPerFat); bootSector.SetRootDirEntries(0); bootSector.SetTotalSectors(0); bootSector.SetHugeSectors(iMaxDiskSectors); bootSector.SetFATFlags(0); bootSector.SetVersionNumber(0x00); bootSector.SetRootClusterNum(iRootClusterNum); bootSector.SetFSInfoSectorNum(KFSInfoSectorNum); bootSector.SetBkBootRecSector(KBkBootSectorNum); } else//fat12 and 16 { bootSector.SetFatSectors32(0); bootSector.SetFatSectors(iSectorsPerFat); bootSector.SetRootDirEntries(iRootDirEntries); if (iMaxDiskSectors<=KMaxTUint16) { bootSector.SetTotalSectors(iMaxDiskSectors); bootSector.SetHugeSectors(0); } else { bootSector.SetTotalSectors(0); bootSector.SetHugeSectors(iMaxDiskSectors); } } //-- write main boot sector to the first sector on media User::LeaveIfError(FatMount().DoWriteBootSector(KBootSectorNum*bootSector.BytesPerSector(), bootSector)); //-- for FAT32 write backup copy of the boot sector if(bFat32) { User::LeaveIfError(FatMount().DoWriteBootSector(KBkBootSectorNum*bootSector.BytesPerSector(), bootSector)); } }
/** 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(); __PRINT3(_L("CFatMountCB::MountL() drv:%d, forceMount=%d, RuggedFAT:%d\n"), driveNo, aForceMount, IsRuggedFSys()); 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 SetFatType(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")); SetFatType(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()); }
/** 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 disk access objects opened on it. This precondition must be checked before calling this function if(aClean && Locked()) { __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 TDriveInterface 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(TUint32 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(TUint32 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); }