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