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