sl@0: // Copyright (c) 1998-2010 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 sl@0: #include "UT_STD.H" sl@0: sl@0: //The default media block size, used in the computations, if the file system guarantees atomic "block write" file operations. sl@0: const TInt KDefaultMediaBlockSize = 512; sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: ///////////////////// TPermanentStoreHeader ///////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: //Returns false, if: sl@0: // - the dirty bit is set (indicates commit phase #2 or #3 is not complete); sl@0: // - the CRC check fails ("backup TOC ref", "handle" and "TOC ref" are protected by 16-bit CRC in the permanent store file header); sl@0: TBool TPermanentStoreHeader::IsValid() const sl@0: { sl@0: if (IsDirty()) sl@0: return EFalse; sl@0: // sl@0: TUint16 crc=0; sl@0: Mem::Crc(crc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc)); sl@0: return crc==iCrc; sl@0: } sl@0: sl@0: //Sets the "backup TOC ref", "handle" and "TOC ref" in current TPermanentStoreHeader object. sl@0: //16-bit CRC is calculated, based on the values of the input parameters, and stored together with them in the sl@0: //TPermanentStoreHeader object. sl@0: void TPermanentStoreHeader::Set(TInt aBackupToc,TInt aHandle,TInt aReference) sl@0: { sl@0: iBackup=TUint32(aBackupToc)<<1; sl@0: iHandle=aHandle; sl@0: iRef=aReference; sl@0: iCrc=0; sl@0: Mem::Crc(iCrc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc)); sl@0: __ASSERT_DEBUG(IsValid(),User::Invariant()); sl@0: } sl@0: sl@0: const TInt KTocGranularity=12; sl@0: sl@0: struct STocEntry sl@0: { sl@0: TInt32 handle; sl@0: TInt32 ref; sl@0: }; sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: ///////////////////// CPermanentStoreToc //////////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: TInt CPermanentStoreToc::TEntry::Compare(const TEntry& aLeft, const TEntry& aRight) sl@0: // sl@0: // Ordering for Stream handles, return <0,0,>0 depending on comparison sl@0: // The index part of the handle is only 24 bits, so just subtract the handles for result sl@0: // sl@0: { sl@0: return (aLeft.handle&KMaskHandleIndex) - (aRight.handle&KMaskHandleIndex); sl@0: } sl@0: sl@0: CPermanentStoreToc* CPermanentStoreToc::NewL(TStreamPos aBase,TStreamExchange& aHost,TInt aToc,TInt aBaseReloc) sl@0: { sl@0: CPermanentStoreToc* table=new(ELeave) CPermanentStoreToc(aBase,aHost); sl@0: CleanupStack::PushL(table); sl@0: table->ConstructL(aToc,aBaseReloc); sl@0: CleanupStack::Pop(); sl@0: return table; sl@0: } sl@0: sl@0: TBool CPermanentStoreToc::IsDelta() const sl@0: // sl@0: // Report whether the current TOC should be written as a delta, or a base TOC sl@0: // sl@0: { sl@0: TInt delta = iEntries.Count(); sl@0: if (delta >= KTocDeltaCap) sl@0: return EFalse; sl@0: TInt magic = iMagic + delta; sl@0: if (magic > KMaxTocDeltaMagic) sl@0: return EFalse; sl@0: return magic*KSizeTocDeltaEntry <= iCount*KSizeTocEntry - KSizeTocDeltaExtra; sl@0: } sl@0: sl@0: void CPermanentStoreToc::Move(TInt aToc,TInt anExtent) sl@0: // sl@0: // Final stage of compaction, update to address new TOC sl@0: // sl@0: { sl@0: __ASSERT_DEBUG(anExtent<=iOff&&anExtent>=aToc&&anExtent-(aToc+KOffsetTocHeader)==iExt-iOff,User::Invariant()); sl@0: if (!HasDelta()) sl@0: { sl@0: // Base-toc only, update the base toc position as well sl@0: TInt window=iWindow-iTocExt; sl@0: iTocOff=aToc+KOffsetTocHeader; sl@0: iTocExt=anExtent; sl@0: iWindow=anExtent+window; sl@0: } sl@0: iOff = aToc+KOffsetTocHeader; sl@0: iExt = anExtent; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::RealizeL(TInt aPrimary,TInt anExtent) const sl@0: { sl@0: __ASSERT_DEBUG(IsVirtual(),User::Invariant()); sl@0: RFrame16Buf buf(Base()); sl@0: buf.PushL(); sl@0: const TBool delta = IsDelta(); sl@0: aPrimary &= KMaskStreamIdValue; sl@0: if (delta) sl@0: aPrimary |= KTocDelta; sl@0: STocHead h; sl@0: h.primary=TInt32(aPrimary); sl@0: h.avail=TInt32(iAvail); sl@0: h.count=TUint32(iCount); sl@0: // sl@0: TInt off; sl@0: if (delta) sl@0: off=DeltaL(buf,anExtent,h); sl@0: else sl@0: off=RewriteL(buf,anExtent,h); sl@0: // sl@0: buf.SynchL(); sl@0: CleanupStack::PopAndDestroy(&buf); sl@0: return off-KOffsetTocHeader; sl@0: } sl@0: sl@0: void CPermanentStoreToc::Adopt(TInt aToc,TInt aPrimary) sl@0: // sl@0: // Final stage of Commit - all file changes have been successful sl@0: // Record the new Toc location and reset the changed flag sl@0: // sl@0: { sl@0: __ASSERT_DEBUG(aToc+KOffsetTocHeader>=iExt,User::Invariant()); sl@0: sl@0: if (IsDelta()) sl@0: { sl@0: // Adjust the TOC-delta location sl@0: TInt c = iEntries.Count(); sl@0: iOff=aToc+KOffsetTocHeader; sl@0: iExt=aToc + KSizeTocDeltaExtra + KSizeTocDeltaEntry*c; sl@0: if (c > KTocDeltaMagic) sl@0: iMagic += c - KTocDeltaMagic; sl@0: } sl@0: else sl@0: { sl@0: // Adjust all the TOC data and reset the delta-set sl@0: TInt window = iTocOff>=0 ? iWindow-iTocOff : -KOffsetTocHeader; sl@0: iTocOff=iOff=aToc+KOffsetTocHeader; sl@0: iTocExt=iExt=aToc+KSizeTocEntry*iCount; sl@0: iWindow = iTocOff + window; sl@0: iEntries.Reset(); sl@0: iMagic = 0; sl@0: } sl@0: iPrimary=aPrimary; sl@0: __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::AllocL(TInt anOffset) sl@0: { sl@0: TEntry& entry=DoAllocL(); sl@0: entry.ref=anOffset; sl@0: return entry.handle; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::AllocL() sl@0: { sl@0: TEntry& entry=DoAllocL(); sl@0: entry.ref=KFrameNonexistent16; sl@0: return entry.handle|=KHandleInvalid; sl@0: } sl@0: sl@0: void CPermanentStoreToc::Cancel(TInt aHandle) sl@0: { sl@0: __ASSERT_DEBUG(aHandle<0&&IsVirtual(),User::Invariant()); sl@0: TEntry* entry=Entry(aHandle); sl@0: __ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant()); sl@0: entry->ref=iAvail; sl@0: iAvail=aHandle; sl@0: } sl@0: sl@0: void CPermanentStoreToc::FreeL(TInt aHandle) sl@0: { sl@0: __ASSERT_DEBUG(aHandle>0,User::Invariant()); sl@0: TEntry& entry=FetchL(aHandle); sl@0: aHandle|=KHandleInvalid; sl@0: entry.handle=aHandle; sl@0: entry.ref=iAvail; sl@0: iAvail=aHandle; sl@0: Changed(); sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::AtL(TInt aHandle) const sl@0: { sl@0: __ASSERT_DEBUG(aHandle>0,User::Invariant()); sl@0: sl@0: // check the delta table sl@0: const TEntry* entry=Entry(aHandle); sl@0: if (entry!=NULL ) sl@0: { sl@0: // if there is an entry in the delta table, but its sl@0: // not an exact match leave with KErrNotFound sl@0: // This happens when the delta indicates it's been deleted sl@0: if (entry->handle!=aHandle) sl@0: User::Leave(KErrNotFound); sl@0: sl@0: return entry->ref; sl@0: } sl@0: // sl@0: // if it's not in the delta table check the base TOC sl@0: return DoAtL(aHandle); sl@0: } sl@0: sl@0: void CPermanentStoreToc::PutL(TInt aHandle, TInt anOffset, TPut aCheck) sl@0: // sl@0: // Used by compaction to update a single stream reference IN-PLACE in the TOC sl@0: // We need to check in which TOC to make the update sl@0: // sl@0: { sl@0: __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); sl@0: __ASSERT_DEBUG(aHandle>0,User::Invariant()); sl@0: __ASSERT_DEBUG(aHandle!=KHandleTocBase || HasDelta(),User::Invariant()); sl@0: sl@0: if (aHandle == KHandleTocBase) sl@0: { sl@0: // update the TOC-base link sl@0: PutTocL(anOffset - KOffsetTocHeader, aCheck); sl@0: if (iTocOff != anOffset) sl@0: { sl@0: TInt size = iTocExt - iTocOff; sl@0: TInt window = iWindow - iTocOff; sl@0: iTocOff = anOffset; sl@0: iTocExt = anOffset + size; sl@0: iWindow = anOffset + window; sl@0: } sl@0: return; sl@0: } sl@0: sl@0: TEntry e; sl@0: e.handle=aHandle; sl@0: TInt i; sl@0: if (iEntries.FindInOrder(e,i,&TEntry::Compare)==0) sl@0: { sl@0: // update TOC-delta entry sl@0: TEntry& entry=iEntries[i]; sl@0: if (entry.handle==aHandle && entry.ref != anOffset) sl@0: { sl@0: PutDeltaL(i,aHandle,anOffset); sl@0: entry.ref=anOffset; sl@0: } sl@0: return; sl@0: } sl@0: sl@0: // update TOC-base entry sl@0: if (aCheck==EWrite || DoAtL(aHandle)!=anOffset) sl@0: PutBaseL(aHandle,anOffset); sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::GetL(TInt aHandle) sl@0: { sl@0: __ASSERT_DEBUG(aHandle>0,User::Invariant()); sl@0: TEntry& entry=FetchL(aHandle); sl@0: return entry.ref; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::Set(TInt aHandle,TInt anOffset) sl@0: { sl@0: __ASSERT_DEBUG(aHandle!=0,User::Invariant()); sl@0: TEntry* entry=Entry(aHandle); sl@0: __ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant()); sl@0: aHandle&=KMaskStreamIdValue; sl@0: entry->handle=aHandle; sl@0: entry->ref=anOffset; sl@0: Changed(); sl@0: return aHandle; sl@0: } sl@0: sl@0: CPermanentStoreToc::CPermanentStoreToc(TStreamPos aBase,TStreamExchange& aHost) sl@0: : /*iPrimary(0),iAvail(0),iCount(0),*/iEntries(KTocGranularity), sl@0: iBase(aBase),iHost(&aHost),iOff(KFrameNonexistent16)/*,iExt(0)*/, sl@0: iTocOff(KFrameNonexistent16)/*,iTocExt(0),iWindow(0)*/ sl@0: {} sl@0: sl@0: CPermanentStoreToc::~CPermanentStoreToc() sl@0: { sl@0: iEntries.Close(); sl@0: } sl@0: sl@0: void CPermanentStoreToc::ConstructL(TInt aToc,TInt aBaseReloc) sl@0: // sl@0: // Read and validate the TOC header at aToc. sl@0: // aBaseReloc may contain a relocated TOC-base offset, which should override the one in a TOC-delta sl@0: // sl@0: { sl@0: if (aToc==0) sl@0: return; sl@0: __ASSERT_DEBUG(aToc>0&&iEntries.Count()==0,User::Invariant()); sl@0: RFrame16Buf buf(Base()); sl@0: aToc+=KOffsetTocHeader; sl@0: buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead); sl@0: buf.PushL(); sl@0: RReadStream stream(&buf); sl@0: STocHead h; sl@0: stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency sl@0: if ((h.primary&~(KMaskStreamIdValue|static_cast(KTocDelta)))!=0|| sl@0: h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0) sl@0: __LEAVE(KErrCorrupt); sl@0: // sl@0: iPrimary=TInt(h.primary) & ~KTocDelta; sl@0: iAvail=TInt(h.avail); sl@0: iCount=TInt(h.count); sl@0: iOff = aToc; sl@0: // sl@0: if (h.primary & KTocDelta) sl@0: { sl@0: // This is a TOC-delta sl@0: aToc = InternalizeL(stream, aBaseReloc) + KOffsetTocHeader; sl@0: sl@0: // Now locate and validate the base TOC sl@0: buf.Release(); sl@0: buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead); sl@0: stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency sl@0: if ((h.primary&~KMaskStreamIdValue)!=0||(h.count&~KMaskHandleIndex)!=0|| sl@0: h.avail>0||(h.avail&KMaskHandleClear)!=0||TInt(h.count)>iCount) sl@0: __LEAVE(KErrCorrupt); sl@0: } sl@0: TInt size = KSizeTocEntry*TInt(h.count); sl@0: // Eagerly load the first few TOC entries into the cache sl@0: // This is good as it almost certainly lies in the file buffer already sl@0: if (size>0) sl@0: stream.ReadL(&iBuf[0],Min(size,TInt(KSizeTocBuf))); sl@0: size-=KOffsetTocHeader; sl@0: if (buf.SizeL()!=size) sl@0: __LEAVE(KErrCorrupt); sl@0: iTocOff=aToc; sl@0: iWindow=aToc-KOffsetTocHeader; sl@0: iTocExt=aToc+size; sl@0: if (iExt == 0) sl@0: iExt = iTocExt; // set extent for non-delta TOC sl@0: // sl@0: CleanupStack::PopAndDestroy(&buf); sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::InternalizeL(RReadStream& aIn, TInt aBaseReloc) sl@0: // sl@0: // Load and validate the delta-toc table sl@0: // sl@0: { sl@0: TInt tocoff = aIn.ReadInt32L(); sl@0: if (aBaseReloc != KFrameNonexistent16) sl@0: tocoff = aBaseReloc - KOffsetTocHeader; sl@0: if (tocoff<0||tocoff>=iOff) sl@0: __LEAVE(KErrCorrupt); sl@0: iMagic = aIn.ReadUint16L(); sl@0: if (!IsDelta()) sl@0: __LEAVE(KErrCorrupt); sl@0: TInt n = aIn.ReadUint8L(); sl@0: TInt size = -KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry * n; sl@0: if (aIn.Source()->SizeL()!=size) sl@0: __LEAVE(KErrCorrupt); sl@0: iExt = iOff + size; sl@0: TInt last = 0; sl@0: while (--n >= 0) sl@0: { sl@0: STocEntry e; sl@0: aIn.ReadL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency sl@0: if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref0||(e.ref&KMaskHandleClear)!=0)) sl@0: __LEAVE(KErrCorrupt); sl@0: TInt i = e.handle&KMaskHandleIndex; sl@0: if (i <= last || i > iCount) sl@0: __LEAVE(KErrCorrupt); sl@0: last = i; sl@0: TEntry entry; sl@0: entry.handle = TInt(e.handle); sl@0: entry.ref = TInt(e.ref); sl@0: User::LeaveIfError(iEntries.Append(entry)); sl@0: } sl@0: sl@0: return tocoff; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::DeltaL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const sl@0: // sl@0: // Write the TOC-delta sl@0: // sl@0: { sl@0: TInt off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite); sl@0: RWriteStream out(&aBuf); sl@0: out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency sl@0: out.WriteInt32L(iTocOff - KOffsetTocHeader); sl@0: TInt n = iEntries.Count(); sl@0: TInt magic = iMagic; sl@0: if (n > KTocDeltaMagic) sl@0: magic += n - KTocDeltaMagic; sl@0: __ASSERT_DEBUG(magic <= KMaxTocDeltaMagic,User::Invariant()); sl@0: __ASSERT_DEBUG(n <= (TInt)KMaxTUint8, User::Invariant()); sl@0: out.WriteUint16L(magic); sl@0: out.WriteUint8L(n); sl@0: for (int i = 0; i < n; ++i) sl@0: { sl@0: const TEntry& entry = iEntries[i]; sl@0: STocEntry e; sl@0: e.handle=TInt32(entry.handle); sl@0: e.ref=TInt32(entry.ref); sl@0: out.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency sl@0: } sl@0: return off; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::RewriteL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const sl@0: // sl@0: // Write the TOC-base sl@0: // sl@0: { sl@0: const TInt KElementsRewrite=304; sl@0: const TInt KSizeRewriteBuf=KElementsRewrite*KSizeTocEntry; sl@0: TUint8 toc[KSizeRewriteBuf]; sl@0: sl@0: RFrame16Buf buf(Base()); sl@0: buf.PushL(); sl@0: TInt oldsize = 0; sl@0: TInt window = 0; sl@0: if (iTocOff>=0) sl@0: { sl@0: oldsize = iTocExt-iTocOff+KOffsetTocHeader; sl@0: window = iWindow + KOffsetTocHeader - iTocOff; sl@0: if (oldsize <= Min(KSizeTocBuf,KSizeRewriteBuf) && window == 0) sl@0: { sl@0: // the old TOC is already in the toc-base cache, no need to read it sl@0: Mem::Copy(&toc[0],&iBuf[0],oldsize); sl@0: oldsize = 0; // this prevents the read request sl@0: } sl@0: else sl@0: { sl@0: buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead); sl@0: buf.SeekL(buf.ERead,TStreamPos(-KOffsetTocHeader)); sl@0: } sl@0: } sl@0: RReadStream in(&buf); sl@0: // defer the initialisation of the write in roder to improve file buffering performance sl@0: RWriteStream out(&aBuf); sl@0: TInt off=-1; sl@0: TInt size=iCount*KSizeTocEntry; sl@0: sl@0: for (TInt base=0,delta=0;base=0,User::Invariant()); sl@0: if (pos>=KSizeRewriteBuf) sl@0: break; sl@0: STocEntry e; sl@0: e.handle=TInt32(entry.handle); sl@0: e.ref=TInt32(entry.ref); sl@0: Mem::Copy(&toc[pos],(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); sl@0: pos += base - window; sl@0: if (TUint(pos) < TUint(KSizeTocBuf)) sl@0: Mem::Copy(MUTABLE_CAST(TUint8*,&iBuf[pos]),(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); sl@0: } sl@0: // write buffer to file sl@0: if (off == -1) sl@0: { sl@0: // initialise writing sl@0: off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite); sl@0: out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency sl@0: } sl@0: out.WriteL(&toc[0],Min(KSizeRewriteBuf,size-base)); sl@0: } sl@0: // sl@0: CleanupStack::PopAndDestroy(&buf); sl@0: return off; sl@0: } sl@0: sl@0: CPermanentStoreToc::TEntry* CPermanentStoreToc::Entry(TInt aHandle) sl@0: { sl@0: TEntry e; sl@0: e.handle=aHandle; sl@0: TInt i; sl@0: if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0) sl@0: return NULL; sl@0: // sl@0: TEntry& entry=iEntries[i]; sl@0: sl@0: // allow entry to return a pointer even if not an exact match sl@0: return &entry; sl@0: } sl@0: sl@0: CPermanentStoreToc::TEntry& CPermanentStoreToc::FetchL(TInt aHandle) sl@0: { sl@0: TEntry e; sl@0: e.handle=aHandle; sl@0: TInt i; sl@0: if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0) sl@0: { sl@0: e.ref=DoAtL(aHandle); sl@0: User::LeaveIfError(iEntries.Insert(e,i)); sl@0: } sl@0: TEntry& entry=iEntries[i]; sl@0: if (entry.handle!=aHandle) sl@0: __LEAVE(KErrNotFound); sl@0: // sl@0: return entry; sl@0: } sl@0: sl@0: CPermanentStoreToc::TEntry& CPermanentStoreToc::DoAllocL() sl@0: { sl@0: TInt handle=iAvail; sl@0: TEntry* entry; sl@0: if (handle==0) sl@0: { sl@0: __ASSERT_DEBUG(iEntries.Count()==0||(iEntries[iEntries.Count()-1].handle&KMaskHandleIndex)<=iCount,User::Invariant()); sl@0: User::LeaveIfError(iEntries.Append(TEntry())); sl@0: entry=&iEntries[iEntries.Count()-1]; sl@0: handle=++iCount; sl@0: } sl@0: else sl@0: { sl@0: entry=&FetchL(handle); sl@0: handle=(handle+KIncHandleGen)&KMaskStreamIdValue; sl@0: // sl@0: TInt avail=entry->ref; sl@0: if (avail>0||(avail&KMaskHandleClear)!=0) sl@0: __LEAVE(KErrCorrupt); sl@0: // sl@0: iAvail=avail; sl@0: } sl@0: entry->handle=handle; sl@0: Changed(); sl@0: return *entry; sl@0: } sl@0: sl@0: TInt CPermanentStoreToc::DoAtL(TInt aHandle) const sl@0: { sl@0: TInt off=iTocOff; sl@0: if (off<0) sl@0: __LEAVE(KErrNotFound); sl@0: // sl@0: off+=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex); sl@0: __ASSERT_DEBUG(off>=iTocOff-KOffsetTocHeader,User::Invariant()); sl@0: if (off>=iTocExt) sl@0: __LEAVE(KErrNotFound); sl@0: // sl@0: TInt window=iWindow; sl@0: if (off-window<0||off-window>=KSizeTocBuf) sl@0: { sl@0: TInt len=iTocOff-KOffsetTocHeader; sl@0: window=Max(len,Min(off-KBackTocBuf,iTocExt-KSizeTocBuf)); sl@0: len=Min(iTocExt-len,TInt(KSizeTocBuf)); sl@0: // sl@0: RFrame16Buf buf(Base()); sl@0: buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead); sl@0: buf.PushL(); sl@0: buf.SeekL(buf.ERead,TStreamPos(window-iTocOff)); sl@0: RReadStream stream(&buf); sl@0: MUTABLE_CAST(TInt&,iWindow)=iTocExt; sl@0: stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[0]),1); // assume half decent read-ahead buffering sl@0: stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[1]),len-1); sl@0: MUTABLE_CAST(TInt&,iWindow)=window; sl@0: CleanupStack::PopAndDestroy(); sl@0: } sl@0: STocEntry e; sl@0: e.handle=aHandle; sl@0: Mem::Copy((TUint8*)&e+KSizeHandleIndex,&iBuf[off-window],KSizeTocEntry); // platform dependency sl@0: if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref0||(e.ref&KMaskHandleClear)!=0)) sl@0: __LEAVE(KErrCorrupt); sl@0: // sl@0: if (TInt(e.handle)!=aHandle) sl@0: __LEAVE(KErrNotFound); sl@0: // sl@0: return TInt(e.ref); sl@0: } sl@0: sl@0: void CPermanentStoreToc::PutBaseL(TInt aHandle,TInt aReference) sl@0: // sl@0: // Update a TOC-base entry sl@0: // sl@0: { sl@0: __ASSERT_DEBUG(iTocOff>=0,User::Invariant()); sl@0: sl@0: TInt pos=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex); sl@0: RFrame16Buf buf(Base()); sl@0: buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.EWrite); sl@0: buf.PushL(); sl@0: buf.SeekL(buf.EWrite,TStreamPos(pos)); sl@0: RWriteStream stream(&buf); sl@0: STocEntry e; sl@0: e.handle=aHandle; sl@0: e.ref=aReference; sl@0: stream.WriteL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency sl@0: CleanupStack::PopAndDestroy(); sl@0: TInt off=pos+iTocOff-iWindow; sl@0: if (off>=0&&off=0,User::Invariant()); sl@0: RFrame16Buf buf(Base()); sl@0: buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite); sl@0: buf.PushL(); sl@0: buf.SeekL(buf.EWrite,TStreamPos(-KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*aPos)); sl@0: RWriteStream stream(&buf); sl@0: STocEntry e; sl@0: e.handle=aHandle; sl@0: e.ref=aReference; sl@0: stream.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency sl@0: CleanupStack::PopAndDestroy(); sl@0: } sl@0: sl@0: void CPermanentStoreToc::PutTocL(TInt aTocBase, TPut aCheck) sl@0: // sl@0: // Update the base TOC link in the TOC-delta sl@0: // sl@0: { sl@0: __ASSERT_DEBUG(HasDelta(),User::Invariant()); sl@0: __ASSERT_DEBUG(iOff>=0,User::Invariant()); sl@0: RFrame16Buf buf(Base()); sl@0: buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite|buf.ERead); sl@0: buf.PushL(); sl@0: buf.SeekL(buf.EWrite|buf.ERead,TStreamPos(-KOffsetTocHeader)); sl@0: RReadStream in(&buf); sl@0: if (aCheck==EWrite || in.ReadInt32L() != aTocBase) sl@0: { sl@0: RWriteStream out(&buf); sl@0: out.WriteInt32L(aTocBase); sl@0: } sl@0: CleanupStack::PopAndDestroy(); sl@0: } sl@0: sl@0: // Used prior to PutL to determine exactly where in the file the TOC update will be written. sl@0: // This may be used to eliminate the pre-put write of the relocation information in the file sl@0: // header in situations where the write is entirely within an atomic file block. sl@0: // sl@0: // The detailed numbers used to offset into the TOCs are dependant on the TOC formats, as sl@0: // implicated by other members of this class sl@0: TInt CPermanentStoreToc::RefSpan(TInt aHandle,TInt& aLength) sl@0: { sl@0: __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); sl@0: __ASSERT_DEBUG(aHandle>0,User::Invariant()); sl@0: sl@0: if (aHandle == KHandleTocBase) sl@0: { // locate the TOC-base link sl@0: __ASSERT_DEBUG(HasDelta(),User::Invariant()); sl@0: __ASSERT_DEBUG(iOff>=0,User::Invariant()); sl@0: aLength = sizeof(TInt32); sl@0: return RFrame16Buf::Position(Base(), iOff - KOffsetTocHeader).Offset(); sl@0: } sl@0: sl@0: TEntry e; sl@0: e.handle=aHandle; sl@0: TInt ix; sl@0: if (iEntries.FindInOrder(e, ix, &TEntry::Compare) == 0) sl@0: { // locate TOC-delta entry sl@0: TEntry& entry=iEntries[ix]; sl@0: if (entry.handle==aHandle) sl@0: { sl@0: __ASSERT_DEBUG(HasDelta(),User::Invariant()); sl@0: __ASSERT_DEBUG(iOff>=0,User::Invariant()); sl@0: aLength = KSizeTocDeltaEntry; sl@0: return RFrame16Buf::Position(Base(),iOff - KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*ix).Offset(); sl@0: } sl@0: } sl@0: sl@0: // locate the TOC-base entry sl@0: __ASSERT_DEBUG(iTocOff>=0,User::Invariant()); sl@0: aLength = KSizeTocEntry; sl@0: return RFrame16Buf::Position(Base(),iTocOff-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex)).Offset(); sl@0: } sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: ///////////////////// RPermanentStoreTocIter //////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: RPermanentStoreTocIter::RPermanentStoreTocIter(const CPermanentStoreToc& aTable) sl@0: : iTable(aTable), iBuf(aTable.Base()), iDelta(0), iDeltaEnd(0) sl@0: { sl@0: TInt off=aTable.iTocOff; sl@0: __ASSERT_DEBUG(off>=KFrameNonexistent16,User::Invariant()); sl@0: TInt ext=off<0?off:aTable.iTocExt; sl@0: iBuf.Set(aTable.Host(),off,ext,EFrameDescriptive16|iBuf.ERead); sl@0: } sl@0: sl@0: void RPermanentStoreTocIter::Release() sl@0: { sl@0: iBuf.Release(); sl@0: } sl@0: sl@0: void RPermanentStoreTocIter::ResetL() sl@0: { sl@0: iNext = iIndex = 1; sl@0: if (iBuf.Offset()<0) sl@0: { sl@0: iCount=0; sl@0: return; sl@0: } sl@0: // sl@0: RReadStream stream(&iBuf); sl@0: STocHead h; sl@0: stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency sl@0: if (h.primary<0||(h.primary&~KMaskStreamIdValue)!=0|| sl@0: h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0) sl@0: __LEAVE(KErrCorrupt); sl@0: // sl@0: const CPermanentStoreToc& table=iTable; sl@0: if (table.HasDelta()) sl@0: { sl@0: TInt c = table.iEntries.Count(); sl@0: if (c) sl@0: { sl@0: iDelta = &table.iEntries[0]; sl@0: iDeltaEnd = iDelta + c; sl@0: } sl@0: iIndex = 0; sl@0: } sl@0: iCount = iTable.iCount; sl@0: } sl@0: sl@0: TBool RPermanentStoreTocIter::NextL(TEntry& anEntry) sl@0: { sl@0: TInt i=iIndex; sl@0: __ASSERT_DEBUG(iCount>=0,User::Invariant()); sl@0: if (i == 0) sl@0: { sl@0: // report TOC-base as a 'stream' sl@0: anEntry.handle = KHandleTocBase; sl@0: anEntry.ref = iTable.iTocOff; sl@0: iIndex = 1; sl@0: return ETrue; sl@0: } sl@0: sl@0: __ASSERT_DEBUG(i>0,User::Invariant()); sl@0: if (i>iCount) sl@0: return EFalse; sl@0: // sl@0: const TEntry* d = iDelta; sl@0: if (d != iDeltaEnd && (d->handle&KMaskHandleIndex) == i) sl@0: { sl@0: anEntry = *d; sl@0: iDelta = d+1; sl@0: iIndex = i+1; sl@0: return ETrue; sl@0: } sl@0: // sl@0: RReadStream stream(&iBuf); sl@0: TInt skip = i - iNext; sl@0: if (skip > 0) sl@0: stream.ReadL(KSizeTocEntry * skip); sl@0: STocEntry e; sl@0: e.handle=i; sl@0: stream.ReadL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency sl@0: if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&e.ref0||(e.ref&KMaskHandleClear)!=0)) sl@0: __LEAVE(KErrCorrupt); sl@0: // sl@0: iNext = iIndex = i+1; sl@0: anEntry.handle=TInt(e.handle); sl@0: anEntry.ref=TInt(e.ref); sl@0: return ETrue; sl@0: } sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: ///////////////////// TPermanentStoreCache ////////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: const TPermanentStoreCache::TItem* TPermanentStoreCache::At(TInt aHandle) const sl@0: { sl@0: const TItem* item=&iItems[0]; sl@0: TInt i=1; sl@0: TInt bit=1; sl@0: while (item->handle!=aHandle) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: TInt step=(aHandle&bit)?i+1:i; sl@0: bit<<=1; sl@0: i+=step; sl@0: if (i>EItems) sl@0: return NULL; sl@0: // sl@0: item+=step; sl@0: } sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: return item; sl@0: } sl@0: sl@0: void TPermanentStoreCache::Relocated(TInt aHandle,TInt anOffset) sl@0: { sl@0: const TItem* item=At(aHandle); sl@0: if (item) sl@0: { sl@0: // update the cache item with the new offset sl@0: // As 'extent' may only be a partial extent of a fragmented frame sl@0: // which is no longer fragmented, we cannot simply shift this by the same sl@0: // amount as 'offset'. Simplest to reset this to 'unknown', i.e. 0 sl@0: const_cast(item)->offset = anOffset; sl@0: const_cast(item)->extent = 0; sl@0: } sl@0: } sl@0: sl@0: void TPermanentStoreCache::Put(const TItem* anItem,TInt anOffset,TInt anExtent) sl@0: { sl@0: TInt handle=anItem->handle; sl@0: TItem* item=&iItems[0]; sl@0: TInt i=1; sl@0: TInt bit=1; sl@0: while (item != anItem) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: TInt step=(handle&bit)?i+1:i; sl@0: bit<<=1; sl@0: i+=step; sl@0: __ASSERT_DEBUG(i<=EItems,User::Invariant()); sl@0: item+=step; sl@0: } sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: sl@0: for(;;) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: TInt step=(handle&bit)?i+1:i; sl@0: bit<<=1; sl@0: i+=step; sl@0: if (i>EItems) sl@0: break; sl@0: // sl@0: TItem* hole=item; sl@0: item+=step; sl@0: *hole=*item; sl@0: }; sl@0: item->handle=handle; sl@0: item->offset=anOffset; sl@0: item->extent=anExtent; sl@0: __ASSERT_DEBUG(At(handle)==item,User::Invariant()); sl@0: } sl@0: sl@0: void TPermanentStoreCache::Add(TInt aHandle,TInt anOffset,TInt anExtent) sl@0: { sl@0: TItem* item=&iItems[0]; sl@0: TInt i=1; sl@0: TInt bit=1; sl@0: for(;;) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1]&&item->handle!=aHandle,User::Invariant()); sl@0: TInt step=(aHandle&bit)?i+1:i; sl@0: bit<<=1; sl@0: i+=step; sl@0: if (i>EItems) sl@0: break; sl@0: // sl@0: TItem* hole=item; sl@0: item+=step; sl@0: *hole=*item; sl@0: }; sl@0: item->handle=aHandle; sl@0: item->offset=anOffset; sl@0: item->extent=anExtent; sl@0: __ASSERT_DEBUG(At(aHandle)==item,User::Invariant()); sl@0: } sl@0: sl@0: void TPermanentStoreCache::Remove(TInt aHandle) sl@0: { sl@0: TItem* item=&iItems[0]; sl@0: TInt i=1; sl@0: TInt bit=1; sl@0: while (item->handle!=aHandle) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: TInt step=(aHandle&bit)?i+1:i; sl@0: bit<<=1; sl@0: i+=step; sl@0: if (i>EItems) sl@0: return; sl@0: // sl@0: item+=step; sl@0: } sl@0: TItem* hole=item; sl@0: TInt mask=bit-1; sl@0: while (item!=&iItems[0]) sl@0: { sl@0: __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); sl@0: TInt step=i; sl@0: bit>>=1; sl@0: i>>=1; sl@0: step-=i; sl@0: item-=step; sl@0: // sl@0: if (((aHandle^item->handle)&mask)==0) sl@0: { sl@0: *hole=*item; sl@0: hole=item; sl@0: mask=bit-1; sl@0: } sl@0: } sl@0: hole->handle=0; sl@0: __ASSERT_DEBUG(i==1&&At(aHandle)==NULL,User::Invariant()); sl@0: } sl@0: sl@0: void TPermanentStoreCache::Invalidate() sl@0: { sl@0: for (TItem* item=&iItems[0],*end=item+EItems;itemhandle=0; sl@0: } sl@0: sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: ///////////////////// CPermanentStoreCoord ////////////////////////////////////////////////////// sl@0: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: //Returns the file system type, according to what the "file write" operation guarantees. sl@0: //The return result could be one of: sl@0: // - ESimple - "single byte write" operations are atomic; sl@0: // - EBlockAtomic - "block/sector write" operations are atomic; sl@0: // - ETransactional - transactional file system; sl@0: CPermanentStoreCoord::TFileQoS CPermanentStoreCoord::FileQoSL() sl@0: { sl@0: //Uncomment, if you want FileQoSL() to return always EBlockAtomic/EBlockAtomic. sl@0: //iFileQos = ETransactional; sl@0: //iFileQos = EBlockAtomic; sl@0: //return iFileQos; sl@0: // sl@0: if (iFileQos == EUnknown) //get the file sytem type, if iFileQos is not set yet sl@0: { sl@0: TStreamExchange &se = Host(); sl@0: RFileBuf *sb = static_cast(se.HostL()); sl@0: RFile &f = sb->File(); sl@0: sl@0: TInt dn; sl@0: TDriveInfo di; sl@0: User::LeaveIfError(f.Drive(dn, di)); sl@0: sl@0: iFileQos = (di.iDriveAtt & KDriveAttTransaction) ? ETransactional : ESimple; sl@0: sl@0: if(iFileQos == ESimple && IsBlockAtomicL(dn)) sl@0: { sl@0: iFileQos = EBlockAtomic; sl@0: } sl@0: } sl@0: return iFileQos; sl@0: } sl@0: sl@0: //The function returns true, if the file system guarantees atomic "block write" operations on aDriveNo. sl@0: //It is not the most effective implementation at the moment (creates/closes a file session), sl@0: //probably TDriveInfo::iType can be used in a more effective implementation. sl@0: TBool CPermanentStoreCoord::IsBlockAtomicL(TInt aDriveNo) const sl@0: { sl@0: __ASSERT_DEBUG(aDriveNo >= EDriveA && aDriveNo <= EDriveZ, User::Invariant()); sl@0: RFs fs; sl@0: CleanupClosePushL(fs); sl@0: User::LeaveIfError(fs.Connect()); sl@0: sl@0: TVolumeIOParamInfo volInfo; sl@0: TInt err = fs.VolumeIOParam(aDriveNo, volInfo); sl@0: CleanupStack::PopAndDestroy(&fs); sl@0: sl@0: //If VolumeIOParam() succeeds, the media block size is >= 512 bytes and the media block size is power of two - report sl@0: //that the media supports atomic "block write" operations. sl@0: return err == KErrNone && volInfo.iBlockSize >= KDefaultMediaBlockSize && (volInfo.iBlockSize & (volInfo.iBlockSize - 1)) == 0; sl@0: } sl@0: sl@0: TStreamPos CPermanentStoreCoord::LimitL() sl@0: { sl@0: TableL(); sl@0: return RFrame16Buf::Position(Base(),iExt); sl@0: } sl@0: sl@0: TStreamId CPermanentStoreCoord::PrimaryL() sl@0: { sl@0: return TStreamId(TableL().Primary()); sl@0: } sl@0: sl@0: void CPermanentStoreCoord::ChangedL() sl@0: { sl@0: ConsolidateL().Changed(); sl@0: } sl@0: sl@0: TBool CPermanentStoreCoord::CommitL(TStreamId aPrimary) sl@0: { sl@0: __ASSERT_DEBUG(IsTrim(),User::Invariant()); sl@0: if (iExtend!=0) sl@0: __LEAVE(KErrInUse); sl@0: // sl@0: CPermanentStoreToc& table=ConsolidateL(); sl@0: if (!table.IsVirtual()) sl@0: return EFalse; sl@0: // sl@0: iState|=EClip; sl@0: TInt toc=table.RealizeL(aPrimary.Value(),iExt); sl@0: // sl@0: TPermanentStoreHeader header(toc); sl@0: header.SetBackupToc(iToc); sl@0: MStreamBuf* buf=BeginL(header); sl@0: buf->SynchL(); sl@0: // it's done, wrap things up sl@0: Host().Share(buf); sl@0: iToc=toc; sl@0: table.Adopt(toc,aPrimary.Value()); sl@0: ++iGen; sl@0: iState=EBackup; sl@0: iExt=table.Extent(); sl@0: return ETrue; sl@0: } sl@0: sl@0: TBool CPermanentStoreCoord::RevertL(TStreamId& aPrimary) sl@0: { sl@0: __ASSERT_ALWAYS(iAccess==0,Panic(EStoreInUse)); sl@0: // can't revert things under people's feet sl@0: CPermanentStoreToc* table=iTable; sl@0: iTable=NULL; sl@0: iCache.Invalidate(); sl@0: if (table==NULL||!table->IsVirtual()) sl@0: { sl@0: __ASSERT_DEBUG(table==NULL||aPrimary==TStreamId(table->Primary()),User::Invariant()); sl@0: delete table; sl@0: return EFalse; sl@0: } sl@0: // sl@0: aPrimary=TStreamId(table->Primary()); sl@0: delete table; sl@0: iState|=EClip; sl@0: // sl@0: TStreamExchange& host=Host(); sl@0: MStreamBuf* buf=host.HostL(); sl@0: host.Release(); sl@0: buf->SynchL(); sl@0: host.Share(buf); sl@0: return ETrue; sl@0: } sl@0: sl@0: TStreamId CPermanentStoreCoord::ExtendL() sl@0: { sl@0: return TStreamId(ConsolidateL().AllocL(KFrameNonexistent16)); sl@0: } sl@0: sl@0: void CPermanentStoreCoord::DeleteL(TStreamId anId) sl@0: { sl@0: TInt handle=anId.Value(); sl@0: ConsolidateL().FreeL(handle); sl@0: iCache.Remove(handle); sl@0: } sl@0: sl@0: CPermanentStoreCoord::CPermanentStoreCoord(TStreamPos aBase,TStreamExchange& aHost) sl@0: : iBase(aBase),iHost(&aHost), iFileQos(EUnknown) sl@0: {} sl@0: sl@0: // sl@0: // Read and analyse the store header. sl@0: // The whole header (14 bytes) is read from the file and: sl@0: // - If the dirty bit is set, the backup TOC will be used; sl@0: // - If the dirty bit is not set, and the backup TOC ref is not the same as the TOC ref, sl@0: // then it means the the backup TOC ref has not been written successfully, so the TOC ref will be used; sl@0: void CPermanentStoreCoord::InternalizeL(RReadStream& aStream) sl@0: { sl@0: if (iTable!=NULL) sl@0: __LEAVE(KErrNotReady); sl@0: // sl@0: iState=EClip; sl@0: TPermanentStoreHeader header; sl@0: aStream.ReadL(header.Ptr(),KPermanentStoreHeaderLength); sl@0: // determine where the toc lives sl@0: TInt toc=header.BackupToc(); sl@0: if (header.IsDirty()) sl@0: { sl@0: iReloc=0; sl@0: iTarget=0; sl@0: } sl@0: else sl@0: { sl@0: TInt handle=header.Handle(); sl@0: TInt ref=header.Reference(); sl@0: if (handle==0&&toc!=ref) sl@0: { // toc pointer not backed up, validate as if it was sl@0: toc=ref; sl@0: iState|=EBackup; sl@0: header.SetBackupToc(toc); sl@0: } sl@0: if (!header.IsValid()) // not a permanent store or damaged beyond recognition sl@0: __LEAVE(KErrNotSupported); sl@0: // sl@0: if (toc<0||((handle&~KMaskStreamIdValue)!=0&&handle!=KHandleTocBase)||ref<0||(handle!=0&&ref>=toc+KOffsetTocHeader)) sl@0: __LEAVE(KErrCorrupt); // integrity compromised sl@0: // sl@0: iReloc=handle; sl@0: iTarget=ref; sl@0: } sl@0: // sl@0: if (iToc!=0 && iToc!=toc) // refresh produced a different toc sl@0: __LEAVE(KErrCorrupt); sl@0: iToc=toc; sl@0: } sl@0: sl@0: CPermanentStoreCoord::~CPermanentStoreCoord() sl@0: { sl@0: __ASSERT_ALWAYS(iRefs==0,Panic(EStoreInUse)); sl@0: delete iTable; sl@0: } sl@0: sl@0: void CPermanentStoreCoord::CanExtendL() sl@0: { sl@0: __ASSERT_DEBUG(IsTrim(),User::Invariant()); sl@0: if (iExtend!=0) sl@0: __LEAVE(KErrInUse); sl@0: // sl@0: ConsolidateL(); sl@0: } sl@0: sl@0: TInt CPermanentStoreCoord::DoCreateL() sl@0: { sl@0: __ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant()); sl@0: TInt handle=Table().AllocL(); sl@0: iExtend=handle; sl@0: Inc(); sl@0: ++iAccess; sl@0: return handle; sl@0: } sl@0: sl@0: void CPermanentStoreCoord::DoReplaceL(TInt aHandle) sl@0: { sl@0: __ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant()); sl@0: TInt off=Table().GetL(aHandle); sl@0: const TItem* item=iCache.At(aHandle); sl@0: __ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant()); sl@0: if (item==NULL) sl@0: iCache.Add(aHandle,off,Min(off,0)); sl@0: iExtend=aHandle; sl@0: Inc(); sl@0: ++iAccess; sl@0: } sl@0: sl@0: TInt CPermanentStoreCoord::DoOpenL(TInt& anOffset,TInt aHandle) sl@0: { sl@0: const TItem* item=iCache.At(aHandle); sl@0: __ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant()); sl@0: TInt off; sl@0: TInt ext; sl@0: if (item==NULL) sl@0: { sl@0: off=TableL().AtL(aHandle); sl@0: if (iReloc==aHandle) sl@0: { sl@0: TInt trg=iTarget; sl@0: if (trg==off) sl@0: iReloc=0; sl@0: else sl@0: off=trg; sl@0: } sl@0: ext=Min(off,0); // ensures ext==off for empty streams sl@0: iCache.Add(aHandle,off,ext); sl@0: } sl@0: else sl@0: { sl@0: off=item->offset; sl@0: ext=item->extent; sl@0: } sl@0: Inc(); sl@0: ++iAccess; sl@0: anOffset=off; sl@0: return ext; sl@0: } sl@0: sl@0: void CPermanentStoreCoord::DoRelease(TInt aHandle,TInt anOffset,TInt anExtent) sl@0: { sl@0: __ASSERT_DEBUG(aHandle!=0,User::Invariant()); sl@0: Dec(); sl@0: --iAccess; sl@0: if (anExtent==0) sl@0: { // failed to commit the extending stream sl@0: __ASSERT_DEBUG(aHandle==iExtend&&IsTrim(),User::Invariant()); sl@0: iState|=EClip; sl@0: iExtend=0; sl@0: if (aHandle<0) sl@0: Table().Cancel(aHandle); sl@0: } sl@0: else sl@0: { sl@0: const TItem* item=iCache.At(aHandle); sl@0: if (item!=NULL&&item->offset==anOffset&&anExtent>item->extent) sl@0: iCache.Put(item,anOffset,anExtent); sl@0: } sl@0: } sl@0: sl@0: TInt CPermanentStoreCoord::DoCommit(TInt aHandle,TInt anOffset,TInt anExtent) sl@0: { sl@0: __ASSERT_DEBUG(aHandle!=0&&aHandle==iExtend&&(anExtent>=iExt||anOffset==anExtent),User::Invariant()); sl@0: aHandle=Table().Set(aHandle,anOffset); sl@0: if (anExtent<0) sl@0: iCache.Remove(aHandle); sl@0: else sl@0: { sl@0: iExt=anExtent; sl@0: const TItem* item=iCache.At(aHandle); sl@0: if (item==NULL) sl@0: iCache.Add(aHandle,anOffset,anExtent); sl@0: else sl@0: iCache.Put(item,anOffset,anExtent); sl@0: } sl@0: iExtend=0; sl@0: return aHandle; sl@0: } sl@0: sl@0: CPermanentStoreToc& CPermanentStoreCoord::TableL() sl@0: { sl@0: CPermanentStoreToc* table=iTable; sl@0: if (table==NULL) sl@0: { sl@0: table=CPermanentStoreToc::NewL(Base(),Host(),iToc,iReloc==KHandleTocBase?iTarget:KFrameNonexistent16); sl@0: iExt=table->Extent(); sl@0: iTable=table; sl@0: } sl@0: return *table; sl@0: } sl@0: sl@0: CPermanentStoreToc& CPermanentStoreCoord::ConsolidateL() sl@0: { sl@0: CPermanentStoreToc& table=TableL(); sl@0: if (iReloc!=0) sl@0: { sl@0: table.PutL(iReloc,iTarget,CPermanentStoreToc::ETestBeforeWrite); sl@0: iCache.Relocated(iReloc,iTarget); sl@0: iReloc=0; sl@0: } sl@0: return table; sl@0: } sl@0: sl@0: //After stream relocation, the stream entry in the TOC has to be updated with the new stream position. sl@0: //If the file system is not transactional or if the file system is "block atomic", but the stream entry is split sl@0: //on a block/sector boundary, then the stream handle will be stored in the permanent file store header, in case sl@0: //if the TOC entry update fails. sl@0: void CPermanentStoreCoord::RelocateL(TInt aHandle,TInt anOffset) sl@0: { sl@0: __ASSERT_DEBUG(!Accessed(),User::Invariant()); sl@0: __ASSERT_DEBUG(iReloc==0,User::Invariant()); sl@0: sl@0: TBool updateStoreHeader = ETrue; sl@0: TFileQoS fileQos = FileQoSL(); sl@0: if(fileQos == ETransactional) sl@0: { sl@0: updateStoreHeader = EFalse; sl@0: } sl@0: else if(fileQos == EBlockAtomic) sl@0: { sl@0: TInt dataLen = 0; sl@0: TInt writePos = iTable->RefSpan(aHandle, dataLen); sl@0: __ASSERT_DEBUG(writePos >= 0 && dataLen > 0, User::Invariant()); sl@0: TInt startSectorAddr = writePos & ~(KDefaultMediaBlockSize - 1); sl@0: TInt endSectorAddr = (writePos + dataLen - 1) & ~(KDefaultMediaBlockSize - 1); sl@0: if(startSectorAddr == endSectorAddr) sl@0: { sl@0: updateStoreHeader = EFalse; sl@0: } sl@0: } sl@0: sl@0: if (updateStoreHeader) sl@0: { sl@0: TPermanentStoreHeader header(iToc,aHandle,anOffset); sl@0: Host().Share(BeginL(header)); sl@0: iReloc=aHandle; sl@0: iTarget=anOffset; sl@0: } sl@0: ++iGen; sl@0: iTable->PutL(aHandle,anOffset,CPermanentStoreToc::EWrite); sl@0: iCache.Relocated(aHandle,anOffset); sl@0: iReloc=0; sl@0: } sl@0: sl@0: void CPermanentStoreCoord::MoveL(TInt aToc,TInt anExtent) sl@0: { sl@0: __ASSERT_DEBUG(iReloc==0,User::Invariant()); sl@0: CPermanentStoreToc& table=Table(); sl@0: TPermanentStoreHeader header(aToc); sl@0: header.SetBackupToc(iToc); sl@0: Host().Share(BeginL(header)); sl@0: // update data structures but defer the write sl@0: iToc=aToc; sl@0: TInt ext=table.Extent(); sl@0: table.Move(aToc,anExtent); sl@0: iState|=EBackup; sl@0: if (iExt==ext) sl@0: { sl@0: iExt=anExtent; sl@0: iState|=EClip; sl@0: } sl@0: } sl@0: sl@0: // sl@0: // Starts a pseudo-atomic update of the permanent file store header. sl@0: // sl@0: // For the effect to be 'atomic', writes need to meet the following requirements: sl@0: // 1. When updating n bytes using a single write, bytes 2 through n remain unchanged unless the first byte also changes. sl@0: // 2. Changes associated with successive write requests happen in strict sequence. sl@0: // 3. Updating a single byte is atomic. sl@0: // sl@0: // Also, a failure to write to a location shall be reported no later than on the next sl@0: // write to that or a different location, or on buffer synchronisation. sl@0: // sl@0: // The preconditions of the operation are: sl@0: // - all stream insert/delete/relocate operations completed, file - updated; sl@0: // - the TOC reference in the file store header points to the current (valid) TOC, which does not include the most recent sl@0: // changes, since the last commit; sl@0: // - the in-memory backup TOC reference updated and made equal to the file stored TOC reference; sl@0: // sl@0: // The procedure consists of 3 "file-write" steps: sl@0: // -1- write the backup TOC ref (4 bytes) to the permanent file store header. sl@0: // if this operation fails, when the store is reopened, the TOC ref will be used; sl@0: // -2- set the dirty bit and write the whole file store header (14 bytes). sl@0: // If this operation fails, but the dirty bit has been successfully set, the backup TOC ref will be used, sl@0: // when the store is reopened; sl@0: // -3- clear the dirty bit (1 byte "file write" op). The commit operation has completed successfully; sl@0: MStreamBuf* CPermanentStoreCoord::BeginL(TPermanentStoreHeader& aHeader) sl@0: { sl@0: __ASSERT_DEBUG(!aHeader.IsDirty() && aHeader.BackupToc() == Toc() && iReloc == 0, User::Invariant()); sl@0: MStreamBuf& buf=*Host().HostL(); sl@0: buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset); sl@0: TFileQoS fileQos = FileQoSL(); sl@0: if (fileQosiHandle=handle; sl@0: CleanupStack::Pop(); sl@0: anId=TStreamId(handle&KMaskStreamIdValue); sl@0: return buf; sl@0: } sl@0: sl@0: HPermanentStoreBuf* HPermanentStoreBuf::ReplaceL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode) sl@0: { sl@0: HPermanentStoreBuf* buf=ExtendLC(aCoord,aMode); sl@0: TInt handle=anId.Value(); sl@0: aCoord.DoReplaceL(handle); sl@0: buf->iHandle=handle; sl@0: CleanupStack::Pop(); sl@0: return buf; sl@0: } sl@0: sl@0: HPermanentStoreBuf* HPermanentStoreBuf::OpenL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode) sl@0: { sl@0: HPermanentStoreBuf* buf=NewLC(aCoord); sl@0: TInt handle=anId.Value(); sl@0: TInt off=KFrameNonexistent16; sl@0: TInt ext=aCoord.DoOpenL(off,handle); sl@0: buf->iHandle=handle; sl@0: if (ext!=0) sl@0: buf->RFrame16Buf::Set(aCoord.Host(),off,ext,EFrameData16|aMode); sl@0: else sl@0: buf->RFrame16Buf::OpenL(aCoord.Host(),off,EFrameData16|aMode); sl@0: CleanupStack::Pop(); sl@0: return buf; sl@0: } sl@0: sl@0: HPermanentStoreBuf::~HPermanentStoreBuf() sl@0: { sl@0: TInt handle=iHandle; sl@0: if (handle!=0) sl@0: Coord().DoRelease(handle,Offset(),Extent()); sl@0: RFrame16Buf::DoRelease(); sl@0: } sl@0: sl@0: HPermanentStoreBuf* HPermanentStoreBuf::NewLC(CPermanentStoreCoord& aCoord) sl@0: { sl@0: HPermanentStoreBuf* buf=new(ELeave) HPermanentStoreBuf(aCoord); sl@0: buf->PushL(); sl@0: return buf; sl@0: } sl@0: sl@0: HPermanentStoreBuf* HPermanentStoreBuf::ExtendLC(CPermanentStoreCoord& aCoord,TInt aMode) sl@0: { sl@0: aCoord.CanExtendL(); sl@0: HPermanentStoreBuf* buf=NewLC(aCoord); sl@0: buf->RFrame16Buf::ExtendL(aCoord.Host(),aCoord.iExt,EFrameData16|aMode); sl@0: __ASSERT_DEBUG(TStreamPos(aCoord.Host().SizeL())==buf->Position(buf->Offset()),User::Invariant()); sl@0: return buf; sl@0: } sl@0: sl@0: void HPermanentStoreBuf::DoRelease() sl@0: { sl@0: delete this; sl@0: } sl@0: sl@0: void HPermanentStoreBuf::DoSynchL() sl@0: { sl@0: __ASSERT_DEBUG(iHandle!=0,User::Invariant()); sl@0: if (IsCommitted()) sl@0: return; sl@0: // sl@0: CommitL(); sl@0: iHandle=Coord().DoCommit(iHandle,Offset(),Extent()); sl@0: } sl@0: