3 * Program to test loss of data at EOM on
6 * Kern Sibbald, August 2003
8 * If you build this program with:
10 * c++ -g -O2 -Wall -c tapetest.c
11 * c++ -g -O2 -Wall tapetest.o -o tapetest
13 * Procedure for testing tape
14 * ./tapetest /dev/your-tape-device
24 * *Begin writing blocks of 64512 bytes.
25 * ++++++++++++++++++++ ...
26 * Write failed. Last block written=17294. stat=0 ERR=Unknown error: 0
28 * Wrote EOF to /dev/nsa0
30 * *Starting scan at file 0
31 * 17294 blocks of 64512 bytes in file 0
35 * Total files=1, blocks=17294, bytes = 1115670528
38 * which is correct. Notice that the return status is
39 * 0, while in the example below, which fails, the return
43 * If you build this program with:
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
50 * Procedure for testing tape
51 * ./tapetest /dev/your-tape-device
61 * *Begin writing blocks of 64512 bytes.
62 * +++++++++++++++++++++++++++++ ...
63 * Write failed. Last block written=17926. stat=-1 ERR=No space left on device
65 * Wrote EOF to /dev/nsa0
67 * *Starting scan at file 0
68 * 17913 blocks of 64512 bytes in file 0
72 * Total files=1, blocks=17913, bytes = 1155603456
75 * which is incroorect because it wrote 17,926 blocks but read
76 * back only 17,913 blocks.
86 #include <sys/ioctl.h>
90 #include <sys/types.h>
98 #define dev_state(dev, state) ((dev)->state & (state))
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 */
116 #define BLOCK_SIZE (512 * 126)
119 /* Exported variables */
140 #define uint32_t unsigned long
141 #define uint64_t unsigned long long
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();
151 /* Static variables */
153 static char cmd[1000];
156 int get_cmd(char *prompt);
159 /*********************************************************************
161 * Main Bacula Pool Creation Program
164 int main(int argc, char *argv[])
168 while ((ch = getopt(argc, argv, "d:v?")) != -1) {
170 case 'd': /* set debug level */
171 debug_level = atoi(optarg);
172 if (debug_level <= 0) {
192 /* See if we can open a device */
194 printf("No archive name specified.\n");
197 } else if (argc != 1) {
198 printf("Improper number of arguments specified.\n");
203 fd = open(argv[0], O_RDWR);
205 printf("Error opening %s ERR=%s\n", argv[0], strerror(errno));
208 dev = (DEVICE *)malloc(sizeof(DEVICE));
209 memset(dev, 0, sizeof(DEVICE));
211 dev->dev_name = strdup(argv[0]);
212 dev->buf_len = BLOCK_SIZE;
213 dev->buf = (char *)malloc(BLOCK_SIZE);
220 int rewind_dev(DEVICE *dev)
225 dev->dev_errno = EBADF;
226 printf("Bad call to rewind_dev. Device %s not open\n",
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;
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));
244 * Write an end of file on the device
245 * Returns: 0 on success
246 * non-zero on failure
249 weof_dev(DEVICE *dev, int num)
255 dev->dev_errno = EBADF;
256 printf("Bad call to fsf_dev. Archive not open\n");
260 dev->state &= ~(ST_EOT | ST_EOF); /* remove EOF/EOT flags */
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);
270 dev->dev_errno = errno;
271 printf("ioctl MTWEOF error on %s. ERR=%s.\n",
272 dev->dev_name, strerror(dev->dev_errno));
290 static void rewindcmd()
292 if (!rewind_dev(dev)) {
293 printf("Bad status from rewind. ERR=%s\n", strerror(dev->dev_errno));
295 printf("Rewound %s\n", dev->dev_name);
300 * Write and end of file on the tape
302 static void weofcmd()
306 if ((stat = weof_dev(dev, 1)) < 0) {
307 printf("Bad status from weof %d. ERR=%s\n", stat, strerror(dev->dev_errno));
310 printf("Wrote EOF to %s\n", dev->dev_name);
316 * Read a record from the tape
323 if (!get_cmd("Enter length to read: ")) {
327 if (len < 0 || len > 1000000) {
328 printf("Bad length entered, using default of 1024 bytes.\n");
331 buf = (char *)malloc(len);
332 stat = read(fd, buf, len);
333 if (stat > 0 && stat <= len) {
336 printf("Read of %d bytes gives stat=%d. ERR=%s\n",
337 len, stat, strerror(errno));
342 * Write a record to the tape
349 rfd = open("/dev/urandom", O_RDONLY);
351 read(rfd, dev->buf, dev->buf_len);
353 printf("Cannot open /dev/urandom.\n");
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) {
360 printf("Bad status from write. ERR=%s\n", strerror(errno));
362 printf("Expected to write %d bytes but wrote only %d.\n",
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.
375 static void scancmd()
378 int blocks, tot_blocks, tot_files;
383 blocks = block_size = tot_blocks = 0;
385 if (dev->state & ST_EOT) {
386 printf("End of tape\n");
389 tot_files = dev->file;
390 printf("Starting scan at file %u\n", dev->file);
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));
396 printf("%d block%s of %d bytes in file %d\n",
397 blocks, blocks>1?"s":"", block_size, dev->file);
400 if (stat != block_size) {
402 printf("%d block%s of %d bytes in file %d\n",
403 blocks, blocks>1?"s":"", block_size, dev->file);
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;
414 dev->state |= ST_EOF;
417 if (dev->state & ST_EOT) {
418 printf("End of tape\n");
421 } else { /* Got data */
422 dev->state &= ~ST_EOF;
428 tot_files = dev->file - tot_files;
429 printf("Total files=%d, blocks=%d, bytes = %d\n", tot_files, tot_blocks,
434 static void rawfill_cmd()
438 uint32_t block_num = 0;
442 rfd = open("/dev/urandom", O_RDONLY);
444 read(rfd, dev->buf, dev->buf_len);
446 printf("Cannot open /dev/urandom.\n");
449 p = (uint32_t *)dev->buf;
450 printf("Begin writing blocks of %u bytes.\n", dev->buf_len);
453 stat = write(dev->fd, dev->buf, dev->buf_len);
454 if (stat == (int)dev->buf_len) {
455 if ((block_num++ % 100) == 0) {
466 printf("Write failed. Last block written=%d. stat=%d ERR=%s\n", (int)block_num, stat,
471 /* Strip any trailing junk from the command */
472 void strip_trailing_junk(char *cmd)
475 p = cmd + strlen(cmd) - 1;
477 /* strip trailing junk from command */
478 while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
482 /* folded search for string - case insensitive */
484 fstrsch(char *a, char *b) /* folded case search */
486 register char *s1,*s2;
487 register char c1=0, c2=0;
491 while (*s1) { /* do it the fast way */
492 if ((*s1++ | 0x20) != (*s2++ | 0x20))
493 return 0; /* failed */
495 while (*a) { /* do it over the correct slow way */
496 if (isupper(c1 = *a)) {
497 c1 = tolower((int)c1);
499 if (isupper(c2 = *b)) {
500 c2 = tolower((int)c2);
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"},
523 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
531 while (get_cmd("*")) {
533 for (i=0; i<comsize; i++) /* search for command */
534 if (fstrsch(cmd, commands[i].key)) {
535 (*commands[i].func)(); /* go execute command */
540 printf("%s is an illegal command\n", cmd);
548 static void helpcmd()
552 printf(" Command Description\n ======= ===========\n");
553 for (i=0; i<comsize; i++)
554 printf(" %-10s %s\n", commands[i].key, commands[i].help);
561 "Usage: tapetest [-d debug_level] [device_name]\n"
562 " -dnn set debug level to nn\n"
563 " -? print this message.\n"
571 * Get next input command from terminal. This
572 * routine is REALLY primitive, and should be enhanced
573 * to have correct backspacing, etc.
576 get_cmd(char *prompt)
580 fprintf(stdout, prompt);
582 /* We really should turn off echoing and pretty this
586 while ((ch = fgetc(stdin)) != EOF) {
588 strip_trailing_junk(cmd);
590 } else if (ch == 4 || ch == 0xd3 || ch == 0x8) {