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