]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/faketape.c
ebl put EOT after 2GB (ok for most regress tests)
[bacula/bacula] / bacula / src / stored / 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    update_pos();
396    return 0;
397 }
398
399 faketape::faketape()
400 {
401    fd = -1;
402
403    atEOF = false;
404    atBOT = false;
405    atEOT = false;
406    atEOD = false;
407    online = false;
408    inplace = false;
409    needEOF = false;
410
411    file_block = 0;
412    last_file = 0;
413    current_file = 0;
414    current_block = -1;
415
416    max_block = 2*1024*2048;      /* 2GB */
417 }
418
419 faketape::~faketape()
420 {
421 }
422
423 int faketape::get_fd()
424 {
425    return this->fd;
426 }
427
428 /*
429  * TODO: check if after a write op, and other tape op put a EOF
430  */
431 int faketape::write(const void *buffer, unsigned int count)
432 {
433    ASSERT(online);
434    ASSERT(current_file >= 0);
435    ASSERT(count > 0);
436    ASSERT(buffer);
437
438    unsigned int nb;
439    Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
440
441    if (atEOT) {
442       Dmsg0(dbglevel, "write nothing, EOT !\n");
443       errno = ENOSPC;
444       return -1;
445    }
446
447    if (!inplace) {
448       seek_file();
449    }
450
451    if (!atEOD) {                /* if not at the end of the data */
452       truncate_file();
453    }
454  
455    if (current_block != -1) {
456       current_block++;
457    }
458
459    atBOT = false;
460    atEOF = false;
461    atEOD = true;                /* End of data */
462
463    needEOF = true;              /* next operation need EOF mark */
464
465 //   if ((count + file_size) > max_size) {
466 //      Dmsg2(dbglevel, 
467 //          "EOT writing only %i of %i requested\n", 
468 //          max_size - file_size, count);
469 //      count = max_size - file_size;
470 //      atEOT = true;
471 //   }
472
473    uint32_t size = count;
474    ::write(fd, &size, sizeof(uint32_t));
475    nb = ::write(fd, buffer, count);
476    
477    if (nb != count) {
478       atEOT = true;
479       Dmsg2(dbglevel, 
480             "Not enough space writing only %i of %i requested\n", 
481             nb, count);
482    }
483
484    update_pos();
485
486    return nb;
487 }
488
489 int faketape::weof(int count)
490 {
491    ASSERT(online);
492    ASSERT(current_file >= 0);
493    Dmsg3(dbglevel, "Writing EOF %i:%i last=%i\n", 
494          current_file, current_block,last_file);
495    if (atEOT) {
496       errno = ENOSPC;
497       current_block = -1;
498       return -1;
499    }
500    needEOF = false;
501    truncate_file();             /* nothing after this point */
502
503    /* TODO: check this */
504    current_file += count;
505    current_block = 0;
506
507    uint32_t c=0;
508    seek_file();
509    ::write(fd, &c, sizeof(uint32_t));
510    seek_file();
511
512    atEOD = false;
513    atBOT = false;
514    atEOF = true;
515
516    update_pos();
517
518    return 0;
519 }
520
521 int faketape::fsf(int count)
522 {   
523    ASSERT(online);
524    ASSERT(current_file >= 0);
525    ASSERT(fd >= 0);
526 /*
527  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
528  */
529    check_eof();
530
531    int ret;
532    if (atEOT) {
533       current_block = -1;
534       return -1;
535    }
536
537    atBOT = atEOF = false;
538    Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
539
540    if ((current_file + count) <= last_file) {
541       current_file += count;
542       current_block = 0;
543       ret = 0;
544    } else {
545       Dmsg0(dbglevel, "Try to FSF after EOT\n");
546       current_file = last_file ;
547       current_block = -1;
548       atEOD=true;
549       ret = -1;
550    }
551    seek_file();
552    return ret;
553 }
554
555 int faketape::fsr(int count)
556 {
557    ASSERT(online);
558    ASSERT(current_file >= 0);
559    ASSERT(fd >= 0);
560    
561    int i,nb, ret=0;
562    off_t where=0;
563    uint32_t s;
564    Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
565
566    check_eof();
567
568    if (atEOT) {
569       errno = EIO;
570       current_block = -1;
571       return -1;
572    }
573
574    if (atEOD) {
575       errno = EIO;
576       return -1;
577    }
578
579    atBOT = atEOF = false;   
580
581    /* check all block record */
582    for(i=0; (i < count) && !atEOF ; i++) {
583       nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
584       if (nb == sizeof(uint32_t) && s) {
585          current_block++;
586          where = lseek(fd, s, SEEK_CUR);     /* seek after this block */
587       } else {
588          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
589                current_file, current_block, nb,s);
590          errno = EIO;
591          ret = -1;
592          if (current_file < last_file) {
593             current_block = 0;
594             current_file++;
595             seek_file();
596          }
597          atEOF = true;          /* stop the loop */
598       }
599    }
600
601    find_maxfile();              /* refresh stats */
602
603    return ret;
604 }
605
606 int faketape::bsr(int count)
607 {
608    Dmsg2(dbglevel, "bsr current_block=%i count=%i\n", 
609          current_block, count);
610
611    ASSERT(online);
612    ASSERT(current_file >= 0);
613    ASSERT(count == 1);
614    ASSERT(fd >= 0);
615
616    check_eof();
617
618    if (!count) {
619       return 0;
620    }
621
622    int ret=0;
623    int last_f=0;
624    int last_b=0;
625
626    off_t last=-1, last2=-1;
627    off_t orig = lseek(fd, 0, SEEK_CUR);
628    int orig_f = current_file;
629    int orig_b = current_block;
630
631    current_block=0;
632    seek_file();
633
634    do {
635       if (!atEOF) {
636          last2 = last;
637          last = lseek(fd, 0, SEEK_CUR);
638          last_f = current_file;
639          last_b = current_block;
640          Dmsg5(dbglevel, "EOF=%i last=%lli orig=%lli %i:%i\n", 
641                atEOF, last, orig, current_file, current_block);
642       }
643       ret = fsr(1);
644    } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
645
646    if (last2 > 0 && atEOF) {    /* we take the previous position */
647       lseek(fd, last2, SEEK_SET);
648       current_file = last_f;
649       current_block = last_b - 1;
650       Dmsg3(dbglevel, "set offset2=%lli %i:%i\n", 
651             last, current_file, current_block);
652
653    } else if (last > 0) {
654       lseek(fd, last, SEEK_SET);
655       current_file = last_f;
656       current_block = last_b;
657       Dmsg3(dbglevel, "set offset=%lli %i:%i\n", 
658             last, current_file, current_block);
659    } else {
660       lseek(fd, orig, SEEK_SET);
661       current_file = orig_f;
662       current_block = orig_b;
663       return -1;
664    }
665
666    Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
667    atEOT = atEOF = atEOD = false;
668
669    return 0;
670 }
671
672 int faketape::bsf(int count)
673 {
674    ASSERT(online);
675    ASSERT(current_file >= 0);
676    Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
677    int ret = 0;
678
679    check_eof();
680    atBOT = atEOF = atEOT = atEOD = false;
681
682    if (current_file - count < 0) {
683       current_file = 0;
684       current_block = 0;
685       atBOT = true;
686       errno = EIO;
687       ret = -1;
688    } else {
689       current_file = current_file - count + 1;
690       current_block = -1;
691       seek_file();
692       current_file--;
693       /* go just before last EOF */
694       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
695    }
696    return ret;
697 }
698
699 /* 
700  * Put faketape in offline mode
701  */
702 int faketape::offline()
703 {
704    close();
705    
706    atEOF = false;               /* End of file */
707    atEOT = false;               /* End of tape */
708    atEOD = false;               /* End of data */
709    atBOT = false;               /* Begin of tape */
710    online = false;
711
712    file_block = 0;
713    current_file = -1;
714    current_block = -1;
715    last_file = -1;
716    return 0;
717 }
718
719 int faketape::close()
720 {
721    check_eof();
722    ::close(fd);
723    fd = -1;
724    return 0;
725 }
726 /*
727  **rb
728  **status
729  * EOF Bacula status: file=2 block=0
730  * Device status: EOF ONLINE IM_REP_EN file=2 block=0
731  **rb
732  **status
733  * EOD EOF Bacula status: file=2 block=0
734  * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
735  *
736  */
737
738 int faketape::read(void *buffer, unsigned int count)
739 {
740    ASSERT(online);
741    ASSERT(current_file >= 0);
742    unsigned int nb;
743    uint32_t s;
744    
745    Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
746
747    if (atEOT || atEOD) {
748       return 0;
749    }
750
751    if (atEOF) {
752       current_file++;
753       current_block=0;
754       inplace = false;
755       atEOF = false;
756    }
757
758    if (!inplace) {
759       seek_file();
760    }
761
762    atEOD = atBOT = false;
763    current_block++;
764
765    nb = ::read(fd, &s, sizeof(uint32_t));
766    if (nb <= 0) {
767       atEOF = true;
768       return 0;
769    }
770    if (s > count) {             /* not enough buffer to read block */
771       Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
772       lseek(fd, s, SEEK_CUR);
773       errno = ENOMEM;
774       return -1;
775    }
776    if (!s) {                    /* EOF */
777       atEOF = true;
778       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
779       return 0;
780    }
781    nb = ::read(fd, buffer, s);
782    if (s != nb) {
783       atEOF = true;
784       if (current_file == last_file) {
785          atEOD = true;
786          current_block = -1;
787       }
788       Dmsg0(dbglevel, "EOF during reading\n");
789    }
790    return nb;
791 }
792
793 int faketape::open(const char *pathname, int uflags)
794 {
795    Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
796
797    online = true;               /* assume that drive contains a tape */
798
799    struct stat statp;   
800    if (stat(pathname, &statp) != 0) {
801       Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
802       if (uflags & O_NONBLOCK) {
803          online = false;
804          fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600);
805       }
806    } else {
807       fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
808    }
809
810    if (fd < 0) {
811       errno = ENOMEDIUM;
812       return -1;
813    }
814
815    /* open volume descriptor and get this->fd */
816    find_maxfile();
817
818    file_block = 0;
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       
840    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
841
842    return last_file;
843 }
844
845 void faketape::update_pos()
846 {
847    ASSERT(online);
848    struct stat statp;
849    if (fstat(fd, &statp) == 0) {
850       file_block = statp.st_blocks;
851    } 
852
853    Dmsg1(dbglevel+1, "update_pos=%i\n", file_block);
854
855    if (file_block > max_block) {
856       atEOT = true;
857    } else {
858       atEOT = false;
859    }
860 }
861
862 int faketape::seek_file()
863 {
864    ASSERT(online);
865    ASSERT(current_file >= 0);
866    Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
867
868    off_t pos = ((off_t)current_file)<<FILE_OFFSET;
869    if(lseek(fd, pos, SEEK_SET) == -1) {
870       return -1;
871    }
872    last_file = MAX(last_file, current_file);
873    if (current_block > 0) {
874       fsr(current_block);
875    }
876    inplace = true;
877
878    return 0;
879 }
880
881 void faketape::dump()
882 {
883    Dmsg0(dbglevel+1, "===================\n");
884    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
885    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
886    Dmsg1(dbglevel+1, "file_block=%i\n", file_block);  
887    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
888          atEOF, atEOT, atEOD, atBOT);  
889 }
890
891 #endif /* USE_FAKETAPE */