userlibandfileserver/fileserver/srofs/dircache.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2001-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:
//

#include <e32std.h>
#include <e32std_private.h>
#include "sr_rofs.h"
#include <rofs.h>


CDirectoryCache::CDirectoryCache( CRofsMountCB& aMount, CProxyDrive& aLocalDrive, const TRofsHeader& aHeader )
	: iMount( aMount ), iLocalDrive( aLocalDrive ), 
	iTreeMediaOffset( aHeader.iDirTreeOffset ),
	iTreeSize( aHeader.iDirTreeSize ),
	iFilesMediaOffset( aHeader.iDirFileEntriesOffset ),
	iFilesSize( aHeader.iDirFileEntriesSize )
	{
	}

CDirectoryCache::~CDirectoryCache()
	{
	delete iTreeBuffer;
	delete iFilesBuffer;
	}
	
void CDirectoryCache::ConstructL()
	{
	iTreeBuffer = HBufC8::NewL( iTreeSize );
	iFilesBuffer = HBufC8::NewL( iFilesSize );

	TPtr8 ptr = iTreeBuffer->Des();
	User::LeaveIfError( iLocalDrive.Read( iTreeMediaOffset, iTreeSize, ptr) );
	TPtr8 ptr2 = iFilesBuffer->Des();
	User::LeaveIfError( iLocalDrive.Read( iFilesMediaOffset, iFilesSize, ptr2) );
	}


void CDirectoryCache::FindLeafDirL(const TDesC& aName, const TRofsDir*& aDir) const
//
// Navigate the path to find the leaf directory, starting at aDir.
//
	{

	TLex lex(aName);
	TInt r;
	FOREVER
		{
		lex.Inc(); // Skip the file separator
		lex.Mark();
		r=lex.Remainder().Locate(KPathDelimiter);
		
		if ( KErrNotFound == r )
			{
			r=lex.Remainder().Length();
			}
		if ( 0 == r ) // End of the path
			{
			break;
			}

		lex.Inc(r); // Set the token length
		
		const TRofsEntry* subDirEntry = NULL;
		TInt r = DoBinaryFindSubDir(lex.MarkedToken(), KEntryAttDir|KEntryAttMatchExclusive, aDir, subDirEntry );
		if( KErrNone != r )
			{
			User::Leave( KErrPathNotFound );
			}
		__ASSERT_DEBUG( 0 != (subDirEntry->iAtt & KEntryAttDir ), CRofs::Panic( CRofs::EPanicEntryNotDir ));
		aDir = RofsDirFromSubDirEntry( subDirEntry );
		}
	}

TInt CDirectoryCache::DoBinaryFindSubDir(const TDesC& aName, TUint aAtt, const TRofsDir* aDir, const TRofsEntry*& aSubDirEntry ) const
	//
	// Scan the directory aDir looking for subdirectory aName using binary search.
	// aName cannot contain wildcards and aSubDirEntry should always be NULL.
	// If a matching entry is found writes pointer to the entry into aSubDirEntry
	// and returns KErrNone.
	//
	{

	if (aSubDirEntry != NULL)
		{
		return DoFindSubDir(aName, aAtt, aDir, aSubDirEntry);
		}

	TInt numDirs=GetDirCount(aDir);
	TInt topIndex=numDirs-1;
	TInt bottomIndex=0;

	TBool bFound=EFalse;
	TInt retVal=KErrNotFound;
	if(topIndex>=0)
		{
		TUint16* offsets = (TUint16*) ((TUint8*) aDir + aDir->iStructSize);
		offsets += 2; //to skip file and dir counts

		while((!bFound) && (topIndex>=bottomIndex))
		{
			TInt32 middleIndex;
			const TRofsEntry* pCurrentEntry=NULL;
			middleIndex=(topIndex+bottomIndex)>>1;

			//Offsets for directories are relative to the start of the
			//directory block
			TUint8* ptr = (TUint8*) aDir + (offsets[middleIndex] << 2);
			pCurrentEntry = (TRofsEntry*)  ptr;

			TPtrC currName=TPtrC((const TText*)&pCurrentEntry->iName[0],pCurrentEntry->iNameLength);

			TInt result=Compare(aName,currName);
			if(result<0)
				topIndex=middleIndex-1;
			else if(result>0)
				bottomIndex=middleIndex+1;
			else
				{
				bFound=ETrue;	// exit loop
				if (iMount.MatchEntryAtt(pCurrentEntry->iAtt, aAtt))
					{
					aSubDirEntry=pCurrentEntry;
					retVal=KErrNone;
					}
				}
			}
		}
	return retVal;
	}

TInt CDirectoryCache::DoFindSubDir(const TDesC& aName, TUint aAtt, const TRofsDir* aDir, const TRofsEntry*& aSubDirEntry ) const
	//
	// Scan the directory aDir looking for subdirectory aName. If aSubDirEntry is
	// not NULL the search starts from the entry AFTER aSubDirEntry.
	// If a matching entry is found writes pointer to the entry into aSubDirEntry
	// and returns KErrNone.
	//
	{
	const TRofsEntry* pEntry = aSubDirEntry;	// which entry
	const TRofsEntry* const pEnd = (TRofsEntry*)EndOfDirPlusOne( aDir );

	if( !pEntry )
		{
		pEntry = FirstSubDirEntryFromDir( aDir );
		}
	else
		{
		// move to next entry
		__ASSERT_DEBUG( pEntry >= FirstSubDirEntryFromDir( aDir ), CRofs::Panic( CRofs::EPanicEntryBeforeDirectory ));
		__ASSERT_DEBUG( pEntry < pEnd, CRofs::Panic( CRofs::EPanicEntryAfterDirectory ));
		pEntry = NextEntry( pEntry );
		}

	while ( pEntry < pEnd )
		{
		TPtrC name( NameAddress( pEntry ), pEntry->iNameLength);
		if ( KErrNotFound != name.MatchF(aName) && iMount.MatchEntryAtt(pEntry->iAtt, aAtt) )
			{
			aSubDirEntry = pEntry;
			return KErrNone;
			}
		
		pEntry = NextEntry( pEntry );
		}
	return KErrNotFound;
	}

TInt CDirectoryCache::GetDirCount(const TRofsDir* aDir) const
//
//Return the number of directory entries contained in aDir
//
	{
	TUint16* dirCount = (TUint16*) ((TUint8*) aDir + aDir->iStructSize);
	return *dirCount;
	}

TInt CDirectoryCache::GetFileCount(const TRofsDir* aDir) const
//
//Return the number of file entries contained in aDir
//
	{
	TUint16* fileCount = (TUint16*) ((TUint8*) aDir + aDir->iStructSize);
	fileCount++; //jump over the dir count
	return *fileCount;
	}

TInt CDirectoryCache::Compare(const TDesC& aLeft, const TDesC& aRight) const
//
//Compares two filenames.  Folds ASCII characters to uppercase
//
	{

	TInt len=Min(aLeft.Length(),aRight.Length());
	const TText* leftString = aLeft.Ptr();
	const TText* rightString = aRight.Ptr();
	while (len--)
		{
		TText leftChar=*leftString++;
		TText rightChar=*rightString++;
		if (leftChar<='Z' && leftChar>='A')
			leftChar +='a'-'A';     // covert to UPPERCASE
		if (rightChar<='Z' && rightChar>='A')
			rightChar +='a'-'A';    // covert to UPPERCASE
		TInt result=leftChar-rightChar;
		if (result != 0)
			return result;
		}
		// match up to end of shorter string, now compare lengths
		return aLeft.Length()-aRight.Length();
	}

TInt CDirectoryCache::ExtractMangleInfo(const TDesC& searchName, TUint8 &MountId, TUint8 &ReservedId) const
{
	#define HexToInt(x) ( x.IsDigit()? (TUint8)(x-(TUint)'0') : 0x0a+(TUint8)((x>='A' && x<='Z')? x-(TUint)'A':x-(TUint)'a') )
	const TInt KOpenBraceRevPos = 7;
	const TInt KHyphenRevPos = 4;
	const TInt KCloseBraceRevPos = 1;

	TInt openBraceRevPos = searchName.LocateReverse('[');
	TInt closeBraceRevPos = searchName.LocateReverse(']');
	TInt hyphenRevPos = searchName.LocateReverse('-');
	TInt searchNameLen = searchName.Length();

	if(openBraceRevPos==KErrNotFound || closeBraceRevPos==KErrNotFound || hyphenRevPos==KErrNotFound)
		return KErrNotFound;

	openBraceRevPos = searchNameLen - openBraceRevPos;
	closeBraceRevPos = searchNameLen - closeBraceRevPos;
	hyphenRevPos = searchNameLen - hyphenRevPos;
	if(openBraceRevPos!=KOpenBraceRevPos || hyphenRevPos!=KHyphenRevPos || closeBraceRevPos!=KCloseBraceRevPos)
		return KErrNotFound;

	const TText* nameString = searchName.Ptr();
	TInt MountIdPos = searchNameLen - KOpenBraceRevPos + 1;
	TInt ReservedIdPos = searchNameLen - KHyphenRevPos + 1;

	TChar MountIdHNibble = *(nameString+MountIdPos);
	TChar MountIdLNibble = *(nameString+MountIdPos+1);

	TChar ReservedIdHNibble = *(nameString+ReservedIdPos);
	TChar ReservedIdLNibble = *(nameString+ReservedIdPos+1);

	if(MountIdHNibble.IsHexDigit() && MountIdLNibble.IsHexDigit() &&
			ReservedIdHNibble.IsHexDigit() && ReservedIdLNibble.IsHexDigit())
	{
		MountId = (TUint8)((HexToInt(MountIdHNibble) << 4) | HexToInt(MountIdLNibble));
		ReservedId = (TUint8)((HexToInt(ReservedIdHNibble) << 4) | HexToInt(ReservedIdLNibble));
		return KErrNone;
	}
	return KErrNotFound;
}

TInt CDirectoryCache::DoBinaryFindFile(const TDesC& aName, TUint aAtt, const TRofsDir* aDir, const TRofsEntry*& aEntry ) const
//
// Scan from aDir looking for file aName using binary search.
// aName cannot contain wildcards and aEntry should always be NULL.
// If found return the result in aEntry.
//
	{
	if (aEntry != NULL)
		{
		return DoFindFile(aName, aAtt, aDir, aEntry);
		}

	TInt numFiles=GetFileCount(aDir);
	TInt topIndex=numFiles-1;
	TInt bottomIndex=0;
	TInt result;
	TBool doNameMangle = ETrue;
	TBuf<KMaxFileName> searchName;

	TBool bFound=EFalse;
	TInt retVal=KErrNotFound;

	searchName.Copy((const TText*)aName.Ptr(), aName.Length());

	if(topIndex>=0)
		{
		TUint16* offsets = (TUint16*) ((TUint8*) aDir + aDir->iStructSize);
		offsets += 2;                 //to skip file and dir counts
		offsets += GetDirCount(aDir); //skip directory offsets

		while((!bFound) && (topIndex>=bottomIndex))
			{
			TInt32 middleIndex;
			const TRofsEntry* pCurrentEntry=NULL;
			TBuf<KMaxFileName> currName;

			middleIndex=(topIndex+bottomIndex)>>1;

			//Offsets for files are relative to the start of the
			//file block
			TInt bufferOffset = (offsets[middleIndex]<<2) - iFilesMediaOffset + 4;
			bufferOffset += aDir->iFileBlockAddress;

			TUint8* ptr = (TUint8*) &iFilesBuffer[0];

			pCurrentEntry=(TRofsEntry*) &ptr[bufferOffset];

			currName.Copy(((const TText*)&pCurrentEntry->iName[0]), pCurrentEntry->iNameLength);

			if(doNameMangle && ((~pCurrentEntry->iAttExtra & (KEntryAttUnique >> 23)) != 0))
				currName.AppendFormat(_L("[%02x-00]"),iMount.iMountId);

			result=Compare(searchName,currName);

			if(result<0)
				topIndex=middleIndex-1;
			else if(result>0)
				bottomIndex=middleIndex+1;
			else
				{
				bFound=ETrue;	// exit loop
				if (iMount.MatchEntryAtt(pCurrentEntry->iAtt, aAtt))
					{
					aEntry=pCurrentEntry;
					retVal = (pCurrentEntry->iFileAddress == KFileHidden) ? KErrHidden : KErrNone;
					}
				/* If we have found a file and we are in second pass of binary search without nameMangle 
					check whether it is unique file */
				if(!doNameMangle)
					{
					/* If it is not a unique file then the file does not exist */
					if((~pCurrentEntry->iAttExtra & (KEntryAttUnique >> 23)) == 0)
						{
						retVal = KErrNotFound;
						aEntry = NULL;
						}
					}
				}

			/* If we are going to return and we are searching for the unique file,there is a possiblity
			   that the binary search would have missed it (since the file entries were sorted without 
			   namemangle).*/
			if((!bFound) && (topIndex<bottomIndex) && doNameMangle)
				{
					TUint8 MountId;
					TUint8 ReservedId;

					if(ExtractMangleInfo(searchName,MountId,ReservedId) != KErrNone)
						break;

					if(MountId != iMount.iMountId || ReservedId != 0)
						break;

					/* If the aNameLength is equal to the mangle name length, we cant proceed our search 
						with null strings */
					if((TUint)(aName.Length()) == KRofsMangleNameLength)
						break;

					searchName.Copy((const TText*)aName.Ptr(), aName.Length()-KRofsMangleNameLength);

					/* The next level of search is sufficient enough to start on the top portion of the list.
						Thus resetting the bottomIndex to 0 is sufficient */
					bottomIndex=0;
					doNameMangle = EFalse;
				}
			}
		}
	return retVal;
	}

TInt CDirectoryCache::DoFindFile(const TDesC& aName, TUint aAtt, const TRofsDir* aDir, const TRofsEntry*& aEntry ) const
//
// Scan from aDir looking for file aName.
// The search starts at the entry after aEntry, unless aEntry is NULL, in which
// case the srach starts from the beginning of the file block.
// If found return the result in aEntry.
//
	{
	__ASSERT_DEBUG( aDir != NULL, CRofs::Panic( CRofs::EPanicNullSubDir ));
	
	if( aDir->iFileBlockAddress )
		{
		const TRofsEntry* pEntry;
		if( !aEntry )
			{
			pEntry = FirstFileEntryFromDir( aDir );
			}
		else
			{
			pEntry = NextEntry( aEntry );
			}
		const TAny* const pEnd = EndOfFileBlockPlusOne( aDir );
		
		while ( pEntry < pEnd )
			{
			TBuf<KMaxFileName> fileName;

			fileName.Copy(NameAddress( pEntry ), pEntry->iNameLength);
			if((~pEntry->iAttExtra & (KEntryAttUnique >> 23)) != 0)
				fileName.AppendFormat(_L("[%02x-00]"),iMount.iMountId);

			if ( KErrNotFound != fileName.MatchF(aName) && iMount.MatchEntryAtt(pEntry->iAtt, aAtt)  )
				{
				aEntry = pEntry;
				return(( pEntry->iFileAddress == KFileHidden ) ? KErrHidden : KErrNone);
				}
			
			pEntry = NextEntry( pEntry );
			}
		}
	return KErrNotFound;
	}


void CDirectoryCache::FindFileEntryL(const TDesC& aName, const TRofsEntry*& aEntry) const
//
// Locate an entry from its full path name.
//
	{
	TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
	const TRofsDir* dir = RootDirectory();
	FindLeafDirL(aName.Left(namePos), dir);

	aEntry=0;
	TInt r = DoBinaryFindFile(aName.Mid(namePos), KEntryAttDir|KEntryAttMatchExclude, dir, aEntry);
	if (r!=KErrNone)
		User::Leave(r);
	}

void CDirectoryCache::FindDirectoryEntryL(const TDesC& aName, const TRofsDir*& aDir) const
//
// Locate an entry from its full path name.
//
	{
	aDir = RootDirectory();
	FindLeafDirL(aName,aDir);
	}


void CDirectoryCache::GetNextMatchingL(const TDesC& aName, TUint aAtt, const TRofsDir*& aDir, const TRofsEntry*& aEntry, TInt aError, TBool bUseBinarySearch) const
	//
	// Retrieves the next directory or file entry from aDir that matches the pattern in aName
	// (which should be the entry name only, not the full path)
	// The search starts at the entry after aEntry
	//
	{
	__ASSERT_DEBUG( aName.LocateReverse(KPathDelimiter) == KErrNotFound, CRofs::Panic( CRofs::EPanicBadMatchName ));

	TInt r = KErrGeneral;
	const TRofsEntry* entry = aEntry;
	TBool searchFiles = EFalse;
	if( entry && (entry < FirstSubDirEntryFromDir(aDir) || entry >= EndOfDirPlusOne(aDir)) )
		{
		searchFiles = ETrue;
		}
	else
		{
		// searching the directory list
		if (!bUseBinarySearch)
			{
			r = DoFindSubDir( aName, aAtt, aDir, entry );
			}
		else
			{
			r = DoBinaryFindSubDir(aName, aAtt, aDir, entry);
			}
		if( KErrNotFound == r )
			{
			// start looking through the file list
			entry = NULL;	// start at beginning of list
			searchFiles = ETrue;
			}
		}


	if( searchFiles )
		{
		if (!bUseBinarySearch)
			{
			r = DoFindFile( aName, aAtt, aDir, entry );
			}
		else
			{
			r = DoBinaryFindFile(aName, aAtt, aDir, entry);
			}
		}

/*
	if( aEntry >= FirstFileEntryFromDir( aDir ) 
		&& aDir->iFileBlockAddress )
		{
		// we are searching the file list
		r = DoFindFile( aName, aAtt, aDir, aEntry );
		}
	else
		{
		// searching the directory list
		r = DoFindSubDir( aName, aAtt, aDir, aEntry );
		if( KErrNotFound == r )
			{
			// start looking through the file list
			TRofsEntry* entry = NULL;	// start at beginning of list
			r = DoFindFile( aName, aAtt, aDir, entry );
			if( KErrNone == r )
				{
				aEntry = entry;
				}
			}
		}
*/

	if( r == KErrNone || r == KErrHidden)
		{
		// Move onto the next entry (this is valid even for hidden entries so
		// that we can move onto the next entry, which may not be hidden)
		aEntry = entry;
		if(r == KErrNone)
			{
			return;
			}
		}

	User::Leave(r == KErrHidden ? r : aError);
	}

void CDirectoryCache::FindGeneralEntryL(const TDesC& aName, TUint aAtt, const TRofsDir*& aDir, const TRofsEntry*& aEntry ) const
	{
	TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
	aDir = RootDirectory();
	FindLeafDirL(aName.Left(namePos), aDir);
	GetNextMatchingL( aName.Mid(namePos), aAtt, aDir, aEntry, KErrNotFound, ETrue );
	}

TUint8 CDirectoryCache::GetMountId( void )
	{
	return (TUint8)(iMount.iMountId);
	};