|
1 // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <s32file.h> |
|
17 #include "UT_STD.H" |
|
18 |
|
19 //The default media block size, used in the computations, if the file system guarantees atomic "block write" file operations. |
|
20 const TInt KDefaultMediaBlockSize = 512; |
|
21 |
|
22 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
23 ///////////////////// TPermanentStoreHeader ///////////////////////////////////////////////////// |
|
24 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
25 |
|
26 //Returns false, if: |
|
27 // - the dirty bit is set (indicates commit phase #2 or #3 is not complete); |
|
28 // - the CRC check fails ("backup TOC ref", "handle" and "TOC ref" are protected by 16-bit CRC in the permanent store file header); |
|
29 TBool TPermanentStoreHeader::IsValid() const |
|
30 { |
|
31 if (IsDirty()) |
|
32 return EFalse; |
|
33 // |
|
34 TUint16 crc=0; |
|
35 Mem::Crc(crc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc)); |
|
36 return crc==iCrc; |
|
37 } |
|
38 |
|
39 //Sets the "backup TOC ref", "handle" and "TOC ref" in current TPermanentStoreHeader object. |
|
40 //16-bit CRC is calculated, based on the values of the input parameters, and stored together with them in the |
|
41 //TPermanentStoreHeader object. |
|
42 void TPermanentStoreHeader::Set(TInt aBackupToc,TInt aHandle,TInt aReference) |
|
43 { |
|
44 iBackup=TUint32(aBackupToc)<<1; |
|
45 iHandle=aHandle; |
|
46 iRef=aReference; |
|
47 iCrc=0; |
|
48 Mem::Crc(iCrc,Ptr(),_FOFF(TPermanentStoreHeader,iCrc)); |
|
49 __ASSERT_DEBUG(IsValid(),User::Invariant()); |
|
50 } |
|
51 |
|
52 const TInt KTocGranularity=12; |
|
53 |
|
54 struct STocEntry |
|
55 { |
|
56 TInt32 handle; |
|
57 TInt32 ref; |
|
58 }; |
|
59 |
|
60 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
61 ///////////////////// CPermanentStoreToc //////////////////////////////////////////////////////// |
|
62 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
63 |
|
64 TInt CPermanentStoreToc::TEntry::Compare(const TEntry& aLeft, const TEntry& aRight) |
|
65 // |
|
66 // Ordering for Stream handles, return <0,0,>0 depending on comparison |
|
67 // The index part of the handle is only 24 bits, so just subtract the handles for result |
|
68 // |
|
69 { |
|
70 return (aLeft.handle&KMaskHandleIndex) - (aRight.handle&KMaskHandleIndex); |
|
71 } |
|
72 |
|
73 CPermanentStoreToc* CPermanentStoreToc::NewL(TStreamPos aBase,TStreamExchange& aHost,TInt aToc,TInt aBaseReloc) |
|
74 { |
|
75 CPermanentStoreToc* table=new(ELeave) CPermanentStoreToc(aBase,aHost); |
|
76 CleanupStack::PushL(table); |
|
77 table->ConstructL(aToc,aBaseReloc); |
|
78 CleanupStack::Pop(); |
|
79 return table; |
|
80 } |
|
81 |
|
82 TBool CPermanentStoreToc::IsDelta() const |
|
83 // |
|
84 // Report whether the current TOC should be written as a delta, or a base TOC |
|
85 // |
|
86 { |
|
87 TInt delta = iEntries.Count(); |
|
88 if (delta >= KTocDeltaCap) |
|
89 return EFalse; |
|
90 TInt magic = iMagic + delta; |
|
91 if (magic > KMaxTocDeltaMagic) |
|
92 return EFalse; |
|
93 return magic*KSizeTocDeltaEntry <= iCount*KSizeTocEntry - KSizeTocDeltaExtra; |
|
94 } |
|
95 |
|
96 void CPermanentStoreToc::Move(TInt aToc,TInt anExtent) |
|
97 // |
|
98 // Final stage of compaction, update to address new TOC |
|
99 // |
|
100 { |
|
101 __ASSERT_DEBUG(anExtent<=iOff&&anExtent>=aToc&&anExtent-(aToc+KOffsetTocHeader)==iExt-iOff,User::Invariant()); |
|
102 if (!HasDelta()) |
|
103 { |
|
104 // Base-toc only, update the base toc position as well |
|
105 TInt window=iWindow-iTocExt; |
|
106 iTocOff=aToc+KOffsetTocHeader; |
|
107 iTocExt=anExtent; |
|
108 iWindow=anExtent+window; |
|
109 } |
|
110 iOff = aToc+KOffsetTocHeader; |
|
111 iExt = anExtent; |
|
112 } |
|
113 |
|
114 TInt CPermanentStoreToc::RealizeL(TInt aPrimary,TInt anExtent) const |
|
115 { |
|
116 __ASSERT_DEBUG(IsVirtual(),User::Invariant()); |
|
117 RFrame16Buf buf(Base()); |
|
118 buf.PushL(); |
|
119 const TBool delta = IsDelta(); |
|
120 aPrimary &= KMaskStreamIdValue; |
|
121 if (delta) |
|
122 aPrimary |= KTocDelta; |
|
123 STocHead h; |
|
124 h.primary=TInt32(aPrimary); |
|
125 h.avail=TInt32(iAvail); |
|
126 h.count=TUint32(iCount); |
|
127 // |
|
128 TInt off; |
|
129 if (delta) |
|
130 off=DeltaL(buf,anExtent,h); |
|
131 else |
|
132 off=RewriteL(buf,anExtent,h); |
|
133 // |
|
134 buf.SynchL(); |
|
135 CleanupStack::PopAndDestroy(&buf); |
|
136 return off-KOffsetTocHeader; |
|
137 } |
|
138 |
|
139 void CPermanentStoreToc::Adopt(TInt aToc,TInt aPrimary) |
|
140 // |
|
141 // Final stage of Commit - all file changes have been successful |
|
142 // Record the new Toc location and reset the changed flag |
|
143 // |
|
144 { |
|
145 __ASSERT_DEBUG(aToc+KOffsetTocHeader>=iExt,User::Invariant()); |
|
146 |
|
147 if (IsDelta()) |
|
148 { |
|
149 // Adjust the TOC-delta location |
|
150 TInt c = iEntries.Count(); |
|
151 iOff=aToc+KOffsetTocHeader; |
|
152 iExt=aToc + KSizeTocDeltaExtra + KSizeTocDeltaEntry*c; |
|
153 if (c > KTocDeltaMagic) |
|
154 iMagic += c - KTocDeltaMagic; |
|
155 } |
|
156 else |
|
157 { |
|
158 // Adjust all the TOC data and reset the delta-set |
|
159 TInt window = iTocOff>=0 ? iWindow-iTocOff : -KOffsetTocHeader; |
|
160 iTocOff=iOff=aToc+KOffsetTocHeader; |
|
161 iTocExt=iExt=aToc+KSizeTocEntry*iCount; |
|
162 iWindow = iTocOff + window; |
|
163 iEntries.Reset(); |
|
164 iMagic = 0; |
|
165 } |
|
166 iPrimary=aPrimary; |
|
167 __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); |
|
168 } |
|
169 |
|
170 TInt CPermanentStoreToc::AllocL(TInt anOffset) |
|
171 { |
|
172 TEntry& entry=DoAllocL(); |
|
173 entry.ref=anOffset; |
|
174 return entry.handle; |
|
175 } |
|
176 |
|
177 TInt CPermanentStoreToc::AllocL() |
|
178 { |
|
179 TEntry& entry=DoAllocL(); |
|
180 entry.ref=KFrameNonexistent16; |
|
181 return entry.handle|=KHandleInvalid; |
|
182 } |
|
183 |
|
184 void CPermanentStoreToc::Cancel(TInt aHandle) |
|
185 { |
|
186 __ASSERT_DEBUG(aHandle<0&&IsVirtual(),User::Invariant()); |
|
187 TEntry* entry=Entry(aHandle); |
|
188 __ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant()); |
|
189 entry->ref=iAvail; |
|
190 iAvail=aHandle; |
|
191 } |
|
192 |
|
193 void CPermanentStoreToc::FreeL(TInt aHandle) |
|
194 { |
|
195 __ASSERT_DEBUG(aHandle>0,User::Invariant()); |
|
196 TEntry& entry=FetchL(aHandle); |
|
197 aHandle|=KHandleInvalid; |
|
198 entry.handle=aHandle; |
|
199 entry.ref=iAvail; |
|
200 iAvail=aHandle; |
|
201 Changed(); |
|
202 } |
|
203 |
|
204 TInt CPermanentStoreToc::AtL(TInt aHandle) const |
|
205 { |
|
206 __ASSERT_DEBUG(aHandle>0,User::Invariant()); |
|
207 |
|
208 // check the delta table |
|
209 const TEntry* entry=Entry(aHandle); |
|
210 if (entry!=NULL ) |
|
211 { |
|
212 // if there is an entry in the delta table, but its |
|
213 // not an exact match leave with KErrNotFound |
|
214 // This happens when the delta indicates it's been deleted |
|
215 if (entry->handle!=aHandle) |
|
216 User::Leave(KErrNotFound); |
|
217 |
|
218 return entry->ref; |
|
219 } |
|
220 // |
|
221 // if it's not in the delta table check the base TOC |
|
222 return DoAtL(aHandle); |
|
223 } |
|
224 |
|
225 void CPermanentStoreToc::PutL(TInt aHandle, TInt anOffset, TPut aCheck) |
|
226 // |
|
227 // Used by compaction to update a single stream reference IN-PLACE in the TOC |
|
228 // We need to check in which TOC to make the update |
|
229 // |
|
230 { |
|
231 __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); |
|
232 __ASSERT_DEBUG(aHandle>0,User::Invariant()); |
|
233 __ASSERT_DEBUG(aHandle!=KHandleTocBase || HasDelta(),User::Invariant()); |
|
234 |
|
235 if (aHandle == KHandleTocBase) |
|
236 { |
|
237 // update the TOC-base link |
|
238 PutTocL(anOffset - KOffsetTocHeader, aCheck); |
|
239 if (iTocOff != anOffset) |
|
240 { |
|
241 TInt size = iTocExt - iTocOff; |
|
242 TInt window = iWindow - iTocOff; |
|
243 iTocOff = anOffset; |
|
244 iTocExt = anOffset + size; |
|
245 iWindow = anOffset + window; |
|
246 } |
|
247 return; |
|
248 } |
|
249 |
|
250 TEntry e; |
|
251 e.handle=aHandle; |
|
252 TInt i; |
|
253 if (iEntries.FindInOrder(e,i,&TEntry::Compare)==0) |
|
254 { |
|
255 // update TOC-delta entry |
|
256 TEntry& entry=iEntries[i]; |
|
257 if (entry.handle==aHandle && entry.ref != anOffset) |
|
258 { |
|
259 PutDeltaL(i,aHandle,anOffset); |
|
260 entry.ref=anOffset; |
|
261 } |
|
262 return; |
|
263 } |
|
264 |
|
265 // update TOC-base entry |
|
266 if (aCheck==EWrite || DoAtL(aHandle)!=anOffset) |
|
267 PutBaseL(aHandle,anOffset); |
|
268 } |
|
269 |
|
270 TInt CPermanentStoreToc::GetL(TInt aHandle) |
|
271 { |
|
272 __ASSERT_DEBUG(aHandle>0,User::Invariant()); |
|
273 TEntry& entry=FetchL(aHandle); |
|
274 return entry.ref; |
|
275 } |
|
276 |
|
277 TInt CPermanentStoreToc::Set(TInt aHandle,TInt anOffset) |
|
278 { |
|
279 __ASSERT_DEBUG(aHandle!=0,User::Invariant()); |
|
280 TEntry* entry=Entry(aHandle); |
|
281 __ASSERT_DEBUG((entry!=NULL) && (entry->handle==aHandle),User::Invariant()); |
|
282 aHandle&=KMaskStreamIdValue; |
|
283 entry->handle=aHandle; |
|
284 entry->ref=anOffset; |
|
285 Changed(); |
|
286 return aHandle; |
|
287 } |
|
288 |
|
289 CPermanentStoreToc::CPermanentStoreToc(TStreamPos aBase,TStreamExchange& aHost) |
|
290 : /*iPrimary(0),iAvail(0),iCount(0),*/iEntries(KTocGranularity), |
|
291 iBase(aBase),iHost(&aHost),iOff(KFrameNonexistent16)/*,iExt(0)*/, |
|
292 iTocOff(KFrameNonexistent16)/*,iTocExt(0),iWindow(0)*/ |
|
293 {} |
|
294 |
|
295 CPermanentStoreToc::~CPermanentStoreToc() |
|
296 { |
|
297 iEntries.Close(); |
|
298 } |
|
299 |
|
300 void CPermanentStoreToc::ConstructL(TInt aToc,TInt aBaseReloc) |
|
301 // |
|
302 // Read and validate the TOC header at aToc. |
|
303 // aBaseReloc may contain a relocated TOC-base offset, which should override the one in a TOC-delta |
|
304 // |
|
305 { |
|
306 if (aToc==0) |
|
307 return; |
|
308 __ASSERT_DEBUG(aToc>0&&iEntries.Count()==0,User::Invariant()); |
|
309 RFrame16Buf buf(Base()); |
|
310 aToc+=KOffsetTocHeader; |
|
311 buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead); |
|
312 buf.PushL(); |
|
313 RReadStream stream(&buf); |
|
314 STocHead h; |
|
315 stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency |
|
316 if ((h.primary&~(KMaskStreamIdValue|static_cast<TUint>(KTocDelta)))!=0|| |
|
317 h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0) |
|
318 __LEAVE(KErrCorrupt); |
|
319 // |
|
320 iPrimary=TInt(h.primary) & ~KTocDelta; |
|
321 iAvail=TInt(h.avail); |
|
322 iCount=TInt(h.count); |
|
323 iOff = aToc; |
|
324 // |
|
325 if (h.primary & KTocDelta) |
|
326 { |
|
327 // This is a TOC-delta |
|
328 aToc = InternalizeL(stream, aBaseReloc) + KOffsetTocHeader; |
|
329 |
|
330 // Now locate and validate the base TOC |
|
331 buf.Release(); |
|
332 buf.OpenL(Host(),aToc,EFrameDescriptive16|buf.ERead); |
|
333 stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency |
|
334 if ((h.primary&~KMaskStreamIdValue)!=0||(h.count&~KMaskHandleIndex)!=0|| |
|
335 h.avail>0||(h.avail&KMaskHandleClear)!=0||TInt(h.count)>iCount) |
|
336 __LEAVE(KErrCorrupt); |
|
337 } |
|
338 TInt size = KSizeTocEntry*TInt(h.count); |
|
339 // Eagerly load the first few TOC entries into the cache |
|
340 // This is good as it almost certainly lies in the file buffer already |
|
341 if (size>0) |
|
342 stream.ReadL(&iBuf[0],Min(size,TInt(KSizeTocBuf))); |
|
343 size-=KOffsetTocHeader; |
|
344 if (buf.SizeL()!=size) |
|
345 __LEAVE(KErrCorrupt); |
|
346 iTocOff=aToc; |
|
347 iWindow=aToc-KOffsetTocHeader; |
|
348 iTocExt=aToc+size; |
|
349 if (iExt == 0) |
|
350 iExt = iTocExt; // set extent for non-delta TOC |
|
351 // |
|
352 CleanupStack::PopAndDestroy(&buf); |
|
353 } |
|
354 |
|
355 TInt CPermanentStoreToc::InternalizeL(RReadStream& aIn, TInt aBaseReloc) |
|
356 // |
|
357 // Load and validate the delta-toc table |
|
358 // |
|
359 { |
|
360 TInt tocoff = aIn.ReadInt32L(); |
|
361 if (aBaseReloc != KFrameNonexistent16) |
|
362 tocoff = aBaseReloc - KOffsetTocHeader; |
|
363 if (tocoff<0||tocoff>=iOff) |
|
364 __LEAVE(KErrCorrupt); |
|
365 iMagic = aIn.ReadUint16L(); |
|
366 if (!IsDelta()) |
|
367 __LEAVE(KErrCorrupt); |
|
368 TInt n = aIn.ReadUint8L(); |
|
369 TInt size = -KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry * n; |
|
370 if (aIn.Source()->SizeL()!=size) |
|
371 __LEAVE(KErrCorrupt); |
|
372 iExt = iOff + size; |
|
373 TInt last = 0; |
|
374 while (--n >= 0) |
|
375 { |
|
376 STocEntry e; |
|
377 aIn.ReadL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency |
|
378 if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref<KFrameNonexistent16)|| |
|
379 e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0)) |
|
380 __LEAVE(KErrCorrupt); |
|
381 TInt i = e.handle&KMaskHandleIndex; |
|
382 if (i <= last || i > iCount) |
|
383 __LEAVE(KErrCorrupt); |
|
384 last = i; |
|
385 TEntry entry; |
|
386 entry.handle = TInt(e.handle); |
|
387 entry.ref = TInt(e.ref); |
|
388 User::LeaveIfError(iEntries.Append(entry)); |
|
389 } |
|
390 |
|
391 return tocoff; |
|
392 } |
|
393 |
|
394 TInt CPermanentStoreToc::DeltaL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const |
|
395 // |
|
396 // Write the TOC-delta |
|
397 // |
|
398 { |
|
399 TInt off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite); |
|
400 RWriteStream out(&aBuf); |
|
401 out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency |
|
402 out.WriteInt32L(iTocOff - KOffsetTocHeader); |
|
403 TInt n = iEntries.Count(); |
|
404 TInt magic = iMagic; |
|
405 if (n > KTocDeltaMagic) |
|
406 magic += n - KTocDeltaMagic; |
|
407 __ASSERT_DEBUG(magic <= KMaxTocDeltaMagic,User::Invariant()); |
|
408 __ASSERT_DEBUG(n <= (TInt)KMaxTUint8, User::Invariant()); |
|
409 out.WriteUint16L(magic); |
|
410 out.WriteUint8L(n); |
|
411 for (int i = 0; i < n; ++i) |
|
412 { |
|
413 const TEntry& entry = iEntries[i]; |
|
414 STocEntry e; |
|
415 e.handle=TInt32(entry.handle); |
|
416 e.ref=TInt32(entry.ref); |
|
417 out.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency |
|
418 } |
|
419 return off; |
|
420 } |
|
421 |
|
422 TInt CPermanentStoreToc::RewriteL(RFrame16Buf& aBuf,TInt aExtent,const STocHead& aHead) const |
|
423 // |
|
424 // Write the TOC-base |
|
425 // |
|
426 { |
|
427 const TInt KElementsRewrite=304; |
|
428 const TInt KSizeRewriteBuf=KElementsRewrite*KSizeTocEntry; |
|
429 TUint8 toc[KSizeRewriteBuf]; |
|
430 |
|
431 RFrame16Buf buf(Base()); |
|
432 buf.PushL(); |
|
433 TInt oldsize = 0; |
|
434 TInt window = 0; |
|
435 if (iTocOff>=0) |
|
436 { |
|
437 oldsize = iTocExt-iTocOff+KOffsetTocHeader; |
|
438 window = iWindow + KOffsetTocHeader - iTocOff; |
|
439 if (oldsize <= Min(KSizeTocBuf,KSizeRewriteBuf) && window == 0) |
|
440 { |
|
441 // the old TOC is already in the toc-base cache, no need to read it |
|
442 Mem::Copy(&toc[0],&iBuf[0],oldsize); |
|
443 oldsize = 0; // this prevents the read request |
|
444 } |
|
445 else |
|
446 { |
|
447 buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead); |
|
448 buf.SeekL(buf.ERead,TStreamPos(-KOffsetTocHeader)); |
|
449 } |
|
450 } |
|
451 RReadStream in(&buf); |
|
452 // defer the initialisation of the write in roder to improve file buffering performance |
|
453 RWriteStream out(&aBuf); |
|
454 TInt off=-1; |
|
455 TInt size=iCount*KSizeTocEntry; |
|
456 |
|
457 for (TInt base=0,delta=0;base<size;base+=KSizeRewriteBuf) |
|
458 { |
|
459 // fill buffer with old TOC data |
|
460 if (base < oldsize) |
|
461 in.ReadL(&toc[0],Min(KSizeRewriteBuf,oldsize-base)); |
|
462 // apply changes to this block |
|
463 for (TInt n=iEntries.Count();delta<n;++delta) |
|
464 { |
|
465 const TEntry& entry=iEntries[delta]; |
|
466 TInt pos = (entry.handle&KMaskHandleIndex)*KSizeTocEntry - KSizeTocEntry - base; |
|
467 __ASSERT_DEBUG(pos>=0,User::Invariant()); |
|
468 if (pos>=KSizeRewriteBuf) |
|
469 break; |
|
470 STocEntry e; |
|
471 e.handle=TInt32(entry.handle); |
|
472 e.ref=TInt32(entry.ref); |
|
473 Mem::Copy(&toc[pos],(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); |
|
474 pos += base - window; |
|
475 if (TUint(pos) < TUint(KSizeTocBuf)) |
|
476 Mem::Copy(MUTABLE_CAST(TUint8*,&iBuf[pos]),(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); |
|
477 } |
|
478 // write buffer to file |
|
479 if (off == -1) |
|
480 { |
|
481 // initialise writing |
|
482 off=aBuf.ExtendL(Host(),aExtent,EFrameDescriptive16|aBuf.EWrite); |
|
483 out.WriteL((TUint8*)&aHead,sizeof(STocHead)); // platform dependency |
|
484 } |
|
485 out.WriteL(&toc[0],Min(KSizeRewriteBuf,size-base)); |
|
486 } |
|
487 // |
|
488 CleanupStack::PopAndDestroy(&buf); |
|
489 return off; |
|
490 } |
|
491 |
|
492 CPermanentStoreToc::TEntry* CPermanentStoreToc::Entry(TInt aHandle) |
|
493 { |
|
494 TEntry e; |
|
495 e.handle=aHandle; |
|
496 TInt i; |
|
497 if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0) |
|
498 return NULL; |
|
499 // |
|
500 TEntry& entry=iEntries[i]; |
|
501 |
|
502 // allow entry to return a pointer even if not an exact match |
|
503 return &entry; |
|
504 } |
|
505 |
|
506 CPermanentStoreToc::TEntry& CPermanentStoreToc::FetchL(TInt aHandle) |
|
507 { |
|
508 TEntry e; |
|
509 e.handle=aHandle; |
|
510 TInt i; |
|
511 if (iEntries.FindInOrder(e,i,&TEntry::Compare)!=0) |
|
512 { |
|
513 e.ref=DoAtL(aHandle); |
|
514 User::LeaveIfError(iEntries.Insert(e,i)); |
|
515 } |
|
516 TEntry& entry=iEntries[i]; |
|
517 if (entry.handle!=aHandle) |
|
518 __LEAVE(KErrNotFound); |
|
519 // |
|
520 return entry; |
|
521 } |
|
522 |
|
523 CPermanentStoreToc::TEntry& CPermanentStoreToc::DoAllocL() |
|
524 { |
|
525 TInt handle=iAvail; |
|
526 TEntry* entry; |
|
527 if (handle==0) |
|
528 { |
|
529 __ASSERT_DEBUG(iEntries.Count()==0||(iEntries[iEntries.Count()-1].handle&KMaskHandleIndex)<=iCount,User::Invariant()); |
|
530 User::LeaveIfError(iEntries.Append(TEntry())); |
|
531 entry=&iEntries[iEntries.Count()-1]; |
|
532 handle=++iCount; |
|
533 } |
|
534 else |
|
535 { |
|
536 entry=&FetchL(handle); |
|
537 handle=(handle+KIncHandleGen)&KMaskStreamIdValue; |
|
538 // |
|
539 TInt avail=entry->ref; |
|
540 if (avail>0||(avail&KMaskHandleClear)!=0) |
|
541 __LEAVE(KErrCorrupt); |
|
542 // |
|
543 iAvail=avail; |
|
544 } |
|
545 entry->handle=handle; |
|
546 Changed(); |
|
547 return *entry; |
|
548 } |
|
549 |
|
550 TInt CPermanentStoreToc::DoAtL(TInt aHandle) const |
|
551 { |
|
552 TInt off=iTocOff; |
|
553 if (off<0) |
|
554 __LEAVE(KErrNotFound); |
|
555 // |
|
556 off+=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex); |
|
557 __ASSERT_DEBUG(off>=iTocOff-KOffsetTocHeader,User::Invariant()); |
|
558 if (off>=iTocExt) |
|
559 __LEAVE(KErrNotFound); |
|
560 // |
|
561 TInt window=iWindow; |
|
562 if (off-window<0||off-window>=KSizeTocBuf) |
|
563 { |
|
564 TInt len=iTocOff-KOffsetTocHeader; |
|
565 window=Max(len,Min(off-KBackTocBuf,iTocExt-KSizeTocBuf)); |
|
566 len=Min(iTocExt-len,TInt(KSizeTocBuf)); |
|
567 // |
|
568 RFrame16Buf buf(Base()); |
|
569 buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.ERead); |
|
570 buf.PushL(); |
|
571 buf.SeekL(buf.ERead,TStreamPos(window-iTocOff)); |
|
572 RReadStream stream(&buf); |
|
573 MUTABLE_CAST(TInt&,iWindow)=iTocExt; |
|
574 stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[0]),1); // assume half decent read-ahead buffering |
|
575 stream.ReadL(MUTABLE_CAST(TUint8*,&iBuf[1]),len-1); |
|
576 MUTABLE_CAST(TInt&,iWindow)=window; |
|
577 CleanupStack::PopAndDestroy(); |
|
578 } |
|
579 STocEntry e; |
|
580 e.handle=aHandle; |
|
581 Mem::Copy((TUint8*)&e+KSizeHandleIndex,&iBuf[off-window],KSizeTocEntry); // platform dependency |
|
582 if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&(e.ref<KFrameNonexistent16)|| |
|
583 e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0)) |
|
584 __LEAVE(KErrCorrupt); |
|
585 // |
|
586 if (TInt(e.handle)!=aHandle) |
|
587 __LEAVE(KErrNotFound); |
|
588 // |
|
589 return TInt(e.ref); |
|
590 } |
|
591 |
|
592 void CPermanentStoreToc::PutBaseL(TInt aHandle,TInt aReference) |
|
593 // |
|
594 // Update a TOC-base entry |
|
595 // |
|
596 { |
|
597 __ASSERT_DEBUG(iTocOff>=0,User::Invariant()); |
|
598 |
|
599 TInt pos=-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex); |
|
600 RFrame16Buf buf(Base()); |
|
601 buf.Set(Host(),iTocOff,iTocExt,EFrameDescriptive16|buf.EWrite); |
|
602 buf.PushL(); |
|
603 buf.SeekL(buf.EWrite,TStreamPos(pos)); |
|
604 RWriteStream stream(&buf); |
|
605 STocEntry e; |
|
606 e.handle=aHandle; |
|
607 e.ref=aReference; |
|
608 stream.WriteL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency |
|
609 CleanupStack::PopAndDestroy(); |
|
610 TInt off=pos+iTocOff-iWindow; |
|
611 if (off>=0&&off<KSizeTocBuf) |
|
612 Mem::Copy(&iBuf[off],(const TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency |
|
613 } |
|
614 |
|
615 void CPermanentStoreToc::PutDeltaL(TInt aPos, TInt aHandle, TInt aReference) |
|
616 // |
|
617 // Update a single stream reference IN-PLACE in the TOC-delta |
|
618 // |
|
619 { |
|
620 __ASSERT_DEBUG(HasDelta(),User::Invariant()); |
|
621 __ASSERT_DEBUG(iOff>=0,User::Invariant()); |
|
622 RFrame16Buf buf(Base()); |
|
623 buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite); |
|
624 buf.PushL(); |
|
625 buf.SeekL(buf.EWrite,TStreamPos(-KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*aPos)); |
|
626 RWriteStream stream(&buf); |
|
627 STocEntry e; |
|
628 e.handle=aHandle; |
|
629 e.ref=aReference; |
|
630 stream.WriteL((TUint8*)&e,KSizeTocDeltaEntry); // platform dependency |
|
631 CleanupStack::PopAndDestroy(); |
|
632 } |
|
633 |
|
634 void CPermanentStoreToc::PutTocL(TInt aTocBase, TPut aCheck) |
|
635 // |
|
636 // Update the base TOC link in the TOC-delta |
|
637 // |
|
638 { |
|
639 __ASSERT_DEBUG(HasDelta(),User::Invariant()); |
|
640 __ASSERT_DEBUG(iOff>=0,User::Invariant()); |
|
641 RFrame16Buf buf(Base()); |
|
642 buf.Set(Host(),iOff,iExt,EFrameDescriptive16|buf.EWrite|buf.ERead); |
|
643 buf.PushL(); |
|
644 buf.SeekL(buf.EWrite|buf.ERead,TStreamPos(-KOffsetTocHeader)); |
|
645 RReadStream in(&buf); |
|
646 if (aCheck==EWrite || in.ReadInt32L() != aTocBase) |
|
647 { |
|
648 RWriteStream out(&buf); |
|
649 out.WriteInt32L(aTocBase); |
|
650 } |
|
651 CleanupStack::PopAndDestroy(); |
|
652 } |
|
653 |
|
654 // Used prior to PutL to determine exactly where in the file the TOC update will be written. |
|
655 // This may be used to eliminate the pre-put write of the relocation information in the file |
|
656 // header in situations where the write is entirely within an atomic file block. |
|
657 // |
|
658 // The detailed numbers used to offset into the TOCs are dependant on the TOC formats, as |
|
659 // implicated by other members of this class |
|
660 TInt CPermanentStoreToc::RefSpan(TInt aHandle,TInt& aLength) |
|
661 { |
|
662 __ASSERT_DEBUG(!IsVirtual(),User::Invariant()); |
|
663 __ASSERT_DEBUG(aHandle>0,User::Invariant()); |
|
664 |
|
665 if (aHandle == KHandleTocBase) |
|
666 { // locate the TOC-base link |
|
667 __ASSERT_DEBUG(HasDelta(),User::Invariant()); |
|
668 __ASSERT_DEBUG(iOff>=0,User::Invariant()); |
|
669 aLength = sizeof(TInt32); |
|
670 return RFrame16Buf::Position(Base(), iOff - KOffsetTocHeader).Offset(); |
|
671 } |
|
672 |
|
673 TEntry e; |
|
674 e.handle=aHandle; |
|
675 TInt ix; |
|
676 if (iEntries.FindInOrder(e, ix, &TEntry::Compare) == 0) |
|
677 { // locate TOC-delta entry |
|
678 TEntry& entry=iEntries[ix]; |
|
679 if (entry.handle==aHandle) |
|
680 { |
|
681 __ASSERT_DEBUG(HasDelta(),User::Invariant()); |
|
682 __ASSERT_DEBUG(iOff>=0,User::Invariant()); |
|
683 aLength = KSizeTocDeltaEntry; |
|
684 return RFrame16Buf::Position(Base(),iOff - KOffsetTocHeader + KSizeTocDeltaExtra + KSizeTocDeltaEntry*ix).Offset(); |
|
685 } |
|
686 } |
|
687 |
|
688 // locate the TOC-base entry |
|
689 __ASSERT_DEBUG(iTocOff>=0,User::Invariant()); |
|
690 aLength = KSizeTocEntry; |
|
691 return RFrame16Buf::Position(Base(),iTocOff-KOffsetTocHeader-KSizeTocEntry+KSizeTocEntry*(aHandle&KMaskHandleIndex)).Offset(); |
|
692 } |
|
693 |
|
694 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
695 ///////////////////// RPermanentStoreTocIter //////////////////////////////////////////////////// |
|
696 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
697 |
|
698 RPermanentStoreTocIter::RPermanentStoreTocIter(const CPermanentStoreToc& aTable) |
|
699 : iTable(aTable), iBuf(aTable.Base()), iDelta(0), iDeltaEnd(0) |
|
700 { |
|
701 TInt off=aTable.iTocOff; |
|
702 __ASSERT_DEBUG(off>=KFrameNonexistent16,User::Invariant()); |
|
703 TInt ext=off<0?off:aTable.iTocExt; |
|
704 iBuf.Set(aTable.Host(),off,ext,EFrameDescriptive16|iBuf.ERead); |
|
705 } |
|
706 |
|
707 void RPermanentStoreTocIter::Release() |
|
708 { |
|
709 iBuf.Release(); |
|
710 } |
|
711 |
|
712 void RPermanentStoreTocIter::ResetL() |
|
713 { |
|
714 iNext = iIndex = 1; |
|
715 if (iBuf.Offset()<0) |
|
716 { |
|
717 iCount=0; |
|
718 return; |
|
719 } |
|
720 // |
|
721 RReadStream stream(&iBuf); |
|
722 STocHead h; |
|
723 stream.ReadL((TUint8*)&h,sizeof(STocHead)); // platform dependency |
|
724 if (h.primary<0||(h.primary&~KMaskStreamIdValue)!=0|| |
|
725 h.avail>0||(h.avail&KMaskHandleClear)!=0||(h.count&~KMaskHandleIndex)!=0) |
|
726 __LEAVE(KErrCorrupt); |
|
727 // |
|
728 const CPermanentStoreToc& table=iTable; |
|
729 if (table.HasDelta()) |
|
730 { |
|
731 TInt c = table.iEntries.Count(); |
|
732 if (c) |
|
733 { |
|
734 iDelta = &table.iEntries[0]; |
|
735 iDeltaEnd = iDelta + c; |
|
736 } |
|
737 iIndex = 0; |
|
738 } |
|
739 iCount = iTable.iCount; |
|
740 } |
|
741 |
|
742 TBool RPermanentStoreTocIter::NextL(TEntry& anEntry) |
|
743 { |
|
744 TInt i=iIndex; |
|
745 __ASSERT_DEBUG(iCount>=0,User::Invariant()); |
|
746 if (i == 0) |
|
747 { |
|
748 // report TOC-base as a 'stream' |
|
749 anEntry.handle = KHandleTocBase; |
|
750 anEntry.ref = iTable.iTocOff; |
|
751 iIndex = 1; |
|
752 return ETrue; |
|
753 } |
|
754 |
|
755 __ASSERT_DEBUG(i>0,User::Invariant()); |
|
756 if (i>iCount) |
|
757 return EFalse; |
|
758 // |
|
759 const TEntry* d = iDelta; |
|
760 if (d != iDeltaEnd && (d->handle&KMaskHandleIndex) == i) |
|
761 { |
|
762 anEntry = *d; |
|
763 iDelta = d+1; |
|
764 iIndex = i+1; |
|
765 return ETrue; |
|
766 } |
|
767 // |
|
768 RReadStream stream(&iBuf); |
|
769 TInt skip = i - iNext; |
|
770 if (skip > 0) |
|
771 stream.ReadL(KSizeTocEntry * skip); |
|
772 STocEntry e; |
|
773 e.handle=i; |
|
774 stream.ReadL((TUint8*)&e+KSizeHandleIndex,KSizeTocEntry); // platform dependency |
|
775 if ((e.handle&KMaskHandleClear)!=0||e.handle>=0&&e.ref<KFrameNonexistent16|| |
|
776 e.handle<0&&(e.ref>0||(e.ref&KMaskHandleClear)!=0)) |
|
777 __LEAVE(KErrCorrupt); |
|
778 // |
|
779 iNext = iIndex = i+1; |
|
780 anEntry.handle=TInt(e.handle); |
|
781 anEntry.ref=TInt(e.ref); |
|
782 return ETrue; |
|
783 } |
|
784 |
|
785 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
786 ///////////////////// TPermanentStoreCache ////////////////////////////////////////////////////// |
|
787 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
788 |
|
789 const TPermanentStoreCache::TItem* TPermanentStoreCache::At(TInt aHandle) const |
|
790 { |
|
791 const TItem* item=&iItems[0]; |
|
792 TInt i=1; |
|
793 TInt bit=1; |
|
794 while (item->handle!=aHandle) |
|
795 { |
|
796 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
797 TInt step=(aHandle&bit)?i+1:i; |
|
798 bit<<=1; |
|
799 i+=step; |
|
800 if (i>EItems) |
|
801 return NULL; |
|
802 // |
|
803 item+=step; |
|
804 } |
|
805 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
806 return item; |
|
807 } |
|
808 |
|
809 void TPermanentStoreCache::Relocated(TInt aHandle,TInt anOffset) |
|
810 { |
|
811 const TItem* item=At(aHandle); |
|
812 if (item) |
|
813 { |
|
814 // update the cache item with the new offset |
|
815 // As 'extent' may only be a partial extent of a fragmented frame |
|
816 // which is no longer fragmented, we cannot simply shift this by the same |
|
817 // amount as 'offset'. Simplest to reset this to 'unknown', i.e. 0 |
|
818 const_cast<TItem*>(item)->offset = anOffset; |
|
819 const_cast<TItem*>(item)->extent = 0; |
|
820 } |
|
821 } |
|
822 |
|
823 void TPermanentStoreCache::Put(const TItem* anItem,TInt anOffset,TInt anExtent) |
|
824 { |
|
825 TInt handle=anItem->handle; |
|
826 TItem* item=&iItems[0]; |
|
827 TInt i=1; |
|
828 TInt bit=1; |
|
829 while (item != anItem) |
|
830 { |
|
831 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
832 TInt step=(handle&bit)?i+1:i; |
|
833 bit<<=1; |
|
834 i+=step; |
|
835 __ASSERT_DEBUG(i<=EItems,User::Invariant()); |
|
836 item+=step; |
|
837 } |
|
838 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
839 |
|
840 for(;;) |
|
841 { |
|
842 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
843 TInt step=(handle&bit)?i+1:i; |
|
844 bit<<=1; |
|
845 i+=step; |
|
846 if (i>EItems) |
|
847 break; |
|
848 // |
|
849 TItem* hole=item; |
|
850 item+=step; |
|
851 *hole=*item; |
|
852 }; |
|
853 item->handle=handle; |
|
854 item->offset=anOffset; |
|
855 item->extent=anExtent; |
|
856 __ASSERT_DEBUG(At(handle)==item,User::Invariant()); |
|
857 } |
|
858 |
|
859 void TPermanentStoreCache::Add(TInt aHandle,TInt anOffset,TInt anExtent) |
|
860 { |
|
861 TItem* item=&iItems[0]; |
|
862 TInt i=1; |
|
863 TInt bit=1; |
|
864 for(;;) |
|
865 { |
|
866 __ASSERT_DEBUG(item==&iItems[i-1]&&item->handle!=aHandle,User::Invariant()); |
|
867 TInt step=(aHandle&bit)?i+1:i; |
|
868 bit<<=1; |
|
869 i+=step; |
|
870 if (i>EItems) |
|
871 break; |
|
872 // |
|
873 TItem* hole=item; |
|
874 item+=step; |
|
875 *hole=*item; |
|
876 }; |
|
877 item->handle=aHandle; |
|
878 item->offset=anOffset; |
|
879 item->extent=anExtent; |
|
880 __ASSERT_DEBUG(At(aHandle)==item,User::Invariant()); |
|
881 } |
|
882 |
|
883 void TPermanentStoreCache::Remove(TInt aHandle) |
|
884 { |
|
885 TItem* item=&iItems[0]; |
|
886 TInt i=1; |
|
887 TInt bit=1; |
|
888 while (item->handle!=aHandle) |
|
889 { |
|
890 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
891 TInt step=(aHandle&bit)?i+1:i; |
|
892 bit<<=1; |
|
893 i+=step; |
|
894 if (i>EItems) |
|
895 return; |
|
896 // |
|
897 item+=step; |
|
898 } |
|
899 TItem* hole=item; |
|
900 TInt mask=bit-1; |
|
901 while (item!=&iItems[0]) |
|
902 { |
|
903 __ASSERT_DEBUG(item==&iItems[i-1],User::Invariant()); |
|
904 TInt step=i; |
|
905 bit>>=1; |
|
906 i>>=1; |
|
907 step-=i; |
|
908 item-=step; |
|
909 // |
|
910 if (((aHandle^item->handle)&mask)==0) |
|
911 { |
|
912 *hole=*item; |
|
913 hole=item; |
|
914 mask=bit-1; |
|
915 } |
|
916 } |
|
917 hole->handle=0; |
|
918 __ASSERT_DEBUG(i==1&&At(aHandle)==NULL,User::Invariant()); |
|
919 } |
|
920 |
|
921 void TPermanentStoreCache::Invalidate() |
|
922 { |
|
923 for (TItem* item=&iItems[0],*end=item+EItems;item<end;++item) |
|
924 item->handle=0; |
|
925 } |
|
926 |
|
927 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
928 ///////////////////// CPermanentStoreCoord ////////////////////////////////////////////////////// |
|
929 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
930 |
|
931 //Returns the file system type, according to what the "file write" operation guarantees. |
|
932 //The return result could be one of: |
|
933 // - ESimple - "single byte write" operations are atomic; |
|
934 // - EBlockAtomic - "block/sector write" operations are atomic; |
|
935 // - ETransactional - transactional file system; |
|
936 CPermanentStoreCoord::TFileQoS CPermanentStoreCoord::FileQoSL() |
|
937 { |
|
938 //Uncomment, if you want FileQoSL() to return always EBlockAtomic/EBlockAtomic. |
|
939 //iFileQos = ETransactional; |
|
940 //iFileQos = EBlockAtomic; |
|
941 //return iFileQos; |
|
942 // |
|
943 if (iFileQos == EUnknown) //get the file sytem type, if iFileQos is not set yet |
|
944 { |
|
945 TStreamExchange &se = Host(); |
|
946 RFileBuf *sb = static_cast<RFileBuf *>(se.HostL()); |
|
947 RFile &f = sb->File(); |
|
948 |
|
949 TInt dn; |
|
950 TDriveInfo di; |
|
951 User::LeaveIfError(f.Drive(dn, di)); |
|
952 |
|
953 iFileQos = (di.iDriveAtt & KDriveAttTransaction) ? ETransactional : ESimple; |
|
954 |
|
955 if(iFileQos == ESimple && IsBlockAtomicL(dn)) |
|
956 { |
|
957 iFileQos = EBlockAtomic; |
|
958 } |
|
959 } |
|
960 return iFileQos; |
|
961 } |
|
962 |
|
963 //The function returns true, if the file system guarantees atomic "block write" operations on aDriveNo. |
|
964 //It is not the most effective implementation at the moment (creates/closes a file session), |
|
965 //probably TDriveInfo::iType can be used in a more effective implementation. |
|
966 TBool CPermanentStoreCoord::IsBlockAtomicL(TInt aDriveNo) const |
|
967 { |
|
968 __ASSERT_DEBUG(aDriveNo >= EDriveA && aDriveNo <= EDriveZ, User::Invariant()); |
|
969 RFs fs; |
|
970 CleanupClosePushL(fs); |
|
971 User::LeaveIfError(fs.Connect()); |
|
972 |
|
973 TVolumeIOParamInfo volInfo; |
|
974 TInt err = fs.VolumeIOParam(aDriveNo, volInfo); |
|
975 CleanupStack::PopAndDestroy(&fs); |
|
976 |
|
977 //If VolumeIOParam() succeeds, the media block size is >= 512 bytes and the media block size is power of two - report |
|
978 //that the media supports atomic "block write" operations. |
|
979 return err == KErrNone && volInfo.iBlockSize >= KDefaultMediaBlockSize && (volInfo.iBlockSize & (volInfo.iBlockSize - 1)) == 0; |
|
980 } |
|
981 |
|
982 TStreamPos CPermanentStoreCoord::LimitL() |
|
983 { |
|
984 TableL(); |
|
985 return RFrame16Buf::Position(Base(),iExt); |
|
986 } |
|
987 |
|
988 TStreamId CPermanentStoreCoord::PrimaryL() |
|
989 { |
|
990 return TStreamId(TableL().Primary()); |
|
991 } |
|
992 |
|
993 void CPermanentStoreCoord::ChangedL() |
|
994 { |
|
995 ConsolidateL().Changed(); |
|
996 } |
|
997 |
|
998 TBool CPermanentStoreCoord::CommitL(TStreamId aPrimary) |
|
999 { |
|
1000 __ASSERT_DEBUG(IsTrim(),User::Invariant()); |
|
1001 if (iExtend!=0) |
|
1002 __LEAVE(KErrInUse); |
|
1003 // |
|
1004 CPermanentStoreToc& table=ConsolidateL(); |
|
1005 if (!table.IsVirtual()) |
|
1006 return EFalse; |
|
1007 // |
|
1008 iState|=EClip; |
|
1009 TInt toc=table.RealizeL(aPrimary.Value(),iExt); |
|
1010 // |
|
1011 TPermanentStoreHeader header(toc); |
|
1012 header.SetBackupToc(iToc); |
|
1013 MStreamBuf* buf=BeginL(header); |
|
1014 buf->SynchL(); |
|
1015 // it's done, wrap things up |
|
1016 Host().Share(buf); |
|
1017 iToc=toc; |
|
1018 table.Adopt(toc,aPrimary.Value()); |
|
1019 ++iGen; |
|
1020 iState=EBackup; |
|
1021 iExt=table.Extent(); |
|
1022 return ETrue; |
|
1023 } |
|
1024 |
|
1025 TBool CPermanentStoreCoord::RevertL(TStreamId& aPrimary) |
|
1026 { |
|
1027 __ASSERT_ALWAYS(iAccess==0,Panic(EStoreInUse)); |
|
1028 // can't revert things under people's feet |
|
1029 CPermanentStoreToc* table=iTable; |
|
1030 iTable=NULL; |
|
1031 iCache.Invalidate(); |
|
1032 if (table==NULL||!table->IsVirtual()) |
|
1033 { |
|
1034 __ASSERT_DEBUG(table==NULL||aPrimary==TStreamId(table->Primary()),User::Invariant()); |
|
1035 delete table; |
|
1036 return EFalse; |
|
1037 } |
|
1038 // |
|
1039 aPrimary=TStreamId(table->Primary()); |
|
1040 delete table; |
|
1041 iState|=EClip; |
|
1042 // |
|
1043 TStreamExchange& host=Host(); |
|
1044 MStreamBuf* buf=host.HostL(); |
|
1045 host.Release(); |
|
1046 buf->SynchL(); |
|
1047 host.Share(buf); |
|
1048 return ETrue; |
|
1049 } |
|
1050 |
|
1051 TStreamId CPermanentStoreCoord::ExtendL() |
|
1052 { |
|
1053 return TStreamId(ConsolidateL().AllocL(KFrameNonexistent16)); |
|
1054 } |
|
1055 |
|
1056 void CPermanentStoreCoord::DeleteL(TStreamId anId) |
|
1057 { |
|
1058 TInt handle=anId.Value(); |
|
1059 ConsolidateL().FreeL(handle); |
|
1060 iCache.Remove(handle); |
|
1061 } |
|
1062 |
|
1063 CPermanentStoreCoord::CPermanentStoreCoord(TStreamPos aBase,TStreamExchange& aHost) |
|
1064 : iBase(aBase),iHost(&aHost), iFileQos(EUnknown) |
|
1065 {} |
|
1066 |
|
1067 // |
|
1068 // Read and analyse the store header. |
|
1069 // The whole header (14 bytes) is read from the file and: |
|
1070 // - If the dirty bit is set, the backup TOC will be used; |
|
1071 // - If the dirty bit is not set, and the backup TOC ref is not the same as the TOC ref, |
|
1072 // then it means the the backup TOC ref has not been written successfully, so the TOC ref will be used; |
|
1073 void CPermanentStoreCoord::InternalizeL(RReadStream& aStream) |
|
1074 { |
|
1075 if (iTable!=NULL) |
|
1076 __LEAVE(KErrNotReady); |
|
1077 // |
|
1078 iState=EClip; |
|
1079 TPermanentStoreHeader header; |
|
1080 aStream.ReadL(header.Ptr(),KPermanentStoreHeaderLength); |
|
1081 // determine where the toc lives |
|
1082 TInt toc=header.BackupToc(); |
|
1083 if (header.IsDirty()) |
|
1084 { |
|
1085 iReloc=0; |
|
1086 iTarget=0; |
|
1087 } |
|
1088 else |
|
1089 { |
|
1090 TInt handle=header.Handle(); |
|
1091 TInt ref=header.Reference(); |
|
1092 if (handle==0&&toc!=ref) |
|
1093 { // toc pointer not backed up, validate as if it was |
|
1094 toc=ref; |
|
1095 iState|=EBackup; |
|
1096 header.SetBackupToc(toc); |
|
1097 } |
|
1098 if (!header.IsValid()) // not a permanent store or damaged beyond recognition |
|
1099 __LEAVE(KErrNotSupported); |
|
1100 // |
|
1101 if (toc<0||((handle&~KMaskStreamIdValue)!=0&&handle!=KHandleTocBase)||ref<0||(handle!=0&&ref>=toc+KOffsetTocHeader)) |
|
1102 __LEAVE(KErrCorrupt); // integrity compromised |
|
1103 // |
|
1104 iReloc=handle; |
|
1105 iTarget=ref; |
|
1106 } |
|
1107 // |
|
1108 if (iToc!=0 && iToc!=toc) // refresh produced a different toc |
|
1109 __LEAVE(KErrCorrupt); |
|
1110 iToc=toc; |
|
1111 } |
|
1112 |
|
1113 CPermanentStoreCoord::~CPermanentStoreCoord() |
|
1114 { |
|
1115 __ASSERT_ALWAYS(iRefs==0,Panic(EStoreInUse)); |
|
1116 delete iTable; |
|
1117 } |
|
1118 |
|
1119 void CPermanentStoreCoord::CanExtendL() |
|
1120 { |
|
1121 __ASSERT_DEBUG(IsTrim(),User::Invariant()); |
|
1122 if (iExtend!=0) |
|
1123 __LEAVE(KErrInUse); |
|
1124 // |
|
1125 ConsolidateL(); |
|
1126 } |
|
1127 |
|
1128 TInt CPermanentStoreCoord::DoCreateL() |
|
1129 { |
|
1130 __ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant()); |
|
1131 TInt handle=Table().AllocL(); |
|
1132 iExtend=handle; |
|
1133 Inc(); |
|
1134 ++iAccess; |
|
1135 return handle; |
|
1136 } |
|
1137 |
|
1138 void CPermanentStoreCoord::DoReplaceL(TInt aHandle) |
|
1139 { |
|
1140 __ASSERT_DEBUG(IsTrim()&&iReloc==0&&iExtend==0,User::Invariant()); |
|
1141 TInt off=Table().GetL(aHandle); |
|
1142 const TItem* item=iCache.At(aHandle); |
|
1143 __ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant()); |
|
1144 if (item==NULL) |
|
1145 iCache.Add(aHandle,off,Min(off,0)); |
|
1146 iExtend=aHandle; |
|
1147 Inc(); |
|
1148 ++iAccess; |
|
1149 } |
|
1150 |
|
1151 TInt CPermanentStoreCoord::DoOpenL(TInt& anOffset,TInt aHandle) |
|
1152 { |
|
1153 const TItem* item=iCache.At(aHandle); |
|
1154 __ASSERT_DEBUG(item==NULL||iTable!=NULL&&item->handle==aHandle,User::Invariant()); |
|
1155 TInt off; |
|
1156 TInt ext; |
|
1157 if (item==NULL) |
|
1158 { |
|
1159 off=TableL().AtL(aHandle); |
|
1160 if (iReloc==aHandle) |
|
1161 { |
|
1162 TInt trg=iTarget; |
|
1163 if (trg==off) |
|
1164 iReloc=0; |
|
1165 else |
|
1166 off=trg; |
|
1167 } |
|
1168 ext=Min(off,0); // ensures ext==off for empty streams |
|
1169 iCache.Add(aHandle,off,ext); |
|
1170 } |
|
1171 else |
|
1172 { |
|
1173 off=item->offset; |
|
1174 ext=item->extent; |
|
1175 } |
|
1176 Inc(); |
|
1177 ++iAccess; |
|
1178 anOffset=off; |
|
1179 return ext; |
|
1180 } |
|
1181 |
|
1182 void CPermanentStoreCoord::DoRelease(TInt aHandle,TInt anOffset,TInt anExtent) |
|
1183 { |
|
1184 __ASSERT_DEBUG(aHandle!=0,User::Invariant()); |
|
1185 Dec(); |
|
1186 --iAccess; |
|
1187 if (anExtent==0) |
|
1188 { // failed to commit the extending stream |
|
1189 __ASSERT_DEBUG(aHandle==iExtend&&IsTrim(),User::Invariant()); |
|
1190 iState|=EClip; |
|
1191 iExtend=0; |
|
1192 if (aHandle<0) |
|
1193 Table().Cancel(aHandle); |
|
1194 } |
|
1195 else |
|
1196 { |
|
1197 const TItem* item=iCache.At(aHandle); |
|
1198 if (item!=NULL&&item->offset==anOffset&&anExtent>item->extent) |
|
1199 iCache.Put(item,anOffset,anExtent); |
|
1200 } |
|
1201 } |
|
1202 |
|
1203 TInt CPermanentStoreCoord::DoCommit(TInt aHandle,TInt anOffset,TInt anExtent) |
|
1204 { |
|
1205 __ASSERT_DEBUG(aHandle!=0&&aHandle==iExtend&&(anExtent>=iExt||anOffset==anExtent),User::Invariant()); |
|
1206 aHandle=Table().Set(aHandle,anOffset); |
|
1207 if (anExtent<0) |
|
1208 iCache.Remove(aHandle); |
|
1209 else |
|
1210 { |
|
1211 iExt=anExtent; |
|
1212 const TItem* item=iCache.At(aHandle); |
|
1213 if (item==NULL) |
|
1214 iCache.Add(aHandle,anOffset,anExtent); |
|
1215 else |
|
1216 iCache.Put(item,anOffset,anExtent); |
|
1217 } |
|
1218 iExtend=0; |
|
1219 return aHandle; |
|
1220 } |
|
1221 |
|
1222 CPermanentStoreToc& CPermanentStoreCoord::TableL() |
|
1223 { |
|
1224 CPermanentStoreToc* table=iTable; |
|
1225 if (table==NULL) |
|
1226 { |
|
1227 table=CPermanentStoreToc::NewL(Base(),Host(),iToc,iReloc==KHandleTocBase?iTarget:KFrameNonexistent16); |
|
1228 iExt=table->Extent(); |
|
1229 iTable=table; |
|
1230 } |
|
1231 return *table; |
|
1232 } |
|
1233 |
|
1234 CPermanentStoreToc& CPermanentStoreCoord::ConsolidateL() |
|
1235 { |
|
1236 CPermanentStoreToc& table=TableL(); |
|
1237 if (iReloc!=0) |
|
1238 { |
|
1239 table.PutL(iReloc,iTarget,CPermanentStoreToc::ETestBeforeWrite); |
|
1240 iCache.Relocated(iReloc,iTarget); |
|
1241 iReloc=0; |
|
1242 } |
|
1243 return table; |
|
1244 } |
|
1245 |
|
1246 //After stream relocation, the stream entry in the TOC has to be updated with the new stream position. |
|
1247 //If the file system is not transactional or if the file system is "block atomic", but the stream entry is split |
|
1248 //on a block/sector boundary, then the stream handle will be stored in the permanent file store header, in case |
|
1249 //if the TOC entry update fails. |
|
1250 void CPermanentStoreCoord::RelocateL(TInt aHandle,TInt anOffset) |
|
1251 { |
|
1252 __ASSERT_DEBUG(!Accessed(),User::Invariant()); |
|
1253 __ASSERT_DEBUG(iReloc==0,User::Invariant()); |
|
1254 |
|
1255 TBool updateStoreHeader = ETrue; |
|
1256 TFileQoS fileQos = FileQoSL(); |
|
1257 if(fileQos == ETransactional) |
|
1258 { |
|
1259 updateStoreHeader = EFalse; |
|
1260 } |
|
1261 else if(fileQos == EBlockAtomic) |
|
1262 { |
|
1263 TInt dataLen = 0; |
|
1264 TInt writePos = iTable->RefSpan(aHandle, dataLen); |
|
1265 __ASSERT_DEBUG(writePos >= 0 && dataLen > 0, User::Invariant()); |
|
1266 TInt startSectorAddr = writePos & ~(KDefaultMediaBlockSize - 1); |
|
1267 TInt endSectorAddr = (writePos + dataLen - 1) & ~(KDefaultMediaBlockSize - 1); |
|
1268 if(startSectorAddr == endSectorAddr) |
|
1269 { |
|
1270 updateStoreHeader = EFalse; |
|
1271 } |
|
1272 } |
|
1273 |
|
1274 if (updateStoreHeader) |
|
1275 { |
|
1276 TPermanentStoreHeader header(iToc,aHandle,anOffset); |
|
1277 Host().Share(BeginL(header)); |
|
1278 iReloc=aHandle; |
|
1279 iTarget=anOffset; |
|
1280 } |
|
1281 ++iGen; |
|
1282 iTable->PutL(aHandle,anOffset,CPermanentStoreToc::EWrite); |
|
1283 iCache.Relocated(aHandle,anOffset); |
|
1284 iReloc=0; |
|
1285 } |
|
1286 |
|
1287 void CPermanentStoreCoord::MoveL(TInt aToc,TInt anExtent) |
|
1288 { |
|
1289 __ASSERT_DEBUG(iReloc==0,User::Invariant()); |
|
1290 CPermanentStoreToc& table=Table(); |
|
1291 TPermanentStoreHeader header(aToc); |
|
1292 header.SetBackupToc(iToc); |
|
1293 Host().Share(BeginL(header)); |
|
1294 // update data structures but defer the write |
|
1295 iToc=aToc; |
|
1296 TInt ext=table.Extent(); |
|
1297 table.Move(aToc,anExtent); |
|
1298 iState|=EBackup; |
|
1299 if (iExt==ext) |
|
1300 { |
|
1301 iExt=anExtent; |
|
1302 iState|=EClip; |
|
1303 } |
|
1304 } |
|
1305 |
|
1306 // |
|
1307 // Starts a pseudo-atomic update of the permanent file store header. |
|
1308 // |
|
1309 // For the effect to be 'atomic', writes need to meet the following requirements: |
|
1310 // 1. When updating n bytes using a single write, bytes 2 through n remain unchanged unless the first byte also changes. |
|
1311 // 2. Changes associated with successive write requests happen in strict sequence. |
|
1312 // 3. Updating a single byte is atomic. |
|
1313 // |
|
1314 // Also, a failure to write to a location shall be reported no later than on the next |
|
1315 // write to that or a different location, or on buffer synchronisation. |
|
1316 // |
|
1317 // The preconditions of the operation are: |
|
1318 // - all stream insert/delete/relocate operations completed, file - updated; |
|
1319 // - the TOC reference in the file store header points to the current (valid) TOC, which does not include the most recent |
|
1320 // changes, since the last commit; |
|
1321 // - the in-memory backup TOC reference updated and made equal to the file stored TOC reference; |
|
1322 // |
|
1323 // The procedure consists of 3 "file-write" steps: |
|
1324 // -1- write the backup TOC ref (4 bytes) to the permanent file store header. |
|
1325 // if this operation fails, when the store is reopened, the TOC ref will be used; |
|
1326 // -2- set the dirty bit and write the whole file store header (14 bytes). |
|
1327 // If this operation fails, but the dirty bit has been successfully set, the backup TOC ref will be used, |
|
1328 // when the store is reopened; |
|
1329 // -3- clear the dirty bit (1 byte "file write" op). The commit operation has completed successfully; |
|
1330 MStreamBuf* CPermanentStoreCoord::BeginL(TPermanentStoreHeader& aHeader) |
|
1331 { |
|
1332 __ASSERT_DEBUG(!aHeader.IsDirty()&&aHeader.BackupToc()==iToc&&iReloc==0,User::Invariant()); |
|
1333 MStreamBuf& buf=*Host().HostL(); |
|
1334 buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset); |
|
1335 TFileQoS fileQos = FileQoSL(); |
|
1336 if (fileQos<EBlockAtomic) |
|
1337 { |
|
1338 if (iState&EBackup) |
|
1339 { // we've yet to write the backup toc, do that before clobbering the header |
|
1340 buf.WriteL(aHeader.Ptr(),KPermanentStoreBackupLength); |
|
1341 buf.SeekL(buf.EWrite,EStreamBeginning); |
|
1342 buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset); |
|
1343 iState&=~EBackup; |
|
1344 } |
|
1345 // first write--dirty flag set in the first byte, backup toc unchanged, new header otherwise |
|
1346 aHeader.MarkDirty(); |
|
1347 } |
|
1348 Host().Release(); // from this point onwards any failure results in store shutdown |
|
1349 // |
|
1350 buf.WriteL(aHeader.Ptr(),KPermanentStoreHeaderLength); |
|
1351 if (fileQos<EBlockAtomic) |
|
1352 { |
|
1353 buf.SeekL(buf.EWrite,EStreamBeginning); |
|
1354 buf.SeekL(buf.EWrite,Base()+KPermanentStoreHeaderOffset); |
|
1355 aHeader.SetBackupToc(iToc); |
|
1356 // second write--single byte write to clear the dirty flag, no change otherwise |
|
1357 buf.WriteL(aHeader.Ptr(),1); |
|
1358 } |
|
1359 // at this point synchronisation is atomic; ie, if successful the change |
|
1360 // has been recorded, and failure means it will never happen |
|
1361 return &buf; |
|
1362 } |
|
1363 |
|
1364 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
1365 ///////////////////// HPermanentStoreBuf //////////////////////////////////////////////////////// |
|
1366 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
1367 |
|
1368 HPermanentStoreBuf* HPermanentStoreBuf::CreateL(CPermanentStoreCoord& aCoord,TStreamId& anId,TInt aMode) |
|
1369 { |
|
1370 HPermanentStoreBuf* buf=ExtendLC(aCoord,aMode); |
|
1371 TInt handle=aCoord.DoCreateL(); |
|
1372 buf->iHandle=handle; |
|
1373 CleanupStack::Pop(); |
|
1374 anId=TStreamId(handle&KMaskStreamIdValue); |
|
1375 return buf; |
|
1376 } |
|
1377 |
|
1378 HPermanentStoreBuf* HPermanentStoreBuf::ReplaceL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode) |
|
1379 { |
|
1380 HPermanentStoreBuf* buf=ExtendLC(aCoord,aMode); |
|
1381 TInt handle=anId.Value(); |
|
1382 aCoord.DoReplaceL(handle); |
|
1383 buf->iHandle=handle; |
|
1384 CleanupStack::Pop(); |
|
1385 return buf; |
|
1386 } |
|
1387 |
|
1388 HPermanentStoreBuf* HPermanentStoreBuf::OpenL(CPermanentStoreCoord& aCoord,TStreamId anId,TInt aMode) |
|
1389 { |
|
1390 HPermanentStoreBuf* buf=NewLC(aCoord); |
|
1391 TInt handle=anId.Value(); |
|
1392 TInt off=KFrameNonexistent16; |
|
1393 TInt ext=aCoord.DoOpenL(off,handle); |
|
1394 buf->iHandle=handle; |
|
1395 if (ext!=0) |
|
1396 buf->RFrame16Buf::Set(aCoord.Host(),off,ext,EFrameData16|aMode); |
|
1397 else |
|
1398 buf->RFrame16Buf::OpenL(aCoord.Host(),off,EFrameData16|aMode); |
|
1399 CleanupStack::Pop(); |
|
1400 return buf; |
|
1401 } |
|
1402 |
|
1403 HPermanentStoreBuf::~HPermanentStoreBuf() |
|
1404 { |
|
1405 TInt handle=iHandle; |
|
1406 if (handle!=0) |
|
1407 Coord().DoRelease(handle,Offset(),Extent()); |
|
1408 RFrame16Buf::DoRelease(); |
|
1409 } |
|
1410 |
|
1411 HPermanentStoreBuf* HPermanentStoreBuf::NewLC(CPermanentStoreCoord& aCoord) |
|
1412 { |
|
1413 HPermanentStoreBuf* buf=new(ELeave) HPermanentStoreBuf(aCoord); |
|
1414 buf->PushL(); |
|
1415 return buf; |
|
1416 } |
|
1417 |
|
1418 HPermanentStoreBuf* HPermanentStoreBuf::ExtendLC(CPermanentStoreCoord& aCoord,TInt aMode) |
|
1419 { |
|
1420 aCoord.CanExtendL(); |
|
1421 HPermanentStoreBuf* buf=NewLC(aCoord); |
|
1422 buf->RFrame16Buf::ExtendL(aCoord.Host(),aCoord.iExt,EFrameData16|aMode); |
|
1423 __ASSERT_DEBUG(TStreamPos(aCoord.Host().SizeL())==buf->Position(buf->Offset()),User::Invariant()); |
|
1424 return buf; |
|
1425 } |
|
1426 |
|
1427 void HPermanentStoreBuf::DoRelease() |
|
1428 { |
|
1429 delete this; |
|
1430 } |
|
1431 |
|
1432 void HPermanentStoreBuf::DoSynchL() |
|
1433 { |
|
1434 __ASSERT_DEBUG(iHandle!=0,User::Invariant()); |
|
1435 if (IsCommitted()) |
|
1436 return; |
|
1437 // |
|
1438 CommitL(); |
|
1439 iHandle=Coord().DoCommit(iHandle,Offset(),Extent()); |
|
1440 } |
|
1441 |