]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/faketape.c
ebl Add O_NONBLOCK option and ERNOMEDIUM
[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    if (!online) {
173       errno = ENOMEDIUM;
174       return -1;
175    }
176    
177    switch (mt_com->mt_op)
178    {
179    case MTRESET:
180    case MTNOP:
181    case MTSETDRVBUFFER:
182       break;
183
184    default:
185    case MTRAS1:
186    case MTRAS2:
187    case MTRAS3:
188    case MTSETDENSITY:
189       errno = ENOTTY;
190       result = -1;
191       break;
192
193    case MTFSF:                  /* Forward space over mt_count filemarks. */
194       result = fsf(mt_com->mt_count);
195       break;
196
197    case MTBSF:                  /* Backward space over mt_count filemarks. */
198       result = bsf(mt_com->mt_count);
199       break;
200
201    case MTFSR:      /* Forward space over mt_count records (tape blocks). */
202 /*
203     file number = 1
204     block number = 0
205    
206     file number = 1
207     block number = 1
208    
209     mt: /dev/lto2: Erreur d'entree/sortie
210    
211     file number = 2
212     block number = 0
213 */
214       /* tester si on se trouve a la fin du fichier */
215       result = fsr(mt_com->mt_count);
216       break;
217
218    case MTBSR:      /* Backward space over mt_count records (tape blocks). */
219       result = bsr(mt_com->mt_count);
220       break;
221
222    case MTWEOF:                 /* Write mt_count filemarks. */
223       result = weof(mt_com->mt_count);
224       break;
225
226    case MTREW:                  /* Rewind. */
227       Dmsg0(dbglevel, "rewind faketape\n");
228       atEOF = atEOD = false;
229       atBOT = true;
230       current_file = 0;
231       current_block = 0;
232       seek_file();
233       break;
234
235    case MTOFFL:                 /* put tape offline */
236       result = offline();
237       break;
238
239    case MTRETEN:                /* Re-tension tape. */
240       result = 0;
241       break;
242
243    case MTBSFM:                 /* not used by bacula */
244       errno = EIO;
245       result = -1;
246       break;
247
248    case MTFSFM:                 /* not used by bacula */
249       errno = EIO;
250       result = -1;
251       break;
252
253    case MTEOM:/* Go to the end of the recorded media (for appending files). */
254 /*
255    file number = 3
256    block number = -1
257 */
258       /* Can be at EOM */
259       atBOT = false;
260       atEOF = false;
261       atEOD = true;
262       atEOT = false;
263
264       current_file = last_file;
265       current_block = -1;
266       seek_file();
267       break;
268
269    case MTERASE:                /* not used by bacula */
270       atEOD = true;
271       atEOF = false;
272       atEOT = false;
273
274       current_file = 0;
275       current_block = -1;
276       seek_file();
277       truncate_file();
278       break;
279
280    case MTSETBLK:
281       break;
282
283    case MTSEEK:
284       break;
285
286    case MTTELL:
287       break;
288
289    case MTFSS:
290       break;
291
292    case MTBSS:
293       break;
294
295    case MTWSM:
296       break;
297
298    case MTLOCK:
299       break;
300
301    case MTUNLOCK:
302       break;
303
304    case MTLOAD:
305       break;
306
307    case MTUNLOAD:
308       break;
309
310    case MTCOMPRESSION:
311       break;
312
313    case MTSETPART:
314       break;
315
316    case MTMKPART:
317       break;
318    }
319
320    return result == 0 ? 0 : -1;
321 }
322
323 int faketape::tape_get(struct mtget *mt_get)
324 {
325    int density = 1;
326    int block_size = 1024;
327
328    mt_get->mt_type = MT_ISSCSI2;
329    mt_get->mt_blkno = current_block;
330    mt_get->mt_fileno = current_file;
331
332    mt_get->mt_resid = -1;
333 //   pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
334
335    /* TODO */
336    mt_get->mt_dsreg = 
337       ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
338       ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
339
340
341    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
342
343    if (atEOF) {
344       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
345    }
346
347    if (atBOT) {
348       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
349    }
350    if (atEOT) {
351       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
352    }
353
354    if (atEOD) {
355       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
356    }
357
358    if (0) { //WriteProtected) {
359       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
360    }
361
362    if (online) {
363       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
364    } else {
365       mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
366    }
367    mt_get->mt_erreg = 0;
368
369    return 0;
370 }
371
372 int faketape::tape_pos(struct mtpos *mt_pos)
373 {
374    if (current_block >= 0) {
375       mt_pos->mt_blkno = current_block;
376       return 0;
377    }
378
379    return -1;
380 }
381
382 /*
383  * This function try to emulate the append only behavior
384  * of a tape. When you wrote something, data after the
385  * current position are discarded.
386  */
387 int faketape::truncate_file()
388 {  
389    Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
390    ftruncate(fd, lseek(fd, 0, SEEK_CUR));
391    last_file = current_file;
392    atEOD=true;
393    return 0;
394 }
395
396 faketape::faketape()
397 {
398    fd = -1;
399
400    atEOF = false;
401    atBOT = false;
402    atEOT = false;
403    atEOD = false;
404    online = false;
405    inplace = false;
406    needEOF = false;
407
408    file_size = 0;
409    last_file = 0;
410    current_file = 0;
411    current_block = -1;
412    current_pos = 0;
413
414    max_block = 1024*1024*1024*1024*8;
415
416 }
417
418 faketape::~faketape()
419 {
420 }
421
422 int faketape::get_fd()
423 {
424    return this->fd;
425 }
426
427 /*
428  * TODO: check if after a write op, and other tape op put a EOF
429  */
430 int faketape::write(const void *buffer, unsigned int count)
431 {
432    ASSERT(online);
433    ASSERT(current_file >= 0);
434    ASSERT(count > 0);
435    ASSERT(buffer);
436
437    unsigned int nb;
438    Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
439
440    if (atEOT) {
441       Dmsg0(dbglevel, "write nothing, EOT !\n");
442       errno = ENOSPC;
443       return -1;
444    }
445
446    if (!inplace) {
447       seek_file();
448    }
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    file_size += sizeof(uint32_t) + nb;
477
478    if (nb != count) {
479       atEOT = true;
480       Dmsg2(dbglevel, 
481             "Not enough space writing only %i of %i requested\n", 
482             nb, count);
483    }
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    truncate_file();             /* nothing after this point */
501
502    /* TODO: check this */
503    current_file += count;
504    current_block = 0;
505
506    uint32_t c=0;
507    seek_file();
508    ::write(fd, &c, sizeof(uint32_t));
509    seek_file();
510
511    atEOD = false;
512    atBOT = false;
513    atEOF = true;
514
515    return 0;
516 }
517
518 int faketape::fsf(int count)
519 {   
520    ASSERT(online);
521    ASSERT(current_file >= 0);
522    ASSERT(fd >= 0);
523 /*
524  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
525  */
526    check_eof();
527
528    int ret;
529    if (atEOT) {
530       current_block = -1;
531       return -1;
532    }
533
534    atBOT = atEOF = false;
535    Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
536
537    if ((current_file + count) <= last_file) {
538       current_file += count;
539       current_block = 0;
540       ret = 0;
541    } else {
542       Dmsg0(dbglevel, "Try to FSF after EOT\n");
543       current_file = last_file ;
544       current_block = -1;
545       atEOD=true;
546       ret = -1;
547    }
548    seek_file();
549    return ret;
550 }
551
552 int faketape::fsr(int count)
553 {
554    ASSERT(online);
555    ASSERT(current_file >= 0);
556    ASSERT(fd >= 0);
557    
558    int i,nb, ret=0;
559    off_t where=0;
560    uint32_t s;
561    Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
562
563    check_eof();
564
565    if (atEOT) {
566       errno = EIO;
567       current_block = -1;
568       return -1;
569    }
570
571    if (atEOD) {
572       errno = EIO;
573       return -1;
574    }
575
576    atBOT = atEOF = false;   
577
578    /* check all block record */
579    for(i=0; (i < count) && !atEOF ; i++) {
580       nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
581       if (nb == sizeof(uint32_t) && s) {
582          current_block++;
583          where = lseek(fd, s, SEEK_CUR);     /* seek after this block */
584       } else {
585          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
586                current_file, current_block, nb,s);
587          errno = EIO;
588          ret = -1;
589          if (current_file < last_file) {
590             current_block = 0;
591             current_file++;
592             seek_file();
593          }
594          atEOF = true;          /* stop the loop */
595       }
596    }
597
598    find_maxfile();              /* refresh stats */
599
600    if (where == file_size) {
601       atEOD = true;
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    current_file = -1;
713    current_block = -1;
714    last_file = -1;
715    return 0;
716 }
717
718 int faketape::close()
719 {
720    check_eof();
721    ::close(fd);
722    fd = -1;
723    return 0;
724 }
725 /*
726  **rb
727  **status
728  * EOF Bacula status: file=2 block=0
729  * Device status: EOF ONLINE IM_REP_EN file=2 block=0
730  **rb
731  **status
732  * EOD EOF Bacula status: file=2 block=0
733  * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
734  *
735  */
736
737 int faketape::read(void *buffer, unsigned int count)
738 {
739    ASSERT(online);
740    ASSERT(current_file >= 0);
741    unsigned int nb;
742    uint32_t s;
743    
744    Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
745
746    if (atEOT || atEOD) {
747       return 0;
748    }
749
750    if (atEOF) {
751       current_file++;
752       current_block=0;
753       inplace = false;
754       atEOF = false;
755    }
756
757    if (!inplace) {
758       seek_file();
759    }
760
761    atEOD = atBOT = false;
762    current_block++;
763
764    nb = ::read(fd, &s, sizeof(uint32_t));
765    if (nb <= 0) {
766       atEOF = true;
767       return 0;
768    }
769    if (s > count) {             /* not enough buffer to read block */
770       Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
771       lseek(fd, s, SEEK_CUR);
772       errno = ENOMEM;
773       return -1;
774    }
775    if (!s) {                    /* EOF */
776       atEOF = true;
777       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
778       return 0;
779    }
780    nb = ::read(fd, buffer, s);
781    if (s != nb) {
782       atEOF = true;
783       if (current_file == last_file) {
784          atEOD = true;
785          current_block = -1;
786       }
787       Dmsg0(dbglevel, "EOF during reading\n");
788    }
789    return nb;
790 }
791
792 int faketape::open(const char *pathname, int uflags)
793 {
794    Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
795
796    online = true;               /* assume that drive contains a tape */
797
798    struct stat statp;   
799    if (stat(pathname, &statp) != 0) {
800       Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
801       if (uflags & O_NONBLOCK) {
802          online = false;
803          fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600);
804       }
805    } else {
806       fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
807    }
808
809    if (fd < 0) {
810       errno = ENOMEDIUM;
811       return -1;
812    }
813
814    /* open volume descriptor and get this->fd */
815    find_maxfile();
816
817    current_block = 0;
818    current_file = 0;
819    needEOF = false;
820    inplace = true;
821    atBOT = true;
822    atEOT = atEOD = false;
823
824    return fd;
825 }
826
827 /*
828  * read volume to get the last file number
829  */
830 int faketape::find_maxfile()
831 {
832    struct stat statp;
833    if (fstat(fd, &statp) != 0) {
834       return 0;
835    }
836    last_file = statp.st_size>>FILE_OFFSET;
837    file_size = statp.st_size;
838       
839    current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
840    Dmsg3(dbglevel+1, "last_file=%i file_size=%u current_pos=%i\n", 
841          last_file, file_size, current_pos);
842
843    return last_file;
844 }
845
846 int faketape::seek_file()
847 {
848    ASSERT(online);
849    ASSERT(current_file >= 0);
850    Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
851
852    off_t pos = ((off_t)current_file)<<FILE_OFFSET;
853    if(lseek(fd, pos, SEEK_SET) == -1) {
854       return -1;
855    }
856    last_file = (last_file > current_file)?last_file:current_file;
857    if (current_block > 0) {
858       fsr(current_block);
859    }
860    inplace = true;
861
862    return 0;
863 }
864
865 void faketape::dump()
866 {
867    Dmsg0(dbglevel+1, "===================\n");
868    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
869    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
870    Dmsg1(dbglevel+1, "file_size=%i\n", file_size);  
871    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
872          atEOF, atEOT, atEOD, atBOT);  
873 }