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