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