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