]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/faketape.c
ebl update
[bacula/bacula] / bacula / patches / testing / faketape.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-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, which is 
11    listed 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 #include "faketape.h"
30 #include <dirent.h>
31 #include <sys/mtio.h>
32 #include <ctype.h>
33
34 static int dbglevel = 0;
35
36 faketape *ftape_list[FTAPE_MAX_DRIVE];
37
38 static faketape *get_tape(int fd)
39 {
40    ASSERT(fd >= 0);
41
42    if (fd >= FTAPE_MAX_DRIVE) {
43       /* error */
44       return NULL;
45    }
46
47    return ftape_list[fd];
48 }
49
50 static bool put_tape(faketape *ftape)
51 {
52    ASSERT(ftape != NULL);
53
54    int fd = ftape->get_fd();
55    if (fd >= FTAPE_MAX_DRIVE) {
56       /* error */
57       return false;
58    }
59    ftape_list[fd] = ftape;
60    return true;
61 }
62
63 /****************************************************************/
64 /* theses function will replace open/read/write/close/ioctl
65  * in bacula core
66  */
67 int faketape_open(const char *pathname, int flags, int mode)
68 {
69    ASSERT(pathname != NULL);
70
71    int fd;
72    faketape *tape = new faketape();
73    fd = tape->open(pathname, flags, mode);
74    if (fd > 0) {
75       put_tape(tape);
76    }
77    return fd;
78 }
79
80 int faketape_read(int fd, void *buffer, unsigned int count)
81 {
82    faketape *tape = get_tape(fd);
83    ASSERT(tape != NULL);
84    return tape->read(buffer, count);
85 }
86
87 int faketape_write(int fd, const void *buffer, unsigned int count)
88 {
89    faketape *tape = get_tape(fd);
90    ASSERT(tape != NULL);
91    return tape->write(buffer, count);
92 }
93
94 int faketape_close(int fd)
95 {
96    faketape *tape = get_tape(fd);
97    ASSERT(tape != NULL);
98    tape->close();
99    delete tape;
100    return 0;
101 }
102
103 int faketape_ioctl(int fd, unsigned long int request, ...)
104 {
105    va_list argp;
106    int result=0;
107
108    faketape *t = get_tape(fd);
109    if (!t) {
110       errno = EBADF;
111       return -1;
112    }
113
114    va_start(argp, request);
115
116    if (request == MTIOCTOP) {
117       result = t->tape_op(va_arg(argp, mtop *));
118    } else if (request == MTIOCGET) {
119       result = t->tape_get(va_arg(argp, mtget *));
120    }
121 //
122 //   case MTIOCPOS:
123 //      result = tape_pos(fd, va_arg(argp, mtpos *));
124 //      break;
125 //
126    else {
127       errno = ENOTTY;
128       result = -1;
129    }
130    va_end(argp);
131
132    return result;
133 }
134
135 /****************************************************************/
136
137 int faketape::tape_op(struct mtop *mt_com)
138 {
139    int result=0;
140    
141    switch (mt_com->mt_op)
142    {
143    case MTRESET:
144    case MTNOP:
145    case MTSETDRVBUFFER:
146       break;
147
148    default:
149    case MTRAS1:
150    case MTRAS2:
151    case MTRAS3:
152    case MTSETDENSITY:
153       errno = ENOTTY;
154       result = -1;
155       break;
156
157    case MTFSF:                  /* Forward space over mt_count filemarks. */
158       result = fsf(mt_com->mt_count);
159       break;
160
161    case MTBSF:                  /* Backward space over mt_count filemarks. */
162       current_file = current_file - mt_com->mt_count;
163       if (current_file < 0) {
164          current_file = 0;
165          errno = EIO;
166          result = -1;
167       }
168       atEOD = false;
169       atEOF = false;
170       atBOT = false;
171       seek_file();
172       break;
173
174    case MTFSR:      /* Forward space over mt_count records (tape blocks). */
175 /*
176     file number = 1
177     block number = 0
178    
179     file number = 1
180     block number = 1
181    
182     mt: /dev/lto2: Erreur d'entrée/sortie
183    
184     file number = 2
185     block number = 0
186 */
187       /* tester si on se trouve a la fin du fichier */
188       result = fsr(mt_com->mt_count);
189       break;
190
191    case MTBSR:      /* Backward space over mt_count records (tape blocks). */
192       result = -1;
193       break;
194
195    case MTWEOF:                 /* Write mt_count filemarks. */
196       weof(mt_com->mt_count);
197       break;
198
199    case MTREW:                  /* Rewind. */
200       atEOF = atEOD = false;
201       atBOT = true;
202       current_file = 0;
203       current_block = 0;
204       seek_file();
205       break;
206
207    case MTOFFL:                 /* put tape offline */
208       result = offline();
209       break;
210
211    case MTRETEN:                /* Re-tension tape. */
212       result = 0;
213       break;
214
215    case MTBSFM:                 /* not used by bacula */
216       errno = EIO;
217       result = -1;
218       break;
219
220    case MTFSFM:                 /* not used by bacula */
221       errno = EIO;
222       result = -1;
223       break;
224
225    case MTEOM:/* Go to the end of the recorded media (for appending files). */
226 /*
227    file number = 3
228    block number = -1
229 */
230       /* Can be at EOM */
231       atBOT = false;
232       atEOF = true;
233       atEOD = true;
234       current_file = last_file+1;
235       current_block = -1;
236       seek_file();
237       /* Ne pas creer le fichier si on est a la fin */
238
239       break;
240
241    case MTERASE:                /* not used by bacula */
242       atEOD = true;
243       atEOF = false;
244       atEOT = false;
245       current_file = 0;
246       current_block = -1;
247       seek_file();
248       truncate_file();
249       break;
250
251    case MTSETBLK:
252       break;
253
254    case MTSEEK:
255       break;
256
257    case MTTELL:
258       break;
259
260    case MTFSS:
261       break;
262
263    case MTBSS:
264       break;
265
266    case MTWSM:
267       break;
268
269    case MTLOCK:
270       break;
271
272    case MTUNLOCK:
273       break;
274
275    case MTLOAD:
276       break;
277
278    case MTUNLOAD:
279       break;
280
281    case MTCOMPRESSION:
282       break;
283
284    case MTSETPART:
285       break;
286
287    case MTMKPART:
288       break;
289    }
290 //
291 //   switch (result) {
292 //   case NO_ERROR:
293 //   case -1:   /* Error has already been translated into errno */
294 //      break;
295 //
296 //   default:
297 //   case ERROR_FILEMARK_DETECTED:
298 //      errno = EIO;
299 //      break;
300 //
301 //   case ERROR_END_OF_MEDIA:
302 //      errno = EIO;
303 //      break;
304 //
305 //   case ERROR_NO_DATA_DETECTED:
306 //      errno = EIO;
307 //      break;
308 //
309 //   case ERROR_NO_MEDIA_IN_DRIVE:
310 //      errno = ENOMEDIUM;
311 //      break;
312 //
313 //   case ERROR_INVALID_HANDLE:
314 //   case ERROR_ACCESS_DENIED:
315 //   case ERROR_LOCK_VIOLATION:
316 //      errno = EBADF;
317 //      break;
318 //   }
319 //
320    return result == 0 ? 0 : -1;
321 }
322
323 int faketape::tape_get(struct mtget *mt_get)
324 {
325    int density = 1;
326    int block_size = 1024;
327
328    mt_get->mt_type = MT_ISSCSI2;
329    mt_get->mt_blkno = current_block;
330    mt_get->mt_fileno = current_file;
331
332    mt_get->mt_resid = -1;
333 //   pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
334
335    /* TODO */
336    mt_get->mt_dsreg = 
337       ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
338       ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
339
340
341    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
342
343    if (atEOF) {
344       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
345    }
346
347    if (atBOT) {
348       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
349    }
350    if (atEOT) {
351       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
352    }
353
354    if (atEOD) {
355       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
356    }
357
358    if (0) { //WriteProtected) {
359       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
360    }
361
362    if (online) {
363       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
364    } else {
365       mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
366    }
367    mt_get->mt_erreg = 0;
368
369    return 0;
370 }
371
372 int faketape::tape_pos(struct mtpos *mt_pos)
373 {
374    return 0;
375 }
376
377 /*
378  * This function try to emulate the append only behavior
379  * of a tape. When you wrote something, data after the
380  * current position are discarded.
381  */
382 int faketape::truncate_file()
383 {  
384    ftruncate(fd, lseek(fd, 0, SEEK_CUR));
385    return 0;
386 }
387
388 faketape::faketape()
389 {
390    fd = -1;
391
392    atEOF = false;
393    atBOT = false;
394    atEOT = false;
395    atEOD = false;
396    online = false;
397    
398    file_size = 0;
399    last_file = 0;
400    current_file = 0;
401    current_block = -1;
402    current_pos = 0;
403
404    max_block = 1024*1024*1024*1024*8;
405
406    volume = get_pool_memory(PM_NAME);
407 }
408
409 faketape::~faketape()
410 {
411    free_pool_memory(volume);
412 }
413
414 int faketape::get_fd()
415 {
416    return this->fd;
417 }
418
419 int faketape::write(const void *buffer, unsigned int count)
420 {
421    ASSERT(count >= 0);
422    ASSERT(buffer);
423
424    unsigned int nb;
425    Dmsg2(dbglevel, "write len=%i blocks=%i\n", count, current_block);
426
427    if (atEOT) {
428       Dmsg0(dbglevel, "write nothing, EOT !\n");
429       return 0;
430    }
431
432    if (!atEOD) {                /* if not at the end of the data */
433       truncate_file();
434       weof(1);
435
436    } else {                     /* already writing something */
437       if (current_block != -1) {
438          current_block++;
439       }
440    }
441
442    atBOT = false;
443    atEOD = true;
444
445 //   if ((count + file_size) > max_size) {
446 //      Dmsg2(dbglevel, 
447 //          "EOT writing only %i of %i requested\n", 
448 //          max_size - file_size, count);
449 //      count = max_size - file_size;
450 //      atEOT = true;
451 //   }
452
453    ::write(fd, &count, sizeof(count));
454    nb = ::write(fd, buffer, count);
455    
456    file_size += sizeof(count) + nb;
457
458    if (nb != count) {
459       atEOT = true;
460       Dmsg2(dbglevel, 
461             "Not enough space writing only %i of %i requested\n", 
462             nb, count);
463    }
464
465    return nb;
466 }
467
468 int faketape::weof(int count)
469 {
470    Dmsg2(dbglevel, "Writing EOF %i:%i\n", current_file, current_block);
471    if (atEOT) {
472       current_block = -1;
473       return -1;
474    }
475
476    count--;                     /* end this file */
477    truncate_file();             /* nothing after this point */
478    current_file++;
479
480    atEOF = true;
481    atEOD = false;
482
483    if (count > 0) {
484       current_block = -1;
485       return -1;
486    } else {
487       current_block = 0;
488    }
489    seek_file();
490
491    return 0;
492 }
493
494 int faketape::fsf(int count)
495 {   
496 /*
497  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
498  */
499    int ret;
500    if (atEOT) {
501       current_block = -1;
502       return -1;
503    }
504
505    atEOF=1;
506    Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
507
508    if ((current_file + count) <= last_file) {
509       current_file += count;
510       current_block = 0;
511       ret = 0;
512    } else {
513       Dmsg0(dbglevel, "Try to FSF after EOT\n");
514       current_file = last_file ;
515       current_block = -1;
516       atEOD=true;
517       ret = -1;
518    }
519
520    seek_file();
521    return ret;
522 }
523
524 int faketape::fsr(int count)
525 {
526    int i,nb;
527    off_t where=0, s;
528    Dmsg2(dbglevel, "fsr current_block=%i count=%i\n", current_block, count);
529
530    if (atEOT) {
531       current_block = -1;
532       return -1;
533    }
534
535    /* check all block record */
536    for(i=0; (i < count) && (where != -1) ; i++) {
537       nb = ::read(fd, &s, sizeof(s));   /* get size of block */
538       if (nb == sizeof(s)) {
539          current_block++;
540          where = lseek(fd, s, SEEK_CUR);        /* seek after this block */
541          if (where == -1) {
542             errno = EIO;
543             return -1;
544          }
545       } else {
546          errno = EIO;
547          return -1;
548       }
549    }
550    find_maxfile();              /* refresh stats */
551
552    if (where == file_size) {
553       atBOT = false;
554       atEOD = true;
555    }
556    return 0;
557 }
558
559 int faketape::bsf(int count)
560 {
561    int ret = 1;
562    atEOT = atEOD = false;
563
564    if (current_file - count < 0) {
565       current_file = 0;
566       current_block = 0;
567       atBOT = true;
568       ret = -1;
569    }
570
571    current_file = current_file - count;
572    current_block = 0;
573    if (!current_file) {
574       atBOT = true;
575    }
576    seek_file();
577    return 0;
578 }
579
580 /* 
581  * Put faketape in offline mode
582  */
583 int faketape::offline()
584 {
585    close();
586    
587    atEOF = false;
588    atEOT = false;
589    atEOD = false;
590    atBOT = false;
591
592    current_file = -1;
593    current_block = -1;
594    last_file = -1;
595    return 0;
596 }
597
598 int faketape::close()
599 {
600    ::close(fd);
601    fd = -1;
602    return 0;
603 }
604 /*
605  **rb
606  **status
607  * EOF Bacula status: file=2 block=0
608  * Device status: EOF ONLINE IM_REP_EN file=2 block=0
609  **rb
610  **status
611  * EOD EOF Bacula status: file=2 block=0
612  * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
613  *
614  */
615
616 int faketape::read(void *buffer, unsigned int count)
617 {
618    unsigned int nb, s;
619
620    if (atEOT || atEOD) {
621       return 0;
622    }
623
624    atBOT = false;
625    current_block++;
626
627    nb = ::read(fd, &s, sizeof(s));
628    if (s > count) {
629       lseek(fd, s, SEEK_CUR);
630       errno = ENOMEM;
631       return -1;
632    }
633    nb = ::read(fd, buffer, s);
634    if (s != nb) {
635       atEOF = true;
636       if (current_file == last_file) {
637          atEOD = true;
638          current_block = -1;
639       }
640       Dmsg0(dbglevel, "EOF during reading\n");
641    }
642    return nb;
643 }
644
645 int faketape::open(const char *pathname, int uflags, int umode)
646 {
647    Dmsg3(dbglevel, "faketape::open(%s, %i, %i)\n", pathname, uflags, umode);
648    pm_strcpy(volume, pathname);
649
650    struct stat statp;   
651    if (stat(volume, &statp) != 0) {
652       Dmsg1(dbglevel, "Can't stat on %s\n", volume);
653       return -1;
654    }
655
656    fd = ::open(pathname, O_CREAT | O_RDWR, 0700);
657    if (fd < 0) {
658       return -1;
659    }
660
661    /* open volume descriptor and get this->fd */
662    if (find_maxfile() < 0) {
663       return -1;
664    }
665
666    current_block=-1;
667
668    return fd;
669 }
670
671 /*
672  * read volume to get the last file number
673  */
674 int faketape::find_maxfile()
675 {
676    struct stat statp;
677    fstat(fd, &statp);
678    last_file = statp.st_size % ((off_t)1<<32);
679    file_size = statp.st_size;
680       
681    current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
682
683    return last_file;
684 }
685
686 int faketape::seek_file()
687 {
688    Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
689
690    off_t pos = ((off_t)current_file)<<32;
691    if(lseek(fd, pos, SEEK_SET) == -1) {
692       return -1;
693    }
694    fsr(current_block);
695    last_file = (last_file > current_file)?last_file:current_file;
696    return 0;
697 }
698
699 void faketape::dump()
700 {
701    Dmsg0(dbglevel+1, "===================\n");
702    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
703    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
704    Dmsg1(dbglevel+1, "volume=%s\n", volume);
705    Dmsg1(dbglevel+1, "file_size=%i\n", file_size);  
706    Dmsg1(dbglevel+1, "EOF=%i\n", atEOF);  
707    Dmsg1(dbglevel+1, "EOT=%i\n", atEOT);  
708    Dmsg1(dbglevel+1, "EOD=%i\n", atEOD);  
709 }
710
711 /****************************************************************
712
713 #define GMT_EOF(x)              ((x) & 0x80000000)
714 #define GMT_BOT(x)              ((x) & 0x40000000)
715 #define GMT_EOT(x)              ((x) & 0x20000000)
716 #define GMT_SM(x)               ((x) & 0x10000000)
717 #define GMT_EOD(x)              ((x) & 0x08000000)
718
719
720  GMT_EOF(x) : La bande est positionnée juste après une filemark (toujours faux
721      après une opération MTSEEK).
722
723  GMT_BOT(x) : La bande est positionnée juste au début du premier fichier
724      (toujours faux après une opération MTSEEK).
725  
726  GMT_EOT(x) : Une opération a atteint la fin physique de la bande (End Of
727  Tape).
728
729  GMT_SM(x) : La bande est positionnée sur une setmark (toujours faux après une
730  opération MTSEEK).
731
732  GMT_EOD(x) : La bande est positionnée à la fin des données enregistrées.
733
734
735 blkno = -1 (after MTBSF MTBSS or MTSEEK)
736 fileno = -1 (after MTBSS or MTSEEK)
737
738 *** mtx load
739 drive type = Generic SCSI-2 tape
740 drive status = 0
741 sense key error = 0
742 residue count = 0
743 file number = 0
744 block number = 0
745 Tape block size 0 bytes. Density code 0x0 (default).
746 Soft error count since last status=0
747 General status bits on (41010000):
748  BOT ONLINE IM_REP_EN
749
750 *** read empty block
751 dd if=/dev/lto2 of=/tmp/toto count=1
752 dd: lecture de `/dev/lto2': Ne peut allouer de la mémoire
753 0+0 enregistrements lus
754 0+0 enregistrements écrits
755 1 octet (1B) copié, 4,82219 seconde, 0,0 kB/s
756
757 file number = 0
758 block number = 1
759
760 *** read file mark
761 dd if=/dev/lto2 of=/tmp/toto count=1
762 0+0 enregistrements lus
763 0+0 enregistrements écrits
764 1 octet (1B) copié, 0,167274 seconde, 0,0 kB/s
765
766 file number = 1
767 block number = 0
768
769  *** write 2 blocks after rewind
770 dd if=/dev/zero of=/dev/lto2 count=2
771 2+0 enregistrements lus
772 2+0 enregistrements écrits
773 1024 octets (1,0 kB) copiés, 6,57402 seconde, 0,2 kB/s
774
775 file number = 1
776 block number = 0
777
778 *** write 2 blocks
779 file number = 2
780 block number = 0
781
782 *** rewind and fsr
783 file number = 0
784 block number = 1
785
786 *** rewind and 2x fsr (we have just 2 blocks)
787 file number = 0
788 block number = 2
789
790 *** fsr
791 mt: /dev/lto2: Erreur
792 file number = 1
793 block number = 0
794
795
796  ****************************************************************/
797
798
799 #ifdef TEST
800
801 int main()
802 {
803    int fd;
804    char buf[500];
805    printf("Starting FakeTape\n");
806
807    mkdir("/tmp/fake", 0700);
808
809
810
811
812    return 0;
813 }
814
815 #endif