X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fbtape.c;h=b4fbf4f23924c1dfe3fc090378f7ec9c2d7c567b;hb=79822b6b171cc2af23c6f3cd1608c59e15897a84;hp=43c00d64d9f991734b9379bfa392b5b73c6cb026;hpb=ddb7c4ab1b2db36baec0e09c7c4495c42366a2d3;p=bacula%2Fbacula diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 43c00d64d9..b4fbf4f239 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -1,3 +1,30 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2010 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ /* * * Bacula Tape manipulation program @@ -10,29 +37,18 @@ * Note, this program reads stored.conf, and will only * talk to devices that are configured. * - * Version $Id$ - * - */ -/* - Copyright (C) 2000-2006 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - version 2 as amended with additional clauses defined in the - file LICENSE in the main source directory. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - the file LICENSE for additional details. - */ #include "bacula.h" #include "stored.h" +#ifdef USE_VTAPE +#include "vtape.h" +#endif + /* Dummy functions */ int generate_daemon_event(JCR *jcr, const char *event) { return 1; } +extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code); /* External subroutines */ extern void free_config_resources(); @@ -56,7 +72,9 @@ static uint32_t btape_state_level = 2; DEVICE *dev = NULL; DCR *dcr; DEVRES *device = NULL; +int exit_code = 0; +#define REC_SIZE 32768 /* Forward referenced subroutines */ static void do_tape_cmds(); @@ -66,6 +84,7 @@ static void rewindcmd(); static void clearcmd(); static void wrcmd(); static void rrcmd(); +static void rbcmd(); static void eodcmd(); static void fillcmd(); static void qfillcmd(); @@ -78,13 +97,13 @@ static bool my_mount_next_read_volume(DCR *dcr); static void scan_blocks(); static void set_volume_name(const char *VolName, int volnum); static void rawfill_cmd(); -static void bfill_cmd(); static bool open_the_device(); static void autochangercmd(); -static void do_unfill(); +static bool do_unfill(); /* Static variables */ +static CONFIG *config; #define CONFIG_FILE "bacula-sd.conf" char *configfile = NULL; @@ -104,7 +123,6 @@ static int stop = 0; static uint64_t vol_size; static uint64_t VolBytes; static time_t now; -static double kbs; static int32_t file_index; static int end_of_tape = 0; static uint32_t LastBlock = 0; @@ -153,6 +171,7 @@ int main(int margc, char *margv[]) bindtextdomain("bacula", LOCALEDIR); textdomain("bacula"); init_stack_dump(); + lmgr_init_thread(); /* Sanity checks */ if (TAPE_BSIZE % B_DEV_BSIZE != 0 || TAPE_BSIZE / B_DEV_BSIZE == 0) { @@ -162,9 +181,9 @@ int main(int margc, char *margv[]) if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE)-1))) { Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"), TAPE_BSIZE); } - if (sizeof(off_t) < 8) { - Pmsg1(-1, _("\n\n!!!! Warning large disk addressing disabled. off_t=%d should be 8 or more !!!!!\n\n\n"), - sizeof(off_t)); + if (sizeof(boffset_t) < 8) { + Pmsg1(-1, _("\n\n!!!! Warning large disk addressing disabled. boffset_t=%d should be 8 or more !!!!!\n\n\n"), + sizeof(boffset_t)); } x32 = 123456789; bsnprintf(buf, sizeof(buf), "%u", x32); @@ -207,9 +226,13 @@ int main(int margc, char *margv[]) break; case 'd': /* set debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) { - debug_level = 1; + if (*optarg == 't') { + dbg_timestamp = true; + } else { + debug_level = atoi(optarg); + if (debug_level <= 0) { + debug_level = 1; + } } break; @@ -248,8 +271,8 @@ int main(int margc, char *margv[]) daemon_start_time = time(NULL); - parse_config(configfile); - + config = new_config_parser(); + parse_sd_config(config, configfile, M_ERROR_TERM); /* See if we can open a device */ if (margc == 0) { @@ -282,15 +305,13 @@ int main(int margc, char *margv[]) } dcr = jcr->dcr; if (!open_the_device()) { - goto terminate; + exit(1); } Dmsg0(200, "Do tape commands\n"); do_tape_cmds(); -terminate: - terminate_btape(0); - return 0; + terminate_btape(exit_code); } static void terminate_btape(int stat) @@ -300,7 +321,11 @@ static void terminate_btape(int stat) if (configfile) { free(configfile); } - free_config_resources(); + if (config) { + config->free_resources(); + free(config); + config = NULL; + } if (args) { free_pool_memory(args); args = NULL; @@ -317,12 +342,12 @@ static void terminate_btape(int stat) free_jcr(jcr); jcr = NULL; + free_volume_lists(); + if (dev) { dev->term(); } - free_volume_list(); - if (debug_level > 10) print_memory_pool_stats(); @@ -332,31 +357,129 @@ static void terminate_btape(int stat) stop_watchdog(); term_msg(); - close_memory_pool(); /* free memory in pool */ term_last_jobs_list(); + close_memory_pool(); /* free memory in pool */ + lmgr_cleanup_main(); sm_dump(false); exit(stat); } + +btime_t total_time=0; +uint64_t total_size=0; + +static void init_total_speed() +{ + total_size = 0; + total_time = 0; +} + +static void print_total_speed() +{ + char ec1[50], ec2[50]; + uint64_t rate = total_size / total_time; + Pmsg2(000, _("Total Volume bytes=%sB. Total Write rate = %sB/s\n"), + edit_uint64_with_suffix(total_size, ec1), + edit_uint64_with_suffix(rate, ec2)); +} + +static void init_speed() +{ + time(&jcr->run_time); /* start counting time for rates */ + jcr->JobBytes=0; +} + +static void print_speed(uint64_t bytes) +{ + char ec1[50], ec2[50]; + uint64_t rate; + + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; /* don't divide by zero */ + } + + total_time += now; + total_size += bytes; + + rate = bytes / now; + Pmsg2(000, _("Volume bytes=%sB. Write rate = %sB/s\n"), + edit_uint64_with_suffix(bytes, ec1), + edit_uint64_with_suffix(rate, ec2)); +} + +/* + * Helper that fill a buffer with random data or not + */ +typedef enum { + FILL_RANDOM, + FILL_ZERO +} fill_mode_t; + +static void fill_buffer(fill_mode_t mode, char *buf, uint32_t len) +{ + int fd; + switch (mode) { + case FILL_RANDOM: + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + read(fd, buf, len); + close(fd); + } else { + uint32_t *p = (uint32_t *)buf; + srandom(time(NULL)); + for (uint32_t i=0; ir_dlock(); Dmsg1(200, "Opening device %s\n", dcr->VolumeName); if (dev->open(dcr, OPEN_READ_WRITE) < 0) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg); - unlock_device(dev); - free_block(block); - return false; + ok = false; + goto bail_out; } Pmsg1(000, _("open device %s: OK\n"), dev->print_name()); dev->set_append(); /* put volume in append mode */ - unlock_device(dev); + +bail_out: + dev->dunlock(); free_block(block); - return true; + return ok; } @@ -384,7 +507,7 @@ static void labelcmd() } } dev->rewind(dcr); - write_new_volume_label_to_dev(dcr, cmd, "Default", true /* label dvd now */); + write_new_volume_label_to_dev(dcr, cmd, "Default", false,/*no relabel*/ true /* label dvd now */); Pmsg1(-1, _("Wrote Volume label for volume \"%s\".\n"), cmd); } @@ -499,7 +622,7 @@ static void weofcmd() */ static void eomcmd() { - if (!dev->eod()) { + if (!dev->eod(dcr)) { Pmsg1(0, "%s", dev->bstrerror()); return; } else { @@ -604,7 +727,7 @@ static void capcmd() } /* - * Test writting larger and larger records. + * Test writing larger and larger records. * This is a torture test for records. */ static void rectestcmd() @@ -613,7 +736,7 @@ static void rectestcmd() DEV_RECORD *rec; int i, blkno = 0; - Pmsg0(0, _("Test writting larger and larger records.\n" + Pmsg0(0, _("Test writing larger and larger records.\n" "This is a torture test for records.\nI am going to write\n" "larger and larger records. It will stop when the record size\n" "plus the header exceeds the block size (by default about 64K)\n")); @@ -655,16 +778,16 @@ static void rectestcmd() * it to make sure it is valid. Bacula can skip this validation * if you set "Backward space record = no" */ -static int re_read_block_test() +static bool re_read_block_test() { DEV_BLOCK *block = dcr->block; DEV_RECORD *rec; - int stat = 0; + bool rc = false; int len; if (!(dev->capabilities & CAP_BSR)) { Pmsg0(-1, _("Skipping read backwards test because BSR turned off.\n")); - return 0; + return true; } Pmsg0(-1, _("\n=== Write, backup, and re-read test ===\n\n" @@ -712,14 +835,14 @@ static int re_read_block_test() Pmsg1(0, _("Wrote third record of %d bytes.\n"), rec->data_len); } weofcmd(); - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } if (!dev->bsf(1)) { Pmsg1(0, _("Backspace file failed! ERR=%s\n"), dev->bstrerror()); goto bail_out; } - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { if (!dev->bsf(1)) { Pmsg1(0, _("Backspace file failed! ERR=%s\n"), dev->bstrerror()); goto bail_out; @@ -733,13 +856,13 @@ static int re_read_block_test() Pmsg0(0, _("Backspace record OK.\n")); if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) { berrno be; - Pmsg1(0, _("Read block failed! ERR=%s\n"), be.strerror(dev->dev_errno)); + Pmsg1(0, _("Read block failed! ERR=%s\n"), be.bstrerror(dev->dev_errno)); goto bail_out; } memset(rec->data, 0, rec->data_len); - if (!read_record_from_block(block, rec)) { + if (!read_record_from_block(dcr, block, rec)) { berrno be; - Pmsg1(0, _("Read block failed! ERR=%s\n"), be.strerror(dev->dev_errno)); + Pmsg1(0, _("Read block failed! ERR=%s\n"), be.bstrerror(dev->dev_errno)); goto bail_out; } for (int i=0; iblock; + int stat; + uint32_t block_num = 0; + int my_errno; + char ed1[200]; + nb_gb *= 1024*1024*1024; /* convert size from nb to GB */ + + init_total_speed(); + fill_buffer(mode, block->buf, block->buf_len); + + Pmsg3(0, _("Begin writing %i files of %sB with raw blocks of %u bytes.\n"), + nb, edit_uint64_with_suffix(nb_gb, ed1), block->buf_len); + + for (uint32_t j=0; jJobBytes < nb_gb; ) { + stat = dev->d_write(dev->fd(), block->buf, block->buf_len); + if (stat == (int)block->buf_len) { + if ((block_num++ % 500) == 0) { + printf("+"); + fflush(stdout); + } + + mix_buffer(mode, block->buf, block->buf_len); + + jcr->JobBytes += stat; + + } else { + my_errno = errno; + printf("\n"); + berrno be; + printf(_("Write failed at block %u. stat=%d ERR=%s\n"), block_num, + stat, be.bstrerror(my_errno)); + return false; + } + } + printf("\n"); + weofcmd(); + print_speed(jcr->JobBytes); + } + print_total_speed(); + printf("\n"); + return true; +} + + +static bool speed_test_bacula(fill_mode_t mode, uint64_t nb_gb, uint32_t nb) +{ + DEV_BLOCK *block = dcr->block; + char ed1[200]; + DEV_RECORD *rec; + uint64_t last_bytes = dev->VolCatInfo.VolCatBytes; + uint64_t written=0; + + nb_gb *= 1024*1024*1024; /* convert size from nb to GB */ + + init_total_speed(); + + empty_block(block); + rec = new_record(); + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + + fill_buffer(mode, rec->data, rec->data_len); + + Pmsg3(0, _("Begin writing %i files of %sB with blocks of %u bytes.\n"), + nb, edit_uint64_with_suffix(nb_gb, ed1), block->buf_len); + + for (uint32_t j=0; jBlockNumber % 500) == 0) { + printf("+"); + fflush(stdout); + } + written += dev->VolCatInfo.VolCatBytes - last_bytes; + last_bytes = dev->VolCatInfo.VolCatBytes; + mix_buffer(mode, rec->data, rec->data_len); + } + printf("\n"); + weofcmd(); + print_speed(written); + } + print_total_speed(); + printf("\n"); + free_record(rec); + return true; + +bail_out: + free_record(rec); + return false; +} + +/* TODO: use UAContext */ +static int btape_find_arg(const char *keyword) +{ + for (int i=1; i 0) { + file_size = atoi(argv[i]); + if (file_size > 100) { + Pmsg0(0, _("The file_size is too big, stop this test with Ctrl-c.\n")); + } + } + + i = btape_find_arg("nb_file"); + if (i > 0) { + nb_file = atoi(argv[i]); + } + + if (btape_find_arg("skip_zero") > 0) { + do_zero = false; + } + + if (btape_find_arg("skip_random") > 0) { + do_random = false; + } + + if (btape_find_arg("skip_raw") > 0) { + do_raw = false; + } + + if (btape_find_arg("skip_block") > 0) { + do_block = false; + } + + if (do_raw) { + dev->rewind(dcr); + if (do_zero) { + Pmsg0(0, _("Test with zero data, should give the " + "maximum throughput.\n")); + if (file_size) { + ok(speed_test_raw(FILL_ZERO, file_size, nb_file)); + } else { + ok(speed_test_raw(FILL_ZERO, 1, nb_file)); + ok(speed_test_raw(FILL_ZERO, 2, nb_file)); + ok(speed_test_raw(FILL_ZERO, 4, nb_file)); + } + } + + if (do_random) { + Pmsg0(0, _("Test with random data, should give the minimum " + "throughput.\n")); + if (file_size) { + ok(speed_test_raw(FILL_RANDOM, file_size, nb_file)); + } else { + ok(speed_test_raw(FILL_RANDOM, 1, nb_file)); + ok(speed_test_raw(FILL_RANDOM, 2, nb_file)); + ok(speed_test_raw(FILL_RANDOM, 4, nb_file)); + } + } + } + + if (do_block) { + dev->rewind(dcr); + if (do_zero) { + Pmsg0(0, _("Test with zero data and bacula block structure.\n")); + if (file_size) { + ok(speed_test_bacula(FILL_ZERO, file_size, nb_file)); + } else { + ok(speed_test_bacula(FILL_ZERO, 1, nb_file)); + ok(speed_test_bacula(FILL_ZERO, 2, nb_file)); + ok(speed_test_bacula(FILL_ZERO, 4, nb_file)); + } + } + + if (do_random) { + Pmsg0(0, _("Test with random data, should give the minimum " + "throughput.\n")); + if (file_size) { + ok(speed_test_bacula(FILL_RANDOM, file_size, nb_file)); + } else { + ok(speed_test_bacula(FILL_RANDOM, 1, nb_file)); + ok(speed_test_bacula(FILL_RANDOM, 2, nb_file)); + ok(speed_test_bacula(FILL_RANDOM, 4, nb_file)); + } + } + } +} + +const int num_recs = 10000; + +static bool write_two_files() { DEV_BLOCK *block; DEV_RECORD *rec; - int stat = 0; int len, i, j; int *p; + bool rc = false; /* bad return code */ - Pmsg0(-1, _("\n=== Write, rewind, and re-read test ===\n\n" - "I'm going to write 1000 records and an EOF\n" - "then write 1000 records and an EOF, then rewind,\n" + Pmsg2(-1, _("\n=== Write, rewind, and re-read test ===\n\n" + "I'm going to write %d records and an EOF\n" + "then write %d records and an EOF, then rewind,\n" "and re-read the data to verify that it is correct.\n\n" - "This is an *essential* feature ...\n\n")); + "This is an *essential* feature ...\n\n"), num_recs, num_recs); + block = dcr->block; + empty_block(block); rec = new_record(); + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + len = rec->data_len/sizeof(i); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; } - rec->data = check_pool_memory_size(rec->data, block->buf_len); - rec->data_len = block->buf_len-100; - len = rec->data_len/sizeof(i); - for (i=1; i<=1000; i++) { + + for (i=1; i<=num_recs; i++) { p = (int *)rec->data; for (j=0; jdata_len); + Pmsg2(0, _("Wrote %d blocks of %d bytes.\n"), num_recs, rec->data_len); weofcmd(); - for (i=1001; i<=2000; i++) { + for (i=num_recs+1; i<=2*num_recs; i++) { p = (int *)rec->data; for (j=0; jdata_len); + Pmsg2(0, _("Wrote %d blocks of %d bytes.\n"), num_recs, rec->data_len); weofcmd(); - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } + rc = true; + +bail_out: + free_record(rec); + if (!rc) { + exit_code = 1; + } + return rc; + +} + +/* + * This test writes Bacula blocks to the tape in + * several files. It then rewinds the tape and attepts + * to read these blocks back checking the data. + */ +static bool write_read_test() +{ + DEV_BLOCK *block; + DEV_RECORD *rec; + bool rc = false; + int len, i, j; + int *p; + + rec = new_record(); + + if (!write_two_files()) { + goto bail_out; + } + + block = dcr->block; + empty_block(block); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; } else { Pmsg0(0, _("Rewind OK.\n")); } - for (i=1; i<=2000; i++) { + + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + len = rec->data_len/sizeof(i); + + /* Now read it back */ + for (i=1; i<=2*num_recs; i++) { read_again: if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) { berrno be; if (dev_state(dev, ST_EOF)) { Pmsg0(-1, _("Got EOF on tape.\n")); - if (i == 1001) { + if (i == num_recs+1) { goto read_again; } } - Pmsg2(0, _("Read block %d failed! ERR=%s\n"), i, be.strerror(dev->dev_errno)); + Pmsg2(0, _("Read block %d failed! ERR=%s\n"), i, be.bstrerror(dev->dev_errno)); goto bail_out; } memset(rec->data, 0, rec->data_len); - if (!read_record_from_block(block, rec)) { + if (!read_record_from_block(dcr, block, rec)) { berrno be; - Pmsg2(0, _("Read record failed. Block %d! ERR=%s\n"), i, be.strerror(dev->dev_errno)); + Pmsg2(0, _("Read record failed. Block %d! ERR=%s\n"), i, be.bstrerror(dev->dev_errno)); goto bail_out; } p = (int *)rec->data; @@ -863,16 +1239,19 @@ read_again: } p++; } - if (i == 1000 || i == 2000) { - Pmsg0(-1, _("1000 blocks re-read correctly.\n")); + if (i == num_recs || i == 2*num_recs) { + Pmsg1(-1, _("%d blocks re-read correctly.\n"), num_recs); } } Pmsg0(-1, _("=== Test Succeeded. End Write, rewind, and re-read test ===\n\n")); - stat = 1; + rc = true; bail_out: free_record(rec); - return stat; + if (!rc) { + exit_code = 1; + } + return rc; } /* @@ -880,67 +1259,26 @@ bail_out: * several files. It then rewinds the tape and attepts * to read these blocks back checking the data. */ -static int position_test() +static bool position_test() { DEV_BLOCK *block = dcr->block; DEV_RECORD *rec; - int stat = 0; - int len, i, j; - bool ok = true; + bool rc = false; + int len, j; + bool more = true; int recno = 0; int file = 0, blk = 0; int *p; bool got_eof = false; - Pmsg0(-1, _("\n=== Write, rewind, and position test ===\n\n" - "I'm going to write 1000 records and an EOF\n" - "then write 1000 records and an EOF, then rewind,\n" - "and position to a few blocks and verify that it is correct.\n\n" - "This is an *essential* feature ...\n\n")); + Pmsg0(0, _("Block position test\n")); + block = dcr->block; empty_block(block); rec = new_record(); - if (!dev->rewind(dcr)) { - Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); - goto bail_out; - } rec->data = check_pool_memory_size(rec->data, block->buf_len); rec->data_len = block->buf_len-100; - len = rec->data_len/sizeof(i); - for (i=1; i<=1000; i++) { - p = (int *)rec->data; - for (j=0; jdata_len); - weofcmd(); - for (i=1001; i<=2000; i++) { - p = (int *)rec->data; - for (j=0; jdata_len); - weofcmd(); - if (dev_cap(dev, CAP_TWOEOF)) { - weofcmd(); - } + len = rec->data_len/sizeof(j); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; @@ -948,8 +1286,11 @@ static int position_test() Pmsg0(0, _("Rewind OK.\n")); } - while(ok) { + while (more) { /* Set up next item to read based on where we are */ + /* At each step, recno is what we print for the "block number" + * and file, blk are the real positions to go to. + */ switch (recno) { case 0: recno = 5; @@ -962,31 +1303,31 @@ static int position_test() blk = 200; break; case 201: - recno = 1000; + recno = num_recs; file = 0; - blk = 999; + blk = num_recs-1; break; - case 1000: - recno = 1001; + case num_recs: + recno = num_recs+1; file = 1; blk = 0; break; - case 1001: - recno = 1601; + case num_recs+1: + recno = num_recs+601; file = 1; blk = 600; break; - case 1601: - recno = 2000; + case num_recs+601: + recno = 2*num_recs; file = 1; - blk = 999; + blk = num_recs-1; break; - case 2000: - ok = false; + case 2*num_recs: + more = false; continue; } Pmsg2(-1, _("Reposition to file:block %d:%d\n"), file, blk); - if (!dev->reposition(file, blk)) { + if (!dev->reposition(dcr, file, blk)) { Pmsg0(0, _("Reposition error.\n")); goto bail_out; } @@ -1001,7 +1342,7 @@ read_again: } } Pmsg4(0, _("Read block %d failed! file=%d blk=%d. ERR=%s\n\n"), - recno, file, blk, be.strerror(dev->dev_errno)); + recno, file, blk, be.bstrerror(dev->dev_errno)); Pmsg0(0, _("This may be because the tape drive block size is not\n" " set to variable blocking as normally used by Bacula.\n" " Please see the Tape Testing chapter in the manual and \n" @@ -1016,9 +1357,9 @@ read_again: goto bail_out; } memset(rec->data, 0, rec->data_len); - if (!read_record_from_block(block, rec)) { + if (!read_record_from_block(dcr, block, rec)) { berrno be; - Pmsg1(0, _("Read record failed! ERR=%s\n"), be.strerror(dev->dev_errno)); + Pmsg1(0, _("Read record failed! ERR=%s\n"), be.bstrerror(dev->dev_errno)); goto bail_out; } p = (int *)rec->data; @@ -1032,11 +1373,11 @@ read_again: Pmsg1(-1, _("Block %d re-read correctly.\n"), recno); } Pmsg0(-1, _("=== Test Succeeded. End Write, rewind, and re-read test ===\n\n")); - stat = 1; + rc = true; bail_out: free_record(rec); - return stat; + return rc; } @@ -1066,7 +1407,7 @@ static int append_test() wrcmd(); wrcmd(); weofcmd(); /* end file 2 */ - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } dev->close(); /* release device */ @@ -1086,7 +1427,7 @@ static int append_test() Pmsg0(-1, _("\nNow the important part, I am going to attempt to append to the tape.\n\n")); wrcmd(); weofcmd(); - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } rewindcmd(); @@ -1115,7 +1456,7 @@ static int autochanger_test() int sleep_time = 0; Dmsg1(100, "Max changer wait = %d sec\n", timeout); - if (!dev_cap(dev, CAP_AUTOCHANGER)) { + if (!dev->has_cap(CAP_AUTOCHANGER)) { return 1; } if (!(dcr->device && dcr->device->changer_name && dcr->device->changer_command)) { @@ -1152,7 +1493,7 @@ try_again: } else { berrno be; Pmsg1(-1, _("3991 Bad autochanger command: %s\n"), changer); - Pmsg2(-1, _("3991 result=\"%s\": ERR=%s\n"), results, be.strerror(status)); + Pmsg2(-1, _("3991 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status)); goto bail_out; } if (loaded) { @@ -1174,7 +1515,7 @@ try_again: if (status != 0) { berrno be; Pmsg1(-1, _("3992 Bad autochanger command: %s\n"), changer); - Pmsg2(-1, _("3992 result=\"%s\": ERR=%s\n"), results, be.strerror(status)); + Pmsg2(-1, _("3992 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status)); } } @@ -1197,7 +1538,7 @@ try_again: } else { berrno be; Pmsg1(-1, _("3993 Bad autochanger command: %s\n"), changer); - Pmsg2(-1, _("3993 result=\"%s\": ERR=%s\n"), results, be.strerror(status)); + Pmsg2(-1, _("3993 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status)); goto bail_out; } @@ -1259,7 +1600,7 @@ static void autochangercmd() * This test assumes that the append test has been done, * then it tests the fsf function. */ -static int fsf_test() +static bool fsf_test() { bool set_off = false; @@ -1282,7 +1623,7 @@ static int fsf_test() weofcmd(); /* end file 3 */ wrcmd(); weofcmd(); /* end file 4 */ - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } @@ -1341,14 +1682,14 @@ test_again: goto bail_out; } Pmsg0(-1, _("\n=== End Forward space files test ===\n\n")); - return 1; + return true; bail_out: Pmsg0(-1, _("\nThe forward space file test failed.\n")); - if (dev_cap(dev, CAP_FASTFSF)) { + if (dev->has_cap(CAP_FASTFSF)) { Pmsg0(-1, _("You have Fast Forward Space File enabled.\n" "I am turning it off then retrying the test.\n")); - dev->capabilities &= ~CAP_FASTFSF; + dev->clear_cap(CAP_FASTFSF); set_off = true; goto test_again; } @@ -1356,7 +1697,7 @@ bail_out: "Some systems, e.g. OpenBSD, require you to set\n" " Use MTIOCGET= no\n" "in your device resource. Use with caution.\n")); - return -2; + return false; } @@ -1372,9 +1713,11 @@ static void testcmd() int stat; if (!write_read_test()) { + exit_code = 1; return; } if (!position_test()) { + exit_code = 1; return; } @@ -1383,13 +1726,13 @@ static void testcmd() goto all_done; } if (stat == -1) { /* first test failed */ - if (dev_cap(dev, CAP_EOM) || dev_cap(dev, CAP_FASTFSF)) { + if (dev->has_cap(CAP_EOM) || dev->has_cap(CAP_FASTFSF)) { Pmsg0(-1, _("\nAppend test failed. Attempting again.\n" "Setting \"Hardware End of Medium = no\n" " and \"Fast Forward Space File = no\n" "and retrying append test.\n\n")); - dev->capabilities &= ~CAP_EOM; /* turn off eom */ - dev->capabilities &= ~CAP_FASTFSF; /* turn off fast fsf */ + dev->clear_cap(CAP_EOM); /* turn off eom */ + dev->clear_cap(CAP_FASTFSF); /* turn off fast fsf */ stat = append_test(); if (stat == 1) { Pmsg0(-1, _("\n\nIt looks like the test worked this time, please add:\n\n" @@ -1437,6 +1780,7 @@ failed: "Some systems, e.g. OpenBSD, require you to set\n" " Use MTIOCGET= no\n" "in your device resource. Use with caution.\n")); + exit_code = 1; return; } @@ -1460,10 +1804,14 @@ all_done: "the tape.\n\n")); if (stat == 1) { - re_read_block_test(); + if (!re_read_block_test()) { + exit_code = 1; + } } - fsf_test(); /* do fast forward space file test */ + if (!fsf_test()) { /* do fast forward space file test */ + exit_code = 1; + } autochanger_test(); /* do autochanger test */ @@ -1513,6 +1861,14 @@ static void fsrcmd() } } +/* + * Read a Bacula block from the tape + */ +static void rbcmd() +{ + dev->open(dcr, OPEN_READ_ONLY); + read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK); +} /* * Write a Bacula block to the tape @@ -1523,6 +1879,9 @@ static void wrcmd() DEV_RECORD *rec = dcr->rec; int i; + if (!dev->is_open()) { + open_the_device(); + } sm_check(__FILE__, __LINE__, false); empty_block(block); if (verbose > 1) { @@ -1569,13 +1928,13 @@ static void rrcmd() len = 1024; } buf = (char *)malloc(len); - stat = read(dev->fd, buf, len); + stat = read(dev->fd(), buf, len); if (stat > 0 && stat <= len) { errno = 0; } berrno be; Pmsg3(0, _("Read of %d bytes gives stat=%d. ERR=%s\n"), - len, stat, be.strerror()); + len, stat, be.bstrerror()); free(buf); } @@ -1600,15 +1959,15 @@ static void scancmd() Pmsg0(0, _("End of tape\n")); return; } - update_pos_dev(dev); + dev->update_pos(dcr); tot_files = dev->file; Pmsg1(0, _("Starting scan at file %u\n"), dev->file); for (;;) { - if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) { + if ((stat = read(dev->fd(), buf, sizeof(buf))) < 0) { berrno be; dev->clrerror(-1); Mmsg2(dev->errmsg, _("read error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror()); + dev->dev_name, be.bstrerror()); Pmsg2(0, _("Bad status from read %d. ERR=%s\n"), stat, dev->bstrerror()); if (blocks > 0) { if (blocks==1) { @@ -1623,7 +1982,7 @@ static void scancmd() Dmsg1(200, "read status = %d\n", stat); /* sleep(1); */ if (stat != block_size) { - update_pos_dev(dev); + dev->update_pos(dcr); if (blocks > 0) { if (blocks==1) { printf(_("1 block of %d bytes in file %d\n"), block_size, dev->file); @@ -1636,7 +1995,7 @@ static void scancmd() block_size = stat; } if (stat == 0) { /* EOF */ - update_pos_dev(dev); + dev->update_pos(dcr); printf(_("End of File mark.\n")); /* Two reads of zero means end of tape */ if (dev->state & ST_EOF) @@ -1656,7 +2015,7 @@ static void scancmd() bytes += stat; } } - update_pos_dev(dev); + dev->update_pos(dcr); tot_files = dev->file - tot_files; printf(_("Total files=%d, blocks=%d, bytes = %s\n"), tot_files, tot_blocks, edit_uint64_with_commas(bytes, ec1)); @@ -1682,7 +2041,7 @@ static void scan_blocks() bytes = 0; empty_block(block); - update_pos_dev(dev); + dev->update_pos(dcr); tot_files = dev->file; for (;;) { if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) { @@ -1743,14 +2102,14 @@ static void scan_blocks() blocks++; tot_blocks++; bytes += block->block_len; - Dmsg6(100, "Blk_blk=%u dev_blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n", - block->BlockNumber, dev->block_num, block->block_len, block->BlockVer, + Dmsg7(100, "Blk_blk=%u file,blk=%u,%u blen=%u bVer=%d SessId=%u SessTim=%u\n", + block->BlockNumber, dev->file, dev->block_num, block->block_len, block->BlockVer, block->VolSessionId, block->VolSessionTime); if (verbose == 1) { DEV_RECORD *rec = new_record(); - read_record_from_block(block, rec); - Pmsg8(-1, _("Blk_block: %u dev_blk=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"), - block->BlockNumber, dev->block_num, block->block_len, + read_record_from_block(dcr, block, rec); + Pmsg9(-1, _("Block=%u file,blk=%u,%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"), + block->BlockNumber, dev->file, dev->block_num, block->block_len, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len); rec->remainder = 0; @@ -1787,11 +2146,12 @@ static void fillcmd() { DEV_RECORD rec; DEV_BLOCK *block = dcr->block; - char ec1[50]; + char ec1[50], ec2[50]; char buf1[100], buf2[100]; - int fd; - uint32_t i; + uint64_t write_eof; + uint64_t rate; uint32_t min_block_size; + int fd; struct tm tm; ok = true; @@ -1800,8 +2160,9 @@ static void fillcmd() last_file = 0; last_block_num = 0; BlockNumber = 0; + exit_code = 0; - Pmsg0(-1, _("\n" + Pmsg1(-1, _("\n" "This command simulates Bacula writing to a tape.\n" "It requires either one or two blank tapes, which it\n" "will label and write.\n\n" @@ -1809,14 +2170,15 @@ static void fillcmd() "the tapes that are in slots 1 and 2, otherwise, you will\n" "be prompted to insert the tapes when necessary.\n\n" "It will print a status approximately\n" -"every 322 MB, and write an EOF every 3.2 GB. If you have\n" +"every 322 MB, and write an EOF every %s. If you have\n" "selected the simple test option, after writing the first tape\n" "it will rewind it and re-read the last block written.\n\n" "If you have selected the multiple tape test, when the first tape\n" "fills, it will ask for a second, and after writing a few more \n" "blocks, it will stop. Then it will begin re-reading the\n" "two tapes.\n\n" -"This may take a long time -- hours! ...\n\n")); +"This may take a long time -- hours! ...\n\n"), + edit_uint64_with_suffix(dev->max_file_size, buf1)); get_cmd(_("Do you want to run the simplified test (s) with one tape\n" "or the complete multiple tape (m) test: (s/m) ")); @@ -1828,6 +2190,7 @@ static void fillcmd() simple = false; } else { Pmsg0(000, _("Command aborted.\n")); + exit_code = 1; return; } @@ -1837,15 +2200,9 @@ static void fillcmd() /* Use fixed block size to simplify read back */ min_block_size = dev->min_block_size; dev->min_block_size = dev->max_block_size; + write_eof = dev->max_file_size / REC_SIZE; /*compute when we add EOF*/ set_volume_name("TestVolume1", 1); - - if (!dev->rewind(dcr)) { - Pmsg0(000, _("Rewind failed.\n")); - } - if (!dev->weof(1)) { - Pmsg0(000, _("Write EOF failed.\n")); - } - labelcmd(); + dir_ask_sysop_to_create_appendable_volume(dcr); dev->set_append(); /* force volume to be relabeled */ /* @@ -1856,6 +2213,7 @@ static void fillcmd() Dmsg0(100, "just before acquire_device\n"); if (!acquire_device_for_append(dcr)) { set_jcr_job_status(jcr, JS_ErrorTerminated); + exit_code = 1; return; } block = jcr->dcr->block; @@ -1874,24 +2232,12 @@ static void fillcmd() memset(&rec, 0, sizeof(rec)); rec.data = get_memory(100000); /* max record size */ - -#define REC_SIZE 32768 rec.data_len = REC_SIZE; /* * Put some random data in the record */ - fd = open("/dev/urandom", O_RDONLY); - if (fd) { - read(fd, rec.data, rec.data_len); - close(fd); - } else { - uint32_t *p = (uint32_t *)rec.data; - srandom(time(NULL)); - for (i=0; iVolCatInfo.VolCatBytes / (1000.0 * (double)now); - Pmsg4(-1, _("Wrote blk_block=%u, dev_blk_num=%u VolBytes=%s rate=%.1f KB/s\n"), - block->BlockNumber, dev->block_num, - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs); + rate = dev->VolCatInfo.VolCatBytes / now; + Pmsg5(-1, _("Wrote block=%u, file,blk=%u,%u VolBytes=%s rate=%sB/s\n"), + block->BlockNumber, dev->file, dev->block_num, + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); } - /* Every 32000 blocks (approx 2GB) write an EOF. + /* Every X blocks (dev->max_file_size) write an EOF. */ - if ((block->BlockNumber % 32000) == 0) { + if ((block->BlockNumber % write_eof) == 0) { now = time(NULL); (void)localtime_r(&now, &tm); strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm); Pmsg1(-1, _("%s Flush block, write EOF\n"), buf1); flush_block(block, 0); +#ifdef needed_xxx dev->weof(1); +#endif } - /* Get out after writing 10 blocks to the second tape */ - if (++BlockNumber > 10 && stop != 0) { /* get out */ + /* Get out after writing 1000 blocks to the second tape */ + if (++BlockNumber > 1000 && stop != 0) { /* get out */ + Pmsg0(000, _("Wrote 1000 blocks on second tape. Done.\n")); break; } } if (!ok) { Pmsg0(000, _("Not OK\n")); + exit_code = 1; break; } jcr->JobBytes += rec.data_len; /* increment bytes this job */ @@ -1973,30 +2322,35 @@ static void fillcmd() FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); - /* Get out after writing 10 blocks to the second tape */ - if (BlockNumber > 10 && stop != 0) { /* get out */ + /* Get out after writing 1000 blocks to the second tape */ + if (BlockNumber > 1000 && stop != 0) { /* get out */ char ed1[50]; Pmsg1(-1, "Done writing %s records ...\n", edit_uint64_with_commas(write_count, ed1)); break; } - } + } /* end big for loop */ + if (vol_num > 1) { Dmsg0(100, "Write_end_session_label()\n"); /* Create Job status for end of session label */ if (!job_canceled(jcr) && ok) { set_jcr_job_status(jcr, JS_Terminated); } else if (!ok) { + Pmsg0(000, _("Job canceled.\n")); set_jcr_job_status(jcr, JS_ErrorTerminated); + exit_code = 1; } if (!write_session_label(dcr, EOS_LABEL)) { - Pmsg1(000, _("Error writting end session label. ERR=%s\n"), dev->bstrerror()); + Pmsg1(000, _("Error writing end session label. ERR=%s\n"), dev->bstrerror()); ok = false; + exit_code = 1; } /* Write out final block of this session */ if (!write_block_to_device(dcr)) { Pmsg0(-1, _("Set ok=false after write_block_to_device.\n")); ok = false; + exit_code = 1; } Pmsg0(-1, _("Wrote End of Session label.\n")); @@ -2022,29 +2376,37 @@ static void fillcmd() write(fd, last_block2->buf, last_block2->buf_len); write(fd, first_block->buf, first_block->buf_len); close(fd); - Pmsg2(-1, _("Wrote state file last_block_num1=%d last_block_num2=%d\n"), + Pmsg2(0, _("Wrote state file last_block_num1=%d last_block_num2=%d\n"), last_block_num1, last_block_num2); } else { berrno be; - Pmsg2(-1, _("Could not create state file: %s ERR=%s\n"), buf, - be.strerror()); + Pmsg2(0, _("Could not create state file: %s ERR=%s\n"), buf, + be.bstrerror()); + exit_code = 1; + ok = false; } now = time(NULL); (void)localtime_r(&now, &tm); strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm); - if (simple) { - Pmsg3(-1, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"), - buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); - } - else { - Pmsg3(-1, _("\n\n%s Done filling tapes at %d:%d. Now beginning re-read of first tape ...\n"), - buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); - } - - jcr->dcr->block = block; - do_unfill(); + if (ok) { + if (simple) { + Pmsg3(0, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"), + buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); + } else { + Pmsg3(0, _("\n\n%s Done filling tapes at %d:%d. Now beginning re-read of first tape ...\n"), + buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); + } + jcr->dcr->block = block; + if (!do_unfill()) { + Pmsg0(000, _("do_unfill failed.\n")); + exit_code = 1; + ok = false; + } + } else { + Pmsg1(000, _("%s: Error during test.\n"), buf1); + } dev->min_block_size = min_block_size; free_memory(rec.data); } @@ -2059,6 +2421,7 @@ static void unfillcmd() { int fd; + exit_code = 0; last_block1 = new_block(dev); last_block2 = new_block(dev); first_block = new_block(dev); @@ -2079,30 +2442,40 @@ static void unfillcmd() if (state_level != btape_state_level) { Pmsg0(-1, _("\nThe state file level has changed. You must redo\n" "the fill command.\n")); + exit_code = 1; return; } } else { berrno be; Pmsg2(-1, _("\nCould not find the state file: %s ERR=%s\n" - "You must redo the fill command.\n"), buf, be.strerror()); + "You must redo the fill command.\n"), buf, be.bstrerror()); + exit_code = 1; return; } - do_unfill(); + if (!do_unfill()) { + exit_code = 1; + } this_block = NULL; } -static void do_unfill() +/* + * This is the second part of the fill command. After the tape or + * tapes are written, we are called here to reread parts, particularly + * the last block. + */ +static bool do_unfill() { DEV_BLOCK *block = dcr->block; - bool autochanger; + int autochanger; + bool rc = false; dumped = 0; VolBytes = 0; LastBlock = 0; - Dmsg0(20, "Enter do_unfill\n"); - dev->capabilities |= CAP_ANONVOLS; /* allow reading any volume */ - dev->capabilities &= ~CAP_LABEL; /* don't label anything here */ + Pmsg0(000, "Enter do_unfill\n"); + dev->set_cap(CAP_ANONVOLS); /* allow reading any volume */ + dev->clear_cap(CAP_LABEL); /* don't label anything here */ end_of_tape = 0; @@ -2117,24 +2490,34 @@ static void do_unfill() last_file = last_file1; last_block = last_block1; + free_restore_volume_list(jcr); + jcr->bsr = NULL; + bstrncpy(dcr->VolumeName, "TestVolume1|TestVolume2", sizeof(dcr->VolumeName)); + create_restore_volume_list(jcr); + if (jcr->VolList != NULL) { + jcr->VolList->Slot = 1; + if (jcr->VolList->next != NULL) { + jcr->VolList->next->Slot = 2; + } + } + + set_volume_name("TestVolume1", 1); + if (!simple) { /* Multiple Volume tape */ /* Close device so user can use autochanger if desired */ - if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { + if (dev->has_cap(CAP_OFFLINEUNMOUNT)) { dev->offline(); } autochanger = autoload_device(dcr, 1, NULL); - if (!autochanger) { + if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); dev->close(); get_cmd(_("Mount first tape. Press enter when ready: ")); + Pmsg0(000, "\n"); } } - free_restore_volume_list(jcr); - jcr->dcr = new_dcr(jcr, dev); - set_volume_name("TestVolume1", 1); - jcr->bsr = NULL; - create_restore_volume_list(jcr); dev->close(); dev->num_writers = 0; if (!acquire_device_for_read(dcr)) { @@ -2157,7 +2540,7 @@ static void do_unfill() read_records(dcr, quickie_cb, my_mount_next_read_volume); Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num, last_file, last_block_num); - if (!dev->reposition(last_file, last_block_num)) { + if (!dev->reposition(dcr, last_file, last_block_num)) { Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror()); goto bail_out; } @@ -2169,6 +2552,7 @@ static void do_unfill() if (compare_blocks(last_block, block)) { if (simple) { Pmsg0(-1, _("\nThe last block on the tape matches. Test succeeded.\n\n")); + rc = true; } else { Pmsg0(-1, _("\nThe last block of the first tape matches.\n\n")); } @@ -2184,18 +2568,18 @@ static void do_unfill() /* Multiple Volume tape */ /* Close device so user can use autochanger if desired */ - if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { + if (dev->has_cap(CAP_OFFLINEUNMOUNT)) { dev->offline(); } - free_restore_volume_list(jcr); set_volume_name("TestVolume2", 2); - jcr->bsr = NULL; - create_restore_volume_list(jcr); + autochanger = autoload_device(dcr, 1, NULL); - if (!autochanger) { + if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); dev->close(); get_cmd(_("Mount second tape. Press enter when ready: ")); + Pmsg0(000, "\n"); } dev->clear_read(); @@ -2208,7 +2592,7 @@ static void do_unfill() * on the previous tape. */ Pmsg2(-1, _("Reposition from %u:%u to 0:1\n"), dev->file, dev->block_num); - if (!dev->reposition(0, 1)) { + if (!dev->reposition(dcr, 0, 1)) { Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror()); goto bail_out; } @@ -2224,7 +2608,7 @@ static void do_unfill() /* Now find and compare the last block */ Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num, last_file, last_block_num); - if (!dev->reposition(last_file, last_block_num)) { + if (!dev->reposition(dcr, last_file, last_block_num)) { Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror()); goto bail_out; } @@ -2235,12 +2619,14 @@ static void do_unfill() } if (compare_blocks(last_block, block)) { Pmsg0(-1, _("\nThe last block on the second tape matches. Test succeeded.\n\n")); + rc = true; } bail_out: free_block(last_block1); free_block(last_block2); free_block(first_block); + return rc; } /* Read 10000 records then stop */ @@ -2289,10 +2675,6 @@ static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block) return true; } - - - - /* * Write current block to tape regardless of whether or * not it is full. If the tape fills, attempt to @@ -2300,11 +2682,12 @@ static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block) */ static int flush_block(DEV_BLOCK *block, int dump) { - char ec1[50]; + char ec1[50], ec2[50]; + uint64_t rate; DEV_BLOCK *tblock; uint32_t this_file, this_block_num; - lock_device(dev); + dev->r_dlock(); if (!this_block) { this_block = new_block(dev); } @@ -2346,11 +2729,12 @@ static int flush_block(DEV_BLOCK *block, int dump) if (now <= 0) { now = 1; /* don't divide by zero */ } - kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now); + rate = dev->VolCatInfo.VolCatBytes / now; vol_size = dev->VolCatInfo.VolCatBytes; - Pmsg4(000, _("End of tape %d:%d. VolumeCapacity=%s. Write rate = %.1f KB/s\n"), + Pmsg4(000, _("End of tape %d:%d. Volume Bytes=%s. Write rate = %sB/s\n"), dev->file, dev->block_num, - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs); + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); if (simple) { stop = -1; /* stop, but do simplified test */ @@ -2359,12 +2743,12 @@ static int flush_block(DEV_BLOCK *block, int dump) if (!fixup_device_block_write_error(jcr->dcr)) { Pmsg1(000, _("Cannot fixup device error. %s\n"), dev->bstrerror()); ok = false; - unlock_device(dev); + dev->dunlock(); return 0; } BlockNumber = 0; /* start counting for second tape */ } - unlock_device(dev); + dev->dunlock(); return 1; /* end of tape reached */ } @@ -2383,7 +2767,7 @@ static int flush_block(DEV_BLOCK *block, int dump) last_file = this_file; last_block_num = this_block_num; - unlock_device(dev); + dev->dunlock(); return 1; } @@ -2415,6 +2799,8 @@ static void qfillcmd() memset(rec->data, i & 0xFF, i); rec->data_len = i; rewindcmd(); + init_speed(); + Pmsg1(0, _("Begin writing %d Bacula blocks to tape ...\n"), count); for (i=0; i < count; i++) { if (i % 100 == 0) { @@ -2431,8 +2817,9 @@ static void qfillcmd() } } printf("\n"); + print_speed(dev->VolCatInfo.VolCatBytes); weofcmd(); - if (dev_cap(dev, CAP_TWOEOF)) { + if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } rewindcmd(); @@ -2449,41 +2836,27 @@ static void rawfill_cmd() { DEV_BLOCK *block = dcr->block; int stat; - int fd; uint32_t block_num = 0; uint32_t *p; int my_errno; - uint32_t i; - fd = open("/dev/urandom", O_RDONLY); - if (fd) { - read(fd, block->buf, block->buf_len); - close(fd); - } else { - uint32_t *p = (uint32_t *)block->buf; - srandom(time(NULL)); - for (i=0; ibuf_len/sizeof(uint32_t); i++) { - p[i] = random(); - } - } + fill_buffer(FILL_RANDOM, block->buf, block->buf_len); + init_speed(); + p = (uint32_t *)block->buf; Pmsg1(0, _("Begin writing raw blocks of %u bytes.\n"), block->buf_len); for ( ;; ) { *p = block_num; - if (dev->is_tape()) { - stat = tape_write(dev->fd, block->buf, block->buf_len); - } else { - stat = write(dev->fd, block->buf, block->buf_len); - } + stat = dev->d_write(dev->fd(), block->buf, block->buf_len); if (stat == (int)block->buf_len) { if ((block_num++ % 100) == 0) { printf("+"); fflush(stdout); } - p[0] += p[13]; - for (i=1; i<(block->buf_len-sizeof(uint32_t))/sizeof(uint32_t)-1; i++) { - p[i] += p[i-1]; - } + + mix_buffer(FILL_RANDOM, block->buf, block->buf_len); + + jcr->JobBytes += stat; continue; } break; @@ -2492,65 +2865,19 @@ static void rawfill_cmd() printf("\n"); berrno be; printf(_("Write failed at block %u. stat=%d ERR=%s\n"), block_num, stat, - be.strerror(my_errno)); + be.bstrerror(my_errno)); + + print_speed(jcr->JobBytes); weofcmd(); } -/* - * Fill a tape using Bacula block writes - */ -static void bfill_cmd() -{ - DEV_BLOCK *block = dcr->block; - uint32_t block_num = 0; - uint32_t *p; - int my_errno; - int fd; - uint32_t i; - - fd = open("/dev/urandom", O_RDONLY); - if (fd) { - read(fd, block->buf, block->buf_len); - close(fd); - } else { - uint32_t *p = (uint32_t *)block->buf; - srandom(time(NULL)); - for (i=0; ibuf_len/sizeof(uint32_t); i++) { - p[i] = random(); - } - } - p = (uint32_t *)block->buf; - Pmsg1(0, _("Begin writing Bacula blocks of %u bytes.\n"), block->buf_len); - for ( ;; ) { - *p = block_num; - block->binbuf = block->buf_len; - block->bufp = block->buf + block->binbuf; - if (!write_block_to_dev(dcr)) { - break; - } - if ((block_num++ % 100) == 0) { - printf("+"); - fflush(stdout); - } - p[0] += p[13]; - for (i=1; i<(block->buf_len/sizeof(uint32_t)-1); i++) { - p[i] += p[i-1]; - } - } - my_errno = errno; - printf("\n"); - printf(_("Write failed at block %u.\n"), block_num); - weofcmd(); -} - struct cmdstruct { const char *key; void (*func)(); const char *help; }; static struct cmdstruct commands[] = { {NT_("autochanger"),autochangercmd, _("test autochanger")}, {NT_("bsf"), bsfcmd, _("backspace file")}, {NT_("bsr"), bsrcmd, _("backspace record")}, - {NT_("bfill"), bfill_cmd, _("fill tape using Bacula writes")}, {NT_("cap"), capcmd, _("list device capabilities")}, {NT_("clear"), clearcmd, _("clear tape errors")}, {NT_("eod"), eodcmd, _("go to end of Bacula data for append")}, @@ -2569,11 +2896,13 @@ static struct cmdstruct commands[] = { {NT_("rewind"), rewindcmd, _("rewind the tape")}, {NT_("scan"), scancmd, _("read() tape block by block to EOT and report")}, {NT_("scanblocks"),scan_blocks, _("Bacula read block by block to EOT and report")}, + {NT_("speed"), speed_test, _("[file_size=n(GB)|nb_file=3|skip_zero|skip_random|skip_raw|skip_block] report drive speed")}, {NT_("status"), statcmd, _("print tape status")}, {NT_("test"), testcmd, _("General test Bacula tape functions")}, {NT_("weof"), weofcmd, _("write an EOF on the tape")}, {NT_("wr"), wrcmd, _("write a single Bacula block")}, {NT_("rr"), rrcmd, _("read a single record")}, + {NT_("rb"), rbcmd, _("read a single Bacula block")}, {NT_("qfill"), qfillcmd, _("quick fill command")} }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) @@ -2594,8 +2923,8 @@ do_tape_cmds() found = true; break; } - if (!found) { - Pmsg1(0, _("\"%s\" is an illegal command\n"), cmd); + if (*cmd && !found) { + Pmsg1(0, _("\"%s\" is an invalid command\n"), cmd); } } } @@ -2614,17 +2943,18 @@ static void helpcmd() static void usage() { fprintf(stderr, _( -"Copyright (C) 2000-%s Kern Sibbald.\n" +PROG_COPYRIGHT "\nVersion: %s (%s)\n\n" "Usage: btape \n" " -b specify bootstrap file\n" " -c set configuration file to file\n" -" -d set debug level to nn\n" +" -d set debug level to \n" +" -dt print timestamp in debug output\n" " -p proceed inspite of I/O errors\n" " -s turn off signals\n" " -v be verbose\n" " -? print this message.\n" -"\n"), BYEAR, VERSION, BDATE); +"\n"), 2000, VERSION, BDATE); } @@ -2638,7 +2968,8 @@ get_cmd(const char *prompt) { int i = 0; int ch; - fprintf(stdout, prompt); + + fprintf(stdout, "%s", prompt); /* We really should turn off echoing and pretty this * up a bit. @@ -2649,8 +2980,9 @@ get_cmd(const char *prompt) strip_trailing_junk(cmd); return 1; } else if (ch == 4 || ch == 0xd3 || ch == 0x8) { - if (i > 0) + if (i > 0) { cmd[--i] = 0; + } continue; } @@ -2665,7 +2997,7 @@ get_cmd(const char *prompt) bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;} bool dir_send_job_status(JCR *jcr) {return 1;} -bool dir_update_volume_info(DCR *dcr, bool relabel) +bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; } @@ -2674,11 +3006,11 @@ bool dir_update_volume_info(DCR *dcr, bool relabel) bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { Dmsg0(20, "Enter dir_get_volume_info\n"); - bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName)); + dcr->setVolCatName(dcr->VolumeName); return 1; } -bool dir_create_jobmedia_record(DCR *dcr) +bool dir_create_jobmedia_record(DCR *dcr, bool zero) { dcr->WroteVol = false; return 1; @@ -2691,7 +3023,7 @@ bool dir_find_next_appendable_volume(DCR *dcr) return dcr->VolumeName[0] != 0; } -bool dir_ask_sysop_to_mount_volume(DCR *dcr) +bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /* mode */) { DEVICE *dev = dcr->dev; Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n"); @@ -2713,7 +3045,7 @@ bool dir_ask_sysop_to_mount_volume(DCR *dcr) bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { - bool autochanger; + int autochanger; DEVICE *dev = dcr->dev; Dmsg0(20, "Enter dir_ask_sysop_to_create_appendable_volume\n"); if (stop == 0) { @@ -2722,17 +3054,18 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) set_volume_name("TestVolume2", 2); } /* Close device so user can use autochanger if desired */ - if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { + if (dev->has_cap(CAP_OFFLINEUNMOUNT)) { dev->offline(); } autochanger = autoload_device(dcr, 1, NULL); - if (!autochanger) { + if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); fprintf(stderr, _("Mount blank Volume on device %s and press return when ready: "), dev->print_name()); dev->close(); getchar(); + Pmsg0(000, "\n"); } - open_device(dcr); labelcmd(); VolumeName = NULL; BlockNumber = 0; @@ -2741,7 +3074,8 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) static bool my_mount_next_read_volume(DCR *dcr) { - char ec1[50]; + char ec1[50], ec2[50]; + uint64_t rate; JCR *jcr = dcr->jcr; DEV_BLOCK *block = dcr->block; @@ -2749,6 +3083,7 @@ static bool my_mount_next_read_volume(DCR *dcr) Pmsg2(000, _("End of Volume \"%s\" %d records.\n"), dcr->VolumeName, quickie_count); + volume_unused(dcr); /* release current volume */ if (LastBlock != block->BlockNumber) { VolBytes += block->block_len; } @@ -2758,19 +3093,18 @@ static bool my_mount_next_read_volume(DCR *dcr) if (now <= 0) { now = 1; } - kbs = (double)VolBytes / (1000.0 * (double)now); - Pmsg3(-1, _("Read block=%u, VolBytes=%s rate=%.1f KB/s\n"), block->BlockNumber, - edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + rate = VolBytes / now; + Pmsg3(-1, _("Read block=%u, VolBytes=%s rate=%sB/s\n"), block->BlockNumber, + edit_uint64_with_commas(VolBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); if (strcmp(dcr->VolumeName, "TestVolume2") == 0) { end_of_tape = 1; return false; } - free_restore_volume_list(jcr); set_volume_name("TestVolume2", 2); - jcr->bsr = NULL; - create_restore_volume_list(jcr); + dev->close(); if (!acquire_device_for_read(dcr)) { Pmsg2(0, _("Cannot open Dev=%s, Vol=%s\n"), dev->print_name(), dcr->VolumeName); @@ -2784,8 +3118,9 @@ static void set_volume_name(const char *VolName, int volnum) DCR *dcr = jcr->dcr; VolumeName = VolName; vol_num = volnum; - bstrncpy(dev->VolCatInfo.VolCatName, VolName, sizeof(dev->VolCatInfo.VolCatName)); - bstrncpy(dcr->VolCatInfo.VolCatName, VolName, sizeof(dcr->VolCatInfo.VolCatName)); + dev->setVolCatName(VolName); + dcr->setVolCatName(VolName); bstrncpy(dcr->VolumeName, VolName, sizeof(dcr->VolumeName)); dcr->VolCatInfo.Slot = volnum; + dcr->VolCatInfo.InChanger = true; }