]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/stored/mtops.cpp
Update the Microsoft Visual Studio build to match the MinGW32 build.
[bacula/bacula] / bacula / src / win32 / stored / mtops.cpp
1 /*
2  * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
3  *
4  * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
5  *
6  * Version $Id$
7  *
8  * Copyright (C) 2006 Kern Sibbald
9  *
10  * This file was contributed to the Bacula project by Robert Nelson.
11  *
12  * Robert Nelson has been granted a perpetual, worldwide,
13  * non-exclusive, no-charge, royalty-free, irrevocable copyright
14  * license to reproduce, prepare derivative works of, publicly
15  * display, publicly perform, sublicense, and distribute the original
16  * work contributed by Robert Nelson to the Bacula project in source 
17  * or object form.
18  *
19  * If you wish to license contributions from Robert Nelson
20  * under an alternate open source license please contact
21  * Robert Nelson <robertn@the-nelsons.org>.
22  */
23 /*
24    Copyright (C) 2006 Kern Sibbald
25
26    This program is free software; you can redistribute it and/or
27    modify it under the terms of the GNU General Public License
28    version 2 as amended with additional clauses defined in the
29    file LICENSE in the main source directory.
30
31    This program is distributed in the hope that it will be useful,
32    but WITHOUT ANY WARRANTY; without even the implied warranty of
33    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
34    the file LICENSE for additional details.
35
36  */
37
38 #include <stdarg.h>
39 #include <stddef.h>
40
41 #include "bacula.h"                   /* pull in global headers */
42 #include "stored.h"                   /* pull in Storage Deamon headers */
43
44 #include "sys/mtio.h"
45 #if defined(_MSC_VER)
46 #include <winioctl.h>
47 #else
48 #include <ntddstor.h>
49 #endif
50 #include <ntddscsi.h>
51
52 //
53 // SCSI bus status codes.
54 //
55
56 #define SCSISTAT_GOOD                  0x00
57 #define SCSISTAT_CHECK_CONDITION       0x02
58 #define SCSISTAT_CONDITION_MET         0x04
59 #define SCSISTAT_BUSY                  0x08
60 #define SCSISTAT_INTERMEDIATE          0x10
61 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
62 #define SCSISTAT_RESERVATION_CONFLICT  0x18
63 #define SCSISTAT_COMMAND_TERMINATED    0x22
64 #define SCSISTAT_QUEUE_FULL            0x28
65
66 inline   SHORT  Read16BitSigned(const unsigned char *pValue)
67 {
68    return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
69 }
70
71 inline   USHORT  Read16BitUnsigned(const unsigned char *pValue)
72 {
73    return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
74 }
75
76 inline   LONG  Read24BitSigned(const unsigned char *pValue)
77 {
78    return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
79                    (ULONG)pValue[2])) << 8 >> 8;
80 }
81
82 inline   ULONG  Read24BitUnsigned(const unsigned char *pValue)
83 {
84    return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
85 }
86
87 inline   LONG  Read32BitSigned(const unsigned char *pValue)
88 {
89    return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
90                  ((ULONG)pValue[2] << 8) |   (ULONG)pValue[3]);
91 }
92
93 inline   ULONG  Read32BitUnsigned(const unsigned char *pValue)
94 {
95    return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
96            ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
97 }
98
99 inline   LONGLONG  Read64BitSigned(const unsigned char *pValue)
100 {
101    return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
102                      ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
103                      ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
104                      ((ULONGLONG)pValue[6] <<  8) |  (ULONGLONG)pValue[7]);
105 }
106
107 inline   ULONGLONG  Read64BitUnsigned(const unsigned char *pValue)
108 {
109    return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
110                      ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
111                      ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
112                      ((ULONGLONG)pValue[6] <<  8) | (ULONGLONG)pValue[7]);
113 }
114
115 typedef  struct   _TAPE_POSITION_INFO
116 {
117    UCHAR       AtPartitionStart:1;
118    UCHAR       AtPartitionEnd:1;
119    UCHAR       PartitionBlockValid:1;
120    UCHAR       FileSetValid:1;
121    UCHAR       :4;
122    UCHAR       Reserved1[3];
123    ULONG       Partition;
124    ULONGLONG   BlockNumber;
125    ULONGLONG   FileNumber;
126    ULONGLONG   SetNumber;
127 }  TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
128
129 typedef  struct   _TAPE_HANDLE_INFO
130 {
131    HANDLE      OSHandle;
132    bool        bEOD;
133    bool        bEOF;
134    bool        bEOT;
135    bool        bBlockValid;
136    ULONG       FeaturesLow;
137    ULONG       FeaturesHigh;
138    ULONG       ulFile;
139    ULONGLONG   ullFileStart;
140
141 }  TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
142
143 TAPE_HANDLE_INFO TapeHandleTable[] =
144 {
145    { INVALID_HANDLE_VALUE },
146    { INVALID_HANDLE_VALUE },
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 };
162
163 #define  NUMBER_HANDLE_ENTRIES      (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
164
165 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
166 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
167
168 int tape_get(int fd, struct mtget *mt_get);
169 int tape_op(int fd, struct mtop *mt_com);
170 int tape_pos(int fd, struct mtpos *mt_pos);
171
172 int
173 tape_open(const char *file, int flags, int mode)
174 {
175    HANDLE hDevice = INVALID_HANDLE_VALUE;
176    char szDeviceName[256] = "\\\\.\\";
177    int  idxFile;
178    DWORD dwResult;
179
180    for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
181       if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
182          break;
183       }
184    }
185
186    if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
187       return EMFILE;
188    }
189
190    memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
191
192    if (file[0] != '\\' && file[0] != '/') {
193        bstrncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
194    } else {
195        bstrncpy(&szDeviceName[0], file, sizeof(szDeviceName));
196    }
197
198    hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
199
200    if (hDevice != INVALID_HANDLE_VALUE) {
201       PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[idxFile];
202
203       memset(pHandleInfo, 0, sizeof(*pHandleInfo));
204
205       pHandleInfo->OSHandle = hDevice;
206
207       TAPE_GET_DRIVE_PARAMETERS  TapeDriveParameters;
208       DWORD    dwSize = sizeof(TapeDriveParameters);
209
210       dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
211       if (dwResult == NO_ERROR) {
212          pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
213          pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
214       }
215
216       TAPE_POSITION_INFO TapePositionInfo;
217
218       dwResult =  GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
219
220       if (dwResult == NO_ERROR) {
221          if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
222              (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
223             pHandleInfo->ulFile = 0;
224             pHandleInfo->bBlockValid = true;
225             pHandleInfo->ullFileStart = 0;
226          } else if (TapePositionInfo.FileSetValid) {
227             pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
228          }
229       }
230    } else {
231       DWORD dwError = GetLastError();
232
233       switch (dwError) {
234       case ERROR_FILE_NOT_FOUND:
235       case ERROR_PATH_NOT_FOUND:
236          errno = ENOENT;
237          break;
238
239       case ERROR_TOO_MANY_OPEN_FILES:
240          errno = EMFILE;
241          break;
242
243       default:
244       case ERROR_ACCESS_DENIED:
245       case ERROR_SHARING_VIOLATION:
246       case ERROR_LOCK_VIOLATION:
247       case ERROR_INVALID_NAME:
248          errno = EACCES;
249          break;
250
251       case ERROR_FILE_EXISTS:
252          errno = EEXIST;
253          break;
254
255       case ERROR_INVALID_PARAMETER:
256          errno = EINVAL;
257          break;
258       }
259
260       return(int) -1;
261    }
262
263    return (int)idxFile + 3;
264 }
265
266 int
267 tape_read(int fd, void *buffer, unsigned int count)
268 {
269    if (buffer == NULL) {
270       errno = EINVAL;
271       return -1;
272    }
273
274    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
275        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
276    {
277       errno = EBADF;
278       return -1;
279    }
280
281    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
282
283    DWORD bytes_read;
284    BOOL bResult;
285
286    bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
287
288    if (bResult) {
289       pHandleInfo->bEOF = false;
290       pHandleInfo->bEOT = false;
291       pHandleInfo->bEOD = false;
292       return bytes_read;
293    } else {
294       int iReturnValue = 0;
295       DWORD last_error = GetLastError();
296
297       switch (last_error) {
298
299       case ERROR_FILEMARK_DETECTED:
300          pHandleInfo->bEOF = true;
301          break;
302
303       case ERROR_END_OF_MEDIA:
304          pHandleInfo->bEOT = true;
305          break;
306
307       case ERROR_NO_MEDIA_IN_DRIVE:
308          pHandleInfo->bEOF = false;
309          pHandleInfo->bEOT = false;
310          pHandleInfo->bEOD = false;
311          errno = ENOMEDIUM;
312          iReturnValue = -1;
313          break;
314
315       case ERROR_NO_DATA_DETECTED:
316          pHandleInfo->bEOD = true;
317          break;
318
319       case ERROR_INVALID_HANDLE:
320       case ERROR_ACCESS_DENIED:
321       case ERROR_LOCK_VIOLATION:
322          errno = EBADF;
323          iReturnValue = -1;
324          break;
325
326       default:
327          pHandleInfo->bEOF = false;
328          pHandleInfo->bEOT = false;
329          pHandleInfo->bEOD = false;
330          errno = EIO;
331          iReturnValue = -1;
332       }
333
334       return iReturnValue;
335    }
336 }
337
338 int
339 tape_write(int fd, const void *buffer, unsigned int count)
340 {
341    if (buffer == NULL) {
342       errno = EINVAL;
343       return -1;
344    }
345
346    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
347    {
348       errno = EBADF;
349       return -1;
350    }
351
352    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
353
354    DWORD bytes_written;
355    BOOL bResult;
356
357    bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
358
359    if (bResult) {
360       pHandleInfo->bEOF = false;
361       pHandleInfo->bEOT = false;
362       return bytes_written;
363    } else {
364       DWORD last_error = GetLastError();
365
366       switch (last_error) {
367       case ERROR_END_OF_MEDIA:
368       case ERROR_DISK_FULL:
369          pHandleInfo->bEOT = true;
370          errno = ENOSPC;
371          break;
372
373       case ERROR_NO_MEDIA_IN_DRIVE:
374          pHandleInfo->bEOF = false;
375          pHandleInfo->bEOT = false;
376          pHandleInfo->bEOD = false;
377          errno = ENOMEDIUM;
378          break;
379
380       case ERROR_INVALID_HANDLE:
381       case ERROR_ACCESS_DENIED:
382          errno = EBADF;
383          break;
384
385       default:
386          pHandleInfo->bEOF = false;
387          pHandleInfo->bEOT = false;
388          pHandleInfo->bEOD = false;
389          errno = EIO;
390          break;
391       }
392       return -1;
393    }
394 }
395
396 int
397 tape_close(int fd)
398 {
399    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
400       TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
401       errno = EBADF;
402       return -1;
403    }
404
405    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
406
407    if (!CloseHandle(pHandleInfo->OSHandle)) {
408       pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
409       errno = EBADF;
410       return -1;
411    }
412
413    pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
414
415    return 0;
416 }
417
418 int
419 tape_ioctl(int fd, unsigned long int request, ...)
420 {
421    va_list argp;
422    int result;
423
424    va_start(argp, request);
425
426    switch (request) {
427    case MTIOCTOP:
428       result = tape_op(fd, va_arg(argp, mtop *));
429       break;
430
431    case MTIOCGET:
432       result = tape_get(fd, va_arg(argp, mtget *));
433       break;
434
435    case MTIOCPOS:
436       result = tape_pos(fd, va_arg(argp, mtpos *));
437       break;
438
439    default:
440       errno = ENOTTY;
441       result = -1;
442       break;
443    }
444
445    va_end(argp);
446
447    return result;
448 }
449
450 int tape_op(int fd, struct mtop *mt_com)
451 {
452    DWORD result = NO_ERROR;
453    int   index;
454
455    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
456        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
457    {
458       errno = EBADF;
459       return -1;
460    }
461
462    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
463
464    switch (mt_com->mt_op)
465    {
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          {
714             SetDriveParameters.ECC = GetDriveParameters.ECC;
715             SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
716             SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
717             SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
718             SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
719
720             result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
721          }
722       }
723       break;
724
725    case MTSETPART:
726       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
727       break;
728
729    case MTMKPART:
730       if (mt_com->mt_count == 0)
731       {
732          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
733       }
734       else
735       {
736          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
737       }
738       break;
739    }
740
741    if ((result == NO_ERROR && pHandleInfo->bEOF) || 
742        (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
743
744       TAPE_POSITION_INFO TapePositionInfo;
745
746       if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
747          pHandleInfo->bBlockValid = true;
748          pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
749       }
750    }
751
752    switch (result) {
753    case NO_ERROR:
754    case (DWORD)-1:   /* Error has already been translated into errno */
755       break;
756
757    default:
758    case ERROR_FILEMARK_DETECTED:
759       errno = EIO;
760       break;
761
762    case ERROR_END_OF_MEDIA:
763       pHandleInfo->bEOT = true;
764       errno = EIO;
765       break;
766
767    case ERROR_NO_DATA_DETECTED:
768       pHandleInfo->bEOD = true;
769       errno = EIO;
770       break;
771
772    case ERROR_NO_MEDIA_IN_DRIVE:
773       pHandleInfo->bEOF = false;
774       pHandleInfo->bEOT = false;
775       pHandleInfo->bEOD = false;
776       errno = ENOMEDIUM;
777       break;
778
779    case ERROR_INVALID_HANDLE:
780    case ERROR_ACCESS_DENIED:
781    case ERROR_LOCK_VIOLATION:
782       errno = EBADF;
783       break;
784    }
785
786    return result == NO_ERROR ? 0 : -1;
787 }
788
789 int tape_get(int fd, struct mtget *mt_get)
790 {
791    TAPE_POSITION_INFO pos_info;
792    BOOL result;
793
794    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
795        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
796       errno = EBADF;
797       return -1;
798    }
799
800    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
801
802    if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
803       return -1;
804    }
805
806    DWORD density = 0;
807    DWORD blocksize = 0;
808
809    result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
810
811    if (result != NO_ERROR) {
812       TAPE_GET_DRIVE_PARAMETERS drive_params;
813       DWORD size;
814
815       size = sizeof(drive_params);
816
817       result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
818
819       if (result == NO_ERROR) {
820          blocksize = drive_params.DefaultBlockSize;
821       }
822    }
823
824    mt_get->mt_type = MT_ISSCSI2;
825
826    // Partition #
827    mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
828
829    // Density / Block Size
830    mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
831                       ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
832
833    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
834
835    if (pHandleInfo->bEOF) {
836       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
837    }
838
839    if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
840       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
841    }
842
843    if (pHandleInfo->bEOT) {
844       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
845    }
846
847    if (pHandleInfo->bEOD) {
848       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
849    }
850
851    TAPE_GET_MEDIA_PARAMETERS  media_params;
852    DWORD size = sizeof(media_params);
853    
854    result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
855
856    if (result == NO_ERROR && media_params.WriteProtected) {
857       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
858    }
859
860    result = GetTapeStatus(pHandleInfo->OSHandle);
861
862    if (result != NO_ERROR) {
863       if (result == ERROR_NO_MEDIA_IN_DRIVE) {
864          mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
865       }
866    } else {
867       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
868    }
869
870    // Recovered Error Count
871    mt_get->mt_erreg = 0;
872
873    // File Number
874    mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
875
876    // Block Number
877    mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
878
879    return 0;
880 }
881
882 #define  SERVICEACTION_SHORT_FORM_BLOCKID             0
883 #define  SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC     1
884 #define  SERVICEACTION_LONG_FORM                      6
885 #define  SERVICEACTION_EXTENDED_FORM                  8
886
887
888 typedef  struct   _SCSI_READ_POSITION_SHORT_BUFFER
889 {
890    UCHAR    :1;
891    UCHAR    PERR:1;
892    UCHAR    BPU:1;
893    UCHAR    :1;
894    UCHAR    BYCU:1;
895    UCHAR    BCU:1;
896    UCHAR    EOP:1;
897    UCHAR    BOP:1;
898    UCHAR    Partition;
899    UCHAR    Reserved1[2];
900    UCHAR    FirstBlock[4];
901    UCHAR    LastBlock[4];
902    UCHAR    Reserved2;
903    UCHAR    NumberBufferBlocks[3];
904    UCHAR    NumberBufferBytes[4];
905 }  SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
906
907 typedef  struct   _SCSI_READ_POSITION_LONG_BUFFER
908 {
909    UCHAR    :2;
910    UCHAR    BPU:1;
911    UCHAR    MPU:1;
912    UCHAR    :2;
913    UCHAR    EOP:1;
914    UCHAR    BOP:1;
915    UCHAR    Reserved3[3];
916    UCHAR    Partition[4];
917    UCHAR    BlockNumber[8];
918    UCHAR    FileNumber[8];
919    UCHAR    SetNumber[8];
920 }  SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
921
922 typedef  struct   _SCSI_READ_POSITION_EXTENDED_BUFFER
923 {
924    UCHAR    :1;
925    UCHAR    PERR:1;
926    UCHAR    LOPU:1;
927    UCHAR    :1;
928    UCHAR    BYCU:1;
929    UCHAR    LOCU:1;
930    UCHAR    EOP:1;
931    UCHAR    BOP:1;
932    UCHAR    Partition;
933    UCHAR    AdditionalLength[2];
934    UCHAR    Reserved1;
935    UCHAR    NumberBufferObjects[3];
936    UCHAR    FirstLogicalObject[8];
937    UCHAR    LastLogicalObject[8];
938    UCHAR    NumberBufferObjectBytes[8];
939 }  SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
940
941 typedef union _READ_POSITION_RESULT {
942    SCSI_READ_POSITION_SHORT_BUFFER     ShortBuffer;
943    SCSI_READ_POSITION_LONG_BUFFER      LongBuffer;
944    SCSI_READ_POSITION_EXTENDED_BUFFER  ExtendedBuffer;
945 }  READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
946
947 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
948 {
949    PSCSI_PASS_THROUGH   ScsiPassThrough;
950    BOOL                 bResult;
951    DWORD                dwBytesReturned;
952
953    const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
954
955    memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
956
957    ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
958
959    for (int pass = 0; pass < 2; pass++)
960    {
961       memset(ScsiPassThrough, 0, dwBufferSize);
962
963       ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
964
965       ScsiPassThrough->CdbLength = 10;
966       ScsiPassThrough->SenseInfoLength = 28;
967       ScsiPassThrough->DataIn = 1;
968       ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
969       ScsiPassThrough->TimeOutValue = 1000;
970       ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
971       ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
972
973       ScsiPassThrough->Cdb[0] = 0x34;  // READ POSITION
974
975       switch (pass)
976       {
977       case 0:
978          ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
979          break;
980
981       case 1:
982          ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
983          break;
984       }
985
986       bResult = DeviceIoControl( hDevice, 
987                                  IOCTL_SCSI_PASS_THROUGH, 
988                                  ScsiPassThrough, sizeof(SCSI_PASS_THROUGH), 
989                                  ScsiPassThrough, dwBufferSize, 
990                                  &dwBytesReturned, 
991                                  NULL);
992
993       if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
994          if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
995             PREAD_POSITION_RESULT   pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
996
997             switch (pass)
998             {
999             case 0:     // SERVICEACTION_LONG_FORM
1000                {
1001                   TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1002                   TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1003
1004                   if (!TapePositionInfo->PartitionBlockValid) {
1005                      TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1006
1007                      if (TapePositionInfo->PartitionBlockValid) {
1008                         TapePositionInfo->Partition =   Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1009                         TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1010                      }
1011                   }
1012
1013                   TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1014                   if (TapePositionInfo->FileSetValid) {
1015                      TapePositionInfo->FileNumber =  Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1016                      TapePositionInfo->SetNumber =   Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1017                   }
1018                }
1019                break;
1020
1021             case 1:     // SERVICEACTION_SHORT_FORM_BLOCKID
1022                {
1023                   // pPosResult->ShortBuffer.PERR;
1024                   // pPosResult->ShortBuffer.BYCU;
1025                   // pPosResult->ShortBuffer.BCU;
1026                   TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1027                   TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1028
1029                   if (!TapePositionInfo->PartitionBlockValid) {
1030                      TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1031
1032                      if (TapePositionInfo->PartitionBlockValid) {
1033                         TapePositionInfo->Partition =   pPosResult->ShortBuffer.Partition;
1034                         TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1035                      }
1036                   }
1037                   // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1038                   // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1039                   // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1040                }
1041                break;
1042             }
1043          }
1044       }
1045    }
1046    free(ScsiPassThrough);
1047
1048    return NO_ERROR;
1049 }
1050
1051 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1052 {
1053    DWORD             dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1054    GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1055    BOOL              bResult;
1056    DWORD             dwResult;
1057
1058    if (pGetMediaTypes == NULL) {
1059       return ERROR_OUTOFMEMORY;
1060    }
1061
1062    do {
1063       DWORD          dwBytesReturned;
1064       
1065       bResult = DeviceIoControl( hDevice, 
1066                                  IOCTL_STORAGE_GET_MEDIA_TYPES_EX, 
1067                                  NULL, 0, 
1068                                  (LPVOID)pGetMediaTypes, dwBufferSize, 
1069                                  &dwBytesReturned, 
1070                                  NULL);
1071
1072       if (!bResult) {
1073          dwResult = GetLastError();
1074
1075          if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1076             free(pGetMediaTypes);
1077             return dwResult;
1078          }
1079
1080          dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1081
1082          GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1083
1084          if (pNewBuffer != pGetMediaTypes) {
1085             free(pGetMediaTypes);
1086
1087             if (pNewBuffer == NULL) {
1088                return ERROR_OUTOFMEMORY;
1089             }
1090
1091             pGetMediaTypes = pNewBuffer;
1092          }
1093       }
1094    } while (!bResult);
1095
1096    if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1097       free(pGetMediaTypes);
1098       return ERROR_BAD_DEVICE;
1099    }
1100
1101    for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1102
1103       if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1104
1105          if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1106             *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1107          } else {
1108             *pdwDensity = 0;
1109          }
1110
1111          *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1112
1113          free(pGetMediaTypes);
1114
1115          return NO_ERROR;
1116       }
1117    }
1118
1119    free(pGetMediaTypes);
1120
1121    return ERROR_NO_MEDIA_IN_DRIVE;
1122 }
1123
1124 int tape_pos(int fd, struct mtpos *mt_pos)
1125 {
1126    DWORD partition;
1127    DWORD offset;
1128    DWORD offsetHi;
1129    BOOL result;
1130
1131    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
1132        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1133       errno = EBADF;
1134       return -1;
1135    }
1136
1137    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
1138
1139    result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1140    if (result == NO_ERROR) {
1141       mt_pos->mt_blkno = offset;
1142       return 0;
1143    }
1144
1145    return -1;
1146 }