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 
Read16BitSigned(const unsigned char * pValue)77 inline   SHORT  Read16BitSigned(const unsigned char *pValue)
78 {
79    return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
80 }
81 
Read16BitUnsigned(const unsigned char * pValue)82 inline   USHORT  Read16BitUnsigned(const unsigned char *pValue)
83 {
84    return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
85 }
86 
Read24BitSigned(const unsigned char * pValue)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 
Read24BitUnsigned(const unsigned char * pValue)93 inline   ULONG  Read24BitUnsigned(const unsigned char *pValue)
94 {
95    return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
96 }
97 
Read32BitSigned(const unsigned char * pValue)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 
Read32BitUnsigned(const unsigned char * pValue)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 
Read64BitSigned(const unsigned char * pValue)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 
Read64BitUnsigned(const unsigned char * pValue)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
win32_tape_open(const char * file,int flags,...)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
win32_read(int fd,void * buffer,size_t count)278 win32_read(int fd, void *buffer, size_t count)
279 {
280    return read(fd, buffer, count);
281 }
282 
283 ssize_t
win32_write(int fd,const void * buffer,size_t count)284 win32_write(int fd, const void *buffer, size_t count)
285 {
286    return write(fd, buffer, count);
287 }
288 
289 int
win32_ioctl(int d,unsigned long int req,...)290 win32_ioctl(int d, unsigned long int req, ...)
291 {
292    return -1;
293 }
294 
295 ssize_t
win32_tape_read(int fd,void * buffer,size_t count)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
win32_tape_write(int fd,const void * buffer,size_t count)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
win32_tape_close(int fd)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
win32_tape_ioctl(int fd,unsigned long int request,...)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 
tape_op(int fd,struct mtop * mt_com)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 
tape_get(int fd,struct mtget * mt_get)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 
GetTapePositionInfo(HANDLE hDevice,PTAPE_POSITION_INFO TapePositionInfo)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 
GetDensityBlockSize(HANDLE hDevice,DWORD * pdwDensity,DWORD * pdwBlockSize)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 
tape_pos(int fd,struct mtpos * mt_pos)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 }
1176