1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2016 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 Sibbald, July 2002
25 */
26 /**
27 * @file
28 * Bootstrap routines.
29 */
30
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/dird_globals.h"
34 #include "dird/ua_input.h"
35 #include "dird/ua_restore.h"
36 #include "dird/ua_server.h"
37 #include "dird/ua_select.h"
38 #include "lib/edit.h"
39
40 namespace directordaemon {
41
42 #define UA_CMD_SIZE 1000
43
44 /* Forward referenced functions */
45
46 /**
47 * Create new FileIndex entry for BootStrapRecord
48 */
new_findex()49 RestoreBootstrapRecordFileIndex *new_findex()
50 {
51 RestoreBootstrapRecordFileIndex *fi = (RestoreBootstrapRecordFileIndex *)bmalloc(sizeof(RestoreBootstrapRecordFileIndex));
52 memset(fi, 0, sizeof(RestoreBootstrapRecordFileIndex));
53 return fi;
54 }
55
56 /**
57 * Free all BootStrapRecord FileIndex entries
58 */
FreeFindex(RestoreBootstrapRecordFileIndex * fi)59 static inline void FreeFindex(RestoreBootstrapRecordFileIndex *fi)
60 {
61 RestoreBootstrapRecordFileIndex *next;
62 for ( ; fi; fi=next) {
63 next = fi->next;
64 free(fi);
65 }
66 }
67
68 /**
69 * Get storage device name from Storage resource
70 */
GetStorageDevice(char * device,char * storage)71 static bool GetStorageDevice(char *device, char *storage)
72 {
73 StorageResource *store;
74 if (storage[0] == 0) {
75 return false;
76 }
77 store = (StorageResource *)my_config->GetResWithName(R_STORAGE, storage);
78 if (!store) {
79 return false;
80 }
81 DeviceResource *dev = (DeviceResource *)(store->device->first());
82 if (!dev) {
83 return false;
84 }
85 bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
86 return true;
87 }
88
89 /**
90 * Print a BootStrapRecord entry into a memory buffer.
91 */
PrintBsrItem(PoolMem * pool_buf,const char * fmt,...)92 static void PrintBsrItem(PoolMem *pool_buf, const char *fmt, ...)
93 {
94 va_list arg_ptr;
95 int len, maxlen;
96 PoolMem item(PM_MESSAGE);
97
98 while (1) {
99 maxlen = item.MaxSize() - 1;
100 va_start(arg_ptr, fmt);
101 len = Bvsnprintf(item.c_str(), maxlen, fmt, arg_ptr);
102 va_end(arg_ptr);
103 if (len < 0 || len >= (maxlen - 5)) {
104 item.ReallocPm(maxlen + maxlen / 2);
105 continue;
106 }
107 break;
108 }
109
110 pool_buf->strcat(item.c_str());
111 }
112
113 /**
114 * Our data structures were not designed completely
115 * correctly, so the file indexes cover the full
116 * range regardless of volume. The FirstIndex and LastIndex
117 * passed in here are for the current volume, so when
118 * writing out the fi, constrain them to those values.
119 *
120 * We are called here once for each JobMedia record
121 * for each Volume.
122 */
write_findex(RestoreBootstrapRecordFileIndex * fi,int32_t FirstIndex,int32_t LastIndex,PoolMem * buffer)123 static inline uint32_t write_findex(RestoreBootstrapRecordFileIndex *fi,
124 int32_t FirstIndex,
125 int32_t LastIndex,
126 PoolMem *buffer)
127 {
128 int32_t findex, findex2;
129 uint32_t count = 0;
130
131 while (fi) {
132 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
133 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
134 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
135 findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
136 findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
137 if (findex == findex2) {
138 PrintBsrItem(buffer, "FileIndex=%d\n", findex);
139 count++;
140 } else {
141 PrintBsrItem(buffer, "FileIndex=%d-%d\n", findex, findex2);
142 count += findex2 - findex + 1;
143 }
144 }
145 fi = fi->next;
146 }
147
148 return count;
149 }
150
151 /**
152 * Find out if Volume defined with FirstIndex and LastIndex
153 * falls within the range of selected files in the bsr.
154 */
is_volume_selected(RestoreBootstrapRecordFileIndex * fi,int32_t FirstIndex,int32_t LastIndex)155 static inline bool is_volume_selected(RestoreBootstrapRecordFileIndex *fi,
156 int32_t FirstIndex,
157 int32_t LastIndex)
158 {
159 while (fi) {
160 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
161 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
162 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
163 return true;
164 }
165 fi = fi->next;
166 }
167 return false;
168 }
169
170 /**
171 * Create a new bootstrap record
172 */
new_bsr()173 RestoreBootstrapRecord *new_bsr()
174 {
175 RestoreBootstrapRecord *bsr = (RestoreBootstrapRecord *)bmalloc(sizeof(RestoreBootstrapRecord));
176
177 memset(bsr, 0, sizeof(RestoreBootstrapRecord));
178 return bsr;
179 }
180
181 namespace directordaemon {
182
183 /**
184 * Free the entire BootStrapRecord
185 */
FreeBsr(RestoreBootstrapRecord * bsr)186 void FreeBsr(RestoreBootstrapRecord *bsr)
187 {
188 RestoreBootstrapRecord *next;
189
190 while (bsr) {
191 FreeFindex(bsr->fi);
192
193 if (bsr->VolParams) {
194 free(bsr->VolParams);
195 }
196
197 if (bsr->fileregex) {
198 free(bsr->fileregex);
199 }
200
201 next = bsr->next;
202 free(bsr);
203 bsr = next;
204 }
205 }
206
207 } /* namespace director */
208
209 /**
210 * Complete the BootStrapRecord by filling in the VolumeName and
211 * VolSessionId and VolSessionTime using the JobId
212 */
CompleteBsr(UaContext * ua,RestoreBootstrapRecord * bsr)213 bool CompleteBsr(UaContext *ua, RestoreBootstrapRecord *bsr)
214 {
215 for ( ; bsr; bsr=bsr->next) {
216 JobDbRecord jr;
217 memset(&jr, 0, sizeof(jr));
218 jr.JobId = bsr->JobId;
219 if (!ua->db->GetJobRecord(ua->jcr, &jr)) {
220 ua->ErrorMsg(_("Unable to get Job record. ERR=%s\n"), ua->db->strerror());
221 return false;
222 }
223 bsr->VolSessionId = jr.VolSessionId;
224 bsr->VolSessionTime = jr.VolSessionTime;
225 if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */
226 bsr->VolCount = 0; /* there are no volumes */
227 continue;
228 }
229 if ((bsr->VolCount = ua->db->GetJobVolumeParameters(ua->jcr, bsr->JobId,
230 &(bsr->VolParams))) == 0) {
231 ua->ErrorMsg(_("Unable to get Job Volume Parameters. ERR=%s\n"), ua->db->strerror());
232 if (bsr->VolParams) {
233 free(bsr->VolParams);
234 bsr->VolParams = NULL;
235 }
236 return false;
237 }
238 }
239 return true;
240 }
241
242 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
243 static uint32_t uniq = 0;
244
MakeUniqueRestoreFilename(UaContext * ua,PoolMem & fname)245 static void MakeUniqueRestoreFilename(UaContext *ua, PoolMem &fname)
246 {
247 JobControlRecord *jcr = ua->jcr;
248 int i = FindArgWithValue(ua, "bootstrap");
249 if (i >= 0) {
250 Mmsg(fname, "%s", ua->argv[i]);
251 jcr->unlink_bsr = false;
252 } else {
253 P(mutex);
254 uniq++;
255 V(mutex);
256 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
257 jcr->unlink_bsr = true;
258 }
259 if (jcr->RestoreBootstrap) {
260 free(jcr->RestoreBootstrap);
261 }
262 jcr->RestoreBootstrap = bstrdup(fname.c_str());
263 }
264
265 /**
266 * Write the bootstrap records to file
267 */
WriteBsrFile(UaContext * ua,RestoreContext & rx)268 uint32_t WriteBsrFile(UaContext *ua, RestoreContext &rx)
269 {
270 FILE *fd;
271 bool err;
272 uint32_t count = 0;
273 PoolMem fname(PM_MESSAGE);
274 PoolMem buffer(PM_MESSAGE);
275
276 MakeUniqueRestoreFilename(ua, fname);
277 fd = fopen(fname.c_str(), "w+b");
278 if (!fd) {
279 BErrNo be;
280 ua->ErrorMsg(_("Unable to create bootstrap file %s. ERR=%s\n"),
281 fname.c_str(), be.bstrerror());
282 goto bail_out;
283 }
284
285 /*
286 * Write them to a buffer.
287 */
288 count = WriteBsr(ua, rx, &buffer);
289
290 /*
291 * Write the buffer to file
292 */
293 fprintf(fd, "%s", buffer.c_str());
294
295 err = ferror(fd);
296 fclose(fd);
297 if (count == 0) {
298 ua->InfoMsg(_("No files found to read. No bootstrap file written.\n"));
299 goto bail_out;
300 }
301 if (err) {
302 ua->ErrorMsg(_("Error writing bsr file.\n"));
303 count = 0;
304 goto bail_out;
305 }
306
307 ua->SendMsg(_("Bootstrap records written to %s\n"), fname.c_str());
308
309 if (debug_level >= 10) {
310 PrintBsr(ua, rx);
311 }
312
313 bail_out:
314 return count;
315 }
316
DisplayVolInfo(UaContext * ua,RestoreContext & rx,JobId_t JobId)317 static void DisplayVolInfo(UaContext *ua, RestoreContext &rx, JobId_t JobId)
318 {
319 int i;
320 RestoreBootstrapRecord *bsr;
321 char online;
322 PoolMem volmsg(PM_MESSAGE);
323 char Device[MAX_NAME_LENGTH];
324
325 for (bsr = rx.bsr; bsr; bsr = bsr->next) {
326 if (JobId && JobId != bsr->JobId) {
327 continue;
328 }
329
330 for (i = 0; i < bsr->VolCount; i++) {
331 if (bsr->VolParams[i].VolumeName[0]) {
332 if (!GetStorageDevice(Device, bsr->VolParams[i].Storage)) {
333 Device[0] = 0;
334 }
335 if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
336 online = '*';
337 } else {
338 online = ' ';
339 }
340 Mmsg(volmsg, "%c%-25s %-25s %-25s",
341 online, bsr->VolParams[i].VolumeName,
342 bsr->VolParams[i].Storage, Device);
343 AddPrompt(ua, volmsg.c_str());
344 }
345 }
346 }
347 }
348
DisplayBsrInfo(UaContext * ua,RestoreContext & rx)349 void DisplayBsrInfo(UaContext *ua, RestoreContext &rx)
350 {
351 int i;
352 char *p;
353 JobId_t JobId;
354
355 /*
356 * Tell the user what he will need to mount
357 */
358 ua->SendMsg("\n");
359 ua->SendMsg(_("The job will require the following\n"
360 " Volume(s) Storage(s) SD Device(s)\n"
361 "===========================================================================\n"));
362
363 /*
364 * Create Unique list of Volumes using prompt list
365 */
366 StartPrompt(ua, "");
367 if (*rx.JobIds == 0) {
368 /*
369 * Print Volumes in any order
370 */
371 DisplayVolInfo(ua, rx, 0);
372 } else {
373 /*
374 * Ensure that the volumes are printed in JobId order
375 */
376 for (p = rx.JobIds; GetNextJobidFromList(&p, &JobId) > 0; ) {
377 DisplayVolInfo(ua, rx, JobId);
378 }
379 }
380
381 for (i = 0; i < ua->num_prompts; i++) {
382 ua->SendMsg(" %s\n", ua->prompt[i]);
383 free(ua->prompt[i]);
384 }
385
386 if (ua->num_prompts == 0) {
387 ua->SendMsg(_("No Volumes found to restore.\n"));
388 } else {
389 ua->SendMsg(_("\nVolumes marked with \"*\" are online.\n"));
390 }
391
392 ua->num_prompts = 0;
393 ua->SendMsg("\n");
394
395 return;
396 }
397
398 /**
399 * Write bsr data for a single bsr record
400 */
write_bsr_item(RestoreBootstrapRecord * bsr,UaContext * ua,RestoreContext & rx,PoolMem * buffer,bool & first,uint32_t & LastIndex)401 static uint32_t write_bsr_item(RestoreBootstrapRecord *bsr, UaContext *ua,
402 RestoreContext &rx, PoolMem *buffer,
403 bool &first, uint32_t &LastIndex)
404 {
405 int i;
406 char ed1[50], ed2[50];
407 uint32_t count = 0;
408 uint32_t total_count = 0;
409 char device[MAX_NAME_LENGTH];
410
411 /*
412 * For a given volume, loop over all the JobMedia records.
413 * VolCount is the number of JobMedia records.
414 */
415 for (i = 0; i < bsr->VolCount; i++) {
416 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
417 bsr->VolParams[i].LastIndex)) {
418 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
419 continue;
420 }
421
422 if (!rx.store) {
423 FindStorageResource(ua, rx, bsr->VolParams[i].Storage,
424 bsr->VolParams[i].MediaType);
425 }
426
427 PrintBsrItem(buffer, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
428 PrintBsrItem(buffer, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
429 PrintBsrItem(buffer, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
430
431 if (bsr->fileregex) {
432 PrintBsrItem(buffer, "FileRegex=%s\n", bsr->fileregex);
433 }
434
435 if (GetStorageDevice(device, bsr->VolParams[i].Storage)) {
436 PrintBsrItem(buffer, "Device=\"%s\"\n", device);
437 }
438
439 if (bsr->VolParams[i].Slot > 0) {
440 PrintBsrItem(buffer, "Slot=%d\n", bsr->VolParams[i].Slot);
441 }
442
443 PrintBsrItem(buffer, "VolSessionId=%u\n", bsr->VolSessionId);
444 PrintBsrItem(buffer, "VolSessionTime=%u\n", bsr->VolSessionTime);
445 PrintBsrItem(buffer, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
446 edit_uint64(bsr->VolParams[i].EndAddr, ed2));
447 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
448 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
449
450 count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
451 bsr->VolParams[i].LastIndex, buffer);
452 if (count) {
453 PrintBsrItem(buffer, "Count=%u\n", count);
454 }
455
456 total_count += count;
457
458 /*
459 * If the same file is present on two tapes or in two files
460 * on a tape, it is a continuation, and should not be treated
461 * twice in the totals.
462 */
463 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
464 total_count--;
465 }
466 first = false;
467 LastIndex = bsr->VolParams[i].LastIndex;
468 }
469
470 return total_count;
471 }
472
473 /**
474 * Here we actually write out the details of the bsr file.
475 * Note, there is one bsr for each JobId, but the bsr may
476 * have multiple volumes, which have been entered in the
477 * order they were written.
478 *
479 * The bsrs must be written out in the order the JobIds
480 * are found in the jobid list.
481 */
WriteBsr(UaContext * ua,RestoreContext & rx,PoolMem * buffer)482 uint32_t WriteBsr(UaContext *ua, RestoreContext &rx, PoolMem *buffer)
483 {
484 bool first = true;
485 uint32_t LastIndex = 0;
486 uint32_t total_count = 0;
487 char *p;
488 JobId_t JobId;
489 RestoreBootstrapRecord *bsr;
490
491 if (*rx.JobIds == 0) {
492 for (bsr = rx.bsr; bsr; bsr = bsr->next) {
493 total_count += write_bsr_item(bsr, ua, rx, buffer, first, LastIndex);
494 }
495 return total_count;
496 }
497
498 for (p = rx.JobIds; GetNextJobidFromList(&p, &JobId) > 0; ) {
499 for (bsr = rx.bsr; bsr; bsr = bsr->next) {
500 if (JobId == bsr->JobId) {
501 total_count += write_bsr_item(bsr, ua, rx, buffer, first, LastIndex);
502 }
503 }
504 }
505
506 return total_count;
507 }
508
PrintBsr(UaContext * ua,RestoreContext & rx)509 void PrintBsr(UaContext *ua, RestoreContext &rx)
510 {
511 PoolMem buffer(PM_MESSAGE);
512
513 WriteBsr(ua, rx, &buffer);
514 fprintf(stdout, "%s", buffer.c_str());
515 }
516
517 /**
518 * Add a FileIndex to the list of BootStrap records.
519 * Here we are only dealing with JobId's and the FileIndexes
520 * associated with those JobIds.
521 *
522 * We expect that JobId, FileIndex are sorted ascending.
523 */
AddFindex(RestoreBootstrapRecord * bsr,uint32_t JobId,int32_t findex)524 void AddFindex(RestoreBootstrapRecord *bsr, uint32_t JobId, int32_t findex)
525 {
526 RestoreBootstrapRecord *nbsr;
527 RestoreBootstrapRecordFileIndex *fi, *lfi;
528
529 if (findex == 0) {
530 return; /* probably a dummy directory */
531 }
532
533 if (bsr->fi == NULL) { /* if no FI add one */
534 /*
535 * This is the first FileIndex item in the chain
536 */
537 bsr->fi = new_findex();
538 bsr->JobId = JobId;
539 bsr->fi->findex = findex;
540 bsr->fi->findex2 = findex;
541 return;
542 }
543
544 /*
545 * Walk down list of bsrs until we find the JobId
546 */
547 if (bsr->JobId != JobId) {
548 for (nbsr = bsr->next; nbsr; nbsr = nbsr->next) {
549 if (nbsr->JobId == JobId) {
550 bsr = nbsr;
551 break;
552 }
553 }
554
555 if (!nbsr) { /* Must add new JobId */
556 /*
557 * Add new JobId at end of chain
558 */
559 for (nbsr = bsr; nbsr->next; nbsr = nbsr->next)
560 { }
561 nbsr->next = new_bsr();
562 nbsr->next->JobId = JobId;
563 nbsr->next->fi = new_findex();
564 nbsr->next->fi->findex = findex;
565 nbsr->next->fi->findex2 = findex;
566 return;
567 }
568 }
569
570 /*
571 * At this point, bsr points to bsr containing this JobId,
572 * and we are sure that there is at least one fi record.
573 */
574 lfi = fi = bsr->fi;
575
576 /*
577 * Check if this findex is a duplicate.
578 */
579 if (findex >= fi->findex && findex <= fi->findex2) {
580 return;
581 }
582
583 /*
584 * Check if this findex is smaller than first item
585 */
586 if (findex < fi->findex) {
587 if ((findex + 1) == fi->findex) {
588 fi->findex = findex; /* extend down */
589 return;
590 }
591 fi = new_findex(); /* yes, insert before first item */
592 fi->findex = findex;
593 fi->findex2 = findex;
594 fi->next = lfi;
595 bsr->fi = fi;
596 return;
597 }
598
599 /*
600 * Walk down fi chain and find where to insert insert new FileIndex
601 */
602 while (fi) {
603 /*
604 * Check if this findex is a duplicate.
605 */
606 if (findex >= fi->findex && findex <= fi->findex2) {
607 return;
608 }
609
610 if (findex == (fi->findex2 + 1)) { /* extend up */
611 RestoreBootstrapRecordFileIndex *nfi;
612 fi->findex2 = findex;
613
614 /*
615 * If the following record contains one higher, merge its
616 * file index by extending it up.
617 */
618 if (fi->next && ((findex + 1) == fi->next->findex)) {
619 nfi = fi->next;
620 fi->findex2 = nfi->findex2;
621 fi->next = nfi->next;
622 free(nfi);
623 }
624 return;
625 }
626
627 if (findex < fi->findex) { /* add before */
628 if ((findex + 1) == fi->findex) {
629 fi->findex = findex;
630 return;
631 }
632 break;
633 }
634
635 lfi = fi;
636 fi = fi->next;
637 }
638
639 /*
640 * Add to last place found
641 */
642 fi = new_findex();
643 fi->findex = findex;
644 fi->findex2 = findex;
645 fi->next = lfi->next;
646 lfi->next = fi;
647
648 return;
649 }
650
651 /**
652 * Add all possible FileIndexes to the list of BootStrap records.
653 * Here we are only dealing with JobId's and the FileIndexes
654 * associated with those JobIds.
655 */
AddFindexAll(RestoreBootstrapRecord * bsr,uint32_t JobId)656 void AddFindexAll(RestoreBootstrapRecord *bsr, uint32_t JobId)
657 {
658 RestoreBootstrapRecord *nbsr;
659 RestoreBootstrapRecordFileIndex *fi;
660
661 if (bsr->fi == NULL) { /* if no FI add one */
662 /*
663 * This is the first FileIndex item in the chain
664 */
665 bsr->fi = new_findex();
666 bsr->JobId = JobId;
667 bsr->fi->findex = 1;
668 bsr->fi->findex2 = INT32_MAX;
669 return;
670 }
671
672 /*
673 * Walk down list of bsrs until we find the JobId
674 */
675 if (bsr->JobId != JobId) {
676 for (nbsr = bsr->next; nbsr; nbsr = nbsr->next) {
677 if (nbsr->JobId == JobId) {
678 bsr = nbsr;
679 break;
680 }
681 }
682
683 if (!nbsr) { /* Must add new JobId */
684 /*
685 * Add new JobId at end of chain
686 */
687 for (nbsr = bsr; nbsr->next; nbsr = nbsr->next)
688 { }
689
690 nbsr->next = new_bsr();
691 nbsr->next->JobId = JobId;
692
693 /*
694 * If we use regexp to restore, set it for each jobid
695 */
696 if (bsr->fileregex) {
697 nbsr->next->fileregex = bstrdup(bsr->fileregex);
698 }
699
700 nbsr->next->fi = new_findex();
701 nbsr->next->fi->findex = 1;
702 nbsr->next->fi->findex2 = INT32_MAX;
703 return;
704 }
705 }
706
707 /*
708 * At this point, bsr points to bsr containing this JobId,
709 * and we are sure that there is at least one fi record.
710 */
711 fi = bsr->fi;
712 fi->findex = 1;
713 fi->findex2 = INT32_MAX;
714 return;
715 }
716
717 /**
718 * Open the bootstrap file and find the first Storage=
719 * Returns ok if able to open
720 *
721 * It fills the storage name (should be the first line)
722 * and the file descriptor to the bootstrap file,
723 * it should be used for next operations, and need to be
724 * closed at the end.
725 */
OpenBootstrapFile(JobControlRecord * jcr,bootstrap_info & info)726 bool OpenBootstrapFile(JobControlRecord *jcr, bootstrap_info &info)
727 {
728 FILE *bs;
729 UaContext *ua;
730 info.bs = NULL;
731 info.ua = NULL;
732
733 if (!jcr->RestoreBootstrap) {
734 return false;
735 }
736 bstrncpy(info.storage, jcr->res.read_storage->name(), MAX_NAME_LENGTH);
737
738 bs = fopen(jcr->RestoreBootstrap, "rb");
739 if (!bs) {
740 BErrNo be;
741 Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
742 jcr->RestoreBootstrap, be.bstrerror());
743 jcr->setJobStatus(JS_ErrorTerminated);
744 return false;
745 }
746
747 ua = new_ua_context(jcr);
748 ua->cmd = CheckPoolMemorySize(ua->cmd, UA_CMD_SIZE + 1);
749 while (!fgets(ua->cmd, UA_CMD_SIZE, bs)) {
750 ParseUaArgs(ua);
751 if (ua->argc != 1) {
752 continue;
753 }
754 if (Bstrcasecmp(ua->argk[0], "Storage")) {
755 bstrncpy(info.storage, ua->argv[0], MAX_NAME_LENGTH);
756 break;
757 }
758 }
759 info.bs = bs;
760 info.ua = ua;
761 fseek(bs, 0, SEEK_SET); /* return to the top of the file */
762 return true;
763 }
764
765 /**
766 * This function compare the given storage name with the
767 * the current one. We compare the name and the address:port.
768 * Returns true if we use the same storage.
769 */
IsOnSameStorage(JobControlRecord * jcr,char * new_one)770 static inline bool IsOnSameStorage(JobControlRecord *jcr, char *new_one)
771 {
772 StorageResource *new_store;
773
774 /*
775 * With old FD, we send the whole bootstrap to the storage
776 */
777 if (jcr->FDVersion < FD_VERSION_2) {
778 return true;
779 }
780
781 /*
782 * We are in init loop ? shoudn't fail here
783 */
784 if (!*new_one) {
785 return true;
786 }
787
788 /*
789 * Same name
790 */
791 if (bstrcmp(new_one, jcr->res.read_storage->name())) {
792 return true;
793 }
794
795 new_store = (StorageResource *)my_config->GetResWithName(R_STORAGE, new_one);
796 if (!new_store) {
797 Jmsg(jcr, M_WARNING, 0,
798 _("Could not get storage resource '%s'.\n"), new_one);
799 return true;
800 }
801
802 /*
803 * If Port and Hostname/IP are same, we are talking to the same
804 * Storage Daemon
805 */
806 if (jcr->res.read_storage->SDport != new_store->SDport ||
807 !bstrcmp(jcr->res.read_storage->address, new_store->address)) {
808 return false;
809 }
810
811 return true;
812 }
813
814 /**
815 * Check if the current line contains Storage="xxx", and compare the
816 * result to the current storage. We use UaContext to analyse the bsr
817 * string.
818 *
819 * Returns true if we need to change the storage, and it set the new
820 * Storage resource name in "storage" arg.
821 */
CheckForNewStorage(JobControlRecord * jcr,bootstrap_info & info)822 static inline bool CheckForNewStorage(JobControlRecord *jcr, bootstrap_info &info)
823 {
824 UaContext *ua = info.ua;
825
826 ParseUaArgs(ua);
827 if (ua->argc != 1) {
828 return false;
829 }
830
831 if (Bstrcasecmp(ua->argk[0], "Storage")) {
832 /*
833 * Continue if this is a volume from the same storage.
834 */
835 if (IsOnSameStorage(jcr, ua->argv[0])) {
836 return false;
837 }
838
839 /*
840 * Note the next storage name
841 */
842 bstrncpy(info.storage, ua->argv[0], MAX_NAME_LENGTH);
843 Dmsg1(5, "Change storage to %s\n", info.storage);
844 return true;
845 }
846
847 return false;
848 }
849
850 /**
851 * Send bootstrap file to Storage daemon section by section.
852 */
SendBootstrapFile(JobControlRecord * jcr,BareosSocket * sock,bootstrap_info & info)853 bool SendBootstrapFile(JobControlRecord *jcr, BareosSocket *sock, bootstrap_info &info)
854 {
855 boffset_t pos;
856 const char *bootstrap = "bootstrap\n";
857 UaContext *ua = info.ua;
858 FILE *bs = info.bs;
859
860 Dmsg1(400, "SendBootstrapFile: %s\n", jcr->RestoreBootstrap);
861 if (!jcr->RestoreBootstrap) {
862 return false;
863 }
864
865 sock->fsend(bootstrap);
866 pos = ftello(bs);
867
868 while (fgets(ua->cmd, UA_CMD_SIZE, bs)) {
869 if (CheckForNewStorage(jcr, info)) {
870 /*
871 * Otherwise, we need to contact another storage daemon.
872 * Reset bs to the beginning of the current segment.
873 */
874 fseeko(bs, pos, SEEK_SET);
875 break;
876 }
877 sock->fsend("%s", ua->cmd);
878 pos = ftello(bs);
879 }
880
881 sock->signal(BNET_EOD);
882
883 return true;
884 }
885
886 /**
887 * Clean the bootstrap_info struct
888 */
CloseBootstrapFile(bootstrap_info & info)889 void CloseBootstrapFile(bootstrap_info &info)
890 {
891 if (info.bs) {
892 fclose(info.bs);
893 }
894 if (info.ua) {
895 FreeUaContext(info.ua);
896 }
897 }
898 } /* namespace directordaemon */
899