]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/match_bsr.c
Fix seg fault during cancel in SD
[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 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  *   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(dbglevel, "Leave match all -1\n");
503       return -1;
504    }
505    Dmsg0(dbglevel, "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(dbglevel, "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
639 #ifdef xxx
640
641    /* For the moment, these tests work only with disk. */
642    if (rec->state & REC_ISTAPE) {
643       uint32_t sFile = (voladdr->saddr)>>32;
644       uint32_t eFile = (voladdr->eaddr)>>32;
645       if (sFile <= rec->File && eFile >= rec->File) {
646          return 1;
647       }
648    }
649
650 #endif
651
652    uint64_t addr = get_record_address(rec);
653    Dmsg6(dbglevel, "match_voladdr: saddr=%llu eaddr=%llu recaddr=%llu sfile=%u efile=%u recfile=%u\n",
654          voladdr->saddr, voladdr->eaddr, addr, voladdr->saddr>>32, voladdr->eaddr>>32, addr>>32);
655
656    if (voladdr->saddr <= addr && voladdr->eaddr >= addr) {
657       return 1;
658    }
659    /* Once we get past last eblock, we are done */
660    if (addr > voladdr->eaddr) {
661       voladdr->done = true;              /* set local done */
662    }
663    if (voladdr->next) {
664       return match_voladdr(bsr, voladdr->next, rec, voladdr->done && done);
665    }
666
667    /* If we are done and all prior matches are done, this bsr is finished */
668    if (voladdr->done && done) {
669       bsr->done = true;
670       bsr->root->reposition = true;
671       Dmsg2(dbglevel, "bsr done from voladdr rec=%llu voleaddr=%llu\n",
672             addr, voladdr->eaddr);
673    }
674    return 0;
675 }
676
677
678 static int match_stream(BSR *bsr, BSR_STREAM *stream, DEV_RECORD *rec, bool done)
679 {
680    if (!stream) {
681       return 1;                       /* no specification matches all */
682    }
683    if (stream->stream == rec->Stream) {
684       return 1;
685    }
686    if (stream->next) {
687       return match_stream(bsr, stream->next, rec, 1);
688    }
689    return 0;
690 }
691
692 static int match_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_RECORD *rec, bool done)
693 {
694    if (!sesstime) {
695       return 1;                       /* no specification matches all */
696    }
697    if (sesstime->sesstime == rec->VolSessionTime) {
698       return 1;
699    }
700    if (rec->VolSessionTime > sesstime->sesstime) {
701       sesstime->done = true;
702    }
703    if (sesstime->next) {
704       return match_sesstime(bsr, sesstime->next, rec, sesstime->done && done);
705    }
706    if (sesstime->done && done) {
707       bsr->done = true;
708       bsr->root->reposition = true;
709       Dmsg0(dbglevel, "bsr done from sesstime\n");
710    }
711    return 0;
712 }
713
714 /* 
715  * Note, we cannot mark bsr done based on session id because we may
716  *  have interleaved records, and there may be more of what we want
717  *  later.
718  */
719 static int match_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_RECORD *rec)
720 {
721    if (!sessid) {
722       return 1;                       /* no specification matches all */
723    }
724    if (sessid->sessid <= rec->VolSessionId && sessid->sessid2 >= rec->VolSessionId) {
725       return 1;
726    }
727    if (sessid->next) {
728       return match_sessid(bsr, sessid->next, rec);
729    }
730    return 0;
731 }
732
733 /*
734  * When reading the Volume, the Volume Findex (rec->FileIndex) always
735  *   are found in sequential order. Thus we can make optimizations.
736  *
737  *  ***FIXME*** optimizations
738  * We could optimize by removing the recursion.
739  */
740 static int match_findex(BSR *bsr, BSR_FINDEX *findex, DEV_RECORD *rec, bool done)
741 {
742    if (!findex) {
743       return 1;                       /* no specification matches all */
744    }
745    if (!findex->done) {
746       if (findex->findex <= rec->FileIndex && findex->findex2 >= rec->FileIndex) {
747          Dmsg3(dbglevel, "Match on findex=%d. bsrFIs=%d,%d\n",
748                rec->FileIndex, findex->findex, findex->findex2);
749          return 1;
750       }
751       if (rec->FileIndex > findex->findex2) {
752          findex->done = true;
753       }
754    }
755    if (findex->next) {
756       return match_findex(bsr, findex->next, rec, findex->done && done);
757    }
758    if (findex->done && done) {
759       bsr->done = true;
760       bsr->root->reposition = true;
761       Dmsg1(dbglevel, "bsr done from findex %d\n", rec->FileIndex);
762    }
763    return 0;
764 }
765
766 uint64_t get_bsr_start_addr(BSR *bsr, uint32_t *file, uint32_t *block)
767 {
768    uint64_t bsr_addr = 0;
769    uint32_t sfile = 0, sblock = 0;
770
771    if (bsr) {
772       if (bsr->voladdr) {
773          bsr_addr = bsr->voladdr->saddr;
774          sfile = bsr_addr>>32;
775          sblock = (uint32_t)bsr_addr;
776          
777       } else if (bsr->volfile && bsr->volblock) {
778          bsr_addr = (((uint64_t)bsr->volfile->sfile)<<32)|bsr->volblock->sblock;
779          sfile = bsr->volfile->sfile;
780          sblock = bsr->volblock->sblock;
781       }
782    }
783
784    if (file && block) {
785       *file = sfile;
786       *block = sblock;
787    }
788
789    return bsr_addr;
790 }