]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/stored/win_tape_device.cpp
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / win32 / stored / win_tape_device.cpp
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 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
68 static inline SHORT Read16BitSigned(const unsigned char *pValue)
69 {
70    return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
71 }
72
73 static inline USHORT Read16BitUnsigned(const unsigned char *pValue)
74 {
75    return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
76 }
77
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
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
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
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
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
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
170 win_tape_device::win_tape_device()
171 {
172    m_fd = -1;
173 }
174
175 win_tape_device::~win_tape_device()
176 { }
177
178 int
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
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
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
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
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
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
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
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
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
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 }