2 * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
4 * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
8 * Copyright (C) 2006 Kern Sibbald
10 * This file was contributed to the Bacula project by Robert Nelson.
12 * Robert Nelson has been granted a perpetual, worldwide,
13 * non-exclusive, no-charge, royalty-free, irrevocable copyright
14 * license to reproduce, prepare derivative works of, publicly
15 * display, publicly perform, sublicense, and distribute the original
16 * work contributed by Robert Nelson to the Bacula project in source
19 * If you wish to license contributions from Robert Nelson
20 * under an alternate open source license please contact
21 * Robert Nelson <robertn@the-nelsons.org>.
24 Copyright (C) 2006 Kern Sibbald
26 This program is free software; you can redistribute it and/or
27 modify it under the terms of the GNU General Public License
28 version 2 as amended with additional clauses defined in the
29 file LICENSE in the main source directory.
31 This program is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 the file LICENSE for additional details.
41 #include "bacula.h" /* pull in global headers */
42 #include "stored.h" /* pull in Storage Deamon headers */
49 // SCSI bus status codes.
52 #define SCSISTAT_GOOD 0x00
53 #define SCSISTAT_CHECK_CONDITION 0x02
54 #define SCSISTAT_CONDITION_MET 0x04
55 #define SCSISTAT_BUSY 0x08
56 #define SCSISTAT_INTERMEDIATE 0x10
57 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
58 #define SCSISTAT_RESERVATION_CONFLICT 0x18
59 #define SCSISTAT_COMMAND_TERMINATED 0x22
60 #define SCSISTAT_QUEUE_FULL 0x28
62 /* Forward referenced functions */
64 extern char my_name[];
65 extern int debug_level;
67 inline SHORT Read16BitSigned(const unsigned char *pValue)
69 return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
72 inline USHORT Read16BitUnsigned(const unsigned char *pValue)
74 return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
77 inline LONG Read24BitSigned(const unsigned char *pValue)
79 return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
80 (ULONG)pValue[2])) << 8 >> 8;
83 inline ULONG Read24BitUnsigned(const unsigned char *pValue)
85 return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
88 inline LONG Read32BitSigned(const unsigned char *pValue)
90 return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
91 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
94 inline ULONG Read32BitUnsigned(const unsigned char *pValue)
96 return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
97 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
100 inline LONGLONG Read64BitSigned(const unsigned char *pValue)
102 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
103 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
104 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
105 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
108 inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
110 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
111 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
112 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
113 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
116 typedef struct _TAPE_POSITION_INFO
118 UCHAR AtPartitionStart:1;
119 UCHAR AtPartitionEnd:1;
120 UCHAR PartitionBlockValid:1;
121 UCHAR FileSetValid:1;
125 ULONGLONG BlockNumber;
126 ULONGLONG FileNumber;
128 } TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
130 typedef struct _TAPE_HANDLE_INFO
140 ULONGLONG ullFileStart;
142 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
144 TAPE_HANDLE_INFO TapeHandleTable[] =
146 { INVALID_HANDLE_VALUE },
147 { INVALID_HANDLE_VALUE },
148 { INVALID_HANDLE_VALUE },
149 { INVALID_HANDLE_VALUE },
150 { INVALID_HANDLE_VALUE },
151 { INVALID_HANDLE_VALUE },
152 { INVALID_HANDLE_VALUE },
153 { INVALID_HANDLE_VALUE },
154 { INVALID_HANDLE_VALUE },
155 { INVALID_HANDLE_VALUE },
156 { INVALID_HANDLE_VALUE },
157 { INVALID_HANDLE_VALUE },
158 { INVALID_HANDLE_VALUE },
159 { INVALID_HANDLE_VALUE },
160 { INVALID_HANDLE_VALUE },
161 { INVALID_HANDLE_VALUE }
164 #define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
166 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
167 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
169 int tape_get(int fd, struct mtget *mt_get);
170 int tape_op(int fd, struct mtop *mt_com);
171 int tape_pos(int fd, struct mtpos *mt_pos);
174 tape_open(const char *file, int flags, int mode)
176 HANDLE hDevice = INVALID_HANDLE_VALUE;
177 char szDeviceName[256] = "\\\\.\\";
181 for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
182 if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
187 if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
191 memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
193 if (file[0] != '\\' && file[0] != '/') {
194 bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
196 bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
199 hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
201 if (hDevice != INVALID_HANDLE_VALUE) {
202 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
204 memset(pHandleInfo, 0, sizeof(*pHandleInfo));
206 pHandleInfo->OSHandle = hDevice;
208 TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
209 DWORD dwSize = sizeof(TapeDriveParameters);
211 dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
212 if (dwResult == NO_ERROR) {
213 pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
214 pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
217 TAPE_POSITION_INFO TapePositionInfo;
219 dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
221 if (dwResult == NO_ERROR) {
222 if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
223 (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
224 pHandleInfo->ulFile = 0;
225 pHandleInfo->bBlockValid = true;
226 pHandleInfo->ullFileStart = 0;
227 } else if (TapePositionInfo.FileSetValid) {
228 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
232 DWORD dwError = GetLastError();
235 case ERROR_FILE_NOT_FOUND:
236 case ERROR_PATH_NOT_FOUND:
240 case ERROR_TOO_MANY_OPEN_FILES:
245 case ERROR_ACCESS_DENIED:
246 case ERROR_SHARING_VIOLATION:
247 case ERROR_LOCK_VIOLATION:
248 case ERROR_INVALID_NAME:
252 case ERROR_FILE_EXISTS:
256 case ERROR_INVALID_PARAMETER:
264 return (int)idxFile + 3;
268 tape_read(int fd, void *buffer, unsigned int count)
270 if (buffer == NULL) {
275 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
276 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
282 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
287 bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
290 pHandleInfo->bEOF = false;
291 pHandleInfo->bEOT = false;
292 pHandleInfo->bEOD = false;
295 int iReturnValue = 0;
296 DWORD last_error = GetLastError();
298 switch (last_error) {
300 case ERROR_FILEMARK_DETECTED:
301 pHandleInfo->bEOF = true;
304 case ERROR_END_OF_MEDIA:
305 pHandleInfo->bEOT = true;
308 case ERROR_NO_MEDIA_IN_DRIVE:
309 pHandleInfo->bEOF = false;
310 pHandleInfo->bEOT = false;
311 pHandleInfo->bEOD = false;
316 case ERROR_NO_DATA_DETECTED:
317 pHandleInfo->bEOD = true;
320 case ERROR_INVALID_HANDLE:
321 case ERROR_ACCESS_DENIED:
322 case ERROR_LOCK_VIOLATION:
328 pHandleInfo->bEOF = false;
329 pHandleInfo->bEOT = false;
330 pHandleInfo->bEOD = false;
340 tape_write(int fd, const void *buffer, unsigned int count)
342 if (buffer == NULL) {
347 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
353 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
358 bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
361 pHandleInfo->bEOF = false;
362 pHandleInfo->bEOT = false;
363 return bytes_written;
365 DWORD last_error = GetLastError();
367 switch (last_error) {
368 case ERROR_END_OF_MEDIA:
369 case ERROR_DISK_FULL:
370 pHandleInfo->bEOT = true;
374 case ERROR_NO_MEDIA_IN_DRIVE:
375 pHandleInfo->bEOF = false;
376 pHandleInfo->bEOT = false;
377 pHandleInfo->bEOD = false;
381 case ERROR_INVALID_HANDLE:
382 case ERROR_ACCESS_DENIED:
387 pHandleInfo->bEOF = false;
388 pHandleInfo->bEOT = false;
389 pHandleInfo->bEOD = false;
400 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
401 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
406 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
408 if (!CloseHandle(pHandleInfo->OSHandle)) {
409 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
414 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
420 tape_ioctl(int fd, unsigned long int request, ...)
425 va_start(argp, request);
429 result = tape_op(fd, va_arg(argp, mtop *));
433 result = tape_get(fd, va_arg(argp, mtget *));
437 result = tape_pos(fd, va_arg(argp, mtpos *));
451 int tape_op(int fd, struct mtop *mt_com)
453 DWORD result = NO_ERROR;
456 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
457 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
463 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
465 switch (mt_com->mt_op)
482 for (index = 0; index < mt_com->mt_count; index++) {
483 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
484 if (result == NO_ERROR) {
485 pHandleInfo->ulFile++;
486 pHandleInfo->bEOF = true;
487 pHandleInfo->bEOT = false;
493 for (index = 0; index < mt_com->mt_count; index++) {
494 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
495 if (result == NO_ERROR) {
496 pHandleInfo->ulFile--;
497 pHandleInfo->bBlockValid = false;
498 pHandleInfo->bEOD = false;
499 pHandleInfo->bEOF = false;
500 pHandleInfo->bEOT = false;
506 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE);
507 if (result == NO_ERROR) {
508 pHandleInfo->bEOD = false;
509 pHandleInfo->bEOF = false;
510 pHandleInfo->bEOT = false;
511 } else if (result == ERROR_FILEMARK_DETECTED) {
512 pHandleInfo->bEOF = true;
517 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
518 if (result == NO_ERROR) {
519 pHandleInfo->bEOD = false;
520 pHandleInfo->bEOF = false;
521 pHandleInfo->bEOT = false;
522 } else if (result == ERROR_FILEMARK_DETECTED) {
523 pHandleInfo->ulFile--;
524 pHandleInfo->bBlockValid = false;
525 pHandleInfo->bEOD = false;
526 pHandleInfo->bEOF = false;
527 pHandleInfo->bEOT = false;
532 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
533 if (result == NO_ERROR) {
534 pHandleInfo->bEOF = true;
535 pHandleInfo->bEOT = false;
536 pHandleInfo->ulFile += mt_com->mt_count;
537 pHandleInfo->bBlockValid = true;
538 pHandleInfo->ullFileStart = 0;
543 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
544 if (result == NO_ERROR) {
545 pHandleInfo->bEOD = false;
546 pHandleInfo->bEOF = false;
547 pHandleInfo->bEOT = false;
548 pHandleInfo->ulFile = 0;
549 pHandleInfo->bBlockValid = true;
550 pHandleInfo->ullFileStart = 0;
555 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
556 if (result == NO_ERROR) {
557 pHandleInfo->bEOD = false;
558 pHandleInfo->bEOF = false;
559 pHandleInfo->bEOT = false;
560 pHandleInfo->ulFile = 0;
561 pHandleInfo->ullFileStart = 0;
566 result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
567 if (result == NO_ERROR) {
568 pHandleInfo->bEOD = false;
569 pHandleInfo->bEOF = false;
570 pHandleInfo->bEOT = false;
571 pHandleInfo->ulFile = 0;
572 pHandleInfo->bBlockValid = true;
573 pHandleInfo->ullFileStart = 0;
578 for (index = 0; index < mt_com->mt_count; index++) {
579 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
580 if (result == NO_ERROR) {
581 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
582 pHandleInfo->bEOD = false;
583 pHandleInfo->bEOF = false;
584 pHandleInfo->bEOT = false;
590 for (index = 0; index < mt_com->mt_count; index++) {
591 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
592 if (result == NO_ERROR) {
593 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
594 pHandleInfo->bEOD = false;
595 pHandleInfo->bEOF = false;
596 pHandleInfo->bEOT = false;
603 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
604 if (result != NO_ERROR) {
605 pHandleInfo->bEOF = false;
607 if (result == ERROR_END_OF_MEDIA) {
608 pHandleInfo->bEOD = true;
609 pHandleInfo->bEOT = true;
612 if (result == ERROR_NO_DATA_DETECTED) {
613 pHandleInfo->bEOD = true;
614 pHandleInfo->bEOT = false;
619 pHandleInfo->bEOF = true;
620 pHandleInfo->ulFile++;
626 result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
627 if (result == NO_ERROR) {
628 pHandleInfo->bEOD = true;
629 pHandleInfo->bEOF = false;
630 pHandleInfo->bEOT = false;
631 pHandleInfo->ulFile = 0;
632 pHandleInfo->bBlockValid = true;
633 pHandleInfo->ullFileStart = 0;
639 TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
641 SetMediaParameters.BlockSize = mt_com->mt_count;
642 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
648 TAPE_POSITION_INFO TapePositionInfo;
650 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
652 memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
653 DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
654 if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
655 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
657 pHandleInfo->ulFile = ~0U;
668 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
669 if (result == NO_ERROR) {
676 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
680 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
684 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
688 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
692 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
696 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
700 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
705 TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
706 TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
709 size = sizeof(GetDriveParameters);
711 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
713 if (result == NO_ERROR)
715 SetDriveParameters.ECC = GetDriveParameters.ECC;
716 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
717 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
718 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
719 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
721 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
727 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
731 if (mt_com->mt_count == 0)
733 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
737 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
742 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
743 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
745 TAPE_POSITION_INFO TapePositionInfo;
747 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
748 pHandleInfo->bBlockValid = true;
749 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
755 case (DWORD)-1: /* Error has already been translated into errno */
759 case ERROR_FILEMARK_DETECTED:
763 case ERROR_END_OF_MEDIA:
764 pHandleInfo->bEOT = true;
768 case ERROR_NO_DATA_DETECTED:
769 pHandleInfo->bEOD = true;
773 case ERROR_NO_MEDIA_IN_DRIVE:
774 pHandleInfo->bEOF = false;
775 pHandleInfo->bEOT = false;
776 pHandleInfo->bEOD = false;
780 case ERROR_INVALID_HANDLE:
781 case ERROR_ACCESS_DENIED:
782 case ERROR_LOCK_VIOLATION:
787 return result == NO_ERROR ? 0 : -1;
790 int tape_get(int fd, struct mtget *mt_get)
792 TAPE_POSITION_INFO pos_info;
795 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
796 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
801 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
803 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
810 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
812 if (result != NO_ERROR) {
813 TAPE_GET_DRIVE_PARAMETERS drive_params;
816 size = sizeof(drive_params);
818 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
820 if (result == NO_ERROR) {
821 blocksize = drive_params.DefaultBlockSize;
825 mt_get->mt_type = MT_ISSCSI2;
828 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
830 // Density / Block Size
831 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
832 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
834 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
836 if (pHandleInfo->bEOF) {
837 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
840 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
841 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
844 if (pHandleInfo->bEOT) {
845 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
848 if (pHandleInfo->bEOD) {
849 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
852 TAPE_GET_MEDIA_PARAMETERS media_params;
853 DWORD size = sizeof(media_params);
855 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
857 if (result == NO_ERROR && media_params.WriteProtected) {
858 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
861 result = GetTapeStatus(pHandleInfo->OSHandle);
863 if (result != NO_ERROR) {
864 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
865 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
868 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
871 // Recovered Error Count
872 mt_get->mt_erreg = 0;
875 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
878 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
883 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
884 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
885 #define SERVICEACTION_LONG_FORM 6
886 #define SERVICEACTION_EXTENDED_FORM 8
889 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
904 UCHAR NumberBufferBlocks[3];
905 UCHAR NumberBufferBytes[4];
906 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
908 typedef struct _SCSI_READ_POSITION_LONG_BUFFER
918 UCHAR BlockNumber[8];
921 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
923 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
934 UCHAR AdditionalLength[2];
936 UCHAR NumberBufferObjects[3];
937 UCHAR FirstLogicalObject[8];
938 UCHAR LastLogicalObject[8];
939 UCHAR NumberBufferObjectBytes[8];
940 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
942 typedef union _READ_POSITION_RESULT {
943 SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
944 SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
945 SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
946 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
948 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
950 PSCSI_PASS_THROUGH ScsiPassThrough;
952 DWORD dwBytesReturned;
954 const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
956 memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
958 ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
960 for (int pass = 0; pass < 2; pass++)
962 memset(ScsiPassThrough, 0, dwBufferSize);
964 ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
966 ScsiPassThrough->CdbLength = 10;
967 ScsiPassThrough->SenseInfoLength = 28;
968 ScsiPassThrough->DataIn = 1;
969 ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
970 ScsiPassThrough->TimeOutValue = 1000;
971 ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
972 ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
974 ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
979 ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
983 ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
987 bResult = DeviceIoControl( hDevice,
988 IOCTL_SCSI_PASS_THROUGH,
989 ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
990 ScsiPassThrough, dwBufferSize,
994 if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
995 if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
996 PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
1000 case 0: // SERVICEACTION_LONG_FORM
1002 TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1003 TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1005 if (!TapePositionInfo->PartitionBlockValid) {
1006 TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1008 if (TapePositionInfo->PartitionBlockValid) {
1009 TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1010 TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1014 TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1015 if (TapePositionInfo->FileSetValid) {
1016 TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1017 TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1022 case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
1024 // pPosResult->ShortBuffer.PERR;
1025 // pPosResult->ShortBuffer.BYCU;
1026 // pPosResult->ShortBuffer.BCU;
1027 TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1028 TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1030 if (!TapePositionInfo->PartitionBlockValid) {
1031 TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1033 if (TapePositionInfo->PartitionBlockValid) {
1034 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
1035 TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1038 // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1039 // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1040 // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1047 free(ScsiPassThrough);
1052 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1054 DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1055 GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1059 if (pGetMediaTypes == NULL) {
1060 return ERROR_OUTOFMEMORY;
1064 DWORD dwBytesReturned;
1066 bResult = DeviceIoControl( hDevice,
1067 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1069 (LPVOID)pGetMediaTypes, dwBufferSize,
1074 dwResult = GetLastError();
1076 if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1077 free(pGetMediaTypes);
1081 dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1083 GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1085 if (pNewBuffer != pGetMediaTypes) {
1086 free(pGetMediaTypes);
1088 if (pNewBuffer == NULL) {
1089 return ERROR_OUTOFMEMORY;
1092 pGetMediaTypes = pNewBuffer;
1097 if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1098 free(pGetMediaTypes);
1099 return ERROR_BAD_DEVICE;
1102 for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1104 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1106 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1107 *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1112 *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1114 free(pGetMediaTypes);
1120 free(pGetMediaTypes);
1122 return ERROR_NO_MEDIA_IN_DRIVE;
1125 int tape_pos(int fd, struct mtpos *mt_pos)
1132 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1133 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1138 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1140 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1141 if (result == NO_ERROR) {
1142 mt_pos->mt_blkno = offset;