879 lines
24 KiB
C
879 lines
24 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
burn.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
A user mode app that allows simple commands to be sent to a
|
|||
|
selected scsi device.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User mode only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
03-26-96 : Created
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <string.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <assert.h>
|
|||
|
#include <sptlib.h>
|
|||
|
#include "burn.h"
|
|||
|
#include <winioctl.h>
|
|||
|
|
|||
|
#define MIN_WRITE_SECTORS (0x10)
|
|||
|
|
|||
|
#if DBG
|
|||
|
#define OUTPUT stdout
|
|||
|
#define FPRINTF(x) fprintf x
|
|||
|
#define PRINTBUFFER(x) PrintBuffer x
|
|||
|
#else
|
|||
|
#define OUTPUT stdout
|
|||
|
#define FPRINTF(x)
|
|||
|
#define PRINTBUFFER(x)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
typedef struct _SENSE_STUFF {
|
|||
|
UCHAR Sense;
|
|||
|
UCHAR Asc;
|
|||
|
UCHAR Ascq;
|
|||
|
UCHAR Reserved;
|
|||
|
} SENSE_STUFF, *PSENSE_STUFF;
|
|||
|
|
|||
|
SENSE_STUFF AllowedBurnSense[] = {
|
|||
|
{SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS, 0},
|
|||
|
{SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_OPERATION_IN_PROGRESS, 0}
|
|||
|
};
|
|||
|
#define AllowedBurnSenseEntries (sizeof(AllowedBurnSense)/sizeof(SENSE_STUFF))
|
|||
|
|
|||
|
SENSE_STUFF AllowedReadDiscInfo[] = {
|
|||
|
{ SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_OPERATION_IN_PROGRESS, 0 },
|
|||
|
{ SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS, 0 },
|
|||
|
{ SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_FORMAT_IN_PROGRESS, 0 },
|
|||
|
{ SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_ILLEGAL_MODE_FOR_THIS_TRACK, 0, 0 },
|
|||
|
{ SCSI_SENSE_UNIT_ATTENTION, SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION, 0, 0 }
|
|||
|
};
|
|||
|
#define AllowedReadDiscInfoEntries (sizeof(AllowedReadDiscInfo)/sizeof(SENSE_STUFF))
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
IsSenseDataInTable(
|
|||
|
IN PSENSE_STUFF Table,
|
|||
|
IN LONG Entries, // in table
|
|||
|
IN PSENSE_DATA SenseData
|
|||
|
)
|
|||
|
{
|
|||
|
LONG i;
|
|||
|
UCHAR sense = SenseData->SenseKey & 0xf;
|
|||
|
UCHAR asc = SenseData->AdditionalSenseCode;
|
|||
|
UCHAR ascq = SenseData->AdditionalSenseCodeQualifier;
|
|||
|
|
|||
|
for (i = 0; i < Entries; i++ ) {
|
|||
|
if ((Table[i].Sense = sense) &&
|
|||
|
(Table[i].Ascq = ascq ) &&
|
|||
|
(Table[i].Asc = asc )
|
|||
|
) {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int __cdecl main(int argc, char *argv[])
|
|||
|
{
|
|||
|
int i = 0;
|
|||
|
HANDLE cdromHandle;
|
|||
|
HANDLE isoImageHandle;
|
|||
|
char buffer[32];
|
|||
|
DWORD Foo;
|
|||
|
BOOLEAN Erase = FALSE;
|
|||
|
|
|||
|
if(argc < 3) {
|
|||
|
printf("Usage: burn <drive> <image> [/Erase]\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
if (argc == 4) {
|
|||
|
|
|||
|
if (!strncmp( "/E", argv[3], 2)) {
|
|||
|
|
|||
|
Erase = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
printf("Unrecognized switch\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sprintf(buffer, "\\\\.\\%s", argv[1]);
|
|||
|
|
|||
|
cdromHandle = CreateFile(buffer,
|
|||
|
GENERIC_READ | GENERIC_WRITE,
|
|||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|||
|
NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_ATTRIBUTE_NORMAL,
|
|||
|
NULL);
|
|||
|
|
|||
|
if(cdromHandle == INVALID_HANDLE_VALUE) {
|
|||
|
printf("Error %d opening device %s\n", GetLastError(), buffer);
|
|||
|
return -2;
|
|||
|
}
|
|||
|
|
|||
|
if (!DeviceIoControl( cdromHandle,
|
|||
|
FSCTL_LOCK_VOLUME,
|
|||
|
NULL, 0,
|
|||
|
NULL, 0,
|
|||
|
&Foo,
|
|||
|
NULL)) {
|
|||
|
|
|||
|
printf("Error %d locking volume\n", GetLastError());
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
printf("Locked volume for burn\n");
|
|||
|
}
|
|||
|
|
|||
|
isoImageHandle = CreateFile(argv[2],
|
|||
|
GENERIC_READ,
|
|||
|
FILE_SHARE_READ,
|
|||
|
NULL,
|
|||
|
OPEN_EXISTING,
|
|||
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|||
|
NULL);
|
|||
|
if (isoImageHandle == INVALID_HANDLE_VALUE) {
|
|||
|
printf("Error %d opening image file %s\n",
|
|||
|
GetLastError(), argv[2]);
|
|||
|
CloseHandle(cdromHandle);
|
|||
|
return -2;
|
|||
|
}
|
|||
|
|
|||
|
BurnCommand(cdromHandle, isoImageHandle, Erase);
|
|||
|
|
|||
|
CloseHandle(isoImageHandle);
|
|||
|
CloseHandle(cdromHandle);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
burns an ISO image to cdrom
|
|||
|
|
|||
|
Arguments:
|
|||
|
CdromHandle - a file handle to send the ioctl to
|
|||
|
|
|||
|
argc - the number of additional arguments (2)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful
|
|||
|
The value of GetLastError() from the point of failure
|
|||
|
|
|||
|
--*/
|
|||
|
DWORD
|
|||
|
BurnCommand(
|
|||
|
HANDLE CdromHandle,
|
|||
|
HANDLE IsoImageHandle,
|
|||
|
BOOLEAN Erase
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD numberOfBlocks;
|
|||
|
DWORD availableBlocks;
|
|||
|
DWORD nwa;
|
|||
|
LONG i;
|
|||
|
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// verify the iso image file looks correct
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
if (!VerifyIsoImage(IsoImageHandle, &numberOfBlocks)) {
|
|||
|
printf("Error verifying ISO image\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// verify (as best as possible) that it's blank media
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (Erase && !EraseMedia(CdromHandle)) {
|
|||
|
printf("Error erasing media\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
if (!VerifyBlankMedia(CdromHandle)) {
|
|||
|
printf("Error verifying blank media\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// verify media capacity
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
if (!VerifyMediaCapacity(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("Error verifying media capacity\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// DVD-R does not require mode page changes, -RW does, try anyway.
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!SetWriteModePageDao(CdromHandle)) {
|
|||
|
printf("Error setting DAO (Required for -RW only) - ignoring.\n");
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// send the time stamp
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
if (!SendTimeStamp(CdromHandle, "20010614000000")) { // YYYYMMDDHHMMSS format
|
|||
|
printf("Error setting timestamp - ignoring\n");
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// optionally, send the User Specific Data
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// Reserve the RZone for this burn
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!ReserveRZone(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("Error reserving zone for burn\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// get NWA via Read RZone Informationcommand, specifying RZone 1 for blank disk
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Special case -- blank disc is always zero
|
|||
|
nwa = 0;
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// start writing
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!BurnThisSession(CdromHandle, IsoImageHandle, numberOfBlocks, nwa)) {
|
|||
|
printf("Error burning ISO image\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// wait for it to finish
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!WaitForBurnToComplete(CdromHandle)) {
|
|||
|
printf("Error waiting for burn to complete\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// eject the newly burned dvd!
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
if (!SendStartStopUnit(CdromHandle, FALSE, TRUE) ||
|
|||
|
!SendStartStopUnit(CdromHandle, TRUE, TRUE)) {
|
|||
|
printf("Error ejecting/reinserting disc\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
printf("burn successful!\n");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
BurnThisSession(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN HANDLE IsoImageHandle,
|
|||
|
IN DWORD NumberOfBlocks,
|
|||
|
IN DWORD FirstLba
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD bufferSize = 0x800 * MIN_WRITE_SECTORS; // sixteen blocks per...
|
|||
|
PUCHAR buffer = NULL;
|
|||
|
DWORD i;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Starting write: "));
|
|||
|
|
|||
|
buffer = LocalAlloc(LPTR, bufferSize);
|
|||
|
if (buffer == NULL) {
|
|||
|
FPRINTF((OUTPUT, "unable to allocate write buffer\n"));
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "............."));
|
|||
|
|
|||
|
for (i = 0; i < NumberOfBlocks; i += MIN_WRITE_SECTORS) {
|
|||
|
|
|||
|
CDB cdb;
|
|||
|
DWORD currentSize;
|
|||
|
DWORD readBytes;
|
|||
|
DWORD j;
|
|||
|
SENSE_DATA senseData;
|
|||
|
|
|||
|
{
|
|||
|
static CHAR progress[4] = { '|', '/', '-', '\\' };
|
|||
|
DWORD percent;
|
|||
|
percent = (i*1000) / NumberOfBlocks;
|
|||
|
// # # # . # % _ d o n e _ *
|
|||
|
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
|
|||
|
printf("%c %3d.%d%% done",
|
|||
|
progress[(i%0x40)/0x10],
|
|||
|
percent / 10, percent % 10
|
|||
|
);
|
|||
|
fflush(stdout);
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(buffer, bufferSize);
|
|||
|
|
|||
|
if (NumberOfBlocks - i >= MIN_WRITE_SECTORS) {
|
|||
|
currentSize = 0x800 * 0x10;
|
|||
|
} else if (NumberOfBlocks - i > 0) {
|
|||
|
// end of file case -- zero memory first!
|
|||
|
RtlZeroMemory(buffer, bufferSize);
|
|||
|
currentSize = (NumberOfBlocks - i) * 0x800;
|
|||
|
} else {
|
|||
|
FPRINTF((OUTPUT, "INTERNAL ERROR line %d\n", __LINE__));
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!ReadFile(IsoImageHandle, buffer, currentSize, &readBytes, NULL)) {
|
|||
|
FPRINTF((OUTPUT, "error reading from file %d\n", GetLastError()));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (readBytes != currentSize) {
|
|||
|
FPRINTF((OUTPUT, "error only read %d of %d bytes\n",
|
|||
|
readBytes, currentSize));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// must write the full buffer each time for DVD-R,
|
|||
|
// since it's a RESTRICTED_OVERWRITE medium and seems
|
|||
|
// to choke otherwise
|
|||
|
//
|
|||
|
|
|||
|
j = 0;
|
|||
|
retryThisWrite:
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.CDB10.OperationCode = SCSIOP_WRITE;
|
|||
|
cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&i)->Byte3;
|
|||
|
cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&i)->Byte2;
|
|||
|
cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&i)->Byte1;
|
|||
|
cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&i)->Byte0;
|
|||
|
|
|||
|
cdb.CDB10.TransferBlocksLsb = MIN_WRITE_SECTORS;
|
|||
|
|
|||
|
//
|
|||
|
// NOTE: we always send full buffer size to ensure 32k alignment
|
|||
|
//
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
buffer,
|
|||
|
&bufferSize,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
FALSE,
|
|||
|
10)) {
|
|||
|
|
|||
|
Sleep(100); // 100ms == .1 seconds
|
|||
|
|
|||
|
if (IsSenseDataInTable(AllowedBurnSense,
|
|||
|
AllowedBurnSenseEntries,
|
|||
|
&senseData)
|
|||
|
) {
|
|||
|
|
|||
|
// just sleep a bit...
|
|||
|
goto retryThisWrite;
|
|||
|
|
|||
|
} else if (j<4) {
|
|||
|
|
|||
|
j++;
|
|||
|
FPRINTF((OUTPUT, "Retrying write to LBA 0x%x\n", i));
|
|||
|
goto retryThisWrite;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "\nError %d in writing LBA 0x%x (%x times)\n",
|
|||
|
GetLastError(), i, j));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
|
|||
|
printf("Finished Writing\n");
|
|||
|
fflush(stdout);
|
|||
|
LocalFree(buffer);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PrintBuffer(
|
|||
|
IN PVOID Buffer,
|
|||
|
IN DWORD Size
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD offset = 0;
|
|||
|
PUCHAR buf = Buffer;
|
|||
|
|
|||
|
while (Size > 0x10) {
|
|||
|
printf("%08x:"
|
|||
|
" %02x %02x %02x %02x %02x %02x %02x %02x"
|
|||
|
" %02x %02x %02x %02x %02x %02x %02x %02x"
|
|||
|
"\n",
|
|||
|
offset,
|
|||
|
*(buf + 0), *(buf + 1), *(buf + 2), *(buf + 3),
|
|||
|
*(buf + 4), *(buf + 5), *(buf + 6), *(buf + 7),
|
|||
|
*(buf + 8), *(buf + 9), *(buf + 10), *(buf + 11),
|
|||
|
*(buf + 12), *(buf + 13), *(buf + 14), *(buf + 15)
|
|||
|
);
|
|||
|
Size -= 0x10;
|
|||
|
offset += 0x10;
|
|||
|
buf += 0x10;
|
|||
|
}
|
|||
|
|
|||
|
if (Size != 0) {
|
|||
|
|
|||
|
DWORD spaceIt;
|
|||
|
|
|||
|
printf("%08x:", offset);
|
|||
|
for (spaceIt = 0; Size != 0; Size--) {
|
|||
|
|
|||
|
if ((spaceIt%8)==0) {
|
|||
|
printf(" "); // extra space every eight chars
|
|||
|
}
|
|||
|
printf(" %02x", *buf);
|
|||
|
spaceIt++;
|
|||
|
buf++;
|
|||
|
}
|
|||
|
printf("\n");
|
|||
|
|
|||
|
}
|
|||
|
return;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
EraseMedia(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD bufferSize;
|
|||
|
|
|||
|
printf( "Attempting to blank media...\n");
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.AsByte[0] = 0xa1;
|
|||
|
cdb.AsByte[1] = 0x01; // minimal blank
|
|||
|
bufferSize = 0;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 12,
|
|||
|
NULL, &bufferSize, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d blanking media\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SetWriteModePageDao(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
PCDVD_WRITE_PARAMETERS_PAGE params = NULL;
|
|||
|
MODE_PARAMETER_HEADER10 header;
|
|||
|
PMODE_PARAMETER_HEADER10 buffer;
|
|||
|
|
|||
|
UCHAR mediumTypeCode;
|
|||
|
|
|||
|
CDB cdb;
|
|||
|
DWORD bufferSize;
|
|||
|
DWORD maxSize;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Setting DAO mode in WriteParameters mode page... "));
|
|||
|
|
|||
|
bufferSize = sizeof(MODE_PARAMETER_HEADER10);
|
|||
|
|
|||
|
RtlZeroMemory(&header, sizeof(MODE_PARAMETER_HEADER10));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|||
|
cdb.MODE_SENSE10.PageCode = 0x5;
|
|||
|
cdb.MODE_SENSE10.Dbd = 1;
|
|||
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8);
|
|||
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
(PUCHAR)&header, &bufferSize, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d getting mode page 0x05 from device(1)\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
bufferSize =
|
|||
|
(header.ModeDataLength[0] << 8) +
|
|||
|
(header.ModeDataLength[1] & 0xff);
|
|||
|
bufferSize += 2; // sizeof area that tells the remaining size
|
|||
|
|
|||
|
maxSize = bufferSize;
|
|||
|
|
|||
|
buffer = LocalAlloc(LPTR, bufferSize);
|
|||
|
if (!buffer) {
|
|||
|
FPRINTF((OUTPUT, "\nError -- unable to alloc %d bytes for mode parameters page\n",
|
|||
|
bufferSize));
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|||
|
cdb.MODE_SENSE10.PageCode = 0x5;
|
|||
|
cdb.MODE_SENSE10.Dbd = 1;
|
|||
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8);
|
|||
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
(PUCHAR)buffer, &bufferSize, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d getting mode page 0x05 from device(2)\n",
|
|||
|
GetLastError()));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
mediumTypeCode = buffer->MediumType;
|
|||
|
|
|||
|
//
|
|||
|
// bufferSize now holds the amount of data returned
|
|||
|
// this should be enough...
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
DWORD t =
|
|||
|
(buffer->BlockDescriptorLength[0] >> 8) +
|
|||
|
(buffer->BlockDescriptorLength[1] & 0xff);
|
|||
|
|
|||
|
if (t != 0) {
|
|||
|
fprintf(stderr, "BlockDescriptor non-zero! (%x)\n", t);
|
|||
|
SetLastError(1);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// pointer arithmetic here. (buffer+1) points just past the
|
|||
|
// end of the mode_parameter_header10.
|
|||
|
//
|
|||
|
|
|||
|
params = (PCDVD_WRITE_PARAMETERS_PAGE)(buffer + 1);
|
|||
|
FPRINTF((OUTPUT, "buffer = %p params = %p\n", buffer, params));
|
|||
|
|
|||
|
//
|
|||
|
// zero the header, but don't modify any settings that don't
|
|||
|
// need to be modified!
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(buffer, FIELD_OFFSET(MODE_PARAMETER_HEADER10,
|
|||
|
BlockDescriptorLength[0]));
|
|||
|
buffer->ModeDataLength[0] = (UCHAR)((bufferSize-2) >> 8);
|
|||
|
buffer->ModeDataLength[1] = (UCHAR)((bufferSize-2) & 0xff);
|
|||
|
buffer->MediumType = mediumTypeCode;
|
|||
|
|
|||
|
params->WriteType = 2; // DAO
|
|||
|
params->TestWrite = 0x00;
|
|||
|
params->Copy = 0x00; // original disc
|
|||
|
params->MultiSession = 0;
|
|||
|
params->BufferUnderrunFree = 1;
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
|
|||
|
cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
|
|||
|
cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR)(bufferSize >> 8);
|
|||
|
cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR)(bufferSize & 0xff);
|
|||
|
cdb.MODE_SELECT10.PFBit = 1;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
(PUCHAR)buffer, &bufferSize, FALSE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d sending mode page 0x05 to device - ignoring\n",
|
|||
|
GetLastError()));
|
|||
|
LocalFree(buffer);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
LocalFree(buffer);
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ReserveRZone(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN DWORD numberOfBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size = 0;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Reserving RZone... "));
|
|||
|
|
|||
|
if (numberOfBlocks % MIN_WRITE_SECTORS) {
|
|||
|
FPRINTF((OUTPUT, "increasing size by 0x%x blocks... ",
|
|||
|
MIN_WRITE_SECTORS - (numberOfBlocks % MIN_WRITE_SECTORS)));
|
|||
|
numberOfBlocks /= MIN_WRITE_SECTORS;
|
|||
|
numberOfBlocks *= MIN_WRITE_SECTORS;
|
|||
|
numberOfBlocks += MIN_WRITE_SECTORS;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.RESERVE_TRACK_RZONE.OperationCode = SCSIOP_RESERVE_TRACK_RZONE;
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[0] = (UCHAR)(numberOfBlocks >> 24);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[1] = (UCHAR)(numberOfBlocks >> 16);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[2] = (UCHAR)(numberOfBlocks >> 8);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[3] = (UCHAR)(numberOfBlocks >> 0);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
NULL, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Error reserving RZone\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SendStartStopUnit(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN BOOLEAN Start,
|
|||
|
IN BOOLEAN Eject
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size = 0;
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
|||
|
cdb.START_STOP.LoadEject = Eject;
|
|||
|
cdb.START_STOP.Start = Start;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 6,
|
|||
|
NULL, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Error sending Start/Stop unit\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SendTimeStamp(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN PUCHAR DateString
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
SEND_DVD_STRUCTURE_TIMESTAMP timeStamp;
|
|||
|
DWORD size;
|
|||
|
|
|||
|
size = sizeof(SEND_DVD_STRUCTURE_TIMESTAMP);
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Sending Timestamp... "));
|
|||
|
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.AsByte[0] = 0xbf;
|
|||
|
cdb.AsByte[7] = 0x0f; // format == time stamp
|
|||
|
cdb.AsByte[8] = (UCHAR)(size >> 8);
|
|||
|
cdb.AsByte[9] = (UCHAR)(size & 0xff);
|
|||
|
|
|||
|
RtlZeroMemory(&timeStamp, sizeof(SEND_DVD_STRUCTURE_TIMESTAMP));
|
|||
|
if (strlen(DateString) != 14) {
|
|||
|
FPRINTF((OUTPUT, "Incorrect string length for date\n"));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(timeStamp.Year, DateString+0x00, 4);
|
|||
|
RtlCopyMemory(timeStamp.Month, DateString+0x04, 2);
|
|||
|
RtlCopyMemory(timeStamp.Day, DateString+0x06, 2);
|
|||
|
RtlCopyMemory(timeStamp.Hour, DateString+0x08, 2);
|
|||
|
RtlCopyMemory(timeStamp.Minute, DateString+0x0a, 2);
|
|||
|
RtlCopyMemory(timeStamp.Second, DateString+0x0c, 2);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 12,
|
|||
|
(PUCHAR)&timeStamp, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Error sending dvd timestamp\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyBlankMedia(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
PDISK_INFORMATION diskInfo;
|
|||
|
DWORD maxSize = sizeof(DISK_INFORMATION);
|
|||
|
DWORD size;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Verifying blank disc... "));
|
|||
|
|
|||
|
diskInfo = LocalAlloc(LPTR, maxSize);
|
|||
|
if (diskInfo == NULL) {
|
|||
|
FPRINTF((OUTPUT, "\nError allocating diskinfo\n"));
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(diskInfo, sizeof(DISK_INFORMATION));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.READ_DISK_INFORMATION.OperationCode = SCSIOP_READ_DISK_INFORMATION;
|
|||
|
cdb.READ_DISK_INFORMATION.AllocationLength[0] = (UCHAR)(maxSize >> 8);
|
|||
|
cdb.READ_DISK_INFORMATION.AllocationLength[1] = (UCHAR)(maxSize & 0xff);
|
|||
|
|
|||
|
size = maxSize;
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
(PUCHAR)diskInfo, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d getting disk info\n",
|
|||
|
GetLastError()));
|
|||
|
LocalFree(diskInfo);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (diskInfo->LastSessionStatus != 0x00) {
|
|||
|
FPRINTF((OUTPUT, "disc is not blank!\n"));
|
|||
|
SetLastError(ERROR_MEDIA_INCOMPATIBLE);
|
|||
|
LocalFree(diskInfo);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
LocalFree(diskInfo);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyIsoImage(
|
|||
|
IN HANDLE IsoImageHandle,
|
|||
|
OUT PDWORD NumberOfBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
BY_HANDLE_FILE_INFORMATION isoImageInfo;
|
|||
|
LONGLONG size;
|
|||
|
|
|||
|
if (!GetFileInformationByHandle(IsoImageHandle, &isoImageInfo)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d getting file info for iso image\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
size = ((LONGLONG)isoImageInfo.nFileSizeHigh) << 32;
|
|||
|
size |= (LONGLONG)isoImageInfo.nFileSizeLow;
|
|||
|
|
|||
|
if ((isoImageInfo.nFileSizeLow % 2048) != 0) {
|
|||
|
FPRINTF((OUTPUT, "Error: The file size is not a multiple of 2048 (%I64d)\n",
|
|||
|
size));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "File size is %I64d bytes (%d blocks)\n",
|
|||
|
size, size / 2048));
|
|||
|
|
|||
|
if ((LONGLONG)((size / 2048) >> 32) != 0) {
|
|||
|
FPRINTF((OUTPUT, "Error: The file is too large (%I64d)\n",
|
|||
|
size));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
*NumberOfBlocks = (DWORD)(size / 2048);
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyMediaCapacity(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN DWORD RequiredBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
printf("NOT VERIFYING MEDIA CAPACITY!\n");
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
WaitForBurnToComplete(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size;
|
|||
|
DWORD i = 0;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Waiting for write to finish (long)... "));
|
|||
|
|
|||
|
//
|
|||
|
// send flush_cache to synchronize the media and the drive's cache
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|||
|
cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
|
|||
|
size = 0;
|
|||
|
|
|||
|
//
|
|||
|
// wait up to ten minutes (600 seconds) for burn to complete
|
|||
|
//
|
|||
|
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
TRUE,
|
|||
|
600)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d sending SYNCHRONIZE_CACHE\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "success\n"));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|