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