]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/faketape.c
c5ae12b95e25736a83c79ebfd7fe4c817890b5c9
[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 = 100;
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    int count = mt_com->mt_count;
174
175    if (!online) {
176       errno = ENOMEDIUM;
177       return -1;
178    }
179    
180    switch (mt_com->mt_op)
181    {
182    case MTRESET:
183    case MTNOP:
184    case MTSETDRVBUFFER:
185       break;
186
187    default:
188    case MTRAS1:
189    case MTRAS2:
190    case MTRAS3:
191    case MTSETDENSITY:
192       errno = ENOTTY;
193       result = -1;
194       break;
195
196    case MTFSF:                  /* Forward space over mt_count filemarks. */
197       do {
198          result = fsf();
199       } while (--count > 0 && result == 0);
200       break;
201
202    case MTBSF:                  /* Backward space over mt_count filemarks. */
203       do {
204          result = bsf();
205       } while (--count > 0 && result == 0);
206       break;
207
208    case MTFSR:      /* Forward space over mt_count records (tape blocks). */
209 /*
210     file number = 1
211     block number = 0
212    
213     file number = 1
214     block number = 1
215    
216     mt: /dev/lto2: Erreur d'entree/sortie
217    
218     file number = 2
219     block number = 0
220 */
221       /* tester si on se trouve a la fin du fichier */
222       result = fsr(mt_com->mt_count);
223       break;
224
225    case MTBSR:      /* Backward space over mt_count records (tape blocks). */
226       result = bsr(mt_com->mt_count);
227       break;
228
229    case MTWEOF:                 /* Write mt_count filemarks. */
230       do {
231          result = weof();
232       } while (result == 0 && --count > 0);
233       break;
234
235    case MTREW:                  /* Rewind. */
236       Dmsg0(dbglevel, "rewind faketape\n");
237       check_eof();
238       atEOF = atEOD = false;
239       atBOT = true;
240       current_file = 0;
241       current_block = 0;
242       lseek(fd, 0, SEEK_SET);
243       read_fm(FT_READ_EOF);
244       break;
245
246    case MTOFFL:                 /* put tape offline */
247       result = offline();
248       break;
249
250    case MTRETEN:                /* Re-tension tape. */
251       result = 0;
252       break;
253
254    case MTBSFM:                 /* not used by bacula */
255       errno = EIO;
256       result = -1;
257       break;
258
259    case MTFSFM:                 /* not used by bacula */
260       errno = EIO;
261       result = -1;
262       break;
263
264    case MTEOM:/* Go to the end of the recorded media (for appending files). */
265       while (next_FM) {
266          lseek(fd, next_FM, SEEK_SET);
267          if (read_fm(FT_READ_EOF)) {
268             current_file++;
269          }
270       }
271       off_t l;
272       while (::read(fd, &l, sizeof(l)) > 0) {
273          if (l) {
274             lseek(fd, l, SEEK_CUR);
275          } else {
276             ASSERT(0);
277          }
278          Dmsg0(dbglevel, "skip 1 block\n");
279       }
280       current_block = -1;
281       atEOF = false;
282       atEOD = true;
283
284 /*
285    file number = 3
286    block number = -1
287 */
288       /* Can be at EOM */
289       break;
290
291    case MTERASE:                /* not used by bacula */
292       atEOD = true;
293       atEOF = false;
294       atEOT = false;
295
296       current_file = 0;
297       current_block = -1;
298       lseek(fd, 0, SEEK_SET);
299       read_fm(FT_READ_EOF);
300       truncate_file();
301       break;
302
303    case MTSETBLK:
304       break;
305
306    case MTSEEK:
307       break;
308
309    case MTTELL:
310       break;
311
312    case MTFSS:
313       break;
314
315    case MTBSS:
316       break;
317
318    case MTWSM:
319       break;
320
321    case MTLOCK:
322       break;
323
324    case MTUNLOCK:
325       break;
326
327    case MTLOAD:
328       break;
329
330    case MTUNLOAD:
331       break;
332
333    case MTCOMPRESSION:
334       break;
335
336    case MTSETPART:
337       break;
338
339    case MTMKPART:
340       break;
341    }
342
343    return result == 0 ? 0 : -1;
344 }
345
346 int faketape::tape_get(struct mtget *mt_get)
347 {
348    int density = 1;
349    int block_size = 1024;
350
351    mt_get->mt_type = MT_ISSCSI2;
352    mt_get->mt_blkno = current_block;
353    mt_get->mt_fileno = current_file;
354
355    mt_get->mt_resid = -1;
356 //   pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
357
358    /* TODO */
359    mt_get->mt_dsreg = 
360       ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
361       ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
362
363
364    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
365
366    if (atEOF) {
367       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
368    }
369
370    if (atBOT) {
371       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
372    }
373    if (atEOT) {
374       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
375    }
376
377    if (atEOD) {
378       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
379    }
380
381    if (0) { //WriteProtected) {
382       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
383    }
384
385    if (online) {
386       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
387    } else {
388       mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
389    }
390    mt_get->mt_erreg = 0;
391
392    return 0;
393 }
394
395 int faketape::tape_pos(struct mtpos *mt_pos)
396 {
397    if (current_block >= 0) {
398       mt_pos->mt_blkno = current_block;
399       return 0;
400    }
401
402    return -1;
403 }
404
405 /*
406  * This function try to emulate the append only behavior
407  * of a tape. When you wrote something, data after the
408  * current position are discarded.
409  */
410 int faketape::truncate_file()
411 {  
412    Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
413    ftruncate(fd, lseek(fd, 0, SEEK_CUR));
414    last_file = current_file;
415    atEOD=true;
416    update_pos();
417    return 0;
418 }
419
420 faketape::faketape()
421 {
422    fd = -1;
423
424    atEOF = false;
425    atBOT = false;
426    atEOT = false;
427    atEOD = false;
428    online = false;
429    needEOF = false;
430
431    file_block = 0;
432    last_file = 0;
433    current_file = 0;
434    current_block = -1;
435
436    max_block = 2*1024*2048;      /* 2GB */
437 }
438
439 faketape::~faketape()
440 {
441 }
442
443 int faketape::get_fd()
444 {
445    return this->fd;
446 }
447
448 /*
449  * TODO: check if after a write op, and other tape op put a EOF
450  */
451 int faketape::write(const void *buffer, unsigned int count)
452 {
453    ASSERT(online);
454    ASSERT(current_file >= 0);
455    ASSERT(count > 0);
456    ASSERT(buffer);
457
458    unsigned int nb;
459    Dmsg3(dbglevel*2, "write len=%i %i:%i\n", 
460          count, current_file,current_block);
461
462    if (atEOT) {
463       Dmsg0(dbglevel, "write nothing, EOT !\n");
464       errno = ENOSPC;
465       return -1;
466    }
467
468    if (!atEOD) {                /* if not at the end of the data */
469       truncate_file();
470    }
471  
472    if (current_block != -1) {
473       current_block++;
474    }
475
476    atBOT = false;
477    atEOF = false;
478    atEOD = true;                /* End of data */
479
480    needEOF = true;              /* next operation need EOF mark */
481
482    uint32_t size = count;
483    ::write(fd, &size, sizeof(uint32_t));
484    nb = ::write(fd, buffer, count);
485    
486    if (nb != count) {
487       atEOT = true;
488       Dmsg2(dbglevel, 
489             "Not enough space writing only %i of %i requested\n", 
490             nb, count);
491    }
492
493    update_pos();
494
495    return nb;
496 }
497
498 /*
499  *  +---+---------+---+------------------+---+-------------------+
500  *  |00N|  DATA   |0LN|   DATA           |0LC|     DATA          |
501  *  +---+---------+---+------------------+---+-------------------+
502  *
503  *  0 : zero
504  *  L : Last FileMark offset
505  *  N : Next FileMark offset
506  *  C : Current FileMark Offset
507  */
508 int faketape::weof()
509 {
510    ASSERT(online);
511    ASSERT(current_file >= 0);
512
513    if (atEOT) {
514       errno = ENOSPC;
515       current_block = -1;
516       return -1;
517    }
518
519    if (!atEOD) {
520       truncate_file();             /* nothing after this point */
521    }
522
523    last_FM = cur_FM;
524    cur_FM = lseek(fd, 0, SEEK_CUR); // current position
525    
526    /* update previous next_FM  */
527    lseek(fd, last_FM + sizeof(uint32_t)+sizeof(off_t), SEEK_SET);
528    ::write(fd, &cur_FM, sizeof(off_t));
529    lseek(fd, cur_FM, SEEK_SET);
530
531    next_FM = 0;
532
533    uint32_t c=0;
534    ::write(fd, &c,       sizeof(uint32_t)); // EOF
535    ::write(fd, &last_FM, sizeof(last_FM));  // F-1
536    ::write(fd, &next_FM, sizeof(next_FM));  // F   (will be updated next time)
537
538    current_file++;
539    current_block = 0;
540
541    needEOF = false;
542    atEOD = false;
543    atBOT = false;
544    atEOF = true;
545
546    last_file = MAX(current_file, last_file);
547
548    Dmsg4(dbglevel, "Writing EOF %i:%i last=%lli cur=%lli next=0\n", 
549          current_file, current_block, last_FM, cur_FM);
550
551    return 0;
552 }
553
554 /*
555  * Go to next FM
556  */
557 int faketape::fsf()
558 {   
559    ASSERT(online);
560    ASSERT(current_file >= 0);
561    ASSERT(fd >= 0);
562 /*
563  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
564  */
565
566    int ret=0;
567    if (atEOT || atEOD) {
568       errno = EIO;
569       current_block = -1;
570       return -1;
571    }
572
573    atBOT = false;
574    Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file);
575
576    if (next_FM > cur_FM) {      /* not the last file */
577       lseek(fd, next_FM, SEEK_SET);
578       read_fm(FT_READ_EOF);
579       current_file++;
580       atEOF = true;
581       ret = 0;
582
583    } else if (atEOF) {          /* last file mark */
584       current_block=-1;
585       errno = EIO;
586       atEOF = false;
587       atEOD = true;
588
589    } else {                     /* last file, but no at the end */
590       fsr(100000);
591
592       Dmsg0(dbglevel, "Try to FSF after EOT\n");
593       errno = EIO;
594       current_file = last_file ;
595       current_block = -1;
596       atEOD=true;
597       ret = -1;
598    }
599    return ret;
600 }
601
602 /* /------------\ /---------------\
603  * +---+------+---+---------------+-+
604  * |OLN|      |0LN|               | |
605  * +---+------+---+---------------+-+
606  */
607
608 bool faketape::read_fm(FT_READ_FM_MODE read_all)
609 {
610    int ret;
611    uint32_t c;
612    if (read_all == FT_READ_EOF) {
613       ::read(fd, &c, sizeof(c));
614       if (c != 0) {
615          lseek(fd, cur_FM, SEEK_SET);
616          return false;
617       }
618    }
619
620    cur_FM = lseek(fd, 0, SEEK_CUR) - sizeof(c);
621
622    ::read(fd, &last_FM, sizeof(last_FM));
623    ret = ::read(fd, &next_FM, sizeof(next_FM));
624
625    current_block=0;
626    
627    Dmsg3(dbglevel, "Read FM cur=%lli last=%lli next=%lli\n", 
628          cur_FM, last_FM, next_FM);
629
630    return (ret == sizeof(next_FM));
631 }
632
633 /*
634  * TODO: Check fsr with EOF
635  */
636 int faketape::fsr(int count)
637 {
638    ASSERT(online);
639    ASSERT(current_file >= 0);
640    ASSERT(fd >= 0);
641    
642    int i,nb, ret=0;
643    off_t where=0;
644    uint32_t s;
645    Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n", 
646          current_file,current_block,atEOF,count);
647
648    check_eof();
649
650    if (atEOT) {
651       errno = EIO;
652       current_block = -1;
653       return -1;
654    }
655
656    if (atEOD) {
657       errno = EIO;
658       return -1;
659    }
660
661    atBOT = atEOF = false;   
662
663    /* check all block record */
664    for(i=0; (i < count) && !atEOF ; i++) {
665       nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
666       if (nb == sizeof(uint32_t) && s) {
667          current_block++;
668          where = lseek(fd, s, SEEK_CUR);     /* seek after this block */
669       } else {
670          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
671                current_file, current_block, nb,s);
672          errno = EIO;
673          ret = -1;
674          if (next_FM) {
675             current_file++;
676             read_fm(FT_SKIP_EOF);
677          }
678          atEOF = true;          /* stop the loop */
679       }
680    }
681
682    return ret;
683 }
684
685 /*
686  * BSR + EOF => begin of EOF + EIO
687  * BSR + BSR + EOF => last block
688  * current_block = -1
689  */
690 int faketape::bsr(int count)
691 {
692    ASSERT(online);
693    ASSERT(current_file >= 0);
694    ASSERT(count == 1);
695    ASSERT(fd >= 0);
696
697    check_eof();
698
699    if (!count) {
700       return 0;
701    }
702
703    int ret=0;
704    int last_f=0;
705    int last_b=0;
706
707    off_t last=-1, last2=-1;
708    off_t orig = lseek(fd, 0, SEEK_CUR);
709    int orig_f = current_file;
710    int orig_b = current_block;
711
712    Dmsg4(dbglevel, "bsr(%i) cur_blk=%i orig=%lli cur_FM=%lli\n", 
713          count, current_block, orig, cur_FM);
714
715    /* begin of tape, do nothing */
716    if (atBOT) {
717       errno = EIO;
718       return -1;
719    }
720
721    /* at EOF 0:-1 BOT=0 EOD=0 EOF=0 ERR: Input/output error  */
722    if (atEOF) {
723       lseek(fd, cur_FM, SEEK_SET);
724       atEOF = false;
725       if (current_file > 0) {
726          current_file--;
727       }
728       current_block=-1;
729       errno = EIO;
730       return -1;
731    }
732
733    /*
734     * First, go to cur/last_FM and read all blocks to find the good one
735     */
736    if (cur_FM == orig) {        /* already just before  EOF */
737       lseek(fd, last_FM, SEEK_SET);
738
739    } else {
740       lseek(fd, cur_FM, SEEK_SET);
741    }
742
743    ret = read_fm(FT_READ_EOF);
744
745    do {
746       if (!atEOF) {
747          last2 = last;          /* keep track of the 2 last blocs position */
748          last = lseek(fd, 0, SEEK_CUR);
749          last_f = current_file;
750          last_b = current_block;
751          Dmsg6(dbglevel, "EOF=%i last2=%lli last=%lli < orig=%lli %i:%i\n", 
752                atEOF, last2, last, orig, current_file, current_block);
753       }
754       ret = fsr(1);
755    } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
756
757    if (last2 > 0 && atEOF) {    /* we take the previous position */
758       lseek(fd, last2, SEEK_SET);
759       current_file = last_f;
760       current_block = last_b - 1;
761       Dmsg3(dbglevel, "1 set offset2=%lli %i:%i\n", 
762             last, current_file, current_block);
763
764    } else if (last > 0) {
765       lseek(fd, last, SEEK_SET);
766       current_file = last_f;
767       current_block = last_b;
768       Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n", 
769             last, current_file, current_block);
770    } else {
771       lseek(fd, orig, SEEK_SET);
772       current_file = orig_f;
773       current_block = orig_b;
774       return -1;
775    }
776
777    Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
778    errno=0;
779    atEOT = atEOF = atEOD = false;
780    atBOT = (lseek(fd, 0, SEEK_CUR) - (sizeof(uint32_t)+2*sizeof(off_t))) == 0;
781
782    if (orig_b == -1) {
783       current_block = orig_b;
784    }
785
786    return 0;
787 }
788
789 /* BSF => just before last EOF
790  * EOF + BSF => just before EOF
791  * file 0 + BSF => BOT + errno
792  */
793 int faketape::bsf()
794 {
795    ASSERT(online);
796    ASSERT(current_file >= 0);
797    Dmsg2(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block);
798    int ret = 0;
799
800    check_eof();
801
802    atBOT = atEOF = atEOT = atEOD = false;
803
804    if (current_file == 0) {/* BOT + errno */      
805       lseek(fd, 0, SEEK_SET);
806       read_fm(FT_READ_EOF);
807       current_file = 0;
808       current_block = 0;
809       atBOT = true;
810       errno = EIO;
811       ret = -1;
812    } else {
813       Dmsg1(dbglevel, "bsf last=%lli\n", last_FM);
814       lseek(fd, cur_FM, SEEK_SET);
815       current_file--;
816       current_block=-1;
817    }
818    return ret;
819 }
820
821 /* 
822  * Put faketape in offline mode
823  */
824 int faketape::offline()
825 {
826    close();
827    
828    atEOF = false;               /* End of file */
829    atEOT = false;               /* End of tape */
830    atEOD = false;               /* End of data */
831    atBOT = false;               /* Begin of tape */
832    online = false;
833
834    file_block = 0;
835    current_file = -1;
836    current_block = -1;
837    last_file = -1;
838    return 0;
839 }
840
841 /* A filemark is automatically written to tape if the last tape operation
842  * before close was a write.
843  */
844 int faketape::close()
845 {
846    check_eof();
847    ::close(fd);
848    fd = -1;
849    return 0;
850 }
851
852 /*
853  * When a filemark is encountered while reading, the following happens.  If
854  * there are data remaining in the buffer when the filemark is found, the
855  * buffered data is returned.  The next read returns zero bytes.  The following
856  * read returns data from the next file.  The end of recorded data is sig‐
857  * naled by returning zero bytes for two consecutive read calls.  The third
858  * read returns an error.
859  */
860 int faketape::read(void *buffer, unsigned int count)
861 {
862    ASSERT(online);
863    ASSERT(current_file >= 0);
864    unsigned int nb;
865    uint32_t s;
866    
867    Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block);
868
869    if (atEOT || atEOD) {
870       errno = EIO;
871       return -1;
872    }
873
874    if (atEOF) {
875       if (!next_FM) {
876          atEOD = true;
877          atEOF = false;
878          current_block=-1;
879          return 0;
880       }
881       atEOF=false;
882    }
883
884    check_eof();
885
886    atEOD = atBOT = false;
887
888    /* reading size of data */
889    nb = ::read(fd, &s, sizeof(uint32_t));
890    if (nb <= 0) {
891       atEOF = true;             /* TODO: check this */
892       return 0;
893    }
894
895    if (s > count) {             /* not enough buffer to read block */
896       Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
897       lseek(fd, s, SEEK_CUR);
898       errno = ENOMEM;
899       return -1;
900    }
901
902    if (!s) {                    /* EOF */
903       atEOF = true;
904       if (read_fm(FT_SKIP_EOF)) {
905          current_file++;
906       }
907
908       return 0;
909    }
910
911    /* reading data itself */
912    nb = ::read(fd, buffer, s);
913    if (s != nb) {               /* read error */
914       errno=EIO;
915       set_eot();
916       current_block = -1;
917       Dmsg0(dbglevel, "EOT during reading\n");
918       return -1;
919    }                    /* read ok */
920
921    if (current_block >= 0) {
922       current_block++;
923    }
924
925    return nb;
926 }
927
928 int faketape::open(const char *pathname, int uflags)
929 {
930    Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
931
932    online = true;               /* assume that drive contains a tape */
933
934    struct stat statp;   
935    if (stat(pathname, &statp) != 0) {
936       Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
937       if (uflags & O_NONBLOCK) {
938          online = false;
939          fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600);
940       }
941    } else {
942       fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
943    }
944
945    if (fd < 0) {
946       errno = ENOMEDIUM;
947       return -1;
948    }
949
950    file_block = 0;
951    current_block = 0;
952    current_file = 0;
953    cur_FM = next_FM = last_FM = 0;
954    needEOF = false;
955    atBOT = true;
956    atEOT = atEOD = false;
957
958    /* If the faketape is empty, start by writing a EOF */
959    if (online && !read_fm(FT_READ_EOF)) {
960       weof();
961       last_file = current_file=0;
962    }
963
964    return fd;
965 }
966
967 /* use this to track file usage */
968 void faketape::update_pos()
969 {
970    ASSERT(online);
971    struct stat statp;
972    if (fstat(fd, &statp) == 0) {
973       file_block = statp.st_blocks;
974    } 
975
976    Dmsg1(dbglevel+1, "update_pos=%i\n", file_block);
977
978    if (file_block > max_block) {
979       set_eot();
980    } else {
981       atEOT = false;
982    }
983 }
984
985 void faketape::dump()
986 {
987    Dmsg0(dbglevel+1, "===================\n");
988    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
989    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
990    Dmsg1(dbglevel+1, "file_block=%i\n", file_block);  
991    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
992          atEOF, atEOT, atEOD, atBOT);  
993 }
994
995 #endif /* USE_FAKETAPE */