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_file.cpp
22 const TInt KSeekIndexSize=128; // Cache 128 clusters
23 const TInt KSeekIndexSizeLog2=7;
24 const TInt KFirstClusterNum=2;
26 CFatFileCB::CFatFileCB()
29 __PRINT1(_L("CFatFileCB created 0x%x"),this);
32 CFatFileCB::~CFatFileCB()
34 __PRINT1(_L("CFatFileCB deleted 0x%x"),this);
36 //-- a nasty trick to find out if the CFatFileCB is in consistent state on the moment of destruction.
37 //-- Because of OOM conditions CFatFileCB might not be fully constructed and to be deleted, while FlushAll()
38 //-- implies valid iMount.
39 const CMountCB* pMount = &Mount();
41 {//-- do some finalisation work if CMountCB is valid
42 if (iAtt&KEntryAttModified)
43 TRAP_IGNORE(FlushAllL());
50 void CFatFileCB::CreateSeekIndex()
52 // Create a seek index
56 iSeekIndex = new TUint32[KSeekIndexSize];
57 if (iSeekIndex == NULL)
60 Mem::FillZ(iSeekIndex, sizeof(TUint32) * KSeekIndexSize);
62 iSeekIndexSize=CalcSeekIndexSize(Size());
65 TInt CFatFileCB::SeekToPosition(TInt aNewRelCluster,TInt aClusterOffset)
67 // Use the seek index to set iCurrentPos.iCluster as close as possible to aNewRelCluster
68 // Return aNewRelCluster-aCurrentPos.iCluster
71 TInt clusterOffset=aClusterOffset;
72 TInt seekPos=(aNewRelCluster>>iSeekIndexSize)-1;
73 __ASSERT_DEBUG(seekPos<KSeekIndexSize,Fault(EFatFileSeekIndexTooSmall));
75 while(seekPos>=0 && iSeekIndex[seekPos]==0 && clusterOffset!=0)
80 if (clusterOffset==0) // Counted back to the current cluster
81 return(aClusterOffset);
84 iCurrentPos.iCluster=iStartCluster;
85 return(aNewRelCluster);
88 iCurrentPos.iCluster=iSeekIndex[seekPos];
89 return(aNewRelCluster-((seekPos+1)<<iSeekIndexSize));
92 void CFatFileCB::SetSeekIndexValueL(TInt aRelCluster,TInt aStoredCluster)
94 // Sets a value in the seekindex
98 TInt seekPos=(aRelCluster>>iSeekIndexSize)-1;
99 __ASSERT_DEBUG(seekPos<KSeekIndexSize,Fault(EFatFileSeekIndexTooSmall));
100 __ASSERT_DEBUG(seekPos>=0,Fault(EFatFileSeekIndexTooSmall2));
101 iSeekIndex[seekPos] = aStoredCluster;
104 TBool CFatFileCB::IsSeekBackwards(TUint aPos)
106 // Return true if aPos<currentPos
110 TUint cluster=iCurrentPos.iCluster<<ClusterSizeLog2();
111 TInt offset=ClusterRelativePos(iCurrentPos.iPos);
112 TUint currentPos=cluster+offset;
113 return(aPos<currentPos);
116 void CFatFileCB::CheckPosL(TUint aPos)
118 // Check that the file is positioned correctly.
119 // If aPos<currentPos attempt to guess the new position.
122 __PRINT1(_L("CFatFileCB::CheckPosL(%d)"), aPos);
123 if (aPos==iCurrentPos.iPos)
125 __ASSERT_DEBUG(aPos <= (TUint)Size(), Fault(EFatFilePosBeyondEnd));
127 if (iFileSizeModified && IsSeekBackwards(aPos))
130 TUint newRelCluster=aPos>>ClusterSizeLog2();
131 if ( aPos && (aPos==(newRelCluster<<ClusterSizeLog2())) )
133 TUint oldRelCluster=iCurrentPos.iPos>>ClusterSizeLog2();
134 if ( iCurrentPos.iPos && (iCurrentPos.iPos==(oldRelCluster<<ClusterSizeLog2())) )
136 TInt clusterOffset=newRelCluster-oldRelCluster;
137 TInt oldCluster=iCurrentPos.iCluster;
138 iCurrentPos.iPos=aPos;
139 if (clusterOffset==0)
141 TInt seekOffset=clusterOffset;
142 if (iSeekIndex!=NULL)
143 { // Can alter iCurrentPos.iCluster
144 seekOffset=SeekToPosition(newRelCluster,seekOffset);
148 if (clusterOffset==-1 && seekOffset!=1)
149 { // Check previous cluster
150 TInt cluster=oldCluster-1;
151 if (FAT().GetNextClusterL(cluster) && cluster==oldCluster)
153 iCurrentPos.iCluster=oldCluster-1;
159 seekOffset=newRelCluster;
160 iCurrentPos.iCluster=iStartCluster;
164 if (!FAT().GetNextClusterL(iCurrentPos.iCluster))
166 __PRINT(_L("CFatFileCB::CheckPosL() corrupt#1"));
167 User::Leave(KErrCorrupt);
169 TInt cluster=newRelCluster-seekOffset;
170 if (iSeekIndex!=NULL && cluster && (cluster>>iSeekIndexSize)<<iSeekIndexSize==cluster)
171 SetSeekIndexValueL(cluster,iCurrentPos.iCluster);
175 void CFatFileCB::SetL(const TFatDirEntry& aFatDirEntry,TShare aShare,const TEntryPos& aPos)
177 // Initialize FileCB from entry data
181 __PRINT(_L("CFatFileCB::SetL"));
182 SetSize(aFatDirEntry.Size());
183 iCurrentPos.iCluster= FatMount().StartCluster(aFatDirEntry);
184 iStartCluster=iCurrentPos.iCluster;
186 iAtt=aFatDirEntry.Attributes();
187 iModified= aFatDirEntry.Time(FatMount().TimeOffset());
191 SetMaxSupportedSize(KMaxSupportedFatFileSize);
194 //-----------------------------------------------------------------------------
195 // from CFileCB::MExtendedFileInterface
196 void CFatFileCB::ReadL(TInt64 aPos,TInt& aLength, TDes8* aDes, const RMessagePtr2& aMessage, TInt aOffset)
198 __PRINT2(_L("CFatFileCB::ReadL aFilePos=%LU aLength=%d"),aPos,aLength);
200 if((TUint64)aPos > KMaxSupportedFatFileSize-1)
201 User::Leave(KErrNotSupported); //-- max. position in the file is 0xFFFFFFFE
203 FatMount().CheckStateConsistentL();
205 CheckPosL(I64LOW(aPos));
207 const TUint startPos = iCurrentPos.iPos;
208 const TUint curSize = (TUint)Size();
209 const TUint length = (TUint)aLength;
211 if((startPos + length > curSize) || (startPos > startPos + length) )
212 aLength=curSize-startPos;
214 FatMount().ReadFromClusterListL(iCurrentPos,aLength,aDes,aMessage,aOffset);
215 aLength=iCurrentPos.iPos-startPos;
219 void CFatFileCB::ReadL(TInt aFilePos,TInt& aLength,const TAny* aTrg,const RMessagePtr2& aMessage)
221 ReadL(TInt64(aFilePos),aLength,(TDes8*) aTrg,aMessage, 0);
224 //-----------------------------------------------------------------------------
225 // from CFileCB::MExtendedFileInterface
226 void CFatFileCB::WriteL(TInt64 aPos,TInt& aLength,const TDesC8* aSrc,const RMessagePtr2& aMessage, TInt aOffset)
228 __PRINT2(_L("CFatFileCB::WriteL aFilePos=%LU aLength=%d"),aPos,aLength);
229 // FAT supports 32 bits only for file size
230 TUint64 endPos = aPos + aLength;
231 if(endPos > KMaxSupportedFatFileSize)
232 User::Leave(KErrNotSupported);
234 FatMount().CheckStateConsistentL();
235 FatMount().CheckWritableL();
236 const TUint pos = I64LOW(aPos);
239 const TUint startCluster = (TUint)iStartCluster;
240 const TUint length = (TUint)aLength;
242 endPos = iCurrentPos.iPos + length;
243 if ((endPos > (TUint)Size()) ||
244 (iCurrentPos.iPos > endPos) ) // Overflow condition
245 DoSetSizeL(iCurrentPos.iPos+length,EFalse);
247 TUint startPos=iCurrentPos.iPos;
251 TRAPD(ret, FatMount().WriteToClusterListL(iCurrentPos,aLength,aSrc,aMessage,aOffset,badcluster, goodcluster));
253 if (ret == KErrCorrupt || ret == KErrDied)
255 if(startCluster == 0)
256 { //Empty File, revert all the clusters allocated.
257 TInt cluster = iStartCluster;
262 iCurrentPos.iCluster = 0;
263 iCurrentPos.iPos = 0;
265 FAT().FreeClusterListL(cluster);
269 { //Calculate the clusters required based on file size, revert extra clusters if allocated.
270 const TUint curSize = (TUint)Size();
271 TUint ClustersNeeded = curSize >> ClusterSizeLog2();
272 if(curSize > (ClustersNeeded << ClusterSizeLog2()))
277 TInt cluster = iStartCluster;
278 while(--ClustersNeeded)
280 FAT().GetNextClusterL(cluster);
283 iCurrentPos.iCluster = cluster;
285 if (FAT().GetNextClusterL(cluster))
287 FAT().FreeClusterListL(cluster);
290 FAT().WriteFatEntryEofL(iCurrentPos.iCluster);
295 User::LeaveIfError(ret);
299 if(iStartCluster == badcluster)
301 iStartCluster = goodcluster;
302 FlushStartClusterL();
306 TInt aCluster = iStartCluster;
309 if((TUint)badcluster == FAT().ReadL(aCluster))
311 FAT().WriteL(aCluster, goodcluster);
316 while(FAT().GetNextClusterL(aCluster));
319 aLength=iCurrentPos.iPos-startPos;
321 if(FatMount().IsRuggedFSys() && pos+(TUint)aLength>(TUint)Size())
323 WriteFileSizeL(pos+aLength);
329 void CFatFileCB::WriteL(TInt aFilePos,TInt& aLength,const TAny* aSrc,const RMessagePtr2& aMessage)
331 WriteL(TInt64(aFilePos),aLength,(TDesC8*) aSrc,aMessage, 0);
336 //-----------------------------------------------------------------------------
338 void CFatFileCB::ResizeIndex(TInt aNewMult,TUint aNewSize)
340 // Resize the seek index to accomodate a larger or smaller filesize
341 // Assumes KSeekIndexSize is a power of 2.
345 TInt maxNewIndex=aNewSize>>(ClusterSizeLog2()+aNewMult);
349 TInt indexEnd=KSeekIndexSize;
350 TInt newValEnd=maxNewIndex;
352 if (iSeekIndexSize<aNewMult)
355 TInt step=1<<(aNewMult-iSeekIndexSize);
357 while(index<indexEnd && newVal<newValEnd)
359 iSeekIndex[newVal] = iSeekIndex[index];
363 while(newVal<indexEnd)
364 iSeekIndex[newVal++] = 0;
368 TInt diffSize = iSeekIndexSize-aNewMult;
369 TInt oldVal=(KSeekIndexSize>>diffSize) - 1;
370 TInt newVal=indexEnd-1;
371 TInt skip=(1<<diffSize)-1;
373 if ((iSeekIndexSize - aNewMult) > KSeekIndexSizeLog2)
375 ClearIndex(0); //-- Invalidate every entry.
382 iSeekIndex[newVal--] = iSeekIndex[oldVal--];
385 for(TInt i=skip;i>0;i--)
387 iSeekIndex[newVal--] = 0;
393 iSeekIndexSize=aNewMult;
398 Zero freed clusters in the index
400 @param aNewSize new size of the file that the index corresponds to.
401 if = 0 all existing index will be zero filled
403 void CFatFileCB::ClearIndex(TUint aNewSize)
411 //-- zero fill all the array
412 Mem::FillZ(iSeekIndex, KSeekIndexSize*sizeof(TUint32));
416 // Files that fill up a cluster exactly do not have a trailing empty
417 // cluster. So the entry for that position must also be invalidated
419 TInt firstInvalidIndex=aNewSize>>(iSeekIndexSize+ClusterSizeLog2());
421 TInt indexLen=KSeekIndexSize-firstInvalidIndex;
423 Mem::FillZ(iSeekIndex+firstInvalidIndex, indexLen * sizeof(TUint32));
426 TInt CFatFileCB::CalcSeekIndexSize(TUint aSize)
428 // Find the nearest power of 2 > aSize
432 const TUint indexSize=KSeekIndexSize<<ClusterSizeLog2();//KSeekIndexSize=128
433 if (aSize<=indexSize)
440 return (count - (KSeekIndexSizeLog2 + ClusterSizeLog2()) + 1);
443 //-----------------------------------------------------------------------------
445 void CFatFileCB::SetSizeL(TInt64 aSize)
447 __PRINT(_L("CFatFileCB::SetSizeL"));
449 // FAT supports 32 bits only for file size
451 User::Leave(KErrNotSupported);
453 if(FatMount().IsRuggedFSys())
454 DoSetSizeL(I64LOW(aSize),ETrue);
456 DoSetSizeL(I64LOW(aSize),EFalse);
460 void CFatFileCB::SetSizeL(TInt aSize)
462 // Envelope function around DoSetSizeL to enable aSize to
463 // be written to disk for rugged fat file system
466 SetSizeL(TInt64(aSize));
469 void CFatFileCB::DoSetSizeL(TUint aSize,TBool aIsSizeWrite)
471 // Extend or truncate the file.
472 // Expects the modified attribute and iSize are set afterwards.
473 // Does not alter iCurrentPos, the current file position.
474 // Writes size of file to disk if aIsSizeWrite set
477 __PRINT2(_L("CFatFileCB::DoSetSizeL sz:%d, fileWrite=%d"),aSize ,aIsSizeWrite);
479 FatMount().CheckStateConsistentL();
480 FatMount().CheckWritableL();
483 // Can not change the file size if it is clamped
484 if(Mount().IsFileClamped(MAKE_TINT64(0,iStartCluster)) > 0)
485 User::Leave(KErrInUse);
487 iFileSizeModified=ETrue;
489 TInt newIndexMult=CalcSeekIndexSize(aSize);
490 if (iSeekIndex!=NULL && newIndexMult!=iSeekIndexSize)
491 ResizeIndex(newIndexMult,aSize);
496 ClearIndex(0); //-- clear seek index array
497 TInt cluster=iStartCluster;
502 FAT().FreeClusterListL(cluster);
507 if (aSize<(TUint)Size())
509 if(aIsSizeWrite) // write file size if decreasing
510 WriteFileSizeL(aSize);
512 TInt cluster=iCurrentPos.iCluster;
513 if (FAT().GetNextClusterL(cluster))
515 FAT().WriteFatEntryEofL(iCurrentPos.iCluster);
516 FAT().FreeClusterListL(cluster);
523 TUint newSize=aSize>>ClusterSizeLog2(); // Number of clusters we now need
524 if (aSize > (newSize<<ClusterSizeLog2()))
525 newSize++; // File size is not an exact multiple of cluster size
526 // Increment the number of clusters required to accomodate tail
528 if (iStartCluster==0)
530 //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
531 ClearIndex(0); //-- clear seek index array
532 TInt tempStartCluster=FAT().AllocateClusterListL(newSize, FAT().FreeClusterHint());
534 iCurrentPos.iCluster=tempStartCluster;
535 iStartCluster=tempStartCluster;
541 const TUint curSize = (TUint)Size();
542 TUint oldSize=curSize>>ClusterSizeLog2(); // Number of clusters we had previously
543 if (curSize>(oldSize<<ClusterSizeLog2()))
546 TInt newClusters=newSize-oldSize; // Number of clusters we need to prepare
549 TEntryPos currentPos=iCurrentPos;
551 FAT().ExtendClusterListL(newClusters,iCurrentPos.iCluster);
552 iCurrentPos=currentPos;
555 if(aIsSizeWrite) // write file size if increasing
556 WriteFileSizeL(aSize);
560 //-----------------------------------------------------------------------------
562 Set the entry's attributes and modified time.
564 void CFatFileCB::SetEntryL(const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
566 __PRINT(_L("CFatFileCB::SetEntryL"));
568 FatMount().CheckStateConsistentL();
569 FatMount().CheckWritableL();
571 TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
572 if (setAttMask|aClearAttMask)
575 iAtt&=(~aClearAttMask);
577 if (aSetAttMask&KEntryAttModified)
579 iAtt|=KEntryAttModified;
583 This is a RuggedFAT - specific method. Writes file size to the corresponding field of this
584 file direcrory entry.
586 void CFatFileCB::WriteFileSizeL(TUint aSize)
588 __PRINT(_L("CFatFileCB::WriteFileSizeL"));
589 TEntryPos entryPos=iFileDirPos;
590 entryPos.iPos+=_FOFF(SFatDirEntry,iSize);
591 TPtrC8 size((TUint8*)&aSize,sizeof(TUint));
593 //-- use directory cache when dealing with directories
594 FatMount().DirWriteL(entryPos,size);
595 iFileSizeModified=EFalse;
598 //-----------------------------------------------------------------------------
600 Flush file size, attributes, time etc. to the media.
601 It doesn't matter if whole directory entry is being written of only part of it. Anyway, a single DOS
602 dir. entry always fits into 1 sector.
604 void CFatFileCB::FlushDataL()
606 __PRINT(_L("CFatFileCB::FlushDataL"));
610 //-----------------------------------------------------------------------------
612 Flush the fide directory entry data: files size, attributes, time etc.
614 void CFatFileCB::FlushAllL()
616 __PRINT(_L("CFatFileCB::FlushAllL()"));
618 if (Mount().IsCurrentMount()==EFalse)
619 User::Leave(KErrDisMounted);
621 FatMount().CheckStateConsistentL();
622 FatMount().CheckWritableL();
625 FatMount().ReadDirEntryL(iFileDirPos,entry);
626 __ASSERT_ALWAYS(entry.IsEndOfDirectory()==EFalse,User::Leave(KErrCorrupt));
627 entry.SetAttributes(iAtt&KEntryAttMaskSupported);
628 entry.SetSize(Size());
629 entry.SetTime(iModified, FatMount().TimeOffset());
630 entry.SetStartCluster(iStartCluster);
632 TBool setNotify = FatMount().GetNotifyUser();
635 FatMount().SetNotifyOff(); // do not launch a notifier
638 TRAPD(ret, FatMount().WriteDirEntryL(iFileDirPos,entry));
642 FatMount().SetNotifyOn();
645 User::LeaveIfError(ret);
646 iAtt&=(~KEntryAttModified);
647 iFileSizeModified=EFalse;
650 //-----------------------------------------------------------------------------
653 Rename already opened file.
654 @param aNewName new file name; all trailing dots from the name will be removed
656 void CFatFileCB::RenameL(const TDesC& aNewName)
658 __PRINT2(_L("CFatFileCB::RenameL[0x%x], name:%S"),this, &aNewName);
660 FatMount().CheckStateConsistentL();
661 FatMount().CheckWritableL();
663 const TPtrC fileName = RemoveTrailingDots(aNewName); //-- remove trailing dots from the name
666 FatMount().DoRenameOrReplaceL(*iFileName, fileName, CFatMountCB::EModeRename,iFileDirPos);
668 AllocBufferL(iFileName, fileName);
670 if(!FatMount().IsRuggedFSys())
675 //***********************************************************
676 //* BlockMap interface
677 //***********************************************************
679 TInt CFatFileCB::BlockMap(SBlockMapInfo& aInfo, TInt64& aStartPos, TInt64 aEndPos)
681 // Retrieves the block map of a given section of the file, in the FAT file system.
684 __PRINT2(_L("CFatFileCB::BlockMap aStartPos=%ld aEndPos=%ld"), aStartPos, aEndPos);
686 if ( I64HIGH(aStartPos) || I64HIGH(aEndPos) )
687 return KErrNotSupported;
689 TUint startPos = I64LOW(aStartPos);
690 TUint endPos = I64LOW(aEndPos);
692 // aEndPos will always be >=0 at this point
693 const TUint length = endPos - startPos;
695 // Store the position of cluster zero in aInfo
696 CFatMountCB& fatMount = FatMount();
699 TBusLocalDrive* locDrv;
700 if((fatMount.LocalDrive()->GetLocalDrive(locDrv)==KErrNone) && ((drvNo=GetLocalDriveNumber(locDrv))>=0) && (drvNo<KMaxLocalDrives))
701 aInfo.iLocalDriveNumber=drvNo;
703 return KErrNotSupported;
705 // Fetch the address of cluster 0
706 aInfo.iStartBlockAddress = fatMount.FAT().DataPositionInBytes(KFirstClusterNum);
708 TRAPD(r, CheckPosL(startPos));
712 aInfo.iBlockStartOffset = fatMount.ClusterRelativePos(iCurrentPos.iPos);
713 aInfo.iBlockGranularity = 1 << FatMount().ClusterSizeLog2();
714 const TUint myStartPos = iCurrentPos.iPos;
715 if ( myStartPos + length > (TUint)Size())
718 TRAP(r, FatMount().BlockMapReadFromClusterListL(iCurrentPos, length, aInfo));
722 aStartPos = iCurrentPos.iPos;
723 if ((I64LOW(aStartPos) == (TUint)Size()) || ( I64LOW(aStartPos) == (myStartPos + length)))
724 return KErrCompletion;
731 TInt CFatFileCB::GetInterface(TInt aInterfaceId,TAny*& aInterface,TAny* aInput)
735 case EExtendedFileInterface:
736 ((CFileCB::MExtendedFileInterface*&) aInterface) = this;
739 case EBlockMapInterface:
740 aInterface = (CFileCB::MBlockMapInterface*) this;
744 return FatMount().LocalDrive()->GetLocalDrive((TBusLocalDrive*&) aInterface);
747 return CFileCB::GetInterface(aInterfaceId,aInterface,aInput);
755 Overwrites file's start cluster (iStartCluster) in its directory entry.
757 void CFatFileCB::FlushStartClusterL()
759 __PRINT(_L("CFatFileCB::FlushStartClusterL"));
761 CFatMountCB& mount = FatMount();
762 TFatDirEntry dirEntry;
764 mount.ReadDirEntryL(iFileDirPos, dirEntry); //-- read this file's dir. entry
765 dirEntry.SetStartCluster(iStartCluster); //-- set new start cluster
766 mount.WriteDirEntryL(iFileDirPos, dirEntry);//-- write the entry back