2 Bacula® - The Network Backup Solution
4 Copyright (C) 2006-2008 Free Software Foundation Europe e.V.
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 two of the GNU General Public
10 License as published by the Free Software Foundation and included
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.
18 You should have received a copy of the GNU 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
23 Bacula® is a registered trademark of John Walker.
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.
29 * mtops.c - Emulate the Linux st (scsi tape) driver on file.
30 * for regression and bug hunting purpose
39 #include "bacula.h" /* pull in global headers */
40 #include "stored.h" /* pull in Storage Deamon headers */
45 // SCSI bus status codes.
48 #define SCSISTAT_GOOD 0x00
49 #define SCSISTAT_CHECK_CONDITION 0x02
50 #define SCSISTAT_CONDITION_MET 0x04
51 #define SCSISTAT_BUSY 0x08
52 #define SCSISTAT_INTERMEDIATE 0x10
53 #define SCSISTAT_INTERMEDIATE_COND_MET 0x14
54 #define SCSISTAT_RESERVATION_CONFLICT 0x18
55 #define SCSISTAT_COMMAND_TERMINATED 0x22
56 #define SCSISTAT_QUEUE_FULL 0x28
59 * +---+-------+----------------+------------------------+
60 * | V | NB FM | FM index tab | Data |
61 * +---+-------+----------------+------------------------+
64 * NB FM : int16 max_file_mark
65 * FM index : int64 file_mark_index[max_file_mark]
73 int16_t max_file_mark;
76 off_t *file_mark_index;
81 #define FTAPE_HEADER_SIZE (2+sizeof(int16_t))
85 int fd : 0; /* Os file descriptor */
86 char *file; /* file name */
88 bool EOF; /* End of file */
89 bool EOT; /* End of tape */
93 int16_t current_file; /* max 65000 files */
94 int32_t current_block; /* max 4G blocks of 512B */
98 #define NB_TAPE_LIST 15
99 FTAPE_INFO tape_list[NB_TAPE_LIST];
102 static int tape_get(int fd, struct mtget *mt_get);
103 static int tape_op(int fd, struct mtop *mt_com);
104 static int tape_pos(int fd, struct mtpos *mt_pos);
106 static int dbglevel = 10;
108 static FTAPE_INFO *get_tape(int fd)
110 if (fd >= NB_TAPE_LIST) {
115 return &tape_list[fd];
118 static FTAPE_INFO *new_volume(int fd, const char *file)
120 FTAPE_INFO *tape = get_tape(fd);
124 memset(tape, 0, sizeof(FTAPE_INFO));
127 tape->file = bstrdup(file);
129 nb = read(tape->fd, &tape->format, FTAPE_HEADER_SIZE);
130 if (nb != FTAPE_HEADER_SIZE) { /* new tape ? */
133 tape->format.file_index = mmap(NULL,
134 tape->format.max_file_mark * sizeof(off_t),
135 PROT_READ | PROT_WRITE,
136 MAP_SHARED, fileno(fd), FTAPE_HEADER_SIZE);
138 if (!tape->format.file_index) {
142 tape->format.data_start =
143 FTAPE_HEADER_SIZE + sizeof(off_t)* tape->format.max_file_mark ;
148 static int free_volume(int fd)
150 FTAPE_INFO *tape = get_tape(fd);
153 munmap(tape->format.file_index, tape->format.max_file_mark * sizeof(off_t));
160 fake_tape_open(const char *file, int flags, int mode)
163 Dmsg(dbglevel, "fake_tape_open(%s, %i, %i)\n", file, flags, mode);
164 fd = open(file, flags, mode);
165 new_volume(fd, file);
170 fake_tape_read(int fd, void *buffer, unsigned int count)
173 if (buffer == NULL) {
177 ret = read(fd, buffer, count);
182 fake_tape_write(int fd, const void *buffer, unsigned int count)
185 if (buffer == NULL) {
189 ret = write(fd, buffer, count);
194 fake_tape_close(int fd)
202 fake_tape_ioctl(int fd, unsigned long int request, ...)
207 va_start(argp, request);
211 result = tape_op(fd, va_arg(argp, mtop *));
215 result = tape_get(fd, va_arg(argp, mtget *));
219 result = tape_pos(fd, va_arg(argp, mtpos *));
233 static int tape_op(int fd, struct mtop *mt_com)
237 FTAPE_INFO *tape = get_tape(fd);
243 switch (mt_com->mt_op)
260 index = tape->current_file + mt_com->mt_count;
262 if (index >= tape->max_file_mark) {
267 tape->current_file = index;
268 tape->current_block = 0;
272 pos = tape->file_mark_index[index];
273 if (lseek(fd, pos, SEEK_SET) == (boffset_t)-1) {
276 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
277 print_name(), be.bstrerror());
284 index = tape->current_file - mt_com->mt_count;
286 if (index < 0 && index => tape->max_file_mark) {
291 tape->current_file = index;
292 tape->current_block = 0;
296 pos = tape->file_mark_index[index];
297 if (lseek(fd, pos, SEEK_SET) == (boffset_t)-1) {
300 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
301 print_name(), be.bstrerror());
306 case MTFSR: /* block forward */
307 if ((tape->current_block + mt_com->mt_count) >= tape->block_max) {
311 } else { // TODO: check for tape mark
313 tape->current_block += mt_com->mt_count;
317 if (lseek(fd, mt_com->mt_count * tape->format.block_size, SEEK_CUR) == (boffset_t)-1) {
320 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
321 print_name(), be.bstrerror());
327 // result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE);
328 // if (result == NO_ERROR) {
329 // pHandleInfo->bEOD = false;
330 // pHandleInfo->bEOF = false;
331 // pHandleInfo->bEOT = false;
332 // } else if (result == ERROR_FILEMARK_DETECTED) {
333 // pHandleInfo->bEOF = true;
339 if ((tape->current_block - mt_com->mt_count) < 0) {
340 // we are on tapemark, we have to BSF
344 result = tape_op(fd, &mt);
348 tape->current_block -= mt_com->mt_count;
352 if (lseek(fd, -mt_com->mt_count * tape->format.block_size, SEEK_CUR) == (boffset_t)-1) {
355 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
356 print_name(), be.bstrerror());
363 if (tape->current_file >= tape->format.max_file_mark) {
367 tape->current_file++;
368 tape->format.file_index[tape->current_file] = ftell(fd);
375 if (lseek(fd, tape->format.data_start, SEEK_SET) < 0) {
378 Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
379 print_name(), be.bstrerror());
387 case MTOFFL: /* put tape offline */
388 // check if can_read/can_write
396 case MTBSFM: /* not used by bacula */
401 case MTFSFM: /* not used by bacula */
409 for (i=tape->format.current_file;
410 (i < tape->format.max_file_mark) &&(tape->format.file_index[i] != 0) ;
419 result = tape_op(fd, &mt);
424 result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
425 if (result == NO_ERROR) {
426 pHandleInfo->bEOD = true;
427 pHandleInfo->bEOF = false;
428 pHandleInfo->bEOT = false;
429 pHandleInfo->ulFile = 0;
430 pHandleInfo->bBlockValid = true;
431 pHandleInfo->ullFileStart = 0;
437 TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
439 SetMediaParameters.BlockSize = mt_com->mt_count;
440 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
446 TAPE_POSITION_INFO TapePositionInfo;
448 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
450 memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
451 DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
452 if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
453 pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
455 pHandleInfo->ulFile = ~0U;
466 result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
467 if (result == NO_ERROR) {
474 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
478 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
482 result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
486 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
490 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
494 result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
498 result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
503 TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
504 TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
507 size = sizeof(GetDriveParameters);
509 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
511 if (result == NO_ERROR)
513 SetDriveParameters.ECC = GetDriveParameters.ECC;
514 SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
515 SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
516 SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
517 SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
519 result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
525 result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
529 if (mt_com->mt_count == 0)
531 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
535 result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
540 if ((result == NO_ERROR && pHandleInfo->bEOF) ||
541 (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
543 TAPE_POSITION_INFO TapePositionInfo;
545 if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
546 pHandleInfo->bBlockValid = true;
547 pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
553 case (DWORD)-1: /* Error has already been translated into errno */
557 case ERROR_FILEMARK_DETECTED:
561 case ERROR_END_OF_MEDIA:
562 pHandleInfo->bEOT = true;
566 case ERROR_NO_DATA_DETECTED:
567 pHandleInfo->bEOD = true;
571 case ERROR_NO_MEDIA_IN_DRIVE:
572 pHandleInfo->bEOF = false;
573 pHandleInfo->bEOT = false;
574 pHandleInfo->bEOD = false;
578 case ERROR_INVALID_HANDLE:
579 case ERROR_ACCESS_DENIED:
580 case ERROR_LOCK_VIOLATION:
585 return result == NO_ERROR ? 0 : -1;
588 static int tape_get(int fd, struct mtget *mt_get)
590 TAPE_POSITION_INFO pos_info;
593 if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
594 TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
599 PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
601 if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
608 result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
610 if (result != NO_ERROR) {
611 TAPE_GET_DRIVE_PARAMETERS drive_params;
614 size = sizeof(drive_params);
616 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
618 if (result == NO_ERROR) {
619 blocksize = drive_params.DefaultBlockSize;
623 mt_get->mt_type = MT_ISSCSI2;
626 mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
628 // Density / Block Size
629 mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
630 ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
632 mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
634 if (pHandleInfo->bEOF) {
635 mt_get->mt_gstat |= 0x80000000; // GMT_EOF
638 if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
639 mt_get->mt_gstat |= 0x40000000; // GMT_BOT
642 if (pHandleInfo->bEOT) {
643 mt_get->mt_gstat |= 0x20000000; // GMT_EOT
646 if (pHandleInfo->bEOD) {
647 mt_get->mt_gstat |= 0x08000000; // GMT_EOD
650 TAPE_GET_MEDIA_PARAMETERS media_params;
651 DWORD size = sizeof(media_params);
653 result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
655 if (result == NO_ERROR && media_params.WriteProtected) {
656 mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
659 result = GetTapeStatus(pHandleInfo->OSHandle);
661 if (result != NO_ERROR) {
662 if (result == ERROR_NO_MEDIA_IN_DRIVE) {
663 mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
666 mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
669 // Recovered Error Count
670 mt_get->mt_erreg = 0;
673 mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
676 mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);