]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bcopy.c
Fix bug #1208
[bacula/bacula] / bacula / src / stored / bcopy.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *  Program to copy a Bacula from one volume to another.
31  *
32  *   Kern E. Sibbald, October 2002
33  *
34  *
35  *   Version $Id$
36  */
37
38 #include "bacula.h"
39 #include "stored.h"
40
41 /* Dummy functions */
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
43 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
44
45 /* Forward referenced functions */
46 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
47 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
48
49
50 /* Global variables */
51 static DEVICE *in_dev = NULL;
52 static DEVICE *out_dev = NULL;
53 static JCR *in_jcr;                    /* input jcr */
54 static JCR *out_jcr;                   /* output jcr */
55 static BSR *bsr = NULL;
56 static const char *wd = "/tmp";
57 static bool list_records = false;
58 static uint32_t records = 0;
59 static uint32_t jobs = 0;
60 static DEV_BLOCK *out_block;
61 static SESSION_LABEL sessrec;
62
63 static CONFIG *config;
64 #define CONFIG_FILE "bacula-sd.conf"
65 char *configfile = NULL;
66 STORES *me = NULL;                    /* our Global resource */
67 bool forge_on = false;                /* proceed inspite of I/O errors */
68 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
70
71
72 static void usage()
73 {
74    fprintf(stderr, _(
75 PROG_COPYRIGHT
76 "\nVersion: %s (%s)\n\n"
77 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
78 "       -b bootstrap      specify a bootstrap file\n"
79 "       -c <file>         specify a Storage configuration file\n"
80 "       -d <nn>           set debug level to <nn>\n"
81 "       -dt               print timestamp in debug output\n"
82 "       -i                specify input Volume names (separated by |)\n"
83 "       -o                specify output Volume names (separated by |)\n"
84 "       -p                proceed inspite of errors\n"
85 "       -v                verbose\n"
86 "       -w <dir>          specify working directory (default /tmp)\n"
87 "       -?                print this message\n\n"), 2002, VERSION, BDATE);
88    exit(1);
89 }
90
91 int main (int argc, char *argv[])
92 {
93    int ch;
94    char *iVolumeName = NULL;
95    char *oVolumeName = NULL;
96    bool ignore_label_errors = false;
97    bool ok;
98
99    setlocale(LC_ALL, "");
100    bindtextdomain("bacula", LOCALEDIR);
101    textdomain("bacula");
102    init_stack_dump();
103
104    my_name_is(argc, argv, "bcopy");
105    init_msg(NULL, NULL);
106
107    while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
108       switch (ch) {
109       case 'b':
110          bsr = parse_bsr(NULL, optarg);
111          break;
112
113       case 'c':                    /* specify config file */
114          if (configfile != NULL) {
115             free(configfile);
116          }
117          configfile = bstrdup(optarg);
118          break;
119
120       case 'd':                    /* debug level */
121          if (*optarg == 't') {
122             dbg_timestamp = true;
123          } else {
124             debug_level = atoi(optarg);
125             if (debug_level <= 0) {
126                debug_level = 1;
127             }
128          }
129          break;
130
131       case 'i':                    /* input Volume name */
132          iVolumeName = optarg;
133          break;
134
135       case 'o':                    /* output Volume name */
136          oVolumeName = optarg;
137          break;
138
139       case 'p':
140          ignore_label_errors = true;
141          forge_on = true;
142          break;
143
144       case 'v':
145          verbose++;
146          break;
147
148       case 'w':
149          wd = optarg;
150          break;
151
152       case '?':
153       default:
154          usage();
155
156       }
157    }
158    argc -= optind;
159    argv += optind;
160
161    if (argc != 2) {
162       Pmsg0(0, _("Wrong number of arguments: \n"));
163       usage();
164    }
165
166    OSDependentInit();
167
168    working_directory = wd;
169
170    if (configfile == NULL) {
171       configfile = bstrdup(CONFIG_FILE);
172    }
173
174    config = new_config_parser();
175    parse_sd_config(config, configfile, M_ERROR_TERM);
176
177    /* Setup and acquire input device for reading */
178    Dmsg0(100, "About to setup input jcr\n");
179    in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
180    if (!in_jcr) {
181       exit(1);
182    }
183    in_jcr->ignore_label_errors = ignore_label_errors;
184    in_dev = in_jcr->dcr->dev;
185    if (!in_dev) {
186       exit(1);
187    }
188
189    /* Setup output device for writing */
190    Dmsg0(100, "About to setup output jcr\n");
191    out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
192    if (!out_jcr) {
193       exit(1);
194    }
195    out_dev = out_jcr->dcr->dev;
196    if (!out_dev) {
197       exit(1);
198    }
199    Dmsg0(100, "About to acquire device for writing\n");
200    /* For we must now acquire the device for writing */
201    out_dev->r_dlock();
202    if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
203       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
204       out_dev->dunlock();
205       exit(1);
206    }
207    out_dev->dunlock();
208    if (!acquire_device_for_append(out_jcr->dcr)) {
209       free_jcr(in_jcr);
210       exit(1);
211    }
212    out_block = out_jcr->dcr->block;
213
214    ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
215
216    if (ok || out_dev->can_write()) {
217       if (!write_block_to_device(out_jcr->dcr)) {
218          Pmsg0(000, _("Write of last block failed.\n"));
219       }
220    }
221
222    Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
223
224    free_jcr(in_jcr);
225    free_jcr(out_jcr);
226
227    in_dev->term();
228    out_dev->term();
229    return 0;
230 }
231
232
233 /*
234  * read_records() calls back here for each record it gets
235  */
236 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
237 {
238    if (list_records) {
239       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
240             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
241             rec->Stream, rec->data_len);
242    }
243    /*
244     * Check for Start or End of Session Record
245     *
246     */
247    if (rec->FileIndex < 0) {
248       get_session_record(in_dcr->dev, rec, &sessrec);
249
250       if (verbose > 1) {
251          dump_label_record(in_dcr->dev, rec, 1);
252       }
253       switch (rec->FileIndex) {
254       case PRE_LABEL:
255          Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
256          return false;
257       case VOL_LABEL:
258          Pmsg0(000, _("Volume label not copied.\n"));
259          return true;
260       case SOS_LABEL:
261          if (bsr && rec->match_stat < 1) {
262             /* Skipping record, because does not match BSR filter */
263             if (verbose) {
264              Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
265             }
266          } else {
267             jobs++;
268          }
269          break;
270       case EOS_LABEL:
271          if (bsr && rec->match_stat < 1) {
272             /* Skipping record, because does not match BSR filter */
273            return true;
274         }
275          while (!write_record_to_block(out_block, rec)) {
276             Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
277                        rec->remainder);
278             if (!write_block_to_device(out_jcr->dcr)) {
279                Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
280                   out_dev->print_name(), out_dev->bstrerror());
281                Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
282                      out_dev->bstrerror());
283                return false;
284             }
285          }
286          if (!write_block_to_device(out_jcr->dcr)) {
287             Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
288                out_dev->print_name(), out_dev->bstrerror());
289             Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
290                   out_dev->bstrerror());
291             return false;
292          }
293          return true;
294       case EOM_LABEL:
295          Pmsg0(000, _("EOM label not copied.\n"));
296          return true;
297       case EOT_LABEL:              /* end of all tapes */
298          Pmsg0(000, _("EOT label not copied.\n"));
299          return true;
300       default:
301          return true;
302       }
303    }
304
305    /*  Write record */
306    if (bsr && rec->match_stat < 1) {
307       /* Skipping record, because does not match BSR filter */
308       return true;
309    }
310    records++;
311    while (!write_record_to_block(out_block, rec)) {
312       Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
313                  rec->remainder);
314       if (!write_block_to_device(out_jcr->dcr)) {
315          Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
316             out_dev->print_name(), out_dev->bstrerror());
317          Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
318                out_dev->bstrerror());
319          return false;
320       }
321    }
322    return true;
323 }
324
325 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
326 {
327    const char *rtype;
328    memset(sessrec, 0, sizeof(sessrec));
329    switch (rec->FileIndex) {
330    case PRE_LABEL:
331       rtype = _("Fresh Volume Label");
332       break;
333    case VOL_LABEL:
334       rtype = _("Volume Label");
335       unser_volume_label(dev, rec);
336       break;
337    case SOS_LABEL:
338       rtype = _("Begin Job Session");
339       unser_session_label(sessrec, rec);
340       break;
341    case EOS_LABEL:
342       rtype = _("End Job Session");
343       unser_session_label(sessrec, rec);
344       break;
345    case 0:
346    case EOM_LABEL:
347       rtype = _("End of Medium");
348       break;
349    default:
350       rtype = _("Unknown");
351       break;
352    }
353    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
354          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
355    if (verbose) {
356       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
357             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
358    }
359 }
360
361
362 /* Dummies to replace askdir.c */
363 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
364 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
365 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
366 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
367 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
368 bool    dir_send_job_status(JCR *jcr) {return 1;}
369
370
371 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
372 {
373    DEVICE *dev = dcr->dev;
374    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
375       dcr->VolumeName, dev->print_name());
376    dev->close();
377    getchar();
378    return true;
379 }
380
381 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
382 {
383    Dmsg0(100, "Fake dir_get_volume_info\n");
384    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
385    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
386    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
387    return 1;
388 }