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