|
1 // Copyright (c) 1996-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 the License "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 // f32\sfat32\fat_table32.cpp |
|
15 // FAT32 File Allocation Table classes implementation |
|
16 // |
|
17 // |
|
18 |
|
19 /** |
|
20 @file |
|
21 @internalTechnology |
|
22 */ |
|
23 |
|
24 |
|
25 |
|
26 #include "sl_std.h" |
|
27 #include "sl_fatcache32.h" |
|
28 #include "fat_table32.h" |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
34 /** |
|
35 Implements automatic locking object. |
|
36 Calls TFatDriveInterface::AcquireLock() on construction and TFatDriveInterface::ReleaseLock() on destruction. |
|
37 Can be constructed on the stack only. |
|
38 */ |
|
39 class XAutoLock |
|
40 { |
|
41 public: |
|
42 inline XAutoLock(CFatMountCB* apOwner) : iDrv(apOwner->DriveInterface()) {iDrv.AcquireLock();} |
|
43 inline XAutoLock(TFatDriveInterface& aDrv) : iDrv(aDrv) {iDrv.AcquireLock();} |
|
44 inline ~XAutoLock() {iDrv.ReleaseLock();} |
|
45 |
|
46 private: |
|
47 void* operator new(TUint); //-- disable creating objects on heap. |
|
48 void* operator new(TUint, void*); |
|
49 |
|
50 private: |
|
51 TFatDriveInterface &iDrv; ///< reference to the drive interface |
|
52 }; |
|
53 |
|
54 |
|
55 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
56 |
|
57 |
|
58 |
|
59 //####################################################################################################################################### |
|
60 //# CFatTable class implementation |
|
61 //####################################################################################################################################### |
|
62 |
|
63 /** |
|
64 FAT object factory method. |
|
65 Constructs either CAtaFatTable or CRamFatTable depending on the media type parameter |
|
66 |
|
67 @param aOwner Pointer to the owning mount |
|
68 @param aLocDrvCaps local drive attributes |
|
69 @leave KErrNoMemory |
|
70 @return Pointer to the Fat table |
|
71 */ |
|
72 CFatTable* CFatTable::NewL(CFatMountCB& aOwner, const TLocalDriveCaps& aLocDrvCaps) |
|
73 { |
|
74 CFatTable* pFatTable=NULL; |
|
75 |
|
76 switch(aLocDrvCaps.iType) |
|
77 { |
|
78 case EMediaRam: |
|
79 {//-- this is RAM media, try to create CRamFatTable instance. |
|
80 const TFatType fatType = aOwner.FatType(); |
|
81 |
|
82 if(fatType != EFat16 && fatType != EFat32) |
|
83 {//-- CRamFatTable doesn't support FAT12, FAT16 & FAT32 only. |
|
84 __PRINT1(_L("CFatTable::NewL() CRamFatTable doesn't support this FAT type:%d"), fatType); |
|
85 ASSERT(0); |
|
86 return NULL; |
|
87 } |
|
88 |
|
89 pFatTable = CRamFatTable::NewL(aOwner); |
|
90 } |
|
91 break; |
|
92 |
|
93 default: |
|
94 //-- other media |
|
95 pFatTable = CAtaFatTable::NewL(aOwner); |
|
96 break; |
|
97 }; |
|
98 |
|
99 return pFatTable; |
|
100 } |
|
101 |
|
102 |
|
103 CFatTable::CFatTable(CFatMountCB& aOwner) |
|
104 { |
|
105 iOwner = &aOwner; |
|
106 ASSERT(iOwner); |
|
107 } |
|
108 |
|
109 CFatTable::~CFatTable() |
|
110 { |
|
111 //-- destroy cache ignoring dirty data in cache |
|
112 //-- the destructor isn't an appropriate place to flush the data. |
|
113 Dismount(ETrue); |
|
114 } |
|
115 |
|
116 //----------------------------------------------------------------------------- |
|
117 |
|
118 /** |
|
119 Initialise the object, get data from the owning CFatMountCB |
|
120 */ |
|
121 void CFatTable::InitializeL() |
|
122 { |
|
123 ASSERT(iOwner); |
|
124 |
|
125 //-- get FAT type from the owner |
|
126 iFatType = iOwner->FatType(); |
|
127 ASSERT(IsFat12() || IsFat16() || IsFat32()); |
|
128 |
|
129 iFreeClusterHint = KFatFirstSearchCluster; |
|
130 |
|
131 //-- cache the media attributes |
|
132 TLocalDriveCapsV2 caps; |
|
133 TPckg<TLocalDriveCapsV2> capsPckg(caps); |
|
134 User::LeaveIfError(iOwner->LocalDrive()->Caps(capsPckg)); |
|
135 iMediaAtt = caps.iMediaAtt; |
|
136 |
|
137 //-- obtain maximal number of entries in the table |
|
138 iMaxEntries = iOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use |
|
139 |
|
140 __PRINT3(_L("CFatTable::InitializeL(), drv:%d, iMediaAtt = %08X, max Entries:%d"), iOwner->DriveNumber(), iMediaAtt, iMaxEntries); |
|
141 } |
|
142 |
|
143 //----------------------------------------------------------------------------- |
|
144 |
|
145 /** |
|
146 Decrements the free cluster count. |
|
147 Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every |
|
148 cluster of a large file. Use more than one cluster granularity. |
|
149 |
|
150 @param aCount a number of clusters |
|
151 */ |
|
152 void CFatTable::DecrementFreeClusterCount(TUint32 aCount) |
|
153 { |
|
154 __ASSERT_DEBUG(iFreeClusters >= aCount, Fault(EFatCorrupt)); |
|
155 iFreeClusters -= aCount; |
|
156 } |
|
157 |
|
158 /** |
|
159 Increments the free cluster count. |
|
160 Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every |
|
161 cluster of a large file. Use more than one cluster granularity. |
|
162 |
|
163 @param aCount a number of clusters |
|
164 */ |
|
165 void CFatTable::IncrementFreeClusterCount(TUint32 aCount) |
|
166 { |
|
167 const TUint32 newVal = iFreeClusters+aCount; |
|
168 __ASSERT_DEBUG(newVal<=MaxEntries(), Fault(EFatCorrupt)); |
|
169 |
|
170 iFreeClusters = newVal; |
|
171 } |
|
172 |
|
173 /** @return number of free clusters in the FAT */ |
|
174 TUint32 CFatTable::NumberOfFreeClusters(TBool /*aSyncOperation=EFalse*/) const |
|
175 { |
|
176 return FreeClusters(); |
|
177 } |
|
178 |
|
179 void CFatTable::SetFreeClusters(TUint32 aFreeClusters) |
|
180 { |
|
181 iFreeClusters=aFreeClusters; |
|
182 } |
|
183 |
|
184 /** |
|
185 Get the hint about the last known free cluster number. |
|
186 Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every |
|
187 cluster of a large file. |
|
188 |
|
189 @return cluster number supposedly close to the free one. |
|
190 */ |
|
191 TUint32 CFatTable::FreeClusterHint() const |
|
192 { |
|
193 ASSERT(ClusterNumberValid(iFreeClusterHint)); |
|
194 return iFreeClusterHint; |
|
195 } |
|
196 |
|
197 /** |
|
198 Set a free cluster hint. The next search fro the free cluster can start from this value. |
|
199 aCluster doesn't have to be a precise number of free FAT entry; it just needs to be as close as possible to the |
|
200 free entries chain. |
|
201 Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every |
|
202 cluster of a large file. |
|
203 |
|
204 @param aCluster cluster number hint. |
|
205 */ |
|
206 void CFatTable::SetFreeClusterHint(TUint32 aCluster) |
|
207 { |
|
208 ASSERT(ClusterNumberValid(aCluster)); |
|
209 iFreeClusterHint=aCluster; |
|
210 } |
|
211 |
|
212 //----------------------------------------------------------------------------- |
|
213 |
|
214 /** |
|
215 Find out the number of free clusters on the volume. |
|
216 Reads whole FAT and counts free clusters. |
|
217 */ |
|
218 void CFatTable::CountFreeClustersL() |
|
219 { |
|
220 __PRINT1(_L("#- CFatTable::CountFreeClustersL(), drv:%d"), iOwner->DriveNumber()); |
|
221 |
|
222 const TUint32 KUsableClusters = iOwner->UsableClusters(); |
|
223 (void)KUsableClusters; |
|
224 |
|
225 TUint32 freeClusters = 0; |
|
226 TUint32 firstFreeCluster = 0; |
|
227 |
|
228 TTime timeStart; |
|
229 TTime timeEnd; |
|
230 timeStart.UniversalTime(); //-- take start time |
|
231 |
|
232 //-- walk through whole FAT table looking for free clusters |
|
233 for(TUint i=KFatFirstSearchCluster; i<MaxEntries(); ++i) |
|
234 { |
|
235 if(ReadL(i) == KSpareCluster) |
|
236 {//-- found a free cluster |
|
237 ++freeClusters; |
|
238 |
|
239 if(!firstFreeCluster) |
|
240 firstFreeCluster = i; |
|
241 } |
|
242 } |
|
243 |
|
244 timeEnd.UniversalTime(); //-- take end time |
|
245 const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec); |
|
246 __PRINT1(_L("#- CFatTable::CountFreeClustersL() finished. Taken:%d ms"), msScanTime); |
|
247 (void)msScanTime; |
|
248 |
|
249 if(!firstFreeCluster) //-- haven't found free clusters on the volume |
|
250 firstFreeCluster = KFatFirstSearchCluster; |
|
251 |
|
252 ASSERT(freeClusters <= KUsableClusters); |
|
253 |
|
254 SetFreeClusters(freeClusters); |
|
255 SetFreeClusterHint(firstFreeCluster); |
|
256 } |
|
257 |
|
258 //----------------------------------------------------------------------------- |
|
259 |
|
260 /** |
|
261 Count the number of contiguous cluster from a start cluster |
|
262 |
|
263 @param aStartCluster cluster to start counting from |
|
264 @param anEndCluster contains the end cluster number upon return |
|
265 @param aMaxCount Maximum cluster required |
|
266 @leave System wide error values |
|
267 @return Number of contiguous clusters from aStartCluster. |
|
268 */ |
|
269 TInt CFatTable::CountContiguousClustersL(TUint32 aStartCluster,TInt& anEndCluster,TUint32 aMaxCount) const |
|
270 { |
|
271 __PRINT2(_L("CFatTable::CountContiguousClustersL() start:%d, max:%d"),aStartCluster, aMaxCount); |
|
272 TUint32 clusterListLen=1; |
|
273 TInt endCluster=aStartCluster; |
|
274 TInt64 endClusterPos=DataPositionInBytes(endCluster); |
|
275 while (clusterListLen<aMaxCount) |
|
276 { |
|
277 TInt oldCluster=endCluster; |
|
278 TInt64 oldClusterPos=endClusterPos; |
|
279 if (GetNextClusterL(endCluster)==EFalse || (endClusterPos=DataPositionInBytes(endCluster))!=(oldClusterPos+(1<<iOwner->ClusterSizeLog2()))) |
|
280 { |
|
281 endCluster=oldCluster; |
|
282 break; |
|
283 } |
|
284 clusterListLen++; |
|
285 } |
|
286 anEndCluster=endCluster; |
|
287 return(clusterListLen); |
|
288 } |
|
289 |
|
290 //----------------------------------------------------------------------------- |
|
291 |
|
292 /** |
|
293 Extend a file or directory cluster chain, leaves if there are no free clusters (the disk is full). |
|
294 |
|
295 @param aNumber amount of clusters to allocate |
|
296 @param aCluster FAT entry index to start with. |
|
297 |
|
298 @leave KErrDiskFull + system wide error codes |
|
299 */ |
|
300 void CFatTable::ExtendClusterListL(TUint32 aNumber,TInt& aCluster) |
|
301 { |
|
302 __PRINT2(_L("CFatTable::ExtendClusterListL() num:%d, clust:%d"), aNumber, aCluster); |
|
303 __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter)); |
|
304 |
|
305 while(aNumber && GetNextClusterL(aCluster)) |
|
306 aNumber--; |
|
307 |
|
308 if(!aNumber) |
|
309 return; |
|
310 |
|
311 if(!RequestFreeClusters(aNumber)) |
|
312 { |
|
313 __PRINT(_L("CFatTable::ExtendClusterListL - leaving KErrDirFull")); |
|
314 User::Leave(KErrDiskFull); |
|
315 } |
|
316 |
|
317 |
|
318 TUint32 freeCluster = 0; |
|
319 |
|
320 //-- note: this can be impoved by trying to fing as long chain of free clusters as possible in FindClosestFreeClusterL() |
|
321 for(TUint i=0; i<aNumber; ++i) |
|
322 { |
|
323 freeCluster = FindClosestFreeClusterL(aCluster); |
|
324 WriteFatEntryEofL(freeCluster); // Must write EOF for FindClosestFreeCluster to work again |
|
325 WriteL(aCluster,freeCluster); |
|
326 aCluster=freeCluster; |
|
327 } |
|
328 |
|
329 //-- decrement number of available clusters |
|
330 DecrementFreeClusterCount(aNumber); |
|
331 |
|
332 //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from |
|
333 SetFreeClusterHint(aCluster); |
|
334 |
|
335 } |
|
336 |
|
337 //----------------------------------------------------------------------------- |
|
338 |
|
339 /** |
|
340 Allocate and mark as EOF a single cluster as close as possible to aNearestCluster |
|
341 |
|
342 @param aNearestCluster Cluster the new cluster should be nearest to |
|
343 @leave System wide error codes |
|
344 @return The cluster number allocated |
|
345 */ |
|
346 TUint32 CFatTable::AllocateSingleClusterL(TUint32 aNearestCluster) |
|
347 { |
|
348 __PRINT1(_L("CFatTable::AllocateSingleCluster() nearest:%d"), aNearestCluster); |
|
349 |
|
350 const TInt freeCluster=FindClosestFreeClusterL(aNearestCluster); |
|
351 WriteFatEntryEofL(freeCluster); |
|
352 DecrementFreeClusterCount(1); |
|
353 |
|
354 //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from. |
|
355 SetFreeClusterHint(freeCluster); |
|
356 |
|
357 return(freeCluster); |
|
358 } |
|
359 |
|
360 //----------------------------------------------------------------------------- |
|
361 |
|
362 /** |
|
363 Allocate and link a cluster chain, leaves if there are not enough free clusters. |
|
364 Chain starts as close as possible to aNearestCluster, last cluster will be marked as EOF. |
|
365 |
|
366 @param aNumber Number of clusters to allocate |
|
367 @param aNearestCluster Cluster the new chain should be nearest to |
|
368 @leave System wide error codes |
|
369 @return The first cluster number allocated |
|
370 */ |
|
371 TUint32 CFatTable::AllocateClusterListL(TUint32 aNumber, TUint32 aNearestCluster) |
|
372 { |
|
373 __PRINT2(_L("CFatTable::AllocateClusterList() N:%d,NearestCL:%d"),aNumber,aNearestCluster); |
|
374 __ASSERT_DEBUG(aNumber>0, Fault(EFatBadParameter)); |
|
375 |
|
376 if(!RequestFreeClusters(aNumber)) |
|
377 { |
|
378 __PRINT(_L("CFatTable::AllocateClusterListL - leaving KErrDirFull")); |
|
379 User::Leave(KErrDiskFull); |
|
380 } |
|
381 |
|
382 TInt firstCluster = aNearestCluster = AllocateSingleClusterL(aNearestCluster); |
|
383 if (aNumber>1) |
|
384 ExtendClusterListL(aNumber-1, (TInt&)aNearestCluster); |
|
385 |
|
386 return(firstCluster); |
|
387 } |
|
388 |
|
389 //----------------------------------------------------------------------------- |
|
390 |
|
391 /** |
|
392 Notify the media drive about media areas that shall be treated as "deleted" if this feature is supported. |
|
393 @param aFreedClusters array with FAT numbers of clusters that shall be marked as "deleted" |
|
394 */ |
|
395 void CFatTable::DoFreedClustersNotify(RClusterArray &aFreedClusters) |
|
396 { |
|
397 ASSERT(iMediaAtt & KMediaAttDeleteNotify); |
|
398 |
|
399 const TUint clusterCount = aFreedClusters.Count(); |
|
400 |
|
401 if(!clusterCount) |
|
402 return; |
|
403 |
|
404 FlushL(); //-- Commit the FAT changes to disk first to be safe |
|
405 |
|
406 const TUint bytesPerCluster = 1 << iOwner->ClusterSizeLog2(); |
|
407 |
|
408 TInt64 byteAddress = 0; |
|
409 TUint deleteLen = 0; // zero indicates no clusters accumulated yet |
|
410 |
|
411 for (TUint i=0; i<clusterCount; ++i) |
|
412 { |
|
413 const TUint currCluster = aFreedClusters[i]; |
|
414 |
|
415 if (deleteLen == 0) |
|
416 byteAddress = DataPositionInBytes(currCluster); //-- start of the media range |
|
417 |
|
418 deleteLen += bytesPerCluster; |
|
419 |
|
420 //-- if this is the last entry in the array or the net cluster number is not consecutive, notify the driver |
|
421 if ((i+1) == clusterCount || aFreedClusters[i+1] != (currCluster+1)) |
|
422 { |
|
423 //__PRINT3(_L("DeleteNotify(%08X:%08X, %u), first cluster %u last cluster #%u"), I64HIGH(byteAddress), I64LOW(byteAddress), deleteLen); |
|
424 //__PRINT2(_L(" first cluster %u last cluster #%u"), I64LOW((byteAddress - iOwner->ClusterBasePosition()) >> iOwner->ClusterSizeLog2()) + 2, cluster); |
|
425 |
|
426 const TInt r = iOwner->LocalDrive()->DeleteNotify(byteAddress, deleteLen); |
|
427 if(r != KErrNone) |
|
428 {//-- if DeleteNotify() failed, it means that something terribly wrong happened to the NAND media; |
|
429 //-- in normal circumstances it can not happen. One of the reasons: totally worn out media. |
|
430 const TBool platSecEnabled = PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement); |
|
431 __PRINT3(_L("CFatTable::DoFreedClustersNotify() DeleteNotify failure! drv:%d err:%d, PlatSec:%d"),iOwner->DriveNumber(), r, platSecEnabled); |
|
432 |
|
433 if(platSecEnabled) |
|
434 { |
|
435 //-- if PlatSec is enabled, we can't afford jeopardize the security; without DeleteNotify() |
|
436 //-- it's possible to pick up data from deleted files, so, panic the file server. |
|
437 Fault(EFatBadLocalDrive); |
|
438 } |
|
439 else |
|
440 { |
|
441 //-- if PlatSec is disabled, it's OK to ignore the NAND fault in release mode. |
|
442 __ASSERT_DEBUG(0, Fault(EFatBadLocalDrive)); |
|
443 } |
|
444 } |
|
445 |
|
446 |
|
447 deleteLen = 0; |
|
448 } |
|
449 |
|
450 } |
|
451 |
|
452 //-- empty the array. |
|
453 aFreedClusters.Reset(); |
|
454 } |
|
455 |
|
456 //----------------------------------------------------------------------------- |
|
457 /** |
|
458 Mark a chain of clusters as free in the FAT. |
|
459 |
|
460 @param aCluster Start cluster of cluster chain to free |
|
461 @leave System wide error codes |
|
462 */ |
|
463 void CFatTable::FreeClusterListL(TUint32 aCluster) |
|
464 { |
|
465 __PRINT1(_L("CFatTable::FreeClusterListL startCluster=%d"),aCluster); |
|
466 if (aCluster == KSpareCluster) |
|
467 return; |
|
468 |
|
469 //-- here we can store array of freed cluster numbers in order to |
|
470 //-- notify media drive about the media addresses marked as "invalid" |
|
471 RClusterArray deletedClusters; |
|
472 CleanupClosePushL(deletedClusters); |
|
473 |
|
474 //-- if ETrue, we need to notify media driver about invalidated media addressses |
|
475 const TBool bFreeClustersNotify = iMediaAtt & KMediaAttDeleteNotify; |
|
476 |
|
477 //-- this is a maximal number of FAT entries in the deletedClusters array. |
|
478 //-- as soon as we collect this number of entries in the array, FAT cache will be flushed |
|
479 //-- and driver notified. The array will be emptied. Used to avoid huge array when deleting |
|
480 //-- large files on NAND media |
|
481 const TUint KSubListLen = 4096; |
|
482 ASSERT(IsPowerOf2(KSubListLen)); |
|
483 |
|
484 TUint32 lastKnownFreeCluster = FreeClusterHint(); |
|
485 TUint32 cntFreedClusters = 0; |
|
486 |
|
487 TUint32 currCluster = aCluster; |
|
488 TInt nextCluster = aCluster; |
|
489 |
|
490 for(;;) |
|
491 { |
|
492 const TBool bEOF = !GetNextClusterL(nextCluster); |
|
493 WriteL(currCluster, KSpareCluster); |
|
494 |
|
495 lastKnownFreeCluster = Min(currCluster, lastKnownFreeCluster); |
|
496 |
|
497 // Keep a record of the deleted clusters so that we can subsequently notify the media driver. This is only safe |
|
498 // to do once the FAT changes have been written to disk. |
|
499 if(bFreeClustersNotify) |
|
500 deletedClusters.Append(currCluster); |
|
501 |
|
502 ++cntFreedClusters; |
|
503 currCluster = nextCluster; |
|
504 |
|
505 if (bEOF || aCluster == KSpareCluster) |
|
506 break; |
|
507 |
|
508 if(bFreeClustersNotify && cntFreedClusters && (cntFreedClusters & (KSubListLen-1))==0) |
|
509 {//-- reached a limit of the entries in the array. Flush FAT cache, notify the driver and empty the array. |
|
510 IncrementFreeClusterCount(cntFreedClusters); |
|
511 cntFreedClusters = 0; |
|
512 |
|
513 SetFreeClusterHint(lastKnownFreeCluster); |
|
514 DoFreedClustersNotify(deletedClusters); |
|
515 } |
|
516 |
|
517 } |
|
518 |
|
519 //-- increase the number of free clusters and notify the driver if required. |
|
520 IncrementFreeClusterCount(cntFreedClusters); |
|
521 SetFreeClusterHint(lastKnownFreeCluster); |
|
522 |
|
523 if(bFreeClustersNotify) |
|
524 DoFreedClustersNotify(deletedClusters); |
|
525 |
|
526 CleanupStack::PopAndDestroy(&deletedClusters); |
|
527 } |
|
528 |
|
529 //----------------------------------------------------------------------------- |
|
530 |
|
531 /** |
|
532 Find a free cluster nearest to aCluster, Always checks to the right of aCluster first |
|
533 but checks in both directions in the Fat. |
|
534 |
|
535 @param aCluster Cluster to find nearest free cluster to. |
|
536 @leave KErrDiskFull + system wide error codes |
|
537 @return cluster number found |
|
538 */ |
|
539 TUint32 CFatTable::FindClosestFreeClusterL(TUint32 aCluster) |
|
540 { |
|
541 __PRINT2(_L("CFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster); |
|
542 |
|
543 if(!ClusterNumberValid(aCluster)) |
|
544 { |
|
545 ASSERT(0); |
|
546 User::Leave(KErrCorrupt); |
|
547 } |
|
548 |
|
549 if(!RequestFreeClusters(1)) |
|
550 {//-- there is no at least 1 free cluster available |
|
551 __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1")); |
|
552 User::Leave(KErrDiskFull); |
|
553 } |
|
554 |
|
555 //-- 1. look if the given index contains a free entry |
|
556 if(ReadL(aCluster) != KSpareCluster) |
|
557 {//-- no, it doesn't... |
|
558 |
|
559 //-- 2. look in both directions starting from the aCluster, looking in the right direction first |
|
560 |
|
561 const TUint32 maxEntries = MaxEntries(); |
|
562 const TUint32 MinIdx = KFatFirstSearchCluster; |
|
563 const TUint32 MaxIdx = maxEntries-1; |
|
564 |
|
565 TBool canGoRight = ETrue; |
|
566 TBool canGoLeft = ETrue; |
|
567 |
|
568 TUint32 rightIdx = aCluster; |
|
569 TUint32 leftIdx = aCluster; |
|
570 |
|
571 for(TUint i=0; i<maxEntries; ++i) |
|
572 { |
|
573 if(canGoRight) |
|
574 { |
|
575 if(rightIdx < MaxIdx) |
|
576 ++rightIdx; |
|
577 else |
|
578 canGoRight = EFalse; |
|
579 } |
|
580 |
|
581 if(canGoLeft) |
|
582 { |
|
583 if(leftIdx > MinIdx) |
|
584 --leftIdx; |
|
585 else |
|
586 canGoLeft = EFalse; |
|
587 } |
|
588 |
|
589 if(!canGoRight && !canGoLeft) |
|
590 { |
|
591 __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #2")); |
|
592 User::Leave(KErrDiskFull); |
|
593 } |
|
594 |
|
595 if(canGoRight && ReadL(rightIdx) == KSpareCluster) |
|
596 { |
|
597 aCluster = rightIdx; |
|
598 break; |
|
599 } |
|
600 |
|
601 if (canGoLeft && ReadL(leftIdx) == KSpareCluster) |
|
602 { |
|
603 aCluster = leftIdx; |
|
604 break; |
|
605 } |
|
606 }//for(..) |
|
607 |
|
608 }//if(ReadL(aCluster) != KSpareCluster) |
|
609 |
|
610 |
|
611 //-- note: do not update free cluster hint here by calling SetFreeClusterHint(). This is going to be |
|
612 //-- expensive especially if overridden methods with synchronisation are called. Instead, set the number of |
|
613 //-- the last known free cluster in the caller of this internal method. |
|
614 |
|
615 //__PRINT1(_L("CFatTable::FindClosestFreeClusterL found:%d"),aCluster); |
|
616 |
|
617 return aCluster; |
|
618 } |
|
619 |
|
620 //----------------------------------------------------------------------------- |
|
621 |
|
622 /** |
|
623 Converts a cluster number to byte offset in the FAT |
|
624 |
|
625 @param aFatIndex Cluster number |
|
626 @return Number of bytes from the beginning of the FAT |
|
627 */ |
|
628 TUint32 CFatTable::PosInBytes(TUint32 aFatIndex) const |
|
629 { |
|
630 switch(FatType()) |
|
631 { |
|
632 case EFat12: |
|
633 return (((aFatIndex>>1)<<1) + (aFatIndex>>1)); //-- 1.5 bytes per FAT entry |
|
634 |
|
635 case EFat16: |
|
636 return aFatIndex<<1; //-- 2 bytes per FAT entry |
|
637 |
|
638 case EFat32: |
|
639 return aFatIndex<<2; //-- 4 bytes per FAT entry |
|
640 |
|
641 default: |
|
642 ASSERT(0); |
|
643 return 0;//-- get rid of warning |
|
644 }; |
|
645 |
|
646 } |
|
647 |
|
648 //----------------------------------------------------------------------------- |
|
649 |
|
650 /** |
|
651 Checks if we have at least aClustersRequired clusters free in the FAT. |
|
652 This is, actually a dummy implementation. |
|
653 |
|
654 @param aClustersRequired number of free clusters required |
|
655 @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise. |
|
656 */ |
|
657 TBool CFatTable::RequestFreeClusters(TUint32 aClustersRequired) const |
|
658 { |
|
659 //__PRINT1(_L("#- CFatTable::RequestFreeClusters(%d)"),aClustersRequired); |
|
660 ASSERT(aClustersRequired >0); |
|
661 return (NumberOfFreeClusters() >= aClustersRequired); |
|
662 } |
|
663 |
|
664 //----------------------------------------------------------------------------- |
|
665 /** |
|
666 @return ETrue if the cluster number aClusterNo is valid, i.e. belongs to the FAT table |
|
667 */ |
|
668 TBool CFatTable::ClusterNumberValid(TUint32 aClusterNo) const |
|
669 { |
|
670 return (aClusterNo >= KFatFirstSearchCluster) && (aClusterNo < iMaxEntries); |
|
671 } |
|
672 |
|
673 |
|
674 |
|
675 //####################################################################################################################################### |
|
676 //# CAtaFatTable class implementation |
|
677 //####################################################################################################################################### |
|
678 |
|
679 /** |
|
680 Constructor |
|
681 */ |
|
682 CAtaFatTable::CAtaFatTable(CFatMountCB& aOwner) |
|
683 :CFatTable(aOwner), iDriveInteface(aOwner.DriveInterface()) |
|
684 { |
|
685 iState = ENotInitialised; |
|
686 } |
|
687 |
|
688 |
|
689 CAtaFatTable::~CAtaFatTable() |
|
690 { |
|
691 DestroyHelperThread(); |
|
692 } |
|
693 |
|
694 |
|
695 /** factory method */ |
|
696 CAtaFatTable* CAtaFatTable::NewL(CFatMountCB& aOwner) |
|
697 { |
|
698 __PRINT1(_L("CAtaFatTable::NewL() drv:%d"),aOwner.DriveNumber()); |
|
699 CAtaFatTable* pSelf = new (ELeave) CAtaFatTable(aOwner); |
|
700 |
|
701 CleanupStack::PushL(pSelf); |
|
702 pSelf->InitializeL(); |
|
703 CleanupStack::Pop(); |
|
704 |
|
705 return pSelf; |
|
706 } |
|
707 |
|
708 |
|
709 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
710 |
|
711 /** |
|
712 CAtaFatTable's FAT cache factory method. |
|
713 Creates fixed cache for FAT12/FAT16 or LRU cache for FAT32 |
|
714 */ |
|
715 void CAtaFatTable::CreateCacheL() |
|
716 { |
|
717 ASSERT(iOwner); |
|
718 const TUint32 fatSize=iOwner->FatSizeInBytes(); |
|
719 __PRINT3(_L("CAtaFatTable::CreateCacheL drv:%d, FAT:%d, FAT Size:%d"), iOwner->DriveNumber(), FatType(), fatSize); |
|
720 |
|
721 |
|
722 //-- according to FAT specs: |
|
723 //-- FAT12 max size is 4084 entries or 6126 bytes => create fixed cache for whole FAT |
|
724 //-- FAT16 min size is 4085 entries or 8170 bytes, max size is 65525 entries or 131048 bytes => create fixed cache for whole FAT |
|
725 //-- FAT32 min size is 65526 entries or 262104 bytes => create LRU paged cache of max size: KFat32LRUCacheSize |
|
726 |
|
727 ASSERT(!iCache); |
|
728 |
|
729 //-- this is used for chaches granularity sanity check |
|
730 const TUint32 KMinGranularityLog2 = KDefSectorSzLog2; //-- 512 bytes is a minimal allowed granularity |
|
731 const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity |
|
732 |
|
733 switch(FatType()) |
|
734 { |
|
735 //-- create fixed FAT12 cache |
|
736 case EFat12: |
|
737 iCache = CFat12Cache::NewL(iOwner, fatSize); |
|
738 break; |
|
739 |
|
740 //-- create fixed FAT16 cache |
|
741 case EFat16: |
|
742 { |
|
743 TUint32 fat16_ReadGranularity_Log2; //-- FAT16 cache read granularity Log2 |
|
744 TUint32 fat16_WriteGranularity_Log2;//-- FAT16 cache write granularity Log2 |
|
745 |
|
746 iOwner->FatConfig().Fat16FixedCacheParams(fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2); |
|
747 |
|
748 //-- check if granularity values look sensible |
|
749 const TBool bParamsValid = fat16_ReadGranularity_Log2 >= KMinGranularityLog2 && fat16_ReadGranularity_Log2 <= KMaxGranularityLog2 && |
|
750 fat16_WriteGranularity_Log2 >= KMinGranularityLog2 && fat16_WriteGranularity_Log2 <= KMaxGranularityLog2; |
|
751 |
|
752 __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); |
|
753 |
|
754 |
|
755 iCache = CFat16FixedCache::NewL(iOwner, fatSize, fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2); |
|
756 } |
|
757 break; |
|
758 |
|
759 //-- create FAT32 LRU paged cache |
|
760 case EFat32: |
|
761 { |
|
762 TUint32 fat32_LRUCache_MaxMemSize; //-- Maximum memory for the LRU FAT32 cache |
|
763 TUint32 fat32_ReadGranularity_Log2; //-- FAT32 cache read granularity Log2 |
|
764 TUint32 fat32_WriteGranularity_Log2;//-- FAT32 cache write granularity Log2 |
|
765 |
|
766 iOwner->FatConfig().Fat32LruCacheParams(fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2, fat32_LRUCache_MaxMemSize); |
|
767 |
|
768 |
|
769 //-- check if granularity and required cache size values look sensible |
|
770 const TBool bParamsValid = fat32_ReadGranularity_Log2 >= KMinGranularityLog2 && fat32_ReadGranularity_Log2 <= KMaxGranularityLog2 && |
|
771 fat32_WriteGranularity_Log2 >= KMinGranularityLog2 && fat32_WriteGranularity_Log2 <= KMaxGranularityLog2 && |
|
772 fat32_LRUCache_MaxMemSize >= 8*K1KiloByte && fat32_LRUCache_MaxMemSize < 4*K1MegaByte; |
|
773 |
|
774 __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); |
|
775 |
|
776 iCache = CFat32LruCache::NewL(iOwner, fat32_LRUCache_MaxMemSize, fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2); |
|
777 } |
|
778 break; |
|
779 |
|
780 default: |
|
781 ASSERT(0); |
|
782 User::Leave(KErrCorrupt); |
|
783 break; |
|
784 }; |
|
785 |
|
786 ASSERT(iCache); |
|
787 } |
|
788 |
|
789 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
790 |
|
791 /** |
|
792 Destroys a helper thread object. |
|
793 If the thread is running, stops it first. than deletes the ipHelperThread and sets it to NULL |
|
794 */ |
|
795 void CAtaFatTable::DestroyHelperThread() |
|
796 { |
|
797 |
|
798 if(!ipHelperThread) |
|
799 return; |
|
800 |
|
801 __PRINT1(_L("CAtaFatTable::DestroyHelperThread(), drv:%d"), iOwner->DriveNumber()); |
|
802 ipHelperThread->ForceStop(); |
|
803 delete ipHelperThread; |
|
804 ipHelperThread = NULL; |
|
805 } |
|
806 |
|
807 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
808 |
|
809 /** |
|
810 Flush the FAT cache on disk |
|
811 @leave System wide error codes |
|
812 */ |
|
813 void CAtaFatTable::FlushL() |
|
814 { |
|
815 __PRINT1(_L("CAtaFatTable::FlushL(), drv:%d"), iOwner->DriveNumber()); |
|
816 |
|
817 //-- the data can't be written if the mount is inconsistent |
|
818 iOwner->CheckStateConsistentL(); |
|
819 |
|
820 if (iCache) |
|
821 iCache->FlushL(); |
|
822 } |
|
823 |
|
824 |
|
825 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
826 |
|
827 /** |
|
828 Dismount the cache. Stops any activity, deallocates caches etc. |
|
829 @param aDiscardDirtyData if ETrue, non-flushed data in the cache will be discarded. |
|
830 */ |
|
831 void CAtaFatTable::Dismount(TBool aDiscardDirtyData) |
|
832 { |
|
833 __PRINT3(_L("#=-= CAtaFatTable::Dismount(%d), drv:%d, state:%d"), aDiscardDirtyData, iOwner->DriveNumber(), State()); |
|
834 |
|
835 //-- if there is a helper thread, stop it and delete its object |
|
836 DestroyHelperThread(); |
|
837 |
|
838 //-- if there is the cache, close it (it may lead to deallocating its memory) |
|
839 if(iCache) |
|
840 { |
|
841 //-- cache's Close() can check if the cache is clean. |
|
842 //-- ignore dirty data in cache if the mount is not in consistent state (it's impossible to flush cache data) |
|
843 //-- or if we are asked to do so. |
|
844 const TBool bIgnoreDirtyData = aDiscardDirtyData || !iOwner->ConsistentState(); |
|
845 iCache->Close(bIgnoreDirtyData); |
|
846 |
|
847 delete iCache; |
|
848 iCache=NULL; |
|
849 } |
|
850 |
|
851 SetState(EDismounted); |
|
852 } |
|
853 |
|
854 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
855 |
|
856 /** |
|
857 Invalidate whole FAT cache. |
|
858 Depending of cache type this may just mark cache invalid with reading on demand or re-read whole cache from the media |
|
859 */ |
|
860 void CAtaFatTable::InvalidateCacheL() |
|
861 { |
|
862 __PRINT1(_L("CAtaFatTable::InvalidateCache(), drv:%d"), iOwner->DriveNumber()); |
|
863 |
|
864 //-- if we have a cache, invalidate it entirely |
|
865 if(iCache) |
|
866 { |
|
867 User::LeaveIfError(iCache->Invalidate()); |
|
868 } |
|
869 |
|
870 //-- invalidating whole FAT cache means that something very serious happened. |
|
871 //-- if we have a helper thread running, abort it. |
|
872 if(ipHelperThread) |
|
873 ipHelperThread->ForceStop(); |
|
874 |
|
875 } |
|
876 |
|
877 |
|
878 //--------------------------------------------------------------------------------------------------------------------------------------- |
|
879 |
|
880 /** |
|
881 Invalidate specified region of the FAT cache |
|
882 Depending of cache type this may just mark part of the cache invalid with reading on demand later |
|
883 or re-read whole cache from the media. |
|
884 |
|
885 @param aPos absolute media position where the region being invalidated starts. |
|
886 @param aLength length in bytes of region to invalidate / refresh |
|
887 */ |
|
888 void CAtaFatTable::InvalidateCacheL(TInt64 aPos, TUint32 aLength) |
|
889 { |
|
890 __PRINT3(_L("CAtaFatTable::InvalidateCacheL() drv:%d, pos:%LU, len:%u,"), iOwner->DriveNumber(), aPos, aLength); |
|
891 |
|
892 if(I64HIGH(aPos) || !aLength || I64HIGH(aPos+aLength)) |
|
893 return; //-- FAT tables can't span over 4G |
|
894 |
|
895 const TUint32 mediaPos32 = I64LOW(aPos); |
|
896 |
|
897 //-- we do not use other copies of FAT, so trach changes only in FAT1 |
|
898 const TUint32 fat1StartPos = iOwner->StartOfFatInBytes(); |
|
899 const TUint32 fat1EndPos = fat1StartPos + iOwner->FatSizeInBytes(); |
|
900 |
|
901 TUint32 invRegionPosStart = 0; //-- media pos where the invalidated region starts |
|
902 TUint32 invRegionLen = 0; //-- size of the invalidated region, bytes |
|
903 |
|
904 //-- calculate the FAT1 region being invalidated |
|
905 if(mediaPos32 < fat1StartPos) |
|
906 { |
|
907 if((mediaPos32 + aLength) <= fat1StartPos) |
|
908 return; |
|
909 |
|
910 invRegionPosStart = fat1StartPos; |
|
911 invRegionLen = aLength - (fat1StartPos-mediaPos32); |
|
912 } |
|
913 else //if(mediaPos32 < fat1StartPos) |
|
914 {//-- mediaPos32 >= fat1StartPos) |
|
915 if(mediaPos32 >= fat1EndPos) |
|
916 return; |
|
917 |
|
918 invRegionPosStart = mediaPos32; |
|
919 |
|
920 if((mediaPos32 + aLength) <= fat1EndPos) |
|
921 { |
|
922 invRegionLen = aLength; |
|
923 } |
|
924 else |
|
925 { |
|
926 invRegionLen = mediaPos32+aLength-fat1EndPos; |
|
927 } |
|
928 } |
|
929 |
|
930 //-- convert the media pos of the region into FAT entries basis, depending on the FAT type |
|
931 ASSERT(invRegionPosStart >= fat1StartPos && invRegionLen <= (TUint)iOwner->FatSizeInBytes()); |
|
932 |
|
933 TUint32 startFatEntry=0; |
|
934 TUint32 numEntries = 0; |
|
935 |
|
936 switch(FatType()) |
|
937 { |
|
938 case EFat12: |
|
939 //-- invalidate whole cache; it is not worth making calculations for such small memory region. |
|
940 User::LeaveIfError(iCache->Invalidate()); |
|
941 return; |
|
942 |
|
943 case EFat16: |
|
944 startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat16EntrySzLog2; |
|
945 numEntries = (invRegionLen + (sizeof(TFat16Entry)-1)) >> KFat16EntrySzLog2; |
|
946 break; |
|
947 |
|
948 case EFat32: |
|
949 startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat32EntrySzLog2; |
|
950 numEntries = (invRegionLen + (sizeof(TFat32Entry)-1)) >> KFat32EntrySzLog2; |
|
951 break; |
|
952 |
|
953 default: |
|
954 ASSERT(0); |
|
955 return; |
|
956 }; |
|
957 |
|
958 if(startFatEntry < KFatFirstSearchCluster) |
|
959 {//-- FAT[0] and FAT[1] can't be legally accessed, they are reserved entries. We need to adjust region being refreshed. |
|
960 if(numEntries <= KFatFirstSearchCluster) |
|
961 return; //-- nothing to refresh |
|
962 |
|
963 startFatEntry += KFatFirstSearchCluster; |
|
964 numEntries -= KFatFirstSearchCluster; |
|
965 } |
|
966 |
|
967 User::LeaveIfError(iCache->InvalidateRegion(startFatEntry, numEntries)); |
|
968 } |
|
969 |
|
970 |
|
971 //----------------------------------------------------------------------------- |
|
972 /** |
|
973 Initialize the object, create FAT cache if required |
|
974 @leave KErrNoMemory |
|
975 */ |
|
976 void CAtaFatTable::InitializeL() |
|
977 { |
|
978 __PRINT2(_L("CAtaFatTable::InitializeL() drv:%d, state%d"), iOwner->DriveNumber(), State()); |
|
979 CFatTable::InitializeL(); |
|
980 |
|
981 ASSERT(!iCache); |
|
982 ASSERT(State() == ENotInitialised); |
|
983 |
|
984 //-- create the FAT cache. |
|
985 CreateCacheL(); |
|
986 |
|
987 SetState(EInitialised); |
|
988 |
|
989 } |
|
990 |
|
991 //----------------------------------------------------------------------------- |
|
992 /** |
|
993 Mount the FAT table to the CFatMountCB. Depending on mount parameters and configuration this method |
|
994 can do various things, like counting free clusters synchronously if data from FSInfo isn't valid, |
|
995 or setting up a FAT backround thread and return immediately etc. |
|
996 |
|
997 @param aMountParam mounting parameters, like some data from FSInfo |
|
998 |
|
999 */ |
|
1000 void CAtaFatTable::MountL(const TMountParams& aMountParam) |
|
1001 { |
|
1002 __PRINT2(_L("CAtaFatTable::MountL() drv:%d, state:%d"), iOwner->DriveNumber(), State()); |
|
1003 |
|
1004 ASSERT(State() == EInitialised); |
|
1005 SetState(EMounting); |
|
1006 |
|
1007 if(ipHelperThread) |
|
1008 { |
|
1009 __PRINT(_L("CAtaFatTable::MountL() Helper thread is present!")); |
|
1010 ASSERT(0); |
|
1011 DestroyHelperThread(); |
|
1012 } |
|
1013 |
|
1014 |
|
1015 //-- Check if we have valid data from FSInfo. In this case we don't need to count free clusters |
|
1016 if(aMountParam.iFsInfoValid) |
|
1017 { |
|
1018 ASSERT(IsFat32()); |
|
1019 ASSERT(aMountParam.iFreeClusters <= MaxEntries()); |
|
1020 |
|
1021 ASSERT(ClusterNumberValid(aMountParam.iFirstFreeCluster)); |
|
1022 |
|
1023 SetFreeClusters(aMountParam.iFreeClusters); |
|
1024 SetFreeClusterHint(aMountParam.iFirstFreeCluster); |
|
1025 |
|
1026 __PRINT2(_L("CAtaFatTable::MountL() Using data from FSInfo sector. free clusters:%d, 1st free:%d"), FreeClusters(), FreeClusterHint()); |
|
1027 |
|
1028 //-- We don't need to scan entire FAT to find out the number of free entries, because the data are taken from FSInfo. |
|
1029 //-- But if we are going to use the FAT32 bit supercache, we need to populate it. So, try to start up a special |
|
1030 //-- populating thread. |
|
1031 CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); |
|
1032 if(pFatBitCache) |
|
1033 {//-- bit cache is present, we need to populate (or repopulate it) |
|
1034 //-- create helper thread object and start the thread |
|
1035 ipHelperThread = CFat32BitCachePopulator::NewL(*this); |
|
1036 |
|
1037 ipHelperThread->Launch(); |
|
1038 //-- background FAT bit cache populating thread is running now. |
|
1039 //-- the result of thread start up and completion isn't very interesting: If it fails to |
|
1040 //-- properly populate the cache, nothing fatal will happen. |
|
1041 } |
|
1042 |
|
1043 //-- CFat32BitCachePopulator doesn't affect FAT table state. |
|
1044 SetState(EMounted); |
|
1045 return; |
|
1046 } |
|
1047 |
|
1048 //-- FSInfo data are invalid; we need to count free clusters by reading whole FAT table |
|
1049 //-- This method can optionally create a background thread (that will count free clusters) and return immideately. |
|
1050 CountFreeClustersL(); |
|
1051 } |
|
1052 |
|
1053 //----------------------------------------------------------------------------- |
|
1054 |
|
1055 /** |
|
1056 Decrements the free cluster count. This is an overridden method with synchronisation. |
|
1057 @param aCount a number of clusters |
|
1058 */ |
|
1059 void CAtaFatTable::DecrementFreeClusterCount(TUint32 aCount) |
|
1060 { |
|
1061 XAutoLock lock(iOwner); //-- enter critical section |
|
1062 CFatTable::DecrementFreeClusterCount(aCount); |
|
1063 } |
|
1064 |
|
1065 /** |
|
1066 Increments the free cluster count. This is an overridden method with synchronisation. |
|
1067 @param aCount a number of clusters |
|
1068 */ |
|
1069 void CAtaFatTable::IncrementFreeClusterCount(TUint32 aCount) |
|
1070 { |
|
1071 XAutoLock lock(iOwner); //-- enter critical section |
|
1072 CFatTable::IncrementFreeClusterCount(aCount); |
|
1073 } |
|
1074 |
|
1075 //----------------------------------------------------------------------------- |
|
1076 |
|
1077 /** |
|
1078 Obtain number of free clusters on the volume. This is an overridden method. |
|
1079 Depending on the "aSyncOperation" parameter this operation can be fully synhronous (exact number of free clusters ) or asynchronous |
|
1080 (current number of free clusters) if the FAT scanning thread is still running. |
|
1081 |
|
1082 @param aSyncOperation if ETrue, this method will wait until FAT scan thread finishes and return exact number of free clusters |
|
1083 if false, it will return current number of free clusters counted by FAT scan thread if it hasn't finished yet. |
|
1084 |
|
1085 @return Number of free clusters. See also CAtaFatTable::RequestFreeClusters() |
|
1086 */ |
|
1087 TUint32 CAtaFatTable::NumberOfFreeClusters(TBool aSyncOperation/*=EFalse*/) const |
|
1088 { |
|
1089 if(ipHelperThread && ipHelperThread->ThreadWorking() && ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner) |
|
1090 {//-- here we have running helper thread that counts free entries in FAT. |
|
1091 //-- if this operation is synchronous, we need to wait until it finish its job in order to get _exact_ number of free cluster, |
|
1092 //-- not currently counted |
|
1093 |
|
1094 //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d enter, sync:%d"), iOwner->DriveNumber(), aSyncOperation); |
|
1095 |
|
1096 if(aSyncOperation) |
|
1097 {//-- wait for background scanning thread to finish counting free clusters if this operation is synchronous |
|
1098 ipHelperThread->BoostPriority(ETrue); |
|
1099 ipHelperThread->WaitToFinish(); |
|
1100 } |
|
1101 |
|
1102 XAutoLock lock(iOwner); //-- enter critical section |
|
1103 |
|
1104 const TUint32 freeClusters = FreeClusters(); |
|
1105 //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d Exit, clusters:%d"), iOwner->DriveNumber(), freeClusters); |
|
1106 return freeClusters; |
|
1107 } |
|
1108 |
|
1109 return FreeClusters(); |
|
1110 |
|
1111 } |
|
1112 |
|
1113 //----------------------------------------------------------------------------- |
|
1114 |
|
1115 /** |
|
1116 Set free cluster count. This is an overridden method with synchronisation. |
|
1117 @param aFreeClusters new value of free clusters |
|
1118 */ |
|
1119 void CAtaFatTable::SetFreeClusters(TUint32 aFreeClusters) |
|
1120 { |
|
1121 XAutoLock lock(iOwner); //-- enter critical section |
|
1122 CFatTable::SetFreeClusters(aFreeClusters); |
|
1123 } |
|
1124 |
|
1125 /** |
|
1126 This is an overridden method with synchronisation. |
|
1127 @return the last known free cluster number. |
|
1128 */ |
|
1129 TUint32 CAtaFatTable::FreeClusterHint() const |
|
1130 { |
|
1131 XAutoLock lock(iOwner); //-- enter critical section |
|
1132 return CFatTable::FreeClusterHint(); |
|
1133 } |
|
1134 |
|
1135 /** Set next free cluster number. This is an overridden method with synchronisation. */ |
|
1136 void CAtaFatTable::SetFreeClusterHint(TUint32 aCluster) |
|
1137 { |
|
1138 XAutoLock lock(iOwner); //-- enter critical section |
|
1139 CFatTable::SetFreeClusterHint(aCluster); |
|
1140 } |
|
1141 |
|
1142 /** |
|
1143 @return ETrue if the state of the object is consistent; i.e. it is |
|
1144 fully constructed, valid and the amount of free entries is known. |
|
1145 Used in the case of asynchronous mounting. |
|
1146 */ |
|
1147 TBool CAtaFatTable::ConsistentState() const |
|
1148 { |
|
1149 return State() == EMounted; |
|
1150 } |
|
1151 |
|
1152 //----------------------------------------------------------------------------- |
|
1153 |
|
1154 /** |
|
1155 Request for the raw write access to the FAT area (all copies of FAT). |
|
1156 If FAT helper thread is running, waits until it finishes. |
|
1157 |
|
1158 @param aPos absolute media position we are going to write to. Be careful with casting it from TInt64 and losing high word. |
|
1159 @param aLen length of the area being written |
|
1160 */ |
|
1161 void CAtaFatTable::RequestRawWriteAccess(TInt64 aPos, TUint32 aLen) const |
|
1162 { |
|
1163 if(I64HIGH(aPos)) |
|
1164 return; |
|
1165 |
|
1166 const TUint32 pos32 = I64LOW(aPos); |
|
1167 const TUint32 posFatStart = iOwner->StartOfFatInBytes(); //-- position of the FAT start on the volume |
|
1168 const TUint32 posFatsEnd = posFatStart + iOwner->NumberOfFats()*iOwner->FatSizeInBytes(); //-- position of the ent of ALL FATs |
|
1169 |
|
1170 if(pos32 >= posFatsEnd || (pos32+aLen) <= posFatStart) |
|
1171 return; |
|
1172 |
|
1173 __PRINT2(_L("#=- CAtaFatTable::RequestRawWriteAccess() pos:%d, len:%d"),pos32, aLen); |
|
1174 |
|
1175 //-- someone tries to write to FAT area directly. Wait for the FAT helper thread to finish |
|
1176 if(ipHelperThread) |
|
1177 ipHelperThread->WaitToFinish(); |
|
1178 |
|
1179 } |
|
1180 |
|
1181 //----------------------------------------------------------------------------- |
|
1182 |
|
1183 /** |
|
1184 Checks if we have at least "aClustersRequired" clusters free in the FAT. |
|
1185 If FAT scannng thread is running, waits until requested number of free clusters counted or the thread finishes. |
|
1186 |
|
1187 @param aClustersRequired number of free clusters required |
|
1188 @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise. |
|
1189 */ |
|
1190 TBool CAtaFatTable::RequestFreeClusters(TUint32 aClustersRequired) const |
|
1191 { |
|
1192 //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters(%d)"),aClustersRequired); |
|
1193 ASSERT(aClustersRequired >0); |
|
1194 |
|
1195 if(!ipHelperThread || !ipHelperThread->ThreadWorking() || ipHelperThread->Type() != CFatHelperThreadBase::EFreeSpaceScanner) |
|
1196 {//-- there is no FAT free space scan thread running, number of free entries can't increase in background |
|
1197 return (FreeClusters() >= aClustersRequired); //-- use simple, non-thread safe method |
|
1198 } |
|
1199 |
|
1200 //-- FAT free space scan thread is running, counting free FAT entries. wait until it has counted enough or finish. |
|
1201 ASSERT(ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner); |
|
1202 |
|
1203 TUint32 currFreeClusters; |
|
1204 const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity |
|
1205 |
|
1206 ipHelperThread->BoostPriority(ETrue); //-- increase thread priority |
|
1207 |
|
1208 for(;;) |
|
1209 { |
|
1210 currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously |
|
1211 if(currFreeClusters >= aClustersRequired) |
|
1212 break; //-- OK, the request is satisfied |
|
1213 |
|
1214 if(!ipHelperThread->ThreadWorking()) |
|
1215 {//-- the thread has finished its work |
|
1216 currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously |
|
1217 break; |
|
1218 } |
|
1219 |
|
1220 User::After(KWaitGranularity); //-- wait some time allowing FAT scanning thread to count free clusters. |
|
1221 } |
|
1222 |
|
1223 ipHelperThread->BoostPriority(EFalse); //-- set thread priority back to normal |
|
1224 //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters() #2 curr:%d"),currFreeClusters); |
|
1225 |
|
1226 return (currFreeClusters >= aClustersRequired); |
|
1227 |
|
1228 } |
|
1229 |
|
1230 //----------------------------------------------------------------------------- |
|
1231 |
|
1232 /** |
|
1233 Parse a buffer filled with FAT16 or FAT32 entries, counting free clusters and looking for the firs free cluster number. |
|
1234 Note that this method can be called from a helper FAT scan thread. |
|
1235 |
|
1236 @param aBuf FAT buffer descriptor. Must contain whole number of FAT16 or FAT32 entries |
|
1237 @param aScanParam the structure to be filled with values, like number of counted free and non-free clusters, etc. |
|
1238 */ |
|
1239 void CAtaFatTable::DoParseFatBuf(const TPtrC8& aBuf, TFatScanParam& aScanParam) const |
|
1240 { |
|
1241 |
|
1242 if(IsFat16()) |
|
1243 {//-- we are processing a buffer of FAT16 entries |
|
1244 ASSERT(!ipHelperThread); |
|
1245 ASSERT((aBuf.Size() & (sizeof(TFat16Entry)-1)) == 0); |
|
1246 const TInt KNumEntries = aBuf.Size() >> KFat16EntrySzLog2; |
|
1247 const TFat16Entry* const pFatEntry = (const TFat16Entry*)(aBuf.Ptr()); |
|
1248 |
|
1249 for(TInt i=0; i<KNumEntries; ++i) |
|
1250 { |
|
1251 if(aScanParam.iEntriesScanned >= KFatFirstSearchCluster) |
|
1252 { |
|
1253 const TFat16Entry entry = pFatEntry[i]; |
|
1254 |
|
1255 if(entry == KSpareCluster) |
|
1256 {//-- found a free FAT entry |
|
1257 aScanParam.iCurrFreeEntries++; |
|
1258 |
|
1259 if(aScanParam.iFirstFree < KFatFirstSearchCluster) |
|
1260 aScanParam.iFirstFree = aScanParam.iEntriesScanned; |
|
1261 |
|
1262 } |
|
1263 else |
|
1264 {//-- found occupied FAT entry, count bad clusters as well |
|
1265 aScanParam.iCurrOccupiedEntries++; |
|
1266 } |
|
1267 |
|
1268 } |
|
1269 |
|
1270 aScanParam.iEntriesScanned++; |
|
1271 } |
|
1272 }//if(IsFat16()) |
|
1273 else |
|
1274 if(IsFat32()) |
|
1275 {//-- we are processing a buffer of FAT32 entries. |
|
1276 //-- note that here we can be in the context of the FAT free entries scan thread. |
|
1277 ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0); |
|
1278 |
|
1279 //-- pointer to the FAT32 bit supercache. If present, we will populate it here |
|
1280 CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); |
|
1281 |
|
1282 const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2; |
|
1283 const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); |
|
1284 |
|
1285 for(TInt i=0; i<KNumEntries; ++i) |
|
1286 { |
|
1287 if(aScanParam.iEntriesScanned >= KFatFirstSearchCluster) |
|
1288 { |
|
1289 const TFat32Entry entry = pFatEntry[i] & KFat32EntryMask; |
|
1290 |
|
1291 if(entry == KSpareCluster) |
|
1292 {//-- found a free FAT32 entry |
|
1293 ++aScanParam.iCurrFreeEntries; |
|
1294 |
|
1295 if(aScanParam.iFirstFree < KFatFirstSearchCluster) |
|
1296 aScanParam.iFirstFree = aScanParam.iEntriesScanned; |
|
1297 |
|
1298 |
|
1299 //-- feed the information about free FAT entry at index aClustersScanned to the FAT bit supercache. |
|
1300 if(pFatBitCache) |
|
1301 { |
|
1302 pFatBitCache->SetFreeFatEntry(aScanParam.iEntriesScanned); |
|
1303 } |
|
1304 |
|
1305 |
|
1306 }//if(entry == KSpareCluster) |
|
1307 else |
|
1308 {//-- found occupied FAT32 entry, count bad clusters as well |
|
1309 aScanParam.iCurrOccupiedEntries++; |
|
1310 } |
|
1311 } |
|
1312 |
|
1313 ++aScanParam.iEntriesScanned; |
|
1314 } |
|
1315 |
|
1316 }//if(IsFat32()) |
|
1317 else |
|
1318 { |
|
1319 ASSERT(0); |
|
1320 } |
|
1321 } |
|
1322 |
|
1323 //----------------------------------------------------------------------------- |
|
1324 |
|
1325 /** |
|
1326 Count free clusters in FAT16 or FAT32. Uses relatively large buffer to read FAT entries into; |
|
1327 This is faster than usual ReadL() calls. |
|
1328 */ |
|
1329 void CAtaFatTable::DoCountFreeClustersL() |
|
1330 { |
|
1331 __PRINT2(_L("#- CAtaFatTable::DoCountFreeClustersL() drv:%d, state:%d"), iOwner->DriveNumber(), State()); |
|
1332 |
|
1333 if(!IsFat16() && !IsFat32()) |
|
1334 { |
|
1335 ASSERT(0); |
|
1336 User::Leave(KErrNotSupported); |
|
1337 } |
|
1338 |
|
1339 const TUint32 KFat1StartPos = iOwner->StartOfFatInBytes(); |
|
1340 const TUint32 KNumClusters = MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters() |
|
1341 const TUint32 KNumFATs = iOwner->NumberOfFats(); |
|
1342 const TUint32 KFatSize = KNumClusters * (IsFat32() ? sizeof(TFat32Entry) : sizeof(TFat16Entry)); //-- usable size of one FAT. |
|
1343 |
|
1344 (void)KNumFATs; |
|
1345 |
|
1346 ASSERT(KFat1StartPos >= 1*KDefaultSectorSize); |
|
1347 ASSERT(KNumClusters > KFatFirstSearchCluster); |
|
1348 ASSERT(KNumFATs > 0); |
|
1349 |
|
1350 const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size |
|
1351 |
|
1352 __ASSERT_COMPILE((KFatBufSz % sizeof(TFat32Entry)) == 0); |
|
1353 __ASSERT_COMPILE((KFatBufSz % sizeof(TFat16Entry)) == 0); |
|
1354 |
|
1355 RBuf8 buf; |
|
1356 CleanupClosePushL(buf); |
|
1357 |
|
1358 //-- allocate memory for FAT parse buffer |
|
1359 buf.CreateMaxL(KFatBufSz); |
|
1360 |
|
1361 //-- read FAT into the large buffer and parse it |
|
1362 TUint32 rem = KFatSize; |
|
1363 TUint32 mediaPos = KFat1StartPos; |
|
1364 |
|
1365 //-- prepare FAT bit supercache to being populated. |
|
1366 //-- actual populating will happen in ::DoParseFatBuf() |
|
1367 CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); |
|
1368 |
|
1369 if(pFatBitCache) |
|
1370 { |
|
1371 pFatBitCache->StartPopulating(); |
|
1372 } |
|
1373 |
|
1374 TFatScanParam fatScanParam; |
|
1375 |
|
1376 //-- used for measuring time |
|
1377 TTime timeStart; |
|
1378 TTime timeEnd; |
|
1379 timeStart.UniversalTime(); //-- take start time |
|
1380 |
|
1381 |
|
1382 while(rem) |
|
1383 { |
|
1384 const TUint32 bytesToRead=Min(rem, KFatBufSz); |
|
1385 TPtrC8 ptrData(buf.Ptr(), bytesToRead); |
|
1386 |
|
1387 //__PRINT2(_L("#=--- CAtaFatTable::DoCountFreeClustersL() read %d bytes pos:0x%x"), bytesToRead, (TUint32)mediaPos); |
|
1388 User::LeaveIfError(iOwner->LocalDrive()->Read(mediaPos, bytesToRead, buf)); |
|
1389 |
|
1390 DoParseFatBuf(ptrData, fatScanParam); |
|
1391 |
|
1392 mediaPos += bytesToRead; |
|
1393 rem -= bytesToRead; |
|
1394 } |
|
1395 |
|
1396 //-- here fatScanParam contains values for the whole FAT. |
|
1397 |
|
1398 timeEnd.UniversalTime(); //-- take end time |
|
1399 const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec); |
|
1400 (void)msScanTime; |
|
1401 __PRINT1(_L("#- CAtaFatTable::DoCountFreeClustersL() finished. Taken:%d ms "), msScanTime); |
|
1402 |
|
1403 |
|
1404 //-- tell FAT bit cache that we have finished populating it |
|
1405 if(pFatBitCache) |
|
1406 { |
|
1407 pFatBitCache->FinishPopulating(ETrue); |
|
1408 pFatBitCache->Dump(); |
|
1409 } |
|
1410 |
|
1411 if(!fatScanParam.iFirstFree)//-- haven't found free clusters on the volume |
|
1412 fatScanParam.iFirstFree = KFatFirstSearchCluster; |
|
1413 |
|
1414 ASSERT(fatScanParam.iCurrFreeEntries <= iOwner->UsableClusters()); |
|
1415 ASSERT(ClusterNumberValid(fatScanParam.iFirstFree)); |
|
1416 |
|
1417 SetFreeClusters(fatScanParam.iCurrFreeEntries); |
|
1418 SetFreeClusterHint(fatScanParam.iFirstFree); |
|
1419 |
|
1420 CleanupStack::PopAndDestroy(&buf); |
|
1421 } |
|
1422 |
|
1423 //----------------------------------------------------------------------------- |
|
1424 |
|
1425 /** |
|
1426 Count free clusters on the volume. |
|
1427 Depending on FAT type can count clusters synchronously or start a thread to do it in background. |
|
1428 */ |
|
1429 void CAtaFatTable::CountFreeClustersL() |
|
1430 { |
|
1431 __PRINT3(_L("#=- CAtaFatTable::CountFreeClustersL() drv:%d, FAT%d, state:%d"),iOwner->DriveNumber(),FatType(), State()); |
|
1432 |
|
1433 ASSERT(State() == EMounting); |
|
1434 ASSERT(!ipHelperThread); |
|
1435 |
|
1436 TInt nRes; |
|
1437 |
|
1438 switch(FatType()) |
|
1439 { |
|
1440 case EFat12: //-- use old default scanning, it is synchronous |
|
1441 CFatTable::CountFreeClustersL(); |
|
1442 SetState(EMounted); |
|
1443 break; |
|
1444 |
|
1445 case EFat16: //-- enhanced FAT scan, but still synchronous |
|
1446 TRAP(nRes, DoCountFreeClustersL()); |
|
1447 if(nRes !=KErrNone) |
|
1448 { |
|
1449 CFatTable::CountFreeClustersL(); //-- fall back to the legacy method |
|
1450 } |
|
1451 |
|
1452 SetState(EMounted); |
|
1453 break; |
|
1454 |
|
1455 case EFat32: //-- This is FAT32, try to set up a FAT scanning thread if allowed |
|
1456 { |
|
1457 TBool bFat32BkGndScan = ETrue; //-- if true, we will try to start up a background scanning thread. |
|
1458 |
|
1459 //-- 1. check if background FAT scanning is disabled in config |
|
1460 if(!iOwner->FatConfig().FAT32_AsynchMount()) |
|
1461 { |
|
1462 __PRINT(_L("#=- FAT32 BkGnd scan is disabled in config.")); |
|
1463 bFat32BkGndScan = EFalse; |
|
1464 } |
|
1465 |
|
1466 //-- 2. check if background FAT scanning is disabled by test interface |
|
1467 #ifdef _DEBUG |
|
1468 TInt nMntDebugFlags; |
|
1469 if(bFat32BkGndScan && RProperty::Get(KSID_Test1, iOwner->DriveNumber(), nMntDebugFlags) == KErrNone) |
|
1470 {//-- test property for this drive is defined |
|
1471 if(nMntDebugFlags & KMntDisable_FatBkGndScan) |
|
1472 { |
|
1473 __PRINT(_L("#- FAT32 BkGnd scan is disabled is disabled by debug interface.")); |
|
1474 bFat32BkGndScan = EFalse; |
|
1475 } |
|
1476 |
|
1477 } |
|
1478 #endif |
|
1479 //-- 3. try to start FAT32 free entries scanning thread. |
|
1480 if(bFat32BkGndScan) |
|
1481 { |
|
1482 __PRINT(_L("#=- Starting up FAT32 free entries scanner thread...")); |
|
1483 TRAP(nRes, DoLaunchFat32FreeSpaceScanThreadL()); |
|
1484 if(nRes == KErrNone) |
|
1485 break; //-- let thread run by itself |
|
1486 |
|
1487 //-- DoLaunchFat32FreeSpaceScanThreadL() has set this object state. |
|
1488 } |
|
1489 |
|
1490 //-- we either failed to launch the thread or this feature was disabled somehow. Fall back to the synchronous scan. |
|
1491 TRAP(nRes, DoCountFreeClustersL()); |
|
1492 if(nRes !=KErrNone) |
|
1493 { |
|
1494 CFatTable::CountFreeClustersL(); //-- fall back to the legacy method |
|
1495 } |
|
1496 |
|
1497 SetState(EMounted); |
|
1498 }//case EFat32 |
|
1499 break; |
|
1500 |
|
1501 default: |
|
1502 ASSERT(0); |
|
1503 break; |
|
1504 |
|
1505 } //switch(FatType()) |
|
1506 } |
|
1507 |
|
1508 //----------------------------------------------------------------------------- |
|
1509 |
|
1510 /** |
|
1511 Set up and start FAT scan thread. |
|
1512 Leaves on error. |
|
1513 */ |
|
1514 void CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL() |
|
1515 { |
|
1516 __PRINT2(_L("#=- CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL() drv:%d, state:%d"),iOwner->DriveNumber(), State()); |
|
1517 ASSERT(State() == EMounting); |
|
1518 |
|
1519 //-- 1. check if something is already working (shan't be!) |
|
1520 if(ipHelperThread) |
|
1521 { |
|
1522 if(ipHelperThread->ThreadWorking()) |
|
1523 { |
|
1524 __PRINT(_L("#=- CAtaFatTable::DoLaunchScanThread() some thread is already running ?")); |
|
1525 ASSERT(0); |
|
1526 User::Leave(KErrAlreadyExists); |
|
1527 } |
|
1528 |
|
1529 DestroyHelperThread(); |
|
1530 } |
|
1531 |
|
1532 //-- 2. create helper thread object and start the thread |
|
1533 ipHelperThread = CFat32FreeSpaceScanner::NewL(*this); |
|
1534 |
|
1535 SetState(EFreeClustersScan); |
|
1536 |
|
1537 ipHelperThread->Launch(); |
|
1538 //-- background FAT scanning thread is running now |
|
1539 } |
|
1540 |
|
1541 //----------------------------------------------------------------------------- |
|
1542 /** |
|
1543 Read an entry from the FAT table |
|
1544 |
|
1545 @param aFatIndex FAT entry number to read |
|
1546 @return FAT entry value |
|
1547 */ |
|
1548 TUint32 CAtaFatTable::ReadL(TUint32 aFatIndex) const |
|
1549 { |
|
1550 if(!ClusterNumberValid(aFatIndex)) |
|
1551 { |
|
1552 //ASSERT(0); //-- deliberately corrupted (by some tests) DOS directory entries can have 0 in the "first cluster" field. |
|
1553 __PRINT1(_L("CAtaFatTable::ReadL(%d) bad Index!"), aFatIndex); |
|
1554 User::Leave(KErrCorrupt); |
|
1555 } |
|
1556 |
|
1557 |
|
1558 const TUint entry = iCache->ReadEntryL(aFatIndex); |
|
1559 return entry; |
|
1560 } |
|
1561 |
|
1562 |
|
1563 //----------------------------------------------------------------------------- |
|
1564 /** |
|
1565 Write an entry to the FAT table |
|
1566 |
|
1567 @param aFatIndex aFatIndex FAT entry number to write |
|
1568 @param aValue FAT entry to write |
|
1569 @leave |
|
1570 */ |
|
1571 void CAtaFatTable::WriteL(TUint32 aFatIndex, TUint32 aValue) |
|
1572 { |
|
1573 |
|
1574 __PRINT2(_L("CAtaFatTable::WriteL() entry:%d, val:0x%x"), aFatIndex, aValue); |
|
1575 |
|
1576 if(!ClusterNumberValid(aFatIndex)) |
|
1577 { |
|
1578 ASSERT(0); |
|
1579 User::Leave(KErrCorrupt); |
|
1580 } |
|
1581 |
|
1582 if(aValue != KSpareCluster && (aValue < KFatFirstSearchCluster || aValue > KFat32EntryMask)) |
|
1583 { |
|
1584 ASSERT(0); |
|
1585 User::Leave(KErrCorrupt); |
|
1586 } |
|
1587 |
|
1588 //-- wait until we are allowed to write FAT entry |
|
1589 if(ipHelperThread && ipHelperThread->ThreadWorking()) |
|
1590 { |
|
1591 ASSERT(ipHelperThread->ThreadId() != RThread().Id()); //-- this method must not be called the FAT helper thread |
|
1592 ipHelperThread->RequestFatEntryWriteAccess(aFatIndex); |
|
1593 } |
|
1594 |
|
1595 //-- write entry to the FAT through FAT cache |
|
1596 iCache->WriteEntryL(aFatIndex, aValue); |
|
1597 |
|
1598 |
|
1599 //-- if we are writing "spare" FAT entry, tell FAT bit supercache about it. |
|
1600 //-- it will store the information that corresponding FAT cache sector has a spare FAT entry. |
|
1601 //-- writing non-spare FAT entry doesn't mean anything: that FAT cache sector might or might not contain free entries. |
|
1602 if(aValue == KSpareCluster && iCache->BitCacheInterface()) |
|
1603 { |
|
1604 CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); |
|
1605 const CFatBitCache::TState cacheState= pFatBitCache->State(); |
|
1606 if(cacheState == CFatBitCache::EPopulated || cacheState == CFatBitCache::EPopulating) |
|
1607 {//-- bit cache is either normally populated or being populated by one of the helper threads |
|
1608 if(ipHelperThread && ipHelperThread->ThreadWorking()) |
|
1609 { |
|
1610 //-- here we have a multithreading issue. Helper FAT thread can be parsing FAT and optionally calling ReportFreeFatEntry(..) as well. |
|
1611 //-- in this case we need either to suspend the helper thread in order to prevent corruption of the FAT bit cache data, |
|
1612 //-- or ignore this call and rely on the fact that the FAT bit supercache is a kind of self-learning and the missing data will be |
|
1613 //-- fixed during conflict resolution (this can lead to performance degradation). |
|
1614 |
|
1615 //-- ok, suspend the helper thread while we are changing data in the bit cache |
|
1616 AcquireLock(); |
|
1617 ipHelperThread->Suspend(); |
|
1618 pFatBitCache->SetFreeFatEntry(aFatIndex); |
|
1619 ipHelperThread->Resume(); |
|
1620 ReleaseLock(); |
|
1621 |
|
1622 } |
|
1623 else |
|
1624 {//-- no one else is accessing FAT in this time |
|
1625 ASSERT(pFatBitCache->UsableState()); |
|
1626 pFatBitCache->SetFreeFatEntry(aFatIndex); |
|
1627 } |
|
1628 } |
|
1629 |
|
1630 }//if(aValue == KSpareCluster) |
|
1631 |
|
1632 } |
|
1633 |
|
1634 //----------------------------------------------------------------------------- |
|
1635 /** |
|
1636 This is an overridden method from CFatTable. See CFatTable::FindClosestFreeClusterL(...) |
|
1637 Does the same, i.e looks for the closest to "aCluster" free FAT entry, but more advanced, |
|
1638 it can use FAT bit supercache for quick lookup. |
|
1639 |
|
1640 @param aCluster Cluster to find nearest free cluster to. |
|
1641 @leave KErrDiskFull + system wide error codes |
|
1642 @return cluster number found |
|
1643 */ |
|
1644 TUint32 CAtaFatTable::FindClosestFreeClusterL(TUint32 aCluster) |
|
1645 { |
|
1646 __PRINT2(_L("CAtaFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster); |
|
1647 |
|
1648 if(!ClusterNumberValid(aCluster)) |
|
1649 { |
|
1650 ASSERT(0); |
|
1651 User::Leave(KErrCorrupt); |
|
1652 } |
|
1653 |
|
1654 |
|
1655 if(!RequestFreeClusters(1)) |
|
1656 {//-- there is no at least 1 free cluster available |
|
1657 __PRINT(_L("CAtaFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1")); |
|
1658 User::Leave(KErrDiskFull); |
|
1659 } |
|
1660 |
|
1661 //-- check if we have FAT bit supercache and it is in consistent state |
|
1662 CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); |
|
1663 if(!pFatBitCache) |
|
1664 return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method |
|
1665 |
|
1666 ASSERT(IsFat32()); |
|
1667 |
|
1668 if(!pFatBitCache->UsableState()) |
|
1669 { |
|
1670 //__PRINT(_L("#++ CAtaFatTable::FindClosestFreeClusterL() FAT bit cache isn't consistent!")); |
|
1671 return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method |
|
1672 } |
|
1673 |
|
1674 //-- ask FAT bit supercache to find us FAT cache sector (closest to the aCluster) that contains free FAT entries. |
|
1675 //__PRINT2(_L("#++ CAtaFatTable::FindClosestFreeClusterL(%d) hint free cl:%d"), aCluster, FreeClusterHint()); |
|
1676 |
|
1677 const TInt KMaxLookupRetries = 2; |
|
1678 for(TInt i=0; i<KMaxLookupRetries; ++i) |
|
1679 { |
|
1680 const TInt nRes = pFatBitCache->FindClosestFreeFatEntry(aCluster); |
|
1681 switch(nRes) |
|
1682 { |
|
1683 case KErrNone: |
|
1684 //-- FAT bit supercache has found a free FAT entry in the FAT32 cache |
|
1685 //__PRINT1(_L("#++ CAtaFatTable::FindClosestFreeClusterL FOUND! cl:%d"), aCluster); |
|
1686 |
|
1687 ASSERT(ClusterNumberValid(aCluster)); |
|
1688 |
|
1689 //-- do not update the last known free cluster, it can be quite expensive. |
|
1690 //-- do it in the caller method with bigger granularity. |
|
1691 return aCluster; |
|
1692 |
|
1693 case KErrNotFound: |
|
1694 //-- there was a bit cache conflict, when FAT cache sector is marked as having free FAT entries, but it doesn't have them in reality. |
|
1695 //-- It can happen because FAT bit cache entry is marked '1' only on populating the bit vector or if someone writes KSpareCluster into the |
|
1696 //-- corresponding FAT cache sector. Such conflict can happen quite often. |
|
1697 //-- Try search again, the search is very likely to succeed very close, because the FAT bit cache entry had already been fixed as the result of conflict resolution. |
|
1698 break; |
|
1699 |
|
1700 case KErrCorrupt: |
|
1701 //-- pFatBitCache->FindClosestFreeFatEntry failed to read a page from the media |
|
1702 //-- break out from the loop and fall back to old search just in case. |
|
1703 |
|
1704 case KErrEof: |
|
1705 //-- there are no '1' entries in whole FAT bit cache vector at all, which is quite unlikely |
|
1706 //-- break out from the loop and fall back to old search. |
|
1707 i=KMaxLookupRetries; |
|
1708 break; |
|
1709 |
|
1710 //-- unexpected result code. |
|
1711 default: |
|
1712 ASSERT(0); |
|
1713 i=KMaxLookupRetries; |
|
1714 break; |
|
1715 |
|
1716 |
|
1717 };//switch(nRes) |
|
1718 |
|
1719 }//for(TInt i=0; i<KMaxLookupRetries; ++i) |
|
1720 |
|
1721 //-- something went wrong, Bit Fat supercache could not find FAT cache sector that contains at least one free FAT entry. |
|
1722 //-- this is most likely because of the FAT data mismatch between FAT and bit cache. |
|
1723 __PRINT(_L("#++ CAtaFatTable::FindClosestFreeClusterL FALLBACK #1")); |
|
1724 |
|
1725 //!!!!?? use not aCluster, but previous search result here ??? |
|
1726 return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method |
|
1727 } |
|
1728 |
|
1729 |
|
1730 |
|
1731 /** |
|
1732 Get the next cluster in the chain from the FAT |
|
1733 |
|
1734 @param aCluster number to read, contains next cluster upon return |
|
1735 @leave |
|
1736 @return False if end of cluster chain |
|
1737 */ |
|
1738 TBool CFatTable::GetNextClusterL(TInt& aCluster) const |
|
1739 { |
|
1740 __PRINT1(_L("CAtaFatTable::GetNextClusterL(%d)"), aCluster); |
|
1741 |
|
1742 const TInt nextCluster = ReadL(aCluster); |
|
1743 TBool ret = EFalse; |
|
1744 |
|
1745 switch(FatType()) |
|
1746 { |
|
1747 case EFat12: |
|
1748 ret=!IsEof12Bit(nextCluster); |
|
1749 break; |
|
1750 |
|
1751 case EFat16: |
|
1752 ret=!IsEof16Bit(nextCluster); |
|
1753 break; |
|
1754 |
|
1755 case EFat32: |
|
1756 ret=!IsEof32Bit(nextCluster); |
|
1757 break; |
|
1758 |
|
1759 default: |
|
1760 ASSERT(0); |
|
1761 return EFalse;//-- get rid of warning |
|
1762 }; |
|
1763 |
|
1764 if (ret) |
|
1765 { |
|
1766 aCluster=nextCluster; |
|
1767 } |
|
1768 |
|
1769 return ret; |
|
1770 |
|
1771 } |
|
1772 |
|
1773 /** |
|
1774 Write EOF to aFatIndex |
|
1775 @param aFatIndex index in FAT (cluster number) to be written |
|
1776 */ |
|
1777 void CFatTable::WriteFatEntryEofL(TUint32 aFatIndex) |
|
1778 { |
|
1779 __PRINT1(_L("CAtaFatTable::WriteFatEntryEofL(%d)"), aFatIndex); |
|
1780 |
|
1781 //-- use EOF_32Bit (0x0fffffff) for all types of FAT, FAT cache will mask it appropriately |
|
1782 WriteL(aFatIndex, EOF_32Bit); |
|
1783 } |
|
1784 |
|
1785 |
|
1786 |
|
1787 /** |
|
1788 Mark cluster number aFatIndex in FAT as bad |
|
1789 @param aFatIndex index in FAT (cluster number) to be written |
|
1790 */ |
|
1791 void CFatTable::MarkAsBadClusterL(TUint32 aFatIndex) |
|
1792 { |
|
1793 __PRINT1(_L("CAtaFatTable::MarkAsBadClusterL(%d)"),aFatIndex); |
|
1794 |
|
1795 //-- use KBad_32Bit (0x0ffffff7) for all types of FAT, FAT cache will mask it appropriately |
|
1796 WriteL(aFatIndex, KBad_32Bit); |
|
1797 |
|
1798 FlushL(); |
|
1799 } |
|
1800 |
|
1801 |
|
1802 /** |
|
1803 Return the location of a Cluster in the data section of the media |
|
1804 |
|
1805 @param aCluster to find location of |
|
1806 @return Byte offset of the cluster data |
|
1807 */ |
|
1808 TInt64 CAtaFatTable::DataPositionInBytes(TUint32 aCluster) const |
|
1809 { |
|
1810 |
|
1811 __ASSERT_DEBUG(ClusterNumberValid(aCluster), Fault(EFatTable_InvalidIndex)); |
|
1812 |
|
1813 const TInt clusterBasePosition=iOwner->ClusterBasePosition(); |
|
1814 return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition); |
|
1815 } |
|
1816 |
|
1817 |
|
1818 |
|
1819 |
|
1820 //####################################################################################################################################### |
|
1821 //# CFatHelperThreadBase implementation |
|
1822 //####################################################################################################################################### |
|
1823 |
|
1824 //----------------------------------------------------------------------------- |
|
1825 CFatHelperThreadBase::CFatHelperThreadBase(CAtaFatTable& aOwner) |
|
1826 :iOwner(aOwner) |
|
1827 { |
|
1828 |
|
1829 SetState(EInvalid); |
|
1830 } |
|
1831 |
|
1832 CFatHelperThreadBase::~CFatHelperThreadBase() |
|
1833 { |
|
1834 Close(); |
|
1835 } |
|
1836 |
|
1837 //----------------------------------------------------------------------------- |
|
1838 /** |
|
1839 Closes the thread object handle. |
|
1840 The thread shall not be running. |
|
1841 */ |
|
1842 void CFatHelperThreadBase::Close() |
|
1843 { |
|
1844 if(ThreadWorking()) |
|
1845 { |
|
1846 ASSERT(0); |
|
1847 ForceStop(); |
|
1848 } |
|
1849 |
|
1850 iThread.Close(); |
|
1851 } |
|
1852 |
|
1853 //----------------------------------------------------------------------------- |
|
1854 /** |
|
1855 Waits for the thread to finish (thread function exit). if it is running. |
|
1856 @return thread completion code. |
|
1857 |
|
1858 !!!! definitely need a timeout processing here to avoid any possibitlity of hanging forever !! |
|
1859 |
|
1860 */ |
|
1861 TInt CFatHelperThreadBase::WaitToFinish() const |
|
1862 { |
|
1863 if(!ThreadWorking()) |
|
1864 return ThreadCompletionCode(); |
|
1865 |
|
1866 |
|
1867 //--todo: use timeout and assert to avoid hanging forever ? |
|
1868 __PRINT1(_L("#= CFatHelperThreadBase::WaitToFinish(), stat:%d"),iThreadStatus.Int()); |
|
1869 User::WaitForRequest(iThreadStatus); |
|
1870 return iThreadStatus.Int(); |
|
1871 } |
|
1872 |
|
1873 //----------------------------------------------------------------------------- |
|
1874 |
|
1875 /** |
|
1876 Requests the fat helper thread function to finish gracefully ASAP; then closes the thread handle. |
|
1877 Just sets a flag that is analysed by the thread function and waits thread's request completion. |
|
1878 */ |
|
1879 void CFatHelperThreadBase::ForceStop() |
|
1880 { |
|
1881 if(ThreadWorking()) |
|
1882 { |
|
1883 DBG_STATEMENT(TName name = iThread.Name();) |
|
1884 __PRINT3(_L("#=!! CFatHelperThreadBase::ForceStop() id:%u, name:%S, status:%d"), (TUint)iThread.Id(), &name, ThreadCompletionCode()); |
|
1885 DBG_STATEMENT(name.Zero()); //-- to avoid warning |
|
1886 |
|
1887 iOwner.AcquireLock(); |
|
1888 |
|
1889 AllowToLive(EFalse) ; //-- signal the thread to exit ASAP |
|
1890 |
|
1891 iOwner.ReleaseLock(); |
|
1892 |
|
1893 WaitToFinish(); //-- wait for the thread to finish. |
|
1894 |
|
1895 //-- don't know why but we need a delay, at least on the emulator. Otherwise thread object doesn't look destroyed. |
|
1896 //-- probably something with scheduling. |
|
1897 User::After(10*K1mSec); |
|
1898 } |
|
1899 |
|
1900 iThread.Close(); |
|
1901 } |
|
1902 |
|
1903 |
|
1904 //----------------------------------------------------------------------------- |
|
1905 |
|
1906 |
|
1907 /** |
|
1908 Created, initialises and starts the helper thread. |
|
1909 |
|
1910 @param aFunction pointer to the thread function |
|
1911 @param aThreadParameter parameter to be passed to the thread function. Its interpretation depends on the thread function. |
|
1912 @return KErrNone on success; standard error code otherwise |
|
1913 */ |
|
1914 TInt CFatHelperThreadBase::DoLaunchThread(TThreadFunction aFunction, TAny* aThreadParameter) |
|
1915 { |
|
1916 __PRINT2(_L("#=- CFatHelperThreadBase::DoLaunchThread() thread stat:%d, state:%d"), ThreadCompletionCode(), State()); |
|
1917 |
|
1918 ASSERT(aFunction); |
|
1919 ASSERT(State() != EWorking); |
|
1920 |
|
1921 if(ThreadWorking()) |
|
1922 { |
|
1923 ASSERT(0); |
|
1924 return KErrInUse; |
|
1925 } |
|
1926 |
|
1927 if(iOwner.OwnerMount()->Drive().IsSynchronous()) |
|
1928 { |
|
1929 //-- if the drive is synchronous, this is a main File Server thread. Don't play with it, it has its own scheduler |
|
1930 //-- and completing other requests rather than native CFsRequest leads to the stray events, because it waits on the |
|
1931 //-- User::WaitForAnyRequest and doesn't check which request has completed. |
|
1932 __PRINT(_L("CFatHelperThreadBase::DoLaunchThread() the drive is synchronous, skipping.")); |
|
1933 return KErrNotSupported; |
|
1934 } |
|
1935 |
|
1936 |
|
1937 TInt nRes; |
|
1938 TName nameBuf; //-- this will be initial thread name, it will rename itself in its thread function |
|
1939 nameBuf.Format(_L("Fat32HelperThread_drv_%d"), iOwner.OwnerMount()->DriveNumber()); |
|
1940 const TInt stackSz = 4*K1KiloByte; //-- thread stack size, 4K |
|
1941 |
|
1942 iThread.Close(); |
|
1943 |
|
1944 //-- 1. create the thread |
|
1945 nRes = iThread.Create(nameBuf, aFunction, stackSz, &User::Allocator(), aThreadParameter, EOwnerProcess); |
|
1946 if(nRes != KErrNone) |
|
1947 { |
|
1948 __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#1 res:%d"), nRes); |
|
1949 iThread.Close(); |
|
1950 ASSERT(0); |
|
1951 return nRes; |
|
1952 } |
|
1953 |
|
1954 //-- 2. set up its working environment |
|
1955 AllowToLive(ETrue); |
|
1956 iThread.SetPriority((TThreadPriority)EHelperPriorityNormal); //-- initially the thread has very low priority |
|
1957 |
|
1958 //-- the state of this object now will be controlled by the thread |
|
1959 SetState(ENotStarted); |
|
1960 |
|
1961 //-- 3. resume thread and wait until it finishes its initialisation |
|
1962 TRequestStatus rqStatInit(KRequestPending); |
|
1963 |
|
1964 iThread.Logon(iThreadStatus); |
|
1965 iThread.Rendezvous(rqStatInit); |
|
1966 iThread.Resume(); |
|
1967 |
|
1968 User::WaitForRequest(rqStatInit); |
|
1969 |
|
1970 if(rqStatInit.Int() != KErrNone) |
|
1971 {//-- thread couldn't initialise |
|
1972 __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#2 res:%d"), nRes); |
|
1973 ForceStop(); |
|
1974 ASSERT(0); |
|
1975 return nRes; |
|
1976 } |
|
1977 |
|
1978 //-- Helper FAT thread is running now |
|
1979 return KErrNone; |
|
1980 } |
|
1981 |
|
1982 |
|
1983 //####################################################################################################################################### |
|
1984 //# CFat32ScanThread implementation |
|
1985 //####################################################################################################################################### |
|
1986 |
|
1987 |
|
1988 CFat32ScanThread::CFat32ScanThread(CAtaFatTable& aOwner) |
|
1989 :CFatHelperThreadBase(aOwner) |
|
1990 { |
|
1991 } |
|
1992 |
|
1993 //----------------------------------------------------------------------------- |
|
1994 |
|
1995 /** |
|
1996 Launches the FAT32_ScanThread scaner thread. |
|
1997 @return standard error code |
|
1998 */ |
|
1999 TInt CFat32ScanThread::Launch() |
|
2000 { |
|
2001 return DoLaunchThread(FAT32_ScanThread, this); |
|
2002 } |
|
2003 |
|
2004 //----------------------------------------------------------------------------- |
|
2005 |
|
2006 /** |
|
2007 FAT32_ScanThread preamble function. It gets called by the scan thread at the very beginning. |
|
2008 Does some initialisation work and its return code is signaled to the thread owner by RThread::Rendezvous(); |
|
2009 |
|
2010 @return Thread object initialisation code, KErrNone on success. |
|
2011 */ |
|
2012 TInt CFat32ScanThread::Thread_Preamble() |
|
2013 { |
|
2014 //__PRINT(_L("#=- CFat32ScanThread::Thread_Preamble()")); |
|
2015 |
|
2016 ipFatBitCache = iOwner.iCache->BitCacheInterface(); |
|
2017 iTimeStart.UniversalTime(); //-- take thread start time |
|
2018 |
|
2019 ASSERT(State() == CFatHelperThreadBase::ENotStarted); //-- see the thread launcher |
|
2020 |
|
2021 if(!iOwner.IsFat32()) |
|
2022 {//-- this stuff is supposed to work for FAT32 only |
|
2023 ASSERT(0); |
|
2024 return KErrArgument; |
|
2025 } |
|
2026 |
|
2027 return KErrNone; |
|
2028 } |
|
2029 |
|
2030 //----------------------------------------------------------------------------- |
|
2031 /** |
|
2032 FAT32_ScanThread postamble function. It gets called by the scan thread just before its function exits. |
|
2033 Does some finalisation work and its return code is the thread completion code; |
|
2034 |
|
2035 @return Thread object finalisation code, KErrNone on success. |
|
2036 */ |
|
2037 TInt CFat32ScanThread::Thread_Postamble(TInt aResult) |
|
2038 { |
|
2039 //__PRINT(_L("#=- CFat32ScanThread::Thread_Postamble()")); |
|
2040 |
|
2041 #ifdef _DEBUG |
|
2042 //-- print out time taken the thread to finish |
|
2043 TName nameBuf; |
|
2044 iTimeEnd.UniversalTime(); //-- take end time |
|
2045 const TInt msScanTime = (TInt)( (iTimeEnd.MicroSecondsFrom(iTimeStart)).Int64() / K1mSec); |
|
2046 nameBuf.Copy(RThread().Name()); |
|
2047 nameBuf.Insert(0,_L("#=-<<<")); |
|
2048 nameBuf.AppendFormat(_L(" Thread Exit. id:%d, Code:%d, time:%d ms"), (TUint)RThread().Id(), aResult, msScanTime); |
|
2049 __PRINT(nameBuf); |
|
2050 #endif |
|
2051 |
|
2052 //-- tell FAT bit supercache (if we have it) that we have finished populating it, successfully or not |
|
2053 if(ipFatBitCache) |
|
2054 { |
|
2055 ipFatBitCache->FinishPopulating(aResult == KErrNone); |
|
2056 ipFatBitCache->Dump(); |
|
2057 } |
|
2058 |
|
2059 //-- close FAT chunk buffer |
|
2060 iFatChunkBuf.Close(); |
|
2061 |
|
2062 //-- set the host object state depending on the work results. |
|
2063 if(aResult == KErrNone) |
|
2064 SetState(CFatHelperThreadBase::EFinished_OK); |
|
2065 else |
|
2066 SetState(CFatHelperThreadBase::EFailed); |
|
2067 |
|
2068 |
|
2069 return aResult; |
|
2070 } |
|
2071 |
|
2072 //####################################################################################################################################### |
|
2073 //# CFat32FreeSpaceScanner implementation |
|
2074 //####################################################################################################################################### |
|
2075 |
|
2076 CFat32FreeSpaceScanner::CFat32FreeSpaceScanner(CAtaFatTable& aOwner) |
|
2077 :CFat32ScanThread(aOwner) |
|
2078 { |
|
2079 } |
|
2080 |
|
2081 /** |
|
2082 Factory method. |
|
2083 @param aOwner owning CAtaFatTable |
|
2084 @return pointer to the constructed instance of the class |
|
2085 */ |
|
2086 CFat32FreeSpaceScanner* CFat32FreeSpaceScanner::NewL(CAtaFatTable& aOwner) |
|
2087 { |
|
2088 CFat32FreeSpaceScanner* pThis = NULL; |
|
2089 pThis = new (ELeave) CFat32FreeSpaceScanner(aOwner); |
|
2090 |
|
2091 return pThis; |
|
2092 } |
|
2093 |
|
2094 //----------------------------------------------------------------------------- |
|
2095 |
|
2096 /** |
|
2097 Waits until FAT32 free clusters scan thread allows other thread (caller) to write to the FAT entry "aFatIndex". |
|
2098 Thread scans FAT from the beginning to the end and just waits untill scanning passes the entry number "aFatIndex" |
|
2099 |
|
2100 @param aFatIndex index of the FAT entry we are going to write. |
|
2101 */ |
|
2102 void CFat32FreeSpaceScanner::RequestFatEntryWriteAccess(TUint32 aFatIndex) const |
|
2103 { |
|
2104 if(!ThreadWorking()) |
|
2105 return; |
|
2106 |
|
2107 ASSERT(iOwner.ClusterNumberValid(aFatIndex)); |
|
2108 |
|
2109 const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity |
|
2110 |
|
2111 //-- wait until FAT[aFatIndex] is available to write |
|
2112 while(aFatIndex > ClustersScanned() && ThreadWorking()) |
|
2113 { |
|
2114 BoostPriority(ETrue); //-- Boost scan thread priority |
|
2115 User::After(KWaitGranularity); |
|
2116 } |
|
2117 } |
|
2118 |
|
2119 //----------------------------------------------------------------------------- |
|
2120 |
|
2121 /** just an internal helper method. Stores the number of FAT entries already scanned by FAT free entries scan thread. */ |
|
2122 void CFat32FreeSpaceScanner::SetClustersScanned(TUint32 aClusters) |
|
2123 { |
|
2124 XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section |
|
2125 iClustersScanned=aClusters; |
|
2126 } |
|
2127 |
|
2128 /** just an internal helper method. returns the number of FAT entries already scanned by FAT free entrie sscan thread. */ |
|
2129 TUint32 CFat32FreeSpaceScanner::ClustersScanned() const |
|
2130 { |
|
2131 XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section |
|
2132 return iClustersScanned; |
|
2133 } |
|
2134 |
|
2135 //----------------------------------------------------------------------------- |
|
2136 |
|
2137 /** |
|
2138 overriden FAT32_ScanThread preamble function. |
|
2139 See CFat32ScanThread::Thread_Preamble() |
|
2140 */ |
|
2141 TInt CFat32FreeSpaceScanner::Thread_Preamble() |
|
2142 { |
|
2143 __PRINT1(_L("#=- CFat32FreeSpaceScanner::Thread_Preamble(), FAT state:%d"), iOwner.State()); |
|
2144 |
|
2145 ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan); |
|
2146 |
|
2147 //-- invoke generic preamble first |
|
2148 TInt nRes = CFat32ScanThread::Thread_Preamble(); |
|
2149 if(nRes != KErrNone) |
|
2150 return nRes; |
|
2151 |
|
2152 //-- do specific to this thread object initialisation work |
|
2153 |
|
2154 //-- rename the thread |
|
2155 TName nameBuf; |
|
2156 const CFatMountCB& fatMount = *(iOwner.OwnerMount()); |
|
2157 nameBuf.Format(_L("Fat32FreeSpaceScanner_drv_%d"), fatMount.DriveNumber()); |
|
2158 RThread::RenameMe(nameBuf); |
|
2159 |
|
2160 //-- allocate FAT chunk buffer; its size will depend on FAT table size. |
|
2161 const TUint32 fatSz = iOwner.MaxEntries() << KFat32EntrySzLog2; |
|
2162 |
|
2163 if(fatSz < KBigSzFat_Threshold) |
|
2164 {//-- create a small buffer |
|
2165 if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone) |
|
2166 return KErrNoMemory; |
|
2167 } |
|
2168 else |
|
2169 {//-- try to create larger buffer |
|
2170 if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Big) != KErrNone && iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone) |
|
2171 return KErrNoMemory; |
|
2172 } |
|
2173 |
|
2174 |
|
2175 //-- setup FAT table's parameters |
|
2176 //-- No free clusters yet; be careful with SetFreeClusters(), free clusters count can be |
|
2177 //-- modified from other thread, e.g. from FreeClusterList. Use read-modify-write instead of assignment. |
|
2178 SetClustersScanned(0); |
|
2179 iOwner.SetFreeClusters(0); |
|
2180 |
|
2181 //-- calculate number of FAT entires need to be processed for CMountCB::SetDiskSpaceChange() call. |
|
2182 //-- if number of processed entries in FAT exceeds iEntriesNotifyThreshold, CMountCB::SetDiskSpaceChange() |
|
2183 //-- will be called and the iEntriesNotifyThreshold will be updated. |
|
2184 iNfyThresholdInc = (TUint32)KVolSpaceNotifyThreshold >> fatMount.ClusterSizeLog2(); |
|
2185 iEntriesNotifyThreshold = iNfyThresholdInc; |
|
2186 |
|
2187 //-- if there is an interface to the FAT bit supercache, tell it to start populating. |
|
2188 //-- We will be populating this cache while reading and parsing FAT32. |
|
2189 if(ipFatBitCache) |
|
2190 ipFatBitCache->StartPopulating(); |
|
2191 |
|
2192 |
|
2193 return KErrNone; |
|
2194 } |
|
2195 |
|
2196 //----------------------------------------------------------------------------- |
|
2197 /** |
|
2198 overriden FAT32_ScanThread postamble function. |
|
2199 See CFat32ScanThread::Thread_Postamble() |
|
2200 */ |
|
2201 TInt CFat32FreeSpaceScanner::Thread_Postamble(TInt aResult) |
|
2202 { |
|
2203 __PRINT2(_L("#=- CFat32FreeSpaceScanner::Thread_Postamble(%d), FAT state:%d"), aResult, iOwner.State()); |
|
2204 __PRINT2(_L("#=- FAT_ScanThread: counted Free clusters:%d, 1st free:%d"), iOwner.NumberOfFreeClusters(), iOwner.FreeClusterHint()); |
|
2205 |
|
2206 ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan); |
|
2207 |
|
2208 //-- there was an error somewhere within FAT32 scan thread |
|
2209 if(aResult != KErrNone) |
|
2210 { |
|
2211 //-- indicate that the FAT table initialisation failed |
|
2212 __PRINT(_L("#=- Asynch FAT table initialisation failed !")); |
|
2213 |
|
2214 iOwner.SetState(CAtaFatTable::EMountAborted); |
|
2215 |
|
2216 //-- fix up some FAT table parameters |
|
2217 if(iOwner.FreeClusterHint() < KFatFirstSearchCluster) |
|
2218 iOwner.SetFreeClusterHint(KFatFirstSearchCluster); |
|
2219 |
|
2220 } |
|
2221 |
|
2222 |
|
2223 //-- call generic postamble |
|
2224 TInt nRes = CFat32ScanThread::Thread_Postamble(aResult); |
|
2225 |
|
2226 if(nRes == KErrNone) |
|
2227 {//-- FAT table now fully initialised |
|
2228 ASSERT(aResult == KErrNone); |
|
2229 iOwner.SetState(CAtaFatTable::EMounted); |
|
2230 |
|
2231 //-- free space counting finished OK, call the notifier last time |
|
2232 CFatMountCB& fatMount = *(iOwner.OwnerMount()); |
|
2233 |
|
2234 iOwner.AcquireLock(); |
|
2235 const TInt64 currFreeSpace = ((TInt64)iOwner.FreeClusters()) << fatMount.ClusterSizeLog2(); |
|
2236 iOwner.ReleaseLock(); |
|
2237 |
|
2238 fatMount.SetDiskSpaceChange(currFreeSpace); |
|
2239 |
|
2240 |
|
2241 } |
|
2242 else if(aResult == KErrNone) |
|
2243 {//-- CFat32ScanThread::Thread_Postamble() signaled a fault |
|
2244 iOwner.SetState(CAtaFatTable::EMountAborted); |
|
2245 } |
|
2246 |
|
2247 return aResult; |
|
2248 } |
|
2249 |
|
2250 //----------------------------------------------------------------------------- |
|
2251 /** |
|
2252 Process free FAT entries collected by the scan thread that parses chunk of FAT data. |
|
2253 This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed |
|
2254 |
|
2255 @param aFreeEntriesInChunk number of free FAT entries counted in FAT chunk |
|
2256 @param aCurrFirstFreeEntry current number of the first free FAT entry found |
|
2257 @param aClustersScanned total number of FAT entries scanned by the thread |
|
2258 |
|
2259 @return standard error code, KErrNone on success |
|
2260 */ |
|
2261 TInt CFat32FreeSpaceScanner::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam) |
|
2262 { |
|
2263 ASSERT(State() == CFatHelperThreadBase::EWorking); |
|
2264 |
|
2265 CAtaFatTable& ataFatTable = iOwner; |
|
2266 |
|
2267 //------------------------------------------- |
|
2268 //-- publish values to the CAtaFatTable object |
|
2269 ataFatTable.AcquireLock(); |
|
2270 |
|
2271 //-- publish free cluster count, use read-modify-write here |
|
2272 //-- CFatTable::iFreeClusters can be already modified from other thread. |
|
2273 TUint32 currFreeClusters = ataFatTable.FreeClusters(); //-- simple non-thread safe method |
|
2274 |
|
2275 currFreeClusters += aFatScanParam.iCurrFreeEntries; |
|
2276 |
|
2277 ataFatTable.SetFreeClusters(currFreeClusters); |
|
2278 |
|
2279 //-- store total number of scanned clusters (not to be modified from other thread) |
|
2280 const TUint32 scannedEntries = aFatScanParam.iEntriesScanned; |
|
2281 SetClustersScanned(scannedEntries); |
|
2282 |
|
2283 |
|
2284 if(aFatScanParam.iFirstFree >= KFatFirstSearchCluster) |
|
2285 ataFatTable.SetFreeClusterHint(aFatScanParam.iFirstFree);//-- probably found next free cluster number |
|
2286 |
|
2287 ataFatTable.ReleaseLock(); |
|
2288 |
|
2289 //-- check if we need to call CMountCB::SetDiskSpaceChange() to notify it that the amount of processed FAT entries has reached the given threshold |
|
2290 if(scannedEntries >= iEntriesNotifyThreshold) |
|
2291 { |
|
2292 iEntriesNotifyThreshold += iNfyThresholdInc; |
|
2293 |
|
2294 CFatMountCB& fatMount = *(iOwner.OwnerMount()); |
|
2295 const TInt64 currFreeSpace = ((TInt64)currFreeClusters) << fatMount.ClusterSizeLog2(); |
|
2296 fatMount.SetDiskSpaceChange(currFreeSpace); |
|
2297 } |
|
2298 |
|
2299 |
|
2300 return KErrNone; |
|
2301 } |
|
2302 |
|
2303 //####################################################################################################################################### |
|
2304 //# CFat32BitCachePopulator implementation |
|
2305 //####################################################################################################################################### |
|
2306 CFat32BitCachePopulator::CFat32BitCachePopulator(CAtaFatTable& aOwner) |
|
2307 :CFat32ScanThread(aOwner) |
|
2308 { |
|
2309 } |
|
2310 |
|
2311 /** |
|
2312 Factory method. |
|
2313 @param aOwner owning CAtaFatTable |
|
2314 @return pointer to the constructed instance of the class |
|
2315 */ |
|
2316 CFat32BitCachePopulator* CFat32BitCachePopulator::NewL(CAtaFatTable& aOwner) |
|
2317 { |
|
2318 CFat32BitCachePopulator* pThis = NULL; |
|
2319 pThis = new (ELeave) CFat32BitCachePopulator(aOwner); |
|
2320 |
|
2321 return pThis; |
|
2322 } |
|
2323 |
|
2324 //----------------------------------------------------------------------------- |
|
2325 |
|
2326 /** |
|
2327 The main FS thread tries to write the "aFatIndex" entry in FAT while this thread is running. |
|
2328 We can't do anything useful here, because FAT32 bit supercache doesn't work on FAT entry level and |
|
2329 deals with much less scale - FAT32 cache sector, which can consist from many FAT32 entries. |
|
2330 The conflict situation will be resolved in the CAtaFatTable::WriteL() |
|
2331 */ |
|
2332 void CFat32BitCachePopulator::RequestFatEntryWriteAccess(TUint32 /*aFatIndex*/) const |
|
2333 { |
|
2334 //-- do nothing here, do not block the caller |
|
2335 } |
|
2336 |
|
2337 |
|
2338 //----------------------------------------------------------------------------- |
|
2339 /** |
|
2340 overriden FAT32_ScanThread preamble function. |
|
2341 See CFat32ScanThread::Thread_Preamble() |
|
2342 */ |
|
2343 TInt CFat32BitCachePopulator::Thread_Preamble() |
|
2344 { |
|
2345 __PRINT(_L("#=- CFat32BitCachePopulator::Thread_Preamble()")); |
|
2346 |
|
2347 //-- invoke generic preamble |
|
2348 TInt nRes = CFat32ScanThread::Thread_Preamble(); |
|
2349 if(nRes != KErrNone) |
|
2350 return nRes; |
|
2351 |
|
2352 //-- do specific to this thread object initialisation work |
|
2353 iTotalOccupiedFatEntries = 0; |
|
2354 |
|
2355 //-- rename the thread |
|
2356 TName nameBuf; |
|
2357 const CFatMountCB& fatMount = *(iOwner.OwnerMount()); |
|
2358 nameBuf.Format(_L("CFat32BitCachePopulator_drv_%d"), fatMount.DriveNumber()); |
|
2359 RThread::RenameMe(nameBuf); |
|
2360 |
|
2361 //-- allocate FAT chunk buffer |
|
2362 nRes = iFatChunkBuf.CreateMax(KFatChunkBufSize); |
|
2363 if(nRes != KErrNone) |
|
2364 return nRes; |
|
2365 |
|
2366 |
|
2367 if(!ipFatBitCache) |
|
2368 {//-- this is a bit cache populator and the bit cache object must have been constructed before setting up the populating thread. |
|
2369 ASSERT(0); |
|
2370 return KErrCorrupt; |
|
2371 } |
|
2372 |
|
2373 //-- Tell FAT bit supercache to start populating. We will be populating this cache while reading and parsing FAT32. |
|
2374 if(ipFatBitCache->StartPopulating()) |
|
2375 nRes = KErrNone; |
|
2376 else |
|
2377 nRes = KErrCorrupt; |
|
2378 |
|
2379 return nRes; |
|
2380 } |
|
2381 |
|
2382 //----------------------------------------------------------------------------- |
|
2383 |
|
2384 /** |
|
2385 overriden FAT32_ScanThread postamble function. |
|
2386 See CFat32ScanThread::Thread_Postamble() |
|
2387 */ |
|
2388 TInt CFat32BitCachePopulator::Thread_Postamble(TInt aResult) |
|
2389 { |
|
2390 __PRINT1(_L("#=- CFat32BitCachePopulator::Thread_Postamble(%d)"), aResult); |
|
2391 |
|
2392 //-- nothing specific to do, just call generic method |
|
2393 return CFat32ScanThread::Thread_Postamble(aResult); |
|
2394 } |
|
2395 |
|
2396 //----------------------------------------------------------------------------- |
|
2397 /** |
|
2398 This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed |
|
2399 @return standard error code, KErrNone on success |
|
2400 */ |
|
2401 TInt CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam) |
|
2402 { |
|
2403 ASSERT(State() == CFatHelperThreadBase::EWorking); |
|
2404 |
|
2405 //-- check the bit cache state |
|
2406 if(ipFatBitCache->State() != CFatBitCache::EPopulating) |
|
2407 {//-- something wrong happened to the cache, e.g. someone forcedly invalidated it (probably from another thread) |
|
2408 return KErrAbort; |
|
2409 } |
|
2410 |
|
2411 |
|
2412 //-- if CFat32BitCachePopulator has already counted all _occupied_ FAT entries, there is no need to |
|
2413 //-- continue FAT reading; just mark the rest of the FAT bit supercache as containing free FAT entries and abort scanning |
|
2414 |
|
2415 CAtaFatTable& ataFatTable = iOwner; |
|
2416 |
|
2417 ataFatTable.AcquireLock(); |
|
2418 |
|
2419 //-- current amount of non-free entries in FAT, excluding FAT[0] & FAT[1] |
|
2420 const TUint32 KCurrNonFreeEntries = ataFatTable.MaxEntries() - ataFatTable.FreeClusters() - KFatFirstSearchCluster; |
|
2421 |
|
2422 iTotalOccupiedFatEntries += aFatScanParam.iCurrOccupiedEntries; |
|
2423 |
|
2424 //-- check if the thread needs to continue it work |
|
2425 const TBool KNoNeedToScanFurther = (iTotalOccupiedFatEntries >= KCurrNonFreeEntries); |
|
2426 |
|
2427 if(KNoNeedToScanFurther) |
|
2428 { |
|
2429 //-- tell FAT bit supercache to mark the range from currently scanned FAT entry to the end of the FAT as containing free entries. |
|
2430 __PRINT2(_L("#=- CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries() counted: %d/%d; aborting scan."), iTotalOccupiedFatEntries, KCurrNonFreeEntries); |
|
2431 |
|
2432 const TUint32 entryStart = aFatScanParam.iEntriesScanned; //-- first FAT entry in the range to be marked as 'free' |
|
2433 const TUint32 entryEnd = ataFatTable.MaxEntries()-1; //-- last FAT entry in the range to be marked as 'free', last FAT entry |
|
2434 |
|
2435 ipFatBitCache->MarkFatRange(entryStart, entryEnd, ETrue); |
|
2436 |
|
2437 //-- signal that the thread shall finish with normal (KErrNone) reason |
|
2438 //-- it will also normally finish FAT bit cache populating in postamble |
|
2439 AllowToLive(EFalse); |
|
2440 } |
|
2441 |
|
2442 ataFatTable.ReleaseLock(); |
|
2443 |
|
2444 |
|
2445 return KErrNone; |
|
2446 } |
|
2447 |
|
2448 |
|
2449 //####################################################################################################################################### |
|
2450 /** |
|
2451 FAT32 free entries scan thread function. Walks through FAT32 and counts free entries. |
|
2452 It uses its own buffer to read FAT and parse it in order to avoid multithreaded problems with FAT cache and don't thrash it. |
|
2453 |
|
2454 @param apHostObject pointer to the host object of CFat32ScanThread base class. |
|
2455 */ |
|
2456 //####################################################################################################################################### |
|
2457 TInt FAT32_ScanThread(TAny* apHostObject) |
|
2458 { |
|
2459 TInt nRes; |
|
2460 |
|
2461 #ifdef _DEBUG |
|
2462 TName nameBuf; |
|
2463 nameBuf.Copy(RThread().Name()); |
|
2464 nameBuf.Insert(0,_L("#=->>>")); nameBuf.AppendFormat(_L(" Thread Enter (id:%d)"), (TUint)RThread().Id()); |
|
2465 __PRINT(nameBuf); |
|
2466 #endif |
|
2467 |
|
2468 ASSERT(apHostObject); |
|
2469 CFat32FreeSpaceScanner* pSelf = (CFat32FreeSpaceScanner*)apHostObject; |
|
2470 |
|
2471 CAtaFatTable& ataFatTable = pSelf->iOwner; |
|
2472 CFatMountCB& fatMount = *(ataFatTable.OwnerMount()); |
|
2473 |
|
2474 const TUint32 KFat32EntrySz = sizeof(TFat32Entry); |
|
2475 const TUint32 KFat1StartPos = fatMount.StartOfFatInBytes(); |
|
2476 const TUint32 KNumClusters = ataFatTable.MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters() |
|
2477 |
|
2478 //-- perform thread preamble work |
|
2479 nRes = pSelf->Thread_Preamble(); |
|
2480 |
|
2481 //-- signal the thread initialisation result |
|
2482 RThread::Rendezvous(nRes); |
|
2483 |
|
2484 |
|
2485 //-- Initialisation OK, do real job: FAT scanning |
|
2486 if(nRes == KErrNone) |
|
2487 { |
|
2488 pSelf->SetState(CFatHelperThreadBase::EWorking); |
|
2489 |
|
2490 TUint32 rem = KNumClusters * KFat32EntrySz; |
|
2491 TUint32 mediaPos = KFat1StartPos; |
|
2492 |
|
2493 CAtaFatTable::TFatScanParam fatScanParam; //-- FAT scanning parameters |
|
2494 |
|
2495 //============================================ |
|
2496 //=== FAT read and parse loop ================ |
|
2497 //-- in this loop we read portions of raw FAT32 data in a buffer, than parse this buffer |
|
2498 //-- in order to find out the number of free FAT entries there and other stuff |
|
2499 while(rem) |
|
2500 { |
|
2501 const TUint32 bytesToRead=Min(rem, (TUint32)pSelf->iFatChunkBuf.Size()); |
|
2502 TPtrC8 ptrData(pSelf->iFatChunkBuf.Ptr(), bytesToRead); |
|
2503 |
|
2504 //-- check for sudden media change |
|
2505 if(fatMount.Drive().IsChanged()) |
|
2506 { |
|
2507 __PRINT(_L("#=--- FAT32_ScanThread: Media change occured, aborting!")); |
|
2508 nRes = KErrAbort; |
|
2509 break; |
|
2510 } |
|
2511 |
|
2512 //------------------------------------------- |
|
2513 //-- read a portion of FAT into the buffer |
|
2514 ataFatTable.AcquireLock(); |
|
2515 |
|
2516 //-- check if the thread was requested to finish |
|
2517 if(!pSelf->AllowedToLive()) |
|
2518 { |
|
2519 ataFatTable.ReleaseLock(); |
|
2520 nRes = KErrAbort; |
|
2521 break; |
|
2522 } |
|
2523 |
|
2524 //-- actual read |
|
2525 //__PRINT3(_L("#=--- FAT32_ScanThread: read %d bytes pos:0x%x, boost:%d"), bytesToRead, mediaPos, pSelf->IsPriorityBoosted()); |
|
2526 |
|
2527 nRes = fatMount.LocalDrive()->Read(mediaPos, bytesToRead, pSelf->iFatChunkBuf); |
|
2528 |
|
2529 ataFatTable.ReleaseLock(); |
|
2530 |
|
2531 //------------------------------------------- |
|
2532 //-- analyse the read error code |
|
2533 if(nRes != KErrNone) |
|
2534 { |
|
2535 __PRINT1(_L("#=--- FAT32_ScanThread read error! res:%d"), nRes); |
|
2536 break; //-- abort scanning |
|
2537 } |
|
2538 |
|
2539 //------------------------------------------- |
|
2540 //-- parse FAT from the buffer |
|
2541 |
|
2542 //-- we need number of free and occupied entries in the _current_ FAT chunk being read and parsed |
|
2543 fatScanParam.iCurrFreeEntries = 0; |
|
2544 fatScanParam.iCurrOccupiedEntries = 0; |
|
2545 |
|
2546 ataFatTable.DoParseFatBuf(ptrData, fatScanParam); |
|
2547 |
|
2548 //--- process the the results of FAT buffer parsing |
|
2549 nRes = pSelf->Thread_ProcessCollectedFreeEntries(fatScanParam); |
|
2550 if(nRes != KErrNone || !pSelf->AllowedToLive()) |
|
2551 {//-- some types of worker threads may wish to finish normally but prematurely, by the result of Thread_ProcessCollectedFreeEntries() |
|
2552 break; //-- abort scanning |
|
2553 } |
|
2554 |
|
2555 |
|
2556 //-- allow this thread to be preempted by another one that wants to access the media driver. |
|
2557 //-- without this wait we will have priority inversion, because this (low priority) thread continiously reads data by big chunks |
|
2558 //-- and doesn't allow others to access the driver. |
|
2559 //-- On the other hand, if the thread's priority is boosted, there is no reason to be polite. |
|
2560 if(!pSelf->IsPriorityBoosted()) |
|
2561 User::After(K1mSec); //-- User::After() granularity can be much coarser than 1ms |
|
2562 |
|
2563 //------------------------------------------- |
|
2564 mediaPos += bytesToRead; |
|
2565 rem -= bytesToRead; |
|
2566 |
|
2567 }//while(rem) |
|
2568 |
|
2569 }//if(nRes == KErrNone) |
|
2570 |
|
2571 |
|
2572 //-- perform thread postamble work |
|
2573 nRes = pSelf->Thread_Postamble(nRes); |
|
2574 |
|
2575 return nRes; |
|
2576 } |
|
2577 |
|
2578 |
|
2579 |
|
2580 |
|
2581 |
|
2582 |
|
2583 |
|
2584 |
|
2585 |
|
2586 |
|
2587 |
|
2588 |
|
2589 |
|
2590 |
|
2591 |
|
2592 |
|
2593 |
|
2594 |
|
2595 |
|
2596 |
|
2597 |
|
2598 |
|
2599 |
|
2600 |
|
2601 |
|
2602 |
|
2603 |
|
2604 |
|
2605 |
|
2606 |
|
2607 |
|
2608 |
|
2609 |