userlibandfileserver/fileserver/sfile/sf_notify.cpp
changeset 0 a41df078684a
child 62 4a8fed1c0ef6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_notify.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,966 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfile\sf_notif.cpp
+// 
+//
+
+#include "sf_std.h"
+
+
+
+TChangeQue FsNotify::iChangeQues[KMaxNotifyQues];
+TDiskSpaceQue FsNotify::iDiskSpaceQues[KMaxDiskQues];
+TDebugQue FsNotify::iDebugQue;
+TDismountNotifyQue FsNotify::iDismountNotifyQue;
+
+void CNotifyInfo::Initialise(TInfoType aType,TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession)
+//
+//
+//
+	{
+	iType=aType;
+	iStatus=aStatus;
+	iMessage=aMessage;
+	iSession=aSession;
+	};
+
+CNotifyInfo::~CNotifyInfo()
+//
+//
+//
+	{
+	__ASSERT_DEBUG(!iLink.iNext,Fault(ENotifyInfoDestructor));
+	}
+
+void CNotifyInfo::Complete(TInt aError)
+//
+//
+//
+	{
+	__PRINT2(_L("CNotifyInfo::Complete 0x%x error=%d"),this,aError);
+	if (iType != EDismount || !iMessage.IsNull())	// Dismount notifiers may be completed but remain in the list
+		{											// until handled by the client or the session is closed.
+		iMessage.Complete(aError);
+		}
+	}
+
+
+void CStdChangeInfo::Initialise(TNotifyType aChangeType,TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession)
+//
+//
+//
+	{
+	iChangeType=aChangeType;
+	CNotifyInfo::Initialise(EStdChange,aStatus,aMessage,aSession);
+	}
+
+TUint CStdChangeInfo::RequestNotifyType(CFsRequest* aRequest)
+//
+// return notification type for the request
+//
+	{
+	TUint notifyType=aRequest->Operation()->NotifyType();
+	if(aRequest->Operation()->Function()==EFsRename)
+		{
+		__ASSERT_DEBUG(notifyType==(ENotifyDir|ENotifyFile|ENotifyEntry),Fault(EStdChangeRequestType));
+		if(aRequest->Src().NamePresent())
+			notifyType=ENotifyFile|ENotifyEntry;
+		else
+			notifyType=ENotifyDir|ENotifyEntry;
+		}
+	return(notifyType);						
+	}
+
+TBool CStdChangeInfo::IsMatching(CFsRequest* aRequest)
+//
+// return ETrue if operation type of request matches that of change notification
+//
+	{
+	if((iChangeType&ENotifyAll) || (iChangeType&aRequest->Operation()->NotifyType()))
+		return(ETrue);
+	else
+		return(EFalse);
+	}
+
+void CExtChangeInfo::Initialise(TNotifyType aChangeType,TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession,const TDesC& aName)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aName.Length()<=KMaxFileName-2,Fault(EExtChangeNameLength));
+	iName=aName;
+	iChangeType=aChangeType;
+	CNotifyInfo::Initialise(EExtChange,aStatus,aMessage,aSession);
+	}
+
+
+TBool CExtChangeInfo::IsMatching(CFsRequest* aRequest)
+//
+// return ETrue if operation notify type of request matches that of change notification
+// and paths match
+//
+	{
+	TInt function=aRequest->Operation()->Function();
+	//	if a rename occurred inform any requests if their path has been changed regardless of the notification type
+	if(function==EFsRename)				
+		{		
+		TBuf<KMaxFileName> renamePath=aRequest->Src().FullName().Mid(2);		
+		renamePath+=_L("*");
+		if (iName.MatchF(renamePath)!=KErrNotFound)	
+			return(ETrue);
+		}
+
+
+	//Special case where the dir the notifier is setup on has just been created
+	if(function==EFsMkDir)	
+		{		
+		TInt notDrive;
+		RFs::CharToDrive(aRequest->Src().Drive()[0],notDrive);	//can not fail as the drive letter has been parsed already
+		if(aRequest->Src().Path().MatchF(iName) == 0 && aRequest->DriveNumber() == notDrive)
+			return ETrue;
+		}
+	
+	//Special case where  the File the notifier is setup on has just been created by temp as the name is not known unti it has been created
+	if(function==EFsRename||function==EFsFileOpen||function==EFsFileCreate||function==EFsFileReplace)
+		{
+		TInt notDrive;
+		RFs::CharToDrive(aRequest->Src().Drive()[0],notDrive);	//can not fail as the drive letter has been parsed already
+		if(aRequest->Src().FullName().Mid(2).MatchF(iName) == 0 && aRequest->DriveNumber() == notDrive)
+			return ETrue;
+		}
+	
+	//For the case of a file created using EFsFileTemp we can probably ignore it for special cases as it 
+	//is created with a random name. Not specifically going to be being looked for
+
+	if((iChangeType&ENotifyAll) || (iChangeType&RequestNotifyType(aRequest)))
+		{
+		switch (function)
+			{	
+		//	Notify interested requests if a SetDriveName(), SetVolume() or RawDiskWrite() operation
+		//	occcurred.  By their nature, these operations have no distinct path.  All outstanding
+		//	requests monitoring the relevant TNotifyType are potentially interested in such operations	
+			case EFsFileWrite:
+			case EFsFileWriteDirty:
+			case EFsFileSet:
+			case EFsFileSetAtt:
+			case EFsFileSetModified:
+			case EFsFileSetSize:
+			{
+			TBuf<KMaxFileName> root=iName;
+			root+=_L("*");	
+			
+			// NB share may be NULL if file server has initiated a flush of the file cache
+			CFileShare* share;
+			CFileCB* fileCache;
+			GetFileFromScratch(aRequest, share, fileCache);
+			if (share && share->File().FileName().MatchF(root) != KErrNotFound)
+				return(ETrue);
+
+			}
+			break;
+			case EFsSetDriveName:
+			case EFsSetVolume:
+			case EFsRawDiskWrite:
+			case EFsLockDrive:
+			case EFsUnlockDrive:
+			case EFsReserveDriveSpace:
+				{
+				return(ETrue);				
+				}
+			
+			default:
+				{	
+				TBuf<KMaxFileName> root = iName;
+				root+=_L("*");	
+				
+				if(aRequest->Src().FullName().Mid(2).MatchF(root)!=KErrNotFound)
+					return(ETrue);	
+				else if(function==EFsRename||function==EFsReplace||function==EFsFileRename)
+					{
+					// - rename/replace causes the file/path to disappear
+					if(aRequest->Dest().FullName().Mid(2).MatchF(root)!=KErrNotFound)
+						{
+						return(ETrue);
+						}
+
+					// - rename/replace causes the file/path to arrive
+					root=aRequest->Dest().FullName().Mid(2);
+					root+=_L("*");
+
+					if (iName.MatchF(root)!=KErrNotFound)
+						{
+						return(ETrue);
+						}
+					}
+				}			
+			}
+		}
+	return(EFalse);
+	}
+
+
+void CDiskSpaceInfo::Initialise(TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession,TInt64 aThreshold)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aThreshold>0,Fault(EDiskSpaceThreshold));
+	iThreshold=aThreshold;
+	CNotifyInfo::Initialise(EDiskSpace,aStatus,aMessage,aSession);
+	}
+
+TBool CDiskSpaceInfo::IsMatching(TInt64& aBefore,TInt64& aAfter)
+//
+// return ETrue if the threshold has been crossed
+//
+	{
+	if((aBefore>=iThreshold&&aAfter<iThreshold)||(aBefore<=iThreshold&&aAfter>iThreshold))
+		return(ETrue);
+	return(EFalse);
+	}
+
+void CDebugChangeInfo::Initialise(TUint aDebugType,TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession)
+//
+//
+//
+	{
+	__ASSERT_DEBUG((aDebugType&KDebugNotifyMask)&&!(aDebugType&~KDebugNotifyMask),Fault(EDebugChangeType));
+	iDebugType=aDebugType;
+	CNotifyInfo::Initialise(EDebugChange,aStatus,aMessage,aSession);
+	}
+
+TBool CDebugChangeInfo::IsMatching(TUint aFunction)
+//
+// return ETrue if debug notification type matches aFunction
+//
+	{
+	if(iDebugType&aFunction)
+		return(ETrue);
+	return(EFalse);
+	}
+
+
+CDismountNotifyInfo::~CDismountNotifyInfo()
+	{
+	switch(iMode)
+		{
+		case EFsDismountNotifyClients:
+			break;
+		case EFsDismountRegisterClient:
+			__ASSERT_ALWAYS(TheDrives[iDriveNumber].DismountUnlock() >= 0, Fault(ENotifyDismountCancel));
+			break;
+		default:
+			break;
+		}
+	}
+
+void CDismountNotifyInfo::Initialise(TNotifyDismountMode aMode, TInt aDriveNumber, TRequestStatus* aStatus,const RMessagePtr2& aMessage,CSessionFs* aSession)
+	{
+	iMode = aMode;
+	iDriveNumber=aDriveNumber;
+	CNotifyInfo::Initialise(EDismount,aStatus,aMessage,aSession);
+
+	if (iMode == EFsDismountRegisterClient)
+		TheDrives[iDriveNumber].DismountLock();
+	}
+
+TBool CDismountNotifyInfo::IsMatching(TNotifyDismountMode aMode, TInt aDriveNumber, CSessionFs* aSession)
+	{
+	if((iDriveNumber == aDriveNumber) && (iMode == aMode) && (aSession == NULL || aSession == Session()))
+		return(ETrue);
+	return(EFalse);
+	}
+
+TBaseQue::TBaseQue()
+//
+//
+//
+	{
+	iHeader.SetOffset(_FOFF(CNotifyInfo,iLink));
+	TInt r=iQLock.CreateLocal();
+	__ASSERT_ALWAYS(r==KErrNone,Fault(EBaseQueConstruction));
+	}
+
+TBaseQue::~TBaseQue()
+//
+//
+//
+	{
+	iQLock.Close();
+	}
+
+void TBaseQue::DoAddNotify(CNotifyInfo* aInfo)
+//
+// Add notification
+// Que should be locked by calling function
+//
+	{
+	iHeader.AddLast(*aInfo);
+	}
+
+TBool TBaseQue::DoCancelSession(CSessionFs* aSession,TInt aCompletionCode, TRequestStatus* aStatus)
+//
+// Cancel notification(s) setup by aSession matching aStatus if set
+// Que should be locked by calling function
+//
+	{
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	TBool isFound=EFalse;
+	while((info=q++)!=NULL)
+		{
+		if(info->Session()==aSession && (!aStatus || aStatus==info->Status()))
+			{
+			isFound=ETrue;
+			info->Complete(aCompletionCode);
+			info->iLink.Deque();
+			delete(info);
+			if(aStatus)
+				break;
+			}
+		}
+	return(isFound);
+	}
+
+void TBaseQue::DoCancelAll(TInt aCompletionCode)
+//
+// Cancel all notifications
+// Que should be locked by calling function
+//
+	{
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		info->Complete(aCompletionCode);
+		info->iLink.Deque();
+		delete(info);
+		}
+	__ASSERT_DEBUG(iHeader.IsEmpty(),Fault(EBaseQueCancel));
+	}
+
+TBool TBaseQue::IsEmpty()
+//
+// Que should be locked by calling function
+//
+	{
+	return iHeader.IsEmpty();
+	}
+
+TBool TChangeQue::IsEmpty()
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBool empty = TBaseQue::IsEmpty();
+	iQLock.Signal();
+	return(empty);
+	}
+	
+TInt TChangeQue::AddNotify(CNotifyInfo* aInfo)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoAddNotify(aInfo);
+	iQLock.Signal();
+	return(KErrNone);
+	}
+
+TBool TChangeQue::CancelSession(CSessionFs* aSession,TInt aCompletionCode,TRequestStatus* aStatus)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBool isFound=TBaseQue::DoCancelSession(aSession,aCompletionCode,aStatus);
+	iQLock.Signal();
+	return(isFound);
+	}
+
+void TChangeQue::CancelAll(TInt aCompletionCode)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoCancelAll(aCompletionCode);
+	iQLock.Signal();
+	}
+
+void TChangeQue::CheckChange(CFsRequest* aRequest)
+//
+// complete any notification in que that matches aRequest
+//
+	{
+	iQLock.Wait();
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		__ASSERT_DEBUG(info->Type()==CNotifyInfo::EStdChange||info->Type()==CNotifyInfo::EExtChange,Fault(EChangeQueType));
+		TBool isMatching;
+		if(info->Type()==CNotifyInfo::EStdChange)
+			isMatching=((CStdChangeInfo*)info)->IsMatching(aRequest);
+		else
+			isMatching=((CExtChangeInfo*)info)->IsMatching(aRequest);
+		if(isMatching)
+			{
+			__PRINT1(_L("TChangeQue::CheckChange()-Matching info=0x%x"),info);
+			info->Complete(KErrNone);
+			info->iLink.Deque();
+			delete(info);
+			}
+		}
+	iQLock.Signal();
+	}
+
+TBool TDiskSpaceQue::IsEmpty()
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBool empty = TBaseQue::IsEmpty();
+	iQLock.Signal();
+	return(empty);
+	}
+
+TInt TDiskSpaceQue::AddNotify(CNotifyInfo* aInfo)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TInt r=KErrNone;
+	if(iHeader.IsEmpty())
+		{
+		r=GetFreeDiskSpace(iFreeDiskSpace);
+		iReservedDiskSpace = TheDrives[iDriveNumber].ReservedSpace();
+		}
+	if(r==KErrNone)
+		TBaseQue::DoAddNotify(aInfo);
+	iQLock.Signal();
+	return(r);
+	}
+
+TInt TDiskSpaceQue::CancelSession(CSessionFs* aSession,TInt aCompletionCode,TRequestStatus* aStatus)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoCancelSession(aSession,aCompletionCode,aStatus);
+	iQLock.Signal();
+	return(KErrNone);
+	}
+
+void TDiskSpaceQue::CancelAll(TInt aCompletionCode)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoCancelAll(aCompletionCode);
+	iQLock.Signal();
+	}
+
+
+void TDiskSpaceQue::CheckDiskSpace()
+//
+// Complete any disk space notification whose threshold has been crossed
+//
+	{
+	iQLock.Wait();
+	if(iHeader.IsEmpty())
+		{
+		iQLock.Signal();
+		return;
+		}
+	TInt64 freeSpace;
+	TInt r=GetFreeDiskSpace(freeSpace);
+	TInt64 reservedSpace(TheDrives[iDriveNumber].ReservedSpace());
+	if(r==KErrNone)
+		{
+		if((freeSpace==iFreeDiskSpace) && (reservedSpace==iReservedDiskSpace))
+			{
+			iQLock.Signal();
+			return;
+			}
+		TDblQueIter<CNotifyInfo> q(iHeader);
+		CNotifyInfo* info;
+		while((info=q++)!=NULL)
+			{
+			__ASSERT_DEBUG(info->Type()==CNotifyInfo::EDiskSpace,Fault(EDiskSpaceQueType1));
+
+			TInt64 newSessionFreeSpace(freeSpace);
+			TInt64 oldSessionFreeSpace(iFreeDiskSpace);
+			if(!info->Session()->ReservedAccess(iDriveNumber))
+				{
+				newSessionFreeSpace -= reservedSpace;
+				oldSessionFreeSpace -= iReservedDiskSpace;
+				}
+
+			if(((CDiskSpaceInfo*)info)->IsMatching(oldSessionFreeSpace,newSessionFreeSpace))
+				{
+				__PRINT1(_L("TDiskSpaceQue::CheckDiskSpace()-Matching info=0x%x"),info);
+				info->Complete(KErrNone);
+				info->iLink.Deque();
+				delete(info);
+				}
+			}
+		iFreeDiskSpace=freeSpace;
+		iReservedDiskSpace=reservedSpace;
+		}
+	else
+		TBaseQue::DoCancelAll(KErrNone);
+	iQLock.Signal();
+	}
+
+void TDiskSpaceQue::CheckDiskSpace(TInt64& aFreeDiskSpace)
+//
+//
+//
+	{
+	iQLock.Wait();
+	if(iHeader.IsEmpty())
+		{
+		iQLock.Signal();
+		return;
+		}
+
+	TInt64 reservedSpace(TheDrives[iDriveNumber].ReservedSpace());
+
+	if((aFreeDiskSpace==iFreeDiskSpace) && (reservedSpace==iReservedDiskSpace))
+		{
+		iQLock.Signal();
+		return;
+		}
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		__ASSERT_DEBUG(info->Type()==CNotifyInfo::EDiskSpace,Fault(EDiskSpaceQueType2));
+
+		TInt64 newSessionFreeSpace(aFreeDiskSpace);
+		TInt64 oldSessionFreeSpace(iFreeDiskSpace);
+		if(!info->Session()->ReservedAccess(iDriveNumber))
+			{
+			newSessionFreeSpace -= reservedSpace;
+			oldSessionFreeSpace -= iReservedDiskSpace;
+			}
+
+		if(((CDiskSpaceInfo*)info)->IsMatching(oldSessionFreeSpace,newSessionFreeSpace))
+			{
+			__PRINT1(_L("TDiskSpaceQue::CheckDiskSpace()-Matching info=0x%x"),info);
+			info->Complete(KErrNone);
+			info->iLink.Deque();
+			delete(info);
+			}
+		}
+	iFreeDiskSpace=aFreeDiskSpace;
+	iReservedDiskSpace=reservedSpace;
+	iQLock.Signal();
+	}
+
+TInt TDiskSpaceQue::GetFreeDiskSpace(TInt64& aFreeDiskSpace)
+//
+// 
+//
+	{
+	__ASSERT_DEBUG(iDriveNumber>=EDriveA&&iDriveNumber<=EDriveZ,Fault(EDiskSpaceQueDrive));
+	__CHECK_DRIVETHREAD(iDriveNumber);
+	TInt r=TheDrives[iDriveNumber].FreeDiskSpace(aFreeDiskSpace);
+	return(r);
+	}
+
+TInt TDebugQue::AddNotify(CNotifyInfo* aInfo)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoAddNotify(aInfo);
+	iQLock.Signal();
+	return(KErrNone);
+	}
+
+TInt TDebugQue::CancelSession(CSessionFs* aSession,TInt aCompletionCode,TRequestStatus* aStatus)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBool isFound=TBaseQue::DoCancelSession(aSession,aCompletionCode,aStatus);
+	iQLock.Signal();
+	return(isFound);
+	}
+
+void TDebugQue::CancelAll(TInt aCompletionCode)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoCancelAll(aCompletionCode);
+	iQLock.Signal();
+	}
+
+void TDebugQue::CheckDebug(TUint aDebugChange)
+//
+// Complete any debug notification whose debug type matches aDebugChange
+//
+	{
+	iQLock.Wait();
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		__ASSERT_DEBUG(info->Type()==CNotifyInfo::EDebugChange,Fault(EDebugQueType));
+		if(((CDebugChangeInfo*)info)->IsMatching(aDebugChange))
+			{
+			__PRINT1(_L("TDebugQue::CheckDebug()-Matching info=0x%x"),info);
+			info->Complete(KErrNone);
+			info->iLink.Deque();
+			delete(info);
+			}
+		}
+	iQLock.Signal();
+	}
+
+TInt TDismountNotifyQue::AddNotify(CNotifyInfo* aInfo)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoAddNotify(aInfo);
+	iQLock.Signal();
+	return(KErrNone);
+	}
+
+TInt TDismountNotifyQue::CancelSession(CSessionFs* aSession,TInt aCompletionCode,TRequestStatus* aStatus)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBool isFound=TBaseQue::DoCancelSession(aSession,aCompletionCode,aStatus);
+	iQLock.Signal();
+	return(isFound);
+	}
+
+void TDismountNotifyQue::CancelAll(TInt aCompletionCode)
+//
+//
+//
+	{
+	iQLock.Wait();
+	TBaseQue::DoCancelAll(aCompletionCode);
+	iQLock.Signal();
+	}
+
+void TDismountNotifyQue::CheckDismount(TNotifyDismountMode aMode, TInt aDrive, TBool aRemove, TInt aError)
+//
+// Complete any dismount notifications on the specified drive.
+//
+	{
+	iQLock.Wait();
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		__ASSERT_DEBUG(info->Type()==CNotifyInfo::EDismount,Fault(EBadDismountNotifyType));
+		if(((CDismountNotifyInfo*)info)->IsMatching(aMode, aDrive, NULL))
+			{
+			__PRINT1(_L("TDismountNotifyQue::CheckDismount()-Matching info=0x%x"),info);
+			info->Complete(aError);
+			if(aRemove)
+				{
+				info->iLink.Deque();
+				delete(info);
+				}
+			}
+		}
+
+	__ASSERT_ALWAYS(!aRemove || TheDrives[aDrive].DismountLocked() == 0, Fault(EDismountLocked));
+
+	iQLock.Signal();
+	}
+
+TBool TDismountNotifyQue::HandlePendingDismount(CSessionFs* aSession, TInt aDrive)
+//
+// Determine if the session has any outstanding dismount notifications on the specified drive.
+//
+	{
+	iQLock.Wait();
+	TDblQueIter<CNotifyInfo> q(iHeader);
+	CNotifyInfo* info;
+	while((info=q++)!=NULL)
+		{
+		__ASSERT_DEBUG(info->Type()==CNotifyInfo::EDismount,Fault(EBadDismountNotifyType));
+		if(((CDismountNotifyInfo*)info)->IsMatching(EFsDismountRegisterClient, aDrive, aSession))
+			{
+			__PRINT1(_L("TDismountNotifyQue::CheckDismount()-Pending info=0x%x"),info);
+			info->iLink.Deque();
+			delete(info);
+			iQLock.Signal();
+			return ETrue;
+			}
+		}
+	iQLock.Signal();
+	return EFalse;
+	}
+
+void FsNotify::Initialise()
+//
+//
+//
+	{
+	for(TInt i=0;i<KMaxDiskQues;++i)
+		{
+		iDiskSpaceQues[i].SetDriveNumber(i);
+		}
+	}
+
+TBool FsNotify::IsChangeQueEmpty(TInt aDrive)
+//
+//
+//
+	{
+	if((iChangeQues[ChangeIndex(aDrive)].IsEmpty()) && (iChangeQues[ChangeIndex(KDriveInvalid)].IsEmpty()))
+		return ETrue;
+
+	return EFalse;
+	}
+
+TInt FsNotify::AddChange(CNotifyInfo* aInfo,TInt aDrive)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aInfo->Type()==CNotifyInfo::EStdChange||aInfo->Type()==CNotifyInfo::EExtChange,Fault(EBadChangeNotifyType));
+	__PRINT2(_L("FsNotify::AddChange() drive=%d,info=0x%x"),aDrive,aInfo);
+	iChangeQues[ChangeIndex(aDrive)].AddNotify(aInfo);
+	return(KErrNone);
+	}
+
+TBool FsNotify::IsDiskSpaceQueEmpty(TInt aDrive)
+//
+//
+//
+	{
+	if(iDiskSpaceQues[aDrive].IsEmpty())
+		return ETrue;
+
+	return EFalse;
+	}
+
+TInt FsNotify::AddDiskSpace(CNotifyInfo* aInfo,TInt aDrive)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aInfo->Type()==CNotifyInfo::EDiskSpace,Fault(EBadDiskNotifyType));
+	__ASSERT_DEBUG((aDrive>=EDriveA && aDrive<=EDriveZ),Fault(EDiskBadIndex1));
+	__PRINT2(_L("FsNotify::AddDiskSpace() drive=%d,info=0x%x"),aDrive,aInfo);
+	return(iDiskSpaceQues[aDrive].AddNotify(aInfo));
+	}
+
+TInt FsNotify::AddDebug(CNotifyInfo* aDebugInfo)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aDebugInfo->Type()==CNotifyInfo::EDebugChange,Fault(EBadDebugNotifyType));
+	__PRINT1(_L("FsNotify::AddDebug() info=0x%x"),aDebugInfo);
+	iDebugQue.AddNotify(aDebugInfo);
+	return(KErrNone);
+	}
+
+TInt FsNotify::AddDismountNotify(CNotifyInfo* aDismountNotifyInfo)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(aDismountNotifyInfo->Type()==CNotifyInfo::EDismount,Fault(EBadDismountNotifyType));
+	__PRINT1(_L("FsNotify::AddDismountNotify() info=0x%x"),aDismountNotifyInfo);
+	iDismountNotifyQue.AddNotify(aDismountNotifyInfo);
+	return(KErrNone);
+	}
+
+void FsNotify::HandleChange(CFsRequest* aRequest,TInt aDrive)
+//
+// Check whether any change notifications need to be completed due to aRequest on aDrive
+//
+	{
+	__PRINT2(_L("FsNotify::HandleChange() aRequest=0x%x, aDrive=%d"),aRequest,aDrive);
+	if(!aRequest->IsChangeNotify())
+		return;
+	iChangeQues[ChangeIndex(aDrive)].CheckChange(aRequest);
+	iChangeQues[ChangeIndex(KDriveInvalid)].CheckChange(aRequest);
+	}
+	
+
+void FsNotify::HandleDiskSpace(CFsRequest* aRequest,TInt aDrive)
+//
+// Check whether any disk space notifications need to be completed due to aRequest on aDrive
+//
+	{
+	__ASSERT_DEBUG((aDrive>=EDriveA && aDrive<=EDriveZ) || aDrive==KDriveInvalid,Fault(EDiskBadIndex2));
+	__PRINT2(_L("FsNotify::HandleDiskSpace() aRequest=0x%x, aDrive=%d"),aRequest,aDrive);
+	
+	if(!aRequest->Operation()->IsDiskSpaceNotify())
+		return;
+	TInt f = aRequest->Operation()->Function();
+	if ((f == EFsFileWrite || f == EFsFileWriteDirty) && !((CFsClientMessageRequest*)aRequest)->IsFreeChanged())
+		return;
+	if (FsThreadManager::IsDriveThread(aDrive,EFalse))	
+		iDiskSpaceQues[aDrive].CheckDiskSpace();
+	}
+
+void FsNotify::HandleDiskSpace(TInt aDrive, TInt64& aFreeSpace)
+//
+//
+//
+	{
+	__ASSERT_DEBUG((aDrive>=EDriveA && aDrive<=EDriveZ),Fault(EDiskBadIndex3));
+	__PRINT1(_L("FsNotify::HandleDiskSpace() aDrive=%d"),aDrive);
+	iDiskSpaceQues[aDrive].CheckDiskSpace(aFreeSpace);
+	}
+
+void FsNotify::HandleDebug(TUint aFunction)
+//
+// Check whether any debug notifications need to be completed due to aFunction
+//
+	{
+	__PRINT1(_L("FsNotify::HandleDebug() aFunction=0x%x"),aFunction);
+	if(!(aFunction&KDebugNotifyMask))
+		return;
+	iDebugQue.CheckDebug(aFunction);
+	}
+
+void FsNotify::HandleDismount(TNotifyDismountMode aMode, TInt aDrive, TBool aRemove, TInt err)
+//
+// Handle dismount notifications for the given drive
+//
+	{
+	__PRINT4(_L("FsNotify::HandleDismount() aMode = %d, aDrive=%d, aRemove=%d, err=%d"),aMode,aDrive,aRemove,err);
+	iDismountNotifyQue.CheckDismount(aMode, aDrive, aRemove, err);
+	}
+
+TBool FsNotify::HandlePendingDismount(CSessionFs* aSession, TInt aDrive)
+//
+// Checks if the session has an outstanding notification registered on the drive
+//
+	{
+	__PRINT1(_L("FsNotify::HandlePendingDismount() aDrive=%d"),aDrive);
+	return iDismountNotifyQue.HandlePendingDismount(aSession, aDrive);
+	}
+
+void FsNotify::DiskChange(TInt aDrive)
+//
+// Complete all notifications in queus due to a disk change
+//
+	{
+	__ASSERT_DEBUG((aDrive>=EDriveA && aDrive<=EDriveZ),Fault(EDiskChangeDrive));
+	__PRINT1(_L("FsNotify::DiskChange() aDrive=%d"),aDrive);
+	iChangeQues[ChangeIndex(aDrive)].CancelAll(KErrNone);
+	iChangeQues[ChangeIndex(KDriveInvalid)].CancelAll(KErrNone);
+	iDiskSpaceQues[aDrive].CancelAll(KErrNone);
+	iDebugQue.CancelAll(KErrNone);
+
+	// if there are any files containing dirty data, start issuing write-dirty data requests to trigger
+	// a critical notifier (CFileCache::HandleWriteDirtyError())
+	// otherwise purge all file caches
+	TDrive& drive=TheDrives[aDrive];
+	drive.FlushCachedFileInfo(ETrue);	
+	}
+
+	
+void FsNotify::CancelChangeSession(CSessionFs* aSession,TRequestStatus* aStatus)
+//
+//	Cancel change notifcation(s) setup by aSession and matching aStatus if not NULL
+//
+	{
+	__PRINT2(_L("FsNotify::CancelChangeSession() aSession=0x%x aStatus=0x%x"),aSession,aStatus);
+	for(TInt i=0;i<KMaxNotifyQues;++i)
+		{
+		TBool isFound=iChangeQues[i].CancelSession(aSession,KErrCancel,aStatus);
+		if(aStatus && isFound)
+			break;
+		}
+	}
+
+void FsNotify::CancelDiskSpaceSession(CSessionFs* aSession,TRequestStatus* aStatus)
+//
+// Cancel disk space notification(s) setup by aSession and matching aStatus if not NULL
+//
+
+	{
+	__PRINT2(_L("FsNotify::CancelDiskSpaceSession() aSession=0x%x aStatus=0x%x"),aSession,aStatus);
+	for(TInt i=0;i<KMaxDiskQues;++i)
+		{
+		TBool isFound=iDiskSpaceQues[i].CancelSession(aSession,KErrCancel,aStatus);
+		if(aStatus && isFound)
+			break;
+		}
+	}
+
+void FsNotify::CancelDebugSession(CSessionFs* aSession, TRequestStatus* aStatus)
+//
+// Cancel debug notification(s) setup by aSession and matching aStatus if not NULL
+//
+	{
+	__PRINT2(_L("FsNotify::CancelDebugSession() aSession=0x%x aStatus=0x%x"),aSession,aStatus);
+	iDebugQue.CancelSession(aSession,KErrCancel,aStatus);
+	}
+
+void FsNotify::CancelDismountNotifySession(CSessionFs* aSession, TRequestStatus* aStatus)
+//
+// Cancel all media removal notification(s) setup by aSession (if aStatus == NULL)
+// else cancels all oustanding notifications(s) for the session
+//
+	{
+	__PRINT2(_L("FsNotify::CancelDismountNotifySession() aSession=0x%x aStatus=0x%x"),aSession,aStatus);
+	iDismountNotifyQue.CancelSession(aSession,KErrCancel,aStatus);
+	}
+
+void FsNotify::CancelSession(CSessionFs* aSession)
+//
+//
+//
+	{
+	__PRINT(_L("FsNotify::CancelSession"));
+	FsNotify::CancelChangeSession(aSession);
+	FsNotify::CancelDiskSpaceSession(aSession);
+	FsNotify::CancelDebugSession(aSession);
+	FsNotify::CancelDismountNotifySession(aSession);
+	}
+
+
+TInt FsNotify::ChangeIndex(TInt aDrive)
+//
+//
+//
+	{
+	__ASSERT_DEBUG((aDrive>=EDriveA && aDrive<=EDriveZ) || aDrive==KDriveInvalid,Fault(EChangeBadIndex));
+	if(aDrive==KDriveInvalid)
+		return(0);
+	else
+		return(aDrive+1);
+	}
+