1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2013-2013 Planets Communications B.V.
6    Copyright (C) 2013-2021 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
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.
17 
18    You should have received a copy of the GNU Affero 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
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, MM
25  * Robert Nelson, May, 2006 <robertn@the-nelsons.org>
26  * Extracted from other source files Marco van Wieringen, December 2013
27  *
28  * Stacking is the following:
29  *
30  *  win32_tape_device::
31  *         |
32  *         v
33  * generic_tape_device::
34  *         |
35  *         v
36  *      Device::
37  */
38 /**
39  * @file
40  * Windows Tape API device abstraction.
41  * Emulate the Linux st (scsi tape) driver on Microsoft Windows.
42  */
43 
44 #include "include/bareos.h"
45 #include "stored/stored.h"
46 #include "generic_tape_device.h"
47 #include "win32_tape_device.h"
48 
49 #include "sys/mtio.h"
50 #if defined(_MSC_VER)
51 #  include <winioctl.h>
52 #else
53 #  include <ntddstor.h>
54 #endif
55 #include <ntddscsi.h>
56 
57 namespace storagedaemon {
58 
59 /**
60  * SCSI bus status codes.
61  */
62 #define SCSISTAT_GOOD 0x00
63 #define SCSISTAT_CHECK_CONDITION 0x02
64 #define SCSISTAT_CONDITION_MET 0x04
65 #define SCSISTAT_BUSY 0x08
66 #define SCSISTAT_INTERMEDIATE 0x10
67 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
68 #define SCSISTAT_RESERVATION_CONFLICT 0x18
69 #define SCSISTAT_COMMAND_TERMINATED 0x22
70 #define SCSISTAT_QUEUE_FULL 0x28
71 
Read16BitSigned(const unsigned char * pValue)72 static inline SHORT Read16BitSigned(const unsigned char* pValue)
73 {
74   return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
75 }
76 
Read16BitUnsigned(const unsigned char * pValue)77 static inline USHORT Read16BitUnsigned(const unsigned char* pValue)
78 {
79   return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
80 }
81 
Read24BitSigned(const unsigned char * pValue)82 static inline LONG Read24BitSigned(const unsigned char* pValue)
83 {
84   return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8)
85                  | (ULONG)pValue[2]))
86              << 8
87          >> 8;
88 }
89 
Read24BitUnsigned(const unsigned char * pValue)90 static inline ULONG Read24BitUnsigned(const unsigned char* pValue)
91 {
92   return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
93 }
94 
Read32BitSigned(const unsigned char * pValue)95 static inline LONG Read32BitSigned(const unsigned char* pValue)
96 {
97   return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16)
98                 | ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
99 }
100 
Read32BitUnsigned(const unsigned char * pValue)101 static inline ULONG Read32BitUnsigned(const unsigned char* pValue)
102 {
103   return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16)
104           | ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
105 }
106 
Read64BitSigned(const unsigned char * pValue)107 static inline LONGLONG Read64BitSigned(const unsigned char* pValue)
108 {
109   return (LONGLONG)(
110       ((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48)
111       | ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32)
112       | ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16)
113       | ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
114 }
115 
Read64BitUnsigned(const unsigned char * pValue)116 static inline ULONGLONG Read64BitUnsigned(const unsigned char* pValue)
117 {
118   return (LONGLONG)(
119       ((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48)
120       | ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32)
121       | ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16)
122       | ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
123 }
124 
125 typedef struct _TAPE_POSITION_INFO {
126   UCHAR AtPartitionStart : 1;
127   UCHAR AtPartitionEnd : 1;
128   UCHAR PartitionBlockValid : 1;
129   UCHAR FileSetValid : 1;
130   UCHAR : 4;
131   UCHAR Reserved1[3];
132   ULONG Partition;
133   ULONGLONG BlockNumber;
134   ULONGLONG FileNumber;
135   ULONGLONG SetNumber;
136 } TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
137 
138 typedef struct _TAPE_HANDLE_INFO {
139   HANDLE OSHandle;
140   bool bEOD;
141   bool bEOF;
142   bool bEOT;
143   bool bBlockValid;
144   ULONG FeaturesLow;
145   ULONG FeaturesHigh;
146   ULONG ulFile;
147   ULONGLONG ullFileStart;
148 
149 } TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
150 
151 TAPE_HANDLE_INFO TapeHandleTable[]
152     = {{INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE},
153        {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE},
154        {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE},
155        {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE},
156        {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE}, {INVALID_HANDLE_VALUE},
157        {INVALID_HANDLE_VALUE}};
158 
159 #define NUMBER_HANDLE_ENTRIES \
160   (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
161 
162 static DWORD GetTapePositionInfo(HANDLE hDevice,
163                                  PTAPE_POSITION_INFO TapePositionInfo);
164 static DWORD GetDensityBlockSize(HANDLE hDevice,
165                                  DWORD* pdwDensity,
166                                  DWORD* pdwBlockSize);
167 
d_open(const char * pathname,int flags,int mode)168 int win32_tape_device::d_open(const char* pathname, int flags, int mode)
169 {
170   HANDLE hDevice = INVALID_HANDLE_VALUE;
171   char szDeviceName[256] = "\\\\.\\";
172   int idxFile;
173   DWORD dwResult;
174 
175   for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
176     if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) { break; }
177   }
178 
179   if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) { return EMFILE; }
180 
181   memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
182   if (!IsPathSeparator(pathname[0])) {
183     bstrncpy(&szDeviceName[4], pathname, sizeof(szDeviceName) - 4);
184   } else {
185     bstrncpy(&szDeviceName[0], pathname, sizeof(szDeviceName));
186   }
187 
188   hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0,
189                        NULL);
190 
191   if (hDevice != INVALID_HANDLE_VALUE) {
192     PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
193 
194     memset(pHandleInfo, 0, sizeof(*pHandleInfo));
195 
196     pHandleInfo->OSHandle = hDevice;
197 
198     TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
199     DWORD dwSize = sizeof(TapeDriveParameters);
200 
201     dwResult
202         = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION,
203                             &dwSize, &TapeDriveParameters);
204     if (dwResult == NO_ERROR) {
205       pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
206       pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
207     }
208 
209     TAPE_POSITION_INFO TapePositionInfo;
210 
211     dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
212 
213     if (dwResult == NO_ERROR) {
214       if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd
215           || (TapePositionInfo.PartitionBlockValid
216               && TapePositionInfo.BlockNumber == 0)) {
217         pHandleInfo->ulFile = 0;
218         pHandleInfo->bBlockValid = true;
219         pHandleInfo->ullFileStart = 0;
220       } else if (TapePositionInfo.FileSetValid) {
221         pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
222       }
223     }
224   } else {
225     DWORD dwError = GetLastError();
226 
227     switch (dwError) {
228       case ERROR_FILE_NOT_FOUND:
229       case ERROR_PATH_NOT_FOUND:
230         errno = ENOENT;
231         break;
232       case ERROR_TOO_MANY_OPEN_FILES:
233         errno = EMFILE;
234         break;
235       case ERROR_ACCESS_DENIED:
236       case ERROR_SHARING_VIOLATION:
237       case ERROR_LOCK_VIOLATION:
238       case ERROR_INVALID_NAME:
239         errno = EACCES;
240         break;
241       case ERROR_FILE_EXISTS:
242         errno = EEXIST;
243         break;
244       case ERROR_INVALID_PARAMETER:
245         errno = EINVAL;
246         break;
247       default:
248         errno = EACCES;
249         break;
250     }
251 
252     return (int)-1;
253   }
254 
255   return (int)idxFile + 3;
256 }
257 
d_read(int fd,void * buffer,size_t count)258 ssize_t win32_tape_device::d_read(int fd, void* buffer, size_t count)
259 {
260   if (buffer == NULL) {
261     errno = EINVAL;
262     return -1;
263   }
264 
265   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
266       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
267     errno = EBADF;
268     return -1;
269   }
270 
271   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
272 
273   DWORD bytes_read;
274   BOOL bResult;
275 
276   bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
277 
278   if (bResult) {
279     pHandleInfo->bEOF = false;
280     pHandleInfo->bEOT = false;
281     pHandleInfo->bEOD = false;
282     return bytes_read;
283   } else {
284     int iReturnValue = 0;
285     DWORD last_error = GetLastError();
286 
287     switch (last_error) {
288       case ERROR_FILEMARK_DETECTED:
289         pHandleInfo->bEOF = true;
290         break;
291       case ERROR_END_OF_MEDIA:
292         pHandleInfo->bEOT = true;
293         break;
294       case ERROR_NO_MEDIA_IN_DRIVE:
295         pHandleInfo->bEOF = false;
296         pHandleInfo->bEOT = false;
297         pHandleInfo->bEOD = false;
298         errno = ENOMEDIUM;
299         iReturnValue = -1;
300         break;
301       case ERROR_NO_DATA_DETECTED:
302         pHandleInfo->bEOD = true;
303         break;
304       case ERROR_INVALID_HANDLE:
305       case ERROR_ACCESS_DENIED:
306       case ERROR_LOCK_VIOLATION:
307         errno = EBADF;
308         iReturnValue = -1;
309         break;
310       default:
311         pHandleInfo->bEOF = false;
312         pHandleInfo->bEOT = false;
313         pHandleInfo->bEOD = false;
314         errno = EIO;
315         iReturnValue = -1;
316     }
317 
318     return iReturnValue;
319   }
320 }
321 
d_write(int fd,const void * buffer,size_t count)322 ssize_t win32_tape_device::d_write(int fd, const void* buffer, size_t count)
323 {
324   if (buffer == NULL) {
325     errno = EINVAL;
326     return -1;
327   }
328 
329   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
330       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
331     errno = EBADF;
332     return -1;
333   }
334 
335   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
336   DWORD bytes_written;
337   BOOL bResult;
338 
339   bResult
340       = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
341 
342   if (bResult) {
343     pHandleInfo->bEOF = false;
344     pHandleInfo->bEOT = false;
345     return bytes_written;
346   } else {
347     DWORD last_error = GetLastError();
348 
349     switch (last_error) {
350       case ERROR_END_OF_MEDIA:
351       case ERROR_DISK_FULL:
352         pHandleInfo->bEOT = true;
353         errno = ENOSPC;
354         break;
355       case ERROR_NO_MEDIA_IN_DRIVE:
356         pHandleInfo->bEOF = false;
357         pHandleInfo->bEOT = false;
358         pHandleInfo->bEOD = false;
359         errno = ENOMEDIUM;
360         break;
361       case ERROR_INVALID_HANDLE:
362       case ERROR_ACCESS_DENIED:
363         errno = EBADF;
364         break;
365       default:
366         pHandleInfo->bEOF = false;
367         pHandleInfo->bEOT = false;
368         pHandleInfo->bEOD = false;
369         errno = EIO;
370         break;
371     }
372     return -1;
373   }
374 }
375 
d_close(int fd)376 int win32_tape_device::d_close(int fd)
377 {
378   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
379       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
380     errno = EBADF;
381     return -1;
382   }
383 
384   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
385   if (!CloseHandle(pHandleInfo->OSHandle)) {
386     pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
387     errno = EBADF;
388     return -1;
389   }
390 
391   pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
392 
393   return 0;
394 }
395 
d_ioctl(int fd,ioctl_req_t request,char * op)396 int win32_tape_device::d_ioctl(int fd, ioctl_req_t request, char* op)
397 {
398   int result = 0;
399 
400   if (request == MTIOCTOP) {
401     result = TapeOp((mtop*)op);
402   } else if (request == MTIOCGET) {
403     result = TapeGet((mtget*)op);
404   } else if (request == MTIOCPOS) {
405     result = TapePos((mtpos*)op);
406   } else {
407     errno = ENOTTY;
408     result = -1;
409   }
410 
411   return result;
412 }
413 
TapeOp(struct mtop * mt_com)414 int win32_tape_device::TapeOp(struct mtop* mt_com)
415 {
416   DWORD result = NO_ERROR;
417   int index;
418 
419   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
420       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
421     errno = EBADF;
422     return -1;
423   }
424 
425   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
426   switch (mt_com->mt_op) {
427     case MTRESET:
428     case MTNOP:
429     case MTSETDRVBUFFER:
430       break;
431     case MTRAS1:
432     case MTRAS2:
433     case MTRAS3:
434     case MTSETDENSITY:
435       errno = ENOTTY;
436       result = (DWORD)-1;
437       break;
438     case MTFSF:
439       for (index = 0; index < mt_com->mt_count; index++) {
440         result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0,
441                                  1, 0, FALSE);
442         if (result == NO_ERROR) {
443           pHandleInfo->ulFile++;
444           pHandleInfo->bEOF = true;
445           pHandleInfo->bEOT = false;
446         }
447       }
448       break;
449     case MTBSF:
450       for (index = 0; index < mt_com->mt_count; index++) {
451         result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0,
452                                  (DWORD)-1, ~0, FALSE);
453         if (result == NO_ERROR) {
454           pHandleInfo->ulFile--;
455           pHandleInfo->bBlockValid = false;
456           pHandleInfo->bEOD = false;
457           pHandleInfo->bEOF = false;
458           pHandleInfo->bEOT = false;
459         }
460       }
461       break;
462     case MTFSR:
463       result
464           = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS,
465                             0, mt_com->mt_count, 0, FALSE);
466       if (result == NO_ERROR) {
467         pHandleInfo->bEOD = false;
468         pHandleInfo->bEOF = false;
469         pHandleInfo->bEOT = false;
470       } else if (result == ERROR_FILEMARK_DETECTED) {
471         pHandleInfo->bEOF = true;
472       }
473       break;
474     case MTBSR:
475       result
476           = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS,
477                             0, -mt_com->mt_count, ~0, FALSE);
478       if (result == NO_ERROR) {
479         pHandleInfo->bEOD = false;
480         pHandleInfo->bEOF = false;
481         pHandleInfo->bEOT = false;
482       } else if (result == ERROR_FILEMARK_DETECTED) {
483         pHandleInfo->ulFile--;
484         pHandleInfo->bBlockValid = false;
485         pHandleInfo->bEOD = false;
486         pHandleInfo->bEOF = false;
487         pHandleInfo->bEOT = false;
488       }
489       break;
490     case MTWEOF:
491       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS,
492                              mt_com->mt_count, FALSE);
493       if (result == NO_ERROR) {
494         pHandleInfo->bEOF = true;
495         pHandleInfo->bEOT = false;
496         pHandleInfo->ulFile += mt_com->mt_count;
497         pHandleInfo->bBlockValid = true;
498         pHandleInfo->ullFileStart = 0;
499       }
500       break;
501     case MTREW:
502       result
503           = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
504       if (result == NO_ERROR) {
505         pHandleInfo->bEOD = false;
506         pHandleInfo->bEOF = false;
507         pHandleInfo->bEOT = false;
508         pHandleInfo->ulFile = 0;
509         pHandleInfo->bBlockValid = true;
510         pHandleInfo->ullFileStart = 0;
511       }
512       break;
513     case MTOFFL:
514       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
515       if (result == NO_ERROR) {
516         pHandleInfo->bEOD = false;
517         pHandleInfo->bEOF = false;
518         pHandleInfo->bEOT = false;
519         pHandleInfo->ulFile = 0;
520         pHandleInfo->ullFileStart = 0;
521       }
522       break;
523     case MTRETEN:
524       result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
525       if (result == NO_ERROR) {
526         pHandleInfo->bEOD = false;
527         pHandleInfo->bEOF = false;
528         pHandleInfo->bEOT = false;
529         pHandleInfo->ulFile = 0;
530         pHandleInfo->bBlockValid = true;
531         pHandleInfo->ullFileStart = 0;
532       }
533       break;
534     case MTBSFM:
535       for (index = 0; index < mt_com->mt_count; index++) {
536         result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0,
537                                  (DWORD)-1, ~0, FALSE);
538         if (result == NO_ERROR) {
539           result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS,
540                                    0, 1, 0, FALSE);
541           pHandleInfo->bEOD = false;
542           pHandleInfo->bEOF = false;
543           pHandleInfo->bEOT = false;
544         }
545       }
546       break;
547     case MTFSFM:
548       for (index = 0; index < mt_com->mt_count; index++) {
549         result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0,
550                                  mt_com->mt_count, 0, FALSE);
551         if (result == NO_ERROR) {
552           result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS,
553                                    0, (DWORD)-1, ~0, FALSE);
554           pHandleInfo->bEOD = false;
555           pHandleInfo->bEOF = false;
556           pHandleInfo->bEOT = false;
557         }
558       }
559       break;
560     case MTEOM:
561       while (1) {
562         result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0,
563                                  1, 0, FALSE);
564         if (result != NO_ERROR) {
565           pHandleInfo->bEOF = false;
566 
567           if (result == ERROR_END_OF_MEDIA) {
568             pHandleInfo->bEOD = true;
569             pHandleInfo->bEOT = true;
570             return 0;
571           }
572           if (result == ERROR_NO_DATA_DETECTED) {
573             pHandleInfo->bEOD = true;
574             pHandleInfo->bEOT = false;
575             return 0;
576           }
577           break;
578         } else {
579           pHandleInfo->bEOF = true;
580           pHandleInfo->ulFile++;
581         }
582       }
583       break;
584     case MTERASE:
585       result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
586       if (result == NO_ERROR) {
587         pHandleInfo->bEOD = true;
588         pHandleInfo->bEOF = false;
589         pHandleInfo->bEOT = false;
590         pHandleInfo->ulFile = 0;
591         pHandleInfo->bBlockValid = true;
592         pHandleInfo->ullFileStart = 0;
593       }
594       break;
595     case MTSETBLK: {
596       TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
597 
598       SetMediaParameters.BlockSize = mt_com->mt_count;
599       result
600           = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION,
601                               &SetMediaParameters);
602       break;
603     }
604     case MTSEEK: {
605       TAPE_POSITION_INFO TapePositionInfo;
606 
607       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0,
608                                mt_com->mt_count, 0, FALSE);
609 
610       memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
611       DWORD dwPosResult
612           = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
613       if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
614         pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
615       } else {
616         pHandleInfo->ulFile = ~0U;
617       }
618       break;
619     }
620     case MTTELL: {
621       DWORD partition;
622       DWORD offset;
623       DWORD offsetHi;
624 
625       result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK,
626                                &partition, &offset, &offsetHi);
627       if (result == NO_ERROR) { return offset; }
628       break;
629     }
630     case MTFSS:
631       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0,
632                                mt_com->mt_count, 0, FALSE);
633       break;
634     case MTBSS:
635       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0,
636                                -mt_com->mt_count, ~0, FALSE);
637       break;
638     case MTWSM:
639       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS,
640                              mt_com->mt_count, FALSE);
641       break;
642     case MTLOCK:
643       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
644       break;
645     case MTUNLOCK:
646       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
647       break;
648     case MTLOAD:
649       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
650       break;
651     case MTUNLOAD:
652       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
653       break;
654     case MTCOMPRESSION: {
655       TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
656       TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
657       DWORD size;
658 
659       size = sizeof(GetDriveParameters);
660 
661       result
662           = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION,
663                               &size, &GetDriveParameters);
664 
665       if (result == NO_ERROR) {
666         SetDriveParameters.ECC = GetDriveParameters.ECC;
667         SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
668         SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
669         SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
670         SetDriveParameters.EOTWarningZoneSize
671             = GetDriveParameters.EOTWarningZoneSize;
672 
673         result = SetTapeParameters(pHandleInfo->OSHandle,
674                                    SET_TAPE_DRIVE_INFORMATION,
675                                    &SetDriveParameters);
676       }
677       break;
678     }
679     case MTSETPART:
680       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK,
681                                mt_com->mt_count, 0, 0, FALSE);
682       break;
683     case MTMKPART:
684       if (mt_com->mt_count == 0) {
685         result = CreateTapePartition(pHandleInfo->OSHandle,
686                                      TAPE_INITIATOR_PARTITIONS, 1, 0);
687       } else {
688         result = CreateTapePartition(pHandleInfo->OSHandle,
689                                      TAPE_INITIATOR_PARTITIONS, 2,
690                                      mt_com->mt_count);
691       }
692       break;
693     default:
694       errno = ENOTTY;
695       result = (DWORD)-1;
696       break;
697   }
698 
699   if ((result == NO_ERROR && pHandleInfo->bEOF)
700       || (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
701     TAPE_POSITION_INFO TapePositionInfo;
702 
703     if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo)
704         == NO_ERROR) {
705       pHandleInfo->bBlockValid = true;
706       pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
707     }
708   }
709 
710   switch (result) {
711     case NO_ERROR:
712     case (DWORD)-1: /* Error has already been translated into errno */
713       break;
714     case ERROR_FILEMARK_DETECTED:
715       errno = EIO;
716       break;
717     case ERROR_END_OF_MEDIA:
718       pHandleInfo->bEOT = true;
719       errno = EIO;
720       break;
721     case ERROR_NO_DATA_DETECTED:
722       pHandleInfo->bEOD = true;
723       errno = EIO;
724       break;
725     case ERROR_NO_MEDIA_IN_DRIVE:
726       pHandleInfo->bEOF = false;
727       pHandleInfo->bEOT = false;
728       pHandleInfo->bEOD = false;
729       errno = ENOMEDIUM;
730       break;
731     case ERROR_INVALID_HANDLE:
732     case ERROR_ACCESS_DENIED:
733     case ERROR_LOCK_VIOLATION:
734       errno = EBADF;
735       break;
736     default:
737       errno = EIO;
738       break;
739   }
740 
741   return result == NO_ERROR ? 0 : -1;
742 }
743 
TapeGet(struct mtget * mt_get)744 int win32_tape_device::TapeGet(struct mtget* mt_get)
745 {
746   TAPE_POSITION_INFO pos_info;
747   BOOL result;
748 
749   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
750       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
751     errno = EBADF;
752     return -1;
753   }
754 
755   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
756 
757   if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
758     return -1;
759   }
760 
761   DWORD density = 0;
762   DWORD blocksize = 0;
763 
764   result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
765 
766   if (result != NO_ERROR) {
767     TAPE_GET_DRIVE_PARAMETERS drive_params;
768     DWORD size;
769 
770     size = sizeof(drive_params);
771     result
772         = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION,
773                             &size, &drive_params);
774     if (result == NO_ERROR) { blocksize = drive_params.DefaultBlockSize; }
775   }
776 
777   mt_get->mt_type = MT_ISSCSI2;
778 
779   /*
780    * Partition #
781    */
782   mt_get->mt_resid
783       = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
784 
785   /*
786    * Density / Block Size
787    */
788   mt_get->mt_dsreg
789       = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK)
790         | ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
791 
792   mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
793   if (pHandleInfo->bEOF) { mt_get->mt_gstat |= 0x80000000; /* GMT_EOF */ }
794 
795   if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
796     mt_get->mt_gstat |= 0x40000000; /* GMT_BOT */
797   }
798 
799   if (pHandleInfo->bEOT) { mt_get->mt_gstat |= 0x20000000; /* GMT_EOT */ }
800 
801   if (pHandleInfo->bEOD) { mt_get->mt_gstat |= 0x08000000; /* GMT_EOD */ }
802 
803   TAPE_GET_MEDIA_PARAMETERS media_params;
804   DWORD size = sizeof(media_params);
805 
806   result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION,
807                              &size, &media_params);
808 
809   if (result == NO_ERROR && media_params.WriteProtected) {
810     mt_get->mt_gstat |= 0x04000000; /* GMT_WR_PROT */
811   }
812 
813   result = GetTapeStatus(pHandleInfo->OSHandle);
814 
815   if (result != NO_ERROR) {
816     if (result == ERROR_NO_MEDIA_IN_DRIVE) {
817       mt_get->mt_gstat |= 0x00040000; /* GMT_DR_OPEN */
818     }
819   } else {
820     mt_get->mt_gstat |= 0x01000000; /* GMT_ONLINE */
821   }
822 
823   /*
824    * Recovered Error Count
825    */
826   mt_get->mt_erreg = 0;
827 
828   /*
829    * File Number
830    */
831   mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
832 
833   /*
834    * Block Number
835    */
836   mt_get->mt_blkno
837       = (__daddr_t)(pHandleInfo->bBlockValid
838                         ? pos_info.BlockNumber - pHandleInfo->ullFileStart
839                         : (ULONGLONG)-1);
840 
841   return 0;
842 }
843 
844 #define SERVICEACTION_SHORT_FORM_BLOCKID 0
845 #define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
846 #define SERVICEACTION_LONG_FORM 6
847 #define SERVICEACTION_EXTENDED_FORM 8
848 
849 typedef struct _SCSI_READ_POSITION_SHORT_BUFFER {
850   UCHAR : 1;
851   UCHAR PERR : 1;
852   UCHAR BPU : 1;
853   UCHAR : 1;
854   UCHAR BYCU : 1;
855   UCHAR BCU : 1;
856   UCHAR EOP : 1;
857   UCHAR BOP : 1;
858   UCHAR Partition;
859   UCHAR Reserved1[2];
860   UCHAR FirstBlock[4];
861   UCHAR LastBlock[4];
862   UCHAR Reserved2;
863   UCHAR NumberBufferBlocks[3];
864   UCHAR NumberBufferBytes[4];
865 } SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
866 
867 typedef struct _SCSI_READ_POSITION_LONG_BUFFER {
868   UCHAR : 2;
869   UCHAR BPU : 1;
870   UCHAR MPU : 1;
871   UCHAR : 2;
872   UCHAR EOP : 1;
873   UCHAR BOP : 1;
874   UCHAR Reserved3[3];
875   UCHAR Partition[4];
876   UCHAR BlockNumber[8];
877   UCHAR FileNumber[8];
878   UCHAR SetNumber[8];
879 } SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
880 
881 typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER {
882   UCHAR : 1;
883   UCHAR PERR : 1;
884   UCHAR LOPU : 1;
885   UCHAR : 1;
886   UCHAR BYCU : 1;
887   UCHAR LOCU : 1;
888   UCHAR EOP : 1;
889   UCHAR BOP : 1;
890   UCHAR Partition;
891   UCHAR AdditionalLength[2];
892   UCHAR Reserved1;
893   UCHAR NumberBufferObjects[3];
894   UCHAR FirstLogicalObject[8];
895   UCHAR LastLogicalObject[8];
896   UCHAR NumberBufferObjectBytes[8];
897 } SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
898 
899 typedef union _READ_POSITION_RESULT {
900   SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
901   SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
902   SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
903 } READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
904 
GetTapePositionInfo(HANDLE hDevice,PTAPE_POSITION_INFO TapePositionInfo)905 static DWORD GetTapePositionInfo(HANDLE hDevice,
906                                  PTAPE_POSITION_INFO TapePositionInfo)
907 {
908   PSCSI_PASS_THROUGH ScsiPassThrough;
909   BOOL bResult;
910   DWORD dwBytesReturned;
911 
912   const DWORD dwBufferSize
913       = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
914 
915   memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
916 
917   ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
918 
919   for (int pass = 0; pass < 2; pass++) {
920     memset(ScsiPassThrough, 0, dwBufferSize);
921 
922     ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
923     ScsiPassThrough->CdbLength = 10;
924     ScsiPassThrough->SenseInfoLength = 28;
925     ScsiPassThrough->DataIn = 1;
926     ScsiPassThrough->DataTransferLength
927         = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
928     ScsiPassThrough->TimeOutValue = 1000;
929     ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
930     ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
931     ScsiPassThrough->Cdb[0] = 0x34; /* READ POSITION */
932 
933     switch (pass) {
934       case 0:
935         ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
936         break;
937       case 1:
938         ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
939         break;
940     }
941 
942     bResult = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, ScsiPassThrough,
943                               sizeof(SCSI_PASS_THROUGH), ScsiPassThrough,
944                               dwBufferSize, &dwBytesReturned, NULL);
945 
946     if (bResult
947         && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus)
948                                + sizeof(ScsiPassThrough->ScsiStatus))) {
949       if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
950         PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)(
951             (PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
952 
953         switch (pass) {
954           case 0: {
955             /* SERVICEACTION_LONG_FORM */
956             TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
957             TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
958 
959             if (!TapePositionInfo->PartitionBlockValid) {
960               TapePositionInfo->PartitionBlockValid
961                   = !pPosResult->LongBuffer.BPU;
962               if (TapePositionInfo->PartitionBlockValid) {
963                 TapePositionInfo->Partition
964                     = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
965                 TapePositionInfo->BlockNumber
966                     = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
967               }
968             }
969 
970             TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
971             if (TapePositionInfo->FileSetValid) {
972               TapePositionInfo->FileNumber
973                   = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
974               TapePositionInfo->SetNumber
975                   = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
976             }
977             break;
978           }
979           case 1: {
980             /*
981              * SERVICEACTION_SHORT_FORM_BLOCKID
982              */
983             // pPosResult->ShortBuffer.PERR;
984             // pPosResult->ShortBuffer.BYCU;
985             // pPosResult->ShortBuffer.BCU;
986             TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
987             TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
988 
989             if (!TapePositionInfo->PartitionBlockValid) {
990               TapePositionInfo->PartitionBlockValid
991                   = !pPosResult->ShortBuffer.BPU;
992               if (TapePositionInfo->PartitionBlockValid) {
993                 TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
994                 TapePositionInfo->BlockNumber
995                     = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
996               }
997             }
998             // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
999             // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1000             // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1001             break;
1002           }
1003           default:
1004             break;
1005         }
1006       }
1007     }
1008   }
1009   free(ScsiPassThrough);
1010 
1011   return NO_ERROR;
1012 }
1013 
GetDensityBlockSize(HANDLE hDevice,DWORD * pdwDensity,DWORD * pdwBlockSize)1014 static DWORD GetDensityBlockSize(HANDLE hDevice,
1015                                  DWORD* pdwDensity,
1016                                  DWORD* pdwBlockSize)
1017 {
1018   DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1019   GET_MEDIA_TYPES* pGetMediaTypes = (GET_MEDIA_TYPES*)malloc(dwBufferSize);
1020   BOOL bResult;
1021   DWORD dwResult;
1022 
1023   if (pGetMediaTypes == NULL) { return ERROR_OUTOFMEMORY; }
1024 
1025   do {
1026     DWORD dwBytesReturned;
1027 
1028     bResult = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL,
1029                               0, (LPVOID)pGetMediaTypes, dwBufferSize,
1030                               &dwBytesReturned, NULL);
1031 
1032     if (!bResult) {
1033       dwResult = GetLastError();
1034 
1035       if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1036         free(pGetMediaTypes);
1037         return dwResult;
1038       }
1039 
1040       dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1041       GET_MEDIA_TYPES* pNewBuffer
1042           = (GET_MEDIA_TYPES*)realloc(pGetMediaTypes, dwBufferSize);
1043       if (pNewBuffer != pGetMediaTypes) {
1044         free(pGetMediaTypes);
1045 
1046         if (pNewBuffer == NULL) { return ERROR_OUTOFMEMORY; }
1047 
1048         pGetMediaTypes = pNewBuffer;
1049       }
1050     }
1051   } while (!bResult);
1052 
1053   if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1054     free(pGetMediaTypes);
1055     return ERROR_BAD_DEVICE;
1056   }
1057 
1058   for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount;
1059        idxMedia++) {
1060     if (pGetMediaTypes->MediaInfo[idxMedia]
1061             .DeviceSpecific.TapeInfo.MediaCharacteristics
1062         & MEDIA_CURRENTLY_MOUNTED) {
1063       if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType
1064           == BusTypeScsi) {
1065         *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia]
1066                           .DeviceSpecific.TapeInfo.BusSpecificData
1067                           .ScsiInformation.DensityCode;
1068       } else {
1069         *pdwDensity = 0;
1070       }
1071 
1072       *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia]
1073                           .DeviceSpecific.TapeInfo.CurrentBlockSize;
1074 
1075       free(pGetMediaTypes);
1076 
1077       return NO_ERROR;
1078     }
1079   }
1080 
1081   free(pGetMediaTypes);
1082 
1083   return ERROR_NO_MEDIA_IN_DRIVE;
1084 }
1085 
TapePos(struct mtpos * mt_pos)1086 int win32_tape_device::TapePos(struct mtpos* mt_pos)
1087 {
1088   DWORD partition;
1089   DWORD offset;
1090   DWORD offsetHi;
1091   BOOL result;
1092 
1093   if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3)
1094       || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1095     errno = EBADF;
1096     return -1;
1097   }
1098 
1099   PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
1100 
1101   result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK,
1102                            &partition, &offset, &offsetHi);
1103   if (result == NO_ERROR) {
1104     mt_pos->mt_blkno = offset;
1105     return 0;
1106   }
1107 
1108   return -1;
1109 }
1110 
win32_tape_device()1111 win32_tape_device::win32_tape_device()
1112 {
1113   SetCap(CAP_ADJWRITESIZE); /* Adjust write size to min/max */
1114 }
1115 
1116 } /* namespace storagedaemon */
1117