1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero 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 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero 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 /*
24 * Kern E. Sibbald, October 2002
25 */
26 /**
27 * @file
28 * Program to copy a Bareos from one volume to another.
29 */
30
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "lib/crypto_cache.h"
35 #include "stored/acquire.h"
36 #include "stored/butil.h"
37 #include "stored/jcr_private.h"
38 #include "stored/label.h"
39 #include "stored/mount.h"
40 #include "stored/read_record.h"
41 #include "lib/address_conf.h"
42 #include "lib/bsignal.h"
43 #include "lib/parse_bsr.h"
44 #include "lib/parse_conf.h"
45 #include "include/jcr.h"
46
47 namespace storagedaemon {
48 extern bool ParseSdConfig(const char* configfile, int exit_code);
49 }
50
51 using namespace storagedaemon;
52
53 /* Forward referenced functions */
54 static void GetSessionRecord(Device* dev,
55 DeviceRecord* rec,
56 Session_Label* sessrec);
57 static bool RecordCb(DeviceControlRecord* dcr, DeviceRecord* rec);
58
59
60 /* Global variables */
61 static Device* in_dev = NULL;
62 static Device* out_dev = NULL;
63 static JobControlRecord* in_jcr; /* input jcr */
64 static JobControlRecord* out_jcr; /* output jcr */
65 static BootStrapRecord* bsr = NULL;
66 static const char* wd = "/tmp";
67 static bool list_records = false;
68 static uint32_t records = 0;
69 static uint32_t jobs = 0;
70 static DeviceBlock* out_block;
71 static Session_Label sessrec;
72
usage()73 static void usage()
74 {
75 kBareosVersionStrings.PrintCopyrightWithFsfAndPlanets(stderr, 2002);
76 fprintf(
77 stderr,
78 _("Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
79 " -b bootstrap specify a bootstrap file\n"
80 " -c <path> specify a Storage configuration file or "
81 "directory\n"
82 " -D <director> specify a director name specified in the "
83 "Storage\n"
84 " configuration file for the Key Encryption Key "
85 "selection\n"
86 " -d <nn> set debug level to <nn>\n"
87 " -dt print timestamp in debug output\n"
88 " -i specify input Volume names (separated by |)\n"
89 " -o specify output Volume names (separated by |)\n"
90 " -p proceed inspite of errors\n"
91 " -v verbose\n"
92 " -w <dir> specify working directory (default /tmp)\n"
93 " -? print this message\n\n"));
94 exit(1);
95 }
96
main(int argc,char * argv[])97 int main(int argc, char* argv[])
98 {
99 int ch;
100 bool ok;
101 char* iVolumeName = NULL;
102 char* oVolumeName = NULL;
103 char* DirectorName = NULL;
104 DirectorResource* director = NULL;
105 bool ignore_label_errors = false;
106 DeviceControlRecord *in_dcr, *out_dcr;
107
108 setlocale(LC_ALL, "");
109 tzset();
110 bindtextdomain("bareos", LOCALEDIR);
111 textdomain("bareos");
112 InitStackDump();
113
114 MyNameIs(argc, argv, "bcopy");
115 InitMsg(NULL, NULL);
116
117 while ((ch = getopt(argc, argv, "b:c:D:d:i:o:pvw:?")) != -1) {
118 switch (ch) {
119 case 'b':
120 bsr = libbareos::parse_bsr(NULL, optarg);
121 break;
122
123 case 'c': /* specify config file */
124 if (configfile != NULL) { free(configfile); }
125 configfile = strdup(optarg);
126 break;
127
128 case 'D': /* specify director name */
129 if (DirectorName != NULL) { free(DirectorName); }
130 DirectorName = strdup(optarg);
131 break;
132
133 case 'd': /* debug level */
134 if (*optarg == 't') {
135 dbg_timestamp = true;
136 } else {
137 debug_level = atoi(optarg);
138 if (debug_level <= 0) { debug_level = 1; }
139 }
140 break;
141
142 case 'i': /* input Volume name */
143 iVolumeName = optarg;
144 break;
145
146 case 'o': /* output Volume name */
147 oVolumeName = optarg;
148 break;
149
150 case 'p':
151 ignore_label_errors = true;
152 forge_on = true;
153 break;
154
155 case 'v':
156 verbose++;
157 break;
158
159 case 'w':
160 wd = optarg;
161 break;
162
163 case '?':
164 default:
165 usage();
166 }
167 }
168 argc -= optind;
169 argv += optind;
170
171 if (argc != 2) {
172 Pmsg0(0, _("Wrong number of arguments: \n"));
173 usage();
174 }
175
176 OSDependentInit();
177
178 working_directory = wd;
179
180 my_config = InitSdConfig(configfile, M_ERROR_TERM);
181 ParseSdConfig(configfile, M_ERROR_TERM);
182
183 if (DirectorName) {
184 foreach_res (director, R_DIRECTOR) {
185 if (bstrcmp(director->resource_name_, DirectorName)) { break; }
186 }
187 if (!director) {
188 Emsg2(
189 M_ERROR_TERM, 0,
190 _("No Director resource named %s defined in %s. Cannot continue.\n"),
191 DirectorName, configfile);
192 }
193 }
194
195 LoadSdPlugins(me->plugin_directory, me->plugin_names);
196
197 ReadCryptoCache(me->working_directory, "bareos-sd",
198 GetFirstPortHostOrder(me->SDaddrs));
199
200 /*
201 * Setup and acquire input device for reading
202 */
203 Dmsg0(100, "About to setup input jcr\n");
204
205 in_dcr = new DeviceControlRecord;
206 in_jcr = SetupJcr("bcopy", argv[0], bsr, director, in_dcr, iVolumeName,
207 true); /* read device */
208 if (!in_jcr) { exit(1); }
209
210 in_jcr->impl->ignore_label_errors = ignore_label_errors;
211
212 in_dev = in_jcr->impl->dcr->dev;
213 if (!in_dev) { exit(1); }
214
215 /*
216 * Setup output device for writing
217 */
218 Dmsg0(100, "About to setup output jcr\n");
219
220 out_dcr = new DeviceControlRecord;
221 out_jcr = SetupJcr("bcopy", argv[1], bsr, director, out_dcr, oVolumeName,
222 false); /* write device */
223 if (!out_jcr) { exit(1); }
224
225 out_dev = out_jcr->impl->dcr->dev;
226 if (!out_dev) { exit(1); }
227
228 Dmsg0(100, "About to acquire device for writing\n");
229
230 /*
231 * For we must now acquire the device for writing
232 */
233 out_dev->rLock(false);
234 if (!out_dev->open(out_jcr->impl->dcr, OPEN_READ_WRITE)) {
235 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
236 out_dev->Unlock();
237 exit(1);
238 }
239 out_dev->Unlock();
240 if (!AcquireDeviceForAppend(out_jcr->impl->dcr)) {
241 FreeJcr(in_jcr);
242 exit(1);
243 }
244 out_block = out_jcr->impl->dcr->block;
245
246 ok = ReadRecords(in_jcr->impl->dcr, RecordCb, MountNextReadVolume);
247
248 if (ok || out_dev->CanWrite()) {
249 if (!out_jcr->impl->dcr->WriteBlockToDevice()) {
250 Pmsg0(000, _("Write of last block failed.\n"));
251 }
252 }
253
254 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
255
256 in_dev->term();
257 out_dev->term();
258
259 FreeJcr(in_jcr);
260 FreeJcr(out_jcr);
261
262 return 0;
263 }
264
265
266 /*
267 * ReadRecords() calls back here for each record it gets
268 */
RecordCb(DeviceControlRecord * in_dcr,DeviceRecord * rec)269 static bool RecordCb(DeviceControlRecord* in_dcr, DeviceRecord* rec)
270 {
271 if (list_records) {
272 Pmsg5(000,
273 _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
274 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, rec->Stream,
275 rec->data_len);
276 }
277 /*
278 * Check for Start or End of Session Record
279 *
280 */
281 if (rec->FileIndex < 0) {
282 GetSessionRecord(in_dcr->dev, rec, &sessrec);
283
284 if (verbose > 1) { DumpLabelRecord(in_dcr->dev, rec, true); }
285 switch (rec->FileIndex) {
286 case PRE_LABEL:
287 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
288 return false;
289 case VOL_LABEL:
290 Pmsg0(000, _("Volume label not copied.\n"));
291 return true;
292 case SOS_LABEL:
293 if (bsr && rec->match_stat < 1) {
294 /* Skipping record, because does not match BootStrapRecord filter */
295 if (verbose) {
296 Pmsg0(-1, _("Copy skipped. Record does not match BootStrapRecord "
297 "filter.\n"));
298 }
299 } else {
300 jobs++;
301 }
302 break;
303 case EOS_LABEL:
304 if (bsr && rec->match_stat < 1) {
305 /* Skipping record, because does not match BootStrapRecord filter */
306 return true;
307 }
308 while (!WriteRecordToBlock(out_jcr->impl->dcr, rec)) {
309 Dmsg2(150, "!WriteRecordToBlock data_len=%d rem=%d\n", rec->data_len,
310 rec->remainder);
311 if (!out_jcr->impl->dcr->WriteBlockToDevice()) {
312 Dmsg2(90, "Got WriteBlockToDev error on device %s: ERR=%s\n",
313 out_dev->print_name(), out_dev->bstrerror());
314 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
315 out_dev->bstrerror());
316 return false;
317 }
318 }
319 if (!out_jcr->impl->dcr->WriteBlockToDevice()) {
320 Dmsg2(90, "Got WriteBlockToDev error on device %s: ERR=%s\n",
321 out_dev->print_name(), out_dev->bstrerror());
322 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
323 out_dev->bstrerror());
324 return false;
325 }
326 return true;
327 case EOM_LABEL:
328 Pmsg0(000, _("EOM label not copied.\n"));
329 return true;
330 case EOT_LABEL: /* end of all tapes */
331 Pmsg0(000, _("EOT label not copied.\n"));
332 return true;
333 default:
334 return true;
335 }
336 }
337
338 /* Write record */
339 if (bsr && rec->match_stat < 1) {
340 /* Skipping record, because does not match BootStrapRecord filter */
341 return true;
342 }
343 records++;
344 while (!WriteRecordToBlock(out_jcr->impl->dcr, rec)) {
345 Dmsg2(150, "!WriteRecordToBlock data_len=%d rem=%d\n", rec->data_len,
346 rec->remainder);
347 if (!out_jcr->impl->dcr->WriteBlockToDevice()) {
348 Dmsg2(90, "Got WriteBlockToDev error on device %s: ERR=%s\n",
349 out_dev->print_name(), out_dev->bstrerror());
350 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
351 out_dev->bstrerror());
352 return false;
353 }
354 }
355 return true;
356 }
357
GetSessionRecord(Device * dev,DeviceRecord * rec,Session_Label * sessrec)358 static void GetSessionRecord(Device* dev,
359 DeviceRecord* rec,
360 Session_Label* sessrec)
361 {
362 const char* rtype;
363 *sessrec = Session_Label{};
364 switch (rec->FileIndex) {
365 case PRE_LABEL:
366 rtype = _("Fresh Volume Label");
367 break;
368 case VOL_LABEL:
369 rtype = _("Volume Label");
370 UnserVolumeLabel(dev, rec);
371 break;
372 case SOS_LABEL:
373 rtype = _("Begin Job Session");
374 UnserSessionLabel(sessrec, rec);
375 break;
376 case EOS_LABEL:
377 rtype = _("End Job Session");
378 UnserSessionLabel(sessrec, rec);
379 break;
380 case 0:
381 case EOM_LABEL:
382 rtype = _("End of Medium");
383 break;
384 default:
385 rtype = _("Unknown");
386 break;
387 }
388 Dmsg5(10,
389 "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
390 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
391 rec->data_len);
392 if (verbose) {
393 Pmsg5(
394 -1,
395 _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
396 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream,
397 rec->data_len);
398 }
399 }
400