--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_notifier.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1225 @@
+// Copyright (c) 2008-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_notifier.cpp
+//
+//
+
+#include "sf_notifier.h"
+#include "sf_file_cache.h"
+
+CFsObjectCon* FsNotificationManager::iNotifyRequests = NULL;
+RFastLock FsNotificationManager::iChainLock;
+TInt FsNotificationManager::iFilterRegister[];
+CFsPool<CFsNotificationBlock>* FsNotificationManager::iPool;
+
+
+CFsNotificationPathFilter* CFsNotificationPathFilter::NewL(const TDesC& aPath, const TDesC& aFilename)
+ {
+ CFsNotificationPathFilter* self = new (ELeave) CFsNotificationPathFilter();
+ CleanupStack::PushL(self);
+ self->ConstructL(aPath,aFilename);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+void CFsNotificationPathFilter::ConstructL(const TDesC& aPath, const TDesC& aFilename)
+ {
+ //Allocate the path and filename
+ iPath = aPath.AllocL();
+ iFilename = aFilename.AllocL();
+ }
+
+CFsNotificationPathFilter::~CFsNotificationPathFilter()
+ {
+ if(iFilename)
+ delete iFilename;
+ if(iPath)
+ delete iPath;
+ }
+
+CFsNotificationPathFilter::CFsNotificationPathFilter()
+: iPath(NULL), iFilename(NULL)
+ {
+ }
+
+CFsNotifyRequest* CFsNotifyRequest::NewL()
+ {
+ CFsNotifyRequest* self = new(ELeave) CFsNotifyRequest();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CFsNotifyRequest::ConstructL()
+ {
+ User::LeaveIfError(iClientSyncLock.CreateLocal());
+ User::LeaveIfError(iTailSemaphore.CreateLocal());
+ }
+
+CFsNotifyRequest::CFsNotifyRequest()
+ {
+ SetActive(EInactive);
+ }
+
+CFsNotifyRequest::~CFsNotifyRequest()
+ {
+ __PRINT(_L("CFsNotifyRequest::~CFsNotifyRequest()"));
+
+ RemoveFilters();
+
+ if(ClientMsgHandle()!=0)
+ iClientMsg.Complete(KErrCancel);
+
+ if(iBufferMsg.Handle()!=0)
+ iBufferMsg.Complete(KErrCancel);
+
+ iClientSyncLock.Close();
+ iTailSemaphore.Close();
+ }
+
+/*
+ * Returns the Array of TypeFilters.
+ * Each TFsNotificationTypeFilter matches to a particular TFsNotification::TFsNotificationType
+ * and has a CFsNotificationFilter which stores the iPath and iName associated with this filter type.
+ *
+ * (These are speerated so that we can have multiple type filters for every name filter)
+ */
+TFsNotificationTypeArray* CFsNotifyRequest::FilterTypeList(TInt aDrive,TInt aIndex)
+ {
+ __ASSERT_DEBUG(aIndex < KNumRegisterableFilters,Fault(ENotificationFault));
+
+ TFsNotificationTypeDriveArray* filters = iDrivesTypesFiltersMap.Find(aDrive);
+ if(filters)
+ return &((*filters)[aIndex]);
+ else
+ return NULL;
+ }
+
+//Sets filter's notification request status
+void CFsNotifyRequest::SetActive(TNotifyRequestStatus aValue)
+ {
+ iNotifyRequestStatus = aValue;
+ }
+
+CFsNotifyRequest::TNotifyRequestStatus CFsNotifyRequest::ActiveStatus()
+ {
+ return (TNotifyRequestStatus)iNotifyRequestStatus;
+ }
+
+//Completes and frees notification request
+//In case of KErrNone must be called with iChainLock already held
+void CFsNotifyRequest::CompleteClientRequest(TInt aReason,TBool aIsCancel)
+ {
+ __PRINT(_L("CFsNotifyRequest::CompleteClientRequest()"));
+
+ iClientSyncLock.Wait();
+
+ if(aReason==KErrNone)
+ {
+ __PRINT(_L("CFsNotifyRequest::CompleteClientRequest() - Complete KErrNone"));
+ //Synchronising the current iServerTail to the client.
+ iClientHead = iClientTail; //Client has read all previous entries
+ iClientTail = iServerTail; //Client's new tail is everything the server has been writing since this function was last called
+ TInt clientTail = iClientTail;
+ TPckg<TInt> tailDes(clientTail);
+ iClientMsg.Write(KMsgPtr0,tailDes);
+ }
+ else if(aIsCancel)
+ {
+ __PRINT(_L("CFsNotifyRequest::CompleteClientRequest() - Complete isCancel"));
+ iServerTail = 0;
+ iClientTail = 0;
+ iClientHead = 0;
+ TPckgBuf<TInt> tailDes(iClientTail);
+ //Perhaps client has crashed so no point checking return:
+ iClientMsg.Write(KMsgPtr0,tailDes);
+ }
+ __PRINT(_L("CFsNotifyRequest::CompleteClientRequest() - Complete Request"));
+ iClientMsg.Complete(aReason);
+ iClientSyncLock.Signal();
+ }
+
+TInt CFsNotifyRequest::SynchroniseBuffer(CFsNotificationBlock& aBlock,TInt aServerTail, TInt aNotificationSize)
+ {
+ TPtrC8 blockDes((TText8*)aBlock.Data(),aNotificationSize);
+ return iBufferMsg.Write(KMsgPtr0,blockDes,aServerTail);
+ }
+
+//Removes all filters.
+//Deletes iPath, iFilename
+TInt CFsNotifyRequest::RemoveFilters()
+ {
+ __PRINT(_L("CFsNotifyRequest::RemoveFilters()"));
+
+ //For every drive with filters set...
+ RHashMap<TInt,TFsNotificationTypeDriveArray>::TIter iterator(iDrivesTypesFiltersMap);
+ TFsNotificationTypeDriveArray* currentDriveFilters = (TFsNotificationTypeDriveArray*)iterator.NextValue();
+ while(currentDriveFilters)
+ {
+ //For every filter array (1 for each type of TFsNotificationType)
+ for(TInt filterType = 0; filterType < KNumRegisterableFilters; filterType++)
+ {
+ TFsNotificationTypeArray& filterList = (*currentDriveFilters)[filterType];
+ TInt filterTypeCount = filterList.Count();
+ if(filterTypeCount)
+ {
+ //Remove this type from the filter register
+ TFsNotification::TFsNotificationType type = FsNotificationHelper::NotificationType(filterType);
+ FsNotificationManager::SetFilterRegister(type,EFalse,filterTypeCount);
+ }
+ filterList.Reset();
+ filterList.Close();
+ }
+ currentDriveFilters->Reset();
+ currentDriveFilters->Close();
+ iterator.RemoveCurrent();
+ currentDriveFilters = (TFsNotificationTypeDriveArray*)iterator.NextValue();
+ }
+ iDrivesTypesFiltersMap.Close();
+ iPathFilterList.ResetAndDestroy();
+ iPathFilterList.Close();
+ return KErrNone;
+ }
+
+TInt CFsNotifyRequest::AddFilterL(CFsNotificationPathFilter* aFilter, TUint aMask)
+ {
+ __PRINT(_L("CFsNotifyRequest::AddFilterL"));
+
+ iPathFilterList.AppendL(aFilter);
+
+ //Get the drive number to so know which drive array to add the filter(s) to.
+ TInt driveNum = FsNotificationHelper::DriveNumber(aFilter->iPath->Des());
+
+ TInt notifyType = 1;
+ TInt r = KErrNone;
+ //Create/Add a TypeFilter for each type in aMask
+ while((notifyType & KNotificationValidFiltersMask) && (aMask & KNotificationValidFiltersMask))
+ {
+ //If this notifyType is present in aMask
+ if(aMask & notifyType)
+ {
+ TFsNotificationTypeFilter typeFilter;
+ typeFilter.iNotificationType = (TFsNotification::TFsNotificationType) notifyType;
+ typeFilter.iPathFilter = aFilter;
+ TInt index = FsNotificationHelper::TypeToIndex(typeFilter.iNotificationType);
+
+ //If the per-drive-filterLists have not
+ //been set up yet then do so now.
+ TFsNotificationTypeDriveArray* driveArray = iDrivesTypesFiltersMap.Find(driveNum);
+ if(!driveArray)
+ {
+ TFsNotificationTypeDriveArray dArray;
+ r = iDrivesTypesFiltersMap.Insert(driveNum,dArray);
+ User::LeaveIfError(r);
+ driveArray = iDrivesTypesFiltersMap.Find(driveNum);
+
+ //Create filter arrays for every type
+ for(TInt i =0; i< KNumRegisterableFilters; i++)
+ {
+ TFsNotificationTypeArray filterArray;
+ driveArray->Append(filterArray);
+ }
+ }
+ TFsNotificationTypeArray& filterArray= (*driveArray)[index];
+ filterArray.Append(typeFilter);
+
+ //Remove this type from our mask
+ //and continue
+ aMask ^= notifyType;
+ }
+ notifyType <<= 1;
+ }
+ return r;
+ }
+
+TInt CFsNotifyRequest::SetClientMessage(const RMessage2& aClientMsg)
+ {
+ __PRINT(_L("CFsNotifyRequest::SetClientMessage"));
+ iClientMsg = aClientMsg;
+ return KErrNone;
+ }
+
+TInt CFsNotifyRequest::ClientMsgHandle()
+ {
+ return iClientMsg.Handle();
+ }
+
+void CFsNotifyRequest::CloseNotification()
+ {
+ __PRINT(_L("CFsNotifyRequest::CloseNotification()"));
+ iBufferMsg.Complete(KErrNone);
+ if(ClientMsgHandle()!=0)
+ CompleteClientRequest(KErrCancel,EFalse);
+ }
+
+//New notification request from client
+void FsNotificationManager::AddNotificationRequestL(CFsNotifyRequest* aNotificationRequest)
+ {
+ __PRINT(_L("FsNotificationManager::AddNotificationRequestL"));
+ Lock();
+ iNotifyRequests->AddL(aNotificationRequest,ETrue);
+ Unlock();
+ }
+
+//Notification request cancelled
+//Must be called with iChainLock held
+void FsNotificationManager::RemoveNotificationRequest(CFsNotifyRequest* aNotificationRequest)
+ {
+ __PRINT(_L("FsNotificationManager::RemoveNotificationRequest"));
+ iNotifyRequests->Remove(aNotificationRequest,ETrue);
+ }
+
+void FsNotificationManager::RemoveNotificationRequest(CSessionFs* aSession)
+ {
+ __PRINT(_L("FsNotificationManager::RemoveNotificationRequest(CSessionFs*)"));
+
+ TInt count = Count();
+ if(count)
+ {
+ Lock();
+ count = Count(); //check again just incase it's changed before we got the lock
+ if(count)
+ {
+ for(TInt i=0; i < count; i++)
+ {
+ //Remove all notification requests associated with this session.
+ CFsNotifyRequest* notify = (CFsNotifyRequest*)(*iNotifyRequests)[i];
+ if(notify->iSession == aSession)
+ {
+ RemoveNotificationRequest(notify);
+ delete notify;
+ }
+ }
+ if(!Count())
+ {
+ __PRINT(_L("FsNotificationManager::RemoveNotificationRequest(CSessionFs*) - Closing Manager"));
+ Close();
+ }
+ }
+ Unlock();
+ }
+ }
+
+TBool FsNotificationManager::IsInitialised()
+ {
+ __PRINT(_L("FsNotificationManager::IsInitialised()"));
+ return (TBool)iNotifyRequests;
+ }
+
+void FsNotificationManager::OpenL()
+ {
+ __PRINT(_L("FsNotificationManager::InitialiseL()"));
+ if(!iNotifyRequests)
+ {
+ if(iChainLock.Handle() == 0)
+ {
+ User::LeaveIfError(iChainLock.CreateLocal());
+ }
+ iNotifyRequests = TheContainer->CreateL();
+ iPool = CFsPool<CFsNotificationBlock>::New(KNotificationPoolSize);
+ User::LeaveIfNull(iPool);
+ }
+ }
+
+void FsNotificationManager::SetFilterRegister(TUint aFilter, TBool aAdd, TInt aCount)
+ {
+ __PRINT2(_L("FsNotificationManager::SetFilterRegister(aFilter=%u,aAdd=%d)"),aFilter,aAdd);
+ TInt index = FsNotificationHelper::TypeToIndex((TFsNotification::TFsNotificationType)aFilter);
+ TInt& fr = FsNotificationManager::FilterRegister(index);
+ __ASSERT_DEBUG((aAdd) ? fr >= 0 : fr > 0,Fault(ENotificationFault));
+ fr+= aAdd ? aCount : -aCount;
+ }
+
+void FsNotificationManager::SetFilterRegisterMask(TUint aMask,TBool aAdd)
+ {
+ __PRINT(_L("FsNotificationManager::RegisterFilterMask()"));
+ TInt notifyType = 1;
+
+ while(notifyType & KNotificationValidFiltersMask && aMask & KNotificationValidFiltersMask)
+ {
+ if(aMask & notifyType)
+ {
+ SetFilterRegister(notifyType,aAdd);
+ aMask ^= notifyType;
+ }
+ notifyType <<= 1;
+ }
+ }
+
+TInt& FsNotificationManager::FilterRegister(TInt aIndex)
+ {
+ __PRINT(_L("FsNotificationManager::FilterRegister()"));
+ __ASSERT_DEBUG(aIndex < KNumRegisterableFilters,Fault(ENotificationFault));
+ return iFilterRegister[aIndex];
+ }
+
+//Must be called with the iChainLock
+void FsNotificationManager::Close()
+ {
+ __PRINT(_L("FsNotificationManager::Stop()"));
+ CFsObjectCon*& request = iNotifyRequests;
+ if(request)
+ {
+ TheContainer->Delete(request);
+ delete iPool;
+ iPool = NULL;
+ }
+ request = NULL;
+ }
+
+TInt FsNotificationManager::Count()
+ {
+ __PRINT(_L("FsNotificationManager::Count()"));
+ if(IsInitialised())
+ return iNotifyRequests->Count();
+ return 0;
+ }
+
+void FsNotificationManager::Lock()
+ {
+ __PRINT(_L("--->FsNotificationManager::Lock()"));
+ iChainLock.Wait();
+ }
+
+void FsNotificationManager::Unlock()
+ {
+ __PRINT(_L("<---FsNotificationManager::Unlock()"));
+ iChainLock.Signal();
+ }
+
+//Get the notification type based on the TFsMessage function
+void FsNotificationHelper::NotificationType(TInt aFunction,TFsNotification::TFsNotificationType& aNotificationType)
+ {
+ __PRINT(_L("FsNotificationHelper::NotificationType"));
+ switch(aFunction)
+ {
+ case EFsFileWrite:
+ case EFsFileWriteDirty:
+ case EFsFileSetSize:
+ {
+ aNotificationType = TFsNotification::EFileChange;
+ break;
+ }
+ case EFsRename:
+ case EFsFileRename:
+ case EFsReplace:
+ {
+ aNotificationType = TFsNotification::ERename;
+ break;
+ }
+ case EFsMkDir:
+ case EFsFileCreate:
+ case EFsFileReplace:
+ {
+ aNotificationType = TFsNotification::ECreate;
+ break;
+ }
+ case EFsFileSetAtt:
+ case EFsFileSet:
+ case EFsSetEntry:
+ {
+ aNotificationType = TFsNotification::EAttribute;
+ break;
+ }
+ case EFsDelete:
+ case EFsRmDir:
+ {
+ aNotificationType = TFsNotification::EDelete;
+ break;
+ }
+ case EFsSetVolume:
+ {
+ aNotificationType = TFsNotification::EVolumeName;
+ break;
+ }
+ case EFsSetDriveName:
+ {
+ aNotificationType = TFsNotification::EDriveName;
+ break;
+ }
+ case EFsDismountFileSystem:
+ case EFsMountFileSystem:
+ case EFsFormatNext:
+ case EFsRawDiskWrite:
+ case EFsMountFileSystemScan:
+ {
+ aNotificationType = TFsNotification::EMediaChange;
+ break;
+ }
+ default:
+ {
+ aNotificationType = (TFsNotification::TFsNotificationType)0;
+ break;
+ }
+ }
+ }
+
+//=====CFsNotificationBlock============================
+// Uses CFsPool
+
+CFsNotificationBlock* CFsNotificationBlock::New()
+ {
+ return new CFsNotificationBlock();
+ }
+CFsNotificationBlock::CFsNotificationBlock()
+ {
+ }
+CFsNotificationBlock::~CFsNotificationBlock()
+ {
+ //Nothing to do here
+ }
+TAny* CFsNotificationBlock::Data()
+ {
+ return (TAny*)&iData;
+ }
+
+
+//=====FsNotificationManager===========================
+
+//Get the path of the file, folder or drive name based on the TFsMessage function
+void FsNotificationHelper::PathName(CFsClientMessageRequest& aRequest, TDes& aPath)
+ {
+ __PRINT(_L("FsNotificationHelper::PathName"));
+ //Get the notification type
+ TInt function = aRequest.Operation()->Function();
+
+ //Get the filename(s)
+ switch(function)
+ {
+ case EFsFileWrite: //EParseSrc | EFileShare
+ case EFsFileSetSize: //EParseSrc | EFileShare
+ case EFsFileSetAtt: //EParseDst | EParseSrc, - should not use these; has share.
+ case EFsFileSet:
+ case EFsFileWriteDirty: //EFileShare
+ {
+ CFileShare* share = NULL;
+ CFileCB* file = NULL;
+ GetFileFromScratch(&aRequest,share,file);
+ aPath.Append(file->DriveNumber() + 'A');
+ aPath.Append(':');
+ aPath.Append(file->FileName().Des());
+ break;
+ }
+ case EFsFileCreate: //EParseSrc
+ case EFsDelete: //EParseSrc
+ case EFsSetEntry: //EParseSrc,
+ case EFsFileRename: //EParseDst | EParseSrc,
+ case EFsRename: //EParseDst | EParseSrc,
+ case EFsReplace: //EParseDst | EParseSrc,
+ case EFsFileReplace: //EParseSrc
+ {
+ aPath.Copy(aRequest.Src().FullName());
+ break;
+ }
+ case EFsRmDir: //EParseSrc
+ case EFsMkDir: //EParseSrc
+ {
+ aPath.Copy(aRequest.Src().DriveAndPath());
+ break;
+ }
+ case EFsFormatNext: //EParseSrc
+ case EFsDismountFileSystem: //0
+ case EFsMountFileSystem: //0
+ case EFsSetVolume: //0
+ case EFsSetDriveName: //ESync
+ case EFsRawDiskWrite: //EParseSrc
+ case EFsMountFileSystemScan:
+ {
+ _LIT(KFormatDrive,"?:");
+ TBuf<2> drive;
+ drive.Append(KFormatDrive);
+ drive[0] = TText(aRequest.Drive()->DriveNumber() + 'A');
+ aPath.Copy(drive);
+ break;
+ }
+ default:
+ ASSERT(0);
+ break;
+ }
+ }
+
+//Get the new path of the file, folder or drive name based on the TFsMessage function
+void FsNotificationHelper::NewPathName(CFsClientMessageRequest& aRequest, TPtrC& aNewPath)
+ {
+ __PRINT(_L("FsNotificationHelper::NewPathName"));
+ //Get the notification type
+ TInt function = aRequest.Operation()->Function();
+
+ //Get the filename(s)
+ switch(function)
+ {
+ case EFsFileRename: //EParseDst | EParseSrc,
+ case EFsRename: //EParseDst | EParseSrc,
+ case EFsReplace: //EParseDst | EParseSrc,
+ {
+ aNewPath.Set(aRequest.Dest().FullName());
+ break;
+ }
+ case EFsSetDriveName: //ESync
+ {
+ TFileName name;
+ aRequest.ReadL(KMsgPtr1, name);
+ aNewPath.Set(name);
+ break;
+ }
+ case EFsSetVolume: //0
+ {
+ TFileName name;
+ aRequest.ReadL(KMsgPtr0, name);
+ aNewPath.Set(name);
+ break;
+ }
+ default:
+ {
+ ASSERT(0);
+ break;
+ }
+ }
+ }
+
+//Get the size of the notification based on its type
+TInt FsNotificationHelper::NotificationSize(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aNotificationType, const TDesC& aName)
+ {
+ __PRINT(_L("FsNotificationHelper::NotificationSize"));
+
+ /*
+ * If there are no new names, the order of the data in the buffer is:
+ * Word1 : NotificationSize (2 bytes) , PathSize (2 bytes)
+ * Word2 : NotificationType (Lower 2 bytes)
+ * Word(s) : Path (TText8) , [Any sub-class members]
+ *
+ * Else for notification types ERename, EVolumeName and EDriveName the order is:
+ * Word1 : NotificationSize (2 bytes) , PathSize (2 bytes)
+ * Word2 : NewNameSize (2 bytes) , NotificationType (2 bytes)
+ * Word(s) : Path (TText8) , NewName (TText8)
+ *
+ * EOverflow size: KNotificationHeaderSize
+ */
+
+ TInt size = KNotificationHeaderSize + Align4(aName.Size());
+
+ switch(aNotificationType)
+ {
+ //NewName
+ case TFsNotification::ERename:
+ case TFsNotification::EVolumeName:
+ case TFsNotification::EDriveName:
+ {
+ TPtrC dest;
+ NewPathName(aRequest,dest);
+ size += Align4(dest.Size());
+ break;
+ }
+ case TFsNotification::EFileChange:
+ {
+ size += sizeof(TInt64);
+ break;
+ }
+ case TFsNotification::EAttribute:
+ {
+ size += sizeof(TUint64);
+ break;
+ }
+ case TFsNotification::ECreate:
+ case TFsNotification::EDelete:
+ case TFsNotification::EMediaChange:
+ {
+ break;
+ }
+ default:
+ {
+ ASSERT(0);
+ break;
+ }
+ }
+ return (TUint16) size;
+ }
+
+TFsNotification::TFsNotificationType FsNotificationHelper::NotificationType(TInt& aIndex)
+ {
+ __PRINT(_L("FsNotificationHelper::NotificationType(TInt)"));
+ __ASSERT_DEBUG(aIndex < KNumRegisterableFilters, Fault(ENotificationFault));
+
+ switch(aIndex) //No break statements here on purpose
+ {
+ case 7 : return TFsNotification::EMediaChange;
+ case 6 : return TFsNotification::EDriveName;
+ case 5 : return TFsNotification::EVolumeName;
+ case 4 : return TFsNotification::EDelete;
+ case 3 : return TFsNotification::EAttribute;
+ case 2 : return TFsNotification::ECreate;
+ case 1 : return TFsNotification::ERename;
+ case 0 : return TFsNotification::EFileChange;
+ default: ASSERT(0); return (TFsNotification::TFsNotificationType) 0;
+ }
+ }
+
+//Get the array index of the notification based on its type
+TInt FsNotificationHelper::TypeToIndex(TFsNotification::TFsNotificationType aType)
+ {
+ __PRINT(_L("FsNotificationHelper::ArrayIndex"));
+
+ TInt index = 0;
+ switch(aType) //No break statements here on purpose
+ {
+ case TFsNotification::EMediaChange: index++;
+ case TFsNotification::EDriveName: index++;
+ case TFsNotification::EVolumeName: index++;
+ case TFsNotification::EDelete: index++;
+ case TFsNotification::EAttribute: index++;
+ case TFsNotification::ECreate: index++;
+ case TFsNotification::ERename: index++;
+ case TFsNotification::EFileChange: // skip;
+ default: break;
+ }
+ __ASSERT_DEBUG(index < KNumRegisterableFilters, Fault(ENotificationFault));
+ return index;
+ }
+
+TInt FsNotificationHelper::DriveNumber(const TPtrC& aPath)
+ {
+ if(aPath.Length() >= 2 && ((TChar)aPath[1])==(TChar)':')
+ {
+ TChar driveChar = ((TChar)aPath[0]);
+ driveChar.UpperCase();
+ TInt driveNum = driveChar-(TChar)'A';
+ return driveNum;
+ }
+ else
+ {
+ return KErrNotFound;
+ }
+ }
+
+//Get the attributes set and cleared
+void FsNotificationHelper::Attributes(CFsClientMessageRequest& aRequest, TUint& aSet, TUint& aClear)
+ {
+ __PRINT(_L("FsNotificationHelper::Attributes"));
+
+ TInt function = aRequest.Operation()->Function();
+ const RMessage2& msg = aRequest.Message();
+
+ switch(function)
+ {
+ case EFsFileSet:
+ {
+ aSet = msg.Int1();
+ aClear = msg.Int2();
+ break;
+ }
+ case EFsFileSetAtt:
+ {
+ aSet = msg.Int0();
+ aClear = msg.Int1();
+ break;
+ }
+ case EFsSetEntry:
+ {
+ aSet = msg.Int2();
+ aClear = msg.Int3();
+ break;
+ }
+ default:
+ {
+ ASSERT(0);
+ break;
+ }
+ }
+ }
+
+
+TBool CFsNotifyRequest::ValidateNotification(TInt aNotificationSize, TInt& aServerTail)
+ {
+ __PRINT(_L("CFsNotifyRequest::ValidateNotification"));
+ //RDebug::Print(_L("CFsNotifyRequest::ValidateNotification - iServerTail=%d, aServerTail=%d, iClientTail=%d,iClientHead=%d, aNotificationSize=%d"),iServerTail,aServerTail,iClientTail,iClientHead,aNotificationSize);
+ //
+ //Start Validation
+ //
+ TBool overflow = EFalse;
+
+ //Check that we have not filled the buffer
+ //Also if iClientMsg is present this is the first notification
+ if (aServerTail == iClientHead && ClientMsgHandle()==0)
+ {
+ //Overflow
+ overflow = ETrue;
+ return overflow;
+ }
+
+ //Work out remaining size taking account of whether the end position is
+ //before or after the overflow position.
+ TInt remainingSize = (iClientHead > aServerTail)
+ ? iClientHead - aServerTail
+ : iClientBufferSize - (aServerTail - iClientHead);
+
+ //In order to ensure that we can always fit in an overflow notification,
+ //Remove the size of an overflow notification from the total free space in the buffer
+ remainingSize -= KNotificationHeaderSize;
+
+ //Check whether there is any chance of this notification fitting in the buffer
+ if (aNotificationSize > remainingSize)
+ {
+ //Overflow
+ overflow = ETrue;
+ }
+ //Check that the notification fits in a contiguous chunk.
+ //If we've wrapped around already..
+ else if (iClientHead > aServerTail)
+ {
+ //Does it fit?
+ if ((iClientHead - aServerTail) < aNotificationSize)
+ {
+ //Overflow
+ overflow = ETrue;
+ }
+ }
+ //Else, We've not wrapped around yet.
+ //Does it fit at the end?
+ else if ((iClientBufferSize - aServerTail) < aNotificationSize)
+ {
+ //Notification won't fit in the space at the end of the buffer
+ //Fill end of buffer with KNotificationBufferFiller (if we're not at the very end already)
+ if(iServerTail != iClientBufferSize)
+ {
+ //If there is any dead space it should always be at least 1 word big
+ TPtrC8 fillerDes((TText8*) &KNotificationBufferFiller, sizeof(TUint));
+ iBufferMsg.Write(KMsgPtr0, fillerDes, aServerTail);
+ }
+
+ //It doesn't fit at the end,
+ //does it fit at the beginning?
+ if (iClientHead < aNotificationSize)
+ {
+ //Overflow
+ overflow = ETrue;
+ }
+ //Notification would fit at the beginning...
+ else
+ {
+ //...however we need to ensure that there is
+ //still space for overflow next time.
+ if ((iClientHead - aNotificationSize) < KNotificationHeaderSize)
+ {
+ overflow = ETrue;
+ }
+ else
+ {
+ //Everything was ok, update aServerTail
+ aServerTail = 0;
+ }
+ }
+ }
+
+ //
+ //End Validation
+ //
+ return overflow;
+ }
+
+// Called from FsNotificationManager::HandleChange().
+// Sends notifications into the client's buffer.
+// If there is a iClientMsg then this is the first time this
+// has been called since the client called RequestNotifications.
+// In this situation we complete the client request.
+TInt CFsNotifyRequest::NotifyChange(CFsClientMessageRequest* aRequest,const TDesC& aName, TFsNotification::TFsNotificationType aNotificationType, CFsNotificationBlock& aBlock)
+ {
+ /*
+ * Different notification types have different data associated with them.
+ *
+ * All types EXCEPT for ERename, EVolumeName and EDriveName have the following data
+ * and are aligned in the buffer like so:
+ * Word1 : NotificationSize (2 bytes) , PathSize (2 bytes)
+ * Word2 : NotificationType (Lower 2 bytes)
+ * Word(s) : Path (TText8) , [Any sub-class members]
+ *
+ * Else for notification types ERename, EVolumeName and EDriveName the order is:
+ * Word1 : NotificationSize (2 bytes) , PathSize (2 bytes)
+ * Word2 : NewNameSize (2 bytes) , NotificationType (2 bytes)
+ * Word(s) : Path (TText8) , NewName (TText8)
+ *
+ * Overflow notification type doesn't have a name, so its namesize is 0
+ * and there will be no Word3.
+ */
+
+ __PRINT(_L("CFsNotifyRequest::NotifyChange()"));
+
+ TInt notificationSize = FsNotificationHelper::NotificationSize(*aRequest,aNotificationType,aName);
+
+ iClientSyncLock.Wait();
+ iTailSemaphore.Wait();
+
+ TInt tail = iServerTail;
+
+ //Validation
+ TBool overflow = ValidateNotification(notificationSize, tail);
+
+ //Now that we know there is enough space in the buffer we can write
+ //the standard attributes that all notifications have.
+
+ //We can store the size of the notification
+ //and the size of the name in the same word.
+
+ TUint16 nameLen = 0; //Overflow has no name
+ TInt notifSize = KNotificationHeaderSize;
+ if(!overflow)
+ {
+ nameLen = (TUint16)aName.Size();
+ notifSize = notificationSize;
+ }
+ else
+ {
+ aNotificationType = TFsNotification::EOverflow;
+ }
+
+ iServerTail = tail + notifSize;
+ iTailSemaphore.Signal();
+
+ TInt writeOffset = 0; //Where to write in the block
+
+ //Store notification Size and NameSize (Word1)
+ TUint sizeNameLen = (notifSize << 16) | nameLen;
+ memcpy((TText8*)aBlock.Data()+writeOffset,&sizeNameLen,sizeof(TUint));
+ writeOffset+=sizeof(TUint);
+
+ TPtrC newName;
+
+ if (aNotificationType == TFsNotification::ERename ||
+ aNotificationType == TFsNotification::EVolumeName ||
+ aNotificationType == TFsNotification::EDriveName)
+ {
+ FsNotificationHelper::NewPathName(*aRequest,newName);
+ //Store NewNameSize and notification Type (Word2)
+ TUint typeNewNameLen = ((TUint16)newName.Size() << 16) | (TUint16)aNotificationType;
+ memcpy((TText8*)aBlock.Data()+writeOffset,&typeNewNameLen,sizeof(TUint));
+ }
+ else
+ {
+ //Store notification Type (Word2)
+ memcpy((TText8*)aBlock.Data()+writeOffset,&aNotificationType,sizeof(TUint));
+ }
+ writeOffset+=sizeof(TUint);
+
+ CFileShare* share = NULL;
+ CFileCB* file = NULL;
+ if(aRequest) //Don't always have a request such as when called from localdrives.
+ {
+ GetFileFromScratch(aRequest, share, file);
+ }
+
+ //
+ //Store UID
+ /*
+ TUid uid;
+ uid.iUid = KErrUnknown;
+ if(aRequest && aRequest->Operation()->iFunction == EFsFileWriteDirty)
+ {
+ uid = aRequest->iUID;
+ }
+ else if(aRequest)
+ {
+ uid = aRequest->Message().Identity();
+ }
+ memcpy((TText8*)aBlock.Data()+writeOffset,&uid.iUid,sizeof(TUint32));
+ writeOffset+=sizeof(TUint32);
+ */
+
+ if(!overflow)
+ {
+ //Store Name (Word3)
+ memcpy((TText8*)aBlock.Data()+writeOffset,aName.Ptr(),aName.Size());
+ writeOffset += Align4(aName.Size());
+
+
+ switch (aNotificationType)
+ {
+ case TFsNotification::EFileChange:
+ {
+ TInt64 size = 0;
+ size = file->CachedSize64();
+ memcpy((TText8*)aBlock.Data()+writeOffset,&size,sizeof(TInt64));
+ writeOffset += sizeof(TInt64);
+ break;
+ }
+ case TFsNotification::ERename:
+ case TFsNotification::EVolumeName:
+ case TFsNotification::EDriveName:
+ {
+ //Store NewName
+ memcpy((TText8*)aBlock.Data()+writeOffset,newName.Ptr(),newName.Size());
+ writeOffset += Align4(newName.Size());
+ break;
+ }
+ case TFsNotification::EAttribute:
+ {
+ TUint set=0;
+ TUint clear=0;
+ FsNotificationHelper::Attributes(*aRequest,set,clear);
+ TUint64 att = MAKE_TUINT64(set,clear);
+ memcpy((TText8*)aBlock.Data()+writeOffset,&att,sizeof(TUint64));
+ writeOffset += sizeof(TUint64);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ //Write to client buffer
+ TInt r = SynchroniseBuffer(aBlock,tail,notifSize);
+
+ //Signal the iClientSyncLock.
+ //When all locks on this are signaled then CompleteClientRequest can be called.
+ //This signal moves when we have a cache system
+ iClientSyncLock.Signal();
+
+ //We need to complete if this was the first
+ //write to the client's buffer
+ if(ClientMsgHandle()!=0 && r==KErrNone)
+ {
+ __PRINT4(_L("CFsNotifyRequest::NotifyChange iClientHead(%d) iClientTail(%d) iServerTail(%d) iClientBufferSize(%d)"),iClientHead,iClientTail,iServerTail,iClientBufferSize);
+ CompleteClientRequest(KErrNone);
+ }
+ else if(!overflow)
+ {
+ SetActive(CFsNotifyRequest::EOutstanding);
+ }
+ else
+ {
+ SetActive(CFsNotifyRequest::EOutstandingOverflow);
+ }
+
+ if(r!= KErrNone)
+ {
+ //RDebug::Print(_L("sf_notifier.cpp line %d function = %d, r = %d"),__LINE__, aRequest->FsFunction(),r);
+ //RDebug::Print(_L("iServerTail=%d, tail=%d, iClientBufferSize=%d, overflow=%d"),iServerTail,tail,iClientBufferSize,overflow);
+ SetActive(CFsNotifyRequest::EInactive);
+ }
+ return r;
+ }
+
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+
+//A change has occurred in f32 represented by this
+//request object. Work out which CfsNotify’s are interested
+// (if any) and call CfsNotifyRequest::NotifyChange.
+void FsNotificationManager::HandleChange(CFsClientMessageRequest* aRequest,const TDesC& aOperationName, TFsNotification::TFsNotificationType aType)
+ {
+ __PRINT2(_L("FsNotificationManager::HandleChange() aRequest=0x%x, aType=%d"),&aRequest,aType);
+
+ Lock(); //ToDo: Read Lock (Read/Write Lock)
+ if(Count())
+ {
+ //Only search while there are filters of this type set up.
+ TInt index = FsNotificationHelper::TypeToIndex(aType);
+ TInt& filterCount = FsNotificationManager::FilterRegister(index);
+ TInt seenFilter = filterCount; //Number of requests set up for this type
+
+ //Iterate CFsNotifyRequests
+ TInt count = iNotifyRequests->Count();
+
+ if(aType == TFsNotification::EMediaChange)
+ seenFilter = count;
+
+ //If there aren't any requests then breakout
+ if(count == 0)
+ {
+ Unlock();
+ return;
+ }
+
+ TInt driveNum = FsNotificationHelper::DriveNumber(aOperationName);
+
+ //For every notification request.
+ for(TInt i=0; i<count && seenFilter; ++i)
+ {
+ CFsNotifyRequest* notifyRequest = (CFsNotifyRequest*)(*iNotifyRequests)[i];
+ CFsNotifyRequest::TNotifyRequestStatus status = notifyRequest->ActiveStatus();
+ if(! (status==CFsNotifyRequest::EActive ||
+ status==CFsNotifyRequest::EOutstanding))
+ {
+ //Not active; check next notification request
+ continue;
+ }
+
+ //Check whether we are interested in this change.
+ //Get the filters associated with this operation on this drive
+ TFsNotificationTypeArray* filterList = notifyRequest->FilterTypeList(driveNum,index);
+ DoHandleChange(filterList,seenFilter,aRequest,notifyRequest,aOperationName,aType);
+
+ if(aType==TFsNotification::EMediaChange)
+ continue; //next request
+
+ //If there are still filters to check
+ if(seenFilter)
+ {
+ //Check changes that are not tied to a particular drive
+ filterList = notifyRequest->FilterTypeList(KErrNotFound,index);
+ DoHandleChange(filterList,seenFilter,aRequest,notifyRequest,aOperationName,aType);
+ }
+ }
+ }
+ Unlock();
+ }
+
+//A change has occurred in f32 represented by this
+//request object. Work out which CfsNotify’s are interested
+// (if any) and call CfsNotifyRequest::NotifyChange.
+void FsNotificationManager::HandleChange(CFsClientMessageRequest& aRequest, TFsNotification::TFsNotificationType aType)
+ {
+ __PRINT(_L("FsNotificationManager::HandleChange"));
+ TFileName currentOperationsName;
+ FsNotificationHelper::PathName(aRequest, currentOperationsName);
+ if(currentOperationsName.Length())
+ HandleChange(&aRequest,currentOperationsName,aType);
+ }
+
+//A change has occurred in f32 represented by this
+//request object. Work out which CfsNotify’s are interested
+// (if any) and call CfsNotifyRequest::NotifyChange.
+void FsNotificationManager::HandleChange(CFsClientMessageRequest& aRequest)
+ {
+ if(Count() && aRequest.Message().Handle() != KLocalMessageHandle)
+ {
+ __PRINT(_L("FsNotificationManager::HandleChange"));
+ TFsNotification::TFsNotificationType operationNotificationType;
+ FsNotificationHelper::NotificationType(aRequest.FsFunction(), operationNotificationType);
+ HandleChange(aRequest,operationNotificationType);
+ }
+ }
+
+
+////
+#else
+////
+
+void FsNotificationManager::HandleChange(CFsClientMessageRequest* ,const TDesC&, TFsNotification::TFsNotificationType)
+ {
+ return;
+ }
+
+void FsNotificationManager::HandleChange(CFsClientMessageRequest& , TFsNotification::TFsNotificationType)
+ {
+ return;
+ }
+
+void FsNotificationManager::HandleChange(CFsClientMessageRequest&)
+ {
+ return;
+ }
+
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+
+//Called from FsNotificationManager::DoHandleChange
+FsNotificationManager::TFsNotificationFilterMatch FsNotificationManager::DoMatchFilter(CFsClientMessageRequest* aRequest, const TDesC& aOperationName,CFsNotificationPathFilter& aFilter)
+ {
+ TFsNotificationFilterMatch filterMatch = EDifferent;
+ TParsePtrC parseOp(aOperationName);
+ TPtrC pathOpDes = parseOp.DriveAndPath();
+ TPtrC nameOpDes = parseOp.NameAndExt();
+ TInt pathLength = aFilter.iPath->Des().Length();
+ TInt nameLength = aFilter.iFilename->Des().Length();
+ TInt paths = -1;
+ TInt names = -1;
+ if(pathLength != 0)
+ {
+ paths = pathOpDes.MatchF(aFilter.iPath->Des());
+ }
+ else //if no path filter was set up
+ // then we need to ensure we don't notify on data-caged areas which we shouldn't
+ {
+ TInt r = PathCheck(aRequest,aOperationName.Mid(2),&KCapFsSysFileTemp,&KCapFsPriFileTemp,&KCapFsROFileTemp, __PLATSEC_DIAGNOSTIC_STRING("FsNotificationManager::DoHandleChange"));
+ if(r != KErrNone)
+ return EContinue; //next filter
+ }
+
+ if(nameLength != 0)
+ {
+ names = nameOpDes.MatchF(aFilter.iFilename->Des());
+ }
+ //Check: Path & Names Match
+ if((paths == 0 || pathLength==0) && // paths match &&
+ (names >= 0 || (nameLength==0 && nameOpDes.Length()==0))) // names match OR there are no names (i.e. operation is a dir and no filename filter)
+ {
+ filterMatch = EMatch;
+ }
+ return filterMatch;
+ }
+
+// This is called on a per drive basis.
+void FsNotificationManager::DoHandleChange(TFsNotificationTypeArray* aFilterTypeArray,TInt& aSeenFilter, CFsClientMessageRequest* aRequest, CFsNotifyRequest* aNotifyRequest, const TDesC& aOperationName, TFsNotification::TFsNotificationType& aType)
+ {
+ __PRINT(_L("FsNotificationManager::DoHandleChange()"));
+
+ if(!aFilterTypeArray)
+ return;
+
+ TInt numFilters = aFilterTypeArray->Count();
+
+ if(aType == TFsNotification::EMediaChange)
+ numFilters = 1; //Only need to notify once per drive.
+
+ //For every filter in this request
+ for(TInt j = 0; j < numFilters;++j)
+ {
+ //Is the correct notification type
+ aSeenFilter--;
+
+ TBool filterMatch = EDifferent;
+ if(aType != TFsNotification::EMediaChange)
+ {
+ CFsNotificationPathFilter& filter = *(((*aFilterTypeArray)[j]).iPathFilter);
+ __PRINT2(_L("FsNotificationManager::DoHandleChange() operationName=%S, filterName=%S"),&aOperationName,filter.iPath);
+
+ filterMatch = DoMatchFilter(aRequest,aOperationName,filter);
+ if(filterMatch == FsNotificationManager::EContinue)
+ continue; //triggers for data cages
+
+ //We need to check for changes coming in to a directory when its rename
+ if(aType == TFsNotification::ERename && filterMatch==FsNotificationManager::EDifferent)
+ {
+ TPtrC aDestinationNamePtrC;
+ FsNotificationHelper::NewPathName(*aRequest,aDestinationNamePtrC);
+ __PRINT2(_L("FsNotificationManager::DoHandleChange() destinationName=%S, filterName=%S"),&aDestinationNamePtrC,filter.iPath);
+ filterMatch = DoMatchFilter(aRequest,aDestinationNamePtrC,filter);
+ }
+ }
+
+ if(filterMatch || (aType == TFsNotification::EMediaChange))//Match or MediaChange (report regardless of filters)
+ {
+ //Matching - Handle change
+
+ //Get a CFsNotificationBlock to use
+ //So that we can do IPC from a single place.
+ CFsNotificationBlock* block = iPool->Allocate();
+
+ TInt r = aNotifyRequest->NotifyChange(aRequest,aOperationName,aType,*block);
+
+ //Free block
+ iPool->Free(block);
+
+ if(r != KErrNone)
+ {
+ //Something went wrong writing to the client's buffer
+ aNotifyRequest->SetActive(CFsNotifyRequest::EInactive);
+ if(aNotifyRequest->ClientMsgHandle()!=0)
+ aNotifyRequest->CompleteClientRequest(r,EFalse);
+ break; //Go to outer for (i.e. next request in HandleChange)
+ }
+ }
+ continue; //next filter
+ }
+ }