TkTech / smartie

Pure-python ATA/SATA/ATAPI/SCSI and disk enumeration library for Linux/Windows/OS X.
https://tkte.ch/smartie/
MIT License
11 stars 5 forks source link

SCSI cmd requested data size can be different to actual data size transferred. No way to see this atm. #14

Closed ZakDanger closed 5 months ago

ZakDanger commented 5 months ago

When doing scsi commands that request for data from a device, the actual size of the data transferred can be different to the requested size. I guess this usually occurs when you ask for a big amount of data, but the device only supports a smaller amount of data.

see: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddscsi/ns-ntddscsi-_scsi_pass_through_direct "The value in DataTransferLength must be an integral multiple of this predefined, minimum length that is specified by the device. If an underrun occurs, the miniport driver must update this member to the number of bytes actually transferred."

An example of this is when doing an Inquiry (12h) command. On a dvd-drive I have I ask for 0xF0 bytes but my device only returns 0x60 bytes. If you aren't made aware that the actual data returned is less than the requested data, you may then try to use the full amount of data which is not going to be the correct or expected values. (Note that the amount of data returned by an Inquiry command can be device specific. So it may not be 0x60 bytes on all devices)

For Windows the value "SCSIPassThroughDirect.data_transfer_length" gets updated during the call to DeviceIoControl() to contain the actual size of the data transferred. So in my above example it would be intially set to 0xF0 bytes before calling DeviceIoControl(), then once the call is finished, reading out the value would show that it is now 0x60.

It looks like SGIO on Linux has a value "resid" to show the actual number of bytes transferred on it.

I am not sure how to best handle this in your smartie code. Do you think it would be safe to truncate the 'data' parameter passed into SCSIDevice.issue_command()? Or instead return 2 values, where one is the sense data and the other is the data? Or include a new parameter passed in for data size?

TkTech commented 5 months ago

We don't know where the data field is coming from, or even what memory allocator was actually used to allocate it, so we have no way to safely resize it - and we probably wouldn't want to, since it'll likely be reused by a following call for performance. We should probably just return the size actually read, if available, or None if unknown, in a tuple with the sense information.

TkTech commented 5 months ago

Can you try the changes in main? I won't have a chance to test on Windows myself until Sunday.