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