First public contribution.
1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // f32\sfat\sl_fat16.cpp
17 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
18 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20 //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
22 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
23 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
29 const TUint KDefFatResvdSec = 1; ///< default number of FAT12/16 reserved sectors
32 Initialize the format parameters for a normal fixed sized disk
33 Setting set to adhere to Rules of Count of clusters for FAT type
35 @param aDiskSizeInSectors Size of volume in sectors
36 @return system-wide error code
38 TInt CFatFormatCB::InitFormatDataForFixedSizeDiskNormal(TInt aDiskSizeInSectors, const TLocalDriveCapsV6& aCaps)
40 if( Drive().IsRemovable() )
41 iNumberOfFats = KNumberOfFatsExternal;
43 iNumberOfFats = KNumberOfFatsInternal;
45 iReservedSectors=KDefFatResvdSec;
46 if (aDiskSizeInSectors<4084*1) // < 2MB
50 iFileSystemName=KFileSystemName12;
51 iSectorsPerFat=MaxFat12Sectors();
53 else if (aDiskSizeInSectors<4084*2) // < 4MB (8168 sectors)
57 iFileSystemName=KFileSystemName12;
58 iSectorsPerFat=MaxFat12Sectors();
60 else if (aDiskSizeInSectors<4084*4) // < 8MB (16336 sectors)
64 iFileSystemName=KFileSystemName12;
65 iSectorsPerFat=MaxFat12Sectors();
67 else if (aDiskSizeInSectors<4084*8) // < 16MB (32672 sectors)
71 iFileSystemName=KFileSystemName12;
72 iSectorsPerFat=MaxFat12Sectors();
74 else // >= 16Mb - FAT16
76 iFileSystemName=KFileSystemName16;
77 TInt minSectorsPerCluster=(aDiskSizeInSectors+KMaxFAT16Entries-1)/KMaxFAT16Entries;
80 while (minSectorsPerCluster>iSectorsPerCluster)
81 iSectorsPerCluster<<=1;
82 iSectorsPerFat=MaxFat16Sectors();
85 // Ensure cluster size is a multiple of the block size
86 TInt blockSizeInSectors = aCaps.iBlockSize >> iSectorSizeLog2;
87 __PRINT1(_L("blockSizeInSectors: %d"),blockSizeInSectors);
88 ASSERT(blockSizeInSectors == 0 || IsPowerOf2(blockSizeInSectors));
89 if (blockSizeInSectors != 0 && IsPowerOf2(blockSizeInSectors))
91 __PRINT1(_L("iSectorsPerCluster (old): %d"),iSectorsPerCluster);
92 AdjustClusterSize(blockSizeInSectors);
93 __PRINT1(_L("iSectorsPerCluster (new): %d"),iSectorsPerCluster);
96 // Align first data sector on an erase block boundary if
97 // (1) the iEraseBlockSize is specified
98 // (2) the start of the partition is already aligned to an erase block boundary,
99 // i.e. iHiddenSectors is zero or a multiple of iEraseBlockSize
100 __PRINT1(_L("iHiddenSectors: %d"),iHiddenSectors);
101 TInt eraseblockSizeInSectors = aCaps.iEraseBlockSize >> iSectorSizeLog2;
102 __PRINT1(_L("eraseblockSizeInSectors: %d"),eraseblockSizeInSectors);
103 ASSERT(eraseblockSizeInSectors == 0 || IsPowerOf2(eraseblockSizeInSectors));
104 ASSERT(eraseblockSizeInSectors == 0 || eraseblockSizeInSectors >= blockSizeInSectors);
105 if ((eraseblockSizeInSectors != 0) &&
106 (iHiddenSectors % eraseblockSizeInSectors == 0) &&
107 (IsPowerOf2(eraseblockSizeInSectors)) &&
108 (eraseblockSizeInSectors >= blockSizeInSectors))
110 TInt r = AdjustFirstDataSectorAlignment(eraseblockSizeInSectors);
111 ASSERT(r == KErrNone);
114 __PRINT1(_L("iReservedSectors: %d"),iReservedSectors);
115 __PRINT1(_L("FirstDataSector: %d"), FirstDataSector());
120 TInt CFatFormatCB::FirstDataSector() const
122 TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector;
123 return iHiddenSectors + iReservedSectors + iNumberOfFats*iSectorsPerFat + rootDirSectors;
126 void CFatFormatCB::AdjustClusterSize(TInt aRecommendedSectorsPerCluster)
128 const TInt KMaxSecPerCluster = 64; // 32K
129 while (aRecommendedSectorsPerCluster > iSectorsPerCluster && iSectorsPerCluster <= (KMaxSecPerCluster/2))
130 iSectorsPerCluster<<= 1;
133 // AdjustFirstDataSectorAlignment()
134 // Attempts to align the first data sector on an erase block boundary by modifying the
135 // number of reserved sectors.
136 TInt CFatFormatCB::AdjustFirstDataSectorAlignment(TInt aEraseBlockSizeInSectors)
138 const TBool bFat16 = Is16BitFat();
140 // Save these 2 values in the event of a convergence failure; this should
141 // hopefully never happen, but we will cater for this in release mode to be safe,
142 TInt reservedSectorsSaved = iReservedSectors;
143 TInt sectorsPerFatSaved = iSectorsPerFat;
145 TInt reservedSectorsOld = 0;
148 TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector;
151 TInt KMaxIterations = 10;
153 for (n=0; n<KMaxIterations && reservedSectorsOld != iReservedSectors; n++)
155 reservedSectorsOld = iReservedSectors;
157 iSectorsPerFat = bFat16 ? MaxFat16Sectors() : MaxFat12Sectors();
159 fatSectors = iSectorsPerFat * iNumberOfFats;
161 // calculate number of blocks
162 TInt nBlocks = (iReservedSectors + fatSectors + rootDirSectors + aEraseBlockSizeInSectors-1) / aEraseBlockSizeInSectors;
164 iReservedSectors = (nBlocks * aEraseBlockSizeInSectors) - rootDirSectors - fatSectors;
167 ASSERT(iReservedSectors >= (TInt) KDefFatResvdSec);
169 if ((FirstDataSector() & (aEraseBlockSizeInSectors-1)) == 0)
175 iReservedSectors = reservedSectorsSaved;
176 iSectorsPerFat = sectorsPerFatSaved;
182 Initialize the user specific format parameters for fixed sized disk.
184 @param aDiskSizeInSectors disk size in sectors
185 @return system-wide error code
187 TInt CFatFormatCB::InitFormatDataForFixedSizeDiskUser(TInt aDiskSizeInSectors)
189 //-- KErrArgument will be returned if iSpecialInfo().iFATBits isn't one of EFB32, EFB16, EFB32
191 if(iSpecialInfo().iFlags & TLDFormatInfo::EOneFatTable)
193 else if(iSpecialInfo().iFlags & TLDFormatInfo::ETwoFatTables)
195 else if(Drive().IsRemovable())
196 iNumberOfFats = KNumberOfFatsExternal;
198 iNumberOfFats = KNumberOfFatsInternal;
201 if(iSpecialInfo().iReservedSectors == 0)
202 iReservedSectors = KDefFatResvdSec; //-- user hasn't specified reserved sectors count, use default (FAT12/16)
204 iReservedSectors = iSpecialInfo().iReservedSectors;
207 const TInt KMaxSecPerCluster = 64;
208 const TInt KDefaultSecPerCluster= 8; //-- default value, if the iSpecialInfo().iSectorsPerCluster isn't specified
210 iSectorsPerCluster = iSpecialInfo().iSectorsPerCluster;
211 if(iSectorsPerCluster <= 0)
212 {//-- default value, user hasn't specified TLDFormatInfo::iSectorsPerCluster
213 iSectorsPerCluster = KDefaultSecPerCluster; //-- will be adjusted later
217 iSectorsPerCluster = Min(1<<Log2(iSectorsPerCluster), KMaxSecPerCluster);
220 //-----------------------------------------
222 if (aDiskSizeInSectors < 4096) // < 2MB
224 iSectorsPerCluster = 1;
225 iRootDirEntries = 128;
227 else if (aDiskSizeInSectors < 8192) // < 4MB
229 iSectorsPerCluster = Min(iSectorsPerCluster, 2);
230 iRootDirEntries = 256;
232 else if (aDiskSizeInSectors < 32768) // < 16MB
234 iSectorsPerCluster = Min(iSectorsPerCluster, 4);
235 iRootDirEntries = 512;
237 else if (aDiskSizeInSectors < 131072) // < 64MB
239 iSectorsPerCluster = Min(iSectorsPerCluster, 8);
240 iRootDirEntries = 512;
243 iRootDirEntries = 512;
245 //-----------------------------------------
247 TLDFormatInfo::TFATBits fatBits = iSpecialInfo().iFATBits;
248 if (fatBits == TLDFormatInfo::EFBDontCare)
250 const TFatType fatType = SuggestFatType();
254 fatBits = TLDFormatInfo::EFB12;
257 fatBits = TLDFormatInfo::EFB16;
260 fatBits = TLDFormatInfo::EFB32;
267 TFatType reqFatType(EInvalid); //-- requested FAT type
271 case TLDFormatInfo::EFB12:
272 iFileSystemName=KFileSystemName12;
273 iSectorsPerFat=MaxFat12Sectors();
277 case TLDFormatInfo::EFB16:
278 iFileSystemName=KFileSystemName16;
279 iSectorsPerFat=MaxFat16Sectors();
283 case TLDFormatInfo::EFB32:
284 __PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() FAT32 Not supported!"));
285 return KErrNotSupported;
288 __PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() Incorrect FAT type specifier!"));
292 //-- check if we can format the volume with requested FAT type
293 const TFatType fatType = SuggestFatType();
294 if(fatType != reqFatType)
295 {//-- volume metrics don't correspond to the requested FAT type
296 __PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() FAT type mismatch!"));
305 Initialize the format parameters for a custom fixed sized disk
307 @param aFormatInfo The custom format parameters
308 @return system-wide error code
310 TInt CFatFormatCB::InitFormatDataForFixedSizeDiskCustom(const TLDFormatInfo& aFormatInfo)
312 if(aFormatInfo.iFlags & TLDFormatInfo::EOneFatTable)
314 else if(aFormatInfo.iFlags & TLDFormatInfo::ETwoFatTables)
316 else if(Drive().IsRemovable())
317 iNumberOfFats = KNumberOfFatsExternal;
319 iNumberOfFats = KNumberOfFatsInternal;
323 iSectorsPerCluster = aFormatInfo.iSectorsPerCluster;
324 iSectorsPerTrack = aFormatInfo.iSectorsPerTrack;
325 iNumberOfHeads = aFormatInfo.iNumberOfSides;
326 iReservedSectors = aFormatInfo.iReservedSectors ? aFormatInfo.iReservedSectors : KDefFatResvdSec;
328 switch (aFormatInfo.iFATBits)
330 case TLDFormatInfo::EFB12:
331 iFileSystemName = KFileSystemName12;
332 iSectorsPerFat = MaxFat12Sectors();
335 case TLDFormatInfo::EFB16:
336 iFileSystemName = KFileSystemName16;
337 iSectorsPerFat = MaxFat16Sectors();
342 TInt64 clusters64 = (aFormatInfo.iCapacity / KDefaultSectorSize) / iSectorsPerCluster;
343 TInt clusters = I64LOW(clusters64);
346 iFileSystemName = KFileSystemName12;
347 iSectorsPerFat = MaxFat12Sectors();
351 iFileSystemName = KFileSystemName16;
352 iSectorsPerFat = MaxFat16Sectors();
360 void CFatFormatCB::RecordOldInfoL()
362 __PRINT(_L("CFatFormatCB::RecordOldInfoL"));
363 // Check if mount or disk is corrupt
364 // This should be stored in member variable because FatMount is remounted
365 // every time RFormat::Next() gets called thus FatMount().Initialised()
366 // will be inconsistent with previous state.
367 TLocalDriveCapsV3Buf caps;
368 User::LeaveIfError(LocalDrive()->Caps(caps));
369 iVariableSize=((caps().iMediaAtt)&KMediaAttVariableSize) ? (TBool)ETrue : (TBool)EFalse;
370 iDiskCorrupt = !FatMount().ConsistentState();
371 iBadClusters.Reset();
373 if (!iVariableSize && !iDiskCorrupt && (iMode & EQuickFormat))
375 iOldFirstFreeSector = FatMount().iFirstFreeByte >> FatMount().SectorSizeLog2();
376 iOldSectorsPerCluster = FatMount().SectorsPerCluster();
378 FatMount().FAT().InvalidateCacheL(); //-- invalidate whole FAT cache
380 const TInt maxClusterNum = FatMount().iUsableClusters + KFatFirstSearchCluster;
382 // Collect bad cluster information from current FAT table
383 const TUint32 mark = FatMount().Is16BitFat() ? KBad_16Bit : KBad_12Bit;
384 for (TInt i=KFatFirstSearchCluster; i<maxClusterNum; i++)
385 if (FatMount().FAT().ReadL(i) == mark)
386 iBadClusters.AppendL(i);
391 Create the boot sector on media for the volume.
393 @leave System wide error codes
395 void CFatFormatCB::CreateBootSectorL()
397 __PRINT1(_L("CFatFormatCB::CreateBootSector() drive:%d"),DriveNumber());
399 TFatBootSector bootSector;
401 bootSector.SetVendorID(KDefaultVendorID);
402 bootSector.SetBytesPerSector(iBytesPerSector);
403 bootSector.SetSectorsPerCluster(iSectorsPerCluster);
404 bootSector.SetReservedSectors(iReservedSectors);
405 bootSector.SetNumberOfFats(iNumberOfFats);
406 bootSector.SetRootDirEntries(iRootDirEntries);
407 if (iMaxDiskSectors<(TInt)KMaxTUint16)
408 bootSector.SetTotalSectors(iMaxDiskSectors);
411 bootSector.SetTotalSectors(0);
412 bootSector.SetHugeSectors(iMaxDiskSectors);
414 TInt numberOfClusters=iMaxDiskSectors/iSectorsPerCluster;
415 if (numberOfClusters>(TInt)KMaxTUint16)
416 User::Leave(KErrTooBig);
417 bootSector.SetFatSectors(iSectorsPerFat);
418 bootSector.SetReservedByte(0);
420 timeID.HomeTime(); // System time in future?
421 bootSector.SetUniqueID(I64LOW(timeID.Int64())); // Generate UniqueID from time
422 bootSector.SetVolumeLabel(_L8(""));
423 bootSector.SetFileSysType(iFileSystemName);
424 // Floppy specific info:
425 bootSector.SetJumpInstruction();
426 bootSector.SetMediaDescriptor(KBootSectorMediaDescriptor);
427 bootSector.SetNumberOfHeads(iNumberOfHeads);
428 bootSector.SetHiddenSectors(iHiddenSectors);
429 bootSector.SetSectorsPerTrack(iSectorsPerTrack);
430 bootSector.SetPhysicalDriveNumber(128);
431 bootSector.SetExtendedBootSignature(0x29);
434 User::LeaveIfError(FatMount().DoWriteBootSector(KBootSectorNum*bootSector.BytesPerSector(), bootSector));
437 //-------------------------------------------------------------------------------------------------------------------
440 Format a disk section, called iteratively to erase whole of media, on last iteration
441 creates an empty volume. If called with quick formatonly erases the Fat leaving the
442 rest of the volume intact.
444 @leave System wide error code
446 void CFatFormatCB::DoFormatStepL()
448 if (iFormatInfo.iFormatIsCurrent==EFalse)
450 if (iMode & EForceErase)
452 TInt r = FatMount().ErasePassword();
453 User::LeaveIfError(r);
454 // CFatMountCB::ErasePassword() calls TBusLocalDrive::ForceRemount(),
455 // so need to stop a remount from occurring in next call to :
456 // TFsFormatNext::DoRequestL((), TDrive::CheckMount().
457 FatMount().Drive().SetChanged(EFalse);
461 InitializeFormatDataL();
462 FatMount().DoDismount();
464 FatMount().ReduceSizeL(0,I64LOW(FatMount().iSize));
467 // Blank disk if not EQuickFormat
469 if (!iVariableSize && !(iMode & EQuickFormat) && iCurrentStep)
471 if (iFormatInfo.iFormatIsCurrent == EFalse)
472 {//-- firstly invalidate sectors 0-6 inclusive
473 DoZeroFillMediaL(0, 7*iBytesPerSector);
476 TInt ret=FatMount().LocalDrive()->Format(iFormatInfo);
477 if (ret!=KErrNone && ret!=KErrEof) // Handle format error
478 ret = HandleCorrupt(ret);
479 if (ret!=KErrNone && ret!=KErrEof) // KErrEof could be set by LocalDrive()->Format()
483 iCurrentStep=100-(100*iFormatInfo.i512ByteSectorsFormatted)/iMaxDiskSectors;
490 // ReMount since MBR may have been rewritten and partition may have moved / changed size
491 TInt ret = LocalDrive()->ForceRemount(0);
492 if (ret != KErrNone && ret != KErrNotSupported)
495 // MBR may have changed, so need to re-read iHiddenSectors etc.before BPB is written
496 InitializeFormatDataL();
498 // Translate bad sector number to cluster number which contains that sector
499 // This only happens in full format, in quick format they are already cluster numbers
500 if (!iVariableSize && !(iMode & EQuickFormat))
501 User::LeaveIfError(BadSectorToCluster());
504 // Do the rest of the disk in one lump
509 //-- zero-fill media from position 0 to the FAT end, i.e main & backup boot sector, FSInfo and its copy and all FATs
510 const TUint32 posFatEnd = ((iSectorsPerFat*iNumberOfFats) + iReservedSectors) * iBytesPerSector; //-- last FAT end position
513 FatMount().EnlargeL(posFatEnd);
515 DoZeroFillMediaL(0, posFatEnd);
517 //-- Zero fill root directory
518 const TInt rootDirSector = iReservedSectors + (iNumberOfFats * iSectorsPerFat);
519 const TInt rootDirSize = iRootDirEntries * KSizeOfFatDirEntry; //-- size in bytes
521 const TUint32 posRootDirStart = rootDirSector * iBytesPerSector;
522 const TUint32 posRootDirEnd = posRootDirStart + rootDirSize;
524 const TInt numOfRootSectors=(rootDirSize%iBytesPerSector) ? (rootDirSize/iBytesPerSector+1) : (rootDirSize/iBytesPerSector);
526 FatMount().EnlargeL(iBytesPerSector*numOfRootSectors);
528 DoZeroFillMediaL(posRootDirStart, posRootDirEnd);
530 // Enlarge ram drive to take into account rounding of
531 // data start to cluster boundary
532 if(iVariableSize && iSectorsPerCluster!=1)
534 const TInt firstFreeSector=rootDirSector+numOfRootSectors;
535 const TInt firstFreeCluster=firstFreeSector%iSectorsPerCluster ? firstFreeSector/iSectorsPerCluster+1 : firstFreeSector/iSectorsPerCluster;
536 const TInt alignedSector=firstFreeCluster*iSectorsPerCluster;
537 if(alignedSector!=firstFreeSector)
538 FatMount().EnlargeL((alignedSector-firstFreeSector)*iBytesPerSector);
541 //-- FAT[0] must contain media descriptor in the low byte, FAT[1] for fat16/32 may contain some flags
542 TBuf8<4> startFat(4);
545 if(iVariableSize||Is16BitFat()) //-- FAT16 or RAM drive which is always FAT16
547 startFat.SetLength(4);
551 startFat.SetLength(3);
554 startFat[0]=KBootSectorMediaDescriptor;
556 //-- write FAT[0] and FAT[1] entries to all copies of FAT
557 for(TInt i=0;i<iNumberOfFats;i++)
559 User::LeaveIfError(LocalDrive()->Write(iBytesPerSector*(iReservedSectors+(iSectorsPerFat*i)),startFat));
562 //-- create boot sectors
565 //-- here we have bad clusters numbers saved by the quick format
566 //-- Interpret old bad cluster number to new cluster number and mark new bad clusters
567 if (!iVariableSize && iBadClusters.Count()>0)
569 //-- Here we need fully mounted volume, so mount it normally.
570 FatMount().MountL(EFalse);
574 TInt mark = FatMount().Is16BitFat() ? KBad_16Bit : KBad_12Bit;
577 for (i=0; i<iBadClusters.Count(); ++i)
578 FatMount().FAT().WriteL(iBadClusters[i], mark);
580 FatMount().FAT().FlushL();
582 TInt r=FatMount().CheckDisk();
583 __PRINT1(_L("CFatFormatCB::DoFormatStepL() CheckDisk res: %d"),r);
588 //-- We do not need to perform full mount in this case, the TDrive object will be marked as changed in ~CFormatCB and the
589 //-- mount will be closed. Therefore on the first access to it it will be mounted normally.
590 FatMount().MountL(ETrue); //-- force mount
593 __PRINT1(_L("CFatFormatCB::DoFormatStepL() Format complete drv:%d"), DriveNumber());
596 TInt CFatFormatCB::BadSectorToCluster()
598 const TInt sizeofFatAndRootDir = iSectorsPerFat*iNumberOfFats + ((iRootDirEntries*KSizeOfFatDirEntry+(1<<iSectorSizeLog2)-1)>>iSectorSizeLog2);
599 TInt firstFreeSector = iReservedSectors + sizeofFatAndRootDir;
602 for (i=0; i<iBadSectors.Count(); ++i)
604 TInt badSector = iBadSectors[i];
605 // Check in rare case that corrupt in critical area
606 // which includes bootsector, FAT table, (and root dir if not FAT32)
607 if (badSector < firstFreeSector)
609 if (badSector == 0) // Boot sector corrupt
611 if (badSector < iReservedSectors) // Harmless in reserved area
613 // Extend reserved area to cover bad sector
614 iReservedSectors = badSector + 1;
615 firstFreeSector = iReservedSectors + sizeofFatAndRootDir;
619 // Figure out bad cluster number and record it
620 TInt cluster = (badSector-firstFreeSector)/iSectorsPerCluster + KFatFirstSearchCluster;
621 if (iBadClusters.Find(cluster) == KErrNotFound)
623 if ((r=iBadClusters.Append(cluster)) != KErrNone)