bool get_bootstrap_file(JCR *jcr, BSOCK *sock) { POOLMEM *fname = get_pool_memory(PM_FNAME); FILE *bs; bool ok = false; if (jcr->RestoreBootstrap) { unlink(jcr->RestoreBootstrap); free_pool_memory(jcr->RestoreBootstrap); } P(bsr_mutex); bsr_uniq++; Mmsg(fname, "%s/%s.%s.%d.bootstrap", me->working_directory, me->hdr.name, jcr->Job, bsr_uniq); V(bsr_mutex); Dmsg1(400, "bootstrap=%s\n", fname); jcr->RestoreBootstrap = fname; bs = fopen(fname, "a+b"); /* create file */ if (!bs) { berrno be; Jmsg(jcr, M_FATAL, 0, _("Could not create bootstrap file %s: ERR=%s\n"), jcr->RestoreBootstrap, be.bstrerror()); goto bail_out; } Dmsg0(10, "=== Bootstrap file ===\n"); while (sock->recv() >= 0) { Dmsg1(10, "%s", sock->msg); fputs(sock->msg, bs); } fclose(bs); Dmsg0(10, "=== end bootstrap file ===\n"); jcr->bsr = parse_bsr(jcr, jcr->RestoreBootstrap); if (!jcr->bsr) { Jmsg(jcr, M_FATAL, 0, _("Error parsing bootstrap file.\n")); goto bail_out; } if (debug_level >= 10) { dump_bsr(jcr->bsr, true); } /* If we got a bootstrap, we are reading, so create read volume list */ create_restore_volume_list(jcr); ok = true; bail_out: unlink(jcr->RestoreBootstrap); free_pool_memory(jcr->RestoreBootstrap); jcr->RestoreBootstrap = NULL; if (!ok) { sock->fsend(ERROR_bootstrap); return false; } return sock->fsend(OK_bootstrap); }
int main (int argc, char *argv[]) { int i, ch; FILE *fd; char line[1000]; char *VolumeName = NULL; char *bsrName = NULL; char *DirectorName = NULL; bool ignore_label_errors = false; DIRRES *director = NULL; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); lmgr_init_thread(); working_directory = "/tmp"; my_name_is(argc, argv, "bls"); init_msg(NULL, NULL); /* initialize message handler */ OSDependentInit(); ff = init_find_files(); while ((ch = getopt(argc, argv, "b:c:D:d:e:i:jkLpvV:?")) != -1) { switch (ch) { case 'b': bsrName = optarg; break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'D': /* specify director name */ if (DirectorName != NULL) { free(DirectorName); } DirectorName = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'e': /* exclude list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(100, "add_exclude %s\n", line); add_fname_to_exclude_list(ff, line); } fclose(fd); break; case 'i': /* include list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(100, "add_include %s\n", line); add_fname_to_include_list(ff, 0, line); } fclose(fd); break; case 'j': list_jobs = true; break; case 'k': list_blocks = true; break; case 'L': dump_label = true; break; case 'p': ignore_label_errors = true; forge_on = true; break; case 'v': verbose++; break; case 'V': /* Volume name */ VolumeName = optarg; break; case '?': default: usage(); } /* end switch */ } /* end while */ argc -= optind; argv += optind; if (!argc) { Pmsg0(0, _("No archive name specified\n")); usage(); } if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } my_config = new_config_parser(); parse_sd_config(my_config, configfile, M_ERROR_TERM); LockRes(); me = (STORES *)GetNextRes(R_STORAGE, NULL); if (!me) { UnlockRes(); Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"), configfile); } UnlockRes(); if (DirectorName) { foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, DirectorName)) { break; } } if (!director) { Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"), DirectorName, configfile); } } load_sd_plugins(me->plugin_directory, me->plugin_names); read_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->SDaddrs)); if (ff->included_files_list == NULL) { add_fname_to_include_list(ff, 0, "/"); } for (i=0; i < argc; i++) { if (bsrName) { bsr = parse_bsr(NULL, bsrName); } jcr = setup_jcr("bls", argv[i], bsr, director, VolumeName, 1); /* acquire for read */ if (!jcr) { exit(1); } jcr->ignore_label_errors = ignore_label_errors; dev = jcr->dcr->dev; if (!dev) { exit(1); } dcr = jcr->dcr; rec = new_record(); attr = new_attr(jcr); /* * Assume that we have already read the volume label. * If on second or subsequent volume, adjust buffer pointer */ if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */ Pmsg1(0, _("\n" "Warning, this Volume is a continuation of Volume %s\n"), dev->VolHdr.PrevVolumeName); } if (list_blocks) { do_blocks(argv[i]); } else if (list_jobs) { do_jobs(argv[i]); } else { do_ls(argv[i]); } do_close(jcr); } if (bsr) { free_bsr(bsr); } term_include_exclude_files(ff); term_find_files(ff); return 0; }
/* * The bootstrap is stored in a file, so open the file, and loop * through it processing each storage device in turn. If the * storage is different from the prior one, we open a new connection * to the new storage and do a restore for that part. * * This permits handling multiple storage daemons for a single * restore. E.g. your Full is stored on tape, and Incrementals * on disk. */ static inline bool do_ndmp_restore_bootstrap(JCR *jcr) { int cnt; BSOCK *sd; BSR *bsr; NIS *nis = NULL; int32_t current_fi; bootstrap_info info; BSR_FINDEX *fileindex; struct ndm_session ndmp_sess; struct ndm_job_param ndmp_job; bool session_initialized = false; bool retval = false; int NdmpLoglevel; if (jcr->res.client->ndmp_loglevel > me->ndmp_loglevel) { NdmpLoglevel = jcr->res.client->ndmp_loglevel; } else { NdmpLoglevel = me->ndmp_loglevel; } /* * We first parse the BSR ourself so we know what to restore. */ jcr->bsr = parse_bsr(jcr, jcr->RestoreBootstrap); if (!jcr->bsr) { Jmsg(jcr, M_FATAL, 0, _("Error parsing bootstrap file.\n")); goto bail_out; } /* * Setup all paired read storage. */ set_paired_storage(jcr); if (!jcr->res.pstore) { Jmsg(jcr, M_FATAL, 0, _("Read storage %s doesn't point to storage definition with paired storage option.\n"), jcr->res.rstore->name()); goto bail_out; } /* * Open the bootstrap file */ if (!open_bootstrap_file(jcr, info)) { goto bail_out; } nis = (NIS *)malloc(sizeof(NIS)); memset(nis, 0, sizeof(NIS)); /* * Read the bootstrap file */ bsr = jcr->bsr; while (!feof(info.bs)) { if (!select_next_rstore(jcr, info)) { goto cleanup; } /* * Initialize the ndmp restore job. We build the generic job once per storage daemon * and reuse the job definition for each seperate sub-restore we perform as * part of the whole job. We only free the env_table between every sub-restore. */ if (!ndmp_build_client_job(jcr, jcr->res.client, jcr->res.pstore, NDM_JOB_OP_EXTRACT, &ndmp_job)) { goto cleanup; } /* * Open a message channel connection with the Storage * daemon. This is to let him know that our client * will be contacting him for a backup session. * */ Dmsg0(10, "Open connection with storage daemon\n"); jcr->setJobStatus(JS_WaitSD); /* * Start conversation with Storage daemon */ if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) { goto cleanup; } sd = jcr->store_bsock; /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->res.rstorage, NULL)) { goto cleanup; } jcr->setJobStatus(JS_Running); /* * Send the bootstrap file -- what Volumes/files to restore */ if (!send_bootstrap_file(jcr, sd, info) || !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { goto cleanup; } if (!sd->fsend("run")) { goto cleanup; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { goto cleanup; } Dmsg0(50, "Storage daemon connection OK\n"); /* * Walk over each bsr record */ cnt = 0; for (bsr = jcr->bsr; bsr; bsr = bsr->next) { /* * Walk each fileindex of the current BSR record. Each different fileindex is * a separate NDMP stream. */ for (fileindex = bsr->FileIndex; fileindex; fileindex = fileindex->next) { for (current_fi = fileindex->findex; current_fi <= fileindex->findex2; current_fi++) { /* * See if this is the first Restore NDMP stream or not. For NDMP we can have multiple Backup * runs as part of the same Job. When we are restoring data from a Native Storage Daemon * we let it know to expect a next restore session. It will generate a new authorization * key so we wait for the nextrun_ready conditional variable to be raised by the msg_thread. */ if (jcr->store_bsock && cnt > 0) { jcr->store_bsock->fsend("nextrun"); P(mutex); pthread_cond_wait(&jcr->nextrun_ready, &mutex); V(mutex); } /* * Perform the actual NDMP job. * Initialize a new NDMP session */ memset(&ndmp_sess, 0, sizeof(ndmp_sess)); ndmp_sess.conn_snooping = (me->ndmp_snooping) ? 1 : 0; ndmp_sess.control_agent_enabled = 1; ndmp_sess.param = (struct ndm_session_param *)malloc(sizeof(struct ndm_session_param)); memset(ndmp_sess.param, 0, sizeof(struct ndm_session_param)); ndmp_sess.param->log.deliver = ndmp_loghandler; ndmp_sess.param->log_level = native_to_ndmp_loglevel(NdmpLoglevel, debug_level, nis); nis->jcr = jcr; ndmp_sess.param->log.ctx = nis; ndmp_sess.param->log_tag = bstrdup("DIR-NDMP"); /* * Initialize the session structure. */ if (ndma_session_initialize(&ndmp_sess)) { goto cleanup_ndmp; } session_initialized = true; /* * Copy the actual job to perform. */ jcr->jr.FileIndex = current_fi; if (bsr->sessid && bsr->sesstime) { jcr->jr.VolSessionId = bsr->sessid->sessid; jcr->jr.VolSessionTime = bsr->sesstime->sesstime; } else { Jmsg(jcr, M_FATAL, 0, _("Wrong BSR missing sessid and/or sesstime\n")); goto cleanup_ndmp; } memcpy(&ndmp_sess.control_acb->job, &ndmp_job, sizeof(struct ndm_job_param)); if (!fill_restore_environment(jcr, current_fi, &ndmp_sess.control_acb->job)) { Jmsg(jcr, M_ERROR, 0, _("ERROR in fill_restore_environment\n")); goto cleanup_ndmp; } ndma_job_auto_adjust(&ndmp_sess.control_acb->job); if (!ndmp_validate_job(jcr, &ndmp_sess.control_acb->job)) { Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmp_validate_job\n")); goto cleanup_ndmp; } /* * Commission the session for a run. */ if (ndma_session_commission(&ndmp_sess)) { Jmsg(jcr, M_ERROR, 0, _("ERROR in ndma_session_commission\n")); goto cleanup_ndmp; } /* * Setup the DMA. */ if (ndmca_connect_control_agent(&ndmp_sess)) { Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmca_connect_control_agent\n")); goto cleanup_ndmp; } ndmp_sess.conn_open = 1; ndmp_sess.conn_authorized = 1; /* * Let the DMA perform its magic. */ if (ndmca_control_agent(&ndmp_sess) != 0) { Jmsg(jcr, M_ERROR, 0, _("ERROR in ndmca_control_agent\n")); goto cleanup_ndmp; } /* * See if there were any errors during the restore. */ if (!extract_post_restore_stats(jcr, &ndmp_sess)) { Jmsg(jcr, M_ERROR, 0, _("ERROR in extract_post_restore_stats\n")); goto cleanup_ndmp; } /* * Reset the NDMP session states. */ ndma_session_decommission(&ndmp_sess); /* * Cleanup the job after it has run. */ ndma_destroy_env_list(&ndmp_sess.control_acb->job.env_tab); ndma_destroy_env_list(&ndmp_sess.control_acb->job.result_env_tab); ndma_destroy_nlist(&ndmp_sess.control_acb->job.nlist_tab); /* * Release any tape device name allocated. */ if (ndmp_sess.control_acb->job.tape_device) { free(ndmp_sess.control_acb->job.tape_device); ndmp_sess.control_acb->job.tape_device = NULL; } /* * Destroy the session. */ ndma_session_destroy(&ndmp_sess); /* * Free the param block. */ free(ndmp_sess.param->log_tag); free(ndmp_sess.param); ndmp_sess.param = NULL; /* * Reset the initialized state so we don't try to cleanup again. */ session_initialized = false; /* * Keep track that we finished this part of the restore. */ cnt++; } } } /* * Tell the storage daemon we are done. */ jcr->store_bsock->fsend("finish"); wait_for_storage_daemon_termination(jcr); } /* * Jump to the generic cleanup done for every Job. */ retval = true; goto cleanup; cleanup_ndmp: /* * Only need to cleanup when things are initialized. */ if (session_initialized) { ndma_destroy_env_list(&ndmp_sess.control_acb->job.env_tab); ndma_destroy_env_list(&ndmp_sess.control_acb->job.result_env_tab); ndma_destroy_nlist(&ndmp_sess.control_acb->job.nlist_tab); if (ndmp_sess.control_acb->job.tape_device) { free(ndmp_sess.control_acb->job.tape_device); } /* * Destroy the session. */ ndma_session_destroy(&ndmp_sess); } if (ndmp_sess.param) { free(ndmp_sess.param->log_tag); free(ndmp_sess.param); } cleanup: if (nis) { free(nis); } free_paired_storage(jcr); close_bootstrap_file(info); bail_out: free_tree(jcr->restore_tree_root); jcr->restore_tree_root = NULL; return retval; }
int main (int argc, char *argv[]) { int ch; FILE *fd; char line[1000]; bool got_inc = false; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); lmgr_init_thread(); working_directory = "/tmp"; my_name_is(argc, argv, "bextract"); init_msg(NULL, NULL); /* setup message handler */ OSDependentInit(); ff = init_find_files(); binit(&bfd); while ((ch = getopt(argc, argv, "b:c:D:d:e:i:pvV:?")) != -1) { switch (ch) { case 'b': /* bootstrap file */ bsr = parse_bsr(NULL, optarg); // dump_bsr(bsr, true); break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'D': /* specify director name */ if (DirectorName != NULL) { free(DirectorName); } DirectorName = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'e': /* exclude list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(900, "add_exclude %s\n", line); add_fname_to_exclude_list(ff, line); } fclose(fd); break; case 'i': /* include list */ if ((fd = fopen(optarg, "rb")) == NULL) { berrno be; Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"), optarg, be.bstrerror()); exit(1); } while (fgets(line, sizeof(line), fd) != NULL) { strip_trailing_junk(line); Dmsg1(900, "add_include %s\n", line); add_fname_to_include_list(ff, 0, line); } fclose(fd); got_inc = true; break; case 'p': forge_on = true; break; case 'v': verbose++; break; case 'V': /* Volume name */ VolumeName = optarg; break; case '?': default: usage(); } /* end switch */ } /* end while */ argc -= optind; argv += optind; if (argc != 2) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } config = new_config_parser(); parse_sd_config(config, configfile, M_ERROR_TERM); LockRes(); me = (STORES *)GetNextRes(R_STORAGE, NULL); if (!me) { UnlockRes(); Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"), configfile); } UnlockRes(); if (DirectorName) { foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, DirectorName)) { break; } } if (!director) { Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"), DirectorName, configfile); } } load_sd_plugins(me->plugin_directory); read_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->sdaddrs)); if (!got_inc) { /* If no include file, */ add_fname_to_include_list(ff, 0, "/"); /* include everything */ } where = argv[1]; do_extract(argv[0]); if (bsr) { free_bsr(bsr); } if (prog_name_msg) { Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"), prog_name_msg); } if (win32_data_msg) { Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"), win32_data_msg); } term_include_exclude_files(ff); term_find_files(ff); return 0; }
int main (int argc, char *argv[]) { int ch; char *iVolumeName = NULL; char *oVolumeName = NULL; bool ignore_label_errors = false; bool ok; setlocale(LC_ALL, ""); bindtextdomain("bacula", LOCALEDIR); textdomain("bacula"); init_stack_dump(); my_name_is(argc, argv, "bcopy"); init_msg(NULL, NULL); while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) { switch (ch) { case 'b': bsr = parse_bsr(NULL, optarg); break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'i': /* input Volume name */ iVolumeName = optarg; break; case 'o': /* output Volume name */ oVolumeName = optarg; break; case 'p': ignore_label_errors = true; forge_on = true; break; case 'v': verbose++; break; case 'w': wd = optarg; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 2) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } OSDependentInit(); working_directory = wd; if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } config = new_config_parser(); parse_sd_config(config, configfile, M_ERROR_TERM); /* Setup and acquire input device for reading */ Dmsg0(100, "About to setup input jcr\n"); in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */ if (!in_jcr) { exit(1); } in_jcr->ignore_label_errors = ignore_label_errors; in_dev = in_jcr->dcr->dev; if (!in_dev) { exit(1); } /* Setup output device for writing */ Dmsg0(100, "About to setup output jcr\n"); out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */ if (!out_jcr) { exit(1); } out_dev = out_jcr->dcr->dev; if (!out_dev) { exit(1); } Dmsg0(100, "About to acquire device for writing\n"); /* For we must now acquire the device for writing */ out_dev->r_dlock(); if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg); out_dev->dunlock(); exit(1); } out_dev->dunlock(); if (!acquire_device_for_append(out_jcr->dcr)) { free_jcr(in_jcr); exit(1); } out_block = out_jcr->dcr->block; ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume); if (ok || out_dev->can_write()) { if (!write_block_to_device(out_jcr->dcr)) { Pmsg0(000, _("Write of last block failed.\n")); } } Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records); free_jcr(in_jcr); free_jcr(out_jcr); in_dev->term(); out_dev->term(); return 0; }
int main (int argc, char *argv[]) { int ch; bool ok; char *iVolumeName = NULL; char *oVolumeName = NULL; char *DirectorName = NULL; DIRRES *director = NULL; bool ignore_label_errors = false; DCR *in_dcr, *out_dcr; setlocale(LC_ALL, ""); bindtextdomain("bareos", LOCALEDIR); textdomain("bareos"); init_stack_dump(); my_name_is(argc, argv, "bcopy"); lmgr_init_thread(); init_msg(NULL, NULL); while ((ch = getopt(argc, argv, "b:c:D:d:i:o:pvw:?")) != -1) { switch (ch) { case 'b': bsr = parse_bsr(NULL, optarg); break; case 'c': /* specify config file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'D': /* specify director name */ if (DirectorName != NULL) { free(DirectorName); } DirectorName = bstrdup(optarg); break; case 'd': /* debug level */ if (*optarg == 't') { dbg_timestamp = true; } else { debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } } break; case 'i': /* input Volume name */ iVolumeName = optarg; break; case 'o': /* output Volume name */ oVolumeName = optarg; break; case 'p': ignore_label_errors = true; forge_on = true; break; case 'v': verbose++; break; case 'w': wd = optarg; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 2) { Pmsg0(0, _("Wrong number of arguments: \n")); usage(); } OSDependentInit(); working_directory = wd; if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } my_config = new_config_parser(); parse_sd_config(my_config, configfile, M_ERROR_TERM); if (DirectorName) { foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, DirectorName)) { break; } } if (!director) { Emsg2(M_ERROR_TERM, 0, _("No Director resource named %s defined in %s. Cannot continue.\n"), DirectorName, configfile); } } load_sd_plugins(me->plugin_directory, me->plugin_names); read_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->SDaddrs)); /* * Setup and acquire input device for reading */ Dmsg0(100, "About to setup input jcr\n"); in_dcr = New(DCR); in_jcr = setup_jcr("bcopy", argv[0], bsr, director, in_dcr, iVolumeName, true); /* read device */ if (!in_jcr) { exit(1); } in_jcr->ignore_label_errors = ignore_label_errors; in_dev = in_jcr->dcr->dev; if (!in_dev) { exit(1); } /* * Setup output device for writing */ Dmsg0(100, "About to setup output jcr\n"); out_dcr = New(DCR); out_jcr = setup_jcr("bcopy", argv[1], bsr, director, out_dcr, oVolumeName, false); /* write device */ if (!out_jcr) { exit(1); } out_dev = out_jcr->dcr->dev; if (!out_dev) { exit(1); } Dmsg0(100, "About to acquire device for writing\n"); /* * For we must now acquire the device for writing */ out_dev->rLock(false); if (!out_dev->open(out_jcr->dcr, OPEN_READ_WRITE)) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg); out_dev->Unlock(); exit(1); } out_dev->Unlock(); if (!acquire_device_for_append(out_jcr->dcr)) { free_jcr(in_jcr); exit(1); } out_block = out_jcr->dcr->block; ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume); if (ok || out_dev->can_write()) { if (!out_jcr->dcr->write_block_to_device()) { Pmsg0(000, _("Write of last block failed.\n")); } } Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records); in_dev->term(); out_dev->term(); free_jcr(in_jcr); free_jcr(out_jcr); return 0; }