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