Question on ATA/SCSI IOCTLs

Hi Scott,

I am running into another issue and wondered if you might have the answer.

On large ATA drives (> 137.4 GB) the LBA addressing I use does not seem to be the same as the small drives.

I have a program that uses the WinAPI read file and multiplies the LBA address with the sector size and sets that as the file pointer offset (for the physical drive 0) and reads a sector.
Using the IOCTL call (both SCSI and ATA) I read the sector with the LBA.
They both match upto LBA 0x3f (also the number of sectors per track is 0x3f or 63). After that the IOCTL calls return arbitrary results. I am using WinHex to see what exactly the returned bytes match to.

My code is as follows:

"
bool ReadMBRUsingScsiPassThruDirect10withSense(int i, std::vector& vIoctlSector)
{
BOOL status = 0;
DWORD accessMode = 0, shareMode = 0;
ULONG alignmentMask = 0; // default == no alignment requirement
PUCHAR dataBuffer = NULL;
PUCHAR pUnAlignedBuffer = NULL;
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptwd;
int sectorSize = 512;

HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if(phDisk == INVALID_HANDLE_VALUE)
return false;

GetAlignmentMaskForDevice(phDisk,&alignmentMask);

ZeroMemory(&sptwd, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
dataBuffer = AllocateAlignedBuffer(sectorSize,alignmentMask, &pUnAlignedBuffer);

ZeroMemory(dataBuffer,sectorSize);

sptwd.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
sptwd.sptd.CdbLength = CDB10GENERIC_LENGTH;
sptwd.sptd.DataIn = SCSI_IOCTL_DATA_IN;
sptwd.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
sptwd.sptd.DataTransferLength = sectorSize;
sptwd.sptd.TimeOutValue = 50;
sptwd.sptd.DataBuffer = dataBuffer;
sptwd.sptd.SenseInfoOffset =
offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);;

sptwd.sptd.Cdb[0] = 0x28;
sptwd.sptd.Cdb[1] = 0x04; //Set to force unit access so that cached data is not used.
sptwd.sptd.Cdb[2] = (i >> 24 ) & 0xFF;
sptwd.sptd.Cdb[3] = (i >> 16 ) & 0xFF;
sptwd.sptd.Cdb[4] = (i >> 8 ) & 0xFF;
sptwd.sptd.Cdb[5] = (i>> 0 ) & 0xFF;
sptwd.sptd.Cdb[6] = 0;
sptwd.sptd.Cdb[7] = (UCHAR)(1 >> 8);
sptwd.sptd.Cdb[8] = 1;
sptwd.sptd.Cdb[9] = 0;
ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
ULONG returned = 0;
status = DeviceIoControl(phDisk,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptwd,
length,
&sptwd,
length,
&returned,
FALSE);

if ((sptwd.sptd.ScsiStatus == 0) && (status != 0)) {
printf(“Using IOCTL SCSI 10\n”);
PrintDataBuffer(dataBuffer,sptwd.sptd.DataTransferLength);
vIoctlSector.resize(sptwd.sptd.DataTransferLength);
memcpy(vIoctlSector.data(), dataBuffer, sptwd.sptd.DataTransferLength);
}
// free(pUnAlignedBuffer);
DWORD error = GetLastError();
CloseHandle(phDisk);
return true;
}

ATA:

bool ReadMBRUsingATAPassthru(int i, std::vector& vATA)
{
HANDLE handle;

UCHAR buffer[512] = {0};

DWORD bytesReturned = 0;

DWORD outBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

int bytescopied = 0;

WCHAR fileName = (WCHAR * ) “\\.\PHYSICALDRIVE0”;

int errorCode = 0;

int bResult = 0;

int ioControlCode = IOCTL_ATA_PASS_THROUGH_DIRECT;

unsigned int inputBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

ATA_PASS_THROUGH_DIRECT inputBuffer = {0};
memset(&inputBuffer,0,40);
memset(buffer, 0, 512);

inputBuffer.AtaFlags = 03 | ATA_FLAGS_48BIT_COMMAND ;

inputBuffer.Length = 40;

inputBuffer.DataTransferLength = 512;

inputBuffer.TimeOutValue = 500;
inputBuffer.DataBuffer = buffer;
inputBuffer.Lun = 0;
inputBuffer.PathId = 0;
inputBuffer.TargetId = 0;

inputBuffer.PreviousTaskFile[0] = 0;
inputBuffer.PreviousTaskFile[1] = 0;
inputBuffer.PreviousTaskFile[2] = (i >> 24) & 0xFF;;
inputBuffer.PreviousTaskFile[3] = (i >> 32 ) & 0xFF;
inputBuffer.PreviousTaskFile[4] = (i >> 40 ) & 0xFF;;
inputBuffer.PreviousTaskFile[5] = 0xE0;
inputBuffer.PreviousTaskFile[6] = 0;
inputBuffer.PreviousTaskFile[7] = 0;

inputBuffer.CurrentTaskFile[0] = 0;
inputBuffer.CurrentTaskFile[1] = 1;
inputBuffer.CurrentTaskFile[2] = (i >> 0 ) & 0xFF;;
inputBuffer.CurrentTaskFile[3] = (i >> 8 ) & 0xFF;;
inputBuffer.CurrentTaskFile[4] = (i >> 16 ) & 0xFF;;
inputBuffer.CurrentTaskFile[5] = 0xE0;
inputBuffer.CurrentTaskFile[6] = 0x20;
inputBuffer.CurrentTaskFile[7] = 0;

handle= CreateFile(L"\\.\PhysicalDrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);

printf(“\n Error = %d”, GetLastError());

if(handle)

{

DeviceIoControl(

handle,

ioControlCode,

&inputBuffer,

outBufferSize,

&inputBuffer,

outBufferSize,

&bytesReturned,

NULL //not an overlapped operation

);

printf(“IOCTL ATA\n”);
printf(“\n status is %d”,inputBuffer.CurrentTaskFile[6]);

printf(“\n Error = %d”, GetLastError());
PrintDataBuffer(buffer, 512);
memcpy(&vATA[0],buffer, 512);

}
CloseHandle(handle);
return true;

}

For ATA the inputBuffer.CurrentTaskFile[6] = 0x20;
for 48 bit is 0x24 (or read Ext - but that works only till LBA 2 and 3 returns 0’s with error 81 or invalid arguments).

Via the API:

bool ReadMBRSystemDrive(int i, std::vector& vBytes, DWORD& size)
{
// Open the physical hard drive
HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
DWORD ret = GetLastError();
if(!(phDisk != NULL && GetLastError() ==0) )
return false;
// Retrieve the sector size
DWORD dwReturnLength = 0;
DISK_GEOMETRY infoDiskGeometry;
if( !::DeviceIoControl(phDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL, 0,
&infoDiskGeometry, sizeof(infoDiskGeometry),
&dwReturnLength, NULL) )
{
DWORD dwErr = GetLastError();
return false;
}
size = infoDiskGeometry.BytesPerSector;
// A vector large enough to hold one sector
vBytes.resize(infoDiskGeometry.BytesPerSector);
// Read first sector from the disk
if( SetFilePointer(phDisk, (i
infoDiskGeometry.BytesPerSector), NULL,FILE_BEGIN) == INVALID_SET_FILE_POINTER )
return false;
DWORD numberRead = 0;
if(! ReadFile(phDisk, reinterpret_cast(&vBytes[0]),infoDiskGeometry.BytesPerSector,&numberRead,NULL) )
{
return false;
}
CloseHandle(phDisk);
return true;
}

And I am looping

while ( true )
{
DWORD size = 0;
std::vector sysDriveApi;
ReadMBRSystemDrive( i, sysDriveApi, size);
std::vector sysDriveScsi (size);
ReadMBRUsingScsiPassThruDirect10withSense(i, sysDriveScsi);
std::vector sysDriveAta (size );
ReadMBRUsingATAPassthru( i, sysDriveAta);
if( memcmp(&sysDriveApi[0],&sysDriveScsi[0], size) != 0 ||
memcmp(&sysDriveApi[0],&sysDriveAta[0], size) != 0 )
{
printf(" LBA %d does not match \n", i);
PrintDataBuffer(reinterpret_cast(&sysDriveApi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveScsi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveAta[0]), size);
if( memcmp( &sysDriveScsi[0], &sysDriveAta[0], size) != 0)
{
printf(" LBA SCSI/ATA %d does not match \n", i);
}
//break;
}
i++;

}

With 0x24 in the ATA command it breaks at LBA 3 with ATA returning 0’s and error 81.
With 0x20, it fails on LBA 3f where SCSI and ATA match but are both incorrect.

Please note this is just working code (it has not been cleaned up and might have some minor inconsistencies).

I would appreciate any guidance/ test code that might be pointed to.

Thanks,
Sonia.

Sorry - please note the correction.

Hi All,

I am running into another issue and wondered if you might have the answer.

On large ATA drives (> 137.4 GB) the LBA addressing I use does not seem to be the same as the small drives.

I have a program that uses the WinAPI read file and multiplies the LBA address with the sector size and sets that as the file pointer offset (for the physical drive 0) and reads a sector.
Using the IOCTL call (both SCSI and ATA) I read the sector with the LBA.
They both match upto LBA 0x3f (also the number of sectors per track is 0x3f or 63). After that the IOCTL calls return arbitrary results. I am using WinHex to see what exactly the returned bytes match to.

My code is as follows:

"
bool ReadMBRUsingScsiPassThruDirect10withSense(int i, std::vector& vIoctlSector)
{
BOOL status = 0;
DWORD accessMode = 0, shareMode = 0;
ULONG alignmentMask = 0; // default == no alignment requirement
PUCHAR dataBuffer = NULL;
PUCHAR pUnAlignedBuffer = NULL;
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptwd;
int sectorSize = 512;

HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if(phDisk == INVALID_HANDLE_VALUE)
return false;

GetAlignmentMaskForDevice(phDisk,&alignmentMask);

ZeroMemory(&sptwd, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
dataBuffer = AllocateAlignedBuffer(sectorSize,alignmentMask, &pUnAlignedBuffer);

ZeroMemory(dataBuffer,sectorSize);

sptwd.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
sptwd.sptd.CdbLength = CDB10GENERIC_LENGTH;
sptwd.sptd.DataIn = SCSI_IOCTL_DATA_IN;
sptwd.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
sptwd.sptd.DataTransferLength = sectorSize;
sptwd.sptd.TimeOutValue = 50;
sptwd.sptd.DataBuffer = dataBuffer;
sptwd.sptd.SenseInfoOffset =
offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);;

sptwd.sptd.Cdb[0] = 0x28;
sptwd.sptd.Cdb[1] = 0x04; //Set to force unit access so that cached data is not used.
sptwd.sptd.Cdb[2] = (i >> 24 ) & 0xFF;
sptwd.sptd.Cdb[3] = (i >> 16 ) & 0xFF;
sptwd.sptd.Cdb[4] = (i >> 8 ) & 0xFF;
sptwd.sptd.Cdb[5] = (i>> 0 ) & 0xFF;
sptwd.sptd.Cdb[6] = 0;
sptwd.sptd.Cdb[7] = (UCHAR)(1 >> 8);
sptwd.sptd.Cdb[8] = 1;
sptwd.sptd.Cdb[9] = 0;
ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
ULONG returned = 0;
status = DeviceIoControl(phDisk,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptwd,
length,
&sptwd,
length,
&returned,
FALSE);

if ((sptwd.sptd.ScsiStatus == 0) && (status != 0)) {
printf(“Using IOCTL SCSI 10\n”);
PrintDataBuffer(dataBuffer,sptwd.sptd.DataTransferLength);
vIoctlSector.resize(sptwd.sptd.DataTransferLength);
memcpy(vIoctlSector.data(), dataBuffer, sptwd.sptd.DataTransferLength);
}
// free(pUnAlignedBuffer);
DWORD error = GetLastError();
CloseHandle(phDisk);
return true;
}

ATA:

bool ReadMBRUsingATAPassthru(int i, std::vector& vATA)
{
HANDLE handle;

UCHAR buffer[512] = {0};

DWORD bytesReturned = 0;

DWORD outBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

int bytescopied = 0;

WCHAR fileName = (WCHAR * ) “\\.\PHYSICALDRIVE0”;

int errorCode = 0;

int bResult = 0;

int ioControlCode = IOCTL_ATA_PASS_THROUGH_DIRECT;

unsigned int inputBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

ATA_PASS_THROUGH_DIRECT inputBuffer = {0};
memset(&inputBuffer,0,40);
memset(buffer, 0, 512);

inputBuffer.AtaFlags = 03 | ATA_FLAGS_48BIT_COMMAND ;

inputBuffer.Length = 40;

inputBuffer.DataTransferLength = 512;

inputBuffer.TimeOutValue = 500;
inputBuffer.DataBuffer = buffer;
inputBuffer.Lun = 0;
inputBuffer.PathId = 0;
inputBuffer.TargetId = 0;

inputBuffer.PreviousTaskFile[0] = 0;
inputBuffer.PreviousTaskFile[1] = 0;
inputBuffer.PreviousTaskFile[2] = (i >> 24) & 0xFF;;
inputBuffer.PreviousTaskFile[3] = (i >> 32 ) & 0xFF;
inputBuffer.PreviousTaskFile[4] = (i >> 40 ) & 0xFF;;
inputBuffer.PreviousTaskFile[5] = 0xE0;
inputBuffer.PreviousTaskFile[6] = 0;
inputBuffer.PreviousTaskFile[7] = 0;

inputBuffer.CurrentTaskFile[0] = 0;
inputBuffer.CurrentTaskFile[1] = 1;
inputBuffer.CurrentTaskFile[2] = (i >> 0 ) & 0xFF;;
inputBuffer.CurrentTaskFile[3] = (i >> 8 ) & 0xFF;;
inputBuffer.CurrentTaskFile[4] = (i >> 16 ) & 0xFF;;
inputBuffer.CurrentTaskFile[5] = 0xE0;
inputBuffer.CurrentTaskFile[6] = 0x20;
inputBuffer.CurrentTaskFile[7] = 0;

handle= CreateFile(L"\\.\PhysicalDrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);

printf(“\n Error = %d”, GetLastError());

if(handle)

{

DeviceIoControl(

handle,

ioControlCode,

&inputBuffer,

outBufferSize,

&inputBuffer,

outBufferSize,

&bytesReturned,

NULL //not an overlapped operation

);

printf(“IOCTL ATA\n”);
printf(“\n status is %d”,inputBuffer.CurrentTaskFile[6]);

printf(“\n Error = %d”, GetLastError());
PrintDataBuffer(buffer, 512);
memcpy(&vATA[0],buffer, 512);

}
CloseHandle(handle);
return true;

}

For ATA the inputBuffer.CurrentTaskFile[6] = 0x20;
for 48 bit is 0x24 (or read Ext - but that works only till LBA 2 and 3 returns 0’s with error 81 or invalid arguments).

Via the API:

bool ReadMBRSystemDrive(int i, std::vector& vBytes, DWORD& size)
{
// Open the physical hard drive
HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
DWORD ret = GetLastError();
if(!(phDisk != NULL && GetLastError() ==0) )
return false;
// Retrieve the sector size
DWORD dwReturnLength = 0;
DISK_GEOMETRY infoDiskGeometry;
if( !::DeviceIoControl(phDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL, 0,
&infoDiskGeometry, sizeof(infoDiskGeometry),
&dwReturnLength, NULL) )
{
DWORD dwErr = GetLastError();
return false;
}
size = infoDiskGeometry.BytesPerSector;
// A vector large enough to hold one sector
vBytes.resize(infoDiskGeometry.BytesPerSector);
// Read first sector from the disk
if( SetFilePointer(phDisk, (i
infoDiskGeometry.BytesPerSector), NULL,FILE_BEGIN) == INVALID_SET_FILE_POINTER )
return false;
DWORD numberRead = 0;
if(! ReadFile(phDisk, reinterpret_cast(&vBytes[0]),infoDiskGeometry.BytesPerSector,&numberRead,NULL) )
{
return false;
}
CloseHandle(phDisk);
return true;
}

And I am looping

while ( true )
{
DWORD size = 0;
std::vector sysDriveApi;
ReadMBRSystemDrive( i, sysDriveApi, size);
std::vector sysDriveScsi (size);
ReadMBRUsingScsiPassThruDirect10withSense(i, sysDriveScsi);
std::vector sysDriveAta (size );
ReadMBRUsingATAPassthru( i, sysDriveAta);
if( memcmp(&sysDriveApi[0],&sysDriveScsi[0], size) != 0 ||
memcmp(&sysDriveApi[0],&sysDriveAta[0], size) != 0 )
{
printf(" LBA %d does not match \n", i);
PrintDataBuffer(reinterpret_cast(&sysDriveApi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveScsi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveAta[0]), size);
if( memcmp( &sysDriveScsi[0], &sysDriveAta[0], size) != 0)
{
printf(" LBA SCSI/ATA %d does not match \n", i);
}
//break;
}
i++;

}

With 0x24 in the ATA command it breaks at LBA 3 with ATA returning 0’s and error 81.
With 0x20, it fails on LBA 3f where SCSI and ATA match but are both incorrect.

Please note this is just working code (it has not been cleaned up and might have some minor inconsistencies).

I would appreciate any guidance/ test code that might be pointed to.

Thanks,
Sonia.

> Hi Scott,

I am running into another issue and wondered if you might have the
answer.

On large ATA drives (> 137.4 GB) the LBA addressing I use does not seem
to
be the same as the small drives.

I have a program that uses the WinAPI read file and multiplies the LBA
address with the sector size and sets that as the file pointer offset
(for
the physical drive 0) and reads a sector.
Using the IOCTL call (both SCSI and ATA) I read the sector with the LBA.
They both match upto LBA 0x3f (also the number of sectors per track is
0x3f or 63). After that the IOCTL calls return arbitrary results. I am
using WinHex to see what exactly the returned bytes match to.

My code is as follows:

WARNING! This code is full of bugs, and furthermore uses incomprehehsible
constants in undefined places to achieve unspecified behavior. I would
not trust most of it as far as I could throw a mainframe. Therefore,
until the bugs are fixed, anything it does is suspect, anything it reports
is suspect, and trying to understand what might be correct assignments of
flags to fields is extremely difficult. A very careful rewrite of the
code is required before anything it does or appears to do can be trusted.
I have added a huge number of comments about the various problems, but I
do not pretend that this list is exhaustive. Maybe because by the end I
was becoming exhausted.
joe

"
bool ReadMBRUsingScsiPassThruDirect10withSense(int i, std::vector&
vIoctlSector)
> {
> BOOL status = 0;

NO! That would be
BOOL status = FALSE;

> DWORD accessMode = 0, shareMode = 0;

Use of commas in declarations makes for unreadable code. One variable,
one line.

Why do you need these as variables anyway?

Furthermore, you don’t actually use them anywhere I can see.

> ULONG alignmentMask = 0; // default == no alignment requirement
PUCHAR dataBuffer = NULL;
> PUCHAR pUnAlignedBuffer = NULL;
> SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptwd;
> int sectorSize = 512;
>

This may not work for all drives, because sector sizes of 4096 can exist.
I would think the best strategy is to query the drive for its sector size.

I see later you query the disk geometry; why aren’t you passing the sector
size value in?

> HANDLE phDisk = NULL;
> phDisk=
> CreateFile(L"\\.\physicaldrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ
| FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
> if(phDisk == INVALID_HANDLE_VALUE)
> return false;

I note that here you check the failure mode correctly, but elsewhere you
seem to assume that if CreateFile fails it will return a NULL handle,
which is incorect, and the code is therefore totaly nonsense.

And why did you call the variable a “pointer to handle” (ph) when it is
just a handle (h)? If you can’t use Hungarian Notation correctly, don’t
use it at all, because incorrect usage makes the program hard to read.

>
> GetAlignmentMaskForDevice(phDisk,&alignmentMask);
>
> ZeroMemory(&sptwd, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
> dataBuffer = AllocateAlignedBuffer(sectorSize,alignmentMask,
> &pUnAlignedBuffer);

It may have been an artifact of all the comments I’ve been adding, or the
email system, but the databuffer= assignment appeared on the same line as
the ::ZeroMemory. This is poor style if that is how it was orginallly
written.

Why do this if the alignment mask plus the buffer address show that the
data is properly aligned?

>
> ZeroMemory(dataBuffer,sectorSize);

Since you know about ::ZeroMemory, why do you do a lot of memsets
elsewhere? For that matter, why are you zeroing a buffer that is going to
be completely overwritten?

>
> sptwd.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
> sptwd.sptd.CdbLength = CDB10GENERIC_LENGTH;
> sptwd.sptd.DataIn = SCSI_IOCTL_DATA_IN;
> sptwd.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
> sptwd.sptd.DataTransferLength = sectorSize;
> sptwd.sptd.TimeOutValue = 50;
> sptwd.sptd.DataBuffer = dataBuffer;
> sptwd.sptd.SenseInfoOffset =
> offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);;
>
> sptwd.sptd.Cdb[0] = 0x28;
> sptwd.sptd.Cdb[1] = 0x04; //Set to force unit access so that
cached
> data is not used.

And this flag does not have a symbolic name? Seriously, now! And what is
the meaning of the magical constant 28? Note that you could have done
something like

#define MyFlags (something | other | whatever)

and written:
sptdw.sptd.Cdb[0] = HIBYTE(MyFlags);
sptdw.sptd.Cdb[1] = LOBYTE(MyFlags);

which is converting from little-endian notation to big-endian notation, or
swap LOBYTE and HIBYTE in the above to store the 16-bit flag value in
little-endian notation. In either case, the meaning is clear. The
meaning of 0x28, 0x4 is NOT clear, and I have no idea what is going on or
is intended.

> sptwd.sptd.Cdb[2] = (i >> 24 ) & 0xFF;
> sptwd.sptd.Cdb[3] = (i >> 16 ) & 0xFF;
> sptwd.sptd.Cdb[4] = (i >> 8 ) & 0xFF;
> sptwd.sptd.Cdb[5] = (i>> 0 ) & 0xFF;

What happens if i is a negative value? Note that you should not use “int”
if you expect an unsigned value. Also, it looks like you are converting
the value from little-endian to big-endian; since I am not an expert at
any of these structures, I can’t say for sure if this is correct or not,
it just jumped out at me as a possible question.

> sptwd.sptd.Cdb[6] = 0;
> sptwd.sptd.Cdb[7] = (UCHAR)(1 >> 8);

1 >> 8 is always going to be zero. Always. And if you meant <<, then 1
<< 8 cast to a UCHAR will be zero. Always.

> sptwd.sptd.Cdb[8] = 1;
> sptwd.sptd.Cdb[9] = 0;
> ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER); ULONG
returned = 0;

Why are there two declarations on the same line?

Note that the length is supposed to be a DWORD, and you are presuming that
the implementation of ULONG and DWORD are the same. Why are you using
different types than the documentation suggests, based on some bizarre
assumption that you know the underlying base types?

For that matter, why do you need to declare a variable for ‘length’ when
you could have simply used the sizeof() in the call below? And, if you
want to declare the variable for convenience, why did you not declare it
as const?

> status = DeviceIoControl(phDisk,
> IOCTL_SCSI_PASS_THROUGH_DIRECT,
> &sptwd,
> length,
> &sptwd,
> length,
> &returned,
> FALSE);
>
>
> if ((sptwd.sptd.ScsiStatus == 0) && (status != 0)) {

Why are you comparing a BOOL with an integer? Why do you need to compare
a BOOL to anything, anyway? It already stands for true or false. Note
also that because BOOL is a weird type introduced long before bool was in
the language, it sometimes takes on values OTHER than 0 or 1, so writing
if(somebool == TRUE)
is often erroneous. Since somebool is already TRUE or FALSE, comparing it
to TRUE or FALSE is just completely silly.

> printf(“Using IOCTL SCSI 10\n”);
> PrintDataBuffer(dataBuffer,sptwd.sptd.DataTransferLength);
vIoctlSector.resize(sptwd.sptd.DataTransferLength);
> memcpy(vIoctlSector.data(), dataBuffer,
> sptwd.sptd.DataTransferLength);
> }
> // free(pUnAlignedBuffer);

So, having allocated the buffer, why do you not free it? You have a
storage leak.

> DWORD error = GetLastError();

Since this appears to be user-level code, you can use the display of
ERR,hr in your watch-window to look at the GetLastError code for whatever
thread broke into the debugger, and this also gives you the text of the
error message. But since you have no idea whether or not the API
succeeded, and printf or PrintDataBuffer might have changed the value,
reading it here is completely nonsensical. If an API fails, the FIRST
think you do is read the error code; after that point, you cannot assume
its integrity. If the API succeeded, with a very few rare exceptions, the
value of GetLastError() is undefined.

> CloseHandle(phDisk);
> return true;
> }

What problem are you trying to solve? Note that computing the “offset” by
simple sector arithmetic does presume that the “file” is not fragmented.
If there is fragmentation, or the sector size is not 512, then I’m not
sure what you are going to get, but I suspect it is not what you are
asking for.

Code like this, that makes presumptions about what is found on the hard
drive and where it is found, is potentially very fragile. It helps a bit
to have some idea what the goal of this code is, so that some hard drive
expert can say, “Oh, that’s not quite the way to do it” or “Sure, that’s
correct, if the second size really is 512” or some other suitable
response.
joe

>
>
> ATA:
>
> bool ReadMBRUsingATAPassthru(int i, std::vector& vATA)
> {
> HANDLE handle;
>
> UCHAR buffer[512] = {0};

Why do you assume the second size is 512? You have no reason to make this
assumption, and you actually query the disk geometry to obtain the value,
which you are ignoring here. Note that you cannot declare a local array
with a dynamic size, but why do you need the local array? Why do you
declare the std::vector to be a vector of BYTEs but declare buffer as a
UCHAR (sure, they’re the same, but really? Two separate names?)

I would have done

vATA.resize(sectorsize); // which must be passed in as a parameter
LPBYTE buffer = &vATA[0];

This also eliminates the need to do the completelly unnecessary memcpy
operation.

>
> DWORD bytesReturned = 0;
>
> DWORD outBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

And you needed to declare a gratuitous variable for what purpose?

>
> int bytescopied = 0;

This does not appear to be used
>
>
> WCHAR fileName = (WCHAR * ) “\\.\PHYSICALDRIVE0”;

This is just plain WRONG! The correct statement is
LPWStr filename = L"\\.\PHYSICALDRIVE0";

You have taken an 8-bit character string and told the program to interpret
it as a set of pairs of bytes defining aa Unicode character, and the
result will be total nonsense.

>
>
>
> int errorCode = 0;
>
> int bResult = 0;

WTF? a “bResult” cannot possibly be an int. Even the Hungarian Notation
suggests it is a BOOL, or possibly a bool, so it can only get TRUE/FALSE
or true/false. Your function returns a bool, not an int, so why are you
setting this int to a numeric value? Why do you have this horrid
fascination of representing everything as signed values when I can’t find
a single place where these values are used as signed values.

>
>
> int ioControlCode = IOCTL_ATA_PASS_THROUGH_DIRECT;
>

Why do you assign a constant to ioControlCode? Why, for goodness sake, do
you make it an “int”? RTFM. The DeviceIoControl API takes a DWORD for
this parameter! But since you use this in only one place, its existence
as a separate variable is nonsense. Just use the constant IOCTAL_ATA_…T
in the call. Or are you one of the people who believe that because the
API definition says DWORD ioctlCode that you MUST have a DWORD variable
declared to represent it (that’s not what the API definition says; it says
that the parameter must evaluate to a DWORD, which means using the
constant in the DeviceIoControl call is perfectly valid)

‘int’ is rarely your friend when you are programming at this low level.
‘int’ is not always your friend in ordinary application code. Note that
‘int’ is ALWAYS 32 bits, even on 64-bit platforms.

> unsigned int inputBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

Why ‘unsigned int’ instead of ‘DWORD’? Those types exist for a reason.
>
> ATA_PASS_THROUGH_DIRECT inputBuffer = {0};
> memset(&inputBuffer,0,40);

Why 40? Why not sizeof() ATA_PASS_…T or InputBuffer? And why are you
zeroing out a buffer you have declared and initialized to be full of
zeroes? And why are you using the rather primitive memset when there is a
::ZeroMemory API? (sure, it’s just a macro that translates to memset, but
using memset is almost-but-not-quite-as-bad as doing an assembly-code
insertion, given that ::ZeroMemory already exists)

And why do you think 40 == sizeof(ATA_PASS_THROUGH_DIRECT) anyway?

> memset(buffer, 0, 512);

Why 512? Ditto. And why are you zeroing it out?
>
> inputBuffer.AtaFlags = 03 | ATA_FLAGS_48BIT_COMMAND ;

And why are you writing an octal 3, when we know it is the same as a hex 3
or a decimal 3? And what does “3” mean, anyway? Isn’t there an ATA_FLAGS
constant or pair of constants that create this value?

>
> inputBuffer.Length = 40;

Didn’t you mean sizeof(ATA_PASS_THROUGH_DIRECT)?

>
> inputBuffer.DataTransferLength = 512;

What is totally bizarre is that you have gone through some effort to
detect the sector size, which you promptly proceed to ignore totally, and
assume it is 512. Why?
>
> inputBuffer.TimeOutValue = 500;
> inputBuffer.DataBuffer = buffer;
> inputBuffer.Lun = 0;
> inputBuffer.PathId = 0;
> inputBuffer.TargetId = 0;
>
>
> inputBuffer.PreviousTaskFile[0] = 0;
> inputBuffer.PreviousTaskFile[1] = 0;
> inputBuffer.PreviousTaskFile[2] = (i >> 24) & 0xFF;;
> inputBuffer.PreviousTaskFile[3] = (i >> 32 ) & 0xFF;
> inputBuffer.PreviousTaskFile[4] = (i >> 40 ) & 0xFF;;

Note that since i is a 32-bit signed integer, >> 32 and >> 40 will be zero
unless the value is negative, in which case the result will end up as
being 0xFF after the propagated size bits are stripped off by the & 0xFF.
You should not be using a signed value here for a length, and if you
expect >> to work for values > 32, you should be passing in a ULONGLONG
(unsigned __int64 on most x86 platforms).

Realistically, you will never have a negative number because that can only
arise if you have a buffer > 2GB to write, and your chances of getting
this on a Win32 platform are vanishingly small. But if i is a count of
bytes, why is it not a DWORD? See previous comments on the overuse of
‘int’. This reads like 1975 PDP-11 code.

> inputBuffer.PreviousTaskFile[5] = 0xE0;

And E0 means what?

> inputBuffer.PreviousTaskFile[6] = 0;
> inputBuffer.PreviousTaskFile[7] = 0;
>
> inputBuffer.CurrentTaskFile[0] = 0;
> inputBuffer.CurrentTaskFile[1] = 1;
> inputBuffer.CurrentTaskFile[2] = (i >> 0 ) & 0xFF;;
> inputBuffer.CurrentTaskFile[3] = (i >> 8 ) & 0xFF;;
> inputBuffer.CurrentTaskFile[4] = (i >> 16 ) & 0xFF;;
> inputBuffer.CurrentTaskFile[5] = 0xE0;
> inputBuffer.CurrentTaskFile[6] = 0x20;
> inputBuffer.CurrentTaskFile[7] = 0;

I’m curious why you have represented these as bytes, when it seems some
much richer structure is being manipulated. I might have written a struct
with fields like

DWORD /
BIG-ENDIAN / whatever;

which has the additional comment that the value is to be represented as a
32-bit big-endian value; I would probably include the network header file
and use htonl to swap the bytes, e.g.,

somestructure.whatever = htonl(i);

which seems a lot cleaner. The magic constants such as 0xE0 an 0x20 also
make me nervous; I’d rather have symbolic names like

inputBuffer.Flags = PAINT_SURFACE_PURPLE | EXCLUDE_GREEN_BITS_ON_LAN
| FRAMMIS_THE_REFLECTED_VALUE | DONT_COMPLAIN_IF_IMPOSSIBLE;

instead of splitting what might be a 16-bit flags value into two
apparently-random constants. Or not. Hard to guess when you are
converting everything to a byte array.

f
Note also that I put spaces around the | so that human beings, not just
the C/C++ compiler, can read this, too.

>
> handle=
> CreateFile(L"\\.\PhysicalDrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ
| FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
>

And what became of that LPWSTR (or WCHAR ) to which you assigned an 8-bit
string?

Put spaces around the | operator. Humans have to read this as well.

>
>
> printf(“\n Error = %d”, GetLastError());
>

Note that this value is meaningless if the CreateFile succeeded, so why
are you printing it out here?

>
>
> if(handle)

So you plan to execute this if CreateFile failed? RTFM. If CreateFile
fails, it returns INVALID_HANDLE_VALUE, not NULL. INVALID_HANDLE_VALUE,
just to know the implementation detail, is
(HANDLE)(UINT_PTR)(INT_PTR)(-1).

>
> {
>
> DeviceIoControl(
>
> handle,
>
> ioControlCode,
>
> &inputBuffer,
>
> outBufferSize,

Why use outBufferSize as the size of the input? For that matter, why did
you create a totally unnecessary variable to hold the value; and if you
feel compelled to use the variable, why was it not declared const?

>
> &inputBuffer,
>
> outBufferSize,
>
> &bytesReturned,
>
> NULL //not an overlapped operation
>
> );
>
>
>
> printf(“IOCTL ATA\n”);
> printf(“\n status is %d”,inputBuffer.CurrentTaskFile[6]);

You are printing this out even though the operation may have failed,
meaning that the value in the status is not defined. You should only
print this out if it succeeded.

>
> printf(“\n Error = %d”, GetLastError());

Again, this value is totally meaningless if the DeviceIoControl succeeded.
You should only print this out if it failed.

> PrintDataBuffer(buffer, 512);

If it failed, the data in buffer is probably meaningless, so why are you
bothering to print it out? This should only be printed out if the
operation succeeded.

> memcpy(&vATA[0],buffer, 512);

And you know that vATA points to a buffer of at least 512 bytes exactly how?

the correct code would be
memcpy(&vATA[0], buffer, min(sizeof(buffer), vATA.size()) );
and if you want to believe the buffer has 512 bytes in it, that is
expecting a lot. The correct code could also be

vATA.resize(sizeof(buffer));
memcpy(&vATA[0], vATA.size());

it is NEVER safe to assume that any buffer pointer which is passed in
represents a buffer of a valid size; if you were not using std::vector,
you would be obliged to pass in the explicit length. In some companies,
failing to check buffer boundaries is a firable offense (usually the first
time is just a stern warning). Since vATA is a std::vector, you CANNOT
assume that it has been properly sized; you must size it yourself before
copying into it, or accept the size it has. Due to errors in the calling
code, it is entirely possible the size of the vector could be zero, in
which case the memcpy is erroneous because it does not know if the target
pointer is even a valid pointer. Defer the resize() to this routine.
>
>
>
> }
> CloseHandle(handle);
> return true;

What happened to that incorrectly-declared and improperly-initialized
bResult? You are returning ‘true’ even if it failed? And you are calling
CloseHandle on a handle whose value is not known, and could be
INVALID_HANDLE_VALUE?
>
>
> }
>
> For ATA the inputBuffer.CurrentTaskFile[6] = 0x20;
> for 48 bit is 0x24 (or read Ext - but that works only till LBA 2 and 3
returns 0’s with error 81 or invalid arguments).

If error code 81 is returned, you have no idea what LBA 2 or 3 returned,
because the value is almost certainly undefined.

And what are the meanings of 0x20 or 0x24? Either they are bit fields, in
which case you should be using symbolic names, or they are truly the
numeric constants 32 and 36. It is hard to guess.

>
> Via the API:
>
> bool ReadMBRSystemDrive(int i, std::vector& vBytes, DWORD& size) {

Note that ‘size’ is unneeded. You can use vBytes.size() to carry this
information back

But you still insist on passing a length in as a signed value!

> // Open the physical hard drive
> HANDLE phDisk = NULL;
> phDisk=
> CreateFile(L"\\.\physicaldrive0",GENERIC_READ,FILE_SHARE_READ |
FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
> DWORD ret = GetLastError();
> if(!(phDisk != NULL && GetLastError() ==0) )
> return false;

This code is complete and utter nonsense. The value of GetLastError() is
undefined if CreateFile succeeds. Note also that if it succeeds, and
phDisk (which is not a pointer to a handle as the name suggests; if you
can’t use HN correctly, don’t use it at all, since it only confuses the
reader) is not NULL, and some random API in history (or the occasional
library call) happened to leave the GetLastError as 0, you will return
false, which says to declare failure if it succeeds, and, by the way, if
it succeeded, you have a handle leak.

The correct code would be
HANDLE hDisk;
hDisk = CreateFile(…as above…);
if(hDisk == INVALID_HANDLE_VALUE)
{ /
failed /
// …maybe some nice printout here such as the reason for the
failure…
return false;
} /
failed /

> // Retrieve the sector size
> DWORD dwReturnLength = 0;
> DISK_GEOMETRY infoDiskGeometry;
> if( !::DeviceIoControl(phDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY,
> NULL, 0,
> &infoDiskGeometry, sizeof(infoDiskGeometry),
> &dwReturnLength, NULL) )
> {
> DWORD dwErr = GetLastError();
> return false;
> }

See, you DO know how to write the correct code! You did it here!

> size = infoDiskGeometry.BytesPerSector;
> // A vector large enough to hold one sector
> vBytes.resize(infoDiskGeometry.BytesPerSector);
> // Read first sector from the disk
> if( SetFilePointer(phDisk, (i
infoDiskGeometry.BytesPerSector),
> NULL,FILE_BEGIN) == INVALID_SET_FILE_POINTER )
> return false;
> DWORD numberRead = 0;
> if(! ReadFile(phDisk,
> reinterpret_cast(&vBytes[0]),infoDiskGeometry.BytesPerSector,&numberRead,NULL)
)

The reinterpret_case is unnecessary since any pointer can be recast to
void
(or LPVOID or PVOID, which would be the more natural expressions of
this in an app)

> {

It might be nice to do some printout of the error reason here. Also,
since you clearly have a valid open handle at this point, you should do a
::CloseHandle on it, or you have a really nice handle leak.

> return false;
> }

At this point, you know how many bytes were read, so why aren’t you
assigning it to ‘size’, the DWORD& whose apparent purpose is to return
this? Actually, I would do a resize() of the vector to be this value, so
the additional reference parameter would be unnecessary. Also, it is
worth pointing out that if the value is OTHER than the size of a sector,
there is something deeply wrong.

> CloseHandle(phDisk);
> return true;
> }
>
> And I am looping
>
> while ( true )
> {
> DWORD size = 0;
> std::vector sysDriveApi;
> ReadMBRSystemDrive( i, sysDriveApi, size);

It won’t, because readMBRSystemDrive does not set size. Also, we have no
idea what i is, since it is not declared or set anywhere. Note also that
if ReadMBRSystemDrive fails, you keep going ahead and running the program,
instead of printing an error message and exiting the loop. Why?

> std::vector sysDriveScsi (size);

Since size is not set, this will create a buffer of size 0, which you
will then pass in and blindly use, and since the meaning of
&sysDriveScsi[0] is meaningless if sysDriveScsi.size() == 0, nothing but
total disaster can follow.

> ReadMBRUsingScsiPassThruDirect10withSense(i, sysDriveScsi);
std::vector sysDriveAta (size );

Why does readMBRUsing…Sense have any right to presume that sysDriveScsi
has a buffer of valid length? See previous comment. You cannot trust the
value to be meaningful in the routine, and it is poor programming practice
to make the assumption that it is correct.

> ReadMBRUsingATAPassthru( i, sysDriveAta);
> if( memcmp(&sysDriveApi[0],&sysDriveScsi[0], size) != 0 ||
> memcmp(&sysDriveApi[0],&sysDriveAta[0], size) != 0 )

Both terms of the above disjunction are identical!!!

> {
> printf(" LBA %d does not match \n", i);
> PrintDataBuffer(reinterpret_cast(&sysDriveApi[0]),
> size);
> PrintDataBuffer(reinterpret_cast(&sysDriveScsi[0]),
> size);
> PrintDataBuffer(reinterpret_cast(&sysDriveAta[0]),
> size);

Why doesn’t PrintDataBuffer take a std::vector & argument? Why all
this clumsiness at the call site?

> if( memcmp( &sysDriveScsi[0], &sysDriveAta[0], size) != 0) {
> printf(" LBA SCSI/ATA %d does not match \n", i);
> }
> //break;
> }
> i++;

I find the i++ to be bizarre. If this is the offset, incrementing it by 1
is weird. If, on the other hand, it is the LBA, then it would not be a
signed int, but it would have a more meaningful name than “i”, e.g.
DWORD LBAnumber = 0;
and
LBAnumber++;
would make it clear what is going on.
>
> }
>
> With 0x24 in the ATA command it breaks at LBA 3 with ATA returning 0’s
and
> error 81.
> With 0x20, it fails on LBA 3f where SCSI and ATA match but are both
incorrect.
>
> Please note this is just working code (it has not been cleaned up and
might have some minor inconsistencies).
>
> I would appreciate any guidance/ test code that might be pointed to.
>
> Thanks,
> Sonia.
>
>
>
> —
> NTDEV is sponsored by OSR
>
> Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev
>
> OSR is HIRING!! See http://www.osr.com/careers
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

On 1/11/2014 6:34 PM, xxxxx@flounder.com wrote:

Note also that because BOOL is a weird type introduced long before bool was in
the language, it sometimes takes on values OTHER than 0 or 1, so writing
if(somebool == TRUE)
is often erroneous. Since somebool is already TRUE or FALSE, comparing it
to TRUE or FALSE is just completely silly.

MSDN states that DeviceIoControl returns a non-zero value on success, so
it’s guaranteed to be FALSE on failure, but not TRUE on success.


Bruce Cran

Hi,

Thanks for all the input and the trouble with reviewing.
The data and code are all very rough hacks just to prototype. 512 etc is very specific to the drive I am testing on.
The others are SCSI and ATA commands (0x20 ATA read sector and 0x28 SCSI read sector).
The comments are very useful and I shall certainly keep in mind.
I appreciate the time and patience and I am sorry for the lack of clarity.
I will use the Hungarian notation in future posts and clean code in all future posts.

My basic problem is that the offsetting to a particular LBA is problematic
when I use the IOCTLs on disks other than VMWare.

I tried passing in the LBA in little endian and big endian formats (the ATA spec mentions Big Endian) and
both ways I get data that is not the same as the ReadFile API. (Thus LBA -which is i here- *sectorSize (on the test drive it is 512))
offsets correctly with readFile, but
when I pass in the LBA to the ATA /SCSI command the sectors match the readfile sector
up to sector 0x3e and after that the buffers are completely different. I also saw that the sector per track were 0x3f

  • so the first track is read correctly and the rest is not.

If there are samples to working code that offset correctly to an LBA for the ATA/SCSI IOCTLs I’d appreciate pointers.

Thanks,
Sonia.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of Bruce Cran
Sent: Saturday, January 11, 2014 7:04 PM
To: Windows System Software Devs Interest List
Subject: Re: [ntdev] Question on ATA/SCSI IOCTLs

On 1/11/2014 6:34 PM, xxxxx@flounder.com wrote:

Note also that because BOOL is a weird type introduced long before bool was in
the language, it sometimes takes on values OTHER than 0 or 1, so writing
if(somebool == TRUE)
is often erroneous. Since somebool is already TRUE or FALSE, comparing it
to TRUE or FALSE is just completely silly.

MSDN states that DeviceIoControl returns a non-zero value on success, so
it’s guaranteed to be FALSE on failure, but not TRUE on success.


Bruce Cran


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

> On 1/11/2014 6:34 PM, xxxxx@flounder.com wrote:

> Note also that because BOOL is a weird type introduced long before bool
> was in
> the language, it sometimes takes on values OTHER than 0 or 1, so writing
> if(somebool == TRUE)
> is often erroneous. Since somebool is already TRUE or FALSE, comparing
> it
> to TRUE or FALSE is just completely silly.

MSDN states that DeviceIoControl returns a non-zero value on success, so
it’s guaranteed to be FALSE on failure, but not TRUE on success.

Which is my point. If somebool is the result of DeviceIoControl, then
writing
if(somebool)
{ /* success */

will work, but writing
if(somebool == TRUE)

might or might not work. But if you get into the bad habit of writing
if(somebool == FALSE)
{ /* failure */
it leads you, or the next maintainer, to think that “somebol == TRUE” is
the complement, which it is not. Comparing a BOOL variable to a literal
to get a boolean result is at best silly and at worst catastrophicslly
wrong. Using the BOOL /as/ a boolean value can never fail, and is always
clear and unambiguous.
joe


Bruce Cran


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

> Hi,

Thanks for all the input and the trouble with reviewing.
The data and code are all very rough hacks just to prototype. 512 etc is
very specific to the drive I am testing on.
The others are SCSI and ATA commands (0x20 ATA read sector and 0x28 SCSI
read sector).

#define ATA_READ_SECTOR 0x20
#define SCSI_READ_SECTOR 0x28

The comments are very useful and I shall certainly keep in mind.
I appreciate the time and patience and I am sorry for the lack of clarity.
I will use the Hungarian notation in future posts and clean code in all
future posts.

My basic problem is that the offsetting to a particular LBA is problematic
when I use the IOCTLs on disks other than VMWare.

I tried passing in the LBA in little endian and big endian formats (the
ATA spec mentions Big Endian) and
both ways I get data that is not the same as the ReadFile API. (Thus LBA
-which is i here- *sectorSize (on the test drive it is 512))
offsets correctly with readFile, but
when I pass in the LBA to the ATA /SCSI command the sectors match the
readfile sector
up to sector 0x3e and after that the buffers are completely different. I
also saw that the sector per track were 0x3f

  • so the first track is read correctly and the rest is not.

If there are samples to working code that offset correctly to an LBA for
the ATA/SCSI IOCTLs I’d appreciate pointers.

What is the nature of what you are trying to accomplish? Writing to raw
sectors carries certain risks. I don’t know the physical organization of
disk drives, but those in ntfsd probably do. You might ask there.

joe

Thanks,
Sonia.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Bruce Cran
Sent: Saturday, January 11, 2014 7:04 PM
To: Windows System Software Devs Interest List
Subject: Re: [ntdev] Question on ATA/SCSI IOCTLs

On 1/11/2014 6:34 PM, xxxxx@flounder.com wrote:
> Note also that because BOOL is a weird type introduced long before bool
> was in
> the language, it sometimes takes on values OTHER than 0 or 1, so writing
> if(somebool == TRUE)
> is often erroneous. Since somebool is already TRUE or FALSE, comparing
> it
> to TRUE or FALSE is just completely silly.

MSDN states that DeviceIoControl returns a non-zero value on success, so
it’s guaranteed to be FALSE on failure, but not TRUE on success.


Bruce Cran


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

> #define SCSI_READ_SECTOR 0x28

I think SCSI has a multitude of READ commands.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

>> #define SCSI_READ_SECTOR 0x28

I think SCSI has a multitude of READ commands.

All the more reason to have definitions for them. I could only make a
suggestion based on the available information in the post.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Hi,

Thanks. Yes my final (non-hacked clean production code uses the #defines). Thanks so much. I plan to go through your comments and make sure my final version does not have any of the issues mentioned. I do use if(!::DeviceIoControl ) and const ints etc. I am not sure I should be pasting that code on the forum as it is for my company/project.
I will certainly make sure in future all code I paste conforms to the standards so it is easier to read and less distracting from the main issue.

I am reading from the sectors (not writing to them).

My current concern is ATA drives -which my poorly written test hack was trying to address. What I noticed when I issued the ATA identify device command (0xEC) and looked at the bits (on further hacking the smartmontools printatadata function ) and comparing with the ATA standards in the ATA spec, VMware uses ATA 4 and the Read Sector command works there. On my drive which is ATA 8 the same read after sector 0x3e does not offset to 0x3f as it should. When I pass in 0x3f, it seems to offset to a very “distant” sector - this I know because I used WinHex to compare the bytes that were read. Also after ATA 6 48 bit LBA support is available and since the ATA 8 drive is 48 bit enabled, I tried that as well (ATA 0x24 or read sector extended) with the same arbitrary results.

From the T13 manuals this particular command and the LBA addressing has not changed and should be implemented by the driver that supports ATA-8.

So I was really confused and wondered if there was any implementation available that I could reference.

I will check in ntfsd.

Thanks so much again for the painstaking code review and I apologize for having taken up your valuable time by pasting my test hack.

Thanks,
Sonia.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@flounder.com
Sent: Sunday, January 12, 2014 7:23 PM
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] Question on ATA/SCSI IOCTLs

> #define SCSI_READ_SECTOR 0x28

I think SCSI has a multitude of READ commands.

All the more reason to have definitions for them. I could only make a
suggestion based on the available information in the post.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

A few suggestions:-

  1. IOCTL_ATA_PASS_THROUGH(_DIRECT) may or may not be supported correctly by the driver stack. Historically there have been various ways in which the disk stack may support injection of ATA commands. I’ve had more success with IOCTL_SCSI_PASS_THROUGH_DIRECT, but using SCSIOP 0x85 (ATA PASS THROUGH 16), and wrap up an ATA command within that, as defined by T10 SAT (SCSI-ATA translation) spec.

  2. There are many ATA READ opcodes. 0x20 (READ SECTORS) is pretty ancient, and I doubt it’s much used any more (hence it wouldn’t surprise me if there are bugs in its implementation). I’d use READ SECTORS EXT (0x24), or better still READ DMA EXT (0x25), at least they’re more likely to support 48-bit addressing.

Thanks very much for your reply - I will try those out.

Thanks,
Sonia.

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@thegreenho.me
Sent: Monday, January 13, 2014 3:33 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Question on ATA/SCSI IOCTLs

A few suggestions:-

  1. IOCTL_ATA_PASS_THROUGH(_DIRECT) may or may not be supported correctly by the driver stack. Historically there have been various ways in which the disk stack may support injection of ATA commands. I’ve had more success with IOCTL_SCSI_PASS_THROUGH_DIRECT, but using SCSIOP 0x85 (ATA PASS THROUGH 16), and wrap up an ATA command within that, as defined by T10 SAT (SCSI-ATA translation) spec.

  2. There are many ATA READ opcodes. 0x20 (READ SECTORS) is pretty ancient, and I doubt it’s much used any more (hence it wouldn’t surprise me if there are bugs in its implementation). I’d use READ SECTORS EXT (0x24), or better still READ DMA EXT (0x25), at least they’re more likely to support 48-bit addressing.


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer