sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: //
sl@0: 
sl@0: #include "UF_STD.H"
sl@0: #include <e32math.h>
sl@0: 
sl@0: const TUid KUidDictionaryFile={0x100000E4};
sl@0: 
sl@0: _LIT(KSystemIniFileLocationSpec,"Z:\\System\\System.ini");
sl@0: 
sl@0: const TUid KSystemIniFileUid = {0x1000010C};
sl@0: 
sl@0: // Thread contention resolution constants
sl@0: 
sl@0: const TInt KSpinCount=40;
sl@0: const TInt KLockoutLimit=0xc0000;	// to keep the lock-out time down to ~1sec (this is microseconds)
sl@0: const TInt KFailTime=0x1000;		// ~ time to fail to open file
sl@0: const TInt KSpinLimit=KLockoutLimit-KSpinCount*KFailTime;
sl@0: 
sl@0: LOCAL_C TUint8 const WaitDistribution[8]={0,0,0,0,0x1f,0x3f,0x7f,0xff};	// wait-time distribution mask
sl@0: 
sl@0: class RYieldThread : public RThread
sl@0: 	{
sl@0: public:
sl@0: 	inline void Yield() const {SetPriority(Priority());}
sl@0: 	};
sl@0: 
sl@0: /////////////////////////////////////////////
sl@0: // CDictionaryFileStore
sl@0: /////////////////////////////////////////////
sl@0: 
sl@0: LOCAL_C void EnsurePathL(RFs& aFs,const TDesC& aName)
sl@0: 	{
sl@0: 	TInt r=aFs.MkDirAll(TParsePtrC(aName).DriveAndPath());
sl@0: 	if (r!=KErrAlreadyExists)
sl@0: 		User::LeaveIfError(r);
sl@0: 	}
sl@0: 
sl@0: EXPORT_C CDictionaryFileStore* CDictionaryFileStore::SystemL(RFs& aFs)
sl@0: /** Opens the system dictionary file store.
sl@0: 
sl@0: @param aFs Handle to a file server session.
sl@0: @return A pointer to the system file based dictionary store object. */
sl@0: 	{
sl@0: 	CDictionaryFileStore* store=SystemLC(aFs);
sl@0: 	CleanupStack::Pop();
sl@0: 	return store;
sl@0: 	}
sl@0: 
sl@0: EXPORT_C CDictionaryFileStore* CDictionaryFileStore::SystemLC(RFs& aFs)
sl@0: /** Opens the system dictionary file store and puts the pointer to the file store 
sl@0: object onto the cleanup stack.
sl@0: 
sl@0: @param aFs Handle to a file server session. 
sl@0: @return A pointer to the system file based dictionary store object. */
sl@0: 	{
sl@0: 	TDriveUnit drive(static_cast<TUint>(RFs::GetSystemDrive()));	
sl@0: 	TParse parse;
sl@0: 	User::LeaveIfError(parse.Set(drive.Name(), &KSystemIniFileLocationSpec, NULL));
sl@0: 	
sl@0: 	EnsurePathL(aFs, parse.FullName());
sl@0: 	return OpenLC(aFs, parse.FullName(), KSystemIniFileUid);
sl@0: 	}
sl@0: 
sl@0: EXPORT_C CDictionaryFileStore* CDictionaryFileStore::OpenL(RFs& aFs,const TDesC& aName,TUid aFileId)
sl@0: 	/** Creates a file based dictionary store object.
sl@0: 	
sl@0: 	If the file with the specified full path name exists, then an attempt is made 
sl@0: 	to open an existing file store contained within this file. Any existing file 
sl@0: 	store must satisfy the following conditions:
sl@0: 	
sl@0: 	it must be a valid dictionary store
sl@0: 	
sl@0: 	the third UID component of the file store type must match the specified UID; 
sl@0: 	this UID serves to differentiate between dictionary stores
sl@0: 	
sl@0: 	otherwise the function leaves with KErrCorrupt.
sl@0: 	
sl@0: 	If the file with the specified full path name does not exist, then an attempt 
sl@0: 	is made to create a new file and to create a file based dictionary within 
sl@0: 	it. The third UID component of the file store type is set to the specified 
sl@0: 	UID value.
sl@0: 	
sl@0: 	Note that the file is opened in exclusive access mode.
sl@0: 	
sl@0: 	@param aFs Handle to a file server session. 
sl@0: 	@param aName The full path name of the file. 
sl@0: 	@param aUid3 The UID used to differentiate between dictionary stores.
sl@0: 	@return A pointer to the file based dictionary store object. 
sl@0: 	@see TUid
sl@0: 	@see TUidType */
sl@0: 	{
sl@0: 	CDictionaryFileStore* self = CDictionaryFileStore::OpenLC(aFs,aName,aFileId);
sl@0: 	CleanupStack::Pop();
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: EXPORT_C CDictionaryFileStore* CDictionaryFileStore::OpenLC(RFs& aFs,const TDesC& aName,TUid aFileId)
sl@0: 	/** Creates a file based dictionary store object and puts the pointer onto the 
sl@0: 	cleanup stack.
sl@0: 	
sl@0: 	If the file with the specified full path name exists, then an attempt is made 
sl@0: 	to open an existing file store contained within this file. Any existing file 
sl@0: 	store must satisfy the following conditions:
sl@0: 	
sl@0: 	it must be a valid dictionary store
sl@0: 	
sl@0: 	the third UID component of the file store type must match the specified UID; 
sl@0: 	this UID serves to differentiate between dictionary stores
sl@0: 	
sl@0: 	otherwise the function leaves with KErrCorrupt.
sl@0: 	
sl@0: 	If the file with the specified full path name does not exist, then an attempt 
sl@0: 	is made to create a new file and to create a file based dictionary within 
sl@0: 	it. The third UID component of the file store type is set to the specified 
sl@0: 	UID value.
sl@0: 	
sl@0: 	Note that the file is opened in exclusive access mode.
sl@0: 	
sl@0: 	@param aFs Handle to a file server session. 
sl@0: 	@param aName The full path name of the file. 
sl@0: 	@param aUid3 The UID used to differentiate between dictionary stores.
sl@0: 	@return A pointer to the file based dictionary store object. 
sl@0: 	@see TUid
sl@0: 	@see TUidType */
sl@0: 	{
sl@0: 	CDictionaryFileStore* self = new(ELeave) CDictionaryFileStore();
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL(aFs,aName,aFileId);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: void CDictionaryFileStore::ConstructL(RFs& aFs,const TDesC& aName,TUid aFileId)
sl@0: //
sl@0: // try to open the file - if this fails KErrNotFound try to create it
sl@0: // if the file is in use retry after a pause
sl@0: //
sl@0: 	{
sl@0: 	RYieldThread thread;
sl@0: 	TInt64 seed;
sl@0: 	const TUidType type(KPermanentFileStoreLayoutUid,KUidDictionaryFile,aFileId);
sl@0: 	for (TInt wait=KLockoutLimit;;)
sl@0: 		{
sl@0: 		RFile file;
sl@0:         //  When the file server write caching is enabled, the order of file write operations is not guaranteed.
sl@0:         // This could cause data inconsistency in some circumstances,for example, when the power is lost in the 
sl@0:         // middle of a database transaction.Therefore, the file write caching is disabled for this file to maintain integrity.
sl@0: 
sl@0: 		TInt r=file.Open(aFs,aName,EFileShareExclusive|EFileWrite|EFileWriteDirectIO);
sl@0: 		switch (r)
sl@0: 			{
sl@0: 		case KErrNone:
sl@0: 			{
sl@0: 			TInt size;
sl@0: 			if (file.Size(size)==KErrNone && size!=0)
sl@0: 				{
sl@0: 				CFileStore* store=NULL;
sl@0: 				TRAP(r,store=CPermanentFileStore::FromL(file));
sl@0: 			    if (r==KErrNotSupported||r==KErrEof)
sl@0: 					r=KErrCorrupt; // treat a bad store file as corrupt
sl@0: 			    else if (r==KErrNone && store->Type()!=type)
sl@0: 					{
sl@0: 					// treat a wrong 3rd UID as corrupt
sl@0: 					delete store;
sl@0: 					store = NULL;
sl@0: 					r=KErrCorrupt;
sl@0: 					}
sl@0: 				if (r==KErrCorrupt)
sl@0: 					{
sl@0: 				// silently replace the entire file if it is corrupt
sl@0: 				//  When the file server write caching is enabled, the order of file write operations is not guaranteed.
sl@0: 				// This could cause data inconsistency in some circumstances,for example, when the power is lost in the 
sl@0: 				// middle of a database transaction.Therefore, the file write caching is disabled for this file to maintain integrity.
sl@0: 				//   
sl@0: 					r=file.Replace(aFs,aName,EFileShareExclusive|EFileWrite|EFileWriteDirectIO);
sl@0: 					if (r==KErrInUse)
sl@0: 						break;  // try again later...
sl@0: 					if (r==KErrNone)
sl@0: 						{
sl@0: 						CreateStoreL(file,type);
sl@0: 						return;
sl@0: 						}
sl@0: 					}
sl@0: 				__LEAVE_IF_ERROR(r);
sl@0: 				__ASSERT_DEBUG(store != NULL, User::Invariant());
sl@0: 				//coverity[use_after_free]
sl@0:     			iStore = store;
sl@0: 				if (store->Root()==KNullStreamId)
sl@0: 					CDictionaryStore::ConstructL();
sl@0: 				}
sl@0: 			else
sl@0: 				CreateStoreL(file,type);
sl@0: 			return;
sl@0: 			}
sl@0: 		case KErrNotFound:
sl@0:             //  When the file server write caching is enabled, the order of file write operations is not guaranteed.
sl@0:             // This could cause data inconsistency in some circumstances,for example, when the power is lost in the 
sl@0:             // middle of a database transaction.Therefore, the file write caching is disabled for this file to maintain integrity.
sl@0: 			r=file.Create(aFs,aName,EFileShareExclusive|EFileWrite|EFileWriteDirectIO);
sl@0: 			if (r==KErrNone)
sl@0: 				{
sl@0: 				CreateStoreL(file,type);
sl@0: 				return;
sl@0: 				}
sl@0: 			else if (r==KErrAlreadyExists)
sl@0: 				;	// try and open after delay
sl@0: 			else
sl@0: 				__LEAVE(r);
sl@0: 			break;
sl@0: 		case KErrInUse:
sl@0: 			break;
sl@0: 		default:
sl@0: 			__LEAVE(r);
sl@0: 			}
sl@0: 		wait-=KFailTime;
sl@0: 		if (wait<=0)
sl@0: 			break;		// waited too long
sl@0: 		if (wait>KSpinLimit)
sl@0: 			{			// straight back to retry
sl@0: 			thread.Yield();	// force another reschedule
sl@0: 			continue;
sl@0: 			}
sl@0: 		// random wait time...
sl@0: 		if (wait==KSpinLimit)
sl@0: 			{	// initialise random number generator
sl@0: 			TThreadId id=thread.Id();
sl@0: 			TUint idVal=*(const TUint*)&id;
sl@0: 			seed = MAKE_TINT64(idVal^TUint(this),idVal^TUint(&id));
sl@0: 			Math::Rand(seed);
sl@0: 			Math::Rand(seed);
sl@0: 			}
sl@0: 		TInt pause=Math::Rand(seed)>>11;
sl@0: 		pause=(pause&WaitDistribution[(pause>>8)&0x7])<<10;
sl@0: 		if (pause)
sl@0: 			{
sl@0: 			wait-=pause;
sl@0: 			User::After(pause);
sl@0: 			}
sl@0: 		}
sl@0: 	__LEAVE(KErrInUse);
sl@0: 	}
sl@0: 
sl@0: void CDictionaryFileStore::CreateStoreL(RFile& aFile,const TUidType& aType)
sl@0: 	{
sl@0: 	CFileStore* store = CPermanentFileStore::NewL(aFile);
sl@0: 	iStore = store;
sl@0: 	store->SetTypeL(aType);
sl@0: 	CDictionaryStore::ConstructL();
sl@0: 	}