Windows 上物理驱动器上的 SCSI 读取 (10)
Posted
技术标签:
【中文标题】Windows 上物理驱动器上的 SCSI 读取 (10)【英文标题】:SCSI Read(10) on a Physical Drive on Windows 【发布时间】:2017-01-04 03:58:56 【问题描述】:我尝试向 Windows 7 机器上的物理驱动器发出 SCSI Read(10) 命令。下面是我正在使用的代码 sn-p。它失败,错误代码为 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);
错误:输出中打印了十六进制转储
【问题讨论】:
【参考方案1】:您收到错误代码 87 - ERROR_INVALID_PARAMETER
,因为代码完全错误。
例如:
const UCHAR cdb[10] = 0x28, 0, 0, 0, 0, 0, 0, 0, 512, 0 ;
但是512
是> 255
(MAXUCHAR
) 你这里没有编译器警告吗?
warning C4305: 'initializing': truncation from 'int' to 'const UCHAR'
看看这条线!
s.DataBufferOffset = buf;
来自SCSI_PASS_THROUGH
结构:
DataBufferOffset
包含从这个结构的开头到数据的偏移量 缓冲。偏移量必须遵守数据对齐要求 设备。
所以 offset 到缓冲区,而不是 指针 到缓冲区
为了使用这个正确的代码,你的代码需要是这样的:
struct MY_DATA : SCSI_PASS_THROUGH
UCHAR buf[512];
s;
s.DataBufferOffset = FIELD_OFFSET(MY_DATA, buf);
但最好将SCSI_PASS_THROUGH_DIRECT
与IOCTL_SCSI_PASS_THROUGH_DIRECT
一起使用
您在运行时需要时硬编码扇区大小 (512)。以及如何初始化CDB
?!?完全不清楚你尝试做什么。
工作代码示例(抱歉,使用 c++
而不是 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:");
【讨论】:
以上是关于Windows 上物理驱动器上的 SCSI 读取 (10)的主要内容,如果未能解决你的问题,请参考以下文章