1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5    Copyright (C) 2016-2016 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Kern Sibbald, June MMII
24  */
25 
26 /**
27  * @file
28  * Match Bootstrap Records (used for restores) against
29  * Volume Records
30  */
31 
32 /*
33  * ***FIXME***
34  * Also for efficiency, once a bsr is done, it really should be
35  *   delinked from the bsr chain.  This will avoid the above
36  *   problem and make traversal of the bsr chain more efficient.
37  *
38  *   To be done ...
39  */
40 
41 #include "include/bareos.h"
42 #include "stored/bsr.h"
43 #include "stored/jcr_private.h"
44 #include "stored/stored.h"
45 #include "include/jcr.h"
46 
47 namespace storagedaemon {
48 
49 const int dbglevel = 500;
50 
51 /* Forward references */
52 static int MatchVolume(BootStrapRecord* bsr,
53                        BsrVolume* volume,
54                        Volume_Label* volrec,
55                        bool done);
56 static int MatchSesstime(BootStrapRecord* bsr,
57                          BsrSessionTime* sesstime,
58                          DeviceRecord* rec,
59                          bool done);
60 static int MatchSessid(BootStrapRecord* bsr,
61                        BsrSessionId* sessid,
62                        DeviceRecord* rec);
63 static int MatchClient(BootStrapRecord* bsr,
64                        BsrClient* client,
65                        Session_Label* sessrec,
66                        bool done);
67 static int MatchJob(BootStrapRecord* bsr,
68                     BsrJob* job,
69                     Session_Label* sessrec,
70                     bool done);
71 static int MatchJobType(BootStrapRecord* bsr,
72                         BsrJobType* job_type,
73                         Session_Label* sessrec,
74                         bool done);
75 static int MatchJobLevel(BootStrapRecord* bsr,
76                          BsrJoblevel* job_level,
77                          Session_Label* sessrec,
78                          bool done);
79 static int MatchJobid(BootStrapRecord* bsr,
80                       BsrJobid* jobid,
81                       Session_Label* sessrec,
82                       bool done);
83 static int MatchFindex(BootStrapRecord* bsr,
84                        BsrFileIndex* findex,
85                        DeviceRecord* rec,
86                        bool done);
87 static int MatchVolfile(BootStrapRecord* bsr,
88                         BsrVolumeFile* volfile,
89                         DeviceRecord* rec,
90                         bool done);
91 static int MatchVoladdr(BootStrapRecord* bsr,
92                         BsrVolumeAddress* voladdr,
93                         DeviceRecord* rec,
94                         bool done);
95 static int MatchStream(BootStrapRecord* bsr,
96                        BsrStream* stream,
97                        DeviceRecord* rec,
98                        bool done);
99 static int MatchAll(BootStrapRecord* bsr,
100                     DeviceRecord* rec,
101                     Volume_Label* volrec,
102                     Session_Label* sessrec,
103                     bool done,
104                     JobControlRecord* jcr);
105 static int MatchBlockSesstime(BootStrapRecord* bsr,
106                               BsrSessionTime* sesstime,
107                               DeviceBlock* block);
108 static int MatchBlockSessid(BootStrapRecord* bsr,
109                             BsrSessionId* sessid,
110                             DeviceBlock* block);
111 static BootStrapRecord* find_smallest_volfile(BootStrapRecord* fbsr,
112                                               BootStrapRecord* bsr);
113 
114 /**
115  *
116  *  If possible, position the archive device (tape) to read the
117  *  next block.
118  */
PositionBsrBlock(BootStrapRecord * bsr,DeviceBlock * block)119 void PositionBsrBlock(BootStrapRecord* bsr, DeviceBlock* block)
120 {
121   /* To be implemented */
122 }
123 
124 /**
125  *
126  *  Do fast block rejection based on bootstrap records.
127  *    use_fast_rejection will be set if we have VolSessionId and VolSessTime
128  *    in each record. When BlockVer is >= 2, we have those in the block header
129  *    so can do fast rejection.
130  *
131  *   returns:  1 if block may contain valid records
132  *             0 if block may be skipped (i.e. it contains no records of
133  *                  that can match the bsr).
134  *
135  */
MatchBsrBlock(BootStrapRecord * bsr,DeviceBlock * block)136 int MatchBsrBlock(BootStrapRecord* bsr, DeviceBlock* block)
137 {
138   if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
139     return 1; /* cannot fast reject */
140   }
141 
142   for (; bsr; bsr = bsr->next) {
143     if (!MatchBlockSesstime(bsr, bsr->sesstime, block)) { continue; }
144     if (!MatchBlockSessid(bsr, bsr->sessid, block)) { continue; }
145     return 1;
146   }
147   return 0;
148 }
149 
MatchBlockSesstime(BootStrapRecord * bsr,BsrSessionTime * sesstime,DeviceBlock * block)150 static int MatchBlockSesstime(BootStrapRecord* bsr,
151                               BsrSessionTime* sesstime,
152                               DeviceBlock* block)
153 {
154   if (!sesstime) { return 1; /* no specification matches all */ }
155   if (sesstime->sesstime == block->VolSessionTime) { return 1; }
156   if (sesstime->next) { return MatchBlockSesstime(bsr, sesstime->next, block); }
157   return 0;
158 }
159 
MatchBlockSessid(BootStrapRecord * bsr,BsrSessionId * sessid,DeviceBlock * block)160 static int MatchBlockSessid(BootStrapRecord* bsr,
161                             BsrSessionId* sessid,
162                             DeviceBlock* block)
163 {
164   if (!sessid) { return 1; /* no specification matches all */ }
165   if (sessid->sessid <= block->VolSessionId &&
166       sessid->sessid2 >= block->VolSessionId) {
167     return 1;
168   }
169   if (sessid->next) { return MatchBlockSessid(bsr, sessid->next, block); }
170   return 0;
171 }
172 
MatchFileregex(BootStrapRecord * bsr,DeviceRecord * rec,JobControlRecord * jcr)173 static int MatchFileregex(BootStrapRecord* bsr,
174                           DeviceRecord* rec,
175                           JobControlRecord* jcr)
176 {
177   if (bsr->fileregex_re == NULL) return 1;
178 
179   if (bsr->attr == NULL) { bsr->attr = new_attr(jcr); }
180 
181   /*
182    * The code breaks if the first record associated with a file is
183    * not of this type
184    */
185   if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
186       rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
187     bsr->skip_file = false;
188     if (UnpackAttributesRecord(jcr, rec->Stream, rec->data, rec->data_len,
189                                bsr->attr)) {
190       if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
191         Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n", bsr->attr->fname,
192               rec->FileIndex);
193       } else {
194         Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
195               bsr->attr->fname, rec->FileIndex);
196         bsr->skip_file = true;
197       }
198     }
199   }
200   return 1;
201 }
202 
203 /**
204  *
205  *      Match Bootstrap records
206  *        returns  1 on match
207  *        returns  0 no match and Reposition is set if we should
208  *                      Reposition the tape
209  *       returns -1 no additional matches possible
210  */
MatchBsr(BootStrapRecord * bsr,DeviceRecord * rec,Volume_Label * volrec,Session_Label * sessrec,JobControlRecord * jcr)211 int MatchBsr(BootStrapRecord* bsr,
212              DeviceRecord* rec,
213              Volume_Label* volrec,
214              Session_Label* sessrec,
215              JobControlRecord* jcr)
216 {
217   int status;
218 
219   /*
220    * The bsr->Reposition flag is set any time a bsr is done.
221    *   In this case, we can probably Reposition the
222    *   tape to the next available bsr position.
223    */
224   if (bsr) {
225     bsr->Reposition = false;
226     status = MatchAll(bsr, rec, volrec, sessrec, true, jcr);
227     /*
228      * Note, bsr->Reposition is set by MatchAll when
229      *  a bsr is done. We turn it off if a match was
230      *  found or if we cannot use positioning
231      */
232     if (status != 0 || !bsr->use_positioning) { bsr->Reposition = false; }
233   } else {
234     status = 1; /* no bsr => match all */
235   }
236   return status;
237 }
238 
239 /**
240  * Find the next bsr that applies to the current tape.
241  *   It is the one with the smallest VolFile position.
242  */
find_next_bsr(BootStrapRecord * root_bsr,Device * dev)243 BootStrapRecord* find_next_bsr(BootStrapRecord* root_bsr, Device* dev)
244 {
245   BootStrapRecord* bsr;
246   BootStrapRecord* found_bsr = NULL;
247 
248   /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
249   if (!root_bsr) {
250     Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
251     return NULL;
252   }
253   if (!root_bsr->use_positioning || !root_bsr->Reposition ||
254       !dev->HasCap(CAP_POSITIONBLOCKS)) {
255     Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n",
256           root_bsr->use_positioning, root_bsr->Reposition);
257     return NULL;
258   }
259   Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning,
260         root_bsr->Reposition);
261   root_bsr->mount_next_volume = false;
262   /* Walk through all bsrs to find the next one to use => smallest file,block */
263   for (bsr = root_bsr; bsr; bsr = bsr->next) {
264     if (bsr->done || !MatchVolume(bsr, bsr->volume, &dev->VolHdr, 1)) {
265       continue;
266     }
267     if (found_bsr == NULL) {
268       found_bsr = bsr;
269     } else {
270       found_bsr = find_smallest_volfile(found_bsr, bsr);
271     }
272   }
273   /*
274    * If we get to this point and found no bsr, it means
275    *  that any additional bsr's must apply to the next
276    *  tape, so set a flag.
277    */
278   if (found_bsr == NULL) { root_bsr->mount_next_volume = true; }
279   return found_bsr;
280 }
281 
282 /**
283  * Get the smallest address from this voladdr part
284  * Don't use "done" elements
285  */
GetSmallestVoladdr(BsrVolumeAddress * va,uint64_t * ret)286 static bool GetSmallestVoladdr(BsrVolumeAddress* va, uint64_t* ret)
287 {
288   bool ok = false;
289   uint64_t min_val = 0;
290 
291   for (; va; va = va->next) {
292     if (!va->done) {
293       if (ok) {
294         min_val = MIN(min_val, va->saddr);
295       } else {
296         min_val = va->saddr;
297         ok = true;
298       }
299     }
300   }
301   *ret = min_val;
302   return ok;
303 }
304 
305 /* FIXME
306  * This routine needs to be fixed to only look at items that
307  *   are not marked as done.  Otherwise, it can find a bsr
308  *   that has already been consumed, and this will cause the
309  *   bsr to be used, thus we may seek back and re-read the
310  *   same records, causing an error.  This deficiency must
311  *   be fixed.  For the moment, it has been kludged in
312  *   read_record.c to avoid seeking back if find_next_bsr
313  *   returns a bsr pointing to a smaller address (file/block).
314  *
315  */
find_smallest_volfile(BootStrapRecord * found_bsr,BootStrapRecord * bsr)316 static BootStrapRecord* find_smallest_volfile(BootStrapRecord* found_bsr,
317                                               BootStrapRecord* bsr)
318 {
319   BootStrapRecord* return_bsr = found_bsr;
320   BsrVolumeFile* vf;
321   BsrVolumeBlock* vb;
322   uint32_t found_bsr_sfile, bsr_sfile;
323   uint32_t found_bsr_sblock, bsr_sblock;
324   uint64_t found_bsr_saddr, bsr_saddr;
325 
326   /* if we have VolAddr, use it, else try with File and Block */
327   if (GetSmallestVoladdr(found_bsr->voladdr, &found_bsr_saddr)) {
328     if (GetSmallestVoladdr(bsr->voladdr, &bsr_saddr)) {
329       if (found_bsr_saddr > bsr_saddr) {
330         return bsr;
331       } else {
332         return found_bsr;
333       }
334     }
335   }
336 
337   /* Find the smallest file in the found_bsr */
338   vf = found_bsr->volfile;
339   found_bsr_sfile = vf->sfile;
340   while ((vf = vf->next)) {
341     if (vf->sfile < found_bsr_sfile) { found_bsr_sfile = vf->sfile; }
342   }
343 
344   /* Find the smallest file in the bsr */
345   vf = bsr->volfile;
346   bsr_sfile = vf->sfile;
347   while ((vf = vf->next)) {
348     if (vf->sfile < bsr_sfile) { bsr_sfile = vf->sfile; }
349   }
350 
351   /* if the bsr file is less than the found_bsr file, return bsr */
352   if (found_bsr_sfile > bsr_sfile) {
353     return_bsr = bsr;
354   } else if (found_bsr_sfile == bsr_sfile) {
355     /* Files are equal */
356     /* find smallest block in found_bsr */
357     vb = found_bsr->volblock;
358     found_bsr_sblock = vb->sblock;
359     while ((vb = vb->next)) {
360       if (vb->sblock < found_bsr_sblock) { found_bsr_sblock = vb->sblock; }
361     }
362     /* Find smallest block in bsr */
363     vb = bsr->volblock;
364     bsr_sblock = vb->sblock;
365     while ((vb = vb->next)) {
366       if (vb->sblock < bsr_sblock) { bsr_sblock = vb->sblock; }
367     }
368     /* Compare and return the smallest */
369     if (found_bsr_sblock > bsr_sblock) { return_bsr = bsr; }
370   }
371   return return_bsr;
372 }
373 
374 /**
375  * Called after the signature record so that
376  *   we can see if the current bsr has been
377  *   fully processed (i.e. is done).
378  *  The bsr argument is not used, but is included
379  *    for consistency with the other match calls.
380  *
381  * Returns: true if we should reposition
382  *        : false otherwise.
383  */
IsThisBsrDone(BootStrapRecord * bsr,DeviceRecord * rec)384 bool IsThisBsrDone(BootStrapRecord* bsr, DeviceRecord* rec)
385 {
386   BootStrapRecord* rbsr = rec->bsr;
387   Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
388   if (!rbsr) { return false; }
389   rec->bsr = NULL;
390   rbsr->found++;
391   if (rbsr->count && rbsr->found >= rbsr->count) {
392     rbsr->done = true;
393     rbsr->root->Reposition = true;
394     Dmsg2(dbglevel, "is_end_this_bsr set Reposition=1 count=%d found=%d\n",
395           rbsr->count, rbsr->found);
396     return true;
397   }
398   Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n", rbsr->count,
399         rbsr->found);
400   return false;
401 }
402 
403 /**
404  * Match all the components of current record
405  *   returns  1 on match
406  *   returns  0 no match
407  *   returns -1 no additional matches possible
408  */
MatchAll(BootStrapRecord * bsr,DeviceRecord * rec,Volume_Label * volrec,Session_Label * sessrec,bool done,JobControlRecord * jcr)409 static int MatchAll(BootStrapRecord* bsr,
410                     DeviceRecord* rec,
411                     Volume_Label* volrec,
412                     Session_Label* sessrec,
413                     bool done,
414                     JobControlRecord* jcr)
415 {
416   Dmsg0(dbglevel, "Enter MatchAll\n");
417   if (bsr->done) {
418     //    Dmsg0(dbglevel, "bsr->done set\n");
419     goto no_match;
420   }
421   if (!MatchVolume(bsr, bsr->volume, volrec, 1)) {
422     Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n",
423           bsr->volume->VolumeName, volrec->VolumeName);
424     goto no_match;
425   }
426   Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n",
427         bsr->volume->VolumeName, volrec->VolumeName);
428 
429   if (!MatchVolfile(bsr, bsr->volfile, rec, 1)) {
430     if (bsr->volfile) {
431       Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n", rec->File,
432             bsr->volfile->sfile, bsr->volfile->efile);
433     }
434     goto no_match;
435   }
436 
437   if (!MatchVoladdr(bsr, bsr->voladdr, rec, 1)) {
438     if (bsr->voladdr) {
439       Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
440             GetRecordAddress(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
441     }
442     goto no_match;
443   }
444 
445   if (!MatchSesstime(bsr, bsr->sesstime, rec, 1)) {
446     Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
447           bsr->sesstime->sesstime, rec->VolSessionTime);
448     goto no_match;
449   }
450 
451   /* NOTE!! This test MUST come after the sesstime test */
452   if (!MatchSessid(bsr, bsr->sessid, rec)) {
453     Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n", bsr->sessid->sessid,
454           rec->VolSessionId);
455     goto no_match;
456   }
457 
458   /* NOTE!! This test MUST come after sesstime and sessid tests */
459   if (!MatchFindex(bsr, bsr->FileIndex, rec, 1)) {
460     if (bsr->FileIndex) {
461       Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n", rec->FileIndex,
462             bsr->FileIndex->findex, bsr->FileIndex->findex2);
463     } else {
464       Dmsg0(dbglevel, "No bsr->FileIndex!\n");
465     }
466     goto no_match;
467   }
468   Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n", rec->FileIndex,
469         bsr->FileIndex->findex, bsr->FileIndex->findex2);
470 
471   if (!MatchFileregex(bsr, rec, jcr)) {
472     Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
473     goto no_match;
474   }
475 
476   /* This flag is set by MatchFileregex (and perhaps other tests) */
477   if (bsr->skip_file) {
478     Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
479     goto no_match;
480   }
481 
482   /*
483    * If a count was specified and we have a FileIndex, assume
484    *   it is a Bareos created bsr (or the equivalent). We
485    *   then save the bsr where the match occurred so that
486    *   after processing the record or records, we can update
487    *   the found count. I.e. rec->bsr points to the bsr that
488    *   satisfied the match.
489    */
490   if (bsr->count && bsr->FileIndex) {
491     rec->bsr = bsr;
492     Dmsg0(dbglevel, "Leave MatchAll 1\n");
493     return 1; /* this is a complete match */
494   }
495 
496   /*
497    * The selections below are not used by Bareos's
498    *   restore command, and don't work because of
499    *   the rec->bsr = bsr optimization above.
500    */
501   if (!MatchJobid(bsr, bsr->JobId, sessrec, 1)) {
502     Dmsg0(dbglevel, "fail on JobId\n");
503     goto no_match;
504   }
505   if (!MatchJob(bsr, bsr->job, sessrec, 1)) {
506     Dmsg0(dbglevel, "fail on Job\n");
507     goto no_match;
508   }
509   if (!MatchClient(bsr, bsr->client, sessrec, 1)) {
510     Dmsg0(dbglevel, "fail on Client\n");
511     goto no_match;
512   }
513   if (!MatchJobType(bsr, bsr->JobType, sessrec, 1)) {
514     Dmsg0(dbglevel, "fail on Job type\n");
515     goto no_match;
516   }
517   if (!MatchJobLevel(bsr, bsr->JobLevel, sessrec, 1)) {
518     Dmsg0(dbglevel, "fail on Job level\n");
519     goto no_match;
520   }
521   if (!MatchStream(bsr, bsr->stream, rec, 1)) {
522     Dmsg0(dbglevel, "fail on stream\n");
523     goto no_match;
524   }
525   return 1;
526 
527 no_match:
528   if (bsr->next) {
529     return MatchAll(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
530   }
531   if (bsr->done && done) {
532     Dmsg0(dbglevel, "Leave match all -1\n");
533     return -1;
534   }
535   Dmsg0(dbglevel, "Leave match all 0\n");
536   return 0;
537 }
538 
MatchVolume(BootStrapRecord * bsr,BsrVolume * volume,Volume_Label * volrec,bool done)539 static int MatchVolume(BootStrapRecord* bsr,
540                        BsrVolume* volume,
541                        Volume_Label* volrec,
542                        bool done)
543 {
544   if (!volume) { return 0; /* Volume must match */ }
545   if (bstrcmp(volume->VolumeName, volrec->VolumeName)) {
546     Dmsg1(dbglevel, "MatchVolume=%s\n", volrec->VolumeName);
547     return 1;
548   }
549   if (volume->next) { return MatchVolume(bsr, volume->next, volrec, 1); }
550   return 0;
551 }
552 
MatchClient(BootStrapRecord * bsr,BsrClient * client,Session_Label * sessrec,bool done)553 static int MatchClient(BootStrapRecord* bsr,
554                        BsrClient* client,
555                        Session_Label* sessrec,
556                        bool done)
557 {
558   if (!client) { return 1; /* no specification matches all */ }
559   if (bstrcmp(client->ClientName, sessrec->ClientName)) { return 1; }
560   if (client->next) { return MatchClient(bsr, client->next, sessrec, 1); }
561   return 0;
562 }
563 
MatchJob(BootStrapRecord * bsr,BsrJob * job,Session_Label * sessrec,bool done)564 static int MatchJob(BootStrapRecord* bsr,
565                     BsrJob* job,
566                     Session_Label* sessrec,
567                     bool done)
568 {
569   if (!job) { return 1; /* no specification matches all */ }
570   if (bstrcmp(job->Job, sessrec->Job)) { return 1; }
571   if (job->next) { return MatchJob(bsr, job->next, sessrec, 1); }
572   return 0;
573 }
574 
MatchJobType(BootStrapRecord * bsr,BsrJobType * job_type,Session_Label * sessrec,bool done)575 static int MatchJobType(BootStrapRecord* bsr,
576                         BsrJobType* job_type,
577                         Session_Label* sessrec,
578                         bool done)
579 {
580   if (!job_type) { return 1; /* no specification matches all */ }
581   if (job_type->JobType == sessrec->JobType) { return 1; }
582   if (job_type->next) { return MatchJobType(bsr, job_type->next, sessrec, 1); }
583   return 0;
584 }
585 
MatchJobLevel(BootStrapRecord * bsr,BsrJoblevel * job_level,Session_Label * sessrec,bool done)586 static int MatchJobLevel(BootStrapRecord* bsr,
587                          BsrJoblevel* job_level,
588                          Session_Label* sessrec,
589                          bool done)
590 {
591   if (!job_level) { return 1; /* no specification matches all */ }
592   if (job_level->JobLevel == sessrec->JobLevel) { return 1; }
593   if (job_level->next) {
594     return MatchJobLevel(bsr, job_level->next, sessrec, 1);
595   }
596   return 0;
597 }
598 
MatchJobid(BootStrapRecord * bsr,BsrJobid * jobid,Session_Label * sessrec,bool done)599 static int MatchJobid(BootStrapRecord* bsr,
600                       BsrJobid* jobid,
601                       Session_Label* sessrec,
602                       bool done)
603 {
604   if (!jobid) { return 1; /* no specification matches all */ }
605   if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
606     return 1;
607   }
608   if (jobid->next) { return MatchJobid(bsr, jobid->next, sessrec, 1); }
609   return 0;
610 }
611 
MatchVolfile(BootStrapRecord * bsr,BsrVolumeFile * volfile,DeviceRecord * rec,bool done)612 static int MatchVolfile(BootStrapRecord* bsr,
613                         BsrVolumeFile* volfile,
614                         DeviceRecord* rec,
615                         bool done)
616 {
617   if (!volfile) { return 1; /* no specification matches all */ }
618   /**
619    * The following code is turned off because this should now work
620    *   with disk files too, though since a "volfile" is 4GB, it does
621    *   not improve performance much.
622    */
623   if (volfile->sfile <= rec->File && volfile->efile >= rec->File) { return 1; }
624   /* Once we get past last efile, we are done */
625   if (rec->File > volfile->efile) { volfile->done = true; /* set local done */ }
626   if (volfile->next) {
627     return MatchVolfile(bsr, volfile->next, rec, volfile->done && done);
628   }
629 
630   /* If we are done and all prior matches are done, this bsr is finished */
631   if (volfile->done && done) {
632     bsr->done = true;
633     bsr->root->Reposition = true;
634     Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n", rec->File,
635           volfile->efile);
636   }
637   return 0;
638 }
639 
MatchVoladdr(BootStrapRecord * bsr,BsrVolumeAddress * voladdr,DeviceRecord * rec,bool done)640 static int MatchVoladdr(BootStrapRecord* bsr,
641                         BsrVolumeAddress* voladdr,
642                         DeviceRecord* rec,
643                         bool done)
644 {
645   if (!voladdr) { return 1; /* no specification matches all */ }
646 
647   uint64_t addr = GetRecordAddress(rec);
648   Dmsg6(dbglevel,
649         "MatchVoladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u "
650         "recfile=%u\n",
651         voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr >> 32,
652         voladdr->eaddr >> 32, addr >> 32);
653 
654   if (voladdr->saddr <= addr && voladdr->eaddr >= addr) { return 1; }
655   /* Once we get past last eblock, we are done */
656   if (addr > voladdr->eaddr) { voladdr->done = true; /* set local done */ }
657   if (voladdr->next) {
658     return MatchVoladdr(bsr, voladdr->next, rec, voladdr->done && done);
659   }
660 
661   /* If we are done and all prior matches are done, this bsr is finished */
662   if (voladdr->done && done) {
663     bsr->done = true;
664     bsr->root->Reposition = true;
665     Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n", addr,
666           voladdr->eaddr);
667   }
668   return 0;
669 }
670 
671 
MatchStream(BootStrapRecord * bsr,BsrStream * stream,DeviceRecord * rec,bool done)672 static int MatchStream(BootStrapRecord* bsr,
673                        BsrStream* stream,
674                        DeviceRecord* rec,
675                        bool done)
676 {
677   if (!stream) { return 1; /* no specification matches all */ }
678   if (stream->stream == rec->Stream) { return 1; }
679   if (stream->next) { return MatchStream(bsr, stream->next, rec, 1); }
680   return 0;
681 }
682 
MatchSesstime(BootStrapRecord * bsr,BsrSessionTime * sesstime,DeviceRecord * rec,bool done)683 static int MatchSesstime(BootStrapRecord* bsr,
684                          BsrSessionTime* sesstime,
685                          DeviceRecord* rec,
686                          bool done)
687 {
688   if (!sesstime) { return 1; /* no specification matches all */ }
689   if (sesstime->sesstime == rec->VolSessionTime) { return 1; }
690   if (rec->VolSessionTime > sesstime->sesstime) { sesstime->done = true; }
691   if (sesstime->next) {
692     return MatchSesstime(bsr, sesstime->next, rec, sesstime->done && done);
693   }
694   if (sesstime->done && done) {
695     bsr->done = true;
696     bsr->root->Reposition = true;
697     Dmsg0(dbglevel, "bsr done from sesstime\n");
698   }
699   return 0;
700 }
701 
702 /**
703  * Note, we cannot mark bsr done based on session id because we may
704  *  have interleaved records, and there may be more of what we want
705  *  later.
706  */
MatchSessid(BootStrapRecord * bsr,BsrSessionId * sessid,DeviceRecord * rec)707 static int MatchSessid(BootStrapRecord* bsr,
708                        BsrSessionId* sessid,
709                        DeviceRecord* rec)
710 {
711   if (!sessid) { return 1; /* no specification matches all */ }
712   if (sessid->sessid <= rec->VolSessionId &&
713       sessid->sessid2 >= rec->VolSessionId) {
714     return 1;
715   }
716   if (sessid->next) { return MatchSessid(bsr, sessid->next, rec); }
717   return 0;
718 }
719 
720 /**
721  * When reading the Volume, the Volume Findex (rec->FileIndex) always
722  *   are found in sequential order. Thus we can make optimizations.
723  *
724  *  ***FIXME*** optimizations
725  * We could optimize by removing the recursion.
726  */
MatchFindex(BootStrapRecord * bsr,BsrFileIndex * findex,DeviceRecord * rec,bool done)727 static int MatchFindex(BootStrapRecord* bsr,
728                        BsrFileIndex* findex,
729                        DeviceRecord* rec,
730                        bool done)
731 {
732   if (!findex) { return 1; /* no specification matches all */ }
733   if (!findex->done) {
734     if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
735       Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n", rec->FileIndex,
736             findex->findex, findex->findex2);
737       return 1;
738     }
739     if (rec->FileIndex > findex->findex2) { findex->done = true; }
740   }
741   if (findex->next) {
742     return MatchFindex(bsr, findex->next, rec, findex->done && done);
743   }
744   if (findex->done && done) {
745     bsr->done = true;
746     bsr->root->Reposition = true;
747     Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
748   }
749   return 0;
750 }
751 
GetBsrStartAddr(BootStrapRecord * bsr,uint32_t * file,uint32_t * block)752 uint64_t GetBsrStartAddr(BootStrapRecord* bsr, uint32_t* file, uint32_t* block)
753 {
754   uint64_t bsr_addr = 0;
755   uint32_t sfile = 0, sblock = 0;
756 
757   if (bsr) {
758     if (bsr->voladdr) {
759       bsr_addr = bsr->voladdr->saddr;
760       sfile = bsr_addr >> 32;
761       sblock = (uint32_t)bsr_addr;
762 
763     } else if (bsr->volfile && bsr->volblock) {
764       bsr_addr =
765           (((uint64_t)bsr->volfile->sfile) << 32) | bsr->volblock->sblock;
766       sfile = bsr->volfile->sfile;
767       sblock = bsr->volblock->sblock;
768     }
769   }
770 
771   if (file && block) {
772     *file = sfile;
773     *block = sblock;
774   }
775 
776   return bsr_addr;
777 }
778 
779 /* ****************************************************************
780  * Routines for handling volumes
781  */
new_restore_volume()782 static VolumeList* new_restore_volume()
783 {
784   VolumeList* vol;
785   vol = (VolumeList*)malloc(sizeof(VolumeList));
786   memset(vol, 0, sizeof(VolumeList));
787   return vol;
788 }
789 
790 /**
791  * Add current volume to end of list, only if the Volume
792  * is not already in the list.
793  *
794  *   returns: 1 if volume added
795  *            0 if volume already in list
796  */
AddRestoreVolume(JobControlRecord * jcr,VolumeList * vol)797 static bool AddRestoreVolume(JobControlRecord* jcr, VolumeList* vol)
798 {
799   VolumeList* next = jcr->impl->VolList;
800 
801   /* Add volume to volume manager's read list */
802   AddReadVolume(jcr, vol->VolumeName);
803 
804   if (!next) {                /* list empty ? */
805     jcr->impl->VolList = vol; /* yes, add volume */
806   } else {
807     /* Loop through all but last */
808     for (; next->next; next = next->next) {
809       if (bstrcmp(vol->VolumeName, next->VolumeName)) {
810         /* Save smallest start file */
811         if (vol->start_file < next->start_file) {
812           next->start_file = vol->start_file;
813         }
814         return false; /* already in list */
815       }
816     }
817     /* Check last volume in list */
818     if (bstrcmp(vol->VolumeName, next->VolumeName)) {
819       if (vol->start_file < next->start_file) {
820         next->start_file = vol->start_file;
821       }
822       return false; /* already in list */
823     }
824     next->next = vol; /* add volume */
825   }
826   return true;
827 }
828 
829 /**
830  * Create a list of Volumes (and Slots and Start positions) to be
831  *  used in the current restore job.
832  */
CreateRestoreVolumeList(JobControlRecord * jcr)833 void CreateRestoreVolumeList(JobControlRecord* jcr)
834 {
835   char *p, *n;
836   VolumeList* vol;
837 
838   /*
839    * Build a list of volumes to be processed
840    */
841   jcr->impl->NumReadVolumes = 0;
842   jcr->impl->CurReadVolume = 0;
843   if (jcr->impl->read_session.bsr) {
844     BootStrapRecord* bsr = jcr->impl->read_session.bsr;
845     if (!bsr->volume || !bsr->volume->VolumeName[0]) { return; }
846     for (; bsr; bsr = bsr->next) {
847       BsrVolume* bsrvol;
848       BsrVolumeFile* volfile;
849       uint32_t sfile = UINT32_MAX;
850 
851       /* Find minimum start file so that we can forward space to it */
852       for (volfile = bsr->volfile; volfile; volfile = volfile->next) {
853         if (volfile->sfile < sfile) { sfile = volfile->sfile; }
854       }
855       /* Now add volumes for this bsr */
856       for (bsrvol = bsr->volume; bsrvol; bsrvol = bsrvol->next) {
857         vol = new_restore_volume();
858         bstrncpy(vol->VolumeName, bsrvol->VolumeName, sizeof(vol->VolumeName));
859         bstrncpy(vol->MediaType, bsrvol->MediaType, sizeof(vol->MediaType));
860         bstrncpy(vol->device, bsrvol->device, sizeof(vol->device));
861         vol->Slot = bsrvol->Slot;
862         vol->start_file = sfile;
863         if (AddRestoreVolume(jcr, vol)) {
864           jcr->impl->NumReadVolumes++;
865           Dmsg2(400, "Added volume=%s mediatype=%s\n", vol->VolumeName,
866                 vol->MediaType);
867         } else {
868           Dmsg1(400, "Duplicate volume %s\n", vol->VolumeName);
869           free((char*)vol);
870         }
871         sfile = 0; /* start at beginning of second volume */
872       }
873     }
874   } else {
875     /* This is the old way -- deprecated */
876     for (p = jcr->impl->dcr->VolumeName; p && *p;) {
877       n = strchr(p, '|'); /* volume name separator */
878       if (n) { *n++ = 0; /* Terminate name */ }
879       vol = new_restore_volume();
880       bstrncpy(vol->VolumeName, p, sizeof(vol->VolumeName));
881       bstrncpy(vol->MediaType, jcr->impl->dcr->media_type,
882                sizeof(vol->MediaType));
883       if (AddRestoreVolume(jcr, vol)) {
884         jcr->impl->NumReadVolumes++;
885       } else {
886         free((char*)vol);
887       }
888       p = n;
889     }
890   }
891 }
892 
FreeRestoreVolumeList(JobControlRecord * jcr)893 void FreeRestoreVolumeList(JobControlRecord* jcr)
894 {
895   VolumeList* vol = jcr->impl->VolList;
896   VolumeList* tmp;
897 
898   for (; vol;) {
899     tmp = vol->next;
900     RemoveReadVolume(jcr, vol->VolumeName);
901     free(vol);
902     vol = tmp;
903   }
904   jcr->impl->VolList = NULL;
905 }
906 
907 } /* namespace storagedaemon */
908