1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/boardsupport/emulator/emulatorbsp/win_drive/win_media_device.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1104 @@
1.4 +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// Implementation of the classes that work directly with the windows devices - files, drives etc.
1.18 +//
1.19 +//
1.20 +
1.21 +/**
1.22 + @file
1.23 +*/
1.24 +
1.25 +#include "win_media_device.h"
1.26 +
1.27 +#ifndef INVALID_SET_FILE_POINTER
1.28 +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
1.29 +#endif
1.30 +
1.31 +
1.32 +const TInt KDiskOpError = 0x134AFF78; ///< internal Disk operation error ID.
1.33 +
1.34 +
1.35 +static TBool CheckBufFill(const TPtrC8& aBufPtr, TUint8 aFill);
1.36 +
1.37 +//#########################################################################################################################
1.38 +//## CWinMediaDeviceBase abstract base class implementation
1.39 +//#########################################################################################################################
1.40 +
1.41 +//-----------------------------------------------------------------------------
1.42 +
1.43 +CWinMediaDeviceBase::CWinMediaDeviceBase()
1.44 +{
1.45 + iDevHandle = NULL;
1.46 + ipScratchBuf = NULL;
1.47 +}
1.48 +
1.49 +CWinMediaDeviceBase::~CWinMediaDeviceBase()
1.50 +{
1.51 + Disconnect();
1.52 +}
1.53 +
1.54 +
1.55 +/**
1.56 + Disconnect from the media device
1.57 +*/
1.58 +void CWinMediaDeviceBase::Disconnect()
1.59 +{
1.60 + FlushFileBuffers(iDevHandle);
1.61 + CloseHandle(iDevHandle);
1.62 + iDevHandle = NULL;
1.63 +
1.64 +}
1.65 +
1.66 +//-----------------------------------------------------------------------------
1.67 +
1.68 +/**
1.69 + "Erase" a region of the media. Effectively just fills the selected region with the specified pattern.
1.70 +
1.71 + @param aPos media position start
1.72 + @param aLength length of the media region to fill
1.73 + @param aFill filler byte.
1.74 +
1.75 + @return EPOC error code.
1.76 +
1.77 +*/
1.78 +TInt CWinMediaDeviceBase::Erase(TInt64 aPos, TUint32 aLength, TUint8 aFill)
1.79 +{
1.80 + //-- this method is called to "format" media.
1.81 + //-- Because Windows is absolute sux on everythins that concerns formattin the media (IOCTL_DISK_FORMAT_TRACKS seems to be applicable only
1.82 + //-- to floppy disks) we have to perform formatting by just filling media region with a given byte.
1.83 + //-- This can be very slow for flash - based removable media (e.g. usb flash drives) so, there is a possibility to check
1.84 + //-- if the given media region is already filled with the pattern and write only if not. See bCheckReadBeforeErase switch.
1.85 +
1.86 + Mem::Fill(ipScratchBuf, KScratchBufSz, aFill);
1.87 +
1.88 + TUint32 rem = aLength;
1.89 + TInt nRes = KErrNone;
1.90 +
1.91 + //-- if True, we firstly will read media region and check if it is already filled with the given byte.
1.92 + //-- this is useful for slow - write media or sparse files on NTFS.
1.93 + //TBool bCheckReadBeforeErase = EFalse;
1.94 + TBool bCheckReadBeforeErase = ETrue;
1.95 +
1.96 + while(rem)
1.97 + {
1.98 + const TUint32 bytesToWrite = Min(KScratchBufSz, rem);
1.99 + TPtr8 ptrData(ipScratchBuf, bytesToWrite, bytesToWrite);
1.100 +
1.101 + if(bCheckReadBeforeErase)
1.102 + {//-- try to read data first and check if we need to write anything
1.103 + ptrData.SetLength(0);
1.104 + nRes = Read(aPos, bytesToWrite, ptrData);
1.105 + if(nRes != KErrNone)
1.106 + break;
1.107 +
1.108 + if(!CheckBufFill(ptrData, aFill))
1.109 + {
1.110 + Mem::Fill(ipScratchBuf, KScratchBufSz, aFill);
1.111 +
1.112 + nRes = Write(aPos, bytesToWrite, ptrData);
1.113 + if(nRes != KErrNone)
1.114 + break;
1.115 +
1.116 + }
1.117 + }
1.118 + else
1.119 + {//-- no need to read first
1.120 + nRes = Write(aPos, bytesToWrite, ptrData);
1.121 + if(nRes != KErrNone)
1.122 + break;
1.123 +
1.124 + }
1.125 +
1.126 +
1.127 + rem-=bytesToWrite;
1.128 + aPos+=bytesToWrite;
1.129 +
1.130 + }
1.131 +
1.132 +
1.133 + return nRes;
1.134 +}
1.135 +
1.136 +
1.137 +//#########################################################################################################################
1.138 +//## CWinVolumeDevice class implementation
1.139 +//#########################################################################################################################
1.140 +
1.141 +
1.142 +CWinVolumeDevice::CWinVolumeDevice()
1.143 + :CWinMediaDeviceBase()
1.144 +{
1.145 + //-- create scratch buffer
1.146 + ipScratchBuf = ::new TUint8[KScratchBufSz];
1.147 + ASSERT(ipScratchBuf);
1.148 +}
1.149 +
1.150 +CWinVolumeDevice::~CWinVolumeDevice()
1.151 +{
1.152 + delete ipScratchBuf;
1.153 +}
1.154 +
1.155 +//-----------------------------------------------------------------------------
1.156 +
1.157 +/**
1.158 + Open the device and do some initalisation work.
1.159 +
1.160 + @param aParams device parameters
1.161 + @return Epoc error code, KErrNone if everything is OK
1.162 +*/
1.163 +TInt CWinVolumeDevice::Connect(const TMediaDeviceParams& aParams)
1.164 +{
1.165 +
1.166 + __PRINT(_L("#-- CWinVolumeDevice::Connect()"));
1.167 +
1.168 + if(!aParams.ipDevName)
1.169 + {
1.170 + __LOG(_L("#-- CWinVolumeDevice::Connect() device name is not set!"));
1.171 + return KErrBadName;
1.172 + }
1.173 +
1.174 + __PRINTF(aParams.ipDevName);
1.175 +
1.176 + ASSERT(!HandleValid() && ipScratchBuf);
1.177 +
1.178 + //-- open the device
1.179 + DWORD dwAccess = GENERIC_READ;
1.180 +
1.181 + if(!aParams.iReadOnly)
1.182 + dwAccess |= GENERIC_WRITE;
1.183 +
1.184 + iDevHandle = CreateFileA(aParams.ipDevName,
1.185 + dwAccess,
1.186 + FILE_SHARE_READ,
1.187 + (LPSECURITY_ATTRIBUTES)NULL,
1.188 + OPEN_EXISTING,
1.189 + FILE_ATTRIBUTE_NORMAL,
1.190 + NULL);
1.191 +
1.192 + if(!HandleValid())
1.193 + {
1.194 + __LOG1(_L("#-- CWinVolumeDevice::Connect() Error creating device handle! WinErr:%d"), GetLastError());
1.195 + return KErrGeneral;
1.196 + }
1.197 +
1.198 + //-- find out device geometry
1.199 + iMediaType = Unknown;
1.200 + iDrvGeometry.iBytesPerSector = KDefaultSectorSz;
1.201 +
1.202 + DWORD junk;
1.203 +
1.204 + //-- 1. try to query disk geometry, but it can produce wrong results for partitioned media
1.205 + BOOL bResult = DeviceIoControl(Handle(),
1.206 + IOCTL_DISK_GET_DRIVE_GEOMETRY,
1.207 + NULL, 0,
1.208 + ipScratchBuf, KScratchBufSz,
1.209 + &junk, (LPOVERLAPPED)NULL);
1.210 +
1.211 + if(bResult)
1.212 + {
1.213 + const DISK_GEOMETRY& dg = (const DISK_GEOMETRY&)*ipScratchBuf;
1.214 +
1.215 + iDrvGeometry.iBytesPerSector = dg.BytesPerSector;
1.216 + iMediaType = dg.MediaType;
1.217 +
1.218 + __PRINT3(_L("#-- dev geometry: Cyl:%d Heads:%d Sectors:%d"), dg.Cylinders.LowPart, dg.TracksPerCylinder, dg.SectorsPerTrack);
1.219 + __PRINT2(_L("#-- dev geometry: MediaType:%d, bps:%d"), dg.MediaType, dg.BytesPerSector);
1.220 +
1.221 + }
1.222 + else
1.223 + {
1.224 + iMediaType = Unknown;
1.225 + iDrvGeometry.iBytesPerSector = KDefaultSectorSz;
1.226 +
1.227 + __LOG1(_L("#-- CWinVolumeDevice::Connect() IOCTL_DISK_GET_DRIVE_GEOMETRY WinError:%d !"), GetLastError());
1.228 + }
1.229 +
1.230 + //-- 1.1 check "bytes per sector" value and how it corresponds to the request from parameters
1.231 + if(aParams.iDrvGeometry.iBytesPerSector == 0)
1.232 + {//-- do nothing, this parameter is not set in config file, use media's
1.233 + }
1.234 + else if(aParams.iDrvGeometry.iBytesPerSector != iDrvGeometry.iBytesPerSector)
1.235 + {//-- we can't set "SectorSize" value for the physical media
1.236 + __LOG1(_L("#-- CWinVolumeDevice::Connect() can not use 'Sec. Size' value from config:%d !"), aParams.iDrvGeometry.iBytesPerSector);
1.237 + Disconnect();
1.238 + return KErrArgument;
1.239 + }
1.240 +
1.241 +
1.242 + ASSERT(IsPowerOf2(BytesPerSector()) && BytesPerSector() >= KDefaultSectorSz && BytesPerSector() < 4096);
1.243 +
1.244 + //-- find out partition information in order to determine volume size.
1.245 + bResult = DeviceIoControl(Handle(),
1.246 + IOCTL_DISK_GET_PARTITION_INFO,
1.247 + NULL, 0,
1.248 + ipScratchBuf, KScratchBufSz,
1.249 + &junk, (LPOVERLAPPED)NULL);
1.250 +
1.251 + if(!bResult)
1.252 + {//-- this is a fatal error
1.253 + __LOG1(_L("#-- CWinVolumeDevice::Connect() IOCTL_DISK_GET_PARTITION_INFO WinError:%d !"), GetLastError());
1.254 + Disconnect();
1.255 + return KErrBadHandle;
1.256 + }
1.257 +
1.258 + //-- get partition informaton
1.259 + const PARTITION_INFORMATION& pi = (const PARTITION_INFORMATION&)*ipScratchBuf;
1.260 + TInt64 volSz = MAKE_TINT64(pi.PartitionLength.HighPart, pi.PartitionLength.LowPart);
1.261 + iDrvGeometry.iSizeInSectors = (TUint32)(volSz / iDrvGeometry.iBytesPerSector);
1.262 +
1.263 + __LOG3(_L("#-- partition size, bytes:%LU (%uMB), sectors:%u"), volSz, (TUint32)(volSz>>20), iDrvGeometry.iSizeInSectors);
1.264 +
1.265 + //-- check if the media size is set in coonfig and if we can use this setting.
1.266 + if(aParams.iDrvGeometry.iSizeInSectors == 0)
1.267 + {//-- do nothing, the media size is not set in the ini file, use existing media parameters
1.268 + }
1.269 + else if(aParams.iDrvGeometry.iSizeInSectors > iDrvGeometry.iSizeInSectors)
1.270 + {//-- requested media size in ini file is bigger than physical media, error.
1.271 + //-- we can't increase physical media size
1.272 + __LOG2(_L("#-- CWinVolumeDevice::Connect() 'MediaSizeSectors' value from config:%d > than physical:%d !"), aParams.iDrvGeometry.iSizeInSectors, iDrvGeometry.iSizeInSectors);
1.273 + Disconnect();
1.274 + return KErrArgument;
1.275 + }
1.276 + else if(aParams.iDrvGeometry.iSizeInSectors < iDrvGeometry.iSizeInSectors)
1.277 + {//-- settings specify smaller media than physical one, adjust the size
1.278 + __PRINT1(_L("#-- reducing media size to %d sectors"), aParams.iDrvGeometry.iSizeInSectors);
1.279 + iDrvGeometry.iSizeInSectors = aParams.iDrvGeometry.iSizeInSectors;
1.280 + }
1.281 +
1.282 +
1.283 + ASSERT(iDrvGeometry.iSizeInSectors > KMinMediaSizeInSectors);
1.284 + return KErrNone;
1.285 +}
1.286 +
1.287 +//-----------------------------------------------------------------------------
1.288 +
1.289 +/**
1.290 + Read a portion of data from the device.
1.291 + Note: at present it _APPENDS_ data to the aDataDes, so the caller must take care of setting its length
1.292 +
1.293 + @param aPos media position in bytes
1.294 + @param aLength how many bytes to read
1.295 + @param aDataDes data descriptor
1.296 +
1.297 + @return KErrNone on success, standard Epoc error code otherwise
1.298 +
1.299 +*/
1.300 +TInt CWinVolumeDevice::Read(TInt64 aPos, TInt aLength, TDes8& aDataDes)
1.301 +{
1.302 + //__PRINT2(_L("#-- CWinVolumeDevice::Read, pos:%LU, len:%u"), aPos, aLength);
1.303 +
1.304 + ASSERT(HandleValid());
1.305 + ASSERT(aLength <= aDataDes.MaxLength());
1.306 +
1.307 + //-- check position on the volume
1.308 + const TInt64 maxPos = iDrvGeometry.TotalSizeInBytes();
1.309 + if(aPos < 0 || aPos > maxPos)
1.310 + return KErrArgument;
1.311 +
1.312 + const TInt64 lastPos = aPos+aLength;
1.313 +
1.314 + if(lastPos > maxPos)
1.315 + return KErrArgument;
1.316 + //--
1.317 +
1.318 +
1.319 + TUint32 dataLen = aLength;
1.320 +
1.321 + if(dataLen == 0)
1.322 + return KErrNone;
1.323 +
1.324 + DWORD dwRes;
1.325 + DWORD dwBytesRead = 0;
1.326 +
1.327 + //aDataDes.SetLength(0);
1.328 +
1.329 + const TUint32 KSectorSize = BytesPerSector();
1.330 +
1.331 + try
1.332 + {
1.333 + LONG mediaPosHi = I64HIGH(aPos);
1.334 + const TUint32 mediaPosLo = I64LOW(aPos);
1.335 + const TUint32 startPosOffset = mediaPosLo & (KSectorSize-1);
1.336 +
1.337 + //-- 1. position to the media with sector size granularity and read 1st sector
1.338 + dwRes = SetFilePointer(iDevHandle, mediaPosLo-startPosOffset, &mediaPosHi, FILE_BEGIN);
1.339 + if(dwRes == INVALID_SET_FILE_POINTER)
1.340 + throw KDiskOpError;
1.341 +
1.342 + //-- 1.1 read 1st sector
1.343 + if(!ReadFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytesRead, NULL))
1.344 + throw KDiskOpError;
1.345 +
1.346 + const TUint32 firstChunkLen = Min(dataLen, KSectorSize - startPosOffset);
1.347 + aDataDes.Append(ipScratchBuf+startPosOffset, firstChunkLen);
1.348 + dataLen-=firstChunkLen;
1.349 +
1.350 + if(dataLen == 0)
1.351 + return KErrNone; //-- no more data to read
1.352 +
1.353 + //-- 2. read whole number of sectors from the meida
1.354 + const TUint32 KBytesTail = dataLen & (KSectorSize-1); //-- number of bytes in the incomplete last sector
1.355 +
1.356 + ASSERT((KScratchBufSz % KSectorSize) == 0);
1.357 +
1.358 + TUint32 rem = dataLen - KBytesTail;
1.359 + while(rem)
1.360 + {
1.361 + const TUint32 bytesToRead = Min(KScratchBufSz, rem);
1.362 +
1.363 + if(!ReadFile(iDevHandle, ipScratchBuf, bytesToRead, &dwBytesRead, NULL))
1.364 + throw KDiskOpError;
1.365 +
1.366 + aDataDes.Append(ipScratchBuf, bytesToRead);
1.367 + rem-=bytesToRead;
1.368 + }
1.369 +
1.370 + //-- 3. read the rest of the bytes in the incomplete last sector
1.371 + if(KBytesTail)
1.372 + {
1.373 + if(!ReadFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytesRead, NULL))
1.374 + throw KDiskOpError;
1.375 +
1.376 + aDataDes.Append(ipScratchBuf, KBytesTail);
1.377 + }
1.378 +
1.379 + }//try
1.380 + catch(TInt nErrId)
1.381 + {//-- some disk operation finished with the error
1.382 + (void)nErrId;
1.383 + ASSERT(nErrId == KDiskOpError);
1.384 + const DWORD dwWinErr = GetLastError();
1.385 + const TInt epocErr = MapWinError(dwWinErr);
1.386 +
1.387 + __PRINT2(_L("#-- CWinVolumeDevice::Read() error! WinErr:%d, EpocErr:%d"), dwWinErr, epocErr);
1.388 + ASSERT(epocErr != KErrNone);
1.389 +
1.390 + return epocErr;
1.391 + }
1.392 +
1.393 + return KErrNone;
1.394 +}
1.395 +
1.396 +//-----------------------------------------------------------------------------
1.397 +/**
1.398 + Write some data to the device.
1.399 +
1.400 + @param aPos media position in bytes
1.401 + @param aLength how many bytes to read
1.402 + @param aDataDes data descriptor
1.403 +
1.404 + @return KErrNone on success, standard Epoc error code otherwise
1.405 +*/
1.406 +TInt CWinVolumeDevice::Write(TInt64 aPos, TInt aLength, const TDesC8& aDataDes)
1.407 +{
1.408 + //__PRINT2(_L("#-- CWinVolumeDevice::Write, pos:%LU, len:%u"), aPos, aLength);
1.409 +
1.410 + ASSERT(HandleValid());
1.411 +
1.412 + if(aLength == 0 || aDataDes.Length() == 0)
1.413 + return KErrNone;
1.414 +
1.415 + if(aLength > aDataDes.Length())
1.416 + {
1.417 + ASSERT(0);
1.418 + return KErrArgument;
1.419 + }
1.420 +
1.421 + //-- check position on the volume
1.422 + const TInt64 maxPos = iDrvGeometry.TotalSizeInBytes();
1.423 + if(aPos < 0 || aPos > maxPos)
1.424 + return KErrArgument;
1.425 +
1.426 + const TInt64 lastPos = aPos+aLength;
1.427 + if(lastPos > maxPos)
1.428 + return KErrArgument;
1.429 +
1.430 + TUint32 dataLen = aLength;
1.431 +
1.432 + DWORD dwRes;
1.433 + DWORD dwBytes = 0;
1.434 +
1.435 + const TUint32 KSectorSize = BytesPerSector();
1.436 + const TUint8 *pData = aDataDes.Ptr();
1.437 +
1.438 + try
1.439 + {
1.440 + LONG mediaPosHi = I64HIGH(aPos);
1.441 + const TUint32 mediaPosLo = I64LOW(aPos);
1.442 + const TUint32 startPosOffset = mediaPosLo & (KSectorSize-1);
1.443 + const TUint32 sectorPos = mediaPosLo-startPosOffset;
1.444 +
1.445 + //-- 1. position to the media with sector size granularity
1.446 + dwRes = SetFilePointer(iDevHandle, sectorPos, &mediaPosHi, FILE_BEGIN);
1.447 + if(dwRes == INVALID_SET_FILE_POINTER)
1.448 + {
1.449 + throw KDiskOpError;
1.450 + }
1.451 +
1.452 + if(startPosOffset || dataLen <= KSectorSize)
1.453 + {//-- need a read-modify-write here.
1.454 + //-- 1.1 read first sector
1.455 + if(!ReadFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytes, NULL))
1.456 + throw KDiskOpError;
1.457 +
1.458 + dwRes = SetFilePointer(iDevHandle, sectorPos, &mediaPosHi, FILE_BEGIN);
1.459 + if(dwRes == INVALID_SET_FILE_POINTER)
1.460 + {
1.461 + throw KDiskOpError;
1.462 + }
1.463 +
1.464 +
1.465 + if(dwRes == INVALID_SET_FILE_POINTER)
1.466 + throw KDiskOpError;
1.467 +
1.468 + //-- 1.2 copy chunk of data there
1.469 + const TUint32 firstChunkLen = Min(dataLen, KSectorSize - startPosOffset);
1.470 + Mem::Copy(ipScratchBuf+startPosOffset, pData, firstChunkLen);
1.471 +
1.472 + //-- 1.3 write sector
1.473 + if(!WriteFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytes, NULL))
1.474 + throw KDiskOpError;
1.475 +
1.476 +
1.477 + dataLen-=firstChunkLen;
1.478 + pData+=firstChunkLen;
1.479 +
1.480 + if(dataLen == 0)
1.481 + return KErrNone; //-- no more data to write
1.482 + }
1.483 +
1.484 + //-- 2. write whole number of sectors to the media
1.485 + const TUint32 KBytesTail = dataLen & (KSectorSize-1); //-- number of bytes in the incomplete last sector
1.486 + TUint32 KMainChunkBytes = dataLen - KBytesTail;
1.487 +
1.488 + ASSERT((KMainChunkBytes % KSectorSize) == 0);
1.489 +
1.490 + //-- the pointer to the data shall be 2-bytes aligned, otherwise WriteFile will fail
1.491 + if(!((DWORD)pData & 0x01))
1.492 + {//-- data pointer aligned, ok
1.493 + if(!WriteFile(iDevHandle, pData, KMainChunkBytes, &dwBytes, NULL))
1.494 + throw KDiskOpError;
1.495 +
1.496 + pData+=KMainChunkBytes;
1.497 + dataLen-=KMainChunkBytes;
1.498 +
1.499 + }
1.500 + else
1.501 + {//-- data pointer is odd, we need to copy data to the aligned buffer
1.502 + TUint32 rem = KMainChunkBytes;
1.503 + while(rem)
1.504 + {
1.505 + const TUint32 nBytesToWrite = Min(KScratchBufSz, rem);
1.506 + Mem::Copy(ipScratchBuf, pData, nBytesToWrite);
1.507 +
1.508 + if(!WriteFile(iDevHandle, ipScratchBuf, nBytesToWrite, &dwBytes, NULL))
1.509 + throw KDiskOpError;
1.510 +
1.511 + rem-=nBytesToWrite;
1.512 + pData+=nBytesToWrite;
1.513 + dataLen-=nBytesToWrite;
1.514 + }
1.515 +
1.516 + }
1.517 +
1.518 +
1.519 + //-- 3. write the rest of the bytes into the incomplete last sector
1.520 + if(KBytesTail)
1.521 + {
1.522 + //-- 3.1 read last sector
1.523 + if(!ReadFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytes, NULL))
1.524 + throw KDiskOpError;
1.525 +
1.526 + LARGE_INTEGER liRelOffset;
1.527 + liRelOffset.QuadPart = -(LONG)KSectorSize;
1.528 +
1.529 + //dwRes = SetFilePointer(iDevHandle, -(LONG)KSectorSize, NULL, FILE_CURRENT);
1.530 +
1.531 + dwRes = SetFilePointer(iDevHandle, liRelOffset.LowPart, &liRelOffset.HighPart, FILE_CURRENT);
1.532 + if(dwRes == INVALID_SET_FILE_POINTER)
1.533 + throw KDiskOpError;
1.534 +
1.535 + //-- 1.2 copy chunk of data there
1.536 + Mem::Copy(ipScratchBuf, pData, KBytesTail);
1.537 +
1.538 + //-- 1.3 write sector
1.539 + if(!WriteFile(iDevHandle, ipScratchBuf, KSectorSize, &dwBytes, NULL))
1.540 + throw KDiskOpError;
1.541 +
1.542 + }
1.543 +
1.544 +
1.545 + }//try
1.546 + catch(TInt nErrId)
1.547 + {//-- some disk operation finished with the error
1.548 + (void)nErrId;
1.549 + ASSERT(nErrId == KDiskOpError);
1.550 + const DWORD dwWinErr = GetLastError();
1.551 + const TInt epocErr = MapWinError(dwWinErr);
1.552 +
1.553 + __PRINT2(_L("#-- CWinVolumeDevice::Write() error! WinErr:%d, EpocErr:%d"), dwWinErr, epocErr);
1.554 + ASSERT(epocErr != KErrNone);
1.555 + return epocErr;
1.556 + }
1.557 +
1.558 + return KErrNone;
1.559 +}
1.560 +
1.561 +//-----------------------------------------------------------------------------
1.562 +
1.563 +/**
1.564 + Check if the buffer is filled with aFill character.
1.565 + @param aBufPtr buffer descriptor
1.566 + @param aFill filling character
1.567 + @return ETrue if the buffer is filled with aFill byte.
1.568 +*/
1.569 +static TBool CheckBufFill(const TPtrC8& aBufPtr, TUint8 aFill)
1.570 +{
1.571 + const TUint32 bufSz = (TUint32)aBufPtr.Size();
1.572 +
1.573 + //-- optimised by using DWORD granularity
1.574 + if(bufSz % sizeof(TUint32) == 0)
1.575 + {
1.576 + TUint32 wordPattern = aFill;
1.577 + wordPattern <<= 8; wordPattern |= aFill;
1.578 + wordPattern <<= 8; wordPattern |= aFill;
1.579 + wordPattern <<= 8; wordPattern |= aFill;
1.580 +
1.581 + const TUint nWords = bufSz / sizeof(TUint32);
1.582 + const TUint32* pWords = (const TUint32*) aBufPtr.Ptr();
1.583 +
1.584 + for(TUint32 i=0; i<nWords; ++i)
1.585 + {
1.586 + if(pWords[i] != wordPattern)
1.587 + return EFalse;
1.588 + }
1.589 +
1.590 + return ETrue;
1.591 + }
1.592 +
1.593 + //-- dumb implementation
1.594 + for(TUint32 i=0; i<bufSz; ++i)
1.595 + {
1.596 + if(aBufPtr[i] != aFill)
1.597 + return EFalse;
1.598 + }
1.599 +
1.600 + return ETrue;
1.601 +}
1.602 +
1.603 +
1.604 +//#########################################################################################################################
1.605 +//## CWinImgFileDevice class implementation
1.606 +//#########################################################################################################################
1.607 +
1.608 +
1.609 +//-----------------------------------------------------------------------------
1.610 +
1.611 +CWinImgFileDevice::CWinImgFileDevice()
1.612 + :CWinMediaDeviceBase()
1.613 +{
1.614 +
1.615 + ihFileMapping = NULL;
1.616 + ipImageFile = NULL;
1.617 +
1.618 + //-- create scratch buffer
1.619 + ipScratchBuf = ::new TUint8[KScratchBufSz];
1.620 + ASSERT(ipScratchBuf);
1.621 +
1.622 +}
1.623 +
1.624 +CWinImgFileDevice::~CWinImgFileDevice()
1.625 +{
1.626 + delete ipScratchBuf;
1.627 +}
1.628 +
1.629 +//-----------------------------------------------------------------------------
1.630 +void CWinImgFileDevice::Disconnect()
1.631 +{
1.632 + CloseHandle(ihFileMapping);
1.633 + ihFileMapping = NULL;
1.634 + ipImageFile = NULL;
1.635 +
1.636 + CWinMediaDeviceBase::Disconnect();
1.637 +}
1.638 +
1.639 +//-----------------------------------------------------------------------------
1.640 +/**
1.641 + Open the device and do some initalisation work.
1.642 +
1.643 + @param aParams device parameters
1.644 + @return Epoc error code, KErrNone if everything is OK
1.645 +*/
1.646 +TInt CWinImgFileDevice::Connect(const TMediaDeviceParams& aParams)
1.647 +{
1.648 + __PRINT(_L("#-- CWinImgFileDevice::Connect()"));
1.649 +
1.650 + if(!aParams.ipDevName)
1.651 + {
1.652 + __LOG(_L("#-- CWinImgFileDevice::Connect() device name is not set!"));
1.653 + return KErrBadName;
1.654 + }
1.655 + __PRINTF(aParams.ipDevName);
1.656 + ASSERT(!HandleValid());
1.657 +
1.658 + //-- 1. try to locate an image file by given name.
1.659 + WIN32_FIND_DATAA wfd;
1.660 + iDevHandle = FindFirstFileA(aParams.ipDevName, &wfd);
1.661 +
1.662 + const TBool ImgFileAlreadyExists = HandleValid(iDevHandle);
1.663 +
1.664 + FindClose(iDevHandle);
1.665 + iDevHandle = NULL;
1.666 +
1.667 + //-- sector size we will use within image file
1.668 + const TUint32 sectorSizeToUse = (aParams.iDrvGeometry.iBytesPerSector == 0) ? KDefaultSectorSz : aParams.iDrvGeometry.iBytesPerSector;
1.669 + TUint32 fileSzInSectorsToUse = 0;
1.670 +
1.671 + const TUint32 reqSizeSec = aParams.iDrvGeometry.iSizeInSectors; //-- required size in sectors
1.672 + const DWORD dwAccessMode = (aParams.iReadOnly) ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE;
1.673 +
1.674 + if(ImgFileAlreadyExists)
1.675 + {//-- if the image file already exists, try to open it and optionally adjust its size
1.676 + const TInt64 ImgFileSize = MAKE_TINT64(wfd.nFileSizeHigh, wfd.nFileSizeLow);
1.677 + const TUint32 ImgFileSectors = (TUint32)(ImgFileSize / sectorSizeToUse);
1.678 + const TBool ImgFileIsRO = wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
1.679 +
1.680 + DWORD dwFileCreationMode = 0;
1.681 + TBool bNeedToAdjustFileSize = EFalse;
1.682 +
1.683 + if(reqSizeSec == 0 || reqSizeSec == ImgFileSectors)
1.684 + {//-- the required size is either not specified (auto) or the same as the existing file has.
1.685 + //-- we can just open this file
1.686 + dwFileCreationMode = OPEN_EXISTING;
1.687 + fileSzInSectorsToUse = ImgFileSectors;
1.688 + }
1.689 + else
1.690 + {//-- we will have to overwrite the image file
1.691 + if(ImgFileIsRO)
1.692 + {//-- we won't be able to overwrite existing file.
1.693 + __LOG(_L("#-- CWinImgFileDevice::Connect() unable to adjust image file size!"));
1.694 + return KErrAccessDenied;
1.695 + }
1.696 +
1.697 + fileSzInSectorsToUse = reqSizeSec;
1.698 + dwFileCreationMode = CREATE_ALWAYS;
1.699 + bNeedToAdjustFileSize = ETrue;
1.700 + }
1.701 +
1.702 + iDevHandle = CreateFileA(aParams.ipDevName,
1.703 + dwAccessMode,
1.704 + FILE_SHARE_READ,
1.705 + (LPSECURITY_ATTRIBUTES)NULL,
1.706 + dwFileCreationMode,
1.707 + FILE_ATTRIBUTE_NORMAL,
1.708 + NULL);
1.709 +
1.710 + if(!HandleValid(iDevHandle))
1.711 + {
1.712 + const DWORD winErr = GetLastError();
1.713 + __LOG1(_L("#-- CWinImgFileDevice::Connect() Error opening/creating file! WinErr:%d"), winErr);
1.714 + return MapWinError(winErr);
1.715 + }
1.716 +
1.717 + //-- adjust file size if we need
1.718 + if(bNeedToAdjustFileSize)
1.719 + {
1.720 + const TInt64 newFileSize = (TInt64)reqSizeSec * sectorSizeToUse;
1.721 + ASSERT(newFileSize);
1.722 +
1.723 + LONG newFSzHi = I64HIGH(newFileSize);
1.724 + DWORD dwRes = SetFilePointer(iDevHandle, I64LOW(newFileSize), &newFSzHi, FILE_BEGIN);
1.725 + if(dwRes == INVALID_SET_FILE_POINTER || !SetEndOfFile(iDevHandle))
1.726 + {
1.727 + const DWORD winErr = GetLastError();
1.728 + Disconnect();
1.729 + __LOG1(_L("#-- CWinImgFileDevice::Connect() unable to set file size! WinErr:%d"), winErr);
1.730 + return MapWinError(winErr);
1.731 + }
1.732 + }
1.733 +
1.734 + }
1.735 + else //if(ImgFileAlreadyExists)
1.736 + {//-- if the image file does not exist or its size differs from required. try to create it
1.737 +
1.738 + if(reqSizeSec == 0)
1.739 + {
1.740 + __LOG(_L("#-- CWinImgFileDevice::Connect() The image file doesn't exist ant its size isn't specified!"));
1.741 + return KErrArgument;
1.742 + }
1.743 +
1.744 + fileSzInSectorsToUse = reqSizeSec;
1.745 +
1.746 + //-- create a new image file
1.747 + iDevHandle = CreateFileA(aParams.ipDevName,
1.748 + GENERIC_READ | GENERIC_WRITE,
1.749 + FILE_SHARE_READ,
1.750 + (LPSECURITY_ATTRIBUTES)NULL,
1.751 + CREATE_ALWAYS,
1.752 + FILE_ATTRIBUTE_NORMAL,
1.753 + NULL);
1.754 +
1.755 + if(!HandleValid(iDevHandle))
1.756 + {
1.757 + const DWORD winErr = GetLastError();
1.758 + __LOG1(_L("#-- CWinImgFileDevice::Connect() can not create file! WinErr:%d"), winErr);
1.759 + return MapWinError(winErr);
1.760 + }
1.761 +
1.762 + //-- set its size
1.763 + const TInt64 newFileSize = (TInt64)reqSizeSec * sectorSizeToUse;
1.764 + ASSERT(newFileSize);
1.765 +
1.766 + LONG newFSzHi = I64HIGH(newFileSize);
1.767 + DWORD dwRes = SetFilePointer(iDevHandle, I64LOW(newFileSize), &newFSzHi, FILE_BEGIN);
1.768 + if(dwRes == INVALID_SET_FILE_POINTER || !SetEndOfFile(iDevHandle))
1.769 + {
1.770 + const DWORD winErr = GetLastError();
1.771 + Disconnect();
1.772 + __LOG1(_L("#-- CWinImgFileDevice::Connect() unable to set file size! WinErr:%d"), winErr);
1.773 + return MapWinError(winErr);
1.774 + }
1.775 +
1.776 + //-- if parametrs require a read-only file, reopen it in RO mode, it doesn't make a lot of sense though...
1.777 + if(aParams.iReadOnly)
1.778 + {
1.779 + CloseHandle(iDevHandle);
1.780 + iDevHandle = NULL;
1.781 +
1.782 + iDevHandle = CreateFileA(aParams.ipDevName,
1.783 + GENERIC_READ ,
1.784 + FILE_SHARE_READ,
1.785 + (LPSECURITY_ATTRIBUTES)NULL,
1.786 + OPEN_EXISTING,
1.787 + FILE_ATTRIBUTE_NORMAL,
1.788 + NULL);
1.789 +
1.790 + if(!HandleValid(iDevHandle))
1.791 + {
1.792 + const DWORD winErr = GetLastError();
1.793 + __LOG1(_L("#-- CWinImgFileDevice::Connect() Can't reopen a file in RO mode! WinErr:%d"), winErr);
1.794 + return MapWinError(winErr);
1.795 + }
1.796 +
1.797 + }//if(aParams.iReadOnly)
1.798 +
1.799 + }//else if(ImgFileAlreadyExists)
1.800 +
1.801 + //-- here we must have the image file created/opened and with correct size
1.802 + ASSERT(HandleValid());
1.803 + ASSERT(sectorSizeToUse);
1.804 +
1.805 + if(fileSzInSectorsToUse < KMinMediaSizeInSectors)
1.806 + {
1.807 + __LOG1(_L("#-- CWinImgFileDevice::Connect() Image file is too small! sectors:%d"), fileSzInSectorsToUse);
1.808 + Disconnect();
1.809 + return KErrGeneral;
1.810 + }
1.811 +
1.812 + iDrvGeometry.iBytesPerSector = sectorSizeToUse;
1.813 + iDrvGeometry.iSizeInSectors = fileSzInSectorsToUse;
1.814 +
1.815 + //-- map the image file into memory.
1.816 + ASSERT(!HandleValid(ihFileMapping));
1.817 + ASSERT(!ipImageFile);
1.818 +
1.819 + /*
1.820 + don't map image file, because it can be > 4G.
1.821 + ihFileMapping = CreateFileMapping(Handle(), NULL,
1.822 + aParams.iReadOnly ? PAGE_READONLY : PAGE_READWRITE,
1.823 + 0, 0, NULL);
1.824 + if(HandleValid(ihFileMapping))
1.825 + {
1.826 + ipImageFile = (TUint8*)MapViewOfFile(ihFileMapping,
1.827 + aParams.iReadOnly ? FILE_MAP_READ : FILE_MAP_WRITE,
1.828 + 0,0,0);
1.829 + }
1.830 +
1.831 + if(!ipImageFile)
1.832 + {
1.833 + __PRINT1(_L("#-- CWinImgFileDevice::Connect() Error mapping file! WinErr:%d"), GetLastError());
1.834 + Disconnect();
1.835 + return KErrGeneral;
1.836 + }
1.837 + */
1.838 +
1.839 + return KErrNone;
1.840 +}
1.841 +
1.842 +
1.843 +/**
1.844 + Read a portion of data from the device.
1.845 + Note: at present it _APPENDS_ data to the aDataDes, so the caller must take care of setting its length
1.846 +
1.847 + @param aPos media position in bytes
1.848 + @param aLength how many bytes to read
1.849 + @param aDataDes data descriptor
1.850 +
1.851 + @return KErrNone on success, standard Epoc error code otherwise
1.852 +
1.853 +*/
1.854 +TInt CWinImgFileDevice::Read(TInt64 aPos,TInt aLength, TDes8& aDataDes)
1.855 +{
1.856 +
1.857 + //__PRINT3(_L("#-- CWinImgFileDevice::Read, pos:%LU, len:%u, desMaxLen:%u"), aPos, aLength, aDataDes.MaxLength());
1.858 +
1.859 + ASSERT(HandleValid());
1.860 + ASSERT(aLength <= aDataDes.MaxLength());
1.861 +
1.862 + //-- check position on the volume
1.863 + const TInt64 maxPos = iDrvGeometry.TotalSizeInBytes();
1.864 + if(aPos < 0 || aPos > maxPos)
1.865 + return KErrArgument;
1.866 +
1.867 + const TInt64 lastPos = aPos+aLength;
1.868 + if(lastPos > maxPos)
1.869 + return KErrArgument;
1.870 +
1.871 + TUint32 dataLen = aLength;
1.872 +
1.873 + if(dataLen == 0)
1.874 + return KErrNone;
1.875 +
1.876 + DWORD dwRes;
1.877 + DWORD dwBytesRead = 0;
1.878 +
1.879 + //aDataDes.SetLength(0);
1.880 +
1.881 + try
1.882 + {
1.883 + //-- 1. position to the media
1.884 + LONG mediaPosHi = I64HIGH(aPos);
1.885 + const TUint32 mediaPosLo = I64LOW(aPos);
1.886 +
1.887 + dwRes = SetFilePointer(iDevHandle, mediaPosLo, &mediaPosHi, FILE_BEGIN);
1.888 + if(dwRes == INVALID_SET_FILE_POINTER)
1.889 + throw KDiskOpError;
1.890 +
1.891 +
1.892 + //-- 2. read data to the scratch buffer and copy it to the descriptor.
1.893 + ASSERT(ipScratchBuf);
1.894 +
1.895 + TUint32 rem = dataLen;
1.896 +
1.897 + while(rem)
1.898 + {
1.899 + const TUint32 bytesToRead = Min(KScratchBufSz, rem);
1.900 + if(!ReadFile(iDevHandle, ipScratchBuf, bytesToRead, &dwBytesRead, NULL))
1.901 + throw KDiskOpError;
1.902 +
1.903 + aDataDes.Append(ipScratchBuf, bytesToRead);
1.904 + rem-=bytesToRead;
1.905 + }
1.906 +
1.907 + }
1.908 + catch(TInt nErrId)
1.909 + {//-- some disk operation finished with the error
1.910 + (void)nErrId;
1.911 + ASSERT(nErrId == KDiskOpError);
1.912 + const DWORD dwWinErr = GetLastError();
1.913 + const TInt epocErr = MapWinError(dwWinErr);
1.914 +
1.915 + __PRINT2(_L("#-- CWinImgFileDevice::Read() error! WinErr:%d, EpocErr:%d"), dwWinErr, epocErr);
1.916 + ASSERT(epocErr != KErrNone);
1.917 +
1.918 + return epocErr;
1.919 + }
1.920 +
1.921 +
1.922 + return KErrNone;
1.923 +}
1.924 +
1.925 +/**
1.926 + Write some data to the device.
1.927 +
1.928 + @param aPos media position in bytes
1.929 + @param aLength how many bytes to read
1.930 + @param aDataDes data descriptor
1.931 +
1.932 + @return KErrNone on success, standard Epoc error code otherwise
1.933 +*/
1.934 +TInt CWinImgFileDevice::Write(TInt64 aPos, TInt aLength, const TDesC8& aDataDes)
1.935 +{
1.936 + //__PRINT3(_L("#-- CWinImgFileDevice::Write, pos:%LU, len:%u, desLen:%u" ), aPos, aLength, aDataDes.Length());
1.937 +
1.938 + ASSERT(HandleValid());
1.939 +
1.940 +
1.941 + if(aLength == 0 || aDataDes.Length() == 0)
1.942 + return KErrNone;
1.943 +
1.944 + if(aLength > aDataDes.Length())
1.945 + {
1.946 + ASSERT(0);
1.947 + return KErrArgument;
1.948 + }
1.949 +
1.950 + //-- check position on the volume
1.951 + const TInt64 maxPos = iDrvGeometry.TotalSizeInBytes();
1.952 + if(aPos < 0 || aPos > maxPos)
1.953 + return KErrArgument;
1.954 +
1.955 + const TInt64 lastPos = aPos+aLength;
1.956 +
1.957 + if(lastPos > maxPos)
1.958 + return KErrArgument;
1.959 +
1.960 + TUint32 dataLen = aLength;
1.961 +
1.962 +
1.963 + DWORD dwRes;
1.964 + DWORD dwBytes = 0;
1.965 +
1.966 + const TUint8 *pData = aDataDes.Ptr();
1.967 +
1.968 + try
1.969 + {
1.970 + //-- 1. position to the media
1.971 + LONG mediaPosHi = I64HIGH(aPos);
1.972 + const TUint32 mediaPosLo = I64LOW(aPos);
1.973 + dwRes = SetFilePointer(iDevHandle, mediaPosLo, &mediaPosHi, FILE_BEGIN);
1.974 + if(dwRes == INVALID_SET_FILE_POINTER)
1.975 + {
1.976 + throw KDiskOpError;
1.977 + }
1.978 +
1.979 + //-- 2. write data to the media
1.980 + //-- check if the pointer is word-aligned
1.981 + const DWORD dwPtrMask = 0x01;
1.982 +
1.983 + if( (DWORD)pData & dwPtrMask)
1.984 + {//-- data pointer isn't aligned, write non-aligned bytes through buffer
1.985 + ASSERT(dataLen);
1.986 +
1.987 + const int oddCnt = 1;
1.988 + ipScratchBuf[0] = *pData;
1.989 +
1.990 + ++pData;
1.991 + --dataLen;
1.992 +
1.993 + if(!WriteFile(iDevHandle, ipScratchBuf, oddCnt, &dwBytes, NULL))
1.994 + throw KDiskOpError;
1.995 + }
1.996 +
1.997 + ASSERT(!((DWORD)pData & dwPtrMask));
1.998 + if(dataLen > 0)
1.999 + {
1.1000 + if(!WriteFile(iDevHandle, pData, dataLen, &dwBytes, NULL))
1.1001 + throw KDiskOpError;
1.1002 + }
1.1003 +
1.1004 + }
1.1005 + catch(TInt nErrId)
1.1006 + {//-- some disk operation finished with the error
1.1007 + (void)nErrId;
1.1008 + ASSERT(nErrId == KDiskOpError);
1.1009 + const DWORD dwWinErr = GetLastError();
1.1010 + const TInt epocErr = MapWinError(dwWinErr);
1.1011 +
1.1012 + __PRINT2(_L("#-- CWinImgFileDevice::Write() error! WinErr:%d, EpocErr:%d"), dwWinErr, epocErr);
1.1013 + ASSERT(epocErr != KErrNone);
1.1014 + return epocErr;
1.1015 + }
1.1016 +
1.1017 +
1.1018 +
1.1019 + return KErrNone;
1.1020 +}
1.1021 +
1.1022 +
1.1023 +//-----------------------------------------------------------------------------
1.1024 +
1.1025 +/**
1.1026 + Make the best effort to map Windows error codes (from GetLastError()) to Epos ones.
1.1027 +
1.1028 + @param aWinError MS Windows error code
1.1029 + @return corresponding EPOC eror code
1.1030 +*/
1.1031 +
1.1032 +TInt CWinMediaDeviceBase::MapWinError(DWORD aWinError) const
1.1033 +{
1.1034 + switch(aWinError)
1.1035 + {
1.1036 + case NO_ERROR:
1.1037 + return KErrNone;
1.1038 +
1.1039 + case ERROR_NOT_READY:
1.1040 + return KErrNotReady;
1.1041 +
1.1042 + case ERROR_WRITE_PROTECT:
1.1043 + case ERROR_ACCESS_DENIED:
1.1044 + return KErrAccessDenied;
1.1045 +
1.1046 + case ERROR_INVALID_HANDLE:
1.1047 + return KErrBadHandle;
1.1048 +
1.1049 + case ERROR_NOT_ENOUGH_MEMORY:
1.1050 + return KErrNoMemory;
1.1051 +
1.1052 + case ERROR_OUTOFMEMORY:
1.1053 + return KErrDiskFull;
1.1054 +
1.1055 + case ERROR_CRC:
1.1056 + return KErrCorrupt;
1.1057 +
1.1058 + case ERROR_WRITE_FAULT:
1.1059 + return KErrWrite;
1.1060 +
1.1061 + case ERROR_GEN_FAILURE:
1.1062 + return KErrGeneral;
1.1063 +
1.1064 + case ERROR_LOCK_VIOLATION:
1.1065 + return KErrLocked;
1.1066 +
1.1067 + case ERROR_SHARING_VIOLATION:
1.1068 + return KErrInUse;
1.1069 +
1.1070 + case ERROR_NOT_SUPPORTED:
1.1071 + return KErrNotSupported;
1.1072 +
1.1073 + default:
1.1074 + return KErrGeneral;
1.1075 +
1.1076 + }
1.1077 +}
1.1078 +
1.1079 +
1.1080 +
1.1081 +
1.1082 +
1.1083 +
1.1084 +
1.1085 +
1.1086 +
1.1087 +
1.1088 +
1.1089 +
1.1090 +
1.1091 +
1.1092 +
1.1093 +
1.1094 +
1.1095 +
1.1096 +
1.1097 +
1.1098 +
1.1099 +
1.1100 +
1.1101 +
1.1102 +
1.1103 +
1.1104 +
1.1105 +
1.1106 +
1.1107 +