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.27 2006/08/24 01:57:16 paddy_s Exp $
30 * tests a tape in a given tape unit and prints a tapetype entry for
37 #define NBLOCKS 32 /* number of random blocks */
41 static char *sProgName;
45 static size_t blockkb = 32;
46 static size_t blocksize;
48 static char *randombytes = (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 char *getrandombytes(void);
60 static int writeblock(int fd);
61 static off_t writeblocks(int fd, off_t nblks);
62 static void allocrandombytes(void);
63 static void do_pass0(off_t size, time_t *seconds, int dorewind);
64 static void do_pass(off_t size, off_t *blocks, off_t *files, time_t *seconds);
65 static void help(void);
66 static void initnotrandombytes(void);
67 static void initrandombytes(void);
68 static void show_progress(off_t *blocks, off_t *files);
69 static void usage(void);
71 int main(int argc, char **argv);
74 allocrandombytes(void)
81 if (randombytes == (char *)NULL) {
82 #if defined(HAVE_GETPAGESIZE)
83 page_size = (size_t)getpagesize();
85 page_size = (size_t)1024;
87 j = (NBLOCKS * blocksize) + page_size; /* buffer space plus one page */
88 j = am_round(j, page_size); /* even number of pages */
90 i = (size_t)(p - (char *)0) & (page_size - 1);/* page boundary offset */
92 randombytes = p + (page_size - i); /* round up to page boundary */
94 randombytes = p; /* alloc already on boundary */
100 initnotrandombytes(void)
107 j = NBLOCKS * blocksize;
109 for(i=0; i < j; ++i) {
110 *p++ = (char) (i % 256);
115 initrandombytes(void)
121 j = NBLOCKS * blocksize;
123 for(i=0; i < j; ++i) {
124 *p++ = (char)random();
131 static unsigned long counter = 0;
133 return randombytes + ((counter++ % NBLOCKS) * blocksize);
136 static int short_write;
144 if ((w = tapefd_write(fd, getrandombytes(), blocksize)) == (ssize_t)blocksize) {
156 /* returns number of blocks actually written */
162 off_t blks = (off_t)0;
164 while (blks < nblks) {
165 if (! writeblock(fd)) {
178 fputs(_("usage: "), stderr);
179 fputs(sProgName, stderr);
180 fputs(_(" [-h]"), stderr);
181 fputs(_(" [-c]"), stderr);
182 fputs(_(" [-o]"), stderr);
183 fputs(_(" [-b blocksize]"), stderr);
184 fputs(_(" -e estsize"), stderr);
185 fputs(_(" [-f tapedev]"), stderr);
186 fputs(_(" [-t typename]"), stderr);
194 fputs(_("-h display this message\n"
195 "-c run hardware compression detection test only\n"
196 "-o overwrite amanda tape\n"
197 "-b blocksize record block size (default: 32k)\n"
198 "-e estsize estimated tape size (No default!)\n"
199 "-f tapedev tape device name (default: $TAPE)\n"
200 "-t typename tapetype name (default: unknown-tapetype)\n"
202 "Note: disable hardware compression when running this program.\n"),
215 plural(_("wrote %lld %zu Kb block"),
216 _("wrote %lld %zu Kb blocks"),
221 plural(_(" in %lld file"),
239 if (tapefd_rewind(fd) == -1) {
240 g_fprintf(stderr, _("%s: could not rewind %s: %s\n"),
241 sProgName, tapedev, strerror(errno));
244 if (((-1 == tapefd_close(fd)) ||
245 (-1 == (fd = tape_open(tapedev, O_RDWR))))) {
246 g_fprintf(stderr, "%s: could not re-open %s: %s\n",
247 sProgName, tapedev, strerror(errno));
255 if ((blks = writeblocks(fd, size)) <= (off_t)0 || tapefd_weof(fd, (off_t)1) != 0)
261 show_progress(blocks, files);
268 if (*blocks == (off_t)0) {
269 g_fprintf(stderr, _("%s: could not write any data in this pass: %s\n"),
270 sProgName, short_write ? _("short write") : strerror(save_errno));
276 * Just in case time warped backward or the device is really, really
277 * fast (e.g. /dev/null testing).
281 *seconds = end - start;
286 show_progress(blocks, files);
288 plural(_(" in %jd second (%s)\n"),
289 _(" in %jd seconds (%s)\n"),
292 short_write ? _("short write") : strerror(save_errno));
306 if (dorewind && tapefd_rewind(fd) == -1) {
307 g_fprintf(stderr, _("%s: could not rewind %s: %s\n"),
308 sProgName, tapedev, strerror(errno));
313 ((-1 == tapefd_close(fd)) ||
314 (-1 == (fd = tape_open(tapedev, O_RDWR))))) {
315 g_fprintf(stderr, "%s: could not re-open %s: %s\n",
316 sProgName, tapedev, strerror(errno));
323 blks = writeblocks(fd, size);
324 tapefd_weof(fd, (off_t)1);
330 if (blks <= (off_t)0) {
331 g_fprintf(stderr, _("%s: could not write any data in this pass: %s\n"),
332 sProgName, short_write ? _("short write") : strerror(save_errno));
338 * Just in case time warped backward or the device is really, really
339 * fast (e.g. /dev/null testing).
343 *seconds = end - start;
353 off_t pass1blocks = (off_t)0;
354 off_t pass2blocks = (off_t)0;
358 off_t pass1files = (off_t)0;
359 off_t pass2files = (off_t)0;
375 int comprtstonly = 0;
376 int overwrite_label = 0;
379 char *datestamp = NULL;
383 * Configure program for internationalization:
384 * 1) Only set the message locale for now.
385 * 2) Set textdomain for all amanda related programs to "amanda"
386 * We don't want to be forced to support dozens of message catalogs.
388 setlocale(LC_MESSAGES, "C");
389 textdomain("amanda");
391 config_init(0, NULL);
393 if ((sProgName = strrchr(*argv, '/')) == NULL) {
399 /* Don't die when child closes pipe */
400 signal(SIGPIPE, SIG_IGN);
403 tapedev = getenv("TAPE");
404 typename = "unknown-tapetype";
406 while ((ch = getopt(argc, argv, "b:e:f:t:hco")) != EOF) {
409 blockkb = (size_t)strtol(optarg, &suffix, 0);
410 if (!(*suffix == '\0' || *suffix == 'k' || *suffix == 'K')) {
411 if (*suffix == 'm' || *suffix == 'M') {
413 } else if (*suffix == 'g' || *suffix == 'G') {
414 blockkb *= (1024 * 1024);
416 g_fprintf(stderr, _("%s: unknown size suffix \'%c\'\n"),
423 estsize = OFF_T_STRTOL(optarg, &suffix, 0);
424 if (!(*suffix == '\0' || *suffix == 'k' || *suffix == 'K')) {
425 if (*suffix == 'm' || *suffix == 'M') {
426 estsize *= (off_t)1024;
427 } else if (*suffix == 'g' || *suffix == 'G') {
428 estsize *= (off_t)(1024 * 1024);
430 g_fprintf(stderr, _("%s: unknown size suffix \'%c\'\n"),
438 tapedev = stralloc(optarg);
442 typename = stralloc(optarg);
458 g_fprintf(stderr, _("%s: unknown option \'%c\'\n"), sProgName, ch);
466 blocksize = blockkb * 1024;
468 if (tapedev == NULL) {
469 g_fprintf(stderr, _("%s: No tapedev specified\n"), sProgName);
480 estsize = (off_t)(1024 * 1024); /* assume 1 GByte for now */
482 g_fprintf(stderr, _("%s: please specify estimated tape capacity (e.g. '-e 4g')\n"), sProgName);
492 fd = tape_open(tapedev, O_RDONLY, 0);
494 g_fprintf(stderr, _("%s: could not open %s: %s\n"),
495 sProgName, tapedev, strerror(errno));
499 if((result = tapefd_rdlabel(fd, &datestamp, &label)) == NULL) {
502 else if (strcmp(result,_("not an amanda tape")) == 0) {
506 if(tapefd_rewind(fd) == -1) {
507 g_fprintf(stderr, _("%s: could not rewind %s: %s\n"),
508 sProgName, tapedev, strerror(errno));
515 if(is_labeled == 1 && overwrite_label == 0) {
516 g_fprintf(stderr, _("%s: The tape is an amanda tape, use -o to overwrite the tape\n"),
520 else if(is_labeled == 2 && overwrite_label == 0) {
521 g_fprintf(stderr, _("%s: The tape is already used, use -o to overwrite the tape\n"),
526 fd = tape_open(tapedev, O_RDWR, 0);
528 g_fprintf(stderr, _("%s: could not open %s: %s\n"),
529 sProgName, tapedev, strerror(errno));
533 do_tty = isatty(fileno(stderr));
536 * Estimate pass: write twice a small file, once with compressable
537 * data and once with uncompressable data.
538 * The theory is that if the drive is in hardware compression mode
539 * we notice a significant difference in writing speed between the two
540 * (at least if we can provide data as fast the tape streams).
543 initnotrandombytes();
545 g_fprintf(stderr, _("Estimate phase 1..."));
546 pass0size = (off_t)(8 * 1024 / blockkb);
550 * To get accurate results, we should write enough data
551 * so that rewind/start/stop time is small compared to
552 * the total time; let's take 10%.
553 * The timer has a 1 sec granularity, so the test
554 * should take at least 10 seconds to measure a
555 * difference with 10% accuracy; let's take 25 seconds.
557 while (pass1time < 25 || ((100*(pass2time-pass1time)/pass2time) >= 10) ) {
558 if (pass1time != 0) {
559 time_t t = pass1time;
561 pass0size *= (off_t)2;
566 * first a dummy pass to rewind, stop, start and
567 * get drive streaming, then do the real timing
569 do_pass0(pass0size, &pass2time, 1);
570 do_pass0(pass0size, &pass1time, 0);
571 if (pass0size >= (off_t)(10 * 1024 * 1024)) {
572 g_fprintf(stderr, "\r");
574 _("Tape device is too fast to detect hardware compression...\n"));
575 break; /* avoid loops if tape is superfast or broken */
578 g_fprintf(stderr, "\r");
580 _("Writing %lld Mbyte compresseable data: %jd sec\n"),
581 (long long)((off_t)blockkb * pass0size / (off_t)1024),
582 (intmax_t)pass1time);
585 * now generate uncompressable data and try again
588 srandom((unsigned)now);
591 g_fprintf(stderr, _("Estimate phase 2..."));
592 do_pass0(pass0size, &pass2time, 1); /* rewind and get drive streaming */
593 do_pass0(pass0size, &pass2time, 0);
594 g_fprintf(stderr, "\r");
595 g_fprintf(stderr, _("Writing %lld Mbyte uncompresseable data: %jd sec\n"),
596 (long long)((off_t)blockkb * pass0size / (off_t)1024),
597 (intmax_t)pass2time);
600 * Compute the time difference between writing the compressable and
601 * uncompressable data. If it differs more than 20%, then warn
602 * user that the tape drive has probably hardware compression enabled.
604 if (pass1time > pass2time) {
606 * Strange! I would expect writing compresseable data to be
607 * much faster (or about equal, if hardware compression is disabled)
611 timediff = pass2time - pass1time;
613 if (((100 * timediff) / pass2time) >= 20) { /* 20% faster? */
614 g_fprintf(stderr, _("WARNING: Tape drive has hardware compression enabled\n"));
619 * Inform about estimated time needed to run the remaining of this program
621 g_fprintf(stderr, _("Estimated time to write 2 * %lu Mbyte: "), (unsigned long) (estsize / (off_t)1024));
622 pass1time = (time_t)(2.0 * (double)pass2time * (double)estsize /
623 (1.0 * (double)pass0size * (double)blockkb));
624 /* avoid overflow and underflow by doing math in floating point */
625 g_fprintf(stderr, _("%jd sec = %jd h %jd min\n"),
627 (intmax_t)(pass1time/(time_t)3600),
628 (intmax_t)((pass1time%(time_t)3600) / (time_t)60));
636 * Do pass 1 -- write files that are 1% of the estimated size until error.
638 pass1size = (off_t)(((double)estsize * 0.01) / (double)blockkb); /* 1% of estimate */
639 if(pass1size <= (off_t)0) {
640 pass1size = (off_t)2; /* strange end case */
642 do_pass(pass1size, &pass1blocks, &pass1files, &pass1time);
645 * Do pass 2 -- write smaller files until error.
647 pass2size = pass1size / (off_t)2;
648 do_pass(pass2size, &pass2blocks, &pass2files, &pass2time);
651 * Compute the size of a filemark as the difference in data written
652 * between pass 1 and pass 2 divided by the difference in number of
653 * file marks written between pass 1 and pass 2. Note that we have
654 * to be careful in case size_t is unsigned (i.e. do not subtract
655 * things and then check for less than zero).
657 if (pass1blocks <= pass2blocks) {
659 * If tape marks take up space, there should be fewer blocks in pass
660 * 2 than in pass 1 since we wrote twice as many tape marks. But
661 * odd things happen, so make sure the result does not go negative.
663 blockdiff = (off_t)0;
665 blockdiff = pass1blocks - pass2blocks;
667 if (pass2files <= pass1files) {
669 * This should not happen, but just in case ...
673 filediff = pass2files - pass1files;
675 filemark = (size_t)((blockdiff * (off_t)blockkb) / filediff);
678 * Compute the length as the average of the two pass sizes including
681 size = ((pass1blocks * (off_t)blockkb + (off_t)filemark * pass1files)
682 + (pass2blocks * (off_t)blockkb + (off_t)filemark * pass2files))
684 if (size >= (off_t)(1024 * 1024 * 1000)) {
685 size /= (off_t)(1024 * 1024);
686 sizeunits = "gbytes";
687 } else if (size >= (off_t)(1024 * 1000)) {
689 sizeunits = "mbytes";
691 sizeunits = "kbytes";
695 * Compute the speed as the average of the two passes.
697 speed = (unsigned long)((((double)pass1blocks
698 * (double)blockkb / (double)pass1time)
699 + ((double)pass2blocks * (double)blockkb / (double)pass2time)) / 2.0);
704 g_printf("define tapetype %s {\n", typename);
705 g_printf(_(" comment \"just produced by tapetype prog (hardware compression %s)\"\n"),
706 hwcompr ? _("on") : _("off"));
707 g_printf(" length %lld %s\n", (long long)size, sizeunits);
708 g_printf(" filemark %zu kbytes\n", filemark);
709 g_printf(" speed %lu kps\n", speed);
712 if (tapefd_rewind(fd) == -1) {
713 g_fprintf(stderr, _("%s: could not rewind %s: %s\n"),
714 sProgName, tapedev, strerror(errno));
718 if (tapefd_close(fd) == -1) {
719 g_fprintf(stderr, _("%s: could not close %s: %s\n"),
720 sProgName, tapedev, strerror(errno));