]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/stored/mtops.cpp
kes Add context menu for floating a window.
[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  * This file was contributed to the Bacula project by Robert Nelson.
9  *
10  * Robert Nelson has been granted a perpetual, worldwide,
11  * non-exclusive, no-charge, royalty-free, irrevocable copyright
12  * license to reproduce, prepare derivative works of, publicly
13  * display, publicly perform, sublicense, and distribute the original
14  * work contributed by Robert Nelson to the Bacula project in source 
15  * or object form.
16  *
17  * If you wish to license contributions from Robert Nelson
18  * under an alternate open source license please contact
19  * Robert Nelson <robertn@the-nelsons.org>.
20  */
21 /*
22    Bacula® - The Network Backup Solution
23
24    Copyright (C) 2006-2006 Free Software Foundation Europe e.V.
25
26    The main author of Bacula is Kern Sibbald, with contributions from
27    many others, a complete list can be found in the file AUTHORS.
28    This program is Free Software; you can redistribute it and/or
29    modify it under the terms of version two of the GNU General Public
30    License as published by the Free Software Foundation plus additions
31    that are listed in the file LICENSE.
32
33    This program is distributed in the hope that it will be useful, but
34    WITHOUT ANY WARRANTY; without even the implied warranty of
35    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36    General Public License for more details.
37
38    You should have received a copy of the GNU General Public License
39    along with this program; if not, write to the Free Software
40    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
41    02110-1301, USA.
42
43    Bacula® is a registered trademark of John Walker.
44    The licensor of Bacula is the Free Software Foundation Europe
45    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
46    Switzerland, email:ftf@fsfeurope.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 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
177 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
178
179 int tape_get(int fd, struct mtget *mt_get);
180 int tape_op(int fd, struct mtop *mt_com);
181 int tape_pos(int fd, struct mtpos *mt_pos);
182
183 int
184 tape_open(const char *file, int flags, int mode)
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 int
278 tape_read(int fd, void *buffer, unsigned int count)
279 {
280    if (buffer == NULL) {
281       errno = EINVAL;
282       return -1;
283    }
284
285    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
286        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
287    {
288       errno = EBADF;
289       return -1;
290    }
291
292    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
293
294    DWORD bytes_read;
295    BOOL bResult;
296
297    bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
298
299    if (bResult) {
300       pHandleInfo->bEOF = false;
301       pHandleInfo->bEOT = false;
302       pHandleInfo->bEOD = false;
303       return bytes_read;
304    } else {
305       int iReturnValue = 0;
306       DWORD last_error = GetLastError();
307
308       switch (last_error) {
309
310       case ERROR_FILEMARK_DETECTED:
311          pHandleInfo->bEOF = true;
312          break;
313
314       case ERROR_END_OF_MEDIA:
315          pHandleInfo->bEOT = true;
316          break;
317
318       case ERROR_NO_MEDIA_IN_DRIVE:
319          pHandleInfo->bEOF = false;
320          pHandleInfo->bEOT = false;
321          pHandleInfo->bEOD = false;
322          errno = ENOMEDIUM;
323          iReturnValue = -1;
324          break;
325
326       case ERROR_NO_DATA_DETECTED:
327          pHandleInfo->bEOD = true;
328          break;
329
330       case ERROR_INVALID_HANDLE:
331       case ERROR_ACCESS_DENIED:
332       case ERROR_LOCK_VIOLATION:
333          errno = EBADF;
334          iReturnValue = -1;
335          break;
336
337       default:
338          pHandleInfo->bEOF = false;
339          pHandleInfo->bEOT = false;
340          pHandleInfo->bEOD = false;
341          errno = EIO;
342          iReturnValue = -1;
343       }
344
345       return iReturnValue;
346    }
347 }
348
349 int
350 tape_write(int fd, const void *buffer, unsigned int count)
351 {
352    if (buffer == NULL) {
353       errno = EINVAL;
354       return -1;
355    }
356
357    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
358    {
359       errno = EBADF;
360       return -1;
361    }
362
363    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
364
365    DWORD bytes_written;
366    BOOL bResult;
367
368    bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
369
370    if (bResult) {
371       pHandleInfo->bEOF = false;
372       pHandleInfo->bEOT = false;
373       return bytes_written;
374    } else {
375       DWORD last_error = GetLastError();
376
377       switch (last_error) {
378       case ERROR_END_OF_MEDIA:
379       case ERROR_DISK_FULL:
380          pHandleInfo->bEOT = true;
381          errno = ENOSPC;
382          break;
383
384       case ERROR_NO_MEDIA_IN_DRIVE:
385          pHandleInfo->bEOF = false;
386          pHandleInfo->bEOT = false;
387          pHandleInfo->bEOD = false;
388          errno = ENOMEDIUM;
389          break;
390
391       case ERROR_INVALID_HANDLE:
392       case ERROR_ACCESS_DENIED:
393          errno = EBADF;
394          break;
395
396       default:
397          pHandleInfo->bEOF = false;
398          pHandleInfo->bEOT = false;
399          pHandleInfo->bEOD = false;
400          errno = EIO;
401          break;
402       }
403       return -1;
404    }
405 }
406
407 int
408 tape_close(int fd)
409 {
410    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
411       TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
412       errno = EBADF;
413       return -1;
414    }
415
416    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
417
418    if (!CloseHandle(pHandleInfo->OSHandle)) {
419       pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
420       errno = EBADF;
421       return -1;
422    }
423
424    pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
425
426    return 0;
427 }
428
429 int
430 tape_ioctl(int fd, unsigned long int request, ...)
431 {
432    va_list argp;
433    int result;
434
435    va_start(argp, request);
436
437    switch (request) {
438    case MTIOCTOP:
439       result = tape_op(fd, va_arg(argp, mtop *));
440       break;
441
442    case MTIOCGET:
443       result = tape_get(fd, va_arg(argp, mtget *));
444       break;
445
446    case MTIOCPOS:
447       result = tape_pos(fd, va_arg(argp, mtpos *));
448       break;
449
450    default:
451       errno = ENOTTY;
452       result = -1;
453       break;
454    }
455
456    va_end(argp);
457
458    return result;
459 }
460
461 int tape_op(int fd, struct mtop *mt_com)
462 {
463    DWORD result = NO_ERROR;
464    int   index;
465
466    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
467        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
468    {
469       errno = EBADF;
470       return -1;
471    }
472
473    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
474
475    switch (mt_com->mt_op)
476    {
477    case MTRESET:
478    case MTNOP:
479    case MTSETDRVBUFFER:
480       break;
481
482    default:
483    case MTRAS1:
484    case MTRAS2:
485    case MTRAS3:
486    case MTSETDENSITY:
487       errno = ENOTTY;
488       result = (DWORD)-1;
489       break;
490
491    case MTFSF:
492       for (index = 0; index < mt_com->mt_count; index++) {
493          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
494          if (result == NO_ERROR) {
495             pHandleInfo->ulFile++;
496             pHandleInfo->bEOF = true;
497             pHandleInfo->bEOT = false;
498          }
499       }
500       break;
501
502    case MTBSF:
503       for (index = 0; index < mt_com->mt_count; index++) {
504          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
505          if (result == NO_ERROR) {
506             pHandleInfo->ulFile--;
507             pHandleInfo->bBlockValid = false;
508             pHandleInfo->bEOD = false;
509             pHandleInfo->bEOF = false;
510             pHandleInfo->bEOT = false;
511          }
512       }
513       break;
514
515    case MTFSR:
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->bEOF = true;
523       }
524       break;
525
526    case MTBSR:
527       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
528       if (result == NO_ERROR) {
529          pHandleInfo->bEOD = false;
530          pHandleInfo->bEOF = false;
531          pHandleInfo->bEOT = false;
532       } else if (result == ERROR_FILEMARK_DETECTED) {
533          pHandleInfo->ulFile--;
534          pHandleInfo->bBlockValid = false;
535          pHandleInfo->bEOD = false;
536          pHandleInfo->bEOF = false;
537          pHandleInfo->bEOT = false;
538       }
539       break;
540
541    case MTWEOF:
542       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
543       if (result == NO_ERROR) {
544          pHandleInfo->bEOF = true;
545          pHandleInfo->bEOT = false;
546          pHandleInfo->ulFile += mt_com->mt_count;
547          pHandleInfo->bBlockValid = true;
548          pHandleInfo->ullFileStart = 0;
549       }
550       break;
551
552    case MTREW:
553       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
554       if (result == NO_ERROR) {
555          pHandleInfo->bEOD = false;
556          pHandleInfo->bEOF = false;
557          pHandleInfo->bEOT = false;
558          pHandleInfo->ulFile = 0;
559          pHandleInfo->bBlockValid = true;
560          pHandleInfo->ullFileStart = 0;
561       }
562       break;
563
564    case MTOFFL:
565       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
566       if (result == NO_ERROR) {
567          pHandleInfo->bEOD = false;
568          pHandleInfo->bEOF = false;
569          pHandleInfo->bEOT = false;
570          pHandleInfo->ulFile = 0;
571          pHandleInfo->ullFileStart = 0;
572       }
573       break;
574
575    case MTRETEN:
576       result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
577       if (result == NO_ERROR) {
578          pHandleInfo->bEOD = false;
579          pHandleInfo->bEOF = false;
580          pHandleInfo->bEOT = false;
581          pHandleInfo->ulFile = 0;
582          pHandleInfo->bBlockValid = true;
583          pHandleInfo->ullFileStart = 0;
584       }
585       break;
586
587    case MTBSFM:
588       for (index = 0; index < mt_com->mt_count; index++) {
589          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
590          if (result == NO_ERROR) {
591             result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
592             pHandleInfo->bEOD = false;
593             pHandleInfo->bEOF = false;
594             pHandleInfo->bEOT = false;
595          }
596       }
597       break;
598
599    case MTFSFM:
600       for (index = 0; index < mt_com->mt_count; index++) {
601          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
602          if (result == NO_ERROR) {
603             result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
604             pHandleInfo->bEOD = false;
605             pHandleInfo->bEOF = false;
606             pHandleInfo->bEOT = false;
607          }
608       }
609       break;
610
611    case MTEOM:
612       for ( ; ; ) {
613          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
614          if (result != NO_ERROR) {
615             pHandleInfo->bEOF = false;
616
617             if (result == ERROR_END_OF_MEDIA) {
618                pHandleInfo->bEOD = true;
619                pHandleInfo->bEOT = true;
620                return 0;
621             }
622             if (result == ERROR_NO_DATA_DETECTED) {
623                pHandleInfo->bEOD = true;
624                pHandleInfo->bEOT = false;
625                return 0;
626             }
627             break;
628          } else {
629             pHandleInfo->bEOF = true;
630             pHandleInfo->ulFile++;
631          }
632       }
633       break;
634
635    case MTERASE:
636       result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
637       if (result == NO_ERROR) {
638          pHandleInfo->bEOD = true;
639          pHandleInfo->bEOF = false;
640          pHandleInfo->bEOT = false;
641          pHandleInfo->ulFile = 0;
642          pHandleInfo->bBlockValid = true;
643          pHandleInfo->ullFileStart = 0;
644       }
645       break;
646
647    case MTSETBLK:
648       {
649          TAPE_SET_MEDIA_PARAMETERS  SetMediaParameters;
650
651          SetMediaParameters.BlockSize = mt_com->mt_count;
652          result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
653       }
654       break;
655
656    case MTSEEK:
657       {
658          TAPE_POSITION_INFO   TapePositionInfo;
659
660          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
661
662          memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
663          DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
664          if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
665             pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
666          } else {
667             pHandleInfo->ulFile = ~0U;
668          }
669       }
670       break;
671
672    case MTTELL:
673       {
674          DWORD partition;
675          DWORD offset;
676          DWORD offsetHi;
677
678          result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
679          if (result == NO_ERROR) {
680             return offset;
681          }
682       }
683       break;
684
685    case MTFSS:
686       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
687       break;
688
689    case MTBSS:
690       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
691       break;
692
693    case MTWSM:
694       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
695       break;
696
697    case MTLOCK:
698       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
699       break;
700
701    case MTUNLOCK:
702       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
703       break;
704
705    case MTLOAD:
706       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
707       break;
708
709    case MTUNLOAD:
710       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
711       break;
712
713    case MTCOMPRESSION:
714       {
715          TAPE_GET_DRIVE_PARAMETERS  GetDriveParameters;
716          TAPE_SET_DRIVE_PARAMETERS  SetDriveParameters;
717          DWORD                      size;
718
719          size = sizeof(GetDriveParameters);
720
721          result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
722
723          if (result == NO_ERROR)
724          {
725             SetDriveParameters.ECC = GetDriveParameters.ECC;
726             SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
727             SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
728             SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
729             SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
730
731             result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
732          }
733       }
734       break;
735
736    case MTSETPART:
737       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
738       break;
739
740    case MTMKPART:
741       if (mt_com->mt_count == 0)
742       {
743          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
744       }
745       else
746       {
747          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
748       }
749       break;
750    }
751
752    if ((result == NO_ERROR && pHandleInfo->bEOF) || 
753        (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
754
755       TAPE_POSITION_INFO TapePositionInfo;
756
757       if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
758          pHandleInfo->bBlockValid = true;
759          pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
760       }
761    }
762
763    switch (result) {
764    case NO_ERROR:
765    case (DWORD)-1:   /* Error has already been translated into errno */
766       break;
767
768    default:
769    case ERROR_FILEMARK_DETECTED:
770       errno = EIO;
771       break;
772
773    case ERROR_END_OF_MEDIA:
774       pHandleInfo->bEOT = true;
775       errno = EIO;
776       break;
777
778    case ERROR_NO_DATA_DETECTED:
779       pHandleInfo->bEOD = true;
780       errno = EIO;
781       break;
782
783    case ERROR_NO_MEDIA_IN_DRIVE:
784       pHandleInfo->bEOF = false;
785       pHandleInfo->bEOT = false;
786       pHandleInfo->bEOD = false;
787       errno = ENOMEDIUM;
788       break;
789
790    case ERROR_INVALID_HANDLE:
791    case ERROR_ACCESS_DENIED:
792    case ERROR_LOCK_VIOLATION:
793       errno = EBADF;
794       break;
795    }
796
797    return result == NO_ERROR ? 0 : -1;
798 }
799
800 int tape_get(int fd, struct mtget *mt_get)
801 {
802    TAPE_POSITION_INFO pos_info;
803    BOOL result;
804
805    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
806        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
807       errno = EBADF;
808       return -1;
809    }
810
811    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
812
813    if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
814       return -1;
815    }
816
817    DWORD density = 0;
818    DWORD blocksize = 0;
819
820    result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
821
822    if (result != NO_ERROR) {
823       TAPE_GET_DRIVE_PARAMETERS drive_params;
824       DWORD size;
825
826       size = sizeof(drive_params);
827
828       result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
829
830       if (result == NO_ERROR) {
831          blocksize = drive_params.DefaultBlockSize;
832       }
833    }
834
835    mt_get->mt_type = MT_ISSCSI2;
836
837    // Partition #
838    mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
839
840    // Density / Block Size
841    mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
842                       ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
843
844    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
845
846    if (pHandleInfo->bEOF) {
847       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
848    }
849
850    if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
851       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
852    }
853
854    if (pHandleInfo->bEOT) {
855       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
856    }
857
858    if (pHandleInfo->bEOD) {
859       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
860    }
861
862    TAPE_GET_MEDIA_PARAMETERS  media_params;
863    DWORD size = sizeof(media_params);
864    
865    result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
866
867    if (result == NO_ERROR && media_params.WriteProtected) {
868       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
869    }
870
871    result = GetTapeStatus(pHandleInfo->OSHandle);
872
873    if (result != NO_ERROR) {
874       if (result == ERROR_NO_MEDIA_IN_DRIVE) {
875          mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
876       }
877    } else {
878       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
879    }
880
881    // Recovered Error Count
882    mt_get->mt_erreg = 0;
883
884    // File Number
885    mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
886
887    // Block Number
888    mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
889
890    return 0;
891 }
892
893 #define  SERVICEACTION_SHORT_FORM_BLOCKID             0
894 #define  SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC     1
895 #define  SERVICEACTION_LONG_FORM                      6
896 #define  SERVICEACTION_EXTENDED_FORM                  8
897
898
899 typedef  struct   _SCSI_READ_POSITION_SHORT_BUFFER
900 {
901    UCHAR    :1;
902    UCHAR    PERR:1;
903    UCHAR    BPU:1;
904    UCHAR    :1;
905    UCHAR    BYCU:1;
906    UCHAR    BCU:1;
907    UCHAR    EOP:1;
908    UCHAR    BOP:1;
909    UCHAR    Partition;
910    UCHAR    Reserved1[2];
911    UCHAR    FirstBlock[4];
912    UCHAR    LastBlock[4];
913    UCHAR    Reserved2;
914    UCHAR    NumberBufferBlocks[3];
915    UCHAR    NumberBufferBytes[4];
916 }  SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
917
918 typedef  struct   _SCSI_READ_POSITION_LONG_BUFFER
919 {
920    UCHAR    :2;
921    UCHAR    BPU:1;
922    UCHAR    MPU:1;
923    UCHAR    :2;
924    UCHAR    EOP:1;
925    UCHAR    BOP:1;
926    UCHAR    Reserved3[3];
927    UCHAR    Partition[4];
928    UCHAR    BlockNumber[8];
929    UCHAR    FileNumber[8];
930    UCHAR    SetNumber[8];
931 }  SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
932
933 typedef  struct   _SCSI_READ_POSITION_EXTENDED_BUFFER
934 {
935    UCHAR    :1;
936    UCHAR    PERR:1;
937    UCHAR    LOPU:1;
938    UCHAR    :1;
939    UCHAR    BYCU:1;
940    UCHAR    LOCU:1;
941    UCHAR    EOP:1;
942    UCHAR    BOP:1;
943    UCHAR    Partition;
944    UCHAR    AdditionalLength[2];
945    UCHAR    Reserved1;
946    UCHAR    NumberBufferObjects[3];
947    UCHAR    FirstLogicalObject[8];
948    UCHAR    LastLogicalObject[8];
949    UCHAR    NumberBufferObjectBytes[8];
950 }  SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
951
952 typedef union _READ_POSITION_RESULT {
953    SCSI_READ_POSITION_SHORT_BUFFER     ShortBuffer;
954    SCSI_READ_POSITION_LONG_BUFFER      LongBuffer;
955    SCSI_READ_POSITION_EXTENDED_BUFFER  ExtendedBuffer;
956 }  READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
957
958 DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
959 {
960    PSCSI_PASS_THROUGH   ScsiPassThrough;
961    BOOL                 bResult;
962    DWORD                dwBytesReturned;
963
964    const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
965
966    memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
967
968    ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
969
970    for (int pass = 0; pass < 2; pass++)
971    {
972       memset(ScsiPassThrough, 0, dwBufferSize);
973
974       ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
975
976       ScsiPassThrough->CdbLength = 10;
977       ScsiPassThrough->SenseInfoLength = 28;
978       ScsiPassThrough->DataIn = 1;
979       ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
980       ScsiPassThrough->TimeOutValue = 1000;
981       ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
982       ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
983
984       ScsiPassThrough->Cdb[0] = 0x34;  // READ POSITION
985
986       switch (pass)
987       {
988       case 0:
989          ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
990          break;
991
992       case 1:
993          ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
994          break;
995       }
996
997       bResult = DeviceIoControl( hDevice, 
998                                  IOCTL_SCSI_PASS_THROUGH, 
999                                  ScsiPassThrough, sizeof(SCSI_PASS_THROUGH), 
1000                                  ScsiPassThrough, dwBufferSize, 
1001                                  &dwBytesReturned, 
1002                                  NULL);
1003
1004       if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
1005          if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
1006             PREAD_POSITION_RESULT   pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
1007
1008             switch (pass)
1009             {
1010             case 0:     // SERVICEACTION_LONG_FORM
1011                {
1012                   TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
1013                   TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
1014
1015                   if (!TapePositionInfo->PartitionBlockValid) {
1016                      TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
1017
1018                      if (TapePositionInfo->PartitionBlockValid) {
1019                         TapePositionInfo->Partition =   Read32BitUnsigned(pPosResult->LongBuffer.Partition);
1020                         TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
1021                      }
1022                   }
1023
1024                   TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
1025                   if (TapePositionInfo->FileSetValid) {
1026                      TapePositionInfo->FileNumber =  Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
1027                      TapePositionInfo->SetNumber =   Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
1028                   }
1029                }
1030                break;
1031
1032             case 1:     // SERVICEACTION_SHORT_FORM_BLOCKID
1033                {
1034                   // pPosResult->ShortBuffer.PERR;
1035                   // pPosResult->ShortBuffer.BYCU;
1036                   // pPosResult->ShortBuffer.BCU;
1037                   TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
1038                   TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
1039
1040                   if (!TapePositionInfo->PartitionBlockValid) {
1041                      TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
1042
1043                      if (TapePositionInfo->PartitionBlockValid) {
1044                         TapePositionInfo->Partition =   pPosResult->ShortBuffer.Partition;
1045                         TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
1046                      }
1047                   }
1048                   // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
1049                   // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
1050                   // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
1051                }
1052                break;
1053             }
1054          }
1055       }
1056    }
1057    free(ScsiPassThrough);
1058
1059    return NO_ERROR;
1060 }
1061
1062 DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
1063 {
1064    DWORD             dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
1065    GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
1066    BOOL              bResult;
1067    DWORD             dwResult;
1068
1069    if (pGetMediaTypes == NULL) {
1070       return ERROR_OUTOFMEMORY;
1071    }
1072
1073    do {
1074       DWORD          dwBytesReturned;
1075       
1076       bResult = DeviceIoControl( hDevice, 
1077                                  IOCTL_STORAGE_GET_MEDIA_TYPES_EX, 
1078                                  NULL, 0, 
1079                                  (LPVOID)pGetMediaTypes, dwBufferSize, 
1080                                  &dwBytesReturned, 
1081                                  NULL);
1082
1083       if (!bResult) {
1084          dwResult = GetLastError();
1085
1086          if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
1087             free(pGetMediaTypes);
1088             return dwResult;
1089          }
1090
1091          dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
1092
1093          GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
1094
1095          if (pNewBuffer != pGetMediaTypes) {
1096             free(pGetMediaTypes);
1097
1098             if (pNewBuffer == NULL) {
1099                return ERROR_OUTOFMEMORY;
1100             }
1101
1102             pGetMediaTypes = pNewBuffer;
1103          }
1104       }
1105    } while (!bResult);
1106
1107    if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
1108       free(pGetMediaTypes);
1109       return ERROR_BAD_DEVICE;
1110    }
1111
1112    for (DWORD idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
1113
1114       if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
1115
1116          if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
1117             *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
1118          } else {
1119             *pdwDensity = 0;
1120          }
1121
1122          *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
1123
1124          free(pGetMediaTypes);
1125
1126          return NO_ERROR;
1127       }
1128    }
1129
1130    free(pGetMediaTypes);
1131
1132    return ERROR_NO_MEDIA_IN_DRIVE;
1133 }
1134
1135 int tape_pos(int fd, struct mtpos *mt_pos)
1136 {
1137    DWORD partition;
1138    DWORD offset;
1139    DWORD offsetHi;
1140    BOOL result;
1141
1142    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
1143        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
1144       errno = EBADF;
1145       return -1;
1146    }
1147
1148    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
1149
1150    result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
1151    if (result == NO_ERROR) {
1152       mt_pos->mt_blkno = offset;
1153       return 0;
1154    }
1155
1156    return -1;
1157 }