]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/vtape.c
ebl Fix vtape on win32 and debian.
[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 int vtape::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 vtape\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(VT_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(VT_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(VT_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 vtape::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 vtape::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 vtape::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 vtape::vtape()
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 vtape::~vtape()
439 {
440 }
441
442 int vtape::get_fd()
443 {
444    return this->fd;
445 }
446
447 /*
448  * Write a variable block of count size.
449  * block = vtape_header + data
450  * vtape_header = sizeof(data)
451  * if vtape_header == 0, this is a EOF
452  */
453 ssize_t vtape::write(const void *buffer, size_t count)
454 {
455    ASSERT(online);
456    ASSERT(current_file >= 0);
457    ASSERT(count > 0);
458    ASSERT(buffer);
459
460    ssize_t nb;
461    Dmsg3(dbglevel*2, "write len=%i %i:%i\n", 
462          count, current_file,current_block);
463
464    if (atEOT) {
465       Dmsg0(dbglevel, "write nothing, EOT !\n");
466       errno = ENOSPC;
467       return -1;
468    }
469
470    if (!atEOD) {                /* if not at the end of the data */
471       truncate_file();
472    }
473  
474    if (current_block != -1) {
475       current_block++;
476    }
477
478    atBOT = false;
479    atEOF = false;
480    atEOD = true;                /* End of data */
481
482    needEOF = true;              /* next operation need EOF mark */
483
484    uint32_t size = count;
485    ::write(fd, &size, sizeof(uint32_t));
486    nb = ::write(fd, buffer, count);
487    
488    if (nb != (ssize_t)count) {
489       atEOT = true;
490       Dmsg2(dbglevel, 
491             "Not enough space writing only %i of %i requested\n", 
492             nb, count);
493    }
494
495    update_pos();
496
497    return nb;
498 }
499
500 /*
501  *  +---+---------+---+------------------+---+-------------------+
502  *  |00N|  DATA   |0LN|   DATA           |0LC|     DATA          |
503  *  +---+---------+---+------------------+---+-------------------+
504  *
505  *  0 : zero
506  *  L : Last FileMark offset
507  *  N : Next FileMark offset
508  *  C : Current FileMark Offset
509  */
510 int vtape::weof()
511 {
512    ASSERT(online);
513    ASSERT(current_file >= 0);
514
515    if (atEOT) {
516       errno = ENOSPC;
517       current_block = -1;
518       return -1;
519    }
520
521    if (!atEOD) {
522       truncate_file();             /* nothing after this point */
523    }
524
525    last_FM = cur_FM;
526    cur_FM = lseek(fd, 0, SEEK_CUR); // current position
527    
528    /* update previous next_FM  */
529    lseek(fd, last_FM + sizeof(uint32_t)+sizeof(off_t), SEEK_SET);
530    ::write(fd, &cur_FM, sizeof(off_t));
531    lseek(fd, cur_FM, SEEK_SET);
532
533    next_FM = 0;
534
535    uint32_t c=0;
536    ::write(fd, &c,       sizeof(uint32_t)); // EOF
537    ::write(fd, &last_FM, sizeof(last_FM));  // F-1
538    ::write(fd, &next_FM, sizeof(next_FM));  // F   (will be updated next time)
539
540    current_file++;
541    current_block = 0;
542
543    needEOF = false;
544    atEOD = false;
545    atBOT = false;
546    atEOF = true;
547
548    last_file = MAX(current_file, last_file);
549
550    Dmsg4(dbglevel, "Writing EOF %i:%i last=%lli cur=%lli next=0\n", 
551          current_file, current_block, last_FM, cur_FM);
552
553    return 0;
554 }
555
556 /*
557  * Go to next FM
558  */
559 int vtape::fsf()
560 {   
561    ASSERT(online);
562    ASSERT(current_file >= 0);
563    ASSERT(fd >= 0);
564 /*
565  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
566  */
567
568    int ret=0;
569    if (atEOT || atEOD) {
570       errno = EIO;
571       current_block = -1;
572       return -1;
573    }
574
575    atBOT = false;
576    Dmsg2(dbglevel+1, "fsf %i <= %i\n", current_file, last_file);
577
578    if (next_FM > cur_FM) {      /* not the last file */
579       lseek(fd, next_FM, SEEK_SET);
580       read_fm(VT_READ_EOF);
581       current_file++;
582       atEOF = true;
583       ret = 0;
584
585    } else if (atEOF) {          /* last file mark */
586       current_block=-1;
587       errno = EIO;
588       atEOF = false;
589       atEOD = true;
590
591    } else {                     /* last file, but no at the end */
592       fsr(100000);
593
594       Dmsg0(dbglevel, "Try to FSF after EOT\n");
595       errno = EIO;
596       current_file = last_file ;
597       current_block = -1;
598       atEOD=true;
599       ret = -1;
600    }
601    return ret;
602 }
603
604 /* /------------\ /---------------\
605  * +---+------+---+---------------+-+
606  * |OLN|      |0LN|               | |
607  * +---+------+---+---------------+-+
608  */
609
610 bool vtape::read_fm(VT_READ_FM_MODE read_all)
611 {
612    int ret;
613    uint32_t c;
614    if (read_all == VT_READ_EOF) {
615       ::read(fd, &c, sizeof(c));
616       if (c != 0) {
617          lseek(fd, cur_FM, SEEK_SET);
618          return false;
619       }
620    }
621
622    cur_FM = lseek(fd, 0, SEEK_CUR) - sizeof(c);
623
624    ::read(fd, &last_FM, sizeof(last_FM));
625    ret = ::read(fd, &next_FM, sizeof(next_FM));
626
627    current_block=0;
628    
629    Dmsg3(dbglevel, "Read FM cur=%lli last=%lli next=%lli\n", 
630          cur_FM, last_FM, next_FM);
631
632    return (ret == sizeof(next_FM));
633 }
634
635 /*
636  * TODO: Check fsr with EOF
637  */
638 int vtape::fsr(int count)
639 {
640    ASSERT(online);
641    ASSERT(current_file >= 0);
642    ASSERT(fd >= 0);
643    
644    int i,nb, ret=0;
645    off_t where=0;
646    uint32_t s;
647    Dmsg4(dbglevel, "fsr %i:%i EOF=%i c=%i\n", 
648          current_file,current_block,atEOF,count);
649
650    check_eof();
651
652    if (atEOT) {
653       errno = EIO;
654       current_block = -1;
655       return -1;
656    }
657
658    if (atEOD) {
659       errno = EIO;
660       return -1;
661    }
662
663    atBOT = atEOF = false;   
664
665    /* check all block record */
666    for(i=0; (i < count) && !atEOF ; i++) {
667       nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
668       if (nb == sizeof(uint32_t) && s) {
669          current_block++;
670          where = lseek(fd, s, SEEK_CUR);     /* seek after this block */
671       } else {
672          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
673                current_file, current_block, nb,s);
674          errno = EIO;
675          ret = -1;
676          if (next_FM) {
677             current_file++;
678             read_fm(VT_SKIP_EOF);
679          }
680          atEOF = true;          /* stop the loop */
681       }
682    }
683
684    return ret;
685 }
686
687 /*
688  * BSR + EOF => begin of EOF + EIO
689  * BSR + BSR + EOF => last block
690  * current_block = -1
691  */
692 int vtape::bsr(int count)
693 {
694    ASSERT(online);
695    ASSERT(current_file >= 0);
696    ASSERT(count == 1);
697    ASSERT(fd >= 0);
698
699    check_eof();
700
701    if (!count) {
702       return 0;
703    }
704
705    int ret=0;
706    int last_f=0;
707    int last_b=0;
708
709    off_t last=-1, last2=-1;
710    off_t orig = lseek(fd, 0, SEEK_CUR);
711    int orig_f = current_file;
712    int orig_b = current_block;
713
714    Dmsg4(dbglevel, "bsr(%i) cur_blk=%i orig=%lli cur_FM=%lli\n", 
715          count, current_block, orig, cur_FM);
716
717    /* begin of tape, do nothing */
718    if (atBOT) {
719       errno = EIO;
720       return -1;
721    }
722
723    /* at EOF 0:-1 BOT=0 EOD=0 EOF=0 ERR: Input/output error  */
724    if (atEOF) {
725       lseek(fd, cur_FM, SEEK_SET);
726       atEOF = false;
727       if (current_file > 0) {
728          current_file--;
729       }
730       current_block=-1;
731       errno = EIO;
732       return -1;
733    }
734
735    /*
736     * First, go to cur/last_FM and read all blocks to find the good one
737     */
738    if (cur_FM == orig) {        /* already just before  EOF */
739       lseek(fd, last_FM, SEEK_SET);
740
741    } else {
742       lseek(fd, cur_FM, SEEK_SET);
743    }
744
745    ret = read_fm(VT_READ_EOF);
746
747    do {
748       if (!atEOF) {
749          last2 = last;          /* keep track of the 2 last blocs position */
750          last = lseek(fd, 0, SEEK_CUR);
751          last_f = current_file;
752          last_b = current_block;
753          Dmsg6(dbglevel, "EOF=%i last2=%lli last=%lli < orig=%lli %i:%i\n", 
754                atEOF, last2, last, orig, current_file, current_block);
755       }
756       ret = fsr(1);
757    } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
758
759    if (last2 > 0 && atEOF) {    /* we take the previous position */
760       lseek(fd, last2, SEEK_SET);
761       current_file = last_f;
762       current_block = last_b - 1;
763       Dmsg3(dbglevel, "1 set offset2=%lli %i:%i\n", 
764             last, current_file, current_block);
765
766    } else if (last > 0) {
767       lseek(fd, last, SEEK_SET);
768       current_file = last_f;
769       current_block = last_b;
770       Dmsg3(dbglevel, "2 set offset=%lli %i:%i\n", 
771             last, current_file, current_block);
772    } else {
773       lseek(fd, orig, SEEK_SET);
774       current_file = orig_f;
775       current_block = orig_b;
776       return -1;
777    }
778
779    Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
780    errno=0;
781    atEOT = atEOF = atEOD = false;
782    atBOT = (lseek(fd, 0, SEEK_CUR) - (sizeof(uint32_t)+2*sizeof(off_t))) == 0;
783
784    if (orig_b == -1) {
785       current_block = orig_b;
786    }
787
788    return 0;
789 }
790
791 /* BSF => just before last EOF
792  * EOF + BSF => just before EOF
793  * file 0 + BSF => BOT + errno
794  */
795 int vtape::bsf()
796 {
797    ASSERT(online);
798    ASSERT(current_file >= 0);
799    Dmsg2(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block);
800    int ret = 0;
801
802    check_eof();
803
804    atBOT = atEOF = atEOT = atEOD = false;
805
806    if (current_file == 0) {/* BOT + errno */      
807       lseek(fd, 0, SEEK_SET);
808       read_fm(VT_READ_EOF);
809       current_file = 0;
810       current_block = 0;
811       atBOT = true;
812       errno = EIO;
813       ret = -1;
814    } else {
815       Dmsg1(dbglevel, "bsf last=%lli\n", last_FM);
816       lseek(fd, cur_FM, SEEK_SET);
817       current_file--;
818       current_block=-1;
819    }
820    return ret;
821 }
822
823 /* 
824  * Put vtape in offline mode
825  */
826 int vtape::offline()
827 {
828    close();
829    
830    atEOF = false;               /* End of file */
831    atEOT = false;               /* End of tape */
832    atEOD = false;               /* End of data */
833    atBOT = false;               /* Begin of tape */
834    online = false;
835
836    file_block = 0;
837    current_file = -1;
838    current_block = -1;
839    last_file = -1;
840    return 0;
841 }
842
843 /* A filemark is automatically written to tape if the last tape operation
844  * before close was a write.
845  */
846 int vtape::close()
847 {
848    check_eof();
849    ::close(fd);
850    fd = -1;
851    return 0;
852 }
853
854 /*
855  * When a filemark is encountered while reading, the following happens.  If
856  * there are data remaining in the buffer when the filemark is found, the
857  * buffered data is returned.  The next read returns zero bytes.  The following
858  * read returns data from the next file.  The end of recorded data is signaled
859  * by returning zero bytes for two consecutive read calls.  The third read
860  * returns an error.
861  */
862 ssize_t vtape::read(void *buffer, size_t count)
863 {
864    ASSERT(online);
865    ASSERT(current_file >= 0);
866    ssize_t nb;
867    uint32_t s;
868    
869    Dmsg2(dbglevel*2, "read %i:%i\n", current_file, current_block);
870
871    if (atEOT || atEOD) {
872       errno = EIO;
873       return -1;
874    }
875
876    if (atEOF) {
877       if (!next_FM) {
878          atEOD = true;
879          atEOF = false;
880          current_block=-1;
881          return 0;
882       }
883       atEOF=false;
884    }
885
886    check_eof();
887
888    atEOD = atBOT = false;
889
890    /* reading size of data */
891    nb = ::read(fd, &s, sizeof(uint32_t));
892    if (nb <= 0) {
893       atEOF = true;             /* TODO: check this */
894       return 0;
895    }
896
897    if (s > count) {             /* not enough buffer to read block */
898       Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
899       lseek(fd, s, SEEK_CUR);
900       errno = ENOMEM;
901       return -1;
902    }
903
904    if (!s) {                    /* EOF */
905       atEOF = true;
906       if (read_fm(VT_SKIP_EOF)) {
907          current_file++;
908       }
909
910       return 0;
911    }
912
913    /* reading data itself */
914    nb = ::read(fd, buffer, s);
915    if (nb != (ssize_t)s) { /* read error */
916       errno=EIO;
917       atEOT=true;
918       current_block = -1;
919       Dmsg0(dbglevel, "EOT during reading\n");
920       return -1;
921    }                    /* read ok */
922
923    if (current_block >= 0) {
924       current_block++;
925    }
926
927    return nb;
928 }
929
930 int vtape::open(const char *pathname, int uflags)
931 {
932    Dmsg2(dbglevel, "vtape::open(%s, %i)\n", pathname, uflags);
933
934    online = true;               /* assume that drive contains a tape */
935
936    struct stat statp;   
937    if (stat(pathname, &statp) != 0) {
938       Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
939       if (uflags & O_NONBLOCK) {
940          online = false;
941          fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600);
942       }
943    } else {
944       fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
945    }
946
947    if (fd < 0) {
948       errno = ENOMEDIUM;
949       return -1;
950    }
951
952    file_block = 0;
953    current_block = 0;
954    current_file = 0;
955    cur_FM = next_FM = last_FM = 0;
956    needEOF = false;
957    atBOT = true;
958    atEOT = atEOD = false;
959
960    /* If the vtape is empty, start by writing a EOF */
961    if (online && !read_fm(VT_READ_EOF)) {
962       weof();
963       last_file = current_file=0;
964    }
965
966    return fd;
967 }
968
969 /* use this to track file usage */
970 void vtape::update_pos()
971 {
972    ASSERT(online);
973    struct stat statp;
974    if (fstat(fd, &statp) == 0) {
975       file_block = statp.st_blocks;
976    } 
977
978    Dmsg1(dbglevel*2, "update_pos=%i\n", file_block);
979
980    if (file_block > max_block) {
981       atEOT = true;
982    } else {
983       atEOT = false;
984    }
985 }
986
987 void vtape::dump()
988 {
989    Dmsg0(dbglevel+1, "===================\n");
990    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
991    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
992    Dmsg1(dbglevel+1, "file_block=%i\n", file_block);  
993    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
994          atEOF, atEOT, atEOD, atBOT);  
995 }
996