]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/mtops.c
Cleanup patches a bit
[bacula/bacula] / bacula / patches / testing / mtops.c
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 two of the GNU 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 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 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.
27 */
28 /*
29  * mtops.c - Emulate the Linux st (scsi tape) driver on file.
30  * for regression and bug hunting purpose
31  *
32  * Version $Id$
33  *
34  */
35
36 #include <stdarg.h>
37 #include <stddef.h>
38
39 #include "bacula.h"                   /* pull in global headers */
40 #include "stored.h"                   /* pull in Storage Deamon headers */
41
42 #include "sys/mtio.h"
43
44 //
45 // SCSI bus status codes.
46 //
47
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
57
58 /*
59  * +---+-------+----------------+------------------------+
60  * | V | NB FM | FM index tab   | Data                   |
61  * +---+-------+----------------+------------------------+
62  *
63  * V : char version
64  * NB FM : int16 max_file_mark
65  * FM index : int64 file_mark_index[max_file_mark]
66  * DATA : data
67  */
68
69 typedef struct
70 {
71    /* format infos */
72    int16_t     version;
73    int16_t     max_file_mark;
74    int16_t     block_size;
75    int32_t     block_max;
76    off_t       *file_mark_index;
77    off_t       data_start;
78
79 } FTAPE_FORMAT;
80
81 #define FTAPE_HEADER_SIZE (2+sizeof(int16_t))
82
83 typedef  struct
84 {
85    int         fd : 0;          /* Os file descriptor */
86    char        *file;           /* file name */
87    bool        EOD;
88    bool        EOF;             /* End of file */
89    bool        EOT;             /* End of tape */
90
91    FTAPE_FORMAT format;
92
93    int16_t     current_file;    /* max 65000 files */
94    int32_t     current_block;   /* max 4G blocks of 512B */
95
96 } FTAPE_INFO;
97
98 #define NB_TAPE_LIST  15
99 FTAPE_INFO tape_list[NB_TAPE_LIST];
100
101
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);
105
106 static int dbglevel = 10;
107
108 static FTAPE_INFO *get_tape(int fd)
109 {
110    if (fd >= NB_TAPE_LIST) {
111       /* error */
112       return NULL;
113    }
114
115    return &tape_list[fd];
116 }
117
118 static FTAPE_INFO *new_volume(int fd, const char *file)
119 {
120    FTAPE_INFO *tape = get_tape(fd);
121    if (!tape) {
122       return NULL;
123    }
124    memset(tape, 0, sizeof(FTAPE_INFO));
125
126    tape->fd = fd;
127    tape->file = bstrdup(file);
128
129    nb = read(tape->fd, &tape->format, FTAPE_HEADER_SIZE);
130    if (nb != FTAPE_HEADER_SIZE) { /* new tape ? */
131       
132    }
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);
137
138    if (!tape->format.file_index) {
139       /* error */
140    }
141
142    tape->format.data_start = 
143       FTAPE_HEADER_SIZE  + sizeof(off_t)* tape->format.max_file_mark ;
144
145    return tape;
146 }
147
148 static int free_volume(int fd)
149 {
150    FTAPE_INFO *tape = get_tape(fd);
151
152    if (tape) {
153       munmap(tape->format.file_index, tape->format.max_file_mark * sizeof(off_t));
154       free(tape->file);
155       free(tape);
156    }
157 }
158
159 int
160 fake_tape_open(const char *file, int flags, int mode)
161 {
162    int fd;
163    Dmsg(dbglevel, "fake_tape_open(%s, %i, %i)\n", file, flags, mode);
164    fd = open(file, flags, mode);
165    new_volume(fd, file);
166    return fd;
167 }
168
169 int
170 fake_tape_read(int fd, void *buffer, unsigned int count)
171 {
172    int ret;
173    if (buffer == NULL) {
174       errno = EINVAL;
175       return -1;
176    }
177    ret = read(fd, buffer, count);
178    return ret;
179 }
180
181 int
182 fake_tape_write(int fd, const void *buffer, unsigned int count)
183 {
184    int ret;
185    if (buffer == NULL) {
186       errno = EINVAL;
187       return -1;
188    }
189    ret = write(fd, buffer, count);
190    return ret;
191 }
192
193 int
194 fake_tape_close(int fd)
195 {
196    free_volume(fd);
197    close(fd);
198    return 0;
199 }
200
201 int
202 fake_tape_ioctl(int fd, unsigned long int request, ...)
203 {
204    va_list argp;
205    int result;
206
207    va_start(argp, request);
208
209    switch (request) {
210    case MTIOCTOP:
211       result = tape_op(fd, va_arg(argp, mtop *));
212       break;
213
214    case MTIOCGET:
215       result = tape_get(fd, va_arg(argp, mtget *));
216       break;
217
218    case MTIOCPOS:
219       result = tape_pos(fd, va_arg(argp, mtpos *));
220       break;
221
222    default:
223       errno = ENOTTY;
224       result = -1;
225       break;
226    }
227
228    va_end(argp);
229
230    return result;
231 }
232
233 static int tape_op(int fd, struct mtop *mt_com)
234 {
235    int result;
236    
237    FTAPE_INFO *tape = get_tape(fd);
238    if (!tape) {
239       errno = EBADF;
240       return -1;
241    }
242    
243    switch (mt_com->mt_op)
244    {
245    case MTRESET:
246    case MTNOP:
247    case MTSETDRVBUFFER:
248       break;
249
250    default:
251    case MTRAS1:
252    case MTRAS2:
253    case MTRAS3:
254    case MTSETDENSITY:
255       errno = ENOTTY;
256       result = (DWORD)-1;
257       break;
258
259    case MTFSF:
260       index = tape->current_file + mt_com->mt_count;
261
262       if (index >= tape->max_file_mark) {
263          errno = EIO;
264          result = -1;
265
266       } else {
267          tape->current_file = index;
268          tape->current_block = 0;
269          tape->EOF = true;
270          tape->EOT = false;
271
272          pos = tape->file_mark_index[index];
273          if (lseek(fd, pos, SEEK_SET) == (boffset_t)-1) {
274             berrno be;
275             dev_errno = errno;
276             Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
277                   print_name(), be.bstrerror());
278             result = -1;
279          }
280       }
281       break;
282
283    case MTBSF:
284       index = tape->current_file - mt_com->mt_count;
285
286       if (index < 0 && index => tape->max_file_mark) {
287          errno = EIO;
288          result = -1;
289
290       } else {
291          tape->current_file = index;
292          tape->current_block = 0;
293          tape->EOF = true;
294          tape->EOT = false;
295
296          pos = tape->file_mark_index[index];
297          if (lseek(fd, pos, SEEK_SET) == (boffset_t)-1) {
298             berrno be;
299             dev_errno = errno;
300             Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
301                   print_name(), be.bstrerror());
302             result = -1;
303          }
304       break;
305
306    case MTFSR:                  /* block forward */
307       if ((tape->current_block + mt_com->mt_count) >= tape->block_max) {
308          errno = ENOSPC;
309          result = -1;
310
311       } else { // TODO: check for tape mark
312
313          tape->current_block += mt_com->mt_count;
314          tape->EOF = false;
315          tape->EOT = false;
316
317          if (lseek(fd, mt_com->mt_count * tape->format.block_size, SEEK_CUR) == (boffset_t)-1) {
318             berrno be;
319             dev_errno = errno;
320             Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
321                   print_name(), be.bstrerror());
322             result = -1;
323          }
324       }
325
326 //
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;
334 //      }
335 //
336       break;
337
338    case MTBSR:
339       if ((tape->current_block - mt_com->mt_count) < 0) {
340          // we are on tapemark, we have to BSF
341          struct mtop mt;
342          mt.mt_op = MTBSF;
343          mt.mt_count = 1;
344          result = tape_op(fd, &mt);
345          
346       } else { 
347
348          tape->current_block -= mt_com->mt_count;
349          tape->EOF = false;
350          tape->EOT = false;
351          
352          if (lseek(fd, -mt_com->mt_count * tape->format.block_size, SEEK_CUR) == (boffset_t)-1) {
353             berrno be;
354             dev_errno = errno;
355             Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
356                   print_name(), be.bstrerror());
357             result = -1;
358          }
359       }
360       break;
361
362    case MTWEOF:
363       if (tape->current_file >= tape->format.max_file_mark) {
364          errno = ENOSPC;
365          result = -1;
366       } else {
367          tape->current_file++;
368          tape->format.file_index[tape->current_file] = ftell(fd);
369          tape->EOF = 1;
370       }
371
372       break;
373
374    case MTREW:
375       if (lseek(fd, tape->format.data_start, SEEK_SET) < 0) {
376          berrno be;
377          dev_errno = errno;
378          Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
379             print_name(), be.bstrerror());
380          result = -1 ;
381       } else {
382          tape->EOF = false;
383          result = NO_ERROR;
384       }
385       break;
386
387    case MTOFFL:                 /* put tape offline */
388       // check if can_read/can_write
389       result = NO_ERROR;
390       break;
391
392    case MTRETEN:
393       result = NO_ERROR;
394       break;
395
396    case MTBSFM:                 /* not used by bacula */
397       errno = EIO;
398       result = -1;
399       break;
400
401    case MTFSFM:                 /* not used by bacula */
402       errno = EIO;
403       result = -1;
404       break;
405
406    case MTEOM:
407       int i=0;
408       int last=0;
409       for (i=tape->format.current_file;
410            (i < tape->format.max_file_mark) &&(tape->format.file_index[i] != 0) ; 
411            i++)
412       {
413          last = i;
414       }
415
416       struct mtop mt;
417       mt.mt_op = MTFSF;
418       mt.mt_count = last;
419       result = tape_op(fd, &mt);
420
421       break;
422
423    case MTERASE:
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;
432       }
433       break;
434
435    case MTSETBLK:
436       {
437          TAPE_SET_MEDIA_PARAMETERS  SetMediaParameters;
438
439          SetMediaParameters.BlockSize = mt_com->mt_count;
440          result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
441       }
442       break;
443
444    case MTSEEK:
445       {
446          TAPE_POSITION_INFO   TapePositionInfo;
447
448          result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
449
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;
454          } else {
455             pHandleInfo->ulFile = ~0U;
456          }
457       }
458       break;
459
460    case MTTELL:
461       {
462          DWORD partition;
463          DWORD offset;
464          DWORD offsetHi;
465
466          result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
467          if (result == NO_ERROR) {
468             return offset;
469          }
470       }
471       break;
472
473    case MTFSS:
474       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
475       break;
476
477    case MTBSS:
478       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
479       break;
480
481    case MTWSM:
482       result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
483       break;
484
485    case MTLOCK:
486       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
487       break;
488
489    case MTUNLOCK:
490       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
491       break;
492
493    case MTLOAD:
494       result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
495       break;
496
497    case MTUNLOAD:
498       result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
499       break;
500
501    case MTCOMPRESSION:
502       {
503          TAPE_GET_DRIVE_PARAMETERS  GetDriveParameters;
504          TAPE_SET_DRIVE_PARAMETERS  SetDriveParameters;
505          DWORD                      size;
506
507          size = sizeof(GetDriveParameters);
508
509          result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
510
511          if (result == NO_ERROR)
512          {
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;
518
519             result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
520          }
521       }
522       break;
523
524    case MTSETPART:
525       result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
526       break;
527
528    case MTMKPART:
529       if (mt_com->mt_count == 0)
530       {
531          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
532       }
533       else
534       {
535          result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
536       }
537       break;
538    }
539
540    if ((result == NO_ERROR && pHandleInfo->bEOF) || 
541        (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
542
543       TAPE_POSITION_INFO TapePositionInfo;
544
545       if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
546          pHandleInfo->bBlockValid = true;
547          pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
548       }
549    }
550
551    switch (result) {
552    case NO_ERROR:
553    case (DWORD)-1:   /* Error has already been translated into errno */
554       break;
555
556    default:
557    case ERROR_FILEMARK_DETECTED:
558       errno = EIO;
559       break;
560
561    case ERROR_END_OF_MEDIA:
562       pHandleInfo->bEOT = true;
563       errno = EIO;
564       break;
565
566    case ERROR_NO_DATA_DETECTED:
567       pHandleInfo->bEOD = true;
568       errno = EIO;
569       break;
570
571    case ERROR_NO_MEDIA_IN_DRIVE:
572       pHandleInfo->bEOF = false;
573       pHandleInfo->bEOT = false;
574       pHandleInfo->bEOD = false;
575       errno = ENOMEDIUM;
576       break;
577
578    case ERROR_INVALID_HANDLE:
579    case ERROR_ACCESS_DENIED:
580    case ERROR_LOCK_VIOLATION:
581       errno = EBADF;
582       break;
583    }
584
585    return result == NO_ERROR ? 0 : -1;
586 }
587
588 static int tape_get(int fd, struct mtget *mt_get)
589 {
590    TAPE_POSITION_INFO pos_info;
591    BOOL result;
592
593    if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || 
594        TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
595       errno = EBADF;
596       return -1;
597    }
598
599    PTAPE_HANDLE_INFO    pHandleInfo = &TapeHandleTable[fd - 3];
600
601    if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
602       return -1;
603    }
604
605    DWORD density = 0;
606    DWORD blocksize = 0;
607
608    result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
609
610    if (result != NO_ERROR) {
611       TAPE_GET_DRIVE_PARAMETERS drive_params;
612       DWORD size;
613
614       size = sizeof(drive_params);
615
616       result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
617
618       if (result == NO_ERROR) {
619          blocksize = drive_params.DefaultBlockSize;
620       }
621    }
622
623    mt_get->mt_type = MT_ISSCSI2;
624
625    // Partition #
626    mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
627
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);
631
632    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
633
634    if (pHandleInfo->bEOF) {
635       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
636    }
637
638    if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
639       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
640    }
641
642    if (pHandleInfo->bEOT) {
643       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
644    }
645
646    if (pHandleInfo->bEOD) {
647       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
648    }
649
650    TAPE_GET_MEDIA_PARAMETERS  media_params;
651    DWORD size = sizeof(media_params);
652    
653    result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
654
655    if (result == NO_ERROR && media_params.WriteProtected) {
656       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
657    }
658
659    result = GetTapeStatus(pHandleInfo->OSHandle);
660
661    if (result != NO_ERROR) {
662       if (result == ERROR_NO_MEDIA_IN_DRIVE) {
663          mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
664       }
665    } else {
666       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
667    }
668
669    // Recovered Error Count
670    mt_get->mt_erreg = 0;
671
672    // File Number
673    mt_get->mt_fileno = (__daddr_t)pHandleInfo->ulFile;
674
675    // Block Number
676    mt_get->mt_blkno = (__daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
677
678    return 0;
679 }