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