]> git.sur5r.net Git - bacula/bacula/blob - bacula/platforms/freebsd/tapetest.c
Apply win32 fixes + add tapetest.c
[bacula/bacula] / bacula / platforms / freebsd / tapetest.c
1 /*
2  *
3  * Program to test loss of data at EOM on
4  *  FreeBSD systems.
5  *
6  *       Kern Sibbald, August 2003
7  *
8  *  If you build this program with:
9  *
10  *  c++ -g -O2 -Wall -c tapetest.c
11  *  c++ -g -O2 -Wall tapetest.o -o tapetest
12  *
13  *  Procedure for testing tape
14  *  ./tapetest /dev/your-tape-device
15  *  rewind
16  *  rawfill
17  *  rewind
18  *  scan
19  *
20  *  The output will be:
21  * 
22  * ========
23  *  Rewound /dev/nsa0
24  *  *Begin writing blocks of 64512 bytes.
25  *  ++++++++++++++++++++ ...
26  *  Write failed.  Last block written=17294. stat=0 ERR=Unknown error: 0
27  *  weof_dev
28  *  Wrote EOF to /dev/nsa0
29  *  *Rewound /dev/nsa0
30  *  *Starting scan at file 0
31  *  17294 blocks of 64512 bytes in file 0
32  *  End of File mark.
33  *  End of File mark.
34  *  End of tape
35  *  Total files=1, blocks=17294, bytes = 1115670528
36  * ========
37  *
38  *  which is correct. Notice that the return status is
39  *  0, while in the example below, which fails, the return
40  *  status is -1.
41  *
42
43  *  If you build this program with:
44  *
45  *  c++ -g -O2 -Wall -pthread -c tapetest.c
46  *  c++ -g -O2 -Wall -pthread tapetest.o -o tapetest
47  *    Note, we simply added -pthread compared to the
48  *    previous example.
49  *                      
50  *  Procedure for testing tape
51  *  ./tapetest /dev/your-tape-device
52  *  rewind
53  *  rawfill
54  *  rewind
55  *  scan
56  *
57  *  The output will be:
58  *
59  * ========
60  *    Rewound /dev/nsa0
61  *    *Begin writing blocks of 64512 bytes.
62  *    +++++++++++++++++++++++++++++ ...
63  *    Write failed.  Last block written=17926. stat=-1 ERR=No space left on device
64  *    weof_dev
65  *    Wrote EOF to /dev/nsa0
66  *    *Rewound /dev/nsa0
67  *    *Starting scan at file 0
68  *    17913 blocks of 64512 bytes in file 0
69  *    End of File mark.
70  *    End of File mark.
71  *    End of tape
72  *    Total files=1, blocks=17913, bytes = 1155603456
73  * ========
74  *
75  * which is incroorect because it wrote 17,926 blocks but read
76  * back only 17,913 blocks.
77  *
78  *
79  */
80
81 #include <stdio.h>
82 #include <unistd.h>
83 #include <stdlib.h>
84 #include <stdarg.h>
85 #include <errno.h>
86 #include <sys/ioctl.h>
87 #include <string.h>
88 #include <strings.h>
89 #include <sys/mtio.h>
90 #include <sys/types.h>
91 #include <sys/stat.h>
92 #include <fcntl.h>
93 #include <ctype.h>
94
95 #define FALSE 0
96 #define TRUE  1
97
98 #define dev_state(dev, state) ((dev)->state & (state))
99
100 /* Device state bits */
101 #define ST_OPENED          (1<<0)     /* set when device opened */
102 #define ST_TAPE            (1<<1)     /* is a tape device */  
103 #define ST_FILE            (1<<2)     /* is a file device */
104 #define ST_FIFO            (1<<3)     /* is a fifo device */
105 #define ST_PROG            (1<<4)     /* is a program device */
106 #define ST_LABEL           (1<<5)     /* label found */
107 #define ST_MALLOC          (1<<6)     /* dev packet malloc'ed in init_dev() */
108 #define ST_APPEND          (1<<7)     /* ready for Bacula append */
109 #define ST_READ            (1<<8)     /* ready for Bacula read */
110 #define ST_EOT             (1<<9)     /* at end of tape */
111 #define ST_WEOT            (1<<10)    /* Got EOT on write */
112 #define ST_EOF             (1<<11)    /* Read EOF i.e. zero bytes */
113 #define ST_NEXTVOL         (1<<12)    /* Start writing on next volume */
114 #define ST_SHORT           (1<<13)    /* Short block read */
115
116 #define BLOCK_SIZE (512 * 126)
117
118
119 /* Exported variables */
120 int quit = 0;
121 char buf[100000];
122 int verbose = 0;
123 int debug_level = 0;
124 int fd = 0;
125
126 struct DEVICE {
127    int fd;
128    int dev_errno;
129    int file;
130    int block_num;
131    int state;
132    char *buf;
133    int buf_len;
134    char *dev_name;
135    int file_addr;
136 };
137
138 DEVICE *dev;
139
140 #define uint32_t unsigned long
141 #define uint64_t unsigned long long
142             
143 /* Forward referenced subroutines */
144 static void do_tape_cmds();
145 static void helpcmd();
146 static void scancmd();
147 static void rewindcmd();
148 static void rawfill_cmd();
149
150
151 /* Static variables */
152
153 static char cmd[1000];
154
155 static void usage();
156 int get_cmd(char *prompt);
157
158
159 /*********************************************************************
160  *
161  *         Main Bacula Pool Creation Program
162  *
163  */
164 int main(int argc, char *argv[])
165 {
166    int ch;
167
168    while ((ch = getopt(argc, argv, "d:v?")) != -1) {
169       switch (ch) {
170          case 'd':                    /* set debug level */
171             debug_level = atoi(optarg);
172             if (debug_level <= 0) {
173                debug_level = 1; 
174             }
175             break;
176
177          case 'v':
178             verbose++;
179             break;
180
181          case '?':
182          default:
183             helpcmd();
184             exit(0);
185
186       }  
187    }
188    argc -= optind;
189    argv += optind;
190
191
192    /* See if we can open a device */
193    if (argc == 0) {
194       printf("No archive name specified.\n");
195       usage();
196       exit(1);
197    } else if (argc != 1) {
198       printf("Improper number of arguments specified.\n");
199       usage();
200       exit(1);
201    }
202
203    fd = open(argv[0], O_RDWR);   
204    if (fd < 0) {
205       printf("Error opening %s ERR=%s\n", argv[0], strerror(errno));
206       exit(1);
207    }
208    dev = (DEVICE *)malloc(sizeof(DEVICE));
209    memset(dev, 0, sizeof(DEVICE));
210    dev->fd = fd;
211    dev->dev_name = strdup(argv[0]);
212    dev->buf_len = BLOCK_SIZE;
213    dev->buf = (char *)malloc(BLOCK_SIZE);
214
215    do_tape_cmds();
216    return 0;
217 }
218
219
220 int rewind_dev(DEVICE *dev)
221 {
222    struct mtop mt_com;
223
224    if (dev->fd < 0) {
225       dev->dev_errno = EBADF;
226       printf("Bad call to rewind_dev. Device %s not open\n",
227             dev->dev_name);
228       return 0;
229    }
230    dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
231    dev->block_num = dev->file = 0;
232    mt_com.mt_op = MTREW;
233    mt_com.mt_count = 1;
234    if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
235       dev->dev_errno = errno;
236       printf("Rewind error on %s. ERR=%s.\n",
237                dev->dev_name, strerror(dev->dev_errno));
238       return 0;
239    }
240    return 1;
241 }
242
243 /*
244  * Write an end of file on the device
245  *   Returns: 0 on success
246  *            non-zero on failure
247  */
248 int 
249 weof_dev(DEVICE *dev, int num)
250
251    struct mtop mt_com;
252    int stat;
253
254    if (dev->fd < 0) {
255       dev->dev_errno = EBADF;
256       printf("Bad call to fsf_dev. Archive not open\n");
257       return -1;
258    }
259
260    dev->state &= ~(ST_EOT | ST_EOF);  /* remove EOF/EOT flags */
261    dev->block_num = 0;
262    printf("weof_dev\n");
263    mt_com.mt_op = MTWEOF;
264    mt_com.mt_count = num;
265    stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
266    if (stat == 0) {
267       dev->file++;
268       dev->file_addr = 0;
269    } else {
270       dev->dev_errno = errno;
271       printf("ioctl MTWEOF error on %s. ERR=%s.\n",
272          dev->dev_name, strerror(dev->dev_errno));
273    }
274    return stat;
275 }
276
277
278
279
280
281 void quitcmd()
282 {
283    quit = 1;
284 }
285
286
287 /*
288  * Rewind the tape.   
289  */
290 static void rewindcmd()
291 {
292    if (!rewind_dev(dev)) {
293       printf("Bad status from rewind. ERR=%s\n", strerror(dev->dev_errno));
294    } else {
295       printf("Rewound %s\n", dev->dev_name);
296    }
297 }
298
299 /*
300  * Write and end of file on the tape   
301  */
302 static void weofcmd()
303 {
304    int stat;
305
306    if ((stat = weof_dev(dev, 1)) < 0) {
307       printf("Bad status from weof %d. ERR=%s\n", stat, strerror(dev->dev_errno));
308       return;
309    } else {
310       printf("Wrote EOF to %s\n", dev->dev_name);
311    }
312 }
313
314
315 /* 
316  * Read a record from the tape
317  */
318 static void rrcmd()
319 {
320    char *buf;
321    int stat, len;
322
323    if (!get_cmd("Enter length to read: ")) {
324       return;
325    }
326    len = atoi(cmd);
327    if (len < 0 || len > 1000000) {
328       printf("Bad length entered, using default of 1024 bytes.\n");
329       len = 1024;
330    }
331    buf = (char *)malloc(len);
332    stat = read(fd, buf, len);
333    if (stat > 0 && stat <= len) {
334       errno = 0;
335    }
336    printf("Read of %d bytes gives stat=%d. ERR=%s\n",
337       len, stat, strerror(errno));
338    free(buf);
339 }
340
341 /* 
342  * Write a record to the tape
343  */
344 static void wrcmd()
345 {
346    int stat;
347    int rfd;
348
349    rfd = open("/dev/urandom", O_RDONLY);
350    if (rfd) {
351       read(rfd, dev->buf, dev->buf_len);
352    } else {
353       printf("Cannot open /dev/urandom.\n");
354       return;
355    }
356    printf("Write one block of %u bytes.\n", dev->buf_len);
357    stat = write(dev->fd, dev->buf, dev->buf_len);
358    if (stat != (int)dev->buf_len) {
359       if (stat == -1) {
360          printf("Bad status from write. ERR=%s\n", strerror(errno));
361       } else {
362          printf("Expected to write %d bytes but wrote only %d.\n",
363             dev->buf_len, stat);
364       }
365    }
366 }
367
368
369
370 /*
371  * Scan tape by reading block by block. Report what is
372  * on the tape.  Note, this command does raw reads, and as such
373  * will not work with fixed block size devices.
374  */
375 static void scancmd()
376 {
377    int stat;
378    int blocks, tot_blocks, tot_files;
379    int block_size;
380    uint64_t bytes;
381
382
383    blocks = block_size = tot_blocks = 0;
384    bytes = 0;
385    if (dev->state & ST_EOT) {
386       printf("End of tape\n");
387       return; 
388    }
389    tot_files = dev->file;
390    printf("Starting scan at file %u\n", dev->file);
391    for (;;) {
392       if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) {
393          dev->dev_errno = errno;
394          printf("Bad status from read %d. ERR=%s\n", stat, strerror(dev->dev_errno));
395          if (blocks > 0)
396             printf("%d block%s of %d bytes in file %d\n",        
397                     blocks, blocks>1?"s":"", block_size, dev->file);
398          return;
399       }
400       if (stat != block_size) {
401          if (blocks > 0) {
402             printf("%d block%s of %d bytes in file %d\n", 
403                  blocks, blocks>1?"s":"", block_size, dev->file);
404             blocks = 0;
405          }
406          block_size = stat;
407       }
408       if (stat == 0) {                /* EOF */
409          printf("End of File mark.\n");
410          /* Two reads of zero means end of tape */
411          if (dev->state & ST_EOF)
412             dev->state |= ST_EOT;
413          else {
414             dev->state |= ST_EOF;
415             dev->file++;
416          }
417          if (dev->state & ST_EOT) {
418             printf("End of tape\n");
419             break;
420          }
421       } else {                        /* Got data */
422          dev->state &= ~ST_EOF;
423          blocks++;
424          tot_blocks++;
425          bytes += stat;
426       }
427    }
428    tot_files = dev->file - tot_files;
429    printf("Total files=%d, blocks=%d, bytes = %d\n", tot_files, tot_blocks, 
430       (int)bytes);                         
431 }
432
433
434 static void rawfill_cmd()
435 {
436    int stat;
437    int rfd;
438    uint32_t block_num = 0;
439    uint32_t *p;
440    int my_errno;
441
442    rfd = open("/dev/urandom", O_RDONLY);
443    if (rfd) {
444       read(rfd, dev->buf, dev->buf_len);
445    } else {
446       printf("Cannot open /dev/urandom.\n");
447       return;
448    }
449    p = (uint32_t *)dev->buf;
450    printf("Begin writing blocks of %u bytes.\n", dev->buf_len);
451    for ( ;; ) {
452       *p = block_num;
453       stat = write(dev->fd, dev->buf, dev->buf_len);
454       if (stat == (int)dev->buf_len) {
455          if ((block_num++ % 100) == 0) {
456             printf("+");
457             fflush(stdout);
458          }
459          continue;
460       }
461       break;
462    }
463    my_errno = errno;
464    printf("\n");
465    weofcmd();
466    printf("Write failed.  Last block written=%d. stat=%d ERR=%s\n", (int)block_num, stat,
467       strerror(my_errno));
468
469 }
470
471 /* Strip any trailing junk from the command */
472 void strip_trailing_junk(char *cmd)
473 {
474    char *p;
475    p = cmd + strlen(cmd) - 1;
476
477    /* strip trailing junk from command */
478    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
479       *p-- = 0;
480 }
481
482 /* folded search for string - case insensitive */
483 int
484 fstrsch(char *a, char *b)   /* folded case search */
485 {
486    register char *s1,*s2;
487    register char c1=0, c2=0;
488
489    s1=a;
490    s2=b;
491    while (*s1) {                      /* do it the fast way */
492       if ((*s1++ | 0x20) != (*s2++ | 0x20))
493          return 0;                    /* failed */
494    }
495    while (*a) {                       /* do it over the correct slow way */
496       if (isupper(c1 = *a)) {
497          c1 = tolower((int)c1);
498       }
499       if (isupper(c2 = *b)) {
500          c2 = tolower((int)c2);
501       }
502       if (c1 != c2) {
503          return 0;
504       }
505       a++;
506       b++;
507    }
508    return 1;
509 }
510    
511
512 struct cmdstruct { char *key; void (*func)(); char *help; }; 
513 static struct cmdstruct commands[] = {
514  {"help",       helpcmd,      "print this command"},
515  {"quit",       quitcmd,      "quit tapetest"},   
516  {"rawfill",    rawfill_cmd,  "use write() to fill tape"},
517  {"rewind",     rewindcmd,    "rewind the tape"},
518  {"rr",         rrcmd,        "raw read the tape"},
519  {"wr",         wrcmd,        "raw write one block to the tape"},
520  {"scan",       scancmd,      "read() tape block by block to EOT and report"}, 
521  {"weof",       weofcmd,      "write an EOF on the tape"},
522              };
523 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
524
525 static void
526 do_tape_cmds()
527 {
528    unsigned int i;
529    int found;
530
531    while (get_cmd("*")) {
532       found = 0;
533       for (i=0; i<comsize; i++)       /* search for command */
534          if (fstrsch(cmd,  commands[i].key)) {
535             (*commands[i].func)();    /* go execute command */
536             found = 1;
537             break;
538          }
539       if (!found) {
540          printf("%s is an illegal command\n", cmd);
541       }
542       if (quit) {
543          break;
544       }
545    }
546 }
547
548 static void helpcmd()
549 {
550    unsigned int i;
551    usage();
552    printf("  Command    Description\n  =======    ===========\n");
553    for (i=0; i<comsize; i++)
554       printf("  %-10s %s\n", commands[i].key, commands[i].help);
555    printf("\n");
556 }
557
558 static void usage()
559 {
560    fprintf(stderr,
561 "Usage: tapetest [-d debug_level] [device_name]\n"
562 "       -dnn        set debug level to nn\n"
563 "       -?          print this message.\n"  
564 "\n");
565
566 }
567
568
569
570 /*      
571  * Get next input command from terminal.  This
572  * routine is REALLY primitive, and should be enhanced
573  * to have correct backspacing, etc.
574  */
575 int 
576 get_cmd(char *prompt)
577 {
578    int i = 0;
579    int ch;
580    fprintf(stdout, prompt);
581
582    /* We really should turn off echoing and pretty this
583     * up a bit.
584     */
585    cmd[i] = 0;
586    while ((ch = fgetc(stdin)) != EOF) { 
587       if (ch == '\n') {
588          strip_trailing_junk(cmd);
589          return 1;
590       } else if (ch == 4 || ch == 0xd3 || ch == 0x8) {
591          if (i > 0)
592             cmd[--i] = 0;
593          continue;
594       } 
595          
596       cmd[i++] = ch;
597       cmd[i] = 0;
598    }
599    quit = 1;
600    return 0;
601 }