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