I tried issuing a SCSI Read(10) command to a physical drive on a Windows 7 machine. Below is the code snippet that I am using. It is failing with error code 87.
void scsi_read() { const UCHAR cdb[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 512, 0 }; UCHAR buf[512]; BYTE senseBuf[196]; const int SENSE_LENGTH = 196; LPCSTR fname = "\\\\.\\E:"; HANDLE fh; DWORD ioctl_bytes; DWORD err = 0; SCSI_PASS_THROUGH s = {0}; memcpy(s.Cdb, cdb, sizeof(cdb)); s.CdbLength = 10; s.DataIn = SCSI_IOCTL_DATA_IN; s.TimeOutValue = 30; s.Length = sizeof(SCSI_PASS_THROUGH); s.ScsiStatus = 0x00; s.SenseInfoOffset = senseBuf; s.SenseInfoLength = SENSE_LENGTH; s.DataBufferOffset = buf; s.DataTransferLength = 512; fh = CreateFile("\\\\.\\E:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(fh == INVALID_HANDLE_VALUE) { printf("Could not open %s file, error %d\n", fname, GetLastError()); return (FALSE); } int ret = DeviceIoControl(fh,IOCTL_SCSI_PASS_THROUGH, &s,sizeof(s), //scsiPassThrough.sizeof, &s, sizeof(s), &ioctl_bytes, NULL); printf("ret %d",(int)ret); if (ret==1) { printf("OK"); } else { err = GetLastError(); printf("Last error code %u\n", err); printf("Return size %d\n", ioctl_bytes); printf("Sense data\n"); int i=0; for (i = 0; i < 20; i++) { printf("\t%x", senseBuf[i]); } printf("\n"); } CloseHandle(fh); }
Error: Hex dumps are printed in the output
you got error code 87 - ERROR_INVALID_PARAMETER
because code totally wrong.
for example:
const UCHAR cdb[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 512, 0 };
but 512
is > 255
(MAXUCHAR
) are you not got compiler warning here ?
warning C4305: 'initializing': truncation from 'int' to 'const UCHAR'
look at this line !
s.DataBufferOffset = buf;
from SCSI_PASS_THROUGH
structure:
DataBufferOffset
Contains an offset from the beginning of this structure to the data buffer. The offset must respect the data alignment requirements of the device.
so offset to buffer, not pointer to buffer
for use this correct you code need be like this:
struct MY_DATA : SCSI_PASS_THROUGH { UCHAR buf[512]; } s; s.DataBufferOffset = FIELD_OFFSET(MY_DATA, buf);
but better use SCSI_PASS_THROUGH_DIRECT
with IOCTL_SCSI_PASS_THROUGH_DIRECT
you hardcode sector size (512), when need get it at runtime. and how you initialize CDB
?!? at all unclear what you try todo.
working code example (sorry but on c++
instead c
)
#define _NTSCSI_USER_MODE_ #include <scsi.h> #include <ntddscsi.h> BOOL scsi_read(HANDLE fh, PVOID buf, DWORD cb, ULONGLONG LogicalBlock, ULONG TransferBlocks) { SCSI_PASS_THROUGH_DIRECT s = { sizeof(SCSI_PASS_THROUGH_DIRECT), 0, 0, 0, 0, 0, 0, SCSI_IOCTL_DATA_IN, cb, 30, buf }; union { PUCHAR Cdb; CDB::_CDB10* Cdb10; CDB::_CDB16* Cdb16; }; Cdb = s.Cdb; if (MAXULONG < LogicalBlock || MAXUSHORT < TransferBlocks) { s.CdbLength = sizeof(CDB::_CDB16); Cdb16->OperationCode = SCSIOP_READ16; *(ULONGLONG*)Cdb16->LogicalBlock = _byteswap_uint64(LogicalBlock); *(ULONG*)Cdb16->TransferLength = _byteswap_ulong(TransferBlocks); } else { s.CdbLength = sizeof(CDB::_CDB10); Cdb10->OperationCode = SCSIOP_READ; *(ULONG*)&Cdb10->LogicalBlockByte0 = _byteswap_ulong((ULONG)LogicalBlock); *(USHORT*)&Cdb10->TransferBlocksMsb = _byteswap_ushort((USHORT)TransferBlocks); } DWORD ioctl_bytes; return DeviceIoControl(fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &s, sizeof(s), &s, sizeof(s), &ioctl_bytes, NULL); } BOOL test_scsi_read(PCWSTR fname) { BOOL fOk = FALSE; HANDLE fh = CreateFileW(fname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (fh != INVALID_HANDLE_VALUE) { DWORD ioctl_bytes; DISK_GEOMETRY_EX dg; if (DeviceIoControl(fh, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &dg, sizeof(dg), &ioctl_bytes, 0)) { // 16 sectors for example ULONG cb = 16 * dg.Geometry.BytesPerSector; if (PVOID buf = new CHAR[cb]) { // read first 16 sectors fOk = scsi_read(fh, buf, cb, 0, 16); if (ULONGLONG LogicalBlock = dg.DiskSize.QuadPart / dg.Geometry.BytesPerSector) { // read last sector fOk = scsi_read(fh, buf, dg.Geometry.BytesPerSector, LogicalBlock - 1, 1); } delete buf; } } CloseHandle(fh); } return fOk; } test_scsi_read(L"\\\\?\\e:");