/*++ 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 #include // 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 = ¤tPosition->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; }