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