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