diff -r 000000000000 -r b16258d2340f applayerpluginsandutils/bookmarksupport/test/cenrepsrv/shrepos.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/applayerpluginsandutils/bookmarksupport/test/cenrepsrv/shrepos.cpp Tue Feb 02 01:09:52 2010 +0200 @@ -0,0 +1,1476 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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 "panic.h" +#include "srvdefs.h" +#include "srvres.h" +#include "shrepos.h" +#include "srvrepos.h" +#include "srvparams.h" + +#ifdef SYMBIAN_CENTRALREPOSITORY_PARANOID_CHECKING + +/** Validates that the required invariants hold: (1) ordered by ascending key value (2) key uniqueness. +Panics upon violation. +*/ +void RSettingsArray::ValidateInvariantProperties() const + { + if (Count() > 0) + { + const TServerSetting* prev = &( operator[](0) ); + const TServerSetting* curr = prev + 1; + const TServerSetting* upperBound = prev + Count() - 1; + while(curr <= upperBound) + { + // future: should replace with diagnostic panic code + ASSERT(curr->Key() > prev->Key()); + prev = curr; + ++curr; + } + } + } + +/** Validates that the required invariants hold for the given setting pointer array: +(1) ordered by ascending key value (2) key uniqueness. Panics upon violation. +*/ +void RSettingsArray::ValidateInvariantProperties(const RSettingPointerArray& aPtrArray) + { + if (aPtrArray.Count() > 0) + { + const TServerSetting** prev = const_cast( &(aPtrArray[0]) ); + const TServerSetting** curr = prev + 1; + const TServerSetting** upperBound = prev + aPtrArray.Count() - 1; + while(curr <= upperBound) + { + // future: should replace with diagnostic panic code + ASSERT((*curr)->Key() > (*prev)->Key()); + prev = curr; + ++curr; + } + } + } + +#endif + +/** Returns pointers to all settings whose key matches all bits of the target id also included +in the bitwise mask. + +@param aTargetKey Bit pattern to match +@param aMask Bitwise mask. Where bits are set the corresponding bit in aTargetKey is significant, ie + AND behaviour conventional with masking +@param aMatches Returns pointers to all settings with keys matching the mask, in ascending key order. The + caller should ensure it is appropriately constructed for the likely growth pattern. +@return Error code. aMatches may be modified even upon error return but the contents must not be relied upon. +*/ +TInt RSettingsArray::Find(TUint32 aTargetKey, TUint32 aMask, RSettingPointerArray& aMatches) const + { +#ifdef SYMBIAN_CENTRALREPOSITORY_PARANOID_CHECKING + ValidateInvariantProperties(); +#endif + + aMatches.Reset(); + // Find the first setting with key >= the lowest possible match + const TInt upperIdx = Count() - 1; + const TUint32 maskedTarget = aTargetKey & aMask; + TInt lowerIdx; + FindInUnsignedKeyOrder(TServerSetting(maskedTarget), lowerIdx); + if(lowerIdx > upperIdx) + { + return KErrNone; + } + + // this simply traverses the settings from the first possible match to the last. For masks with + // only with higher bits this would be wasteful - in principle could jump to next possible match start + // with another Find...() call. However logic more complex and counterproductive in some cases; would + // need some heuristic such as probing N settings ahead of current point and only invoking the refind + // if that still falls below the next range start, where N would allow for the cost of the binsearch + // and other book-keeping (for real smartness construct a new RArray from the current point onwards, + // so binsearch excludes the already known infertile preceding settings). Only worth pursuing after + // profiling. + const TUint32 upperMatchingKey = aTargetKey | ~aMask; + const TServerSetting* setting = &( operator[](lowerIdx) ); + const TServerSetting* upperBound = setting + (upperIdx - lowerIdx); + while(setting <= upperBound && setting->Key() <= upperMatchingKey) + { + if((setting->Key() & aMask) == maskedTarget) + { + TInt err = aMatches.Append(setting); + // Small optimisation of not testing err in while() condition, presuming + // that Append() far less common than key tests, ie repeatedly testing err + // is wasteful. However (arguably) makes code more complex + if(err != KErrNone) + { + aMatches.Reset(); + return err; + } + } + ++setting; + } + return KErrNone; + } + +/** Given source and destination arrays of settings, replaces the destination with the union of the +arrays. The properties of ascending key order and key uniqueness are preserved: where a key is common +to both arrays the source setting is kept, excepting where the source setting is flagged as deleted, which +simply deletes the destination + +@param aDst destination settings array +@param aSrc source settings array +@return Error code. If not KErrNone then the contents of aDst are may have been modified and must be + disregarded +@post aDst contains the union and matches the setting array invariant +*/ +TInt RSettingsArray::Merge(RSettingPointerArray& aDst, const RSettingPointerArray& aSrc) + { + const TServerSetting** srcBound; + const TServerSetting** srcPtr; + TInt count = aSrc.Count(); + if(count > 0) + { + srcPtr = const_cast( &(aSrc[0]) ); + srcBound = srcPtr + count; + } + else + { + srcPtr = NULL; + srcBound = NULL; + } + + TInt dstIdx = 0; + const TServerSetting** dstPtr=NULL; + const TServerSetting** dstBound=NULL; + + // The approach taken here of inserting entries one at a time is necessarily inefficient + // given the RPointerArray interface, but may be adequate for the projected (small) use. For + // greater efficiency the ability to insert ranges is required, ie probably custom array code + while(srcPtr < srcBound) + { + if (aDst.Count() > 0) + { + dstBound = const_cast( &(aDst[0]) ) + aDst.Count(); + dstPtr=const_cast( &(aDst[0]) )+ dstIdx; + } + const TUint32 srcKey = (*srcPtr)->Key(); + while(dstPtr < dstBound && srcKey > (*dstPtr)->Key()) + { + ++dstPtr; + ++dstIdx; + } + // Three possibilities: end of dst reached (append src), src and dst match (replace dst), + // and dst now greater than src (insert src) + if((dstPtr == dstBound || (*dstPtr)->Key() > srcKey)) + { + if(!(*srcPtr)->IsDeleted()) // drop deleted records + { + TInt ret = aDst.Insert(*srcPtr, dstIdx); // presumes insert at end == append + if(ret != KErrNone) + { + return ret; + } + ++dstIdx; + } + } + else + { + // Src key matches dst, replace or delete dst + if(!(*srcPtr)->IsDeleted()) + { + *dstPtr= *srcPtr; + ++dstIdx; + } + else + { + // may be more efficient ways of handling remove + aDst.Remove(dstIdx); + --dstBound; + } + } + ++srcPtr; + } +#ifdef SYMBIAN_CENTRALREPOSITORY_PARANOID_CHECKING + RSettingsArray::ValidateInvariantProperties(aDst); +#endif + return KErrNone; + } + +CSharedRepository::CSharedRepository(TUid aUid) : + iSettings(), iUid(aUid), + iSinglePolicies(KGranularity), + iTransactors(_FOFF(CRepositoryTransactor, iLink)) + { + iPersistsIniFileExists = ETrue; + + // debug check that CRepository::TTransactionMode modes match those used internally + // from CRepositoryTransactor: internal state logic relies on this + // there should be a better location for these checks... + ASSERT(CRepository::EReadTransaction == EReadTransaction); + ASSERT(CRepository::EConcurrentReadWriteTransaction == EConcurrentReadWriteTransaction); + ASSERT(CRepository::EReadWriteTransaction == EReadWriteTransaction); + } + +CSharedRepository::~CSharedRepository() + { + // sanity check that no transactions are active + ASSERT(!IsTransactionActive()); + // check transactor queue is empty - note this includes failed transactions + ASSERT(NULL == (CRepositoryTransactor*)TDblQueIter(iTransactors)); + + iSinglePolicies.ResetAndDestroy(); + iDeletedSettings.Close(); + } + +TUid CSharedRepository::Uid() const + { + return iUid; + } + +/** +Stores the repository in-memory content to the related repository file on drive C. +If the operation fails, the in-memory content won't match the content of +the repository file (which will be kept as it was before the CommitChangesL() call). +In order to keep the consistency, the in-memory repository content is deleted now +and restored later, on the next repository operation. +*/ +TInt CSharedRepository::CommitChanges() + { + TRAPD(error, DoCommitChangesL()); + if (error != KErrNone) + { + ResetContent(); + } + return error; + } + +/** Merges the aChanges settings into array. Is used for + Transaction merges + Restore merges + SWI merges + + Works as follows: +(a) EDeleted settings in aChanges destroy the corresponding setting in this array (if any) +(b) Where matching settings exist in both arrays, data from aChanges replaces originals. + Matching depends on aKeyOnly parameter's value. If true, matching is only + based on key. If false, settings values will be compared + For SWI merges this replacement is also dependent on whether the setting has been modified +(c) aMergeType is the merge type +Future: All descriptors are deeply copied; must change to use shallow copy for greater efficiency. +@post Following a requirement that notifications are only made if values have changed: +1. Settings flagged as deleted without a counterpart in the persistent array are removed from aChanges. +2. Where the new value and the existing value are the same, the setting is removed from aChanges. +*/ +TInt RSettingsArray::MergeArray(RSettingsArray& aChanges, RArray & aDeletedSettings, TMergeType aMergeType, TBool aKeyOnly) + { + TInt error = KErrNone; + TInt numChanges; + TInt i; + + // For downgrade remove SWI only keys + if(aMergeType==ESWIDowngradeMerge) + { + numChanges = Count(); + i = 0; + while ((i < numChanges) && (KErrNone == error)) + { + TServerSetting source = operator[](i); + // find index of item in array matching the index of this change + TInt targetIndex = aChanges.FindIndex(source); + if (targetIndex == KErrNotFound) + { + // If key is default and wasn't in the ROM, it was an install key + // so remove it + if(source.IsClean()) + { + DeleteElement(i); + --numChanges; + --i; + } + } + ++i; + } + } + + numChanges = aChanges.Count(); + i=0; + + while ((i < numChanges) && (KErrNone == error)) + { + const TServerSetting& source = aChanges[i]; + // find index of item in array matching the index of this change + TInt targetIndex = FindIndex(source); + if (targetIndex == KErrNotFound) + { + if (source.IsDeleted()) + { + // remove setting that is flagged as deleted to prevent notification + aChanges.DeleteElement(i); + i--; + numChanges--; + } + else + { + // add the new setting + TServerSetting newSetting(source.Key()); + error = newSetting.Replace(source); + if (error == KErrNone) + { + error = OrderedInsert(newSetting); + TInt deletedSetting = aDeletedSettings.FindInUnsignedKeyOrder(source.Key()); + if (KErrNotFound != deletedSetting) + { + aDeletedSettings.Remove(deletedSetting) ; + } + if (error != KErrNone) + { + newSetting.Reset(); + } + } + } + } + else + { + if (source.IsDeleted()) + { + // Retain "deleted element" state for settings marked for backup so that + // we can persist state correctly across a backup/restore cycle. + if (operator[](targetIndex).Meta() & KMetaBackup) + { + aDeletedSettings.InsertInUnsignedKeyOrder(source.Key()) ; + } + DeleteElement(targetIndex); + } + else + { + TServerSetting& target = operator[](targetIndex); + if ((target == source) && (aKeyOnly==EFalse)) + { + // value not changing: remove setting to prevent notification + // Note that for SWI merges, settings are not deleted from + // aChanges unless they are marked with EDeleted. + aChanges.DeleteElement(i); + i--; + numChanges--; + } + else + { + if( (aMergeType==ESWIUpgradeMerge) || (aMergeType==ESWIDowngradeMerge)) + { + if(target.IsClean()) + { + error = target.Replace(source); + } + else + { + // value not changing: remove setting to prevent notification + aChanges.DeleteElement(i); + i--; + numChanges--; + } + } + else + { + error = target.Replace(source); + } + } + } + } + i++; + } + aDeletedSettings.Compress(); + return error; + } + +// merge transaction settings (which may include entries flagged as deleted), persist and notify +// private method relies on calling code to ensure it is permitted to make changes here. +// if this method is committing any changes, it cancels all other sessions' transactions +TInt CSharedRepository::DoCommitTransactionSettings(CRepositoryTransactor& aTransactor, TUint32& aKeyInfo) + { + aKeyInfo = KUnspecifiedKey; + if (0 == aTransactor.iTransactionSettings.Count()) + { + aKeyInfo = 0; // == number of settings modified + return KErrNone; // nothing to do + } + TInt error = iSettings.MergeArray(aTransactor.iTransactionSettings, iDeletedSettings, ETransactionMerge, EFalse); + TInt numChanges = aTransactor.iTransactionSettings.Count(); + if (numChanges == 0) + { + if (error == KErrNone) + { + aKeyInfo = 0; // no changes + } + // no changes were made, so the internal cache is still valid. + // This could be because there were no changes: empty list, only deletes on + // non-existent items (a setting created and deleted in the transaction), + // or because of error, such as failure of an initial realloc failure. + return error; + } + if (error != KErrNone) + { + // the repository is corrupted. Dump it for lazy loading later + ResetContent(); + } + if (error == KErrNone) + { + // changes have been made: fail all other sessions' transactions so we can commit + FailAllTransactions(/*aExcludeTransactor=*/&aTransactor); + error = CommitChanges(); // this already calls ResetContent() in case of failure + } + if (error == KErrNone) + { + // settings are now persistent on disk: we can now notify about the changes + // following will notify about objects that are created and deleted in the transaction + // this could be made faster by having a multiple Notify method. + // That would also allow Notify messages to be more descriptive - ranges of Keys + for (TInt i = 0; i < numChanges; i++) + { + Notify(aTransactor.iTransactionSettings[i].Key()); + } + aKeyInfo = /*reinterpret_cast*/numChanges; + } + return error; + } + +void CSharedRepository::SetMetaDataOnRead(TServerSetting& aSetting, TBool aSingleMetaFound) + { + TInt isMetaFlagSet = aSetting.Meta() & KMetaDefaultValue; + + if(!aSingleMetaFound) + // No single metadata set for this key + { + // First check for a matching "range" default metadata + // setting + TSettingsDefaultMeta *defaultMeta = iRangeMeta.Find(aSetting.Key()); + if (defaultMeta) + { + if (isMetaFlagSet) + //sets a default meta data + //also sets the flag back to indicate that it is a default setting from ROM + //or previous install so it can be replaced later with a new one. + aSetting.SetMeta(defaultMeta->GetDefaultMetadata() | KMetaDefaultValue); + else + aSetting.SetMeta(defaultMeta->GetDefaultMetadata()); + } + else + { + // Range value not found, try for a repository default + if (isMetaFlagSet) + aSetting.SetMeta(iDefaultMeta | KMetaDefaultValue) ; + else + aSetting.SetMeta(iDefaultMeta) ; + } + } + } + +TInt CSharedRepository::CreateL(TServerSetting& aSetting, TSettingsAccessPolicy* &aPolicy, TBool aSingleMetaFound) + { + if(iSettings.Find(aSetting.Key())) + return KErrAlreadyExists; + + SetMetaDataOnRead( aSetting, aSingleMetaFound); + + TUint32 key = aSetting.Key(); + aSetting.SetAccessPolicy(aPolicy); + + iSettings.OrderedInsertL(aSetting); + Notify(aSetting.Key()); + return KErrNone; + } + +// deletes an individual setting in the shared repository and makes it persistent +// if changes are made, all sessions' transactions are failed +TInt CSharedRepository::DeleteAndPersist(TUint32 aId) + { + TServerSetting* s = iSettings.Find(aId); + if(!s) + return KErrNotFound; + + iSettings.Remove(aId); + // removed a setting, so must fail all sessions' transactions before commit possible + FailAllTransactions(/*aExcludeTransactor=*/NULL); + TInt error = CommitChanges(); + if (error == KErrNone) + { + Notify(aId); + } + return error; + } + +// deletes an individual setting without making it persistent +// must not be called while any sessions are in transactions +TInt CSharedRepository::DeleteNoPersist(TUint32 aId) + { + // should only be calling this if no transactions are active + ASSERT(!IsTransactionActive()); + + TServerSetting* s = iSettings.Find(aId); + if(!s) + return KErrNotFound; + + iSettings.Remove(aId); + Notify(aId); + return KErrNone; + } + +TInt CSharedRepository::ResetNoPersistL(TServerSetting& aSetting) + { + TServerSetting* s = iSettings.Find(aSetting.Key()); + if ((!s) || (*s != aSetting)) + { + if (s) + { + // save access policy of setting + TSettingsAccessPolicy* policy=s->AccessPolicy(); + s->Transfer(aSetting); + // restore access policy of setting + s->SetAccessPolicy(policy); + } + else + { + TServerSetting setting; + setting.Transfer(aSetting); + setting.SetAccessPolicy(GetFallbackAccessPolicy(setting.Key())); + setting.PushL(); + iSettings.OrderedInsertL(setting); + setting.Pop(); + } + } + else + { + return KErrGeneral; + } + return KErrNone; + } + +// if changes are made, all sessions' transactions are failed +void CSharedRepository::ResetAndPersistL(TServerSetting& aSetting) + { + if (ResetNoPersistL(aSetting) == KErrNone) + { + // changed a setting, so must fail all sessions' transactions + // before commit possible + FailAllTransactions(/*aExcludeTransactor=*/NULL); + CommitChangesL(); + Notify(aSetting.Key()); + } + } + +TInt CSharedRepository::ResetAllNoPersistL(CSharedRepository& aNewContent) + { + // mark cache as inconsistent in case Reset fails, so it is reloaded. + iInconsistentData = ETrue; + + // should not change repository while transactions in progress: should fail them first + ASSERT(!IsTransactionActive()); + TInt newCount = aNewContent.iSettings.Count(); + TInt count = iSettings.Count(); + + TInt newIndex = 0; + TInt index = 0; + + while(newIndexkey) + { + Notify(key); + index++; + } + } + + while(newIndexGetReadAccessPolicy()); + } + +// returns the write security policy used if there is no per-setting policy at aId +const TSecurityPolicy& CSharedRepository::GetFallbackWriteAccessPolicy(TUint32 aId) + { + return *(GetFallbackAccessPolicy(aId)->GetWriteAccessPolicy()); + } + +// Get pointer to security policy that applies to a given setting +TSettingsAccessPolicy* CSharedRepository::GetFallbackAccessPolicy(TUint32 aId) + { + // Check for single policy + TSettingsAccessPolicy policy(aId); + TIdentityRelation identity(SinglePolicyMatchOnKey); + TInt index = iSinglePolicies.Find(&policy, identity); + if(KErrNotFound != index) + return iSinglePolicies[index]; + + // check if the aId falls into any range specified in the ini file + TSettingsAccessPolicy* rangePolicy = iRangePolicies.Find(aId); + if(rangePolicy) + return rangePolicy; + + // If no single policy or range policy, return default policy + return &iDefaultPolicy; + } + + +TInt CSharedRepository::ReadSettingSavePolicyL(CIniFileIn& aFile,TServerSetting& aSetting, TSettingsAccessPolicy* &aPolicy, TBool& aSingleMetaFound) + { + TBool singleReadPolicyFound; + TBool singleWritePolicyFound; + TSecurityPolicy singleReadPolicy; + TSecurityPolicy singleWritePolicy; + + TInt err=aFile.ReadSettingL(aSetting,singleReadPolicy, singleWritePolicy, singleReadPolicyFound, singleWritePolicyFound, aSingleMetaFound); + if(err!=KErrNone) + return err; + + // Set up single policies + if(!singleReadPolicyFound) + singleReadPolicy=GetDefaultReadAccessPolicy(); + if(!singleWritePolicyFound) + singleWritePolicy=GetDefaultWriteAccessPolicy(); + + aSetting.PushL(); + if(singleReadPolicyFound || singleWritePolicyFound) + { + aPolicy=new (ELeave) TSettingsAccessPolicy(singleReadPolicy,singleWritePolicy,aSetting.Key()); + CleanupStack::PushL(aPolicy); + TLinearOrder order(&CSharedRepository::CompareKeyIds); + SinglePolicyArray().InsertInOrderL(aPolicy, order); + CleanupStack::Pop(aPolicy); + } + else + { + // check if the aId falls into any range specified in the ini file + // otherwise set policy to default policy + TSettingsAccessPolicy* rangePolicy = iRangePolicies.Find(aSetting.Key()); + if(rangePolicy) + aPolicy=rangePolicy; + else + aPolicy=&iDefaultPolicy; + } + + aSetting.Pop(); + return err; + } + +// Merge settings in this->iSettings with the iSettings of aMergeRep +// During an intsall/upgrade event aMergeRep will be created from the installed file +// During an upinstall event aMergeRep will be created from the ROM file +void CSharedRepository::MergeL(CSharedRepository& aMergeRep, TMergeType aMergeType) + { + // Process settings from main section - this updates values only + User::LeaveIfError(iSettings.MergeArray(aMergeRep.iSettings, iDeletedSettings, aMergeType)); + + // Update all access policies and meta + for(TInt i=0; iDes(),entry); + if( r == KErrNone) + { + aModified=entry.iModified; + } + else if( r== KErrNotFound) + { + // Look for txt file + TServerResources::CreateRepositoryFileNameLC(fullPath, iUid, location, EIni); + r=TServerResources::iFs.Entry(fullPath->Des(),entry); + if( r == KErrNone) + { + aModified=entry.iModified; + } + } + return r; + } + +// Handle creation or upgrade of file in install directory +void CSharedRepository::HandleUpdateMergeL(TTime aInstallFileTimeStamp, CSharedRepository& aInstallRep) + { + MergeL(aInstallRep, ESWIUpgradeMerge); + + SetInstallTime(aInstallFileTimeStamp); // Set merge timestamp + CommitChangesL(); // Commit changes to write C: file + + // settings are now persistent on disk: we can now notify about the changes + for (TInt i = 0; i < aInstallRep.iSettings.Count(); i++) + { + Notify(aInstallRep.iSettings[i].Key()); + } + } + +// Handle merge activity due to an uninstall +void CSharedRepository::HandleDeleteMergeL(CSharedRepository& aRomRep) + { + MergeL(aRomRep, ESWIDowngradeMerge); + + SetInstallTime(0); // Reset timestamp + CommitChangesL(); // Commit changes to write C: file + + // settings are now persistent on disk: we can now notify about the changes + for (TInt i = 0; i < aRomRep.iSettings.Count(); i++) + { + Notify(aRomRep.iSettings[i].Key()); + } + } + + +/** +Statement "iInconsistentData = ETrue;" must be the first statement in the method, +"iInconsistentData = EFalse;" must be the last. It is used for lasy-load implementation +for the repository and solves the problem that if CommitChangesL() fails the in-memory +repository data won't match the repository data, stored in the file. +This routine is being retained for testing purposes +*/ +void CSharedRepository::DoCommitChangesToIniFileL(const TDesC& aOutFileName) + { + iInconsistentData = ETrue; + + // should not be committing while transactions are still active + ASSERT(!IsTransactionActive()); + + CIniFileOut* out = CIniFileOut::NewLC(); + + out->WriteHeaderL(); + out->WriteOwnerSectionL(iOwner); + out->WriteTimeStampL(iTimeStamp); + out->WriteMetaDataL(iDefaultMeta, iRangeMeta); + out->WritePlatSecL(iDefaultReadPolicy, iDefaultWritePolicy, iRangePolicies); + + out->WriteMainSectionHeaderL(); + for(TInt i=0; iWriteSettingL(setting, *setting.AccessPolicy()); + } + else + { + out->WriteSettingL(setting); + } + } + + out->CommitL(aOutFileName); + CleanupStack::PopAndDestroy(out);//out + iInconsistentData = EFalse; + } + + +/** +Statement "iInconsistentData = ETrue;" must be the first statement in the method, +"iInconsistentData = EFalse;" must be the last. It is used for lasy-load implementation +for the repository and solves the problem that if CommitChangesL() fails the in-memory +repository data won't match the repository data, stored in the file. +*/ +void CSharedRepository::DoCommitChangesL() + { + iInconsistentData = ETrue; + + // should not be committing while transactions are still active + ASSERT(!IsTransactionActive()); + + TCentRepLocation location = EPersists; + HBufC* persistsTrnsFilePath(NULL); + //allocates memory on the heap + TServerResources::CreateRepositoryFileNameLC(persistsTrnsFilePath,iUid,location,ETmp); + // Create file store + CDirectFileStore* store = CDirectFileStore::ReplaceLC(TServerResources::iFs, *persistsTrnsFilePath, + (EFileWrite | EFileShareExclusive)); + const TUid uid2 = KNullUid ; + store->SetTypeL(TUidType(KDirectFileStoreLayoutUid, uid2, KServerUid3)) ; + + // Write the stream index/dictionary as root stream within the store + // so we can access it when we do a restore later on + RStoreWriteStream rootStream ; + TStreamId rootStreamId = rootStream.CreateLC(*store) ; + ExternalizeCre(rootStream) ; + rootStream.CommitL() ; + + CleanupStack::PopAndDestroy(&rootStream) ; + store->SetRootL(rootStreamId); + store->CommitL(); + CleanupStack::PopAndDestroy(store) ; + + HBufC* persistsFilePath(NULL); + TServerResources::CreateRepositoryFileNameLC(persistsFilePath,iUid,location,ECre); + TEntry entry; + if(TServerResources::iFs.Entry(*persistsFilePath,entry)==KErrNone) + { + User::LeaveIfError(TServerResources::iFs.Replace(*persistsTrnsFilePath, *persistsFilePath)); + } + else + { + User::LeaveIfError(TServerResources::iFs.Rename(*persistsTrnsFilePath, *persistsFilePath)); + } + + CleanupStack::PopAndDestroy(persistsFilePath); + CleanupStack::PopAndDestroy(persistsTrnsFilePath); + iInconsistentData = EFalse; + + // Delete any exitsing ini file on 1st sucessful externalizing of repository + if(iPersistsIniFileExists) + { + HBufC* iniFileName(NULL); + TServerResources::CreateRepositoryFileNameLC(iniFileName,iUid,location,EIni); + TServerResources::iFs.Delete(*iniFileName); + CleanupStack::PopAndDestroy(iniFileName); + iPersistsIniFileExists=EFalse; + } + } + +/** +The method reloads the repository content from a repository file. +The current repository must be emptied (or must be empty already) before the call is made. +@param aIniFile A reference to CIniFileIn object, which will be used to load + the repository content. +@return KErrCorrupt Corrupted repository file. + KErrNone The repository content was seccessfully loaded into memory. + KErrNotFound Setting not found in the file. +@leave System-wide error codes. +@leave KErrGeneral It's probably a programmer's mistake - current CSharedRepository + object is partially initialised. +*/ +TInt CSharedRepository::ReloadContentL(CIniFileIn& aIniFile) + { + // Preconditions - CSharedRepository object should be an empty one. + if(iSettings.Count() != 0 || iRangeMeta.Count() != 0 || + iSinglePolicies.Count() != 0 || iRangePolicies.Count() != 0) + { + User::Leave(KErrGeneral); + } + // Look for an "owner" section + TUint32 uidValue(KNullUid.iUid); + TInt err = aIniFile.ReadOwnerSectionL(uidValue); + if(err == KErrCorrupt) + { + return err; + } + iOwner.iUid = uidValue; + // Timestamp + TTime timeStamp (0); + err = aIniFile.ReadTimeStampSectionL(timeStamp); + if(err == KErrCorrupt) + { + return err; + } + if(timeStamp.Int64() != 0) + { + iTimeStamp = timeStamp; + } + + // Metadata + err = aIniFile.ReadDefaultMetaSecSectionL(iDefaultMeta, iRangeMeta); + if(err == KErrCorrupt) + { + return err; + } + // Default read/write policies + TBool gotDefaultReadPolicy; + TBool gotDefaultWritePolicy; + err = aIniFile.ReadPlatSecSectionL(iDefaultReadPolicy, gotDefaultReadPolicy, + iDefaultWritePolicy, gotDefaultWritePolicy, + iRangePolicies); + if(err == KErrCorrupt) + { + return err; + } + + iDefaultPolicy=TSettingsAccessPolicy(iDefaultReadPolicy,iDefaultWritePolicy, KUnspecifiedKey); + + // Settings + TServerSetting setting; + TSettingsAccessPolicy* policy; + TBool singleMetaFound; + while((err = ReadSettingSavePolicyL(aIniFile, setting, policy, singleMetaFound)) == KErrNone) + { + setting.PushL(); + if(iSettings.IsDefault()) + { + setting.SetClean(); + } + User::LeaveIfError(CreateL(setting, policy, singleMetaFound)); + setting.Pop(); + } + if(err == KErrNotFound) + { + return KErrNone; + } + return err; + } + +/** +Resets current repository data - actually all of them, which may be loaded from +the related ini file. +The iUid data member value is kept as it was at the moment of creation of +CSharedRepository object. +*/ +void CSharedRepository::ResetContent() + { + iSettings.Reset(); + iOwner = KNullUid; + iTimeStamp = TTime(0); + + for (TInt i=0;i (aNotificationState); + *notificationState = ETrue; + } + +/** +The method reloads the repository content from the related ini file if previous +CommitChangesL() has not completed successfully. +*/ +void CSharedRepository::RestoreConsistencyL() + { + //Do nothing if previous CommitChangesL() completed successfully. + if(!iInconsistentData) + { + return; + } + //Reset current repository data + ResetContent(); + //Disable notifications + TCleanupItem restoreNotification(&RestoreNotification, &iNotificationState); + CleanupStack::PushL(restoreNotification); + iNotificationState = EFalse; + //Reload the repository content from the related ini file + DoRestoreConsistencyL(); + //Activate notifications + CleanupStack::PopAndDestroy();//restoreNotification + + TCentRepLocation location = EPersists; + HBufC* persistsTmpFilePath(NULL); + //allocates memory on the heap + TServerResources::CreateRepositoryFileNameLC(persistsTmpFilePath,iUid,location,ETmp); + // Remove any .tmp file + TServerResources::iFs.Delete(*persistsTmpFilePath); + CleanupStack::PopAndDestroy(persistsTmpFilePath); + + iInconsistentData = EFalse; + } + +/** +This method looks for and sets a location for a given repository. +It is based on EAuto mode thus it goes through all locations in the +same order (EPersists - EInstall - ERom) + +@param aLocation - returns a location for a repository +@param aUid - id of a repository which location should be found +@param aType - repository file type (.txt or .cre) +@return KErrNone if aLocation succesfully set for a given repository, + KErrNotFound if a repository was not found in any locations. + +@internalTechnology +*/ +TInt CSharedRepository::FindLocationForFileL(TCentRepLocation& aLocation,TUid aUid,const TCentRepFileType aType) const + { + if(TServerResources::CentrepFileExistsL(aUid, EPersists, aType)) + { + aLocation = EPersists; + return KErrNone; + } + + if(TServerResources::CentrepFileExistsL(aUid, EInstall, aType)) + { + aLocation = EInstall; + return KErrNone; + } + + if(TServerResources::CentrepFileExistsL(aUid, ERom, aType)) + { + aLocation = ERom; + return KErrNone; + } + + return KErrNotFound; + } + +/** +The method reloads the repository content from the related cre or ini file. +@leave System-wide error codes +*/ +void CSharedRepository::DoRestoreConsistencyL() + { + TCentRepLocation location; + + TInt err = FindLocationForFileL(location,iUid,ECre); + if (err != KErrNotFound) + { + User::LeaveIfError(CreateRepositoryFromCreFileL(location)); + return; + } + + User::LeaveIfError(FindLocationForFileL(location,iUid,EIni)); + + HBufC* fileName(NULL); + TServerResources::CreateRepositoryFileNameLC(fileName,iUid,location,EIni); + + CIniFileIn* iniFile = NULL; + err = CIniFileIn::NewLC(iniFile,fileName,location); + User::LeaveIfError(err); + + + err = ReloadContentL(*iniFile); + User::LeaveIfError(err); + + CleanupStack::PopAndDestroy(iniFile); //iniFile + CleanupStack::PopAndDestroy(fileName); //fileName + fileName=0; + } + +TInt CSharedRepository::CreateRepositoryFromCreFileL( TCentRepLocation aLocation) + { + // Get file path name from location + HBufC* filePath(NULL); + TServerResources::CreateRepositoryFileNameLC(filePath,iUid, aLocation,ECre); + // Trap errors from repository creation so we can delete corrupt repositories + TRAPD(error, CreateRepositoryFromCreFileL(*filePath)); + if(error!=KErrNone && error!=KErrNotFound && error!=KErrNoMemory) + { + error=KErrCorrupt; + // store wasn't quite what we were expecting - can't return an error, can't leave + // so all we can do is close the file, tidy up as best we can, and return corrupt + if (aLocation != ERom) + { + TServerResources::iFs.Delete(*filePath) ; + } + } + else if( error==KErrNoMemory) + { + User::Leave(KErrNoMemory); + } + CleanupStack::PopAndDestroy(filePath); + return error; + } + +void CSharedRepository::CreateRepositoryFromCreFileL(TDesC& aFilePath ) + { + + TEntry entry; + TInt e = TServerResources::iFs.Entry(aFilePath, entry); + if(e==KErrNotFound || e==KErrPathNotFound) + { + User::Leave( KErrNotFound); + } + + CDirectFileStore* store = + CDirectFileStore::OpenLC (TServerResources::iFs,aFilePath, EFileRead|EFileShareReadersOnly); + if(store->Type()[0] != KDirectFileStoreLayoutUid) + { + User::Leave(KErrCorrupt); + } + + // Get the root stream and attempt to read the index from it + TStreamId rootStreamId = store->Root() ; + RStoreReadStream rootStream ; + rootStream.OpenLC(*store, rootStreamId); + // Internalize the repository + InternalizeCreL(rootStream); + CleanupStack::PopAndDestroy(&rootStream); + CleanupStack::PopAndDestroy(store); + } + +/** Attempts to start a transaction. +Guaranteed to succeed (return KErrNone) for EConcurrentReadWriteTransaction mode only. +@param aTransactor transactor attempting to start transaction +@param aMode type of transaction to be started +@pre transactor is not in a transaction +@return KErrNone if the transaction is started, KErrLocked if read/write locks prevented that +type of transaction from starting now, and KErrArgument for invalid aMode. +@post On returning KErrNone, transaction is started and read/write locks are obtained for it +in the shared repository. Any other return: transaction has not started. +*/ +TInt CSharedRepository::StartTransaction(CRepositoryTransactor& aTransactor, TInt aMode) + { + // session can only be in one transaction + ASSERT(!aTransactor.IsInTransaction()); + switch (aMode) + { + case EConcurrentReadWriteTransaction: + // can always start this type of transaction + iNumActiveConcurrentReadWriteTransactions++; + break; + case EReadTransaction: + // negative lock means there is an active EReadWriteTransaction + if (iPessimisticTransactionLockCount < 0) + { + ASSERT(iPessimisticTransactionLockCount == -1); // sanity check + return KErrLocked; + } + // when non-negative lock equals number of active EReadTransactions. + iPessimisticTransactionLockCount++; + break; + case EReadWriteTransaction: + // lock is zero if there are no active pessimistic transactions + if (iPessimisticTransactionLockCount != 0) + { + return KErrLocked; + } + // lock value of -1 means the exclusive EReadWriteTransaction is active + iPessimisticTransactionLockCount = -1; + break; + default: + // not a valid transaction mode + return KErrArgument; + } + aTransactor.AddToQueue(iTransactors, aMode); + return KErrNone; + } + +/** Commit transaction +@return KErrNone on success, or error code. +@param aKeyInfo + on success (return KErrNone): aKeyInfo returns number of modified settings; + on failure (other error code): KUnspecifiedKey +@pre transactor is in a transaction. +@post transactor is not in a transaction +*/ +TInt CSharedRepository::CommitTransaction(CRepositoryTransactor& aTransactor, TUint32& aKeyInfo) + { + // calling code should have panicked the client if not in a transaction + ASSERT(aTransactor.IsInTransaction()); + TInt result = aTransactor.iTransactionResult; + if (aTransactor.IsInFailedTransaction()) + { + ASSERT(result != KErrNone); + aKeyInfo = aTransactor.iTransactionErrorKey; + } + else + { + ASSERT(result == KErrNone); + ASSERT(aTransactor.iTransactionErrorKey == KUnspecifiedKey); + aKeyInfo = 0; + // must release locks otherwise shared repository will not commit changes + // failed transactions have already released their locks + ReleaseTransactionLock(aTransactor); + } + + // transactions that haven't made any changes can be closed at any time + if (aTransactor.IsInActiveReadWriteTransaction() && + (aTransactor.iTransactionSettings.Count() > 0)) + { + result = DoCommitTransactionSettings(aTransactor, aKeyInfo); + } + + // transaction is complete - remove from queue + aTransactor.Deque(); + + return result; + } + +/** Cancels the transaction, discarding changes. +@post Not in a transaction +*/ +void CSharedRepository::CancelTransaction(CRepositoryTransactor& aTransactor) + { + if (aTransactor.IsInTransaction()) + { + ReleaseTransactionLock(aTransactor); + aTransactor.Deque(); + } + } + +TInt CSharedRepository::FailTransaction(CRepositoryTransactor& aTransactor, TInt aError, TUint32 aErrorKey) + { + ASSERT(aError != KErrNone); // must fail for a reason + if (aTransactor.IsInActiveTransaction()) + { + // locks cannot be removed from a failed transaction, so release before failing + ReleaseTransactionLock(aTransactor); + aTransactor.SetFailed(aError, aErrorKey); + } + return aError; // to allow "return FailTransaction(error, errorKey);" - error written once + } + +/** Fails all active transactions - except for the optional aExcludeTransactor, releasing locks. +All transactions are failed with reason "KErrLocked" meaning they are "locked out". +This should only be done to allow another agent to change values in the repository. +Beware that all concurrent read/write transactions that are failed with KErrLocked are +expected to retry the transactions straight afterwards - must be careful to allow their +retry strategy to be successful. +*/ +void CSharedRepository::FailAllTransactions(const CRepositoryTransactor* aExcludeTransactor) + { + TDblQueIter transIter(iTransactors); + CRepositoryTransactor* transactor; + while ((transactor = transIter++) != NULL) + { + if (transactor != aExcludeTransactor) + { + FailTransaction(*transactor, KErrLocked, KUnspecifiedKey); + } + } + } + +/** must currently be in active Read transaction. Does not fail +transaction here if promotion to read/write failed. +@return KErrNone if promoted, KErrLocked if not +*/ +TInt CSharedRepository::AttemptPromoteTransactionToReadWrite(CRepositoryTransactor& aTransactor) + { + // transactor should currently be in an active read transaction + ASSERT(aTransactor.IsInActiveReadTransaction()); + // sanity check: must only be pessimistic reads active + ASSERT(iPessimisticTransactionLockCount > 0); + // can promote only if there are no other active read transactions: + if (1 == iPessimisticTransactionLockCount) + { + // may only promote to exclusive read/write as it has the same commit semantics + // as Read transaction: concurrent R/W must wait for reads to finish first. + aTransactor.PromoteToExclusiveReadWrite(); + // lock value of -1 means the exclusive EReadWriteTransaction is active + iPessimisticTransactionLockCount = -1; + return KErrNone; + } + return KErrLocked; + } + +/** Private helper method which releases any read/write locks held in the shared repository +by this transactor. Caller must set transactor's state or remove from queue as appropriate. +@param aTransactor transactor whose read/write locks are to be released. +@post Any read/write locks held by transactor are released. +*/ +void CSharedRepository::ReleaseTransactionLock(CRepositoryTransactor& aTransactor) + { + if (aTransactor.IsInActiveConcurrentReadWriteTransaction()) + { + iNumActiveConcurrentReadWriteTransactions--; + ASSERT(iNumActiveConcurrentReadWriteTransactions >= 0); // sanity check + } + else if (aTransactor.IsInActiveReadTransaction()) + { + iPessimisticTransactionLockCount--; + ASSERT(iPessimisticTransactionLockCount >= 0); // sanity check + } + else if (aTransactor.IsInActiveExclusiveReadWriteTransaction()) + { + // can only be one exclusive read/write transaction active (lock value -1) + ASSERT(iPessimisticTransactionLockCount == -1); + iPessimisticTransactionLockCount = 0; + } + } + +void CSharedRepository::ExternalizeCre(RWriteStream& aStream) const + { + aStream << TServerResources::iPersistsVersion; + aStream << iUid ; + aStream << iOwner ; + + TUint32 count=iSinglePolicies.Count(); + aStream << count; + for(TInt i=0; i> version; + aStream >> iUid ; + aStream >> iOwner ; + + TUint32 count; + aStream >> count; + for(TInt i=0; i> *singlePolicy; + iSinglePolicies.AppendL(singlePolicy); + CleanupStack::Pop(singlePolicy); + } + + iRangePolicies.Reset(); + aStream >> iRangePolicies ; + + HBufC8* securityPolicyPackage ; + securityPolicyPackage = HBufC8::NewLC(aStream, 10000) ; + iDefaultReadPolicy.Set(securityPolicyPackage->Des()) ; + CleanupStack::PopAndDestroy(securityPolicyPackage) ; + securityPolicyPackage = HBufC8::NewLC(aStream, 10000) ; + iDefaultWritePolicy.Set(securityPolicyPackage->Des()) ; + CleanupStack::PopAndDestroy(securityPolicyPackage) ; + + iDefaultPolicy=TSettingsAccessPolicy(iDefaultReadPolicy,iDefaultWritePolicy, KUnspecifiedKey); + + aStream >> iDefaultMeta ; + + iRangeMeta.Reset(); + aStream >> iRangeMeta ; + + TInt64 timeStampInt ; + aStream >> timeStampInt ; + iTimeStamp = timeStampInt ; + + iSettings.Reset() ; + aStream >> iSettings ; + + if (version >= KPersistFormatSupportsDeletedSettings) + { + // Deleted Settings + // Deleted settings + aStream >> count ; + for (TInt i=0; i> keyValue ; + iDeletedSettings.InsertInUnsignedKeyOrder(keyValue); + } + } + // Set up access policies + TInt32 numElements = iSettings.Count(); + for (TInt32 count = 0; count < numElements; count++) + { + TServerSetting* setting= &iSettings[count]; + TUint32 key=setting->Key(); + setting->SetAccessPolicy(GetFallbackAccessPolicy(key)); + } + } + +void CSharedRepository::SetAllDefaultMetaFlags() + { + TInt y = iSettings.Count(); + for (TInt i=0; i < iSettings.Count(); i++) + { + TUint32 meta = iSettings.operator[](i).Meta(); + meta |= KMetaDefaultValue; + iSettings.operator[](i).SetMeta(meta); + } + }