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/jcr_private.h"
35 #include "dird/ua_input.h"
36 #include "dird/ua_restore.h"
37 #include "dird/ua_server.h"
38 #include "dird/ua_select.h"
39 #include "lib/berrno.h"
40 #include "lib/edit.h"
41 #include "lib/parse_conf.h"
42 #include "include/make_unique.h"
43 
44 #include <algorithm>
45 
46 namespace directordaemon {
47 
48 #define UA_CMD_SIZE 1000
49 
RestoreBootstrapRecord()50 RestoreBootstrapRecord::RestoreBootstrapRecord()
51 {
52   fi = std::make_unique<RestoreBootstrapRecordFileIndex>();
53 }
54 
RestoreBootstrapRecord(JobId_t t_JobId)55 RestoreBootstrapRecord::RestoreBootstrapRecord(JobId_t t_JobId)
56     : RestoreBootstrapRecord::RestoreBootstrapRecord()
57 {
58   JobId = t_JobId;
59 }
60 
~RestoreBootstrapRecord()61 RestoreBootstrapRecord::~RestoreBootstrapRecord()
62 {
63   if (VolParams) { free(VolParams); }
64   if (fileregex) { free(fileregex); }
65 }
66 
Add(int32_t findex)67 void RestoreBootstrapRecordFileIndex::Add(int32_t findex)
68 {
69   if (findex == 0) { return; /* probably a dummy directory */ }
70   fileIds_.push_back(findex);
71   sorted_ = false;
72 }
73 
AddAll()74 void RestoreBootstrapRecordFileIndex::AddAll() { allFiles_ = true; }
Sort()75 void RestoreBootstrapRecordFileIndex::Sort()
76 {
77   if (sorted_) return;
78 
79   if (!std::is_sorted(fileIds_.begin(), fileIds_.end())) {
80     std::sort(fileIds_.begin(), fileIds_.end());
81   }
82   sorted_ = true;
83 }
84 
85 std::vector<std::pair<int32_t, int32_t>>
GetRanges()86 RestoreBootstrapRecordFileIndex::GetRanges()
87 {
88   if (allFiles_) {
89     return std::vector<std::pair<int32_t, int32_t>>{
90         std::make_pair(1, INT32_MAX)};
91   } else {
92     Sort();
93 
94     auto ranges = std::vector<std::pair<int32_t, int32_t>>{};
95     auto begin = int32_t{0};
96     auto end = int32_t{0};
97 
98     for (auto fileId : fileIds_) {
99       if (begin == 0) {
100         begin = end = fileId;
101       } else if (fileId > end + 1) {
102         ranges.push_back(std::make_pair(begin, end));
103         begin = end = fileId;
104       } else {
105         end = fileId;
106       }
107     }
108     ranges.push_back(std::make_pair(begin, end));
109     return ranges;
110   }
111 }
112 
113 /**
114  * Get storage device name from Storage resource
115  */
GetStorageDevice(char * device,char * storage)116 static bool GetStorageDevice(char* device, char* storage)
117 {
118   StorageResource* store;
119   if (storage[0] == 0) { return false; }
120   store = (StorageResource*)my_config->GetResWithName(R_STORAGE, storage);
121   if (!store) { return false; }
122   DeviceResource* dev = (DeviceResource*)(store->device->first());
123   if (!dev) { return false; }
124   bstrncpy(device, dev->resource_name_, MAX_NAME_LENGTH);
125   return true;
126 }
127 
128 /**
129  * Print a BootStrapRecord entry into a memory buffer.
130  */
PrintBsrItem(std::string & buffer,const char * fmt,...)131 static void PrintBsrItem(std::string& buffer, const char* fmt, ...)
132 {
133   va_list arg_ptr;
134   int len, maxlen;
135   PoolMem item(PM_MESSAGE);
136 
137   while (1) {
138     maxlen = item.MaxSize() - 1;
139     va_start(arg_ptr, fmt);
140     len = Bvsnprintf(item.c_str(), maxlen, fmt, arg_ptr);
141     va_end(arg_ptr);
142     if (len < 0 || len >= (maxlen - 5)) {
143       item.ReallocPm(maxlen + maxlen / 2);
144       continue;
145     }
146     break;
147   }
148 
149   buffer += item.c_str();
150 }
151 
152 /**
153  * Our data structures were not designed completely
154  * correctly, so the file indexes cover the full
155  * range regardless of volume. The FirstIndex and LastIndex
156  * passed in here are for the current volume, so when
157  * writing out the fi, constrain them to those values.
158  *
159  * We are called here once for each JobMedia record
160  * for each Volume.
161  */
write_findex(RestoreBootstrapRecordFileIndex * fi,int32_t FirstIndex,int32_t LastIndex,std::string & buffer)162 uint32_t write_findex(RestoreBootstrapRecordFileIndex* fi,
163                       int32_t FirstIndex,
164                       int32_t LastIndex,
165                       std::string& buffer)
166 {
167   auto count = uint32_t{0};
168   auto bsrItems = std::string{};
169 
170   for (auto& range : fi->GetRanges()) {
171     auto first = range.first;
172     auto last = range.second;
173     if ((first >= FirstIndex && first <= LastIndex) ||
174         (last >= FirstIndex && last <= LastIndex) ||
175         (first < FirstIndex && last > LastIndex)) {
176       first = std::max(first, FirstIndex);
177       last = std::min(last, LastIndex);
178 
179       if (first == last) {
180         bsrItems += "FileIndex=" + std::to_string(first) + "\n";
181         count++;
182       } else {
183         bsrItems += "FileIndex=" + std::to_string(first) + "-" +
184                     std::to_string(last) + "\n";
185         count += last - first + 1;
186       }
187     }
188   }
189   buffer += bsrItems.c_str();
190   return count;
191 }
192 
193 /**
194  * Find out if Volume defined with FirstIndex and LastIndex
195  * falls within the range of selected files in the bsr.
196  */
is_volume_selected(RestoreBootstrapRecordFileIndex * fi,int32_t FirstIndex,int32_t LastIndex)197 static inline bool is_volume_selected(RestoreBootstrapRecordFileIndex* fi,
198                                       int32_t FirstIndex,
199                                       int32_t LastIndex)
200 {
201   for (auto range : fi->GetRanges()) {
202     auto first = range.first;
203     auto last = range.second;
204     if ((first >= FirstIndex && first <= LastIndex) ||
205         (last >= FirstIndex && last <= LastIndex) ||
206         (first < FirstIndex && last > LastIndex)) {
207       return true;
208     }
209   }
210   return false;
211 }
212 
213 
214 /**
215  * Complete the BootStrapRecord by filling in the VolumeName and
216  * VolSessionId and VolSessionTime using the JobId
217  */
AddVolumeInformationToBsr(UaContext * ua,RestoreBootstrapRecord * bsr)218 bool AddVolumeInformationToBsr(UaContext* ua, RestoreBootstrapRecord* bsr)
219 {
220   for (; bsr; bsr = bsr->next.get()) {
221     JobDbRecord jr;
222     jr.JobId = bsr->JobId;
223     if (!ua->db->GetJobRecord(ua->jcr, &jr)) {
224       ua->ErrorMsg(_("Unable to get Job record. ERR=%s\n"), ua->db->strerror());
225       return false;
226     }
227     bsr->VolSessionId = jr.VolSessionId;
228     bsr->VolSessionTime = jr.VolSessionTime;
229     if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */
230       bsr->VolCount = 0;    /*   there are no volumes */
231       continue;
232     }
233     if ((bsr->VolCount = ua->db->GetJobVolumeParameters(
234              ua->jcr, bsr->JobId, &(bsr->VolParams))) == 0) {
235       ua->ErrorMsg(_("Unable to get Job Volume Parameters. ERR=%s\n"),
236                    ua->db->strerror());
237       if (bsr->VolParams) {
238         free(bsr->VolParams);
239         bsr->VolParams = NULL;
240       }
241       return false;
242     }
243   }
244   return true;
245 }
246 
247 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
248 static uint32_t uniq = 0;
249 
MakeUniqueRestoreFilename(UaContext * ua,PoolMem & fname)250 static void MakeUniqueRestoreFilename(UaContext* ua, PoolMem& fname)
251 {
252   JobControlRecord* jcr = ua->jcr;
253   int i = FindArgWithValue(ua, "bootstrap");
254   if (i >= 0) {
255     Mmsg(fname, "%s", ua->argv[i]);
256     jcr->impl->unlink_bsr = false;
257   } else {
258     P(mutex);
259     uniq++;
260     V(mutex);
261     Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
262     jcr->impl->unlink_bsr = true;
263   }
264   if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); }
265   jcr->RestoreBootstrap = strdup(fname.c_str());
266 }
267 
268 /**
269  * Write the bootstrap records to file
270  */
WriteBsrFile(UaContext * ua,RestoreContext & rx)271 uint32_t WriteBsrFile(UaContext* ua, RestoreContext& rx)
272 {
273   FILE* fd;
274   bool err;
275   uint32_t count = 0;
276   PoolMem fname(PM_MESSAGE);
277   std::string buffer;
278 
279   MakeUniqueRestoreFilename(ua, fname);
280   fd = fopen(fname.c_str(), "w+b");
281   if (!fd) {
282     BErrNo be;
283     ua->ErrorMsg(_("Unable to create bootstrap file %s. ERR=%s\n"),
284                  fname.c_str(), be.bstrerror());
285     goto bail_out;
286   }
287 
288   /*
289    * Write them to a buffer.
290    */
291   count = WriteBsr(ua, rx, buffer);
292 
293   /*
294    * Write the buffer to file
295    */
296   fprintf(fd, "%s", buffer.c_str());
297 
298   err = ferror(fd);
299   fclose(fd);
300   if (count == 0) {
301     ua->InfoMsg(_("No files found to read. No bootstrap file written.\n"));
302     goto bail_out;
303   }
304   if (err) {
305     ua->ErrorMsg(_("Error writing bsr file.\n"));
306     count = 0;
307     goto bail_out;
308   }
309 
310   ua->SendMsg(_("Bootstrap records written to %s\n"), fname.c_str());
311 
312   if (debug_level >= 10) { PrintBsr(ua, rx); }
313 
314 bail_out:
315   return count;
316 }
317 
DisplayVolInfo(UaContext * ua,RestoreContext & rx,JobId_t JobId)318 static void DisplayVolInfo(UaContext* ua, RestoreContext& rx, JobId_t JobId)
319 {
320   int i;
321   RestoreBootstrapRecord* bsr;
322   char online;
323   PoolMem volmsg(PM_MESSAGE);
324   char Device[MAX_NAME_LENGTH];
325 
326   for (bsr = rx.bsr.get(); bsr; bsr = bsr->next.get()) {
327     if (JobId && JobId != bsr->JobId) { continue; }
328 
329     for (i = 0; i < bsr->VolCount; i++) {
330       if (bsr->VolParams[i].VolumeName[0]) {
331         if (!GetStorageDevice(Device, bsr->VolParams[i].Storage)) {
332           Device[0] = 0;
333         }
334         if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
335           online = '*';
336         } else {
337           online = ' ';
338         }
339         Mmsg(volmsg, "%c%-25s %-25s %-25s", online,
340              bsr->VolParams[i].VolumeName, bsr->VolParams[i].Storage, Device);
341         AddPrompt(ua, volmsg.c_str());
342       }
343     }
344   }
345 }
346 
DisplayBsrInfo(UaContext * ua,RestoreContext & rx)347 void DisplayBsrInfo(UaContext* ua, RestoreContext& rx)
348 {
349   int i;
350   char* p;
351   JobId_t JobId;
352 
353   /*
354    * Tell the user what he will need to mount
355    */
356   ua->SendMsg("\n");
357   ua->SendMsg(
358       _("The job will require the following\n"
359         "   Volume(s)                 Storage(s)                SD Device(s)\n"
360         "======================================================================"
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,std::string & buffer,bool & first,uint32_t & LastIndex)401 static uint32_t write_bsr_item(RestoreBootstrapRecord* bsr,
402                                UaContext* ua,
403                                RestoreContext& rx,
404                                std::string& buffer,
405                                bool& first,
406                                uint32_t& LastIndex)
407 {
408   int i;
409   char ed1[50], ed2[50];
410   uint32_t count = 0;
411   uint32_t total_count = 0;
412   char device[MAX_NAME_LENGTH];
413 
414   /*
415    * For a given volume, loop over all the JobMedia records.
416    * VolCount is the number of JobMedia records.
417    */
418   for (i = 0; i < bsr->VolCount; i++) {
419     if (!is_volume_selected(bsr->fi.get(), bsr->VolParams[i].FirstIndex,
420                             bsr->VolParams[i].LastIndex)) {
421       bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
422       continue;
423     }
424 
425     if (!rx.store) {
426       FindStorageResource(ua, rx, bsr->VolParams[i].Storage,
427                           bsr->VolParams[i].MediaType);
428     }
429 
430     PrintBsrItem(buffer, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
431     PrintBsrItem(buffer, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
432     PrintBsrItem(buffer, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
433 
434     if (bsr->fileregex) {
435       PrintBsrItem(buffer, "FileRegex=%s\n", bsr->fileregex);
436     }
437 
438     if (GetStorageDevice(device, bsr->VolParams[i].Storage)) {
439       PrintBsrItem(buffer, "Device=\"%s\"\n", device);
440     }
441 
442     if (bsr->VolParams[i].Slot > 0) {
443       PrintBsrItem(buffer, "Slot=%d\n", bsr->VolParams[i].Slot);
444     }
445 
446     PrintBsrItem(buffer, "VolSessionId=%u\n", bsr->VolSessionId);
447     PrintBsrItem(buffer, "VolSessionTime=%u\n", bsr->VolSessionTime);
448     PrintBsrItem(buffer, "VolAddr=%s-%s\n",
449                  edit_uint64(bsr->VolParams[i].StartAddr, ed1),
450                  edit_uint64(bsr->VolParams[i].EndAddr, ed2));
451 
452     count = write_findex(bsr->fi.get(), bsr->VolParams[i].FirstIndex,
453                          bsr->VolParams[i].LastIndex, buffer);
454     if (count) { PrintBsrItem(buffer, "Count=%u\n", count); }
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) { total_count--; }
464     first = false;
465     LastIndex = bsr->VolParams[i].LastIndex;
466   }
467 
468   return total_count;
469 }
470 
471 /**
472  * Here we actually write out the details of the bsr file.
473  * Note, there is one bsr for each JobId, but the bsr may
474  * have multiple volumes, which have been entered in the
475  * order they were written.
476  *
477  * The bsrs must be written out in the order the JobIds
478  * are found in the jobid list.
479  */
WriteBsr(UaContext * ua,RestoreContext & rx,std::string & buffer)480 uint32_t WriteBsr(UaContext* ua, RestoreContext& rx, std::string& buffer)
481 {
482   bool first = true;
483   uint32_t LastIndex = 0;
484   uint32_t total_count = 0;
485   char* p;
486   JobId_t JobId;
487   RestoreBootstrapRecord* bsr;
488 
489   if (*rx.JobIds == 0) {
490     for (bsr = rx.bsr.get(); bsr; bsr = bsr->next.get()) {
491       total_count += write_bsr_item(bsr, ua, rx, buffer, first, LastIndex);
492     }
493     return total_count;
494   }
495 
496   for (p = rx.JobIds; GetNextJobidFromList(&p, &JobId) > 0;) {
497     for (bsr = rx.bsr.get(); bsr; bsr = bsr->next.get()) {
498       if (JobId == bsr->JobId) {
499         total_count += write_bsr_item(bsr, ua, rx, buffer, first, LastIndex);
500       }
501     }
502   }
503 
504   return total_count;
505 }
506 
PrintBsr(UaContext * ua,RestoreContext & rx)507 void PrintBsr(UaContext* ua, RestoreContext& rx)
508 {
509   std::string buffer;
510 
511   WriteBsr(ua, rx, buffer);
512   fprintf(stdout, "%s", buffer.c_str());
513 }
514 
515 /**
516  * Add a FileIndex to the list of BootStrap records.
517  * Here we are only dealing with JobId's and the FileIndexes
518  * associated with those JobIds.
519  */
AddFindex(RestoreBootstrapRecord * bsr,uint32_t JobId,int32_t findex)520 void AddFindex(RestoreBootstrapRecord* bsr, uint32_t JobId, int32_t findex)
521 {
522   RestoreBootstrapRecord* nbsr;
523 
524   if (findex == 0) { return; /* probably a dummy directory */ }
525 
526   if (bsr->fi->Empty()) { /* if no FI yet, jobid is not yet set */
527     bsr->JobId = JobId;
528   }
529 
530   /*
531    * Walk down list of bsrs until we find the JobId
532    */
533   if (bsr->JobId != JobId) {
534     for (nbsr = bsr->next.get(); nbsr; nbsr = nbsr->next.get()) {
535       if (nbsr->JobId == JobId) {
536         bsr = nbsr;
537         break;
538       }
539     }
540 
541     if (!nbsr) { /* Must add new JobId */
542       /*
543        * Add new JobId at end of chain
544        */
545       for (nbsr = bsr; nbsr->next.get(); nbsr = nbsr->next.get()) {}
546       nbsr->next = std::make_unique<RestoreBootstrapRecord>(JobId);
547       bsr = nbsr->next.get();
548     }
549   }
550 
551   bsr->fi->Add(findex);
552 }
553 
554 /**
555  * Add all possible  FileIndexes to the list of BootStrap records.
556  * Here we are only dealing with JobId's and the FileIndexes
557  * associated with those JobIds.
558  */
AddFindexAll(RestoreBootstrapRecord * bsr,uint32_t JobId)559 void AddFindexAll(RestoreBootstrapRecord* bsr, uint32_t JobId)
560 {
561   RestoreBootstrapRecord* nbsr;
562 
563   if (bsr->fi->Empty()) { /* if no FI add one */
564     bsr->JobId = JobId;
565   }
566 
567   /*
568    * Walk down list of bsrs until we find the JobId
569    */
570   if (bsr->JobId != JobId) {
571     for (nbsr = bsr->next.get(); nbsr; nbsr = nbsr->next.get()) {
572       if (nbsr->JobId == JobId) {
573         bsr = nbsr;
574         break;
575       }
576     }
577 
578     if (!nbsr) { /* Must add new JobId */
579       for (nbsr = bsr; nbsr->next.get(); nbsr = nbsr->next.get()) {}
580       nbsr->next = std::make_unique<RestoreBootstrapRecord>(JobId);
581       if (bsr->fileregex) { nbsr->next->fileregex = strdup(bsr->fileregex); }
582       bsr = nbsr->next.get();
583     }
584   }
585 
586   bsr->fi->AddAll();
587 }
588 
589 /**
590  * Open the bootstrap file and find the first Storage=
591  * Returns ok if able to open
592  *
593  * It fills the storage name (should be the first line)
594  * and the file descriptor to the bootstrap file,
595  * it should be used for next operations, and need to be
596  * closed at the end.
597  */
OpenBootstrapFile(JobControlRecord * jcr,bootstrap_info & info)598 bool OpenBootstrapFile(JobControlRecord* jcr, bootstrap_info& info)
599 {
600   FILE* bs;
601   UaContext* ua;
602   info.bs = NULL;
603   info.ua = NULL;
604 
605   if (!jcr->RestoreBootstrap) { return false; }
606   bstrncpy(info.storage, jcr->impl->res.read_storage->resource_name_,
607            MAX_NAME_LENGTH);
608 
609   bs = fopen(jcr->RestoreBootstrap, "rb");
610   if (!bs) {
611     BErrNo be;
612     Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
613          jcr->RestoreBootstrap, be.bstrerror());
614     jcr->setJobStatus(JS_ErrorTerminated);
615     return false;
616   }
617 
618   ua = new_ua_context(jcr);
619   ua->cmd = CheckPoolMemorySize(ua->cmd, UA_CMD_SIZE + 1);
620   while (!fgets(ua->cmd, UA_CMD_SIZE, bs)) {
621     ParseUaArgs(ua);
622     if (ua->argc != 1) { continue; }
623     if (Bstrcasecmp(ua->argk[0], "Storage")) {
624       bstrncpy(info.storage, ua->argv[0], MAX_NAME_LENGTH);
625       break;
626     }
627   }
628   info.bs = bs;
629   info.ua = ua;
630   fseek(bs, 0, SEEK_SET); /* return to the top of the file */
631   return true;
632 }
633 
634 /**
635  * This function compare the given storage name with the
636  * the current one. We compare the name and the address:port.
637  * Returns true if we use the same storage.
638  */
IsOnSameStorage(JobControlRecord * jcr,char * new_one)639 static inline bool IsOnSameStorage(JobControlRecord* jcr, char* new_one)
640 {
641   StorageResource* new_store;
642 
643   /*
644    * With old FD, we send the whole bootstrap to the storage
645    */
646   if (jcr->impl->FDVersion < FD_VERSION_2) { return true; }
647 
648   /*
649    * We are in init loop ? shoudn't fail here
650    */
651   if (!*new_one) { return true; }
652 
653   /*
654    * Same name
655    */
656   if (bstrcmp(new_one, jcr->impl->res.read_storage->resource_name_)) {
657     return true;
658   }
659 
660   new_store = (StorageResource*)my_config->GetResWithName(R_STORAGE, new_one);
661   if (!new_store) {
662     Jmsg(jcr, M_WARNING, 0, _("Could not get storage resource '%s'.\n"),
663          new_one);
664     return true;
665   }
666 
667   /*
668    * If Port and Hostname/IP are same, we are talking to the same
669    * Storage Daemon
670    */
671   if (jcr->impl->res.read_storage->SDport != new_store->SDport ||
672       !bstrcmp(jcr->impl->res.read_storage->address, new_store->address)) {
673     return false;
674   }
675 
676   return true;
677 }
678 
679 /**
680  * Check if the current line contains Storage="xxx", and compare the
681  * result to the current storage. We use UaContext to analyse the bsr
682  * string.
683  *
684  * Returns true if we need to change the storage, and it set the new
685  * Storage resource name in "storage" arg.
686  */
CheckForNewStorage(JobControlRecord * jcr,bootstrap_info & info)687 static inline bool CheckForNewStorage(JobControlRecord* jcr,
688                                       bootstrap_info& info)
689 {
690   UaContext* ua = info.ua;
691 
692   ParseUaArgs(ua);
693   if (ua->argc != 1) { return false; }
694 
695   if (Bstrcasecmp(ua->argk[0], "Storage")) {
696     /*
697      * Continue if this is a volume from the same storage.
698      */
699     if (IsOnSameStorage(jcr, ua->argv[0])) { return false; }
700 
701     /*
702      * Note the next storage name
703      */
704     bstrncpy(info.storage, ua->argv[0], MAX_NAME_LENGTH);
705     Dmsg1(5, "Change storage to %s\n", info.storage);
706     return true;
707   }
708 
709   return false;
710 }
711 
712 /**
713  * Send bootstrap file to Storage daemon section by section.
714  */
SendBootstrapFile(JobControlRecord * jcr,BareosSocket * sock,bootstrap_info & info)715 bool SendBootstrapFile(JobControlRecord* jcr,
716                        BareosSocket* sock,
717                        bootstrap_info& info)
718 {
719   boffset_t pos;
720   const char* bootstrap = "bootstrap\n";
721   UaContext* ua = info.ua;
722   FILE* bs = info.bs;
723 
724   Dmsg1(400, "SendBootstrapFile: %s\n", jcr->RestoreBootstrap);
725   if (!jcr->RestoreBootstrap) { return false; }
726 
727   sock->fsend(bootstrap);
728   pos = ftello(bs);
729 
730   while (fgets(ua->cmd, UA_CMD_SIZE, bs)) {
731     if (CheckForNewStorage(jcr, info)) {
732       /*
733        * Otherwise, we need to contact another storage daemon.
734        * Reset bs to the beginning of the current segment.
735        */
736       fseeko(bs, pos, SEEK_SET);
737       break;
738     }
739     sock->fsend("%s", ua->cmd);
740     pos = ftello(bs);
741   }
742 
743   sock->signal(BNET_EOD);
744 
745   return true;
746 }
747 
748 /**
749  * Clean the bootstrap_info struct
750  */
CloseBootstrapFile(bootstrap_info & info)751 void CloseBootstrapFile(bootstrap_info& info)
752 {
753   if (info.bs) { fclose(info.bs); }
754   if (info.ua) { FreeUaContext(info.ua); }
755 }
756 } /* namespace directordaemon */
757