]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/faketape.c
ebl update
[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    off_t size = count;
472    ::write(fd, &size, sizeof(off_t));
473    nb = ::write(fd, buffer, count);
474    
475    file_size += sizeof(off_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    off_t c=0;
504    seek_file();
505    ::write(fd, &c, sizeof(off_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, s;
556    Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
557
558    check_eof();
559
560    if (atEOT) {
561       errno = EIO;
562       current_block = -1;
563       return -1;
564    }
565
566    if (atEOD) {
567       errno = EIO;
568       return -1;
569    }
570
571    atBOT = atEOF = false;   
572
573    /* check all block record */
574    for(i=0; (i < count) && !atEOF ; i++) {
575       nb = ::read(fd, &s, sizeof(off_t)); /* get size of next block */
576       if (nb == sizeof(off_t) && s) {
577          current_block++;
578          where = lseek(fd, s, SEEK_CUR);        /* seek after this block */
579       } else {
580          Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
581                current_file, current_block, nb,s);
582          errno = EIO;
583          ret = -1;
584          if (current_file < last_file) {
585             current_block = 0;
586             current_file++;
587             seek_file();
588          }
589          atEOF = true;          /* stop the loop */
590       }
591    }
592
593    find_maxfile();              /* refresh stats */
594
595    if (where == file_size) {
596       atEOD = true;
597    }
598    return ret;
599 }
600
601 // TODO: Make it working, at this time we get only the EOF position...
602 int faketape::bsr(int count)
603 {
604    Dmsg2(dbglevel, "bsr current_block=%i count=%i\n", 
605          current_block, count);
606
607    ASSERT(current_file >= 0);
608    ASSERT(fd >= 0);
609
610    check_eof();
611
612    ASSERT(count == 1);
613
614    if (!count) {
615       return 0;
616    }
617
618    int ret=0;
619    int last_f=0;
620    int last_b=0;
621
622    off_t last=-1;
623    off_t orig = lseek(fd, 0, SEEK_CUR);
624
625    current_block=0;
626    seek_file();
627
628    do {
629       if (!atEOF) {
630          last = lseek(fd, 0, SEEK_CUR);
631          last_f = current_file;
632          last_b = current_block;
633          Dmsg5(dbglevel, "EOF=%i last=%lli orig=%lli %i:%i\n", atEOF, last, orig, current_file, current_block);
634       }
635       ret = fsr(1);
636    } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
637
638    if (last > 0) {
639       lseek(fd, last, SEEK_SET);
640       current_file = last_f;
641       current_block = last_b;
642       Dmsg3(dbglevel, "set offset=%lli %i:%i\n", 
643             last, current_file, current_block);
644    }
645
646    Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
647    atEOT = atEOF = atEOD = false;
648
649    return 0;
650 }
651
652 //int faketape::read_eof()
653 //{
654 //   int s, nb;
655 //   off_t old = lseek(fd, 0, SEEK_CUR);
656 //   nb = ::read(fd, &s, sizeof(s));
657 //   if (nb >= 0 && (nb != sizeof(s) || !s)) { /* EOF */
658 //      atEOF = true;
659 //   }
660 //   lseek(fd, old, SEEK_SET);
661 //   return 0;
662 //}
663
664 int faketape::bsf(int count)
665 {
666    ASSERT(current_file >= 0);
667    Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
668    int ret = 0;
669
670    check_eof();
671    atBOT = atEOF = atEOT = atEOD = false;
672
673    if (current_file - count < 0) {
674       current_file = 0;
675       current_block = 0;
676       atBOT = true;
677       errno = EIO;
678       ret = -1;
679    } else {
680       current_file = current_file - count + 1;
681       current_block = -1;
682       seek_file();
683       current_file--;
684       /* go just before last EOF */
685       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(off_t), SEEK_SET);
686    }
687    return ret;
688 }
689
690 /* 
691  * Put faketape in offline mode
692  */
693 int faketape::offline()
694 {
695    check_eof();
696    close();
697    
698    atEOF = false;               /* End of file */
699    atEOT = false;               /* End of tape */
700    atEOD = false;               /* End of data */
701    atBOT = false;               /* Begin of tape */
702
703    current_file = -1;
704    current_block = -1;
705    last_file = -1;
706    return 0;
707 }
708
709 int faketape::close()
710 {
711    check_eof();
712    ::close(fd);
713    fd = -1;
714    return 0;
715 }
716 /*
717  **rb
718  **status
719  * EOF Bacula status: file=2 block=0
720  * Device status: EOF ONLINE IM_REP_EN file=2 block=0
721  **rb
722  **status
723  * EOD EOF Bacula status: file=2 block=0
724  * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
725  *
726  */
727
728 int faketape::read(void *buffer, unsigned int count)
729 {
730    ASSERT(current_file >= 0);
731    unsigned int nb;
732    off_t s;
733
734    if (atEOT || atEOD) {
735       return 0;
736    }
737
738    if (!inplace) {
739       seek_file();
740    }
741    
742    Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
743
744    atBOT = false;
745    current_block++;
746
747    nb = ::read(fd, &s, sizeof(off_t));
748    if (nb <= 0) {
749       atEOF = true;
750       return 0;
751    }
752    if (s > count) {             /* not enough buffer to read block */
753       lseek(fd, s, SEEK_CUR);
754       errno = ENOMEM;
755       return -1;
756    }
757    if (!s) {                    /* EOF */
758       atEOF = true;
759       lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(off_t), SEEK_SET);
760       return 0;
761    }
762    nb = ::read(fd, buffer, s);
763    if (s != nb) {
764       atEOF = true;
765       if (current_file == last_file) {
766          atEOD = true;
767          current_block = -1;
768       }
769       Dmsg0(dbglevel, "EOF during reading\n");
770    }
771    return nb;
772 }
773
774 int faketape::open(const char *pathname, int uflags)
775 {
776    Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
777    pm_strcpy(volume, pathname);
778
779    struct stat statp;   
780    if (stat(volume, &statp) != 0) {
781       Dmsg1(dbglevel, "Can't stat on %s\n", volume);
782       return -1;
783    }
784
785    fd = ::open(pathname, O_CREAT | O_RDWR, 0700);
786    if (fd < 0) {
787       return -1;
788    }
789
790    /* open volume descriptor and get this->fd */
791    if (find_maxfile() < 0) {
792       return -1;
793    }
794
795    current_block = 0;
796    current_file = 0;
797    needEOF = false;
798    online = inplace = true;
799    atBOT = true;
800    atEOT = atEOD = false;
801
802    return fd;
803 }
804
805 /*
806  * read volume to get the last file number
807  */
808 int faketape::find_maxfile()
809 {
810    struct stat statp;
811    if (fstat(fd, &statp) != 0) {
812       return 0;
813    }
814    last_file = statp.st_size>>FILE_OFFSET;
815    file_size = statp.st_size;
816       
817    current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
818    Dmsg3(dbglevel+1, "last_file=%i file_size=%u current_pos=%i\n", 
819          last_file, file_size, current_pos);
820
821    return last_file;
822 }
823
824 int faketape::seek_file()
825 {
826    ASSERT(current_file >= 0);
827    Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
828
829    off_t pos = ((off_t)current_file)<<FILE_OFFSET;
830    if(lseek(fd, pos, SEEK_SET) == -1) {
831       return -1;
832    }
833    if (current_block > 0) {
834       fsr(current_block);
835    }
836    last_file = (last_file > current_file)?last_file:current_file;
837    inplace = true;
838
839    return 0;
840 }
841
842 void faketape::dump()
843 {
844    Dmsg0(dbglevel+1, "===================\n");
845    Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
846    Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
847    Dmsg1(dbglevel+1, "volume=%s\n", volume);
848    Dmsg1(dbglevel+1, "file_size=%i\n", file_size);  
849    Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
850          atEOF, atEOT, atEOD, atBOT);  
851 }
852
853 /****************************************************************
854
855 #define GMT_EOF(x)              ((x) & 0x80000000)
856 #define GMT_BOT(x)              ((x) & 0x40000000)
857 #define GMT_EOT(x)              ((x) & 0x20000000)
858 #define GMT_SM(x)               ((x) & 0x10000000)
859 #define GMT_EOD(x)              ((x) & 0x08000000)
860
861
862  GMT_EOF(x) : La bande est positionnée juste après une filemark (toujours faux
863      après une opération MTSEEK).
864
865  GMT_BOT(x) : La bande est positionnée juste au début du premier fichier
866      (toujours faux après une opération MTSEEK).
867  
868  GMT_EOT(x) : Une opération a atteint la fin physique de la bande (End Of
869  Tape).
870
871  GMT_SM(x) : La bande est positionnée sur une setmark (toujours faux après une
872  opération MTSEEK).
873
874  GMT_EOD(x) : La bande est positionnée à la fin des données enregistrées.
875
876
877 blkno = -1 (after MTBSF MTBSS or MTSEEK)
878 fileno = -1 (after MTBSS or MTSEEK)
879
880 *** mtx load
881 drive type = Generic SCSI-2 tape
882 drive status = 0
883 sense key error = 0
884 residue count = 0
885 file number = 0
886 block number = 0
887 Tape block size 0 bytes. Density code 0x0 (default).
888 Soft error count since last status=0
889 General status bits on (41010000):
890  BOT ONLINE IM_REP_EN
891
892 *** read empty block
893 dd if=/dev/lto2 of=/tmp/toto count=1
894 dd: lecture de `/dev/lto2': Ne peut allouer de la mémoire
895 0+0 enregistrements lus
896 0+0 enregistrements écrits
897 1 octet (1B) copié, 4,82219 seconde, 0,0 kB/s
898
899 file number = 0
900 block number = 1
901
902 *** read file mark
903 dd if=/dev/lto2 of=/tmp/toto count=1
904 0+0 enregistrements lus
905 0+0 enregistrements écrits
906 1 octet (1B) copié, 0,167274 seconde, 0,0 kB/s
907
908 file number = 1
909 block number = 0
910
911  *** write 2 blocks after rewind
912 dd if=/dev/zero of=/dev/lto2 count=2
913 2+0 enregistrements lus
914 2+0 enregistrements écrits
915 1024 octets (1,0 kB) copiés, 6,57402 seconde, 0,2 kB/s
916
917 file number = 1
918 block number = 0
919
920 *** write 2 blocks
921 file number = 2
922 block number = 0
923
924 *** rewind and fsr
925 file number = 0
926 block number = 1
927
928 *** rewind and 2x fsr (we have just 2 blocks)
929 file number = 0
930 block number = 2
931
932 *** fsr
933 mt: /dev/lto2: Erreur
934 file number = 1
935 block number = 0
936
937
938  ****************************************************************/
939
940
941 #ifdef TEST
942
943 int main()
944 {
945    int fd;
946    char buf[500];
947    printf("Starting FakeTape\n");
948
949    mkdir("/tmp/fake", 0700);
950
951
952
953
954    return 0;
955 }
956
957 #endif