]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Change copyright as per agreement with FSFE
[bacula/bacula] / bacula / src / stored / match_bsr.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Match Bootstrap Records (used for restores) against
21  *     Volume Records
22  *
23  *     Kern Sibbald, June MMII
24  *
25  */
26
27 /*
28  * ***FIXME***
29  * Also for efficiency, once a bsr is done, it really should be
30  *   delinked from the bsr chain.  This will avoid the above
31  *   problem and make traversal of the bsr chain more efficient.
32  *
33  *   To be done ...
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38 #ifdef HAVE_FNMATCH
39 #include <fnmatch.h>
40 #else
41 #include "lib/fnmatch.h"
42 #endif
43
44 const int dbglevel = 500;
45
46 /* Forward references */
47 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done);
48 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done);
49 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec);
50 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done);
51 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done);
52 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done);
53 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done);
54 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done);
55 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done);
56 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done);
57 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done);
58 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done);
59 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done, JCR *jcr);
60 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
61 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
62 static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
63
64
65 /*********************************************************************
66  *
67  *  If possible, position the archive device (tape) to read the
68  *  next block.
69  */
70 void position_bsr_block(BSR *bsr, DEV_BLOCK *block)
71 {
72    /* To be implemented */
73 }
74
75 /*********************************************************************
76  *
77  *  Do fast block rejection based on bootstrap records.
78  *    use_fast_rejection will be set if we have VolSessionId and VolSessTime
79  *    in each record. When BlockVer is >= 2, we have those in the block header
80  *    so can do fast rejection.
81  *
82  *   returns:  1 if block may contain valid records
83  *             0 if block may be skipped (i.e. it contains no records of
84  *                  that can match the bsr).
85  *
86  */
87 int match_bsr_block(BSR *bsr, DEV_BLOCK *block)
88 {
89    if (!bsr || !bsr->use_fast_rejection || (block->BlockVer < 2)) {
90       return 1;                       /* cannot fast reject */
91    }
92
93    for ( ; bsr; bsr=bsr->next) {
94       if (!match_block_sesstime(bsr, bsr->sesstime, block)) {
95          continue;
96       }
97       if (!match_block_sessid(bsr, bsr->sessid, block)) {
98          continue;
99       }
100       return 1;
101    }
102    return 0;
103 }
104
105 static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block)
106 {
107    if (!sesstime) {
108       return 1;                       /* no specification matches all */
109    }
110    if (sesstime->sesstime == block->VolSessionTime) {
111       return 1;
112    }
113    if (sesstime->next) {
114       return match_block_sesstime(bsr, sesstime->next, block);
115    }
116    return 0;
117 }
118
119 static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block)
120 {
121    if (!sessid) {
122       return 1;                       /* no specification matches all */
123    }
124    if (sessid->sessid <= block->VolSessionId && sessid->sessid2 >= block->VolSessionId) {
125       return 1;
126    }
127    if (sessid->next) {
128       return match_block_sessid(bsr, sessid->next, block);
129    }
130    return 0;
131 }
132
133 static int match_fileregex(BSR *bsr, DEV_RECORD *rec, JCR *jcr)
134 {
135    if (bsr->fileregex_re == NULL)
136       return 1;
137
138    if (bsr->attr == NULL) {
139       bsr->attr = new_attr(jcr);
140    }
141
142    /*
143     * The code breaks if the first record associated with a file is
144     * not of this type
145     */
146    if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
147        rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
148       bsr->skip_file = false;
149       if (unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, bsr->attr)) {
150          if (regexec(bsr->fileregex_re, bsr->attr->fname, 0, NULL, 0) == 0) {
151             Dmsg2(dbglevel, "Matched pattern, fname=%s FI=%d\n",
152                   bsr->attr->fname, rec->FileIndex);
153          } else {
154             Dmsg2(dbglevel, "Didn't match, skipping fname=%s FI=%d\n",
155                   bsr->attr->fname, rec->FileIndex);
156             bsr->skip_file = true;
157          }
158       }
159    }
160    return 1;
161 }
162
163 /*********************************************************************
164  *
165  *      Match Bootstrap records
166  *        returns  1 on match
167  *        returns  0 no match and reposition is set if we should
168  *                      reposition the tape
169  *       returns -1 no additional matches possible
170  */
171 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, JCR *jcr)
172 {
173    int stat;
174
175    /*
176     * The bsr->reposition flag is set any time a bsr is done.
177     *   In this case, we can probably reposition the
178     *   tape to the next available bsr position.
179     */
180    if (bsr) {
181       bsr->reposition = false;
182       stat = match_all(bsr, rec, volrec, sessrec, true, jcr);
183       /*
184        * Note, bsr->reposition is set by match_all when
185        *  a bsr is done. We turn it off if a match was
186        *  found or if we cannot use positioning
187        */
188       if (stat != 0 || !bsr->use_positioning) {
189          bsr->reposition = false;
190       }
191    } else {
192       stat = 1;                       /* no bsr => match all */
193    }
194    return stat;
195 }
196
197 /*
198  * Find the next bsr that applies to the current tape.
199  *   It is the one with the smallest VolFile position.
200  */
201 BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
202 {
203    BSR *bsr;
204    BSR *found_bsr = NULL;
205
206    /* Do tape/disk seeking only if CAP_POSITIONBLOCKS is on */
207    if (!root_bsr) {
208       Dmsg0(dbglevel, "NULL root bsr pointer passed to find_next_bsr.\n");
209       return NULL;
210    }
211    if (!root_bsr->use_positioning ||
212        !root_bsr->reposition || !dev->has_cap(CAP_POSITIONBLOCKS)) {
213       Dmsg2(dbglevel, "No nxt_bsr use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
214       return NULL;
215    }
216    Dmsg2(dbglevel, "use_pos=%d repos=%d\n", root_bsr->use_positioning, root_bsr->reposition);
217    root_bsr->mount_next_volume = false;
218    /* Walk through all bsrs to find the next one to use => smallest file,block */
219    for (bsr=root_bsr; bsr; bsr=bsr->next) {
220       if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
221          continue;
222       }
223       if (found_bsr == NULL) {
224          found_bsr = bsr;
225       } else {
226          found_bsr = find_smallest_volfile(found_bsr, bsr);
227       }
228    }
229    /*
230     * If we get to this point and found no bsr, it means
231     *  that any additional bsr's must apply to the next
232     *  tape, so set a flag.
233     */
234    if (found_bsr == NULL) {
235       root_bsr->mount_next_volume = true;
236    }
237    return found_bsr;
238 }
239
240 /*
241  * Get the smallest address from this voladdr part
242  * Don't use "done" elements
243  */
244 static bool get_smallest_voladdr(BSR_VOLADDR *va, uint64_t *ret)
245 {
246    bool ok=false;
247    uint64_t min_val=0;
248
249    for (; va ; va = va->next) {
250       if (!va->done) {
251          if (ok) {
252             min_val = MIN(min_val, va->saddr);
253          } else {
254             min_val = va->saddr;
255             ok=true;
256          }
257       }
258    }
259    *ret = min_val;
260    return ok;
261 }
262
263 /* FIXME
264  * This routine needs to be fixed to only look at items that
265  *   are not marked as done.  Otherwise, it can find a bsr
266  *   that has already been consumed, and this will cause the
267  *   bsr to be used, thus we may seek back and re-read the
268  *   same records, causing an error.  This deficiency must
269  *   be fixed.  For the moment, it has been kludged in
270  *   read_record.c to avoid seeking back if find_next_bsr
271  *   returns a bsr pointing to a smaller address (file/block).
272  *
273  */
274 static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
275 {
276    BSR *return_bsr = found_bsr;
277    BSR_VOLFILE *vf;
278    BSR_VOLBLOCK *vb;
279    uint32_t found_bsr_sfile, bsr_sfile;
280    uint32_t found_bsr_sblock, bsr_sblock;
281    uint64_t found_bsr_saddr, bsr_saddr;
282
283    /* if we have VolAddr, use it, else try with File and Block */
284    if (get_smallest_voladdr(found_bsr->voladdr, &found_bsr_saddr)) {
285       if (get_smallest_voladdr(bsr->voladdr, &bsr_saddr)) {
286          if (found_bsr_saddr > bsr_saddr) {
287             return bsr;
288          } else {
289             return found_bsr;
290          }
291       }
292    }
293
294    /* Find the smallest file in the found_bsr */
295    vf = found_bsr->volfile;
296    found_bsr_sfile = vf->sfile;
297    while ( (vf=vf->next) ) {
298       if (vf->sfile < found_bsr_sfile) {
299          found_bsr_sfile = vf->sfile;
300       }
301    }
302
303    /* Find the smallest file in the bsr */
304    vf = bsr->volfile;
305    bsr_sfile = vf->sfile;
306    while ( (vf=vf->next) ) {
307       if (vf->sfile < bsr_sfile) {
308          bsr_sfile = vf->sfile;
309       }
310    }
311
312    /* if the bsr file is less than the found_bsr file, return bsr */
313    if (found_bsr_sfile > bsr_sfile) {
314       return_bsr = bsr;
315    } else if (found_bsr_sfile == bsr_sfile) {
316       /* Files are equal */
317       /* find smallest block in found_bsr */
318       vb = found_bsr->volblock;
319       found_bsr_sblock = vb->sblock;
320       while ( (vb=vb->next) ) {
321          if (vb->sblock < found_bsr_sblock) {
322             found_bsr_sblock = vb->sblock;
323          }
324       }
325       /* Find smallest block in bsr */
326       vb = bsr->volblock;
327       bsr_sblock = vb->sblock;
328       while ( (vb=vb->next) ) {
329          if (vb->sblock < bsr_sblock) {
330             bsr_sblock = vb->sblock;
331          }
332       }
333       /* Compare and return the smallest */
334       if (found_bsr_sblock > bsr_sblock) {
335          return_bsr = bsr;
336       }
337    }
338    return return_bsr;
339 }
340
341 /*
342  * Called after the signature record so that
343  *   we can see if the current bsr has been
344  *   fully processed (i.e. is done).
345  *  The bsr argument is not used, but is included
346  *    for consistency with the other match calls.
347  *
348  * Returns: true if we should reposition
349  *        : false otherwise.
350  */
351 bool is_this_bsr_done(BSR *bsr, DEV_RECORD *rec)
352 {
353    BSR *rbsr = rec->bsr;
354    Dmsg1(dbglevel, "match_set %d\n", rbsr != NULL);
355    if (!rbsr) {
356       return false;
357    }
358    rec->bsr = NULL;
359    rbsr->found++;
360    if (rbsr->count && rbsr->found >= rbsr->count) {
361       rbsr->done = true;
362       rbsr->root->reposition = true;
363       Dmsg2(dbglevel, "is_end_this_bsr set reposition=1 count=%d found=%d\n",
364          rbsr->count, rbsr->found);
365       return true;
366    }
367    Dmsg2(dbglevel, "is_end_this_bsr not done count=%d found=%d\n",
368         rbsr->count, rbsr->found);
369    return false;
370 }
371
372 /*
373  * Match all the components of current record
374  *   returns  1 on match
375  *   returns  0 no match
376  *   returns -1 no additional matches possible
377  */
378 static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
379                      SESSION_LABEL *sessrec, bool done, JCR *jcr)
380 {
381    Dmsg0(dbglevel, "Enter match_all\n");
382    if (bsr->done) {
383 //    Dmsg0(dbglevel, "bsr->done set\n");
384       goto no_match;
385    }
386    if (!match_volume(bsr, bsr->volume, volrec, 1)) {
387       Dmsg2(dbglevel, "bsr fail bsr_vol=%s != rec read_vol=%s\n", bsr->volume->VolumeName,
388             volrec->VolumeName);
389       goto no_match;
390    }
391    Dmsg2(dbglevel, "OK bsr match bsr_vol=%s read_vol=%s\n", bsr->volume->VolumeName,
392             volrec->VolumeName);
393
394    if (!match_volfile(bsr, bsr->volfile, rec, 1)) {
395       if (bsr->volfile) {
396          Dmsg3(dbglevel, "Fail on file=%u. bsr=%u,%u\n",
397                rec->File, bsr->volfile->sfile, bsr->volfile->efile);
398       }
399       goto no_match;
400    }
401
402    if (!match_voladdr(bsr, bsr->voladdr, rec, 1)) {
403       if (bsr->voladdr) {
404          Dmsg3(dbglevel, "Fail on Addr=%llu. bsr=%llu,%llu\n",
405                get_record_address(rec), bsr->voladdr->saddr, bsr->voladdr->eaddr);
406       }
407       goto no_match;
408    }
409
410    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
411       Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
412          bsr->sesstime->sesstime, rec->VolSessionTime);
413       goto no_match;
414    }
415
416    /* NOTE!! This test MUST come after the sesstime test */
417    if (!match_sessid(bsr, bsr->sessid, rec)) {
418       Dmsg2(dbglevel, "Fail on sessid. bsr=%u rec=%u\n",
419          bsr->sessid->sessid, rec->VolSessionId);
420       goto no_match;
421    }
422
423    /* NOTE!! This test MUST come after sesstime and sessid tests */
424    if (!match_findex(bsr, bsr->FileIndex, rec, 1)) {
425       Dmsg3(dbglevel, "Fail on findex=%d. bsr=%d,%d\n",
426          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
427       goto no_match;
428    }
429    if (bsr->FileIndex) {
430       Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
431             rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
432    }
433
434    if (!match_fileregex(bsr, rec, jcr)) {
435      Dmsg1(dbglevel, "Fail on fileregex='%s'\n", NPRT(bsr->fileregex));
436      goto no_match;
437    }
438
439    /* This flag is set by match_fileregex (and perhaps other tests) */
440    if (bsr->skip_file) {
441       Dmsg1(dbglevel, "Skipping findex=%d\n", rec->FileIndex);
442       goto no_match;
443    }
444
445    /*
446     * If a count was specified and we have a FileIndex, assume
447     *   it is a Bacula created bsr (or the equivalent). We
448     *   then save the bsr where the match occurred so that
449     *   after processing the record or records, we can update
450     *   the found count. I.e. rec->bsr points to the bsr that
451     *   satisfied the match.
452     */
453    if (bsr->count && bsr->FileIndex) {
454       rec->bsr = bsr;
455       Dmsg0(dbglevel, "Leave match_all 1\n");
456       return 1;                       /* this is a complete match */
457    }
458
459    /*
460     * The selections below are not used by Bacula's
461     *   restore command, and don't work because of
462     *   the rec->bsr = bsr optimization above.
463     */
464    if (!match_jobid(bsr, bsr->JobId, sessrec, 1)) {
465       Dmsg0(dbglevel, "fail on JobId\n");
466       goto no_match;
467
468    }
469    if (!match_job(bsr, bsr->job, sessrec, 1)) {
470       Dmsg0(dbglevel, "fail on Job\n");
471       goto no_match;
472    }
473    if (!match_client(bsr, bsr->client, sessrec, 1)) {
474       Dmsg0(dbglevel, "fail on Client\n");
475       goto no_match;
476    }
477    if (!match_job_type(bsr, bsr->JobType, sessrec, 1)) {
478       Dmsg0(dbglevel, "fail on Job type\n");
479       goto no_match;
480    }
481    if (!match_job_level(bsr, bsr->JobLevel, sessrec, 1)) {
482       Dmsg0(dbglevel, "fail on Job level\n");
483       goto no_match;
484    }
485    if (!match_stream(bsr, bsr->stream, rec, 1)) {
486       Dmsg0(dbglevel, "fail on stream\n");
487       goto no_match;
488    }
489    return 1;
490
491 no_match:
492    if (bsr->next) {
493       return match_all(bsr->next, rec, volrec, sessrec, bsr->done && done, jcr);
494    }
495    if (bsr->done && done) {
496       Dmsg0(dbglevel, "Leave match all -1\n");
497       return -1;
498    }
499    Dmsg0(dbglevel, "Leave match all 0\n");
500    return 0;
501 }
502
503 static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done)
504 {
505    if (!volume) {
506       return 0;                       /* Volume must match */
507    }
508    if (strcmp(volume->VolumeName, volrec->VolumeName) == 0) {
509       Dmsg1(dbglevel, "match_volume=%s\n", volrec->VolumeName);
510       return 1;
511    }
512    if (volume->next) {
513       return match_volume(bsr, volume->next, volrec, 1);
514    }
515    return 0;
516 }
517
518 static int match_client(BSR *bsr, BSR_CLIENT *client, SESSION_LABEL *sessrec, bool done)
519 {
520    if (!client) {
521       return 1;                       /* no specification matches all */
522    }
523    if (strcmp(client->ClientName, sessrec->ClientName) == 0) {
524       return 1;
525    }
526    if (client->next) {
527       return match_client(bsr, client->next, sessrec, 1);
528    }
529    return 0;
530 }
531
532 static int match_job(BSR *bsr, BSR_JOB *job, SESSION_LABEL *sessrec, bool done)
533 {
534    if (!job) {
535       return 1;                       /* no specification matches all */
536    }
537    if (strcmp(job->Job, sessrec->Job) == 0) {
538       return 1;
539    }
540    if (job->next) {
541       return match_job(bsr, job->next, sessrec, 1);
542    }
543    return 0;
544 }
545
546 static int match_job_type(BSR *bsr, BSR_JOBTYPE *job_type, SESSION_LABEL *sessrec, bool done)
547 {
548    if (!job_type) {
549       return 1;                       /* no specification matches all */
550    }
551    if (job_type->JobType == sessrec->JobType) {
552       return 1;
553    }
554    if (job_type->next) {
555       return match_job_type(bsr, job_type->next, sessrec, 1);
556    }
557    return 0;
558 }
559
560 static int match_job_level(BSR *bsr, BSR_JOBLEVEL *job_level, SESSION_LABEL *sessrec, bool done)
561 {
562    if (!job_level) {
563       return 1;                       /* no specification matches all */
564    }
565    if (job_level->JobLevel == sessrec->JobLevel) {
566       return 1;
567    }
568    if (job_level->next) {
569       return match_job_level(bsr, job_level->next, sessrec, 1);
570    }
571    return 0;
572 }
573
574 static int match_jobid(BSR *bsr, BSR_JOBID *jobid, SESSION_LABEL *sessrec, bool done)
575 {
576    if (!jobid) {
577       return 1;                       /* no specification matches all */
578    }
579    if (jobid->JobId <= sessrec->JobId && jobid->JobId2 >= sessrec->JobId) {
580       return 1;
581    }
582    if (jobid->next) {
583       return match_jobid(bsr, jobid->next, sessrec, 1);
584    }
585    return 0;
586 }
587
588 static int match_volfile(BSR *bsr, BSR_VOLFILE *volfile, DEV_RECORD *rec, bool done)
589 {
590    if (!volfile) {
591       return 1;                       /* no specification matches all */
592    }
593 /*
594  * The following code is turned off because this should now work
595  *   with disk files too, though since a "volfile" is 4GB, it does
596  *   not improve performance much.
597  */
598 #ifdef xxx
599    /* For the moment, these tests work only with tapes. */
600    if (!(rec->state & REC_ISTAPE)) {
601       return 1;                       /* All File records OK for this match */
602    }
603    Dmsg3(dbglevel, "match_volfile: sfile=%u efile=%u recfile=%u\n",
604              volfile->sfile, volfile->efile, rec->File);
605 #endif
606    if (volfile->sfile <= rec->File && volfile->efile >= rec->File) {
607       return 1;
608    }
609    /* Once we get past last efile, we are done */
610    if (rec->File > volfile->efile) {
611       volfile->done = true;              /* set local done */
612    }
613    if (volfile->next) {
614       return match_volfile(bsr, volfile->next, rec, volfile->done && done);
615    }
616
617    /* If we are done and all prior matches are done, this bsr is finished */
618    if (volfile->done && done) {
619       bsr->done = true;
620       bsr->root->reposition = true;
621       Dmsg2(dbglevel, "bsr done from volfile rec=%u volefile=%u\n",
622          rec->File, volfile->efile);
623    }
624    return 0;
625 }
626
627 static int match_voladdr(BSR *bsr, BSR_VOLADDR *voladdr, DEV_RECORD *rec, bool done)
628 {
629    if (!voladdr) {
630       return 1;                       /* no specification matches all */
631    }
632
633 #ifdef xxx
634
635    /* For the moment, these tests work only with disk. */
636    if (rec->state & REC_ISTAPE) {
637       uint32_t sFile = (voladdr->saddr)>>32;
638       uint32_t eFile = (voladdr->eaddr)>>32;
639       if (sFile <= rec->File && eFile >= rec->File) {
640          return 1;
641       }
642    }
643
644 #endif
645
646    uint64_t addr = get_record_address(rec);
647    Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
648          voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
649
650    if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
651       return 1;
652    }
653    /* Once we get past last eblock, we are done */
654    if (addr > voladdr->eaddr) {
655       voladdr->done = true;              /* set local done */
656    }
657    if (voladdr->next) {
658       return match_voladdr(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",
666             addr, voladdr->eaddr);
667    }
668    return 0;
669 }
670
671
672 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
673 {
674    if (!stream) {
675       return 1;                       /* no specification matches all */
676    }
677    if (stream->stream == rec->Stream) {
678       return 1;
679    }
680    if (stream->next) {
681       return match_stream(bsr, stream->next, rec, 1);
682    }
683    return 0;
684 }
685
686 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
687 {
688    if (!sesstime) {
689       return 1;                       /* no specification matches all */
690    }
691    if (sesstime->sesstime == rec->VolSessionTime) {
692       return 1;
693    }
694    if (rec->VolSessionTime > sesstime->sesstime) {
695       sesstime->done = true;
696    }
697    if (sesstime->next) {
698       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
699    }
700    if (sesstime->done && done) {
701       bsr->done = true;
702       bsr->root->reposition = true;
703       Dmsg0(dbglevel, "bsr done from sesstime\n");
704    }
705    return 0;
706 }
707
708 /*
709  * Note, we cannot mark bsr done based on session id because we may
710  *  have interleaved records, and there may be more of what we want
711  *  later.
712  */
713 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
714 {
715    if (!sessid) {
716       return 1;                       /* no specification matches all */
717    }
718    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
719       return 1;
720    }
721    if (sessid->next) {
722       return match_sessid(bsr, sessid->next, rec);
723    }
724    return 0;
725 }
726
727 /*
728  * When reading the Volume, the Volume Findex (rec->FileIndex) always
729  *   are found in sequential order. Thus we can make optimizations.
730  *
731  *  ***FIXME*** optimizations
732  * We could optimize by removing the recursion.
733  */
734 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
735 {
736    if (!findex) {
737       return 1;                       /* no specification matches all */
738    }
739    if (!findex->done) {
740       if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
741          Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
742                rec->FileIndex, findex->findex, findex->findex2);
743          return 1;
744       }
745       if (rec->FileIndex > findex->findex2) {
746          findex->done = true;
747       }
748    }
749    if (findex->next) {
750       return match_findex(bsr, findex->next, rec, findex->done && done);
751    }
752    if (findex->done && done) {
753       bsr->done = true;
754       bsr->root->reposition = true;
755       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
756    }
757    return 0;
758 }
759
760 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
761 {
762    uint64_t bsr_addr = 0;
763    uint32_t sfile = 0, sblock = 0;
764
765    if (bsr) {
766       if (bsr->voladdr) {
767          bsr_addr = bsr->voladdr->saddr;
768          sfile = bsr_addr>>32;
769          sblock = (uint32_t)bsr_addr;
770
771       } else if (bsr->volfile && bsr->volblock) {
772          bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
773          sfile = bsr->volfile->sfile;
774          sblock = bsr->volblock->sblock;
775       }
776    }
777
778    if (file && block) {
779       *file = sfile;
780       *block = sblock;
781    }
782
783    return bsr_addr;
784 }