diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/sfile/sf_notifier.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfile/sf_notifier.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1294 @@ +// 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* 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 tailDes(clientTail); + iClientMsg.Write(KMsgPtr0,tailDes); + } + else if(aIsCancel) + { + __PRINT(_L("CFsNotifyRequest::CompleteClientRequest() - Complete isCancel")); + iServerTail = 0; + iClientTail = 0; + iClientHead = 0; + TPckgBuf 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::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::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 + if (aServerTail == iClientHead) + { + // Buffer is empty when Client Tail = Client Head + if (iClientHead != iClientTail) + { + 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); + + TInt reservedSize = aNotificationSize; + // + Save additional space for OVERFLOW + reservedSize += KNotificationHeaderSize; + + // + // Have we wrapped around already? + // + if (iClientHead > aServerTail) + { + // Yes, + // Buffer looks something like this: + // + // |CH + // [5678------1234] + // |ST + + // + // Check if we can insert in the middle section: + // + if (remainingSize < reservedSize) + { + overflow = ETrue; + } + //else: + // { + // We add new notification to middle + // [5678***---1234] + // } + // + return overflow; + } + + + // + // We have not wrapped around yet.. + // + // Buffer looks something like this: + // + // |CH + // [--123456789--] + // |ST + // + + + // + // Check up-front whether its possible for overflow to go at the beginning. + // If there is not enough space at the start for overflow then we need to + // check that's there's space for overflow at the end and must not rollover. + // + TBool canRollOver = ETrue; + + if (iClientHead < KNotificationHeaderSize) + { + // + // |CH + // [123456789----] + // |ST + // + // No space for overflow at the beginning of buffer. + // + canRollOver = EFalse; + } + + // + // IF: Cannot rollover + // + if (!canRollOver) + { + //IF (notification + overflow) does not fit at the end overflow now. + if ((iClientBufferSize - aServerTail) < reservedSize) + { + overflow = ETrue; + } + //Else + // { + // Add notification (**) to end [---12345678**---] + // } + + } + else + // Can rollover + // - need to check that notification fits at the end + // or that notification+overflow fits at the beginning. + { + // If not enough space at end, rollover + if ((iClientBufferSize - aServerTail) < aNotificationSize) + { + // + // Add notification to start and fill end with Filler char + // [----0123456789#] + // + + // IF we are not at the very end of the buffer, + // insert a KNotificationBufferFiller at iServerTail. + // When the client reads this, it sets iHead to 0 and reads from there. + if(iServerTail != iClientBufferSize) + { + //If there is space it will always be at least 1 word big + TPtrC8 fillerDes((TText8*) &KNotificationBufferFiller, sizeof(TUint)); + iBufferMsg.Write(KMsgPtr0, fillerDes, aServerTail); + } + + // Now that we have rolled over we need to check whether there is + // space at the beginning for notification + overflow + // We already know that overflow fits. + if (reservedSize > iClientHead) + { + // [ov--0123456789-] + overflow = ETrue; + } + // + // Add notification/overflow to the beginning + // [**--0123456789(#)] + // + aServerTail = 0; + } + // + // else - notification fits at the end so there is nothing to do here. + // + // + } + // + //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 (r == KErrNone) + { + //We need to complete if this was the first + //write to the client's buffer + if(ClientMsgHandle()!=0) + { + //RDebug::Print(_L("CFsNotifyRequest::NotifyChange iClientHead(%d) iClientTail(%d) iServerTail(%d) iClientBufferSize(%d)"),iClientHead,iClientTail,iServerTail,iClientBufferSize); + __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 //Overflow + { + SetActive(CFsNotifyRequest::EOutstandingOverflow); + } + } + else // 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); + } + 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; iActiveStatus(); + 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 + } + }