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