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