os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,2460 @@
     1.4 +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of the License "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +// f32\sfile\sf_file_cache.cpp
    1.18 +// 
    1.19 +//
    1.20 +
    1.21 +/**
    1.22 + @file
    1.23 + @internalTechnology
    1.24 +*/
    1.25 +
    1.26 +#include <e32std.h>
    1.27 +#include <e32std_private.h>
    1.28 +#include "sf_std.h"
    1.29 +#include <e32uid.h>
    1.30 +#include <e32wins.h>
    1.31 +#include <f32file.h>
    1.32 +#include <hal.h>
    1.33 +#include "sf_file_cache.h"
    1.34 +#include "sf_cache_man.h"
    1.35 +#include "sf_cache_client.h"
    1.36 +#include "sf_file_cache_defs.h"
    1.37 +
    1.38 +
    1.39 +// disables flushing of stale cachelines before each write
    1.40 +#define LAZY_WRITE
    1.41 +
    1.42 +enum TFileCacheFault
    1.43 +	{
    1.44 +	ECacheSettingsInitFailed,
    1.45 +	ECacheSettingsNotFound,
    1.46 +	ECacheSettingGetFailed,
    1.47 +	ECacheCodeFault,
    1.48 +	ECacheBadOperationIndex,
    1.49 +	ENoFileCache,
    1.50 +	EFileAlreadyClosed,
    1.51 +	EClosingDirtyFile,
    1.52 +	ECompletingWriteWithDataRemaining,
    1.53 +	EPosBeyondSize,
    1.54 +	EMsgAlreadyCompleted,
    1.55 +	EBadRetCode,
    1.56 +	EInternalError,
    1.57 +	ERequestUncancelled,
    1.58 +	ELockAlreadyOpen,
    1.59 +	EBadSegmentCount,
    1.60 +	EReNewingOpenCache,
    1.61 +	EClosingUnNamedFile,
    1.62 +	EFileNameAlreadyOwned,
    1.63 +	EClosedFileHasNoName,
    1.64 +	EReOpeningUnNamedFile,
    1.65 +	EStartingDirtyAWrongStage,
    1.66 +	EUnexpectedReNewLFailure,
    1.67 +	EDirtyDataOwnerNull,
    1.68 +	EFlushingWithSessionNull,
    1.69 +	};
    1.70 +
    1.71 +
    1.72 +LOCAL_C void Fault(TFileCacheFault aFault)
    1.73 +//
    1.74 +// Report a fault in the file cache
    1.75 +//
    1.76 +	{
    1.77 +	User::Panic(_L("FSFILECACHE"), aFault);
    1.78 +	}
    1.79 +
    1.80 +const TInt KMinSequentialReadsBeforeReadAhead = 3;
    1.81 +
    1.82 +//************************************
    1.83 +// CFileCache
    1.84 +//************************************
    1.85 +
    1.86 +inline TBool ReadCachingEnabled(CFileShare& aShare)
    1.87 +	{
    1.88 +	TUint mode = aShare.iMode;
    1.89 +	
    1.90 +	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
    1.91 +
    1.92 +	if (((mode & EFileReadBuffered) && (fileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))) ||
    1.93 +		(((mode & EFileReadDirectIO) == 0) && (fileCacheFlags & EFileCacheReadOn)))
    1.94 +		{
    1.95 +		return ETrue;
    1.96 +		}
    1.97 +	else
    1.98 +		{
    1.99 +		return EFalse;
   1.100 +		}
   1.101 +	}
   1.102 +
   1.103 +inline TBool WriteCachingEnabled(CFileShare& aShare)
   1.104 +	{
   1.105 +	TUint mode = aShare.iMode;
   1.106 +	
   1.107 +	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
   1.108 +
   1.109 +	if ((mode & EFileWrite) == 0)
   1.110 +		{
   1.111 +		return EFalse;
   1.112 +		}
   1.113 +	else if (((mode & EFileWriteBuffered) && (fileCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))) ||
   1.114 +		(((mode & EFileWriteDirectIO) == 0) && (fileCacheFlags & EFileCacheWriteOn)))
   1.115 +		{
   1.116 +		return ETrue;
   1.117 +		}
   1.118 +	else
   1.119 +		{
   1.120 +		return EFalse;
   1.121 +		}
   1.122 +	}
   1.123 +
   1.124 +void CFileCache::SetFileCacheFlags(CFileShare& aShare)
   1.125 +	{
   1.126 +	TInt fileCacheFlags = TFileCacheSettings::Flags(iDriveNum);
   1.127 +
   1.128 +	TUint& mode = aShare.iMode;
   1.129 +
   1.130 +	// enable/disable read ahead
   1.131 +	if (((mode & EFileReadAheadOn) && (fileCacheFlags & (EFileCacheReadAheadEnabled | EFileCacheReadAheadOn))) ||
   1.132 +		 (((mode & EFileReadAheadOff) == 0) && (fileCacheFlags & EFileCacheReadAheadOn)))
   1.133 +		{
   1.134 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead ENABLED"));
   1.135 +		mode|= EFileReadAheadOn;
   1.136 +		}
   1.137 +	else
   1.138 +		{
   1.139 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead disabled"));
   1.140 +		mode&= ~EFileReadAheadOn;
   1.141 +		}
   1.142 +
   1.143 +	// enable/disable read caching 
   1.144 +	if (ReadCachingEnabled(aShare))
   1.145 +		{
   1.146 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead ENABLED"));
   1.147 +		mode|= EFileReadBuffered;
   1.148 +		}
   1.149 +	else
   1.150 +		{
   1.151 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead disabled"));
   1.152 +		// if read caching is off, turn off read-ahead too
   1.153 +		mode&= ~(EFileReadBuffered | EFileReadAheadOn);
   1.154 +		}
   1.155 +
   1.156 +	// enable/disable write caching 
   1.157 +	if (WriteCachingEnabled(aShare))
   1.158 +		{
   1.159 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite ENABLED"));
   1.160 +		mode|= EFileWriteBuffered;
   1.161 +		}
   1.162 +	else
   1.163 +		{
   1.164 +		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite disabled"));
   1.165 +		mode&= ~EFileWriteBuffered;
   1.166 +		}
   1.167 +
   1.168 +	}
   1.169 +
   1.170 +void CFileCache::ConstructL(CFileShare& aShare)
   1.171 +	{
   1.172 +	iDrive = &aShare.File().Drive();
   1.173 +	iDriveNum = iDrive->DriveNumber();
   1.174 +	iMount=&iDrive->CurrentMount();
   1.175 +
   1.176 +	DoInitL(iDriveNum);	
   1.177 +	
   1.178 +
   1.179 +	CCacheManager* manager = CCacheManagerFactory::CacheManager();
   1.180 +
   1.181 +	iCacheClient = manager->CreateClientL();
   1.182 +	manager->RegisterClient(*iCacheClient);
   1.183 +	
   1.184 +
   1.185 +	TInt segmentSize = SegmentSize();
   1.186 +	TInt segmentSizeMask = I64LOW(SegmentSizeMask());
   1.187 +	// Get file cache size
   1.188 +	iCacheSize			= TFileCacheSettings::CacheSize(iDriveNum);
   1.189 +	// must be at least one segment
   1.190 +	iCacheSize			= Max(iCacheSize, segmentSize);
   1.191 +	// round up to nearest whole segment
   1.192 +	iCacheSize			= (iCacheSize + segmentSize - 1) & segmentSizeMask;
   1.193 +
   1.194 +	// Get max read-ahead length
   1.195 +	iMaxReadAheadLen	= TFileCacheSettings::MaxReadAheadLen(iDriveNum);
   1.196 +	// must be at least one segment
   1.197 +	iMaxReadAheadLen	= Max(iMaxReadAheadLen, segmentSize);
   1.198 +	// round up to nearest whole segment
   1.199 +	iMaxReadAheadLen	= (iMaxReadAheadLen + segmentSize - 1) & segmentSizeMask;
   1.200 +	// read-ahead should not be greater than the cache size minus one segment
   1.201 +	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheSize - segmentSize);
   1.202 +	// ... or greater than one cacheline (128K should be enough !)
   1.203 +	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheClient->CacheLineSize());
   1.204 +
   1.205 +	iFileCacheReadAsync = TFileCacheSettings::FileCacheReadAsync(iDriveNum);
   1.206 +
   1.207 +	iClosedFileKeepAliveTime	= TFileCacheSettings::ClosedFileKeepAliveTime(iDriveNum);
   1.208 +	iDirtyDataFlushTime			= TFileCacheSettings::DirtyDataFlushTime(iDriveNum);
   1.209 +
   1.210 +	// Calculate max number of segments to cache
   1.211 +	TInt maxSegmentsToCache = iCacheSize >> SegmentSizeLog2();
   1.212 +
   1.213 +	__CACHE_PRINT1(_L("CACHEFILE: maxSegmentsToCache %d"), maxSegmentsToCache);
   1.214 +
   1.215 +	iCacheClient->SetMaxSegments(maxSegmentsToCache);
   1.216 +
   1.217 +	CFileCache* fileCache = ReNewL(aShare);
   1.218 +	__ASSERT_ALWAYS(fileCache != NULL, Fault(EUnexpectedReNewLFailure));
   1.219 +	}
   1.220 +
   1.221 +CFileCache* CFileCache::NewL(CFileShare& aShare)
   1.222 +	{
   1.223 +	__CACHE_PRINT(_L("CACHEFILE: CFileCache::NewL()"));
   1.224 +
   1.225 +	CFileCB* file = &aShare.File();
   1.226 +	if ((CCacheManagerFactory::CacheManager() == NULL) ||
   1.227 +		(!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare)) ||
   1.228 +		(FsThreadManager::IsDriveSync(file->Drive().DriveNumber(),EFalse)))
   1.229 +		return NULL;
   1.230 +
   1.231 +	CFileCache* fileCache = new(ELeave) CFileCache();
   1.232 +	CleanupClosePushL(*fileCache);
   1.233 +	fileCache->ConstructL(aShare);
   1.234 +	CleanupStack::Pop(1, fileCache);
   1.235 +	return fileCache;
   1.236 +	}
   1.237 +
   1.238 +CFileCache* CFileCache::ReNewL(CFileShare& aShare)
   1.239 +	{
   1.240 +	__CACHE_PRINT(_L("CACHEFILE: CFileCache::ReNewL()"));
   1.241 +
   1.242 +	// check not already open i.e. attached to a CFileCB
   1.243 +	__ASSERT_DEBUG(iFileCB == NULL, Fault(EReNewingOpenCache));
   1.244 +
   1.245 +	// make sure the drive thread exists (the mount may have been mounted
   1.246 +	// synchronously since the drive was last open)
   1.247 +	const TInt r = FsThreadManager::GetDriveThread(iDriveNum, &iDriveThread);
   1.248 +	if ((r!= KErrNone) || !iDriveThread)
   1.249 +		return NULL;
   1.250 +
   1.251 +	// if re-opening in DirectIo mode, destroy the file cache 
   1.252 +	if (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare))
   1.253 +		{
   1.254 +		Close();
   1.255 +		return NULL;
   1.256 +		}
   1.257 +
   1.258 +	SetFileCacheFlags(aShare);
   1.259 +
   1.260 +	// assign ownership of this object to aFileCB before any leaves occur
   1.261 +	iFileCB = &aShare.File();
   1.262 +	iFileCB->iBody->iFileCache = this;
   1.263 +	
   1.264 +	__ASSERT_DEBUG(iLock.Handle() == KNullHandle, Fault(ELockAlreadyOpen));
   1.265 +	User::LeaveIfError(iLock.CreateLocal());
   1.266 +
   1.267 +	// delete the file name to save heap space (it's only needed 
   1.268 +	// while the file is on the closed queue so that it can be matched)
   1.269 +	delete iFileNameF;
   1.270 +	iFileNameF = NULL;
   1.271 +
   1.272 +	return this;
   1.273 +	}
   1.274 +
   1.275 +void CFileCache::Init(CFileShare& aShare)
   1.276 +	{
   1.277 +	SetFileCacheFlags(aShare);
   1.278 +	}
   1.279 +
   1.280 +CFileCache::CFileCache() : 
   1.281 +	iClosedTimer(ClosedTimerEvent, this),
   1.282 +	iDirtyTimer(DirtyTimerEvent, this)
   1.283 +	{
   1.284 +	}
   1.285 +
   1.286 +CFileCache::~CFileCache()
   1.287 +	{
   1.288 +	iLock.Close();
   1.289 +
   1.290 +	// stop the owning CFileCB from pointing to an about-to-be deleted object
   1.291 +	if (iFileCB && iFileCB->iBody)
   1.292 +		iFileCB->iBody->iFileCache = NULL;
   1.293 +
   1.294 +	CCacheManagerFactory::CacheManager()->DeregisterClient(*iCacheClient);
   1.295 +
   1.296 +	delete iCacheClient;
   1.297 +
   1.298 +	delete iFileNameF;
   1.299 +	}
   1.300 +
   1.301 +
   1.302 +TInt CFileCache::ClosedTimerEvent(TAny* aFileCache)
   1.303 +	{
   1.304 +	TClosedFileUtils::Remove((CFileCache*) aFileCache);
   1.305 +	return KErrNone;
   1.306 +	}
   1.307 +
   1.308 +TInt CFileCache::DirtyTimerEvent(TAny* aFileCache)
   1.309 +	{
   1.310 +	// Cannot report errors here
   1.311 +	// coverity [unchecked_value]
   1.312 +	(void)((CFileCache*) aFileCache)->FlushDirty();
   1.313 +
   1.314 +	return KErrNone;
   1.315 +	}
   1.316 +
   1.317 +
   1.318 +
   1.319 +void CFileCache::Close()
   1.320 +	{
   1.321 +	__CACHE_PRINT1(_L("CFileCache::Close() 0x%x"),this);
   1.322 +
   1.323 +	TInt r = KErrNone;
   1.324 +
   1.325 +#ifdef _DEBUG
   1.326 +	if (iCacheClient)	// NB Object may not have been fully constructed
   1.327 +		{
   1.328 +		TInt64  pos;
   1.329 +		TUint8* addr;
   1.330 +		__ASSERT_DEBUG(((iCacheClient->FindFirstDirtySegment(pos, addr)) == 0), Fault(EClosingDirtyFile));
   1.331 +		__ASSERT_DEBUG(!iDirtyDataOwner, Fault(EClosingDirtyFile));
   1.332 +		}
   1.333 +#endif
   1.334 +		
   1.335 +		
   1.336 +	__CACHE_PRINT2(_L("CFileCache::Close() iFileCB %08X IsClosed %d"), 
   1.337 +		iFileCB, TClosedFileUtils::IsClosed(this));
   1.338 +	// if not already closed move to closed file queue
   1.339 +	if (iFileCB != NULL && 
   1.340 +		!iFileCB->DeleteOnClose() &&
   1.341 +		!TClosedFileUtils::IsClosed(this) && 
   1.342 +		IsDriveThread())
   1.343 +		{
   1.344 +		// add to ClosedFiles container
   1.345 +		__CACHE_PRINT1(_L("CLOSEDFILES: Adding %S\n"), &iFileCB->FileNameF() );
   1.346 +		TRAP(r, TClosedFileUtils::AddL(this, ETrue));
   1.347 +
   1.348 +		// Acquire ownership of the CFileCB's file name
   1.349 +		__ASSERT_DEBUG(iFileCB->iFileNameF, Fault(EClosingUnNamedFile));
   1.350 +		__ASSERT_DEBUG(iFileNameF == NULL, Fault(EFileNameAlreadyOwned));
   1.351 +		iFileNameF = iFileCB->iFileNameF;
   1.352 +		iNameHash = iFileCB->iNameHash;
   1.353 +		iFileCB->iFileNameF = NULL;
   1.354 +
   1.355 +		// remove pointer to owning CFileCB as this is called from CFileCB's destructor
   1.356 +		iFileCB = NULL;
   1.357 +
   1.358 +		// Successfully moved file !
   1.359 +		if (r == KErrNone)
   1.360 +			{
   1.361 +			// close the RFastLock object here to prevent OOM kernel tests from failing
   1.362 +			iLock.Close();
   1.363 +			return;
   1.364 +			}
   1.365 +		}
   1.366 +
   1.367 +	iClosedTimer.Stop();
   1.368 +
   1.369 +	// if already closed, close for good. N.B. CFsObject::DoClose() will 
   1.370 +	// cause it to be removed from the ClosedFiles container
   1.371 +	CFsDispatchObject::Close();
   1.372 +	}
   1.373 +
   1.374 +
   1.375 +CMountCB& CFileCache::Mount() const
   1.376 +	{
   1.377 +	return *iMount;
   1.378 +	}
   1.379 +
   1.380 +CFileCB* CFileCache::FileCB()
   1.381 +	{
   1.382 +	return iFileCB;
   1.383 +	}
   1.384 +
   1.385 +
   1.386 +
   1.387 +TInt CFileCache::SegmentSize() const
   1.388 +	{
   1.389 +	return iCacheClient->SegmentSize();
   1.390 +	}
   1.391 +
   1.392 +TInt CFileCache::SegmentSizeLog2() const
   1.393 +	{
   1.394 +	return iCacheClient->SegmentSizeLog2();
   1.395 +	}
   1.396 +
   1.397 +TInt64 CFileCache::SegmentSizeMask() const
   1.398 +	{
   1.399 +	return iCacheClient->SegmentSizeMask();
   1.400 +	}
   1.401 +
   1.402 +/**
   1.403 +Sets the cached file size  
   1.404 +
   1.405 +@param aSize The size of the file.
   1.406 +*/  
   1.407 +void CFileCache::SetSize64(TInt64 aSize)
   1.408 +	{
   1.409 +	__e32_atomic_store_ord64(&iSize64, aSize);
   1.410 +	}
   1.411 +
   1.412 +TDrive& CFileCache::Drive() const
   1.413 +	{
   1.414 +	return *iDrive;
   1.415 +	}
   1.416 +
   1.417 +TUint32 CFileCache::NameHash() const
   1.418 +	{
   1.419 +	return(iNameHash);
   1.420 +	}
   1.421 +
   1.422 +HBufC& CFileCache::FileNameF() const
   1.423 +	{
   1.424 +	__ASSERT_DEBUG(iFileNameF, Fault(EClosedFileHasNoName));
   1.425 +	return(*iFileNameF);
   1.426 +	}
   1.427 +
   1.428 +
   1.429 +
   1.430 +
   1.431 +TBool CFileCache::IsDriveThread()
   1.432 +	{
   1.433 +	return FsThreadManager::IsDriveThread(iDriveNum, EFalse);
   1.434 +	}
   1.435 +
   1.436 +
   1.437 +void CFileCache::ResetReadAhead()
   1.438 +	{
   1.439 +//	iSequentialReads = 0;
   1.440 +	iReadAheadLen = 0;
   1.441 +	iReadAheadPos = 0;
   1.442 +	}
   1.443 +
   1.444 +/**
   1.445 +ReadAhead() - 
   1.446 +dispatches a new message to fill the read cache if 
   1.447 +(ReadAheadPos - ShareReadPos) <= (ReadAheadLen)
   1.448 +
   1.449 +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   1.450 +		^				^
   1.451 +		|----- ReadAheadLen ----------------->
   1.452 +		|				| 
   1.453 +  ShareReadPos		ReadAheadPos	
   1.454 +
   1.455 +Every successful read-ahead doubles ReadAheadLen until it reaches the maximum 
   1.456 +(KDefaultFileCacheMaxReadAheadLen). 
   1.457 +*/
   1.458 +void CFileCache::ReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
   1.459 +	{
   1.460 +	if (!(aMode & EFileReadAheadOn) ||
   1.461 +		(iSequentialReads < KMinSequentialReadsBeforeReadAhead) ||
   1.462 +		iMaxReadAheadLen == 0)
   1.463 +		return;
   1.464 +
   1.465 +
   1.466 +	TInt segmentSize = SegmentSize();
   1.467 +	TInt64 segmentSizeMask = SegmentSizeMask();
   1.468 +	
   1.469 +	// if the read-ahead pos has been reset to zero, then the read-ahead 
   1.470 +	// position and length must be re-calculated
   1.471 +
   1.472 +	TBool resetting = (iReadAheadPos == 0)?(TBool)ETrue:(TBool)EFalse;
   1.473 +	if (resetting)
   1.474 +		{
   1.475 +		iReadAheadPos = (iLastReadPos + segmentSize - 1) & segmentSizeMask;	
   1.476 +
   1.477 +		// ensure read ahead len at least as big as last read
   1.478 +		iReadAheadLen = Max(iReadAheadLen, iLastReadLen);	
   1.479 +			
   1.480 +		// round up to a segment size
   1.481 +		iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
   1.482 +
   1.483 +		// ensure read ahead len at least as big as 1 segments 
   1.484 +		iReadAheadLen = Max(iReadAheadLen, segmentSize);	
   1.485 +			
   1.486 +		// ensure read ahead len not greater than the maximum read ahead len
   1.487 +		iReadAheadLen = Min(iReadAheadLen, iMaxReadAheadLen);
   1.488 +
   1.489 +		iReadAheadRequest = NULL;
   1.490 +		}
   1.491 +
   1.492 +	TInt bytesBuffered = (TInt) (iReadAheadPos - iLastReadPos);
   1.493 +	TInt bytesNotBuffered = iMaxReadAheadLen - bytesBuffered;
   1.494 +
   1.495 +
   1.496 +	// if read-ahead buffer len > current read-ahead len OR
   1.497 +	// read-ahead buffer is more than half full, do nothing
   1.498 +	if ((iReadAheadRequest) ||
   1.499 +		(bytesBuffered > iReadAheadLen) || 
   1.500 +		(bytesBuffered > (iMaxReadAheadLen>>1)) ||
   1.501 +		(iReadAheadPos >= iSize64))
   1.502 +		{
   1.503 +		return;
   1.504 +		}
   1.505 +
   1.506 +	// double the read-ahead length - unless this is the first
   1.507 +	if (!resetting)
   1.508 +		iReadAheadLen<<= 1;
   1.509 +
   1.510 +	// ensure read ahead len not greater than the free space available in buffer
   1.511 +	iReadAheadLen = Min(iReadAheadLen, bytesNotBuffered);
   1.512 +
   1.513 +	// round up to a segment size
   1.514 +	iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
   1.515 +
   1.516 +#if defined (_DEBUG_READ_AHEAD)
   1.517 +	TInt64 oldReadAheadPos = iReadAheadPos;
   1.518 +#endif
   1.519 +
   1.520 +	DoReadAhead(aMsgRequest, aMode);
   1.521 +
   1.522 +
   1.523 +//	RDebug::Print(_L("Buffered: old %d new %d"), bytesBuffered, (iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos);
   1.524 +
   1.525 +#if defined (_DEBUG_READ_AHEAD)
   1.526 +RDebug::Print(_L("Buffered: old %d new %d iLastReadPos %d ReadAheadPos old %d new %d iReadAheadLen %d"), 
   1.527 +	bytesBuffered, 
   1.528 +	(TInt) ((iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos),
   1.529 +	I64LOW(iLastReadPos),
   1.530 +	I64LOW(oldReadAheadPos),
   1.531 +	I64LOW(iReadAheadPos),
   1.532 +	iReadAheadLen);
   1.533 +#endif	// (_DEBUG_READ_AHEAD)
   1.534 +
   1.535 +	
   1.536 +	return;
   1.537 +	}
   1.538 +
   1.539 +
   1.540 +void CFileCache::DoReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
   1.541 +	{
   1.542 +
   1.543 +
   1.544 +	if (iCacheClient->FindSegment(iReadAheadPos) != NULL)
   1.545 +		{
   1.546 +		// if read ahead pos is already cached, then synchronous reads have caught up,
   1.547 +		// so reset read ahead pos.
   1.548 +#if defined (_DEBUG_READ_AHEAD)
   1.549 +		RDebug::Print(_L("ReadAhead: pos %d already cached"), I64LOW(iReadAheadPos));
   1.550 +#endif
   1.551 +		iReadAheadPos = 0;
   1.552 +		return;
   1.553 +		}
   1.554 +
   1.555 +
   1.556 +	CFsClientMessageRequest* newRequest = NULL;
   1.557 +	TInt r = AllocateRequest(newRequest, EFalse, aMsgRequest.Session());
   1.558 +	if (r != KErrNone)
   1.559 +		return;
   1.560 +
   1.561 +	r = newRequest->PushOperation(
   1.562 +		iReadAheadPos, 
   1.563 +		iReadAheadLen,
   1.564 +		(TUint8*) NULL, 
   1.565 +		0,						// aOffset 
   1.566 +		NULL);					// aCallback
   1.567 +	if (r != KErrNone)
   1.568 +		{
   1.569 +		newRequest->Free();
   1.570 +		newRequest = NULL;
   1.571 +		return;
   1.572 +		}
   1.573 +
   1.574 +	__CACHE_PRINT2(_L("TFsFileRead: ReadAhead pos %ld len %d"), newRequest->CurrentOperation().iReadWriteArgs.iPos, newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
   1.575 +
   1.576 +//	RDebug::Print(_L("ReadH:\tpos %d\tlen %d"), I64LOW(newRequest->CurrentOperation().iReadWriteArgs.iPos), newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
   1.577 +
   1.578 +	r = ReadBuffered(*newRequest, aMode);
   1.579 +	if (r != CFsRequest::EReqActionContinue)
   1.580 +		{
   1.581 +		// if read ahead pos is already cached, then synchronous reads have caught up,
   1.582 +		// so reset read ahead pos.
   1.583 +		if (r == CFsRequest::EReqActionComplete)
   1.584 +			{
   1.585 +#if defined (_DEBUG_READ_AHEAD)
   1.586 +			RDebug::Print(_L("ReadAhead pos %d ALREADY DONE !!!"), I64LOW(iReadAheadPos));
   1.587 +#endif
   1.588 +			iReadAheadPos = 0;
   1.589 +			}
   1.590 +
   1.591 +		newRequest->PopOperation();
   1.592 +		newRequest->Free();
   1.593 +		newRequest = NULL;
   1.594 +		return;
   1.595 +		}
   1.596 +
   1.597 +	iReadAheadPos = iReadAheadPos + iReadAheadLen;
   1.598 +	iReadAheadRequest = newRequest;
   1.599 +
   1.600 +#if defined (_DEBUG_READ_AHEAD)
   1.601 +	RDebug::Print(_L("Dispatching ReadAhead with %s priority"), iFileCacheReadAsync?_S16("HIGH"):_S16("LOW"));
   1.602 +#endif
   1.603 +	// If if media driver reads are synchronous (i.e. not interrupt driven) dispatch read-ahead 
   1.604 +	// with low priority  so that it doesn't prevent client thread from running
   1.605 +	newRequest->Dispatch(
   1.606 +		EFalse,					// don't init
   1.607 +		iFileCacheReadAsync?(TBool)EFalse:(TBool)ETrue, 
   1.608 +		EFalse);				// dispatch to back
   1.609 +	}
   1.610 +
   1.611 +TInt CFileCache::CompleteRead(CFsRequest* aRequest)
   1.612 +	{
   1.613 +	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
   1.614 +
   1.615 +	__ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
   1.616 +
   1.617 +	CFileShare* share;
   1.618 +	CFileCB* file;
   1.619 +	GetFileFromScratch(aRequest, share, file);
   1.620 +	CFileCache* fileCache = file->FileCache();
   1.621 +
   1.622 +	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
   1.623 +#ifdef _DEBUG
   1.624 +	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
   1.625 +#endif
   1.626 +
   1.627 +	TUint mode = share?share->iMode : EFileReadBuffered;
   1.628 +	TInt r = fileCache->ReadBuffered(msgRequest, mode);
   1.629 +
   1.630 +	// if this request has been cancelled we mustn't dispatch it again - 
   1.631 +	// we still need to call state machine however so that any locked segments can be unlocked
   1.632 +
   1.633 +
   1.634 +	TInt lastError = msgRequest.LastError();
   1.635 +
   1.636 +#ifdef _DEBUG
   1.637 +	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
   1.638 +#endif
   1.639 +
   1.640 +	if (lastError == KErrCancel)
   1.641 +		r = CFsRequest::EReqActionComplete;
   1.642 +
   1.643 +	return r;
   1.644 +	}
   1.645 +
   1.646 +/**
   1.647 +ReadBuffered - attempts to read from cache. 
   1.648 +Called from TFsFileRead::Initialise() and CFileCache::CompleteRead()
   1.649 +
   1.650 +@return CFsRequest::EReqActionComplete if request entirely satisfied
   1.651 +		CFsRequest::EReqActionContinue if request not yet complete. 
   1.652 +			Results in transition from :
   1.653 +				TFsFileRead::PostInitialise() to TFsFileRead::DoRequestL() or
   1.654 +				CFileCache::CompleteRead() to TFsFileRead::DoRequestL()
   1.655 +		CFsRequest::EReqActionBusy if filecache is "busy"
   1.656 +*/
   1.657 +TInt CFileCache::ReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
   1.658 +	{
   1.659 +	iLock.Wait();
   1.660 +
   1.661 +	CFsClientMessageRequest* newRequest = NULL;
   1.662 +
   1.663 +	__CACHE_PRINT2(_L("CFileCache::ReadBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
   1.664 +	TInt r = DoReadBuffered(aMsgRequest, aMode, newRequest);
   1.665 +	
   1.666 +	iLock.Signal();
   1.667 +
   1.668 +	if (newRequest)
   1.669 +		newRequest->Dispatch();
   1.670 +
   1.671 +	return r;
   1.672 +	}
   1.673 +
   1.674 +
   1.675 +void CFileCache::UpdateSharePosition(CFsMessageRequest& aMsgRequest, TMsgOperation& aCurrentOperation)
   1.676 +	{
   1.677 +	// update the file share's position if this request came from client
   1.678 +	if (aCurrentOperation.iClientRequest)
   1.679 +		{
   1.680 +		CFileShare* share = (CFileShare*)aMsgRequest.ScratchValue();
   1.681 +		if (aMsgRequest.LastError() == KErrNone)
   1.682 +			{
   1.683 +			__e32_atomic_store_ord64(&share->iPos, aCurrentOperation.iReadWriteArgs.iPos);
   1.684 +			}
   1.685 +		}
   1.686 +	}
   1.687 +
   1.688 +TInt CFileCache::DoReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode, CFsClientMessageRequest*& aNewRequest)
   1.689 +	{
   1.690 +	enum states
   1.691 +		{
   1.692 +		EStBegin=0,
   1.693 +		EStReadFromCache,
   1.694 +		EStReadFromDiskComplete,
   1.695 +		EStCopyToClient,
   1.696 +		EStReStart,
   1.697 +		EStEnd
   1.698 +		};
   1.699 +
   1.700 +
   1.701 +	TInt retCode = CFsRequest::EReqActionComplete;
   1.702 +	TInt& lastError = aMsgRequest.LastError();
   1.703 +	TBool driveThread = IsDriveThread();
   1.704 +
   1.705 +	// temp storage for transition between EStReadFromCache / EStReadFromDiskComplete and EStCopyToClient
   1.706 +	TInt readLen = 0;
   1.707 +	TUint8* addr = NULL;
   1.708 +	TInt64 segmentStartPos = 0;
   1.709 +
   1.710 +	TInt segmentSize = SegmentSize();
   1.711 +	TInt segmentSizeLog2 = SegmentSizeLog2();
   1.712 +	TInt64 segmentSizeMask = SegmentSizeMask();
   1.713 +
   1.714 +	for(;;)
   1.715 +		{
   1.716 +		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
   1.717 +		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
   1.718 +		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
   1.719 +		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
   1.720 +		TBool readAhead = (currentOperation->iReadWriteArgs.iData == NULL)?(TBool)ETrue:(TBool)EFalse;
   1.721 +
   1.722 +		switch(currentOperation->iState)
   1.723 +			{
   1.724 +			case EStBegin:
   1.725 +
   1.726 +
   1.727 +				// if EFileReadDirectIO, flush write cache and read direct from  disk
   1.728 +				if (!(aMode & EFileReadBuffered))
   1.729 +					{
   1.730 +					iLock.Signal();
   1.731 +					TInt r = FlushDirty(&aMsgRequest);
   1.732 +					iLock.Wait();
   1.733 +					if (r == CFsRequest::EReqActionBusy)
   1.734 +						return CFsRequest::EReqActionBusy;
   1.735 +					return CFsRequest::EReqActionContinue;	// read uncached
   1.736 +					}
   1.737 +
   1.738 +				if (currentPos > iSize64)
   1.739 +					currentPos = iSize64;
   1.740 +
   1.741 +				currentOperation->iState = EStReadFromCache;
   1.742 +
   1.743 +				// count the number of sequential reads for read-ahead
   1.744 +				if (currentOperation->iClientRequest)
   1.745 +					{
   1.746 +					if (currentPos == iLastReadPos)
   1.747 +						{
   1.748 +						iSequentialReads++;
   1.749 +						}
   1.750 +					else
   1.751 +						{
   1.752 +						iSequentialReads = 0;
   1.753 +						ResetReadAhead();
   1.754 +						}
   1.755 +					iLastReadPos = currentPos + totalLen;
   1.756 +					iLastReadLen = totalLen;
   1.757 +					}
   1.758 +
   1.759 +				
   1.760 +				// fall into...
   1.761 +
   1.762 +			case EStReadFromCache:
   1.763 +				{
   1.764 +				// reading past end of file ?
   1.765 +				if (currentPos + totalLen > iSize64)
   1.766 +					totalLen = (TInt) (iSize64 - currentPos);
   1.767 +				
   1.768 +
   1.769 +				if (totalLen == 0 || lastError != KErrNone)
   1.770 +					{
   1.771 +					currentOperation->iState = EStEnd;
   1.772 +					break;
   1.773 +					}
   1.774 +
   1.775 +				segmentStartPos = currentPos & segmentSizeMask;
   1.776 +
   1.777 +			
   1.778 +				TInt64 endPos = currentPos + totalLen;
   1.779 +				TUint maxLenToRead = (TUint)Min((TInt64)(iCacheClient->CacheLineSize()), (endPos - segmentStartPos));
   1.780 +				TInt maxSegments = (maxLenToRead + segmentSize - 1) >> segmentSizeLog2;
   1.781 +
   1.782 +
   1.783 +				TInt segmentCount = maxSegments;
   1.784 +				TInt filledSegmentCount;
   1.785 +				TInt lockError;
   1.786 +				addr = iCacheClient->FindAndLockSegments(segmentStartPos, segmentCount, filledSegmentCount, lockError, EFalse);
   1.787 +
   1.788 +
   1.789 +#if defined (_DEBUG_READ_AHEAD)
   1.790 +				if (addr && readAhead)
   1.791 +					RDebug::Print(_L("READAHEAD CACHELINE ALREADY EXISTS POS %d"), I64LOW(segmentStartPos));
   1.792 +#endif
   1.793 +
   1.794 +				// if cacheline contains filled and empty segments, deal with these seperately
   1.795 +				// to simplify the code.....
   1.796 +				if (filledSegmentCount > 0 && segmentCount > filledSegmentCount)
   1.797 +					{
   1.798 +					segmentCount = Min(segmentCount, filledSegmentCount);
   1.799 +					}
   1.800 +
   1.801 +				if (lockError == KErrInUse)
   1.802 +					{
   1.803 +					// cacheline in use (by other thread):
   1.804 +					// if this is a read-ahead, abandon it
   1.805 +					// otherwise re-post the request
   1.806 +					if (readAhead)
   1.807 +						{
   1.808 +						totalLen = 0;
   1.809 +						retCode = CFsRequest::EReqActionComplete;
   1.810 +						currentOperation->iState = EStEnd;
   1.811 +						}
   1.812 +					else
   1.813 +						{
   1.814 +#if defined (_DEBUG_READ_AHEAD)
   1.815 +						RDebug::Print(_L("READC CACHELINE BUSY POS %d"), I64LOW(segmentStartPos));
   1.816 +#endif
   1.817 +
   1.818 +						return CFsRequest::EReqActionBusy;
   1.819 +						}
   1.820 +					break;
   1.821 +					}
   1.822 +
   1.823 +				// if not found, try to allocate as many contiguous segments 
   1.824 +				// as possible so that the number of reads issued to the media 
   1.825 +				// driver is kept to a minumum
   1.826 +
   1.827 +				if (addr == NULL)	// not found ?
   1.828 +					{
   1.829 +					// if read len > size of the cache, don't read excess 
   1.830 +					// through the cache as this is wasteful
   1.831 +					if (totalLen > iCacheSize)
   1.832 +						{
   1.833 +						TInt len = totalLen - iCacheSize;
   1.834 +						TInt r = aMsgRequest.PushOperation(
   1.835 +							currentPos, len, 
   1.836 +							(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
   1.837 +							CompleteRead, 
   1.838 +							EStReadFromCache);
   1.839 +						if (r != KErrNone)
   1.840 +							{
   1.841 +							currentOperation->iState = EStReStart;
   1.842 +							break;
   1.843 +							}
   1.844 +
   1.845 +						currentOffset+= len;
   1.846 +						currentPos+= len;
   1.847 +						totalLen-= len;
   1.848 +						return CFsRequest::EReqActionContinue;
   1.849 +						}
   1.850 +
   1.851 +					// if position not cached postpone Initialise() to drive thread
   1.852 +					// as there may be a read ahead already in the queue
   1.853 +
   1.854 +					if (!driveThread && !readAhead)
   1.855 +						{
   1.856 +#if defined (_DEBUG_READ_AHEAD)
   1.857 +						RDebug::Print(_L("*** POSTING READ TO DRIVE THREAD POS %d ***"), I64LOW(segmentStartPos));
   1.858 +#endif
   1.859 +						return CFsRequest::EReqActionBusy;
   1.860 +						}
   1.861 +
   1.862 +					segmentCount = maxSegments;
   1.863 +					addr = iCacheClient->AllocateAndLockSegments(segmentStartPos, segmentCount, EFalse, !readAhead);
   1.864 +					if (addr == NULL)
   1.865 +						{
   1.866 +						__CACHE_PRINT(_L("AllocateSegment failed"));
   1.867 +						currentOperation->iState = EStReStart;
   1.868 +						break;
   1.869 +						}
   1.870 +					}
   1.871 +
   1.872 +				
   1.873 +				readLen = segmentCount << segmentSizeLog2;
   1.874 +				__ASSERT_DEBUG(iSize64 > segmentStartPos, Fault(EPosBeyondSize));
   1.875 +				readLen = (TInt)Min((TInt64)readLen, (iSize64 - segmentStartPos));
   1.876 +
   1.877 +				if (iCacheClient->SegmentEmpty(segmentStartPos))
   1.878 +					{
   1.879 +					// store readLen & addr in scratch area
   1.880 +					currentOperation->iScratchValue0 = addr;						
   1.881 +					currentOperation->iScratchValue1 = (TAny*) readLen;						
   1.882 +
   1.883 +					// read into cache segment(s)
   1.884 +					__CACHE_PRINT2(_L("CACHEFILE: read pos %ld, readLen %d"), segmentStartPos, readLen);
   1.885 +					TInt r = aMsgRequest.PushOperation(
   1.886 +						segmentStartPos, readLen, addr, 0,
   1.887 +						CompleteRead, 
   1.888 +						EStReadFromDiskComplete);
   1.889 +					if (r != KErrNone)
   1.890 +						{
   1.891 +						iCacheClient->UnlockSegments(segmentStartPos);
   1.892 +						currentOperation->iState = EStReStart;
   1.893 +						break;
   1.894 +						}
   1.895 +
   1.896 +					return CFsRequest::EReqActionContinue;
   1.897 +					}
   1.898 +				else
   1.899 +					{
   1.900 +					currentOperation->iState = EStCopyToClient;
   1.901 +					}
   1.902 +				}
   1.903 +				// fall into ...
   1.904 +			
   1.905 +			case EStCopyToClient:
   1.906 +				{
   1.907 +				TInt segmentsLocked = (readLen + segmentSize - 1) >> segmentSizeLog2;
   1.908 +
   1.909 +				if (lastError == KErrNone)
   1.910 +					{
   1.911 +					// copy to user buffer
   1.912 +					TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
   1.913 +					TInt len = Min(readLen - offset, totalLen);
   1.914 +					
   1.915 +					// if addr is NULL then this is a read ahead request
   1.916 +					if (currentOperation->iReadWriteArgs.iData != NULL)
   1.917 +						{
   1.918 +						if (currentOperation->iClientRequest)
   1.919 +							{
   1.920 +							__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
   1.921 +							TPtrC8 ptr(addr+offset, len);
   1.922 +							lastError = aMsgRequest.Write(0, ptr, currentOffset);
   1.923 +							}
   1.924 +						else
   1.925 +							{
   1.926 +							memcpy(((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, addr+offset, len);
   1.927 +							}
   1.928 +						}
   1.929 +					else
   1.930 +						{
   1.931 +						iReadAheadRequest = NULL;
   1.932 +						}
   1.933 +
   1.934 +					currentOffset+= len;
   1.935 +					currentPos+= len;
   1.936 +					totalLen-= len;
   1.937 +					}
   1.938 +
   1.939 +				if (lastError == KErrNone)
   1.940 +					iCacheClient->MarkSegmentsAsFilled(segmentStartPos, segmentsLocked);
   1.941 +				iCacheClient->UnlockSegments(segmentStartPos);
   1.942 +
   1.943 +				if (lastError != KErrNone)
   1.944 +					{
   1.945 +					retCode = CFsRequest::EReqActionComplete;
   1.946 +					currentOperation->iState = EStEnd;
   1.947 +					break;
   1.948 +					}
   1.949 +
   1.950 +				if (totalLen > 0 && lastError == KErrNone)
   1.951 +					{
   1.952 +					currentOperation->iState = EStReadFromCache;
   1.953 +					break;
   1.954 +					}
   1.955 +				currentOperation->iState = EStEnd;
   1.956 +				}
   1.957 +			// fall into ...
   1.958 +			
   1.959 +
   1.960 +			case EStEnd:
   1.961 +				// update the file share's position if this request came from client
   1.962 +				UpdateSharePosition(aMsgRequest, *currentOperation);
   1.963 +				return retCode;
   1.964 +
   1.965 +			case EStReadFromDiskComplete:
   1.966 +				{
   1.967 +				// restore readLen etc from scratch area
   1.968 +				segmentStartPos = currentPos & segmentSizeMask;
   1.969 +				addr = (TUint8*) currentOperation->iScratchValue0;
   1.970 +				readLen = (TInt) currentOperation->iScratchValue1;
   1.971 +				
   1.972 +				aMsgRequest.CurrentOperation().iState = EStCopyToClient;
   1.973 +				}
   1.974 +				break;
   1.975 +
   1.976 +			case EStReStart:
   1.977 +
   1.978 +				// ignore the failure if this is a read-ahead
   1.979 +				if (readAhead)
   1.980 +					{
   1.981 +					totalLen = 0;
   1.982 +					retCode = CFsRequest::EReqActionComplete;
   1.983 +					currentOperation->iState = EStEnd;
   1.984 +					break;
   1.985 +					}
   1.986 +
   1.987 +				// We need to flush all dirty data to disk in case this read overlaps
   1.988 +				// with any dirty data already in the file cache
   1.989 +				if (aMode & EFileWriteBuffered)
   1.990 +					{
   1.991 +					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
   1.992 +					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
   1.993 +						return r;
   1.994 +					}
   1.995 +
   1.996 +				retCode = CFsRequest::EReqActionContinue;	// read uncached
   1.997 +				currentOperation->iState = EStEnd;
   1.998 +
   1.999 +				/*
  1.1000 +				We're now going to by-pass the file cache.
  1.1001 +				If we've already written something to the client's buffer then, in the flexible
  1.1002 +				memory model, the KDesWrittenShift bit will be set and so the descriptor length will 
  1.1003 +				updated in RMessageK::CallbackFunc() when the client thread runs. This (shorter) 
  1.1004 +				length will overwrite the descriptor length written by the local media subsystem.
  1.1005 +				To get round this problem, we set the descriptor length artificially by writing a 
  1.1006 +				zero-length descriptor at the end of the client's buffer.
  1.1007 +				*/
  1.1008 +				if (currentOffset > 0 && currentOperation->iClientRequest)
  1.1009 +					{
  1.1010 +					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
  1.1011 +					TPtrC8 ptr(NULL, 0);
  1.1012 +					TInt r = aMsgRequest.Write(0, ptr, currentOffset+totalLen);
  1.1013 +					__CACHE_PRINT3(_L("CFileCache::DoReadBuffered() failed at pos %lx offset %x totalLen %x\n"), currentPos, currentOffset, totalLen);
  1.1014 +					__CACHE_PRINT2(_L("CFileCache::DoReadBuffered() writing zero bytes at offset %x r %d\n"), currentOffset + totalLen, r);
  1.1015 +					if (r != KErrNone)
  1.1016 +						retCode = r;
  1.1017 +					}
  1.1018 +
  1.1019 +				aMsgRequest.ReStart();
  1.1020 +				UpdateSharePosition(aMsgRequest, *currentOperation);
  1.1021 +				return retCode;
  1.1022 +
  1.1023 +
  1.1024 +			};
  1.1025 +		}	// for (;;)
  1.1026 +
  1.1027 +	}
  1.1028 +
  1.1029 +
  1.1030 +
  1.1031 +/**
  1.1032 +WriteBuffered - attempts to write to cache. 
  1.1033 +Called from TFsFileRead::Initialise and TFsFileRead::DoRequestL
  1.1034 +
  1.1035 +@return CFsRequest::EReqActionComplete if request entirely satisfied
  1.1036 +		CFsRequest::EReqActionContinue if request not yet complete
  1.1037 +		CFsRequest::EReqActionBusy if filecache is busy
  1.1038 +*/
  1.1039 +TInt CFileCache::WriteBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
  1.1040 +	{
  1.1041 +	iLock.Wait();
  1.1042 +
  1.1043 +
  1.1044 +	CFsClientMessageRequest* newRequest = NULL;
  1.1045 +
  1.1046 +	__CACHE_PRINT2(_L("CFileCache::WriteBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
  1.1047 +	TInt r = DoWriteBuffered(aMsgRequest, newRequest, aMode);
  1.1048 +	
  1.1049 +	iLock.Signal();
  1.1050 +
  1.1051 +	if (newRequest)
  1.1052 +		newRequest->Dispatch();
  1.1053 +
  1.1054 +	// completion ?
  1.1055 +	if (r == CFsRequest::EReqActionComplete)
  1.1056 +		{
  1.1057 +		TMsgOperation& currentOperation = aMsgRequest.CurrentOperation();
  1.1058 +		
  1.1059 +		__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
  1.1060 +
  1.1061 +		TFsFileWrite::CommonEnd(&aMsgRequest, r, iInitialSize, iSize64, currentOperation.iReadWriteArgs.iPos, EFalse);
  1.1062 +		}
  1.1063 +	
  1.1064 +	return r;
  1.1065 +	}
  1.1066 +
  1.1067 +
  1.1068 +
  1.1069 +
  1.1070 +TInt CFileCache::CompleteWrite(CFsRequest* aRequest)
  1.1071 +	{
  1.1072 +	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
  1.1073 +
  1.1074 +	CFileShare* share = (CFileShare*)aRequest->ScratchValue();
  1.1075 +	CFileCB& file = share->File();
  1.1076 +	CFileCache* fileCache = file.FileCache(); //&file;
  1.1077 +	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
  1.1078 +
  1.1079 +#ifdef _DEBUG
  1.1080 +	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
  1.1081 +#endif
  1.1082 +	
  1.1083 +
  1.1084 +	TInt r = fileCache->WriteBuffered(msgRequest, share->iMode);
  1.1085 +
  1.1086 +#ifdef _DEBUG
  1.1087 +	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
  1.1088 +#endif
  1.1089 +
  1.1090 +	// if this request has been cancelled we mustn't dispatch it again - 
  1.1091 +	// we still need to call state machine however so that any locked segments can be unlocked
  1.1092 +	TInt lastError = msgRequest.LastError();
  1.1093 +	if (lastError == KErrCancel)
  1.1094 +		r = CFsRequest::EReqActionComplete;
  1.1095 +
  1.1096 +	return r;
  1.1097 +	}
  1.1098 +
  1.1099 +
  1.1100 +
  1.1101 +TInt CFileCache::DoWriteBuffered(CFsMessageRequest& aMsgRequest, CFsClientMessageRequest*& aNewRequest, TUint aMode)
  1.1102 +
  1.1103 +	{
  1.1104 +	enum states
  1.1105 +		{
  1.1106 +		EStBegin=0,
  1.1107 +		EStWriteToCache,
  1.1108 +		EStReadFromDisk,
  1.1109 +		EStReadFromDiskComplete,
  1.1110 +		EStWriteToCacheComplete,
  1.1111 +		EStCopyFromClient,
  1.1112 +		EStReStart,
  1.1113 +		EStEnd,
  1.1114 +		EStWriteThrough
  1.1115 +		};
  1.1116 +
  1.1117 +	enum flags
  1.1118 +		{
  1.1119 +		EReadFirstSegment	= 0x01,
  1.1120 +		EReadLastSegment	= 0x02
  1.1121 +		};
  1.1122 +
  1.1123 +	TBool cachingWrites = (aMode & EFileWriteBuffered)?(TBool)ETrue:(TBool)EFalse;
  1.1124 +	TInt& lastError = aMsgRequest.LastError();
  1.1125 +	TBool driveThread = IsDriveThread();
  1.1126 +	TInt segmentSize = SegmentSize();
  1.1127 +	TInt segmentSizeLog2 = SegmentSizeLog2();
  1.1128 +	TInt64 segmentSizeMask = SegmentSizeMask();
  1.1129 +
  1.1130 +	// temp storage for transition between EStWriteToCache / EStWriteToCacheComplete and EStCopyFromClient
  1.1131 +	TInt segmentCount = 0;
  1.1132 +	TUint8* addr = NULL;
  1.1133 +	TInt64 firstSegmentStartPos = 0;
  1.1134 +	TInt64 readPos = 0;
  1.1135 +	TUint8* readAddr = NULL;
  1.1136 +	TInt readSegmentCount = 0;
  1.1137 +
  1.1138 +	TInt readFlags = 0;
  1.1139 +	
  1.1140 +	for(;;)
  1.1141 +		{
  1.1142 +		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
  1.1143 +		TInt retCode = CFsRequest::EReqActionComplete;
  1.1144 +
  1.1145 +		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
  1.1146 +		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
  1.1147 +		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
  1.1148 +		switch(currentOperation->iState)
  1.1149 +			{
  1.1150 +			case EStBegin:
  1.1151 +				{				
  1.1152 +				// Report background flush errors back to client
  1.1153 +				CFileShare* share = (CFileShare*) aMsgRequest.ScratchValue(); 
  1.1154 +				if (share->iFlushError != KErrNone)
  1.1155 +					{
  1.1156 +					TInt r = share->iFlushError;
  1.1157 +					share->iFlushError = KErrNone;
  1.1158 +					return r;
  1.1159 +					}
  1.1160 +
  1.1161 +				if (currentPos > iSize64)
  1.1162 +					currentPos = iSize64;
  1.1163 +				iInitialSize = iSize64;
  1.1164 +
  1.1165 +				// if EFileWriteDirectIO OR 
  1.1166 +				// (caching writes and requested write len > size of the cache), 
  1.1167 +				// flush the write cache
  1.1168 +				if ((aMode & EFileWriteBuffered) == 0 || 
  1.1169 +					(cachingWrites && totalLen > iCacheSize))
  1.1170 +					{
  1.1171 +					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
  1.1172 +					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
  1.1173 +						return r;
  1.1174 +					}
  1.1175 +				// if EFileWriteDirectIO then overwrite any non-dirty data and 
  1.1176 +				// write direct to disk....
  1.1177 +
  1.1178 +				// if caching writes and requested write len > size of the cache, 
  1.1179 +				// don't write excess  through the cache as this is wasteful
  1.1180 +				if (cachingWrites && totalLen > iCacheSize)
  1.1181 +					{
  1.1182 +					// Destroy ALL cached data (dirty and non-dirty) to ensure 
  1.1183 +					// cache is consistent with data written to disk
  1.1184 +					iCacheClient->Purge(ETrue);
  1.1185 +
  1.1186 +					TInt len = totalLen - iCacheSize;
  1.1187 +					TInt r = aMsgRequest.PushOperation(
  1.1188 +						currentPos, len, 
  1.1189 +						(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
  1.1190 +						CompleteWrite, 
  1.1191 +						EStWriteToCache);
  1.1192 +					if (r != KErrNone)
  1.1193 +						{
  1.1194 +						currentOperation->iState = EStReStart;
  1.1195 +						break;
  1.1196 +						}
  1.1197 +
  1.1198 +					currentOffset+= len;
  1.1199 +					currentPos+= len;
  1.1200 +					totalLen-= len;
  1.1201 +					return CFsRequest::EReqActionContinue;
  1.1202 +					}
  1.1203 +
  1.1204 +				currentOperation->iState = EStWriteToCache;
  1.1205 +				}		
  1.1206 +				break;
  1.1207 +
  1.1208 +			case EStWriteToCache:
  1.1209 +				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
  1.1210 +				{
  1.1211 +				// NB we must carry on if we get an error to ensure cache is consistent with disk
  1.1212 +				if (totalLen == 0 || lastError == KErrCancel)
  1.1213 +					{
  1.1214 +					currentOperation->iState = EStWriteToCacheComplete;
  1.1215 +					break;
  1.1216 +					}
  1.1217 +
  1.1218 +				firstSegmentStartPos = currentPos & segmentSizeMask;
  1.1219 +				TInt64 endPos = currentPos + totalLen;
  1.1220 +
  1.1221 +				// Find the maximum number of contiguous segments we need to lock
  1.1222 +				// in this cacheline - when we unlock these will be marked as filled
  1.1223 +				const TInt64 dataRange = Min((TInt64)iCacheSize, (endPos + segmentSize - 1 - firstSegmentStartPos));
  1.1224 +				TInt maxSegmentCount =  (TInt)(dataRange >> segmentSizeLog2);
  1.1225 +				
  1.1226 +				segmentCount = maxSegmentCount;
  1.1227 +
  1.1228 +				TInt lockError;
  1.1229 +				TInt filledSegmentCount;
  1.1230 +				addr = iCacheClient->FindAndLockSegments(firstSegmentStartPos, segmentCount, filledSegmentCount, lockError, cachingWrites);
  1.1231 +				
  1.1232 +				if (lockError == KErrInUse)
  1.1233 +					return CFsRequest::EReqActionBusy;
  1.1234 +
  1.1235 +#ifdef LAZY_WRITE
  1.1236 +				if (cachingWrites && addr == NULL && lastError == KErrNone && iCacheClient->TooManyLockedSegments())
  1.1237 +					{
  1.1238 +					// Flush a single dirty cacheline (if there is one for this file)
  1.1239 +					// or all dirty data  on this drive (if there is any).
  1.1240 +					// If there's nothing to flush then the dirty (locked) data
  1.1241 +					// must belong to another drive, so there's not much we can do
  1.1242 +					// but write through 
  1.1243 +					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
  1.1244 +					if (r == CFsRequest::EReqActionBusy)
  1.1245 +						return CFsRequest::EReqActionBusy;
  1.1246 +					if (r != KErrInUse)
  1.1247 +						{
  1.1248 +						// Need to switch to drive thread to flush all data on this drive
  1.1249 +						if (!driveThread)
  1.1250 +							return CFsRequest::EReqActionBusy;
  1.1251 +						iLock.Signal();
  1.1252 +						TInt r = iDrive->FlushCachedFileInfo();
  1.1253 +						iLock.Wait();
  1.1254 +						if (r == CFsRequest::EReqActionBusy)
  1.1255 +							return CFsRequest::EReqActionBusy;
  1.1256 +
  1.1257 +						lastError = KErrNoMemory;	// write through
  1.1258 +						}
  1.1259 +					}
  1.1260 +#else
  1.1261 +				// flush cache before allocating a new cacheline
  1.1262 +				if (cachingWrites && addr == NULL && lastError == KErrNone)
  1.1263 +					{
  1.1264 +					// if no segment available, flush a single dirty cacheline 
  1.1265 +					// or wait if already flushing
  1.1266 +					if (iFlushBusy)
  1.1267 +						return CFsRequest::EReqActionBusy;
  1.1268 +					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
  1.1269 +					if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
  1.1270 +						lastError = r;
  1.1271 +					}
  1.1272 +#endif
  1.1273 +				// if no cacheline found & write caching is enabled, allocate a new cacheline
  1.1274 +				if (cachingWrites && addr == NULL && lastError == KErrNone)
  1.1275 +					{
  1.1276 +					// try to allocate up to write cache size - this may not 
  1.1277 +					// be possible if a segment in the range is already cached
  1.1278 +					segmentCount = maxSegmentCount;
  1.1279 +					addr = iCacheClient->AllocateAndLockSegments(currentPos, segmentCount, cachingWrites, ETrue);
  1.1280 +					
  1.1281 +					// continue if alloc failed
  1.1282 +					if (addr == NULL)
  1.1283 +						lastError = KErrNoMemory;
  1.1284 +				
  1.1285 +					}
  1.1286 +
  1.1287 +				if (addr == NULL)
  1.1288 +					{
  1.1289 +					if (cachingWrites && lastError == KErrNone)
  1.1290 +						lastError = KErrNoMemory;
  1.1291 +					segmentCount = 1;
  1.1292 +					currentOperation->iState = EStCopyFromClient;
  1.1293 +					break;
  1.1294 +					}
  1.1295 +
  1.1296 +				// if the first or last segment are empty then we'll have to read-before-write
  1.1297 +				TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
  1.1298 +				
  1.1299 +				TInt startPosSegOffset = iCacheClient->CacheOffset(currentPos);
  1.1300 +				TInt endPosSegOffset = iCacheClient->CacheOffset(endPos);
  1.1301 +
  1.1302 +				// partial first segment ?
  1.1303 +				if (startPosSegOffset != 0 && 
  1.1304 +					firstSegmentStartPos < iFileCB->Size64() &&
  1.1305 +					iCacheClient->SegmentEmpty(firstSegmentStartPos))
  1.1306 +					{
  1.1307 +					readFlags|= EReadFirstSegment;
  1.1308 +					}
  1.1309 +		
  1.1310 +				// partial last segment ? 
  1.1311 +				// NB this may be the first segment too !
  1.1312 +				if (endPosSegOffset != 0 && 
  1.1313 +					endPos < lastSegmentStartPos + segmentSize &&
  1.1314 +					endPos < iFileCB->Size64() &&
  1.1315 +					iCacheClient->SegmentEmpty(lastSegmentStartPos))
  1.1316 +					{
  1.1317 +					readFlags|= EReadLastSegment;
  1.1318 +					}
  1.1319 +
  1.1320 +				// read-before-write required ?
  1.1321 +				if (readFlags & EReadFirstSegment)
  1.1322 +					{
  1.1323 +					readFlags&= ~EReadFirstSegment;
  1.1324 +					readPos = firstSegmentStartPos;
  1.1325 +					readAddr = addr;
  1.1326 +					readSegmentCount = 1;
  1.1327 +					// if the last segment is empty and it's the same as the first or contiguous,
  1.1328 +					// then read that too 
  1.1329 +					if ((readFlags & EReadLastSegment) && (segmentCount <= 2))
  1.1330 +						{
  1.1331 +						readSegmentCount = segmentCount;
  1.1332 +						readFlags&= ~EReadLastSegment;
  1.1333 +						}
  1.1334 +					currentOperation->iState = EStReadFromDisk;
  1.1335 +					}
  1.1336 +				else if (readFlags & EReadLastSegment)
  1.1337 +					{
  1.1338 +					readFlags&= ~EReadLastSegment;
  1.1339 +					readPos = lastSegmentStartPos;
  1.1340 +					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
  1.1341 +					readSegmentCount = 1;
  1.1342 +					currentOperation->iState = EStReadFromDisk;
  1.1343 +					}
  1.1344 +				else
  1.1345 +					{
  1.1346 +					currentOperation->iState = EStCopyFromClient;
  1.1347 +					}
  1.1348 +				}
  1.1349 +				break;
  1.1350 +
  1.1351 +			case EStReadFromDisk:
  1.1352 +				{
  1.1353 +				// Save address & segmentCount in scratch area
  1.1354 +				currentOperation->iScratchValue0 = addr;	
  1.1355 +				__ASSERT_DEBUG(segmentCount < 0xFFFF, Fault(EBadSegmentCount));
  1.1356 +				currentOperation->iScratchValue1 = (TAny*) ((readFlags << 16) | segmentCount);
  1.1357 +
  1.1358 +				TInt maxReadLen = readSegmentCount << segmentSizeLog2;
  1.1359 +#ifdef __VC32__
  1.1360 +#pragma warning( disable : 4244 )  
  1.1361 +//Disable Compiler Warning (levels 3 and 4) C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
  1.1362 +// maxReadLen is of 31 bits. Hence Min result is reduced to 31 bits
  1.1363 +#endif
  1.1364 +				TInt readLen = (TInt)Min((TInt64)maxReadLen, (iSize64 - readPos));
  1.1365 +#ifdef __VC32__
  1.1366 +#pragma warning( default : 4244 )
  1.1367 +#endif
  1.1368 +				__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));					
  1.1369 +				TInt r = aMsgRequest.PushOperation(
  1.1370 +					readPos, readLen, readAddr, 0, 
  1.1371 +					CompleteWrite,
  1.1372 +					EStReadFromDiskComplete,
  1.1373 +					EFsFileRead);
  1.1374 +				if (r != KErrNone)
  1.1375 +					{
  1.1376 +					lastError = KErrNoMemory;
  1.1377 +					currentOperation->iState = EStEnd;
  1.1378 +					iCacheClient->UnlockSegments(firstSegmentStartPos);
  1.1379 +					break;
  1.1380 +					}
  1.1381 +
  1.1382 +				// requeue this request
  1.1383 +				return CFsRequest::EReqActionContinue;
  1.1384 +				}
  1.1385 +
  1.1386 +			case EStReadFromDiskComplete:
  1.1387 +				{
  1.1388 +				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
  1.1389 +				// restore addr & segmentCount etc from scratch area
  1.1390 +				firstSegmentStartPos = currentPos & segmentSizeMask;
  1.1391 +				addr = (TUint8*) currentOperation->iScratchValue0;
  1.1392 +				segmentCount = ((TInt) currentOperation->iScratchValue1) & 0xFFFF;
  1.1393 +				readFlags = ((TInt) currentOperation->iScratchValue1) >> 16;
  1.1394 +
  1.1395 +				if (readFlags & EReadLastSegment)
  1.1396 +					{
  1.1397 +					TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
  1.1398 +					readFlags&= ~EReadLastSegment;
  1.1399 +					readPos = lastSegmentStartPos;
  1.1400 +					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
  1.1401 +					readSegmentCount = 1;
  1.1402 +					currentOperation->iState = EStReadFromDisk;
  1.1403 +					break;
  1.1404 +					}
  1.1405 +				
  1.1406 +				aMsgRequest.CurrentOperation().iState = EStCopyFromClient;
  1.1407 +				}
  1.1408 +				break;
  1.1409 +
  1.1410 +			case EStCopyFromClient:
  1.1411 +				{
  1.1412 +				TInt writeLen = segmentCount << segmentSizeLog2;
  1.1413 +				TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
  1.1414 +				writeLen = Min(writeLen - offset, totalLen);
  1.1415 +
  1.1416 +				if (addr != NULL)
  1.1417 +					{
  1.1418 +					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
  1.1419 +					// copy from user buffer to cache buffer
  1.1420 +					TInt writeError = KErrNone;
  1.1421 +					if (currentOperation->iClientRequest)
  1.1422 +						{
  1.1423 +						TPtr8 ptr(addr+offset, writeLen, writeLen);
  1.1424 +						writeError = aMsgRequest.Read(0, ptr, currentOffset);
  1.1425 +						}
  1.1426 +					else
  1.1427 +						{
  1.1428 +						memcpy(addr+offset, ((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, writeLen);
  1.1429 +						}
  1.1430 +
  1.1431 +					// "writing" past end of file ?
  1.1432 +					if (currentPos + writeLen > iSize64 && cachingWrites && lastError == KErrNone && writeError == KErrNone)
  1.1433 +						{
  1.1434 +						__CACHE_PRINT2(_L("CACHEFILE: extending size old %ld new %ld"), iSize64, currentPos + totalLen);
  1.1435 +						iSize64 = currentPos + writeLen;
  1.1436 +						}
  1.1437 +
  1.1438 +					TInt anyError = (writeError != KErrNone)?writeError:lastError;
  1.1439 +
  1.1440 +					if (anyError == KErrNone)
  1.1441 +						iCacheClient->MarkSegmentsAsFilled(firstSegmentStartPos, segmentCount);
  1.1442 +
  1.1443 +					if (cachingWrites && anyError == KErrNone)
  1.1444 +						{
  1.1445 +						iCacheClient->MarkSegmentsAsDirty(firstSegmentStartPos, segmentCount);
  1.1446 +						// start dirty data timer
  1.1447 +						FileDirty(aMsgRequest);
  1.1448 +						}
  1.1449 +
  1.1450 +					// unlock if we're not buffering writes (segments won't be unlocked if dirty)
  1.1451 +					iCacheClient->UnlockSegments(firstSegmentStartPos);
  1.1452 +					}
  1.1453 +
  1.1454 +				currentOffset+= writeLen;
  1.1455 +				currentPos+= writeLen;
  1.1456 +				totalLen-= writeLen;
  1.1457 +
  1.1458 +				currentOperation->iState = EStWriteToCache;
  1.1459 +				break;
  1.1460 +				}
  1.1461 +
  1.1462 +			case EStWriteToCacheComplete:
  1.1463 +				{
  1.1464 +				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
  1.1465 +
  1.1466 +				if (lastError == KErrCancel)
  1.1467 +					{
  1.1468 +					currentOperation->iState = EStEnd;
  1.1469 +					}
  1.1470 +				else if ((!cachingWrites) || (lastError != KErrNone))
  1.1471 +					{
  1.1472 +					// allow TFsFileWrite::DoRequestL() to proceed normally using original pos & len
  1.1473 +					__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
  1.1474 +	
  1.1475 +					currentOperation->iState = EStWriteThrough;
  1.1476 +					}
  1.1477 +				else
  1.1478 +					{
  1.1479 +					__CACHE_PRINT2(_L("CACHEFILE: write buffered pos %ld, len %d"), 
  1.1480 +						aMsgRequest.CurrentOperation().iReadWriteArgs.iPos, aMsgRequest.CurrentOperation().iReadWriteArgs.iOffset);
  1.1481 +					__ASSERT_DEBUG(totalLen == 0, Fault(ECompletingWriteWithDataRemaining));
  1.1482 +
  1.1483 +					currentOperation->iState = EStEnd;
  1.1484 +					}
  1.1485 +				break;
  1.1486 +				}
  1.1487 +
  1.1488 +			case EStWriteThrough:
  1.1489 +
  1.1490 +				if (lastError == KErrCancel)
  1.1491 +					{
  1.1492 +					currentOperation->iState = EStEnd;
  1.1493 +					break;
  1.1494 +					}
  1.1495 +
  1.1496 +				// we're going to issue an uncached write so clear any error
  1.1497 +				lastError = KErrNone;
  1.1498 +
  1.1499 +				if (cachingWrites)
  1.1500 +					{
  1.1501 +					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
  1.1502 +#ifdef _DEBUG
  1.1503 +					if (r == CFsRequest::EReqActionBusy)
  1.1504 +						CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
  1.1505 +#endif
  1.1506 +					// Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure 
  1.1507 +					// TFsFileWrite::PostInitialise() doesn't think there's no data left to process
  1.1508 +					aMsgRequest.ReStart();
  1.1509 +					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
  1.1510 +						return r;
  1.1511 +					}
  1.1512 +
  1.1513 +				retCode = CFsRequest::EReqActionContinue;
  1.1514 +
  1.1515 +				// fall into...EStRestart
  1.1516 +				currentOperation->iState = EStReStart;
  1.1517 +
  1.1518 +			case EStReStart:
  1.1519 +
  1.1520 +				__ASSERT_DEBUG(retCode == CFsRequest::EReqActionBusy || retCode == CFsRequest::EReqActionContinue, Fault(EBadRetCode));
  1.1521 +
  1.1522 +				aMsgRequest.ReStart();
  1.1523 +				UpdateSharePosition(aMsgRequest, *currentOperation);
  1.1524 +				if (currentOperation->iClientRequest)
  1.1525 +					currentOperation->iReadWriteArgs.iPos = currentOperation->iClientPosition; // NB maybe KCurrentPosition64
  1.1526 +				return retCode;
  1.1527 +
  1.1528 +			case EStEnd:
  1.1529 +				return retCode;
  1.1530 +			}
  1.1531 +		}
  1.1532 +	}
  1.1533 +
  1.1534 +
  1.1535 +
  1.1536 +TInt CFileCache::AllocateRequest(CFsClientMessageRequest*& aNewRequest, TBool aWrite, CSessionFs* aSession)
  1.1537 +	{
  1.1538 +
  1.1539 +	RLocalMessage msgNew;
  1.1540 +	const TOperation& oP = OperationArray[aWrite?EFsFileWriteDirty:EFsFileRead];
  1.1541 +	TInt r = RequestAllocator::GetMessageRequest(oP, msgNew, aNewRequest);
  1.1542 +	if (r != KErrNone)
  1.1543 +		return r;
  1.1544 +
  1.1545 +	aNewRequest->Set(msgNew, oP, aSession);
  1.1546 +	aNewRequest->SetDrive(iDrive);
  1.1547 +	
  1.1548 +	// read-aheads and write-dirty requests should not be posted to plugins
  1.1549 +	// If there are data-modifying plugins, then these should sit above the file cache
  1.1550 +	aNewRequest->iCurrentPlugin = NULL;
  1.1551 +	aNewRequest->EnablePostIntercept(EFalse);
  1.1552 +
  1.1553 +	// Scratch value is a CFileCB pointer NOT a CFileShare for requests
  1.1554 +	// allocated by the file cache
  1.1555 +	aNewRequest->SetScratchValue64( MAKE_TINT64(EFalse, (TUint) iFileCB) );
  1.1556 +
  1.1557 +	// don't call Initialise(), don't call PostInitialise()
  1.1558 +	aNewRequest->SetState(CFsRequest::EReqStateDoRequest);
  1.1559 +	
  1.1560 +	// don't call Message().Complete()
  1.1561 +	aNewRequest->SetCompleted(EFalse);		
  1.1562 +
  1.1563 +	__ASSERT_DEBUG(aNewRequest->CurrentOperationPtr() == NULL, Fault(EBadOperationIndex));
  1.1564 +
  1.1565 +	return KErrNone;
  1.1566 +	}
  1.1567 +
  1.1568 +TInt CFileCache::FlushDirty(CFsRequest* aOldRequest)
  1.1569 +	{
  1.1570 +	iLock.Wait();
  1.1571 +
  1.1572 +	CFsClientMessageRequest* newRequest = NULL;
  1.1573 +
  1.1574 +	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
  1.1575 +
  1.1576 +	iLock.Signal();
  1.1577 +
  1.1578 +	if (newRequest)
  1.1579 +	    {
  1.1580 +		//To be used in notification framework.
  1.1581 +	    //newRequest->iUID = aOldRequest->Message().Identity();
  1.1582 +		newRequest->Dispatch();
  1.1583 +	    }
  1.1584 +
  1.1585 +	return r;
  1.1586 +	}
  1.1587 +
  1.1588 +
  1.1589 +void CFileCache::Purge(TBool aPurgeDirty)
  1.1590 +	{
  1.1591 +	iLock.Wait();
  1.1592 +
  1.1593 +	iCacheClient->Purge(aPurgeDirty);
  1.1594 +
  1.1595 +	iLock.Signal();
  1.1596 +	}
  1.1597 +
  1.1598 +void CFileCache::PropagateFlushErrorToAllFileShares()
  1.1599 +	{
  1.1600 +	FileShares->Lock();
  1.1601 +	TInt count = FileShares->Count();
  1.1602 +	while(count--)
  1.1603 +		{
  1.1604 +		CFileShare* share = (CFileShare*)(*FileShares)[count];
  1.1605 +		if (&share->File() == iFileCB)
  1.1606 +			{
  1.1607 +			share->iFlushError = iFlushError;
  1.1608 +			}
  1.1609 +		}
  1.1610 +	FileShares->Unlock();
  1.1611 +	}
  1.1612 +
  1.1613 +/**
  1.1614 +CFileCache::DoFlushDirty()
  1.1615 +
  1.1616 +@param aFlushAll. If EFalse then only a single cacheline will be flushed
  1.1617 +
  1.1618 +returns	CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
  1.1619 +		CFsRequest::EReqActionComplete if nothing to flush / flush completed 
  1.1620 +		KErrNoMemory if failed to allocate a request
  1.1621 +		or one of the system wide error codes
  1.1622 +
  1.1623 + */
  1.1624 +TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
  1.1625 +	{
  1.1626 +	TInt64  pos;
  1.1627 +	TUint8* addr;
  1.1628 +
  1.1629 +	if (iFlushBusy)
  1.1630 +		return CFsRequest::EReqActionBusy;
  1.1631 +
  1.1632 +	TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
  1.1633 +
  1.1634 +	if (segmentCount == 0)
  1.1635 +		{
  1.1636 +		TInt flushError = iFlushError;
  1.1637 +		iFlushError = KErrNone;
  1.1638 +
  1.1639 +		// If this flush didn't originate from a client request return last error
  1.1640 +		if (!aOldRequest || !aOldRequest->Session())
  1.1641 +			return (flushError == KErrNone)? CFsRequest::EReqActionComplete : flushError;
  1.1642 +
  1.1643 +		// Return the last error from CFileShare::iFlushError 
  1.1644 +		// and then clear CFileShare::iFlushError 
  1.1645 +
  1.1646 +		CFileShare* share = (CFileShare*) SessionObjectFromHandle(
  1.1647 +			aOldRequest->Message().Int3(),
  1.1648 +			FileShares->UniqueID(),
  1.1649 +			aOldRequest->Session());
  1.1650 +
  1.1651 +		TInt r = KErrNone;
  1.1652 +		if (share)
  1.1653 +			{
  1.1654 +			r = share->iFlushError;
  1.1655 +			share->iFlushError = KErrNone;
  1.1656 +			}
  1.1657 +		if (r == KErrNone)
  1.1658 +			return CFsRequest::EReqActionComplete;
  1.1659 +
  1.1660 +		return r;
  1.1661 +		}
  1.1662 +
  1.1663 +
  1.1664 +	// NB aOldRequest->Session may be NULL - e.g for FileShareCloseOp
  1.1665 +	CSessionFs* session = aOldRequest && aOldRequest->Session() ? aOldRequest->Session() : iDirtyDataOwner;
  1.1666 +
  1.1667 +	__ASSERT_ALWAYS(session, Fault(EFlushingWithSessionNull));
  1.1668 +
  1.1669 +	TInt r = AllocateRequest(aNewRequest, ETrue, session);
  1.1670 +	if (r != KErrNone)
  1.1671 +		return r;
  1.1672 +	
  1.1673 +	r = aNewRequest->PushOperation(0, 0, (TUint8*) NULL, 0);
  1.1674 +
  1.1675 +	if (r != KErrNone)
  1.1676 +		{
  1.1677 +		aNewRequest->Free();
  1.1678 +		aNewRequest = NULL;
  1.1679 +		return r;
  1.1680 +		}
  1.1681 +
  1.1682 +	// set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
  1.1683 +	aNewRequest->CurrentOperation().iScratchValue0 = 
  1.1684 +		(TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
  1.1685 +
  1.1686 +
  1.1687 +	// issue flush request
  1.1688 +	r = FlushDirtySm(*aNewRequest);
  1.1689 +
  1.1690 +	// We should only get three possible return codes from FlushDirtySm() :
  1.1691 +	// CFsRequest::EReqActionContinue	- a write request (aNewRequest) needs to be dispatched
  1.1692 +	// CFsRequest::EReqActionComplete	- completed already - this can happen if dirty data was beyond file end
  1.1693 +	// CFsRequest::EReqActionBusy		- first dirty cacheline is already in use.
  1.1694 +	__ASSERT_DEBUG(	r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionComplete || r == CFsRequest::EReqActionBusy, Fault(EBadRetCode));
  1.1695 +	if (r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionBusy)
  1.1696 +		{
  1.1697 +		// requeue the caller's request (aOldRequest) if there is one
  1.1698 +		return CFsRequest::EReqActionBusy;
  1.1699 +		}
  1.1700 +	else	// CFsRequest::EReqActionComplete
  1.1701 +		{
  1.1702 +		aNewRequest->PopOperation();
  1.1703 +		aNewRequest->Free();
  1.1704 +		aNewRequest = NULL;
  1.1705 +		return r;
  1.1706 +		}
  1.1707 +	
  1.1708 +	}
  1.1709 +
  1.1710 +
  1.1711 +TInt TFsFileWriteDirty::PostInitialise(CFsRequest* aRequest)
  1.1712 +	{
  1.1713 +	return CFileCache::CompleteFlushDirty(aRequest);
  1.1714 +	}
  1.1715 +
  1.1716 +TInt CFileCache::CompleteFlushDirty(CFsRequest* aRequest)
  1.1717 +	{
  1.1718 +	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
  1.1719 +
  1.1720 +	CFileShare* share;
  1.1721 +	CFileCB* file;
  1.1722 +	GetFileFromScratch(aRequest, share, file);
  1.1723 +	CFileCache* fileCache = file->FileCache();
  1.1724 +
  1.1725 +	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
  1.1726 +
  1.1727 +#ifdef _DEBUG
  1.1728 +	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
  1.1729 +#endif
  1.1730 +
  1.1731 +	fileCache->iLock.Wait();
  1.1732 +	TInt r = fileCache->FlushDirtySm(msgRequest);
  1.1733 +	fileCache->iLock.Signal();
  1.1734 +
  1.1735 +#ifdef _DEBUG
  1.1736 +	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
  1.1737 +#endif
  1.1738 +
  1.1739 +	// if this request has been cancelled we mustn't dispatch it again - 
  1.1740 +	// we still need to call state machine however so that any locked segments can be unlocked
  1.1741 +	if (msgRequest.LastError() == KErrCancel)
  1.1742 +		r = CFsRequest::EReqActionComplete;
  1.1743 +
  1.1744 +	return r;
  1.1745 +	}
  1.1746 +
  1.1747 +TInt CFileCache::FlushDirtySm(CFsMessageRequest& aMsgRequest)
  1.1748 +	{
  1.1749 +	enum states
  1.1750 +		{
  1.1751 +		EStBegin=0,
  1.1752 +		EStWriteToDisk,
  1.1753 +		EStWriteToDiskComplete,
  1.1754 +		EStMarkAsClean,
  1.1755 +		EStEnd
  1.1756 +		};
  1.1757 +
  1.1758 +	TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
  1.1759 +	TInt& lastError = aMsgRequest.LastError();
  1.1760 +	TInt64 pos = 0;
  1.1761 +
  1.1762 +	for(;;)
  1.1763 +		{
  1.1764 +
  1.1765 +		switch(currentOperation->iState)
  1.1766 +			{
  1.1767 +			case EStBegin:
  1.1768 +				iFlushBusy = ETrue;
  1.1769 +
  1.1770 +				// fall into...
  1.1771 +
  1.1772 +			case EStWriteToDisk:
  1.1773 +				{
  1.1774 +				currentOperation->iState = EStWriteToDisk;
  1.1775 +
  1.1776 +				TUint8* addr;
  1.1777 +				TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
  1.1778 +				
  1.1779 +				// keep track of how many segments we've written
  1.1780 +				currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
  1.1781 +
  1.1782 +				// if no more dirty segments of if a genuine error then finish
  1.1783 +				// NB if the request has been cancelled then we still need to proceed and mark all the
  1.1784 +				// segments as clean, otherwise they will never get freed !
  1.1785 +				if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel))
  1.1786 +					{
  1.1787 +					currentOperation->iState = EStEnd;
  1.1788 +					break;
  1.1789 +					}
  1.1790 +
  1.1791 +				TInt  len = segmentCount << SegmentSizeLog2();
  1.1792 +
  1.1793 +				if (pos < iSize64)
  1.1794 +					// Result of Min shall be of size TInt
  1.1795 +					// Hence to suppress warning
  1.1796 +					len = (TInt)(Min(iSize64 - pos, (TInt64)len));
  1.1797 +				else
  1.1798 +					len = 0;
  1.1799 +
  1.1800 +
  1.1801 +				// if writing past end of file or this request has been cancelled, just mark as clean
  1.1802 +				if (len == 0 || lastError == KErrCancel)
  1.1803 +					{
  1.1804 +					currentOperation->iState = EStMarkAsClean;
  1.1805 +					break;
  1.1806 +					}
  1.1807 +
  1.1808 +				__CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
  1.1809 +
  1.1810 +				TInt filledSegmentCount;
  1.1811 +				TInt lockError;
  1.1812 +				addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue);
  1.1813 +
  1.1814 +				// If cacheline is busy, we need to post request to back of drive queue
  1.1815 +				// To dispatch to drive thread and intercept before DoRequestL() we must call iPostInitialise(), 
  1.1816 +				// so set the state back to CFsRequest::EReqStatePostInitialise
  1.1817 +				if (lockError == KErrInUse)
  1.1818 +					{
  1.1819 +					__CACHE_PRINT(_L("FlushDirtySm() - cacheline BUSY !"));
  1.1820 +					aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
  1.1821 +					return CFsRequest::EReqActionBusy;
  1.1822 +					}
  1.1823 +				else if (lockError != KErrNone)
  1.1824 +					{
  1.1825 +					iFlushBusy = EFalse;
  1.1826 +					lastError = lockError;
  1.1827 +					return CFsRequest::EReqActionComplete;
  1.1828 +					}
  1.1829 +
  1.1830 +				currentOperation->Set(pos, len, addr, 0, EStWriteToDiskComplete);
  1.1831 +				if (pos < iSize64)
  1.1832 +					{
  1.1833 +					TInt r = aMsgRequest.PushOperation(
  1.1834 +						pos, len, addr, 0,
  1.1835 +						CompleteFlushDirty, 
  1.1836 +						EStWriteToDiskComplete);
  1.1837 +					if (r != KErrNone)
  1.1838 +						{
  1.1839 +						iCacheClient->UnlockSegments(pos);
  1.1840 +						iFlushBusy = EFalse;
  1.1841 +						lastError = r;
  1.1842 +						return CFsRequest::EReqActionComplete;
  1.1843 +						}
  1.1844 +
  1.1845 +					return CFsRequest::EReqActionContinue;	// continue on to TFsFileWrite::DoRequestL()()
  1.1846 +					}
  1.1847 +				}
  1.1848 +
  1.1849 +			case EStWriteToDiskComplete:
  1.1850 +				{
  1.1851 +#ifdef _DEBUG
  1.1852 +				// simulate a media eject to test critical error server
  1.1853 +				if (CCacheManagerFactory::CacheManager()->SimulateWriteFailureEnabled())
  1.1854 +					{
  1.1855 +					lastError = KErrNotReady;
  1.1856 +					iDrive->Dismount();
  1.1857 +					}
  1.1858 +#endif
  1.1859 +				pos = currentOperation->iReadWriteArgs.iPos;
  1.1860 +				iCacheClient->UnlockSegments(pos);
  1.1861 +				}
  1.1862 +				// fall into...
  1.1863 +
  1.1864 +			case EStMarkAsClean:
  1.1865 +				{
  1.1866 +				// NB pos must be set by EStWriteToDiskComplete or EStWriteToDisk
  1.1867 +
  1.1868 +				if (lastError != KErrNone)
  1.1869 +					{
  1.1870 +					__CACHE_PRINT1(_L("CACHEFILE: WriteThrough FAILED %d"), lastError);
  1.1871 +					
  1.1872 +					lastError = HandleWriteDirtyError(lastError);
  1.1873 +
  1.1874 +					// retry ?
  1.1875 +					if (lastError == KErrNone)
  1.1876 +						{
  1.1877 +						// clear error and try again
  1.1878 +						currentOperation->iState = EStWriteToDisk;
  1.1879 +						break;
  1.1880 +						}
  1.1881 +
  1.1882 +					iFlushError = lastError;
  1.1883 +					PropagateFlushErrorToAllFileShares();
  1.1884 +
  1.1885 +					__CACHE_PRINT2(_L("CACHEFILE: Resetting size from %ld to %ld"), iSize64, iFileCB->Size64());
  1.1886 + 					SetSize64(iFileCB->Size64());
  1.1887 +					}
  1.1888 +
  1.1889 +				if (lastError != KErrNone)
  1.1890 +					{
  1.1891 +					// Destroy ALL cached data (dirty and non-dirty) !
  1.1892 +					iCacheClient->Purge(ETrue);
  1.1893 +					}
  1.1894 +				else
  1.1895 +					{
  1.1896 +					// Mark segment as clean
  1.1897 +					iCacheClient->MarkSegmentsAsClean(pos);
  1.1898 +					}
  1.1899 +
  1.1900 +
  1.1901 +				if (TInt(currentOperation->iScratchValue0) > 0)
  1.1902 +					currentOperation->iState = EStWriteToDisk;
  1.1903 +				else
  1.1904 +					currentOperation->iState = EStEnd;
  1.1905 +
  1.1906 +
  1.1907 +				}
  1.1908 +				break;
  1.1909 +
  1.1910 +			case EStEnd:
  1.1911 +				{
  1.1912 +				iFlushBusy = EFalse;
  1.1913 +
  1.1914 +				TUint8* addr;
  1.1915 +				MarkFileClean();
  1.1916 +				// Re-start dirty data timer if there is still some dirty data
  1.1917 +				if (iCacheClient->FindFirstDirtySegment(pos, addr) != 0)
  1.1918 +					FileDirty(aMsgRequest);
  1.1919 +
  1.1920 +				return CFsRequest::EReqActionComplete;
  1.1921 +				}
  1.1922 +			}
  1.1923 +		}
  1.1924 +	}
  1.1925 +
  1.1926 +/**
  1.1927 +Handle a dirty data write error
  1.1928 +
  1.1929 +Returns aError if dirty data should be thrown away or
  1.1930 +		KErrNone if write should be retried 
  1.1931 +*/
  1.1932 +TInt CFileCache::HandleWriteDirtyError(TInt aError)
  1.1933 +	{
  1.1934 +	__THRD_PRINT3(_L(">TRACE: CFileCache::HandleWriteDirtyError() aError %d mounted %d changed %d"), aError, iDrive->IsMounted(), iDrive->IsChanged());
  1.1935 +
  1.1936 +	// normally the disk change will have been detected by TDrive::CheckMount() but occasionally,
  1.1937 +	// the change will occur while writing - in which case we need to mimick what TDrive::CheckMount does,
  1.1938 +	// to make sure we are in a consisten state i.e. dismount the drive and set iCurrentMount to NULL
  1.1939 +	if (iDrive->IsChanged())
  1.1940 +		{
  1.1941 +		iDrive->SetChanged(EFalse);
  1.1942 +		if (iDrive->IsMounted())		//	Dismount the mount if it is still marked as mounted
  1.1943 +            iDrive->Dismount();
  1.1944 +		}
  1.1945 +
  1.1946 +	// if error didn't occur because of a media eject, do nothing 
  1.1947 +	if (aError == KErrNotReady && !iDrive->IsMounted())
  1.1948 +		{
  1.1949 +	
  1.1950 +		TLocaleMessage line1;
  1.1951 +		TLocaleMessage line2;
  1.1952 +		line1=EFileServer_PutTheCardBackLine1;
  1.1953 +		line2=EFileServer_PutTheCardBackLine2;
  1.1954 +
  1.1955 +		//-- create Notifier  
  1.1956 +		CAsyncNotifier* notifier = CAsyncNotifier::New(); 
  1.1957 +		if( !notifier )
  1.1958 +			{
  1.1959 +			return aError;
  1.1960 +			}
  1.1961 +
  1.1962 +		notifier->SetMount(iMount);
  1.1963 +
  1.1964 +		
  1.1965 +		// While this (drive) thread is calling the notifier server (& effectively suspended), 
  1.1966 +		// the main thread may call TFsFileRead::PostInitialise() or TFsFileWrite::PostInitialise() 
  1.1967 +		// which would cause dead-lock unless we release the lock here. We also need to release 
  1.1968 +		// the lock before calling CDriveThread::CompleteReadWriteRequests().
  1.1969 +		iLock.Signal();	
  1.1970 +
  1.1971 +		FOREVER
  1.1972 +			{
  1.1973 +			TInt buttonVal;
  1.1974 +
  1.1975 +			TInt ret = notifier->Notify(
  1.1976 +				TLocaleMessageText(line1),
  1.1977 +				TLocaleMessageText(line2),
  1.1978 +				TLocaleMessageText(EFileServer_Button1),
  1.1979 +				TLocaleMessageText(EFileServer_Button2),
  1.1980 +				buttonVal);
  1.1981 +			if (ret!=KErrNone)
  1.1982 +				break; 
  1.1983 +			if (buttonVal!=1)
  1.1984 +				break; // Abort
  1.1985 +
  1.1986 +
  1.1987 +			// Wait up to 3 seconds for a disk change - this is because there is often a substantial 
  1.1988 +			// (one/two second) delay between inserting a card & getting a notification that the card is ready;
  1.1989 +			// if we give up too soon and fire of the notifier again, it's not very user-friendly !
  1.1990 +			const TTimeIntervalMicroSeconds32 KHalfSecond(500000);
  1.1991 +			const TInt KRetries = 6;;
  1.1992 +			for (TInt n=0; !iDrive->IsChanged() && n<KRetries; n++)
  1.1993 +				User::After(KHalfSecond);
  1.1994 +
  1.1995 +
  1.1996 +
  1.1997 +			// Without this code, retry will indiscriminately write over whatever disk happens to be present.
  1.1998 +			// However if the write error is to the bootsector remounting will always fail because the boot
  1.1999 +			// sector will have changed and hence the disk is useless.
  1.2000 +			// 
  1.2001 +			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
  1.2002 +
  1.2003 +			TInt remountSuccess = iDrive->ReMount(*iMount);
  1.2004 +
  1.2005 +			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
  1.2006 +			if (!remountSuccess)
  1.2007 +				continue;
  1.2008 +			
  1.2009 +			
  1.2010 +			iMount->Drive().SetChanged(EFalse);
  1.2011 +
  1.2012 +			aError = KErrNone; // Retry
  1.2013 +			break;
  1.2014 +			}
  1.2015 +
  1.2016 +		delete notifier;
  1.2017 +
  1.2018 +
  1.2019 +		// Cancel hung state 
  1.2020 +		FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
  1.2021 +
  1.2022 +		
  1.2023 +		// media had been removed and NOT replaced: so destroy ALL cached data 
  1.2024 +		// (dirty and non-dirty) for all filecaches on this drive
  1.2025 +		if (aError != KErrNone)
  1.2026 +			{
  1.2027 +			CDriveThread* pT=NULL;
  1.2028 +			TInt r=FsThreadManager::GetDriveThread(DriveNumber(), &pT);
  1.2029 +			if(r==KErrNone)
  1.2030 +				{
  1.2031 +				pT->CompleteReadWriteRequests();
  1.2032 +				iDrive->PurgeDirty(*iMount);
  1.2033 +				}
  1.2034 +			}
  1.2035 +
  1.2036 +		iLock.Wait();
  1.2037 +		}
  1.2038 +
  1.2039 +
  1.2040 +	return aError;
  1.2041 +	}
  1.2042 +
  1.2043 +
  1.2044 +/**
  1.2045 +Mark file as dirty
  1.2046 +*/
  1.2047 +void CFileCache::FileDirty(CFsMessageRequest& aMsgRequest)
  1.2048 +	{
  1.2049 +	CSessionFs* session = aMsgRequest.Session();
  1.2050 +	__ASSERT_ALWAYS(session, Fault(EDirtyDataOwnerNull));
  1.2051 +
  1.2052 +	// Remember the last session which caused the file to become dirty
  1.2053 +	// Always record whether any session has reserved access so the CheckDiskSpace() behaves correctly
  1.2054 +	if (iDirtyDataOwner == NULL || session->ReservedAccess(iDriveNum))
  1.2055 +		iDirtyDataOwner = session;
  1.2056 +
  1.2057 +	// start a timer after which file will be flushed
  1.2058 +	CDriveThread* driveThread=NULL;
  1.2059 +	TInt r = FsThreadManager::GetDriveThread(iDriveNum, &driveThread);
  1.2060 +	if(r == KErrNone && driveThread != NULL)
  1.2061 +		iDirtyTimer.Start(driveThread, iDirtyDataFlushTime);
  1.2062 +	}
  1.2063 +
  1.2064 +//----------------------------------------------------------------------------
  1.2065 +/**
  1.2066 +    Mark the file as clean and stop dirty data timer
  1.2067 +*/
  1.2068 +void CFileCache::MarkFileClean()
  1.2069 +	{
  1.2070 +	iDirtyDataOwner = NULL;
  1.2071 +
  1.2072 +	if (!iDriveThread)
  1.2073 +		return;
  1.2074 +
  1.2075 +	iDirtyTimer.Stop();
  1.2076 +	}
  1.2077 +
  1.2078 +
  1.2079 +
  1.2080 +//************************************
  1.2081 +// TFileCacheSettings
  1.2082 +//************************************
  1.2083 +
  1.2084 +RArray<TFileCacheSettings::TFileCacheConfig>* TFileCacheSettings::iFileCacheSettings = NULL;
  1.2085 +
  1.2086 +
  1.2087 +
  1.2088 +const TInt KDriveCacheSettingsArrayGranularity = 4;
  1.2089 +
  1.2090 +
  1.2091 +TFileCacheSettings::TFileCacheConfig::TFileCacheConfig(TInt aDrive) : 
  1.2092 +	iDrive(aDrive), 
  1.2093 +	iFileCacheReadAsync(KDefaultFileCacheReadAsync),
  1.2094 +	iFairSchedulingLen(KDefaultFairSchedulingLen << KByteToByteShift),
  1.2095 +	iCacheSize(KDefaultFileCacheSize << KByteToByteShift),
  1.2096 +	iMaxReadAheadLen(KDefaultFileCacheMaxReadAheadLen << KByteToByteShift),
  1.2097 +	iClosedFileKeepAliveTime(KDefaultClosedFileKeepAliveTime  << KByteToByteShift),	// convert milliSecs -> microSecs (approximately)
  1.2098 +	iDirtyDataFlushTime(KDefaultDirtyDataFlushTime  << KByteToByteShift)				// convert milliSecs -> microSecs (approximately)
  1.2099 +	{
  1.2100 +	iFlags = ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
  1.2101 +	}
  1.2102 +
  1.2103 +
  1.2104 +TFileCacheFlags TFileCacheSettings::TFileCacheConfig::ConvertEnumToFlags(const TInt aFileCacheRead, const TInt aFileCacheReadAhead, const TInt aFileCacheWrite) 
  1.2105 +	{
  1.2106 +	TInt flags(0);
  1.2107 +
  1.2108 +	// read caching
  1.2109 +	if (aFileCacheRead == EFileCacheFlagEnabled)
  1.2110 +		flags|= EFileCacheReadEnabled;
  1.2111 +	else if (aFileCacheRead == EFileCacheFlagOn)
  1.2112 +		flags|= EFileCacheReadEnabled | EFileCacheReadOn;
  1.2113 +
  1.2114 +	// read ahead
  1.2115 +	if (aFileCacheReadAhead == EFileCacheFlagEnabled)
  1.2116 +		flags|= EFileCacheReadAheadEnabled;
  1.2117 +	else if (aFileCacheReadAhead == EFileCacheFlagOn)
  1.2118 +		flags|= EFileCacheReadAheadEnabled | EFileCacheReadAheadOn;
  1.2119 +
  1.2120 +	// write caching
  1.2121 +	if (aFileCacheWrite == EFileCacheFlagEnabled)
  1.2122 +		flags|= EFileCacheWriteEnabled;
  1.2123 +	else if (aFileCacheWrite == EFileCacheFlagOn)
  1.2124 +		flags|= EFileCacheWriteEnabled | EFileCacheWriteOn;
  1.2125 +
  1.2126 +	return TFileCacheFlags(flags);
  1.2127 +	}
  1.2128 +
  1.2129 +
  1.2130 +	
  1.2131 +_LIT8(KLitSectionNameDrive,"Drive%C");
  1.2132 +
  1.2133 +static const TPtrC8 KCacheFlagEnumStrings[]=
  1.2134 +	{
  1.2135 +	_S8("OFF"),
  1.2136 +	_S8("ENABLED"),
  1.2137 +	_S8("ON"),
  1.2138 +	_S8(""),	// terminator
  1.2139 +	};
  1.2140 +const TInt KMaxEnumLen = 7;
  1.2141 +
  1.2142 +void TFileCacheSettings::ReadEnum(const TDesC8& aSection, const TDesC8& aProperty, TInt32& aEnumVal, const TPtrC8* aEnumStrings)
  1.2143 +	{
  1.2144 +	TBuf8<KMaxEnumLen> buf;
  1.2145 +	if (!F32Properties::GetString(aSection, aProperty, buf))
  1.2146 +		return;
  1.2147 +	TInt n;
  1.2148 +	const TPtrC8* enumString;
  1.2149 +	for (enumString=aEnumStrings, n=0; enumString->Length()!= 0; enumString++, n++)
  1.2150 +		{
  1.2151 +		if (buf.LeftTPtr(enumString->Length()).MatchF(*enumString) == 0)
  1.2152 +			{
  1.2153 +			aEnumVal = n;
  1.2154 +			break;
  1.2155 +			}
  1.2156 +		}
  1.2157 +	}
  1.2158 +	
  1.2159 +TInt TFileCacheSettings::ReadPropertiesFile(TInt aDriveNumber)
  1.2160 +	{
  1.2161 +	Init();
  1.2162 +
  1.2163 +
  1.2164 +	if (!TGlobalFileCacheSettings::Enabled())
  1.2165 +		return KErrNone;
  1.2166 +
  1.2167 +	TFileCacheConfig* driveCacheSettings;
  1.2168 +	TInt r = GetFileCacheConfig(aDriveNumber, driveCacheSettings);
  1.2169 +	if (r != KErrNone)
  1.2170 +		return r;
  1.2171 +
  1.2172 +	// restore default settings in case they've been changed by SetFlags()
  1.2173 +	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
  1.2174 +
  1.2175 +
  1.2176 +	// Get file cache configuration settings for this drive
  1.2177 +	// N.B. Size/length values are specified in Kilobytes, timer values in Milliseconds
  1.2178 +	TBuf8<8> sectionName;
  1.2179 +	sectionName.Format(KLitSectionNameDrive, 'A' + aDriveNumber);
  1.2180 +
  1.2181 +	TInt32 val;
  1.2182 +	//  Read FileCacheSize
  1.2183 +	if (F32Properties::GetInt(sectionName, _L8("FileCacheSize"),		val))
  1.2184 +		driveCacheSettings->iCacheSize = val << KByteToByteShift;
  1.2185 +
  1.2186 +	// ensure read-ahead len is not greater than the cache size
  1.2187 +	driveCacheSettings->iMaxReadAheadLen = 
  1.2188 +		Min(driveCacheSettings->iMaxReadAheadLen, driveCacheSettings->iCacheSize);
  1.2189 +
  1.2190 +	// Read FileCacheReadAsync
  1.2191 +	TBool bVal;
  1.2192 +	if (F32Properties::GetBool(sectionName, _L8("FileCacheReadAsync"), bVal))
  1.2193 +		driveCacheSettings->iFileCacheReadAsync = bVal;
  1.2194 +
  1.2195 +	// Read FairSchedulingLen
  1.2196 +	if (F32Properties::GetInt(sectionName, _L8("FairSchedulingLen"),	val))
  1.2197 +		driveCacheSettings->iFairSchedulingLen = val << KByteToByteShift;
  1.2198 +
  1.2199 +	// Read ClosedFileKeepAliveTime - convert miliSecs to microSecs (approximately)
  1.2200 +	if (F32Properties::GetInt(sectionName, _L8("ClosedFileKeepAliveTime"),	val))
  1.2201 +		driveCacheSettings->iClosedFileKeepAliveTime = val << KByteToByteShift;
  1.2202 +
  1.2203 +	// Read DirtyDataFlushTime - convert miliSecs to microSecs (approximately)
  1.2204 +	if (F32Properties::GetInt(sectionName, _L8("DirtyDataFlushTime"),	val))
  1.2205 +		driveCacheSettings->iDirtyDataFlushTime = val << KByteToByteShift;
  1.2206 +
  1.2207 +	// get read, read-ahead and write states
  1.2208 +	TInt32 readVal = KDefaultFileCacheRead;
  1.2209 +	TInt32 readAheadVal = KDefaultFileCacheReadAhead;
  1.2210 +	TInt32 writeVal = KDefaultFileCacheWrite;
  1.2211 +
  1.2212 +	ReadEnum(sectionName, _L8("FileCacheRead"), readVal, &KCacheFlagEnumStrings[0]);
  1.2213 +	ReadEnum(sectionName, _L8("FileCacheReadAhead"), readAheadVal, &KCacheFlagEnumStrings[0]);
  1.2214 +	ReadEnum(sectionName, _L8("FileCacheWrite"), writeVal, &KCacheFlagEnumStrings[0]);
  1.2215 +	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(readVal, readAheadVal, writeVal);
  1.2216 +
  1.2217 +	__CACHE_PRINT7(_L("ReadPropertiesFile() drive %C flags %08X CacheSize %d FileCacheReadAsync %d iFairSchedulingLen %d iClosedFileKeepAliveTime %d iDirtyDataFlushTime %d\n"),
  1.2218 +		aDriveNumber + 'A', 
  1.2219 +		driveCacheSettings->iFlags, 
  1.2220 +		driveCacheSettings->iCacheSize, 
  1.2221 +		driveCacheSettings->iFileCacheReadAsync,
  1.2222 +		driveCacheSettings->iFairSchedulingLen,
  1.2223 +		driveCacheSettings->iClosedFileKeepAliveTime,
  1.2224 +		driveCacheSettings->iDirtyDataFlushTime);
  1.2225 +	
  1.2226 +	return KErrNone;
  1.2227 +	}
  1.2228 +
  1.2229 +
  1.2230 +TInt TFileCacheSettings::GetFileCacheConfig(TInt aDrive, TFileCacheConfig*& aConfig)
  1.2231 +	{
  1.2232 +	Init();
  1.2233 +
  1.2234 +	aConfig = NULL;
  1.2235 +
  1.2236 +	TFileCacheConfig driveCacheSettingsToMatch(aDrive);
  1.2237 +	
  1.2238 +	TInt index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
  1.2239 +	if (index == KErrNotFound)
  1.2240 +		{
  1.2241 +		// create a new entry. 
  1.2242 +		TInt r = iFileCacheSettings->InsertInUnsignedKeyOrder(driveCacheSettingsToMatch);
  1.2243 +		if (r != KErrNone)
  1.2244 +			return r;
  1.2245 +		index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
  1.2246 +		__ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheSettingsNotFound));
  1.2247 +		}
  1.2248 +
  1.2249 +	aConfig = &(*iFileCacheSettings)[index];
  1.2250 +	return KErrNone;
  1.2251 +	}
  1.2252 +
  1.2253 +void TFileCacheSettings::SetFlags(TInt aDrive, TFileCacheFlags aFlags)
  1.2254 +	{
  1.2255 +	TFileCacheConfig* driveCacheSettings;
  1.2256 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2257 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2258 +
  1.2259 +	driveCacheSettings->iFlags = aFlags;
  1.2260 +	}
  1.2261 +
  1.2262 +TFileCacheFlags TFileCacheSettings::Flags(TInt aDrive)
  1.2263 +	{
  1.2264 +	TFileCacheConfig* driveCacheSettings;
  1.2265 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2266 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2267 +
  1.2268 +	return driveCacheSettings->iFlags;
  1.2269 +	}
  1.2270 +
  1.2271 +void TFileCacheSettings::Init()
  1.2272 +	{
  1.2273 +	if (iFileCacheSettings == NULL)
  1.2274 +		{
  1.2275 +		iFileCacheSettings = new RArray<TFileCacheConfig>(KDriveCacheSettingsArrayGranularity, _FOFF(TFileCacheConfig, iDrive));
  1.2276 +		__ASSERT_ALWAYS(iFileCacheSettings != NULL, Fault(ECacheSettingsInitFailed));
  1.2277 +		}
  1.2278 +	}
  1.2279 +
  1.2280 +
  1.2281 +TBool TFileCacheSettings::FileCacheReadAsync(TInt aDrive)
  1.2282 +	{
  1.2283 +	TFileCacheConfig* driveCacheSettings;
  1.2284 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2285 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2286 +
  1.2287 +	return driveCacheSettings->iFileCacheReadAsync;
  1.2288 +	}
  1.2289 +
  1.2290 +TInt TFileCacheSettings::FairSchedulingLen(TInt aDrive)
  1.2291 +	{
  1.2292 +	TFileCacheConfig* driveCacheSettings;
  1.2293 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2294 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2295 +
  1.2296 +	return driveCacheSettings->iFairSchedulingLen;
  1.2297 +	}
  1.2298 +
  1.2299 +TInt TFileCacheSettings::MaxReadAheadLen(TInt aDrive)
  1.2300 +	{
  1.2301 +	TFileCacheConfig* driveCacheSettings;
  1.2302 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2303 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2304 +
  1.2305 +	return driveCacheSettings->iMaxReadAheadLen;
  1.2306 +	}
  1.2307 +
  1.2308 +TInt TFileCacheSettings::CacheSize(TInt aDrive)
  1.2309 +	{
  1.2310 +	TFileCacheConfig* driveCacheSettings;
  1.2311 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2312 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2313 +
  1.2314 +	return driveCacheSettings->iCacheSize;
  1.2315 +	}
  1.2316 +
  1.2317 +TInt TFileCacheSettings::ClosedFileKeepAliveTime(TInt aDrive)
  1.2318 +	{
  1.2319 +	TFileCacheConfig* driveCacheSettings;
  1.2320 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2321 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2322 +
  1.2323 +	return driveCacheSettings->iClosedFileKeepAliveTime;
  1.2324 +	}
  1.2325 +
  1.2326 +
  1.2327 +TInt TFileCacheSettings::DirtyDataFlushTime(TInt aDrive)
  1.2328 +	{
  1.2329 +	TFileCacheConfig* driveCacheSettings;
  1.2330 +	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
  1.2331 +	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
  1.2332 +
  1.2333 +	return driveCacheSettings->iDirtyDataFlushTime;
  1.2334 +	}
  1.2335 +
  1.2336 +//************************************
  1.2337 +// TClosedFileUtils
  1.2338 +//************************************
  1.2339 +
  1.2340 +CFsObjectCon* TClosedFileUtils::iClosedFiles = NULL;
  1.2341 +
  1.2342 +void TClosedFileUtils::InitL()
  1.2343 +	{
  1.2344 +	iClosedFiles = TheContainer->CreateL();
  1.2345 +	if (iClosedFiles == NULL)
  1.2346 +		User::LeaveIfError(KErrNoMemory);
  1.2347 +
  1.2348 +	}
  1.2349 +
  1.2350 +
  1.2351 +TBool TClosedFileUtils::IsClosed(CFileCache* aFileCache)
  1.2352 +	{
  1.2353 +	return (aFileCache->Container() == iClosedFiles);
  1.2354 +	}
  1.2355 +
  1.2356 +TInt TClosedFileUtils::Count()
  1.2357 +	{
  1.2358 +	return iClosedFiles->Count();
  1.2359 +	}
  1.2360 +
  1.2361 +CFileCache* TClosedFileUtils::At(TInt aIndex)
  1.2362 +	{
  1.2363 +	return (CFileCache*) (*iClosedFiles)[aIndex];
  1.2364 +	}
  1.2365 +
  1.2366 +
  1.2367 +// Add a closed file to closed file container
  1.2368 +void TClosedFileUtils::AddL(CFileCache* aFileCache, TBool aLock)
  1.2369 +	{
  1.2370 +	if (aFileCache->iDriveThread)
  1.2371 +		{
  1.2372 +		iClosedFiles->AddL(aFileCache, aLock);
  1.2373 +
  1.2374 +		// start a timer after which file will be removed from closed queue
  1.2375 +		CDriveThread* driveThread=NULL;
  1.2376 +		TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &driveThread);
  1.2377 +		if(r == KErrNone && driveThread != NULL)
  1.2378 +			aFileCache->iClosedTimer.Start(driveThread, aFileCache->iClosedFileKeepAliveTime);
  1.2379 +		}
  1.2380 +	}
  1.2381 +
  1.2382 +
  1.2383 +
  1.2384 +// Remove a closed file from closed file container so that it can be re-opened
  1.2385 +void TClosedFileUtils::ReOpen(CFileCache* aFileCache, TBool aLock)
  1.2386 +	{
  1.2387 +	// get the drive thread in case it has changed since the file was last open
  1.2388 +	const TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &aFileCache->iDriveThread);
  1.2389 +	if ((r == KErrNone) && aFileCache->iDriveThread)
  1.2390 +		aFileCache->iClosedTimer.Stop();
  1.2391 +
  1.2392 +	iClosedFiles->Remove(aFileCache, aLock);
  1.2393 +	}
  1.2394 +
  1.2395 +// Remove all closed files from closed file container and close them for good
  1.2396 +void TClosedFileUtils::Remove()
  1.2397 +	{
  1.2398 +	RemoveFiles(NULL, NULL);
  1.2399 +	}
  1.2400 +
  1.2401 +// Remove all closed files belonging to a particular TDrive from closed file container and close them for good
  1.2402 +void TClosedFileUtils::Remove(TInt aDrvNumber)
  1.2403 +	{
  1.2404 +	RemoveFiles(&TClosedFileUtils::TestDrive, (TAny*) aDrvNumber);
  1.2405 +	}
  1.2406 +
  1.2407 +// Remove a closed file from closed file container and close it for good
  1.2408 +void TClosedFileUtils::Remove(CFileCache* aFileCache)
  1.2409 +	{
  1.2410 +	RemoveFiles(&TClosedFileUtils::TestFile, (TAny*) aFileCache);
  1.2411 +	}
  1.2412 +
  1.2413 +void TClosedFileUtils::RemoveFiles(TTestFunc aTestFunc, TAny* aTestVal)
  1.2414 +	{
  1.2415 +	Lock();
  1.2416 +
  1.2417 +	TInt count = TClosedFileUtils::Count();
  1.2418 +	while(count--)
  1.2419 +		{
  1.2420 +		CFileCache& file = *(CFileCache*)(*iClosedFiles)[count];
  1.2421 +		if ((aTestFunc == NULL) || ((*aTestFunc)(file, aTestVal)))
  1.2422 +			{
  1.2423 +			iClosedFiles->Remove(&file, EFalse);
  1.2424 +			file.Close();
  1.2425 +			}
  1.2426 +		}
  1.2427 +
  1.2428 +	Unlock();
  1.2429 +	}
  1.2430 +
  1.2431 +
  1.2432 +TBool TClosedFileUtils::TestDrive(CFileCache& aFileCache, TAny* aVal)
  1.2433 +	{
  1.2434 +	TBool r = (aFileCache.iDriveNum == (TInt) aVal)?(TBool) ETrue: (TBool) EFalse;
  1.2435 +	if (r)
  1.2436 +		{
  1.2437 +		__CACHE_PRINT1(_L("CLOSEDFILES: TestDrive() closing %S\n"), &aFileCache.FileNameF() );
  1.2438 +		}
  1.2439 +	return r;
  1.2440 +	}
  1.2441 +TBool TClosedFileUtils::TestFile(CFileCache& aFileCache, TAny* aVal)
  1.2442 +	{
  1.2443 +	TBool r = (&aFileCache == ((CFileCache*) aVal))?(TBool)ETrue:(TBool)EFalse;
  1.2444 +	if (r)
  1.2445 +		{
  1.2446 +		__CACHE_PRINT1(_L("CLOSEDFILES: TestFile() closing %S\n"), &aFileCache.FileNameF() );
  1.2447 +		}
  1.2448 +	return r;
  1.2449 +	}
  1.2450 +
  1.2451 +	
  1.2452 +	
  1.2453 +void TClosedFileUtils::Lock()
  1.2454 +	{
  1.2455 +	iClosedFiles->Lock();
  1.2456 +	}
  1.2457 +
  1.2458 +void TClosedFileUtils::Unlock()
  1.2459 +	{
  1.2460 +	iClosedFiles->Unlock();
  1.2461 +	}
  1.2462 +	
  1.2463 +