2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
22 * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
25 * This file was contributed to the Bacula project by Robert Nelson.
27 * Robert Nelson has been granted a perpetual, worldwide,
28 * non-exclusive, no-charge, royalty-free, irrevocable copyright
29 * license to reproduce, prepare derivative works of, publicly
30 * display, publicly perform, sublicense, and distribute the original
31 * work contributed by Robert Nelson to the Bacula project in source
34 * If you wish to license contributions from Robert Nelson
35 * under an alternate open source license please contact
36 * Robert Nelson <robertn@the-nelsons.org>.
42 #include "bacula.h" /* pull in global headers */
43 #include "stored.h" /* pull in Storage Deamon headers */
44 #include "win_tape_device.h"
55 // SCSI bus status codes.
58 #define SCSISTAT_GOOD 0x00
59 #define SCSISTAT_CHECK_CONDITION 0x02
60 #define SCSISTAT_CONDITION_MET 0x04
61 #define SCSISTAT_BUSY 0x08
62 #define SCSISTAT_INTERMEDIATE 0x10
63 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
64 #define SCSISTAT_RESERVATION_CONFLICT 0x18
65 #define SCSISTAT_COMMAND_TERMINATED 0x22
66 #define SCSISTAT_QUEUE_FULL 0x28
68 static inline SHORT Read16BitSigned(const unsigned char *pValue)
70 return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
73 static inline USHORT Read16BitUnsigned(const unsigned char *pValue)
75 return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
78 static inline LONG Read24BitSigned(const unsigned char *pValue)
80 return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
81 (ULONG)pValue[2])) << 8 >> 8;
84 static inline ULONG Read24BitUnsigned(const unsigned char *pValue)
86 return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
89 static inline LONG Read32BitSigned(const unsigned char *pValue)
91 return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
92 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
95 static inline ULONG Read32BitUnsigned(const unsigned char *pValue)
97 return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
98 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
101 static inline LONGLONG Read64BitSigned(const unsigned char *pValue)
103 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
104 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
105 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
106 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
109 static inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
111 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
112 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
113 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
114 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
117 typedef struct _TAPE_POSITION_INFO
119 UCHAR AtPartitionStart:1;
120 UCHAR AtPartitionEnd:1;
121 UCHAR PartitionBlockValid:1;
122 UCHAR FileSetValid:1;
126 ULONGLONG BlockNumber;
127 ULONGLONG FileNumber;
129 } TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
131 typedef struct _TAPE_HANDLE_INFO
141 ULONGLONG ullFileStart;
143 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
145 TAPE_HANDLE_INFO TapeHandleTable[] =
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 },
162 { INVALID_HANDLE_VALUE }
165 #define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
167 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
168 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
170 win_tape_device::win_tape_device()
175 win_tape_device::~win_tape_device()
179 win_tape_device::d_open(const char *file, int flags, ...)
181 HANDLE hDevice = INVALID_HANDLE_VALUE;
182 char szDeviceName[256] = "\\\\.\\";
186 for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
187 if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
192 if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
196 memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
198 if (!IsPathSeparator(file[0])) {
199 bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
201 bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
204 hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
206 if (hDevice != INVALID_HANDLE_VALUE) {
207 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
209 memset(pHandleInfo, 0, sizeof(*pHandleInfo));
211 pHandleInfo->OSHandle = hDevice;
213 TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
214 DWORD dwSize = sizeof(TapeDriveParameters);
216 dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
217 if (dwResult == NO_ERROR) {
218 pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
219 pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
222 TAPE_POSITION_INFO TapePositionInfo;
224 dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
226 if (dwResult == NO_ERROR) {
227 if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
228 (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
229 pHandleInfo->ulFile = 0;
230 pHandleInfo->bBlockValid = true;
231 pHandleInfo->ullFileStart = 0;
232 } else if (TapePositionInfo.FileSetValid) {
233 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
237 DWORD dwError = GetLastError();
240 case ERROR_FILE_NOT_FOUND:
241 case ERROR_PATH_NOT_FOUND:
245 case ERROR_TOO_MANY_OPEN_FILES:
250 case ERROR_ACCESS_DENIED:
251 case ERROR_SHARING_VIOLATION:
252 case ERROR_LOCK_VIOLATION:
253 case ERROR_INVALID_NAME:
257 case ERROR_FILE_EXISTS:
261 case ERROR_INVALID_PARAMETER:
269 return (int)idxFile + 3;
272 ssize_t win_tape_device::d_read(int fd, void *buffer, size_t count)
274 if (buffer == NULL) {
279 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
280 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
285 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
290 bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
293 pHandleInfo->bEOF = false;
294 pHandleInfo->bEOT = false;
295 pHandleInfo->bEOD = false;
298 int iReturnValue = 0;
299 DWORD last_error = GetLastError();
301 switch (last_error) {
303 case ERROR_FILEMARK_DETECTED:
304 pHandleInfo->bEOF = true;
307 case ERROR_END_OF_MEDIA:
308 pHandleInfo->bEOT = true;
311 case ERROR_NO_MEDIA_IN_DRIVE:
312 pHandleInfo->bEOF = false;
313 pHandleInfo->bEOT = false;
314 pHandleInfo->bEOD = false;
319 case ERROR_NO_DATA_DETECTED:
320 pHandleInfo->bEOD = true;
323 case ERROR_INVALID_HANDLE:
324 case ERROR_ACCESS_DENIED:
325 case ERROR_LOCK_VIOLATION:
331 pHandleInfo->bEOF = false;
332 pHandleInfo->bEOT = false;
333 pHandleInfo->bEOD = false;
342 ssize_t win_tape_device::d_write(int fd, const void *buffer, size_t count)
344 if (buffer == NULL) {
349 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
350 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
355 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
360 bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
363 pHandleInfo->bEOF = false;
364 pHandleInfo->bEOT = false;
365 return bytes_written;
367 DWORD last_error = GetLastError();
369 switch (last_error) {
370 case ERROR_END_OF_MEDIA:
371 case ERROR_DISK_FULL:
372 pHandleInfo->bEOT = true;
376 case ERROR_NO_MEDIA_IN_DRIVE:
377 pHandleInfo->bEOF = false;
378 pHandleInfo->bEOT = false;
379 pHandleInfo->bEOD = false;
383 case ERROR_INVALID_HANDLE:
384 case ERROR_ACCESS_DENIED:
389 pHandleInfo->bEOF = false;
390 pHandleInfo->bEOT = false;
391 pHandleInfo->bEOD = false;
399 int win_tape_device::d_close(int fd)
401 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
402 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
407 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
409 if (!CloseHandle(pHandleInfo->OSHandle)) {
410 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
415 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
420 int win_tape_device::d_ioctl(int fd, ioctl_req_t request, char *op)
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 win_tape_device::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) {
481 for (index = 0; index < mt_com->mt_count; index++) {
482 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
483 if (result == NO_ERROR) {
484 pHandleInfo->ulFile++;
485 pHandleInfo->bEOF = true;
486 pHandleInfo->bEOT = false;
492 for (index = 0; index < mt_com->mt_count; index++) {
493 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
494 if (result == NO_ERROR) {
495 pHandleInfo->ulFile--;
496 pHandleInfo->bBlockValid = false;
497 pHandleInfo->bEOD = false;
498 pHandleInfo->bEOF = false;
499 pHandleInfo->bEOT = false;
505 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE);
506 if (result == NO_ERROR) {
507 pHandleInfo->bEOD = false;
508 pHandleInfo->bEOF = false;
509 pHandleInfo->bEOT = false;
510 } else if (result == ERROR_FILEMARK_DETECTED) {
511 pHandleInfo->bEOF = true;
516 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
517 if (result == NO_ERROR) {
518 pHandleInfo->bEOD = false;
519 pHandleInfo->bEOF = false;
520 pHandleInfo->bEOT = false;
521 } else if (result == ERROR_FILEMARK_DETECTED) {
522 pHandleInfo->ulFile--;
523 pHandleInfo->bBlockValid = false;
524 pHandleInfo->bEOD = false;
525 pHandleInfo->bEOF = false;
526 pHandleInfo->bEOT = false;
531 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
532 if (result == NO_ERROR) {
533 pHandleInfo->bEOF = true;
534 pHandleInfo->bEOT = false;
535 pHandleInfo->ulFile += mt_com->mt_count;
536 pHandleInfo->bBlockValid = true;
537 pHandleInfo->ullFileStart = 0;
542 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
543 if (result == NO_ERROR) {
544 pHandleInfo->bEOD = false;
545 pHandleInfo->bEOF = false;
546 pHandleInfo->bEOT = false;
547 pHandleInfo->ulFile = 0;
548 pHandleInfo->bBlockValid = true;
549 pHandleInfo->ullFileStart = 0;
554 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
555 if (result == NO_ERROR) {
556 pHandleInfo->bEOD = false;
557 pHandleInfo->bEOF = false;
558 pHandleInfo->bEOT = false;
559 pHandleInfo->ulFile = 0;
560 pHandleInfo->ullFileStart = 0;
565 result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
566 if (result == NO_ERROR) {
567 pHandleInfo->bEOD = false;
568 pHandleInfo->bEOF = false;
569 pHandleInfo->bEOT = false;
570 pHandleInfo->ulFile = 0;
571 pHandleInfo->bBlockValid = true;
572 pHandleInfo->ullFileStart = 0;
577 for (index = 0; index < mt_com->mt_count; index++) {
578 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
579 if (result == NO_ERROR) {
580 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
581 pHandleInfo->bEOD = false;
582 pHandleInfo->bEOF = false;
583 pHandleInfo->bEOT = false;
589 for (index = 0; index < mt_com->mt_count; index++) {
590 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
591 if (result == NO_ERROR) {
592 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
593 pHandleInfo->bEOD = false;
594 pHandleInfo->bEOF = false;
595 pHandleInfo->bEOT = false;
602 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
603 if (result != NO_ERROR) {
604 pHandleInfo->bEOF = false;
606 if (result == ERROR_END_OF_MEDIA) {
607 pHandleInfo->bEOD = true;
608 pHandleInfo->bEOT = true;
611 if (result == ERROR_NO_DATA_DETECTED) {
612 pHandleInfo->bEOD = true;
613 pHandleInfo->bEOT = false;
618 pHandleInfo->bEOF = true;
619 pHandleInfo->ulFile++;
625 result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
626 if (result == NO_ERROR) {
627 pHandleInfo->bEOD = true;
628 pHandleInfo->bEOF = false;
629 pHandleInfo->bEOT = false;
630 pHandleInfo->ulFile = 0;
631 pHandleInfo->bBlockValid = true;
632 pHandleInfo->ullFileStart = 0;
638 TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
640 SetMediaParameters.BlockSize = mt_com->mt_count;
641 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
647 TAPE_POSITION_INFO TapePositionInfo;
649 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
651 memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
652 DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
653 if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
654 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
656 pHandleInfo->ulFile = ~0U;
667 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
668 if (result == NO_ERROR) {
675 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
679 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
683 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
687 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
691 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
695 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
699 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
704 TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
705 TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
708 size = sizeof(GetDriveParameters);
710 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
712 if (result == NO_ERROR) {
713 SetDriveParameters.ECC = GetDriveParameters.ECC;
714 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
715 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
716 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
717 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
719 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
725 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
729 if (mt_com->mt_count == 0) {
730 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
732 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
737 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
738 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
740 TAPE_POSITION_INFO TapePositionInfo;
742 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
743 pHandleInfo->bBlockValid = true;
744 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
750 case (DWORD)-1: /* Error has already been translated into errno */
754 case ERROR_FILEMARK_DETECTED:
758 case ERROR_END_OF_MEDIA:
759 pHandleInfo->bEOT = true;
763 case ERROR_NO_DATA_DETECTED:
764 pHandleInfo->bEOD = true;
768 case ERROR_NO_MEDIA_IN_DRIVE:
769 pHandleInfo->bEOF = false;
770 pHandleInfo->bEOT = false;
771 pHandleInfo->bEOD = false;
775 case ERROR_INVALID_HANDLE:
776 case ERROR_ACCESS_DENIED:
777 case ERROR_LOCK_VIOLATION:
782 return result == NO_ERROR ? 0 : -1;
785 int win_tape_device::tape_get(int fd, struct mtget *mt_get)
787 TAPE_POSITION_INFO pos_info;
790 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
791 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
796 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
798 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
805 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
807 if (result != NO_ERROR) {
808 TAPE_GET_DRIVE_PARAMETERS drive_params;
811 size = sizeof(drive_params);
813 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
815 if (result == NO_ERROR) {
816 blocksize = drive_params.DefaultBlockSize;
820 mt_get->mt_type = MT_ISSCSI2;
823 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
825 // Density / Block Size
826 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
827 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
829 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
831 if (pHandleInfo->bEOF) {
832 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
835 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
836 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
839 if (pHandleInfo->bEOT) {
840 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
843 if (pHandleInfo->bEOD) {
844 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
847 TAPE_GET_MEDIA_PARAMETERS media_params;
848 DWORD size = sizeof(media_params);
850 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
852 if (result == NO_ERROR && media_params.WriteProtected) {
853 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
856 result = GetTapeStatus(pHandleInfo->OSHandle);
858 if (result != NO_ERROR) {
859 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
860 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
863 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
866 // Recovered Error Count
867 mt_get->mt_erreg = 0;
870 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
873 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
878 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
879 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
880 #define SERVICEACTION_LONG_FORM 6
881 #define SERVICEACTION_EXTENDED_FORM 8
884 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
899 UCHAR NumberBufferBlocks[3];
900 UCHAR NumberBufferBytes[4];
901 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
903 typedef struct _SCSI_READ_POSITION_LONG_BUFFER
913 UCHAR BlockNumber[8];
916 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
918 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
929 UCHAR AdditionalLength[2];
931 UCHAR NumberBufferObjects[3];
932 UCHAR FirstLogicalObject[8];
933 UCHAR LastLogicalObject[8];
934 UCHAR NumberBufferObjectBytes[8];
935 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
937 typedef union _READ_POSITION_RESULT {
938 SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
939 SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
940 SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
941 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
943 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
945 PSCSI_PASS_THROUGH ScsiPassThrough;
947 DWORD dwBytesReturned;
949 const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
951 memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
953 ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
955 for (int pass = 0; pass < 2; pass++)
957 memset(ScsiPassThrough, 0, dwBufferSize);
959 ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
961 ScsiPassThrough->CdbLength = 10;
962 ScsiPassThrough->SenseInfoLength = 28;
963 ScsiPassThrough->DataIn = 1;
964 ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
965 ScsiPassThrough->TimeOutValue = 1000;
966 ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
967 ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
969 ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
974 ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
978 ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
982 bResult = DeviceIoControl( hDevice,
983 IOCTL_SCSI_PASS_THROUGH,
984 ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
985 ScsiPassThrough, dwBufferSize,
989 if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
990 if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
991 PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
995 case 0: // SERVICEACTION_LONG_FORM
997 TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
998 TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1000 if (!TapePositionInfo->PartitionBlockValid) {
1001 TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1003 if (TapePositionInfo->PartitionBlockValid) {
1004 TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1005 TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1009 TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1010 if (TapePositionInfo->FileSetValid) {
1011 TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1012 TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1017 case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
1019 // pPosResult->ShortBuffer.PERR;
1020 // pPosResult->ShortBuffer.BYCU;
1021 // pPosResult->ShortBuffer.BCU;
1022 TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1023 TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1025 if (!TapePositionInfo->PartitionBlockValid) {
1026 TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1028 if (TapePositionInfo->PartitionBlockValid) {
1029 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
1030 TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1033 // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1034 // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1035 // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1042 free(ScsiPassThrough);
1047 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1049 DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1050 GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1054 if (pGetMediaTypes == NULL) {
1055 return ERROR_OUTOFMEMORY;
1059 DWORD dwBytesReturned;
1061 bResult = DeviceIoControl( hDevice,
1062 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1064 (LPVOID)pGetMediaTypes, dwBufferSize,
1069 dwResult = GetLastError();
1071 if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1072 free(pGetMediaTypes);
1076 dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1078 GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1080 if (pNewBuffer != pGetMediaTypes) {
1081 free(pGetMediaTypes);
1083 if (pNewBuffer == NULL) {
1084 return ERROR_OUTOFMEMORY;
1087 pGetMediaTypes = pNewBuffer;
1092 if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1093 free(pGetMediaTypes);
1094 return ERROR_BAD_DEVICE;
1097 for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1099 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1101 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1102 *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1107 *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1109 free(pGetMediaTypes);
1115 free(pGetMediaTypes);
1117 return ERROR_NO_MEDIA_IN_DRIVE;
1120 int win_tape_device::tape_pos(int fd, struct mtpos *mt_pos)
1127 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1128 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1133 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1135 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1136 if (result == NO_ERROR) {
1137 mt_pos->mt_blkno = offset;