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