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