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