0
|
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 |
|