2 * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
4 * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
8 * This file was contributed to the Bacula project by Robert Nelson.
10 * Robert Nelson has been granted a perpetual, worldwide,
11 * non-exclusive, no-charge, royalty-free, irrevocable copyright
12 * license to reproduce, prepare derivative works of, publicly
13 * display, publicly perform, sublicense, and distribute the original
14 * work contributed by Robert Nelson to the Bacula project in source
17 * If you wish to license contributions from Robert Nelson
18 * under an alternate open source license please contact
19 * Robert Nelson <robertn@the-nelsons.org>.
22 Bacula® - The Network Backup Solution
24 Copyright (C) 2006-2006 Free Software Foundation Europe e.V.
26 The main author of Bacula is Kern Sibbald, with contributions from
27 many others, a complete list can be found in the file AUTHORS.
28 This program is Free Software; you can redistribute it and/or
29 modify it under the terms of version two of the GNU General Public
30 License as published by the Free Software Foundation plus additions
31 that are listed in the file LICENSE.
33 This program is distributed in the hope that it will be useful, but
34 WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 General Public License for more details.
38 You should have received a copy of the GNU General Public License
39 along with this program; if not, write to the Free Software
40 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
43 Bacula® is a registered trademark of John Walker.
44 The licensor of Bacula is the Free Software Foundation Europe
45 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
46 Switzerland, email:ftf@fsfeurope.org.
52 #include "bacula.h" /* pull in global headers */
53 #include "stored.h" /* pull in Storage Deamon headers */
64 // SCSI bus status codes.
67 #define SCSISTAT_GOOD 0x00
68 #define SCSISTAT_CHECK_CONDITION 0x02
69 #define SCSISTAT_CONDITION_MET 0x04
70 #define SCSISTAT_BUSY 0x08
71 #define SCSISTAT_INTERMEDIATE 0x10
72 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
73 #define SCSISTAT_RESERVATION_CONFLICT 0x18
74 #define SCSISTAT_COMMAND_TERMINATED 0x22
75 #define SCSISTAT_QUEUE_FULL 0x28
77 inline SHORT Read16BitSigned(const unsigned char *pValue)
79 return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
82 inline USHORT Read16BitUnsigned(const unsigned char *pValue)
84 return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
87 inline LONG Read24BitSigned(const unsigned char *pValue)
89 return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
90 (ULONG)pValue[2])) << 8 >> 8;
93 inline ULONG Read24BitUnsigned(const unsigned char *pValue)
95 return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
98 inline LONG Read32BitSigned(const unsigned char *pValue)
100 return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
101 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
104 inline ULONG Read32BitUnsigned(const unsigned char *pValue)
106 return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
107 ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
110 inline LONGLONG Read64BitSigned(const unsigned char *pValue)
112 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
113 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
114 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
115 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
118 inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
120 return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
121 ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
122 ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
123 ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
126 typedef struct _TAPE_POSITION_INFO
128 UCHAR AtPartitionStart:1;
129 UCHAR AtPartitionEnd:1;
130 UCHAR PartitionBlockValid:1;
131 UCHAR FileSetValid:1;
135 ULONGLONG BlockNumber;
136 ULONGLONG FileNumber;
138 } TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
140 typedef struct _TAPE_HANDLE_INFO
150 ULONGLONG ullFileStart;
152 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
154 TAPE_HANDLE_INFO TapeHandleTable[] =
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 },
163 { INVALID_HANDLE_VALUE },
164 { INVALID_HANDLE_VALUE },
165 { INVALID_HANDLE_VALUE },
166 { INVALID_HANDLE_VALUE },
167 { INVALID_HANDLE_VALUE },
168 { INVALID_HANDLE_VALUE },
169 { INVALID_HANDLE_VALUE },
170 { INVALID_HANDLE_VALUE },
171 { INVALID_HANDLE_VALUE }
174 #define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
176 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
177 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
179 int tape_get(int fd, struct mtget *mt_get);
180 int tape_op(int fd, struct mtop *mt_com);
181 int tape_pos(int fd, struct mtpos *mt_pos);
184 tape_open(const char *file, int flags, int mode)
186 HANDLE hDevice = INVALID_HANDLE_VALUE;
187 char szDeviceName[256] = "\\\\.\\";
191 for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
192 if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
197 if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
201 memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
203 if (!IsPathSeparator(file[0])) {
204 bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
206 bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
209 hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
211 if (hDevice != INVALID_HANDLE_VALUE) {
212 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
214 memset(pHandleInfo, 0, sizeof(*pHandleInfo));
216 pHandleInfo->OSHandle = hDevice;
218 TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
219 DWORD dwSize = sizeof(TapeDriveParameters);
221 dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
222 if (dwResult == NO_ERROR) {
223 pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
224 pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
227 TAPE_POSITION_INFO TapePositionInfo;
229 dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
231 if (dwResult == NO_ERROR) {
232 if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
233 (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
234 pHandleInfo->ulFile = 0;
235 pHandleInfo->bBlockValid = true;
236 pHandleInfo->ullFileStart = 0;
237 } else if (TapePositionInfo.FileSetValid) {
238 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
242 DWORD dwError = GetLastError();
245 case ERROR_FILE_NOT_FOUND:
246 case ERROR_PATH_NOT_FOUND:
250 case ERROR_TOO_MANY_OPEN_FILES:
255 case ERROR_ACCESS_DENIED:
256 case ERROR_SHARING_VIOLATION:
257 case ERROR_LOCK_VIOLATION:
258 case ERROR_INVALID_NAME:
262 case ERROR_FILE_EXISTS:
266 case ERROR_INVALID_PARAMETER:
274 return (int)idxFile + 3;
278 tape_read(int fd, void *buffer, unsigned int count)
280 if (buffer == NULL) {
285 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
286 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
292 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
297 bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
300 pHandleInfo->bEOF = false;
301 pHandleInfo->bEOT = false;
302 pHandleInfo->bEOD = false;
305 int iReturnValue = 0;
306 DWORD last_error = GetLastError();
308 switch (last_error) {
310 case ERROR_FILEMARK_DETECTED:
311 pHandleInfo->bEOF = true;
314 case ERROR_END_OF_MEDIA:
315 pHandleInfo->bEOT = true;
318 case ERROR_NO_MEDIA_IN_DRIVE:
319 pHandleInfo->bEOF = false;
320 pHandleInfo->bEOT = false;
321 pHandleInfo->bEOD = false;
326 case ERROR_NO_DATA_DETECTED:
327 pHandleInfo->bEOD = true;
330 case ERROR_INVALID_HANDLE:
331 case ERROR_ACCESS_DENIED:
332 case ERROR_LOCK_VIOLATION:
338 pHandleInfo->bEOF = false;
339 pHandleInfo->bEOT = false;
340 pHandleInfo->bEOD = false;
350 tape_write(int fd, const void *buffer, unsigned int count)
352 if (buffer == NULL) {
357 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
363 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
368 bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
371 pHandleInfo->bEOF = false;
372 pHandleInfo->bEOT = false;
373 return bytes_written;
375 DWORD last_error = GetLastError();
377 switch (last_error) {
378 case ERROR_END_OF_MEDIA:
379 case ERROR_DISK_FULL:
380 pHandleInfo->bEOT = true;
384 case ERROR_NO_MEDIA_IN_DRIVE:
385 pHandleInfo->bEOF = false;
386 pHandleInfo->bEOT = false;
387 pHandleInfo->bEOD = false;
391 case ERROR_INVALID_HANDLE:
392 case ERROR_ACCESS_DENIED:
397 pHandleInfo->bEOF = false;
398 pHandleInfo->bEOT = false;
399 pHandleInfo->bEOD = false;
410 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
411 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
416 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
418 if (!CloseHandle(pHandleInfo->OSHandle)) {
419 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
424 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
430 tape_ioctl(int fd, unsigned long int request, ...)
435 va_start(argp, request);
439 result = tape_op(fd, va_arg(argp, mtop *));
443 result = tape_get(fd, va_arg(argp, mtget *));
447 result = tape_pos(fd, va_arg(argp, mtpos *));
461 int tape_op(int fd, struct mtop *mt_com)
463 DWORD result = NO_ERROR;
466 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
467 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
473 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
475 switch (mt_com->mt_op)
492 for (index = 0; index < mt_com->mt_count; index++) {
493 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
494 if (result == NO_ERROR) {
495 pHandleInfo->ulFile++;
496 pHandleInfo->bEOF = true;
497 pHandleInfo->bEOT = false;
503 for (index = 0; index < mt_com->mt_count; index++) {
504 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
505 if (result == NO_ERROR) {
506 pHandleInfo->ulFile--;
507 pHandleInfo->bBlockValid = false;
508 pHandleInfo->bEOD = false;
509 pHandleInfo->bEOF = false;
510 pHandleInfo->bEOT = false;
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->bEOF = true;
527 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
528 if (result == NO_ERROR) {
529 pHandleInfo->bEOD = false;
530 pHandleInfo->bEOF = false;
531 pHandleInfo->bEOT = false;
532 } else if (result == ERROR_FILEMARK_DETECTED) {
533 pHandleInfo->ulFile--;
534 pHandleInfo->bBlockValid = false;
535 pHandleInfo->bEOD = false;
536 pHandleInfo->bEOF = false;
537 pHandleInfo->bEOT = false;
542 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
543 if (result == NO_ERROR) {
544 pHandleInfo->bEOF = true;
545 pHandleInfo->bEOT = false;
546 pHandleInfo->ulFile += mt_com->mt_count;
547 pHandleInfo->bBlockValid = true;
548 pHandleInfo->ullFileStart = 0;
553 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
554 if (result == NO_ERROR) {
555 pHandleInfo->bEOD = false;
556 pHandleInfo->bEOF = false;
557 pHandleInfo->bEOT = false;
558 pHandleInfo->ulFile = 0;
559 pHandleInfo->bBlockValid = true;
560 pHandleInfo->ullFileStart = 0;
565 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
566 if (result == NO_ERROR) {
567 pHandleInfo->bEOD = false;
568 pHandleInfo->bEOF = false;
569 pHandleInfo->bEOT = false;
570 pHandleInfo->ulFile = 0;
571 pHandleInfo->ullFileStart = 0;
576 result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
577 if (result == NO_ERROR) {
578 pHandleInfo->bEOD = false;
579 pHandleInfo->bEOF = false;
580 pHandleInfo->bEOT = false;
581 pHandleInfo->ulFile = 0;
582 pHandleInfo->bBlockValid = true;
583 pHandleInfo->ullFileStart = 0;
588 for (index = 0; index < mt_com->mt_count; index++) {
589 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
590 if (result == NO_ERROR) {
591 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
592 pHandleInfo->bEOD = false;
593 pHandleInfo->bEOF = false;
594 pHandleInfo->bEOT = false;
600 for (index = 0; index < mt_com->mt_count; index++) {
601 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
602 if (result == NO_ERROR) {
603 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
604 pHandleInfo->bEOD = false;
605 pHandleInfo->bEOF = false;
606 pHandleInfo->bEOT = false;
613 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
614 if (result != NO_ERROR) {
615 pHandleInfo->bEOF = false;
617 if (result == ERROR_END_OF_MEDIA) {
618 pHandleInfo->bEOD = true;
619 pHandleInfo->bEOT = true;
622 if (result == ERROR_NO_DATA_DETECTED) {
623 pHandleInfo->bEOD = true;
624 pHandleInfo->bEOT = false;
629 pHandleInfo->bEOF = true;
630 pHandleInfo->ulFile++;
636 result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
637 if (result == NO_ERROR) {
638 pHandleInfo->bEOD = true;
639 pHandleInfo->bEOF = false;
640 pHandleInfo->bEOT = false;
641 pHandleInfo->ulFile = 0;
642 pHandleInfo->bBlockValid = true;
643 pHandleInfo->ullFileStart = 0;
649 TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
651 SetMediaParameters.BlockSize = mt_com->mt_count;
652 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
658 TAPE_POSITION_INFO TapePositionInfo;
660 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
662 memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
663 DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
664 if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
665 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
667 pHandleInfo->ulFile = ~0U;
678 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
679 if (result == NO_ERROR) {
686 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
690 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
694 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
698 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
702 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
706 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
710 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
715 TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
716 TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
719 size = sizeof(GetDriveParameters);
721 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
723 if (result == NO_ERROR)
725 SetDriveParameters.ECC = GetDriveParameters.ECC;
726 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
727 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
728 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
729 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
731 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
737 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
741 if (mt_com->mt_count == 0)
743 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
747 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
752 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
753 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
755 TAPE_POSITION_INFO TapePositionInfo;
757 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
758 pHandleInfo->bBlockValid = true;
759 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
765 case (DWORD)-1: /* Error has already been translated into errno */
769 case ERROR_FILEMARK_DETECTED:
773 case ERROR_END_OF_MEDIA:
774 pHandleInfo->bEOT = true;
778 case ERROR_NO_DATA_DETECTED:
779 pHandleInfo->bEOD = true;
783 case ERROR_NO_MEDIA_IN_DRIVE:
784 pHandleInfo->bEOF = false;
785 pHandleInfo->bEOT = false;
786 pHandleInfo->bEOD = false;
790 case ERROR_INVALID_HANDLE:
791 case ERROR_ACCESS_DENIED:
792 case ERROR_LOCK_VIOLATION:
797 return result == NO_ERROR ? 0 : -1;
800 int tape_get(int fd, struct mtget *mt_get)
802 TAPE_POSITION_INFO pos_info;
805 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
806 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
811 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
813 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
820 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
822 if (result != NO_ERROR) {
823 TAPE_GET_DRIVE_PARAMETERS drive_params;
826 size = sizeof(drive_params);
828 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
830 if (result == NO_ERROR) {
831 blocksize = drive_params.DefaultBlockSize;
835 mt_get->mt_type = MT_ISSCSI2;
838 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
840 // Density / Block Size
841 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
842 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
844 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
846 if (pHandleInfo->bEOF) {
847 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
850 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
851 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
854 if (pHandleInfo->bEOT) {
855 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
858 if (pHandleInfo->bEOD) {
859 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
862 TAPE_GET_MEDIA_PARAMETERS media_params;
863 DWORD size = sizeof(media_params);
865 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
867 if (result == NO_ERROR && media_params.WriteProtected) {
868 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
871 result = GetTapeStatus(pHandleInfo->OSHandle);
873 if (result != NO_ERROR) {
874 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
875 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
878 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
881 // Recovered Error Count
882 mt_get->mt_erreg = 0;
885 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
888 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
893 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
894 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
895 #define SERVICEACTION_LONG_FORM 6
896 #define SERVICEACTION_EXTENDED_FORM 8
899 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
914 UCHAR NumberBufferBlocks[3];
915 UCHAR NumberBufferBytes[4];
916 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
918 typedef struct _SCSI_READ_POSITION_LONG_BUFFER
928 UCHAR BlockNumber[8];
931 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
933 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
944 UCHAR AdditionalLength[2];
946 UCHAR NumberBufferObjects[3];
947 UCHAR FirstLogicalObject[8];
948 UCHAR LastLogicalObject[8];
949 UCHAR NumberBufferObjectBytes[8];
950 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
952 typedef union _READ_POSITION_RESULT {
953 SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
954 SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
955 SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
956 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
958 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
960 PSCSI_PASS_THROUGH ScsiPassThrough;
962 DWORD dwBytesReturned;
964 const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
966 memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
968 ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
970 for (int pass = 0; pass < 2; pass++)
972 memset(ScsiPassThrough, 0, dwBufferSize);
974 ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
976 ScsiPassThrough->CdbLength = 10;
977 ScsiPassThrough->SenseInfoLength = 28;
978 ScsiPassThrough->DataIn = 1;
979 ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
980 ScsiPassThrough->TimeOutValue = 1000;
981 ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
982 ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
984 ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
989 ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
993 ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
997 bResult = DeviceIoControl( hDevice,
998 IOCTL_SCSI_PASS_THROUGH,
999 ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
1000 ScsiPassThrough, dwBufferSize,
1004 if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
1005 if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
1006 PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
1010 case 0: // SERVICEACTION_LONG_FORM
1012 TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1013 TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1015 if (!TapePositionInfo->PartitionBlockValid) {
1016 TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1018 if (TapePositionInfo->PartitionBlockValid) {
1019 TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1020 TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1024 TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1025 if (TapePositionInfo->FileSetValid) {
1026 TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1027 TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1032 case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
1034 // pPosResult->ShortBuffer.PERR;
1035 // pPosResult->ShortBuffer.BYCU;
1036 // pPosResult->ShortBuffer.BCU;
1037 TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1038 TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1040 if (!TapePositionInfo->PartitionBlockValid) {
1041 TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1043 if (TapePositionInfo->PartitionBlockValid) {
1044 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
1045 TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1048 // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1049 // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1050 // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1057 free(ScsiPassThrough);
1062 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1064 DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1065 GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1069 if (pGetMediaTypes == NULL) {
1070 return ERROR_OUTOFMEMORY;
1074 DWORD dwBytesReturned;
1076 bResult = DeviceIoControl( hDevice,
1077 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1079 (LPVOID)pGetMediaTypes, dwBufferSize,
1084 dwResult = GetLastError();
1086 if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1087 free(pGetMediaTypes);
1091 dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1093 GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1095 if (pNewBuffer != pGetMediaTypes) {
1096 free(pGetMediaTypes);
1098 if (pNewBuffer == NULL) {
1099 return ERROR_OUTOFMEMORY;
1102 pGetMediaTypes = pNewBuffer;
1107 if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1108 free(pGetMediaTypes);
1109 return ERROR_BAD_DEVICE;
1112 for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1114 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1116 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1117 *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1122 *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1124 free(pGetMediaTypes);
1130 free(pGetMediaTypes);
1132 return ERROR_NO_MEDIA_IN_DRIVE;
1135 int tape_pos(int fd, struct mtpos *mt_pos)
1142 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1143 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1148 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1150 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1151 if (result == NO_ERROR) {
1152 mt_pos->mt_blkno = offset;