]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bcopy.c
Make out of freespace non-fatal for removable devices -- i.e. behaves like tape
[bacula/bacula] / bacula / src / stored / bcopy.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
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.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *  Program to copy a Bacula from one volume to another.
22  *
23  *   Kern E. Sibbald, October 2002
24  */
25
26 #include "bacula.h"
27 #include "stored.h"
28
29 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
30
31 /* Forward referenced functions */
32 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
33 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
34
35
36 /* Global variables */
37 static DEVICE *in_dev = NULL;
38 static DEVICE *out_dev = NULL;
39 static JCR *in_jcr;                    /* input jcr */
40 static JCR *out_jcr;                   /* output jcr */
41 static BSR *bsr = NULL;
42 static const char *wd = "/tmp";
43 static bool list_records = false;
44 static uint32_t records = 0;
45 static uint32_t jobs = 0;
46 static DEV_BLOCK *out_block;
47 static SESSION_LABEL sessrec;
48
49 static CONFIG *config;
50 #define CONFIG_FILE "bacula-sd.conf"
51
52 void *start_heap;
53 char *configfile = NULL;
54
55 static void usage()
56 {
57    fprintf(stderr, _(
58 PROG_COPYRIGHT
59 "\n%sVersion: %s (%s)\n\n"
60 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
61 "       -b bootstrap      specify a bootstrap file\n"
62 "       -c <file>         specify a Storage configuration file\n"
63 "       -d <nn>           set debug level to <nn>\n"
64 "       -dt               print timestamp in debug output\n"
65 "       -i                specify input Volume names (separated by |)\n"
66 "       -o                specify output Volume names (separated by |)\n"
67 "       -p                proceed inspite of errors\n"
68 "       -v                verbose\n"
69 "       -w <dir>          specify working directory (default /tmp)\n"
70 "       -?                print this message\n\n"), 2002, "", VERSION, BDATE);
71    exit(1);
72 }
73
74 int main (int argc, char *argv[])
75 {
76    int ch;
77    char *iVolumeName = NULL;
78    char *oVolumeName = NULL;
79    bool ignore_label_errors = false;
80    bool ok;
81    BtoolsAskDirHandler askdir_handler;
82
83    init_askdir_handler(&askdir_handler);
84    setlocale(LC_ALL, "");
85    bindtextdomain("bacula", LOCALEDIR);
86    textdomain("bacula");
87    init_stack_dump();
88
89    my_name_is(argc, argv, "bcopy");
90    lmgr_init_thread();
91    init_msg(NULL, NULL);
92
93    while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
94       switch (ch) {
95       case 'b':
96          bsr = parse_bsr(NULL, optarg);
97          break;
98
99       case 'c':                    /* specify config file */
100          if (configfile != NULL) {
101             free(configfile);
102          }
103          configfile = bstrdup(optarg);
104          break;
105
106       case 'd':                    /* debug level */
107          if (*optarg == 't') {
108             dbg_timestamp = true;
109          } else {
110             debug_level = atoi(optarg);
111             if (debug_level <= 0) {
112                debug_level = 1;
113             }
114          }
115          break;
116
117       case 'i':                    /* input Volume name */
118          iVolumeName = optarg;
119          break;
120
121       case 'o':                    /* output Volume name */
122          oVolumeName = optarg;
123          break;
124
125       case 'p':
126          ignore_label_errors = true;
127          forge_on = true;
128          break;
129
130       case 'v':
131          verbose++;
132          break;
133
134       case 'w':
135          wd = optarg;
136          break;
137
138       case '?':
139       default:
140          usage();
141
142       }
143    }
144    argc -= optind;
145    argv += optind;
146
147    if (argc != 2) {
148       Pmsg0(0, _("Wrong number of arguments: \n"));
149       usage();
150    }
151
152    OSDependentInit();
153
154    working_directory = wd;
155
156    if (configfile == NULL) {
157       configfile = bstrdup(CONFIG_FILE);
158    }
159
160    config = New(CONFIG());
161    parse_sd_config(config, configfile, M_ERROR_TERM);
162    setup_me();
163    load_sd_plugins(me->plugin_directory);
164
165    /* Setup and acquire input device for reading */
166    Dmsg0(100, "About to setup input jcr\n");
167    in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, SD_READ, true/*read dedup data*/); /* read device */
168    if (!in_jcr) {
169       exit(1);
170    }
171    in_jcr->ignore_label_errors = ignore_label_errors;
172    in_dev = in_jcr->dcr->dev;
173    if (!in_dev) {
174       exit(1);
175    }
176
177    /* Setup output device for writing */
178    Dmsg0(100, "About to setup output jcr\n");
179    out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, SD_APPEND); /* no acquire */
180    if (!out_jcr) {
181       exit(1);
182    }
183    out_dev = out_jcr->dcr->dev;
184    if (!out_dev) {
185       exit(1);
186    }
187    Dmsg0(100, "About to acquire device for writing\n");
188    /* For we must now acquire the device for writing */
189    out_dev->rLock(false);
190    if (!out_dev->open_device(out_jcr->dcr, OPEN_READ_WRITE)) {
191       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
192       out_dev->Unlock();
193       exit(1);
194    }
195    out_dev->Unlock();
196    if (!acquire_device_for_append(out_jcr->dcr)) {
197       free_jcr(in_jcr);
198       exit(1);
199    }
200    out_block = out_jcr->dcr->block;
201
202    ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
203
204    if (ok || out_dev->can_write()) {
205       if (!out_jcr->dcr->write_final_block_to_device()) {
206          Pmsg0(000, _("Write of last block failed.\n"));
207       }
208    }
209
210    Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
211
212    free_jcr(in_jcr);
213    free_jcr(out_jcr);
214
215    in_dev->term(NULL);
216    out_dev->term(NULL);
217
218    return 0;
219 }
220
221
222 /*
223  * read_records() calls back here for each record it gets
224  */
225 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
226 {
227    if (list_records) {
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);
231    }
232    /*
233     * Check for Start or End of Session Record
234     *
235     */
236    if (rec->FileIndex < 0) {
237       get_session_record(in_dcr->dev, rec, &sessrec);
238
239       if (verbose > 1) {
240          dump_label_record(in_dcr->dev, rec, 1/*verbose*/, false/*check err*/);
241       }
242       switch (rec->FileIndex) {
243       case PRE_LABEL:
244          Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
245          return false;
246       case VOL_LABEL:
247          Pmsg0(000, _("Volume label not copied.\n"));
248          return true;
249       case SOS_LABEL:
250          if (bsr && rec->match_stat < 1) {
251             /* Skipping record, because does not match BSR filter */
252             if (verbose) {
253              Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
254             }
255          } else {
256             jobs++;
257          }
258          break;
259       case EOS_LABEL:
260          if (bsr && rec->match_stat < 1) {
261             /* Skipping record, because does not match BSR filter */
262            return true;
263         }
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,
266                        rec->remainder);
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());
272                return false;
273             }
274          }
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());
280             return false;
281          }
282          return true;
283       case EOM_LABEL:
284          Pmsg0(000, _("EOM label not copied.\n"));
285          return true;
286       case EOT_LABEL:              /* end of all tapes */
287          Pmsg0(000, _("EOT label not copied.\n"));
288          return true;
289       default:
290          return true;
291       }
292    }
293
294    /*  Write record */
295    if (bsr && rec->match_stat < 1) {
296       /* Skipping record, because does not match BSR filter */
297       return true;
298    }
299    records++;
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,
302                  rec->remainder);
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());
308          return false;
309       }
310    }
311    return true;
312 }
313
314 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
315 {
316    const char *rtype;
317    memset(sessrec, 0, sizeof(SESSION_LABEL));
318    switch (rec->FileIndex) {
319    case PRE_LABEL:
320       rtype = _("Fresh Volume Label");
321       break;
322    case VOL_LABEL:
323       rtype = _("Volume Label");
324       unser_volume_label(dev, rec);
325       break;
326    case SOS_LABEL:
327       rtype = _("Begin Job Session");
328       unser_session_label(sessrec, rec);
329       break;
330    case EOS_LABEL:
331       rtype = _("End Job Session");
332       unser_session_label(sessrec, rec);
333       break;
334    case 0:
335    case EOM_LABEL:
336       rtype = _("End of Medium");
337       break;
338    default:
339       rtype = _("Unknown");
340       break;
341    }
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);
344    if (verbose) {
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);
347    }
348 }