NT4/private/ntos/dd/qic117/ioctl.c
2020-09-30 17:12:29 +02:00

1890 lines
41 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1993 - Colorado Memory Systems, Inc.
All Rights Reserved
Module Name:
ioctl.c
Abstract:
Tape IOCTL support for NT Backup aplication.
Revision History:
--*/
//
// Includes
//
#include <ntddk.h>
#include <ntddtape.h> // tape device driver I/O control codes
#include "common.h"
#include "q117.h"
#include "protos.h"
#include "frb.h"
#define FCT_ID 0x0126
NTSTATUS
q117IoCtlGetPosition (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PTAPE_GET_POSITION currentPosition;
PLARGE_INTEGER offset;
context = DeviceObject->DeviceExtension;
currentPosition = Irp->AssociatedIrp.SystemBuffer;
Irp->IoStatus.Information = sizeof(TAPE_GET_POSITION);
//
// signal no partition support
//
currentPosition->Partition = 0;
//
// Fill in the CurrentOperation.Position based on the mode
//
offset = &currentPosition->Offset;
offset->HighPart = (LONG)0;
switch (context->CurrentOperation.Type) {
case BackupInProgress:
offset->LowPart = context->CurrentOperation.BytesOnTape;
break;
case RestoreInProgress:
offset->LowPart = context->CurrentOperation.BytesRead;
break;
case NoOperation:
offset->LowPart = context->CurrentOperation.BytesRead;
break;
}
offset->LowPart /= BLOCK_SIZE;
CheckedDump(QIC117SHOWAPI,("%d=GetPosition()",currentPosition->Offset.LowPart));
return STATUS_SUCCESS;
}
NTSTATUS
q117IoCtlGetMediaParameters (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
NTSTATUS ntStatus;
IO_REQUEST ioreq;
context = DeviceObject->DeviceExtension;
//
// Make sure there is a tape in the drive and that the tape information
// has been loaded
//
if (context->CurrentOperation.Type == NoOperation) {
//
// Because NTBackup pools using
// this function, we need to error out
// this operation until a prepare load or prepare lock operation
// is performed
//
//
// Just check to see if a new tape has been inserted
//
ntStatus = q117ConvertStatus(
DeviceObject,
q117DoCmd(&ioreq, CMD_REPORT_STATUS, NULL, context));
if (ntStatus == STATUS_MEDIA_CHANGED) {
context->CurrentTape.State = NeedInfoLoaded;
}
} else {
ntStatus = STATUS_SUCCESS;
}
if ( NT_SUCCESS( ntStatus ) ) {
//
// Copy already formed (by q117CheckNewTape) information into callers buffer
//
Irp->IoStatus.Information = sizeof(TAPE_GET_MEDIA_PARAMETERS);
*(PTAPE_GET_MEDIA_PARAMETERS)Irp->AssociatedIrp.SystemBuffer =
*context->CurrentTape.MediaInfo;
}
return ntStatus;
}
NTSTATUS
q117IoCtlSetMediaParameters (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PTAPE_SET_MEDIA_PARAMETERS setMedia;
context = DeviceObject->DeviceExtension;
setMedia = (PTAPE_SET_MEDIA_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
CheckedDump((QIC117SHOWAPI | QIC117WARN),("SetDriveParameters not implemented yet\n"));
CheckedDump(QIC117SHOWAPI,("BlockSize: %x",setMedia->BlockSize));
if (setMedia->BlockSize != BLOCK_SIZE)
return STATUS_INVALID_PARAMETER;
else
return STATUS_SUCCESS;
}
NTSTATUS
q117IoCtlGetDriveParameters (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PTAPE_GET_DRIVE_PARAMETERS driveInfo;
context = DeviceObject->DeviceExtension;
//
// Copy already formed (by q117CheckNewTape) information into callers buffer
//
//
driveInfo = (PTAPE_GET_DRIVE_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
Irp->IoStatus.Information = sizeof(TAPE_GET_DRIVE_PARAMETERS);
driveInfo->ECC = TRUE;
driveInfo->Compression = FALSE;
driveInfo->DataPadding = FALSE;
driveInfo->ReportSetmarks = TRUE;
driveInfo->DefaultBlockSize = BLOCK_SIZE;
driveInfo->MaximumBlockSize = BLOCK_SIZE;
driveInfo->MinimumBlockSize = BLOCK_SIZE;
driveInfo->MaximumPartitionCount = 0;
driveInfo->FeaturesLow =
TAPE_DRIVE_ERASE_SHORT |
TAPE_DRIVE_ERASE_BOP_ONLY |
TAPE_DRIVE_TAPE_CAPACITY |
TAPE_DRIVE_TAPE_REMAINING |
TAPE_DRIVE_FIXED_BLOCK |
TAPE_DRIVE_WRITE_PROTECT |
TAPE_DRIVE_ECC |
// TAPE_DRIVE_COMPRESSION |
TAPE_DRIVE_REPORT_SMKS |
TAPE_DRIVE_GET_ABSOLUTE_BLK |
TAPE_DRIVE_GET_LOGICAL_BLK;
driveInfo->FeaturesHigh =
// TAPE_DRIVE_LOAD_UNLOAD |
TAPE_DRIVE_TENSION |
TAPE_DRIVE_LOCK_UNLOCK |
TAPE_DRIVE_ABSOLUTE_BLK |
TAPE_DRIVE_LOGICAL_BLK |
TAPE_DRIVE_END_OF_DATA |
TAPE_DRIVE_RELATIVE_BLKS |
TAPE_DRIVE_FILEMARKS |
TAPE_DRIVE_SEQUENTIAL_FMKS |
TAPE_DRIVE_SETMARKS |
TAPE_DRIVE_SEQUENTIAL_SMKS |
TAPE_DRIVE_REVERSE_POSITION |
TAPE_DRIVE_WRITE_SETMARKS |
TAPE_DRIVE_WRITE_FILEMARKS;
if (!context->Parameters.FormatDisabled) {
driveInfo->FeaturesHigh |= TAPE_DRIVE_FORMAT;
}
driveInfo->FeaturesHigh &= ~TAPE_DRIVE_HIGH_FEATURES;
return STATUS_SUCCESS;
}
NTSTATUS
q117IoCtlSetDriveParameters (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PTAPE_SET_DRIVE_PARAMETERS driveInfo;
NTSTATUS ntStatus;
context = DeviceObject->DeviceExtension;
//
// Copy already formed (by q117CheckNewTape) information into callers buffer
//
//
driveInfo = (PTAPE_SET_DRIVE_PARAMETERS)Irp->AssociatedIrp.SystemBuffer;
CheckedDump((QIC117SHOWAPI | QIC117WARN),("SetDriveParameters not implemented yet\n"));
CheckedDump(QIC117SHOWAPI,("ECC: %x",driveInfo->ECC));
CheckedDump(QIC117SHOWAPI,("Compression: %x",driveInfo->Compression));
CheckedDump(QIC117SHOWAPI,("DataPadding: %x",driveInfo->DataPadding));
CheckedDump(QIC117SHOWAPI,("ReportSetmarks: %x",driveInfo->ReportSetmarks));
ntStatus = STATUS_SUCCESS;
if (!driveInfo->ECC ||
driveInfo->Compression ||
driveInfo->DataPadding ||
!driveInfo->ReportSetmarks) {
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
return ntStatus;
}
NTSTATUS
q117IoCtlWriteMarks (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Handle user request to write tape mark
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
#ifndef NO_MARKS
PQ117_CONTEXT context;
PTAPE_WRITE_MARKS tapeMarks = Irp->AssociatedIrp.SystemBuffer;
ULONG numMarks;
NTSTATUS ntStatus;
ULONG type;
context = DeviceObject->DeviceExtension;
//
// We don't support immediate mode, so error out if specified
//
if (tapeMarks->Immediate) {
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Make sure we are in write mode
//
ntStatus = q117ConvertStatus(DeviceObject, q117OpenForWrite(context));
numMarks = tapeMarks->Count;
type = tapeMarks->Type;
//
// Don't allow long/short filemarks
//
switch(type) {
case TAPE_LONG_FILEMARKS:
case TAPE_SHORT_FILEMARKS:
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
//
// Put as many marks as the user asked for, in the mark array
//
while (numMarks && NT_SUCCESS( ntStatus )) {
// if there is not enough room to add the mark, make the array bigger
if (context->MarkArray.TotalMarks+numMarks+1 > context->MarkArray.MarksAllocated) {
ntStatus = q117MakeMarkArrayBigger(context, numMarks);
// Must have run out of memory, so abort
if (!NT_SUCCESS( ntStatus)) return ntStatus;
}
// If there are no more marks, set tape full condition
if (context->MarkArray.TotalMarks >= context->MarkArray.MaxMarks) {
ntStatus = q117ConvertStatus(
DeviceObject,
ERROR_ENCODE(ERR_TAPE_FULL,FCT_ID, 1)
);
} else {
context->MarkArray.MarkEntry[
context->MarkArray.TotalMarks].Type = tapeMarks->Type;
context->MarkArray.MarkEntry[
context->MarkArray.TotalMarks].Offset =
context->CurrentOperation.BytesOnTape;
--numMarks;
++context->MarkArray.TotalMarks;
++context->CurrentMark;
//
// Always make the (last mark) huge so we don't have to check
// for the end of the table in the rest of the code.
//
context->MarkArray.MarkEntry[
context->MarkArray.TotalMarks].Offset = 0xffffffff;
//
// For each mark, write a "fake" block on the tape. This
// is due to the ntBackup program assuming that a filemark
// takes a block
//
ntStatus = q117ConvertStatus(
DeviceObject,
q117WriteTape(NULL,BLOCK_SIZE,context)
);
}
}
//
// If no other problems, check for early warning on filemarks.
//
if (NT_SUCCESS( ntStatus)) {
// Give application an early warning when 10 marks are left
if (context->MarkArray.TotalMarks + 10 >= context->MarkArray.MaxMarks) {
ntStatus = q117ConvertStatus(
DeviceObject,
ERROR_ENCODE(ERR_EARLY_WARNING,FCT_ID, 1)
);
}
}
return ntStatus;
#else
return STATUS_INVALID_DEVICE_REQUEST;
#endif
}
NTSTATUS q117MakeMarkArrayBigger(
PQ117_CONTEXT Context,
int MinimumToAdd
)
{
// go in chunks of 4K (what is a better value? 1K? Bob??)
#define STEPPING_AMOUNT (4*1024)/sizeof(struct _MARKLIST);
struct _MARKLIST *newList;
// Allocate at least STEPPING_AMOUNT more to minimize the
// number of times the memory grows
MinimumToAdd += Context->MarkArray.MarksAllocated + MinimumToAdd + STEPPING_AMOUNT;
// Allocate the new array for the mark list
newList = ExAllocatePool(
NonPagedPool,
sizeof(struct _MARKLIST)*MinimumToAdd);
if (newList == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// If we have already allocated a mark array, then copy it over to the
// new entry
if (Context->MarkArray.MarkEntry) {
// copy over all marks (+1, so we don't forget the terminator mark)
RtlMoveMemory(newList, Context->MarkArray.MarkEntry, Context->MarkArray.TotalMarks+1);
// Now, free the old mark array
ExFreePool(Context->MarkArray.MarkEntry);
}
// Now, hook up the new entry, and we are done
Context->MarkArray.MarkEntry = newList;
Context->MarkArray.MarksAllocated = MinimumToAdd;
return STATUS_SUCCESS;
}
#ifdef NOT_NOW
NTSTATUS q117MakeBadSectorListBigger(
PQ117_CONTEXT Context,
int MinimumToAdd
)
{
// go in chunks of 4K (what is a better value? 1K? Bob??)
#define STEPPING_AMOUNT (4*1024)
BAD_MAP_PTR newList;
// Allocate at least STEPPING_AMOUNT more to minimize the
// number of times the memory grows
MinimumToAdd += Context->CurrentTape.BadSectorListSize +
((MinimumToAdd*LIST_ENTRY_SIZE + STEPPING_AMOUNT - 1) / STEPPING_AMOUNT);
// Allocate the new array for the mark list
newList = ExAllocatePool(
NonPagedPool,
MinimumToAdd);
if (newList == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// If we have already allocated a mark array, then copy it over to the
// new entry
if (Context->CurrentTape.BadSectorListPtr) {
// copy over all marks (+1, so we don't forget the terminator mark)
RtlMoveMemory(newList, Context->CurrentTape.BadSectorListPtr, Context->CurrentTape.BadSectorListCount*LIST_ENTRY_SIZE);
// Now, free the old mark array
ExFreePool(Context->CurrentTape.BadSectorListPtr);
}
// Now, hook up the new entry, and we are done
Context->CurrentTape.BadSectorListPtr = newList;
Context->CurrentTape.BadSectorListSize = MinimumToAdd;
return STATUS_SUCCESS;
}
#endif
NTSTATUS
q117IoCtlSetPosition (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PTAPE_SET_POSITION tapePosition = Irp->AssociatedIrp.SystemBuffer;
dStatus status;
NTSTATUS ntStatus;
ULONG offset;
#ifndef NO_MARKS
int x = 0;
#endif
context = DeviceObject->DeviceExtension;
status = ERR_NO_ERR;
ntStatus = STATUS_SUCCESS;
//
// We don't support immediate mode, so error out
//
if (tapePosition->Immediate) {
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
} else {
//
// If we are in the middle of a read or write operation,
// then we need to do some clean-up before processing
// the tape position request.
//
//
if (context->CurrentOperation.Type != NoOperation) {
switch(context->CurrentOperation.Type) {
case BackupInProgress:
status = q117EndWriteOperation(context);
break;
case RestoreInProgress:
if (tapePosition->Method == TAPE_REWIND) {
status = q117EndReadOperation(context);
}
break;
}
}
context->CurrentOperation.Position = tapePosition->Method;
switch(tapePosition->Method) {
case TAPE_REWIND:
CheckedDump(QIC117SHOWAPI,("Rewind()\n"));
status = q117DoRewind(context);
// context->TapeStatus.Status |= TAPE_STATUS_BEGINNING_OF_MEDIA;
// context->TapeStatus.Status &= ~TAPE_STATUS_END_OF_MEDIA;
context->CurrentOperation.BytesRead = 0;
#ifndef NO_MARKS
context->CurrentMark = 0;
#endif
break;
case TAPE_LOGICAL_BLOCK:
case TAPE_ABSOLUTE_BLOCK:
CheckedDump(QIC117SHOWAPI,(
"%s SeekBlock(%d)\n",
tapePosition->Method==TAPE_LOGICAL_BLOCK?"Logical":"Absolute",
tapePosition->Offset.LowPart
));
offset = (tapePosition->Offset.LowPart)*BLOCK_SIZE;
ntStatus = q117SeekToOffset(offset, context, DeviceObject);
break;
case TAPE_SPACE_END_OF_DATA:
//
// This will be taken care of when backup starts
// by using the context->CurrentOperation.Position
//
// It is assumed that this function will only be called prior
// to a backup operation only.
CheckedDump(QIC117SHOWAPI,("SeekEOD()\n"));
context->CurrentOperation.BytesRead =
context->ActiveVolume.DataSize;
#ifndef NO_MARKS
context->CurrentMark = context->MarkArray.TotalMarks;
#endif
// context->TapeStatus.Status |= TAPE_STATUS_END_OF_MEDIA;
// context->TapeStatus.Status &= ~TAPE_STATUS_BEGINNING_OF_MEDIA;
break;
case TAPE_SPACE_RELATIVE_BLOCKS:
CheckedDump(QIC117SHOWAPI,("SeekRelBlock(%d)\n",tapePosition->Offset.LowPart));
//
// Convert relative offset into absolute
//
offset = (LONG)context->CurrentOperation.BytesRead +
((LONG)tapePosition->Offset.LowPart*BLOCK_SIZE);
//
// Perform absolute seek.
//
ntStatus = q117SeekToOffset(offset,context, DeviceObject);
break;
#ifndef NO_MARKS
case TAPE_SPACE_SETMARKS:
++x;
case TAPE_SPACE_FILEMARKS:
++x;
case TAPE_SPACE_SEQUENTIAL_FMKS:
++x;
case TAPE_SPACE_SEQUENTIAL_SMKS:
#if DBG
{
static char *type[4] = {"SequentialSet","SequentialFile","File","Set"};
CheckedDump(QIC117SHOWAPI,("Seek%sMark(%d)\n",type[x],tapePosition->Offset.LowPart));
}
#endif
ntStatus = q117FindMark(tapePosition->Method,
tapePosition->Offset.LowPart, context, DeviceObject);
break;
#else
case TAPE_SPACE_SETMARKS:
case TAPE_SPACE_FILEMARKS:
case TAPE_SPACE_SEQUENTIAL_FMKS:
case TAPE_SPACE_SEQUENTIAL_SMKS:
#endif
default:
CheckedDump(QIC117DBGP,("TAPE: Position: Invalid Position Code (%x)\n",
tapePosition->Method));
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
} // end switch(tapePosition->Method)
}
if (status)
ntStatus = q117ConvertStatus(DeviceObject, status);
return ntStatus;
}
#ifndef NO_MARKS
NTSTATUS
q117FindMark(
ULONG Type,
LONG Number,
PQ117_CONTEXT Context,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Arguments:
Return Value:
NT Status
--*/
{
NTSTATUS ntStatus;
BOOLEAN forwardSeek;
int nummarks,count,left;
struct _MARKLIST *prev,*next;
ntStatus = STATUS_SUCCESS;
//
// Convert the SetPosition commands into WriteMark types
//
switch(Type) {
case TAPE_SPACE_SEQUENTIAL_FMKS:
//
// Set the nummarks flag to the number of filemarks in a row
// To look for. Set the number of filemarks to one (we only
// will allow scanning for one set of "Number" filemarks)
//
nummarks = Number;
Number = 1;
//
// Don't let a reverse seek work (no code to support it and
// I (kurt) don't know if SCSI drives allow this either.
//
if (nummarks < 1) {
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
Type = TAPE_FILEMARKS;
break;
case TAPE_SPACE_FILEMARKS:
Type = TAPE_FILEMARKS;
nummarks = 0;
break;
case TAPE_SPACE_SEQUENTIAL_SMKS:
//
// Set the nummarks flag to the number of filemarks in a row
// To look for. Set the number of filemarks to one (we only
// will allow scanning for one set of "Number" filemarks)
//
nummarks = Number;
Number = 1;
Type = TAPE_SETMARKS;
//
// Don't let a reverse seek work (no code to support it and
// I (kurt) don't know if SCSI drives allow this either.
//
if (nummarks < 1) {
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
}
break;
case TAPE_SPACE_SETMARKS:
Type = TAPE_SETMARKS;
nummarks = 0;
break;
}
if (Number > 0) {
forwardSeek = TRUE;
} else {
forwardSeek = FALSE;
}
//
// Now seek the appropriate amount
//
while (NT_SUCCESS( ntStatus ) && Number != 0) {
if (forwardSeek) {
if (Context->CurrentMark >= Context->MarkArray.TotalMarks) {
ntStatus = STATUS_END_OF_MEDIA;
} else {
if (Context->MarkArray.MarkEntry[Context->CurrentMark].Type ==
Type) {
// If we are looking for sequential marks, then
// count the number of marks a this position and
// Compare it with what we are looking for.
if (nummarks) {
count = 1;
prev = &Context->MarkArray.MarkEntry[Context->CurrentMark];
next = prev+1;
left = Context->MarkArray.TotalMarks - Context->CurrentMark - 1;
while (left && next->Type == Type &&
prev->Offset + BLOCK_SIZE == next->Offset) {
++count;
++prev;
++next;
--left;
}
//
// If we found a match, stop here, else skip
// past all of the marks that we counted (that
// are sequential)
//
if (count == nummarks) {
//
// Note: code below expects the mark pointer
// to be just before the data that we are
// positioning to. So, set the pointer
// To the last mark.
//
Context->CurrentMark += count-1;
Number = 0; // Signal completion
} else {
Context->CurrentMark += count;
}
} else {
//
// If we found one, decrement the count
//
--Number;
//
// Don't increment the current mark on the last one we
// find. This is because current mark points to
// the mark we are going to hit next.
//
if (Number) {
++Context->CurrentMark;
}
}
} else {
++Context->CurrentMark;
}
}
} else {
if (Context->CurrentMark == 0) {
ntStatus = STATUS_END_OF_MEDIA;
} else {
--Context->CurrentMark;
if (Context->MarkArray.MarkEntry[Context->CurrentMark].Type ==
Type) {
++Number;
}
}
}
}
if (NT_SUCCESS( ntStatus )) {
if (Context->CurrentMark >= Context->MarkArray.TotalMarks) {
ntStatus = STATUS_END_OF_MEDIA;
} else {
//
// Seek to proper location. Note: forward seek
// seeks to block after file mark (successive read will return
// block after filemark.
// A backward seek will position at the filemark. The next read
// will return filemark found, and successive reads will read
// data after the mark)
//
ntStatus = q117SeekToOffset(
Context->MarkArray.MarkEntry[Context->CurrentMark].Offset+
(forwardSeek?BLOCK_SIZE:0),
Context,
DeviceObject
);
}
}
return ntStatus;
}
#endif
NTSTATUS
q117SeekToOffset(
ULONG Offset,
PQ117_CONTEXT Context,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Seek to specified offset on the tape (absolute offset from 0) in bytes
Arguments:
Offset - Bytes from begining of the volume to seek.
Return Value:
NT Status
--*/
{
NTSTATUS ntStatus;
CheckedDump(QIC117SHOWAPI,("Absolute seek: %x\n",Offset));
ntStatus = STATUS_SUCCESS;
//
// If not in read mode, switch into read mode
//
if (Context->CurrentOperation.Type == NoOperation) {
ntStatus = q117OpenForRead(0, Context, DeviceObject);
//
// if there is no data on the tape
//
if (ntStatus == STATUS_NO_DATA_DETECTED) {
return ntStatus;
}
Context->CurrentOperation.Type = RestoreInProgress;
}
if (Offset < Context->CurrentOperation.BytesRead) {
//
// Backward seek, so stop current operation,
// rewind to begining of volume, and drop
// through to a forward seek.
//
ntStatus = q117ConvertStatus(
DeviceObject,
q117EndReadOperation(Context)
);
if (NT_SUCCESS(ntStatus)) {
ntStatus = q117OpenForRead(0, Context, DeviceObject);
//
// if there is no data on the tape
//
if (ntStatus == STATUS_NO_DATA_DETECTED) {
return ntStatus;
}
Context->CurrentOperation.Type = RestoreInProgress;
}
}
if (NT_SUCCESS(ntStatus)) {
//
// Forward seek only (if we were doing a backward seek, the operation
// has been re-started this point and BytesRead == 0)
//
Offset -= Context->CurrentOperation.BytesRead;
//
// Skip to the appropriate place
//
ntStatus = q117ConvertStatus(
DeviceObject,
q117SkipBlock(&Offset, Context)
);
}
return ntStatus;
}
NTSTATUS
q117IoCtlErase (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
NTSTATUS ntStatus;
dStatus status;
PTAPE_ERASE tapeErase = Irp->AssociatedIrp.SystemBuffer;
context = DeviceObject->DeviceExtension;
//
// We don't support immediate mode, so error out if specified
//
if (tapeErase->Immediate) {
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Complete any operation in progress
//
switch(context->CurrentOperation.Type) {
case BackupInProgress:
status = q117EndWriteOperation(context);
break;
case RestoreInProgress:
status = q117EndReadOperation(context);
break;
}
//
// Make sure there is a tape in the drive and that the tape information
// has been loaded
//
ntStatus = q117ConvertStatus(
DeviceObject,
q117CheckNewTape(context));
if ( NT_SUCCESS( ntStatus ) ) {
//
// Don't allow an erase if write protected
//
if (context->CurrentTape.MediaInfo->WriteProtected) {
return STATUS_MEDIA_WRITE_PROTECTED;
}
//
// Erase the tape
//
status = q117EraseQ(context);
ntStatus = q117ConvertStatus(DeviceObject, status);
}
return ntStatus;
}
NTSTATUS
q117IoCtlPrepare (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
NTSTATUS ntStatus;
dStatus status;
PTAPE_PREPARE tapePrepare;
IO_REQUEST ioreq;
QIC40_VENDOR_UNIQUE vendorUnique;
LONG numberBad;
context = DeviceObject->DeviceExtension;
status = ERR_NO_ERR;
//
// Complete any operation in progress
//
switch(context->CurrentOperation.Type) {
case BackupInProgress:
status = q117EndWriteOperation(context);
break;
case RestoreInProgress:
status = q117EndReadOperation(context);
break;
}
//
// All prepare except LOCK and UNLOCK operations rewind the media.
//
tapePrepare = Irp->AssociatedIrp.SystemBuffer;
if ((tapePrepare->Operation != TAPE_LOCK) &&
(tapePrepare->Operation != TAPE_UNLOCK)) {
context->CurrentOperation.BytesRead = 0;
context->CurrentOperation.Position = 0;
#ifndef NO_MARKS
context->CurrentMark = 0;
#endif
}
if (status) {
return q117ConvertStatus(DeviceObject, status);
}
ntStatus = STATUS_SUCCESS;
switch (tapePrepare->Operation) {
case TAPE_LOCK:
case TAPE_LOAD:
CheckedDump(QIC117SHOWAPI,("TAPE_%s ... ",tapePrepare->Operation == TAPE_LOCK?"LOCK":"LOAD"));
ntStatus = q117ConvertStatus(DeviceObject, q117CheckNewTape(context));
break;
case TAPE_UNLOAD:
case TAPE_UNLOCK:
//
// Just rewind the tape
//
CheckedDump(QIC117SHOWAPI,("TAPE_UN% ... ",tapePrepare->Operation == TAPE_UNLOCK?"LOCK":"LOAD"));
ntStatus = q117ConvertStatus (
DeviceObject,
q117DoRewind(context) );
break;
case TAPE_TENSION:
CheckedDump(QIC117SHOWAPI,("TAPE_TENSION ... "));
ntStatus = q117ConvertStatus (
DeviceObject,
q117DoCmd(&ioreq, CMD_RETENSION, NULL, context) );
break;
case TAPE_FORMAT:
CheckedDump(QIC117SHOWAPI,("TAPE_FORMAT ... "));
ntStatus = q117ConvertStatus (
DeviceObject,
q117Format(
&numberBad,
TRUE,
&vendorUnique,
context ) );
break;
default:
CheckedDump(QIC117SHOWAPI,("INVALID ... "));
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
} // end switch (tapePrepare->Operation)
return ntStatus;
}
dStatus
q117DoRewind(
PQ117_CONTEXT Context
)
{
dStatus status;
IO_REQUEST ioreq;
status = q117DoCmd(&ioreq, CMD_UNLOAD_TAPE, NULL, Context);
if (status == ERR_NO_ERR)
status = q117DoCmd(&ioreq, CMD_DESELECT_DEVICE, NULL, Context);
if (status == ERR_NO_ERR)
status = q117DoCmd(&ioreq, CMD_SELECT_DEVICE, NULL, Context);
return status;
}
NTSTATUS
q117IoCtlGetStatus (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
// PTAPE_STATUS tapeStatus;
dStatus status;
IO_REQUEST ioreq;
context = DeviceObject->DeviceExtension;
//
// Complete any operation in progress
//
switch(context->CurrentOperation.Type) {
case BackupInProgress:
status = ERR_NO_ERR;
break;
case RestoreInProgress:
status = ERR_NO_ERR;
break;
case NoOperation:
status = q117DoCmd(&ioreq, CMD_REPORT_STATUS, NULL, context);
break;
default:
status = ERROR_ENCODE(ERR_PROGRAM_FAILURE, FCT_ID, 1);
}
//tapeStatus = Irp->UserBuffer;
//
// Is this supported in the tape API ?????
//
// *tapeStatus = context->TapeStatus;
//
// Reset media changed flag.
//
// context->TapeStatus.Status &= ~TAPE_STATUS_MEDIA_CHANGED;
return q117ConvertStatus(DeviceObject, status);
}
NTSTATUS
q117IoCtlReadAbs (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PSEGMENT_BUFFER bufferInfo;
PVOID scrbuf;
PIO_REQUEST ioreq;
PCMS_RW_ABS readWrite;
dStatus status;
ULONG len;
PIO_STACK_LOCATION irpStack;
context = DeviceObject->DeviceExtension;
readWrite = Irp->AssociatedIrp.SystemBuffer;
irpStack = IoGetCurrentIrpStackLocation(Irp);
scrbuf = q117GetFreeBuffer(&bufferInfo,context);
status=q117IssIOReq(
scrbuf,
CMD_READ,
readWrite->Block,
bufferInfo,
context
);
if (!status) {
//
// Wait for data to be written
//
ioreq=q117Dequeue(WaitForItem,context);
status = ioreq->x.adi_hdr.status;
if ((readWrite->flags & RW_ABS_DOECC) && (ERROR_DECODE(status) == ERR_BAD_BLOCK_DETECTED || status == ERR_NO_ERR)) {
if (q117DoCorrect(ioreq->x.adi_hdr.cmd_buffer_ptr,0l,ioreq->x.ioDeviceIO.crc)) {
status = ERROR_ENCODE(ERR_CORRECTION_FAILED, FCT_ID, 1);
} else {
status = ERR_NO_ERR;
}
}
}
readWrite->Status = status;
len = BYTES_PER_SECTOR*readWrite->Count;
if (len > irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
len = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
}
RtlMoveMemory(
readWrite+1,
scrbuf,
len
);
Irp->IoStatus.Information = sizeof(CMS_RW_ABS)+len;
return q117ConvertStatus(DeviceObject, status);
}
NTSTATUS
q117IoCtlDetect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PSEGMENT_BUFFER bufferInfo;
PVOID scrbuf;
IO_REQUEST ioreq;
PCMS_DETECT detect;
dStatus status;
ULONG len;
PIO_STACK_LOCATION irpStack;
DriveCfgData cfg;
context = DeviceObject->DeviceExtension;
detect = Irp->AssociatedIrp.SystemBuffer;
irpStack = IoGetCurrentIrpStackLocation(Irp);
if (sizeof(CMS_DETECT) > irpStack->Parameters.DeviceIoControl.OutputBufferLength)
return STATUS_INVALID_PARAMETER;
//
// Get the drive configuration info
//
memset(detect, 0, sizeof(*detect));
detect->driveConfigStatus = 0;
detect->driveDescriptorStatus = 0;
detect->driveConfig = context->DriveCfg.device_cfg;
detect->driveDescriptor = context->DriveCfg.device_descriptor;
//
// Now get the manufacture information from the drive
//
status = q117DoCmd(&ioreq, CMD_REPORT_DEVICE_INFO, NULL, context);
detect->driveInfoStatus = status;
detect->driveInfo = ioreq.x.ioDeviceInfo.device_info;
//
// Now get the information about the tape
//
status = q117DoCmd(&ioreq, CMD_LOAD_TAPE, NULL, context);
detect->tapeConfigStatus = status;
detect->tapeConfig = ioreq.x.ioLoadTape.tape_cfg;
Irp->IoStatus.Information = sizeof(CMS_DETECT);
return STATUS_SUCCESS;
}
NTSTATUS
q117IoCtlWriteAbs (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
DeviceObject
Return Value:
NT Status
--*/
{
PQ117_CONTEXT context;
PSEGMENT_BUFFER bufferInfo;
PVOID scrbuf;
PIO_REQUEST ioreq;
PCMS_RW_ABS readWrite;
dStatus status;
context = DeviceObject->DeviceExtension;
readWrite = Irp->AssociatedIrp.SystemBuffer;
scrbuf = q117GetFreeBuffer(&bufferInfo,context);
RtlMoveMemory(
scrbuf,
readWrite+1,
BYTES_PER_SECTOR*readWrite->Count
);
status = q117IssIOReq(
scrbuf,
CMD_WRITE,
readWrite->Block,
bufferInfo,
context);
if (!status) {
//
// Wait for data to be written
//
ioreq=q117Dequeue(WaitForItem,context);
status = ioreq->x.adi_hdr.status;
}
readWrite->Status = status;
return q117ConvertStatus(DeviceObject, status);
}
dStatus
q117CheckNewTape (
PQ117_CONTEXT Context
)
/*++
Routine Description:
This routine checks for new tape and reads header if necessary
Arguments:
Context - Current context information
Return Value:
NT Status
--*/
{
dStatus stat;
IO_REQUEST ioreq;
PTAPE_HEADER header;
VOLUME_TABLE_ENTRY tempVolume;
BOOLEAN found;
BOOLEAN notNt;
USHORT volumesRead;
SEGMENT curseg;
CQDTapeCfg *tparms; // tape parameters from the driver
USHORT retry;
retry = 100;
do {
//
// Check to see if there is a tape in the drive
//
stat = q117DoCmd(&ioreq, CMD_REPORT_STATUS, NULL, Context);
} while(ERROR_DECODE(stat) == ERR_DRV_NOT_READY && retry--);
//
// If we found a tape and need to read the header and volume tables,
// do it now.
//
if (ERROR_DECODE(stat) == ERR_NEW_TAPE ||
(stat == ERR_NO_ERR && Context->CurrentTape.State == NeedInfoLoaded) ) {
CheckedDump(QIC117SHOWAPI,("New Cart Detected\n"));
//
// Check to see if there is a tape in the drive
//
stat = q117DoCmd(&ioreq, CMD_LOAD_TAPE, NULL, Context);
//
// Saw new cart, so set need loaded flag
//
Context->CurrentTape.State = NeedInfoLoaded;
//
// Check to see if tape is write protected
//
Context->CurrentTape.MediaInfo->WriteProtected = ioreq.x.ioLoadTape.tape_cfg.write_protected;
if (stat) {
switch(ERROR_DECODE(stat)) {
case ERR_UNSUPPORTED_FORMAT:
//
// Check to see if tape is correct format
//
Context->CurrentTape.MediaInfo->WriteProtected = TRUE;
//stat = ERR_NO_ERR;
break;
}
if (stat)
return stat;
}
// set segments on tape, etc
tparms = &ioreq.x.ioLoadTape.tape_cfg;
Context->CurrentTape.LastSegment = (SEGMENT)tparms->formattable_segments - 1;
Context->CurrentTape.TapeFormatCode = tparms->tape_format_code;
//
// Check to see if drive is formatted
//
if (ioreq.x.ioLoadTape.operation_status.cart_referenced == FALSE) {
return ERROR_ENCODE(ERR_TAPE_NOT_FORMATED, FCT_ID, 1);
}
//
// Now read the tape header (and bad sector map)
//
if (stat = q117LoadTape(&header,Context,&ioreq.x.ioLoadTape.tape_cfg.tape_format_code)) {
switch(ERROR_DECODE(stat)) {
// List of persistent errors (until new tape inserted) */
case ERR_BAD_TAPE:
case ERR_CORRECTION_FAILED:
case ERR_BAD_SIGNATURE:
case ERR_UNKNOWN_FORMAT_CODE:
case ERR_UNUSABLE_TAPE:
Context->CurrentTape.State = BadTapeInDrive;
Context->CurrentTape.BadTapeError = stat;
default:
return stat;
}
}
CheckedDump(QIC117SHOWAPI,("LoadTape successful\n"));
//
// If this capacity not supported by this drive,
// (i.e.. Pegasus cart in a QIC-40 drive)
// return invalid format
//
if (header->FormatCode != ioreq.x.ioLoadTape.tape_cfg.tape_format_code) {
CheckedDump(QIC117DBGP,("IOCTL format code mismatch\n"));
return ERROR_ENCODE(ERR_UNRECOGNIZED_FORMAT, FCT_ID, 1);
}
//
// Now scan volume list and get last volume on tape as well as the
// NT volume (if one exists
//
if (stat = q117SelectTD(Context))
return stat;
volumesRead = 0;
found = FALSE;
do {
/* get a volume directory from the tape (if error then)*/
stat = q117ReadVolumeEntry(&tempVolume,Context);
if (stat && (ERROR_DECODE(stat) != ERR_END_OF_VOLUME))
return stat;
if (!stat) {
volumesRead++;
//
// For now, let the system find ANY volume type and select
// it. This will allow NT Backup software to read the
// volume and detect that it is a non-nt format.
// To do this, the if statement is removed, allowing
// the first volume found, be the one that we select.
//
if (!(tempVolume.VendorSpecific &&
tempVolume.Vendor.cms_QIC40.OpSysType == OP_WINDOWS_NT)) {
Context->CurrentOperation.BytesOnTape =
Context->CurrentOperation.BytesRead = 0;
notNt = TRUE;
#ifndef NO_MARKS
// Zero out the mark array
Context->MarkArray.TotalMarks = 0;
Context->CurrentMark = Context->MarkArray.TotalMarks;
Context->MarkArray.MarkEntry[Context->CurrentMark].Offset =
0xffffffff;
#endif
/* force the data size to be the entire backup */
tempVolume.DataSize = 0;
curseg = tempVolume.StartSegment;
while (curseg <= tempVolume.EndingSegment) {
tempVolume.DataSize +=
q117GoodDataBytes(
curseg,
Context);
++curseg;
}
} else {
notNt = FALSE;
}
found = TRUE;
Context->ActiveVolume = tempVolume;
}
} while (!stat && volumesRead < Context->CurrentTape.MaximumVolumes);
if (stat = q117EndRest(Context))
return(stat);
//
// If we did not find a volume, then signal others that
// the ActiveVolume information is invalid.
//
if (!found)
Context->ActiveVolumeNumber = 0;
//
// Zero out bytes saved (incase user trys to read)
// Also set CurrentOperation.BytesRead to zero (start of tape)
//
Context->CurrentOperation.BytesOnTape =
Context->CurrentOperation.BytesRead = 0;
//
// Flag that we have done everything
//
Context->CurrentTape.State = TapeInfoLoaded;
#ifndef NO_MARKS
//
// Read the mark list from the active volume
//
if (found && !notNt) {
stat = q117GetMarks(Context);
}
#endif
//
// If no more pressing error, return ERR_NEW_TAPE
// so application can be aware of the new insertion.
//
if (stat == ERR_NO_ERR) {
stat = ERROR_ENCODE(ERR_NEW_TAPE, FCT_ID, 1);
}
} else {
if (stat == ERR_NO_TAPE) {
// Context->TapeStatus.Status |= TAPE_STATUS_NO_MEDIA;
Context->ActiveVolumeNumber = 0;
Context->CurrentOperation.BytesOnTape =
Context->CurrentOperation.BytesRead = 0;
#ifndef NO_MARKS
Context->MarkArray.TotalMarks = 0;
Context->CurrentMark = Context->MarkArray.TotalMarks;
Context->MarkArray.MarkEntry[Context->CurrentMark].Offset =
0xffffffff;
#endif
}
}
if (!stat && Context->CurrentTape.State == BadTapeInDrive) {
stat = Context->CurrentTape.BadTapeError;
}
return stat;
}