// Copyright (c) 2004-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:
// CMassStorageMountCB implementation.
// 
//

/**
 @file
 @internalTechnology
*/

#include <f32fsys.h>
#include <f32file.h>
#include "cmassstoragemountcb.h"
#include "cmassstoragefilesystem.h"
#include "drivemanager.h"
#include "massstoragedebug.h"
#include "massstorageutil.h"

CMassStorageMountCB::CMassStorageMountCB(const RArray<TInt>& aDriveMapping)
    : iDriveMapping(aDriveMapping)
	{
	}

CMassStorageMountCB* CMassStorageMountCB::NewL(const RArray<TInt>& aDriveMapping)
	{
	return new (ELeave) CMassStorageMountCB(aDriveMapping);
	}

/**
Checks that the drive number is supported.

@leave KErrNotReady The drive number is not supported.
*/
TInt CMassStorageMountCB::CheckDriveNumberL()
	{
	__FNLOG("CMassStorageMountCB::CheckDriveNumberL");
	TInt driveNumber;
	driveNumber = Drive().DriveNumber();
	if (!IsValidLocalDriveMapping(driveNumber))
		{
		__PRINT1(_L("CMassStorageMountCB::CheckDriveNumberL: Drive number %d not supported"), driveNumber);
		User::Leave(KErrNotReady);
		}
	__PRINT1(_L("CMassStorageMountCB::CheckDriveNumberL: Drive number = %d"), driveNumber);
	return driveNumber;
	}

/**
Registers the drive with the Mass Storage drive manager.

@leave KErrNotSupported The drive is not compatible with Mass Storage.
*/
void CMassStorageMountCB::MountL(TBool /*aForceMount*/)
	{
	__FNLOG("CMassStorageMountCB::MountL");

	CheckDriveNumberL();
	CMassStorageFileSystem& msFsys = *reinterpret_cast<CMassStorageFileSystem*>(Drive().GetFSys());

	TInt lun = DriveNumberToLun(Drive().DriveNumber());

	if(lun < 0)
		{
		// This is not a supported Mass Storage drive
		User::Leave(KErrNotSupported);
		}

	TBusLocalDrive& localDrive = msFsys.iLocalDriveForMediaFlag[lun];

	TInt err = CreateLocalDrive(localDrive);
	User::LeaveIfError(err);

	CProxyDrive* proxyDrive = LocalDrive();

	TLocalDriveCapsV2Buf caps;
	err = localDrive.Caps(caps);
	//Make sure file system is FAT and removable
	if (err == KErrNone)
		{
		err = KErrNotSupported;
		if ((caps().iDriveAtt & KDriveAttRemovable) == KDriveAttRemovable)
			{
			if (caps().iType != EMediaNotPresent)
				{
				err = KErrNone;
				}
			}
		}

	if (err != KErrNone && err != KErrNotReady)
		{
		__PRINT1(_L("CMassStorageMountCB::MountL: Drive is not compatible with Mass Storage, err=%d"), err);
		User::Leave(err);
		}

	__PRINT(_L("CMassStorageMountCB::MountL: Registering drive"));

	// Set media changed to true so that Win2K doesn't used cached drive data
	(*msFsys.iMediaChanged)[lun] = ETrue;

	msFsys.Controller().DriveManager().RegisterDrive(*proxyDrive, (*msFsys.iMediaChanged)[lun], lun);

	SetVolumeName(_L("MassStorage").AllocL());
	}

/**
Returns the LUN that corresponds to the specified drive number.

@param aDriveNumber The drive number.
*/
TInt CMassStorageMountCB::DriveNumberToLun(TInt aDriveNumber)
	{
	__FNLOG("CMassStorageMountCB::DriveNumberToLun");
	TInt lun = -1;
	for (TInt i = 0; i < iDriveMapping.Count(); i++)
		{
		if (iDriveMapping[i] == aDriveNumber)
			{
			lun = i;
			break;
			}
		}
	__PRINT2(_L("CMassStorageMountCB::DriveNumberToLun: Drive %d maps to LUN %d"), aDriveNumber, lun);
	return lun;
	}

/**
Deregisters the drive from the Drive Manager.
*/
void CMassStorageMountCB::Dismounted()
	{
	__FNLOG("CMassStorageMountCB::Dismounted");
	TInt driveNumber = -1;
	TRAPD(err, driveNumber = CheckDriveNumberL());
	if (err != KErrNone)
		{
		return;
		}
	__PRINT(_L("CMassStorageMountCB::Dismounted: Deregistering drive"));
    CMassStorageFileSystem& msFsys = *reinterpret_cast<CMassStorageFileSystem*>(Drive().GetFSys());
	msFsys.Controller().DriveManager().DeregisterDrive(DriveNumberToLun(driveNumber));
	
	DismountedLocalDrive();
	}

/**
Unlocks the drive with the specified password, optionally storing the password for later use.

@param aPassword The password to use for unlocking the drive.
@param aStore True if the password is to be stored.
*/
TInt CMassStorageMountCB::Unlock(TMediaPassword& aPassword, TBool aStore)
	{
	__FNLOG("CMassStorageMountCB::Unlock");
	TInt driveNumber = -1;
	TRAPD(err, driveNumber = CheckDriveNumberL());
	if (err != KErrNone)
		{
		return err;
		}
	TBusLocalDrive& localDrive=GetLocalDrive(driveNumber);
	if(localDrive.Status() == KErrLocked)
		{
		localDrive.Status() = KErrNotReady;
		}
	TInt r = localDrive.Unlock(aPassword, aStore);
	if(r == KErrNone && aStore)
		{
		WritePasswordData();
		}
	return(r);
	}

/**
Stores the password for the drive to the password file.
*/
void CMassStorageMountCB::WritePasswordData()
	{
	__FNLOG("CMassStorageMountCB::WritePasswordData");
	TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
	TInt length = local.PasswordStoreLengthInBytes();
	if(length==0)
		{
		TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
		mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
		WriteToDisk(mediaPWrdFile,_L8(""));
		return;
		}
	HBufC8* hDes=HBufC8::New(length);
	if(hDes==NULL)
		{
		return;
		}
	TPtr8 pDes=hDes->Des();
	TInt r=local.ReadPasswordData(pDes);
	if(r==KErrNone)
		{
		TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
		mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
		WriteToDisk(mediaPWrdFile,pDes);
		}
	delete hDes;
	}

/**
Make sure that the file system is fat.
*/
TBool CMassStorageMountCB::ValidateBootSector()
	{
	__FNLOG("CMassStorageMountCB::ValidateBootSector");

	TFatBootSector bootSector;
	TInt r=ReadBootSector(bootSector);
	__PRINT1(_L("CMassStorageMountCB::MountL - ReadBootSector returned %d"),r);
	if (r != KErrNone)
		{
		return EFalse;
		}

	__PRINT(_L("\nBootSector info"));
	__PRINT8BIT1(_L("FAT type = %S"),bootSector.FileSysType());
	__PRINT8BIT1(_L("Vendor ID = %S"),bootSector.VendorId());
	__PRINT1(_L("BytesPerSector %d"),bootSector.BytesPerSector());
	__PRINT1(_L("SectorsPerCluster %d"),bootSector.SectorsPerCluster());
	__PRINT1(_L("ReservedSectors %d"),bootSector.ReservedSectors());
	__PRINT1(_L("NumberOfFats %d"),bootSector.NumberOfFats());
	__PRINT1(_L("RootDirEntries %d"),bootSector.RootDirEntries());
	__PRINT1(_L("Total Sectors = %d"),bootSector.TotalSectors());
	__PRINT1(_L("MediaDescriptor = 0x%x"),bootSector.MediaDescriptor());
	__PRINT1(_L("FatSectors %d"),bootSector.FatSectors());
	__PRINT1(_L("SectorsPerTrack %d"),bootSector.SectorsPerTrack());
	__PRINT1(_L("NumberOfHeads %d"),bootSector.NumberOfHeads());
	__PRINT1(_L("HugeSectors %d"),bootSector.HugeSectors());
	__PRINT1(_L("Fat32 Sectors %d"),bootSector.FatSectors32());
	__PRINT1(_L("Fat32 Flags %d"),bootSector.FATFlags());
	__PRINT1(_L("Fat32 Version Number %d"),bootSector.VersionNumber());
	__PRINT1(_L("Root Cluster Number %d"),bootSector.RootClusterNum());
	__PRINT1(_L("FSInfo Sector Number %d"),bootSector.FSInfoSectorNum());
	__PRINT1(_L("Backup Boot Rec Sector Number %d"),bootSector.BkBootRecSector());
	__PRINT1(_L("PhysicalDriveNumber %d"),bootSector.PhysicalDriveNumber());
	__PRINT1(_L("ExtendedBootSignature %d"),bootSector.ExtendedBootSignature());
	__PRINT1(_L("UniqueID %d"),bootSector.UniqueID());
	__PRINT8BIT1(_L("VolumeLabel %S"),bootSector.VolumeLabel());
	__PRINT8BIT1(_L("FileSysType %S\n"),bootSector.FileSysType());

    iUniqueID=bootSector.UniqueID();
	iIs16BitFat=bootSector.Is16BitFat();

	iIs32BitFat=bootSector.Is32BitFat();
	switch (DetermineFatType(bootSector))
		{
		case 12:
			iIs16BitFat = EFalse;
			iIs32BitFat = EFalse;
			break;
		case 16:
			iIs16BitFat = ETrue;
			iIs32BitFat = EFalse;
			break;
		case 32:
			iIs16BitFat = EFalse;
			iIs32BitFat = ETrue;
			break;
		default:
			return EFalse;
		}

	TInt sectorsPerCluster=bootSector.SectorsPerCluster();
	if (!IsPowerOfTwo(sectorsPerCluster))
		return EFalse;

	TInt sectorSizeLog2=Log2(bootSector.BytesPerSector());
	if (sectorSizeLog2<0 || !IsPowerOfTwo(bootSector.BytesPerSector()))
		return EFalse;

	TInt firstFatSector=bootSector.ReservedSectors();
	if (firstFatSector<1)
		return EFalse;

	TInt fatSizeInBytes;
	if(iIs32BitFat)
		{
		fatSizeInBytes=bootSector.FatSectors32()*bootSector.BytesPerSector();
		if (fatSizeInBytes<bootSector.BytesPerSector())
			return EFalse;
		}
	else
		{
		fatSizeInBytes=bootSector.FatSectors()*bootSector.BytesPerSector();
		if (fatSizeInBytes<bootSector.BytesPerSector())
			return EFalse;

		TInt rootDirectorySector=firstFatSector+bootSector.FatSectors()*bootSector.NumberOfFats();
		if (rootDirectorySector<3)
			return EFalse;

		TInt rootDirSizeInBytes=bootSector.RootDirEntries()*KSizeOfFatDirEntry;
		TInt numOfRootDirSectors=(rootDirSizeInBytes+(1<<sectorSizeLog2)-1)>>sectorSizeLog2;
		TInt rootDirEnd=(rootDirectorySector+numOfRootDirSectors)<<sectorSizeLog2;
		if (rootDirEnd<(4<<sectorSizeLog2))
			return EFalse;
		}


	TInt totalSectors=bootSector.TotalSectors();
	if (totalSectors==0)
		totalSectors=bootSector.HugeSectors();
	if (totalSectors<5)
		return EFalse;

	TInt numberOfFats=bootSector.NumberOfFats();
	if (numberOfFats<1)
		return EFalse;

	return ETrue;
	}

/**
Read non aligned boot data from media into TFatBootSector structure

@param aBootSector refrence to TFatBootSector populate
@return Media read error code
*/
TInt CMassStorageMountCB::ReadBootSector(TFatBootSector& aBootSector)
	{
	__FNLOG("CMassStorageMountCB::ReadBootSector");
	TInt pos=0;
	TUint8 data[KSizeOfFatBootSector];
    TPtr8 buf(&data[0],KSizeOfFatBootSector);
    TInt r=LocalDrive()->Read(0,KSizeOfFatBootSector,buf);
	if (r!=KErrNone)
		{
		__PRINT1(_L("LocalDrive::Read() failed - %d"),r);
		return(r);
		}
//	0	TUint8 iJumpInstruction[3]
	Mem::Copy(&aBootSector.iJumpInstruction,&data[pos],3);
	pos+=3;
// 3	TUint8 iVendorId[KVendorIdSize]
	Mem::Copy(&aBootSector.iVendorId,&data[pos],KVendorIdSize);
	pos+=KVendorIdSize;
// 11	TUint16 iBytesPerSector
	Mem::Copy(&aBootSector.iBytesPerSector,&data[pos],2);
	pos+=2;
// 13	TUint8 sectorsPerCluster
	Mem::Copy(&aBootSector.iSectorsPerCluster,&data[pos],1);
	pos+=1;
// 14	TUint16 iReservedSectors
	Mem::Copy(&aBootSector.iReservedSectors,&data[pos],2);
	pos+=2;
// 16	TUint8 numberOfFats
	Mem::Copy(&aBootSector.iNumberOfFats,&data[pos],1);
	pos+=1;
// 17	TUint16 iRootDirEntries
	Mem::Copy(&aBootSector.iRootDirEntries,&data[pos],2);
	pos+=2;
// 19	TUint16 totalSectors
	Mem::Copy(&aBootSector.iTotalSectors,&data[pos],2);
	pos+=2;
// 21	TUint8 iMediaDescriptor
	Mem::Copy(&aBootSector.iMediaDescriptor,&data[pos],1);
	pos+=1;
// 22	TUint16 iFatSectors
	Mem::Copy(&aBootSector.iFatSectors,&data[pos],2);
	pos+=2;
// 24	TUint16 iSectorsPerTrack
	Mem::Copy(&aBootSector.iSectorsPerTrack,&data[pos],2);
	pos+=2;
// 26	TUint16 iNumberOfHeads
	Mem::Copy(&aBootSector.iNumberOfHeads,&data[pos],2);
	pos+=2;
// 28	TUint32 iHiddenSectors
	Mem::Copy(&aBootSector.iHiddenSectors,&data[pos],4);
	pos+=4;
// 32	TUint32 iHugeSectors
	Mem::Copy(&aBootSector.iHugeSectors,&data[pos],4);
	pos+=4;

	if(aBootSector.iRootDirEntries == 0)	//indicates we have FAT32 volume
		{
		__PRINT(_L("\nFile system thinks Fat32"));

		//36 TUint32 iFatSectors32
		Mem::Copy(&aBootSector.iFatSectors32, &data[pos],4);
		pos+=4;
		//40 TUint16 iFATFlags
		Mem::Copy(&aBootSector.iFATFlags, &data[pos],2);
		pos+=2;
		//42 TUint16 iVersionNumber
		Mem::Copy(&aBootSector.iVersionNumber, &data[pos],2);
		pos+=2;
		//44 TUint32 iRootClusterNum
		Mem::Copy(&aBootSector.iRootClusterNum, &data[pos],4);
		pos+=4;
		//48 TUint16 iFSInfoSectorNum
		Mem::Copy(&aBootSector.iFSInfoSectorNum, &data[pos],2);
		pos+=2;
		//50 TUint16 iBkBootRecSector
		Mem::Copy(&aBootSector.iBkBootRecSector, &data[pos],2);
		pos+=(2+12);//extra 12 for the reserved bytes
		}

// 36|64	TUint8 iPhysicalDriveNumber
	Mem::Copy(&aBootSector.iPhysicalDriveNumber,&data[pos],1);
	pos+=1;
// 37|65	TUint8 iReserved
	Mem::Copy(&aBootSector.iReserved,&data[pos],1);
	pos+=1;
// 38|66	TUint8 iExtendedBootSignature
	Mem::Copy(&aBootSector.iExtendedBootSignature,&data[pos],1);
	pos+=1;
// 39|67	TUint32 iUniqueID
	Mem::Copy(&aBootSector.iUniqueID,&data[pos],4);
	pos+=4;
// 43|71	TUint8 iVolumeLabel[KVolumeLabelSize]
	Mem::Copy(&aBootSector.iVolumeLabel,&data[pos],KVolumeLabelSize);
	pos+=KVolumeLabelSize;
// 54|82	TUint8 iFileSysType[KFileSysTypeSize]
	Mem::Copy(&aBootSector.iFileSysType,&data[pos],KFileSysTypeSize);
// 62|90

	return(KErrNone);
	}

/**
Work out if we have a FAT12|16|32 volume.
Returns 12, 16 or 32 as appropriate.
Returns 0 if can't be calculated (invalid values)
*/
TInt CMassStorageMountCB::DetermineFatType(TFatBootSector& aBootSector)
	{
	TUint32 ressectors = aBootSector.ReservedSectors();

	if (aBootSector.SectorsPerCluster() < 1)
		return 0;

	if (aBootSector.RootDirEntries() != 0)
		{
		TUint32 rootdirbytes;
		rootdirbytes = aBootSector.RootDirEntries() * 32 + aBootSector.BytesPerSector() - 1;
		ressectors += rootdirbytes / aBootSector.BytesPerSector();
		}

	if (aBootSector.FatSectors() != 0)
		ressectors += aBootSector.NumberOfFats() * aBootSector.FatSectors();
	else
		ressectors += aBootSector.NumberOfFats() * aBootSector.FatSectors32();

	TUint32 totalsectors;
	if (aBootSector.TotalSectors() != 0)
		totalsectors = aBootSector.TotalSectors();
	else
		totalsectors = aBootSector.HugeSectors();

	if (ressectors < 1 || totalsectors < 1)
		return 0;

	TUint32 datasec;
	datasec = totalsectors - ressectors;

	TUint32 countofclusters;
	countofclusters = datasec / aBootSector.SectorsPerCluster();

	__PRINT1(_L("CFatMountCB: Count of clusters = %d\n"), countofclusters);

	if (countofclusters < 4085)
		{
		return 12;
		}
	else if (countofclusters < 65525)
		{
		return 16;
		}
	else
		{
		return 32;
		}
	}

TInt CMassStorageMountCB::ReMount()
	{
	return KErrNotReady;
	}

void CMassStorageMountCB::VolumeL(TVolumeInfo& /*aVolume*/) const
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::SetVolumeL(TDes& /*aName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::MkDirL(const TDesC& /*aName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::RmDirL(const TDesC& /*aName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::DeleteL(const TDesC& /*aName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::RenameL(const TDesC& /*anOldName*/,const TDesC& /*anNewName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::ReplaceL(const TDesC& /*anOldName*/,const TDesC& /*anNewName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::EntryL(const TDesC& /*aName*/,TEntry& /*anEntry*/) const
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::SetEntryL(const TDesC& /*aName*/,const TTime& /*aTime*/,TUint /*aSetAttMask*/,TUint /*aClearAttMask*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::FileOpenL(const TDesC& /*aName*/,TUint /*aMode*/,TFileOpen /*anOpen*/,CFileCB* /*aFile*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::DirOpenL(const TDesC& /*aName*/,CDirCB* /*aDir*/)
	{
	User::Leave(KErrNotReady);
	}


void CMassStorageMountCB::RawReadL(TInt64 /*aPos*/,TInt /*aLength*/,const TAny* /*aTrg*/,TInt /*anOffset*/,const RMessagePtr2& /*aMessage*/) const
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::RawWriteL(TInt64 /*aPos*/,TInt /*aLength*/,const TAny* /*aSrc*/,TInt /*anOffset*/,const RMessagePtr2& /*aMessage*/)
	{
	User::Leave(KErrNotReady);
	}


void CMassStorageMountCB::GetShortNameL(const TDesC& /*aLongName*/,TDes& /*aShortName*/)
	{
	User::Leave(KErrNotReady);
	}

void CMassStorageMountCB::GetLongNameL(const TDesC& /*aShorName*/,TDes& /*aLongName*/)
	{
	User::Leave(KErrNotReady);
	}

#if defined(_DEBUG)
TInt CMassStorageMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
//
// Debug function
//
	{
	if(aCommand>=(KMaxTInt/2))
		return LocalDrive()->ControlIO(aMessage,aCommand-(KMaxTInt/2),aParam1,aParam2);
	else
		return KErrNotSupported;
	}
#else
TInt CMassStorageMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
	{return(KErrNotSupported);}
#endif

void CMassStorageMountCB::ReadSectionL(const TDesC& /*aName*/,TInt /*aPos*/,TAny* /*aTrg*/,TInt /*aLength*/,const RMessagePtr2& /*aMessage*/)
	{
	User::Leave(KErrNotReady);
	}

