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