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