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