--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_local.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,586 @@
+// 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:
+//
+
+#include "sf_std.h"
+#include "e32cmn.h"
+
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+#include "sf_notifier.h"
+#endif
+
+GLREF_C CProxyDriveFactory* GetExtension(const TDesC& aName);
+GLREF_C CExtProxyDriveFactory* GetProxyDriveFactory(const TDesC& aName);
+
+TBusLocalDrive LocalDrives::iLocalDrives[KMaxLocalDrives];
+TInt LocalDrives::iMapping[KMaxDrives];
+TInt LocalDrives::iReverseMapping[KMaxLocalDrives];
+TBool LocalDrives::iMappingSet;
+LocalDrives::TSocketDesc LocalDrives::iSocketDescs[KMaxPBusSockets];
+CExtProxyDrive* LocalDrives::iProxyDriveMapping[KMaxProxyDrives];
+TBool LocalDrives::iIsMultiSlotDrive[KMaxDrives];
+const TInt KInvalidSocketNumber = -1;
+
+void LocalDrives::Initialise()
+//
+//
+//
+ {
+ iMappingSet = EFalse;
+ TInt i;
+ Mem::FillZ((TAny*)iProxyDriveMapping,sizeof(CExtProxyDriveFactory*)*KMaxProxyDrives);
+ // initialise mapping from drive number to local drive
+ for(i=0;i<KMaxDrives;i++)
+ {
+ iMapping[i] = KDriveInvalid;
+ iIsMultiSlotDrive[i] = EFalse;
+ }
+ // initialise reverse mapping from local drive to drive.
+ for(i=0;i<KMaxLocalDrives;i++)
+ {
+ iReverseMapping[i] = KDriveInvalid;
+ }
+ // initialise mapping from socket number to drive numbers
+ for(i=0;i<KMaxPBusSockets;++i)
+ {
+ TSocketDesc& socketDesc = iSocketDescs[i];
+ socketDesc.iMediaType = EInvalidMedia;
+ socketDesc.iControllerRelativeSocket = KInvalidSocketNumber;
+ for(TInt j=0;j<KMaxDrivesPerSocket;++j)
+ socketDesc.iDriveNumbers[j]=KDriveInvalid;
+ }
+ }
+
+// Searches for a local socket which matches the media type and
+// controller relative socket number.
+// If none is found then this function returns a new socket number.
+// If no more free sockets available, returns KErrNoMemory
+TInt LocalDrives::GetLocalSocket(TInt aControllerRelativeSocket, TMediaDevice aMediaType)
+ {
+ TInt i;
+ TSocketDesc* socketDesc = NULL;
+ for (i=0; i<KMaxPBusSockets; i++)
+ {
+ socketDesc = &iSocketDescs[i];
+ TMediaDevice mediaType = socketDesc->iMediaType;
+ if (mediaType == aMediaType && socketDesc->iControllerRelativeSocket == aControllerRelativeSocket)
+ return i;
+ if (mediaType == EInvalidMedia) // socket unassigned ?
+ break;
+ }
+ if (i == KMaxPBusSockets)
+ return KErrNoMemory;
+
+ // assign a new local socket for this controller relative socket number & media type
+ socketDesc->iMediaType = aMediaType;
+ socketDesc->iControllerRelativeSocket = aControllerRelativeSocket;
+
+ return i;
+ }
+
+TBusLocalDrive& LocalDrives::GetLocalDrive(TInt aDrive)
+//
+// Export localdrives
+//
+ {
+ __ASSERT_DEBUG(aDrive>=0 && aDrive<KMaxDrives,Fault(EGetLocalDrive1));
+ __ASSERT_DEBUG(iMapping[aDrive]!=KDriveInvalid && iMapping[aDrive]<KMaxLocalDrives,Fault(EGetLocalDrive2));
+ return(iLocalDrives[iMapping[aDrive]]);
+ }
+
+
+TInt LocalDrives::GetLocalDriveNumber(TBusLocalDrive* aLocDrv)
+//
+// Get the local drive number for the local drive object passed in
+//
+ {
+ for(TInt i=0;i<KMaxLocalDrives;++i)
+ if(&iLocalDrives[i]==aLocDrv)
+ return(i);
+ return(KDriveInvalid);
+ }
+
+
+
+CExtProxyDrive* LocalDrives::GetProxyDrive(TInt aDrive)
+ {
+ __ASSERT_DEBUG(aDrive>=0 && aDrive<KMaxDrives,Fault(EGetProxyDriveMapping1));
+ __ASSERT_DEBUG(iMapping[aDrive]!=KDriveInvalid && iMapping[aDrive]>=KMaxLocalDrives && iMapping[aDrive]<KMaxDrives,Fault(EGetProxyDriveMapping1));
+ return iProxyDriveMapping[iMapping[aDrive]-KMaxLocalDrives];
+ }
+
+
+LOCAL_C TBool DriveNumberIsInRange(TInt aDrive)
+ {
+
+ return((aDrive>=0) && (aDrive<KMaxDrives));
+ }
+
+
+TBool LocalDrives::IsValidDriveMapping(TInt aDrive)
+//
+// Is the drive number to local drive mapping valid?
+//
+ {
+
+ __ASSERT_DEBUG(DriveNumberIsInRange(aDrive),Fault(EIsValidDriveMapping));
+ return(iMapping[aDrive]!=KDriveInvalid);
+ }
+
+
+TInt LocalDrives::DriveNumberToLocalDriveNumber(TInt aDrive)
+//
+// Get the mapping from drive number to local drive
+//
+ {
+ return(iMapping[aDrive]);
+ }
+
+
+TInt LocalDrives::SetDriveMappingL(CFsRequest* aRequest)
+//
+//
+//
+ {
+
+ __PRINT(_L("LocalDrives::SetDriveMappingL()"));
+ if (iMappingSet)
+ return(KErrAccessDenied);
+
+ TLocalDriveMappingInfoBuf mBuf;
+ mBuf.FillZ();
+ aRequest->ReadL(KMsgPtr0,mBuf);
+ TLocalDriveMappingInfo& ldmi=mBuf();
+
+ if (ldmi.iOperation==TLocalDriveMappingInfo::ESwapIntMappingAndSet)
+ {
+ // Only the 1st two entries of the mapping table are valid - holding the drive numbers to be swapped
+ TInt r=KErrNone;
+ if (DriveNumberIsInRange(ldmi.iDriveMapping[0]) && DriveNumberIsInRange(ldmi.iDriveMapping[1]))
+ r=SwapDriveMapping(ldmi.iDriveMapping[0],ldmi.iDriveMapping[1]);
+ iMappingSet=ETrue;
+ return(r);
+ }
+
+ // That just leaves EWriteMappingsAndSet and EWriteMappingsNoSet
+ for (TInt i=0;i<KMaxLocalDrives;++i)
+ {
+ TInt driveLetter=ldmi.iDriveMapping[i];
+ if(driveLetter==KDriveInvalid)
+ continue;
+ if ( !DriveNumberIsInRange(driveLetter))
+ {
+ // invalid mapping list passed in, clear all mappings set up
+ for(TInt j=0;j<KMaxDrives;j++)
+ iMapping[j] = KDriveInvalid;
+ return(KErrArgument);
+ }
+ __PRINT2(_L("drive letter %d -> local drive %d"),driveLetter,i);
+
+ // If this mapping (letter -> localdrive) is already set then
+ // this must be a multislot device. Save this mapping as an
+ // alternative mapping (by storing it in iReverseMapping)
+ if(iMapping[driveLetter] != KDriveInvalid)
+ {
+ iIsMultiSlotDrive[driveLetter] = ETrue;
+ }
+ // first time we've seen this drive letter
+ iMapping[driveLetter]=i;
+ // following mapping is used when we want to swap back again.
+ iReverseMapping[i]=driveLetter;
+ }
+
+ InitDriveMapping();
+ if (ldmi.iOperation==TLocalDriveMappingInfo::EWriteMappingsAndSet)
+ iMappingSet=ETrue;
+ return(KErrNone);
+ }
+
+// Changes here must be reflected in SwapDriveMapping()
+void LocalDrives::InitDriveMapping()
+ {
+ __PRINT(_L("InitDriveMapping()"));
+ TDriveInfoV1Buf driveInfo;
+ TInt r=UserHal::DriveInfo(driveInfo);
+ __ASSERT_ALWAYS(r==KErrNone,Fault(EInitDriveMappingDriveInfo));
+
+ // initialise the local drives
+ TInt i;
+ for(i=0;i<KMaxLocalDrives;++i)
+ {
+ TInt driveNo = iReverseMapping[i];
+ if(driveNo!=KDriveInvalid)
+ {
+ r=iLocalDrives[i].Connect(i,TheDrives[driveNo].iChanged);
+ __ASSERT_ALWAYS(r==KErrNone,Fault(EInitConnectLocalDrive));
+ __PRINT2(_L("connect to locdrv %d using drive %d"),i,driveNo);
+ //If this is a multislot then we need to set the iChanged to True
+ //So that we are mapped to the correct drive when we're booted.
+ if(iIsMultiSlotDrive[driveNo])
+ {
+ TheDrives[driveNo].iChanged = ETrue;
+ }
+ if (driveInfo().iDriveName[i].Length()==0)
+ continue;
+ TheDriveNames[driveNo]=driveInfo().iDriveName[i].Alloc();
+ __ASSERT_ALWAYS(TheDriveNames[driveNo],Fault(EInitCreateDriveName));
+ }
+ }
+
+ TInt drivesPerSocket[KMaxPBusSockets];
+ Mem::FillZ(&drivesPerSocket,KMaxPBusSockets*sizeof(TInt));
+ TInt nSockets=driveInfo().iTotalSockets;
+ for(i=0;i<KMaxLocalDrives;++i)
+ {
+ TInt socket;
+ if(iLocalDrives[i].Handle()==0 || !iLocalDrives[i].IsRemovable(socket))
+ {
+ TInt driveNo = iReverseMapping[i];
+ // Non-removable drive so shouldn't be listed as a Multislot drive
+ // Drives such as composite drives may have been
+ // set to true as the drive letter had been encountered before.
+ // make sure those drives are set to false here.
+ iIsMultiSlotDrive[driveNo]=EFalse;
+ continue;
+ }
+ __ASSERT_ALWAYS(socket>=0 && socket<nSockets,Fault(EInitDriveMappingSocketNo));
+ TInt drv=GetDriveFromLocalDrive(i);
+ // get local socket number
+ TMediaDevice mediaDevice = iLocalDrives[i].MediaDevice();
+ TInt localSocket = LocalDrives::GetLocalSocket(socket, mediaDevice);
+ __ASSERT_ALWAYS(localSocket>=0 && localSocket<KMaxPBusSockets,Fault(EInitDriveMappingSocketNo));
+ __PRINT4(_L("InitDriveMapping(), i = %d, , mediaDevice = %d, socket = %d, localSocket = %d"),
+ i, mediaDevice, socket, localSocket);
+ __PRINT2(_L("drv = %d (%C:)"), drv, 'A' + drv);
+
+ TSocketDesc& socketDesc = iSocketDescs[localSocket];
+ if(drv!=KDriveInvalid)
+ {
+ TInt& count = drivesPerSocket[localSocket];
+ // setup up socket to drive mapping
+ __ASSERT_ALWAYS(count < KMaxDrivesPerSocket,Fault(ETooManyDrivesPerSocket));
+ socketDesc.iDriveNumbers[count]=drv;
+ if(count==0)
+ {
+ // setup media change notifier if this is first local drive found on socket
+ CNotifyMediaChange* pN=new CNotifyMediaChange(&iLocalDrives[i],localSocket);
+ __ASSERT_ALWAYS(pN!=NULL,Fault(EInitCreateMediaChangeNotifier));
+ __PRINT2(_L("created CNotifyMediaChange media 0x%x using local drive %d"), localSocket,i);
+ socketDesc.iMediaChanges = pN;
+ CActiveSchedulerFs::Add(pN);
+ pN->RunL();
+ }
+ ++count;
+ }
+ }
+ }
+
+TInt LocalDrives::InitProxyDrive(CFsRequest* aRequest)
+ {
+ __PRINT(_L("LocalDrives::InitProxyDrive"));
+
+ TInt drive = aRequest->Message().Int0() ;
+
+ if (drive < 0 || drive >= KMaxDrives)
+ return KErrArgument;
+
+ if (drive!=EDriveZ && iMapping[drive]!=KDriveInvalid)
+ return KErrInUse; // Z is special case for composite
+
+ TFullName extname;
+ aRequest->ReadL(KMsgPtr1,extname);
+
+ // leave info thing for now
+ CExtProxyDriveFactory* pF = GetProxyDriveFactory(extname);
+ if (!pF)
+ return KErrArgument; // that extension has not been added
+ FsThreadManager::LockDrive(drive);
+ // find a free mapping to place this drive into
+ TInt i;
+ for (i=0; i <KMaxProxyDrives; i++)
+ {
+ if (!iProxyDriveMapping[i])
+ break;
+ }
+ FsThreadManager::UnlockDrive(drive);
+ if (i==KMaxProxyDrives)
+ return KErrInUse; // there are no free proxy drives left
+
+ // Create the actual proxy drive...
+ CProxyDrive* pD = NULL;
+ TInt r = pF->CreateProxyDrive(pD, NULL);
+ __ASSERT_ALWAYS(r == KErrNone, User::Panic(_L("CreateProxyDrive Error"), r));
+ __ASSERT_ALWAYS(pD != NULL, User::Panic(_L("CreateProxyDrive returned NULL"), -999));
+
+ iMapping[drive] = i+KMaxLocalDrives;
+
+ aRequest->SetDrive(&TheDrives[drive]);
+ aRequest->SetScratchValue((TUint)pD);
+
+ return KErrNone;
+ }
+
+TInt LocalDrives::MountProxyDrive(CFsRequest* aRequest)
+ {
+ CExtProxyDrive* pProxyDrive = (CExtProxyDrive*)aRequest->ScratchValue();
+ __ASSERT_ALWAYS(pProxyDrive != NULL, User::Panic(_L("MountProxyDrive has NULL proxy extension class"), -999));
+
+ TInt driveNumber = aRequest->Drive()->DriveNumber();
+
+
+ TInt proxyDriveNo = iMapping[driveNumber] - KMaxLocalDrives;
+ FsThreadManager::LockDrive(driveNumber);
+ iProxyDriveMapping[proxyDriveNo] = pProxyDrive;
+ pProxyDrive->SetDriveNumber(driveNumber);
+ FsThreadManager::UnlockDrive(driveNumber);
+ //
+ // Pass initialisation information onto the extension to allow it to initialise
+ //
+ TInt err = pProxyDrive->SetInfo(aRequest->Message(),
+ (TAny*)aRequest->Message().Ptr2(),
+ (TAny*)aRequest->Message().Ptr3());
+ if (err != KErrNone)
+ {
+ //
+ // If we fail to initialise the extension, then close the drive (destroying the thread)
+ // and remove the mapping so we can attempt to mount again in the future.
+ //
+ FsThreadManager::LockDrive(driveNumber);
+ FsThreadManager::CloseDrive(driveNumber);
+ ClearProxyDriveMapping(driveNumber);
+ FsThreadManager::UnlockDrive(driveNumber);
+ return err;
+ }
+
+ return(iMapping[driveNumber]);
+ }
+
+TBool LocalDrives::IsProxyDrive(TInt aDrive)
+ {
+ __ASSERT_ALWAYS(aDrive>=0 && aDrive<KMaxDrives,Fault(EIsProxyDrive));
+ return (iMapping[aDrive] >= KMaxLocalDrives);
+ }
+
+TBool LocalDrives::IsProxyDriveInUse(CExtProxyDriveFactory* aDevice)
+ {
+ for (TInt i=0; i < KMaxProxyDrives; i++)
+ if (iProxyDriveMapping[i] && (iProxyDriveMapping[i]->FactoryP() == aDevice))
+ return(ETrue);
+
+ return(EFalse);
+ }
+
+void LocalDrives::ClearProxyDriveMapping(TInt aDrive)
+ {
+ __ASSERT_ALWAYS(aDrive>=0 && aDrive<KMaxDrives,Fault(EClearProxyDriveMapping1));
+ __ASSERT_DEBUG(iMapping[aDrive]>= KMaxLocalDrives && iProxyDriveMapping[iMapping[aDrive]-KMaxLocalDrives],Fault(EClearProxyDriveMapping2));
+ TInt idx = iMapping[aDrive]-KMaxLocalDrives;
+ delete iProxyDriveMapping[idx];
+ iProxyDriveMapping[idx] = NULL;
+ iMapping[aDrive] = KDriveInvalid;
+ }
+
+TInt LocalDrives::SetupMediaChange(TInt aDrive)
+ {
+ CExtProxyDrive* pProxyDrive = LocalDrives::GetProxyDrive(aDrive);
+ __ASSERT_ALWAYS(pProxyDrive != NULL,User::Panic(_L("SetupMediaChange - pProxyDrive == NULL"), ESetupMediaChange));
+
+ return pProxyDrive->SetupMediaChange();
+ }
+
+void LocalDrives::NotifyChangeCancel(TInt aDrive)
+ {
+ CExtProxyDrive* pProxyDrive = LocalDrives::GetProxyDrive(aDrive);
+ __ASSERT_ALWAYS(pProxyDrive != NULL,User::Panic(_L("NotifyChangeCancel - pProxyDrive == NULL"), ECancelNotifyChange));
+
+ pProxyDrive->NotifyChangeCancel();
+ }
+
+TInt LocalDrives::SwapDriveMapping(TInt aFirstDrive,TInt aSecondDrive)
+ {
+
+ __PRINT(_L("SwapDriveMapping()"));
+ TInt firstLocalDrv=iMapping[aFirstDrive];
+ TInt secondLocalDrv=iMapping[aSecondDrive];
+
+ // First, check this swap doesn't affect removable drives
+ TInt socket;
+ if (iLocalDrives[firstLocalDrv].Handle()!=0 && iLocalDrives[firstLocalDrv].IsRemovable(socket))
+ return(KErrAccessDenied);
+ if (iLocalDrives[secondLocalDrv].Handle()!=0 && iLocalDrives[secondLocalDrv].IsRemovable(socket))
+ return(KErrAccessDenied);
+
+ // Now swap the mappings over
+ iMapping[aFirstDrive]=secondLocalDrv;
+ iMapping[aSecondDrive]=firstLocalDrv;
+
+ iReverseMapping[firstLocalDrv]=aSecondDrive;
+ iReverseMapping[secondLocalDrv]=aFirstDrive;
+
+ // Finally, swap the drive names over
+ HBufC* drvName=TheDriveNames[aSecondDrive];
+ TheDriveNames[aSecondDrive]=TheDriveNames[aFirstDrive];
+ TheDriveNames[aFirstDrive]=drvName;
+ return(KErrNone);
+ }
+
+void LocalDrives::CompleteNotifications(TInt aSocket)
+//
+//
+//
+ {
+ __ASSERT_DEBUG(aSocket>=0 && aSocket<KMaxPBusSockets && iSocketDescs[aSocket].iDriveNumbers[0]!=KDriveInvalid,Fault(ECompleteNotifSocketNo));
+ TInt i=0;
+
+ // In a data-paging environment, the local media subsytem will only update the TDrive::iChanged flag
+ // for drives which have a CNotifyMediaChange object, i.e. for drives which call TBusLocalDrive::NotifyChange().
+ // Since we only create ONE CNotifyMediaChange object for each socket (no matter how many partitions/local drives
+ // are associated with that socket), we need to propagate the TDrive::iChanged flag to all drives on the socket.
+ TBool changedFlag = TheDrives[iSocketDescs[aSocket].iDriveNumbers[0]].IsChanged();
+
+ while(i<KMaxDrivesPerSocket && iSocketDescs[aSocket].iDriveNumbers[i]!=KDriveInvalid)
+ {
+ TheDrives[iSocketDescs[aSocket].iDriveNumbers[i]].SetChanged(changedFlag);
+ CompleteDriveNotifications(iSocketDescs[aSocket].iDriveNumbers[i++]);
+ }
+ }
+
+void LocalDrives::CompleteDriveNotifications(TInt aDrive)
+//
+//
+//
+ {
+ // If the drive is hung, then don't complete any disk change
+ // notifications until the request causing the hang completes
+ if(FsThreadManager::IsDriveHung(aDrive))
+ FsThreadManager::SetMediaChangePending(aDrive);
+ else
+ {
+ FsNotify::DiskChange(aDrive);
+
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+ if(FsNotificationManager::IsInitialised())
+ {
+ __PRINT3(_L("LocalDrives::CompleteDriveNotifications() Initialised=%d, Count=%d, Drive=%d"),FsNotificationManager::IsInitialised(),FsNotificationManager::Count(), aDrive);
+ TBuf<2> driveDes;
+ driveDes.Append((TChar)aDrive+(TChar)'A');
+ driveDes.Append((TChar)':');
+ FsNotificationManager::HandleChange(NULL,driveDes,TFsNotification::EMediaChange);
+ }
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+
+ //If this is a multislot device we should update mappings here.
+ TheDrives[aDrive].MultiSlotDriveCheck();
+ }
+ }
+
+TInt LocalDrives::GetDriveFromLocalDrive(TInt aLocDrv)
+//
+//
+//
+ {
+ return iReverseMapping[aLocDrv];
+ }
+
+
+CNotifyMediaChange::CNotifyMediaChange(RLocalDrive* aDrive,TInt aSocketNo)
+//
+// Constructor
+//
+ : CActive(EPriorityHigh), iDrive(aDrive), iSocket(aSocketNo)
+ {}
+
+void CNotifyMediaChange::RunL()
+//
+// Notification that a card has been mounted/removed
+//
+ {
+ LocalDrives::CompleteNotifications(iSocket);
+ iDrive->NotifyChange(&iStatus);
+ SetActive();
+ }
+
+
+CExtNotifyMediaChange::CExtNotifyMediaChange(CExtProxyDrive* aDrive)
+//
+// Constructor
+//
+ : CActive(EPriorityHigh),
+ iDrive(aDrive),
+ iPtr((TUint8*)&TheDrives[aDrive->DriveNumber()].iChanged,sizeof(TBool))
+ {
+ }
+
+
+CExtNotifyMediaChange* CExtNotifyMediaChange::NewL(CExtProxyDrive* aDrive)
+ {
+ CExtNotifyMediaChange* pSelf = new(ELeave) CExtNotifyMediaChange(aDrive);
+
+ CleanupStack::PushL(pSelf);
+ pSelf->ConstructL();
+ CleanupStack::Pop();
+
+ return pSelf;
+ }
+
+void CExtNotifyMediaChange::ConstructL()
+ {
+ CActiveSchedulerFs::Add(this);
+
+ TRAPD(err, RunL());
+ if(err != KErrNone)
+ Deque();
+
+ User::LeaveIfError(err);
+ }
+
+CExtNotifyMediaChange::~CExtNotifyMediaChange()
+ {
+ Cancel();
+ }
+
+void CExtNotifyMediaChange::RequestL()
+ {
+ if (!IsActive())
+ {
+ User::LeaveIfError(iDrive->NotifyChange(iPtr, &iStatus));
+ SetActive();
+ }
+ }
+
+void CExtNotifyMediaChange::DoCancel()
+ {
+ iDrive->NotifyChangeCancel();
+ }
+
+void CExtNotifyMediaChange::RunL()
+ {
+ if(iStatus==KErrDisconnected || iStatus==KErrCancel)
+ return;
+
+ TInt driveNum = iDrive->DriveNumber();
+ LocalDrives::CompleteDriveNotifications(driveNum);
+
+ /* NOTE: We need SetChanged here though the iChanged variable is set in the MSC, since the cache is not getting cleared
+ (inside the CompleteDriveNotifications call) during the initial first notification */
+ TheDrives[driveNum].SetChanged(ETrue);
+
+ if(iStatus != KErrNotSupported)
+ {
+ RequestL();
+ }
+ }
+
+
+