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