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