kerneltest/f32test/ext/t_rawext.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/f32test/ext/t_rawext.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,771 @@
+/*
+* Copyright (c) 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:
+*
+*/
+//
+// RAWEXT.CPP
+//
+// This file system extension provides a way to access a drive on the local windows system in "raw format".
+// It can be used to test large files / drives
+// 
+// NB This should be used WITH CARE to avoid unintentionally overwriting or formatting a windows disk
+// 
+
+#include <f32fsys.h>
+
+#include <emulator.h>
+
+#include <windows.h>
+
+#define WIN32_LEAN_AND_MEAN
+#pragma warning (disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
+
+#include <winioctl.h>
+
+// Enable this macro to find the last REMOVABLE drive (e.g. a card reader drive) 
+// (to minimise the chance of doing any damage !
+// Doesn't even try any drive letters below E (again for safety):
+#define __LOOK_FOR_DRIVE__
+
+// Otherwise the windows drive letter can be hard-coded here:
+#ifndef __LOOK_FOR_DRIVE__
+	char diskName[] = "\\\\.\\Y:";
+#endif
+
+
+const TInt KSectorSize = 512;
+
+class CRawWinDiskExtProxyDrive : public CBaseExtProxyDrive
+	{
+public:
+	static CRawWinDiskExtProxyDrive* NewL(CProxyDrive* aProxyDrive, CMountCB* aMount);
+	~CRawWinDiskExtProxyDrive();
+public:
+	virtual TInt Initialise();
+	virtual TInt Dismounted();
+	virtual TInt Enlarge(TInt aLength);
+	virtual TInt ReduceSize(TInt aPos, TInt aLength);
+	virtual TInt Read(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt aThreadHandle,TInt anOffset);
+	virtual TInt Read(TInt64 aPos,TInt aLength,TDes8& aTrg);
+	virtual TInt Write(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt aThreadHandle,TInt anOffset);
+	virtual TInt Write(TInt64 aPos,const TDesC8& aSrc);
+	virtual TInt Caps(TDes8& anInfo);
+	virtual TInt Format(TFormatInfo& anInfo);
+private:
+	CRawWinDiskExtProxyDrive(CProxyDrive* aProxyDrive, CMountCB* aMount);
+private:
+	HANDLE iDeviceHandle; 
+	};
+
+class CRawWinDiskProxyDriveFactory : public CProxyDriveFactory
+	{
+public:
+	CRawWinDiskProxyDriveFactory();
+	virtual TInt Install();			
+	virtual CProxyDrive* NewProxyDriveL(CProxyDrive* aProxy,CMountCB* aMount);
+	};
+
+
+
+CRawWinDiskExtProxyDrive* CRawWinDiskExtProxyDrive::NewL(CProxyDrive* aProxyDrive, CMountCB* aMount)
+//
+//
+//
+	{
+	CRawWinDiskExtProxyDrive* temp=new(ELeave) CRawWinDiskExtProxyDrive(aProxyDrive,aMount);
+	return(temp);
+	}
+
+
+CRawWinDiskExtProxyDrive::CRawWinDiskExtProxyDrive(CProxyDrive* aProxyDrive, CMountCB* aMount):CBaseExtProxyDrive(aProxyDrive,aMount)
+	{
+	RDebug::Print(_L("CRawWinDiskExtProxyDrive::CRawWinDiskExtProxyDrive"));
+	}
+
+CRawWinDiskExtProxyDrive::~CRawWinDiskExtProxyDrive()
+//
+//
+//
+	{
+	CloseHandle(iDeviceHandle);
+	}
+
+TInt CRawWinDiskExtProxyDrive::Initialise()
+//
+//
+//
+	{
+	RDebug::Print(_L("CRawWinDiskExtProxyDrive::Initialise()"));
+
+
+#ifdef __LOOK_FOR_DRIVE__
+	// Find the last REMOVABLE drive (to minimise the chance of doing any damage !
+	// Don't even try any drive letters below E (agai for safety):
+	char diskName[] = "\\\\.\\?:";
+	char driveLetter;
+	for (driveLetter = 'Z'; driveLetter > 'D'; driveLetter--)
+		{
+		diskName[4] = driveLetter;
+
+		DISK_GEOMETRY geometry;
+		DWORD dummy;
+
+		iDeviceHandle = CreateFileA(diskName, 
+			GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 
+			NULL, OPEN_EXISTING, 0, NULL); 
+ 
+		if (iDeviceHandle == INVALID_HANDLE_VALUE) 
+			{
+			continue;
+			}
+
+		if(DeviceIoControl(iDeviceHandle, 
+							IOCTL_DISK_GET_DRIVE_GEOMETRY, 
+							NULL, 
+							0, 
+							&geometry, 
+							sizeof(geometry), 
+							&dummy, 
+							(LPOVERLAPPED)NULL))
+			{
+			RDebug::Print(_L("Drive %c MediaType %d removable %s"), 
+				driveLetter, 
+				geometry.MediaType,
+				geometry.MediaType==RemovableMedia ? _S16("True") : _S16("False"));
+
+			}
+
+		CloseHandle(iDeviceHandle);
+		if (geometry.MediaType==RemovableMedia)
+			break;
+		}
+	if (driveLetter == 'D')
+		return KErrNotFound;
+#endif
+
+	// Creating a handle to drive a: using CreateFile () function ..
+	TPtrC8 diskName8((const TUint8*) diskName);
+	TBuf16<16> diskName16;
+	diskName16.Copy(diskName8);
+	RDebug::Print(_L("RAWEXT: Opening drive %S"), &diskName16);
+
+	iDeviceHandle = CreateFileA(
+//		"\\\\.\\H:",
+		diskName,
+        GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 
+        NULL, OPEN_EXISTING, 0, NULL); 
+ 
+    if (iDeviceHandle == INVALID_HANDLE_VALUE) 
+		{
+		return Emulator::LastError();
+		}
+
+	return KErrNone;
+	}
+
+TInt CRawWinDiskExtProxyDrive::Dismounted()
+//
+//
+//
+	{
+	RDebug::Print(_L("CRawWinDiskExtProxyDrive::Dismounted()"));
+	return(KErrNone);
+	}
+
+TInt CRawWinDiskExtProxyDrive::Enlarge(TInt /*aLength*/)
+//
+//
+//
+	{
+	return(KErrNotSupported);
+	}
+
+
+TInt CRawWinDiskExtProxyDrive::ReduceSize(TInt /*aPos*/, TInt /*aLength*/)
+//
+//
+//
+	{
+	return(KErrNotSupported);
+	}
+
+LOCAL_C TInt ParameterNum(const TInt aMessageHandle, const TAny* aAddress)
+	{
+	RMessage2 tmpMessage(*(RMessagePtr2 *) &aMessageHandle);
+		
+	if (tmpMessage.Ptr0() == aAddress)
+		{
+		return 0;
+		}
+	if (tmpMessage.Ptr1() == aAddress)
+		{
+		return 1;
+		}
+	if (tmpMessage.Ptr2() == aAddress)
+		{
+		return 2;
+		}
+	if (tmpMessage.Ptr3() == aAddress)
+		{
+		return 3;
+		}
+	User::Panic(_L("RAWEXT"),KErrBadHandle);
+	return -1;
+	}
+
+
+
+TInt CRawWinDiskExtProxyDrive::Read(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt aThreadHandle,TInt aOffset)
+//
+//
+//
+	{
+
+	//
+	// Set file position to where we want to read...
+	//
+	LARGE_INTEGER li;
+	li.QuadPart = aPos;
+
+	long posH = I64HIGH(aPos);
+	long posL = I64LOW(aPos);
+
+	TInt off = posL & 0x1FF;
+	posL &= ~0x1FF;
+
+	if(SetFilePointer (iDeviceHandle, posL, &posH, FILE_BEGIN) == 0xFFFFFFFF)
+		{
+		return Emulator::LastError();
+		}
+
+	const TInt KLocalBufferSize = 256*1024;
+	char buffer[KLocalBufferSize];
+
+	const TInt param = ParameterNum(aThreadHandle, aTrg);
+
+	TInt totalBytesRead = 0;
+
+	//
+	// Read first sector if start of data is offset from 512 bytes...
+	//
+	if(off)
+		{
+		// read 1st sector if offset
+		DWORD bytesRead;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesRead, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesRead != KSectorSize)
+			{
+			return KErrNotReady;
+			}
+
+		bytesRead-= off;
+		TInt copyLen = Min(aLength, bytesRead);;
+
+		totalBytesRead += copyLen;
+		aLength -= copyLen;
+
+		TPtrC8 des((TUint8*) buffer+off, copyLen);
+		((RMessagePtr2 *) &aThreadHandle)->Write(param, des, aOffset);
+		}
+
+	//
+	// Read the remainder of the data, accounting for partial last sector...
+	//
+	while(aLength > 0)
+		{
+		TUint32 bytesThisTime = min(KLocalBufferSize, (TUint32)aLength);
+		
+		bytesThisTime = (bytesThisTime + KSectorSize-1) & ~(KSectorSize-1);
+
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, bytesThisTime, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != (TUint32)bytesThisTime)
+			{
+			return KErrNotReady;
+			}
+    
+		TPtrC8 des((TUint8*)buffer, min((TUint32)aLength, bytesThisTime));
+		((RMessagePtr2 *) &aThreadHandle)->Write(param, des, aOffset + totalBytesRead);
+		totalBytesRead += bytesThisTime;
+		aLength -= bytesThisTime;
+		}
+
+	return KErrNone;
+	}
+
+//////
+
+GLDEF_C void DumpBuffer( const TDesC8& aBuffer )
+	/**
+	 * Dump the content of aBuffer in hex
+	 */
+	{
+	static const TText hextab[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
+										'A', 'B', 'C', 'D', 'E', 'F' };
+	const TInt KBytesPerLine = 32;
+	const TInt KCharsPerLine = KBytesPerLine * 2;
+
+	TInt remaining = aBuffer.Length();
+	TUint8* pSrc = const_cast<TUint8*>(aBuffer.Ptr());
+
+	TBuf<KCharsPerLine> line;
+	line.SetLength( KCharsPerLine );	// don't need to print trailing space
+	TInt bytesPerLine = KBytesPerLine;
+	TInt lineOffs = 0;
+	while( remaining )
+		{
+		if( remaining < KBytesPerLine )
+			{
+			bytesPerLine = remaining;
+			line.SetLength( (bytesPerLine*2) );
+			}
+		TUint16* pDest = const_cast<TUint16*>(line.Ptr());
+		remaining -= bytesPerLine;
+		for( TInt i = bytesPerLine; i > 0; --i )
+			{
+			TUint8 c = *pSrc++;
+			*pDest++ = hextab[c >> 4];
+			*pDest++ = hextab[c & 0xF];
+			}
+		_LIT( KFmt, "%06x: %S\n\r" );
+		RDebug::Print( KFmt, lineOffs, &line );
+		lineOffs += bytesPerLine;
+		}
+	}
+
+
+
+TInt CRawWinDiskExtProxyDrive::Read(TInt64 aPos,TInt aLength,TDes8& aTrg)
+//
+//
+//
+	{
+
+	//
+	// Set file position to where we want to read...
+	//
+	LARGE_INTEGER li;
+	li.QuadPart = aPos;
+
+	long posH = I64HIGH(aPos);
+	long posL = I64LOW(aPos);
+
+	TInt off = posL & 0x1FF;
+	posL &= ~0x1FF;
+
+	if(SetFilePointer (iDeviceHandle, posL, &posH, FILE_BEGIN) == 0xFFFFFFFF)
+		{
+		return Emulator::LastError();
+		}
+
+	const TInt KLocalBufferSize = 256*1024;
+	char buffer[KLocalBufferSize];
+
+	TInt totalBytesRead = 0;
+
+	aTrg.SetLength(aLength);
+	
+	//
+	// Read first sector if start of data is offset from 512 bytes...
+	//
+	if(off)
+		{
+		// read 1st sector if offset
+		DWORD bytesRead;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesRead, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesRead != KSectorSize)
+			{
+			return KErrNotReady;
+			}
+
+		bytesRead-= off;
+		TInt copyLen = Min(aLength, bytesRead);;
+
+		totalBytesRead += copyLen;
+		aLength -= copyLen;
+
+		memcpy(&aTrg[0], &buffer[off], copyLen);
+		}
+
+	//
+	// Read the remainder of the data, accounting for partial last sector...
+	//
+	while(aLength > 0)
+		{
+		TInt bytesThisTime = (aLength > KLocalBufferSize) ? KLocalBufferSize : aLength;
+
+		bytesThisTime = (bytesThisTime + KSectorSize-1) & ~(KSectorSize-1);
+
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, bytesThisTime, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != (TUint32)bytesThisTime)
+			 return KErrNotReady;
+		
+		TInt copyLen = aLength < bytesThisTime ? aLength : bytesThisTime;
+
+		memcpy(&aTrg[totalBytesRead], buffer, copyLen);
+
+		totalBytesRead += bytesThisTime;
+		aLength -= bytesThisTime;
+		}
+
+//	DumpBuffer(aTrg);
+	
+	return KErrNone;
+	}
+
+TInt CRawWinDiskExtProxyDrive::Write(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt aThreadHandle,TInt aOffset)
+//
+//
+//
+	{
+	//
+	// Set file position to where we want to write...
+	//
+	long posStartH = I64HIGH(aPos);
+	long posStartL = I64LOW(aPos);
+
+	TInt offStart = posStartL & 0x1FF;
+	posStartL &= ~0x1FF;
+
+	if(SetFilePointer (iDeviceHandle, posStartL, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+		{
+		return Emulator::LastError();
+		}
+
+	const TInt KLocalBufferSize = 256*1024;
+	char buffer[KLocalBufferSize];
+
+	const TInt param = ParameterNum(aThreadHandle, aSrc);
+
+	TInt totalBytesRead = 0;
+
+	//
+	// Read-Modify-Write on first sector if start of data is offset from 512 bytes
+	// or if this is a partial write...
+	//
+	if(offStart || aLength < KSectorSize)
+		{
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != KSectorSize)
+			{
+			return KErrNotReady;
+			}
+
+		totalBytesRead = min(KSectorSize-offStart, aLength);
+		aLength -= totalBytesRead;
+
+		TPtr8 des((TUint8*)&buffer[offStart], totalBytesRead);
+		((RMessagePtr2 *) &aThreadHandle)->Read(param, des, aOffset);
+
+		if(SetFilePointer (iDeviceHandle, posStartL, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+			{
+			return Emulator::LastError();
+			}
+
+		if (!WriteFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+		}
+
+	//
+	// Write the remainder of the aligned data
+	//
+	while(aLength >= KSectorSize)
+		{
+		TInt alignedLength = aLength & ~0x1FF;
+
+		TInt bytesThisTime = (alignedLength > KLocalBufferSize) ? KLocalBufferSize : alignedLength;
+
+		TPtr8 des((TUint8*)buffer, bytesThisTime);
+		((RMessagePtr2 *) &aThreadHandle)->Read(param, des, aOffset + totalBytesRead);
+	
+		DWORD bytesWritten;    
+		if (!WriteFile (iDeviceHandle, buffer, bytesThisTime, &bytesWritten, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesWritten != (TUint32)bytesThisTime)
+			{
+			return KErrNotReady;
+			}
+		
+		totalBytesRead += bytesThisTime;
+		aLength -= bytesThisTime;
+		}
+
+	//
+	// Read-Modify-Write on the last block if a partial write...
+	//
+	if(aLength > 0)
+		{
+		DWORD curPos = SetFilePointer(iDeviceHandle, 0, NULL, FILE_CURRENT);
+
+		// RMW last sector if offset
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != KSectorSize)
+			 return KErrNotReady;
+
+		TPtr8 des((TUint8*)buffer, aLength);
+		((RMessagePtr2 *) &aThreadHandle)->Read(param, des, aOffset + totalBytesRead);
+
+		if(SetFilePointer (iDeviceHandle, curPos, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+			{
+			return Emulator::LastError();
+			}
+
+		if (!WriteFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+		}
+
+	return KErrNone;
+	}
+
+TInt CRawWinDiskExtProxyDrive::Write(TInt64 aPos,const TDesC8& aSrc)
+//
+//
+//
+	{
+	//
+	// Set file position to where we want to write...
+	//
+	TInt length = aSrc.Length();
+
+	long posStartH = I64HIGH(aPos);
+	long posStartL = I64LOW(aPos);
+
+	TInt offStart = posStartL & 0x1FF;
+	posStartL &= ~0x1FF;
+
+	if(SetFilePointer (iDeviceHandle, posStartL, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+		{
+		return Emulator::LastError();
+		}
+
+	const TInt KLocalBufferSize = 256*1024;
+	char buffer[KLocalBufferSize];
+
+	TInt totalBytesRead = 0;
+
+	//
+	// Read-Modify-Write on first sector if start of data is offset from 512 bytes
+	// or if this is a partial write...
+	//
+	if(offStart || length < KSectorSize)
+		{
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != KSectorSize)
+			{
+			return KErrNotReady;
+			}
+
+		totalBytesRead = min(KSectorSize-offStart, length);
+		length -= totalBytesRead;
+
+		memcpy(&buffer[offStart], &aSrc[0], totalBytesRead);
+
+		if(SetFilePointer (iDeviceHandle, posStartL, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+			{
+			return Emulator::LastError();
+			}
+
+		if (!WriteFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+		}
+
+	//
+	// Write the remainder of the aligned data
+	//
+	while(length >= KSectorSize)
+		{
+		TInt alignedLength = length & ~0x1FF;
+
+		TInt bytesThisTime = (alignedLength > KLocalBufferSize) ? KLocalBufferSize : alignedLength;
+
+		memcpy(buffer, &aSrc[totalBytesRead], bytesThisTime);
+	
+		DWORD bytesWritten;    
+		if (!WriteFile (iDeviceHandle, buffer, bytesThisTime, &bytesWritten, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesWritten != (TUint32)bytesThisTime)
+			{
+			return KErrNotReady;
+			}
+		
+		totalBytesRead += bytesThisTime;
+		length -= bytesThisTime;
+		}
+
+	//
+	// Read-Modify-Write on the last block if a partial write...
+	//
+	if(length > 0)
+		{
+		DWORD curPos = SetFilePointer(iDeviceHandle, 0, NULL, FILE_CURRENT);
+
+		// RMW last sector if offset
+		DWORD bytesread;    
+		if (!ReadFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+
+		if(bytesread != KSectorSize)
+			 return KErrNotReady;
+
+		memcpy(&buffer[0], &aSrc[totalBytesRead], length);
+
+		if(SetFilePointer (iDeviceHandle, curPos, &posStartH, FILE_BEGIN) == 0xFFFFFFFF)
+			{
+			return Emulator::LastError();
+			}
+
+		if (!WriteFile (iDeviceHandle, buffer, KSectorSize, &bytesread, NULL) )
+			{
+			return Emulator::LastError();
+			}
+		}
+
+
+	return KErrNone;
+	}
+
+TInt CRawWinDiskExtProxyDrive::Caps(TDes8& anInfo)
+//
+//
+//
+	{
+	TLocalDriveCapsV3Buf caps;
+	TInt err = CBaseExtProxyDrive::Caps(caps);
+	if(err == KErrNone)
+		{
+		DISK_GEOMETRY geometry;
+		DWORD dummy;
+
+		if(!DeviceIoControl(iDeviceHandle, 
+							IOCTL_DISK_GET_DRIVE_GEOMETRY, 
+							NULL, 
+							0, 
+							&geometry, 
+							sizeof(geometry), 
+							&dummy, 
+							(LPOVERLAPPED)NULL))
+			{
+			return KErrNotReady;
+			}
+
+		caps().iExtraInfo = EFalse;
+
+		// NB This seems to be incorrect:If the disk is formatted by Windows, then the 
+		// number of sectors reported in the Boot Sector is slightly greater than
+		// the number of sectors calculated here ... resulting in CheckDisk failures.
+		// One solution is to ensure the disk is formatted by the emulator.
+		caps().iSize =	MAKE_TINT64(geometry.Cylinders.HighPart, geometry.Cylinders.LowPart) * 
+									geometry.TracksPerCylinder * 
+									geometry.SectorsPerTrack * 
+									geometry.BytesPerSector;
+
+		anInfo = caps.Left(Min(caps.Length(),anInfo.MaxLength()));
+		}
+
+	return(err);
+	}
+
+TInt CRawWinDiskExtProxyDrive::Format(TFormatInfo& /*anInfo*/)
+//
+//
+//
+	{
+	return(KErrEof);
+	}
+
+
+CRawWinDiskProxyDriveFactory::CRawWinDiskProxyDriveFactory()
+//
+//
+//
+	{
+	RDebug::Print(_L("CRawWinDiskProxyDriveFactory::CRawWinDiskProxyDriveFactory"));
+	}
+
+TInt CRawWinDiskProxyDriveFactory::Install()
+//
+//
+//
+	{
+	_LIT(KLoggerName,"RAWEXT");
+	return(SetName(&KLoggerName));
+	}
+
+
+CProxyDrive* CRawWinDiskProxyDriveFactory::NewProxyDriveL(CProxyDrive* aProxy,CMountCB* aMount)
+//
+//
+//
+	{
+	return(CRawWinDiskExtProxyDrive::NewL(aProxy,aMount));
+	}
+
+extern "C" {
+
+EXPORT_C CProxyDriveFactory* CreateFileSystem()
+//
+// Create a new file system
+//
+	{
+	return(new CRawWinDiskProxyDriveFactory());
+	}
+}
+