2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 * Program to copy a Bacula from one volume to another.
20 * Written by Kern E. Sibbald, October 2002
27 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
29 /* Forward referenced functions */
30 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
31 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
34 /* Global variables */
35 static DEVICE *in_dev = NULL;
36 static DEVICE *out_dev = NULL;
37 static JCR *in_jcr; /* input jcr */
38 static JCR *out_jcr; /* output jcr */
39 static BSR *bsr = NULL;
40 static const char *wd = "/tmp";
41 static bool list_records = false;
42 static uint32_t records = 0;
43 static uint32_t jobs = 0;
44 static DEV_BLOCK *out_block;
45 static SESSION_LABEL sessrec;
47 static CONFIG *config;
48 #define CONFIG_FILE "bacula-sd.conf"
51 char *configfile = NULL;
52 STORES *me = NULL; /* our Global resource */
53 bool forge_on = false; /* proceed inspite of I/O errors */
54 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
62 "\nVersion: %s (%s)\n\n"
63 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
64 " -b bootstrap specify a bootstrap file\n"
65 " -c <file> specify a Storage configuration file\n"
66 " -d <nn> set debug level to <nn>\n"
67 " -dt print timestamp in debug output\n"
68 " -i specify input Volume names (separated by |)\n"
69 " -o specify output Volume names (separated by |)\n"
70 " -p proceed inspite of errors\n"
72 " -w <dir> specify working directory (default /tmp)\n"
73 " -? print this message\n\n"), 2002, VERSION, BDATE);
77 int main (int argc, char *argv[])
80 char *iVolumeName = NULL;
81 char *oVolumeName = NULL;
82 bool ignore_label_errors = false;
85 setlocale(LC_ALL, "");
86 bindtextdomain("bacula", LOCALEDIR);
90 my_name_is(argc, argv, "bcopy");
94 while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
97 bsr = parse_bsr(NULL, optarg);
100 case 'c': /* specify config file */
101 if (configfile != NULL) {
104 configfile = bstrdup(optarg);
107 case 'd': /* debug level */
108 if (*optarg == 't') {
109 dbg_timestamp = true;
111 debug_level = atoi(optarg);
112 if (debug_level <= 0) {
118 case 'i': /* input Volume name */
119 iVolumeName = optarg;
122 case 'o': /* output Volume name */
123 oVolumeName = optarg;
127 ignore_label_errors = true;
149 Pmsg0(0, _("Wrong number of arguments: \n"));
155 working_directory = wd;
157 if (configfile == NULL) {
158 configfile = bstrdup(CONFIG_FILE);
161 config = new_config_parser();
162 parse_sd_config(config, configfile, M_ERROR_TERM);
164 load_sd_plugins(me->plugin_directory);
166 /* Setup and acquire input device for reading */
167 Dmsg0(100, "About to setup input jcr\n");
168 in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, SD_READ); /* read device */
172 in_jcr->ignore_label_errors = ignore_label_errors;
173 in_dev = in_jcr->dcr->dev;
178 /* Setup output device for writing */
179 Dmsg0(100, "About to setup output jcr\n");
180 out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, SD_APPEND); /* no acquire */
184 out_dev = out_jcr->dcr->dev;
188 Dmsg0(100, "About to acquire device for writing\n");
189 /* For we must now acquire the device for writing */
190 out_dev->rLock(false);
191 if (!out_dev->open(out_jcr->dcr, OPEN_READ_WRITE)) {
192 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
197 if (!acquire_device_for_append(out_jcr->dcr)) {
201 out_block = out_jcr->dcr->block;
203 ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
205 if (ok || out_dev->can_write()) {
206 if (!out_jcr->dcr->write_block_to_device()) {
207 Pmsg0(000, _("Write of last block failed.\n"));
211 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
223 * read_records() calls back here for each record it gets
225 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
228 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
229 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
230 rec->Stream, rec->data_len);
233 * Check for Start or End of Session Record
236 if (rec->FileIndex < 0) {
237 get_session_record(in_dcr->dev, rec, &sessrec);
240 dump_label_record(in_dcr->dev, rec, 1);
242 switch (rec->FileIndex) {
244 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
247 Pmsg0(000, _("Volume label not copied.\n"));
250 if (bsr && rec->match_stat < 1) {
251 /* Skipping record, because does not match BSR filter */
253 Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
260 if (bsr && rec->match_stat < 1) {
261 /* Skipping record, because does not match BSR filter */
264 while (!write_record_to_block(out_jcr->dcr, rec)) {
265 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
267 if (!out_jcr->dcr->write_block_to_device()) {
268 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
269 out_dev->print_name(), out_dev->bstrerror());
270 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
271 out_dev->bstrerror());
275 if (!out_jcr->dcr->write_block_to_device()) {
276 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
277 out_dev->print_name(), out_dev->bstrerror());
278 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
279 out_dev->bstrerror());
284 Pmsg0(000, _("EOM label not copied.\n"));
286 case EOT_LABEL: /* end of all tapes */
287 Pmsg0(000, _("EOT label not copied.\n"));
295 if (bsr && rec->match_stat < 1) {
296 /* Skipping record, because does not match BSR filter */
300 while (!write_record_to_block(out_jcr->dcr, rec)) {
301 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
303 if (!out_jcr->dcr->write_block_to_device()) {
304 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
305 out_dev->print_name(), out_dev->bstrerror());
306 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
307 out_dev->bstrerror());
314 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
317 memset(sessrec, 0, sizeof(SESSION_LABEL));
318 switch (rec->FileIndex) {
320 rtype = _("Fresh Volume Label");
323 rtype = _("Volume Label");
324 unser_volume_label(dev, rec);
327 rtype = _("Begin Job Session");
328 unser_session_label(sessrec, rec);
331 rtype = _("End Job Session");
332 unser_session_label(sessrec, rec);
336 rtype = _("End of Medium");
339 rtype = _("Unknown");
342 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
343 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
345 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
346 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
351 /* Dummies to replace askdir.c */
352 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
353 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
354 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
355 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
356 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
357 bool dir_send_job_status(JCR *jcr) {return 1;}
360 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
362 DEVICE *dev = dcr->dev;
363 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
364 dcr->VolumeName, dev->print_name());
370 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
372 Dmsg0(100, "Fake dir_get_volume_info\n");
373 dcr->setVolCatName(dcr->VolumeName);
375 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
377 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);