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 */
53 // SCSI bus status codes.
56 #define SCSISTAT_GOOD 0x00
57 #define SCSISTAT_CHECK_CONDITION 0x02
58 #define SCSISTAT_CONDITION_MET 0x04
59 #define SCSISTAT_BUSY 0x08
60 #define SCSISTAT_INTERMEDIATE 0x10
61 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
62 #define SCSISTAT_RESERVATION_CONFLICT 0x18
63 #define SCSISTAT_COMMAND_TERMINATED 0x22
64 #define SCSISTAT_QUEUE_FULL 0x28
66 inline SHORT Read16BitSigned(const unsigned char *pValue)
68 return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
71 inline USHORT Read16BitUnsigned(const unsigned char *pValue)
73 return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
76 inline LONG Read24BitSigned(const unsigned char *pValue)
78 return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
79 (ULONG)pValue[2])) << 8 >> 8;
82 inline ULONG Read24BitUnsigned(const unsigned char *pValue)
84 return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
87 inline LONG Read32BitSigned(const unsigned char *pValue)
89 return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
90 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
93 inline ULONG Read32BitUnsigned(const unsigned char *pValue)
95 return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
96 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
99 inline LONGLONG Read64BitSigned(const unsigned char *pValue)
101 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
102 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
103 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
104 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
107 inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
109 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
110 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
111 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
112 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
115 typedef struct _TAPE_POSITION_INFO
117 UCHAR AtPartitionStart:1;
118 UCHAR AtPartitionEnd:1;
119 UCHAR PartitionBlockValid:1;
120 UCHAR FileSetValid:1;
124 ULONGLONG BlockNumber;
125 ULONGLONG FileNumber;
127 } TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
129 typedef struct _TAPE_HANDLE_INFO
139 ULONGLONG ullFileStart;
141 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
143 TAPE_HANDLE_INFO TapeHandleTable[] =
145 { INVALID_HANDLE_VALUE },
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 }
163 #define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
165 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
166 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
168 int tape_get(int fd, struct mtget *mt_get);
169 int tape_op(int fd, struct mtop *mt_com);
170 int tape_pos(int fd, struct mtpos *mt_pos);
173 tape_open(const char *file, int flags, int mode)
175 HANDLE hDevice = INVALID_HANDLE_VALUE;
176 char szDeviceName[256] = "\\\\.\\";
180 for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
181 if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
186 if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
190 memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
192 if (!IsPathSeparator(file[0])) {
193 bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
195 bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
198 hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
200 if (hDevice != INVALID_HANDLE_VALUE) {
201 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
203 memset(pHandleInfo, 0, sizeof(*pHandleInfo));
205 pHandleInfo->OSHandle = hDevice;
207 TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
208 DWORD dwSize = sizeof(TapeDriveParameters);
210 dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
211 if (dwResult == NO_ERROR) {
212 pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
213 pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
216 TAPE_POSITION_INFO TapePositionInfo;
218 dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
220 if (dwResult == NO_ERROR) {
221 if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
222 (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
223 pHandleInfo->ulFile = 0;
224 pHandleInfo->bBlockValid = true;
225 pHandleInfo->ullFileStart = 0;
226 } else if (TapePositionInfo.FileSetValid) {
227 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
231 DWORD dwError = GetLastError();
234 case ERROR_FILE_NOT_FOUND:
235 case ERROR_PATH_NOT_FOUND:
239 case ERROR_TOO_MANY_OPEN_FILES:
244 case ERROR_ACCESS_DENIED:
245 case ERROR_SHARING_VIOLATION:
246 case ERROR_LOCK_VIOLATION:
247 case ERROR_INVALID_NAME:
251 case ERROR_FILE_EXISTS:
255 case ERROR_INVALID_PARAMETER:
263 return (int)idxFile + 3;
267 tape_read(int fd, void *buffer, unsigned int count)
269 if (buffer == NULL) {
274 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
275 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
281 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
286 bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
289 pHandleInfo->bEOF = false;
290 pHandleInfo->bEOT = false;
291 pHandleInfo->bEOD = false;
294 int iReturnValue = 0;
295 DWORD last_error = GetLastError();
297 switch (last_error) {
299 case ERROR_FILEMARK_DETECTED:
300 pHandleInfo->bEOF = true;
303 case ERROR_END_OF_MEDIA:
304 pHandleInfo->bEOT = true;
307 case ERROR_NO_MEDIA_IN_DRIVE:
308 pHandleInfo->bEOF = false;
309 pHandleInfo->bEOT = false;
310 pHandleInfo->bEOD = false;
315 case ERROR_NO_DATA_DETECTED:
316 pHandleInfo->bEOD = true;
319 case ERROR_INVALID_HANDLE:
320 case ERROR_ACCESS_DENIED:
321 case ERROR_LOCK_VIOLATION:
327 pHandleInfo->bEOF = false;
328 pHandleInfo->bEOT = false;
329 pHandleInfo->bEOD = false;
339 tape_write(int fd, const void *buffer, unsigned int count)
341 if (buffer == NULL) {
346 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
352 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
357 bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
360 pHandleInfo->bEOF = false;
361 pHandleInfo->bEOT = false;
362 return bytes_written;
364 DWORD last_error = GetLastError();
366 switch (last_error) {
367 case ERROR_END_OF_MEDIA:
368 case ERROR_DISK_FULL:
369 pHandleInfo->bEOT = true;
373 case ERROR_NO_MEDIA_IN_DRIVE:
374 pHandleInfo->bEOF = false;
375 pHandleInfo->bEOT = false;
376 pHandleInfo->bEOD = false;
380 case ERROR_INVALID_HANDLE:
381 case ERROR_ACCESS_DENIED:
386 pHandleInfo->bEOF = false;
387 pHandleInfo->bEOT = false;
388 pHandleInfo->bEOD = false;
399 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
400 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
405 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
407 if (!CloseHandle(pHandleInfo->OSHandle)) {
408 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
413 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
419 tape_ioctl(int fd, unsigned long int request, ...)
424 va_start(argp, request);
428 result = tape_op(fd, va_arg(argp, mtop *));
432 result = tape_get(fd, va_arg(argp, mtget *));
436 result = tape_pos(fd, va_arg(argp, mtpos *));
450 int tape_op(int fd, struct mtop *mt_com)
452 DWORD result = NO_ERROR;
455 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
456 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
462 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
464 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)
714 SetDriveParameters.ECC = GetDriveParameters.ECC;
715 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
716 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
717 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
718 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
720 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
726 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
730 if (mt_com->mt_count == 0)
732 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
736 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
741 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
742 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
744 TAPE_POSITION_INFO TapePositionInfo;
746 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
747 pHandleInfo->bBlockValid = true;
748 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
754 case (DWORD)-1: /* Error has already been translated into errno */
758 case ERROR_FILEMARK_DETECTED:
762 case ERROR_END_OF_MEDIA:
763 pHandleInfo->bEOT = true;
767 case ERROR_NO_DATA_DETECTED:
768 pHandleInfo->bEOD = true;
772 case ERROR_NO_MEDIA_IN_DRIVE:
773 pHandleInfo->bEOF = false;
774 pHandleInfo->bEOT = false;
775 pHandleInfo->bEOD = false;
779 case ERROR_INVALID_HANDLE:
780 case ERROR_ACCESS_DENIED:
781 case ERROR_LOCK_VIOLATION:
786 return result == NO_ERROR ? 0 : -1;
789 int tape_get(int fd, struct mtget *mt_get)
791 TAPE_POSITION_INFO pos_info;
794 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
795 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
800 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
802 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
809 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
811 if (result != NO_ERROR) {
812 TAPE_GET_DRIVE_PARAMETERS drive_params;
815 size = sizeof(drive_params);
817 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
819 if (result == NO_ERROR) {
820 blocksize = drive_params.DefaultBlockSize;
824 mt_get->mt_type = MT_ISSCSI2;
827 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
829 // Density / Block Size
830 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
831 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
833 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
835 if (pHandleInfo->bEOF) {
836 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
839 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
840 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
843 if (pHandleInfo->bEOT) {
844 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
847 if (pHandleInfo->bEOD) {
848 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
851 TAPE_GET_MEDIA_PARAMETERS media_params;
852 DWORD size = sizeof(media_params);
854 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
856 if (result == NO_ERROR && media_params.WriteProtected) {
857 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
860 result = GetTapeStatus(pHandleInfo->OSHandle);
862 if (result != NO_ERROR) {
863 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
864 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
867 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
870 // Recovered Error Count
871 mt_get->mt_erreg = 0;
874 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
877 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
882 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
883 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
884 #define SERVICEACTION_LONG_FORM 6
885 #define SERVICEACTION_EXTENDED_FORM 8
888 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
903 UCHAR NumberBufferBlocks[3];
904 UCHAR NumberBufferBytes[4];
905 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
907 typedef struct _SCSI_READ_POSITION_LONG_BUFFER
917 UCHAR BlockNumber[8];
920 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
922 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
933 UCHAR AdditionalLength[2];
935 UCHAR NumberBufferObjects[3];
936 UCHAR FirstLogicalObject[8];
937 UCHAR LastLogicalObject[8];
938 UCHAR NumberBufferObjectBytes[8];
939 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
941 typedef union _READ_POSITION_RESULT {
942 SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
943 SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
944 SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
945 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
947 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
949 PSCSI_PASS_THROUGH ScsiPassThrough;
951 DWORD dwBytesReturned;
953 const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
955 memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
957 ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
959 for (int pass = 0; pass < 2; pass++)
961 memset(ScsiPassThrough, 0, dwBufferSize);
963 ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
965 ScsiPassThrough->CdbLength = 10;
966 ScsiPassThrough->SenseInfoLength = 28;
967 ScsiPassThrough->DataIn = 1;
968 ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
969 ScsiPassThrough->TimeOutValue = 1000;
970 ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
971 ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
973 ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
978 ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
982 ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
986 bResult = DeviceIoControl( hDevice,
987 IOCTL_SCSI_PASS_THROUGH,
988 ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
989 ScsiPassThrough, dwBufferSize,
993 if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
994 if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
995 PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
999 case 0: // SERVICEACTION_LONG_FORM
1001 TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1002 TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1004 if (!TapePositionInfo->PartitionBlockValid) {
1005 TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1007 if (TapePositionInfo->PartitionBlockValid) {
1008 TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1009 TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1013 TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1014 if (TapePositionInfo->FileSetValid) {
1015 TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1016 TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1021 case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
1023 // pPosResult->ShortBuffer.PERR;
1024 // pPosResult->ShortBuffer.BYCU;
1025 // pPosResult->ShortBuffer.BCU;
1026 TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1027 TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1029 if (!TapePositionInfo->PartitionBlockValid) {
1030 TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1032 if (TapePositionInfo->PartitionBlockValid) {
1033 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
1034 TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1037 // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1038 // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1039 // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1046 free(ScsiPassThrough);
1051 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1053 DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1054 GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1058 if (pGetMediaTypes == NULL) {
1059 return ERROR_OUTOFMEMORY;
1063 DWORD dwBytesReturned;
1065 bResult = DeviceIoControl( hDevice,
1066 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1068 (LPVOID)pGetMediaTypes, dwBufferSize,
1073 dwResult = GetLastError();
1075 if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1076 free(pGetMediaTypes);
1080 dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1082 GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1084 if (pNewBuffer != pGetMediaTypes) {
1085 free(pGetMediaTypes);
1087 if (pNewBuffer == NULL) {
1088 return ERROR_OUTOFMEMORY;
1091 pGetMediaTypes = pNewBuffer;
1096 if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1097 free(pGetMediaTypes);
1098 return ERROR_BAD_DEVICE;
1101 for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1103 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1105 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1106 *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1111 *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1113 free(pGetMediaTypes);
1119 free(pGetMediaTypes);
1121 return ERROR_NO_MEDIA_IN_DRIVE;
1124 int tape_pos(int fd, struct mtpos *mt_pos)
1131 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1132 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1137 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1139 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1140 if (result == NO_ERROR) {
1141 mt_pos->mt_blkno = offset;