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