|
|
1.1 ! root 1: /*++ ! 2: ! 3: Copyright (c) 1993 - Colorado Memory Systems, Inc. ! 4: All Rights Reserved ! 5: ! 6: Module Name: ! 7: ! 8: ioctl.c ! 9: ! 10: Abstract: ! 11: ! 12: Tape IOCTL support for NT Backup aplication. ! 13: ! 14: Revision History: ! 15: ! 16: ! 17: ! 18: ! 19: --*/ ! 20: ! 21: // ! 22: // Includes ! 23: // ! 24: ! 25: #include <ntddk.h> ! 26: #include <ntddtape.h> // tape device driver I/O control codes ! 27: #include "common.h" ! 28: #include "q117.h" ! 29: #include "protos.h" ! 30: #include "cms.h" ! 31: ! 32: ! 33: NTSTATUS ! 34: q117IoCtlGetPosition ( ! 35: IN PDEVICE_OBJECT DeviceObject, ! 36: IN PIRP Irp ! 37: ) ! 38: /*++ ! 39: ! 40: Routine Description: ! 41: ! 42: ! 43: ! 44: Arguments: ! 45: ! 46: DeviceObject ! 47: ! 48: ! 49: Return Value: ! 50: ! 51: NT Status ! 52: ! 53: --*/ ! 54: ! 55: { ! 56: PQ117_CONTEXT context; ! 57: PTAPE_GET_POSITION currentPosition; ! 58: PLARGE_INTEGER offset; ! 59: ! 60: context = DeviceObject->DeviceExtension; ! 61: currentPosition = Irp->AssociatedIrp.SystemBuffer; ! 62: ! 63: Irp->IoStatus.Information = sizeof(TAPE_GET_POSITION); ! 64: ! 65: // ! 66: // signal no partition support ! 67: // ! 68: currentPosition->Partition = 0; ! 69: ! 70: // ! 71: // Fill in the CurrentOperation.Position based on the mode ! 72: // ! 73: offset = ¤tPosition->Offset; ! 74: ! 75: offset->HighPart = (LONG)0; ! 76: ! 77: ! 78: switch (context->CurrentOperation.Type) { ! 79: ! 80: case BackupInProgress: ! 81: offset->LowPart = context->CurrentOperation.BytesOnTape; ! 82: break; ! 83: ! 84: case RestoreInProgress: ! 85: offset->LowPart = context->CurrentOperation.BytesRead; ! 86: break; ! 87: ! 88: case NoOperation: ! 89: offset->LowPart = context->CurrentOperation.BytesRead; ! 90: break; ! 91: ! 92: } ! 93: offset->LowPart /= BLOCK_SIZE; ! 94: ! 95: CheckedDump(QIC117SHOWTD,("%d=GetPosition()",currentPosition->Offset.LowPart)); ! 96: ! 97: return STATUS_SUCCESS; ! 98: } ! 99: ! 100: NTSTATUS ! 101: q117IoCtlGetMediaParameters ( ! 102: IN PDEVICE_OBJECT DeviceObject, ! 103: IN PIRP Irp ! 104: ) ! 105: /*++ ! 106: ! 107: Routine Description: ! 108: ! 109: ! 110: ! 111: Arguments: ! 112: ! 113: DeviceObject ! 114: ! 115: ! 116: Return Value: ! 117: ! 118: NT Status ! 119: ! 120: --*/ ! 121: ! 122: { ! 123: PQ117_CONTEXT context; ! 124: NTSTATUS ntStatus; ! 125: ! 126: context = DeviceObject->DeviceExtension; ! 127: ! 128: // ! 129: // Make sure there is a tape in the drive and that the tape information ! 130: // has been loaded ! 131: // ! 132: if (context->CurrentOperation.Type == NoOperation) { ! 133: ! 134: ntStatus = q117ConvertStatus(DeviceObject, q117CheckNewTape(context)); ! 135: ! 136: } else { ! 137: ! 138: ntStatus = STATUS_SUCCESS; ! 139: ! 140: } ! 141: ! 142: ! 143: if ( NT_SUCCESS( ntStatus ) ) { ! 144: // ! 145: // Copy already formed (by q117CheckNewTape) information into callers buffer ! 146: // ! 147: Irp->IoStatus.Information = sizeof(TAPE_GET_MEDIA_PARAMETERS); ! 148: ! 149: *(PTAPE_GET_MEDIA_PARAMETERS)Irp->AssociatedIrp.SystemBuffer = ! 150: *context->CurrentTape.MediaInfo; ! 151: ! 152: } ! 153: ! 154: return ntStatus; ! 155: } ! 156: ! 157: NTSTATUS ! 158: q117IoCtlSetMediaParameters ( ! 159: IN PDEVICE_OBJECT DeviceObject, ! 160: IN PIRP Irp ! 161: ) ! 162: /*++ ! 163: ! 164: Routine Description: ! 165: ! 166: ! 167: ! 168: Arguments: ! 169: ! 170: DeviceObject ! 171: ! 172: ! 173: Return Value: ! 174: ! 175: NT Status ! 176: ! 177: --*/ ! 178: ! 179: { ! 180: PQ117_CONTEXT context; ! 181: PTAPE_SET_MEDIA_PARAMETERS setMedia; ! 182: ! 183: context = DeviceObject->DeviceExtension; ! 184: ! 185: setMedia = (PTAPE_SET_MEDIA_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; ! 186: ! 187: CheckedDump((QIC117SHOWTD | QIC117WARN),("SetDriveParameters not implemented yet\n")); ! 188: CheckedDump(QIC117SHOWTD,("BlockSize: %x",setMedia->BlockSize)); ! 189: ! 190: if (setMedia->BlockSize != BLOCK_SIZE) ! 191: return STATUS_INVALID_PARAMETER; ! 192: else ! 193: return STATUS_SUCCESS; ! 194: } ! 195: ! 196: NTSTATUS ! 197: q117IoCtlGetDriveParameters ( ! 198: IN PDEVICE_OBJECT DeviceObject, ! 199: IN PIRP Irp ! 200: ) ! 201: /*++ ! 202: ! 203: Routine Description: ! 204: ! 205: ! 206: ! 207: Arguments: ! 208: ! 209: DeviceObject ! 210: ! 211: ! 212: Return Value: ! 213: ! 214: NT Status ! 215: ! 216: --*/ ! 217: ! 218: { ! 219: PQ117_CONTEXT context; ! 220: PTAPE_GET_DRIVE_PARAMETERS driveInfo; ! 221: ! 222: context = DeviceObject->DeviceExtension; ! 223: ! 224: // ! 225: // Copy already formed (by q117CheckNewTape) information into callers buffer ! 226: // ! 227: // ! 228: driveInfo = (PTAPE_GET_DRIVE_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; ! 229: Irp->IoStatus.Information = sizeof(TAPE_GET_DRIVE_PARAMETERS); ! 230: ! 231: driveInfo->ECC = TRUE; ! 232: driveInfo->Compression = FALSE; ! 233: driveInfo->DataPadding = FALSE; ! 234: driveInfo->ReportSetmarks = TRUE; ! 235: driveInfo->DefaultBlockSize = BLOCK_SIZE; ! 236: driveInfo->MaximumBlockSize = BLOCK_SIZE; ! 237: driveInfo->MinimumBlockSize = BLOCK_SIZE; ! 238: driveInfo->MaximumPartitionCount = 0; ! 239: driveInfo->FeaturesLow = ! 240: TAPE_DRIVE_ERASE_SHORT | ! 241: TAPE_DRIVE_ERASE_BOP_ONLY | ! 242: TAPE_DRIVE_TAPE_CAPACITY | ! 243: TAPE_DRIVE_TAPE_REMAINING | ! 244: TAPE_DRIVE_FIXED_BLOCK | ! 245: TAPE_DRIVE_WRITE_PROTECT | ! 246: TAPE_DRIVE_ECC | ! 247: TAPE_DRIVE_COMPRESSION | ! 248: TAPE_DRIVE_REPORT_SMKS | ! 249: TAPE_DRIVE_GET_ABSOLUTE_BLK | ! 250: TAPE_DRIVE_GET_LOGICAL_BLK; ! 251: driveInfo->FeaturesHigh = ! 252: TAPE_DRIVE_LOAD_UNLOAD | ! 253: TAPE_DRIVE_TENSION | ! 254: TAPE_DRIVE_LOCK_UNLOCK | ! 255: TAPE_DRIVE_ABSOLUTE_BLK | ! 256: TAPE_DRIVE_LOGICAL_BLK | ! 257: TAPE_DRIVE_END_OF_DATA | ! 258: TAPE_DRIVE_RELATIVE_BLKS | ! 259: TAPE_DRIVE_FILEMARKS | ! 260: TAPE_DRIVE_SEQUENTIAL_FMKS | ! 261: TAPE_DRIVE_SETMARKS | ! 262: TAPE_DRIVE_SEQUENTIAL_SMKS | ! 263: TAPE_DRIVE_REVERSE_POSITION | ! 264: TAPE_DRIVE_WRITE_SETMARKS | ! 265: TAPE_DRIVE_WRITE_FILEMARKS | ! 266: TAPE_DRIVE_FORMAT; ! 267: ! 268: driveInfo->FeaturesHigh &= ~TAPE_DRIVE_HIGH_FEATURES; ! 269: ! 270: return STATUS_SUCCESS; ! 271: } ! 272: ! 273: NTSTATUS ! 274: q117IoCtlSetDriveParameters ( ! 275: IN PDEVICE_OBJECT DeviceObject, ! 276: IN PIRP Irp ! 277: ) ! 278: /*++ ! 279: ! 280: Routine Description: ! 281: ! 282: ! 283: ! 284: Arguments: ! 285: ! 286: DeviceObject ! 287: ! 288: ! 289: Return Value: ! 290: ! 291: NT Status ! 292: ! 293: --*/ ! 294: ! 295: { ! 296: PQ117_CONTEXT context; ! 297: PTAPE_SET_DRIVE_PARAMETERS driveInfo; ! 298: NTSTATUS ntStatus; ! 299: ! 300: context = DeviceObject->DeviceExtension; ! 301: ! 302: // ! 303: // Copy already formed (by q117CheckNewTape) information into callers buffer ! 304: // ! 305: // ! 306: driveInfo = (PTAPE_SET_DRIVE_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; ! 307: ! 308: CheckedDump((QIC117SHOWTD | QIC117WARN),("SetDriveParameters not implemented yet\n")); ! 309: CheckedDump(QIC117SHOWTD,("ECC: %x",driveInfo->ECC)); ! 310: CheckedDump(QIC117SHOWTD,("Compression: %x",driveInfo->Compression)); ! 311: CheckedDump(QIC117SHOWTD,("DataPadding: %x",driveInfo->DataPadding)); ! 312: CheckedDump(QIC117SHOWTD,("ReportSetmarks: %x",driveInfo->ReportSetmarks)); ! 313: ntStatus = STATUS_SUCCESS; ! 314: if (!driveInfo->ECC || ! 315: driveInfo->Compression || ! 316: driveInfo->DataPadding || ! 317: !driveInfo->ReportSetmarks) { ! 318: ! 319: ntStatus = STATUS_INVALID_DEVICE_REQUEST; ! 320: } ! 321: ! 322: return ntStatus; ! 323: } ! 324: ! 325: NTSTATUS ! 326: q117IoCtlWriteMarks ( ! 327: IN PDEVICE_OBJECT DeviceObject, ! 328: IN PIRP Irp ! 329: ) ! 330: /*++ ! 331: ! 332: Routine Description: ! 333: ! 334: Handle user request to write tape mark ! 335: ! 336: Arguments: ! 337: ! 338: DeviceObject ! 339: ! 340: ! 341: Return Value: ! 342: ! 343: NT Status ! 344: ! 345: --*/ ! 346: ! 347: { ! 348: #ifndef NO_MARKS ! 349: ! 350: PQ117_CONTEXT context; ! 351: PTAPE_WRITE_MARKS tapeMarks = Irp->AssociatedIrp.SystemBuffer; ! 352: ULONG numMarks; ! 353: NTSTATUS ntStatus; ! 354: ULONG type; ! 355: ! 356: context = DeviceObject->DeviceExtension; ! 357: ! 358: // ! 359: // Make sure we are in write mode ! 360: // ! 361: ntStatus = q117ConvertStatus(DeviceObject, q117OpenForWrite(context)); ! 362: ! 363: numMarks = tapeMarks->Count; ! 364: type = tapeMarks->Type; ! 365: ! 366: // ! 367: // Don't allow long/short filemarks ! 368: // ! 369: switch(type) { ! 370: case TAPE_LONG_FILEMARKS: ! 371: case TAPE_SHORT_FILEMARKS: ! 372: ntStatus = STATUS_INVALID_DEVICE_REQUEST; ! 373: } ! 374: ! 375: // ! 376: // Put as many marks as the user asked for, in the mark array ! 377: // ! 378: while (numMarks && NT_SUCCESS( ntStatus )) { ! 379: ! 380: context->MarkArray.MarkEntry[ ! 381: context->MarkArray.TotalMarks].Type = tapeMarks->Type; ! 382: ! 383: context->MarkArray.MarkEntry[ ! 384: context->MarkArray.TotalMarks].Offset = ! 385: context->CurrentOperation.BytesOnTape; ! 386: ! 387: --numMarks; ! 388: ! 389: ++context->MarkArray.TotalMarks; ! 390: ++context->CurrentMark; ! 391: ! 392: // ! 393: // Always make the (last mark) huge so we don't have to check ! 394: // for the end of the table in the rest of the code. ! 395: // ! 396: context->MarkArray.MarkEntry[ ! 397: context->MarkArray.TotalMarks].Offset = 0xffffffff; ! 398: ! 399: // ! 400: // For each mark, write a "fake" block on the tape. This ! 401: // is due to the ntBackup program assuming that a filemark ! 402: // takes a block ! 403: // ! 404: ! 405: ntStatus = q117ConvertStatus( ! 406: DeviceObject, ! 407: q117WriteTape(NULL,BLOCK_SIZE,context) ! 408: ); ! 409: } ! 410: ! 411: return ntStatus; ! 412: ! 413: #else ! 414: ! 415: return STATUS_INVALID_DEVICE_REQUEST; ! 416: ! 417: #endif ! 418: } ! 419: ! 420: NTSTATUS ! 421: q117IoCtlSetPosition ( ! 422: IN PDEVICE_OBJECT DeviceObject, ! 423: IN PIRP Irp ! 424: ) ! 425: /*++ ! 426: ! 427: Routine Description: ! 428: ! 429: ! 430: ! 431: Arguments: ! 432: ! 433: DeviceObject ! 434: ! 435: ! 436: Return Value: ! 437: ! 438: NT Status ! 439: ! 440: --*/ ! 441: ! 442: { ! 443: PQ117_CONTEXT context; ! 444: PTAPE_SET_POSITION tapePosition = Irp->AssociatedIrp.SystemBuffer; ! 445: STATUS status; ! 446: NTSTATUS ntStatus; ! 447: IO_REQUEST ioreq; ! 448: ULONG offset; ! 449: #ifndef NO_MARKS ! 450: int x = 0; ! 451: #endif ! 452: ! 453: context = DeviceObject->DeviceExtension; ! 454: ! 455: status = NoErr; ! 456: ntStatus = STATUS_SUCCESS; ! 457: ! 458: if (context->CurrentOperation.Type != NoOperation) { ! 459: switch(context->CurrentOperation.Type) { ! 460: ! 461: case BackupInProgress: ! 462: status = q117EndWriteOperation(context); ! 463: break; ! 464: ! 465: case RestoreInProgress: ! 466: if ( ! 467: tapePosition->Method == TAPE_REWIND ! 468: ) { ! 469: status = q117EndReadOperation(context); ! 470: } ! 471: ! 472: break; ! 473: } ! 474: } ! 475: ! 476: context->CurrentOperation.Position = tapePosition->Method; ! 477: switch(tapePosition->Method) { ! 478: ! 479: case TAPE_REWIND: ! 480: ! 481: CheckedDump(QIC117INFO,("Rewind()\n")); ! 482: status = q117DoCmd(&ioreq, DEject, NULL, context); ! 483: ! 484: // context->TapeStatus.Status |= TAPE_STATUS_BEGINNING_OF_MEDIA; ! 485: // context->TapeStatus.Status &= ~TAPE_STATUS_END_OF_MEDIA; ! 486: ! 487: context->CurrentOperation.BytesRead = 0; ! 488: ! 489: #ifndef NO_MARKS ! 490: context->CurrentMark = 0; ! 491: #endif ! 492: break; ! 493: ! 494: case TAPE_LOGICAL_BLOCK: ! 495: case TAPE_ABSOLUTE_BLOCK: ! 496: ! 497: CheckedDump(QIC117SHOWTD,( ! 498: "%s SeekBlock(%d)\n", ! 499: tapePosition->Method==TAPE_LOGICAL_BLOCK?"Logical":"Absolute", ! 500: tapePosition->Offset.LowPart ! 501: )); ! 502: ! 503: offset = (tapePosition->Offset.LowPart)*BLOCK_SIZE; ! 504: ! 505: ntStatus = q117SeekToOffset(offset, context, DeviceObject); ! 506: ! 507: break; ! 508: ! 509: case TAPE_SPACE_END_OF_DATA: ! 510: // ! 511: // This will be taken care of when backup starts ! 512: // by using the context->CurrentOperation.Position ! 513: // ! 514: // It is assumed that this function will only be called prior ! 515: // to a backup operation only. ! 516: ! 517: CheckedDump(QIC117SHOWTD,("SeekEOD()\n")); ! 518: context->CurrentOperation.BytesRead = ! 519: context->ActiveVolume.DataSize; ! 520: ! 521: #ifndef NO_MARKS ! 522: context->CurrentMark = context->MarkArray.TotalMarks; ! 523: #endif ! 524: ! 525: // context->TapeStatus.Status |= TAPE_STATUS_END_OF_MEDIA; ! 526: // context->TapeStatus.Status &= ~TAPE_STATUS_BEGINNING_OF_MEDIA; ! 527: break; ! 528: ! 529: ! 530: case TAPE_SPACE_RELATIVE_BLOCKS: ! 531: ! 532: ! 533: CheckedDump(QIC117SHOWTD,("SeekRelBlock(%d)\n",tapePosition->Offset.LowPart)); ! 534: // ! 535: // Convert relative offset into absolute ! 536: // ! 537: offset = (LONG)context->CurrentOperation.BytesRead + ! 538: ((LONG)tapePosition->Offset.LowPart*BLOCK_SIZE); ! 539: ! 540: // ! 541: // Perform absolute seek. ! 542: // ! 543: ntStatus = q117SeekToOffset(offset,context, DeviceObject); ! 544: ! 545: break; ! 546: ! 547: #ifndef NO_MARKS ! 548: case TAPE_SPACE_SETMARKS: ! 549: ++x; ! 550: case TAPE_SPACE_FILEMARKS: ! 551: ++x; ! 552: case TAPE_SPACE_SEQUENTIAL_FMKS: ! 553: ++x; ! 554: case TAPE_SPACE_SEQUENTIAL_SMKS: ! 555: if (1) { ! 556: static char *type[4] = {"SequentialSet","SequentialFile","File","Set"}; ! 557: ! 558: CheckedDump(QIC117SHOWTD,("Seek%sMark(%d)\n",type[x],tapePosition->Offset.LowPart)); ! 559: } ! 560: ntStatus = q117FindMark(tapePosition->Method, ! 561: tapePosition->Offset.LowPart, context, DeviceObject); ! 562: break; ! 563: #else ! 564: case TAPE_SPACE_SETMARKS: ! 565: case TAPE_SPACE_FILEMARKS: ! 566: case TAPE_SPACE_SEQUENTIAL_FMKS: ! 567: case TAPE_SPACE_SEQUENTIAL_SMKS: ! 568: #endif ! 569: default: ! 570: CheckedDump(QIC117DBGP,("TAPE: Position: Invalid Position Code (%x)\n", ! 571: tapePosition->Method)); ! 572: ! 573: ntStatus = STATUS_INVALID_DEVICE_REQUEST; ! 574: break; ! 575: ! 576: } // end switch(tapePosition->Method) ! 577: ! 578: if (status) ! 579: ntStatus = q117ConvertStatus(DeviceObject, status); ! 580: ! 581: return ntStatus; ! 582: ! 583: } ! 584: ! 585: #ifndef NO_MARKS ! 586: NTSTATUS ! 587: q117FindMark( ! 588: ULONG Type, ! 589: LONG Number, ! 590: PQ117_CONTEXT Context, ! 591: IN PDEVICE_OBJECT DeviceObject ! 592: ) ! 593: ! 594: /*++ ! 595: ! 596: Routine Description: ! 597: ! 598: ! 599: ! 600: Arguments: ! 601: ! 602: ! 603: Return Value: ! 604: ! 605: NT Status ! 606: ! 607: --*/ ! 608: ! 609: { ! 610: NTSTATUS ntStatus; ! 611: BOOLEAN forwardSeek; ! 612: ! 613: ntStatus = STATUS_SUCCESS; ! 614: ! 615: // ! 616: // Convert the SetPosition commands into WriteMark types ! 617: // ! 618: switch(Type) { ! 619: ! 620: case TAPE_SPACE_SEQUENTIAL_FMKS: ! 621: ! 622: // ! 623: // If sequential mark, then always start from the first mark. ! 624: // ! 625: Context->CurrentMark = 0; ! 626: ! 627: case TAPE_SPACE_FILEMARKS: ! 628: ! 629: Type = TAPE_FILEMARKS; ! 630: break; ! 631: ! 632: case TAPE_SPACE_SEQUENTIAL_SMKS: ! 633: ! 634: // ! 635: // If sequential mark, then always start from the first mark. ! 636: // ! 637: Context->CurrentMark = 0; ! 638: ! 639: case TAPE_SPACE_SETMARKS: ! 640: ! 641: Type = TAPE_SETMARKS; ! 642: break; ! 643: ! 644: } ! 645: ! 646: if (Number > 0) { ! 647: forwardSeek = TRUE; ! 648: } else { ! 649: forwardSeek = FALSE; ! 650: } ! 651: ! 652: ! 653: // ! 654: // Now seek the appropriate amount ! 655: // ! 656: while (NT_SUCCESS( ntStatus ) && Number != 0) { ! 657: ! 658: if (forwardSeek) { ! 659: ! 660: if (Context->CurrentMark >= Context->MarkArray.TotalMarks) { ! 661: ! 662: ntStatus = STATUS_END_OF_MEDIA; ! 663: ! 664: } else { ! 665: ! 666: if (Context->MarkArray.MarkEntry[Context->CurrentMark].Type == ! 667: Type) { ! 668: ! 669: // ! 670: // If we found one, decrement the count ! 671: // ! 672: ! 673: --Number; ! 674: ! 675: ! 676: // ! 677: // Don't increment the current mark on the last one we ! 678: // find. This is because current mark points to ! 679: // the mark we are going to hit next. ! 680: // ! 681: ! 682: if (Number) { ! 683: ! 684: ++Context->CurrentMark; ! 685: } ! 686: ! 687: } else { ! 688: ! 689: ++Context->CurrentMark; ! 690: ! 691: } ! 692: } ! 693: ! 694: } else { ! 695: ! 696: if (Context->CurrentMark == 0) { ! 697: ! 698: ntStatus = STATUS_END_OF_MEDIA; ! 699: ! 700: } else { ! 701: ! 702: --Context->CurrentMark; ! 703: ! 704: if (Context->MarkArray.MarkEntry[Context->CurrentMark].Type == ! 705: Type) { ! 706: ! 707: ++Number; ! 708: ! 709: } ! 710: ! 711: } ! 712: ! 713: } ! 714: ! 715: } ! 716: ! 717: if (NT_SUCCESS( ntStatus )) { ! 718: ! 719: if (Context->CurrentMark >= Context->MarkArray.TotalMarks) { ! 720: ! 721: ntStatus = STATUS_END_OF_MEDIA; ! 722: ! 723: } else { ! 724: ! 725: // ! 726: // Seek to proper location. Note: forward seek ! 727: // seeks to block after file mark (successive read will return ! 728: // block after filemark. ! 729: // A backward seek will position at the filemark. The next read ! 730: // will return filemark found, and successive reads will read ! 731: // data after the mark) ! 732: // ! 733: ntStatus = q117SeekToOffset( ! 734: Context->MarkArray.MarkEntry[Context->CurrentMark].Offset+ ! 735: (forwardSeek?BLOCK_SIZE:0), ! 736: Context, ! 737: DeviceObject ! 738: ); ! 739: ! 740: } ! 741: ! 742: } ! 743: ! 744: return ntStatus; ! 745: } ! 746: #endif ! 747: ! 748: NTSTATUS ! 749: q117SeekToOffset( ! 750: ULONG Offset, ! 751: PQ117_CONTEXT Context, ! 752: IN PDEVICE_OBJECT DeviceObject ! 753: ) ! 754: ! 755: /*++ ! 756: ! 757: Routine Description: ! 758: ! 759: Seek to specified offset on the tape (absolute offset from 0) in bytes ! 760: ! 761: Arguments: ! 762: ! 763: Offset - Bytes from begining of the volume to seek. ! 764: ! 765: Return Value: ! 766: ! 767: NT Status ! 768: ! 769: --*/ ! 770: ! 771: { ! 772: NTSTATUS ntStatus; ! 773: ! 774: ! 775: CheckedDump(QIC117SHOWTD,("Absolute seek: %x\n",Offset)); ! 776: ! 777: ntStatus = STATUS_SUCCESS; ! 778: ! 779: // ! 780: // If not in read mode, switch into read mode ! 781: // ! 782: if (Context->CurrentOperation.Type == NoOperation) { ! 783: ! 784: ntStatus = q117OpenForRead(0, Context, DeviceObject); ! 785: ! 786: // ! 787: // if there is no data on the tape ! 788: // ! 789: if (ntStatus == STATUS_NO_DATA_DETECTED) { ! 790: return ntStatus; ! 791: } ! 792: ! 793: Context->CurrentOperation.Type = RestoreInProgress; ! 794: } ! 795: ! 796: ! 797: if (Offset < Context->CurrentOperation.BytesRead) { ! 798: ! 799: // ! 800: // Backward seek, so stop current operation, ! 801: // rewind to begining of volume, and drop ! 802: // through to a forward seek. ! 803: // ! 804: ! 805: ntStatus = q117ConvertStatus( ! 806: DeviceObject, ! 807: q117EndReadOperation(Context) ! 808: ); ! 809: ! 810: if (NT_SUCCESS(ntStatus)) { ! 811: ! 812: ntStatus = q117OpenForRead(0, Context, DeviceObject); ! 813: ! 814: // ! 815: // if there is no data on the tape ! 816: // ! 817: if (ntStatus == STATUS_NO_DATA_DETECTED) { ! 818: return ntStatus; ! 819: } ! 820: ! 821: Context->CurrentOperation.Type = RestoreInProgress; ! 822: } ! 823: } ! 824: ! 825: ! 826: if (NT_SUCCESS(ntStatus)) { ! 827: // ! 828: // Forward seek only (if we were doing a backward seek, the operation ! 829: // has been re-started this point and BytesRead == 0) ! 830: // ! 831: Offset -= Context->CurrentOperation.BytesRead; ! 832: ! 833: // ! 834: // Skip to the appropriate place ! 835: // ! 836: ntStatus = q117ConvertStatus( ! 837: DeviceObject, ! 838: q117SkipBlock(&Offset, Context) ! 839: ); ! 840: ! 841: } ! 842: ! 843: return ntStatus; ! 844: } ! 845: ! 846: NTSTATUS ! 847: q117IoCtlErase ( ! 848: IN PDEVICE_OBJECT DeviceObject, ! 849: IN PIRP Irp ! 850: ) ! 851: /*++ ! 852: ! 853: Routine Description: ! 854: ! 855: ! 856: ! 857: Arguments: ! 858: ! 859: DeviceObject ! 860: ! 861: ! 862: Return Value: ! 863: ! 864: NT Status ! 865: ! 866: --*/ ! 867: ! 868: { ! 869: PQ117_CONTEXT context; ! 870: NTSTATUS ntStatus; ! 871: STATUS status; ! 872: ! 873: context = DeviceObject->DeviceExtension; ! 874: ! 875: // ! 876: // Complete any operation in progress ! 877: // ! 878: switch(context->CurrentOperation.Type) { ! 879: ! 880: case BackupInProgress: ! 881: status = q117EndWriteOperation(context); ! 882: break; ! 883: ! 884: case RestoreInProgress: ! 885: status = q117EndReadOperation(context); ! 886: break; ! 887: } ! 888: ! 889: // ! 890: // Make sure there is a tape in the drive and that the tape information ! 891: // has been loaded ! 892: // ! 893: ntStatus = q117ConvertStatus( ! 894: DeviceObject, ! 895: q117CheckNewTape(context)); ! 896: ! 897: if ( NT_SUCCESS( ntStatus ) ) { ! 898: // ! 899: // Don't allow an erase if write protected ! 900: // ! 901: if (context->CurrentTape.MediaInfo->WriteProtected) { ! 902: return STATUS_MEDIA_WRITE_PROTECTED; ! 903: } ! 904: ! 905: // ! 906: // Erase the tape ! 907: // ! 908: status = q117EraseQ(context); ! 909: ! 910: ntStatus = q117ConvertStatus(DeviceObject, status); ! 911: } ! 912: ! 913: return ntStatus; ! 914: } ! 915: ! 916: NTSTATUS ! 917: q117IoCtlPrepare ( ! 918: IN PDEVICE_OBJECT DeviceObject, ! 919: IN PIRP Irp ! 920: ) ! 921: /*++ ! 922: ! 923: Routine Description: ! 924: ! 925: ! 926: ! 927: Arguments: ! 928: ! 929: DeviceObject ! 930: ! 931: ! 932: Return Value: ! 933: ! 934: NT Status ! 935: ! 936: --*/ ! 937: ! 938: { ! 939: PQ117_CONTEXT context; ! 940: NTSTATUS ntStatus; ! 941: STATUS status; ! 942: PTAPE_PREPARE tapePrepare; ! 943: IO_REQUEST ioreq; ! 944: QIC40_VENDOR_UNIQUE vendorUnique; ! 945: LONG numberBad; ! 946: ! 947: context = DeviceObject->DeviceExtension; ! 948: ! 949: status = NoErr; ! 950: ! 951: // ! 952: // Complete any operation in progress ! 953: // ! 954: switch(context->CurrentOperation.Type) { ! 955: ! 956: case BackupInProgress: ! 957: status = q117EndWriteOperation(context); ! 958: break; ! 959: ! 960: case RestoreInProgress: ! 961: status = q117EndReadOperation(context); ! 962: break; ! 963: } ! 964: ! 965: // ! 966: // All prepare except LOCK and UNLOCK operations rewind the media. ! 967: // ! 968: tapePrepare = Irp->AssociatedIrp.SystemBuffer; ! 969: ! 970: if ((tapePrepare->Operation != TAPE_LOCK) && ! 971: (tapePrepare->Operation != TAPE_UNLOCK)) { ! 972: context->CurrentOperation.BytesRead = 0; ! 973: context->CurrentOperation.Position = 0; ! 974: #ifndef NO_MARKS ! 975: context->CurrentMark = 0; ! 976: #endif ! 977: } ! 978: ! 979: ! 980: if (status) { ! 981: return q117ConvertStatus(DeviceObject, status); ! 982: } ! 983: ! 984: ntStatus = STATUS_SUCCESS; ! 985: ! 986: switch (tapePrepare->Operation) { ! 987: ! 988: case TAPE_LOAD: ! 989: CheckedDump(QIC117SHOWTD,("TAPE_LOAD ... ")); ! 990: ntStatus = q117ConvertStatus(DeviceObject, q117CheckNewTape(context)); ! 991: break; ! 992: ! 993: case TAPE_UNLOAD: ! 994: ! 995: // ! 996: // Just rewind the tape ! 997: // ! 998: CheckedDump(QIC117SHOWTD,("TAPE_UNLOAD ... ")); ! 999: ntStatus = q117ConvertStatus ( ! 1000: DeviceObject, ! 1001: q117DoCmd(&ioreq, DEject, NULL, context) ); ! 1002: ! 1003: break; ! 1004: ! 1005: case TAPE_TENSION: ! 1006: ! 1007: CheckedDump(QIC117SHOWTD,("TAPE_TENSION ... ")); ! 1008: ntStatus = q117ConvertStatus ( ! 1009: DeviceObject, ! 1010: q117DoCmd(&ioreq, DReten, NULL, context) ); ! 1011: ! 1012: break; ! 1013: ! 1014: case TAPE_UNLOCK: ! 1015: CheckedDump(QIC117SHOWTD,("UN")); ! 1016: case TAPE_LOCK: ! 1017: CheckedDump(QIC117SHOWTD,("LOCK TAPE ...")); ! 1018: ! 1019: // ! 1020: // These commands mean nothing for a QIC-40 drive. However, the current ! 1021: // operation will be flushed allowing user to remove cartridge. ! 1022: // ! 1023: break; ! 1024: ! 1025: case TAPE_FORMAT: ! 1026: CheckedDump(QIC117SHOWTD,("TAPE_FORMAT ... ")); ! 1027: ! 1028: ntStatus = q117ConvertStatus ( ! 1029: DeviceObject, ! 1030: q117Format( ! 1031: &numberBad, ! 1032: TRUE, ! 1033: &vendorUnique, ! 1034: context ) ); ! 1035: break; ! 1036: ! 1037: default: ! 1038: CheckedDump(QIC117SHOWTD,("INVALID ... ")); ! 1039: ! 1040: ntStatus = STATUS_INVALID_DEVICE_REQUEST; ! 1041: ! 1042: } // end switch (tapePrepare->Operation) ! 1043: ! 1044: return ntStatus; ! 1045: } ! 1046: ! 1047: NTSTATUS ! 1048: q117IoCtlGetStatus ( ! 1049: IN PDEVICE_OBJECT DeviceObject, ! 1050: IN PIRP Irp ! 1051: ) ! 1052: /*++ ! 1053: ! 1054: Routine Description: ! 1055: ! 1056: ! 1057: ! 1058: Arguments: ! 1059: ! 1060: DeviceObject ! 1061: ! 1062: ! 1063: Return Value: ! 1064: ! 1065: NT Status ! 1066: ! 1067: --*/ ! 1068: ! 1069: { ! 1070: PQ117_CONTEXT context; ! 1071: // PTAPE_STATUS tapeStatus; ! 1072: STATUS status; ! 1073: ! 1074: context = DeviceObject->DeviceExtension; ! 1075: ! 1076: ! 1077: // ! 1078: // Complete any operation in progress ! 1079: // ! 1080: switch(context->CurrentOperation.Type) { ! 1081: ! 1082: case BackupInProgress: ! 1083: status = NoErr; ! 1084: break; ! 1085: ! 1086: case RestoreInProgress: ! 1087: status = NoErr; ! 1088: break; ! 1089: ! 1090: case NoOperation: ! 1091: status = q117CheckNewTape(context); ! 1092: break; ! 1093: ! 1094: default: ! 1095: status = FCodeErr; ! 1096: ! 1097: } ! 1098: ! 1099: //tapeStatus = Irp->UserBuffer; ! 1100: ! 1101: // ! 1102: // Is this supported in the tape API ????? ! 1103: // ! 1104: // *tapeStatus = context->TapeStatus; ! 1105: ! 1106: // ! 1107: // Reset media changed flag. ! 1108: // ! 1109: // context->TapeStatus.Status &= ~TAPE_STATUS_MEDIA_CHANGED; ! 1110: ! 1111: return q117ConvertStatus(DeviceObject, status); ! 1112: } ! 1113: ! 1114: NTSTATUS ! 1115: q117IoCtlReadAbs ( ! 1116: IN PDEVICE_OBJECT DeviceObject, ! 1117: IN PIRP Irp ! 1118: ) ! 1119: /*++ ! 1120: ! 1121: Routine Description: ! 1122: ! 1123: ! 1124: ! 1125: Arguments: ! 1126: ! 1127: DeviceObject ! 1128: ! 1129: ! 1130: Return Value: ! 1131: ! 1132: NT Status ! 1133: ! 1134: --*/ ! 1135: ! 1136: { ! 1137: PQ117_CONTEXT context; ! 1138: PSEGMENT_BUFFER bufferInfo; ! 1139: PVOID scrbuf; ! 1140: PIO_REQUEST ioreq; ! 1141: PCMS_RW_ABS readWrite; ! 1142: STATUS status; ! 1143: ULONG len; ! 1144: PIO_STACK_LOCATION irpStack; ! 1145: ! 1146: context = DeviceObject->DeviceExtension; ! 1147: readWrite = Irp->AssociatedIrp.SystemBuffer; ! 1148: irpStack = IoGetCurrentIrpStackLocation(Irp); ! 1149: ! 1150: scrbuf = q117GetFreeBuffer(&bufferInfo,context); ! 1151: ! 1152: status=q117IssIOReq( ! 1153: scrbuf, ! 1154: DRead, ! 1155: readWrite->Block, ! 1156: bufferInfo, ! 1157: context ! 1158: ); ! 1159: ! 1160: if (!status) { ! 1161: ! 1162: // ! 1163: // Wait for data to be written ! 1164: // ! 1165: ioreq=q117Dequeue(WaitForItem,context); ! 1166: ! 1167: status = ioreq->Status; ! 1168: ! 1169: } ! 1170: ! 1171: readWrite->Status = status; ! 1172: ! 1173: len = BYTES_PER_SECTOR*readWrite->Count; ! 1174: ! 1175: if (len > irpStack->Parameters.DeviceIoControl.OutputBufferLength) { ! 1176: ! 1177: len = irpStack->Parameters.DeviceIoControl.OutputBufferLength; ! 1178: ! 1179: } ! 1180: ! 1181: RtlMoveMemory( ! 1182: readWrite+1, ! 1183: scrbuf, ! 1184: len ! 1185: ); ! 1186: ! 1187: Irp->IoStatus.Information = sizeof(CMS_RW_ABS)+len; ! 1188: ! 1189: return q117ConvertStatus(DeviceObject, status); ! 1190: ! 1191: ! 1192: } ! 1193: ! 1194: NTSTATUS ! 1195: q117IoCtlWriteAbs ( ! 1196: IN PDEVICE_OBJECT DeviceObject, ! 1197: IN PIRP Irp ! 1198: ) ! 1199: /*++ ! 1200: ! 1201: Routine Description: ! 1202: ! 1203: ! 1204: ! 1205: Arguments: ! 1206: ! 1207: DeviceObject ! 1208: ! 1209: ! 1210: Return Value: ! 1211: ! 1212: NT Status ! 1213: ! 1214: --*/ ! 1215: ! 1216: { ! 1217: PQ117_CONTEXT context; ! 1218: PSEGMENT_BUFFER bufferInfo; ! 1219: PVOID scrbuf; ! 1220: PIO_REQUEST ioreq; ! 1221: PCMS_RW_ABS readWrite; ! 1222: STATUS status; ! 1223: ! 1224: context = DeviceObject->DeviceExtension; ! 1225: readWrite = Irp->AssociatedIrp.SystemBuffer; ! 1226: ! 1227: scrbuf = q117GetFreeBuffer(&bufferInfo,context); ! 1228: ! 1229: RtlMoveMemory( ! 1230: scrbuf, ! 1231: readWrite+1, ! 1232: BYTES_PER_SECTOR*readWrite->Count ! 1233: ); ! 1234: ! 1235: status = q117IssIOReq( ! 1236: scrbuf, ! 1237: DWrite, ! 1238: readWrite->Block, ! 1239: bufferInfo, ! 1240: context); ! 1241: ! 1242: if (!status) { ! 1243: ! 1244: // ! 1245: // Wait for data to be written ! 1246: // ! 1247: ioreq=q117Dequeue(WaitForItem,context); ! 1248: ! 1249: status = ioreq->Status; ! 1250: ! 1251: } ! 1252: ! 1253: readWrite->Status = status; ! 1254: ! 1255: return q117ConvertStatus(DeviceObject, status); ! 1256: ! 1257: } ! 1258: ! 1259: STATUS ! 1260: q117CheckNewTape ( ! 1261: PQ117_CONTEXT Context ! 1262: ) ! 1263: /*++ ! 1264: ! 1265: Routine Description: ! 1266: ! 1267: This routine checks for new tape and reads header if necessary ! 1268: ! 1269: Arguments: ! 1270: ! 1271: Context - Current context information ! 1272: ! 1273: Return Value: ! 1274: ! 1275: NT Status ! 1276: ! 1277: --*/ ! 1278: ! 1279: { ! 1280: STATUS stat; ! 1281: IO_REQUEST ioreq; ! 1282: PTAPE_HEADER header; ! 1283: VOLUME_TABLE_ENTRY tempVolume; ! 1284: UCHAR tapeType; ! 1285: BOOLEAN found; ! 1286: BOOLEAN notNt; ! 1287: USHORT volumesRead; ! 1288: DRIVE_CAPACITY driveCapacity; ! 1289: SEGMENT curseg; ! 1290: ! 1291: ! 1292: // ! 1293: // Check to see if there is a tape in the drive ! 1294: // ! 1295: stat = q117DoCmd(&ioreq, DGetCart, &tapeType, Context); ! 1296: ! 1297: // ! 1298: // If we found a tape and need to read the header and volume tables, ! 1299: // do it now. ! 1300: // ! 1301: if (stat == NewCart) { ! 1302: // Context->TapeStatus.Status |= TAPE_STATUS_MEDIA_CHANGED; ! 1303: } ! 1304: ! 1305: if (stat == NewCart || ! 1306: (stat == NoErr && Context->CurrentTape.State == NeedInfoLoaded) ) { ! 1307: ! 1308: CheckedDump(QIC117SHOWTD,("New Cart Detected\n")); ! 1309: ! 1310: // ! 1311: // Check to see if there is a tape in the drive ! 1312: // ! 1313: ! 1314: stat = q117DoCmd(&ioreq, DClearNewCart, NULL, Context); ! 1315: ! 1316: if (stat) { ! 1317: return stat; ! 1318: } ! 1319: ! 1320: // ! 1321: // Saw new cart, so set need loaded flag ! 1322: // ! 1323: Context->CurrentTape.State = NeedInfoLoaded; ! 1324: ! 1325: // clear no media flag and end of media flag ! 1326: // Context->TapeStatus.Status &= ! 1327: // ~(TAPE_STATUS_NO_MEDIA|TAPE_STATUS_END_OF_MEDIA); ! 1328: ! 1329: // Set bom and device ready. ! 1330: // Context->TapeStatus.Status |= ! 1331: // TAPE_STATUS_DEVICE_READY|TAPE_STATUS_BEGINNING_OF_MEDIA; ! 1332: ! 1333: // ! 1334: // Check to see if tape is write protected ! 1335: // ! 1336: if (stat = q117DoCmd(&ioreq, DSndWPro, NULL, Context)) { ! 1337: ! 1338: if (stat != WProt) { ! 1339: return stat; ! 1340: } ! 1341: ! 1342: } ! 1343: ! 1344: Context->CurrentTape.MediaInfo->WriteProtected = (stat == WProt); ! 1345: if (stat == WProt) { ! 1346: // Context->TapeStatus.Status |= TAPE_STATUS_WRITE_PROTECTED; ! 1347: } ! 1348: ! 1349: // ! 1350: // Check to see if drive is formatted ! 1351: // ! 1352: stat = q117DoCmd(&ioreq, DGetCap, &driveCapacity, Context); ! 1353: ! 1354: if (stat) { ! 1355: return stat; ! 1356: } ! 1357: ! 1358: // ! 1359: // If tape not referenced. ! 1360: // ! 1361: if (driveCapacity.referenced == FALSE) { ! 1362: return BadFmt; ! 1363: } ! 1364: ! 1365: // ! 1366: // Check to see if tape is correct format ! 1367: // ! 1368: stat = q117DoCmd(&ioreq, DChkFmt, NULL, Context); ! 1369: ! 1370: if (stat) { ! 1371: if (stat == WrongFmt) { ! 1372: Context->CurrentTape.MediaInfo->WriteProtected = ! 1373: TRUE; ! 1374: stat = NoErr; ! 1375: } else { ! 1376: return stat; ! 1377: } ! 1378: } ! 1379: ! 1380: // ! 1381: // Now read the tape header (and bad sector map) ! 1382: // ! 1383: if (stat = q117LoadTape(&header,Context)) { ! 1384: return stat; ! 1385: } ! 1386: CheckedDump(QIC117SHOWTD,("LoadTape successful\n")); ! 1387: ! 1388: // ! 1389: // If this capacity not supported by this drive, ! 1390: // (i.e.. Pegasus cart in a QIC-40 drive) ! 1391: // return invalid format ! 1392: // ! 1393: if (header->FormatCode != driveCapacity.TapeFormatCode) { ! 1394: return BadFmt; ! 1395: } ! 1396: ! 1397: // ! 1398: // Copy over bad sector map, etc. ! 1399: // ! 1400: RtlMoveMemory( ! 1401: Context->CurrentTape.TapeHeader, ! 1402: header, ! 1403: sizeof(*Context->CurrentTape.TapeHeader) ); ! 1404: ! 1405: // ! 1406: // Now scan volume list and get last volume on tape as well as the ! 1407: // NT volume (if one exists ! 1408: // ! 1409: ! 1410: if (stat = q117SelectTD(Context)) ! 1411: return stat; ! 1412: ! 1413: volumesRead = 0; ! 1414: ! 1415: found = FALSE; ! 1416: ! 1417: do { ! 1418: /* get a volume directory from the tape (if error then)*/ ! 1419: stat = q117ReadVolumeEntry(&tempVolume,Context); ! 1420: ! 1421: if (stat && (stat != EndOfVol)) ! 1422: return stat; ! 1423: ! 1424: if (!stat) { ! 1425: volumesRead++; ! 1426: ! 1427: // ! 1428: // For now, let the system find ANY volume type and select ! 1429: // it. This will allow NT Backup software to read the ! 1430: // volume and detect that it is a non-nt format. ! 1431: // To do this, the if statement is removed, allowing ! 1432: // the first volume found, be the one that we select. ! 1433: // ! 1434: if (!(tempVolume.VendorSpecific && ! 1435: tempVolume.Vendor.cms_QIC40.OpSysType == OP_WINDOWS_NT)) { ! 1436: ! 1437: Context->CurrentOperation.BytesOnTape = ! 1438: Context->CurrentOperation.BytesRead = 0; ! 1439: ! 1440: notNt = TRUE; ! 1441: ! 1442: #ifndef NO_MARKS ! 1443: Context->MarkArray.TotalMarks = 0; ! 1444: Context->CurrentMark = Context->MarkArray.TotalMarks; ! 1445: Context->MarkArray.MarkEntry[Context->CurrentMark].Offset = ! 1446: 0xffffffff; ! 1447: #endif ! 1448: ! 1449: /* force the data size to be the entire backup */ ! 1450: tempVolume.DataSize = 0; ! 1451: curseg = tempVolume.StartSegment; ! 1452: while (curseg <= tempVolume.EndingSegment) { ! 1453: ! 1454: tempVolume.DataSize += ! 1455: q117GoodDataBytes( ! 1456: curseg, ! 1457: Context); ! 1458: ++curseg; ! 1459: ! 1460: ! 1461: } ! 1462: ! 1463: } else { ! 1464: ! 1465: notNt = FALSE; ! 1466: ! 1467: } ! 1468: found = TRUE; ! 1469: Context->ActiveVolume = tempVolume; ! 1470: } ! 1471: ! 1472: } while (!stat && volumesRead < Context->CurrentTape.MaximumVolumes); ! 1473: ! 1474: if (stat = q117EndRest(Context)) ! 1475: return(stat); ! 1476: ! 1477: // ! 1478: // If we did not find a volume, then signal others that ! 1479: // the ActiveVolume information is invalid. ! 1480: // ! 1481: if (!found) ! 1482: Context->ActiveVolumeNumber = 0; ! 1483: ! 1484: // ! 1485: // Zero out bytes saved (incase user trys to read) ! 1486: // Also set CurrentOperation.BytesRead to zero (start of tape) ! 1487: // ! 1488: Context->CurrentOperation.BytesOnTape = ! 1489: Context->CurrentOperation.BytesRead = 0; ! 1490: ! 1491: // ! 1492: // Flag that we have done everything ! 1493: // ! 1494: Context->CurrentTape.State = TapeInfoLoaded; ! 1495: ! 1496: #ifndef NO_MARKS ! 1497: ! 1498: // ! 1499: // Read the mark list from the active volume ! 1500: // ! 1501: if (found && !notNt) { ! 1502: ! 1503: stat = q117GetMarks(Context); ! 1504: ! 1505: } ! 1506: ! 1507: #endif ! 1508: ! 1509: // ! 1510: // If no more pressing error, return NewCart ! 1511: // so application can be aware of the new insertion. ! 1512: // ! 1513: if (stat == NoErr) { ! 1514: stat = NewCart; ! 1515: } ! 1516: ! 1517: } else { ! 1518: ! 1519: if (stat == NoTape) { ! 1520: // Context->TapeStatus.Status |= TAPE_STATUS_NO_MEDIA; ! 1521: Context->ActiveVolumeNumber = 0; ! 1522: Context->CurrentOperation.BytesOnTape = ! 1523: Context->CurrentOperation.BytesRead = 0; ! 1524: ! 1525: #ifndef NO_MARKS ! 1526: Context->MarkArray.TotalMarks = 0; ! 1527: Context->CurrentMark = Context->MarkArray.TotalMarks; ! 1528: Context->MarkArray.MarkEntry[Context->CurrentMark].Offset = ! 1529: 0xffffffff; ! 1530: #endif ! 1531: ! 1532: } ! 1533: ! 1534: } ! 1535: ! 1536: return stat; ! 1537: } ! 1538:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.