]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/faketape.c
ebl Update fake tape driver
[bacula/bacula] / bacula / patches / testing / faketape.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 John Walker.
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  */
45
46 #include "faketape.h"
47 #include <dirent.h>
48 #include <sys/mtio.h>
49 #include <ctype.h>
50
51 static int dbglevel = 10;
52 #define FILE_OFFSET 30
53 faketape *ftape_list[FTAPE_MAX_DRIVE];
54
55 static faketape *get_tape(int fd)
56 {
57    ASSERT(fd >= 0);
58
59    if (fd >= FTAPE_MAX_DRIVE) {
60       /* error */
61       return NULL;
62    }
63
64    return ftape_list[fd];
65 }
66
67 static bool put_tape(faketape *ftape)
68 {
69    ASSERT(ftape != NULL);
70
71    int fd = ftape->get_fd();
72    if (fd >= FTAPE_MAX_DRIVE) {
73       /* error */
74       return false;
75    }
76    ftape_list[fd] = ftape;
77    return true;
78 }
79
80 void faketape_debug(int level)
81 {
82    dbglevel = level;
83 }
84
85 /****************************************************************/
86 /* theses function will replace open/read/write/close/ioctl
87  * in bacula core
88  */
89 int faketape_open(const char *pathname, int flags)
90 {
91    ASSERT(pathname != NULL);
92
93    int fd;
94    faketape *tape = new faketape();
95    fd = tape->open(pathname, flags);
96    if (fd > 0) {
97       put_tape(tape);
98    }
99    return fd;
100 }
101
102 int faketape_read(int fd, void *buffer, unsigned int count)
103 {
104    faketape *tape = get_tape(fd);
105    ASSERT(tape != NULL);
106    return tape->read(buffer, count);
107 }
108
109 int faketape_write(int fd, const void *buffer, unsigned int count)
110 {
111    faketape *tape = get_tape(fd);
112    ASSERT(tape != NULL);
113    return tape->write(buffer, count);
114 }
115
116 int faketape_close(int fd)
117 {
118    faketape *tape = get_tape(fd);
119    ASSERT(tape != NULL);
120    tape->close();
121    delete tape;
122    return 0;
123 }
124
125 int faketape_ioctl(int fd, unsigned long int request, ...)
126 {
127    va_list argp;
128    int result=0;
129
130    faketape *t = get_tape(fd);
131    if (!t) {
132       errno = EBADF;
133       return -1;
134    }
135
136    va_start(argp, request);
137
138    if (request == MTIOCTOP) {
139       result = t->tape_op(va_arg(argp, mtop *));
140    } else if (request == MTIOCGET) {
141       result = t->tape_get(va_arg(argp, mtget *));
142    } else if (request == MTIOCPOS) {
143       result = t->tape_pos(va_arg(argp, mtpos *));
144    } else {
145       errno = ENOTTY;
146       result = -1;
147    }
148    va_end(argp);
149
150    return result;
151 }
152
153 /****************************************************************/
154
155 int faketape::tape_op(struct mtop *mt_com)
156 {
157    int result=0;
158    
159    switch (mt_com->mt_op)
160    {
161    case MTRESET:
162    case MTNOP:
163    case MTSETDRVBUFFER:
164       break;
165
166    default:
167    case MTRAS1:
168    case MTRAS2:
169    case MTRAS3:
170    case MTSETDENSITY:
171       errno = ENOTTY;
172       result = -1;
173       break;
174
175    case MTFSF:                  /* Forward space over mt_count filemarks. */
176       result = fsf(mt_com->mt_count);
177       break;
178
179    case MTBSF:                  /* Backward space over mt_count filemarks. */
180       result = bsf(mt_com->mt_count);
181       break;
182
183    case MTFSR:      /* Forward space over mt_count records (tape blocks). */
184 /*
185     file number = 1
186     block number = 0
187    
188     file number = 1
189     block number = 1
190    
191     mt: /dev/lto2: Erreur d'entrée/sortie
192    
193     file number = 2
194     block number = 0
195 */
196       /* tester si on se trouve a la fin du fichier */
197       result = fsr(mt_com->mt_count);
198       break;
199
200    case MTBSR:      /* Backward space over mt_count records (tape blocks). */
201       result = bsr(mt_com->mt_count);
202       break;
203
204    case MTWEOF:                 /* Write mt_count filemarks. */
205       result = weof(mt_com->mt_count);
206       break;
207
208    case MTREW:                  /* Rewind. */
209       Dmsg0(dbglevel, "rewind faketape\n");
210       atEOF = atEOD = false;
211       atBOT = true;
212       current_file = 0;
213       current_block = 0;
214       seek_file();
215       break;
216
217    case MTOFFL:                 /* put tape offline */
218       result = offline();
219       break;
220
221    case MTRETEN:                /* Re-tension tape. */
222       result = 0;
223       break;
224
225    case MTBSFM:                 /* not used by bacula */
226       errno = EIO;
227       result = -1;
228       break;
229
230    case MTFSFM:                 /* not used by bacula */
231       errno = EIO;
232       result = -1;
233       break;
234
235    case MTEOM:/* Go to the end of the recorded media (for appending files). */
236 /*
237    file number = 3
238    block number = -1
239 */
240       /* Can be at EOM */
241       atBOT = false;
242       atEOF = false;
243       atEOD = true;
244       atEOT = false;
245
246       current_file = last_file;
247       current_block = -1;
248       seek_file();
249       break;
250
251    case MTERASE:                /* not used by bacula */
252       atEOD = true;
253       atEOF = false;
254       atEOT = false;
255
256       current_file = 0;
257       current_block = -1;
258       seek_file();
259       truncate_file();
260       break;
261
262    case MTSETBLK:
263       break;
264
265    case MTSEEK:
266       break;
267
268    case MTTELL:
269       break;
270
271    case MTFSS:
272       break;
273
274    case MTBSS:
275       break;
276
277    case MTWSM:
278       break;
279
280    case MTLOCK:
281       break;
282
283    case MTUNLOCK:
284       break;
285
286    case MTLOAD:
287       break;
288
289    case MTUNLOAD:
290       break;
291
292    case MTCOMPRESSION:
293       break;
294
295    case MTSETPART:
296       break;
297
298    case MTMKPART:
299       break;
300    }
301 //
302 //   switch (result) {
303 //   case NO_ERROR:
304 //   case -1:   /* Error has already been translated into errno */
305 //      break;
306 //
307 //   default:
308 //   case ERROR_FILEMARK_DETECTED:
309 //      errno = EIO;
310 //      break;
311 //
312 //   case ERROR_END_OF_MEDIA:
313 //      errno = EIO;
314 //      break;
315 //
316 //   case ERROR_NO_DATA_DETECTED:
317 //      errno = EIO;
318 //      break;
319 //
320 //   case ERROR_NO_MEDIA_IN_DRIVE:
321 //      errno = ENOMEDIUM;
322 //      break;
323 //
324 //   case ERROR_INVALID_HANDLE:
325 //   case ERROR_ACCESS_DENIED:
326 //   case ERROR_LOCK_VIOLATION:
327 //      errno = EBADF;
328 //      break;
329 //   }
330 //
331    return result == 0 ? 0 : -1;
332 }
333
334 int faketape::tape_get(struct mtget *mt_get)
335 {
336    int density = 1;
337    int block_size = 1024;
338
339    mt_get->mt_type = MT_ISSCSI2;
340    mt_get->mt_blkno = current_block;
341    mt_get->mt_fileno = current_file;
342
343    mt_get->mt_resid = -1;
344 //   pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
345
346    /* TODO */
347    mt_get->mt_dsreg = 
348       ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
349       ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
350
351
352    mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
353
354    if (atEOF) {
355       mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
356    }
357
358    if (atBOT) {
359       mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
360    }
361    if (atEOT) {
362       mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
363    }
364
365    if (atEOD) {
366       mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
367    }
368
369    if (0) { //WriteProtected) {
370       mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
371    }
372
373    if (online) {
374       mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
375    } else {
376       mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
377    }
378    mt_get->mt_erreg = 0;
379
380    return 0;
381 }
382
383 int faketape::tape_pos(struct mtpos *mt_pos)
384 {
385    if (current_block >= 0) {
386       mt_pos->mt_blkno = current_block;
387       return 0;
388    }
389
390    return -1;
391 }
392
393 /*
394  * This function try to emulate the append only behavior
395  * of a tape. When you wrote something, data after the
396  * current position are discarded.
397  */
398 int faketape::truncate_file()
399 {  
400    Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
401    ftruncate(fd, lseek(fd, 0, SEEK_CUR));
402    last_file = current_file;
403    atEOD=true;
404    return 0;
405 }
406
407 faketape::faketape()
408 {
409    fd = -1;
410
411    atEOF = false;
412    atBOT = false;
413    atEOT = false;
414    atEOD = false;
415    online = false;
416    inplace = false;
417    needEOF = false;
418
419    file_size = 0;
420    last_file = 0;
421    current_file = 0;
422    current_block = -1;
423    current_pos = 0;
424
425    max_block = 1024*1024*1024*1024*8;
426
427    volume = get_pool_memory(PM_NAME);
428 }
429
430 faketape::~faketape()
431 {
432    free_pool_memory(volume);
433 }
434
435 int faketape::get_fd()
436 {
437    return this->fd;
438 }
439
440 /*
441  * TODO: regarder si apres un write une operation x pose un EOF
442  */
443 int faketape::write(const void *buffer, unsigned int count)
444 {
445    ASSERT(current_file >= 0);
446    ASSERT(count > 0);
447    ASSERT(buffer);
448
449    unsigned int nb;
450    Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
451
452    if (atEOT) {
453       Dmsg0(dbglevel, "write nothing, EOT !\n");
454       errno = ENOSPC;
455       return -1;
456    }
457
458    if (!inplace) {
459       seek_file();
460    }
461
462    if (!atEOD) {                /* if not at the end of the data */
463       truncate_file();
464    }
465  
466    if (current_block != -1) {
467       current_block++;
468    }
469
470    atBOT = false;
471    atEOF = false;
472    atEOD = true;                /* End of data */
473
474    needEOF = true;              /* next operation need EOF mark */
475
476 //   if ((count + file_size) > max_size) {
477 //      Dmsg2(dbglevel, 
478 //          "EOT writing only %i of %i requested\n", 
479 //          max_size - file_size, count);
480 //      count = max_size - file_size;
481 //      atEOT = true;
482 //   }
483
484    uint32_t size = count;
485    ::write(fd, &size, sizeof(uint32_t));
486    nb = ::write(fd, buffer, count);
487    
488    file_size += sizeof(uint32_t) + nb;
489
490    if (nb != count) {
491       atEOT = true;
492       Dmsg2(dbglevel, 
493             "Not enough space writing only %i of %i requested\n", 
494             nb, count);
495    }
496
497    return nb;
498 }
499
500 int faketape::weof(int count)
501 {
502    ASSERT(current_file >= 0);
503    Dmsg3(dbglevel, "Writing EOF %i:%i last=%i\n", 
504          current_file, current_block,last_file);
505    if (atEOT) {
506       current_block = -1;
507       return -1;
508    }
509    needEOF = false;
510    truncate_file();             /* nothing after this point */
511
512    /* TODO: check this */
513    current_file += count;
514    current_block = 0;
515
516    uint32_t c=0;
517    seek_file();
518    ::write(fd, &c, sizeof(uint32_t));
519    seek_file();
520
521    atEOD = false;
522    atBOT = false;
523    atEOF = true;
524
525    return 0;
526 }
527
528 int faketape::fsf(int count)
529 {   
530    ASSERT(current_file >= 0);
531    ASSERT(fd >= 0);
532 /*
533  * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
534  */
535    check_eof();
536
537    int ret;
538    if (atEOT) {
539       current_block = -1;
540       return -1;
541    }
542
543    atBOT = atEOF = false;
544    Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
545
546    if ((current_file + count) <= last_file) {
547       current_file += count;
548       current_block = 0;
549       ret = 0;
550    } else {
551       Dmsg0(dbglevel, "Try to FSF after EOT\n");
552       current_file = last_file ;
553       current_block = -1;
554       atEOD=true;
555       ret = -1;
556    }
557    seek_file();
558 //   read_eof();
559    return ret;
560 }
561
562 int faketape::fsr(int count)
563 {
564    ASSERT(current_file >= 0);
565    ASSERT(fd >= 0);
566    
567    int i,nb, ret=0;
568    off_t where=0;
569    uint32_t s;
570    Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
571
572    check_eof();
573
574    if (atEOT) {
575       errno = EIO;
576       current_block = -1;
577       return -1;
578    }
579
580    if (atEOD) {
581       errno = EIO;
582       return -1;
583    }
584
585    atBOT = atEOF = false;   
586
587    /* check all block record */
588    for(i=0; (i < count) && !atEOF ; i++) {
589       nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
590       if (nb == sizeof(uint32_t) && s) {
591          current_block++;
592          where = lseek(fd, s, SEEK_CUR);        /* seek after this block */
593       } else {
594          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
595                current_file, current_block, nb,s);
596          errno = EIO;
597          ret = -1;
598          if (current_file < last_file) {
599             current_block = 0;
600             current_file++;
601             seek_file();
602          }
603          atEOF = true;          /* stop the loop */
604       }
605    }
606
607    find_maxfile();              /* refresh stats */
608
609    if (where == file_size) {
610       atEOD = true;
611    }
612    return ret;
613 }
614
615 // TODO: Make it working, at this time we get only the EOF position...
616 int faketape::bsr(int count)
617 {
618    Dmsg2(dbglevel, "bsr current_block=%i count=%i\n", 
619          current_block, count);
620
621    ASSERT(current_file >= 0);
622    ASSERT(fd >= 0);
623
624    check_eof();
625
626    ASSERT(count == 1);
627
628    if (!count) {
629       return 0;
630    }
631
632    int ret=0;
633    int last_f=0;
634    int last_b=0;
635
636    off_t last=-1, last2=-1;
637    off_t orig = lseek(fd, 0, SEEK_CUR);
638    int orig_f = current_file;
639    int orig_b = current_block;
640
641    current_block=0;
642    seek_file();
643
644    do {
645       if (!atEOF) {
646          last2 = last;
647          last = lseek(fd, 0, SEEK_CUR);
648          last_f = current_file;
649          last_b = current_block;
650          Dmsg5(dbglevel, "EOF=%i last=%lli orig=%lli %i:%i\n", atEOF, last, orig, current_file, current_block);
651       }
652       ret = fsr(1);
653    } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
654
655    if (last2 > 0 && atEOF) {    /* we take the previous position */
656       lseek(fd, last2, SEEK_SET);
657       current_file = last_f;
658       current_block = last_b - 1;
659       Dmsg3(dbglevel, "set offset2=%lli %i:%i\n", 
660             last, current_file, current_block);
661
662    } else if (last > 0) {
663       lseek(fd, last, SEEK_SET);
664       current_file = last_f;
665       current_block = last_b;
666       Dmsg3(dbglevel, "set offset=%lli %i:%i\n", 
667             last, current_file, current_block);
668    } else {
669       lseek(fd, orig, SEEK_SET);
670       current_file = orig_f;
671       current_block = orig_b;
672       return -1;
673    }
674
675    Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
676    atEOT = atEOF = atEOD = false;
677
678    return 0;
679 }
680
681 //int faketape::read_eof()
682 //{
683 //   int s, nb;
684 //   off_t old = lseek(fd, 0, SEEK_CUR);
685 //   nb = ::read(fd, &s, sizeof(s));
686 //   if (nb >= 0 && (nb != sizeof(s) || !s)) { /* EOF */
687 //      atEOF = true;
688 //   }
689 //   lseek(fd, old, SEEK_SET);
690 //   return 0;
691 //}
692
693 int faketape::bsf(int count)
694 {
695    ASSERT(current_file >= 0);
696    Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
697    int ret = 0;
698
699    check_eof();
700    atBOT = atEOF = atEOT = atEOD = false;
701
702    if (current_file - count < 0) {
703       current_file = 0;
704       current_block = 0;
705       atBOT = true;
706       errno = EIO;
707       ret = -1;
708    } else {
709       current_file = current_file - count + 1;
710       current_block = -1;
711       seek_file();
712       current_file--;
713       /* go just before last EOF */
714       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
715    }
716    return ret;
717 }
718
719 /* 
720  * Put faketape in offline mode
721  */
722 int faketape::offline()
723 {
724    check_eof();
725    close();
726    
727    atEOF = false;               /* End of file */
728    atEOT = false;               /* End of tape */
729    atEOD = false;               /* End of data */
730    atBOT = false;               /* Begin of tape */
731
732    current_file = -1;
733    current_block = -1;
734    last_file = -1;
735    return 0;
736 }
737
738 int faketape::close()
739 {
740    check_eof();
741    ::close(fd);
742    fd = -1;
743    return 0;
744 }
745 /*
746  **rb
747  **status
748  * EOF Bacula status: file=2 block=0
749  * Device status: EOF ONLINE IM_REP_EN file=2 block=0
750  **rb
751  **status
752  * EOD EOF Bacula status: file=2 block=0
753  * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
754  *
755  */
756
757 int faketape::read(void *buffer, unsigned int count)
758 {
759    ASSERT(current_file >= 0);
760    unsigned int nb;
761    uint32_t s;
762    
763    Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
764
765    if (atEOT || atEOD) {
766       return 0;
767    }
768
769    if (atEOF) {
770       current_file++;
771       current_block=0;
772       inplace = false;
773       atEOF = false;
774    }
775
776    if (!inplace) {
777       seek_file();
778    }
779
780    atEOD = atBOT = false;
781    current_block++;
782
783    nb = ::read(fd, &s, sizeof(uint32_t));
784    if (nb <= 0) {
785       atEOF = true;
786       return 0;
787    }
788    if (s > count) {             /* not enough buffer to read block */
789       lseek(fd, s, SEEK_CUR);
790       errno = ENOMEM;
791       return -1;
792    }
793    if (!s) {                    /* EOF */
794       atEOF = true;
795       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
796       return 0;
797    }
798    nb = ::read(fd, buffer, s);
799    if (s != nb) {
800       atEOF = true;
801       if (current_file == last_file) {
802          atEOD = true;
803          current_block = -1;
804       }
805       Dmsg0(dbglevel, "EOF during reading\n");
806    }
807    return nb;
808 }
809
810 int faketape::open(const char *pathname, int uflags)
811 {
812    Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
813    pm_strcpy(volume, pathname);
814
815    struct stat statp;   
816    if (stat(volume, &statp) != 0) {
817       Dmsg1(dbglevel, "Can't stat on %s\n", volume);
818       return -1;
819    }
820
821    fd = ::open(pathname, O_CREAT | O_RDWR, 0700);
822    if (fd < 0) {
823       return -1;
824    }
825
826    /* open volume descriptor and get this->fd */
827    if (find_maxfile() < 0) {
828       return -1;
829    }
830
831    current_block = 0;
832    current_file = 0;
833    needEOF = false;
834    online = inplace = true;
835    atBOT = true;
836    atEOT = atEOD = false;
837
838    return fd;
839 }
840
841 /*
842  * read volume to get the last file number
843  */
844 int faketape::find_maxfile()
845 {
846    struct stat statp;
847    if (fstat(fd, &statp) != 0) {
848       return 0;
849    }
850    last_file = statp.st_size>>FILE_OFFSET;
851    file_size = statp.st_size;
852       
853    current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
854    Dmsg3(dbglevel+1, "last_file=%i file_size=%u current_pos=%i\n", 
855          last_file, file_size, current_pos);
856
857    return last_file;
858 }
859
860 int faketape::seek_file()
861 {
862    ASSERT(current_file >= 0);
863    Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
864
865    off_t pos = ((off_t)current_file)<<FILE_OFFSET;
866    if(lseek(fd, pos, SEEK_SET) == -1) {
867       return -1;
868    }
869    if (current_block > 0) {
870       fsr(current_block);
871    }
872    last_file = (last_file > current_file)?last_file:current_file;
873    inplace = true;
874
875    return 0;
876 }
877
878 void faketape::dump()
879 {
880    Dmsg0(dbglevel+1, "===================\n");
881    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
882    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
883    Dmsg1(dbglevel+1, "volume=%s\n", volume);
884    Dmsg1(dbglevel+1, "file_size=%i\n", file_size);  
885    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
886          atEOF, atEOT, atEOD, atBOT);  
887 }
888
889 /****************************************************************
890
891 #define GMT_EOF(x)              ((x) & 0x80000000)
892 #define GMT_BOT(x)              ((x) & 0x40000000)
893 #define GMT_EOT(x)              ((x) & 0x20000000)
894 #define GMT_SM(x)               ((x) & 0x10000000)
895 #define GMT_EOD(x)              ((x) & 0x08000000)
896
897
898  GMT_EOF(x) : La bande est positionnée juste après une filemark (toujours faux
899      après une opération MTSEEK).
900
901  GMT_BOT(x) : La bande est positionnée juste au début du premier fichier
902      (toujours faux après une opération MTSEEK).
903  
904  GMT_EOT(x) : Une opération a atteint la fin physique de la bande (End Of
905  Tape).
906
907  GMT_SM(x) : La bande est positionnée sur une setmark (toujours faux après une
908  opération MTSEEK).
909
910  GMT_EOD(x) : La bande est positionnée à la fin des données enregistrées.
911
912
913 blkno = -1 (after MTBSF MTBSS or MTSEEK)
914 fileno = -1 (after MTBSS or MTSEEK)
915
916 *** mtx load
917 drive type = Generic SCSI-2 tape
918 drive status = 0
919 sense key error = 0
920 residue count = 0
921 file number = 0
922 block number = 0
923 Tape block size 0 bytes. Density code 0x0 (default).
924 Soft error count since last status=0
925 General status bits on (41010000):
926  BOT ONLINE IM_REP_EN
927
928 *** read empty block
929 dd if=/dev/lto2 of=/tmp/toto count=1
930 dd: lecture de `/dev/lto2': Ne peut allouer de la mémoire
931 0+0 enregistrements lus
932 0+0 enregistrements écrits
933 1 octet (1B) copié, 4,82219 seconde, 0,0 kB/s
934
935 file number = 0
936 block number = 1
937
938 *** read file mark
939 dd if=/dev/lto2 of=/tmp/toto count=1
940 0+0 enregistrements lus
941 0+0 enregistrements écrits
942 1 octet (1B) copié, 0,167274 seconde, 0,0 kB/s
943
944 file number = 1
945 block number = 0
946
947  *** write 2 blocks after rewind
948 dd if=/dev/zero of=/dev/lto2 count=2
949 2+0 enregistrements lus
950 2+0 enregistrements écrits
951 1024 octets (1,0 kB) copiés, 6,57402 seconde, 0,2 kB/s
952
953 file number = 1
954 block number = 0
955
956 *** write 2 blocks
957 file number = 2
958 block number = 0
959
960 *** rewind and fsr
961 file number = 0
962 block number = 1
963
964 *** rewind and 2x fsr (we have just 2 blocks)
965 file number = 0
966 block number = 2
967
968 *** fsr
969 mt: /dev/lto2: Erreur
970 file number = 1
971 block number = 0
972
973
974  ****************************************************************/
975
976
977 #ifdef TEST
978
979 int main()
980 {
981    int fd;
982    char buf[500];
983    printf("Starting FakeTape\n");
984
985    mkdir("/tmp/fake", 0700);
986
987
988
989
990    return 0;
991 }
992
993 #endif