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