2 Bacula® - The Network Backup Solution
4 Copyright (C) 2006-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
31 * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
35 * This file was contributed to the Bacula project by Robert Nelson.
37 * Robert Nelson has been granted a perpetual, worldwide,
38 * non-exclusive, no-charge, royalty-free, irrevocable copyright
39 * license to reproduce, prepare derivative works of, publicly
40 * display, publicly perform, sublicense, and distribute the original
41 * work contributed by Robert Nelson to the Bacula project in source
44 * If you wish to license contributions from Robert Nelson
45 * under an alternate open source license please contact
46 * Robert Nelson <robertn@the-nelsons.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 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
177 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
179 static int tape_get(int fd, struct mtget *mt_get);
180 static int tape_op(int fd, struct mtop *mt_com);
181 static int tape_pos(int fd, struct mtpos *mt_pos);
184 win32_tape_open(const char *file, int flags, ...)
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 win32_read(int fd, void *buffer, size_t count)
280 return read(fd, buffer, count);
284 win32_write(int fd, const void *buffer, size_t count)
286 return write(fd, buffer, count);
290 ioctl(int d, unsigned long int req, ...)
296 win32_tape_read(int fd, void *buffer, size_t count)
298 if (buffer == NULL) {
303 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
304 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
310 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
315 bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
318 pHandleInfo->bEOF = false;
319 pHandleInfo->bEOT = false;
320 pHandleInfo->bEOD = false;
323 int iReturnValue = 0;
324 DWORD last_error = GetLastError();
326 switch (last_error) {
328 case ERROR_FILEMARK_DETECTED:
329 pHandleInfo->bEOF = true;
332 case ERROR_END_OF_MEDIA:
333 pHandleInfo->bEOT = true;
336 case ERROR_NO_MEDIA_IN_DRIVE:
337 pHandleInfo->bEOF = false;
338 pHandleInfo->bEOT = false;
339 pHandleInfo->bEOD = false;
344 case ERROR_NO_DATA_DETECTED:
345 pHandleInfo->bEOD = true;
348 case ERROR_INVALID_HANDLE:
349 case ERROR_ACCESS_DENIED:
350 case ERROR_LOCK_VIOLATION:
356 pHandleInfo->bEOF = false;
357 pHandleInfo->bEOT = false;
358 pHandleInfo->bEOD = false;
368 win32_tape_write(int fd, const void *buffer, size_t count)
370 if (buffer == NULL) {
375 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
381 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
386 bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
389 pHandleInfo->bEOF = false;
390 pHandleInfo->bEOT = false;
391 return bytes_written;
393 DWORD last_error = GetLastError();
395 switch (last_error) {
396 case ERROR_END_OF_MEDIA:
397 case ERROR_DISK_FULL:
398 pHandleInfo->bEOT = true;
402 case ERROR_NO_MEDIA_IN_DRIVE:
403 pHandleInfo->bEOF = false;
404 pHandleInfo->bEOT = false;
405 pHandleInfo->bEOD = false;
409 case ERROR_INVALID_HANDLE:
410 case ERROR_ACCESS_DENIED:
415 pHandleInfo->bEOF = false;
416 pHandleInfo->bEOT = false;
417 pHandleInfo->bEOD = false;
426 win32_tape_close(int fd)
428 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
429 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
434 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
436 if (!CloseHandle(pHandleInfo->OSHandle)) {
437 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
442 pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
448 win32_tape_ioctl(int fd, unsigned long int request, ...)
453 va_start(argp, request);
457 result = tape_op(fd, va_arg(argp, mtop *));
461 result = tape_get(fd, va_arg(argp, mtget *));
465 result = tape_pos(fd, va_arg(argp, mtpos *));
479 static int tape_op(int fd, struct mtop *mt_com)
481 DWORD result = NO_ERROR;
484 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
485 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
491 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
493 switch (mt_com->mt_op)
510 for (index = 0; index < mt_com->mt_count; index++) {
511 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
512 if (result == NO_ERROR) {
513 pHandleInfo->ulFile++;
514 pHandleInfo->bEOF = true;
515 pHandleInfo->bEOT = false;
521 for (index = 0; index < mt_com->mt_count; index++) {
522 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
523 if (result == NO_ERROR) {
524 pHandleInfo->ulFile--;
525 pHandleInfo->bBlockValid = false;
526 pHandleInfo->bEOD = false;
527 pHandleInfo->bEOF = false;
528 pHandleInfo->bEOT = false;
534 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE);
535 if (result == NO_ERROR) {
536 pHandleInfo->bEOD = false;
537 pHandleInfo->bEOF = false;
538 pHandleInfo->bEOT = false;
539 } else if (result == ERROR_FILEMARK_DETECTED) {
540 pHandleInfo->bEOF = true;
545 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
546 if (result == NO_ERROR) {
547 pHandleInfo->bEOD = false;
548 pHandleInfo->bEOF = false;
549 pHandleInfo->bEOT = false;
550 } else if (result == ERROR_FILEMARK_DETECTED) {
551 pHandleInfo->ulFile--;
552 pHandleInfo->bBlockValid = false;
553 pHandleInfo->bEOD = false;
554 pHandleInfo->bEOF = false;
555 pHandleInfo->bEOT = false;
560 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
561 if (result == NO_ERROR) {
562 pHandleInfo->bEOF = true;
563 pHandleInfo->bEOT = false;
564 pHandleInfo->ulFile += mt_com->mt_count;
565 pHandleInfo->bBlockValid = true;
566 pHandleInfo->ullFileStart = 0;
571 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
572 if (result == NO_ERROR) {
573 pHandleInfo->bEOD = false;
574 pHandleInfo->bEOF = false;
575 pHandleInfo->bEOT = false;
576 pHandleInfo->ulFile = 0;
577 pHandleInfo->bBlockValid = true;
578 pHandleInfo->ullFileStart = 0;
583 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
584 if (result == NO_ERROR) {
585 pHandleInfo->bEOD = false;
586 pHandleInfo->bEOF = false;
587 pHandleInfo->bEOT = false;
588 pHandleInfo->ulFile = 0;
589 pHandleInfo->ullFileStart = 0;
594 result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
595 if (result == NO_ERROR) {
596 pHandleInfo->bEOD = false;
597 pHandleInfo->bEOF = false;
598 pHandleInfo->bEOT = false;
599 pHandleInfo->ulFile = 0;
600 pHandleInfo->bBlockValid = true;
601 pHandleInfo->ullFileStart = 0;
606 for (index = 0; index < mt_com->mt_count; index++) {
607 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
608 if (result == NO_ERROR) {
609 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
610 pHandleInfo->bEOD = false;
611 pHandleInfo->bEOF = false;
612 pHandleInfo->bEOT = false;
618 for (index = 0; index < mt_com->mt_count; index++) {
619 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
620 if (result == NO_ERROR) {
621 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
622 pHandleInfo->bEOD = false;
623 pHandleInfo->bEOF = false;
624 pHandleInfo->bEOT = false;
631 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
632 if (result != NO_ERROR) {
633 pHandleInfo->bEOF = false;
635 if (result == ERROR_END_OF_MEDIA) {
636 pHandleInfo->bEOD = true;
637 pHandleInfo->bEOT = true;
640 if (result == ERROR_NO_DATA_DETECTED) {
641 pHandleInfo->bEOD = true;
642 pHandleInfo->bEOT = false;
647 pHandleInfo->bEOF = true;
648 pHandleInfo->ulFile++;
654 result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
655 if (result == NO_ERROR) {
656 pHandleInfo->bEOD = true;
657 pHandleInfo->bEOF = false;
658 pHandleInfo->bEOT = false;
659 pHandleInfo->ulFile = 0;
660 pHandleInfo->bBlockValid = true;
661 pHandleInfo->ullFileStart = 0;
667 TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
669 SetMediaParameters.BlockSize = mt_com->mt_count;
670 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
676 TAPE_POSITION_INFO TapePositionInfo;
678 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
680 memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
681 DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
682 if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
683 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
685 pHandleInfo->ulFile = ~0U;
696 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
697 if (result == NO_ERROR) {
704 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
708 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
712 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
716 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
720 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
724 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
728 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
733 TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
734 TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
737 size = sizeof(GetDriveParameters);
739 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
741 if (result == NO_ERROR)
743 SetDriveParameters.ECC = GetDriveParameters.ECC;
744 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
745 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
746 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
747 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
749 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
755 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
759 if (mt_com->mt_count == 0)
761 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
765 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
770 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
771 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
773 TAPE_POSITION_INFO TapePositionInfo;
775 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
776 pHandleInfo->bBlockValid = true;
777 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
783 case (DWORD)-1: /* Error has already been translated into errno */
787 case ERROR_FILEMARK_DETECTED:
791 case ERROR_END_OF_MEDIA:
792 pHandleInfo->bEOT = true;
796 case ERROR_NO_DATA_DETECTED:
797 pHandleInfo->bEOD = true;
801 case ERROR_NO_MEDIA_IN_DRIVE:
802 pHandleInfo->bEOF = false;
803 pHandleInfo->bEOT = false;
804 pHandleInfo->bEOD = false;
808 case ERROR_INVALID_HANDLE:
809 case ERROR_ACCESS_DENIED:
810 case ERROR_LOCK_VIOLATION:
815 return result == NO_ERROR ? 0 : -1;
818 static int tape_get(int fd, struct mtget *mt_get)
820 TAPE_POSITION_INFO pos_info;
823 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
824 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
829 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
831 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
838 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
840 if (result != NO_ERROR) {
841 TAPE_GET_DRIVE_PARAMETERS drive_params;
844 size = sizeof(drive_params);
846 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
848 if (result == NO_ERROR) {
849 blocksize = drive_params.DefaultBlockSize;
853 mt_get->mt_type = MT_ISSCSI2;
856 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
858 // Density / Block Size
859 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
860 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
862 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
864 if (pHandleInfo->bEOF) {
865 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
868 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
869 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
872 if (pHandleInfo->bEOT) {
873 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
876 if (pHandleInfo->bEOD) {
877 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
880 TAPE_GET_MEDIA_PARAMETERS media_params;
881 DWORD size = sizeof(media_params);
883 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
885 if (result == NO_ERROR && media_params.WriteProtected) {
886 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
889 result = GetTapeStatus(pHandleInfo->OSHandle);
891 if (result != NO_ERROR) {
892 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
893 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
896 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
899 // Recovered Error Count
900 mt_get->mt_erreg = 0;
903 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
906 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
911 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
912 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
913 #define SERVICEACTION_LONG_FORM 6
914 #define SERVICEACTION_EXTENDED_FORM 8
917 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
932 UCHAR NumberBufferBlocks[3];
933 UCHAR NumberBufferBytes[4];
934 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
936 typedef struct _SCSI_READ_POSITION_LONG_BUFFER
946 UCHAR BlockNumber[8];
949 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
951 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
962 UCHAR AdditionalLength[2];
964 UCHAR NumberBufferObjects[3];
965 UCHAR FirstLogicalObject[8];
966 UCHAR LastLogicalObject[8];
967 UCHAR NumberBufferObjectBytes[8];
968 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
970 typedef union _READ_POSITION_RESULT {
971 SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
972 SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
973 SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
974 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
976 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
978 PSCSI_PASS_THROUGH ScsiPassThrough;
980 DWORD dwBytesReturned;
982 const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
984 memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
986 ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
988 for (int pass = 0; pass < 2; pass++)
990 memset(ScsiPassThrough, 0, dwBufferSize);
992 ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
994 ScsiPassThrough->CdbLength = 10;
995 ScsiPassThrough->SenseInfoLength = 28;
996 ScsiPassThrough->DataIn = 1;
997 ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
998 ScsiPassThrough->TimeOutValue = 1000;
999 ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
1000 ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
1002 ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
1007 ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
1011 ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
1015 bResult = DeviceIoControl( hDevice,
1016 IOCTL_SCSI_PASS_THROUGH,
1017 ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
1018 ScsiPassThrough, dwBufferSize,
1022 if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
1023 if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
1024 PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
1028 case 0: // SERVICEACTION_LONG_FORM
1030 TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1031 TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1033 if (!TapePositionInfo->PartitionBlockValid) {
1034 TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1036 if (TapePositionInfo->PartitionBlockValid) {
1037 TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1038 TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1042 TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1043 if (TapePositionInfo->FileSetValid) {
1044 TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1045 TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1050 case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
1052 // pPosResult->ShortBuffer.PERR;
1053 // pPosResult->ShortBuffer.BYCU;
1054 // pPosResult->ShortBuffer.BCU;
1055 TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1056 TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1058 if (!TapePositionInfo->PartitionBlockValid) {
1059 TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1061 if (TapePositionInfo->PartitionBlockValid) {
1062 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
1063 TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1066 // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1067 // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1068 // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1075 free(ScsiPassThrough);
1080 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1082 DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1083 GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1087 if (pGetMediaTypes == NULL) {
1088 return ERROR_OUTOFMEMORY;
1092 DWORD dwBytesReturned;
1094 bResult = DeviceIoControl( hDevice,
1095 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1097 (LPVOID)pGetMediaTypes, dwBufferSize,
1102 dwResult = GetLastError();
1104 if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1105 free(pGetMediaTypes);
1109 dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1111 GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1113 if (pNewBuffer != pGetMediaTypes) {
1114 free(pGetMediaTypes);
1116 if (pNewBuffer == NULL) {
1117 return ERROR_OUTOFMEMORY;
1120 pGetMediaTypes = pNewBuffer;
1125 if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1126 free(pGetMediaTypes);
1127 return ERROR_BAD_DEVICE;
1130 for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1132 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1134 if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1135 *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1140 *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1142 free(pGetMediaTypes);
1148 free(pGetMediaTypes);
1150 return ERROR_NO_MEDIA_IN_DRIVE;
1153 static int tape_pos(int fd, struct mtpos *mt_pos)
1160 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1161 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1166 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1168 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1169 if (result == NO_ERROR) {
1170 mt_pos->mt_blkno = offset;