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