1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
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.
8 
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.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
21  *
22  * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
23  *
24  *
25  * This file was contributed to the Bacula project by Robert Nelson.
26  *
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
32  * or object form.
33  *
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>.
37  */
38 
39 #include <stdarg.h>
40 #include <stddef.h>
41 
42 #include "bacula.h"                   /* pull in global headers */
43 #include "stored.h"                   /* pull in Storage Deamon headers */
44 #include "win_tape_device.h"
45 
46 #include "sys/mtio.h"
47 #if defined(_MSC_VER)
48 #include <winioctl.h>
49 #else
50 #include <ntddstor.h>
51 #endif
52 #include <ntddscsi.h>
53 
54 //
55 // SCSI bus status codes.
56 //
57 
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
67 
Read16BitSigned(const unsigned char * pValue)68 static inline SHORT Read16BitSigned(const unsigned char *pValue)
69 {
70    return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
71 }
72 
Read16BitUnsigned(const unsigned char * pValue)73 static inline USHORT Read16BitUnsigned(const unsigned char *pValue)
74 {
75    return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
76 }
77 
Read24BitSigned(const unsigned char * pValue)78 static inline LONG Read24BitSigned(const unsigned char *pValue)
79 {
80    return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
81                    (ULONG)pValue[2])) << 8 >> 8;
82 }
83 
Read24BitUnsigned(const unsigned char * pValue)84 static inline ULONG Read24BitUnsigned(const unsigned char *pValue)
85 {
86    return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
87 }
88 
Read32BitSigned(const unsigned char * pValue)89 static inline LONG Read32BitSigned(const unsigned char *pValue)
90 {
91    return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
92                  ((ULONG)pValue[2] << 8) |   (ULONG)pValue[3]);
93 }
94 
Read32BitUnsigned(const unsigned char * pValue)95 static inline ULONG Read32BitUnsigned(const unsigned char *pValue)
96 {
97    return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
98            ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
99 }
100 
Read64BitSigned(const unsigned char * pValue)101 static inline LONGLONG Read64BitSigned(const unsigned char *pValue)
102 {
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]);
107 }
108 
Read64BitUnsigned(const unsigned char * pValue)109 static inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
110 {
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]);
115 }
116 
117 typedef struct _TAPE_POSITION_INFO
118 {
119    UCHAR       AtPartitionStart:1;
120    UCHAR       AtPartitionEnd:1;
121    UCHAR       PartitionBlockValid:1;
122    UCHAR       FileSetValid:1;
123    UCHAR       :4;
124    UCHAR       Reserved1[3];
125    ULONG       Partition;
126    ULONGLONG   BlockNumber;
127    ULONGLONG   FileNumber;
128    ULONGLONG   SetNumber;
129 }  TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
130 
131 typedef struct _TAPE_HANDLE_INFO
132 {
133    HANDLE      OSHandle;
134    bool        bEOD;
135    bool        bEOF;
136    bool        bEOT;
137    bool        bBlockValid;
138    ULONG       FeaturesLow;
139    ULONG       FeaturesHigh;
140    ULONG       ulFile;
141    ULONGLONG   ullFileStart;
142 
143 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
144 
145 TAPE_HANDLE_INFO TapeHandleTable[] =
146 {
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 }
163 };
164 
165 #define  NUMBER_HANDLE_ENTRIES      (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
166 
167 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
168 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
169 
win_tape_device()170 win_tape_device::win_tape_device()
171 {
172    m_fd = -1;
173 }
174 
~win_tape_device()175 win_tape_device::~win_tape_device()
176 { }
177 
178 int
d_open(const char * file,int flags,...)179 win_tape_device::d_open(const char *file, int flags, ...)
180 {
181    HANDLE hDevice = INVALID_HANDLE_VALUE;
182    char szDeviceName[256] = "\\\\.\\";
183    int  idxFile;
184    DWORD dwResult;
185 
186    for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
187       if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
188          break;
189       }
190    }
191 
192    if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
193       return EMFILE;
194    }
195 
196    memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
197 
198    if (!IsPathSeparator(file[0])) {
199        bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
200    } else {
201        bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
202    }
203 
204    hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
205 
206    if (hDevice != INVALID_HANDLE_VALUE) {
207       PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[idxFile];
208 
209       memset(pHandleInfo, 0, sizeof(*pHandleInfo));
210 
211       pHandleInfo->OSHandle = hDevice;
212 
213       TAPE_GET_DRIVE_PARAMETERS  TapeDriveParameters;
214       DWORD    dwSize = sizeof(TapeDriveParameters);
215 
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;
220       }
221 
222       TAPE_POSITION_INFO TapePositionInfo;
223 
224       dwResult =  GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
225 
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;
234          }
235       }
236    } else {
237       DWORD dwError = GetLastError();
238 
239       switch (dwError) {
240       case ERROR_FILE_NOT_FOUND:
241       case ERROR_PATH_NOT_FOUND:
242          errno = ENOENT;
243          break;
244 
245       case ERROR_TOO_MANY_OPEN_FILES:
246          errno = EMFILE;
247          break;
248 
249       default:
250       case ERROR_ACCESS_DENIED:
251       case ERROR_SHARING_VIOLATION:
252       case ERROR_LOCK_VIOLATION:
253       case ERROR_INVALID_NAME:
254          errno = EACCES;
255          break;
256 
257       case ERROR_FILE_EXISTS:
258          errno = EEXIST;
259          break;
260 
261       case ERROR_INVALID_PARAMETER:
262          errno = EINVAL;
263          break;
264       }
265 
266       return(int) -1;
267    }
268 
269    return (int)idxFile + 3;
270 }
271 
d_read(int fd,void * buffer,size_t count)272 ssize_t win_tape_device::d_read(int fd, void *buffer, size_t count)
273 {
274    if (buffer == NULL) {
275       errno = EINVAL;
276       return -1;
277    }
278 
279    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
280        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
281       errno = EBADF;
282       return -1;
283    }
284 
285    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
286 
287    DWORD bytes_read;
288    BOOL bResult;
289 
290    bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
291 
292    if (bResult) {
293       pHandleInfo->bEOF = false;
294       pHandleInfo->bEOT = false;
295       pHandleInfo->bEOD = false;
296       return bytes_read;
297    } else {
298       int iReturnValue = 0;
299       DWORD last_error = GetLastError();
300 
301       switch (last_error) {
302 
303       case ERROR_FILEMARK_DETECTED:
304          pHandleInfo->bEOF = true;
305          break;
306 
307       case ERROR_END_OF_MEDIA:
308          pHandleInfo->bEOT = true;
309          break;
310 
311       case ERROR_NO_MEDIA_IN_DRIVE:
312          pHandleInfo->bEOF = false;
313          pHandleInfo->bEOT = false;
314          pHandleInfo->bEOD = false;
315          errno = ENOMEDIUM;
316          iReturnValue = -1;
317          break;
318 
319       case ERROR_NO_DATA_DETECTED:
320          pHandleInfo->bEOD = true;
321          break;
322 
323       case ERROR_INVALID_HANDLE:
324       case ERROR_ACCESS_DENIED:
325       case ERROR_LOCK_VIOLATION:
326          errno = EBADF;
327          iReturnValue = -1;
328          break;
329 
330       default:
331          pHandleInfo->bEOF = false;
332          pHandleInfo->bEOT = false;
333          pHandleInfo->bEOD = false;
334          errno = EIO;
335          iReturnValue = -1;
336       }
337 
338       return iReturnValue;
339    }
340 }
341 
d_write(int fd,const void * buffer,size_t count)342 ssize_t win_tape_device::d_write(int fd, const void *buffer, size_t count)
343 {
344    if (buffer == NULL) {
345       errno = EINVAL;
346       return -1;
347    }
348 
349    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
350        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
351       errno = EBADF;
352       return -1;
353    }
354 
355    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
356 
357    DWORD bytes_written;
358    BOOL bResult;
359 
360    bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
361 
362    if (bResult) {
363       pHandleInfo->bEOF = false;
364       pHandleInfo->bEOT = false;
365       return bytes_written;
366    } else {
367       DWORD last_error = GetLastError();
368 
369       switch (last_error) {
370       case ERROR_END_OF_MEDIA:
371       case ERROR_DISK_FULL:
372          pHandleInfo->bEOT = true;
373          errno = ENOSPC;
374          break;
375 
376       case ERROR_NO_MEDIA_IN_DRIVE:
377          pHandleInfo->bEOF = false;
378          pHandleInfo->bEOT = false;
379          pHandleInfo->bEOD = false;
380          errno = ENOMEDIUM;
381          break;
382 
383       case ERROR_INVALID_HANDLE:
384       case ERROR_ACCESS_DENIED:
385          errno = EBADF;
386          break;
387 
388       default:
389          pHandleInfo->bEOF = false;
390          pHandleInfo->bEOT = false;
391          pHandleInfo->bEOD = false;
392          errno = EIO;
393          break;
394       }
395       return -1;
396    }
397 }
398 
d_close(int fd)399 int win_tape_device::d_close(int fd)
400 {
401    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
402       TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
403       errno = EBADF;
404       return -1;
405    }
406 
407    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
408 
409    if (!CloseHandle(pHandleInfo->OSHandle)) {
410       pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
411       errno = EBADF;
412       return -1;
413    }
414 
415    pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
416 
417    return 0;
418 }
419 
d_ioctl(int fd,ioctl_req_t request,char * op)420 int win_tape_device::d_ioctl(int fd, ioctl_req_t request, char *op)
421 {
422    va_list argp;
423    int result;
424 
425    va_start(argp, request);
426 
427    switch (request) {
428    case MTIOCTOP:
429       result = tape_op(fd, va_arg(argp, mtop *));
430       break;
431 
432    case MTIOCGET:
433       result = tape_get(fd, va_arg(argp, mtget *));
434       break;
435 
436    case MTIOCPOS:
437       result = tape_pos(fd, va_arg(argp, mtpos *));
438       break;
439 
440    default:
441       errno = ENOTTY;
442       result = -1;
443       break;
444    }
445 
446    va_end(argp);
447 
448    return result;
449 }
450 
tape_op(int fd,struct mtop * mt_com)451 int win_tape_device::tape_op(int fd, struct mtop *mt_com)
452 {
453    DWORD result = NO_ERROR;
454    int   index;
455 
456    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
457        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
458    {
459       errno = EBADF;
460       return -1;
461    }
462 
463    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
464 
465    switch (mt_com->mt_op) {
466    case MTRESET:
467    case MTNOP:
468    case MTSETDRVBUFFER:
469       break;
470 
471    default:
472    case MTRAS1:
473    case MTRAS2:
474    case MTRAS3:
475    case MTSETDENSITY:
476       errno = ENOTTY;
477       result = (DWORD)-1;
478       break;
479 
480    case MTFSF:
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;
487          }
488       }
489       break;
490 
491    case MTBSF:
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;
500          }
501       }
502       break;
503 
504    case MTFSR:
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;
512       }
513       break;
514 
515    case MTBSR:
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;
527       }
528       break;
529 
530    case MTWEOF:
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;
538       }
539       break;
540 
541    case MTREW:
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;
550       }
551       break;
552 
553    case MTOFFL:
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;
561       }
562       break;
563 
564    case MTRETEN:
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;
573       }
574       break;
575 
576    case MTBSFM:
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;
584          }
585       }
586       break;
587 
588    case MTFSFM:
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;
596          }
597       }
598       break;
599 
600    case MTEOM:
601       for ( ; ; ) {
602          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
603          if (result != NO_ERROR) {
604             pHandleInfo->bEOF = false;
605 
606             if (result == ERROR_END_OF_MEDIA) {
607                pHandleInfo->bEOD = true;
608                pHandleInfo->bEOT = true;
609                return 0;
610             }
611             if (result == ERROR_NO_DATA_DETECTED) {
612                pHandleInfo->bEOD = true;
613                pHandleInfo->bEOT = false;
614                return 0;
615             }
616             break;
617          } else {
618             pHandleInfo->bEOF = true;
619             pHandleInfo->ulFile++;
620          }
621       }
622       break;
623 
624    case MTERASE:
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;
633       }
634       break;
635 
636    case MTSETBLK:
637       {
638          TAPE_SET_MEDIA_PARAMETERS  SetMediaParameters;
639 
640          SetMediaParameters.BlockSize = mt_com->mt_count;
641          result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
642       }
643       break;
644 
645    case MTSEEK:
646       {
647          TAPE_POSITION_INFO   TapePositionInfo;
648 
649          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
650 
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;
655          } else {
656             pHandleInfo->ulFile = ~0U;
657          }
658       }
659       break;
660 
661    case MTTELL:
662       {
663          DWORD partition;
664          DWORD offset;
665          DWORD offsetHi;
666 
667          result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
668          if (result == NO_ERROR) {
669             return offset;
670          }
671       }
672       break;
673 
674    case MTFSS:
675       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
676       break;
677 
678    case MTBSS:
679       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
680       break;
681 
682    case MTWSM:
683       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
684       break;
685 
686    case MTLOCK:
687       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
688       break;
689 
690    case MTUNLOCK:
691       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
692       break;
693 
694    case MTLOAD:
695       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
696       break;
697 
698    case MTUNLOAD:
699       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
700       break;
701 
702    case MTCOMPRESSION:
703       {
704          TAPE_GET_DRIVE_PARAMETERS  GetDriveParameters;
705          TAPE_SET_DRIVE_PARAMETERS  SetDriveParameters;
706          DWORD                      size;
707 
708          size = sizeof(GetDriveParameters);
709 
710          result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
711 
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;
718 
719             result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
720          }
721       }
722       break;
723 
724    case MTSETPART:
725       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
726       break;
727 
728    case MTMKPART:
729       if (mt_com->mt_count == 0) {
730          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
731       } else {
732          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
733       }
734       break;
735    }
736 
737    if ((result == NO_ERROR && pHandleInfo->bEOF) ||
738        (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
739 
740       TAPE_POSITION_INFO TapePositionInfo;
741 
742       if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
743          pHandleInfo->bBlockValid = true;
744          pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
745       }
746    }
747 
748    switch (result) {
749    case NO_ERROR:
750    case (DWORD)-1:   /* Error has already been translated into errno */
751       break;
752 
753    default:
754    case ERROR_FILEMARK_DETECTED:
755       errno = EIO;
756       break;
757 
758    case ERROR_END_OF_MEDIA:
759       pHandleInfo->bEOT = true;
760       errno = EIO;
761       break;
762 
763    case ERROR_NO_DATA_DETECTED:
764       pHandleInfo->bEOD = true;
765       errno = EIO;
766       break;
767 
768    case ERROR_NO_MEDIA_IN_DRIVE:
769       pHandleInfo->bEOF = false;
770       pHandleInfo->bEOT = false;
771       pHandleInfo->bEOD = false;
772       errno = ENOMEDIUM;
773       break;
774 
775    case ERROR_INVALID_HANDLE:
776    case ERROR_ACCESS_DENIED:
777    case ERROR_LOCK_VIOLATION:
778       errno = EBADF;
779       break;
780    }
781 
782    return result == NO_ERROR ? 0 : -1;
783 }
784 
tape_get(int fd,struct mtget * mt_get)785 int win_tape_device::tape_get(int fd, struct mtget *mt_get)
786 {
787    TAPE_POSITION_INFO pos_info;
788    BOOL result;
789 
790    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
791        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
792       errno = EBADF;
793       return -1;
794    }
795 
796    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
797 
798    if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
799       return -1;
800    }
801 
802    DWORD density = 0;
803    DWORD blocksize = 0;
804 
805    result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
806 
807    if (result != NO_ERROR) {
808       TAPE_GET_DRIVE_PARAMETERS drive_params;
809       DWORD size;
810 
811       size = sizeof(drive_params);
812 
813       result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
814 
815       if (result == NO_ERROR) {
816          blocksize = drive_params.DefaultBlockSize;
817       }
818    }
819 
820    mt_get->mt_type = MT_ISSCSI2;
821 
822    // Partition #
823    mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
824 
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);
828 
829    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
830 
831    if (pHandleInfo->bEOF) {
832       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
833    }
834 
835    if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
836       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
837    }
838 
839    if (pHandleInfo->bEOT) {
840       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
841    }
842 
843    if (pHandleInfo->bEOD) {
844       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
845    }
846 
847    TAPE_GET_MEDIA_PARAMETERS  media_params;
848    DWORD size = sizeof(media_params);
849 
850    result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
851 
852    if (result == NO_ERROR && media_params.WriteProtected) {
853       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
854    }
855 
856    result = GetTapeStatus(pHandleInfo->OSHandle);
857 
858    if (result != NO_ERROR) {
859       if (result == ERROR_NO_MEDIA_IN_DRIVE) {
860          mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
861       }
862    } else {
863       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
864    }
865 
866    // Recovered Error Count
867    mt_get->mt_erreg = 0;
868 
869    // File Number
870    mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
871 
872    // Block Number
873    mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
874 
875    return 0;
876 }
877 
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
882 
883 
884 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
885 {
886    UCHAR    :1;
887    UCHAR    PERR:1;
888    UCHAR    BPU:1;
889    UCHAR    :1;
890    UCHAR    BYCU:1;
891    UCHAR    BCU:1;
892    UCHAR    EOP:1;
893    UCHAR    BOP:1;
894    UCHAR    Partition;
895    UCHAR    Reserved1[2];
896    UCHAR    FirstBlock[4];
897    UCHAR    LastBlock[4];
898    UCHAR    Reserved2;
899    UCHAR    NumberBufferBlocks[3];
900    UCHAR    NumberBufferBytes[4];
901 }  SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
902 
903 typedef  struct   _SCSI_READ_POSITION_LONG_BUFFER
904 {
905    UCHAR    :2;
906    UCHAR    BPU:1;
907    UCHAR    MPU:1;
908    UCHAR    :2;
909    UCHAR    EOP:1;
910    UCHAR    BOP:1;
911    UCHAR    Reserved3[3];
912    UCHAR    Partition[4];
913    UCHAR    BlockNumber[8];
914    UCHAR    FileNumber[8];
915    UCHAR    SetNumber[8];
916 }  SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
917 
918 typedef  struct   _SCSI_READ_POSITION_EXTENDED_BUFFER
919 {
920    UCHAR    :1;
921    UCHAR    PERR:1;
922    UCHAR    LOPU:1;
923    UCHAR    :1;
924    UCHAR    BYCU:1;
925    UCHAR    LOCU:1;
926    UCHAR    EOP:1;
927    UCHAR    BOP:1;
928    UCHAR    Partition;
929    UCHAR    AdditionalLength[2];
930    UCHAR    Reserved1;
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;
936 
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;
942 
GetTapePositionInfo(HANDLE hDevice,PTAPE_POSITION_INFO TapePositionInfo)943 static DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
944 {
945    PSCSI_PASS_THROUGH   ScsiPassThrough;
946    BOOL                 bResult;
947    DWORD                dwBytesReturned;
948 
949    const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
950 
951    memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
952 
953    ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
954 
955    for (int pass = 0; pass < 2; pass++)
956    {
957       memset(ScsiPassThrough, 0, dwBufferSize);
958 
959       ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
960 
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);
968 
969       ScsiPassThrough->Cdb[0] = 0x34;  // READ POSITION
970 
971       switch (pass)
972       {
973       case 0:
974          ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
975          break;
976 
977       case 1:
978          ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
979          break;
980       }
981 
982       bResult = DeviceIoControl( hDevice,
983                                  IOCTL_SCSI_PASS_THROUGH,
984                                  ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
985                                  ScsiPassThrough, dwBufferSize,
986                                  &dwBytesReturned,
987                                  NULL);
988 
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);
992 
993             switch (pass)
994             {
995             case 0:     // SERVICEACTION_LONG_FORM
996                {
997                   TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
998                   TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
999 
1000                   if (!TapePositionInfo->PartitionBlockValid) {
1001                      TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1002 
1003                      if (TapePositionInfo->PartitionBlockValid) {
1004                         TapePositionInfo->Partition =   Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1005                         TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1006                      }
1007                   }
1008 
1009                   TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1010                   if (TapePositionInfo->FileSetValid) {
1011                      TapePositionInfo->FileNumber =  Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1012                      TapePositionInfo->SetNumber =   Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1013                   }
1014                }
1015                break;
1016 
1017             case 1:     // SERVICEACTION_SHORT_FORM_BLOCKID
1018                {
1019                   // pPosResult->ShortBuffer.PERR;
1020                   // pPosResult->ShortBuffer.BYCU;
1021                   // pPosResult->ShortBuffer.BCU;
1022                   TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1023                   TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1024 
1025                   if (!TapePositionInfo->PartitionBlockValid) {
1026                      TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1027 
1028                      if (TapePositionInfo->PartitionBlockValid) {
1029                         TapePositionInfo->Partition =   pPosResult->ShortBuffer.Partition;
1030                         TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1031                      }
1032                   }
1033                   // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1034                   // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1035                   // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1036                }
1037                break;
1038             }
1039          }
1040       }
1041    }
1042    free(ScsiPassThrough);
1043 
1044    return NO_ERROR;
1045 }
1046 
GetDensityBlockSize(HANDLE hDevice,DWORD * pdwDensity,DWORD * pdwBlockSize)1047 static DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1048 {
1049    DWORD             dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1050    GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1051    BOOL              bResult;
1052    DWORD             dwResult;
1053 
1054    if (pGetMediaTypes == NULL) {
1055       return ERROR_OUTOFMEMORY;
1056    }
1057 
1058    do {
1059       DWORD          dwBytesReturned;
1060 
1061       bResult = DeviceIoControl( hDevice,
1062                                  IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
1063                                  NULL, 0,
1064                                  (LPVOID)pGetMediaTypes, dwBufferSize,
1065                                  &dwBytesReturned,
1066                                  NULL);
1067 
1068       if (!bResult) {
1069          dwResult = GetLastError();
1070 
1071          if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1072             free(pGetMediaTypes);
1073             return dwResult;
1074          }
1075 
1076          dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1077 
1078          GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1079 
1080          if (pNewBuffer != pGetMediaTypes) {
1081             free(pGetMediaTypes);
1082 
1083             if (pNewBuffer == NULL) {
1084                return ERROR_OUTOFMEMORY;
1085             }
1086 
1087             pGetMediaTypes = pNewBuffer;
1088          }
1089       }
1090    } while (!bResult);
1091 
1092    if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1093       free(pGetMediaTypes);
1094       return ERROR_BAD_DEVICE;
1095    }
1096 
1097    for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1098 
1099       if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1100 
1101          if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1102             *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1103          } else {
1104             *pdwDensity = 0;
1105          }
1106 
1107          *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1108 
1109          free(pGetMediaTypes);
1110 
1111          return NO_ERROR;
1112       }
1113    }
1114 
1115    free(pGetMediaTypes);
1116 
1117    return ERROR_NO_MEDIA_IN_DRIVE;
1118 }
1119 
tape_pos(int fd,struct mtpos * mt_pos)1120 int win_tape_device::tape_pos(int fd, struct mtpos *mt_pos)
1121 {
1122    DWORD partition;
1123    DWORD offset;
1124    DWORD offsetHi;
1125    BOOL result;
1126 
1127    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
1128        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1129       errno = EBADF;
1130       return -1;
1131    }
1132 
1133    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
1134 
1135    result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1136    if (result == NO_ERROR) {
1137       mt_pos->mt_blkno = offset;
1138       return 0;
1139    }
1140 
1141    return -1;
1142 }
1143