]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bcopy.c
kes Apply patch submitted for bug #1107 with a small modification.
[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
44 /* Forward referenced functions */
45 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
46 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
47
48
49 /* Global variables */
50 static DEVICE *in_dev = NULL;
51 static DEVICE *out_dev = NULL;
52 static JCR *in_jcr;                    /* input jcr */
53 static JCR *out_jcr;                   /* output jcr */
54 static BSR *bsr = NULL;
55 static const char *wd = "/tmp";
56 static bool list_records = false;
57 static uint32_t records = 0;
58 static uint32_t jobs = 0;
59 static DEV_BLOCK *out_block;
60 static SESSION_LABEL sessrec;
61
62 #define CONFIG_FILE "bacula-sd.conf"
63 char *configfile = NULL;
64 STORES *me = NULL;                    /* our Global resource */
65 bool forge_on = false;                /* proceed inspite of I/O errors */
66 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
67 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
68
69
70 static void usage()
71 {
72    fprintf(stderr, _(
73 PROG_COPYRIGHT
74 "\nVersion: %s (%s)\n\n"
75 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
76 "       -b bootstrap      specify a bootstrap file\n"
77 "       -c <file>         specify configuration file\n"
78 "       -d <nn>           set debug level to <nn>\n"
79 "       -i                specify input Volume names (separated by |)\n"
80 "       -o                specify output Volume names (separated by |)\n"
81 "       -p                proceed inspite of errors\n"
82 "       -v                verbose\n"
83 "       -w <dir>          specify working directory (default /tmp)\n"
84 "       -?                print this message\n\n"), 2002, VERSION, BDATE);
85    exit(1);
86 }
87
88 int main (int argc, char *argv[])
89 {
90    int ch;
91    char *iVolumeName = NULL;
92    char *oVolumeName = NULL;
93    bool ignore_label_errors = false;
94    bool ok;
95
96    setlocale(LC_ALL, "");
97    bindtextdomain("bacula", LOCALEDIR);
98    textdomain("bacula");
99    init_stack_dump();
100
101    my_name_is(argc, argv, "bcopy");
102    init_msg(NULL, NULL);
103
104    while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
105       switch (ch) {
106       case 'b':
107          bsr = parse_bsr(NULL, optarg);
108          break;
109
110       case 'c':                    /* specify config file */
111          if (configfile != NULL) {
112             free(configfile);
113          }
114          configfile = bstrdup(optarg);
115          break;
116
117       case 'd':                    /* debug level */
118          if (*optarg == 't') {
119             dbg_timestamp = true;
120          } else {
121             debug_level = atoi(optarg);
122             if (debug_level <= 0) {
123                debug_level = 1;
124             }
125          }
126          break;
127
128       case 'i':                    /* input Volume name */
129          iVolumeName = optarg;
130          break;
131
132       case 'o':                    /* output Volume name */
133          oVolumeName = optarg;
134          break;
135
136       case 'p':
137          ignore_label_errors = true;
138          forge_on = true;
139          break;
140
141       case 'v':
142          verbose++;
143          break;
144
145       case 'w':
146          wd = optarg;
147          break;
148
149       case '?':
150       default:
151          usage();
152
153       }
154    }
155    argc -= optind;
156    argv += optind;
157
158    if (argc != 2) {
159       Pmsg0(0, _("Wrong number of arguments: \n"));
160       usage();
161    }
162
163    OSDependentInit();
164
165    working_directory = wd;
166
167    if (configfile == NULL) {
168       configfile = bstrdup(CONFIG_FILE);
169    }
170
171    parse_config(configfile);
172
173    /* Setup and acquire input device for reading */
174    Dmsg0(100, "About to setup input jcr\n");
175    in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
176    if (!in_jcr) {
177       exit(1);
178    }
179    in_jcr->ignore_label_errors = ignore_label_errors;
180    in_dev = in_jcr->dcr->dev;
181    if (!in_dev) {
182       exit(1);
183    }
184
185    /* Setup output device for writing */
186    Dmsg0(100, "About to setup output jcr\n");
187    out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
188    if (!out_jcr) {
189       exit(1);
190    }
191    out_dev = out_jcr->dcr->dev;
192    if (!out_dev) {
193       exit(1);
194    }
195    Dmsg0(100, "About to acquire device for writing\n");
196    /* For we must now acquire the device for writing */
197    out_dev->r_dlock();
198    if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
199       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
200       out_dev->dunlock();
201       exit(1);
202    }
203    out_dev->dunlock();
204    if (!acquire_device_for_append(out_jcr->dcr)) {
205       free_jcr(in_jcr);
206       exit(1);
207    }
208    out_block = out_jcr->dcr->block;
209
210    ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
211
212    if (ok || out_dev->can_write()) {
213       if (!write_block_to_device(out_jcr->dcr)) {
214          Pmsg0(000, _("Write of last block failed.\n"));
215       }
216    }
217
218    Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
219
220    free_jcr(in_jcr);
221    free_jcr(out_jcr);
222
223    in_dev->term();
224    out_dev->term();
225    return 0;
226 }
227
228
229 /*
230  * read_records() calls back here for each record it gets
231  */
232 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
233 {
234    if (list_records) {
235       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
236             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
237             rec->Stream, rec->data_len);
238    }
239    /*
240     * Check for Start or End of Session Record
241     *
242     */
243    if (rec->FileIndex < 0) {
244       get_session_record(in_dcr->dev, rec, &sessrec);
245
246       if (verbose > 1) {
247          dump_label_record(in_dcr->dev, rec, 1);
248       }
249       switch (rec->FileIndex) {
250       case PRE_LABEL:
251          Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
252          return false;
253       case VOL_LABEL:
254          Pmsg0(000, _("Volume label not copied.\n"));
255          return true;
256       case SOS_LABEL:
257          if (bsr && rec->match_stat < 1) {
258             /* Skipping record, because does not match BSR filter */
259             if (verbose) {
260              Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
261             }
262          } else {
263             jobs++;
264          }
265          break;
266       case EOS_LABEL:
267          if (bsr && rec->match_stat < 1) {
268             /* Skipping record, because does not match BSR filter */
269            return true;
270         }
271          while (!write_record_to_block(out_block, rec)) {
272             Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
273                        rec->remainder);
274             if (!write_block_to_device(out_jcr->dcr)) {
275                Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
276                   out_dev->print_name(), out_dev->bstrerror());
277                Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
278                      out_dev->bstrerror());
279                return false;
280             }
281          }
282          if (!write_block_to_device(out_jcr->dcr)) {
283             Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
284                out_dev->print_name(), out_dev->bstrerror());
285             Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
286                   out_dev->bstrerror());
287             return false;
288          }
289          return true;
290       case EOM_LABEL:
291          Pmsg0(000, _("EOM label not copied.\n"));
292          return true;
293       case EOT_LABEL:              /* end of all tapes */
294          Pmsg0(000, _("EOT label not copied.\n"));
295          return true;
296       default:
297          return true;
298       }
299    }
300
301    /*  Write record */
302    if (bsr && rec->match_stat < 1) {
303       /* Skipping record, because does not match BSR filter */
304       return true;
305    }
306    records++;
307    while (!write_record_to_block(out_block, rec)) {
308       Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
309                  rec->remainder);
310       if (!write_block_to_device(out_jcr->dcr)) {
311          Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
312             out_dev->print_name(), out_dev->bstrerror());
313          Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
314                out_dev->bstrerror());
315          return false;
316       }
317    }
318    return true;
319 }
320
321 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
322 {
323    const char *rtype;
324    memset(sessrec, 0, sizeof(sessrec));
325    switch (rec->FileIndex) {
326    case PRE_LABEL:
327       rtype = _("Fresh Volume Label");
328       break;
329    case VOL_LABEL:
330       rtype = _("Volume Label");
331       unser_volume_label(dev, rec);
332       break;
333    case SOS_LABEL:
334       rtype = _("Begin Job Session");
335       unser_session_label(sessrec, rec);
336       break;
337    case EOS_LABEL:
338       rtype = _("End Job Session");
339       unser_session_label(sessrec, rec);
340       break;
341    case 0:
342    case EOM_LABEL:
343       rtype = _("End of Medium");
344       break;
345    default:
346       rtype = _("Unknown");
347       break;
348    }
349    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
350          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
351    if (verbose) {
352       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
353             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
354    }
355 }
356
357
358 /* Dummies to replace askdir.c */
359 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
360 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
361 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
362 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
363 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
364 bool    dir_send_job_status(JCR *jcr) {return 1;}
365
366
367 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
368 {
369    DEVICE *dev = dcr->dev;
370    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
371       dcr->VolumeName, dev->print_name());
372    dev->close();
373    getchar();
374    return true;
375 }
376
377 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
378 {
379    Dmsg0(100, "Fake dir_get_volume_info\n");
380    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
381    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
382    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
383    return 1;
384 }