diff -r 2d65c2f76d7b -r 4a8fed1c0ef6 userlibandfileserver/fileserver/sfat32/sl_vfat.cpp --- a/userlibandfileserver/fileserver/sfat32/sl_vfat.cpp Tue Feb 02 01:24:03 2010 +0200 +++ b/userlibandfileserver/fileserver/sfat32/sl_vfat.cpp Sat Feb 20 00:10:51 2010 +0200 @@ -29,11 +29,14 @@ // use second half of ISO Latin 1 character set for extended chars const TUint KExtendedCharStart=0x80; const TUint KExtendedCharEnd=0xff; +const TUint KMaxVFatEntries = 21; ///< Max possible number of entries in the VFAT entryset -LOCAL_C TBool IsLegalChar(TChar aCharacter,TBool aAllowWildChars,TBool aUseExtendedChars=EFalse,TBool aInScanDrive=EFalse) -// -// Returns ETrue if aCharacter is legal inside a dos filename -// + +//----------------------------------------------------------------------------- +/** + Returns ETrue if aCharacter is legal inside a dos filename +*/ +static TBool IsLegalChar(TChar aCharacter,TBool aAllowWildChars,TBool aUseExtendedChars=EFalse,TBool aInScanDrive=EFalse) { if ((aCharacter==KMatchOne) || (aCharacter==KMatchAny)) return(aAllowWildChars); @@ -46,7 +49,8 @@ return LocaleUtils::IsLegalShortNameCharacter(aCharacter,aUseExtendedChars); } -LOCAL_C void ReplaceFirstCharacterIfClashesWithE5L(TDes8& aShortName) +//----------------------------------------------------------------------------- +static void ReplaceFirstCharacterIfClashesWithE5L(TDes8& aShortName) { if (0 < aShortName.Length() && aShortName[0] == KEntryErasedMarker) { @@ -54,7 +58,8 @@ } } -LOCAL_C void ReplaceIllegalCharactersL(TDes& aLongName, TUint aCharacterToReplaceWith) +//----------------------------------------------------------------------------- +static void ReplaceIllegalCharactersL(TDes& aLongName, TUint aCharacterToReplaceWith) { TBool alreadyFoundExtensionDelimiter=EFalse; @@ -130,10 +135,11 @@ } } +//----------------------------------------------------------------------------- +/** + Create a legal shortname from aLongName +*/ TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively) -// -// Create a legal shortname from aLongName -// { TFileName longName(aLongName); @@ -259,7 +265,7 @@ return shortName; } - +//----------------------------------------------------------------------------- /** Check whether a Dos name is legal or not. @@ -271,7 +277,6 @@ @return ETrue if the name is a legal DOS one. */ - static TBool DoCheckLegalDosName(const TDesC& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation) { const TInt count=aName.Length(); @@ -388,6 +393,7 @@ return ETrue; } +//----------------------------------------------------------------------------- /** Check whether a Dos name is legal or not. Unicode version parameters and return value absolutely the same as in DoCheckLegalDosName() @@ -395,18 +401,19 @@ TBool IsLegalDosName(const TDesC16& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation) { - __PRINT(_L("IsLegalDosName 16")); + //__PRINT(_L("IsLegalDosName 16")); return DoCheckLegalDosName(aName, anAllowWildCards, aUseExtendedChars, aInScanDrive, aAllowLowerCase, aIsForFileCreation); } +//----------------------------------------------------------------------------- +/** + Returns ETrue and the entryPos of aName if found or EFalse +*/ TBool CFatMountCB::FindShortNameL(const TShortName& aName,TEntryPos& anEntryPos) -// -// Returns ETrue and the entryPos of aName if found or EFalse -// { - __PRINT(_L("VFAT::CFatMountCB::FindShortNameL")); + __PRINT(_L("CFatMountCB::FindShortNameL")); TFatDirEntry fatEntry; TInt count=0; FOREVER @@ -428,27 +435,29 @@ return EFalse; } +//----------------------------------------------------------------------------- +/** + Returns ETrue if aName is unique, EFalse if a matching name is found. +*/ TBool CFatMountCB::IsUniqueNameL(const TShortName& aName,TInt aDirCluster) -// -// Returns ETrue if aName is unique, EFalse if a matching name is found. -// { - __PRINT(_L("VFAT::CFatMountCB::IsUniqueNameL")); + __PRINT(_L("CFatMountCB::IsUniqueNameL")); TEntryPos entryPos(aDirCluster,0); if (FindShortNameL(aName,entryPos)) return(EFalse); return(ETrue); } +//----------------------------------------------------------------------------- +/** + A legal dos name has been typed that clashes with a computer generated shortname + Change the shortname to something else. +*/ void CFatMountCB::ReplaceClashingNameL(const TShortName& aNewName,const TEntryPos& anEntryPos) -// -// A legal dos name has been typed that clashes with a computer generated shortname -// Change the shortname to something else. -// { - __PRINT(_L("VFAT::CFatMountCB::ReplaceClashingNameL")); + __PRINT(_L("CFatMountCB::ReplaceClashingNameL")); TFatDirEntry entry; ReadDirEntryL(anEntryPos,entry); __ASSERT_ALWAYS(entry.IsEndOfDirectory()==EFalse,User::Leave(KErrCorrupt)); @@ -469,14 +478,22 @@ } } +//----------------------------------------------------------------------------- +/** + Generate a legal dos filename as an alias for aName. + @return ETrue if aName is a legal dos name. +*/ TBool CFatMountCB::GenerateShortNameL(TInt aDirCluster,const TDesC& aName,TShortName& aGeneratedName, TBool aForceRandomize) -// -// Generate a legal dos filename as an alias for aName. -// Returns ETrue if aName is a legal dos name. -// { - __PRINT(_L("VFAT::CFatMountCB::GenerateShortNameL")); + __PRINT1(_L("CFatMountCB::GenerateShortNameL() cl:%d"), aDirCluster); + + if(!ClusterNumberValid(aDirCluster)) + { + ASSERT(0); + User::Leave(KErrCorrupt); + } + // Given the long file-name "ABCDEFGHI.TXT", EPOC used to generate short // file-names in the following pecking order: // "ABCDEFGH.TXT", @@ -546,142 +563,307 @@ } -void TFatDirEntry::InitializeAsVFat(TUint8 aCheckSum) -// -// Initialize a FAT entry as a VFAT filename -// + +//----------------------------------------------------------------------------- +/** + Write up to KMaxVFatEntryName unicode chars from aName to the entry + @param aName long file name part that will be converted into the VFAT entryset + @param aLen length of the remaining name + @param aCheckSum DOS entry name checksum. +*/ +void TFatDirEntry::SetVFatEntry(const TDesC& aName, TUint aLen, TUint8 aCheckSum) { - - Mem::Fill(this,sizeof(SFatDirEntry),0xFF); + //-- initialise some VFAT entry specific fields iData[0x0B]=0x0F; iData[0x0C]=0x00; iData[0x0D]=aCheckSum; iData[0x1A]=0x00; iData[0x1B]=0x00; - } - -void TFatDirEntry::SetVFatEntry(const TDesC& aName,TInt aLen) -// -// Write up to KMaxVFatEntryName unicode chars from aName to the entry -// - { TInt rem=aName.Length()-aLen; TPtrC section(aName.Ptr()+aLen,Min(rem,KMaxVFatEntryName)); TBuf16 buf16; buf16.Copy(section); + if (remdefault size - TInt dosOffset=numEntries<>SectorSizeLog2())==0 && ((TUint)(1<= ((numEntries+1)<SetVFatEntry(aLongName, KMaxVFatEntryName*(numEntries-1), cksum); //KMaxVFatEntryName=13 + + posInBuf += KSizeOfFatDirEntry; + MoveToNextEntryL(aPos); + + numEntries--; + movedCluster = (startPos.Cluster() != aPos.Cluster()); //-- if moved to another cluser, need to flush buffer + + if(!numEntries || movedCluster) + break; //-- VFAT entryset is completed + } + + if(movedCluster) + { + DirWriteL(startPos, TPtrC8(&scratchBuf[0], posInBuf)); + continue; + } + + if(!numEntries) + {//-- need to append DOS entry + Mem::Copy(&scratchBuf[posInBuf], &aDosEntry, KSizeOfFatDirEntry); + posInBuf+= KSizeOfFatDirEntry; + DirWriteL(startPos, TPtrC8(&scratchBuf[0], posInBuf)); + break; + } + + }//for(;;) + } + + + +//--------------------------------------------------------------------------------- + +void CFatMountCB::DoEraseEntrySetChunkL(const TEntrySetChunkInfo& aEntrySetChunk) + { + + //-- scratch buffer for whole VFAT entryset. Max number of entries in it is 21 entry or 672 bytes. + //-- in the worst case the entryset can span across 3 clusters (512 bytes per cluster) + //-- Using the scratch buffer is not ideal, but write-back directory cache isn't in place yet + + const TUint KBufSize = 680; + TBuf8 scratchBuf; + + TUint numEntries = aEntrySetChunk.iNumEntries; + + ASSERT(numEntries >0 && numEntries <= KMaxVFatEntries); + const TUint32 KChunkLen = numEntries << KSizeOfFatDirEntryLog2; + + DirReadL(aEntrySetChunk.iEntryPos, KChunkLen, scratchBuf); + + TInt posInBuf = 0; while (numEntries--) { - vFatEntry.SetVFatEntry(aLongName,KMaxVFatEntryName*numEntries);// KMaxVFatEntryName=13 - if(isSameSector) - { - Mem::Copy(&localBuf[offset],&vFatEntry,KSizeOfFatDirEntry); - offset+=KSizeOfFatDirEntry; - MoveToNextEntryL(aPos); - } - else - { - WriteDirEntryL(aPos,vFatEntry); - MoveToNextEntryL(aPos); - } - } - if(isSameSector) - { - Mem::Copy(&localBuf[offset],&aFatDirEntry,KSizeOfFatDirEntry); - - //-- use special interface to access FAT directory file - DirWriteL(startPos,TPtrC8(&localBuf[0],dosOffset+KSizeOfFatDirEntry)); + TFatDirEntry* pEntry = (TFatDirEntry*)(scratchBuf.Ptr()+posInBuf); + pEntry->SetErased(); + posInBuf += KSizeOfFatDirEntry; } - else - WriteDirEntryL(aPos,aFatDirEntry); - } + + DirWriteL(aEntrySetChunk.iEntryPos, scratchBuf); + } +//--------------------------------------------------------------------------------- +/** + Erase whole VFAT entryset. + For Rugged FAT the situation is more complicated: we need firstly delete the DOS entry _atomically_ i.e. if this operation fails, + the whole VFAT entryset won't be broken. Deleting VFAT entries doesn't require the atomic media writes; DOS entry contains necessary + information about data stream. + + @param aPos position of the entryset start in the directory. + @param aFirstEntry first entry in the entryset, it can be DOS entry + +*/ void CFatMountCB::EraseDirEntryL(TEntryPos aPos,const TFatDirEntry& aFirstEntry) -// -// Mark all entries in a VFat directory entry as erased -// { - __PRINT(_L("VFAT::CFatMountCB::EraseDirEntryL")); - TInt numEntries=0; + __PRINT2(_L("CFatMountCB::EraseDirEntryL() cl:%d, offset:%d"), aPos.Cluster(), aPos.Pos()); + + TUint numEntries=0; if (aFirstEntry.IsVFatEntry()) + { numEntries=aFirstEntry.NumFollowing(); - if(IsRuggedFSys()&&numEntries) - { - TInt count=numEntries; - TEntryPos pos=aPos; - while(count--) - MoveToNextEntryL(pos); - EraseDirEntryL(pos); - numEntries--; + numEntries++; //-- take into account the last DOS entry } - FOREVER - { + else + {//-- we are deleting a single DOS entry. This is an atomic operation. EraseDirEntryL(aPos); - if (!numEntries--) - break; - MoveToNextEntryL(aPos); - } - } + return; + } + + ASSERT(numEntries > 1 && numEntries <= KMaxVFatEntries); + + TEntrySetChunkInfo chunksInfo[TEntrySetChunkInfo::KMaxChunks]; + + //-- 1. check if the entryset fits into a unit of write ganularity. This will be 1 sector for rugged FAT or 1 cluster otherwise + + TUint32 MaxWriteGranularityLog2; + + if(IsRuggedFSys()) + { + MaxWriteGranularityLog2 = AtomicWriteGranularityLog2(); + } + else if(IsRootDir(aPos)) + {//-- root dir. for FAT12/16 is a special case, it is not made of clusters. it's unit is 1 sector. + MaxWriteGranularityLog2 = KDefSectorSzLog2; + } + else + {//-- minimal unit size will be a cluster + MaxWriteGranularityLog2 = ClusterSizeLog2(); + } + + + { + const TUint64 KEntrySetStartPos = MakeLinAddrL(aPos); + const TUint64 KEntrySetLogicalEndPos = KEntrySetStartPos + (numEntries << KSizeOfFatDirEntryLog2); + + const TUint64 KBlockEndPos = ((KEntrySetLogicalEndPos-1) >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; + const TUint64 KBlockStartPos = (KEntrySetStartPos >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; + + if(KBlockEndPos == KBlockStartPos) + {//-- whole entryet is in the same block; the whole entryset erase operation will be atomic for Rugged/non-rugged FAT + chunksInfo[0].iEntryPos = aPos; + chunksInfo[0].iNumEntries = numEntries; + DoEraseEntrySetChunkL(chunksInfo[0]); + return; + } + + } + + //-- the entryset is split on max. 3 parts between units of write granularity (see MaxWriteGranularityLog2). + ASSERT(numEntries > 1 && numEntries <= KMaxVFatEntries); + + TInt cntChunk = 1; //-- there is at least 1 entries chunk + TEntrySetChunkInfo* pChunkInfo = chunksInfo; + + //-- collect information about dir. entry chunks that reside in different units of write granularity + for(;;) + { + TBool movedUnit = EFalse; + pChunkInfo->iEntryPos = aPos; + pChunkInfo->iNumEntries = 0; + + const TUint64 KChunkStartPos = MakeLinAddrL(aPos); + const TUint64 KChunkBlockStartPos = (KChunkStartPos >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; + const TUint64 KChunkBlockEndPos = (KChunkBlockStartPos-1) + (1<iNumEntries++; + MoveToNextEntryL(aPos); + + numEntries--; + const TUint64 currPos = MakeLinAddrL(aPos); + movedUnit = !(currPos >= KChunkBlockStartPos && currPos <= KChunkBlockEndPos); + + if(!numEntries || movedUnit) + { + break; + } + + } + + if(movedUnit && numEntries) + {//-- move to the next unit of write granularity + ++pChunkInfo; + ++cntChunk; + ASSERT(cntChunk <= TEntrySetChunkInfo::KMaxChunks); + continue; + } + + + ASSERT(!numEntries); + break; + } + + //-- now do bulk deletion, write data based on collected entries chunks. + ASSERT(cntChunk > 0); + + //-- if it is a rugged FAT, we need to delete DOS entry first; it will be in the last chunk. + if(IsRuggedFSys()) + { + const TInt dosEntryChunk = cntChunk-1; + DoEraseEntrySetChunkL(chunksInfo[dosEntryChunk]); + cntChunk--; + } + + //-- it is also possible to joint entryset chunks together here if they belong to the same cluster. + //-- the atomic write here is not required. + + //-- erase the rest of entries in reamining chunks. + for(TInt i=0; i