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