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.3.2.3.4.3.2.9.2.2 2004/11/10 16:28:43 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;
50 /* If the C library does not define random(), try to use rand() by
51 defining USE_RAND, but then make sure you are not using hardware
52 compression, because the low-order bits of rand() may not be that
54 #define random() rand()
55 #define srandom(seed) srand(seed)
58 static void allocrandombytes() {
62 if (randombytes == (char *)NULL) {
63 #if defined(HAVE_GETPAGESIZE)
64 page_size = getpagesize();
68 j = (NBLOCKS * blocksize) + page_size; /* buffer space plus one page */
69 j = am_round(j, page_size); /* even number of pages */
71 i = (p - (char *)0) & (page_size - 1); /* page boundary offset */
73 randombytes = p + page_size - i; /* round up to page boundary */
75 randombytes = p; /* alloc already on boundary */
80 static void initnotrandombytes() {
85 j =NBLOCKS * blocksize;
87 for(i=0; i < j; ++i) {
88 *p++ = (char) (i % 256);
92 static void initrandombytes() {
97 j = NBLOCKS * blocksize;
99 for(i=0; i < j; ++i) {
100 *p++ = (char)random();
104 static char *getrandombytes() {
105 static int counter = 0;
107 return randombytes + ((counter++ % NBLOCKS) * blocksize);
110 static int short_write;
117 if ((w = tapefd_write(fd, getrandombytes(), blocksize)) == blocksize) {
129 /* returns number of blocks actually written */
130 size_t writeblocks(int fd, size_t nblks)
134 while (blks < nblks) {
135 if (! writeblock(fd)) {
147 fputs("usage: ", stderr);
148 fputs(sProgName, stderr);
149 fputs(" [-h]", stderr);
150 fputs(" [-c]", stderr);
151 fputs(" [-o]", stderr);
152 fputs(" [-b blocksize]", stderr);
153 fputs(" [-e estsize]", stderr);
154 fputs(" [-f tapedev]", stderr);
155 fputs(" [-t typename]", stderr);
163 -h display this message\n\
164 -c run hardware compression detection test only\n\
165 -o overwrite amanda tape\n\
166 -b blocksize record block size (default: 32k)\n\
167 -e estsize estimated tape size (default: 1g == 1024m)\n\
168 -f tapedev tape device name (default: $TAPE)\n\
169 -t typename tapetype name (default: unknown-tapetype)\n\
171 Note: disable hardware compression when running this program.\n\
178 void show_progress(blocks, files)
179 size_t *blocks, *files;
181 fprintf(stderr, "wrote %ld %dKb block%s in %ld file%s",
182 (long)*blocks, blockkb, (*blocks == 1) ? "" : "s",
183 (long)*files, (*files == 1) ? "" : "s");
187 void do_pass(size, blocks, files, seconds)
188 size_t size, *blocks, *files;
195 if (tapefd_rewind(fd) == -1) {
196 fprintf(stderr, "%s: could not rewind %s: %s\n",
197 sProgName, tapedev, strerror(errno));
205 if ((blks = writeblocks(fd, size)) <= 0 || tapefd_weof(fd, 1) != 0)
211 show_progress(blocks, files);
219 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
220 sProgName, short_write ? "short write" : strerror(save_errno));
226 * Just in case time warped backward or the device is really, really
227 * fast (e.g. /dev/null testing).
231 *seconds = end - start;
236 show_progress(blocks, files);
237 fprintf(stderr, " in %ld second%s (%s)\n",
238 (long)*seconds, ((long)*seconds == 1) ? "" : "s",
239 short_write ? "short write" : strerror(save_errno));
243 void do_pass0(size, seconds, dorewind)
252 if (dorewind && tapefd_rewind(fd) == -1) {
253 fprintf(stderr, "%s: could not rewind %s: %s\n",
254 sProgName, tapedev, strerror(errno));
260 blks = writeblocks(fd, size);
268 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
269 sProgName, short_write ? "short write" : strerror(save_errno));
275 * Just in case time warped backward or the device is really, really
276 * fast (e.g. /dev/null testing).
280 *seconds = end - start;
289 size_t pass1blocks = 0;
290 size_t pass2blocks = 0;
294 size_t pass1files = 0;
295 size_t pass2files = 0;
311 int comprtstonly = 0;
312 int overwrite_label = 0;
315 char *datestamp = NULL;
319 if ((sProgName = strrchr(*argv, '/')) == NULL) {
325 estsize = 1024 * 1024; /* assume 1 GByte for now */
326 tapedev = getenv("TAPE");
327 typename = "unknown-tapetype";
329 while ((ch = getopt(argc, argv, "b:e:f:t:hco")) != EOF) {
332 blockkb = strtol(optarg, &suffix, 0);
333 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
334 } else if (*suffix == 'm' || *suffix == 'M') {
336 } else if (*suffix == 'g' || *suffix == 'G') {
337 blockkb *= 1024 * 1024;
339 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
344 estsize = strtol(optarg, &suffix, 0);
345 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
346 } else if (*suffix == 'm' || *suffix == 'M') {
348 } else if (*suffix == 'g' || *suffix == 'G') {
349 estsize *= 1024 * 1024;
351 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
356 tapedev = stralloc(optarg);
359 typename = stralloc(optarg);
372 fprintf(stderr, "%s: unknown option \'%c\'\n", sProgName, ch);
373 /* fall through to ... */
380 blocksize = blockkb * 1024;
382 if (tapedev == NULL || optind < argc) {
390 fd = tape_open(tapedev, O_RDONLY);
392 fprintf(stderr, "%s: could not open %s: %s\n",
393 sProgName, tapedev, strerror(errno));
397 if((result = tapefd_rdlabel(fd, &datestamp, &label)) == NULL) {
400 else if (strcmp(result,"not an amanda tape") == 0) {
404 if(tapefd_rewind(fd) == -1) {
405 fprintf(stderr, "%s: could not rewind %s: %s\n",
406 sProgName, tapedev, strerror(errno));
413 if(is_labeled == 1 && overwrite_label == 0) {
414 fprintf(stderr, "%s: The tape is an amanda tape, use -o to overwrite the tape\n",
418 else if(is_labeled == 2 && overwrite_label == 0) {
419 fprintf(stderr, "%s: The tape is already used, use -o to overwrite the tape\n",
424 fd = tape_open(tapedev, O_RDWR);
426 fprintf(stderr, "%s: could not open %s: %s\n",
427 sProgName, tapedev, strerror(errno));
431 do_tty = isatty(fileno(stderr));
434 * Estimate pass: write twice a small file, once with compressable
435 * data and once with uncompressable data.
436 * The theory is that if the drive is in hardware compression mode
437 * we notice a significant difference in writing speed between the two
438 * (at least if we can provide data as fast the tape streams).
441 initnotrandombytes();
443 fprintf(stderr, "Estimate phase 1...");
444 pass0size = 8 * 1024 / blockkb;
448 * To get accurate results, we should write enough data
449 * so that rewind/start/stop time is small compared to
450 * the total time; let's take 10%.
451 * The timer has a 1 sec granularity, so the test
452 * should take at least 10 seconds to measure a
453 * difference with 10% accuracy; let's take 25 seconds.
455 while (pass1time < 25 || ((100*(pass2time-pass1time)/pass2time) >= 10) ) {
456 if (pass1time != 0) {
464 * first a dummy pass to rewind, stop, start and
465 * get drive streaming, then do the real timing
467 do_pass0(pass0size, &pass2time, 1);
468 do_pass0(pass0size, &pass1time, 0);
469 if (pass0size >= 10 * 1024 * 1024) {
471 "\rTape device is too fast to detect hardware compression...\n");
472 break; /* avoid loops if tape is superfast or broken */
475 fprintf(stderr, "\rWriting %d Mbyte compresseable data: %d sec\n",
476 (int)(blockkb * pass0size / 1024), (int)pass1time);
479 * now generate uncompressable data and try again
485 fprintf(stderr, "Estimate phase 2...");
486 do_pass0(pass0size, &pass2time, 1); /* rewind and get drive streaming */
487 do_pass0(pass0size, &pass2time, 0);
488 fprintf(stderr, "\rWriting %d Mbyte uncompresseable data: %d sec\n",
489 (int)(blockkb * pass0size / 1024), (int)pass2time);
492 * Compute the time difference between writing the compressable and
493 * uncompressable data. If it differs more than 20%, then warn
494 * user that the tape drive has probably hardware compression enabled.
496 if (pass1time > pass2time) {
498 * Strange! I would expect writing compresseable data to be
499 * much faster (or about equal, if hardware compression is disabled)
503 timediff = pass2time - pass1time;
505 if (((100 * timediff) / pass2time) >= 20) { /* 20% faster? */
506 fprintf(stderr, "WARNING: Tape drive has hardware compression enabled\n");
511 * Inform about estimated time needed to run the remaining of this program
513 fprintf(stderr, "Estimated time to write 2 * %d Mbyte: ", estsize / 1024);
514 pass1time = (time_t)(2.0 * pass2time * estsize / (1.0 * pass0size * blockkb));
515 /* avoid overflow and underflow by doing math in floating point */
516 fprintf(stderr, "%ld sec = ", pass1time);
517 fprintf(stderr, "%ld h %ld min\n", (pass1time/3600), ((pass1time%3600) / 60));
525 * Do pass 1 -- write files that are 1% of the estimated size until error.
527 pass1size = (estsize * 0.01) / blockkb; /* 1% of estimate */
529 pass1size = 2; /* strange end case */
531 do_pass(pass1size, &pass1blocks, &pass1files, &pass1time);
534 * Do pass 2 -- write smaller files until error.
536 pass2size = pass1size / 2;
537 do_pass(pass2size, &pass2blocks, &pass2files, &pass2time);
540 * Compute the size of a filemark as the difference in data written
541 * between pass 1 and pass 2 divided by the difference in number of
542 * file marks written between pass 1 and pass 2. Note that we have
543 * to be careful in case size_t is unsigned (i.e. do not subtract
544 * things and then check for less than zero).
546 if (pass1blocks <= pass2blocks) {
548 * If tape marks take up space, there should be fewer blocks in pass
549 * 2 than in pass 1 since we wrote twice as many tape marks. But
550 * odd things happen, so make sure the result does not go negative.
554 blockdiff = pass1blocks - pass2blocks;
556 if (pass2files <= pass1files) {
558 * This should not happen, but just in case ...
562 filediff = pass2files - pass1files;
564 filemark = blockdiff * blockkb / filediff;
567 * Compute the length as the average of the two pass sizes including
570 size = ((pass1blocks * blockkb + filemark * pass1files)
571 + (pass2blocks * blockkb + filemark * pass2files)) / 2;
572 if (size >= 1024 * 1024 * 1000) {
574 sizeunits = "gbytes";
575 } else if (size >= 1024 * 1000) {
577 sizeunits = "mbytes";
579 sizeunits = "kbytes";
583 * Compute the speed as the average of the two passes.
585 speed = (((double)pass1blocks * blockkb / pass1time)
586 + ((double)pass2blocks * blockkb / pass2time)) / 2;
591 printf("define tapetype %s {\n", typename);
592 printf(" comment \"just produced by tapetype prog (hardware compression %s)\"\n",
593 hwcompr ? "on" : "off");
594 printf(" length %ld %s\n", (long)size, sizeunits);
595 printf(" filemark %ld kbytes\n", filemark);
596 printf(" speed %ld kps\n", speed);
599 if (tapefd_rewind(fd) == -1) {
600 fprintf(stderr, "%s: could not rewind %s: %s\n",
601 sProgName, tapedev, strerror(errno));
605 if (tapefd_close(fd) == -1) {
606 fprintf(stderr, "%s: could not close %s: %s\n",
607 sProgName, tapedev, strerror(errno));