2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: tapetype.c,v 1.24 2006/03/10 11:56:06 martinea Exp $
30 * tests a tape in a given tape unit and prints a tapetype entry for
36 #define NBLOCKS 32 /* number of random blocks */
40 static char *sProgName;
44 static int blockkb = 32;
47 static char *randombytes = (char *) NULL;
48 static char *prandombytes = (char *) NULL;
51 /* If the C library does not define random(), try to use rand() by
52 defining USE_RAND, but then make sure you are not using hardware
53 compression, because the low-order bits of rand() may not be that
55 #define random() rand()
56 #define srandom(seed) srand(seed)
59 static void allocrandombytes() {
63 if (randombytes == (char *)NULL) {
64 #if defined(HAVE_GETPAGESIZE)
65 page_size = getpagesize();
69 j = (NBLOCKS * blocksize) + page_size; /* buffer space plus one page */
70 j = am_round(j, page_size); /* even number of pages */
72 i = (p - (char *)0) & (page_size - 1); /* page boundary offset */
74 randombytes = p + page_size - i; /* round up to page boundary */
76 randombytes = p; /* alloc already on boundary */
82 static void initnotrandombytes() {
87 j =NBLOCKS * blocksize;
89 for(i=0; i < j; ++i) {
90 *p++ = (char) (i % 256);
94 static void initrandombytes() {
99 j = NBLOCKS * blocksize;
101 for(i=0; i < j; ++i) {
102 *p++ = (char)random();
106 static char *getrandombytes() {
107 static int counter = 0;
109 return randombytes + ((counter++ % NBLOCKS) * blocksize);
112 static int short_write;
119 if ((w = tapefd_write(fd, getrandombytes(), blocksize)) == blocksize) {
131 /* returns number of blocks actually written */
132 size_t writeblocks(int fd, size_t nblks)
136 while (blks < nblks) {
137 if (! writeblock(fd)) {
149 fputs("usage: ", stderr);
150 fputs(sProgName, stderr);
151 fputs(" [-h]", stderr);
152 fputs(" [-c]", stderr);
153 fputs(" [-o]", stderr);
154 fputs(" [-b blocksize]", stderr);
155 fputs(" [-e estsize]", stderr);
156 fputs(" [-f tapedev]", stderr);
157 fputs(" [-t typename]", stderr);
165 -h display this message\n\
166 -c run hardware compression detection test only\n\
167 -o overwrite amanda tape\n\
168 -b blocksize record block size (default: 32k)\n\
169 -e estsize estimated tape size (default: 1g == 1024m)\n\
170 -f tapedev tape device name (default: $TAPE)\n\
171 -t typename tapetype name (default: unknown-tapetype)\n\
173 Note: disable hardware compression when running this program.\n\
180 void show_progress(blocks, files)
181 size_t *blocks, *files;
183 fprintf(stderr, "wrote %ld %dKb block%s in %ld file%s",
184 (long)*blocks, blockkb, (*blocks == 1) ? "" : "s",
185 (long)*files, (*files == 1) ? "" : "s");
189 void do_pass(size, blocks, files, seconds)
190 size_t size, *blocks, *files;
197 if (tapefd_rewind(fd) == -1) {
198 fprintf(stderr, "%s: could not rewind %s: %s\n",
199 sProgName, tapedev, strerror(errno));
207 if ((blks = writeblocks(fd, size)) <= 0 || tapefd_weof(fd, 1) != 0)
213 show_progress(blocks, files);
221 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
222 sProgName, short_write ? "short write" : strerror(save_errno));
228 * Just in case time warped backward or the device is really, really
229 * fast (e.g. /dev/null testing).
233 *seconds = end - start;
238 show_progress(blocks, files);
239 fprintf(stderr, " in %ld second%s (%s)\n",
240 (long)*seconds, ((long)*seconds == 1) ? "" : "s",
241 short_write ? "short write" : strerror(save_errno));
245 void do_pass0(size, seconds, dorewind)
254 if (dorewind && tapefd_rewind(fd) == -1) {
255 fprintf(stderr, "%s: could not rewind %s: %s\n",
256 sProgName, tapedev, strerror(errno));
262 blks = writeblocks(fd, size);
270 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
271 sProgName, short_write ? "short write" : strerror(save_errno));
277 * Just in case time warped backward or the device is really, really
278 * fast (e.g. /dev/null testing).
282 *seconds = end - start;
291 size_t pass1blocks = 0;
292 size_t pass2blocks = 0;
296 size_t pass1files = 0;
297 size_t pass2files = 0;
313 int comprtstonly = 0;
314 int overwrite_label = 0;
317 char *datestamp = NULL;
321 if ((sProgName = strrchr(*argv, '/')) == NULL) {
327 /* Don't die when child closes pipe */
328 signal(SIGPIPE, SIG_IGN);
330 estsize = 1024 * 1024; /* assume 1 GByte for now */
331 tapedev = getenv("TAPE");
332 typename = "unknown-tapetype";
334 while ((ch = getopt(argc, argv, "b:e:f:t:hco")) != EOF) {
337 blockkb = strtol(optarg, &suffix, 0);
338 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
339 } else if (*suffix == 'm' || *suffix == 'M') {
341 } else if (*suffix == 'g' || *suffix == 'G') {
342 blockkb *= 1024 * 1024;
344 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
349 estsize = strtol(optarg, &suffix, 0);
350 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
351 } else if (*suffix == 'm' || *suffix == 'M') {
353 } else if (*suffix == 'g' || *suffix == 'G') {
354 estsize *= 1024 * 1024;
356 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
361 tapedev = stralloc(optarg);
364 typename = stralloc(optarg);
377 fprintf(stderr, "%s: unknown option \'%c\'\n", sProgName, ch);
378 /* fall through to ... */
385 blocksize = blockkb * 1024;
387 if (tapedev == NULL || optind < argc) {
395 fd = tape_open(tapedev, O_RDONLY);
397 fprintf(stderr, "%s: could not open %s: %s\n",
398 sProgName, tapedev, strerror(errno));
402 if((result = tapefd_rdlabel(fd, &datestamp, &label)) == NULL) {
405 else if (strcmp(result,"not an amanda tape") == 0) {
409 if(tapefd_rewind(fd) == -1) {
410 fprintf(stderr, "%s: could not rewind %s: %s\n",
411 sProgName, tapedev, strerror(errno));
418 if(is_labeled == 1 && overwrite_label == 0) {
419 fprintf(stderr, "%s: The tape is an amanda tape, use -o to overwrite the tape\n",
423 else if(is_labeled == 2 && overwrite_label == 0) {
424 fprintf(stderr, "%s: The tape is already used, use -o to overwrite the tape\n",
429 fd = tape_open(tapedev, O_RDWR);
431 fprintf(stderr, "%s: could not open %s: %s\n",
432 sProgName, tapedev, strerror(errno));
436 do_tty = isatty(fileno(stderr));
439 * Estimate pass: write twice a small file, once with compressable
440 * data and once with uncompressable data.
441 * The theory is that if the drive is in hardware compression mode
442 * we notice a significant difference in writing speed between the two
443 * (at least if we can provide data as fast the tape streams).
446 initnotrandombytes();
448 fprintf(stderr, "Estimate phase 1...");
449 pass0size = 8 * 1024 / blockkb;
453 * To get accurate results, we should write enough data
454 * so that rewind/start/stop time is small compared to
455 * the total time; let's take 10%.
456 * The timer has a 1 sec granularity, so the test
457 * should take at least 10 seconds to measure a
458 * difference with 10% accuracy; let's take 25 seconds.
460 while (pass1time < 25 || ((100*(pass2time-pass1time)/pass2time) >= 10) ) {
461 if (pass1time != 0) {
469 * first a dummy pass to rewind, stop, start and
470 * get drive streaming, then do the real timing
472 do_pass0(pass0size, &pass2time, 1);
473 do_pass0(pass0size, &pass1time, 0);
474 if (pass0size >= 10 * 1024 * 1024) {
476 "\rTape device is too fast to detect hardware compression...\n");
477 break; /* avoid loops if tape is superfast or broken */
480 fprintf(stderr, "\rWriting %d Mbyte compresseable data: %d sec\n",
481 (int)(blockkb * pass0size / 1024), (int)pass1time);
484 * now generate uncompressable data and try again
490 fprintf(stderr, "Estimate phase 2...");
491 do_pass0(pass0size, &pass2time, 1); /* rewind and get drive streaming */
492 do_pass0(pass0size, &pass2time, 0);
493 fprintf(stderr, "\rWriting %d Mbyte uncompresseable data: %d sec\n",
494 (int)(blockkb * pass0size / 1024), (int)pass2time);
497 * Compute the time difference between writing the compressable and
498 * uncompressable data. If it differs more than 20%, then warn
499 * user that the tape drive has probably hardware compression enabled.
501 if (pass1time > pass2time) {
503 * Strange! I would expect writing compresseable data to be
504 * much faster (or about equal, if hardware compression is disabled)
508 timediff = pass2time - pass1time;
510 if (((100 * timediff) / pass2time) >= 20) { /* 20% faster? */
511 fprintf(stderr, "WARNING: Tape drive has hardware compression enabled\n");
516 * Inform about estimated time needed to run the remaining of this program
518 fprintf(stderr, "Estimated time to write 2 * %lu Mbyte: ", (unsigned long) (estsize / 1024));
519 pass1time = (time_t)(2.0 * pass2time * estsize / (1.0 * pass0size * blockkb));
520 /* avoid overflow and underflow by doing math in floating point */
521 fprintf(stderr, "%ld sec = ", pass1time);
522 fprintf(stderr, "%ld h %ld min\n", (pass1time/3600), ((pass1time%3600) / 60));
530 * Do pass 1 -- write files that are 1% of the estimated size until error.
532 pass1size = (estsize * 0.01) / blockkb; /* 1% of estimate */
534 pass1size = 2; /* strange end case */
536 do_pass(pass1size, &pass1blocks, &pass1files, &pass1time);
539 * Do pass 2 -- write smaller files until error.
541 pass2size = pass1size / 2;
542 do_pass(pass2size, &pass2blocks, &pass2files, &pass2time);
545 * Compute the size of a filemark as the difference in data written
546 * between pass 1 and pass 2 divided by the difference in number of
547 * file marks written between pass 1 and pass 2. Note that we have
548 * to be careful in case size_t is unsigned (i.e. do not subtract
549 * things and then check for less than zero).
551 if (pass1blocks <= pass2blocks) {
553 * If tape marks take up space, there should be fewer blocks in pass
554 * 2 than in pass 1 since we wrote twice as many tape marks. But
555 * odd things happen, so make sure the result does not go negative.
559 blockdiff = pass1blocks - pass2blocks;
561 if (pass2files <= pass1files) {
563 * This should not happen, but just in case ...
567 filediff = pass2files - pass1files;
569 filemark = blockdiff * blockkb / filediff;
572 * Compute the length as the average of the two pass sizes including
575 size = ((pass1blocks * blockkb + filemark * pass1files)
576 + (pass2blocks * blockkb + filemark * pass2files)) / 2;
577 if (size >= 1024 * 1024 * 1000) {
579 sizeunits = "gbytes";
580 } else if (size >= 1024 * 1000) {
582 sizeunits = "mbytes";
584 sizeunits = "kbytes";
588 * Compute the speed as the average of the two passes.
590 speed = (((double)pass1blocks * blockkb / pass1time)
591 + ((double)pass2blocks * blockkb / pass2time)) / 2;
596 printf("define tapetype %s {\n", typename);
597 printf(" comment \"just produced by tapetype prog (hardware compression %s)\"\n",
598 hwcompr ? "on" : "off");
599 printf(" length %ld %s\n", (long)size, sizeunits);
600 printf(" filemark %ld kbytes\n", filemark);
601 printf(" speed %ld kps\n", speed);
604 if (tapefd_rewind(fd) == -1) {
605 fprintf(stderr, "%s: could not rewind %s: %s\n",
606 sProgName, tapedev, strerror(errno));
611 if (tapefd_close(fd) == -1) {
612 fprintf(stderr, "%s: could not close %s: %s\n",
613 sProgName, tapedev, strerror(errno));