From: Akihiro Sagawa sagawa.aki@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58257 --- dlls/ntdll/unix/cdrom.c | 101 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/cdrom.c b/dlls/ntdll/unix/cdrom.c index 6fc444dee31..08365087f49 100644 --- a/dlls/ntdll/unix/cdrom.c +++ b/dlls/ntdll/unix/cdrom.c @@ -232,6 +232,23 @@ typedef struct _SCSI_PASS_THROUGH32 UCHAR Cdb[16]; } SCSI_PASS_THROUGH32, *PSCSI_PASS_THROUGH32;
+typedef struct _SCSI_PASS_THROUGH_DIRECT32 + { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG DataBuffer; /* actually, VOID* POINTER_32 */ + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH_DIRECT32, *PSCSI_PASS_THROUGH_DIRECT32; + /* The documented format of DVD_LAYER_DESCRIPTOR is wrong. Even the format in the * DDK's header is wrong. There are four bytes at the start defined by * MMC-5. The first two are the size of the structure in big-endian order as @@ -1729,6 +1746,72 @@ static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, const SCSI_PASS_THROUGH_DIRE return ret; }
+static NTSTATUS CDROM_ScsiPassThroughDirect32(int fd, const SCSI_PASS_THROUGH_DIRECT32 *in_pkt32, SCSI_PASS_THROUGH_DIRECT32 *out_pkt32) +{ + SCSI_PASS_THROUGH_DIRECT *pkt; + ULONG_PTR ptr; + NTSTATUS ret; + + if (in_pkt32->Length < sizeof(SCSI_PASS_THROUGH_DIRECT32)) + return STATUS_BUFFER_TOO_SMALL; + + if (in_pkt32->CdbLength > 16) + return STATUS_INVALID_PARAMETER; + +#ifdef SENSEBUFLEN + if (in_pkt32->SenseInfoLength > SENSEBUFLEN) + return STATUS_INVALID_PARAMETER; +#endif + + if (in_pkt32->SenseInfoLength > 0) + { + if (in_pkt32->SenseInfoOffset < sizeof(SCSI_PASS_THROUGH_DIRECT32)) + return STATUS_INVALID_PARAMETER; + ptr = (ULONG_PTR)in_pkt32 + in_pkt32->SenseInfoOffset; + if (ptr < (ULONG_PTR)in_pkt32) + return STATUS_INVALID_PARAMETER; + if ((ptr + in_pkt32->SenseInfoLength) < ptr) + return STATUS_INVALID_PARAMETER; + } + + pkt = calloc(1, sizeof(SCSI_PASS_THROUGH_DIRECT) + in_pkt32->SenseInfoLength); + if (!pkt) return STATUS_NO_MEMORY; + + pkt->Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + pkt->CdbLength = in_pkt32->CdbLength; + pkt->SenseInfoLength = in_pkt32->SenseInfoLength; + pkt->DataIn = in_pkt32->DataIn; + pkt->DataTransferLength = in_pkt32->DataTransferLength; + pkt->TimeOutValue = in_pkt32->TimeOutValue; + pkt->DataBuffer = ULongToPtr(in_pkt32->DataBuffer); + pkt->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT); + memcpy(pkt->Cdb, in_pkt32->Cdb, sizeof(pkt->Cdb)); + + ret = CDROM_ScsiPassThroughDirect(fd, pkt, pkt); + if (NT_ERROR(ret)) goto done; + + out_pkt32->Length = sizeof(SCSI_PASS_THROUGH_DIRECT32); + out_pkt32->ScsiStatus = pkt->ScsiStatus; + out_pkt32->PathId = pkt->PathId; + out_pkt32->TargetId = pkt->TargetId; + out_pkt32->Lun = pkt->Lun; + out_pkt32->CdbLength = pkt->CdbLength; + out_pkt32->SenseInfoLength = pkt->SenseInfoLength; + out_pkt32->DataIn = pkt->DataIn; + out_pkt32->DataTransferLength = pkt->DataTransferLength; + out_pkt32->TimeOutValue = pkt->TimeOutValue; + out_pkt32->DataBuffer = in_pkt32->DataBuffer; + out_pkt32->SenseInfoOffset = in_pkt32->SenseInfoOffset; + memcpy(out_pkt32->Cdb, pkt->Cdb, sizeof(out_pkt32->Cdb)); + memcpy((char*)out_pkt32 + out_pkt32->SenseInfoOffset, + (const char*)pkt + pkt->SenseInfoOffset, + pkt->SenseInfoLength); + +done: + free(pkt); + return ret; +} + /****************************************************************** * CDROM_ScsiPassThrough * Implements IOCTL_SCSI_PASS_THROUGH @@ -3026,10 +3109,20 @@ NTSTATUS cdrom_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROUTINE apc else status = CDROM_GetAddress(fd, out_buffer); break; case IOCTL_SCSI_PASS_THROUGH_DIRECT: - sz = sizeof(SCSI_PASS_THROUGH_DIRECT); - if (in_buffer == NULL || out_buffer == NULL) status = STATUS_INVALID_PARAMETER; - else if (out_size < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL; - else status = CDROM_ScsiPassThroughDirect(fd, in_buffer, out_buffer); + if (in_wow64_call()) + { + sz = sizeof(SCSI_PASS_THROUGH_DIRECT32); + if (in_buffer == NULL || out_buffer == NULL) status = STATUS_INVALID_PARAMETER; + else if (out_size < sizeof(SCSI_PASS_THROUGH_DIRECT32)) status = STATUS_BUFFER_TOO_SMALL; + else status = CDROM_ScsiPassThroughDirect32(fd, in_buffer, out_buffer); + } + else + { + sz = sizeof(SCSI_PASS_THROUGH_DIRECT); + if (in_buffer == NULL || out_buffer == NULL) status = STATUS_INVALID_PARAMETER; + else if (out_size < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL; + else status = CDROM_ScsiPassThroughDirect(fd, in_buffer, out_buffer); + } break; case IOCTL_SCSI_PASS_THROUGH: if (in_wow64_call())